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 11cad40..1e06a80 100644 (file)
-#include <devices/generic.h>
+/* 
+ * 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 <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>
 
-#define GENERIC_DEBUG 1
+#ifdef V3_CONFIG_HOST_DEVICE
+#include <interfaces/vmm_host_dev.h>
+#endif
 
-#if GENERIC_DEBUG
-#define GENERIC_DEBUG_PRINT(first, rest...) PrintDebug(first, ##rest) 
-#else
-#define GENERIC_DEBUG_PRINT(first, rest...)
+#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;
+    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];
 };
 
 
-  
-    
 
 
+static int generic_write_port_passthrough(struct guest_info * core, 
+                                         uint16_t port, 
+                                         void * src, 
+                                         uint_t length, 
+                                         void * priv_data) 
+{
+    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);
+
+    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_reset_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) 
 {
-  GENERIC_DEBUG_PRINT("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:
+                   ((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_start_device(struct vm_device *dev)
-{
-  GENERIC_DEBUG_PRINT("generic: start device\n");
-  return 0;
+    rc=generic_read_port_passthrough(core,port,src,length,priv_data);
+
+    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_stop_device(struct vm_device *dev)
-{
-  GENERIC_DEBUG_PRINT("generic: stop device\n");
-  return 0;
+static int generic_read_port_ignore(struct guest_info * core, uint16_t port, void * src, 
+                                   uint_t length, void * priv_data) {
+
+    memset((uint8_t *)src, 0, length);
+
+    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
 
+    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;
+    memset((uint8_t *)src, 0, length);
+    PrintDebug(core->vm_info, core, " ignored (return zeroed buffer)\n");
 
-  GENERIC_DEBUG_PRINT("generic: writing 0x");
+    return length;
+}
 
-  for (i = 0; i < length; i++) { 
-    GENERIC_DEBUG_PRINT("%x", ((uchar_t*)src)[i]);
-  }
-  
-  GENERIC_DEBUG_PRINT(" 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]);
-    }
-  }
+static int generic_write_port_ignore(struct guest_info * core, uint16_t port, void * src, 
+                                    uint_t length, void * priv_data) {
 
-  GENERIC_DEBUG_PRINT(" done\n");
-  
-  return length;
+    return length;
 }
 
-int generic_read_port_passthrough(ushort_t port,
-                                 void * src, 
-                                 uint_t length,
-                                 struct vm_device * dev)
-{
-  uint_t i;
-
-  GENERIC_DEBUG_PRINT("generic: reading 0x%x bytes from port 0x%x ...", length, port);
-
-  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:
+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;
+
+#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");
+    
+    memset((uint8_t *)src, 0, length);
+    PrintDebug(core->vm_info, core, " ignored - data was: 0x");
+
     for (i = 0; i < length; i++) { 
-      ((uchar_t*)src)[i] = In_Byte(port);
+       PrintDebug(core->vm_info, core, "%x", ((uint8_t *)src)[i]);
     }
-  }
+    
+    PrintDebug(core->vm_info, core, "\n");
 
-  GENERIC_DEBUG_PRINT(" done ... read 0x");
+    return length;
+}
 
-  for (i = 0; i < length; i++) { 
-    GENERIC_DEBUG_PRINT("%x", ((uchar_t*)src)[i]);
-  }
 
-  GENERIC_DEBUG_PRINT("\n");
 
-  return length;
+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;
+    }
 }
 
-int generic_write_port_ignore(ushort_t port,
-                             void * src, 
-                             uint_t length,
-                             struct vm_device * dev)
+static int generic_write_mem_print_and_passthrough(struct guest_info * core, 
+                                                  addr_t              gpa,
+                                                  void              * src,
+                                                  uint_t              len,
+                                                  void              * priv)
 {
-  uint_t i;
-
-  GENERIC_DEBUG_PRINT("generic: writing 0x");
+#ifdef V3_CONFIG_DEBUG_GENERIC
+    struct vm_device *dev = (struct vm_device *) priv;
+    struct generic_internal *state = (struct generic_internal *) dev->private_data;
+#endif
 
-  for (i = 0; i < length; i++) { 
-    GENERIC_DEBUG_PRINT("%x", ((uchar_t*)src)[i]);
-  }
-  
-  GENERIC_DEBUG_PRINT(" to port 0x%x ... ", port);
+    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);
 
-  GENERIC_DEBUG_PRINT(" ignored\n");
-  
-  return length;
+    PrintDebug(core->vm_info, core, "done\n");
+    
+    return rc;
 }
 
-int generic_read_port_ignore(ushort_t port,
-                            void * src, 
-                            uint_t length,
-                            struct vm_device * dev)
+static int generic_write_mem_ignore(struct guest_info * core, 
+                                   addr_t              gpa,
+                                   void              * src,
+                                   uint_t              len,
+                                   void              * priv)
 {
+    return len;
+}
 
-  GENERIC_DEBUG_PRINT("generic: reading 0x%x bytes from port 0x%x ...", length, port);
+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
 
-  memset((char*)src,0,length);
-  GENERIC_DEBUG_PRINT(" ignored (return zeroed buffer)\n");
+    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 length;
+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
 
+    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);
 
-int generic_interrupt(uint_t irq,
-                     struct vm_device * dev) 
+    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)
 {
-  PrintDebug("generic: interrupt 0x%x - injecting into VM\n", irq);
+#ifdef V3_CONFIG_DEBUG_GENERIC
+    struct vm_device *dev = (struct vm_device *) priv;
+    struct generic_internal *state = (struct generic_internal *) dev->private_data;
+#endif
 
-  dev->vm->vm_ops.raise_irq(dev->vm, irq);
+    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");
 
-  return 0;
+    memset((uint8_t *)dst, 0, len);
 
+    PrintDebug(core->vm_info, core, "returning zeros\n");
+
+    return len;
 }
 
 
-int generic_init_device(struct vm_device * dev) 
+static int generic_read_mem_print_and_ignore(struct guest_info * core, 
+                                            addr_t              gpa,
+                                            void              * dst,
+                                            uint_t              len,
+                                            void              * priv)
 {
-  struct generic_internal *state = (struct generic_internal *)(dev->private_data);
-  uint_t i, j;
+    memset((uint8_t *)dst, 0, len);
+    return len;
+}
 
-  GENERIC_DEBUG_PRINT("generic: init_device\n");
 
-  // Would read state here
+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
+    
+    // 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;
+}
 
-  generic_reset_device(dev);
 
-  for (i = 0; i < state->num_port_ranges; i++) { 
-    GENERIC_DEBUG_PRINT("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");
 
-#if PORT_HOOKS
-    for (j = state->port_ranges[i][0]; j <= state->port_ranges[i][1]; j++) { 
-      if (state->port_ranges[i][2]==GENERIC_PRINT_AND_PASSTHROUGH) { 
-       if (dev_hook_io(dev, j, &generic_read_port_passthrough, &generic_write_port_passthrough)) { 
-         GENERIC_DEBUG_PRINT("generic: can't hook port 0x%x (already hooked?)\n", j);
-       }
-      } else if (state->port_ranges[i][2]==GENERIC_PRINT_AND_IGNORE) { 
-       if (dev_hook_io(dev, j, &generic_read_port_ignore, &generic_write_port_ignore)) { 
-         GENERIC_DEBUG_PRINT("generic: can't hook port 0x%x (already hooked?)\n", j);
-       }
-      } 
-    }
-#else
-    GENERIC_DEBUG_PRINT("generic: hooking ports not supported\n");
-#endif
 
-  }
 
-  for (i = 0; i < state->num_address_ranges; i++) { 
-    GENERIC_DEBUG_PRINT("generic: hooking 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_hook_mem(dev, state->address_ranges[i][0], state->address_ranges[i][1])) {
-      GENERIC_DEBUG_PRINT("generic: Can't hook addresses 0x%x to 0x%x (already hooked?)\n",
-                 state->address_ranges[i][0], state->address_ranges[i][1]); 
-    }
-#else
-    GENERIC_DEBUG_PRINT("generic: hooking addresses not supported\n");
-#endif
 
-  }
 
-  for (i = 0; i < state->num_irq_ranges; i++) { 
-    GENERIC_DEBUG_PRINT("generic: hooking irqs 0x%x to 0x%x\n",state->irq_ranges[i][0],state->irq_ranges[i][1]);
 
-#if IRQ_HOOKS
-    for (j = state->irq_ranges[i][0]; j <= state->irq_ranges[i][1]; j++) { 
-      if (dev_hook_irq(dev, j, &generic_interrupt)) { 
-       GENERIC_DEBUG_PRINT("generic: can't hook irq  0x%x (already hooked?)\n", j);
-      }
+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;
+       }
     }
-#else
-    GENERIC_DEBUG_PRINT("generic: hooking irqs not supported\n");
-#endif
+    
+    return 0;
+}
 
-  }
 
-  return 0;
+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;
 }
 
-int generic_deinit_device(struct vm_device *dev)
-{
-  struct generic_internal *state = (struct generic_internal *)(dev->private_data);
-  uint_t i, j;
 
-  GENERIC_DEBUG_PRINT("generic: deinit_device\n");
+#if 1
 
-  for (i = 0; i < state->num_irq_ranges; i++) { 
-    GENERIC_DEBUG_PRINT("generic: unhooking irqs 0x%x to 0x%x\n", state->irq_ranges[i][0], state->irq_ranges[i][1]);
+//This is a hack for host device testing and will be removed
 
-#if IRQ_HOOKS
-    for (j = state->irq_ranges[i][0]; j <= state->irq_ranges[i][1]; j++) { 
-      if (dev_unhook_irq(dev, j)) {
-       GENERIC_DEBUG_PRINT("generic: can't unhook irq 0x%x (already unhooked?)\n",j);
-      }
-    }
-#else
-    GENERIC_DEBUG_PRINT("generic: unhooking irqs not supported\n");
-#endif
+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;
+       }
+    }  
 
-  }
+    return 0;
+}
 
-  for (i = 0; i < state->num_address_ranges; i++) { 
-    GENERIC_DEBUG_PRINT("generic: unhooking addresses 0x%x to 0x%x\n",state->address_ranges[i][0],state->address_ranges[i][1]); 
+#endif
 
-#if MEM_HOOKS
-    if (dev_unhook_mem(dev, state->address_ranges[i][0], state->address_ranges[i][1])) {
-      GENERIC_DEBUG_PRINT("generic: Can't unhook addresses 0x%x to 0x%x (already unhooked?)\n",
-                 state->address_ranges[i][0], state->address_ranges[i][1]); 
+#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);
+       }
     }
-#else
-    GENERIC_DEBUG_PRINT("generic: unhooking addresses not supported\n");
+}
 #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
 
-  for (i = 0; i < state->num_port_ranges; i++) { 
-    GENERIC_DEBUG_PRINT("generic: unhooking ports 0x%x to 0x%x\n",state->port_ranges[i][0],state->port_ranges[i][1]);
+        GENERIC_PASSTHROUGH => send writes and reads to physical device or host
+        GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
 
-#if PORT_HOOKS
-    for (j = state->port_ranges[i][0]; j <= state->port_ranges[i][1]; j++) { 
-      if (dev_unhook_io(dev, j)) {
-       GENERIC_DEBUG_PRINT("generic: can't unhook port 0x%x (already unhooked?)\n", j);
-      }
-    }
-#else
-    GENERIC_DEBUG_PRINT("generic: unhooking ports not supported\n");
-#endif
+        GENERIC_IGNORE => ignore writes and reads
+        GENERIC_PRINT_AND_PASSTHROUGH => also print what it's doing
 
-  }
 
-  generic_reset_device(dev);
-  return 0;
-}
+       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)
 
+     <ports>
+         <start>portno1</start>
+         <end>portno2</end>   => portno1 through portno2 (inclusive)
+         <mode>PRINT_AND_PASSTHROUGH</mode>  (as above)
+     </ports>
 
-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,
-};
-
+     <memory>
+         <start>gpa1</start>
+         <end>gpa2</end>     => memory addreses gpa1 through gpa2 (inclusive); page granularity
+         <mode> ... as above </mode>
+     </memory>
 
+*/
 
+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");
 
-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, num_address_ranges, num_irq_ranges;
 
-  num_port_ranges=0;
-  if (port_ranges!=NULL) { 
-    i=0;
-    while (port_ranges[i]!=NULL && port_ranges[i][0]!=0 && port_ranges[i][1]!=0 && port_ranges[i][2]!=0) { num_port_ranges++; i++; }
-  }
+    state = (struct generic_internal *)V3_Malloc(sizeof(struct generic_internal));
 
-  
-  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++; }
-  }
+    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;
+
+    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);
 
-  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;
-  }
+    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
 
-  generic_state->num_address_ranges = num_address_ranges;
+    PrintDebug(vm, VCORE_NONE, "generic (%s): init_device\n", state->name);
 
-  if (num_address_ranges > 0) { 
-    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);
-  } else {
-    generic_state->address_ranges = NULL;
-  }
+    // 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;
+       }
 
+       port_cfg = v3_cfg_next_branch(port_cfg);
+    }
 
-  generic_state->num_irq_ranges = num_irq_ranges;
+    // 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 (num_irq_ranges > 0) { 
-    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);
-  } else {
-    generic_state->irq_ranges = NULL;
-  }
+       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++;
 
+       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)