From 8d9ee7943f06d286d10f5bfd3dcfe75e092ca9aa Mon Sep 17 00:00:00 2001 From: iximeow Date: Tue, 29 Sep 2026 23:51:33 +0000 Subject: [PATCH 1/2] ps2: controller should survive weird accesses practically speaking, no driver ever issues reads or writes against PS/2 I/O ports that are not one byte wide, but if you do we shouldn't panic over it. that is to say, `mdb -KFe '0x60::out -L 2 0'` should not kill Propolis.. --- lib/propolis/src/hw/ps2/ctrl.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/propolis/src/hw/ps2/ctrl.rs b/lib/propolis/src/hw/ps2/ctrl.rs index a1c10f777..4e3736102 100644 --- a/lib/propolis/src/hw/ps2/ctrl.rs +++ b/lib/propolis/src/hw/ps2/ctrl.rs @@ -399,7 +399,18 @@ impl PS2Ctrl { } fn pio_rw(&self, port: u16, rwo: RWOp) { - assert_eq!(rwo.len(), 1); + if rwo.len() != 1 { + // TODO: It would be nice to record an error somewhere. + // + // The guest has operated the PS/2 controller.. poorly. Port + // I/O to the controller's registers should be one byte wide. In + // practice I *think* wider accesses are masked down to the one byte + // that is actually wired up to the port, but behavior in this case + // is not tested. Either way, we haven't (?yet?) seen a PS/2 driver + // that does these kinds of accesses. + return; + } + match port { ibmpc::PORT_PS2_DATA => match rwo { RWOp::Read(ro) => ro.write_u8(self.data_read()), From b3d07a4ea36c072b18f2c0cb901d87261e690ae3 Mon Sep 17 00:00:00 2001 From: iximeow Date: Wed, 30 Sep 2026 00:21:54 +0000 Subject: [PATCH 2/2] ps2: controller RAM should be accessible trying to use byte 31 of controller RAM would go out of bounds and panic Propolis. additionally, the accessed offset for a multi-part PS/2 controller write was mapped to RAM offsets incorrectly, so such writes would get processed incorrectly. that is to say, `mdb -KFe '0x64::out 0L 1 0x3f'` should not kill Propolis.. --- lib/propolis/src/hw/ps2/ctrl.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/propolis/src/hw/ps2/ctrl.rs b/lib/propolis/src/hw/ps2/ctrl.rs index 4e3736102..fce0efb05 100644 --- a/lib/propolis/src/hw/ps2/ctrl.rs +++ b/lib/propolis/src/hw/ps2/ctrl.rs @@ -291,8 +291,18 @@ const PS2C_CMD_WRITE_AUX_IN: u8 = 0xd4; const PS2C_CMD_PULSE_START: u8 = 0xf0; const PS2C_CMD_PULSE_END: u8 = 0xff; -const PS2C_RAM_LEN: usize = - (PS2C_CMD_WRITE_RAM_END - PS2C_CMD_WRITE_RAM_START) as usize; +// PS/2 Controller RAM is modeled as 31 bytes of variable data, with the first +// byte of the controller RAM region (read with command 0x20, written with +// command 0x60) is handled separately through `ctrl_cfg`. +// +// That leaves 31 bytes of controller RAM here. +const PS2C_RAM_LEN: usize = const { + // The command consts above are inclusive of the accessed range, so we must + // add one for the RAM array to span all 31 bytes. + let size = (PS2C_CMD_WRITE_RAM_END - PS2C_CMD_WRITE_RAM_START + 1) as usize; + assert!(size == 31); + size +}; #[derive(Default)] struct PS2State { @@ -445,7 +455,7 @@ impl PS2Ctrl { state.ctrl_cfg = cfg; } PS2C_CMD_WRITE_RAM_START..=PS2C_CMD_WRITE_RAM_END => { - let off = v - PS2C_CMD_WRITE_RAM_START; + let off = prefix - PS2C_CMD_WRITE_RAM_START; state.ram[off as usize] = v; } PS2C_CMD_WRITE_CTLR_OUT => {