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 d9bf3ea..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 <geekos/io.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 {
-  generic_port_range_type    * port_ranges;
-  uint_t                     num_port_ranges;
-  generic_address_range_type * address_ranges;
-  uint_t                     num_address_ranges;
-  generic_irq_range_type     * irq_ranges;
-  uint_t                     num_irq_ranges;
-};
-
-
-
-int generic_reset_device(struct vm_device * dev)
-{
-  PrintDebug("generic: reset device\n");
-  return 0;
-
-}
-
-
+    enum {GENERIC_PHYSICAL, GENERIC_HOST} forward_type;
+#ifdef V3_CONFIG_HOST_DEVICE
+    v3_host_dev_t                         host_dev;
+#endif
+    struct vm_device                      *dev; // me
 
+    char                                  name[MAX_NAME];
+    
+    uint32_t                              num_mem_hooks;
+    addr_t                                mem_hook[MAX_MEM_HOOKS];
+};
 
 
-int generic_start_device(struct vm_device * dev)
-{
-  PrintDebug("generic: start device\n");
-  return 0;
-}
 
 
-int generic_stop_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: 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:
+                   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");
 
-int generic_write_port_passthrough(ushort_t port,
-                                  void * src, 
-                                  uint_t length,
-                                  struct vm_device * dev)
-{
-  uint_t i;
+    PrintDebug(core->vm_info, core, "generic (%s): writing 0x", state->name);
 
-  PrintDebug("generic: writing 0x");
-
-  for (i = 0; i < length; i++) { 
-    PrintDebug("%x", ((uchar_t*)src)[i]);
-  }
-  
-  PrintDebug(" to port 0x%x ... ", port);
-
-
-  switch (length) {
-  case 1:
-    Out_Byte(port, ((uchar_t*)src)[0]);
-    break;
-  case 2:
-    Out_Word(port, ((ushort_t*)src)[0]);
-    break;
-  case 4:
-    Out_DWord(port, ((uint_t*)src)[0]);
-    break;
-  default:
     for (i = 0; i < length; i++) { 
-      Out_Byte(port, ((uchar_t*)src)[i]);
+       PrintDebug(core->vm_info, core, "%x", ((uint8_t *)src)[i]);
     }
-  } //switch length
+  
+    PrintDebug(core->vm_info, core, " to port 0x%x ... ", port);
 
+    rc=generic_write_port_passthrough(core,port,src,length,priv_data);
 
-  PrintDebug(" done\n");
+    PrintDebug(core->vm_info, core, " done\n");
   
-  return length;
+    return rc;
 }
 
-int generic_read_port_passthrough(ushort_t port,
-                                 void * src, 
-                                 uint_t length,
-                                 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) 
 {
-  uint_t i;
+    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;
+    }
 
-  PrintDebug("generic: reading 0x%x bytes from port 0x%x ...", length, port);
+    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;
 
-    switch (length) {
-    case 1:
-      ((uchar_t*)src)[0] = In_Byte(port);
-      break;
-    case 2:
-      ((ushort_t*)src)[0] = In_Word(port);
-      break;
-    case 4:
-      ((uint_t*)src)[0] = In_DWord(port);
-      break;
-    default:
-      for (i = 0; i < length; i++) { 
-       ((uchar_t*)src)[i] = In_Byte(port);
-      }
-    }//switch length
+#ifdef V3_CONFIG_DEBUG_GENERIC
+    struct generic_internal *state = (struct generic_internal *) priv_data;
+#endif
 
-  PrintDebug(" done ... read 0x");
+    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");
 
-  for (i = 0; i < length; i++) { 
-    PrintDebug("%x", ((uchar_t*)src)[i]);
-  }
 
-  PrintDebug("\n");
+    rc=generic_read_port_passthrough(core,port,src,length,priv_data);
 
-  return length;
+    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");
+    }
+    
+    return rc;
 }
 
