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 The device can be used to forward to the underlying physical device
589 or to a host device that has a given url. Both memory and ports can be forwarded as
591 GENERIC_PASSTHROUGH => send writes and reads to physical device or host
592 GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
594 GENERIC_IGNORE => ignore writes and reads
595 GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
598 The purpose of the "PRINT" variants is to make it easy to spy on
599 device interactions (although you will not see DMA or interrupts)
602 <device class="generic" id="my_id"
603 empty | forward="physical_device" or forward="host_device" host_device="url">
605 (empty implies physical_dev)
608 <start>portno1</start>
609 <end>portno2</end> => portno1 through portno2 (inclusive)
610 <mode>PRINT_AND_PASSTHROUGH</mode> (as above)
615 <end>gpa2</end> => memory addreses gpa1 through gpa2 (inclusive); page granularity
616 <mode> ... as above </mode>
621 static int generic_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
622 struct generic_internal * state = NULL;
623 char * dev_id = v3_cfg_val(cfg, "ID");
624 char * forward = v3_cfg_val(cfg, "forward");
625 #ifdef V3_CONFIG_HOST_DEVICE
626 char * host_dev = v3_cfg_val(cfg, "hostdev");
628 v3_cfg_tree_t * port_cfg = v3_cfg_subtree(cfg, "ports");
629 v3_cfg_tree_t * mem_cfg = v3_cfg_subtree(cfg, "memory");
632 state = (struct generic_internal *)V3_Malloc(sizeof(struct generic_internal));
635 PrintError("generic (%s): could not allocate generic state\n",dev_id);
639 memset(state, 0, sizeof(struct generic_internal));
640 strncpy(state->name,dev_id,MAX_NAME);
643 state->forward_type=GENERIC_PHYSICAL;
645 if (!strcasecmp(forward,"physical_device")) {
646 state->forward_type=GENERIC_PHYSICAL;
647 } else if (!strcasecmp(forward,"host_device")) {
648 #ifdef V3_CONFIG_HOST_DEVICE
649 state->forward_type=GENERIC_HOST;
651 PrintError("generic (%s): cannot configure host device since host device support is not built in\n", state->name);
656 PrintError("generic (%s): unknown forwarding type \"%s\"\n", state->name, forward);
662 struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, state);
665 PrintError("generic: could not attach device %s\n", state->name);
673 #ifdef V3_CONFIG_HOST_DEVICE
674 if (state->forward_type==GENERIC_HOST) {
676 PrintError("generic (%s): host forwarding requested, but no host device given\n", state->name);
677 v3_remove_device(dev);
680 state->host_dev = v3_host_dev_open(host_dev,V3_BUS_CLASS_DIRECT,dev,vm);
681 if (!(state->host_dev)) {
682 PrintError("generic (%s): unable to open host device \"%s\"\n", state->name,host_dev);
683 v3_remove_device(dev);
686 PrintDebug("generic (%s): successfully attached host device \"%s\"\n", state->name,host_dev);
692 PrintDebug("generic (%s): init_device\n", state->name);
694 // scan port list....
696 uint16_t start = atox(v3_cfg_val(port_cfg, "start"));
697 uint16_t end = atox(v3_cfg_val(port_cfg, "end"));
698 char * mode_str = v3_cfg_val(port_cfg, "mode");
699 generic_mode_t mode = GENERIC_IGNORE;
700 if (strcasecmp(mode_str, "print_and_ignore") == 0) {
701 mode = GENERIC_PRINT_AND_IGNORE;
702 } else if (strcasecmp(mode_str, "print_and_passthrough") == 0) {
703 mode = GENERIC_PRINT_AND_PASSTHROUGH;
704 } else if (strcasecmp(mode_str, "passthrough") == 0) {
705 mode = GENERIC_PASSTHROUGH;
706 } else if (strcasecmp(mode_str, "ignore") == 0) {
707 mode = GENERIC_IGNORE;
709 PrintError("generic (%s): invalid mode %s in adding ports\n", state->name, mode_str);
710 v3_remove_device(dev);
715 if (add_port_range(dev, start, end, mode) == -1) {
716 PrintError("generic (%s): could not add port range 0x%x to 0x%x\n", state->name, start, end);
717 v3_remove_device(dev);
721 port_cfg = v3_cfg_next_branch(port_cfg);
724 // scan memory list....
726 addr_t start = atox(v3_cfg_val(mem_cfg, "start"));
727 addr_t end = atox(v3_cfg_val(mem_cfg, "end"));
728 char * mode_str = v3_cfg_val(mem_cfg, "mode");
729 generic_mode_t mode = GENERIC_IGNORE;
731 if (strcasecmp(mode_str, "print_and_ignore") == 0) {
732 mode = GENERIC_PRINT_AND_IGNORE;
733 } else if (strcasecmp(mode_str, "print_and_passthrough") == 0) {
734 mode = GENERIC_PRINT_AND_PASSTHROUGH;
735 } else if (strcasecmp(mode_str, "passthrough") == 0) {
736 mode = GENERIC_PASSTHROUGH;
737 } else if (strcasecmp(mode_str, "ignore") == 0) {
738 mode = GENERIC_IGNORE;
740 PrintError("generic (%s): invalid mode %s for adding memory\n", state->name, mode_str);
741 v3_remove_device(dev);
745 if (state->num_mem_hooks>=MAX_MEM_HOOKS) {
746 PrintError("generic (%s): cannot add another memory hook (increase MAX_MEM_HOOKS)\n", state->name);
747 v3_remove_device(dev);
751 if (add_mem_range(dev, start, end, mode) == -1) {
752 PrintError("generic (%s): could not add memory range 0x%p to 0x%p\n", state->name, (void*)start, (void*)end);
753 v3_remove_device(dev);
757 state->mem_hook[state->num_mem_hooks] = start;
758 state->num_mem_hooks++;
760 mem_cfg = v3_cfg_next_branch(port_cfg);
763 PrintDebug("generic (%s): initialization complete\n", state->name);
768 device_register("GENERIC", generic_init)