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("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("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("generic (%s): writing 0x", state->name);
123 for (i = 0; i < length; i++) {
124 PrintDebug("%x", ((uint8_t *)src)[i]);
127 PrintDebug(" to port 0x%x ... ", port);
129 rc=generic_write_port_passthrough(core,port,src,length,priv_data);
131 PrintDebug(" 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("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("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(" done ... read 0x");
199 for (i = 0; i < rc; i++) {
200 PrintDebug("%x", ((uint8_t *)src)[i]);
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("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(" 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("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(" ignored - data was: 0x");
256 for (i = 0; i < length; i++) {
257 PrintDebug("%x", ((uint8_t *)src)[i]);
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("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("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("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("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("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("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("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("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("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("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("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("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("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("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("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("generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
521 PrintError("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("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("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("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("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("generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
578 PrintError("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;
600 PrintDebug("generic (%s): handling hypercall (len=%d) as sequence of port writes\n",
601 state->name, msg_len);
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);
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);
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);
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
632 GENERIC_PASSTHROUGH => send writes and reads to physical device or host
633 GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
635 GENERIC_IGNORE => ignore writes and reads
636 GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
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)
643 <device class="generic" id="my_id"
644 empty | forward="physical_device" or forward="host_device" host_device="url">
646 (empty implies physical_dev)
649 <start>portno1</start>
650 <end>portno2</end> => portno1 through portno2 (inclusive)
651 <mode>PRINT_AND_PASSTHROUGH</mode> (as above)
656 <end>gpa2</end> => memory addreses gpa1 through gpa2 (inclusive); page granularity
657 <mode> ... as above </mode>
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");
669 v3_cfg_tree_t * port_cfg = v3_cfg_subtree(cfg, "ports");
670 v3_cfg_tree_t * mem_cfg = v3_cfg_subtree(cfg, "memory");
673 state = (struct generic_internal *)V3_Malloc(sizeof(struct generic_internal));
676 PrintError("generic (%s): could not allocate generic state\n",dev_id);
680 memset(state, 0, sizeof(struct generic_internal));
681 strncpy(state->name,dev_id,MAX_NAME);
684 state->forward_type=GENERIC_PHYSICAL;
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;
692 PrintError("generic (%s): cannot configure host device since host device support is not built in\n", state->name);
697 PrintError("generic (%s): unknown forwarding type \"%s\"\n", state->name, forward);
703 struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, state);
706 PrintError("generic: could not attach device %s\n", state->name);
714 #ifdef V3_CONFIG_HOST_DEVICE
715 if (state->forward_type==GENERIC_HOST) {
717 PrintError("generic (%s): host forwarding requested, but no host device given\n", state->name);
718 v3_remove_device(dev);
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);
727 PrintDebug("generic (%s): successfully attached host device \"%s\"\n", state->name,host_dev);
733 PrintDebug("generic (%s): init_device\n", state->name);
735 // scan port list....
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;
750 PrintError("generic (%s): invalid mode %s in adding ports\n", state->name, mode_str);
751 v3_remove_device(dev);
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);
762 port_cfg = v3_cfg_next_branch(port_cfg);
765 // scan memory list....
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;
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;
781 PrintError("generic (%s): invalid mode %s for adding memory\n", state->name, mode_str);
782 v3_remove_device(dev);
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);
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);
798 state->mem_hook[state->num_mem_hooks] = start;
799 state->num_mem_hooks++;
801 mem_cfg = v3_cfg_next_branch(port_cfg);
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);
812 PrintDebug("generic (%s): initialization complete\n", state->name);
817 device_register("GENERIC", generic_init)