mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-30 20:08:57 +00:00
crosvm: carve-out unix specific set and get arguments
BUG=b:227659915 TEST=presubmit Change-Id: I1e7080c6cb19ffe34c052524933711b12d750b2f Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3643041 Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Vikram Auradkar <auradkar@google.com> Reviewed-by: Noah Gold <nkgold@google.com>
This commit is contained in:
parent
a6945f4a49
commit
5b3cfb9143
4 changed files with 433 additions and 385 deletions
391
src/main.rs
391
src/main.rs
|
@ -5,6 +5,7 @@
|
|||
//! Runs a virtual machine
|
||||
|
||||
pub mod panic_hook;
|
||||
pub mod sys;
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::convert::TryFrom;
|
||||
|
@ -24,11 +25,9 @@ use arch::{
|
|||
set_default_serial_parameters, MsrAction, MsrConfig, MsrValueFrom, Pstore, VcpuAffinity,
|
||||
};
|
||||
use base::{debug, error, getpid, info, kill_process_group, pagesize, reap_child, syslog, warn};
|
||||
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
|
||||
use crosvm::platform::GpuRenderServerParameters;
|
||||
use crosvm::{
|
||||
argument::{self, parse_hex_or_decimal, print_help, set_arguments, Argument},
|
||||
platform, BindMount, Config, Executable, FileBackedMappingParameters, GidMap, SharedDir,
|
||||
platform, BindMount, Config, Executable, FileBackedMappingParameters, GidMap,
|
||||
TouchDeviceOption, VfioCommand, VhostUserFsOption, VhostUserOption, VhostUserWlOption,
|
||||
VvuOption,
|
||||
};
|
||||
|
@ -574,56 +573,6 @@ fn parse_gpu_display_options(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
|
||||
fn parse_gpu_render_server_options(s: Option<&str>) -> argument::Result<GpuRenderServerParameters> {
|
||||
let mut path: Option<PathBuf> = None;
|
||||
let mut cache_path = None;
|
||||
let mut cache_size = None;
|
||||
|
||||
if let Some(s) = s {
|
||||
let opts = s
|
||||
.split(',')
|
||||
.map(|frag| frag.split('='))
|
||||
.map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
|
||||
|
||||
for (k, v) in opts {
|
||||
match k {
|
||||
"path" => {
|
||||
path =
|
||||
Some(
|
||||
PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
|
||||
value: v.to_string(),
|
||||
expected: e.to_string(),
|
||||
})?,
|
||||
)
|
||||
}
|
||||
"cache-path" => cache_path = Some(v.to_string()),
|
||||
"cache-size" => cache_size = Some(v.to_string()),
|
||||
"" => {}
|
||||
_ => {
|
||||
return Err(argument::Error::UnknownArgument(format!(
|
||||
"gpu-render-server parameter {}",
|
||||
k
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(p) = path {
|
||||
Ok(GpuRenderServerParameters {
|
||||
path: p,
|
||||
cache_path,
|
||||
cache_size,
|
||||
})
|
||||
} else {
|
||||
Err(argument::Error::InvalidValue {
|
||||
value: s.unwrap_or("").to_string(),
|
||||
expected: String::from("gpu-render-server must include 'path'"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "audio")]
|
||||
fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
|
||||
let mut ac97_params: Ac97Parameters = Default::default();
|
||||
|
@ -1101,39 +1050,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
|
||||
cfg.kvm_device_path = kvm_device_path;
|
||||
}
|
||||
"vhost-vsock-fd" => {
|
||||
if cfg.vhost_vsock_device.is_some() {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: String::from("A vhost-vsock device was already specified"),
|
||||
});
|
||||
}
|
||||
let fd: i32 = value
|
||||
.unwrap()
|
||||
.parse()
|
||||
.map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: String::from("this value for `vhost-vsock-fd` needs to be integer"),
|
||||
})?;
|
||||
cfg.vhost_vsock_device = Some(PathBuf::from(format!("/proc/self/fd/{}", fd)));
|
||||
}
|
||||
"vhost-vsock-device" => {
|
||||
if cfg.vhost_vsock_device.is_some() {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: String::from("A vhost-vsock device was already specified"),
|
||||
});
|
||||
}
|
||||
let vhost_vsock_device_path = PathBuf::from(value.unwrap());
|
||||
if !vhost_vsock_device_path.exists() {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: String::from("this vhost-vsock device path does not exist"),
|
||||
});
|
||||
}
|
||||
|
||||
cfg.vhost_vsock_device = Some(vhost_vsock_device_path);
|
||||
}
|
||||
"vhost-net-device" => {
|
||||
let vhost_net_device_path = PathBuf::from(value.unwrap());
|
||||
if !vhost_net_device_path.exists() {
|
||||
|
@ -1211,15 +1127,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
|
||||
cfg.vcpu_cgroup_path = Some(vcpu_cgroup_path);
|
||||
}
|
||||
#[cfg(feature = "audio_cras")]
|
||||
"cras-snd" => {
|
||||
cfg.cras_snds.push(
|
||||
value
|
||||
.unwrap()
|
||||
.parse()
|
||||
.map_err(|e: CrasSndError| argument::Error::Syntax(e.to_string()))?,
|
||||
);
|
||||
}
|
||||
"no-smt" => {
|
||||
cfg.no_smt = true;
|
||||
}
|
||||
|
@ -1447,23 +1354,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
},
|
||||
});
|
||||
}
|
||||
"host_ip" => {
|
||||
if cfg.host_ip.is_some() {
|
||||
return Err(argument::Error::TooManyArguments(
|
||||
"`host_ip` already given".to_owned(),
|
||||
));
|
||||
}
|
||||
cfg.host_ip =
|
||||
Some(
|
||||
value
|
||||
.unwrap()
|
||||
.parse()
|
||||
.map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""),
|
||||
})?,
|
||||
)
|
||||
}
|
||||
"netmask" => {
|
||||
if cfg.netmask.is_some() {
|
||||
return Err(argument::Error::TooManyArguments(
|
||||
|
@ -1629,150 +1519,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
})?,
|
||||
);
|
||||
}
|
||||
"shared-dir" => {
|
||||
// This is formatted as multiple fields, each separated by ":". The first 2 fields are
|
||||
// fixed (src:tag). The rest may appear in any order:
|
||||
//
|
||||
// * type=TYPE - must be one of "p9" or "fs" (default: p9)
|
||||
// * uidmap=UIDMAP - a uid map in the format "inner outer count[,inner outer count]"
|
||||
// (default: "0 <current euid> 1")
|
||||
// * gidmap=GIDMAP - a gid map in the same format as uidmap
|
||||
// (default: "0 <current egid> 1")
|
||||
// * privileged_quota_uids=UIDS - Space-separated list of privileged uid values. When
|
||||
// performing quota-related operations, these UIDs are treated as if they have
|
||||
// CAP_FOWNER.
|
||||
// * timeout=TIMEOUT - a timeout value in seconds, which indicates how long attributes
|
||||
// and directory contents should be considered valid (default: 5)
|
||||
// * cache=CACHE - one of "never", "always", or "auto" (default: auto)
|
||||
// * writeback=BOOL - indicates whether writeback caching should be enabled (default: false)
|
||||
let param = value.unwrap();
|
||||
let mut components = param.split(':');
|
||||
let src =
|
||||
PathBuf::from(
|
||||
components
|
||||
.next()
|
||||
.ok_or_else(|| argument::Error::InvalidValue {
|
||||
value: param.to_owned(),
|
||||
expected: String::from("missing source path for `shared-dir`"),
|
||||
})?,
|
||||
);
|
||||
let tag = components
|
||||
.next()
|
||||
.ok_or_else(|| argument::Error::InvalidValue {
|
||||
value: param.to_owned(),
|
||||
expected: String::from("missing tag for `shared-dir`"),
|
||||
})?
|
||||
.to_owned();
|
||||
|
||||
if !src.is_dir() {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: param.to_owned(),
|
||||
expected: String::from("source path for `shared-dir` must be a directory"),
|
||||
});
|
||||
}
|
||||
|
||||
let mut shared_dir = SharedDir {
|
||||
src,
|
||||
tag,
|
||||
..Default::default()
|
||||
};
|
||||
for opt in components {
|
||||
let mut o = opt.splitn(2, '=');
|
||||
let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
|
||||
value: opt.to_owned(),
|
||||
expected: String::from("`shared-dir` options must not be empty"),
|
||||
})?;
|
||||
let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
|
||||
value: opt.to_owned(),
|
||||
expected: String::from("`shared-dir` options must be of the form `kind=value`"),
|
||||
})?;
|
||||
|
||||
match kind {
|
||||
"type" => {
|
||||
shared_dir.kind =
|
||||
value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from("`type` must be one of `fs` or `9p`"),
|
||||
})?
|
||||
}
|
||||
"uidmap" => shared_dir.uid_map = value.into(),
|
||||
"gidmap" => shared_dir.gid_map = value.into(),
|
||||
#[cfg(feature = "chromeos")]
|
||||
"privileged_quota_uids" => {
|
||||
shared_dir.fs_cfg.privileged_quota_uids =
|
||||
value.split(' ').map(|s| s.parse().unwrap()).collect();
|
||||
}
|
||||
"timeout" => {
|
||||
let seconds = value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from("`timeout` must be an integer"),
|
||||
})?;
|
||||
|
||||
let dur = Duration::from_secs(seconds);
|
||||
shared_dir.fs_cfg.entry_timeout = dur;
|
||||
shared_dir.fs_cfg.attr_timeout = dur;
|
||||
}
|
||||
"cache" => {
|
||||
let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from(
|
||||
"`cache` must be one of `never`, `always`, or `auto`",
|
||||
),
|
||||
})?;
|
||||
shared_dir.fs_cfg.cache_policy = policy;
|
||||
}
|
||||
"writeback" => {
|
||||
let writeback =
|
||||
value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from("`writeback` must be a boolean"),
|
||||
})?;
|
||||
shared_dir.fs_cfg.writeback = writeback;
|
||||
}
|
||||
"rewrite-security-xattrs" => {
|
||||
let rewrite_security_xattrs =
|
||||
value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from(
|
||||
"`rewrite-security-xattrs` must be a boolean",
|
||||
),
|
||||
})?;
|
||||
shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs;
|
||||
}
|
||||
"ascii_casefold" => {
|
||||
let ascii_casefold =
|
||||
value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from("`ascii_casefold` must be a boolean"),
|
||||
})?;
|
||||
shared_dir.fs_cfg.ascii_casefold = ascii_casefold;
|
||||
shared_dir.p9_cfg.ascii_casefold = ascii_casefold;
|
||||
}
|
||||
"dax" => {
|
||||
let use_dax = value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from("`dax` must be a boolean"),
|
||||
})?;
|
||||
shared_dir.fs_cfg.use_dax = use_dax;
|
||||
}
|
||||
"posix_acl" => {
|
||||
let posix_acl =
|
||||
value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from("`posix_acl` must be a boolean"),
|
||||
})?;
|
||||
shared_dir.fs_cfg.posix_acl = posix_acl;
|
||||
}
|
||||
_ => {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: kind.to_owned(),
|
||||
expected: String::from("unrecognized option for `shared-dir`"),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
cfg.shared_dirs.push(shared_dir);
|
||||
}
|
||||
"seccomp-policy-dir" => {
|
||||
if let Some(jail_config) = &mut cfg.jail_config {
|
||||
// `value` is Some because we are in this match so it's safe to unwrap.
|
||||
|
@ -1859,22 +1605,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
}
|
||||
}
|
||||
"vhost-net" => cfg.vhost_net = true,
|
||||
"tap-fd" => {
|
||||
cfg.tap_fd.push(
|
||||
value
|
||||
.unwrap()
|
||||
.parse()
|
||||
.map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: String::from(
|
||||
"this value for `tap-fd` must be an unsigned integer",
|
||||
),
|
||||
})?,
|
||||
);
|
||||
}
|
||||
"tap-name" => {
|
||||
cfg.tap_name.push(value.unwrap().to_owned());
|
||||
}
|
||||
#[cfg(feature = "gpu")]
|
||||
"gpu" => {
|
||||
let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
|
||||
|
@ -1885,10 +1615,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
|
||||
parse_gpu_display_options(value, gpu_parameters)?;
|
||||
}
|
||||
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
|
||||
"gpu-render-server" => {
|
||||
cfg.gpu_render_server_parameters = Some(parse_gpu_render_server_options(value)?);
|
||||
}
|
||||
"software-tpm" => {
|
||||
cfg.software_tpm = true;
|
||||
}
|
||||
|
@ -2308,63 +2034,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
|
||||
cfg.vvu_proxy.push(vvu_opt);
|
||||
}
|
||||
"coiommu" => {
|
||||
let mut params: devices::CoIommuParameters = Default::default();
|
||||
if let Some(v) = value {
|
||||
let opts = v
|
||||
.split(',')
|
||||
.map(|frag| frag.splitn(2, '='))
|
||||
.map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
|
||||
|
||||
for (k, v) in opts {
|
||||
match k {
|
||||
"unpin_policy" => {
|
||||
params.unpin_policy = v
|
||||
.parse::<devices::CoIommuUnpinPolicy>()
|
||||
.map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
|
||||
}
|
||||
"unpin_interval" => {
|
||||
params.unpin_interval =
|
||||
Duration::from_secs(v.parse::<u64>().map_err(|e| {
|
||||
argument::Error::UnknownArgument(format!("{}", e))
|
||||
})?)
|
||||
}
|
||||
"unpin_limit" => {
|
||||
let limit = v
|
||||
.parse::<u64>()
|
||||
.map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?;
|
||||
|
||||
if limit == 0 {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: v.to_owned(),
|
||||
expected: String::from("Please use non-zero unpin_limit value"),
|
||||
});
|
||||
}
|
||||
|
||||
params.unpin_limit = Some(limit)
|
||||
}
|
||||
"unpin_gen_threshold" => {
|
||||
params.unpin_gen_threshold = v
|
||||
.parse::<u64>()
|
||||
.map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
|
||||
}
|
||||
_ => {
|
||||
return Err(argument::Error::UnknownArgument(format!(
|
||||
"coiommu parameter {}",
|
||||
k
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.coiommu_param.is_some() {
|
||||
return Err(argument::Error::TooManyArguments(
|
||||
"coiommu param already given".to_owned(),
|
||||
));
|
||||
}
|
||||
cfg.coiommu_param = Some(params);
|
||||
}
|
||||
"file-backed-mapping" => {
|
||||
cfg.file_backed_mappings
|
||||
.push(parse_file_backed_mapping(value)?);
|
||||
|
@ -2534,7 +2203,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
cfg.pcie_ecam = Some(MemRegion { base, size: len });
|
||||
}
|
||||
"help" => return Err(argument::Error::PrintHelp),
|
||||
_ => unreachable!(),
|
||||
_ => sys::set_arguments(cfg, name, value)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -2715,10 +2384,9 @@ enum CommandStatus {
|
|||
}
|
||||
|
||||
fn run_vm(args: std::env::Args) -> std::result::Result<CommandStatus, ()> {
|
||||
let arguments =
|
||||
&[Argument::positional("KERNEL", "bzImage of kernel to run"),
|
||||
let mut arguments =
|
||||
vec![Argument::positional("KERNEL", "bzImage of kernel to run"),
|
||||
Argument::value("kvm-device", "PATH", "Path to the KVM device. (default /dev/kvm)"),
|
||||
Argument::value("vhost-vsock-fd", "FD", "Open FD to the vhost-vsock device, mutually exclusive with vhost-vsock-device."),
|
||||
Argument::value("vhost-vsock-device", "PATH", "Path to the vhost-vsock device. (default /dev/vhost-vsock)"),
|
||||
Argument::value("vhost-net-device", "PATH", "Path to the vhost-net device. (default /dev/vhost-net)"),
|
||||
Argument::value("android-fstab", "PATH", "Path to Android fstab"),
|
||||
|
@ -2736,20 +2404,6 @@ fn run_vm(args: std::env::Args) -> std::result::Result<CommandStatus, ()> {
|
|||
making all vCPU threads share same cookie for core scheduling.
|
||||
This option is no-op on devices that have neither MDS nor L1TF vulnerability."),
|
||||
Argument::value("vcpu-cgroup-path", "PATH", "Move all vCPU threads to this CGroup (default: nothing moves)."),
|
||||
#[cfg(feature = "audio_cras")]
|
||||
Argument::value("cras-snd",
|
||||
"[capture=true,client=crosvm,socket=unified,num_output_streams=1,num_input_streams=1]",
|
||||
"Comma separated key=value pairs for setting up cras snd devices.
|
||||
|
||||
Possible key values:
|
||||
|
||||
capture - Enable audio capture. Default to false.
|
||||
|
||||
client_type - Set specific client type for cras backend.
|
||||
|
||||
num_output_streams - Set number of output PCM streams
|
||||
|
||||
num_input_streams - Set number of input PCM streams"),
|
||||
Argument::flag("no-smt", "Don't use SMT in the guest"),
|
||||
Argument::value("rt-cpus", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on. (e.g. 0,1-3,5) (default: none)"),
|
||||
Argument::flag("delay-rt", "Don't set VCPUs real-time until make-rt command is run"),
|
||||
|
@ -2785,9 +2439,6 @@ fn run_vm(args: std::env::Args) -> std::result::Result<CommandStatus, ()> {
|
|||
Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
|
||||
Argument::value("pmem-device", "PATH", "Path to a disk image."),
|
||||
Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file followed by size."),
|
||||
Argument::value("host_ip",
|
||||
"IP",
|
||||
"IP address to assign to host tap interface."),
|
||||
Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
|
||||
Argument::value("mac", "MAC", "MAC address for VM."),
|
||||
Argument::value("net-vq-pairs", "N", "virtio net virtual queue paris. (default: 1)"),
|
||||
|
@ -2887,12 +2538,6 @@ There is a cost of slightly increased latency the first time the file is accesse
|
|||
#[cfg(feature = "plugin")]
|
||||
Argument::value("plugin-gid-map-file", "PATH", "Path to the file listing supplemental GIDs that should be mapped in plugin jail. Can be given more than once."),
|
||||
Argument::flag("vhost-net", "Use vhost for networking."),
|
||||
Argument::value("tap-name",
|
||||
"NAME",
|
||||
"Name of a configured persistent TAP interface to use for networking. A different virtual network card will be added each time this argument is given."),
|
||||
Argument::value("tap-fd",
|
||||
"fd",
|
||||
"File descriptor for configured tap device. A different virtual network card will be added each time this argument is given."),
|
||||
#[cfg(feature = "gpu")]
|
||||
Argument::flag_or_value("gpu",
|
||||
"[width=INT,height=INT]",
|
||||
|
@ -2931,18 +2576,6 @@ There is a cost of slightly increased latency the first time the file is accesse
|
|||
width=INT - The width of the virtual display connected to the virtio-gpu.
|
||||
|
||||
height=INT - The height of the virtual display connected to the virtio-gpu."),
|
||||
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
|
||||
Argument::flag_or_value("gpu-render-server",
|
||||
"[path=PATH]",
|
||||
"(EXPERIMENTAL) Comma separated key=value pairs for setting up a render server for the virtio-gpu device
|
||||
|
||||
Possible key values:
|
||||
|
||||
path=PATH - The path to the render server executable.
|
||||
|
||||
cache-path=PATH - The path to the render server shader cache.
|
||||
|
||||
cache-size=SIZE - The maximum size of the shader cache."),
|
||||
#[cfg(feature = "tpm")]
|
||||
Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
|
||||
Argument::value("evdev", "PATH", "Path to an event device node. The device will be grabbed (unusable from the host) and made available to the guest with the same configuration it shows on the host"),
|
||||
|
@ -3045,19 +2678,6 @@ iommu=on|off - indicates whether to enable virtio IOMMU for this device"),
|
|||
subsystem_device=NUM - PCI subsystem device ID
|
||||
|
||||
revision=NUM - revision"),
|
||||
Argument::flag_or_value("coiommu",
|
||||
"unpin_policy=POLICY,unpin_interval=NUM,unpin_limit=NUM,unpin_gen_threshold=NUM ",
|
||||
"Comma separated key=value pairs for setting up coiommu devices.
|
||||
|
||||
Possible key values:
|
||||
|
||||
unpin_policy=lru - LRU unpin policy.
|
||||
|
||||
unpin_interval=NUM - Unpin interval time in seconds.
|
||||
|
||||
unpin_limit=NUM - Unpin limit for each unpin cycle, in unit of page count. 0 is invalid.
|
||||
|
||||
unpin_gen_threshold=NUM - Number of unpin intervals a pinned page must be busy for to be aged into the older which is less frequently checked generation."),
|
||||
Argument::value("file-backed-mapping", "addr=NUM,size=SIZE,path=PATH[,offset=NUM][,ro][,rw][,sync]", "Map the given file into guest memory at the specified address.
|
||||
|
||||
Parameters (addr, size, path are required):
|
||||
|
@ -3094,6 +2714,7 @@ iommu=on|off - indicates whether to enable virtio IOMMU for this device"),
|
|||
"Base and length for PCIE Enhanced Configuration Access Mechanism"),
|
||||
Argument::short_flag('h', "help", "Print help message.")];
|
||||
|
||||
arguments.append(&mut sys::get_arguments());
|
||||
let mut cfg = Config::default();
|
||||
let match_res = set_arguments(args, &arguments[..], |name, value| {
|
||||
set_argument(&mut cfg, name, value)
|
||||
|
|
14
src/sys.rs
Normal file
14
src/sys.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2022 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(unix)] {
|
||||
pub(crate) mod unix;
|
||||
use unix as platform;
|
||||
} else {
|
||||
compile_error!("Unsupported platform");
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use platform::main::{get_arguments, set_arguments};
|
5
src/sys/unix.rs
Normal file
5
src/sys/unix.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright 2022 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
pub(crate) mod main;
|
408
src/sys/unix/main.rs
Normal file
408
src/sys/unix/main.rs
Normal file
|
@ -0,0 +1,408 @@
|
|||
// Copyright 2022 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
|
||||
use std::str::FromStr;
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
|
||||
#[cfg(feature = "audio_cras")]
|
||||
use devices::virtio::{
|
||||
snd::cras_backend::Error as CrasSndError, vhost::user::device::run_cras_snd_device,
|
||||
};
|
||||
|
||||
use crate::argument::{self, Argument};
|
||||
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
|
||||
use crate::platform::GpuRenderServerParameters;
|
||||
use crosvm::{Config, SharedDir};
|
||||
|
||||
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
|
||||
fn parse_gpu_render_server_options(s: Option<&str>) -> argument::Result<GpuRenderServerParameters> {
|
||||
let mut path: Option<PathBuf> = None;
|
||||
let mut cache_path = None;
|
||||
let mut cache_size = None;
|
||||
|
||||
if let Some(s) = s {
|
||||
let opts = s
|
||||
.split(',')
|
||||
.map(|frag| frag.split('='))
|
||||
.map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
|
||||
|
||||
for (k, v) in opts {
|
||||
match k {
|
||||
"path" => {
|
||||
path =
|
||||
Some(
|
||||
PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
|
||||
value: v.to_string(),
|
||||
expected: e.to_string(),
|
||||
})?,
|
||||
)
|
||||
}
|
||||
"cache-path" => cache_path = Some(v.to_string()),
|
||||
"cache-size" => cache_size = Some(v.to_string()),
|
||||
"" => {}
|
||||
_ => {
|
||||
return Err(argument::Error::UnknownArgument(format!(
|
||||
"gpu-render-server parameter {}",
|
||||
k
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(p) = path {
|
||||
Ok(GpuRenderServerParameters {
|
||||
path: p,
|
||||
cache_path,
|
||||
cache_size,
|
||||
})
|
||||
} else {
|
||||
Err(argument::Error::InvalidValue {
|
||||
value: s.unwrap_or("").to_string(),
|
||||
expected: String::from("gpu-render-server must include 'path'"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_arguments() -> Vec<Argument> {
|
||||
vec![
|
||||
#[cfg(feature = "audio_cras")]
|
||||
Argument::value("cras-snd",
|
||||
"[capture=true,client=crosvm,socket=unified,num_output_streams=1,num_input_streams=1]",
|
||||
"Comma separated key=value pairs for setting up cras snd devices.
|
||||
|
||||
Possible key values:
|
||||
|
||||
capture - Enable audio capture. Default to false.
|
||||
|
||||
client_type - Set specific client type for cras backend.
|
||||
|
||||
num_output_streams - Set number of output PCM streams
|
||||
|
||||
num_input_streams - Set number of input PCM streams"),
|
||||
Argument::value("host_ip",
|
||||
"IP",
|
||||
"IP address to assign to host tap interface."),
|
||||
Argument::value("tap-name",
|
||||
"NAME",
|
||||
"Name of a configured persistent TAP interface to use for networking. A different virtual network card will be added each time this argument is given."),
|
||||
Argument::value("tap-fd",
|
||||
"fd",
|
||||
"File descriptor for configured tap device. A different virtual network card will be added each time this argument is given."),
|
||||
Argument::value("vhost-vsock-fd", "FD", "Open FD to the vhost-vsock device, mutually exclusive with vhost-vsock-device."),
|
||||
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
|
||||
Argument::flag_or_value("gpu-render-server",
|
||||
"[path=PATH]",
|
||||
"(EXPERIMENTAL) Comma separated key=value pairs for setting up a render server for the virtio-gpu device
|
||||
|
||||
Possible key values:
|
||||
|
||||
path=PATH - The path to the render server executable.
|
||||
|
||||
cache-path=PATH - The path to the render server shader cache.
|
||||
|
||||
cache-size=SIZE - The maximum size of the shader cache."),
|
||||
Argument::flag_or_value("coiommu",
|
||||
"unpin_policy=POLICY,unpin_interval=NUM,unpin_limit=NUM,unpin_gen_threshold=NUM ",
|
||||
"Comma separated key=value pairs for setting up coiommu devices.
|
||||
|
||||
Possible key values:
|
||||
|
||||
unpin_policy=lru - LRU unpin policy.
|
||||
|
||||
unpin_interval=NUM - Unpin interval time in seconds.
|
||||
|
||||
unpin_limit=NUM - Unpin limit for each unpin cycle, in unit of page count. 0 is invalid.
|
||||
|
||||
unpin_gen_threshold=NUM - Number of unpin intervals a pinned page must be busy for to be aged into the older which is less frequently checked generation."),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn set_arguments(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
|
||||
match name {
|
||||
#[cfg(feature = "audio_cras")]
|
||||
"cras-snd" => {
|
||||
cfg.cras_snds.push(
|
||||
value
|
||||
.unwrap()
|
||||
.parse()
|
||||
.map_err(|e: CrasSndError| argument::Error::Syntax(e.to_string()))?,
|
||||
);
|
||||
}
|
||||
"vhost-vsock-fd" => {
|
||||
if cfg.vhost_vsock_device.is_some() {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: String::from("A vhost-vsock device was already specified"),
|
||||
});
|
||||
}
|
||||
let fd: i32 = value
|
||||
.unwrap()
|
||||
.parse()
|
||||
.map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: String::from("this value for `vhost-vsock-fd` needs to be integer"),
|
||||
})?;
|
||||
cfg.vhost_vsock_device = Some(PathBuf::from(format!("/proc/self/fd/{}", fd)));
|
||||
}
|
||||
"vhost-vsock-device" => {
|
||||
if cfg.vhost_vsock_device.is_some() {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: String::from("A vhost-vsock device was already specified"),
|
||||
});
|
||||
}
|
||||
let vhost_vsock_device_path = PathBuf::from(value.unwrap());
|
||||
if !vhost_vsock_device_path.exists() {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: String::from("this vhost-vsock device path does not exist"),
|
||||
});
|
||||
}
|
||||
|
||||
cfg.vhost_vsock_device = Some(vhost_vsock_device_path);
|
||||
}
|
||||
"shared-dir" => {
|
||||
// This is formatted as multiple fields, each separated by ":". The first 2 fields are
|
||||
// fixed (src:tag). The rest may appear in any order:
|
||||
//
|
||||
// * type=TYPE - must be one of "p9" or "fs" (default: p9)
|
||||
// * uidmap=UIDMAP - a uid map in the format "inner outer count[,inner outer count]"
|
||||
// (default: "0 <current euid> 1")
|
||||
// * gidmap=GIDMAP - a gid map in the same format as uidmap
|
||||
// (default: "0 <current egid> 1")
|
||||
// * privileged_quota_uids=UIDS - Space-separated list of privileged uid values. When
|
||||
// performing quota-related operations, these UIDs are treated as if they have
|
||||
// CAP_FOWNER.
|
||||
// * timeout=TIMEOUT - a timeout value in seconds, which indicates how long attributes
|
||||
// and directory contents should be considered valid (default: 5)
|
||||
// * cache=CACHE - one of "never", "always", or "auto" (default: auto)
|
||||
// * writeback=BOOL - indicates whether writeback caching should be enabled (default: false)
|
||||
let param = value.unwrap();
|
||||
let mut components = param.split(':');
|
||||
let src =
|
||||
PathBuf::from(
|
||||
components
|
||||
.next()
|
||||
.ok_or_else(|| argument::Error::InvalidValue {
|
||||
value: param.to_owned(),
|
||||
expected: String::from("missing source path for `shared-dir`"),
|
||||
})?,
|
||||
);
|
||||
let tag = components
|
||||
.next()
|
||||
.ok_or_else(|| argument::Error::InvalidValue {
|
||||
value: param.to_owned(),
|
||||
expected: String::from("missing tag for `shared-dir`"),
|
||||
})?
|
||||
.to_owned();
|
||||
|
||||
if !src.is_dir() {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: param.to_owned(),
|
||||
expected: String::from("source path for `shared-dir` must be a directory"),
|
||||
});
|
||||
}
|
||||
|
||||
let mut shared_dir = SharedDir {
|
||||
src,
|
||||
tag,
|
||||
..Default::default()
|
||||
};
|
||||
for opt in components {
|
||||
let mut o = opt.splitn(2, '=');
|
||||
let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
|
||||
value: opt.to_owned(),
|
||||
expected: String::from("`shared-dir` options must not be empty"),
|
||||
})?;
|
||||
let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
|
||||
value: opt.to_owned(),
|
||||
expected: String::from("`shared-dir` options must be of the form `kind=value`"),
|
||||
})?;
|
||||
|
||||
match kind {
|
||||
"type" => {
|
||||
shared_dir.kind =
|
||||
value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from("`type` must be one of `fs` or `9p`"),
|
||||
})?
|
||||
}
|
||||
"uidmap" => shared_dir.uid_map = value.into(),
|
||||
"gidmap" => shared_dir.gid_map = value.into(),
|
||||
#[cfg(feature = "chromeos")]
|
||||
"privileged_quota_uids" => {
|
||||
shared_dir.fs_cfg.privileged_quota_uids =
|
||||
value.split(' ').map(|s| s.parse().unwrap()).collect();
|
||||
}
|
||||
"timeout" => {
|
||||
let seconds = value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from("`timeout` must be an integer"),
|
||||
})?;
|
||||
|
||||
let dur = Duration::from_secs(seconds);
|
||||
shared_dir.fs_cfg.entry_timeout = dur;
|
||||
shared_dir.fs_cfg.attr_timeout = dur;
|
||||
}
|
||||
"cache" => {
|
||||
let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from(
|
||||
"`cache` must be one of `never`, `always`, or `auto`",
|
||||
),
|
||||
})?;
|
||||
shared_dir.fs_cfg.cache_policy = policy;
|
||||
}
|
||||
"writeback" => {
|
||||
let writeback =
|
||||
value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from("`writeback` must be a boolean"),
|
||||
})?;
|
||||
shared_dir.fs_cfg.writeback = writeback;
|
||||
}
|
||||
"rewrite-security-xattrs" => {
|
||||
let rewrite_security_xattrs =
|
||||
value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from(
|
||||
"`rewrite-security-xattrs` must be a boolean",
|
||||
),
|
||||
})?;
|
||||
shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs;
|
||||
}
|
||||
"ascii_casefold" => {
|
||||
let ascii_casefold =
|
||||
value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from("`ascii_casefold` must be a boolean"),
|
||||
})?;
|
||||
shared_dir.fs_cfg.ascii_casefold = ascii_casefold;
|
||||
shared_dir.p9_cfg.ascii_casefold = ascii_casefold;
|
||||
}
|
||||
"dax" => {
|
||||
let use_dax = value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from("`dax` must be a boolean"),
|
||||
})?;
|
||||
shared_dir.fs_cfg.use_dax = use_dax;
|
||||
}
|
||||
"posix_acl" => {
|
||||
let posix_acl =
|
||||
value.parse().map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.to_owned(),
|
||||
expected: String::from("`posix_acl` must be a boolean"),
|
||||
})?;
|
||||
shared_dir.fs_cfg.posix_acl = posix_acl;
|
||||
}
|
||||
_ => {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: kind.to_owned(),
|
||||
expected: String::from("unrecognized option for `shared-dir`"),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
cfg.shared_dirs.push(shared_dir);
|
||||
}
|
||||
"host_ip" => {
|
||||
if cfg.host_ip.is_some() {
|
||||
return Err(argument::Error::TooManyArguments(
|
||||
"`host_ip` already given".to_owned(),
|
||||
));
|
||||
}
|
||||
cfg.host_ip =
|
||||
Some(
|
||||
value
|
||||
.unwrap()
|
||||
.parse()
|
||||
.map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""),
|
||||
})?,
|
||||
)
|
||||
}
|
||||
"tap-name" => {
|
||||
cfg.tap_name.push(value.unwrap().to_owned());
|
||||
}
|
||||
"tap-fd" => {
|
||||
cfg.tap_fd.push(
|
||||
value
|
||||
.unwrap()
|
||||
.parse()
|
||||
.map_err(|_| argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: String::from(
|
||||
"this value for `tap-fd` must be an unsigned integer",
|
||||
),
|
||||
})?,
|
||||
);
|
||||
}
|
||||
"coiommu" => {
|
||||
let mut params: devices::CoIommuParameters = Default::default();
|
||||
if let Some(v) = value {
|
||||
let opts = v
|
||||
.split(',')
|
||||
.map(|frag| frag.splitn(2, '='))
|
||||
.map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
|
||||
|
||||
for (k, v) in opts {
|
||||
match k {
|
||||
"unpin_policy" => {
|
||||
params.unpin_policy = v
|
||||
.parse::<devices::CoIommuUnpinPolicy>()
|
||||
.map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
|
||||
}
|
||||
"unpin_interval" => {
|
||||
params.unpin_interval =
|
||||
Duration::from_secs(v.parse::<u64>().map_err(|e| {
|
||||
argument::Error::UnknownArgument(format!("{}", e))
|
||||
})?)
|
||||
}
|
||||
"unpin_limit" => {
|
||||
let limit = v
|
||||
.parse::<u64>()
|
||||
.map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?;
|
||||
|
||||
if limit == 0 {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: v.to_owned(),
|
||||
expected: String::from("Please use non-zero unpin_limit value"),
|
||||
});
|
||||
}
|
||||
|
||||
params.unpin_limit = Some(limit)
|
||||
}
|
||||
"unpin_gen_threshold" => {
|
||||
params.unpin_gen_threshold = v
|
||||
.parse::<u64>()
|
||||
.map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
|
||||
}
|
||||
_ => {
|
||||
return Err(argument::Error::UnknownArgument(format!(
|
||||
"coiommu parameter {}",
|
||||
k
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.coiommu_param.is_some() {
|
||||
return Err(argument::Error::TooManyArguments(
|
||||
"coiommu param already given".to_owned(),
|
||||
));
|
||||
}
|
||||
cfg.coiommu_param = Some(params);
|
||||
}
|
||||
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
|
||||
"gpu-render-server" => {
|
||||
cfg.gpu_render_server_parameters = Some(parse_gpu_render_server_options(value)?);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in a new issue