1e342083cSJohn Floren #include "kvm/read-write.h" 2e342083cSJohn Floren #include "kvm/ioport.h" 3e342083cSJohn Floren #include "kvm/mutex.h" 4e342083cSJohn Floren #include "kvm/util.h" 5e342083cSJohn Floren #include "kvm/term.h" 6e342083cSJohn Floren #include "kvm/kvm.h" 7e342083cSJohn Floren #include "kvm/i8042.h" 8714e5b7fSSasha Levin #include "kvm/kvm-cpu.h" 9e342083cSJohn Floren 10e342083cSJohn Floren #include <stdint.h> 11e342083cSJohn Floren 1267307cddSPekka Enberg /* 1367307cddSPekka Enberg * IRQs 1467307cddSPekka Enberg */ 15e342083cSJohn Floren #define KBD_IRQ 1 16e342083cSJohn Floren #define AUX_IRQ 12 17e342083cSJohn Floren 1867307cddSPekka Enberg /* 1967307cddSPekka Enberg * Registers 2067307cddSPekka Enberg */ 2167307cddSPekka Enberg #define I8042_DATA_REG 0x60 2248bed025SPekka Enberg #define I8042_PORT_B_REG 0x61 2367307cddSPekka Enberg #define I8042_COMMAND_REG 0x64 2467307cddSPekka Enberg 25b4a721d9SPekka Enberg /* 26b4a721d9SPekka Enberg * Commands 27b4a721d9SPekka Enberg */ 28b4a721d9SPekka Enberg #define I8042_CMD_CTL_RCTR 0x20 29b4a721d9SPekka Enberg #define I8042_CMD_CTL_WCTR 0x60 30b4a721d9SPekka Enberg #define I8042_CMD_AUX_LOOP 0xD3 31b4a721d9SPekka Enberg #define I8042_CMD_AUX_SEND 0xD4 32b4a721d9SPekka Enberg #define I8042_CMD_AUX_TEST 0xA9 33b4a721d9SPekka Enberg #define I8042_CMD_AUX_DISABLE 0xA7 34b4a721d9SPekka Enberg #define I8042_CMD_AUX_ENABLE 0xA8 35714e5b7fSSasha Levin #define I8042_CMD_SYSTEM_RESET 0xFE 36e342083cSJohn Floren 37e342083cSJohn Floren #define RESPONSE_ACK 0xFA 38e342083cSJohn Floren 39e342083cSJohn Floren #define MODE_DISABLE_AUX 0x20 40e342083cSJohn Floren 41e342083cSJohn Floren #define AUX_ENABLE_REPORTING 0x20 42e342083cSJohn Floren #define AUX_SCALING_FLAG 0x10 43e342083cSJohn Floren #define AUX_DEFAULT_RESOLUTION 0x2 44e342083cSJohn Floren #define AUX_DEFAULT_SAMPLE 100 45e342083cSJohn Floren 4698cbe4d9SPekka Enberg /* 4798cbe4d9SPekka Enberg * Status register bits 4898cbe4d9SPekka Enberg */ 49e9f4d662SPekka Enberg #define I8042_STR_AUXDATA 0x20 50e9f4d662SPekka Enberg #define I8042_STR_KEYLOCK 0x10 51e9f4d662SPekka Enberg #define I8042_STR_CMDDAT 0x08 52e9f4d662SPekka Enberg #define I8042_STR_MUXERR 0x04 53e9f4d662SPekka Enberg #define I8042_STR_OBF 0x01 54e342083cSJohn Floren 55e342083cSJohn Floren #define KBD_MODE_KBD_INT 0x01 56e342083cSJohn Floren #define KBD_MODE_SYS 0x02 57e342083cSJohn Floren 58e342083cSJohn Floren #define QUEUE_SIZE 128 59e342083cSJohn Floren 60e342083cSJohn Floren /* 61e342083cSJohn Floren * This represents the current state of the PS/2 keyboard system, 62e342083cSJohn Floren * including the AUX device (the mouse) 63e342083cSJohn Floren */ 64e342083cSJohn Floren struct kbd_state { 65e342083cSJohn Floren struct kvm *kvm; 66e342083cSJohn Floren 67e342083cSJohn Floren char kq[QUEUE_SIZE]; /* Keyboard queue */ 68e342083cSJohn Floren int kread, kwrite; /* Indexes into the queue */ 69e342083cSJohn Floren int kcount; /* number of elements in queue */ 70e342083cSJohn Floren 71e342083cSJohn Floren char mq[QUEUE_SIZE]; 72e342083cSJohn Floren int mread, mwrite; 73e342083cSJohn Floren int mcount; 74e342083cSJohn Floren 75e342083cSJohn Floren u8 mstatus; /* Mouse status byte */ 76e342083cSJohn Floren u8 mres; /* Current mouse resolution */ 77e342083cSJohn Floren u8 msample; /* Current mouse samples/second */ 78e342083cSJohn Floren 79e342083cSJohn Floren u8 mode; /* i8042 mode register */ 80e342083cSJohn Floren u8 status; /* i8042 status register */ 81e342083cSJohn Floren /* 82e342083cSJohn Floren * Some commands (on port 0x64) have arguments; 83e342083cSJohn Floren * we store the command here while we wait for the argument 84e342083cSJohn Floren */ 85e342083cSJohn Floren u32 write_cmd; 86e342083cSJohn Floren }; 87e342083cSJohn Floren 88e342083cSJohn Floren static struct kbd_state state; 89e342083cSJohn Floren 90e342083cSJohn Floren /* 91e342083cSJohn Floren * If there are packets to be read, set the appropriate IRQs high 92e342083cSJohn Floren */ 93e342083cSJohn Floren static void kbd_update_irq(void) 94e342083cSJohn Floren { 95e342083cSJohn Floren u8 klevel = 0; 96e342083cSJohn Floren u8 mlevel = 0; 97e342083cSJohn Floren 98e342083cSJohn Floren /* First, clear the kbd and aux output buffer full bits */ 99e9f4d662SPekka Enberg state.status &= ~(I8042_STR_OBF | I8042_STR_AUXDATA); 100e342083cSJohn Floren 101e342083cSJohn Floren if (state.kcount > 0) { 102e9f4d662SPekka Enberg state.status |= I8042_STR_OBF; 103e342083cSJohn Floren klevel = 1; 104e342083cSJohn Floren } 105e342083cSJohn Floren 106e342083cSJohn Floren /* Keyboard has higher priority than mouse */ 107e342083cSJohn Floren if (klevel == 0 && state.mcount != 0) { 108e9f4d662SPekka Enberg state.status |= I8042_STR_OBF | I8042_STR_AUXDATA; 109e342083cSJohn Floren mlevel = 1; 110e342083cSJohn Floren } 111e342083cSJohn Floren 112e342083cSJohn Floren kvm__irq_line(state.kvm, KBD_IRQ, klevel); 113e342083cSJohn Floren kvm__irq_line(state.kvm, AUX_IRQ, mlevel); 114e342083cSJohn Floren } 115e342083cSJohn Floren 116e342083cSJohn Floren /* 117e342083cSJohn Floren * Add a byte to the mouse queue, then set IRQs 118e342083cSJohn Floren */ 119b6f60a85SPekka Enberg void mouse_queue(u8 c) 120e342083cSJohn Floren { 121e342083cSJohn Floren if (state.mcount >= QUEUE_SIZE) 122e342083cSJohn Floren return; 123e342083cSJohn Floren 124e342083cSJohn Floren state.mq[state.mwrite++ % QUEUE_SIZE] = c; 125e342083cSJohn Floren 126e342083cSJohn Floren state.mcount++; 127e342083cSJohn Floren kbd_update_irq(); 128e342083cSJohn Floren } 129e342083cSJohn Floren 130e342083cSJohn Floren /* 131e342083cSJohn Floren * Add a byte to the keyboard queue, then set IRQs 132e342083cSJohn Floren */ 133b6f60a85SPekka Enberg void kbd_queue(u8 c) 134e342083cSJohn Floren { 135e342083cSJohn Floren if (state.kcount >= QUEUE_SIZE) 136e342083cSJohn Floren return; 137e342083cSJohn Floren 138e342083cSJohn Floren state.kq[state.kwrite++ % QUEUE_SIZE] = c; 139e342083cSJohn Floren 140e342083cSJohn Floren state.kcount++; 141e342083cSJohn Floren kbd_update_irq(); 142e342083cSJohn Floren } 143e342083cSJohn Floren 144714e5b7fSSasha Levin static void kbd_write_command(struct kvm *kvm, u8 val) 145e342083cSJohn Floren { 146e342083cSJohn Floren switch (val) { 147b4a721d9SPekka Enberg case I8042_CMD_CTL_RCTR: 148e342083cSJohn Floren kbd_queue(state.mode); 149e342083cSJohn Floren break; 150b4a721d9SPekka Enberg case I8042_CMD_CTL_WCTR: 151b4a721d9SPekka Enberg case I8042_CMD_AUX_SEND: 152b4a721d9SPekka Enberg case I8042_CMD_AUX_LOOP: 153e342083cSJohn Floren state.write_cmd = val; 154e342083cSJohn Floren break; 155b4a721d9SPekka Enberg case I8042_CMD_AUX_TEST: 156e342083cSJohn Floren /* 0 means we're a normal PS/2 mouse */ 157e342083cSJohn Floren mouse_queue(0); 158e342083cSJohn Floren break; 159b4a721d9SPekka Enberg case I8042_CMD_AUX_DISABLE: 160e342083cSJohn Floren state.mode |= MODE_DISABLE_AUX; 161e342083cSJohn Floren break; 162b4a721d9SPekka Enberg case I8042_CMD_AUX_ENABLE: 163e342083cSJohn Floren state.mode &= ~MODE_DISABLE_AUX; 164e342083cSJohn Floren break; 165714e5b7fSSasha Levin case I8042_CMD_SYSTEM_RESET: 1662aa76b26SWill Deacon kvm__reboot(kvm); 167714e5b7fSSasha Levin break; 168e342083cSJohn Floren default: 169e342083cSJohn Floren break; 170e342083cSJohn Floren } 171e342083cSJohn Floren } 172e342083cSJohn Floren 173e342083cSJohn Floren /* 174e342083cSJohn Floren * Called when the OS reads from port 0x60 (PS/2 data) 175e342083cSJohn Floren */ 176e342083cSJohn Floren static u32 kbd_read_data(void) 177e342083cSJohn Floren { 178e342083cSJohn Floren u32 ret; 179e342083cSJohn Floren int i; 180e342083cSJohn Floren 181e342083cSJohn Floren if (state.kcount != 0) { 182e342083cSJohn Floren /* Keyboard data gets read first */ 183e342083cSJohn Floren ret = state.kq[state.kread++ % QUEUE_SIZE]; 184e342083cSJohn Floren state.kcount--; 185e342083cSJohn Floren kvm__irq_line(state.kvm, KBD_IRQ, 0); 186e342083cSJohn Floren kbd_update_irq(); 187e342083cSJohn Floren } else if (state.mcount > 0) { 188e342083cSJohn Floren /* Followed by the mouse */ 189e342083cSJohn Floren ret = state.mq[state.mread++ % QUEUE_SIZE]; 190e342083cSJohn Floren state.mcount--; 191e342083cSJohn Floren kvm__irq_line(state.kvm, AUX_IRQ, 0); 192e342083cSJohn Floren kbd_update_irq(); 19376857a39SCong Ding } else { 194e342083cSJohn Floren i = state.kread - 1; 195e342083cSJohn Floren if (i < 0) 196e342083cSJohn Floren i = QUEUE_SIZE; 197e342083cSJohn Floren ret = state.kq[i]; 198e342083cSJohn Floren } 199e342083cSJohn Floren return ret; 200e342083cSJohn Floren } 201e342083cSJohn Floren 202e342083cSJohn Floren /* 203e342083cSJohn Floren * Called when the OS read from port 0x64, the command port 204e342083cSJohn Floren */ 205e342083cSJohn Floren static u32 kbd_read_status(void) 206e342083cSJohn Floren { 207e342083cSJohn Floren return (u32)state.status; 208e342083cSJohn Floren } 209e342083cSJohn Floren 210e342083cSJohn Floren /* 211e342083cSJohn Floren * Called when the OS writes to port 0x60 (data port) 212e342083cSJohn Floren * Things written here are generally arguments to commands previously 213e342083cSJohn Floren * written to port 0x64 and stored in state.write_cmd 214e342083cSJohn Floren */ 215e342083cSJohn Floren static void kbd_write_data(u32 val) 216e342083cSJohn Floren { 217e342083cSJohn Floren switch (state.write_cmd) { 218b4a721d9SPekka Enberg case I8042_CMD_CTL_WCTR: 219e342083cSJohn Floren state.mode = val; 220e342083cSJohn Floren kbd_update_irq(); 221e342083cSJohn Floren break; 222b4a721d9SPekka Enberg case I8042_CMD_AUX_LOOP: 223e342083cSJohn Floren mouse_queue(val); 224e342083cSJohn Floren mouse_queue(RESPONSE_ACK); 225e342083cSJohn Floren break; 226b4a721d9SPekka Enberg case I8042_CMD_AUX_SEND: 227e342083cSJohn Floren /* The OS wants to send a command to the mouse */ 228e342083cSJohn Floren mouse_queue(RESPONSE_ACK); 229e342083cSJohn Floren switch (val) { 230e342083cSJohn Floren case 0xe6: 231e342083cSJohn Floren /* set scaling = 1:1 */ 232e342083cSJohn Floren state.mstatus &= ~AUX_SCALING_FLAG; 233e342083cSJohn Floren break; 234e342083cSJohn Floren case 0xe8: 235e342083cSJohn Floren /* set resolution */ 236e342083cSJohn Floren state.mres = val; 237e342083cSJohn Floren break; 238e342083cSJohn Floren case 0xe9: 239e342083cSJohn Floren /* Report mouse status/config */ 240e342083cSJohn Floren mouse_queue(state.mstatus); 241e342083cSJohn Floren mouse_queue(state.mres); 242e342083cSJohn Floren mouse_queue(state.msample); 243e342083cSJohn Floren break; 244e342083cSJohn Floren case 0xf2: 245e342083cSJohn Floren /* send ID */ 246e342083cSJohn Floren mouse_queue(0); /* normal mouse */ 247e342083cSJohn Floren break; 248e342083cSJohn Floren case 0xf3: 249e342083cSJohn Floren /* set sample rate */ 250e342083cSJohn Floren state.msample = val; 251e342083cSJohn Floren break; 252e342083cSJohn Floren case 0xf4: 253e342083cSJohn Floren /* enable reporting */ 254e342083cSJohn Floren state.mstatus |= AUX_ENABLE_REPORTING; 255e342083cSJohn Floren break; 256e342083cSJohn Floren case 0xf5: 257e342083cSJohn Floren state.mstatus &= ~AUX_ENABLE_REPORTING; 258e342083cSJohn Floren break; 259e342083cSJohn Floren case 0xf6: 260e342083cSJohn Floren /* set defaults, just fall through to reset */ 261e342083cSJohn Floren case 0xff: 262e342083cSJohn Floren /* reset */ 263e342083cSJohn Floren state.mstatus = 0x0; 264e342083cSJohn Floren state.mres = AUX_DEFAULT_RESOLUTION; 265e342083cSJohn Floren state.msample = AUX_DEFAULT_SAMPLE; 266e342083cSJohn Floren break; 267e342083cSJohn Floren default: 268e342083cSJohn Floren break; 269e342083cSJohn Floren } 270e342083cSJohn Floren break; 271e342083cSJohn Floren case 0: 272e342083cSJohn Floren /* Just send the ID */ 273e342083cSJohn Floren kbd_queue(RESPONSE_ACK); 274e342083cSJohn Floren kbd_queue(0xab); 275e342083cSJohn Floren kbd_queue(0x41); 276e342083cSJohn Floren kbd_update_irq(); 277e342083cSJohn Floren break; 278e342083cSJohn Floren default: 279e342083cSJohn Floren /* Yeah whatever */ 280e342083cSJohn Floren break; 281e342083cSJohn Floren } 282e342083cSJohn Floren state.write_cmd = 0; 283e342083cSJohn Floren } 284e342083cSJohn Floren 285e342083cSJohn Floren static void kbd_reset(void) 286e342083cSJohn Floren { 287e342083cSJohn Floren state = (struct kbd_state) { 288e9f4d662SPekka Enberg .status = I8042_STR_MUXERR | I8042_STR_CMDDAT | I8042_STR_KEYLOCK, /* 0x1c */ 289e342083cSJohn Floren .mode = KBD_MODE_KBD_INT | KBD_MODE_SYS, /* 0x3 */ 290e342083cSJohn Floren .mres = AUX_DEFAULT_RESOLUTION, 291e342083cSJohn Floren .msample = AUX_DEFAULT_SAMPLE, 292e342083cSJohn Floren }; 293e342083cSJohn Floren } 294e342083cSJohn Floren 295e342083cSJohn Floren /* 296e342083cSJohn Floren * Called when the OS has written to one of the keyboard's ports (0x60 or 0x64) 297e342083cSJohn Floren */ 2984123ca55SMarc Zyngier static bool kbd_in(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size) 299e342083cSJohn Floren { 300ca9326bbSPekka Enberg switch (port) { 301ca9326bbSPekka Enberg case I8042_COMMAND_REG: { 302ca9326bbSPekka Enberg u8 value = kbd_read_status(); 303ca9326bbSPekka Enberg ioport__write8(data, value); 304ca9326bbSPekka Enberg break; 305e342083cSJohn Floren } 306ca9326bbSPekka Enberg case I8042_DATA_REG: { 307ca9326bbSPekka Enberg u32 value = kbd_read_data(); 308ca9326bbSPekka Enberg ioport__write32(data, value); 309ca9326bbSPekka Enberg break; 310ca9326bbSPekka Enberg } 31148bed025SPekka Enberg case I8042_PORT_B_REG: { 31248bed025SPekka Enberg ioport__write8(data, 0x20); 31348bed025SPekka Enberg break; 31448bed025SPekka Enberg } 315ca9326bbSPekka Enberg default: 316ca9326bbSPekka Enberg return false; 317ca9326bbSPekka Enberg } 318ca9326bbSPekka Enberg 319e342083cSJohn Floren return true; 320e342083cSJohn Floren } 321e342083cSJohn Floren 3224123ca55SMarc Zyngier static bool kbd_out(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size) 323e342083cSJohn Floren { 324ca9326bbSPekka Enberg switch (port) { 325ca9326bbSPekka Enberg case I8042_COMMAND_REG: { 326ca9326bbSPekka Enberg u8 value = ioport__read8(data); 3274123ca55SMarc Zyngier kbd_write_command(vcpu->kvm, value); 328ca9326bbSPekka Enberg break; 329ca9326bbSPekka Enberg } 330ca9326bbSPekka Enberg case I8042_DATA_REG: { 331ca9326bbSPekka Enberg u32 value = ioport__read32(data); 332ca9326bbSPekka Enberg kbd_write_data(value); 333ca9326bbSPekka Enberg break; 334ca9326bbSPekka Enberg } 33548bed025SPekka Enberg case I8042_PORT_B_REG: { 33648bed025SPekka Enberg break; 33748bed025SPekka Enberg } 338ca9326bbSPekka Enberg default: 339ca9326bbSPekka Enberg return false; 340ca9326bbSPekka Enberg } 341e342083cSJohn Floren 342e342083cSJohn Floren return true; 343e342083cSJohn Floren } 344e342083cSJohn Floren 345e342083cSJohn Floren static struct ioport_operations kbd_ops = { 346e342083cSJohn Floren .io_in = kbd_in, 347e342083cSJohn Floren .io_out = kbd_out, 348e342083cSJohn Floren }; 349e342083cSJohn Floren 350be1eb89bSSasha Levin int kbd__init(struct kvm *kvm) 351e342083cSJohn Floren { 352*8f160708SAlexandru Elisei int r; 353*8f160708SAlexandru Elisei 354e342083cSJohn Floren kbd_reset(); 355e342083cSJohn Floren state.kvm = kvm; 356*8f160708SAlexandru Elisei r = ioport__register(kvm, I8042_DATA_REG, &kbd_ops, 2, NULL); 357*8f160708SAlexandru Elisei if (r < 0) 358*8f160708SAlexandru Elisei return r; 359*8f160708SAlexandru Elisei r = ioport__register(kvm, I8042_COMMAND_REG, &kbd_ops, 2, NULL); 360*8f160708SAlexandru Elisei if (r < 0) { 361*8f160708SAlexandru Elisei ioport__unregister(kvm, I8042_DATA_REG); 362*8f160708SAlexandru Elisei return r; 363*8f160708SAlexandru Elisei } 364be1eb89bSSasha Levin 365be1eb89bSSasha Levin return 0; 366e342083cSJohn Floren } 36749a8afd1SSasha Levin dev_init(kbd__init); 368