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.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
10 * Copyright (c) 2008, Peter Dinda <pdinda@northwestern.edu>
11 * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
12 * All rights reserved.
14 * Author: Peter Dinda <pdinda@northwestern.edu>
15 * Contributor: 2008, Jack Lange <jarusl@cs.northwestern.edu>
18 * This is free software. You are permitted to use,
19 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
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>
29 #ifdef V3_CONFIG_HOST_DEVICE
30 #include <interfaces/vmm_host_dev.h>
33 #ifndef V3_CONFIG_DEBUG_GENERIC
35 #define PrintDebug(fmt, args...)
39 #define MAX_MEM_HOOKS 16
41 typedef enum {GENERIC_IGNORE,
43 GENERIC_PRINT_AND_PASSTHROUGH,
44 GENERIC_PRINT_AND_IGNORE} generic_mode_t;
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;
51 struct vm_device *dev; // me
55 uint32_t num_mem_hooks;
56 addr_t mem_hook[MAX_MEM_HOOKS];
62 static int generic_write_port_passthrough(struct guest_info * core,
68 struct generic_internal *state = (struct generic_internal *) priv_data;
71 switch (state->forward_type) {
72 case GENERIC_PHYSICAL:
75 v3_outb(port, ((uint8_t *)src)[0]);
78 v3_outw(port, ((uint16_t *)src)[0]);
81 v3_outdw(port, ((uint32_t *)src)[0]);
84 for (i = 0; i < length; i++) {
85 v3_outb(port, ((uint8_t *)src)[i]);
91 #ifdef V3_CONFIG_HOST_DEVICE
93 if (state->host_dev) {
94 return v3_host_dev_write_io(state->host_dev,port,src,length);
101 PrintError(core->vm_info, core, "generic (%s): unknown forwarding type\n", state->name);
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) {
112 #ifdef V3_CONFIG_DEBUG_GENERIC
113 struct generic_internal *state = (struct generic_internal *) priv_data;
116 PrintDebug(core->vm_info, core, "generic (%s): writing 0x%x bytes to port 0x%x using %s ...", state->name,
118 state->forward_type == GENERIC_PHYSICAL ? "physical" :
119 state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
121 PrintDebug(core->vm_info, core, "generic (%s): writing 0x", state->name);
123 for (i = 0; i < length; i++) {
124 PrintDebug(core->vm_info, core, "%x", ((uint8_t *)src)[i]);
127 PrintDebug(core->vm_info, core, " to port 0x%x ... ", port);
129 rc=generic_write_port_passthrough(core,port,src,length,priv_data);
131 PrintDebug(core->vm_info, core, " done\n");
136 static int generic_read_port_passthrough(struct guest_info * core,
142 struct generic_internal *state = (struct generic_internal *) priv_data;
146 switch (state->forward_type) {
147 case GENERIC_PHYSICAL:
150 ((uint8_t *)dst)[0] = v3_inb(port);
153 ((uint16_t *)dst)[0] = v3_inw(port);
156 ((uint32_t *)dst)[0] = v3_indw(port);
159 for (i = 0; i < length; i++) {
160 ((uint8_t *)dst)[i] = v3_inb(port);
165 #ifdef V3_CONFIG_HOST_DEVICE
167 if (state->host_dev) {
168 return v3_host_dev_read_io(state->host_dev,port,dst,length);
173 PrintError(core->vm_info, core, "generic (%s): unknown forwarding type\n", state->name);
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) {
186 #ifdef V3_CONFIG_DEBUG_GENERIC
187 struct generic_internal *state = (struct generic_internal *) priv_data;
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");
195 rc=generic_read_port_passthrough(core,port,src,length,priv_data);
197 PrintDebug(core->vm_info, core, " done ... read 0x");
199 for (i = 0; i < rc; i++) {
200 PrintDebug(core->vm_info, core, "%x", ((uint8_t *)src)[i]);
203 PrintDebug(core->vm_info, core, "\n");
209 static int generic_read_port_ignore(struct guest_info * core, uint16_t port, void * src,
210 uint_t length, void * priv_data) {
212 memset((uint8_t *)src, 0, length);
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) {
220 #ifdef V3_CONFIG_DEBUG_GENERIC
221 struct generic_internal *state = (struct generic_internal *) priv_data;
224 PrintDebug(core->vm_info, core, "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");
229 memset((uint8_t *)src, 0, length);
230 PrintDebug(core->vm_info, core, " ignored (return zeroed buffer)\n");
235 static int generic_write_port_ignore(struct guest_info * core, uint16_t port, void * src,
236 uint_t length, void * priv_data) {
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) {
245 #ifdef V3_CONFIG_DEBUG_GENERIC
246 struct generic_internal *state = (struct generic_internal *) priv_data;
249 PrintDebug(core->vm_info, core, "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");
253 memset((uint8_t *)src, 0, length);
254 PrintDebug(core->vm_info, core, " ignored - data was: 0x");
256 for (i = 0; i < length; i++) {
257 PrintDebug(core->vm_info, core, "%x", ((uint8_t *)src)[i]);
260 PrintDebug(core->vm_info, core, "\n");
267 static int generic_write_mem_passthrough(struct guest_info * core,
273 struct vm_device *dev = (struct vm_device *) priv;
274 struct generic_internal *state = (struct generic_internal *) dev->private_data;
276 switch (state->forward_type) {
277 case GENERIC_PHYSICAL:
278 memcpy(V3_VAddr((void*)gpa),src,len);
281 #ifdef V3_CONFIG_HOST_DEVICE
283 if (state->host_dev) {
284 return v3_host_dev_write_mem(state->host_dev,gpa,src,len);
291 PrintError(core->vm_info, core, "generic (%s): unknown forwarding type\n", state->name);
297 static int generic_write_mem_print_and_passthrough(struct guest_info * core,
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;
308 PrintDebug(core->vm_info, core, "generic (%s): writing %u bytes to GPA 0x%p via %s ... ", state->name,
310 state->forward_type == GENERIC_PHYSICAL ? "physical" :
311 state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
313 int rc = generic_write_mem_passthrough(core,gpa,src,len,priv);
315 PrintDebug(core->vm_info, core, "done\n");
320 static int generic_write_mem_ignore(struct guest_info * core,
329 static int generic_write_mem_print_and_ignore(struct guest_info * core,
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;
340 PrintDebug(core->vm_info, core, "generic (%s): ignoring write of %u bytes to GPA 0x%p via %s", state->name,
342 state->forward_type == GENERIC_PHYSICAL ? "physical" :
343 state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
348 static int generic_read_mem_passthrough(struct guest_info * core,
354 struct vm_device *dev = (struct vm_device *) priv;
355 struct generic_internal *state = (struct generic_internal *) dev->private_data;
357 switch (state->forward_type) {
358 case GENERIC_PHYSICAL:
359 memcpy(dst,V3_VAddr((void*)gpa),len);
362 #ifdef V3_CONFIG_HOST_DEVICE
364 if (state->host_dev) {
365 return v3_host_dev_read_mem(state->host_dev,gpa,dst,len);
372 PrintError(core->vm_info, core, "generic (%s): unknown forwarding type\n", state->name);
379 static int generic_read_mem_print_and_passthrough(struct guest_info * core,
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;
390 PrintDebug(core->vm_info, core, "generic (%s): attempting to read %u bytes from GPA 0x%p via %s ... ", state->name,
392 state->forward_type == GENERIC_PHYSICAL ? "physical" :
393 state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
395 int rc = generic_read_mem_passthrough(core,gpa,dst,len,priv);
397 PrintDebug(core->vm_info, core, "done - read %d bytes\n", rc);
402 static int generic_read_mem_ignore(struct guest_info * core,
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;
413 PrintDebug(core->vm_info, core, "generic (%s): ignoring attempt to read %u bytes from GPA 0x%p via %s ... ", state->name,
415 state->forward_type == GENERIC_PHYSICAL ? "physical" :
416 state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
418 memset((uint8_t *)dst, 0, len);
420 PrintDebug(core->vm_info, core, "returning zeros\n");
426 static int generic_read_mem_print_and_ignore(struct guest_info * core,
432 memset((uint8_t *)dst, 0, len);
437 static int generic_free(struct generic_internal * state) {
440 PrintDebug(VM_NONE,VCORE_NONE, "generic (%s): deinit_device\n", state->name);
442 #ifdef V3_CONFIG_HOST_DEVICE
443 if (state->host_dev) {
444 v3_host_dev_close(state->host_dev);
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(VM_NONE,VCORE_NONE , "generic (%s): unable to unhook memory starting at 0x%p\n", state->name,(void*)(state->mem_hook[i]));
466 static struct v3_device_ops dev_ops = {
467 .free = (int (*)(void *))generic_free,
473 static int add_port_range(struct vm_device * dev, uint_t start, uint_t end, generic_mode_t mode) {
476 struct generic_internal *state = (struct generic_internal *) dev->private_data;
478 PrintDebug(VM_NONE, VCORE_NONE, "generic (%s): adding port range 0x%x to 0x%x as %s\n", state->name,
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");
485 for (i = start; i <= end; i++) {
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(VM_NONE, VCORE_NONE, "generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
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(VM_NONE, VCORE_NONE, "generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
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(VM_NONE, VCORE_NONE, "generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
513 if (v3_dev_hook_io(dev, i,
514 &generic_read_port_ignore,
515 &generic_write_port_ignore) == -1) {
516 PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
521 PrintError(VM_NONE, VCORE_NONE, "generic (%s): huh?\n", state->name);
530 static int add_mem_range(struct vm_device * dev, addr_t start, addr_t end, generic_mode_t mode) {
532 struct generic_internal *state = (struct generic_internal *) dev->private_data;
534 PrintDebug(VM_NONE, VCORE_NONE, "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");
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(VM_NONE, VCORE_NONE, "generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
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(VM_NONE, VCORE_NONE, "generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
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(VM_NONE, VCORE_NONE, "generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
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(VM_NONE, VCORE_NONE, "generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
578 PrintError(VM_NONE, VCORE_NONE, "generic (%s): huh?\n",state->name);
588 //This is a hack for host device testing and will be removed
590 static int osdebug_hcall(struct guest_info *core, uint_t hcall_id, void * priv_data)
592 struct generic_internal * state = (struct generic_internal *)priv_data;
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;
599 PrintDebug(core->vm_info, core, "generic (%s): handling hypercall (len=%d) as sequence of port writes\n",
600 state->name, msg_len);
603 for (i=0;i<msg_len;i++) {
604 if (buf_is_va == 1) {
605 if (v3_read_gva_memory(core, msg_gpa+i, 1, &c) != 1) {
606 PrintError(core->vm_info, core, "generic (%s): could not read debug message\n",state->name);
610 if (v3_read_gpa_memory(core, msg_gpa+i, 1, &c) != 1) {
611 PrintError(core->vm_info, core, "generic (%s): Could not read debug message\n",state->name);
615 if (generic_write_port_print_and_passthrough(core,0xc0c0,&c,1,priv_data)!=1) {
616 PrintError(core->vm_info, core, "generic (%s): write port passthrough failed\n",state->name);
626 #ifdef V3_CONFIG_HOST_DEVICE
627 static void generic_intr_update_callback(v3_host_dev_t hdev, v3_guest_dev_t gdev, uint8_t irq, int raise)
630 struct vm_device *dev = (struct vm_device *) gdev;
632 v3_raise_irq(dev->vm,irq);
634 v3_lower_irq(dev->vm,irq);
641 The device can be used to forward to the underlying physical device
642 or to a host device that has a given url. Both memory and ports can be forwarded as
644 GENERIC_PASSTHROUGH => send writes and reads to physical device or host
645 GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
647 GENERIC_IGNORE => ignore writes and reads
648 GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
651 The purpose of the "PRINT" variants is to make it easy to spy on
652 device interactions (although you will not see DMA or interrupts)
655 <device class="generic" id="my_id"
656 empty | forward="physical_device" or forward="host_device" hostdev="url">
658 (empty implies physical_dev)
661 <start>portno1</start>
662 <end>portno2</end> => portno1 through portno2 (inclusive)
663 <mode>PRINT_AND_PASSTHROUGH</mode> (as above)
668 <end>gpa2</end> => memory addreses gpa1 through gpa2 (inclusive); page granularity
669 <mode> ... as above </mode>
674 static int generic_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
675 struct generic_internal * state = NULL;
676 char * dev_id = v3_cfg_val(cfg, "ID");
677 char * forward = v3_cfg_val(cfg, "forward");
678 #ifdef V3_CONFIG_HOST_DEVICE
679 char * host_dev = v3_cfg_val(cfg, "hostdev");
681 v3_cfg_tree_t * port_cfg = v3_cfg_subtree(cfg, "ports");
682 v3_cfg_tree_t * mem_cfg = v3_cfg_subtree(cfg, "memory");
685 state = (struct generic_internal *)V3_Malloc(sizeof(struct generic_internal));
688 PrintError(vm, VCORE_NONE, "generic (%s): could not allocate generic state\n",dev_id);
692 memset(state, 0, sizeof(struct generic_internal));
693 strncpy(state->name,dev_id,MAX_NAME);
696 state->forward_type=GENERIC_PHYSICAL;
698 if (!strcasecmp(forward,"physical_device")) {
699 state->forward_type=GENERIC_PHYSICAL;
700 } else if (!strcasecmp(forward,"host_device")) {
701 #ifdef V3_CONFIG_HOST_DEVICE
702 state->forward_type=GENERIC_HOST;
704 PrintError(vm, VCORE_NONE, "generic (%s): cannot configure host device since host device support is not built in\n", state->name);
709 PrintError(vm, VCORE_NONE, "generic (%s): unknown forwarding type \"%s\"\n", state->name, forward);
715 struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, state);
718 PrintError(vm, VCORE_NONE, "generic: could not attach device %s\n", state->name);
726 #ifdef V3_CONFIG_HOST_DEVICE
727 if (state->forward_type==GENERIC_HOST) {
729 PrintError(vm, VCORE_NONE, "generic (%s): host forwarding requested, but no host device given\n", state->name);
730 v3_remove_device(dev);
733 state->host_dev = v3_host_dev_open(host_dev,V3_BUS_CLASS_DIRECT,dev,generic_intr_update_callback,vm);
734 if (!(state->host_dev)) {
735 PrintError(vm, VCORE_NONE, "generic (%s): unable to open host device \"%s\"\n", state->name,host_dev);
736 v3_remove_device(dev);
739 PrintDebug(vm, VCORE_NONE, "generic (%s): successfully attached host device \"%s\"\n", state->name,host_dev);
745 PrintDebug(vm, VCORE_NONE, "generic (%s): init_device\n", state->name);
747 // scan port list....
749 uint16_t start = atox(v3_cfg_val(port_cfg, "start"));
750 uint16_t end = atox(v3_cfg_val(port_cfg, "end"));
751 char * mode_str = v3_cfg_val(port_cfg, "mode");
753 PrintError(vm, VCORE_NONE, "generic (%s): error getting port mode\n", state->name);
756 generic_mode_t mode = GENERIC_IGNORE;
757 if (strcasecmp(mode_str, "print_and_ignore") == 0) {
758 mode = GENERIC_PRINT_AND_IGNORE;
759 } else if (strcasecmp(mode_str, "print_and_passthrough") == 0) {
760 mode = GENERIC_PRINT_AND_PASSTHROUGH;
761 } else if (strcasecmp(mode_str, "passthrough") == 0) {
762 mode = GENERIC_PASSTHROUGH;
763 } else if (strcasecmp(mode_str, "ignore") == 0) {
764 mode = GENERIC_IGNORE;
766 PrintError(vm, VCORE_NONE, "generic (%s): invalid mode %s in adding ports\n", state->name, mode_str);
767 v3_remove_device(dev);
772 if (add_port_range(dev, start, end, mode) == -1) {
773 PrintError(vm, VCORE_NONE, "generic (%s): could not add port range 0x%x to 0x%x\n", state->name, start, end);
774 v3_remove_device(dev);
778 port_cfg = v3_cfg_next_branch(port_cfg);
781 // scan memory list....
783 addr_t start = atox(v3_cfg_val(mem_cfg, "start"));
784 addr_t end = atox(v3_cfg_val(mem_cfg, "end"));
785 char * mode_str = v3_cfg_val(mem_cfg, "mode");
786 generic_mode_t mode = GENERIC_IGNORE;
788 if (strcasecmp(mode_str, "print_and_ignore") == 0) {
789 mode = GENERIC_PRINT_AND_IGNORE;
790 } else if (strcasecmp(mode_str, "print_and_passthrough") == 0) {
791 mode = GENERIC_PRINT_AND_PASSTHROUGH;
792 } else if (strcasecmp(mode_str, "passthrough") == 0) {
793 mode = GENERIC_PASSTHROUGH;
794 } else if (strcasecmp(mode_str, "ignore") == 0) {
795 mode = GENERIC_IGNORE;
797 PrintError(vm, VCORE_NONE, "generic (%s): invalid mode %s for adding memory\n", state->name, mode_str);
798 v3_remove_device(dev);
802 if (state->num_mem_hooks>=MAX_MEM_HOOKS) {
803 PrintError(vm, VCORE_NONE, "generic (%s): cannot add another memory hook (increase MAX_MEM_HOOKS)\n", state->name);
804 v3_remove_device(dev);
808 if (add_mem_range(dev, start, end, mode) == -1) {
809 PrintError(vm, VCORE_NONE, "generic (%s): could not add memory range 0x%p to 0x%p\n", state->name, (void*)start, (void*)end);
810 v3_remove_device(dev);
814 state->mem_hook[state->num_mem_hooks] = start;
815 state->num_mem_hooks++;
817 mem_cfg = v3_cfg_next_branch(port_cfg);
821 // hack for os debug testing
822 if (strcasecmp(state->name,"os debug")==0) {
823 PrintDebug(vm, VCORE_NONE, "generic (%s): adding hypercall for os debug device\n", state->name);
824 v3_register_hypercall(vm,0xc0c0,osdebug_hcall,state);
828 PrintDebug(vm, VCORE_NONE, "generic (%s): initialization complete\n", state->name);
833 device_register("GENERIC", generic_init)