chipset/i8042/
mod.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Intel 8042 controller for PS/2 keyboard and mouse input.
5
6#![warn(missing_docs)]
7
8mod ps2keyboard;
9mod ps2mouse;
10pub mod resolver;
11mod spec;
12
13use self::ps2keyboard::Ps2Keyboard;
14use self::ps2mouse::Ps2Mouse;
15use chipset_device::ChipsetDevice;
16use chipset_device::io::IoError;
17use chipset_device::io::IoResult;
18use chipset_device::pio::PortIoIntercept;
19use chipset_device::poll_device::PollDevice;
20use input_core::InputSource;
21use input_core::KeyboardData;
22use inspect::Inspect;
23use inspect::InspectMut;
24use open_enum::open_enum;
25use spec::CommandFlag;
26use spec::ControllerCommand;
27use spec::KeyboardStatus;
28use spec::OutputPort;
29use std::task::Context;
30use std::task::Waker;
31use vmcore::device_state::ChangeDeviceState;
32use vmcore::line_interrupt::LineInterrupt;
33
34/// An Intel 8042 keyboard/mouse controller.
35#[derive(InspectMut)]
36pub struct I8042Device {
37    // Runtime glue
38    #[inspect(skip)]
39    trigger_reset: Box<dyn Fn() + Send + Sync>,
40    keyboard_interrupt: LineInterrupt,
41    mouse_interrupt: LineInterrupt,
42
43    // Sub-emulators
44    keyboard: Ps2Keyboard,
45    mouse: Ps2Mouse,
46
47    // Runtime book-keeping
48    #[inspect(skip)]
49    waker: Option<Waker>,
50
51    // Volatile state
52    state: I8042State,
53}
54
55#[derive(Inspect, Clone)]
56struct I8042State {
57    command_flag: CommandFlag,
58    #[inspect(flatten)]
59    data_port_target: DataPortTarget,
60    output_buffer: u8,
61    output_buffer_state: OutputBufferState,
62    a20_gate: bool,
63    memory: [u8; 32],
64}
65
66#[derive(Inspect, Copy, Clone, PartialEq, Eq)]
67enum OutputBufferState {
68    Empty,
69    Controller,
70    Keyboard,
71    Mouse,
72}
73
74#[derive(Copy, Clone, Inspect)]
75#[inspect(tag = "data_port_target")]
76enum DataPortTarget {
77    Keyboard,
78    Mouse,
79    Controller(#[inspect(rename = "target_command")] ControllerCommand),
80}
81
82impl I8042State {
83    fn new() -> Self {
84        Self {
85            command_flag: CommandFlag::new()
86                .with_allow_keyboard_interrupts(true)
87                .with_allow_mouse_interrupts(true)
88                .with_keyboard_self_test(true)
89                .with_enable_scan_code(true),
90            memory: [0; 32],
91            data_port_target: DataPortTarget::Keyboard,
92            output_buffer: 0,
93            output_buffer_state: OutputBufferState::Empty,
94            a20_gate: true,
95        }
96    }
97}
98
99open_enum! {
100     enum ControllerPort: u16 {
101        DATA = 0x60,
102        COMMAND = 0x64,
103    }
104}
105
106impl I8042Device {
107    /// Returns a new controller with an attached PS/2 keyboard.
108    ///
109    /// Calls `reset` to reset the VM on guest request.
110    pub async fn new(
111        reset: Box<dyn Fn() + Send + Sync>,
112        keyboard_interrupt: LineInterrupt,
113        mouse_interrupt: LineInterrupt,
114        mut keyboard_input: Box<dyn InputSource<KeyboardData>>,
115    ) -> Self {
116        // Activate the input immediately.
117        keyboard_input.set_active(true).await;
118        I8042Device {
119            trigger_reset: reset,
120            keyboard_interrupt,
121            mouse_interrupt,
122            state: I8042State::new(),
123            keyboard: Ps2Keyboard::new(keyboard_input),
124            mouse: Ps2Mouse::new(),
125            waker: None,
126        }
127    }
128}
129
130impl ChangeDeviceState for I8042Device {
131    fn start(&mut self) {}
132
133    async fn stop(&mut self) {}
134
135    async fn reset(&mut self) {
136        let Self {
137            trigger_reset: _,
138            keyboard_interrupt: _,
139            mouse_interrupt: _,
140            keyboard,
141            mouse,
142            waker: _,
143            state,
144        } = self;
145
146        *state = I8042State::new();
147        keyboard.reset();
148        mouse.reset();
149
150        self.sync_interrupts();
151    }
152}
153
154impl ChipsetDevice for I8042Device {
155    fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
156        Some(self)
157    }
158
159    fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
160        Some(self)
161    }
162}
163
164impl PollDevice for I8042Device {
165    fn poll_device(&mut self, cx: &mut Context<'_>) {
166        self.keyboard.poll(cx);
167        self.load_device_output();
168        self.waker = Some(cx.waker().clone());
169    }
170}
171
172impl PortIoIntercept for I8042Device {
173    fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
174        if data.len() != 1 {
175            return IoResult::Err(IoError::InvalidAccessSize);
176        }
177        data[0] = match ControllerPort(io_port) {
178            ControllerPort::DATA => {
179                // Read a piece of data from the output buffer
180                self.read_output_byte()
181            }
182            ControllerPort::COMMAND => {
183                // Read the current keyboard status.
184                let data = KeyboardStatus::new()
185                    .with_output_buffer_full(
186                        self.state.output_buffer_state != OutputBufferState::Empty,
187                    )
188                    .with_input_buffer_full(false)
189                    .with_keyboard_self_test(true)
190                    .with_input_buffer_for_controller(matches!(
191                        self.state.data_port_target,
192                        DataPortTarget::Controller(_)
193                    ))
194                    .with_keyboard_unlocked(true)
195                    .with_output_buffer_for_mouse(
196                        self.state.output_buffer_state == OutputBufferState::Mouse,
197                    );
198
199                data.into()
200            }
201            _ => return IoResult::Err(IoError::InvalidRegister),
202        };
203        // Populate the next output byte if appropriate.
204        self.check_devices_for_output();
205        IoResult::Ok
206    }
207
208    fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
209        let &[data] = data else {
210            return IoResult::Err(IoError::InvalidAccessSize);
211        };
212        match ControllerPort(io_port) {
213            ControllerPort::DATA => {
214                match std::mem::replace(&mut self.state.data_port_target, DataPortTarget::Keyboard)
215                {
216                    DataPortTarget::Keyboard => {
217                        self.state.command_flag.set_disable_keyboard(false);
218                        self.keyboard.input(data);
219                    }
220                    DataPortTarget::Mouse => {
221                        self.state.command_flag.set_disable_mouse(false);
222                        self.mouse.input(data);
223                    }
224                    DataPortTarget::Controller(command) => {
225                        self.handle_command(command, Some(data));
226                    }
227                }
228
229                // Check to see if the keyboard or mouse
230                // have more input for the controller.
231                self.check_devices_for_output();
232            }
233            ControllerPort::COMMAND => {
234                // Next data port write defaults to the keyboard.
235                self.state.data_port_target = DataPortTarget::Keyboard;
236                let command = ControllerCommand(data);
237                if self.handle_command(command, None).is_none() {
238                    self.state.data_port_target = DataPortTarget::Controller(command);
239                }
240
241                // Check to see if the keyboard or mouse
242                // have more input for the controller.
243                self.check_devices_for_output();
244            }
245            _ => return IoResult::Err(IoError::InvalidRegister),
246        }
247        IoResult::Ok
248    }
249
250    fn get_static_regions(&mut self) -> &[(&str, std::ops::RangeInclusive<u16>)] {
251        &[
252            ("data", ControllerPort::DATA.0..=ControllerPort::DATA.0),
253            (
254                "command",
255                ControllerPort::COMMAND.0..=ControllerPort::COMMAND.0,
256            ),
257        ]
258    }
259}
260
261impl I8042Device {
262    fn sync_interrupts(&mut self) {
263        self.keyboard_interrupt.set_level(
264            self.state.command_flag.allow_keyboard_interrupts()
265                && self.state.output_buffer_state == OutputBufferState::Keyboard,
266        );
267        self.mouse_interrupt.set_level(
268            self.state.command_flag.allow_mouse_interrupts()
269                && self.state.output_buffer_state == OutputBufferState::Mouse,
270        );
271    }
272
273    fn write_output_byte(&mut self, state: OutputBufferState, data: u8) {
274        self.state.output_buffer = data;
275        self.state.output_buffer_state = state;
276        self.sync_interrupts();
277    }
278
279    fn read_output_byte(&mut self) -> u8 {
280        self.state.output_buffer_state = OutputBufferState::Empty;
281        self.sync_interrupts();
282        self.state.output_buffer
283    }
284
285    /// Returns `None` if the command needs to wait for more data.
286    fn handle_command(
287        &mut self,
288        command: ControllerCommand,
289        command_data: Option<u8>,
290    ) -> Option<()> {
291        tracing::debug!(?command, command_data, "8042 command");
292        match command {
293            ControllerCommand::READ_COMMAND_BYTE => {
294                self.write_output_byte(
295                    OutputBufferState::Controller,
296                    self.state.command_flag.into(),
297                );
298            }
299            ControllerCommand::WRITE_COMMAND_BYTE => {
300                self.state.command_flag = command_data?.into();
301            }
302            ControllerCommand::DISABLE_AUX_INTERFACE => {
303                self.state.command_flag.set_disable_mouse(true);
304            }
305            ControllerCommand::ENABLE_AUX_INTERFACE => {
306                self.state.command_flag.set_disable_mouse(false);
307            }
308            ControllerCommand::CHECK_AUX_INTERFACE => {
309                self.write_output_byte(OutputBufferState::Controller, 0);
310            }
311            ControllerCommand::SELF_TEST => {
312                self.write_output_byte(OutputBufferState::Controller, 0x55);
313            }
314            ControllerCommand::CHECK_INTERFACE => {
315                self.write_output_byte(OutputBufferState::Controller, 0);
316            }
317            ControllerCommand::DISABLE_KEYBOARD => {
318                self.state.command_flag.set_disable_keyboard(true);
319            }
320            ControllerCommand::ENABLE_KEYBOARD => {
321                self.state.command_flag.set_disable_keyboard(false);
322            }
323            ControllerCommand::READ_INPUT_PORT => {
324                // Specify that the keyboard is not locked
325                self.write_output_byte(OutputBufferState::Controller, 0x80);
326            }
327            ControllerCommand::READ_OUT_INPUT_PORT_LO
328            | ControllerCommand::READ_OUT_INPUT_PORT_HI => {
329                //
330                // Over multiple releases of Hyper-V these have never
331                // been implemented and are clearly not needed.
332                // Silently ignore these two commands.
333                //
334            }
335            ControllerCommand::READ_OUTPUT_PORT => {
336                let output_port = OutputPort::new()
337                    .with_reset(true)
338                    .with_a20_gate(self.state.a20_gate)
339                    .with_aux_clock(true)
340                    .with_aux_data(false)
341                    .with_keyboard_output_buffered(
342                        self.state.output_buffer_state == OutputBufferState::Keyboard,
343                    )
344                    .with_mouse_output_buffered(
345                        self.state.output_buffer_state == OutputBufferState::Mouse,
346                    )
347                    .with_clock(true)
348                    .with_data(false);
349
350                self.write_output_byte(OutputBufferState::Controller, output_port.into());
351            }
352            ControllerCommand::WRITE_OUTPUT_PORT => {
353                let output_port = OutputPort::from(command_data?);
354                if output_port.a20_gate() != self.state.a20_gate {
355                    tracelimit::warn_ratelimited!(
356                        a20_gate = output_port.a20_gate(),
357                        "a20 gate changed, not supported"
358                    );
359                    self.state.a20_gate = output_port.a20_gate();
360                }
361                if !output_port.reset() {
362                    tracing::info!("initiated reset via WRITE_OUTPUT_PORT command");
363                    (self.trigger_reset)();
364                }
365            }
366            ControllerCommand::WRITE_OUTPUT_BUFFER => {
367                // Write the data to the output buffer, making it look as
368                // though the keyboard put it there. This means that there
369                // will be an interrupt (IRQ1) requested.
370                self.write_output_byte(OutputBufferState::Keyboard, command_data?);
371            }
372            ControllerCommand::WRITE_AUX_OUTPUT_BUFFER => {
373                // Write the data to the output buffer, making it look as
374                // though the mouse put it there.
375                self.write_output_byte(OutputBufferState::Mouse, command_data?);
376            }
377            ControllerCommand::WRITE_AUX_DEVICE => {
378                // The next byte written to port 60 will go to the mouse instead of the keyboard
379                self.state.data_port_target = DataPortTarget::Mouse;
380            }
381            cmd if (ControllerCommand::PULSE_OUTPUT_F0..=ControllerCommand::PULSE_OUTPUT_FF)
382                .contains(&cmd) =>
383            {
384                if (cmd.0 & 1) == 0 {
385                    // If we get this command, the program wants to restart the
386                    // machine if bit 0 of the command is clear.
387                    tracing::info!("initiated reset via PULSE_OUTPUT_FX command");
388                    (self.trigger_reset)();
389                } else {
390                    // This command (along with commands 0xF0 through 0xFE) strobes the
391                    // four output bits on the keyboard controller. Except for 0xFE, all
392                    // of these commands are NOPs. This one in particular is used by the
393                    // BIOS and various other applications just to check to make sure the
394                    // keyboard controller is still responding.
395                }
396            }
397            cmd if (0x20..=0x3f).contains(&cmd.0) => {
398                // There are 31 spare bytes of data storage in the keyboard controller
399                // that can be addressed and written to by commands 0x61 - 0x7F.
400                // When these registers are read using commands 0x21 - 0x3f, they
401                // apparently simulate data bytes coming from the keyboard.
402                // NetOp (a remote control program) uses this functionality.
403
404                self.write_output_byte(
405                    OutputBufferState::Keyboard,
406                    self.state.memory[cmd.0 as usize & 0x1f],
407                );
408            }
409            command if (0x60..=0x7f).contains(&command.0) => {
410                self.state.memory[command.0 as usize & 0x1f] = command_data?;
411            }
412
413            ControllerCommand::UNKNOWN_A1 => {
414                self.write_output_byte(OutputBufferState::Controller, 0);
415            }
416            ControllerCommand::PWD_CHECK => {
417                self.write_output_byte(OutputBufferState::Controller, 0xf1);
418            }
419            cmd => {
420                tracelimit::warn_ratelimited!(?cmd, "unsupported keyboard command");
421            }
422        }
423        Some(())
424    }
425
426    /// Loads the output buffer with the next device output byte.
427    fn load_device_output(&mut self) -> bool {
428        if self.state.output_buffer_state != OutputBufferState::Empty {
429            return true;
430        }
431
432        if !self.state.command_flag.disable_mouse() {
433            if let Some(byte) = self.mouse.output() {
434                self.write_output_byte(OutputBufferState::Mouse, byte);
435                return true;
436            }
437        }
438
439        if let Some(byte) = self.keyboard.output() {
440            self.write_output_byte(OutputBufferState::Keyboard, byte);
441            return true;
442        }
443
444        false
445    }
446
447    /// Loads the output buffer with the next device output byte, waking the
448    /// controller and devices to be polled if there are no more output bytes.
449    fn check_devices_for_output(&mut self) {
450        if !self.load_device_output() {
451            // Wake the poll function to poll the devices.
452            if let Some(waker) = self.waker.take() {
453                waker.wake();
454            }
455        }
456    }
457}
458
459mod save_restore {
460    use super::*;
461    use vmcore::save_restore::RestoreError;
462    use vmcore::save_restore::SaveError;
463    use vmcore::save_restore::SaveRestore;
464
465    mod state {
466        use mesh::payload::Protobuf;
467        use vmcore::save_restore::SaveRestore;
468        use vmcore::save_restore::SavedStateRoot;
469
470        /// Saved state.
471        #[derive(Protobuf, SavedStateRoot)]
472        #[mesh(package = "chipset.i8042")]
473        pub struct SavedState {
474            #[mesh(1)]
475            pub controller: SavedControllerState,
476            #[mesh(2)]
477            pub keyboard: <super::ps2keyboard::Ps2Keyboard as SaveRestore>::SavedState,
478            #[mesh(3)]
479            pub mouse: <super::ps2mouse::Ps2Mouse as SaveRestore>::SavedState,
480        }
481
482        #[derive(Protobuf)]
483        #[mesh(package = "chipset.i8042")]
484        pub struct SavedControllerState {
485            #[mesh(1)]
486            pub command_flag: u8,
487            #[mesh(2)]
488            pub data_port_target: SavedDataPortTarget,
489            #[mesh(3)]
490            pub output_buffer: u8,
491            #[mesh(4)]
492            pub output_buffer_state: SavedOutputBufferState,
493            #[mesh(5)]
494            pub a20_gate: bool,
495            #[mesh(6)]
496            pub memory: [u8; 32],
497        }
498
499        #[derive(Protobuf)]
500        #[mesh(package = "chipset.i8042")]
501        pub enum SavedOutputBufferState {
502            #[mesh(1)]
503            Empty,
504            #[mesh(2)]
505            Controller,
506            #[mesh(3)]
507            Keyboard,
508            #[mesh(4)]
509            Mouse,
510        }
511
512        #[derive(Protobuf)]
513        #[mesh(package = "chipset.i8042")]
514        pub enum SavedDataPortTarget {
515            #[mesh(1)]
516            Keyboard,
517            #[mesh(2)]
518            Mouse,
519            #[mesh(3)]
520            Controller(u8),
521        }
522    }
523
524    impl SaveRestore for I8042Device {
525        type SavedState = state::SavedState;
526
527        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
528            let I8042State {
529                command_flag,
530                data_port_target,
531                output_buffer,
532                output_buffer_state,
533                a20_gate,
534                memory,
535            } = self.state;
536
537            let saved_state = state::SavedState {
538                controller: state::SavedControllerState {
539                    command_flag: command_flag.into(),
540                    data_port_target: match data_port_target {
541                        DataPortTarget::Keyboard => state::SavedDataPortTarget::Keyboard,
542                        DataPortTarget::Mouse => state::SavedDataPortTarget::Mouse,
543                        DataPortTarget::Controller(ControllerCommand(b)) => {
544                            state::SavedDataPortTarget::Controller(b)
545                        }
546                    },
547                    output_buffer,
548                    output_buffer_state: match output_buffer_state {
549                        OutputBufferState::Empty => state::SavedOutputBufferState::Empty,
550                        OutputBufferState::Controller => state::SavedOutputBufferState::Controller,
551                        OutputBufferState::Keyboard => state::SavedOutputBufferState::Keyboard,
552                        OutputBufferState::Mouse => state::SavedOutputBufferState::Mouse,
553                    },
554                    a20_gate,
555                    memory,
556                },
557                keyboard: self.keyboard.save()?,
558                mouse: self.mouse.save()?,
559            };
560
561            Ok(saved_state)
562        }
563
564        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
565            let state::SavedState {
566                controller,
567                keyboard,
568                mouse,
569            } = state;
570
571            {
572                let state::SavedControllerState {
573                    command_flag,
574                    data_port_target,
575                    output_buffer,
576                    output_buffer_state,
577                    a20_gate,
578                    memory,
579                } = controller;
580
581                self.state = I8042State {
582                    command_flag: CommandFlag::from(command_flag), // no unused bits
583                    data_port_target: match data_port_target {
584                        state::SavedDataPortTarget::Keyboard => DataPortTarget::Keyboard,
585                        state::SavedDataPortTarget::Mouse => DataPortTarget::Mouse,
586                        state::SavedDataPortTarget::Controller(b) => {
587                            DataPortTarget::Controller(ControllerCommand(b))
588                        }
589                    },
590                    output_buffer,
591                    output_buffer_state: match output_buffer_state {
592                        state::SavedOutputBufferState::Empty => OutputBufferState::Empty,
593                        state::SavedOutputBufferState::Controller => OutputBufferState::Controller,
594                        state::SavedOutputBufferState::Keyboard => OutputBufferState::Keyboard,
595                        state::SavedOutputBufferState::Mouse => OutputBufferState::Mouse,
596                    },
597                    a20_gate,
598                    memory,
599                };
600            }
601
602            self.keyboard.restore(keyboard)?;
603            self.mouse.restore(mouse)?;
604
605            self.sync_interrupts();
606
607            Ok(())
608        }
609    }
610}