Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Revised mouse-related host events, graphics console, and userland support; mouse...
[palacios.git] / palacios / src / devices / keyboard.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2008, Peter Dinda <pdinda@northwestern.edu> 
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Peter Dinda <pdinda@northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <palacios/vmm.h>
21 #include <palacios/vmm_dev_mgr.h>
22 #include <palacios/vmm_types.h>
23
24 #include <palacios/vmm_ringbuffer.h>
25 #include <palacios/vmm_lock.h>
26 #include <palacios/vmm_intr.h>
27 #include <palacios/vmm_host_events.h>
28 #include <palacios/vm_guest.h>
29 #include <palacios/vmm_debug.h>
30
31
32 #ifndef V3_CONFIG_DEBUG_KEYBOARD
33 #undef PrintDebug
34 #define PrintDebug(fmt, args...)
35 #endif
36
37 #define KEYBOARD_DEBUG_80H   0
38
39
40
41 #define KEYBOARD_60H          0x60  // keyboard microcontroller
42 #define KEYBOARD_64H          0x64  // onboard microcontroller
43
44 #define KEYBOARD_DELAY_80H    0x80  // written for timing
45
46 #define KEYBOARD_IRQ          0x1
47 #define MOUSE_IRQ             0xc   
48
49
50
51 // bits for the output port 
52 #define OUTPUT_RESET        0x01  // System reset on 0
53 #define OUTPUT_A20          0x02  // A20 gate (1= A20 is gated)
54 #define OUTPUT_RES1         0x04  // reserved
55 #define OUTPUT_RES2         0x08  // reserved
56 #define OUTPUT_OUTPUT_FULL  0x10  // output buffer full
57 #define OUTPUT_INPUT_EMPTY  0x20  // input buffer empty
58 #define OUTPUT_KBD_CLOCK    0x40  // keyboard clock (?)
59 #define OUTPUT_KBD_DATA     0x80  // keyboard data
60
61 // bits for the input port
62
63 #define INPUT_RES0          0x01  // reserved
64 #define INPUT_RES1          0x02  // reserved
65 #define INPUT_RES2          0x04  // reserved
66 #define INPUT_RES3          0x08  // reserved
67 #define INPUT_RAM           0x10  // set to 1 if RAM exists?
68 #define INPUT_JUMPER        0x20  // manufacturing jumper?
69 #define INPUT_DISPLAY       0x40  // 0=color, 1=mono
70 #define INPUT_KBD_INHIBIT   0x80  // 1=inhibit keyboard ?
71
72
73 #define MOUSE_ACK           0xfa
74
75 // for queue operations
76 #define QUEUE               0
77 #define OVERWRITE           1
78
79 // for queue operations - whether it's data or cmd waiting on 60h
80 #define DATA                0
81 #define COMMAND             1
82
83 // for queue operations - whether this is keyboard or mouse data on 60h
84 #define KEYBOARD            0
85 #define MOUSE               1
86
87
88
89 struct cmd_reg {
90     union {
91         uint8_t val;
92         struct {
93             uint8_t irq_en        : 1;  // 1=interrupts enabled
94             uint8_t mouse_irq_en  : 1;  // 1=interrupts enabled for mouse
95             uint8_t self_test_ok  : 1;  // 1= self test passed
96             uint8_t override      : 1;  // MBZ for  PS2
97             uint8_t disable       : 1;  // 1=disabled keyboard
98             uint8_t mouse_disable : 1;  // 1=disabled mouse
99             uint8_t translate     : 1;  // 1=translate to set 1 scancodes (For PC Compatibility)
100             uint8_t rsvd          : 1;  // must be zero
101         } __attribute__((packed));
102     } __attribute__((packed));
103 } __attribute__((packed));
104
105
106
107
108 struct status_reg {
109     union {
110         uint8_t val;
111         struct {
112             uint8_t out_buf_full        : 1; // 1=full (data for system)
113             uint8_t in_buf_full         : 1; // 1=full (data for 8042)
114             uint8_t self_test_ok        : 1; // 1=self-test-passed
115             uint8_t cmd                 : 1; // 0=data on 60h, 1=cmd on 64h
116             uint8_t enabled             : 1; // 1=keyboard is enabled
117             uint8_t mouse_buf_full      : 1; // 1= mouse output buffer full
118             uint8_t timeout_err         : 1; // 1=timeout of keybd
119             uint8_t parity_err          : 1; // 1=parity error
120         } __attribute__((packed));
121     } __attribute__((packed));
122 } __attribute__((packed));
123
124
125
126
127
128
129 /* This QUEUE_SIZE must be 256 */
130 /* Its designed this way to cause the start/end index to automatically
131    wrap around (2^8 = 256) so an overrun will automatically readjust the 
132    indexes 
133 */
134 #define QUEUE_SIZE 256
135 struct queue {
136     uint8_t queue[QUEUE_SIZE];
137
138     uint8_t start;
139     uint8_t end;
140     int count;
141 };
142
143 struct keyboard_internal {
144     // 
145     // 0x60 is the port for the keyboard microcontroller
146     //   writes are commands
147     //   reads from it usually return scancodes
148     //   however, it can also return other data 
149     //   depending on the state of the onboard microcontroller
150     //
151     // 0x64 is the port for the onboard microcontroller
152     //   writes are commands
153     //   reads are status
154     //
155
156     // state of the onboard microcontroller
157     // this is needed because sometimes 0x60 reads come
158     // from the onboard microcontroller
159     enum {// Normal mode measn we deliver keys
160         // to the vm and accept commands from it
161         NORMAL,
162         // after receiving cmd 0x60
163         // keybaord uC cmd will subsequently arrive
164         WRITING_CMD_BYTE,  
165         // after recieving 0xa5
166         // password arrives on data port, null terminated
167         TRANSMIT_PASSWD,
168         // after having a d1 sent to 64
169         // we wait for a new output byte on 60
170         WRITING_OUTPUT_PORT,
171         // after having a d2 sent to 64
172         // we wait for a new output byte on 60
173         // then make it available as a keystroke
174         INJECTING_KEY,
175         // after having a d3 sent to 64
176         // we wait for a new output byte on 60
177         // then make it available as a mouse event
178         INJECTING_MOUSE,
179         // after having a d4 sent to 64
180         // we wait for a new output byte on 60
181         // then send it to the mouse
182         IN_MOUSE,
183         // After the Keyboard LEDs are enabled
184         // we wait for the output byte on 64?
185         SET_LEDS,
186         // After the Keyboard SET_RATE is called
187         // we wait for the output byte on 64?
188         SET_RATE,
189         // after having a f0 sent to 60
190         // we wait for a new output byte on 60
191         GETSET_SCANCODES,
192         // first send ACK (0xfa)
193         // then wait for reception, and reset kb state
194         SET_DEFAULTS,
195     } state;
196
197
198     enum {
199         // Normal mouse state
200         STREAM, 
201         // this is used for setting sample rate
202         SAMPLE,
203         // set resolution
204         SET_RES,
205     } mouse_state;
206
207
208
209     struct cmd_reg cmd;
210     struct status_reg status;
211
212     uint8_t output_byte;      //  output port of onboard uC (e.g. A20)
213     uint8_t input_byte;       //  input port of onboard uC
214
215     // Data for system
216     uint8_t wrap;     
217
218     uint8_t mouse_enabled;
219     uint8_t scancode_set;
220
221     struct queue kbd_queue;
222     struct queue mouse_queue;
223
224     struct v3_vm_info * vm;
225
226     v3_lock_t kb_lock;
227 };
228
229
230 static int keyboard_reset_device(struct keyboard_internal * kbd);
231
232
233 static int update_kb_irq(struct keyboard_internal * state) {
234     int irq_num = 0;
235
236
237     state->status.out_buf_full = 0;
238     state->status.mouse_buf_full = 0;
239
240
241     // If there is pending Keyboard data then it overrides mouse data
242     if (state->kbd_queue.count > 0) {
243         irq_num = KEYBOARD_IRQ;
244     } else if (state->mouse_queue.count > 0) {
245         irq_num = MOUSE_IRQ;
246         state->status.mouse_buf_full = 1;
247     } 
248     
249     PrintDebug(VM_NONE, VCORE_NONE, "keyboard: interrupt 0x%x\n", irq_num);
250     
251     if (irq_num) {
252         // Global output buffer flag (for both Keyboard and mouse)
253         state->status.out_buf_full = 1;
254         
255         if ((irq_num==KEYBOARD_IRQ && state->cmd.irq_en == 1) || 
256             (irq_num==MOUSE_IRQ && state->cmd.mouse_irq_en == 1)) { 
257
258             v3_raise_irq(state->vm, irq_num);
259         }
260     }
261
262     return 0;
263 }
264
265
266
267 /* Only one byte is read per irq 
268  * So if the queue is still full after a data read, we re-raise the irq
269  * If we keep reading an empty queue we return the last queue entry
270  */
271
272 static int push_to_output_queue(struct keyboard_internal * state, uint8_t value, uint8_t cmd, uint8_t mouse) {
273     struct queue * q = NULL;
274
275
276     if (mouse) {
277         q = &(state->mouse_queue);
278     } else {
279         q = &(state->kbd_queue);
280     }
281
282     if (q->count >= QUEUE_SIZE) {
283         return 0;
284     }
285
286     if (cmd) {
287         state->status.cmd = 1;
288     } else {
289         state->status.cmd = 0;
290     }
291
292     q->queue[q->end] = value;
293
294     if (q->end >= (QUEUE_SIZE - 1)) {
295         q->end = 0;
296     } else {
297         q->end++;
298     }
299
300     q->count++;
301
302
303     update_kb_irq(state);
304
305     return 0;
306 }
307
308
309
310 static int pull_from_output_queue(struct keyboard_internal * state, uint8_t * value) {
311     struct queue * q = NULL;
312
313     if (state->kbd_queue.count > 0) {
314         q = &(state->kbd_queue);
315         PrintDebug(VM_NONE, VCORE_NONE, "Reading from Keyboard Queue\n");
316     } else if (state->mouse_queue.count > 0) {
317         q = &(state->mouse_queue);
318         PrintDebug(VM_NONE, VCORE_NONE, "Reading from Mouse Queue\n");
319     } else {
320         uint8_t idx = state->kbd_queue.start - 1;
321         PrintDebug(VM_NONE, VCORE_NONE, "No Data in any queue\n");
322         *value = state->kbd_queue.queue[idx];
323         return 0;
324     }
325
326     *value = q->queue[q->start];
327
328     if (q->start >= (QUEUE_SIZE - 1)) {
329         q->start = 0;
330     } else {
331         q->start++;
332     }
333
334     q->count--;
335
336
337     PrintDebug(VM_NONE, VCORE_NONE, "Read from Queue: %x\n", *value);
338     PrintDebug(VM_NONE, VCORE_NONE, "QStart=%d, QEnd=%d\n", q->start, q->end);
339
340     update_kb_irq(state);
341
342     return 0;
343 }
344
345
346 #include <palacios/vmm_telemetry.h>
347 #ifdef V3_CONFIG_SYMMOD
348 #include <palacios/vmm_symmod.h>
349 #endif
350
351 static int key_event_handler(struct v3_vm_info * vm, 
352                              struct v3_keyboard_event * evt, 
353                              void * private_data) {
354     struct keyboard_internal * state = (struct keyboard_internal *)private_data;
355
356     PrintDebug(vm, VCORE_NONE, "keyboard: injected status 0x%x, and scancode 0x%x\n", evt->status, evt->scan_code);
357
358     if (evt->scan_code == 0x44) { // F10 debug dump
359         int i = 0;
360         for (i = 0; i < vm->num_cores; i++) {
361             v3_print_guest_state(&(vm->cores[i]));
362         }
363         //      PrintGuestPageTables(info, info->shdw_pg_state.guest_cr3);
364     } 
365 #ifdef V3_CONFIG_SYMCALL
366     else if (evt->scan_code == 0x43) { // F9 Sym test
367         struct guest_info * core = &(vm->cores[0]);
368         PrintDebug(vm, VCORE_NONE, "Testing sym call\n");
369         sym_arg_t a0 = 0x1111;
370         sym_arg_t a1 = 0x2222;
371         sym_arg_t a2 = 0x3333;
372         sym_arg_t a3 = 0x4444;
373         sym_arg_t a4 = 0x5555;
374         uint64_t call_start = 0;
375         uint64_t call_end = 0;
376         
377         V3_Print(vm, VCORE_NONE, "Exits before symcall: %d\n", (uint32_t)core->num_exits);
378
379         rdtscll(call_start);
380         v3_sym_call5(core, SYMCALL_TEST, &a0, &a1, &a2, &a3, &a4);
381         rdtscll(call_end);
382         
383         V3_Print(vm, VCORE_NONE, "Symcall latency = %d cycles (%d exits)\n", (uint32_t)(call_end - call_start), (uint32_t)core->num_exits);
384
385         V3_Print(vm, VCORE_NONE, "Symcall  Test Returned arg0=%x, arg1=%x, arg2=%x, arg3=%x, arg4=%x\n",
386                  (uint32_t)a0, (uint32_t)a1, (uint32_t)a2, (uint32_t)a3, (uint32_t)a4);
387
388     } 
389 #endif
390     else if (evt->scan_code == 0x42) { // F8 debug toggle
391         extern int v3_dbg_enable;
392         
393         PrintDebug(vm, VCORE_NONE, "Toggling Debugging\n");     
394         v3_dbg_enable ^= 1;
395
396     } 
397 #ifdef V3_CONFIG_TELEMETRY
398
399     else if (evt->scan_code == 0x41) { // F7 telemetry dump
400         v3_print_global_telemetry(vm);
401     } 
402 #endif
403 #ifdef V3_CONFIG_SYMMOD
404     else if (evt->scan_code == 0x40) { // F6 Test symmod load
405         v3_load_sym_capsule(vm, "lnx_test");
406     }
407 #endif
408
409
410     addr_t irq_state = v3_lock_irqsave(state->kb_lock);
411
412     if ( (state->status.enabled == 1)      // onboard is enabled
413          && (state->cmd.disable == 0) )  {   // keyboard is enabled
414     
415         push_to_output_queue(state, evt->scan_code, DATA, KEYBOARD);
416     }
417
418     v3_unlock_irqrestore(state->kb_lock, irq_state);
419   
420     return 0;
421 }
422
423
424 static int mouse_event_handler(struct v3_vm_info * vm, 
425                                struct v3_mouse_event * evt, 
426                                void * private_data) {
427     struct keyboard_internal * kbd = (struct keyboard_internal *)private_data;
428     int ret = 0;
429
430     PrintDebug(vm, VCORE_NONE, "keyboard: injected mouse packet 0x %x %x %x\n",
431                evt->data[0], evt->data[1], evt->data[2]);
432   
433     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
434
435     switch (kbd->mouse_state) { 
436         case STREAM:
437             // packet is 3 bytes of form
438             // YO | XO | YS | XS | 1 | MIDDLE | RIGHT | LEFT
439             // DX
440             // YY
441             if (kbd->cmd.mouse_disable == 0) {
442                 uint8_t h;
443                 // YO=0
444                 // XO=0
445                 // bit 3 set
446                 h=0x08; 
447                 // YS bit
448                 h |= (!!(evt->sy)) << 5;
449                 // XS bit
450                 h |= (!!(evt->sx)) << 4;
451                 // buttons
452                 h |= (evt->buttons) & 0x7;
453                 // header byte
454                 push_to_output_queue(kbd, h, DATA, MOUSE);
455                 // dx
456                 push_to_output_queue(kbd, evt->dx, DATA, MOUSE);
457                 // dy
458                 push_to_output_queue(kbd, evt->dy, DATA, MOUSE);
459             } else {
460                 PrintDebug(vm,VCORE_NONE, "Ignoring mouse event because mouse is disabled\n");
461             }
462             break;
463         default:
464             PrintDebug(vm, VCORE_NONE, "Ignoring mouse event due to mouse not being in stream mode\n");
465             ret = 0;
466             break;
467     }
468
469
470     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
471
472     return ret;
473 }
474
475
476
477
478
479
480
481
482
483
484 static int mouse_write_output(struct keyboard_internal * kbd, uint8_t data) {
485     switch (kbd->mouse_state) { 
486         case NORMAL:
487             switch (data) {
488
489                 case 0xff: //reset
490                     if (kbd->mouse_enabled == 0) {
491                         push_to_output_queue(kbd, 0xfe, DATA, MOUSE) ;   // no mouse!
492                     } else {
493                         push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
494                         push_to_output_queue(kbd, 0xaa, DATA, MOUSE) ; 
495                         push_to_output_queue(kbd, 0x00, DATA, MOUSE) ; 
496                     }
497                     break;
498
499 /*              case 0xfe: //resend */
500 /*                  PushToOutputQueue(kbd, 0xfa, OVERWRITE, DATA, MOUSE) ;  */
501 /*                  PrintDebug(VM_NONE, VCORE_NONE, " mouse resend begins "); */
502 /*                  kbd->mouse_done_after_ack = 0; */
503 /*                  kbd->mouse_needs_ack = 0; */
504 /*                  kbd->mouse_state = STREAM1; */
505 /*                  return 0;  // not done */
506 /*                  break; */
507       
508                 case 0xf6: // set defaults
509                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
510                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set defaults ");
511
512                     break;
513       
514                 case 0xf5: // disable data reporting 
515                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
516                     PrintDebug(VM_NONE, VCORE_NONE, " mouse disable data reporting ");
517                     break;
518       
519                 case 0xf4: // enable data reporting 
520                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
521                     PrintDebug(VM_NONE, VCORE_NONE, " mouse enable data reporting ");
522                     break;
523       
524                 case 0xf3: // set sample rate
525                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
526                     kbd->mouse_state = SAMPLE;
527                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set sample rate begins ");
528                     break;
529       
530                 case 0xf2: // get device id
531                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
532                     push_to_output_queue(kbd, 0x0,  DATA, MOUSE); 
533                     PrintDebug(VM_NONE, VCORE_NONE, " mouse get device id begins ");
534                     break;
535       
536                 case 0xf0: // set remote mode
537                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
538                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set remote mode  ");
539                     break;
540
541                 case 0xee: // set wrap mode
542                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
543                     PrintError(VM_NONE, VCORE_NONE, " mouse set wrap mode (ignored)  ");
544                     break;
545
546                 case 0xec: // reset wrap mode
547                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
548                     PrintError(VM_NONE, VCORE_NONE, " mouse reset wrap mode (ignored)  ");
549                     break;
550
551                 case 0xeb: // read data
552                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
553                     PrintError(VM_NONE, VCORE_NONE, " mouse switch to wrap mode (ignored)  ");
554                     break;
555       
556                 case 0xea: // set stream mode
557                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
558                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set stream mode  ");
559                     break;
560
561                 case 0xe9: // status request
562                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
563                     push_to_output_queue(kbd, 0x00, DATA, MOUSE); 
564                     push_to_output_queue(kbd, 0x00, DATA, MOUSE);
565                     push_to_output_queue(kbd, 0x00, DATA, MOUSE); 
566                     PrintDebug(VM_NONE, VCORE_NONE, " mouse status request begins  ");
567                     break;
568
569                 case 0xe8: // set resolution
570                     push_to_output_queue(kbd, MOUSE_ACK,  DATA, MOUSE) ; 
571                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set resolution begins  ");
572                     kbd->mouse_state = SET_RES;
573                     break;
574
575                 case 0xe7: // set scaling 2:1
576                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
577                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set scaling 2:1 ");
578                     break;
579
580                 case 0xe6: // set scaling 1:1
581                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
582                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set scaling 1:1 ");
583                     break;
584       
585                 default:
586                     PrintDebug(VM_NONE, VCORE_NONE, " receiving unknown mouse command (0x%x) in acceptable kbd ", data);
587                     break;
588             }
589
590             break;
591         case SAMPLE:
592         case SET_RES:
593         default:
594             PrintDebug(VM_NONE, VCORE_NONE, " receiving mouse output in unhandled kbd (0x%x) ", kbd->mouse_state);
595             return -1;
596     }
597
598     return 0;
599 }
600
601
602
603 #if KEYBOARD_DEBUG_80H
604 static int keyboard_write_delay(struct guest_info *core, ushort_t port, void * src,  uint_t length, void * priv_data) {
605
606     if (length == 1) { 
607         PrintDebug(core->vm_info, core, "keyboard: write of 0x%x to 80h\n", *((uint8_t*)src));
608         return 1;
609     } else {
610         PrintDebug(core->vm_info, core, "keyboard: write of >1 byte to 80h\n", *((uint8_t*)src));
611         return length;
612     }
613 }
614
615 static int keyboard_read_delay(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
616
617     if (length == 1) { 
618         *(uint8_t *)dest = v3_inb(port);
619
620         PrintDebug(core->vm_info, core, "keyboard: read of 0x%x from 80h\n", *((uint8_t*)dest));
621
622         return 1;
623     } else {
624         PrintDebug(core->vm_info, core, "keyboard: read of >1 byte from 80h\n");
625
626         return length;
627     }
628 }
629 #endif
630     
631   
632
633
634
635 static int keyboard_write_command(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
636     struct keyboard_internal * kbd = priv_data;
637     uint8_t cmd = *(uint8_t *)src;
638
639     // Should always be single byte write
640     if (length != 1) { 
641         PrintError(core->vm_info, core, "keyboard: write of >1 bytes (%d) to 64h\n", length);
642         return -1;
643     }
644
645
646     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
647
648     if (kbd->state != NORMAL) { 
649         PrintDebug(core->vm_info, core, "keyboard: warning - receiving command on 64h but state != NORMAL\n");
650     }
651   
652     PrintDebug(core->vm_info, core, "keyboard: command 0x%x on 64h\n", cmd);
653
654     switch (cmd) { 
655         case 0x20:  // READ COMMAND BYTE (returned in 60h)
656             push_to_output_queue(kbd, kbd->cmd.val, COMMAND, KEYBOARD);
657             PrintDebug(core->vm_info, core, "keyboard: command byte 0x%x returned\n", kbd->cmd.val);
658             break;
659
660         case 0x60:  // WRITE COMMAND BYTE (read from 60h)
661             kbd->state = WRITING_CMD_BYTE; // we need to make sure we send the next 0x60 byte appropriately
662             PrintDebug(core->vm_info, core, "keyboard: prepare to write command byte\n");
663             break;
664
665             // case 0x90-9f - write to output port  (?)
666
667         case 0xa1: // Get version number
668             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
669             PrintDebug(core->vm_info, core, "keyboard: version number 0x0 returned\n");
670             break;
671
672         case 0xa4:  // is password installed?  send result to 0x60
673             // we don't support passwords
674             push_to_output_queue(kbd, 0xf1, COMMAND, KEYBOARD);
675             PrintDebug(core->vm_info, core, "keyboard: password not installed\n");
676             break;
677
678         case 0xa5:  // new password will arrive on 0x60
679             kbd->state = TRANSMIT_PASSWD;
680             PrintDebug(core->vm_info, core, "keyboard: pepare to transmit password\n");
681             break;
682
683         case 0xa6:  // check passwd;
684             // since we do not support passwords, we will simply ignore this
685             // the implication is that any password check immediately succeeds 
686             // with a blank password
687             PrintDebug(core->vm_info, core, "keyboard: password check succeeded\n");
688             break;
689
690         case 0xa7:  // disable mouse
691             kbd->cmd.mouse_disable = 1;
692             PrintDebug(core->vm_info, core, "keyboard: mouse disabled\n");
693             break;
694
695         case 0xa8:  // enable mouse
696             kbd->cmd.mouse_disable = 0;
697             PrintDebug(core->vm_info, core, "keyboard: mouse enabled\n");
698             break;
699
700         case 0xa9:  // mouse interface test  (always succeeds)
701             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
702             PrintDebug(core->vm_info, core, "keyboard: mouse interface test succeeded\n");
703             break;
704
705         case 0xaa:  // controller self test (always succeeds)
706             push_to_output_queue(kbd, 0x55, COMMAND, KEYBOARD);
707             PrintDebug(core->vm_info, core, "keyboard: controller self test succeeded\n");
708             break;
709
710         case 0xab:  // keyboard interface test (always succeeds)
711             push_to_output_queue(kbd, 0, COMMAND, KEYBOARD);
712             PrintDebug(core->vm_info, core, "keyboard: keyboard interface test succeeded\n");
713             break;
714
715         case 0xad:  // disable keyboard
716             kbd->cmd.disable = 1;
717             PrintDebug(core->vm_info, core, "keyboard: keyboard disabled\n");
718             break;
719
720         case 0xae:  // enable keyboard
721             kbd->cmd.disable = 0;
722             PrintDebug(core->vm_info, core, "keyboard: keyboard enabled\n");
723             break;
724
725         case 0xaf:  // get version
726             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
727             PrintDebug(core->vm_info, core, "keyboard: version 0 returned \n");
728             break;
729
730         case 0xd0: // return microcontroller output on 60h
731             push_to_output_queue(kbd, kbd->output_byte, COMMAND, KEYBOARD);
732             PrintDebug(core->vm_info, core, "keyboard: output byte 0x%x returned\n", kbd->output_byte);
733             break;
734
735         case 0xd1: // request to write next byte on 60h to the microcontroller output port
736             kbd->state = WRITING_OUTPUT_PORT;
737             PrintDebug(core->vm_info, core, "keyboard: prepare to write output byte\n");
738             break;
739
740         case 0xd2:  //  write keyboard buffer (inject key)
741             kbd->state = INJECTING_KEY;
742             PrintDebug(core->vm_info, core, "keyboard: prepare to inject key\n");
743             break;
744
745         case 0xd3: //  write mouse buffer (inject mouse)
746             kbd->state = INJECTING_MOUSE;
747             PrintDebug(core->vm_info, core, "keyboard: prepare to inject mouse\n");
748             break;
749
750         case 0xd4: // write mouse device (command to mouse?)
751             kbd->state = IN_MOUSE;
752             PrintDebug(core->vm_info, core, "keyboard: prepare to inject mouse command\n");
753             break;
754
755         case 0xc0: //  read input port 
756             push_to_output_queue(kbd, kbd->input_byte, COMMAND, KEYBOARD);
757             PrintDebug(core->vm_info, core, "keyboard: input byte 0x%x returned\n", kbd->input_byte);
758             break;
759
760         case 0xc1:  //copy input port lsn to status msn
761             kbd->status.val &= 0x0f;
762             kbd->status.val |= (kbd->input_byte & 0xf) << 4;
763             PrintDebug(core->vm_info, core, "keyboard: copied input byte low 4 bits to status reg hi 4 bits\n");
764             break;
765
766         case 0xc2: // copy input port msn to status msn
767             kbd->status.val &= 0x0f;
768             kbd->status.val |= (kbd->input_byte & 0xf0);
769             PrintDebug(core->vm_info, core, "keyboard: copied input byte hi 4 bits to status reg hi 4 bits\n");
770             break;
771     
772         case 0xe0: // read test port
773             push_to_output_queue(kbd, kbd->output_byte >> 6, COMMAND, KEYBOARD);
774             PrintDebug(core->vm_info, core, "keyboard: read 0x%x from test port\n", kbd->output_byte >> 6);
775             break;
776
777    
778         case 0xf0:   // pulse output port
779         case 0xf1:   // this should pulse 0..3 of cmd_byte on output port 
780         case 0xf2:   // instead of what is currently in output_byte (I think)
781         case 0xf3:   // main effect is taht if bit zero is zero
782         case 0xf4:   // should cause reset
783         case 0xf5:   // I doubt anything more recent than a 286 running 
784         case 0xf6:   // OS2 with the penalty box will care
785         case 0xf7:
786         case 0xf8:
787         case 0xf9:
788         case 0xfa:
789         case 0xfb:
790         case 0xfc:
791         case 0xfd:
792         case 0xfe:
793         case 0xff:
794             PrintDebug(core->vm_info, core, "keyboard: ignoring pulse of 0x%x (low=pulsed) on output port\n", (cmd & 0xf));
795             break;
796    
797             // case ac  diagonstic - returns 16 bytes from keyboard microcontroler on 60h
798         default:
799             PrintDebug(core->vm_info, core, "keyboard: ignoring command (unimplemented)\n");
800             break;
801     }
802
803     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
804
805     return length;
806 }
807
808 static int keyboard_read_status(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
809     struct keyboard_internal * kbd = priv_data;
810
811     if (length != 1) { 
812         PrintError(core->vm_info, core, "keyboard: >1 byte read for status (64h)\n");
813         return -1;
814     }
815
816     PrintDebug(core->vm_info, core, "keyboard: read status (64h): ");
817
818     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
819
820     *(uint8_t *)dest = kbd->status.val;
821
822     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
823     
824     PrintDebug(core->vm_info, core, "0x%x\n", *(uint8_t *)dest);
825     
826     return length;
827 }
828
829 static int keyboard_write_output(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
830     struct keyboard_internal * kbd = priv_data;
831     int ret = length;
832
833     if (length != 1) { 
834         PrintError(core->vm_info, core, "keyboard: write of 60h with >1 byte\n");
835         return -1;
836     }
837
838     uint8_t data = *(uint8_t *)src;
839   
840     PrintDebug(core->vm_info, core, "keyboard: output 0x%x on 60h\n", data);
841
842     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
843
844     switch (kbd->state) {
845         case WRITING_CMD_BYTE:
846             kbd->cmd.val = data;
847             kbd->state = NORMAL;
848             PrintDebug(core->vm_info, core, "keyboard: wrote new command byte 0x%x\n", kbd->cmd.val);
849             break;
850
851         case WRITING_OUTPUT_PORT:
852             kbd->output_byte = data;
853             kbd->state = NORMAL;
854             PrintDebug(core->vm_info, core, "keyboard: wrote new output byte 0x%x\n", kbd->output_byte);
855             break;
856
857         case INJECTING_KEY:
858             push_to_output_queue(kbd, data, COMMAND, KEYBOARD);  // probably should be a call to deliver_key_to_vmm()
859             kbd->state = NORMAL;
860             PrintDebug(core->vm_info, core, "keyboard: injected key 0x%x\n", data);
861             break;
862
863         case INJECTING_MOUSE:
864             push_to_output_queue(kbd, data, DATA, MOUSE);
865             //      PrintDebug(core->vm_info, core, "keyboard: ignoring injected mouse event 0x%x\n", data);
866             PrintDebug(core->vm_info, core, "keyboard: injected mouse event 0x%x\n", data);
867             kbd->state = NORMAL;
868             break;
869
870         case IN_MOUSE:
871             PrintDebug(core->vm_info, core, "keyboard: mouse action: ");
872             if (mouse_write_output(kbd, data)) { 
873                 kbd->state = NORMAL;
874             }
875             PrintDebug(core->vm_info, core, "\n");
876             break;
877
878         case TRANSMIT_PASSWD:
879             if (data) {
880                 //ignore passwd
881                 PrintDebug(core->vm_info, core, "keyboard: ignoring password character 0x%x\n",data);
882             } else {
883                 // end of password
884                 kbd->state = NORMAL;
885                 PrintDebug(core->vm_info, core, "keyboard: done with password\n");
886             }
887             break;
888
889         case SET_LEDS:
890             PrintDebug(core->vm_info, core, "Keyboard: LEDs being set...\n");
891             push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
892             kbd->state = NORMAL;
893             break;
894
895         case SET_RATE:
896             PrintDebug(core->vm_info, core, "Keyboard: Rate being set...\n");
897             push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
898             kbd->state = NORMAL;
899             break;
900
901         case GETSET_SCANCODES:
902             switch (data) {
903                 case 0:
904                     PrintDebug(core->vm_info, core, "Keyboard: scancode set being read\n");
905                     push_to_output_queue(kbd, 0x45 - 2 * kbd->scancode_set, COMMAND, KEYBOARD);
906                     break;
907                 case 1:
908                     PrintError(core->vm_info, core, "keyboard: unsupported scancode set %d selected\n", data);
909                     return -1;
910                 case 2:
911                     PrintDebug(core->vm_info, core, "Keyboard: scancode set being set to %d\n", data);
912                     kbd->scancode_set = data;
913                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
914                     break;
915                 case 3:
916                     /* OpenBSD wants scancode set 3, but falls back to 2 if a
917                      * subsequent read reveals that the request was ignored
918                      */
919                     PrintError(core->vm_info, core, "keyboard: ignoring request for scancode set %d\n", data);
920                     break;
921                 default:
922                     PrintError(core->vm_info, core, "keyboard: unknown scancode set %d selected\n", data);
923                     ret = -1;
924                     break;
925   
926             }
927             kbd->state = NORMAL;
928             break;
929
930         case SET_DEFAULTS:
931             keyboard_reset_device(kbd);
932             kbd->state = NORMAL;
933             break;
934
935         default:
936         case NORMAL: {
937             // command is being sent to keyboard controller
938             switch (data) { 
939                 case 0xff: // reset
940                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD); // ack
941                     push_to_output_queue(kbd, 0xaa, COMMAND, KEYBOARD);
942                     PrintDebug(core->vm_info, core, "keyboard: reset complete and acked\n");
943                     break;
944
945                 case 0xf5: // disable scanning
946                 case 0xf4: // enable scanning
947                     // ack
948                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
949                     // should do something here... PAD
950                     PrintDebug(core->vm_info, core, "keyboard: %s scanning done and acked\n", (data == 0xf5) ? "disable" : "enable");
951                     break;
952
953                 case 0xf3:
954                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
955                     kbd->state = SET_RATE;
956                     break;
957
958                 case 0xf2: // get keyboard ID
959                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
960                     push_to_output_queue(kbd, 0xab, COMMAND, KEYBOARD);
961                     push_to_output_queue(kbd, 0x83, COMMAND, KEYBOARD);
962                     PrintDebug(core->vm_info, core, "Keyboard: Requesting Keyboard ID\n");
963                     break;
964
965                 case 0xed: // enable keyboard LEDs
966                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
967                     kbd->state = SET_LEDS;
968                     break;
969
970                 case 0xee: // echo, used by FreeBSD to probe controller
971                     push_to_output_queue(kbd, 0xee, COMMAND, KEYBOARD);
972                     break;
973
974                 case 0xf0: // get/set scancode set
975                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
976                     kbd->state = GETSET_SCANCODES;
977                     break;
978
979
980                 case 0xf6: // set defaults
981                     // ACK command
982                     // clear output buffer
983                     // reset to init state
984                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
985                     kbd->state = SET_DEFAULTS;
986                     break;
987
988                 case 0xfe: // resend
989                 case 0xfd: // set key type make
990                 case 0xfc: // set key typ make/break
991                 case 0xfb: // set key type typematic
992                 case 0xfa: // set all typematic make/break/typematic
993                 case 0xf9: // set all make
994                 case 0xf8: // set all make/break
995                 case 0xf7: // set all typemaktic
996
997                     PrintError(core->vm_info, core, "keyboard: unhandled known command 0x%x on output buffer (60h)\n", data);
998                     ret = -1;
999                     break;
1000
1001                 default:
1002                     PrintError(core->vm_info, core, "keyboard: unhandled unknown command 0x%x on output buffer (60h)\n", data);
1003                     kbd->status.out_buf_full = 1;
1004                     ret = -1;
1005                     break;
1006             }
1007             break;
1008         }
1009     }
1010   
1011     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
1012
1013     return ret;
1014 }
1015
1016 static int keyboard_read_input(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
1017     struct keyboard_internal * kbd = priv_data;
1018
1019     if (length != 1) {
1020         PrintError(core->vm_info, core, "keyboard: unknown size read from input (60h)\n");
1021         return -1;
1022     }
1023     
1024     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
1025
1026     pull_from_output_queue(kbd, (uint8_t *)dest);
1027       
1028     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
1029
1030     PrintDebug(core->vm_info, core, "keyboard: read from input (60h): 0x%x\n", *(uint8_t *)dest);
1031
1032     return length;
1033 }
1034
1035
1036
1037
1038
1039
1040 static int keyboard_free(struct keyboard_internal * kbd) {
1041     
1042
1043     // unhook host events
1044
1045     V3_Free(kbd);
1046     return 0;
1047 }
1048
1049
1050
1051
1052 static int keyboard_reset_device(struct keyboard_internal * kbd) {
1053   
1054
1055     kbd->mouse_queue.start = 0;
1056     kbd->mouse_queue.end = 0;
1057     kbd->mouse_queue.count = 0;
1058
1059     kbd->kbd_queue.start = 0;
1060     kbd->kbd_queue.end = 0;
1061     kbd->kbd_queue.count = 0;
1062
1063     kbd->mouse_enabled = 0;
1064     kbd->scancode_set = 2;
1065
1066     kbd->state = NORMAL;
1067     kbd->mouse_state = STREAM;
1068
1069     // PS2, keyboard+mouse enabled, generic translation    
1070     kbd->cmd.val = 0;
1071
1072     kbd->cmd.irq_en = 1;
1073     kbd->cmd.mouse_irq_en = 1;
1074     kbd->cmd.self_test_ok = 1;
1075     /** **/
1076
1077
1078     // buffers empty, no errors
1079     kbd->status.val = 0; 
1080
1081     kbd->status.self_test_ok = 1; // self-tests passed
1082     kbd->status.enabled = 1;// keyboard ready
1083     /** **/
1084
1085     
1086     kbd->output_byte = 0;  //  ?
1087
1088     kbd->input_byte = INPUT_RAM;  // we have some
1089     // also display=color, jumper 0, keyboard enabled 
1090
1091     PrintDebug(VM_NONE, VCORE_NONE, "keyboard: reset device\n");
1092  
1093     return 0;
1094
1095 }
1096
1097 #ifdef V3_CONFIG_CHECKPOINT
1098 static int keyboard_save(struct v3_chkpt_ctx * ctx, void * private_data) {
1099     struct keyboard_internal * kbd = (struct keyboard_internal *)private_data;
1100
1101     V3_CHKPT_SAVE(ctx, "CMD_REG", kbd->cmd.val, savefailout);
1102     V3_CHKPT_SAVE(ctx, "STATUS_REG", kbd->status.val, savefailout);
1103     V3_CHKPT_SAVE(ctx, "STATE", kbd->state, savefailout);
1104     V3_CHKPT_SAVE(ctx, "MOUSE_STATE", kbd->mouse_state, savefailout);
1105     V3_CHKPT_SAVE(ctx, "OUTPUT", kbd->output_byte, savefailout);
1106     V3_CHKPT_SAVE(ctx, "INPUT", kbd->input_byte, savefailout);
1107     V3_CHKPT_SAVE(ctx, "SCANCODE_SET", kbd->scancode_set, savefailout);
1108     V3_CHKPT_SAVE(ctx, "MOUSE_ENABLED", kbd->mouse_enabled, savefailout);
1109
1110
1111     return 0;
1112
1113  savefailout:
1114     PrintError(VM_NONE, VCORE_NONE, "Failed to save keyboard\n");
1115     return -1;
1116
1117 }
1118
1119
1120 static int keyboard_load(struct v3_chkpt_ctx * ctx, void * private_data) {
1121     struct keyboard_internal * kbd = (struct keyboard_internal *)private_data;
1122     keyboard_reset_device(kbd);
1123
1124     V3_CHKPT_LOAD(ctx, "CMD_REG", kbd->cmd.val, loadfailout);
1125     V3_CHKPT_LOAD(ctx, "STATUS_REG", kbd->status.val, loadfailout);
1126     V3_CHKPT_LOAD(ctx, "STATE", kbd->state, loadfailout);
1127     V3_CHKPT_LOAD(ctx, "MOUSE_STATE", kbd->mouse_state, loadfailout);
1128     V3_CHKPT_LOAD(ctx, "OUTPUT", kbd->output_byte, loadfailout);
1129     V3_CHKPT_LOAD(ctx, "INPUT", kbd->input_byte, loadfailout);
1130     V3_CHKPT_LOAD(ctx, "SCANCODE_SET", kbd->scancode_set, loadfailout);
1131     V3_CHKPT_LOAD(ctx, "MOUSE_ENABLED", kbd->mouse_enabled, loadfailout);
1132
1133
1134     return 0;
1135
1136  loadfailout:
1137     PrintError(VM_NONE, VCORE_NONE, "Failed to load keyboard\n");
1138     return -1;
1139 }
1140
1141 #endif
1142
1143
1144 static struct v3_device_ops dev_ops = { 
1145     .free = (int (*)(void *))keyboard_free,
1146 #ifdef V3_CONFIG_CHECKPOINT
1147     .save = keyboard_save,
1148     .load = keyboard_load
1149 #endif
1150 };
1151
1152
1153
1154
1155 static int keyboard_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
1156     struct keyboard_internal * kbd = NULL;
1157     char * dev_id = v3_cfg_val(cfg, "ID");
1158     int ret = 0;
1159
1160     PrintDebug(vm, VCORE_NONE, "keyboard: init_device\n");
1161
1162     kbd = (struct keyboard_internal *)V3_Malloc(sizeof(struct keyboard_internal));
1163
1164     if (!kbd) {
1165         PrintError(vm, VCORE_NONE, "Cannot allocate in init\n");
1166         return -1;
1167     }
1168
1169     memset(kbd, 0, sizeof(struct keyboard_internal));
1170
1171     kbd->vm = vm;
1172
1173     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, kbd);
1174
1175     if (dev == NULL) {
1176         PrintError(vm, VCORE_NONE, "Could not attach device %s\n", dev_id);
1177         V3_Free(kbd);
1178         return -1;
1179     }
1180
1181     keyboard_reset_device(kbd);
1182
1183
1184     v3_lock_init(&(kbd->kb_lock));
1185
1186
1187     // hook ports
1188     ret |= v3_dev_hook_io(dev, KEYBOARD_64H, &keyboard_read_status, &keyboard_write_command);
1189     ret |= v3_dev_hook_io(dev, KEYBOARD_60H, &keyboard_read_input, &keyboard_write_output);
1190
1191     if (ret != 0) {
1192         PrintError(vm, VCORE_NONE, "Error hooking keyboard IO ports\n");
1193         v3_remove_device(dev);
1194         return -1;
1195     }
1196
1197     v3_hook_host_event(vm, HOST_KEYBOARD_EVT, V3_HOST_EVENT_HANDLER(key_event_handler), kbd);
1198     v3_hook_host_event(vm, HOST_MOUSE_EVT, V3_HOST_EVENT_HANDLER(mouse_event_handler), kbd);
1199
1200
1201 #if KEYBOARD_DEBUG_80H
1202     v3_dev_hook_io(dev, KEYBOARD_DELAY_80H, &keyboard_read_delay, &keyboard_write_delay);
1203 #endif
1204
1205   
1206     //
1207     // We do not hook the IRQ here.  Instead, the underlying device driver
1208     // is responsible to call us back
1209     // 
1210
1211     return 0;
1212 }
1213
1214
1215 device_register("KEYBOARD", keyboard_init)