-int generic_write_port_ignore(ushort_t port,
-                             void * src, 
-                             uint_t length,
-                             struct vm_device * dev)
-{
-  uint_t i;
 
-  PrintDebug("generic: writing 0x");
+static int generic_read_port_ignore(struct guest_info * core, uint16_t port, void * src, 
+                                   uint_t length, void * priv_data) {
 
-  for (i = 0; i < length; i++) { 
-    PrintDebug("%x", ((uchar_t*)src)[i]);
-  }
-  
-  PrintDebug(" to port 0x%x ... ", port);
+    memset((uint8_t *)src, 0, length);
 
-  PrintDebug(" ignored\n");
-  return length;
+    return length;
 }
 
-int generic_read_port_ignore(ushort_t port,
-                            void * src, 
-                            uint_t length,
-                            struct vm_device * dev)
-{
+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
+
+    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("generic: reading 0x%x bytes from port 0x%x ...", length, port);
 
-  memset((char*)src, 0, length);
-  PrintDebug(" ignored (return zeroed buffer)\n");
+    memset((uint8_t *)src, 0, length);
+    PrintDebug(core->vm_info, core, " ignored (return zeroed buffer)\n");
 
-  return length;
+    return length;
 }
 
+static int generic_write_port_ignore(struct guest_info * core, uint16_t port, void * src, 
+                                    uint_t length, void * priv_data) {
 
+    return length;
+}
 
-int generic_interrupt(uint_t irq, struct vm_device * dev) {
-  PrintDebug("generic: interrupt 0x%x - injecting into VM\n", irq);
+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;
 
-  dev->vm->vm_ops.raise_irq(dev->vm, irq);
+#ifdef V3_CONFIG_DEBUG_GENERIC
+    struct generic_internal *state = (struct generic_internal *) priv_data;
+#endif
 
-  return 0;
-}
+    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");
 
-int generic_init_device(struct vm_device * dev) {
-  struct generic_internal * state = (struct generic_internal *)(dev->private_data);
-  uint_t i, j;
+    return length;
+}
 
-  PrintDebug("generic: init_device\n");
 
-  // Would read state here
 
-  generic_reset_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;
+    }
+}
 
-  for (i = 0; i < state->num_port_ranges; i++) { 
+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
 
-    PrintDebug("generic: hooking ports 0x%x to 0x%x as %x\n", 
-              state->port_ranges[i][0], state->port_ranges[i][1], 
-              (state->port_ranges[i][2] == GENERIC_PRINT_AND_PASSTHROUGH) ? "print-and-passthrough" : "print-and-ignore");
+    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);
 
-    if (PORT_HOOKS) { // This is a runtime conditional on a #define
-      for (j = state->port_ranges[i][0]; j <= state->port_ranges[i][1]; j++) { 
-       if (state->port_ranges[i][2] == GENERIC_PRINT_AND_PASSTHROUGH) { 
+    PrintDebug(core->vm_info, core, "done\n");
+    
+    return rc;
+}
 
-         if (dev_hook_io(dev, j, &generic_read_port_passthrough, &generic_write_port_passthrough)) { 
-           PrintDebug("generic: can't hook port 0x%x (already hooked?)\n", j);
-         }
+static int generic_write_mem_ignore(struct guest_info * core, 
+                                   addr_t              gpa,
+                                   void              * src,
+                                   uint_t              len,
+                                   void              * priv)
+{
+    return len;
+}
 
-       } else if (state->port_ranges[i][2] == GENERIC_PRINT_AND_IGNORE) { 
+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
 
-         if (dev_hook_io(dev, j, &generic_read_port_ignore, &generic_write_port_ignore)) { 
-           PrintDebug("generic: can't hook port 0x%x (already hooked?)\n", j);
-         }
+    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;
+}
 
-       } 
-      }
-    } else {
-      PrintDebug("generic: hooking ports not supported\n");
+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;
+}
 
-  }
-
-  for (i = 0; i < state->num_address_ranges; i++) { 
+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
 
-    PrintDebug("generic: hooking addresses 0x%x to 0x%x\n", 
-              state->address_ranges[i][0], state->address_ranges[i][1]); 
+    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);
 
-    if (MEM_HOOKS) { // This is a runtime conditional on a #define
-      if (dev_hook_mem(dev, state->address_ranges[i][0], state->address_ranges[i][1])) {
-       PrintDebug("generic: Can't hook addresses 0x%x to 0x%x (already hooked?)\n",
-                  state->address_ranges[i][0], state->address_ranges[i][1]); 
-      }
-    } else {
-      PrintDebug("generic: hooking addresses not supported\n");
-    }
-  }
+    PrintDebug(core->vm_info, core, "done - read %d bytes\n", rc);
+    
+    return rc;
+}
 
