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.


Lots of pedantic error checking in Palacios proper, especially for memory
[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("keyboard: interrupt 0x%d\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 (state->cmd.irq_en == 1) { 
256             v3_raise_irq(state->vm, irq_num);
257         }
258     }
259
260     return 0;
261 }
262
263
264
265 /* Only one byte is read per irq 
266  * So if the queue is still full after a data read, we re-raise the irq
267  * If we keep reading an empty queue we return the last queue entry
268  */
269
270 static int push_to_output_queue(struct keyboard_internal * state, uint8_t value, uint8_t cmd, uint8_t mouse) {
271     struct queue * q = NULL;
272
273
274     if (mouse) {
275         q = &(state->mouse_queue);
276     } else {
277         q = &(state->kbd_queue);
278     }
279
280     if (q->count >= QUEUE_SIZE) {
281         return 0;
282     }
283
284     if (cmd) {
285         state->status.cmd = 1;
286     } else {
287         state->status.cmd = 0;
288     }
289
290     q->queue[q->end] = value;
291
292     if (q->end >= (QUEUE_SIZE - 1)) {
293         q->end = 0;
294     } else {
295         q->end++;
296     }
297
298     q->count++;
299
300
301     update_kb_irq(state);
302
303     return 0;
304 }
305
306
307
308 static int pull_from_output_queue(struct keyboard_internal * state, uint8_t * value) {
309     struct queue * q = NULL;
310
311     if (state->kbd_queue.count > 0) {
312         q = &(state->kbd_queue);
313         PrintDebug("Reading from Keyboard Queue\n");
314     } else if (state->mouse_queue.count > 0) {
315         q = &(state->mouse_queue);
316         PrintDebug("Reading from Mouse Queue\n");
317     } else {
318         uint8_t idx = state->kbd_queue.start - 1;
319         PrintDebug("No Data in any queue\n");
320         *value = state->kbd_queue.queue[idx];
321         return 0;
322     }
323
324     *value = q->queue[q->start];
325
326     if (q->start >= (QUEUE_SIZE - 1)) {
327         q->start = 0;
328     } else {
329         q->start++;
330     }
331
332     q->count--;
333
334
335     PrintDebug("Read from Queue: %x\n", *value);
336     PrintDebug("QStart=%d, QEnd=%d\n", q->start, q->end);
337
338     update_kb_irq(state);
339
340     return 0;
341 }
342
343
344 #include <palacios/vmm_telemetry.h>
345 #ifdef V3_CONFIG_SYMMOD
346 #include <palacios/vmm_symmod.h>
347 #endif
348
349 static int key_event_handler(struct v3_vm_info * vm, 
350                              struct v3_keyboard_event * evt, 
351                              void * private_data) {
352     struct keyboard_internal * state = (struct keyboard_internal *)private_data;
353
354     PrintDebug("keyboard: injected status 0x%x, and scancode 0x%x\n", evt->status, evt->scan_code);
355
356     if (evt->scan_code == 0x44) { // F10 debug dump
357         int i = 0;
358         for (i = 0; i < vm->num_cores; i++) {
359             v3_print_guest_state(&(vm->cores[i]));
360         }
361         //      PrintGuestPageTables(info, info->shdw_pg_state.guest_cr3);
362     } 
363 #ifdef V3_CONFIG_SYMCALL
364     else if (evt->scan_code == 0x43) { // F9 Sym test
365         struct guest_info * core = &(vm->cores[0]);
366         PrintDebug("Testing sym call\n");
367         sym_arg_t a0 = 0x1111;
368         sym_arg_t a1 = 0x2222;
369         sym_arg_t a2 = 0x3333;
370         sym_arg_t a3 = 0x4444;
371         sym_arg_t a4 = 0x5555;
372         uint64_t call_start = 0;
373         uint64_t call_end = 0;
374         
375         V3_Print("Exits before symcall: %d\n", (uint32_t)core->num_exits);
376
377         rdtscll(call_start);
378         v3_sym_call5(core, SYMCALL_TEST, &a0, &a1, &a2, &a3, &a4);
379         rdtscll(call_end);
380         
381         V3_Print("Symcall latency = %d cycles (%d exits)\n", (uint32_t)(call_end - call_start), (uint32_t)core->num_exits);
382
383         V3_Print("Symcall  Test Returned arg0=%x, arg1=%x, arg2=%x, arg3=%x, arg4=%x\n",
384                  (uint32_t)a0, (uint32_t)a1, (uint32_t)a2, (uint32_t)a3, (uint32_t)a4);
385
386     } 
387 #endif
388     else if (evt->scan_code == 0x42) { // F8 debug toggle
389         extern int v3_dbg_enable;
390         
391         PrintDebug("Toggling Debugging\n");     
392         v3_dbg_enable ^= 1;
393
394     } 
395 #ifdef V3_CONFIG_TELEMETRY
396
397     else if (evt->scan_code == 0x41) { // F7 telemetry dump
398         v3_print_global_telemetry(vm);
399     } 
400 #endif
401 #ifdef V3_CONFIG_SYMMOD
402     else if (evt->scan_code == 0x40) { // F6 Test symmod load
403         v3_load_sym_capsule(vm, "lnx_test");
404     }
405 #endif
406
407
408     addr_t irq_state = v3_lock_irqsave(state->kb_lock);
409
410     if ( (state->status.enabled == 1)      // onboard is enabled
411          && (state->cmd.disable == 0) )  {   // keyboard is enabled
412     
413         push_to_output_queue(state, evt->scan_code, DATA, KEYBOARD);
414     }
415
416     v3_unlock_irqrestore(state->kb_lock, irq_state);
417   
418     return 0;
419 }
420
421
422 static int mouse_event_handler(struct v3_vm_info * vm, 
423                                struct v3_mouse_event * evt, 
424                                void * private_data) {
425     struct keyboard_internal * kbd = (struct keyboard_internal *)private_data;
426     int ret = 0;
427
428     PrintDebug("keyboard: injected mouse packet 0x %x %x %x\n",
429                evt->data[0], evt->data[1], evt->data[2]);
430   
431     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
432
433     switch (kbd->mouse_state) { 
434         case STREAM:
435
436             if (kbd->cmd.mouse_disable == 0) {
437                 push_to_output_queue(kbd, evt->data[0], DATA, MOUSE);
438                 push_to_output_queue(kbd, evt->data[1], DATA, MOUSE);
439                 push_to_output_queue(kbd, evt->data[2], DATA, MOUSE);
440             }
441             break;
442         default:
443             PrintError("Invalid mouse state\n");
444             ret = -1;
445             break;
446     }
447
448
449     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
450
451     return ret;
452 }
453
454
455
456
457
458
459
460
461
462
463 static int mouse_write_output(struct keyboard_internal * kbd, uint8_t data) {
464     switch (kbd->mouse_state) { 
465         case NORMAL:
466             switch (data) {
467
468                 case 0xff: //reset
469                     if (kbd->mouse_enabled == 0) {
470                         push_to_output_queue(kbd, 0xfe, DATA, MOUSE) ;   // no mouse!
471                     } else {
472                         push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
473                         push_to_output_queue(kbd, 0xaa, DATA, MOUSE) ; 
474                         push_to_output_queue(kbd, 0x00, DATA, MOUSE) ; 
475                     }
476                     break;
477
478 /*              case 0xfe: //resend */
479 /*                  PushToOutputQueue(kbd, 0xfa, OVERWRITE, DATA, MOUSE) ;  */
480 /*                  PrintDebug(" mouse resend begins "); */
481 /*                  kbd->mouse_done_after_ack = 0; */
482 /*                  kbd->mouse_needs_ack = 0; */
483 /*                  kbd->mouse_state = STREAM1; */
484 /*                  return 0;  // not done */
485 /*                  break; */
486       
487                 case 0xf6: // set defaults
488                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
489                     PrintDebug(" mouse set defaults ");
490
491                     break;
492       
493                 case 0xf5: // disable data reporting 
494                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
495                     PrintDebug(" mouse disable data reporting ");
496                     break;
497       
498                 case 0xf4: // enable data reporting 
499                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
500                     PrintDebug(" mouse enable data reporting ");
501                     break;
502       
503                 case 0xf3: // set sample rate
504                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
505                     kbd->mouse_state = SAMPLE;
506                     PrintDebug(" mouse set sample rate begins ");
507                     break;
508       
509                 case 0xf2: // get device id
510                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
511                     push_to_output_queue(kbd, 0x0,  DATA, MOUSE); 
512                     PrintDebug(" mouse get device id begins ");
513                     break;
514       
515                 case 0xf0: // set remote mode
516                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
517                     PrintDebug(" mouse set remote mode  ");
518                     break;
519
520                 case 0xee: // set wrap mode
521                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
522                     PrintError(" mouse set wrap mode (ignored)  ");
523                     break;
524
525                 case 0xec: // reset wrap mode
526                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
527                     PrintError(" mouse reset wrap mode (ignored)  ");
528                     break;
529
530                 case 0xeb: // read data
531                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
532                     PrintError(" mouse switch to wrap mode (ignored)  ");
533                     break;
534       
535                 case 0xea: // set stream mode
536                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
537                     PrintDebug(" mouse set stream mode  ");
538                     break;
539
540                 case 0xe9: // status request
541                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
542                     push_to_output_queue(kbd, 0x00, DATA, MOUSE); 
543                     push_to_output_queue(kbd, 0x00, DATA, MOUSE);
544                     push_to_output_queue(kbd, 0x00, DATA, MOUSE); 
545                     PrintDebug(" mouse status request begins  ");
546                     break;
547
548                 case 0xe8: // set resolution
549                     push_to_output_queue(kbd, MOUSE_ACK,  DATA, MOUSE) ; 
550                     PrintDebug(" mouse set resolution begins  ");
551                     kbd->mouse_state = SET_RES;
552                     break;
553
554                 case 0xe7: // set scaling 2:1
555                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
556                     PrintDebug(" mouse set scaling 2:1 ");
557                     break;
558
559                 case 0xe6: // set scaling 1:1
560                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
561                     PrintDebug(" mouse set scaling 1:1 ");
562                     break;
563       
564                 default:
565                     PrintDebug(" receiving unknown mouse command (0x%x) in acceptable kbd ", data);
566                     break;
567             }
568
569             break;
570         case SAMPLE:
571         case SET_RES:
572         default:
573             PrintDebug(" receiving mouse output in unhandled kbd (0x%x) ", kbd->mouse_state);
574             return -1;
575     }
576
577     return 0;
578 }
579
580
581
582 #if KEYBOARD_DEBUG_80H
583 static int keyboard_write_delay(struct guest_info *core, ushort_t port, void * src,  uint_t length, void * priv_data) {
584
585     if (length == 1) { 
586         PrintDebug("keyboard: write of 0x%x to 80h\n", *((uint8_t*)src));
587         return 1;
588     } else {
589         PrintDebug("keyboard: write of >1 byte to 80h\n", *((uint8_t*)src));
590         return length;
591     }
592 }
593
594 static int keyboard_read_delay(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
595
596     if (length == 1) { 
597         *(uint8_t *)dest = v3_inb(port);
598
599         PrintDebug("keyboard: read of 0x%x from 80h\n", *((uint8_t*)dest));
600
601         return 1;
602     } else {
603         PrintDebug("keyboard: read of >1 byte from 80h\n");
604
605         return length;
606     }
607 }
608 #endif
609     
610   
611
612
613
614 static int keyboard_write_command(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
615     struct keyboard_internal * kbd = priv_data;
616     uint8_t cmd = *(uint8_t *)src;
617
618     // Should always be single byte write
619     if (length != 1) { 
620         PrintError("keyboard: write of >1 bytes (%d) to 64h\n", length);
621         return -1;
622     }
623
624
625     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
626
627     if (kbd->state != NORMAL) { 
628         PrintDebug("keyboard: warning - receiving command on 64h but state != NORMAL\n");
629     }
630   
631     PrintDebug("keyboard: command 0x%x on 64h\n", cmd);
632
633     switch (cmd) { 
634         case 0x20:  // READ COMMAND BYTE (returned in 60h)
635             push_to_output_queue(kbd, kbd->cmd.val, COMMAND, KEYBOARD);
636             PrintDebug("keyboard: command byte 0x%x returned\n", kbd->cmd.val);
637             break;
638
639         case 0x60:  // WRITE COMMAND BYTE (read from 60h)
640             kbd->state = WRITING_CMD_BYTE; // we need to make sure we send the next 0x60 byte appropriately
641             PrintDebug("keyboard: prepare to write command byte\n");
642             break;
643
644             // case 0x90-9f - write to output port  (?)
645
646         case 0xa1: // Get version number
647             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
648             PrintDebug("keyboard: version number 0x0 returned\n");
649             break;
650
651         case 0xa4:  // is password installed?  send result to 0x60
652             // we don't support passwords
653             push_to_output_queue(kbd, 0xf1, COMMAND, KEYBOARD);
654             PrintDebug("keyboard: password not installed\n");
655             break;
656
657         case 0xa5:  // new password will arrive on 0x60
658             kbd->state = TRANSMIT_PASSWD;
659             PrintDebug("keyboard: pepare to transmit password\n");
660             break;
661
662         case 0xa6:  // check passwd;
663             // since we do not support passwords, we will simply ignore this
664             // the implication is that any password check immediately succeeds 
665             // with a blank password
666             PrintDebug("keyboard: password check succeeded\n");
667             break;
668
669         case 0xa7:  // disable mouse
670             kbd->cmd.mouse_disable = 1;
671             PrintDebug("keyboard: mouse disabled\n");
672             break;
673
674         case 0xa8:  // enable mouse
675             kbd->cmd.mouse_disable = 0;
676             PrintDebug("keyboard: mouse enabled\n");
677             break;
678
679         case 0xa9:  // mouse interface test  (always succeeds)
680             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
681             PrintDebug("keyboard: mouse interface test succeeded\n");
682             break;
683
684         case 0xaa:  // controller self test (always succeeds)
685             push_to_output_queue(kbd, 0x55, COMMAND, KEYBOARD);
686             PrintDebug("keyboard: controller self test succeeded\n");
687             break;
688
689         case 0xab:  // keyboard interface test (always succeeds)
690             push_to_output_queue(kbd, 0, COMMAND, KEYBOARD);
691             PrintDebug("keyboard: keyboard interface test succeeded\n");
692             break;
693
694         case 0xad:  // disable keyboard
695             kbd->cmd.disable = 1;
696             PrintDebug("keyboard: keyboard disabled\n");
697             break;
698
699         case 0xae:  // enable keyboard
700             kbd->cmd.disable = 0;
701             PrintDebug("keyboard: keyboard enabled\n");
702             break;
703
704         case 0xaf:  // get version
705             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
706             PrintDebug("keyboard: version 0 returned \n");
707             break;
708
709         case 0xd0: // return microcontroller output on 60h
710             push_to_output_queue(kbd, kbd->output_byte, COMMAND, KEYBOARD);
711             PrintDebug("keyboard: output byte 0x%x returned\n", kbd->output_byte);
712             break;
713
714         case 0xd1: // request to write next byte on 60h to the microcontroller output port
715             kbd->state = WRITING_OUTPUT_PORT;
716             PrintDebug("keyboard: prepare to write output byte\n");
717             break;
718
719         case 0xd2:  //  write keyboard buffer (inject key)
720             kbd->state = INJECTING_KEY;
721             PrintDebug("keyboard: prepare to inject key\n");
722             break;
723
724         case 0xd3: //  write mouse buffer (inject mouse)
725             kbd->state = INJECTING_MOUSE;
726             PrintDebug("keyboard: prepare to inject mouse\n");
727             break;
728
729         case 0xd4: // write mouse device (command to mouse?)
730             kbd->state = IN_MOUSE;
731             PrintDebug("keyboard: prepare to inject mouse command\n");
732             break;
733
734         case 0xc0: //  read input port 
735             push_to_output_queue(kbd, kbd->input_byte, COMMAND, KEYBOARD);
736             PrintDebug("keyboard: input byte 0x%x returned\n", kbd->input_byte);
737             break;
738
739         case 0xc1:  //copy input port lsn to status msn
740             kbd->status.val &= 0x0f;
741             kbd->status.val |= (kbd->input_byte & 0xf) << 4;
742             PrintDebug("keyboard: copied input byte low 4 bits to status reg hi 4 bits\n");
743             break;
744
745         case 0xc2: // copy input port msn to status msn
746             kbd->status.val &= 0x0f;
747             kbd->status.val |= (kbd->input_byte & 0xf0);
748             PrintDebug("keyboard: copied input byte hi 4 bits to status reg hi 4 bits\n");
749             break;
750     
751         case 0xe0: // read test port
752             push_to_output_queue(kbd, kbd->output_byte >> 6, COMMAND, KEYBOARD);
753             PrintDebug("keyboard: read 0x%x from test port\n", kbd->output_byte >> 6);
754             break;
755
756    
757         case 0xf0:   // pulse output port
758         case 0xf1:   // this should pulse 0..3 of cmd_byte on output port 
759         case 0xf2:   // instead of what is currently in output_byte (I think)
760         case 0xf3:   // main effect is taht if bit zero is zero
761         case 0xf4:   // should cause reset
762         case 0xf5:   // I doubt anything more recent than a 286 running 
763         case 0xf6:   // OS2 with the penalty box will care
764         case 0xf7:
765         case 0xf8:
766         case 0xf9:
767         case 0xfa:
768         case 0xfb:
769         case 0xfc:
770         case 0xfd:
771         case 0xfe:
772         case 0xff:
773             PrintDebug("keyboard: ignoring pulse of 0x%x (low=pulsed) on output port\n", (cmd & 0xf));
774             break;
775    
776             // case ac  diagonstic - returns 16 bytes from keyboard microcontroler on 60h
777         default:
778             PrintDebug("keyboard: ignoring command (unimplemented)\n");
779             break;
780     }
781
782     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
783
784     return length;
785 }
786
787 static int keyboard_read_status(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
788     struct keyboard_internal * kbd = priv_data;
789
790     if (length != 1) { 
791         PrintError("keyboard: >1 byte read for status (64h)\n");
792         return -1;
793     }
794
795     PrintDebug("keyboard: read status (64h): ");
796
797     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
798
799     *(uint8_t *)dest = kbd->status.val;
800
801     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
802     
803     PrintDebug("0x%x\n", *(uint8_t *)dest);
804     
805     return length;
806 }
807
808 static int keyboard_write_output(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
809     struct keyboard_internal * kbd = priv_data;
810     int ret = length;
811
812     if (length != 1) { 
813         PrintError("keyboard: write of 60h with >1 byte\n");
814         return -1;
815     }
816
817     uint8_t data = *(uint8_t *)src;
818   
819     PrintDebug("keyboard: output 0x%x on 60h\n", data);
820
821     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
822
823     switch (kbd->state) {
824         case WRITING_CMD_BYTE:
825             kbd->cmd.val = data;
826             kbd->state = NORMAL;
827             PrintDebug("keyboard: wrote new command byte 0x%x\n", kbd->cmd.val);
828             break;
829
830         case WRITING_OUTPUT_PORT:
831             kbd->output_byte = data;
832             kbd->state = NORMAL;
833             PrintDebug("keyboard: wrote new output byte 0x%x\n", kbd->output_byte);
834             break;
835
836         case INJECTING_KEY:
837             push_to_output_queue(kbd, data, COMMAND, KEYBOARD);  // probably should be a call to deliver_key_to_vmm()
838             kbd->state = NORMAL;
839             PrintDebug("keyboard: injected key 0x%x\n", data);
840             break;
841
842         case INJECTING_MOUSE:
843             push_to_output_queue(kbd, data, DATA, MOUSE);
844             //      PrintDebug("keyboard: ignoring injected mouse event 0x%x\n", data);
845             PrintDebug("keyboard: injected mouse event 0x%x\n", data);
846             kbd->state = NORMAL;
847             break;
848
849         case IN_MOUSE:
850             PrintDebug("keyboard: mouse action: ");
851             if (mouse_write_output(kbd, data)) { 
852                 kbd->state = NORMAL;
853             }
854             PrintDebug("\n");
855             break;
856
857         case TRANSMIT_PASSWD:
858             if (data) {
859                 //ignore passwd
860                 PrintDebug("keyboard: ignoring password character 0x%x\n",data);
861             } else {
862                 // end of password
863                 kbd->state = NORMAL;
864                 PrintDebug("keyboard: done with password\n");
865             }
866             break;
867
868         case SET_LEDS:
869             PrintDebug("Keyboard: LEDs being set...\n");
870             push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
871             kbd->state = NORMAL;
872             break;
873
874         case SET_RATE:
875             PrintDebug("Keyboard: Rate being set...\n");
876             push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
877             kbd->state = NORMAL;
878             break;
879
880         case GETSET_SCANCODES:
881             switch (data) {
882                 case 0:
883                     PrintDebug("Keyboard: scancode set being read\n");
884                     push_to_output_queue(kbd, 0x45 - 2 * kbd->scancode_set, COMMAND, KEYBOARD);
885                     break;
886                 case 1:
887                     PrintError("keyboard: unsupported scancode set %d selected\n", data);
888                     return -1;
889                 case 2:
890                     PrintDebug("Keyboard: scancode set being set to %d\n", data);
891                     kbd->scancode_set = data;
892                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
893                     break;
894                 case 3:
895                     /* OpenBSD wants scancode set 3, but falls back to 2 if a
896                      * subsequent read reveals that the request was ignored
897                      */
898                     PrintError("keyboard: ignoring request for scancode set %d\n", data);
899                     break;
900                 default:
901                     PrintError("keyboard: unknown scancode set %d selected\n", data);
902                     ret = -1;
903                     break;
904   
905             }
906             kbd->state = NORMAL;
907             break;
908
909         case SET_DEFAULTS:
910             keyboard_reset_device(kbd);
911             kbd->state = NORMAL;
912             break;
913
914         default:
915         case NORMAL: {
916             // command is being sent to keyboard controller
917             switch (data) { 
918                 case 0xff: // reset
919                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD); // ack
920                     push_to_output_queue(kbd, 0xaa, COMMAND, KEYBOARD);
921                     PrintDebug("keyboard: reset complete and acked\n");
922                     break;
923
924                 case 0xf5: // disable scanning
925                 case 0xf4: // enable scanning
926                     // ack
927                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
928                     // should do something here... PAD
929                     PrintDebug("keyboard: %s scanning done and acked\n", (data == 0xf5) ? "disable" : "enable");
930                     break;
931
932                 case 0xf3:
933                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
934                     kbd->state = SET_RATE;
935                     break;
936
937                 case 0xf2: // get keyboard ID
938                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
939                     push_to_output_queue(kbd, 0xab, COMMAND, KEYBOARD);
940                     push_to_output_queue(kbd, 0x83, COMMAND, KEYBOARD);
941                     PrintDebug("Keyboard: Requesting Keyboard ID\n");
942                     break;
943
944                 case 0xed: // enable keyboard LEDs
945                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
946                     kbd->state = SET_LEDS;
947                     break;
948
949                 case 0xee: // echo, used by FreeBSD to probe controller
950                     push_to_output_queue(kbd, 0xee, COMMAND, KEYBOARD);
951                     break;
952
953                 case 0xf0: // get/set scancode set
954                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
955                     kbd->state = GETSET_SCANCODES;
956                     break;
957
958
959                 case 0xf6: // set defaults
960                     // ACK command
961                     // clear output buffer
962                     // reset to init state
963                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
964                     kbd->state = SET_DEFAULTS;
965                     break;
966
967                 case 0xfe: // resend
968                 case 0xfd: // set key type make
969                 case 0xfc: // set key typ make/break
970                 case 0xfb: // set key type typematic
971                 case 0xfa: // set all typematic make/break/typematic
972                 case 0xf9: // set all make
973                 case 0xf8: // set all make/break
974                 case 0xf7: // set all typemaktic
975
976                     PrintError("keyboard: unhandled known command 0x%x on output buffer (60h)\n", data);
977                     ret = -1;
978                     break;
979
980                 default:
981                     PrintError("keyboard: unhandled unknown command 0x%x on output buffer (60h)\n", data);
982                     kbd->status.out_buf_full = 1;
983                     ret = -1;
984                     break;
985             }
986             break;
987         }
988     }
989   
990     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
991
992     return ret;
993 }
994
995 static int keyboard_read_input(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
996     struct keyboard_internal * kbd = priv_data;
997
998     if (length != 1) {
999         PrintError("keyboard: unknown size read from input (60h)\n");
1000         return -1;
1001     }
1002     
1003     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
1004
1005     pull_from_output_queue(kbd, (uint8_t *)dest);
1006       
1007     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
1008
1009     PrintDebug("keyboard: read from input (60h): 0x%x\n", *(uint8_t *)dest);
1010
1011     return length;
1012 }
1013
1014
1015
1016
1017
1018
1019 static int keyboard_free(struct keyboard_internal * kbd) {
1020     
1021
1022     // unhook host events
1023
1024     V3_Free(kbd);
1025     return 0;
1026 }
1027
1028
1029
1030
1031 static int keyboard_reset_device(struct keyboard_internal * kbd) {
1032   
1033
1034     kbd->mouse_queue.start = 0;
1035     kbd->mouse_queue.end = 0;
1036     kbd->mouse_queue.count = 0;
1037
1038     kbd->kbd_queue.start = 0;
1039     kbd->kbd_queue.end = 0;
1040     kbd->kbd_queue.count = 0;
1041
1042     kbd->mouse_enabled = 0;
1043     kbd->scancode_set = 2;
1044
1045     kbd->state = NORMAL;
1046     kbd->mouse_state = STREAM;
1047
1048     // PS2, keyboard+mouse enabled, generic translation    
1049     kbd->cmd.val = 0;
1050
1051     kbd->cmd.irq_en = 1;
1052     kbd->cmd.mouse_irq_en = 1;
1053     kbd->cmd.self_test_ok = 1;
1054     /** **/
1055
1056
1057     // buffers empty, no errors
1058     kbd->status.val = 0; 
1059
1060     kbd->status.self_test_ok = 1; // self-tests passed
1061     kbd->status.enabled = 1;// keyboard ready
1062     /** **/
1063
1064     
1065     kbd->output_byte = 0;  //  ?
1066
1067     kbd->input_byte = INPUT_RAM;  // we have some
1068     // also display=color, jumper 0, keyboard enabled 
1069
1070     PrintDebug("keyboard: reset device\n");
1071  
1072     return 0;
1073
1074 }
1075
1076 #ifdef V3_CONFIG_CHECKPOINT
1077 static int keyboard_save(struct v3_chkpt_ctx * ctx, void * private_data) {
1078     struct keyboard_internal * kbd = (struct keyboard_internal *)private_data;
1079
1080     v3_chkpt_save_8(ctx, "CMD_REG", &(kbd->cmd.val));
1081     v3_chkpt_save_8(ctx, "STATUS_REG", &(kbd->status.val));
1082     v3_chkpt_save_8(ctx, "STATE", &(kbd->state));
1083     v3_chkpt_save_8(ctx, "MOUSE_STATE", &(kbd->mouse_state));
1084     v3_chkpt_save_8(ctx, "OUTPUT", &(kbd->output_byte));
1085     v3_chkpt_save_8(ctx, "INPUT", &(kbd->input_byte));
1086     v3_chkpt_save_8(ctx, "SCANCODE_SET", &(kbd->scancode_set));
1087     v3_chkpt_save_8(ctx, "MOUSE_ENABLED", &(kbd->mouse_enabled));
1088
1089
1090     return 0;
1091 }
1092
1093
1094 static int keyboard_load(struct v3_chkpt_ctx * ctx, void * private_data) {
1095     struct keyboard_internal * kbd = (struct keyboard_internal *)private_data;
1096     keyboard_reset_device(kbd);
1097
1098     v3_chkpt_load_8(ctx, "CMD_REG", &(kbd->cmd.val));
1099     v3_chkpt_load_8(ctx, "STATUS_REG", &(kbd->status.val));
1100     v3_chkpt_load_8(ctx, "STATE", &(kbd->state));
1101     v3_chkpt_load_8(ctx, "MOUSE_STATE", &(kbd->mouse_state));
1102     v3_chkpt_load_8(ctx, "OUTPUT", &(kbd->output_byte));
1103     v3_chkpt_load_8(ctx, "INPUT", &(kbd->input_byte));
1104     v3_chkpt_load_8(ctx, "SCANCODE_SET", &(kbd->scancode_set));
1105     v3_chkpt_load_8(ctx, "MOUSE_ENABLED", &(kbd->mouse_enabled));
1106
1107
1108     return 0;
1109 }
1110
1111 #endif
1112
1113
1114 static struct v3_device_ops dev_ops = { 
1115     .free = (int (*)(void *))keyboard_free,
1116 #ifdef V3_CONFIG_CHECKPOINT
1117     .save = keyboard_save,
1118     .load = keyboard_load
1119 #endif
1120 };
1121
1122
1123
1124
1125 static int keyboard_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
1126     struct keyboard_internal * kbd = NULL;
1127     char * dev_id = v3_cfg_val(cfg, "ID");
1128     int ret = 0;
1129
1130     PrintDebug("keyboard: init_device\n");
1131
1132     kbd = (struct keyboard_internal *)V3_Malloc(sizeof(struct keyboard_internal));
1133
1134     if (!kbd) {
1135         PrintError("Cannot allocate in init\n");
1136         return -1;
1137     }
1138
1139     memset(kbd, 0, sizeof(struct keyboard_internal));
1140
1141     kbd->vm = vm;
1142
1143     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, kbd);
1144
1145     if (dev == NULL) {
1146         PrintError("Could not attach device %s\n", dev_id);
1147         V3_Free(kbd);
1148         return -1;
1149     }
1150
1151     keyboard_reset_device(kbd);
1152
1153
1154     v3_lock_init(&(kbd->kb_lock));
1155
1156
1157     // hook ports
1158     ret |= v3_dev_hook_io(dev, KEYBOARD_64H, &keyboard_read_status, &keyboard_write_command);
1159     ret |= v3_dev_hook_io(dev, KEYBOARD_60H, &keyboard_read_input, &keyboard_write_output);
1160
1161     if (ret != 0) {
1162         PrintError("Error hooking keyboard IO ports\n");
1163         v3_remove_device(dev);
1164         return -1;
1165     }
1166
1167     v3_hook_host_event(vm, HOST_KEYBOARD_EVT, V3_HOST_EVENT_HANDLER(key_event_handler), kbd);
1168     v3_hook_host_event(vm, HOST_MOUSE_EVT, V3_HOST_EVENT_HANDLER(mouse_event_handler), kbd);
1169
1170
1171 #if KEYBOARD_DEBUG_80H
1172     v3_dev_hook_io(dev, KEYBOARD_DELAY_80H, &keyboard_read_delay, &keyboard_write_delay);
1173 #endif
1174
1175   
1176     //
1177     // We do not hook the IRQ here.  Instead, the underlying device driver
1178     // is responsible to call us back
1179     // 
1180
1181     return 0;
1182 }
1183
1184
1185 device_register("KEYBOARD", keyboard_init)