vmm/devices/legacy/
i8042.rs

1// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3//
4// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
5// Use of this source code is governed by a BSD-style license that can be
6// found in the THIRD-PARTY file.
7
8use std::io;
9use std::num::Wrapping;
10use std::sync::{Arc, Barrier};
11
12use log::warn;
13use serde::Serialize;
14use vmm_sys_util::eventfd::EventFd;
15
16use crate::logger::{IncMetric, SharedIncMetric, error};
17use crate::vstate::bus::BusDevice;
18
19/// Errors thrown by the i8042 device.
20#[derive(Debug, thiserror::Error, displaydoc::Display)]
21pub enum I8042Error {
22    /// i8042 internal buffer full.
23    InternalBufferFull,
24    /// Keyboard interrupt disabled by guest driver.
25    KbdInterruptDisabled,
26    /// Could not trigger keyboard interrupt: {0}.
27    KbdInterruptFailure(io::Error),
28}
29
30/// Metrics specific to the i8042 device.
31#[derive(Debug, Serialize)]
32pub(super) struct I8042DeviceMetrics {
33    /// Errors triggered while using the i8042 device.
34    error_count: SharedIncMetric,
35    /// Number of superfluous read intents on this i8042 device.
36    missed_read_count: SharedIncMetric,
37    /// Number of superfluous write intents on this i8042 device.
38    missed_write_count: SharedIncMetric,
39    /// Bytes read by this device.
40    read_count: SharedIncMetric,
41    /// Number of resets done by this device.
42    reset_count: SharedIncMetric,
43    /// Bytes written by this device.
44    write_count: SharedIncMetric,
45}
46impl I8042DeviceMetrics {
47    /// Const default construction.
48    const fn new() -> Self {
49        Self {
50            error_count: SharedIncMetric::new(),
51            missed_read_count: SharedIncMetric::new(),
52            missed_write_count: SharedIncMetric::new(),
53            read_count: SharedIncMetric::new(),
54            reset_count: SharedIncMetric::new(),
55            write_count: SharedIncMetric::new(),
56        }
57    }
58}
59
60/// Stores aggregated metrics
61pub(super) static METRICS: I8042DeviceMetrics = I8042DeviceMetrics::new();
62
63/// Offset of the status port (port 0x64)
64const OFS_STATUS: u64 = 4;
65
66/// Offset of the data port (port 0x60)
67const OFS_DATA: u64 = 0;
68
69/// i8042 commands
70/// These values are written by the guest driver to port 0x64.
71const CMD_READ_CTR: u8 = 0x20; // Read control register
72const CMD_WRITE_CTR: u8 = 0x60; // Write control register
73const CMD_READ_OUTP: u8 = 0xD0; // Read output port
74const CMD_WRITE_OUTP: u8 = 0xD1; // Write output port
75const CMD_RESET_CPU: u8 = 0xFE; // Reset CPU
76
77/// i8042 status register bits
78const SB_OUT_DATA_AVAIL: u8 = 0x0001; // Data available at port 0x60
79const SB_I8042_CMD_DATA: u8 = 0x0008; // i8042 expecting command parameter at port 0x60
80const SB_KBD_ENABLED: u8 = 0x0010; // 1 = kbd enabled, 0 = kbd locked
81
82/// i8042 control register bits
83const CB_KBD_INT: u8 = 0x0001; // kbd interrupt enabled
84const CB_POST_OK: u8 = 0x0004; // POST ok (should always be 1)
85
86/// Key scan codes
87const KEY_CTRL: u16 = 0x0014;
88const KEY_ALT: u16 = 0x0011;
89const KEY_DEL: u16 = 0xE071;
90
91/// Internal i8042 buffer size, in bytes
92const BUF_SIZE: usize = 16;
93
94/// A i8042 PS/2 controller that emulates just enough to shutdown the machine.
95#[derive(Debug)]
96pub struct I8042Device {
97    /// CPU reset eventfd. We will set this event when the guest issues CMD_RESET_CPU.
98    reset_evt: EventFd,
99
100    /// Keyboard interrupt event (IRQ 1).
101    pub kbd_interrupt_evt: EventFd,
102
103    /// The i8042 status register.
104    status: u8,
105
106    /// The i8042 control register.
107    control: u8,
108
109    /// The i8042 output port.
110    outp: u8,
111
112    /// The last command sent to port 0x64.
113    cmd: u8,
114
115    /// The internal i8042 data buffer.
116    buf: [u8; BUF_SIZE],
117    bhead: Wrapping<usize>,
118    btail: Wrapping<usize>,
119}
120
121impl I8042Device {
122    /// Constructs an i8042 device that will signal the given event when the guest requests it.
123    pub fn new(reset_evt: EventFd) -> Result<I8042Device, std::io::Error> {
124        Ok(I8042Device {
125            reset_evt,
126            kbd_interrupt_evt: EventFd::new(libc::EFD_NONBLOCK)?,
127            control: CB_POST_OK | CB_KBD_INT,
128            cmd: 0,
129            outp: 0,
130            status: SB_KBD_ENABLED,
131            buf: [0; BUF_SIZE],
132            bhead: Wrapping(0),
133            btail: Wrapping(0),
134        })
135    }
136
137    /// Signal a ctrl-alt-del (reset) event.
138    #[inline]
139    pub fn trigger_ctrl_alt_del(&mut self) -> Result<(), I8042Error> {
140        // The CTRL+ALT+DEL sequence is 4 bytes in total (1 extended key + 2 normal keys).
141        // Fail if we don't have room for the whole sequence.
142        if BUF_SIZE - self.buf_len() < 4 {
143            return Err(I8042Error::InternalBufferFull);
144        }
145        self.trigger_key(KEY_CTRL)?;
146        self.trigger_key(KEY_ALT)?;
147        self.trigger_key(KEY_DEL)?;
148        Ok(())
149    }
150
151    fn trigger_kbd_interrupt(&self) -> Result<(), I8042Error> {
152        if (self.control & CB_KBD_INT) == 0 {
153            warn!("Failed to trigger i8042 kbd interrupt (disabled by guest OS)");
154            return Err(I8042Error::KbdInterruptDisabled);
155        }
156        self.kbd_interrupt_evt
157            .write(1)
158            .map_err(I8042Error::KbdInterruptFailure)
159    }
160
161    fn trigger_key(&mut self, key: u16) -> Result<(), I8042Error> {
162        if key & 0xff00 != 0 {
163            // Check if there is enough room in the buffer, before pushing an extended (2-byte) key.
164            if BUF_SIZE - self.buf_len() < 2 {
165                return Err(I8042Error::InternalBufferFull);
166            }
167            self.push_byte((key >> 8) as u8)?;
168        }
169        self.push_byte((key & 0xff) as u8)?;
170
171        match self.trigger_kbd_interrupt() {
172            Ok(_) | Err(I8042Error::KbdInterruptDisabled) => Ok(()),
173            Err(err) => Err(err),
174        }
175    }
176
177    #[inline]
178    fn push_byte(&mut self, byte: u8) -> Result<(), I8042Error> {
179        self.status |= SB_OUT_DATA_AVAIL;
180        if self.buf_len() == BUF_SIZE {
181            return Err(I8042Error::InternalBufferFull);
182        }
183        self.buf[self.btail.0 % BUF_SIZE] = byte;
184        self.btail += Wrapping(1usize);
185        Ok(())
186    }
187
188    #[inline]
189    fn pop_byte(&mut self) -> Option<u8> {
190        if self.buf_len() == 0 {
191            return None;
192        }
193        let res = self.buf[self.bhead.0 % BUF_SIZE];
194        self.bhead += Wrapping(1usize);
195        if self.buf_len() == 0 {
196            self.status &= !SB_OUT_DATA_AVAIL;
197        }
198        Some(res)
199    }
200
201    #[inline]
202    fn flush_buf(&mut self) {
203        self.bhead = Wrapping(0usize);
204        self.btail = Wrapping(0usize);
205        self.status &= !SB_OUT_DATA_AVAIL;
206    }
207
208    #[inline]
209    fn buf_len(&self) -> usize {
210        (self.btail - self.bhead).0
211    }
212}
213
214impl BusDevice for I8042Device {
215    fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
216        // All our ports are byte-wide. We don't know how to handle any wider data.
217        if data.len() != 1 {
218            METRICS.missed_read_count.inc();
219            return;
220        }
221
222        let mut read_ok = true;
223
224        match offset {
225            OFS_STATUS => data[0] = self.status,
226            OFS_DATA => {
227                // The guest wants to read a byte from port 0x60. For the 8042, that means the top
228                // byte in the internal buffer. If the buffer is empty, the guest will get a 0.
229                data[0] = self.pop_byte().unwrap_or(0);
230
231                // Check if we still have data in the internal buffer. If so, we need to trigger
232                // another interrupt, to let the guest know they need to issue another read from
233                // port 0x60.
234                if (self.status & SB_OUT_DATA_AVAIL) != 0
235                    && let Err(I8042Error::KbdInterruptFailure(err)) = self.trigger_kbd_interrupt()
236                {
237                    warn!("Failed to trigger i8042 kbd interrupt {:?}", err);
238                }
239            }
240            _ => read_ok = false,
241        }
242        if read_ok {
243            METRICS.read_count.add(data.len() as u64);
244        } else {
245            METRICS.missed_read_count.inc();
246        }
247    }
248
249    fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
250        // All our ports are byte-wide. We don't know how to handle any wider data.
251        if data.len() != 1 {
252            METRICS.missed_write_count.inc();
253            return None;
254        }
255
256        let mut write_ok = true;
257
258        match offset {
259            OFS_STATUS if data[0] == CMD_RESET_CPU => {
260                // The guest wants to assert the CPU reset line. We handle that by triggering
261                // our exit event fd. Meaning Firecracker will be exiting as soon as the VMM
262                // thread wakes up to handle this event.
263                if let Err(err) = self.reset_evt.write(1) {
264                    error!("Failed to trigger i8042 reset event: {:?}", err);
265                    METRICS.error_count.inc();
266                }
267                METRICS.reset_count.inc();
268            }
269            OFS_STATUS if data[0] == CMD_READ_CTR => {
270                // The guest wants to read the control register.
271                // Let's make sure only the control register will be available for reading from
272                // the data port, for the next inb(0x60).
273                self.flush_buf();
274                let control = self.control;
275                // Buffer is empty, push() will always succeed.
276                self.push_byte(control).unwrap();
277            }
278            OFS_STATUS if data[0] == CMD_WRITE_CTR => {
279                // The guest wants to write the control register. This is a two-step command:
280                // 1. port 0x64 < CMD_WRITE_CTR
281                // 2. port 0x60 < <control reg value>
282                // Make sure we'll be expecting the control reg value on port 0x60 for the next
283                // write.
284                self.flush_buf();
285                self.status |= SB_I8042_CMD_DATA;
286                self.cmd = data[0];
287            }
288            OFS_STATUS if data[0] == CMD_READ_OUTP => {
289                // The guest wants to read the output port (for lack of a better name - this is
290                // just another register on the 8042, that happens to also have its bits connected
291                // to some output pins of the 8042).
292                self.flush_buf();
293                let outp = self.outp;
294                // Buffer is empty, push() will always succeed.
295                self.push_byte(outp).unwrap();
296            }
297            OFS_STATUS if data[0] == CMD_WRITE_OUTP => {
298                // Similar to writing the control register, this is a two-step command.
299                // I.e. write CMD_WRITE_OUTP at port 0x64, then write the actual out port value
300                // to port 0x60.
301                self.status |= SB_I8042_CMD_DATA;
302                self.cmd = data[0];
303            }
304            OFS_DATA if (self.status & SB_I8042_CMD_DATA) != 0 => {
305                // The guest is writing to port 0x60. This byte can either be:
306                // 1. the payload byte of a CMD_WRITE_CTR or CMD_WRITE_OUTP command, in which case
307                //    the status reg bit SB_I8042_CMD_DATA will be set, or
308                // 2. a direct command sent to the keyboard
309                // This match arm handles the first option (when the SB_I8042_CMD_DATA bit is set).
310                match self.cmd {
311                    CMD_WRITE_CTR => self.control = data[0],
312                    CMD_WRITE_OUTP => self.outp = data[0],
313                    _ => (),
314                }
315                self.status &= !SB_I8042_CMD_DATA;
316            }
317            OFS_DATA => {
318                // The guest is sending a command straight to the keyboard (so this byte is not
319                // addressed to the 8042, but to the keyboard). Since we're emulating a pretty
320                // dumb keyboard, we can get away with blindly ack-in anything (byte 0xFA).
321                // Something along the lines of "Yeah, uhm-uhm, yeah, okay, honey, that's great."
322                self.flush_buf();
323                // Buffer is empty, push() will always succeed.
324                self.push_byte(0xFA).unwrap();
325                if let Err(I8042Error::KbdInterruptFailure(err)) = self.trigger_kbd_interrupt() {
326                    warn!("Failed to trigger i8042 kbd interrupt {:?}", err);
327                }
328            }
329            _ => {
330                write_ok = false;
331            }
332        }
333
334        if write_ok {
335            METRICS.write_count.inc();
336        } else {
337            METRICS.missed_write_count.inc();
338        }
339
340        None
341    }
342}
343
344#[cfg(test)]
345mod tests {
346
347    use super::*;
348
349    impl PartialEq for I8042Error {
350        fn eq(&self, other: &I8042Error) -> bool {
351            self.to_string() == other.to_string()
352        }
353    }
354
355    #[test]
356    fn test_i8042_read_write_and_event() {
357        let mut i8042 = I8042Device::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
358        let reset_evt = i8042.reset_evt.try_clone().unwrap();
359
360        // Check if reading in a 2-length array doesn't have side effects.
361        let mut data = [1, 2];
362        i8042.read(0x0, 0, &mut data);
363        assert_eq!(data, [1, 2]);
364        i8042.read(0x0, 1, &mut data);
365        assert_eq!(data, [1, 2]);
366
367        // Check if reset works.
368        // Write 1 to the reset event fd, so that read doesn't block in case the event fd
369        // counter doesn't change (for 0 it blocks).
370        reset_evt.write(1).unwrap();
371        let mut data = [CMD_RESET_CPU];
372        i8042.write(0x0, OFS_STATUS, &data);
373        assert_eq!(reset_evt.read().unwrap(), 2);
374
375        // Check if reading with offset 1 doesn't have side effects.
376        i8042.read(0x0, 1, &mut data);
377        assert_eq!(data[0], CMD_RESET_CPU);
378
379        // Check invalid `write`s.
380        let before = METRICS.missed_write_count.count();
381        // offset != 0.
382        i8042.write(0x0, 1, &data);
383        // data != CMD_RESET_CPU
384        data[0] = CMD_RESET_CPU + 1;
385        i8042.write(0x0, 1, &data);
386        // data.len() != 1
387        let data = [CMD_RESET_CPU; 2];
388        i8042.write(0x0, 1, &data);
389        assert_eq!(METRICS.missed_write_count.count(), before + 3);
390    }
391
392    #[test]
393    fn test_i8042_commands() {
394        let mut i8042 = I8042Device::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
395        let mut data = [1];
396
397        // Test reading/writing the control register.
398        data[0] = CMD_WRITE_CTR;
399        i8042.write(0x0, OFS_STATUS, &data);
400        assert_ne!(i8042.status & SB_I8042_CMD_DATA, 0);
401        data[0] = 0x52;
402        i8042.write(0x0, OFS_DATA, &data);
403        data[0] = CMD_READ_CTR;
404        i8042.write(0x0, OFS_STATUS, &data);
405        assert_ne!(i8042.status & SB_OUT_DATA_AVAIL, 0);
406        i8042.read(0x0, OFS_DATA, &mut data);
407        assert_eq!(data[0], 0x52);
408
409        // Test reading/writing the output port.
410        data[0] = CMD_WRITE_OUTP;
411        i8042.write(0x0, OFS_STATUS, &data);
412        assert_ne!(i8042.status & SB_I8042_CMD_DATA, 0);
413        data[0] = 0x52;
414        i8042.write(0x0, OFS_DATA, &data);
415        data[0] = CMD_READ_OUTP;
416        i8042.write(0x0, OFS_STATUS, &data);
417        assert_ne!(i8042.status & SB_OUT_DATA_AVAIL, 0);
418        i8042.read(0x0, OFS_DATA, &mut data);
419        assert_eq!(data[0], 0x52);
420
421        // Test kbd commands.
422        data[0] = 0x52;
423        i8042.write(0x0, OFS_DATA, &data);
424        assert_ne!(i8042.status & SB_OUT_DATA_AVAIL, 0);
425        i8042.read(0x0, OFS_DATA, &mut data);
426        assert_eq!(data[0], 0xFA);
427    }
428
429    #[test]
430    fn test_i8042_buffer() {
431        let mut i8042 = I8042Device::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
432
433        // Test push/pop.
434        i8042.push_byte(52).unwrap();
435        assert_ne!(i8042.status & SB_OUT_DATA_AVAIL, 0);
436        assert_eq!(i8042.pop_byte().unwrap(), 52);
437        assert_eq!(i8042.status & SB_OUT_DATA_AVAIL, 0);
438
439        // Test empty buffer pop.
440        assert!(i8042.pop_byte().is_none());
441
442        // Test buffer full.
443        for i in 0..BUF_SIZE {
444            i8042.push_byte(i.try_into().unwrap()).unwrap();
445            assert_eq!(i8042.buf_len(), i + 1);
446        }
447        assert_eq!(
448            i8042.push_byte(0).unwrap_err(),
449            I8042Error::InternalBufferFull
450        );
451    }
452
453    #[test]
454    fn test_i8042_kbd() {
455        let mut i8042 = I8042Device::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
456
457        fn expect_key(i8042: &mut I8042Device, key: u16) {
458            let mut data = [1];
459
460            // The interrupt line should be on.
461            i8042.trigger_kbd_interrupt().unwrap();
462            assert!(i8042.kbd_interrupt_evt.read().unwrap() > 1);
463
464            // The "data available" flag should be on.
465            i8042.read(0x0, OFS_STATUS, &mut data);
466
467            let mut key_byte: u8;
468            if key & 0xFF00 != 0 {
469                // For extended keys, we should be able to read the MSB first.
470                key_byte = ((key & 0xFF00) >> 8) as u8;
471                i8042.read(0x0, OFS_DATA, &mut data);
472                assert_eq!(data[0], key_byte);
473
474                // And then do the same for the LSB.
475
476                // The interrupt line should be on.
477                i8042.trigger_kbd_interrupt().unwrap();
478                assert!(i8042.kbd_interrupt_evt.read().unwrap() > 1);
479                // The "data available" flag should be on.
480                i8042.read(0x0, OFS_STATUS, &mut data);
481            }
482            key_byte = (key & 0xFF) as u8;
483            i8042.read(0x0, OFS_DATA, &mut data);
484            assert_eq!(data[0], key_byte);
485        }
486
487        // Test key trigger.
488        i8042.trigger_key(KEY_CTRL).unwrap();
489        expect_key(&mut i8042, KEY_CTRL);
490
491        // Test extended key trigger.
492        i8042.trigger_key(KEY_DEL).unwrap();
493        expect_key(&mut i8042, KEY_DEL);
494
495        // Test CTRL+ALT+DEL trigger.
496        i8042.trigger_ctrl_alt_del().unwrap();
497        expect_key(&mut i8042, KEY_CTRL);
498        expect_key(&mut i8042, KEY_ALT);
499        expect_key(&mut i8042, KEY_DEL);
500
501        // Almost fill up the buffer, so we can test trigger failures.
502        for _i in 0..BUF_SIZE - 1 {
503            i8042.push_byte(1).unwrap();
504        }
505
506        // Test extended key trigger failure.
507        assert_eq!(i8042.buf_len(), BUF_SIZE - 1);
508        assert_eq!(
509            i8042.trigger_key(KEY_DEL).unwrap_err(),
510            I8042Error::InternalBufferFull
511        );
512
513        // Test ctrl+alt+del trigger failure.
514        i8042.pop_byte().unwrap();
515        i8042.pop_byte().unwrap();
516        assert_eq!(i8042.buf_len(), BUF_SIZE - 3);
517        assert_eq!(
518            i8042.trigger_ctrl_alt_del().unwrap_err(),
519            I8042Error::InternalBufferFull
520        );
521
522        // Test kbd interrupt disable.
523        let mut data = [1];
524        data[0] = CMD_WRITE_CTR;
525        i8042.write(0x0, OFS_STATUS, &data);
526        data[0] = i8042.control & !CB_KBD_INT;
527        i8042.write(0x0, OFS_DATA, &data);
528        i8042.trigger_key(KEY_CTRL).unwrap();
529        assert_eq!(
530            i8042.trigger_kbd_interrupt().unwrap_err(),
531            I8042Error::KbdInterruptDisabled
532        )
533    }
534}