diff options
-rw-r--r-- | src/device_manager.rs | 81 | ||||
-rw-r--r-- | src/linux.rs | 151 |
2 files changed, 114 insertions, 118 deletions
diff --git a/src/device_manager.rs b/src/device_manager.rs index 284f3e1..4c02da0 100644 --- a/src/device_manager.rs +++ b/src/device_manager.rs @@ -5,38 +5,28 @@ //! Manages jailing and connecting virtio devices to the system bus. use std::fmt; -use std::io; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; use libc::STDERR_FILENO; use io_jail::Minijail; -use kvm::IoeventAddress; +use kvm::{Vm, IoeventAddress}; use sys_util::{GuestMemory, syslog}; use sys_util; use devices; use kernel_cmdline; -use vm_control::VmRequest; /// Errors for device manager. #[derive(Debug)] pub enum Error { /// Could not create the mmio device to wrap a VirtioDevice. CreateMmioDevice(sys_util::Error), - /// Failed to clone a queue's ioeventfd. - CloneIoeventFd(sys_util::Error), - /// Failed to clone the mmio irqfd. - CloneIrqFd(sys_util::Error), - /// There was an error creating a sync EventFd. - CreateSync(sys_util::Error), - /// There was an error writing the uid_map. - WriteUidMap(io::Error), - /// There was an error writing the gid_map. - WriteGidMap(io::Error), - /// There was an error synchronizing the child process. - Sync(sys_util::Error), + /// Failed to register ioevent with VM. + RegisterIoevent(sys_util::Error), + /// Failed to register irq eventfd with VM. + RegisterIrqfd(sys_util::Error), /// Failed to initialize proxy device for jailed device. ProxyDeviceCreation(devices::ProxyError), /// Appending to kernel command line failed. @@ -49,19 +39,17 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { &Error::CreateMmioDevice(ref e) => write!(f, "failed to create mmio device: {:?}", e), - &Error::CloneIoeventFd(ref e) => write!(f, "failed to clone ioeventfd: {:?}", e), - &Error::CloneIrqFd(ref e) => write!(f, "failed to clone irqfd: {:?}", e), - &Error::CreateSync(ref e) => write!(f, "failed to create sync eventfd: {:?}", e), - &Error::WriteUidMap(ref e) => write!(f, "failed to write uid map: {}", e), - &Error::WriteGidMap(ref e) => write!(f, "failed to write gid map: {}", e), - &Error::Sync(ref e) => write!(f, "failed to sync proxy device start: {:?}", e), + &Error::RegisterIoevent(ref e) => { + write!(f, "failed to register ioevent to VM: {:?}", e) + } + &Error::RegisterIrqfd(ref e) => { + write!(f, "failed to register irq eventfd to VM: {:?}", e) + } &Error::ProxyDeviceCreation(ref e) => write!(f, "failed to create proxy device: {}", e), &Error::Cmdline(ref e) => { write!(f, "unable to add device to kernel command line: {}", e) } - &Error::IrqsExhausted => { - write!(f, "no more IRQs are available") - } + &Error::IrqsExhausted => write!(f, "no more IRQs are available"), } } } @@ -71,25 +59,30 @@ type Result<T> = ::std::result::Result<T, Error>; const MAX_IRQ: u32 = 15; /// Manages the complexities of adding a device. -pub struct DeviceManager { +pub struct DeviceManager<'a> { pub bus: devices::Bus, - pub vm_requests: Vec<VmRequest>, + vm: &'a mut Vm, guest_mem: GuestMemory, mmio_len: u64, mmio_base: u64, irq: u32, } -impl DeviceManager { +impl<'a> DeviceManager<'a> { /// Create a new DeviceManager. - pub fn new(guest_mem: GuestMemory, mmio_len: u64, mmio_base: u64, irq_base: u32) -> DeviceManager { + pub fn new(vm: &mut Vm, + guest_mem: GuestMemory, + mmio_len: u64, + mmio_base: u64, + irq_base: u32) + -> DeviceManager { DeviceManager { - guest_mem: guest_mem, - vm_requests: Vec::new(), - mmio_len: mmio_len, - mmio_base: mmio_base, - irq: irq_base, bus: devices::Bus::new(), + vm, + guest_mem, + mmio_len, + mmio_base, + irq: irq_base, } } @@ -113,25 +106,22 @@ impl DeviceManager { for (i, queue_evt) in mmio_device.queue_evts().iter().enumerate() { let io_addr = IoeventAddress::Mmio(self.mmio_base + devices::virtio::NOITFY_REG_OFFSET as u64); - self.vm_requests.push(VmRequest::RegisterIoevent(queue_evt - .try_clone() - .map_err(Error::CloneIoeventFd)?, - io_addr, - i as u32)); + self.vm + .register_ioevent(&queue_evt, io_addr, i as u32) + .map_err(Error::RegisterIoevent)?; keep_fds.push(queue_evt.as_raw_fd()); } if let Some(interrupt_evt) = mmio_device.interrupt_evt() { - self.vm_requests.push(VmRequest::RegisterIrqfd(interrupt_evt - .try_clone() - .map_err(Error::CloneIrqFd)?, - self.irq)); + self.vm + .register_irqfd(&interrupt_evt, self.irq) + .map_err(Error::RegisterIrqfd)?; keep_fds.push(interrupt_evt.as_raw_fd()); } if let Some(jail) = jail { let proxy_dev = devices::ProxyDevice::new(mmio_device, &jail, keep_fds) - .map_err(|e| Error::ProxyDeviceCreation(e))?; + .map_err(Error::ProxyDeviceCreation)?; self.bus .insert(Arc::new(Mutex::new(proxy_dev)), @@ -163,6 +153,7 @@ mod tests { use std::sync::atomic::AtomicUsize; use std::os::unix::io::RawFd; use sys_util::{EventFd, GuestAddress, GuestMemory}; + use kvm::*; use device_manager; use kernel_cmdline; use devices; @@ -199,13 +190,15 @@ mod tests { } #[test] + #[ignore] // no access to /dev/kvm fn register_device() { let start_addr1 = GuestAddress(0x0); let start_addr2 = GuestAddress(0x1000); let guest_mem = GuestMemory::new(&vec![(start_addr1, 0x1000), (start_addr2, 0x1000)]) .unwrap(); + let mut vm = Vm::new(&Kvm::new().unwrap(), guest_mem.clone()).unwrap(); let mut device_manager = - device_manager::DeviceManager::new(guest_mem, 0x1000, 0xd0000000, 5); + device_manager::DeviceManager::new(&mut vm, guest_mem, 0x1000, 0xd0000000, 5); let mut cmdline = kernel_cmdline::Cmdline::new(4096); let dummy_box = Box::new(DummyDevice { dummy: 0 }); diff --git a/src/linux.rs b/src/linux.rs index c5c7001..03e2952 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -25,7 +25,7 @@ use kernel_loader; use kvm::*; use sys_util::*; use sys_util; -use vm_control::{VmResponse, VmRequest}; +use vm_control::VmRequest; use Config; @@ -181,40 +181,15 @@ fn create_base_minijail(root: &Path, seccomp_policy: &Path) -> Result<Minijail> Ok(j) } -pub fn run_config(cfg: Config) -> Result<()> { +fn setup_mmio_bus(vm: &mut Vm, + guest_mem: &GuestMemory, + cfg: Config, + cmdline: &mut kernel_cmdline::Cmdline, + control_sockets: &mut Vec<UnlinkUnixDatagram>, + balloon_device_socket: UnixDatagram) + -> Result<devices::Bus> { static DEFAULT_PIVOT_ROOT: &'static str = "/var/empty"; - - if cfg.multiprocess { - // Printing something to the syslog before entering minijail so that libc's syslogger has a - // chance to open files necessary for its operation, like `/etc/localtime`. After jailing, - // access to those files will not be possible. - info!("crosvm entering multiprocess mode"); - } - - let kernel_image = File::open(cfg.kernel_path.as_path()) - .map_err(|e| Error::OpenKernel(cfg.kernel_path.clone(), e))?; - - let mut control_sockets = Vec::new(); - if let Some(ref path) = cfg.socket_path { - let path = Path::new(path); - let control_socket = UnixDatagram::bind(path).map_err(|e| Error::Socket(e))?; - control_sockets.push(UnlinkUnixDatagram(control_socket)); - } - - let mem_size = cfg.memory.unwrap_or(256) << 20; - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] - let arch_mem_regions = vec![(GuestAddress(0), mem_size)]; - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - let arch_mem_regions = x86_64::arch_memory_regions(mem_size); - let guest_mem = - GuestMemory::new(&arch_mem_regions).expect("new mmap failed"); - - let mut cmdline = kernel_cmdline::Cmdline::new(CMDLINE_MAX_SIZE); - cmdline - .insert_str("console=ttyS0 noacpi reboot=k panic=1 pci=off") - .unwrap(); - - let mut device_manager = DeviceManager::new(guest_mem.clone(), 0x1000, 0xd0000000, 5); + let mut device_manager = DeviceManager::new(vm, guest_mem.clone(), 0x1000, 0xd0000000, 5); // An empty directory for jailed device's pivot root. let empty_root_path = Path::new(DEFAULT_PIVOT_ROOT); @@ -239,8 +214,9 @@ pub fn run_config(cfg: Config) -> Result<()> { None }; - device_manager.register_mmio(block_box, jail, &mut cmdline) - .map_err(Error::RegisterBlock)?; + device_manager + .register_mmio(block_box, jail, cmdline) + .map_err(Error::RegisterBlock)?; } let rng_box = Box::new(devices::virtio::Rng::new().map_err(Error::RngDeviceNew)?); @@ -250,10 +226,10 @@ pub fn run_config(cfg: Config) -> Result<()> { } else { None }; - device_manager.register_mmio(rng_box, rng_jail, &mut cmdline) + device_manager + .register_mmio(rng_box, rng_jail, cmdline) .map_err(Error::RegisterRng)?; - let (balloon_host_socket, balloon_device_socket) = UnixDatagram::pair().map_err(Error::Socket)?; let balloon_box = Box::new(devices::virtio::Balloon::new(balloon_device_socket) .map_err(Error::BalloonDeviceNew)?); let balloon_jail = if cfg.multiprocess { @@ -262,7 +238,7 @@ pub fn run_config(cfg: Config) -> Result<()> { } else { None }; - device_manager.register_mmio(balloon_box, balloon_jail, &mut cmdline) + device_manager.register_mmio(balloon_box, balloon_jail, cmdline) .map_err(Error::RegisterBalloon)?; // We checked above that if the IP is defined, then the netmask is, too. @@ -289,7 +265,9 @@ pub fn run_config(cfg: Config) -> Result<()> { None }; - device_manager.register_mmio(net_box, jail, &mut cmdline).map_err(Error::RegisterNet)?; + device_manager + .register_mmio(net_box, jail, cmdline) + .map_err(Error::RegisterNet)?; } } @@ -356,7 +334,7 @@ pub fn run_config(cfg: Config) -> Result<()> { None }; device_manager - .register_mmio(wl_box, jail, &mut cmdline) + .register_mmio(wl_box, jail, cmdline) .map_err(Error::RegisterWayland)?; } @@ -372,7 +350,9 @@ pub fn run_config(cfg: Config) -> Result<()> { None }; - device_manager.register_mmio(vsock_box, jail, &mut cmdline).map_err(Error::RegisterVsock)?; + device_manager + .register_mmio(vsock_box, jail, cmdline) + .map_err(Error::RegisterVsock)?; } if !cfg.params.is_empty() { @@ -380,52 +360,75 @@ pub fn run_config(cfg: Config) -> Result<()> { .insert_str(cfg.params) .map_err(|e| Error::Cmdline(e))?; } + Ok(device_manager.bus) +} + +pub fn run_config(cfg: Config) -> Result<()> { + if cfg.multiprocess { + // Printing something to the syslog before entering minijail so that libc's syslogger has a + // chance to open files necessary for its operation, like `/etc/localtime`. After jailing, + // access to those files will not be possible. + info!("crosvm entering multiprocess mode"); + } + + let kernel_image = File::open(cfg.kernel_path.as_path()) + .map_err(|e| Error::OpenKernel(cfg.kernel_path.clone(), e))?; + + let mut control_sockets = Vec::new(); + if let Some(ref path) = cfg.socket_path { + let path = Path::new(path); + let control_socket = UnixDatagram::bind(path).map_err(|e| Error::Socket(e))?; + control_sockets.push(UnlinkUnixDatagram(control_socket)); + } - run_kvm(device_manager.vm_requests, + let mem_size = cfg.memory.unwrap_or(256) << 20; + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + let arch_mem_regions = vec![(GuestAddress(0), mem_size)]; + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + let arch_mem_regions = x86_64::arch_memory_regions(mem_size); + let guest_mem = GuestMemory::new(&arch_mem_regions).expect("new mmap failed"); + + let kvm = Kvm::new().map_err(Error::Kvm)?; + let mut vm = Vm::new(&kvm, guest_mem.clone()).map_err(Error::Vm)?; + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + let tss_addr = GuestAddress(0xfffbd000); + vm.set_tss_addr(tss_addr).expect("set tss addr failed"); + vm.create_pit().expect("create pit failed"); + } + vm.create_irq_chip().expect("create irq chip failed"); + + let mut cmdline = kernel_cmdline::Cmdline::new(CMDLINE_MAX_SIZE); + cmdline + .insert_str("console=ttyS0 noacpi reboot=k panic=1 pci=off") + .unwrap(); + + let vcpu_count = cfg.vcpu_count.unwrap_or(1); + let (balloon_host_socket, balloon_device_socket) = UnixDatagram::pair().map_err(Error::Socket)?; + let mmio_bus = setup_mmio_bus(&mut vm, &guest_mem, cfg, &mut cmdline, &mut control_sockets, balloon_device_socket)?; + + run_kvm(kvm, + vm, kernel_image, &CString::new(cmdline).unwrap(), - cfg.vcpu_count.unwrap_or(1), - guest_mem, - &device_manager.bus, + vcpu_count, + mmio_bus, control_sockets, balloon_host_socket) } -fn run_kvm(requests: Vec<VmRequest>, +fn run_kvm(kvm: Kvm, + vm: Vm, mut kernel_image: File, cmdline: &CStr, vcpu_count: u32, - guest_mem: GuestMemory, - mmio_bus: &devices::Bus, + mmio_bus: devices::Bus, control_sockets: Vec<UnlinkUnixDatagram>, balloon_host_socket: UnixDatagram) -> Result<()> { - let kvm = Kvm::new().map_err(Error::Kvm)?; let kernel_start_addr = GuestAddress(KERNEL_START_OFFSET); let cmdline_addr = GuestAddress(CMDLINE_OFFSET); - let mut vm = Vm::new(&kvm, guest_mem).map_err(Error::Vm)?; - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - { - let tss_addr = GuestAddress(0xfffbd000); - vm.set_tss_addr(tss_addr).expect("set tss addr failed"); - vm.create_pit().expect("create pit failed"); - } - vm.create_irq_chip().expect("create irq chip failed"); - - let mut next_dev_pfn = BASE_DEV_MEMORY_PFN; - for request in requests { - let mut running = false; - if let VmResponse::Err(e) = request.execute(&mut vm, &mut next_dev_pfn, - &mut running, &balloon_host_socket) { - return Err(Error::Vm(e)); - } - if !running { - info!("configuration requested exit"); - return Ok(()); - } - } - kernel_loader::load_kernel(vm.get_memory(), kernel_start_addr, &mut kernel_image)?; kernel_loader::load_cmdline(vm.get_memory(), cmdline_addr, cmdline)?; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -571,7 +574,6 @@ fn run_kvm(requests: Vec<VmRequest>, run_control(vm, control_sockets, - next_dev_pfn, stdio_serial, exit_evt, sigchld_fd, @@ -582,7 +584,6 @@ fn run_kvm(requests: Vec<VmRequest>, fn run_control(mut vm: Vm, control_sockets: Vec<UnlinkUnixDatagram>, - mut next_dev_pfn: u64, stdio_serial: Arc<Mutex<devices::Serial>>, exit_evt: EventFd, sigchld_fd: SignalFd, @@ -597,6 +598,8 @@ fn run_control(mut vm: Vm, const CHILD_SIGNAL: u32 = 2; const VM_BASE: u32 = 3; + let mut next_dev_pfn = BASE_DEV_MEMORY_PFN; + let stdin_handle = stdin(); let stdin_lock = stdin_handle.lock(); stdin_lock |