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.


Cleanup and sanity-checking of switch issues, negative array indexes, operand indepen...
[palacios.git] / palacios / src / devices / generic.c
index 98836d1..1e06a80 100644 (file)
-/* (c) 2008, Peter Dinda <pdinda@northwestern.edu> */
-/* (c) 2008, The V3VEE Project <http://www.v3vee.org> */
+/* 
+ * This file is part of the Palacios Virtual Machine Monitor developed
+ * by the V3VEE Project with funding from the United States National 
+ * Science Foundation and the Department of Energy.  
+ *
+ * The V3VEE Project is a joint project between Northwestern University
+ * and the University of New Mexico.  You can find out more at 
+ * http://www.v3vee.org
+ *
+ * Copyright (c) 2008, Peter Dinda <pdinda@northwestern.edu> 
+ * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
+ * All rights reserved.
+ *
+ * Author: Peter Dinda <pdinda@northwestern.edu>
+ * Contributor: 2008, Jack Lange <jarusl@cs.northwestern.edu>
+ *        
+ *
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
+ */
 
-
-
-
-#include <devices/generic.h>
 #include <palacios/vmm.h>
 #include <palacios/vmm_types.h>
 #include <palacios/vmm_list.h>
+#include <palacios/vmm_io.h>
+#include <palacios/vmm_dev_mgr.h>
+#include <palacios/vm_guest_mem.h>
 
+#ifdef V3_CONFIG_HOST_DEVICE
+#include <interfaces/vmm_host_dev.h>
+#endif
 
-
-#ifndef DEBUG_GENERIC
+#ifndef V3_CONFIG_DEBUG_GENERIC
 #undef PrintDebug
 #define PrintDebug(fmt, args...)
 #endif
 
+#define MAX_NAME      32
+#define MAX_MEM_HOOKS 16
 
-#define PORT_HOOKS 1
-#define MEM_HOOKS  0   // not yet implmented in device model
-#define IRQ_HOOKS  0   // not yet implemented in device model
-
+typedef enum {GENERIC_IGNORE, 
+             GENERIC_PASSTHROUGH, 
+             GENERIC_PRINT_AND_PASSTHROUGH, 
+             GENERIC_PRINT_AND_IGNORE} generic_mode_t;
 
 struct generic_internal {
-  struct list_head port_list;
-  uint_t num_port_ranges;
-  struct list_head mem_list;
-  uint_t num_mem_ranges;
-  struct list_head irq_list;
-  uint_t num_irq_ranges;
-};
-
-
-struct port_range {
-  uint_t start;
-  uint_t end;
-  uint_t type;
-  struct list_head range_link;
-};
+    enum {GENERIC_PHYSICAL, GENERIC_HOST} forward_type;
+#ifdef V3_CONFIG_HOST_DEVICE
+    v3_host_dev_t                         host_dev;
+#endif
+    struct vm_device                      *dev; // me
 
-struct mem_range {
-  void * start;
-  void * end;
-  uint_t type;
-  struct list_head range_link;
+    char                                  name[MAX_NAME];
+    
+    uint32_t                              num_mem_hooks;
+    addr_t                                mem_hook[MAX_MEM_HOOKS];
 };
 
-struct irq_range {
-  uint_t start;
-  uint_t end;
-  uint_t type;
-  struct list_head range_link;
-};
 
 
 
-int generic_reset_device(struct vm_device * dev)
+static int generic_write_port_passthrough(struct guest_info * core, 
+                                         uint16_t port, 
+                                         void * src, 
+                                         uint_t length, 
+                                         void * priv_data) 
 {
-  PrintDebug("generic: reset device\n");
-  return 0;
-
+    struct generic_internal *state = (struct generic_internal *) priv_data;
+    uint_t i;
+
+    switch (state->forward_type) { 
+       case GENERIC_PHYSICAL:
+           switch (length) {
+               case 1:
+                   v3_outb(port, ((uint8_t *)src)[0]);
+                   break;
+               case 2:
+                   v3_outw(port, ((uint16_t *)src)[0]);
+                   break;
+               case 4:
+                   v3_outdw(port, ((uint32_t *)src)[0]);
+                   break;
+               default:
+                   for (i = 0; i < length; i++) { 
+                       v3_outb(port, ((uint8_t *)src)[i]);
+                   }
+                   break;
+           }
+           return length;
+           break;
+#ifdef V3_CONFIG_HOST_DEVICE
+       case GENERIC_HOST:
+           if (state->host_dev) { 
+               return v3_host_dev_write_io(state->host_dev,port,src,length);
+           } else {
+               return -1;
+           }
+           break;
+#endif
+       default:
+           PrintError(core->vm_info, core, "generic (%s): unknown forwarding type\n", state->name);
+           return -1;
+           break;
+    }
 }
 