-  for (i = 0; i < state->num_irq_ranges; i++) { 
+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("generic: hooking irqs 0x%x to 0x%x\n",
-              state->irq_ranges[i][0], state->irq_ranges[i][1]);
+    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 (IRQ_HOOKS) { // This is a runtime conditional on a #define
-      for (j = state->irq_ranges[i][0]; j <= state->irq_ranges[i][1]; j++) { 
-       if (dev_hook_irq(dev, j, &generic_interrupt)) { 
-         PrintDebug("generic: can't hook irq  0x%x (already hooked?)\n", j);
-       }
-      }
-    } else {
-      PrintDebug("generic: hooking irqs not supported\n");
-    }
+    memset((uint8_t *)dst, 0, len);
 
-  }
+    PrintDebug(core->vm_info, core, "returning zeros\n");
 
-  return 0;
+    return len;
 }
 
-int generic_deinit_device(struct vm_device * dev) {
-  struct generic_internal *state = (struct generic_internal *)(dev->private_data);
-  uint_t i, j;
 
-  PrintDebug("generic: deinit_device\n");
+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;
+}
 
 
-  for (i = 0; i < state->num_irq_ranges; i++) { 
+static int generic_free(struct generic_internal * state) {
+    int i;
+    
+    PrintDebug(VM_NONE,VCORE_NONE, "generic (%s): deinit_device\n", state->name);
     
-    PrintDebug("generic: unhooking irqs 0x%x to 0x%x\n", 
-              state->irq_ranges[i][0], state->irq_ranges[i][1]);
+#ifdef V3_CONFIG_HOST_DEVICE
+    if (state->host_dev) { 
+       v3_host_dev_close(state->host_dev);
+       state->host_dev=0;
+    }
+#endif
+    
+    // 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;
+       }
+    }
+            
+    V3_Free(state);
+    return 0;
+}
 
-    if (IRQ_HOOKS) { // This is a runtime conditional on a #define
-      for (j = state->irq_ranges[i][0]; j <= state->irq_ranges[i][1]; j++) { 
 
-       if (dev_unhook_irq(dev, j)) {
-         PrintDebug("generic: can't unhook irq 0x%x (already unhooked?)\n",j);
-       }
 
-      }
-    } else {
-      PrintDebug("generic: unhooking irqs not supported\n");
-    }
-  }
 
-  for (i = 0; i < state->num_address_ranges; i++) { 
 
-    PrintDebug("generic: unhooking addresses 0x%x to 0x%x\n",
-              state->address_ranges[i][0], state->address_ranges[i][1]); 
+static struct v3_device_ops dev_ops = { 
+    .free = (int (*)(void *))generic_free, 
+};
 
-    if (MEM_HOOKS) {
-      if (dev_unhook_mem(dev, state->address_ranges[i][0], state->address_ranges[i][1])) {
 
-       PrintDebug("generic: Can't unhook addresses 0x%x to 0x%x (already unhooked?)\n",
-                  state->address_ranges[i][0], state->address_ranges[i][1]); 
 
-      }
-    } else {
-      PrintDebug("generic: unhooking addresses not supported\n");
+
+static int add_port_range(struct vm_device * dev, uint_t start, uint_t end, generic_mode_t mode) {
+    uint_t i = 0;
+
+    struct generic_internal *state = (struct generic_internal *) dev->private_data;
+
+    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;
+       }
     }
-  }
+    
+    return 0;
+}
 
-  for (i = 0; i < state->num_port_ranges; i++) { 
-    PrintDebug("generic: unhooking ports 0x%x to 0x%x\n",
-              state->port_ranges[i][0], state->port_ranges[i][1]);
 
-    if (PORT_HOOKS) {
-      for (j = state->port_ranges[i][0]; j <= state->port_ranges[i][1]; j++) { 
-       if (dev_unhook_io(dev, j)) {
-         PrintDebug("generic: can't unhook port 0x%x (already unhooked?)\n", j);
-       }
-      }
-    } else {
-      PrintDebug("generic: unhooking ports not supported\n");
+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;
     }
+    
+    return 0;
+}
+
 
-  }
+#if 1
+
+//This is a hack for host device testing and will be removed
+
+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;
+           }
+       }
+       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;
+       }
+    }  
 
-  generic_reset_device(dev);
-  return 0;
+    return 0;
 }
 
+#endif
 
+#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
 
