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.


Correct irq behavior for 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
438             if (kbd->cmd.mouse_disable == 0) {
439                 push_to_output_queue(kbd, evt->data[0], DATA, MOUSE);
440                 push_to_output_queue(kbd, evt->data[1], DATA, MOUSE);
441                 push_to_output_queue(kbd, evt->data[2], DATA, MOUSE);
442             }
443             break;
444         default:
445             PrintError(vm, VCORE_NONE, "Invalid mouse state\n");
446             ret = -1;
447             break;
448     }
449
450
451     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
452
453     return ret;
454 }
455
456
457
458
459
460
461
462
463
464
465 static int mouse_write_output(struct keyboard_internal * kbd, uint8_t data) {
466     switch (kbd->mouse_state) { 
467         case NORMAL:
468             switch (data) {
469
470                 case 0xff: //reset
471                     if (kbd->mouse_enabled == 0) {
472                         push_to_output_queue(kbd, 0xfe, DATA, MOUSE) ;   // no mouse!
473                     } else {
474                         push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
475                         push_to_output_queue(kbd, 0xaa, DATA, MOUSE) ; 
476                         push_to_output_queue(kbd, 0x00, DATA, MOUSE) ; 
477                     }
478                     break;
479
480 /*              case 0xfe: //resend */
481 /*                  PushToOutputQueue(kbd, 0xfa, OVERWRITE, DATA, MOUSE) ;  */
482 /*                  PrintDebug(VM_NONE, VCORE_NONE, " mouse resend begins "); */
483 /*                  kbd->mouse_done_after_ack = 0; */
484 /*                  kbd->mouse_needs_ack = 0; */
485 /*                  kbd->mouse_state = STREAM1; */
486 /*                  return 0;  // not done */
487 /*                  break; */
488       
489                 case 0xf6: // set defaults
490                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
491                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set defaults ");
492
493                     break;
494       
495                 case 0xf5: // disable data reporting 
496                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
497                     PrintDebug(VM_NONE, VCORE_NONE, " mouse disable data reporting ");
498                     break;
499       
500                 case 0xf4: // enable data reporting 
501                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
502                     PrintDebug(VM_NONE, VCORE_NONE, " mouse enable data reporting ");
503                     break;
504       
505                 case 0xf3: // set sample rate
506                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
507                     kbd->mouse_state = SAMPLE;
508                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set sample rate begins ");
509                     break;
510       
511                 case 0xf2: // get device id
512                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
513                     push_to_output_queue(kbd, 0x0,  DATA, MOUSE); 
514                     PrintDebug(VM_NONE, VCORE_NONE, " mouse get device id begins ");
515                     break;
516       
517                 case 0xf0: // set remote mode
518                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
519                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set remote mode  ");
520                     break;
521
522                 case 0xee: // set wrap mode
523                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
524                     PrintError(VM_NONE, VCORE_NONE, " mouse set wrap mode (ignored)  ");
525                     break;
526
527                 case 0xec: // reset wrap mode
528                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
529                     PrintError(VM_NONE, VCORE_NONE, " mouse reset wrap mode (ignored)  ");
530                     break;
531
532                 case 0xeb: // read data
533                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
534                     PrintError(VM_NONE, VCORE_NONE, " mouse switch to wrap mode (ignored)  ");
535                     break;
536       
537                 case 0xea: // set stream mode
538                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
539                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set stream mode  ");
540                     break;
541
542                 case 0xe9: // status request
543                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
544                     push_to_output_queue(kbd, 0x00, DATA, MOUSE); 
545                     push_to_output_queue(kbd, 0x00, DATA, MOUSE);
546                     push_to_output_queue(kbd, 0x00, DATA, MOUSE); 
547                     PrintDebug(VM_NONE, VCORE_NONE, " mouse status request begins  ");
548                     break;
549
550                 case 0xe8: // set resolution
551                     push_to_output_queue(kbd, MOUSE_ACK,  DATA, MOUSE) ; 
552                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set resolution begins  ");
553                     kbd->mouse_state = SET_RES;
554                     break;
555
556                 case 0xe7: // set scaling 2:1
557                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
558                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set scaling 2:1 ");
559                     break;
560
561                 case 0xe6: // set scaling 1:1
562                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
563                     PrintDebug(VM_NONE, VCORE_NONE, " mouse set scaling 1:1 ");
564                     break;
565       
566                 default:
567                     PrintDebug(VM_NONE, VCORE_NONE, " receiving unknown mouse command (0x%x) in acceptable kbd ", data);
568                     break;
569             }
570
571             break;
572         case SAMPLE:
573         case SET_RES:
574         default:
575             PrintDebug(VM_NONE, VCORE_NONE, " receiving mouse output in unhandled kbd (0x%x) ", kbd->mouse_state);
576             return -1;
577     }
578
579     return 0;
580 }
581
582
583
584 #if KEYBOARD_DEBUG_80H
585 static int keyboard_write_delay(struct guest_info *core, ushort_t port, void * src,  uint_t length, void * priv_data) {
586
587     if (length == 1) { 
588         PrintDebug(core->vm_info, core, "keyboard: write of 0x%x to 80h\n", *((uint8_t*)src));
589         return 1;
590     } else {
591         PrintDebug(core->vm_info, core, "keyboard: write of >1 byte to 80h\n", *((uint8_t*)src));
592         return length;
593     }
594 }
595
596 static int keyboard_read_delay(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
597
598     if (length == 1) { 
599         *(uint8_t *)dest = v3_inb(port);
600
601         PrintDebug(core->vm_info, core, "keyboard: read of 0x%x from 80h\n", *((uint8_t*)dest));
602
603         return 1;
604     } else {
605         PrintDebug(core->vm_info, core, "keyboard: read of >1 byte from 80h\n");
606
607         return length;
608     }
609 }
610 #endif
611     
612   
613
614
615
616 static int keyboard_write_command(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
617     struct keyboard_internal * kbd = priv_data;
618     uint8_t cmd = *(uint8_t *)src;
619
620     // Should always be single byte write
621     if (length != 1) { 
622         PrintError(core->vm_info, core, "keyboard: write of >1 bytes (%d) to 64h\n", length);
623         return -1;
624     }
625
626
627     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
628
629     if (kbd->state != NORMAL) { 
630         PrintDebug(core->vm_info, core, "keyboard: warning - receiving command on 64h but state != NORMAL\n");
631     }
632   
633     PrintDebug(core->vm_info, core, "keyboard: command 0x%x on 64h\n", cmd);
634
635     switch (cmd) { 
636         case 0x20:  // READ COMMAND BYTE (returned in 60h)
637             push_to_output_queue(kbd, kbd->cmd.val, COMMAND, KEYBOARD);
638             PrintDebug(core->vm_info, core, "keyboard: command byte 0x%x returned\n", kbd->cmd.val);
639             break;
640
641         case 0x60:  // WRITE COMMAND BYTE (read from 60h)
642             kbd->state = WRITING_CMD_BYTE; // we need to make sure we send the next 0x60 byte appropriately
643             PrintDebug(core->vm_info, core, "keyboard: prepare to write command byte\n");
644             break;
645
646             // case 0x90-9f - write to output port  (?)
647
648         case 0xa1: // Get version number
649             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
650             PrintDebug(core->vm_info, core, "keyboard: version number 0x0 returned\n");
651             break;
652
653         case 0xa4:  // is password installed?  send result to 0x60
654             // we don't support passwords
655             push_to_output_queue(kbd, 0xf1, COMMAND, KEYBOARD);
656             PrintDebug(core->vm_info, core, "keyboard: password not installed\n");
657             break;
658
659         case 0xa5:  // new password will arrive on 0x60
660             kbd->state = TRANSMIT_PASSWD;
661             PrintDebug(core->vm_info, core, "keyboard: pepare to transmit password\n");
662             break;
663
664         case 0xa6:  // check passwd;
665             // since we do not support passwords, we will simply ignore this
666             // the implication is that any password check immediately succeeds 
667             // with a blank password
668             PrintDebug(core->vm_info, core, "keyboard: password check succeeded\n");
669             break;
670
671         case 0xa7:  // disable mouse
672             kbd->cmd.mouse_disable = 1;
673             PrintDebug(core->vm_info, core, "keyboard: mouse disabled\n");
674             break;
675
676         case 0xa8:  // enable mouse
677             kbd->cmd.mouse_disable = 0;
678             PrintDebug(core->vm_info, core, "keyboard: mouse enabled\n");
679             break;
680
681         case 0xa9:  // mouse interface test  (always succeeds)
682             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
683             PrintDebug(core->vm_info, core, "keyboard: mouse interface test succeeded\n");
684             break;
685
686         case 0xaa:  // controller self test (always succeeds)
687             push_to_output_queue(kbd, 0x55, COMMAND, KEYBOARD);
688             PrintDebug(core->vm_info, core, "keyboard: controller self test succeeded\n");
689             break;
690
691         case 0xab:  // keyboard interface test (always succeeds)
692             push_to_output_queue(kbd, 0, COMMAND, KEYBOARD);
693             PrintDebug(core->vm_info, core, "keyboard: keyboard interface test succeeded\n");
694             break;
695
696         case 0xad:  // disable keyboard
697             kbd->cmd.disable = 1;
698             PrintDebug(core->vm_info, core, "keyboard: keyboard disabled\n");
699             break;
700
701         case 0xae:  // enable keyboard
702             kbd->cmd.disable = 0;
703             PrintDebug(core->vm_info, core, "keyboard: keyboard enabled\n");
704             break;
705
706         case 0xaf:  // get version
707             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
708             PrintDebug(core->vm_info, core, "keyboard: version 0 returned \n");
709             break;
710
711         case 0xd0: // return microcontroller output on 60h
712             push_to_output_queue(kbd, kbd->output_byte, COMMAND, KEYBOARD);
713             PrintDebug(core->vm_info, core, "keyboard: output byte 0x%x returned\n", kbd->output_byte);
714             break;
715
716         case 0xd1: // request to write next byte on 60h to the microcontroller output port
717             kbd->state = WRITING_OUTPUT_PORT;
718             PrintDebug(core->vm_info, core, "keyboard: prepare to write output byte\n");
719             break;
720
721         case 0xd2:  //  write keyboard buffer (inject key)
722             kbd->state = INJECTING_KEY;
723             PrintDebug(core->vm_info, core, "keyboard: prepare to inject key\n");
724             break;
725
726         case 0xd3: //  write mouse buffer (inject mouse)
727             kbd->state = INJECTING_MOUSE;
728             PrintDebug(core->vm_info, core, "keyboard: prepare to inject mouse\n");
729             break;
730
731         case 0xd4: // write mouse device (command to mouse?)
732             kbd->state = IN_MOUSE;
733             PrintDebug(core->vm_info, core, "keyboard: prepare to inject mouse command\n");
734             break;
735
736         case 0xc0: //  read input port 
737             push_to_output_queue(kbd, kbd->input_byte, COMMAND, KEYBOARD);
738             PrintDebug(core->vm_info, core, "keyboard: input byte 0x%x returned\n", kbd->input_byte);
739             break;
740
741         case 0xc1:  //copy input port lsn to status msn
742             kbd->status.val &= 0x0f;
743             kbd->status.val |= (kbd->input_byte & 0xf) << 4;
744             PrintDebug(core->vm_info, core, "keyboard: copied input byte low 4 bits to status reg hi 4 bits\n");
745             break;
746
747         case 0xc2: // copy input port msn to status msn
748             kbd->status.val &= 0x0f;
749             kbd->status.val |= (kbd->input_byte & 0xf0);
750             PrintDebug(core->vm_info, core, "keyboard: copied input byte hi 4 bits to status reg hi 4 bits\n");
751             break;
752     
753         case 0xe0: // read test port
754             push_to_output_queue(kbd, kbd->output_byte >> 6, COMMAND, KEYBOARD);
755             PrintDebug(core->vm_info, core, "keyboard: read 0x%x from test port\n", kbd->output_byte >> 6);
756             break;
757
758    
759         case 0xf0:   // pulse output port
760         case 0xf1:   // this should pulse 0..3 of cmd_byte on output port 
761         case 0xf2:   // instead of what is currently in output_byte (I think)
762         case 0xf3:   // main effect is taht if bit zero is zero
763         case 0xf4:   // should cause reset
764         case 0xf5:   // I doubt anything more recent than a 286 running 
765         case 0xf6:   // OS2 with the penalty box will care
766         case 0xf7:
767         case 0xf8:
768         case 0xf9:
769         case 0xfa:
770         case 0xfb:
771         case 0xfc:
772         case 0xfd:
773         case 0xfe:
774         case 0xff:
775             PrintDebug(core->vm_info, core, "keyboard: ignoring pulse of 0x%x (low=pulsed) on output port\n", (cmd & 0xf));
776             break;
777    
778             // case ac  diagonstic - returns 16 bytes from keyboard microcontroler on 60h
779         default:
780             PrintDebug(core->vm_info, core, "keyboard: ignoring command (unimplemented)\n");
781             break;
782     }
783
784     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
785
786     return length;
787 }
788
789 static int keyboard_read_status(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
790     struct keyboard_internal * kbd = priv_data;
791
792     if (length != 1) { 
793         PrintError(core->vm_info, core, "keyboard: >1 byte read for status (64h)\n");
794         return -1;
795     }
796
797     PrintDebug(core->vm_info, core, "keyboard: read status (64h): ");
798
799     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
800
801     *(uint8_t *)dest = kbd->status.val;
802
803     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
804     
805     PrintDebug(core->vm_info, core, "0x%x\n", *(uint8_t *)dest);
806     
807     return length;
808 }
809
810 static int keyboard_write_output(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
811     struct keyboard_internal * kbd = priv_data;
812     int ret = length;
813
814     if (length != 1) { 
815         PrintError(core->vm_info, core, "keyboard: write of 60h with >1 byte\n");
816         return -1;
817     }
818
819     uint8_t data = *(uint8_t *)src;
820   
821     PrintDebug(core->vm_info, core, "keyboard: output 0x%x on 60h\n", data);
822
823     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
824
825     switch (kbd->state) {
826         case WRITING_CMD_BYTE:
827             kbd->cmd.val = data;
828             kbd->state = NORMAL;
829             PrintDebug(core->vm_info, core, "keyboard: wrote new command byte 0x%x\n", kbd->cmd.val);
830             break;
831
832         case WRITING_OUTPUT_PORT:
833             kbd->output_byte = data;
834             kbd->state = NORMAL;
835             PrintDebug(core->vm_info, core, "keyboard: wrote new output byte 0x%x\n", kbd->output_byte);
836             break;
837
838         case INJECTING_KEY:
839             push_to_output_queue(kbd, data, COMMAND, KEYBOARD);  // probably should be a call to deliver_key_to_vmm()
840             kbd->state = NORMAL;
841             PrintDebug(core->vm_info, core, "keyboard: injected key 0x%x\n", data);
842             break;
843
844         case INJECTING_MOUSE:
845             push_to_output_queue(kbd, data, DATA, MOUSE);
846             //      PrintDebug(core->vm_info, core, "keyboard: ignoring injected mouse event 0x%x\n", data);
847             PrintDebug(core->vm_info, core, "keyboard: injected mouse event 0x%x\n", data);
848             kbd->state = NORMAL;
849             break;
850
851         case IN_MOUSE:
852             PrintDebug(core->vm_info, core, "keyboard: mouse action: ");
853             if (mouse_write_output(kbd, data)) { 
854                 kbd->state = NORMAL;
855             }
856             PrintDebug(core->vm_info, core, "\n");
857             break;
858
859         case TRANSMIT_PASSWD:
860             if (data) {
861                 //ignore passwd
862                 PrintDebug(core->vm_info, core, "keyboard: ignoring password character 0x%x\n",data);
863             } else {
864                 // end of password
865                 kbd->state = NORMAL;
866                 PrintDebug(core->vm_info, core, "keyboard: done with password\n");
867             }
868             break;
869
870         case SET_LEDS:
871             PrintDebug(core->vm_info, core, "Keyboard: LEDs being set...\n");
872             push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
873             kbd->state = NORMAL;
874             break;
875
876         case SET_RATE:
877             PrintDebug(core->vm_info, core, "Keyboard: Rate being set...\n");
878             push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
879             kbd->state = NORMAL;
880             break;
881
882         case GETSET_SCANCODES:
883             switch (data) {
884                 case 0:
885                     PrintDebug(core->vm_info, core, "Keyboard: scancode set being read\n");
886                     push_to_output_queue(kbd, 0x45 - 2 * kbd->scancode_set, COMMAND, KEYBOARD);
887                     break;
888                 case 1:
889                     PrintError(core->vm_info, core, "keyboard: unsupported scancode set %d selected\n", data);
890                     return -1;
891                 case 2:
892                     PrintDebug(core->vm_info, core, "Keyboard: scancode set being set to %d\n", data);
893                     kbd->scancode_set = data;
894                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
895                     break;
896                 case 3:
897                     /* OpenBSD wants scancode set 3, but falls back to 2 if a
898                      * subsequent read reveals that the request was ignored
899                      */
900                     PrintError(core->vm_info, core, "keyboard: ignoring request for scancode set %d\n", data);
901                     break;
902                 default:
903                     PrintError(core->vm_info, core, "keyboard: unknown scancode set %d selected\n", data);
904                     ret = -1;
905                     break;
906   
907             }
908             kbd->state = NORMAL;
909             break;
910
911         case SET_DEFAULTS:
912             keyboard_reset_device(kbd);
913             kbd->state = NORMAL;
914             break;
915
916         default:
917         case NORMAL: {
918             // command is being sent to keyboard controller
919             switch (data) { 
920                 case 0xff: // reset
921                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD); // ack
922                     push_to_output_queue(kbd, 0xaa, COMMAND, KEYBOARD);
923                     PrintDebug(core->vm_info, core, "keyboard: reset complete and acked\n");
924                     break;
925
926                 case 0xf5: // disable scanning
927                 case 0xf4: // enable scanning
928                     // ack
929                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
930                     // should do something here... PAD
931                     PrintDebug(core->vm_info, core, "keyboard: %s scanning done and acked\n", (data == 0xf5) ? "disable" : "enable");
932                     break;
933
934                 case 0xf3:
935                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
936                     kbd->state = SET_RATE;
937                     break;
938
939                 case 0xf2: // get keyboard ID
940                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
941                     push_to_output_queue(kbd, 0xab, COMMAND, KEYBOARD);
942                     push_to_output_queue(kbd, 0x83, COMMAND, KEYBOARD);
943                     PrintDebug(core->vm_info, core, "Keyboard: Requesting Keyboard ID\n");
944                     break;
945
946                 case 0xed: // enable keyboard LEDs
947                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
948                     kbd->state = SET_LEDS;
949                     break;
950
951                 case 0xee: // echo, used by FreeBSD to probe controller
952                     push_to_output_queue(kbd, 0xee, COMMAND, KEYBOARD);
953                     break;
954
955                 case 0xf0: // get/set scancode set
956                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
957                     kbd->state = GETSET_SCANCODES;
958                     break;
959
960
961                 case 0xf6: // set defaults
962                     // ACK command
963                     // clear output buffer
964                     // reset to init state
965                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
966                     kbd->state = SET_DEFAULTS;
967                     break;
968
969                 case 0xfe: // resend
970                 case 0xfd: // set key type make
971                 case 0xfc: // set key typ make/break
972                 case 0xfb: // set key type typematic
973                 case 0xfa: // set all typematic make/break/typematic
974                 case 0xf9: // set all make
975                 case 0xf8: // set all make/break
976                 case 0xf7: // set all typemaktic
977
978                     PrintError(core->vm_info, core, "keyboard: unhandled known command 0x%x on output buffer (60h)\n", data);
979                     ret = -1;
980                     break;
981
982                 default:
983                     PrintError(core->vm_info, core, "keyboard: unhandled unknown command 0x%x on output buffer (60h)\n", data);
984                     kbd->status.out_buf_full = 1;
985                     ret = -1;
986                     break;
987             }
988             break;
989         }
990     }
991   
992     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
993
994     return ret;
995 }
996
997 static int keyboard_read_input(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
998     struct keyboard_internal * kbd = priv_data;
999
1000     if (length != 1) {
1001         PrintError(core->vm_info, core, "keyboard: unknown size read from input (60h)\n");
1002         return -1;
1003     }
1004     
1005     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
1006
1007     pull_from_output_queue(kbd, (uint8_t *)dest);
1008       
1009     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
1010
1011     PrintDebug(core->vm_info, core, "keyboard: read from input (60h): 0x%x\n", *(uint8_t *)dest);
1012
1013     return length;
1014 }
1015
1016
1017
1018
1019
1020
1021 static int keyboard_free(struct keyboard_internal * kbd) {
1022     
1023
1024     // unhook host events
1025
1026     V3_Free(kbd);
1027     return 0;
1028 }
1029
1030
1031
1032
1033 static int keyboard_reset_device(struct keyboard_internal * kbd) {
1034   
1035
1036     kbd->mouse_queue.start = 0;
1037     kbd->mouse_queue.end = 0;
1038     kbd->mouse_queue.count = 0;
1039
1040     kbd->kbd_queue.start = 0;
1041     kbd->kbd_queue.end = 0;
1042     kbd->kbd_queue.count = 0;
1043
1044     kbd->mouse_enabled = 0;
1045     kbd->scancode_set = 2;
1046
1047     kbd->state = NORMAL;
1048     kbd->mouse_state = STREAM;
1049
1050     // PS2, keyboard+mouse enabled, generic translation    
1051     kbd->cmd.val = 0;
1052
1053     kbd->cmd.irq_en = 1;
1054     kbd->cmd.mouse_irq_en = 1;
1055     kbd->cmd.self_test_ok = 1;
1056     /** **/
1057
1058
1059     // buffers empty, no errors
1060     kbd->status.val = 0; 
1061
1062     kbd->status.self_test_ok = 1; // self-tests passed
1063     kbd->status.enabled = 1;// keyboard ready
1064     /** **/
1065
1066     
1067     kbd->output_byte = 0;  //  ?
1068
1069     kbd->input_byte = INPUT_RAM;  // we have some
1070     // also display=color, jumper 0, keyboard enabled 
1071
1072     PrintDebug(VM_NONE, VCORE_NONE, "keyboard: reset device\n");
1073  
1074     return 0;
1075
1076 }
1077
1078 #ifdef V3_CONFIG_CHECKPOINT
1079 static int keyboard_save(struct v3_chkpt_ctx * ctx, void * private_data) {
1080     struct keyboard_internal * kbd = (struct keyboard_internal *)private_data;
1081
1082     V3_CHKPT_SAVE(ctx, "CMD_REG", kbd->cmd.val, savefailout);
1083     V3_CHKPT_SAVE(ctx, "STATUS_REG", kbd->status.val, savefailout);
1084     V3_CHKPT_SAVE(ctx, "STATE", kbd->state, savefailout);
1085     V3_CHKPT_SAVE(ctx, "MOUSE_STATE", kbd->mouse_state, savefailout);
1086     V3_CHKPT_SAVE(ctx, "OUTPUT", kbd->output_byte, savefailout);
1087     V3_CHKPT_SAVE(ctx, "INPUT", kbd->input_byte, savefailout);
1088     V3_CHKPT_SAVE(ctx, "SCANCODE_SET", kbd->scancode_set, savefailout);
1089     V3_CHKPT_SAVE(ctx, "MOUSE_ENABLED", kbd->mouse_enabled, savefailout);
1090
1091
1092     return 0;
1093
1094  savefailout:
1095     PrintError(VM_NONE, VCORE_NONE, "Failed to save keyboard\n");
1096     return -1;
1097
1098 }
1099
1100
1101 static int keyboard_load(struct v3_chkpt_ctx * ctx, void * private_data) {
1102     struct keyboard_internal * kbd = (struct keyboard_internal *)private_data;
1103     keyboard_reset_device(kbd);
1104
1105     V3_CHKPT_LOAD(ctx, "CMD_REG", kbd->cmd.val, loadfailout);
1106     V3_CHKPT_LOAD(ctx, "STATUS_REG", kbd->status.val, loadfailout);
1107     V3_CHKPT_LOAD(ctx, "STATE", kbd->state, loadfailout);
1108     V3_CHKPT_LOAD(ctx, "MOUSE_STATE", kbd->mouse_state, loadfailout);
1109     V3_CHKPT_LOAD(ctx, "OUTPUT", kbd->output_byte, loadfailout);
1110     V3_CHKPT_LOAD(ctx, "INPUT", kbd->input_byte, loadfailout);
1111     V3_CHKPT_LOAD(ctx, "SCANCODE_SET", kbd->scancode_set, loadfailout);
1112     V3_CHKPT_LOAD(ctx, "MOUSE_ENABLED", kbd->mouse_enabled, loadfailout);
1113
1114
1115     return 0;
1116
1117  loadfailout:
1118     PrintError(VM_NONE, VCORE_NONE, "Failed to load keyboard\n");
1119     return -1;
1120 }
1121
1122 #endif
1123
1124
1125 static struct v3_device_ops dev_ops = { 
1126     .free = (int (*)(void *))keyboard_free,
1127 #ifdef V3_CONFIG_CHECKPOINT
1128     .save = keyboard_save,
1129     .load = keyboard_load
1130 #endif
1131 };
1132
1133
1134
1135
1136 static int keyboard_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
1137     struct keyboard_internal * kbd = NULL;
1138     char * dev_id = v3_cfg_val(cfg, "ID");
1139     int ret = 0;
1140
1141     PrintDebug(vm, VCORE_NONE, "keyboard: init_device\n");
1142
1143     kbd = (struct keyboard_internal *)V3_Malloc(sizeof(struct keyboard_internal));
1144
1145     if (!kbd) {
1146         PrintError(vm, VCORE_NONE, "Cannot allocate in init\n");
1147         return -1;
1148     }
1149
1150     memset(kbd, 0, sizeof(struct keyboard_internal));
1151
1152     kbd->vm = vm;
1153
1154     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, kbd);
1155
1156     if (dev == NULL) {
1157         PrintError(vm, VCORE_NONE, "Could not attach device %s\n", dev_id);
1158         V3_Free(kbd);
1159         return -1;
1160     }
1161
1162     keyboard_reset_device(kbd);
1163
1164
1165     v3_lock_init(&(kbd->kb_lock));
1166
1167
1168     // hook ports
1169     ret |= v3_dev_hook_io(dev, KEYBOARD_64H, &keyboard_read_status, &keyboard_write_command);
1170     ret |= v3_dev_hook_io(dev, KEYBOARD_60H, &keyboard_read_input, &keyboard_write_output);
1171
1172     if (ret != 0) {
1173         PrintError(vm, VCORE_NONE, "Error hooking keyboard IO ports\n");
1174         v3_remove_device(dev);
1175         return -1;
1176     }
1177
1178     v3_hook_host_event(vm, HOST_KEYBOARD_EVT, V3_HOST_EVENT_HANDLER(key_event_handler), kbd);
1179     v3_hook_host_event(vm, HOST_MOUSE_EVT, V3_HOST_EVENT_HANDLER(mouse_event_handler), kbd);
1180
1181
1182 #if KEYBOARD_DEBUG_80H
1183     v3_dev_hook_io(dev, KEYBOARD_DELAY_80H, &keyboard_read_delay, &keyboard_write_delay);
1184 #endif
1185
1186   
1187     //
1188     // We do not hook the IRQ here.  Instead, the underlying device driver
1189     // is responsible to call us back
1190     // 
1191
1192     return 0;
1193 }
1194
1195
1196 device_register("KEYBOARD", keyboard_init)