+static int generic_write_port_print_and_passthrough(struct guest_info * core, uint16_t port, void * src, 
+                                                   uint_t length, void * priv_data) {
+    uint_t i;
+    int rc;
 
+#ifdef V3_CONFIG_DEBUG_GENERIC
+    struct generic_internal *state = (struct generic_internal *) priv_data;
+#endif
 
+    PrintDebug(core->vm_info, core, "generic (%s): writing 0x%x bytes to port 0x%x using %s ...", state->name,
+              length, port,
+              state->forward_type == GENERIC_PHYSICAL ? "physical" :
+              state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
 
+    PrintDebug(core->vm_info, core, "generic (%s): writing 0x", state->name);
 
-int generic_start_device(struct vm_device * dev)
-{
-  PrintDebug("generic: start device\n");
-  return 0;
-}
+    for (i = 0; i < length; i++) { 
+       PrintDebug(core->vm_info, core, "%x", ((uint8_t *)src)[i]);
+    }
+  
+    PrintDebug(core->vm_info, core, " to port 0x%x ... ", port);
 
+    rc=generic_write_port_passthrough(core,port,src,length,priv_data);
+
+    PrintDebug(core->vm_info, core, " done\n");
+  
+    return rc;
+}
 
-int generic_stop_device(struct vm_device * dev)
+static int generic_read_port_passthrough(struct guest_info * core, 
+                                        uint16_t port, 
+                                        void * dst, 
+                                        uint_t length, 
+                                        void * priv_data) 
 {
-  PrintDebug("generic: stop device\n");
-  return 0;
+    struct generic_internal *state = (struct generic_internal *) priv_data;
+
+    uint_t i;
+
+    switch (state->forward_type) { 
+       case GENERIC_PHYSICAL:
+           switch (length) {
+               case 1:
+                   ((uint8_t *)dst)[0] = v3_inb(port);
+                   break;
+               case 2:
+                   ((uint16_t *)dst)[0] = v3_inw(port);
+                   break;
+               case 4:
+                   ((uint32_t *)dst)[0] = v3_indw(port);
+                   break;
+               default:
+                   for (i = 0; i < length; i++) { 
+                       ((uint8_t *)dst)[i] = v3_inb(port);
+                   }
+           }
+           return length;
+           break;
+#ifdef V3_CONFIG_HOST_DEVICE
+       case GENERIC_HOST:
+           if (state->host_dev) { 
+               return v3_host_dev_read_io(state->host_dev,port,dst,length);
+           }
+           break;
+#endif
+       default:
+           PrintError(core->vm_info, core, "generic (%s): unknown forwarding type\n", state->name);
+           return -1;
+           break;
+    }
+
+    return -1;
 }
 
+static int generic_read_port_print_and_passthrough(struct guest_info * core, uint16_t port, void * src, 
+                                                  uint_t length, void * priv_data) {
+    uint_t i;
+    int rc;
 
+#ifdef V3_CONFIG_DEBUG_GENERIC
+    struct generic_internal *state = (struct generic_internal *) priv_data;
+#endif
 
+    PrintDebug(core->vm_info, core, "generic (%s): reading 0x%x bytes from port 0x%x using %s ...", state->name, length, port,
+              state->forward_type == GENERIC_PHYSICAL ? "physical" :
+              state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
 
-int generic_write_port_passthrough(ushort_t port,
-                                  void * src, 
-                                  uint_t length,
-                                  struct vm_device * dev)
-{
-  uint_t i;
 
-  PrintDebug("generic: writing 0x");
+    rc=generic_read_port_passthrough(core,port,src,length,priv_data);
 
-  for (i = 0; i < length; i++) { 
-    PrintDebug("%x", ((uchar_t*)src)[i]);
-  }
-  
-  PrintDebug(" to port 0x%x ... ", port);
-
-
-  switch (length) {
-  case 1:
-    v3_outb(port,((uchar_t*)src)[0]);
-    break;
-  case 2:
-    v3_outw(port,((ushort_t*)src)[0]);
-    break;
-  case 4:
-    v3_outdw(port,((uint_t*)src)[0]);
-    break;
-  default:
-    for (i = 0; i < length; i++) { 
-      v3_outb(port, ((uchar_t*)src)[i]);
+    if (rc<0) { 
+       PrintError(core->vm_info, core, "FAILED\n");
+       return rc;
+    } else {
+       PrintDebug(core->vm_info, core, " done ... read 0x");
+       
+       for (i = 0; i < rc; i++) { 
+           PrintDebug(core->vm_info, core, "%x", ((uint8_t *)src)[i]);
+       }
+       
+       PrintDebug(core->vm_info, core, "\n");
     }
-  } //switch length
+    
+    return rc;
+}
 
 
-  PrintDebug(" done\n");
-  
-  return length;
-}
+static int generic_read_port_ignore(struct guest_info * core, uint16_t port, void * src, 
+                                   uint_t length, void * priv_data) {
 
-int generic_read_port_passthrough(ushort_t port,
-                                 void * src, 
-                                 uint_t length,
-                                 struct vm_device * dev)
-{
-  uint_t i;
+    memset((uint8_t *)src, 0, length);
 
-  PrintDebug("generic: reading 0x%x bytes from port 0x%x ...", length, port);
+    return length;
+}
 
+static int generic_read_port_print_and_ignore(struct guest_info * core, uint16_t port, void * src, 
+                                             uint_t length, void * priv_data) {
+   
+#ifdef V3_CONFIG_DEBUG_GENERIC
+    struct generic_internal *state = (struct generic_internal *) priv_data;
+#endif
 
-    switch (length) {
-    case 1:
-      ((uchar_t*)src)[0] = v3_inb(port);
-      break;
-    case 2:
-      ((ushort_t*)src)[0] = v3_inw(port);
-      break;
-    case 4:
-      ((uint_t*)src)[0] = v3_indw(port);
-      break;
-    default:
-      for (i = 0; i < length; i++) { 
-       ((uchar_t*)src)[i] = v3_inb(port);
-      }
-    }//switch length
+    PrintDebug(core->vm_info, core, "generic (%s): reading 0x%x bytes from port 0x%x using %s ...", state->name, length, port,
+              state->forward_type == GENERIC_PHYSICAL ? "physical" :
+              state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
 
-  PrintDebug(" done ... read 0x");
 
-  for (i = 0; i < length; i++) { 
-    PrintDebug("%x", ((uchar_t*)src)[i]);
-  }
+    memset((uint8_t *)src, 0, length);
+    PrintDebug(core->vm_info, core, " ignored (return zeroed buffer)\n");
+
+    return length;
+}
 
-  PrintDebug("\n");
+static int generic_write_port_ignore(struct guest_info * core, uint16_t port, void * src, 
+                                    uint_t length, void * priv_data) {
 
-  return length;
+    return length;
 }
 
-int generic_write_port_ignore(ushort_t port,
-                             void * src, 
-                             uint_t length,
-                             struct vm_device * dev)
-{
-  uint_t i;
+static int generic_write_port_print_and_ignore(struct guest_info * core, uint16_t port, void * src, 
+                                             uint_t length, void * priv_data) {
+    int i;
 
-  PrintDebug("generic: writing 0x");
+#ifdef V3_CONFIG_DEBUG_GENERIC
+    struct generic_internal *state = (struct generic_internal *) priv_data;
+#endif
 
-  for (i = 0; i < length; i++) { 
-    PrintDebug("%x", ((uchar_t*)src)[i]);
-  }
-  
-  PrintDebug(" to port 0x%x ... ignored\n", port);
-  return length;
+    PrintDebug(core->vm_info, core, "generic (%s): writing 0x%x bytes to port 0x%x using %s ", state->name, length, port,
+              state->forward_type == GENERIC_PHYSICAL ? "physical" :
+              state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
+    
+    memset((uint8_t *)src, 0, length);
+    PrintDebug(core->vm_info, core, " ignored - data was: 0x");
+
+    for (i = 0; i < length; i++) { 
+       PrintDebug(core->vm_info, core, "%x", ((uint8_t *)src)[i]);
+    }
+    
+    PrintDebug(core->vm_info, core, "\n");
+
+    return length;
 }
 
-int generic_read_port_ignore(ushort_t port,
-                            void * src, 
-                            uint_t length,
-                            struct vm_device * dev)
+
+
+static int generic_write_mem_passthrough(struct guest_info * core, 
+                                        addr_t              gpa,
+                                        void              * src,
+                                        uint_t              len,
+                                        void              * priv)
 {
+    struct vm_device *dev = (struct vm_device *) priv;
+    struct generic_internal *state = (struct generic_internal *) dev->private_data;
+    
+    switch (state->forward_type) { 
+       case GENERIC_PHYSICAL:
+           memcpy(V3_VAddr((void*)gpa),src,len);
+           return len;
+           break;
+#ifdef V3_CONFIG_HOST_DEVICE
+       case GENERIC_HOST:
+           if (state->host_dev) { 
+               return v3_host_dev_write_mem(state->host_dev,gpa,src,len);
+           } else {
+               return -1;
+           }
+           break;
+#endif
+       default:
+           PrintError(core->vm_info, core, "generic (%s): unknown forwarding type\n", state->name);
+           return -1;
+           break;
+    }
+}
 
-  PrintDebug("generic: reading 0x%x bytes from port 0x%x ...", length, port);
+static int generic_write_mem_print_and_passthrough(struct guest_info * core, 
+                                                  addr_t              gpa,
+                                                  void              * src,
+                                                  uint_t              len,
+                                                  void              * priv)
+{
+#ifdef V3_CONFIG_DEBUG_GENERIC
+    struct vm_device *dev = (struct vm_device *) priv;
+    struct generic_internal *state = (struct generic_internal *) dev->private_data;
+#endif
 
-  memset((char*)src, 0, length);
-  PrintDebug(" ignored (return zeroed buffer)\n");
+    PrintDebug(core->vm_info, core, "generic (%s): writing %u bytes to GPA 0x%p via %s ... ", state->name,
+              len,(void*)gpa,
+              state->forward_type == GENERIC_PHYSICAL ? "physical" : 
+              state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
+    
+    int rc = generic_write_mem_passthrough(core,gpa,src,len,priv);
 
-  return length;
+    PrintDebug(core->vm_info, core, "done\n");
+    
+    return rc;
 }
 
+static int generic_write_mem_ignore(struct guest_info * core, 
+                                   addr_t              gpa,
+                                   void              * src,
+                                   uint_t              len,
+                                   void              * priv)
+{
+    return len;
+}
 
+static int generic_write_mem_print_and_ignore(struct guest_info * core, 
+                                             addr_t              gpa,
+                                             void              * src,
+                                             uint_t              len,
+                                             void              * priv)
+{
+#ifdef V3_CONFIG_DEBUG_GENERIC
+    struct vm_device *dev = (struct vm_device *) priv;
+    struct generic_internal *state = (struct generic_internal *) dev->private_data;
+#endif
 
-int generic_interrupt(uint_t irq, struct vm_device * dev) {
-  PrintDebug("generic: interrupt 0x%x - injecting into VM\n", irq);
-
-  dev->vm->vm_ops.raise_irq(dev->vm, irq);
+    PrintDebug(core->vm_info, core, "generic (%s): ignoring write of %u bytes to GPA 0x%p via %s", state->name,
+              len,(void*)gpa,
+              state->forward_type == GENERIC_PHYSICAL ? "physical" : 
+              state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
+    
+    return len;
+}
 
-  return 0;
+static int generic_read_mem_passthrough(struct guest_info * core, 
+                                       addr_t              gpa,
+                                       void              * dst,
+                                       uint_t              len,
+                                       void              * priv)
+{
+    struct vm_device *dev = (struct vm_device *) priv;
+    struct generic_internal *state = (struct generic_internal *) dev->private_data;
+    
+    switch (state->forward_type) { 
+       case GENERIC_PHYSICAL:
+           memcpy(dst,V3_VAddr((void*)gpa),len);
+           return len;
+           break;
+#ifdef V3_CONFIG_HOST_DEVICE
+       case GENERIC_HOST:
+           if (state->host_dev) { 
+               return v3_host_dev_read_mem(state->host_dev,gpa,dst,len);
+           } else {
+               return -1;
+           }
+           break;
+#endif
+       default:
+           PrintError(core->vm_info, core, "generic (%s): unknown forwarding type\n", state->name);
+           break;
+    }
+    
+    return -1;
 }
 
+static int generic_read_mem_print_and_passthrough(struct guest_info * core, 
+                                                 addr_t              gpa,
+                                                 void              * dst,
+                                                 uint_t              len,
+                                                 void              * priv)
+{
+#ifdef V3_CONFIG_DEBUG_GENERIC
+    struct vm_device *dev = (struct vm_device *) priv;
+    struct generic_internal *state = (struct generic_internal *) dev->private_data;
+#endif
 
-int generic_init_device(struct vm_device * dev) {
-  struct generic_internal * state = (struct generic_internal *)(dev->private_data);
-
-  PrintDebug("generic: init_device\n");
-  generic_reset_device(dev);
-
-
-  if (PORT_HOOKS) { // This is a runtime conditional on a #define
-    struct port_range * tmp = NULL;
-
-    list_for_each_entry(tmp, &(state->port_list), range_link) {
-      uint_t i = 0;
-      
-      PrintDebug("generic: hooking ports 0x%x to 0x%x as %x\n", 
-                tmp->start, tmp->end, 
-                (tmp->type == GENERIC_PRINT_AND_PASSTHROUGH) ? "print-and-passthrough" : "print-and-ignore");
-      
-      for (i = tmp->start; i <= tmp->end; i++) { 
-       if (tmp->type == GENERIC_PRINT_AND_PASSTHROUGH) { 
-         
-         if (dev_hook_io(dev, i, &generic_read_port_passthrough, &generic_write_port_passthrough)) { 
-           PrintDebug("generic: can't hook port 0x%x (already hooked?)\n", i);
-         }
-         
-       } else if (tmp->type == GENERIC_PRINT_AND_IGNORE) { 
-         
-         if (dev_hook_io(dev, i, &generic_read_port_ignore, &generic_write_port_ignore)) { 
-           PrintDebug("generic: can't hook port 0x%x (already hooked?)\n", i);
-         }
-       } 
-      }
+    PrintDebug(core->vm_info, core, "generic (%s): attempting to read %u bytes from GPA 0x%p via %s ... ", state->name,
+              len,(void*)gpa,
+              state->forward_type == GENERIC_PHYSICAL ? "physical" : 
+              state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
+    
+    int rc = generic_read_mem_passthrough(core,gpa,dst,len,priv);
 
-    }
-  } else {
-    PrintDebug("generic: hooking ports not supported\n");
-  }
+    PrintDebug(core->vm_info, core, "done - read %d bytes\n", rc);
+    
+    return rc;
+}
 
+static int generic_read_mem_ignore(struct guest_info * core, 
+                                  addr_t              gpa,
+                                  void              * dst,
+                                  uint_t              len,
+                                  void              * priv)
+{
+#ifdef V3_CONFIG_DEBUG_GENERIC
+    struct vm_device *dev = (struct vm_device *) priv;
+    struct generic_internal *state = (struct generic_internal *) dev->private_data;
+#endif
 
+    PrintDebug(core->vm_info, core, "generic (%s): ignoring attempt to read %u bytes from GPA 0x%p via %s ... ", state->name,
+              len,(void*)gpa,
+              state->forward_type == GENERIC_PHYSICAL ? "physical" : 
+              state->forward_type == GENERIC_HOST ? "host" : "UNKNOWN");
 
-  if (MEM_HOOKS) { // This is a runtime conditional on a #define
-    struct mem_range * tmp;
+    memset((uint8_t *)dst, 0, len);
 
-    list_for_each_entry(tmp, &(state->mem_list), range_link) {
+    PrintDebug(core->vm_info, core, "returning zeros\n");
 
-      PrintDebug("generic: hooking addresses 0x%x to 0x%x\n", 
-                tmp->start, tmp->end); 
-      
-      
-      if (dev_hook_mem(dev, tmp->start, tmp->end)) {
-       PrintDebug("generic: Can't hook addresses 0x%x to 0x%x (already hooked?)\n",
-                  tmp->start, tmp->end); 
-      }
-    }
-  } else {
-    PrintDebug("generic: hooking addresses not supported\n");
-  }
+    return len;
+}
 
 
+static int generic_read_mem_print_and_ignore(struct guest_info * core, 
+                                            addr_t              gpa,
+                                            void              * dst,
+                                            uint_t              len,
+                                            void              * priv)
+{
+    memset((uint8_t *)dst, 0, len);
+    return len;
+}
 
 
-  if (IRQ_HOOKS) { // This is a runtime conditional on a #define
-    struct irq_range * tmp;
+static int generic_free(struct generic_internal * state) {
+    int i;
+    
+    PrintDebug(VM_NONE,VCORE_NONE, "generic (%s): deinit_device\n", state->name);
+    
+#ifdef V3_CONFIG_HOST_DEVICE
+    if (state->host_dev) { 
+       v3_host_dev_close(state->host_dev);
+       state->host_dev=0;
+    }
+#endif
     
-    list_for_each_entry(tmp, &(state->irq_list), range_link) {
-      uint_t i;
-
-      PrintDebug("generic: hooking irqs 0x%x to 0x%x\n",
-                tmp->start, tmp->end);
-      
-      for (i = tmp->start; i <= tmp->end; i++) { 
-       if (dev_hook_irq(dev, i, &generic_interrupt)) { 
-         PrintDebug("generic: can't hook irq  0x%x (already hooked?)\n", i);
+    // Note that the device manager handles unhooking the I/O ports
+    // We need to handle unhooking memory regions    
+    for (i=0;i<state->num_mem_hooks;i++) {
+       if (v3_unhook_mem(state->dev->vm,V3_MEM_CORE_ANY,state->mem_hook[i])<0) { 
+           PrintError(VM_NONE,VCORE_NONE , "generic (%s): unable to unhook memory starting at 0x%p\n", state->name,(void*)(state->mem_hook[i]));
+           return -1;
        }
-      }
-
     }
-  } else {
-    PrintDebug("generic: hooking irqs not supported\n");
-  }
+            
+    V3_Free(state);
+    return 0;
+}
+
 
 
 
-  return 0;
-}
 
-int generic_deinit_device(struct vm_device * dev) {
-  struct generic_internal * state = (struct generic_internal *)(dev->private_data);
+static struct v3_device_ops dev_ops = { 
+    .free = (int (*)(void *))generic_free, 
+};
 
 
-  PrintDebug("generic: deinit_device\n");
 
 
-  if (IRQ_HOOKS) { // This is a runtime conditional on a #define
-    struct irq_range * tmp;
-    struct irq_range * cur;
-    
-    list_for_each_entry_safe(cur, tmp, &(state->irq_list), range_link) {
-      uint_t i;
+static int add_port_range(struct vm_device * dev, uint_t start, uint_t end, generic_mode_t mode) {
+    uint_t i = 0;
 
-      PrintDebug("generic: unhooking irqs 0x%x to 0x%x\n", 
-                cur->start, cur->end);
-      
+    struct generic_internal *state = (struct generic_internal *) dev->private_data;
 
-      for (i = cur->start; i <= cur->end; i++) { 
-       if (dev_unhook_irq(dev, i)) {
-         PrintDebug("generic: can't unhook irq 0x%x (already unhooked?)\n", i);
+    PrintDebug(VM_NONE, VCORE_NONE, "generic (%s): adding port range 0x%x to 0x%x as %s\n", state->name,
+              start, end, 
+              (mode == GENERIC_PRINT_AND_PASSTHROUGH) ? "print-and-passthrough" : 
+              (mode == GENERIC_PRINT_AND_IGNORE) ? "print-and-ignore" :
+              (mode == GENERIC_PASSTHROUGH) ? "passthrough" :
+              (mode == GENERIC_IGNORE) ? "ignore" : "UNKNOWN");
+       
+    for (i = start; i <= end; i++) { 
+       switch (mode) { 
+           case GENERIC_PRINT_AND_PASSTHROUGH:
+               if (v3_dev_hook_io(dev, i, 
+                                  &generic_read_port_print_and_passthrough, 
+                                  &generic_write_port_print_and_passthrough) == -1) { 
+                   PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
+                   return -1;
+               }
+               break;
+               
+           case GENERIC_PRINT_AND_IGNORE:
+               if (v3_dev_hook_io(dev, i, 
+                                  &generic_read_port_print_and_ignore, 
+                                  &generic_write_port_print_and_ignore) == -1) { 
+                   PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
+                   return -1;
+               }
+               break;
+           case GENERIC_PASSTHROUGH:
+               if (v3_dev_hook_io(dev, i, 
+                                  &generic_read_port_passthrough, 
+                                  &generic_write_port_passthrough) == -1) { 
+                   PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
+                   return -1;
+               }
+               break;
+           case  GENERIC_IGNORE:
+               if (v3_dev_hook_io(dev, i, 
+                                  &generic_read_port_ignore, 
+                                  &generic_write_port_ignore) == -1) { 
+                   PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook port 0x%x (already hooked?)\n", state->name, i);
+                   return -1;
+               }
+               break;
+           default:
+               PrintError(VM_NONE, VCORE_NONE, "generic (%s): huh?\n", state->name);
+               break;
        }
-      }
-
-      list_del(&(cur->range_link));
-      state->num_irq_ranges--;
-      V3_Free(cur);
     }
-  } else {
-    PrintDebug("generic: unhooking irqs not supported\n");
-  }
+    
+    return 0;
+}
 
 
-  if (MEM_HOOKS) {
-    struct mem_range * tmp;
-    struct mem_range * cur;
+static int add_mem_range(struct vm_device * dev, addr_t start, addr_t end, generic_mode_t mode) {
+
+    struct generic_internal *state = (struct generic_internal *) dev->private_data;
+
+    PrintDebug(VM_NONE, VCORE_NONE, "generic (%s): adding memory range 0x%p to 0x%p as %s\n", state->name,
+              (void*)start, (void*)end, 
+              (mode == GENERIC_PRINT_AND_PASSTHROUGH) ? "print-and-passthrough" : 
+              (mode == GENERIC_PRINT_AND_IGNORE) ? "print-and-ignore" :
+              (mode == GENERIC_PASSTHROUGH) ? "passthrough" :
+              (mode == GENERIC_IGNORE) ? "ignore" : "UNKNOWN");
+       
+    switch (mode) { 
+       case GENERIC_PRINT_AND_PASSTHROUGH:
+           if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, start, end+1, 
+                                &generic_read_mem_print_and_passthrough, 
+                                &generic_write_mem_print_and_passthrough, dev) == -1) { 
+               PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
+               return -1;
+           }
+           break;
+           
+       case GENERIC_PRINT_AND_IGNORE:
+           if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, start, end+1, 
+                                &generic_read_mem_print_and_ignore, 
+                                &generic_write_mem_print_and_ignore, dev) == -1) { 
+               PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
+               return -1;
+           }
+           break;
+
+       case GENERIC_PASSTHROUGH:
+           if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, start, end+1, 
+                                &generic_read_mem_passthrough, 
+                                &generic_write_mem_passthrough, dev) == -1) { 
+               PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
+               return -1;
+           }
+           break;
+           
+       case  GENERIC_IGNORE:
+           if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, start, end+1, 
+                                &generic_read_mem_ignore, 
+                                &generic_write_mem_ignore, dev) == -1) { 
+               PrintError(VM_NONE, VCORE_NONE, "generic (%s): can't hook memory region 0x%p to 0x%p\n", state->name,(void*)start,(void*)end);
+               return -1;
+           }
+           break;
+       default:
+           PrintError(VM_NONE, VCORE_NONE, "generic (%s): huh?\n",state->name);
+           break;
+    }
     
-    list_for_each_entry_safe(cur, tmp, &(state->mem_list), range_link) {
+    return 0;
+}
 
-      PrintDebug("generic: unhooking addresses 0x%x to 0x%x\n",
-                cur->start, cur->end); 
 
-      if (dev_unhook_mem(dev, cur->start, cur->end)) {
-       PrintDebug("generic: Can't unhook addresses 0x%x to 0x%x (already unhooked?)\n",
-                  cur->start, cur->end); 
-      }
+#if 1
 
-      list_del(&(cur->range_link));
-      state->num_mem_ranges--;
-      V3_Free(cur);
-    }
-  } else {
-    PrintDebug("generic: unhooking addresses not supported\n");
-  }
-  
+//This is a hack for host device testing and will be removed
 
-  if (PORT_HOOKS) {
-    struct port_range * tmp;
-    struct port_range * cur;
-    
-    list_for_each_entry_safe(cur, tmp, &(state->port_list), range_link) {
-      uint_t i;
-
-      PrintDebug("generic: unhooking ports 0x%x to 0x%x\n",
-                  cur->start, cur->end);
-               
-      for (i = cur->start; i <= cur->end; i++) {
-       if (dev_unhook_io(dev, i)) {
-         PrintDebug("generic: can't unhook port 0x%x (already unhooked?)\n", i);
+static int osdebug_hcall(struct guest_info *core, uint_t hcall_id, void * priv_data) 
+{
+    struct generic_internal * state = (struct generic_internal *)priv_data;
+
+    int msg_len = core->vm_regs.rcx;
+    addr_t msg_gpa = core->vm_regs.rbx;
+    int buf_is_va = core->vm_regs.rdx;
+    int i;
+    uint8_t c; 
+    PrintDebug(core->vm_info, core, "generic (%s): handling hypercall (len=%d) as sequence of port writes\n",
+              state->name, msg_len);
+
+
+    for (i=0;i<msg_len;i++) { 
+       if (buf_is_va == 1) {
+           if (v3_read_gva_memory(core, msg_gpa+i, 1, &c) != 1) {
+               PrintError(core->vm_info, core, "generic (%s): could not read debug message\n",state->name);
+               return -1;
+           }
+       } else {
+           if (v3_read_gpa_memory(core, msg_gpa+i, 1, &c) != 1) { 
+               PrintError(core->vm_info, core, "generic (%s): Could not read debug message\n",state->name);
+               return -1;
+           }
        }
-      }
-
-      list_del(&(cur->range_link));
-      state->num_port_ranges--;
-      V3_Free(cur);
-    }
-  } else {
-    PrintDebug("generic: unhooking ports not supported\n");
-  }
+       if (generic_write_port_print_and_passthrough(core,0xc0c0,&c,1,priv_data)!=1) { 
+           PrintError(core->vm_info, core, "generic (%s): write port passthrough failed\n",state->name);
+           return -1;
+       }
+    }  
 
+    return 0;
+}
 
+#endif
 
-  generic_reset_device(dev);
-  return 0;
+#ifdef V3_CONFIG_HOST_DEVICE
+static void generic_intr_update_callback(v3_host_dev_t hdev, v3_guest_dev_t gdev, uint8_t irq, int raise)
+{
+    if (gdev) { 
+       struct vm_device *dev = (struct vm_device *) gdev;
+       if (raise) { 
+           v3_raise_irq(dev->vm,irq);
+       } else {
+           v3_lower_irq(dev->vm,irq);
+       }
+    }
 }
+#endif
 
+/*
+   The device can be used to forward to the underlying physical device 
+   or to a host device that has a given url.   Both memory and ports can be forwarded as
 
+        GENERIC_PASSTHROUGH => send writes and reads to physical device or host
+        GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
 
+        GENERIC_IGNORE => ignore writes and reads
+        GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
 
 
-static struct vm_device_ops dev_ops = { 
-  .init = generic_init_device, 
-  .deinit = generic_deinit_device,
-  .reset = generic_reset_device,
-  .start = generic_start_device,
-  .stop = generic_stop_device,
-};
+       The purpose of the "PRINT" variants is to make it easy to spy on
+       device interactions (although you will not see DMA or interrupts)
 
 
+   <device class="generic" id="my_id" 
+         empty | forward="physical_device" or forward="host_device" hostdev="url">
 
+  (empty implies physical_dev)
 
-int v3_generic_add_port_range(struct vm_device * dev, uint_t start, uint_t end, uint_t type) {
+     <ports>
+         <start>portno1</start>
+         <end>portno2</end>   => portno1 through portno2 (inclusive)
+         <mode>PRINT_AND_PASSTHROUGH</mode>  (as above)
+     </ports>
 
-  if (PORT_HOOKS) {
-    struct generic_internal * state = (struct generic_internal *)(dev->private_data);
+     <memory>
+         <start>gpa1</start>
+         <end>gpa2</end>     => memory addreses gpa1 through gpa2 (inclusive); page granularity
+         <mode> ... as above </mode>
+     </memory>
 
-    struct port_range * range = (struct port_range *)V3_Malloc(sizeof(struct port_range));
-    range->start = start;
-    range->end = end;
-    range->type = type;
-    
-      
-    PrintDebug("generic: Adding Port Range: 0x%x to 0x%x as %x\n", 
-              range->start, range->end, 
-              (range->type == GENERIC_PRINT_AND_PASSTHROUGH) ? "print-and-passthrough" : "print-and-ignore");
-    
-    list_add(&(range->range_link), &(state->port_list));
-    state->num_port_ranges++;
-  } else {
-    PrintDebug("generic: hooking IO ports not supported\n");
-    return -1;
-  }
+*/
 
-  return 0;
-}
+static int generic_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
+    struct generic_internal * state = NULL;
+    char * dev_id = v3_cfg_val(cfg, "ID");
+    char * forward = v3_cfg_val(cfg, "forward");
+#ifdef V3_CONFIG_HOST_DEVICE
+    char * host_dev = v3_cfg_val(cfg, "hostdev");
+#endif
+    v3_cfg_tree_t * port_cfg = v3_cfg_subtree(cfg, "ports");
+    v3_cfg_tree_t * mem_cfg = v3_cfg_subtree(cfg, "memory");
 
-int v3_generic_add_mem_range(struct vm_device * dev, void * start, void * end, uint_t type) {
 
-  if (MEM_HOOKS) {
-    struct generic_internal * state = (struct generic_internal *)(dev->private_data);
+    state = (struct generic_internal *)V3_Malloc(sizeof(struct generic_internal));
+
+    if (state == NULL) {
+       PrintError(vm, VCORE_NONE, "generic (%s): could not allocate generic state\n",dev_id);
+       return -1;
+    }
     
-    struct mem_range * range = (struct mem_range *)V3_Malloc(sizeof(struct mem_range));
-    range->start = start;
-    range->end = end;
-    range->type = type;
+    memset(state, 0, sizeof(struct generic_internal));
+    strncpy(state->name,dev_id,MAX_NAME);
+    state->name[MAX_NAME-1] = 0;
+
+    if (!forward) { 
+       state->forward_type=GENERIC_PHYSICAL;
+    } else {
+       if (!strcasecmp(forward,"physical_device")) { 
+           state->forward_type=GENERIC_PHYSICAL;
+       } else if (!strcasecmp(forward,"host_device")) { 
+#ifdef V3_CONFIG_HOST_DEVICE
+           state->forward_type=GENERIC_HOST;
+#else
+           PrintError(vm, VCORE_NONE, "generic (%s): cannot configure host device since host device support is not built in\n", state->name);
+           V3_Free(state);
+           return -1;
+#endif
+       } else {
+           PrintError(vm, VCORE_NONE, "generic (%s): unknown forwarding type \"%s\"\n", state->name, forward);
+           V3_Free(state);
+           return -1;
+       }
+    }
     
-    list_add(&(range->range_link), &(state->port_list));
-    state->num_mem_ranges++;
-  } else {
-    PrintDebug("generic: hooking memory not supported\n");
-    return -1;
-  }
+    struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, state);
 
-  return 0;
-}
+    if (dev == NULL) {
+       PrintError(vm, VCORE_NONE, "generic: could not attach device %s\n", state->name);
+       V3_Free(state);
+       return -1;
+    }
 
+    state->dev=dev;
+
+
+#ifdef V3_CONFIG_HOST_DEVICE
+    if (state->forward_type==GENERIC_HOST) { 
+       if (!host_dev) { 
+           PrintError(vm, VCORE_NONE, "generic (%s): host forwarding requested, but no host device given\n", state->name);
+           v3_remove_device(dev);
+           return -1;
+       } else {
+           state->host_dev = v3_host_dev_open(host_dev,V3_BUS_CLASS_DIRECT,dev,generic_intr_update_callback,vm);
+           if (!(state->host_dev)) { 
+               PrintError(vm, VCORE_NONE, "generic (%s): unable to open host device \"%s\"\n", state->name,host_dev);
+               v3_remove_device(dev);
+               return -1;
+           } else {
+               PrintDebug(vm, VCORE_NONE, "generic (%s): successfully attached host device \"%s\"\n", state->name,host_dev);
+           }
+       }
+    }
+#endif
 
-int v3_generic_add_irq_range(struct vm_device * dev, uint_t start, uint_t end, uint_t type) {
+    PrintDebug(vm, VCORE_NONE, "generic (%s): init_device\n", state->name);
 
-  if (IRQ_HOOKS) {
-    struct generic_internal * state = (struct generic_internal *)(dev->private_data);
-    
-    struct irq_range * range = (struct irq_range *)V3_Malloc(sizeof(struct irq_range));
-    range->start = start;
-    range->end = end;
-    range->type = type;
-    
-    list_add(&(range->range_link), &(state->port_list));
-    state->num_irq_ranges++;
-  } else {
-    PrintDebug("generic: hooking IRQs not supported\n");
-    return -1;
-  }
+    // scan port list....
+    while (port_cfg) {
+       uint16_t start = atox(v3_cfg_val(port_cfg, "start"));
+       uint16_t end = atox(v3_cfg_val(port_cfg, "end"));
+       char * mode_str = v3_cfg_val(port_cfg, "mode");
+    if (!mode_str) {
+        PrintError(vm, VCORE_NONE, "generic (%s): error getting port mode\n", state->name);
+        return -1;
+    }
+       generic_mode_t mode = GENERIC_IGNORE;
+       if (strcasecmp(mode_str, "print_and_ignore") == 0) {
+           mode = GENERIC_PRINT_AND_IGNORE;
+       } else if (strcasecmp(mode_str, "print_and_passthrough") == 0) {
+           mode = GENERIC_PRINT_AND_PASSTHROUGH;
+       } else if (strcasecmp(mode_str, "passthrough") == 0) {
+           mode = GENERIC_PASSTHROUGH;
+       } else if (strcasecmp(mode_str, "ignore") == 0) {
+           mode = GENERIC_IGNORE;
+       } else {
+           PrintError(vm, VCORE_NONE, "generic (%s): invalid mode %s in adding ports\n", state->name, mode_str);
+           v3_remove_device(dev);
+           return -1;
+       }
+       
+       
+       if (add_port_range(dev, start, end, mode) == -1) {
+           PrintError(vm, VCORE_NONE, "generic (%s): could not add port range 0x%x to 0x%x\n", state->name, start, end);
+           v3_remove_device(dev);
+           return -1;
+       }
 
-  return 0;
-}
+       port_cfg = v3_cfg_next_branch(port_cfg);
+    }
 
+    // scan memory list....
+    while (mem_cfg) {
+       addr_t  start = atox(v3_cfg_val(mem_cfg, "start"));
+       addr_t end = atox(v3_cfg_val(mem_cfg, "end"));
+       char * mode_str = v3_cfg_val(mem_cfg, "mode");
+       generic_mode_t mode = GENERIC_IGNORE;
+
+       if (strcasecmp(mode_str, "print_and_ignore") == 0) {
+           mode = GENERIC_PRINT_AND_IGNORE;
+       } else if (strcasecmp(mode_str, "print_and_passthrough") == 0) {
+           mode = GENERIC_PRINT_AND_PASSTHROUGH;
+       } else if (strcasecmp(mode_str, "passthrough") == 0) {
+           mode = GENERIC_PASSTHROUGH;
+       } else if (strcasecmp(mode_str, "ignore") == 0) {
+           mode = GENERIC_IGNORE;
+       } else {
+           PrintError(vm, VCORE_NONE, "generic (%s): invalid mode %s for adding memory\n", state->name, mode_str);
+           v3_remove_device(dev);
+           return -1;
+       }
 
+       if (state->num_mem_hooks>=MAX_MEM_HOOKS) { 
+           PrintError(vm, VCORE_NONE, "generic (%s): cannot add another memory hook (increase MAX_MEM_HOOKS)\n", state->name);
+           v3_remove_device(dev);
+           return -1;
+       }
+       
+       if (add_mem_range(dev, start, end, mode) == -1) {
+           PrintError(vm, VCORE_NONE, "generic (%s): could not add memory range 0x%p to 0x%p\n", state->name, (void*)start, (void*)end);
+           v3_remove_device(dev);
+           return -1;
+       }
+       
+       state->mem_hook[state->num_mem_hooks] = start;
+       state->num_mem_hooks++;
 
-struct vm_device * create_generic() {
-  struct generic_internal * generic_state = (struct generic_internal *)V3_Malloc(sizeof(struct generic_internal));
-  
-  generic_state->num_port_ranges = 0;
-  generic_state->num_mem_ranges = 0;
-  generic_state->num_irq_ranges = 0;
+       mem_cfg = v3_cfg_next_branch(port_cfg);
+    }
 
-  INIT_LIST_HEAD(&(generic_state->port_list));
-  INIT_LIST_HEAD(&(generic_state->mem_list));
-  INIT_LIST_HEAD(&(generic_state->irq_list));
+#if 1
+    // hack for os debug testing
+    if (strcasecmp(state->name,"os debug")==0) { 
+       PrintDebug(vm, VCORE_NONE, "generic (%s): adding hypercall for os debug device\n", state->name);
+       v3_register_hypercall(vm,0xc0c0,osdebug_hcall,state);
+    }
+#endif
     
-  struct vm_device * device = create_device("GENERIC", &dev_ops, generic_state);
+    PrintDebug(vm, VCORE_NONE, "generic (%s): initialization complete\n", state->name);
 
-  return device;
+    return 0;
 }
+
+device_register("GENERIC", generic_init)