diff --git a/devices/src/legacy/i8042.rs b/devices/src/legacy/i8042.rs new file mode 100644 index 000000000..68a12faff --- /dev/null +++ b/devices/src/legacy/i8042.rs @@ -0,0 +1,42 @@ +// Copyright 2017 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. + +use vmm_sys_util::EventFd; + +use BusDevice; + +/// A i8042 PS/2 controller that emulates just enough to shutdown the machine. +pub struct I8042Device { + reset_evt: EventFd, +} + +impl I8042Device { + /// Constructs a i8042 device that will signal the given event when the guest requests it. + pub fn new(reset_evt: EventFd) -> I8042Device { + I8042Device { reset_evt } + } +} + +// i8042 device is located at I/O port 0x61. We partially implement two 8-bit +// registers: port 0x61 (I8042_PORT_B_REG, offset 0 from base of 0x61), and +// port 0x64 (I8042_COMMAND_REG, offset 3 from base of 0x61). +impl BusDevice for I8042Device { + fn read(&mut self, offset: u64, data: &mut [u8]) { + if data.len() == 1 && offset == 3 { + data[0] = 0x0; + } else if data.len() == 1 && offset == 0 { + // Like kvmtool, we return bit 5 set in I8042_PORT_B_REG to + // avoid hang in pit_calibrate_tsc() in Linux kernel. + data[0] = 0x20; + } + } + + fn write(&mut self, offset: u64, data: &[u8]) { + if data.len() == 1 && data[0] == 0xfe && offset == 3 { + if let Err(e) = self.reset_evt.write(1) { + println!("Error triggering i8042 reset event: {}", e); + } + } + } +} diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index bca23a0b5..7e0f571d1 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -6,5 +6,7 @@ // found in the THIRD-PARTY file. mod serial; +mod i8042; pub use self::serial::Serial; +pub use self::i8042::I8042Device; diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 49fdacebd..4b5ac236c 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -213,6 +213,9 @@ struct DeviceManager { // Serial port on 0x3f8 serial: Arc>, serial_evt: EventFd, + + // i8042 device for exit + i8042: Arc>, exit_evt: EventFd, } @@ -226,20 +229,31 @@ impl DeviceManager { ))); let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?; + let i8042 = Arc::new(Mutex::new(devices::legacy::I8042Device::new( + exit_evt.try_clone().map_err(Error::EventFd)?, + ))); Ok(DeviceManager { io_bus, serial, serial_evt, + i8042, exit_evt, }) } /// Register legacy devices. pub fn register_devices(&mut self) -> Result<()> { + // Insert serial device self.io_bus .insert(self.serial.clone(), 0x3f8, 0x8) .map_err(Error::BusError)?; + + // Insert i8042 device + self.io_bus + .insert(self.i8042.clone(), 0x61, 0x4) + .map_err(Error::BusError)?; + Ok(()) } }