char url[MAX_URL]; // what is the url describing the device
v3_guest_dev_t guestdev; // what is the palacios-side device
+ v3_guest_dev_intr_t guestintr; // what is the palacios-side device interrupt info
wait_queue_head_t user_wait_queue; // user space processes waiting on us (should be only one)
wait_queue_head_t host_wait_queue; // host threads (should only be one) waiting on user space
}
break;
- case PALACIOS_HOST_DEV_USER_REQUEST_IRQ_GUEST: {
+ case PALACIOS_HOST_DEV_USER_REQUEST_IRQ_RAISE_GUEST: {
- DEEP_DEBUG_PRINT("palacios: hostdev: irq guest\n");
+ DEEP_DEBUG_PRINT("palacios: hostdev: irq raise guest\n");
- return v3_host_dev_raise_irq(dev, dev->guestdev, op.irq);
+ return v3_host_dev_raise_irq(dev, dev->guestdev, dev->guestintr, op.irq);
+ }
+ break;
+ case PALACIOS_HOST_DEV_USER_REQUEST_IRQ_LOWER_GUEST: {
+
+ DEEP_DEBUG_PRINT("palacios: hostdev: irq lower guest\n");
+
+ return v3_host_dev_lower_irq(dev, dev->guestdev, dev->guestintr, op.irq);
}
break;
static v3_host_dev_t palacios_host_dev_open_deferred(char *url,
v3_bus_class_t bus,
v3_guest_dev_t gdev,
+ v3_guest_dev_intr_t gintr,
void *host_priv_data)
{
struct v3_guest *guest= (struct v3_guest*)host_priv_data;
dev->guestdev = gdev;
+ dev->guestintr = gintr;
+
dev->guest = guest;
palacios_spinlock_init(&(dev->lock));
struct palacios_host_dev_user_op {
#define PALACIOS_HOST_DEV_USER_REQUEST_READ_GUEST 1
#define PALACIOS_HOST_DEV_USER_REQUEST_WRITE_GUEST 2
-#define PALACIOS_HOST_DEV_USER_REQUEST_IRQ_GUEST 3
+#define PALACIOS_HOST_DEV_USER_REQUEST_IRQ_RAISE_GUEST 3
+#define PALACIOS_HOST_DEV_USER_REQUEST_IRQ_LOWER_GUEST 4
uint32_t type; // type of operation (from the #defs above)
void *gpa; // physical address in guest to read or write
void USER *data; // user address of data that will be read or written
return do_user(devfd,&op);
}
-int v3_user_host_dev_inject_irq(int devfd, uint8_t irq)
+int v3_user_host_dev_raise_irq(int devfd, uint8_t irq)
{
struct palacios_host_dev_user_op op;
- op.type= PALACIOS_HOST_DEV_USER_REQUEST_IRQ_GUEST;
+ op.type= PALACIOS_HOST_DEV_USER_REQUEST_IRQ_RAISE_GUEST;
+ op.gpa=0;
+ op.data=0;
+ op.len=0;
+ op.irq=irq;
+
+ return do_user(devfd,&op);
+}
+
+int v3_user_host_dev_lower_irq(int devfd, uint8_t irq)
+{
+ struct palacios_host_dev_user_op op;
+
+ op.type= PALACIOS_HOST_DEV_USER_REQUEST_IRQ_LOWER_GUEST;
op.gpa=0;
op.data=0;
op.len=0;
uint64_t v3_user_host_dev_read_guest_mem(int devfd, void *gpa, void *dest, uint64_t len);
uint64_t v3_user_host_dev_write_guest_mem(int devfd, void *gpa, void *src, uint64_t len);
-int v3_user_host_dev_inject_guest_irq(int devfd, uint8_t irq);
+
+// Note that "IRQ" here is context-dependent. For a legacy device, it is the IRQ
+// For a PCI device, it is the PCI int #, etc.
+int v3_user_host_dev_raise_guest_irq(int devfd, uint8_t irq);
+int v3_user_host_dev_lower_guest_irq(int devfd, uint8_t irq);
#endif
//
// uint64_t v3_user_host_dev_read_guest_mem(int devfd, void *gpa, void *dest, uint64_t len);
// uint64_t v3_user_host_dev_write_guest_mem(int devfd, void *gpa, void *src, uint64_t len);
- // int v3_user_host_dev_inject_guest_irq(int devfd, uint8_t irq);
+ // int v3_user_host_dev_raise_irq(int devfd, uint8_t irq);
+ // int v3_user_host_dev_lower_irq(int devfd, uint8_t irq);
//
// determine datasize - # bytes to include in response
//
The purpose of this interface is to make it possible to implement
virtual devices in the host OS. It is intended to be used by
passthrough device implementations, such as the generic device
- and the PCI passthrough device.
+ and the PCI front-end device.
One use of this interface, and the generic and PCI passthrough devices
- might be to build an interface with simulated devices in SST
+ might be to build an interface with simulated devices in QEMU, SST, etc
under a Linux host. That scenario would look like this:
Guest config:
generic device:
- <device class="generic" id="mydev" impl="host_sst">
+ <device class="generic" id="mydev" forward="host_device" hostdev="url" >
ports, memory regions, interrupts set with PASSTHROUGH option
</device>
- PCI passthrough devive:
- <device class="pci_passthrough" id="mydev", impl="host_sst">
+ PCI front devive:
+ <device class="pci_front" id="mydev", hostdev="url">
vendor and device ids, etc
</device>
-impl="physical" or lack of an impl key would indicate that direct hardware
-access is expected, which is how these devices currently operate.
-
Host (Linux) side:
typedef void * v3_host_dev_t;
/* A guest device is opaque to the host */
typedef void * v3_guest_dev_t;
-
+/* Optional support for special interrupt handling ; raise=1 => raise otherwise lower */
+typedef void (*v3_guest_dev_intr_t)(v3_host_dev_t hdev, v3_guest_dev_t gdev, uint8_t irq, int raise);
/* There is a notion of a bus class to which the device is attached */
typedef enum { V3_BUS_CLASS_DIRECT, V3_BUS_CLASS_PCI } v3_bus_class_t;
v3_host_dev_t v3_host_dev_open(char *impl,
v3_bus_class_t bus,
v3_guest_dev_t gdev,
+ v3_guest_dev_intr_t intr,
struct v3_vm_info *vm);
int v3_host_dev_close(v3_host_dev_t hdev);
v3_host_dev_t (*open)(char *impl,
v3_bus_class_t bus,
v3_guest_dev_t gdev,
+ v3_guest_dev_intr_t intr,
void *host_priv_data);
int (*close)(v3_host_dev_t hdev);
};
/* This function is how the host will raise an irq to palacios
- for the device. The IRQ argument will be ignored for devices
- whose irqs are managed by palacios */
+ for the device. */
int v3_host_dev_raise_irq(v3_host_dev_t hostdev,
v3_guest_dev_t guest_dev,
+ v3_guest_dev_intr_t intr,
+ uint8_t irq);
+int v3_host_dev_lower_irq(v3_host_dev_t hostdev,
+ v3_guest_dev_t guest_dev,
+ v3_guest_dev_intr_t intr,
uint8_t irq);
/* These functions allow the host to read and write the guest
#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
<device class="generic" id="my_id"
- empty | forward="physical_device" or forward="host_device" host_device="url">
+ empty | forward="physical_device" or forward="host_device" hostdev="url">
(empty implies physical_dev)
v3_remove_device(dev);
return -1;
} else {
- state->host_dev = v3_host_dev_open(host_dev,V3_BUS_CLASS_DIRECT,dev,vm);
+ 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);
// We assume that someone has called pull_config to get a local
// copy of the config data from the host device by this point
//
+// It might be smarter to do the pull config here since
+// in init we may not yet have the host device running...
+//
static int pci_bar_init(int bar_num, uint32_t * dst, void * private_data) {
struct vm_device * dev = (struct vm_device *)private_data;
struct pci_front_internal * state = (struct pci_front_internal *)(dev->private_data);
return 0;
}
+#ifdef V3_CONFIG_HOST_DEVICE
+static void pci_front_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;
+ struct pci_front_internal *state = (struct pci_front_internal *) dev->private_data;
+
+ // We expect the host device will raise and lower irqs as needed, so we
+ // don't need an "acked" irq. Also, we expect the host is using INTX, not
+ // MSI. It's doubtful that MSI will work.
+ // expect: state->pci_dev->irq_type==IRQ_INTX
+ if (raise) {
+ v3_pci_raise_irq(state->pci_bus, state->pci_dev, irq);
+ } else {
+ v3_pci_lower_irq(state->pci_bus, state->pci_dev, irq);
+ }
+ }
+}
+#endif
+
static struct v3_device_ops dev_ops = {
//
return -1;
}
- if (!(state->host_dev=v3_host_dev_open(url,V3_BUS_CLASS_PCI,dev,vm))) {
+ if (!(state->host_dev=v3_host_dev_open(url,V3_BUS_CLASS_PCI,dev,pci_front_intr_update_callback,vm))) {
PrintError(info->vm_info, info, "pci_front (%s): unable to attach to host device %s\n",state->name, url);
v3_remove_device(dev);
return -1;
v3_host_dev_t v3_host_dev_open(char *impl,
v3_bus_class_t bus,
v3_guest_dev_t gdev,
+ v3_guest_dev_intr_t intr,
struct v3_vm_info *vm)
{
V3_ASSERT(VM_NONE, VCORE_NONE, host_dev_hooks != NULL);
V3_ASSERT(VM_NONE, VCORE_NONE, host_dev_hooks->open != NULL);
- return host_dev_hooks->open(impl,bus,gdev,vm->host_priv_data);
+ return host_dev_hooks->open(impl,bus,gdev,intr,vm->host_priv_data);
}
int v3_host_dev_close(v3_host_dev_t hdev)
int v3_host_dev_raise_irq(v3_host_dev_t hostdev,
v3_guest_dev_t guest_dev,
+ v3_guest_dev_intr_t intr,
uint8_t irq)
{
- // Make this smarter later...
+ if (intr) {
+ intr(hostdev,guest_dev,irq,1);
+ return 0;
+ } else {
+ struct vm_device *dev = (struct vm_device *) guest_dev;
+
+ if (dev && dev->vm) {
+ return v3_raise_irq(dev->vm,irq);
+ } else {
+ return -1;
+ }
+ }
+}
+int v3_host_dev_lower_irq(v3_host_dev_t hostdev,
+ v3_guest_dev_t guest_dev,
+ v3_guest_dev_intr_t intr,
+ uint8_t irq)
+{
struct vm_device *dev = (struct vm_device *) guest_dev;
if (dev && dev->vm) {
- return v3_raise_irq(dev->vm,irq);
+ if (intr) {
+ intr(hostdev,guest_dev,irq,0);
+ return 0;
+ } else {
+ return v3_lower_irq(dev->vm,irq);
+ }
} else {
return -1;
}