diff --git a/devices/src/legacy/i8042.rs b/devices/src/legacy/i8042.rs index 6333087db..a97f2f11a 100644 --- a/devices/src/legacy/i8042.rs +++ b/devices/src/legacy/i8042.rs @@ -34,6 +34,7 @@ impl BusDevice for I8042Device { fn write(&mut self, _base: u64, offset: u64, data: &[u8]) { if data.len() == 1 && data[0] == 0xfe && offset == 3 { + debug!("i8042 reset signalled"); if let Err(e) = self.reset_evt.write(1) { error!("Error triggering i8042 reset event: {}", e); } diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index dd4037614..a2706ad68 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -15,7 +15,7 @@ pub mod config; pub mod vm; use self::config::VmConfig; -use self::vm::Vm; +use self::vm::{ExitBehaviour, Vm}; /// Errors associated with VM management #[derive(Debug)] @@ -55,11 +55,15 @@ impl Vmm { } pub fn boot_kernel(config: VmConfig) -> Result<()> { - let vmm = Vmm::new()?; - let mut vm = Vm::new(&vmm.kvm, config).map_err(Error::VmNew)?; + loop { + let vmm = Vmm::new()?; + let mut vm = Vm::new(&vmm.kvm, &config).map_err(Error::VmNew)?; - let entry = vm.load_kernel().map_err(Error::LoadKernel)?; - vm.start(entry).map_err(Error::VmStart)?; + let entry = vm.load_kernel().map_err(Error::LoadKernel)?; + if vm.start(entry).map_err(Error::VmStart)? == ExitBehaviour::Shutdown { + break; + } + } Ok(()) } diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index d67665523..208c3e94d 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -605,9 +605,12 @@ struct DeviceManager { serial: Option>>, console_input: Option>, - // i8042 device for exit + // i8042 device for i8042 reset i8042: Arc>, + + // Shutdown (exit) and reboot (reset) control exit_evt: EventFd, + reset_evt: EventFd, // IOAPIC ioapic: Option>>, @@ -679,6 +682,7 @@ impl DeviceManager { // Add a shutdown device (i8042) let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(DeviceManagerError::EventFd)?; + let reset_evt = EventFd::new(EFD_NONBLOCK).map_err(DeviceManagerError::EventFd)?; let i8042 = Arc::new(Mutex::new(devices::legacy::I8042Device::new( exit_evt.try_clone().map_err(DeviceManagerError::EventFd)?, ))); @@ -734,6 +738,7 @@ impl DeviceManager { console_input: console, i8042, exit_evt, + reset_evt, ioapic, pci, }) @@ -1298,6 +1303,7 @@ impl DeviceManager { #[derive(Debug, Clone, Copy, PartialEq)] enum EpollDispatch { Exit, + Reset, Stdin, } @@ -1313,7 +1319,7 @@ impl EpollContext { // Initial capacity needs to be large enough to hold: // * 1 exit event // * 1 stdin event - let mut dispatch_table = Vec::with_capacity(3); + let mut dispatch_table = Vec::with_capacity(4); dispatch_table.push(None); Ok(EpollContext { @@ -1359,6 +1365,12 @@ impl AsRawFd for EpollContext { } } +#[derive(PartialEq)] +pub enum ExitBehaviour { + Shutdown = 1, + Reset = 2, +} + pub struct Vm<'a> { fd: Arc, kernel: File, @@ -1366,14 +1378,14 @@ pub struct Vm<'a> { vcpus: Vec>, devices: DeviceManager, cpuid: CpuId, - config: VmConfig<'a>, + config: &'a VmConfig<'a>, epoll: EpollContext, on_tty: bool, creation_ts: std::time::Instant, } impl<'a> Vm<'a> { - pub fn new(kvm: &Kvm, config: VmConfig<'a>) -> Result { + pub fn new(kvm: &Kvm, config: &'a VmConfig<'a>) -> Result { let kernel = File::open(&config.kernel.path).map_err(Error::KernelFile)?; let fd = kvm.create_vm().map_err(Error::VmCreate)?; let fd = Arc::new(fd); @@ -1553,7 +1565,7 @@ impl<'a> Vm<'a> { let vm_info = VmInfo { memory: &guest_memory, vm_fd: &fd, - vm_cfg: &config, + vm_cfg: config, }; let device_manager = DeviceManager::new( @@ -1577,6 +1589,9 @@ impl<'a> Vm<'a> { epoll .add_event(&device_manager.exit_evt, EpollDispatch::Exit) .map_err(Error::EpollError)?; + epoll + .add_event(&device_manager.reset_evt, EpollDispatch::Reset) + .map_err(Error::EpollError)?; let vcpus = Vec::with_capacity(u8::from(&config.cpus) as usize); @@ -1662,7 +1677,7 @@ impl<'a> Vm<'a> { } } - pub fn control_loop(&mut self) -> Result<()> { + pub fn control_loop(&mut self) -> Result { // Let's start the STDIN polling thread. const EPOLL_EVENTS_LEN: usize = 100; @@ -1676,6 +1691,8 @@ impl<'a> Vm<'a> { .map_err(Error::SetTerminalRaw)?; } + let exit_behaviour; + 'outer: loop { let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) { Ok(res) => res, @@ -1702,6 +1719,14 @@ impl<'a> Vm<'a> { EpollDispatch::Exit => { // Consume the event. self.devices.exit_evt.read().map_err(Error::EventFd)?; + exit_behaviour = ExitBehaviour::Shutdown; + + break 'outer; + } + EpollDispatch::Reset => { + // Consume the event. + self.devices.reset_evt.read().map_err(Error::EventFd)?; + exit_behaviour = ExitBehaviour::Reset; break 'outer; } @@ -1749,7 +1774,7 @@ impl<'a> Vm<'a> { .map_err(Error::SetTerminalCanon)?; } - Ok(()) + Ok(exit_behaviour) } fn os_signal_handler(signals: Signals, console_input_clone: Arc) { @@ -1761,7 +1786,7 @@ impl<'a> Vm<'a> { } } - pub fn start(&mut self, entry_addr: GuestAddress) -> Result<()> { + pub fn start(&mut self, entry_addr: GuestAddress) -> Result { self.devices.register_devices()?; let vcpu_count = u8::from(&self.config.cpus); @@ -1824,9 +1849,7 @@ impl<'a> Vm<'a> { Err(e) => error!("Signal not found {}", e), } } - self.control_loop()?; - - Ok(()) + self.control_loop() } /// Gets an Arc to the guest memory owned by this VM.