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 CONFIG_HOST_DEVICE
30 #include <interfaces/vmm_host_dev.h>
33 #ifndef CONFIG_DEBUG_GENERIC
35 #define PrintDebug(fmt, args...)
39 typedef enum {GENERIC_IGNORE,
41 GENERIC_PRINT_AND_PASSTHROUGH,
42 GENERIC_PRINT_AND_IGNORE} generic_mode_t;
44 struct generic_internal {
45 enum {GENERIC_PHYSICAL, GENERIC_HOST} forward_type;
46 #ifdef CONFIG_HOST_DEVICE
47 v3_host_dev_t host_dev;
54 static int generic_write_port_passthrough(struct guest_info * core,
60 struct generic_internal *state = (struct generic_internal *) priv_data;
63 switch (state->forward_type) {
64 case GENERIC_PHYSICAL:
67 v3_outb(port, ((uint8_t *)src)[0]);
70 v3_outw(port, ((uint16_t *)src)[0]);
73 v3_outdw(port, ((uint32_t *)src)[0]);
76 for (i = 0; i < length; i++) {
77 v3_outb(port, ((uint8_t *)src)[i]);
83 #ifdef CONFIG_HOST_DEVICE
85 if (state->host_dev) {
86 return v3_host_dev_write_io(state->host_dev,port,src,length);
93 PrintError("generic: unknown forwarding type\n");
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) {
104 #ifdef CONFIG_DEBUG_GENERIC
105 struct generic_internal *state = (struct generic_internal *) priv_data;
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");
112 PrintDebug("generic: writing 0x");
114 for (i = 0; i < length; i++) {
115 PrintDebug("%x", ((uint8_t *)src)[i]);
118 PrintDebug(" to port 0x%x ... ", port);
120 rc=generic_write_port_passthrough(core,port,src,length,priv_data);
122 PrintDebug(" done\n");
127 static int generic_read_port_passthrough(struct guest_info * core,
133 struct generic_internal *state = (struct generic_internal *) priv_data;
137 switch (state->forward_type) {
138 case GENERIC_PHYSICAL:
141 ((uint8_t *)dst)[0] = v3_inb(port);
144 ((uint16_t *)dst)[0] = v3_inw(port);
147 ((uint32_t *)dst)[0] = v3_indw(port);
150 for (i = 0; i < length; i++) {
151 ((uint8_t *)dst)[i] = v3_inb(port);
156 #ifdef CONFIG_HOST_DEVICE
158 if (state->host_dev) {
159 return v3_host_dev_read_io(state->host_dev,port,dst,length);
164 PrintError("generic: unknown forwarding type\n");
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) {
177 #ifdef CONFIG_DEBUG_GENERIC
178 struct generic_internal *state = (struct generic_internal *) priv_data;
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");
186 rc=generic_read_port_passthrough(core,port,src,length,priv_data);
188 PrintDebug(" done ... read 0x");
190 for (i = 0; i < rc; i++) {
191 PrintDebug("%x", ((uint8_t *)src)[i]);
200 static int generic_read_port_ignore(struct guest_info * core, uint16_t port, void * src,
201 uint_t length, void * priv_data) {
203 memset((uint8_t *)src, 0, length);
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) {
211 #ifdef CONFIG_DEBUG_GENERIC
212 struct generic_internal *state = (struct generic_internal *) priv_data;
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");
220 memset((uint8_t *)src, 0, length);
221 PrintDebug(" ignored (return zeroed buffer)\n");
226 static int generic_write_port_ignore(struct guest_info * core, uint16_t port, void * src,
227 uint_t length, void * priv_data) {
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) {
236 #ifdef CONFIG_DEBUG_GENERIC
237 struct generic_internal *state = (struct generic_internal *) priv_data;
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");
244 memset((uint8_t *)src, 0, length);
245 PrintDebug(" ignored - data was: 0x");
247 for (i = 0; i < length; i++) {
248 PrintDebug("%x", ((uint8_t *)src)[i]);
258 static int generic_write_mem_passthrough(struct guest_info * core,
264 struct vm_device *dev = (struct vm_device *) priv;
265 struct generic_internal *state = (struct generic_internal *) dev->private_data;
267 switch (state->forward_type) {
268 case GENERIC_PHYSICAL:
269 memcpy(V3_VAddr((void*)gpa),src,len);
272 #ifdef CONFIG_HOST_DEVICE
274 if (state->host_dev) {
275 return v3_host_dev_write_mem(state->host_dev,gpa,src,len);
282 PrintError("generic: unknown forwarding type\n");
288 static int generic_write_mem_print_and_passthrough(struct guest_info * core,
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;
299 PrintDebug("generic: writing %u bytes to GPA 0x%p via %s ... ",
301 state->forward_type == GENERIC_PHYSICAL ? "physical" :
302 state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
304 int rc = generic_write_mem_passthrough(core,gpa,src,len,priv);
306 PrintDebug("done\n");
311 static int generic_write_mem_ignore(struct guest_info * core,
320 static int generic_write_mem_print_and_ignore(struct guest_info * core,
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;
331 PrintDebug("generic: ignoring write of %u bytes to GPA 0x%p via %s",
333 state->forward_type == GENERIC_PHYSICAL ? "physical" :
334 state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
339 static int generic_read_mem_passthrough(struct guest_info * core,
345 struct vm_device *dev = (struct vm_device *) priv;
346 struct generic_internal *state = (struct generic_internal *) dev->private_data;
348 switch (state->forward_type) {
349 case GENERIC_PHYSICAL:
350 memcpy(dst,V3_VAddr((void*)gpa),len);
353 #ifdef CONFIG_HOST_DEVICE
355 if (state->host_dev) {
356 return v3_host_dev_read_mem(state->host_dev,gpa,dst,len);
363 PrintError("generic: unknown forwarding type\n");
370 static int generic_read_mem_print_and_passthrough(struct guest_info * core,
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;
381 PrintDebug("generic: attempting to read %u bytes from GPA 0x%p via %s ... ",
383 state->forward_type == GENERIC_PHYSICAL ? "physical" :
384 state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
386 int rc = generic_read_mem_passthrough(core,gpa,dst,len,priv);
388 PrintDebug("done - read %d bytes\n", rc);
393 static int generic_read_mem_ignore(struct guest_info * core,
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;
404 PrintDebug("generic: ignoring attempt to read %u bytes from GPA 0x%p via %s ... ",
406 state->forward_type == GENERIC_PHYSICAL ? "physical" :
407 state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
409 memset((uint8_t *)dst, 0, len);
411 PrintDebug("returning zeros\n");
417 static int generic_read_mem_print_and_ignore(struct guest_info * core,
423 memset((uint8_t *)dst, 0, len);
428 static int generic_free(struct generic_internal * state) {
429 PrintDebug("generic: deinit_device\n");
431 #ifdef CONFIG_HOST_DEVICE
432 if (state->host_dev) {
433 v3_host_dev_close(state->host_dev);
446 static struct v3_device_ops dev_ops = {
447 .free = (int (*)(void *))generic_free,
453 static int add_port_range(struct vm_device * dev, uint_t start, uint_t end, generic_mode_t mode) {
456 PrintDebug("generic: adding port range 0x%x to 0x%x as %s\n",
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");
463 for (i = start; i <= end; i++) {
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);
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);
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);
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);
499 PrintError("generic: huh?\n");
508 static int add_mem_range(struct vm_device * dev, addr_t start, addr_t end, generic_mode_t mode) {
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");
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);
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);
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);
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);
554 PrintError("generic: huh?\n");
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
567 GENERIC_PASSTHROUGH => send writes and reads to physical device or host
568 GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
570 GENERIC_IGNORE => ignore writes and reads
571 GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
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)
578 <device class="generic" id="my_id"
579 empty | forward="physical_device" or forward="host_device" host_device="url">
581 (empty implies physical_dev)
584 <start>portno1</start>
585 <end>portno2</end> => portno1 through portno2 (inclusive)
586 <mode>PRINT_AND_PASSTHROUGH</mode> (as above)
591 <end>gpa2</end> => memory addreses gpa1 through gpa2 (inclusive); page granularity
592 <mode> ... as above </mode>
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");
604 v3_cfg_tree_t * port_cfg = v3_cfg_subtree(cfg, "ports");
605 v3_cfg_tree_t * mem_cfg = v3_cfg_subtree(cfg, "memory");
608 state = (struct generic_internal *)V3_Malloc(sizeof(struct generic_internal));
611 PrintError("Could not allocate generic state\n");
615 memset(state, 0, sizeof(struct generic_internal));
618 state->forward_type=GENERIC_PHYSICAL;
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;
626 PrintError("generic: cannot configure host device since host device support is not built in\n");
631 PrintError("generic: unknown forwarding type \"%s\"\n", forward);
637 struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, state);
640 PrintError("Could not attach device %s\n", dev_id);
646 #ifdef CONFIG_HOST_DEVICE
647 if (state->forward_type==GENERIC_HOST) {
649 PrintError("generic: host forwarding requested, but no host device given\n");
650 v3_remove_device(dev);
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);
659 PrintDebug("generic: successfully attached host device \"%s\"\n",host_dev);
665 PrintDebug("generic: init_device\n");
667 // scan port list....
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;
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;
683 PrintError("generic: invalid mode %s in adding ports\n", mode_str);
684 v3_remove_device(dev);
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);
694 port_cfg = v3_cfg_next_branch(port_cfg);
697 // scan memory list....
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;
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;
713 PrintError("generic: invalid mode %s for adding memory\n", mode_str);
714 v3_remove_device(dev);
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);
724 mem_cfg = v3_cfg_next_branch(port_cfg);
727 PrintDebug("generic: initialization complete\n");
732 device_register("GENERIC", generic_init)