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.


More detailed error handling on msr writes
[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("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("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("generic (%s): writing 0x", state->name);
122
123     for (i = 0; i < length; i++) { 
124         PrintDebug("%x", ((uint8_t *)src)[i]);
125     }
126   
127     PrintDebug(" to port 0x%x ... ", port);
128
129     rc=generic_write_port_passthrough(core,port,src,length,priv_data);
130
131     PrintDebug(" 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("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("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     PrintDebug(" done ... read 0x");
198
199     for (i = 0; i < rc; i++) { 
200         PrintDebug("%x", ((uint8_t *)src)[i]);
201     }
202
203     PrintDebug("\n");
204
205     return rc;
206 }
207
208
209 static int generic_read_port_ignore(struct guest_info * core, uint16_t port, void * src, 
210                                     uint_t length, void * priv_data) {
211
212     memset((uint8_t *)src, 0, length);
213
214     return length;
215 }
216
217 static int generic_read_port_print_and_ignore(struct guest_info * core, uint16_t port, void * src, 
218                                               uint_t length, void * priv_data) {
219    
220 #ifdef V3_CONFIG_DEBUG_GENERIC
221     struct generic_internal *state = (struct generic_internal *) priv_data;
222 #endif
223
224     PrintDebug("generic (%s): reading 0x%x bytes from port 0x%x using %s ...", state->name, length, port,
225                state->forward_type == GENERIC_PHYSICAL ? "physical" :
226                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
227
228
229     memset((uint8_t *)src, 0, length);
230     PrintDebug(" ignored (return zeroed buffer)\n");
231
232     return length;
233 }
234
235 static int generic_write_port_ignore(struct guest_info * core, uint16_t port, void * src, 
236                                      uint_t length, void * priv_data) {
237
238     return length;
239 }
240
241 static int generic_write_port_print_and_ignore(struct guest_info * core, uint16_t port, void * src, 
242                                               uint_t length, void * priv_data) {
243     int i;
244
245 #ifdef V3_CONFIG_DEBUG_GENERIC
246     struct generic_internal *state = (struct generic_internal *) priv_data;
247 #endif
248
249     PrintDebug("generic (%s): writing 0x%x bytes to port 0x%x using %s ", state->name, length, port,
250                state->forward_type == GENERIC_PHYSICAL ? "physical" :
251                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
252     
253     memset((uint8_t *)src, 0, length);
254     PrintDebug(" ignored - data was: 0x");
255
256     for (i = 0; i < length; i++) { 
257         PrintDebug("%x", ((uint8_t *)src)[i]);
258     }
259     
260     PrintDebug("\n");
261
262     return length;
263 }
264
265
266
267 static int generic_write_mem_passthrough(struct guest_info * core, 
268                                          addr_t              gpa,
269                                          void              * src,
270                                          uint_t              len,
271                                          void              * priv)
272 {
273     struct vm_device *dev = (struct vm_device *) priv;
274     struct generic_internal *state = (struct generic_internal *) dev->private_data;
275     
276     switch (state->forward_type) { 
277         case GENERIC_PHYSICAL:
278             memcpy(V3_VAddr((void*)gpa),src,len);
279             return len;
280             break;
281 #ifdef V3_CONFIG_HOST_DEVICE
282         case GENERIC_HOST:
283             if (state->host_dev) { 
284                 return v3_host_dev_write_mem(state->host_dev,gpa,src,len);
285             } else {
286                 return -1;
287             }
288             break;
289 #endif
290         default:
291             PrintError("generic (%s): unknown forwarding type\n", state->name);
292             return -1;
293             break;
294     }
295 }
296
297 static int generic_write_mem_print_and_passthrough(struct guest_info * core, 
298                                                    addr_t              gpa,
299                                                    void              * src,
300                                                    uint_t              len,
301                                                    void              * priv)
302 {
303 #ifdef V3_CONFIG_DEBUG_GENERIC
304     struct vm_device *dev = (struct vm_device *) priv;
305     struct generic_internal *state = (struct generic_internal *) dev->private_data;
306 #endif
307
308     PrintDebug("generic (%s): writing %u bytes to GPA 0x%p via %s ... ", state->name,
309                len,(void*)gpa,
310                state->forward_type == GENERIC_PHYSICAL ? "physical" : 
311                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
312     
313     int rc = generic_write_mem_passthrough(core,gpa,src,len,priv);
314
315     PrintDebug("done\n");
316     
317     return rc;
318 }
319
320 static int generic_write_mem_ignore(struct guest_info * core, 
321                                     addr_t              gpa,
322                                     void              * src,
323                                     uint_t              len,
324                                     void              * priv)
325 {
326     return len;
327 }
328
329 static int generic_write_mem_print_and_ignore(struct guest_info * core, 
330                                               addr_t              gpa,
331                                               void              * src,
332                                               uint_t              len,
333                                               void              * priv)
334 {
335 #ifdef V3_CONFIG_DEBUG_GENERIC
336     struct vm_device *dev = (struct vm_device *) priv;
337     struct generic_internal *state = (struct generic_internal *) dev->private_data;
338 #endif
339
340     PrintDebug("generic (%s): ignoring write of %u bytes to GPA 0x%p via %s", state->name,
341                len,(void*)gpa,
342                state->forward_type == GENERIC_PHYSICAL ? "physical" : 
343                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
344     
345     return len;
346 }
347
348 static int generic_read_mem_passthrough(struct guest_info * core, 
349                                         addr_t              gpa,
350                                         void              * dst,
351                                         uint_t              len,
352                                         void              * priv)
353 {
354     struct vm_device *dev = (struct vm_device *) priv;
355     struct generic_internal *state = (struct generic_internal *) dev->private_data;
356     
357     switch (state->forward_type) { 
358         case GENERIC_PHYSICAL:
359             memcpy(dst,V3_VAddr((void*)gpa),len);
360             return len;
361             break;
362 #ifdef V3_CONFIG_HOST_DEVICE
363         case GENERIC_HOST:
364             if (state->host_dev) { 
365                 return v3_host_dev_read_mem(state->host_dev,gpa,dst,len);
366             } else {
367                 return -1;
368             }
369             break;
370 #endif
371         default:
372             PrintError("generic (%s): unknown forwarding type\n", state->name);
373             break;
374     }
375     
376     return -1;
377 }
378
379 static int generic_read_mem_print_and_passthrough(struct guest_info * core, 
380                                                   addr_t              gpa,
381                                                   void              * dst,
382                                                   uint_t              len,
383                                                   void              * priv)
384 {
385 #ifdef V3_CONFIG_DEBUG_GENERIC
386     struct vm_device *dev = (struct vm_device *) priv;
387     struct generic_internal *state = (struct generic_internal *) dev->private_data;
388 #endif
389
390     PrintDebug("generic (%s): attempting to read %u bytes from GPA 0x%p via %s ... ", state->name,
391                len,(void*)gpa,
392                state->forward_type == GENERIC_PHYSICAL ? "physical" : 
393                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
394     
395     int rc = generic_read_mem_passthrough(core,gpa,dst,len,priv);
396
397     PrintDebug("done - read %d bytes\n", rc);
398     
399     return rc;
400 }
401
402 static int generic_read_mem_ignore(struct guest_info * core, 
403                                    addr_t              gpa,
404                                    void              * dst,
405                                    uint_t              len,
406                                    void              * priv)
407 {
408 #ifdef V3_CONFIG_DEBUG_GENERIC
409     struct vm_device *dev = (struct vm_device *) priv;
410     struct generic_internal *state = (struct generic_internal *) dev->private_data;
411 #endif
412
413     PrintDebug("generic (%s): ignoring attempt to read %u bytes from GPA 0x%p via %s ... ", state->name,
414                len,(void*)gpa,
415                state->forward_type == GENERIC_PHYSICAL ? "physical" : 
416                state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
417
418     memset((uint8_t *)dst, 0, len);
419
420     PrintDebug("returning zeros\n");
421
422     return len;
423 }
424
425
426 static int generic_read_mem_print_and_ignore(struct guest_info * core, 
427                                              addr_t              gpa,
428                                              void              * dst,
429                                              uint_t              len,
430                                              void              * priv)
431 {
432     memset((uint8_t *)dst, 0, len);
433     return len;
434 }
435
436
437 static int generic_free(struct generic_internal * state) {
438     int i;
439     
440     PrintDebug("generic (%s): deinit_device\n", state->name);
441     
442 #ifdef V3_CONFIG_HOST_DEVICE
443     if (state->host_dev) { 
444         v3_host_dev_close(state->host_dev);
445         state->host_dev=0;
446     }
447 #endif
448     
449     // Note that the device manager handles unhooking the I/O ports
450     // We need to handle unhooking memory regions    
451     for (i=0;i<state->num_mem_hooks;i++) {
452         if (v3_unhook_mem(state->dev->vm,V3_MEM_CORE_ANY,state->mem_hook[i])<0) { 
453             PrintError("generic (%s): unable to unhook memory starting at 0x%p\n", state->name,(void*)(state->mem_hook[i]));
454             return -1;
455         }
456     }
457              
458     V3_Free(state);
459     return 0;
460 }
461
462
463
464
465
466 static struct v3_device_ops dev_ops = { 
467     .free = (int (*)(void *))generic_free, 
468 };
469
470
471
472
473 static int add_port_range(struct vm_device * dev, uint_t start, uint_t end, generic_mode_t mode) {
474     uint_t i = 0;
475
476     struct generic_internal *state = (struct generic_internal *) dev->private_data;
477
478     PrintDebug("generic (%s): adding port range 0x%x to 0x%x as %s\n", state->name,
479                start, end, 
480                (mode == GENERIC_PRINT_AND_PASSTHROUGH) ? "print-and-passthrough" : 
481                (mode == GENERIC_PRINT_AND_IGNORE) ? "print-and-ignore" :
482                (mode == GENERIC_PASSTHROUGH) ? "passthrough" :
483                (mode == GENERIC_IGNORE) ? "ignore" : "UNKNOWN");
484         
485     for (i = start; i <= end; i++) { 
486         switch (mode) { 
487             case GENERIC_PRINT_AND_PASSTHROUGH:
488                 if (v3_dev_hook_io(dev, i, 
489                                    &generic_read_port_print_and_passthrough, 
490                                    &generic_write_port_print_and_passthrough) == -1) { 
491                     PrintError("generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
492                     return -1;
493                 }
494                 break;
495                 
496             case GENERIC_PRINT_AND_IGNORE:
497                 if (v3_dev_hook_io(dev, i, 
498                                    &generic_read_port_print_and_ignore, 
499                                    &generic_write_port_print_and_ignore) == -1) { 
500                     PrintError("generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
501                     return -1;
502                 }
503                 break;
504             case GENERIC_PASSTHROUGH:
505                 if (v3_dev_hook_io(dev, i, 
506                                    &generic_read_port_passthrough, 
507                                    &generic_write_port_passthrough) == -1) { 
508                     PrintError("generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
509                     return -1;
510                 }
511                 break;
512             case  GENERIC_IGNORE:
513                 if (v3_dev_hook_io(dev, i, 
514                                    &generic_read_port_ignore, 
515                                    &generic_write_port_ignore) == -1) { 
516                     PrintError("generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
517                     return -1;
518                 }
519                 break;
520             default:
521                 PrintError("generic (%s): huh?\n", state->name);
522                 break;
523         }
524     }
525     
526     return 0;
527 }
528
529
530 static int add_mem_range(struct vm_device * dev, addr_t start, addr_t end, generic_mode_t mode) {
531
532     struct generic_internal *state = (struct generic_internal *) dev->private_data;
533
534     PrintDebug("generic (%s): adding memory range 0x%p to 0x%p as %s\n", state->name,
535                (void*)start, (void*)end, 
536                (mode == GENERIC_PRINT_AND_PASSTHROUGH) ? "print-and-passthrough" : 
537                (mode == GENERIC_PRINT_AND_IGNORE) ? "print-and-ignore" :
538                (mode == GENERIC_PASSTHROUGH) ? "passthrough" :
539                (mode == GENERIC_IGNORE) ? "ignore" : "UNKNOWN");
540         
541     switch (mode) { 
542         case GENERIC_PRINT_AND_PASSTHROUGH:
543             if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, start, end+1, 
544                                  &generic_read_mem_print_and_passthrough, 
545                                  &generic_write_mem_print_and_passthrough, dev) == -1) { 
546                 PrintError("generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
547                 return -1;
548             }
549             break;
550             
551         case GENERIC_PRINT_AND_IGNORE:
552             if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, start, end+1, 
553                                  &generic_read_mem_print_and_ignore, 
554                                  &generic_write_mem_print_and_ignore, dev) == -1) { 
555                 PrintError("generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
556                 return -1;
557             }
558             break;
559
560         case GENERIC_PASSTHROUGH:
561             if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, start, end+1, 
562                                  &generic_read_mem_passthrough, 
563                                  &generic_write_mem_passthrough, dev) == -1) { 
564                 PrintError("generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
565                 return -1;
566             }
567             break;
568             
569         case  GENERIC_IGNORE:
570             if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, start, end+1, 
571                                  &generic_read_mem_ignore, 
572                                  &generic_write_mem_ignore, dev) == -1) { 
573                 PrintError("generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
574                 return -1;
575             }
576             break;
577         default:
578             PrintError("generic (%s): huh?\n",state->name);
579             break;
580     }
581     
582     return 0;
583 }
584
585
586 #if 1
587
588 //This is a hack for host device testing and will be removed
589
590 static int osdebug_hcall(struct guest_info *core, uint_t hcall_id, void * priv_data) 
591 {
592     struct generic_internal * state = (struct generic_internal *)priv_data;
593
594     int msg_len = core->vm_regs.rcx;
595     addr_t msg_gpa = core->vm_regs.rbx;
596     int buf_is_va = core->vm_regs.rdx;
597     int i;
598     uint8_t c;
599
600     PrintDebug("generic (%s): handling hypercall (len=%d) as sequence of port writes\n",
601                state->name, msg_len);
602
603
604     for (i=0;i<msg_len;i++) { 
605         if (buf_is_va == 1) {
606             if (v3_read_gva_memory(core, msg_gpa+i, 1, &c) != 1) {
607                 PrintError("generic (%s): could not read debug message\n",state->name);
608                 return -1;
609             }
610         } else {
611             if (v3_read_gpa_memory(core, msg_gpa+i, 1, &c) != 1) { 
612                 PrintError("generic (%s): Could not read debug message\n",state->name);
613                 return -1;
614             }
615         }
616         if (generic_write_port_print_and_passthrough(core,0xc0c0,&c,1,priv_data)!=1) { 
617             PrintError("generic (%s): write port passthrough failed\n",state->name);
618             return -1;
619         }
620     }   
621
622     return 0;
623 }
624
625 #endif
626
627
628 /*
629    The device can be used to forward to the underlying physical device 
630    or to a host device that has a given url.   Both memory and ports can be forwarded as
631
632         GENERIC_PASSTHROUGH => send writes and reads to physical device or host
633         GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
634
635         GENERIC_IGNORE => ignore writes and reads
636         GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
637
638
639         The purpose of the "PRINT" variants is to make it easy to spy on
640         device interactions (although you will not see DMA or interrupts)
641
642
643    <device class="generic" id="my_id" 
644          empty | forward="physical_device" or forward="host_device" host_device="url">
645
646   (empty implies physical_dev)
647
648      <ports>
649          <start>portno1</start>
650          <end>portno2</end>   => portno1 through portno2 (inclusive)
651          <mode>PRINT_AND_PASSTHROUGH</mode>  (as above)
652      </ports>
653
654      <memory>
655          <start>gpa1</start>
656          <end>gpa2</end>     => memory addreses gpa1 through gpa2 (inclusive); page granularity
657          <mode> ... as above </mode>
658      </memory>
659
660 */
661
662 static int generic_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
663     struct generic_internal * state = NULL;
664     char * dev_id = v3_cfg_val(cfg, "ID");
665     char * forward = v3_cfg_val(cfg, "forward");
666 #ifdef V3_CONFIG_HOST_DEVICE
667     char * host_dev = v3_cfg_val(cfg, "hostdev");
668 #endif
669     v3_cfg_tree_t * port_cfg = v3_cfg_subtree(cfg, "ports");
670     v3_cfg_tree_t * mem_cfg = v3_cfg_subtree(cfg, "memory");
671
672
673     state = (struct generic_internal *)V3_Malloc(sizeof(struct generic_internal));
674
675     if (state == NULL) {
676         PrintError("generic (%s): could not allocate generic state\n",dev_id);
677         return -1;
678     }
679     
680     memset(state, 0, sizeof(struct generic_internal));
681     strncpy(state->name,dev_id,MAX_NAME);
682
683     if (!forward) { 
684         state->forward_type=GENERIC_PHYSICAL;
685     } else {
686         if (!strcasecmp(forward,"physical_device")) { 
687             state->forward_type=GENERIC_PHYSICAL;
688         } else if (!strcasecmp(forward,"host_device")) { 
689 #ifdef V3_CONFIG_HOST_DEVICE
690             state->forward_type=GENERIC_HOST;
691 #else
692             PrintError("generic (%s): cannot configure host device since host device support is not built in\n", state->name);
693             V3_Free(state);
694             return -1;
695 #endif
696         } else {
697             PrintError("generic (%s): unknown forwarding type \"%s\"\n", state->name, forward);
698             V3_Free(state);
699             return -1;
700         }
701     }
702     
703     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, state);
704
705     if (dev == NULL) {
706         PrintError("generic: could not attach device %s\n", state->name);
707         V3_Free(state);
708         return -1;
709     }
710
711     state->dev=dev;
712
713
714 #ifdef V3_CONFIG_HOST_DEVICE
715     if (state->forward_type==GENERIC_HOST) { 
716         if (!host_dev) { 
717             PrintError("generic (%s): host forwarding requested, but no host device given\n", state->name);
718             v3_remove_device(dev);
719             return -1;
720         } else {
721             state->host_dev = v3_host_dev_open(host_dev,V3_BUS_CLASS_DIRECT,dev,vm);
722             if (!(state->host_dev)) { 
723                 PrintError("generic (%s): unable to open host device \"%s\"\n", state->name,host_dev);
724                 v3_remove_device(dev);
725                 return -1;
726             } else {
727                 PrintDebug("generic (%s): successfully attached host device \"%s\"\n", state->name,host_dev);
728             }
729         }
730     }
731 #endif
732
733     PrintDebug("generic (%s): init_device\n", state->name);
734
735     // scan port list....
736     while (port_cfg) {
737         uint16_t start = atox(v3_cfg_val(port_cfg, "start"));
738         uint16_t end = atox(v3_cfg_val(port_cfg, "end"));
739         char * mode_str = v3_cfg_val(port_cfg, "mode");
740         generic_mode_t mode = GENERIC_IGNORE;
741         if (strcasecmp(mode_str, "print_and_ignore") == 0) {
742             mode = GENERIC_PRINT_AND_IGNORE;
743         } else if (strcasecmp(mode_str, "print_and_passthrough") == 0) {
744             mode = GENERIC_PRINT_AND_PASSTHROUGH;
745         } else if (strcasecmp(mode_str, "passthrough") == 0) {
746             mode = GENERIC_PASSTHROUGH;
747         } else if (strcasecmp(mode_str, "ignore") == 0) {
748             mode = GENERIC_IGNORE;
749         } else {
750             PrintError("generic (%s): invalid mode %s in adding ports\n", state->name, mode_str);
751             v3_remove_device(dev);
752             return -1;
753         }
754         
755         
756         if (add_port_range(dev, start, end, mode) == -1) {
757             PrintError("generic (%s): could not add port range 0x%x to 0x%x\n", state->name, start, end);
758             v3_remove_device(dev);
759             return -1;
760         }
761
762         port_cfg = v3_cfg_next_branch(port_cfg);
763     }
764
765     // scan memory list....
766     while (mem_cfg) {
767         addr_t  start = atox(v3_cfg_val(mem_cfg, "start"));
768         addr_t end = atox(v3_cfg_val(mem_cfg, "end"));
769         char * mode_str = v3_cfg_val(mem_cfg, "mode");
770         generic_mode_t mode = GENERIC_IGNORE;
771
772         if (strcasecmp(mode_str, "print_and_ignore") == 0) {
773             mode = GENERIC_PRINT_AND_IGNORE;
774         } else if (strcasecmp(mode_str, "print_and_passthrough") == 0) {
775             mode = GENERIC_PRINT_AND_PASSTHROUGH;
776         } else if (strcasecmp(mode_str, "passthrough") == 0) {
777             mode = GENERIC_PASSTHROUGH;
778         } else if (strcasecmp(mode_str, "ignore") == 0) {
779             mode = GENERIC_IGNORE;
780         } else {
781             PrintError("generic (%s): invalid mode %s for adding memory\n", state->name, mode_str);
782             v3_remove_device(dev);
783             return -1;
784         }
785
786         if (state->num_mem_hooks>=MAX_MEM_HOOKS) { 
787             PrintError("generic (%s): cannot add another memory hook (increase MAX_MEM_HOOKS)\n", state->name);
788             v3_remove_device(dev);
789             return -1;
790         }
791         
792         if (add_mem_range(dev, start, end, mode) == -1) {
793             PrintError("generic (%s): could not add memory range 0x%p to 0x%p\n", state->name, (void*)start, (void*)end);
794             v3_remove_device(dev);
795             return -1;
796         }
797         
798         state->mem_hook[state->num_mem_hooks] = start;
799         state->num_mem_hooks++;
800
801         mem_cfg = v3_cfg_next_branch(port_cfg);
802     }
803
804 #if 1
805     // hack for os debug testing
806     if (strcasecmp(state->name,"os debug")==0) { 
807         PrintDebug("generic (%s): adding hypercall for os debug device\n", state->name);
808         v3_register_hypercall(vm,0xc0c0,osdebug_hcall,state);
809     }
810 #endif
811     
812     PrintDebug("generic (%s): initialization complete\n", state->name);
813
814     return 0;
815 }
816
817 device_register("GENERIC", generic_init)