diff options
author | Jingkui Wang <jkwang@google.com> | 2019-03-08 20:41:57 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2019-03-17 21:33:08 -0700 |
commit | 100e6e48ad292406fb6f0a7eeb85465850bc28c7 (patch) | |
tree | 30f59fcb07b7cbee649a07390cd2397cfb65c463 /src | |
parent | c324429b467d530fbeadef1fc9b527bb23ce1632 (diff) | |
download | crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.tar crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.tar.gz crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.tar.bz2 crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.tar.lz crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.tar.xz crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.tar.zst crosvm-100e6e48ad292406fb6f0a7eeb85465850bc28c7.zip |
implement xhci and add it to pci bus
Implement xhci controller, setup seccomp filters and add xhci to pci bus. CQ-DEPEND=CL:1512761 BUG=chromium:831850 TEST=local build Change-Id: I5c05452ece66e99d3a670e259e095fca616e835d Reviewed-on: https://chromium-review.googlesource.com/1512762 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Tested-by: Jingkui Wang <jkwang@google.com> Reviewed-by: Jingkui Wang <jkwang@google.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/linux.rs | 16 | ||||
-rw-r--r-- | src/main.rs | 212 |
2 files changed, 217 insertions, 11 deletions
diff --git a/src/linux.rs b/src/linux.rs index 37910f4..4674241 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -25,7 +25,7 @@ use libc::{self, c_int, gid_t, uid_t}; use audio_streams::DummyStreamSource; use byteorder::{ByteOrder, LittleEndian}; use devices::virtio::{self, VirtioDevice}; -use devices::{self, PciDevice, VirtioPciDevice}; +use devices::{self, HostBackendDeviceProvider, PciDevice, VirtioPciDevice, XhciController}; use io_jail::{self, Minijail}; use kvm::*; use libcras::CrasClient; @@ -44,7 +44,7 @@ use sys_util::{ #[cfg(feature = "gpu-forward")] use sys_util::{GuestAddress, MemoryMapping, Protection}; use vhost; -use vm_control::{VmRequest, VmResponse, VmRunMode}; +use vm_control::{UsbControlSocket, VmRequest, VmResponse, VmRunMode}; use crate::{Config, DiskOption, TouchDeviceOption}; @@ -80,6 +80,7 @@ pub enum Error { CreateTapDevice(NetError), CreateTimerFd(sys_util::Error), CreateTpmStorage(PathBuf, io::Error), + CreateUsbProvider(devices::usb::host_backend::error::Error), DetectImageType(qcow::Error), DeviceJail(io_jail::Error), DevicePivotRoot(io_jail::Error), @@ -151,6 +152,7 @@ impl Display for Error { CreateTpmStorage(p, e) => { write!(f, "failed to create tpm storage dir {}: {}", p.display(), e) } + CreateUsbProvider(e) => write!(f, "failed to create usb provider: {}", e), DetectImageType(e) => write!(f, "failed to detect disk image type: {}", e), DeviceJail(e) => write!(f, "failed to jail device: {}", e), DevicePivotRoot(e) => write!(f, "failed to pivot root device: {}", e), @@ -765,6 +767,7 @@ fn create_devices( wayland_device_socket: UnixSeqpacket, balloon_device_socket: UnixSeqpacket, disk_device_sockets: &mut Vec<UnixSeqpacket>, + usb_provider: HostBackendDeviceProvider, ) -> DeviceResult<Vec<(Box<PciDevice>, Option<Minijail>)>> { let stubs = create_virtio_devices( &cfg, @@ -802,6 +805,9 @@ fn create_devices( simple_jail(&cfg, "null_audio_device.policy")?, )); } + // Create xhci controller. + let usb_controller = Box::new(XhciController::new(mem.clone(), usb_provider)); + pci_devices.push((usb_controller, simple_jail(&cfg, "xhci.policy")?)); Ok(pci_devices) } @@ -1068,6 +1074,8 @@ pub fn run_config(cfg: Config) -> Result<()> { info!("crosvm entering multiprocess mode"); } + let (usb_control_socket, usb_provider) = + HostBackendDeviceProvider::new().map_err(|e| Error::CreateUsbProvider(e))?; // Masking signals is inherently dangerous, since this can persist across clones/execs. Do this // before any jailed devices have been spawned, so that we can catch any of them that fail very // quickly. @@ -1130,6 +1138,7 @@ pub fn run_config(cfg: Config) -> Result<()> { wayland_device_socket, balloon_device_socket, &mut disk_device_sockets, + usb_provider, ) }) .map_err(Error::BuildVm)?; @@ -1174,6 +1183,7 @@ pub fn run_config(cfg: Config) -> Result<()> { control_sockets, balloon_host_socket, &disk_host_sockets, + usb_control_socket, sigchld_fd, _render_node_host, sandbox, @@ -1186,6 +1196,7 @@ fn run_control( mut control_sockets: Vec<MsgSocket<VmResponse, VmRequest>>, balloon_host_socket: UnixSeqpacket, disk_host_sockets: &[MsgSocket<VmRequest, VmResponse>], + usb_control_socket: UsbControlSocket, sigchld_fd: SignalFd, _render_node_host: RenderNodeHost, sandbox: bool, @@ -1465,6 +1476,7 @@ fn run_control( &mut run_mode_opt, &balloon_host_socket, disk_host_sockets, + &usb_control_socket, ); if let Err(e) = socket.send(&response) { error!("failed to send VmResponse: {}", e); diff --git a/src/main.rs b/src/main.rs index a0cc1c2..d48b166 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,18 +43,22 @@ pub mod panic_hook; #[cfg(feature = "plugin")] pub mod plugin; -use std::fs::OpenOptions; +use std::fmt; +use std::fs::{File, OpenOptions}; use std::net; -use std::os::unix::io::RawFd; -use std::path::PathBuf; +use std::num::ParseIntError; +use std::os::unix::io::{FromRawFd, RawFd}; +use std::path::{Path, PathBuf}; use std::string::String; use std::thread::sleep; use std::time::Duration; use msg_socket::{MsgReceiver, MsgSender, MsgSocket}; use qcow::QcowFile; -use sys_util::{getpid, kill_process_group, net::UnixSeqpacket, reap_child, syslog}; -use vm_control::{VmRequest, VmResponse}; +use sys_util::{ + getpid, kill_process_group, net::UnixSeqpacket, reap_child, syslog, validate_raw_fd, +}; +use vm_control::{MaybeOwnedFd, UsbControlCommand, UsbControlResult, VmRequest, VmResponse}; use crate::argument::{print_help, set_arguments, Argument}; @@ -777,8 +781,11 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> { } } -fn vms_request(request: &VmRequest, args: std::env::Args) -> std::result::Result<(), ()> { - let mut return_result = Ok(()); +fn handle_request( + request: &VmRequest, + args: std::env::Args, +) -> std::result::Result<VmResponse, ()> { + let mut return_result = Err(()); for socket_path in args { match UnixSeqpacket::connect(&socket_path) { Ok(s) => { @@ -792,7 +799,7 @@ fn vms_request(request: &VmRequest, args: std::env::Args) -> std::result::Result continue; } match socket.recv() { - Ok(response) => info!("request response was {}", response), + Ok(response) => return_result = Ok(response), Err(e) => { error!( "failed to send request to socket at2 '{}': {}", @@ -813,6 +820,12 @@ fn vms_request(request: &VmRequest, args: std::env::Args) -> std::result::Result return_result } +fn vms_request(request: &VmRequest, args: std::env::Args) -> std::result::Result<(), ()> { + let response = handle_request(request, args)?; + info!("request response was {}", response); + Ok(()) +} + fn stop_vms(args: std::env::Args) -> std::result::Result<(), ()> { if args.len() == 0 { print_help("crosvm stop", "VM_SOCKET...", &[]); @@ -930,13 +943,193 @@ fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> { vms_request(&request, args) } +enum ModifyUsbError { + ArgMissing(&'static str), + ArgParse(&'static str, String), + ArgParseInt(&'static str, String, ParseIntError), + FailedFdValidate(sys_util::Error), + PathDoesNotExist(PathBuf), + SocketFailed, + UnexpectedResponse(VmResponse), + UnknownCommand(String), + UsbControl(UsbControlResult), +} + +impl fmt::Display for ModifyUsbError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::ModifyUsbError::*; + + match self { + ArgMissing(a) => write!(f, "argument missing: {}", a), + ArgParse(name, value) => { + write!(f, "failed to parse argument {} value `{}`", name, value) + } + ArgParseInt(name, value, e) => write!( + f, + "failed to parse integer argument {} value `{}`: {}", + name, value, e + ), + FailedFdValidate(e) => write!(f, "failed to validate file descriptor: {}", e), + PathDoesNotExist(p) => write!(f, "path `{}` does not exist", p.display()), + SocketFailed => write!(f, "socket failed"), + UnexpectedResponse(r) => write!(f, "unexpected response: {}", r), + UnknownCommand(c) => write!(f, "unknown command: `{}`", c), + UsbControl(e) => write!(f, "{}", e), + } + } +} + +type ModifyUsbResult<T> = std::result::Result<T, ModifyUsbError>; + +fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> { + debug!("parse_bus_id_addr: {}", v); + let mut ids = v.split(":"); + match (ids.next(), ids.next(), ids.next(), ids.next()) { + (Some(bus_id), Some(addr), Some(vid), Some(pid)) => { + let bus_id = bus_id + .parse::<u8>() + .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?; + let addr = addr + .parse::<u8>() + .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?; + let vid = u16::from_str_radix(&vid, 16) + .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?; + let pid = u16::from_str_radix(&pid, 16) + .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?; + Ok((bus_id, addr, vid, pid)) + } + _ => Err(ModifyUsbError::ArgParse( + "BUS_ID_ADDR_BUS_NUM_DEV_NUM", + v.to_owned(), + )), + } +} + +fn raw_fd_from_path(path: &Path) -> ModifyUsbResult<RawFd> { + if !path.exists() { + return Err(ModifyUsbError::PathDoesNotExist(path.to_owned())); + } + let raw_fd = path + .file_name() + .and_then(|fd_osstr| fd_osstr.to_str()) + .map_or( + Err(ModifyUsbError::ArgParse( + "USB_DEVICE_PATH", + path.to_string_lossy().into_owned(), + )), + |fd_str| { + fd_str.parse::<libc::c_int>().map_err(|e| { + ModifyUsbError::ArgParseInt("USB_DEVICE_PATH", fd_str.to_owned(), e) + }) + }, + )?; + validate_raw_fd(raw_fd).map_err(|e| ModifyUsbError::FailedFdValidate(e)) +} + +fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> { + let val = args + .next() + .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?; + let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?; + let dev_path = PathBuf::from( + args.next() + .ok_or(ModifyUsbError::ArgMissing("usb device path"))?, + ); + let usb_file: Option<File> = if dev_path == Path::new("-") { + None + } else if dev_path.parent() == Some(Path::new("/proc/self/fd")) { + // Special case '/proc/self/fd/*' paths. The FD is already open, just use it. + // Safe because we will validate |raw_fd|. + Some(unsafe { File::from_raw_fd(raw_fd_from_path(&dev_path)?) }) + } else { + Some( + OpenOptions::new() + .read(true) + .write(true) + .open(&dev_path) + .map_err(|_| ModifyUsbError::UsbControl(UsbControlResult::FailedToOpenDevice))?, + ) + }; + + let request = VmRequest::UsbCommand(UsbControlCommand::AttachDevice { + bus, + addr, + vid, + pid, + fd: usb_file.map(|f| MaybeOwnedFd::Owned(f)), + }); + let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?; + match response { + VmResponse::UsbResponse(usb_resp) => Ok(usb_resp), + r => Err(ModifyUsbError::UnexpectedResponse(r)), + } +} + +fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> { + let port: u8 = args + .next() + .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| { + p.parse::<u8>() + .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e)) + })?; + let request = VmRequest::UsbCommand(UsbControlCommand::DetachDevice { port }); + let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?; + match response { + VmResponse::UsbResponse(usb_resp) => Ok(usb_resp), + r => Err(ModifyUsbError::UnexpectedResponse(r)), + } +} + +fn usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> { + let port: u8 = args + .next() + .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| { + p.parse::<u8>() + .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e)) + })?; + let request = VmRequest::UsbCommand(UsbControlCommand::ListDevice { port }); + let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?; + match response { + VmResponse::UsbResponse(usb_resp) => Ok(usb_resp), + r => Err(ModifyUsbError::UnexpectedResponse(r)), + } +} + +fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> { + if args.len() < 3 { + print_help("crosvm usb", + "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list PORT] VM_SOCKET...", &[]); + return Err(()); + } + + // This unwrap will not panic because of the above length check. + let command = args.next().unwrap(); + let result = match command.as_ref() { + "attach" => usb_attach(args), + "detach" => usb_detach(args), + "list" => usb_list(args), + other => Err(ModifyUsbError::UnknownCommand(other.to_owned())), + }; + match result { + Ok(response) => { + println!("{}", response); + Ok(()) + } + Err(e) => { + println!("error {}", e); + Err(()) + } + } +} + fn print_usage() { print_help("crosvm", "[stop|run]", &[]); println!("Commands:"); println!(" stop - Stops crosvm instances via their control sockets."); println!(" run - Start a new crosvm instance."); println!(" create_qcow2 - Create a new qcow2 disk image file."); - println!(" disk - Manage attached virtual disk devices.") + println!(" disk - Manage attached virtual disk devices."); + println!(" usb - Manage attached virtual USB devices."); } fn crosvm_main() -> std::result::Result<(), ()> { @@ -966,6 +1159,7 @@ fn crosvm_main() -> std::result::Result<(), ()> { Some("balloon") => balloon_vms(args), Some("create_qcow2") => create_qcow2(args), Some("disk") => disk_cmd(args), + Some("usb") => modify_usb(args), Some(c) => { println!("invalid subcommand: {:?}", c); print_usage(); |