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.


Cleanup and sanity-checking of switch issues, negative array indexes, operand indepen...
[palacios.git] / palacios / src / devices / generic.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  * Contributor: 2008, Jack Lange <jarusl@cs.northwestern.edu>
16  *        
17  *
18  * This is free software.  You are permitted to use,
19  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
20  */
21
22 #include <palacios/vmm.h>
23 #include <palacios/vmm_types.h>
24 #include <palacios/vmm_list.h>
25 #include <palacios/vmm_io.h>
26 #include <palacios/vmm_dev_mgr.h>
27 #include <palacios/vm_guest_mem.h>
28
29 #ifdef V3_CONFIG_HOST_DEVICE
30 #include <interfaces/vmm_host_dev.h>
31 #endif
32
33 #ifndef V3_CONFIG_DEBUG_GENERIC
34 #undef PrintDebug
35 #define PrintDebug(fmt, args...)
36 #endif
37
38 #define MAX_NAME      32
39 #define MAX_MEM_HOOKS 16
40
41 typedef enum {GENERIC_IGNORE, 
42               GENERIC_PASSTHROUGH, 
43               GENERIC_PRINT_AND_PASSTHROUGH, 
44               GENERIC_PRINT_AND_IGNORE} generic_mode_t;
45
46 struct generic_internal {
47     enum {GENERIC_PHYSICAL, GENERIC_HOST} forward_type;
48 #ifdef V3_CONFIG_HOST_DEVICE
49     v3_host_dev_t                         host_dev;
50 #endif
51     struct vm_device                      *dev; // me
52
53     char                                  name[MAX_NAME];
54     
55     uint32_t                              num_mem_hooks;
56     addr_t                                mem_hook[MAX_MEM_HOOKS];
57 };
58
59
60
61
62 static int generic_write_port_passthrough(struct guest_info * core, 
63                                           uint16_t port, 
64                                           void * src, 
65                                           uint_t length, 
66                                           void * priv_data) 
67 {
68     struct generic_internal *state = (struct generic_internal *) priv_data;
69     uint_t i;
70
71     switch (state->forward_type) { 
72         case GENERIC_PHYSICAL:
73             switch (length) {
74                 case 1:
75                     v3_outb(port, ((uint8_t *)src)[0]);
76                     break;
77                 case 2:
78                     v3_outw(port, ((uint16_t *)src)[0]);
79                     break;
80                 case 4:
81                     v3_outdw(port, ((uint32_t *)src)[0]);
82                     break;
83                 default:
84                     for (i = 0; i < length; i++) { 
85                         v3_outb(port, ((uint8_t *)src)[i]);
86                     }
87                     break;
88             }
89             return length;
90             break;
91 #ifdef V3_CONFIG_HOST_DEVICE
92         case GENERIC_HOST:
93             if (state->host_dev) { 
94                 return v3_host_dev_write_io(state->host_dev,port,src,length);
95             } else {
96                 return -1;
97             }
98             break;
99 #endif
100         default:
101             PrintError(core->vm_info, core, "generic (%s): unknown forwarding type\n", state->name);
102             return -1;
103             break;
104     }
105 }
106
107 static int generic_write_port_print_and_passthrough(struct guest_info * core, uint16_t port, void * src, 
108                                                     uint_t length, void * priv_data) {
109     uint_t i;
110     int rc;
111
112 #ifdef V3_CONFIG_DEBUG_GENERIC
113     struct generic_internal *state = (struct generic_internal *) priv_data;
114 #endif
115
116     PrintDebug(core->vm_info, core, "generic (%s): writing 0x%x bytes to port 0x%x using %s ...", state->name,
117                length, port,
118                state->forward_type == GENERIC_PHYSICAL ? "physical" :
119                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
120
121     PrintDebug(core->vm_info, core, "generic (%s): writing 0x", state->name);
122
123     for (i = 0; i < length; i++) { 
124         PrintDebug(core->vm_info, core, "%x", ((uint8_t *)src)[i]);
125     }
126   
127     PrintDebug(core->vm_info, core, " to port 0x%x ... ", port);
128
129     rc=generic_write_port_passthrough(core,port,src,length,priv_data);
130
131     PrintDebug(core->vm_info, core, " done\n");
132   
133     return rc;
134 }
135
136 static int generic_read_port_passthrough(struct guest_info * core, 
137                                          uint16_t port, 
138                                          void * dst, 
139                                          uint_t length, 
140                                          void * priv_data) 
141 {
142     struct generic_internal *state = (struct generic_internal *) priv_data;
143
144     uint_t i;
145
146     switch (state->forward_type) { 
147         case GENERIC_PHYSICAL:
148             switch (length) {
149                 case 1:
150                     ((uint8_t *)dst)[0] = v3_inb(port);
151                     break;
152                 case 2:
153                     ((uint16_t *)dst)[0] = v3_inw(port);
154                     break;
155                 case 4:
156                     ((uint32_t *)dst)[0] = v3_indw(port);
157                     break;
158                 default:
159                     for (i = 0; i < length; i++) { 
160                         ((uint8_t *)dst)[i] = v3_inb(port);
161                     }
162             }
163             return length;
164             break;
165 #ifdef V3_CONFIG_HOST_DEVICE
166         case GENERIC_HOST:
167             if (state->host_dev) { 
168                 return v3_host_dev_read_io(state->host_dev,port,dst,length);
169             }
170             break;
171 #endif
172         default:
173             PrintError(core->vm_info, core, "generic (%s): unknown forwarding type\n", state->name);
174             return -1;
175             break;
176     }
177
178     return -1;
179 }
180
181 static int generic_read_port_print_and_passthrough(struct guest_info * core, uint16_t port, void * src, 
182                                                    uint_t length, void * priv_data) {
183     uint_t i;
184     int rc;
185
186 #ifdef V3_CONFIG_DEBUG_GENERIC
187     struct generic_internal *state = (struct generic_internal *) priv_data;
188 #endif
189
190     PrintDebug(core->vm_info, core, "generic (%s): reading 0x%x bytes from port 0x%x using %s ...", state->name, length, port,
191                state->forward_type == GENERIC_PHYSICAL ? "physical" :
192                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
193
194
195     rc=generic_read_port_passthrough(core,port,src,length,priv_data);
196
197     if (rc<0) { 
198         PrintError(core->vm_info, core, "FAILED\n");
199         return rc;
200     } else {
201         PrintDebug(core->vm_info, core, " done ... read 0x");
202         
203         for (i = 0; i < rc; i++) { 
204             PrintDebug(core->vm_info, core, "%x", ((uint8_t *)src)[i]);
205         }
206         
207         PrintDebug(core->vm_info, core, "\n");
208     }
209     
210     return rc;
211 }
212
213
214 static int generic_read_port_ignore(struct guest_info * core, uint16_t port, void * src, 
215                                     uint_t length, void * priv_data) {
216
217     memset((uint8_t *)src, 0, length);
218
219     return length;
220 }
221
222 static int generic_read_port_print_and_ignore(struct guest_info * core, uint16_t port, void * src, 
223                                               uint_t length, void * priv_data) {
224    
225 #ifdef V3_CONFIG_DEBUG_GENERIC
226     struct generic_internal *state = (struct generic_internal *) priv_data;
227 #endif
228
229     PrintDebug(core->vm_info, core, "generic (%s): reading 0x%x bytes from port 0x%x using %s ...", state->name, length, port,
230                state->forward_type == GENERIC_PHYSICAL ? "physical" :
231                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
232
233
234     memset((uint8_t *)src, 0, length);
235     PrintDebug(core->vm_info, core, " ignored (return zeroed buffer)\n");
236
237     return length;
238 }
239
240 static int generic_write_port_ignore(struct guest_info * core, uint16_t port, void * src, 
241                                      uint_t length, void * priv_data) {
242
243     return length;
244 }
245
246 static int generic_write_port_print_and_ignore(struct guest_info * core, uint16_t port, void * src, 
247                                               uint_t length, void * priv_data) {
248     int i;
249
250 #ifdef V3_CONFIG_DEBUG_GENERIC
251     struct generic_internal *state = (struct generic_internal *) priv_data;
252 #endif
253
254     PrintDebug(core->vm_info, core, "generic (%s): writing 0x%x bytes to port 0x%x using %s ", state->name, length, port,
255                state->forward_type == GENERIC_PHYSICAL ? "physical" :
256                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
257     
258     memset((uint8_t *)src, 0, length);
259     PrintDebug(core->vm_info, core, " ignored - data was: 0x");
260
261     for (i = 0; i < length; i++) { 
262         PrintDebug(core->vm_info, core, "%x", ((uint8_t *)src)[i]);
263     }
264     
265     PrintDebug(core->vm_info, core, "\n");
266
267     return length;
268 }
269
270
271
272 static int generic_write_mem_passthrough(struct guest_info * core, 
273                                          addr_t              gpa,
274                                          void              * src,
275                                          uint_t              len,
276                                          void              * priv)
277 {
278     struct vm_device *dev = (struct vm_device *) priv;
279     struct generic_internal *state = (struct generic_internal *) dev->private_data;
280     
281     switch (state->forward_type) { 
282         case GENERIC_PHYSICAL:
283             memcpy(V3_VAddr((void*)gpa),src,len);
284             return len;
285             break;
286 #ifdef V3_CONFIG_HOST_DEVICE
287         case GENERIC_HOST:
288             if (state->host_dev) { 
289                 return v3_host_dev_write_mem(state->host_dev,gpa,src,len);
290             } else {
291                 return -1;
292             }
293             break;
294 #endif
295         default:
296             PrintError(core->vm_info, core, "generic (%s): unknown forwarding type\n", state->name);
297             return -1;
298             break;
299     }
300 }
301
302 static int generic_write_mem_print_and_passthrough(struct guest_info * core, 
303                                                    addr_t              gpa,
304                                                    void              * src,
305                                                    uint_t              len,
306                                                    void              * priv)
307 {
308 #ifdef V3_CONFIG_DEBUG_GENERIC
309     struct vm_device *dev = (struct vm_device *) priv;
310     struct generic_internal *state = (struct generic_internal *) dev->private_data;
311 #endif
312
313     PrintDebug(core->vm_info, core, "generic (%s): writing %u bytes to GPA 0x%p via %s ... ", state->name,
314                len,(void*)gpa,
315                state->forward_type == GENERIC_PHYSICAL ? "physical" : 
316                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
317     
318     int rc = generic_write_mem_passthrough(core,gpa,src,len,priv);
319
320     PrintDebug(core->vm_info, core, "done\n");
321     
322     return rc;
323 }
324
325 static int generic_write_mem_ignore(struct guest_info * core, 
326                                     addr_t              gpa,
327                                     void              * src,
328                                     uint_t              len,
329                                     void              * priv)
330 {
331     return len;
332 }
333
334 static int generic_write_mem_print_and_ignore(struct guest_info * core, 
335                                               addr_t              gpa,
336                                               void              * src,
337                                               uint_t              len,
338                                               void              * priv)
339 {
340 #ifdef V3_CONFIG_DEBUG_GENERIC
341     struct vm_device *dev = (struct vm_device *) priv;
342     struct generic_internal *state = (struct generic_internal *) dev->private_data;
343 #endif
344
345     PrintDebug(core->vm_info, core, "generic (%s): ignoring write of %u bytes to GPA 0x%p via %s", state->name,
346                len,(void*)gpa,
347                state->forward_type == GENERIC_PHYSICAL ? "physical" : 
348                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
349     
350     return len;
351 }
352
353 static int generic_read_mem_passthrough(struct guest_info * core, 
354                                         addr_t              gpa,
355                                         void              * dst,
356                                         uint_t              len,
357                                         void              * priv)
358 {
359     struct vm_device *dev = (struct vm_device *) priv;
360     struct generic_internal *state = (struct generic_internal *) dev->private_data;
361     
362     switch (state->forward_type) { 
363         case GENERIC_PHYSICAL:
364             memcpy(dst,V3_VAddr((void*)gpa),len);
365             return len;
366             break;
367 #ifdef V3_CONFIG_HOST_DEVICE
368         case GENERIC_HOST:
369             if (state->host_dev) { 
370                 return v3_host_dev_read_mem(state->host_dev,gpa,dst,len);
371             } else {
372                 return -1;
373             }
374             break;
375 #endif
376         default:
377             PrintError(core->vm_info, core, "generic (%s): unknown forwarding type\n", state->name);
378             break;
379     }
380     
381     return -1;
382 }
383
384 static int generic_read_mem_print_and_passthrough(struct guest_info * core, 
385                                                   addr_t              gpa,
386                                                   void              * dst,
387                                                   uint_t              len,
388                                                   void              * priv)
389 {
390 #ifdef V3_CONFIG_DEBUG_GENERIC
391     struct vm_device *dev = (struct vm_device *) priv;
392     struct generic_internal *state = (struct generic_internal *) dev->private_data;
393 #endif
394
395     PrintDebug(core->vm_info, core, "generic (%s): attempting to read %u bytes from GPA 0x%p via %s ... ", state->name,
396                len,(void*)gpa,
397                state->forward_type == GENERIC_PHYSICAL ? "physical" : 
398                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
399     
400     int rc = generic_read_mem_passthrough(core,gpa,dst,len,priv);
401
402     PrintDebug(core->vm_info, core, "done - read %d bytes\n", rc);
403     
404     return rc;
405 }
406
407 static int generic_read_mem_ignore(struct guest_info * core, 
408                                    addr_t              gpa,
409                                    void              * dst,
410                                    uint_t              len,
411                                    void              * priv)
412 {
413 #ifdef V3_CONFIG_DEBUG_GENERIC
414     struct vm_device *dev = (struct vm_device *) priv;
415     struct generic_internal *state = (struct generic_internal *) dev->private_data;
416 #endif
417
418     PrintDebug(core->vm_info, core, "generic (%s): ignoring attempt to read %u bytes from GPA 0x%p via %s ... ", state->name,
419                len,(void*)gpa,
420                state->forward_type == GENERIC_PHYSICAL ? "physical" : 
421                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
422
423     memset((uint8_t *)dst, 0, len);
424
425     PrintDebug(core->vm_info, core, "returning zeros\n");
426
427     return len;
428 }
429
430
431 static int generic_read_mem_print_and_ignore(struct guest_info * core, 
432                                              addr_t              gpa,
433                                              void              * dst,
434                                              uint_t              len,
435                                              void              * priv)
436 {
437     memset((uint8_t *)dst, 0, len);
438     return len;
439 }
440
441
442 static int generic_free(struct generic_internal * state) {
443     int i;
444     
445     PrintDebug(VM_NONE,VCORE_NONE, "generic (%s): deinit_device\n", state->name);
446     
447 #ifdef V3_CONFIG_HOST_DEVICE
448     if (state->host_dev) { 
449         v3_host_dev_close(state->host_dev);
450         state->host_dev=0;
451     }
452 #endif
453     
454     // Note that the device manager handles unhooking the I/O ports
455     // We need to handle unhooking memory regions    
456     for (i=0;i<state->num_mem_hooks;i++) {
457         if (v3_unhook_mem(state->dev->vm,V3_MEM_CORE_ANY,state->mem_hook[i])<0) { 
458             PrintError(VM_NONE,VCORE_NONE , "generic (%s): unable to unhook memory starting at 0x%p\n", state->name,(void*)(state->mem_hook[i]));
459             return -1;
460         }
461     }
462              
463     V3_Free(state);
464     return 0;
465 }
466
467
468
469
470
471 static struct v3_device_ops dev_ops = { 
472     .free = (int (*)(void *))generic_free, 
473 };
474
475
476
477
478 static int add_port_range(struct vm_device * dev, uint_t start, uint_t end, generic_mode_t mode) {
479     uint_t i = 0;
480
481     struct generic_internal *state = (struct generic_internal *) dev->private_data;
482
483     PrintDebug(VM_NONE, VCORE_NONE, "generic (%s): adding port range 0x%x to 0x%x as %s\n", state->name,
484                start, end, 
485                (mode == GENERIC_PRINT_AND_PASSTHROUGH) ? "print-and-passthrough" : 
486                (mode == GENERIC_PRINT_AND_IGNORE) ? "print-and-ignore" :
487                (mode == GENERIC_PASSTHROUGH) ? "passthrough" :
488                (mode == GENERIC_IGNORE) ? "ignore" : "UNKNOWN");
489         
490     for (i = start; i <= end; i++) { 
491         switch (mode) { 
492             case GENERIC_PRINT_AND_PASSTHROUGH:
493                 if (v3_dev_hook_io(dev, i, 
494                                    &generic_read_port_print_and_passthrough, 
495                                    &generic_write_port_print_and_passthrough) == -1) { 
496                     PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
497                     return -1;
498                 }
499                 break;
500                 
501             case GENERIC_PRINT_AND_IGNORE:
502                 if (v3_dev_hook_io(dev, i, 
503                                    &generic_read_port_print_and_ignore, 
504                                    &generic_write_port_print_and_ignore) == -1) { 
505                     PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
506                     return -1;
507                 }
508                 break;
509             case GENERIC_PASSTHROUGH:
510                 if (v3_dev_hook_io(dev, i, 
511                                    &generic_read_port_passthrough, 
512                                    &generic_write_port_passthrough) == -1) { 
513                     PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
514                     return -1;
515                 }
516                 break;
517             case  GENERIC_IGNORE:
518                 if (v3_dev_hook_io(dev, i, 
519                                    &generic_read_port_ignore, 
520                                    &generic_write_port_ignore) == -1) { 
521                     PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
522                     return -1;
523                 }
524                 break;
525             default:
526                 PrintError(VM_NONE, VCORE_NONE, "generic (%s): huh?\n", state->name);
527                 break;
528         }
529     }
530     
531     return 0;
532 }
533
534
535 static int add_mem_range(struct vm_device * dev, addr_t start, addr_t end, generic_mode_t mode) {
536
537     struct generic_internal *state = (struct generic_internal *) dev->private_data;
538
539     PrintDebug(VM_NONE, VCORE_NONE, "generic (%s): adding memory range 0x%p to 0x%p as %s\n", state->name,
540                (void*)start, (void*)end, 
541                (mode == GENERIC_PRINT_AND_PASSTHROUGH) ? "print-and-passthrough" : 
542                (mode == GENERIC_PRINT_AND_IGNORE) ? "print-and-ignore" :
543                (mode == GENERIC_PASSTHROUGH) ? "passthrough" :
544                (mode == GENERIC_IGNORE) ? "ignore" : "UNKNOWN");
545         
546     switch (mode) { 
547         case GENERIC_PRINT_AND_PASSTHROUGH:
548             if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, start, end+1, 
549                                  &generic_read_mem_print_and_passthrough, 
550                                  &generic_write_mem_print_and_passthrough, dev) == -1) { 
551                 PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
552                 return -1;
553             }
554             break;
555             
556         case GENERIC_PRINT_AND_IGNORE:
557             if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, start, end+1, 
558                                  &generic_read_mem_print_and_ignore, 
559                                  &generic_write_mem_print_and_ignore, dev) == -1) { 
560                 PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
561                 return -1;
562             }
563             break;
564
565         case GENERIC_PASSTHROUGH:
566             if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, start, end+1, 
567                                  &generic_read_mem_passthrough, 
568                                  &generic_write_mem_passthrough, dev) == -1) { 
569                 PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
570                 return -1;
571             }
572             break;
573             
574         case  GENERIC_IGNORE:
575             if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, start, end+1, 
576                                  &generic_read_mem_ignore, 
577                                  &generic_write_mem_ignore, dev) == -1) { 
578                 PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
579                 return -1;
580             }
581             break;
582         default:
583             PrintError(VM_NONE, VCORE_NONE, "generic (%s): huh?\n",state->name);
584             break;
585     }
586     
587     return 0;
588 }
589
590
591 #if 1
592
593 //This is a hack for host device testing and will be removed
594
595 static int osdebug_hcall(struct guest_info *core, uint_t hcall_id, void * priv_data) 
596 {
597     struct generic_internal * state = (struct generic_internal *)priv_data;
598
599     int msg_len = core->vm_regs.rcx;
600     addr_t msg_gpa = core->vm_regs.rbx;
601     int buf_is_va = core->vm_regs.rdx;
602     int i;
603     uint8_t c; 
604     PrintDebug(core->vm_info, core, "generic (%s): handling hypercall (len=%d) as sequence of port writes\n",
605                state->name, msg_len);
606
607
608     for (i=0;i<msg_len;i++) { 
609         if (buf_is_va == 1) {
610             if (v3_read_gva_memory(core, msg_gpa+i, 1, &c) != 1) {
611                 PrintError(core->vm_info, core, "generic (%s): could not read debug message\n",state->name);
612                 return -1;
613             }
614         } else {
615             if (v3_read_gpa_memory(core, msg_gpa+i, 1, &c) != 1) { 
616                 PrintError(core->vm_info, core, "generic (%s): Could not read debug message\n",state->name);
617                 return -1;
618             }
619         }
620         if (generic_write_port_print_and_passthrough(core,0xc0c0,&c,1,priv_data)!=1) { 
621             PrintError(core->vm_info, core, "generic (%s): write port passthrough failed\n",state->name);
622             return -1;
623         }
624     }   
625
626     return 0;
627 }
628
629 #endif
630
631 #ifdef V3_CONFIG_HOST_DEVICE
632 static void generic_intr_update_callback(v3_host_dev_t hdev, v3_guest_dev_t gdev, uint8_t irq, int raise)
633 {
634     if (gdev) { 
635         struct vm_device *dev = (struct vm_device *) gdev;
636         if (raise) { 
637             v3_raise_irq(dev->vm,irq);
638         } else {
639             v3_lower_irq(dev->vm,irq);
640         }
641     }
642 }
643 #endif
644
645 /*
646    The device can be used to forward to the underlying physical device 
647    or to a host device that has a given url.   Both memory and ports can be forwarded as
648
649         GENERIC_PASSTHROUGH => send writes and reads to physical device or host
650         GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
651
652         GENERIC_IGNORE => ignore writes and reads
653         GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
654
655
656         The purpose of the "PRINT" variants is to make it easy to spy on
657         device interactions (although you will not see DMA or interrupts)
658
659
660    <device class="generic" id="my_id" 
661          empty | forward="physical_device" or forward="host_device" hostdev="url">
662
663   (empty implies physical_dev)
664
665      <ports>
666          <start>portno1</start>
667          <end>portno2</end>   => portno1 through portno2 (inclusive)
668          <mode>PRINT_AND_PASSTHROUGH</mode>  (as above)
669      </ports>
670
671      <memory>
672          <start>gpa1</start>
673          <end>gpa2</end>     => memory addreses gpa1 through gpa2 (inclusive); page granularity
674          <mode> ... as above </mode>
675      </memory>
676
677 */
678
679 static int generic_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
680     struct generic_internal * state = NULL;
681     char * dev_id = v3_cfg_val(cfg, "ID");
682     char * forward = v3_cfg_val(cfg, "forward");
683 #ifdef V3_CONFIG_HOST_DEVICE
684     char * host_dev = v3_cfg_val(cfg, "hostdev");
685 #endif
686     v3_cfg_tree_t * port_cfg = v3_cfg_subtree(cfg, "ports");
687     v3_cfg_tree_t * mem_cfg = v3_cfg_subtree(cfg, "memory");
688
689
690     state = (struct generic_internal *)V3_Malloc(sizeof(struct generic_internal));
691
692     if (state == NULL) {
693         PrintError(vm, VCORE_NONE, "generic (%s): could not allocate generic state\n",dev_id);
694         return -1;
695     }
696     
697     memset(state, 0, sizeof(struct generic_internal));
698     strncpy(state->name,dev_id,MAX_NAME);
699     state->name[MAX_NAME-1] = 0;
700
701     if (!forward) { 
702         state->forward_type=GENERIC_PHYSICAL;
703     } else {
704         if (!strcasecmp(forward,"physical_device")) { 
705             state->forward_type=GENERIC_PHYSICAL;
706         } else if (!strcasecmp(forward,"host_device")) { 
707 #ifdef V3_CONFIG_HOST_DEVICE
708             state->forward_type=GENERIC_HOST;
709 #else
710             PrintError(vm, VCORE_NONE, "generic (%s): cannot configure host device since host device support is not built in\n", state->name);
711             V3_Free(state);
712             return -1;
713 #endif
714         } else {
715             PrintError(vm, VCORE_NONE, "generic (%s): unknown forwarding type \"%s\"\n", state->name, forward);
716             V3_Free(state);
717             return -1;
718         }
719     }
720     
721     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, state);
722
723     if (dev == NULL) {
724         PrintError(vm, VCORE_NONE, "generic: could not attach device %s\n", state->name);
725         V3_Free(state);
726         return -1;
727     }
728
729     state->dev=dev;
730
731
732 #ifdef V3_CONFIG_HOST_DEVICE
733     if (state->forward_type==GENERIC_HOST) { 
734         if (!host_dev) { 
735             PrintError(vm, VCORE_NONE, "generic (%s): host forwarding requested, but no host device given\n", state->name);
736             v3_remove_device(dev);
737             return -1;
738         } else {
739             state->host_dev = v3_host_dev_open(host_dev,V3_BUS_CLASS_DIRECT,dev,generic_intr_update_callback,vm);
740             if (!(state->host_dev)) { 
741                 PrintError(vm, VCORE_NONE, "generic (%s): unable to open host device \"%s\"\n", state->name,host_dev);
742                 v3_remove_device(dev);
743                 return -1;
744             } else {
745                 PrintDebug(vm, VCORE_NONE, "generic (%s): successfully attached host device \"%s\"\n", state->name,host_dev);
746             }
747         }
748     }
749 #endif
750
751     PrintDebug(vm, VCORE_NONE, "generic (%s): init_device\n", state->name);
752
753     // scan port list....
754     while (port_cfg) {
755         uint16_t start = atox(v3_cfg_val(port_cfg, "start"));
756         uint16_t end = atox(v3_cfg_val(port_cfg, "end"));
757         char * mode_str = v3_cfg_val(port_cfg, "mode");
758     if (!mode_str) {
759         PrintError(vm, VCORE_NONE, "generic (%s): error getting port mode\n", state->name);
760         return -1;
761     }
762         generic_mode_t mode = GENERIC_IGNORE;
763         if (strcasecmp(mode_str, "print_and_ignore") == 0) {
764             mode = GENERIC_PRINT_AND_IGNORE;
765         } else if (strcasecmp(mode_str, "print_and_passthrough") == 0) {
766             mode = GENERIC_PRINT_AND_PASSTHROUGH;
767         } else if (strcasecmp(mode_str, "passthrough") == 0) {
768             mode = GENERIC_PASSTHROUGH;
769         } else if (strcasecmp(mode_str, "ignore") == 0) {
770             mode = GENERIC_IGNORE;
771         } else {
772             PrintError(vm, VCORE_NONE, "generic (%s): invalid mode %s in adding ports\n", state->name, mode_str);
773             v3_remove_device(dev);
774             return -1;
775         }
776         
777         
778         if (add_port_range(dev, start, end, mode) == -1) {
779             PrintError(vm, VCORE_NONE, "generic (%s): could not add port range 0x%x to 0x%x\n", state->name, start, end);
780             v3_remove_device(dev);
781             return -1;
782         }
783
784         port_cfg = v3_cfg_next_branch(port_cfg);
785     }
786
787     // scan memory list....
788     while (mem_cfg) {
789         addr_t  start = atox(v3_cfg_val(mem_cfg, "start"));
790         addr_t end = atox(v3_cfg_val(mem_cfg, "end"));
791         char * mode_str = v3_cfg_val(mem_cfg, "mode");
792         generic_mode_t mode = GENERIC_IGNORE;
793
794         if (strcasecmp(mode_str, "print_and_ignore") == 0) {
795             mode = GENERIC_PRINT_AND_IGNORE;
796         } else if (strcasecmp(mode_str, "print_and_passthrough") == 0) {
797             mode = GENERIC_PRINT_AND_PASSTHROUGH;
798         } else if (strcasecmp(mode_str, "passthrough") == 0) {
799             mode = GENERIC_PASSTHROUGH;
800         } else if (strcasecmp(mode_str, "ignore") == 0) {
801             mode = GENERIC_IGNORE;
802         } else {
803             PrintError(vm, VCORE_NONE, "generic (%s): invalid mode %s for adding memory\n", state->name, mode_str);
804             v3_remove_device(dev);
805             return -1;
806         }
807
808         if (state->num_mem_hooks>=MAX_MEM_HOOKS) { 
809             PrintError(vm, VCORE_NONE, "generic (%s): cannot add another memory hook (increase MAX_MEM_HOOKS)\n", state->name);
810             v3_remove_device(dev);
811             return -1;
812         }
813         
814         if (add_mem_range(dev, start, end, mode) == -1) {
815             PrintError(vm, VCORE_NONE, "generic (%s): could not add memory range 0x%p to 0x%p\n", state->name, (void*)start, (void*)end);
816             v3_remove_device(dev);
817             return -1;
818         }
819         
820         state->mem_hook[state->num_mem_hooks] = start;
821         state->num_mem_hooks++;
822
823         mem_cfg = v3_cfg_next_branch(port_cfg);
824     }
825
826 #if 1
827     // hack for os debug testing
828     if (strcasecmp(state->name,"os debug")==0) { 
829         PrintDebug(vm, VCORE_NONE, "generic (%s): adding hypercall for os debug device\n", state->name);
830         v3_register_hypercall(vm,0xc0c0,osdebug_hcall,state);
831     }
832 #endif
833     
834     PrintDebug(vm, VCORE_NONE, "generic (%s): initialization complete\n", state->name);
835
836     return 0;
837 }
838
839 device_register("GENERIC", generic_init)