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