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.


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