-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,
-};
+        GENERIC_IGNORE => ignore writes and reads
+        GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
 
 
+       The purpose of the "PRINT" variants is to make it easy to spy on
+       device interactions (although you will not see DMA or interrupts)
 
 
-struct vm_device *create_generic(generic_port_range_type    port_ranges[], 
-                                generic_address_range_type address_ranges[],
-                                generic_irq_range_type     irq_ranges[])
-{
-  struct generic_internal * generic_state = (struct generic_internal *)V3_Malloc(sizeof(struct generic_internal));
-  int i;
-  uint_t num_port_ranges;
-  uint_t num_address_ranges;
-  uint_t num_irq_ranges;
+   <device class="generic" id="my_id" 
+         empty | forward="physical_device" or forward="host_device" hostdev="url">
 
+  (empty implies physical_dev)
 
-  num_port_ranges = 0;
+     <ports>
+         <start>portno1</start>
+         <end>portno2</end>   => portno1 through portno2 (inclusive)
+         <mode>PRINT_AND_PASSTHROUGH</mode>  (as above)
+     </ports>
 
-  if (port_ranges != NULL) { 
-    i = 0;
+     <memory>
+         <start>gpa1</start>
+         <end>gpa2</end>     => memory addreses gpa1 through gpa2 (inclusive); page granularity
+         <mode> ... as above </mode>
+     </memory>
 
-    while ((port_ranges[i] != NULL) && 
-          ( !( (port_ranges[i][0] == 0) && 
-               (port_ranges[i][1] == 0) && 
-               (port_ranges[i][2] == 0)) ) ) 
-      {
-       num_port_ranges++; 
-       i++; 
-      }
-  }
+*/
 
-  
-  num_address_ranges = 0;
-
-  if (address_ranges != NULL) { 
-    i = 0;
-
-    while ((address_ranges[i] != NULL)  && 
-          ( !( (address_ranges[i][0] == 0) && 
-               (address_ranges[i][1] == 0) && 
-               (address_ranges[i][2] == 0)) ) ) 
-      { 
-       num_address_ranges++; 
-       i++; 
-      }
-  }
-
-  num_irq_ranges = 0;
-
-  if (irq_ranges != NULL) { 
-    i = 0;
-
-    while ((irq_ranges[i] != NULL) && 
-          ( !( (irq_ranges[i][0] == 0) && 
-               (irq_ranges[i][1] == 0) && 
-               (irq_ranges[i][2] == 0)) ) ) 
-      { 
-       num_irq_ranges++; 
-       i++; 
-      }
-  }
-    
+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");
 
-  generic_state->num_port_ranges = num_port_ranges;
 
-  if (num_port_ranges > 0) { 
-   generic_state->port_ranges = V3_Malloc(sizeof(generic_address_range_type) * num_port_ranges);
-   memcpy(generic_state->port_ranges, port_ranges, 
-         (sizeof(generic_port_range_type) * num_port_ranges));
-   
-  } else {
-    generic_state->port_ranges = NULL;
-  }
+    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;
+    }
+    
+    memset(state, 0, sizeof(struct generic_internal));
+    strncpy(state->name,dev_id,MAX_NAME);
+    state->name[MAX_NAME-1] = 0;
 
-  generic_state->num_address_ranges = num_address_ranges;
+    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;
+       }
+    }
+    
+    struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, state);
 
-  if (num_address_ranges > 0) { 
+    if (dev == NULL) {
+       PrintError(vm, VCORE_NONE, "generic: could not attach device %s\n", state->name);
+       V3_Free(state);
+       return -1;
+    }
 
-    generic_state->address_ranges = V3_Malloc(sizeof(generic_address_range_type) * num_address_ranges);
-    memcpy(generic_state->address_ranges, address_ranges, 
-          (sizeof(generic_address_range_type) * num_address_ranges));
+    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
 
-  } else {
-    generic_state->address_ranges = NULL;
-  }
+    PrintDebug(vm, VCORE_NONE, "generic (%s): init_device\n", state->name);
 
+    // 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;
+       }
 
-  generic_state->num_irq_ranges = num_irq_ranges;
+       port_cfg = v3_cfg_next_branch(port_cfg);
+    }
 
-  if (num_irq_ranges > 0) { 
+    // 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;
+       }
 
-    generic_state->irq_ranges = V3_Malloc(sizeof(generic_address_range_type) * num_irq_ranges);
-    memcpy(generic_state->irq_ranges, irq_ranges, 
-          (sizeof(generic_irq_range_type) * num_port_ranges));
+       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++;
 
-  } else {
-    generic_state->irq_ranges = NULL;
-  }
+       mem_cfg = v3_cfg_next_branch(port_cfg);
+    }
 
-  struct vm_device *device = create_device("GENERIC", &dev_ops, generic_state);
+#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
+    
+    PrintDebug(vm, VCORE_NONE, "generic (%s): initialization complete\n", state->name);
 
-  return device;
+    return 0;
 }
+
+device_register("GENERIC", generic_init)