1 // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE-BSD-3-Clause file. 4 // 5 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause 6 7 use std::sync::{ 8 atomic::{AtomicBool, Ordering}, 9 Arc, Barrier, 10 }; 11 use std::thread; 12 use vm_device::BusDevice; 13 use vmm_sys_util::eventfd::EventFd; 14 15 /// A i8042 PS/2 controller that emulates just enough to shutdown the machine. 16 pub struct I8042Device { 17 reset_evt: EventFd, 18 vcpus_kill_signalled: Arc<AtomicBool>, 19 } 20 21 impl I8042Device { 22 /// Constructs a i8042 device that will signal the given event when the guest requests it. 23 pub fn new(reset_evt: EventFd, vcpus_kill_signalled: Arc<AtomicBool>) -> I8042Device { 24 I8042Device { 25 reset_evt, 26 vcpus_kill_signalled, 27 } 28 } 29 } 30 31 // i8042 device is located at I/O port 0x61. We partially implement two 8-bit 32 // registers: port 0x61 (I8042_PORT_B_REG, offset 0 from base of 0x61), and 33 // port 0x64 (I8042_COMMAND_REG, offset 3 from base of 0x61). 34 impl BusDevice for I8042Device { 35 fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) { 36 if data.len() == 1 && offset == 3 { 37 data[0] = 0x0; 38 } else if data.len() == 1 && offset == 0 { 39 // Like kvmtool, we return bit 5 set in I8042_PORT_B_REG to 40 // avoid hang in pit_calibrate_tsc() in Linux kernel. 41 data[0] = 0x20; 42 } 43 } 44 45 fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> { 46 if data.len() == 1 && data[0] == 0xfe && offset == 3 { 47 info!("i8042 reset signalled"); 48 if let Err(e) = self.reset_evt.write(1) { 49 error!("Error triggering i8042 reset event: {}", e); 50 } 51 // Spin until we are sure the reset_evt has been handled and that when 52 // we return from the KVM_RUN we will exit rather than re-enter the guest. 53 while !self.vcpus_kill_signalled.load(Ordering::SeqCst) { 54 // This is more effective than thread::yield_now() at 55 // avoiding a priority inversion with the VMM thread 56 thread::sleep(std::time::Duration::from_millis(1)); 57 } 58 } 59 60 None 61 } 62 } 63