typedef enum { PCI_BAR_IO,
- PCI_BAR_MEM16,
+ PCI_BAR_MEM24,
PCI_BAR_MEM32,
PCI_BAR_MEM64_LOW,
PCI_BAR_MEM64_HIGH,
+ PCI_BAR_PASSTHROUGH,
PCI_BAR_NONE } pci_bar_type_t;
-typedef enum {PCI_STD_DEVICE, PCI_TO_PCI_BRIDGE, PCI_CARDBUS, PCI_MULTIFUNCTION} pci_device_type_t;
+typedef enum {PCI_STD_DEVICE, PCI_TO_PCI_BRIDGE, PCI_CARDBUS, PCI_MULTIFUNCTION, PCI_PASSTHROUGH} pci_device_type_t;
int (*io_read)(ushort_t port, void * dst, uint_t length, void * private_data);
int (*io_write)(ushort_t port, void * src, uint_t length, void * private_data);
};
+
+ struct {
+ int (*bar_init)(int bar_num, uint32_t * dst,void * private_data);
+ int (*bar_write)(int bar_num, uint32_t * src, void * private_data);
+ };
};
void * private_data;
#define PCI_IO_MASK 0xfffffffc
-#define PCI_MEM32_MASK 0xfffffff0
+#define PCI_MEM_MASK 0xfffffff0
+#define PCI_MEM24_MASK 0x000ffff0
#define PCI_IO_BASE(bar_val) (bar_val & PCI_IO_MASK)
-#define PCI_MEM32_BASE(bar_val) (bar_val & PCI_MEM32_MASK)
+#define PCI_MEM32_BASE(bar_val) (bar_val & PCI_MEM_MASK)
+#define PCI_MEM24_BASE(bar_val) (bar_val & PCI_MEM24_MASK)
struct pci_device {
+ pci_device_type_t type;
+
union {
uint8_t config_space[256];
struct vm_device * vm_dev; //the corresponding virtual device
- int (*config_update)(struct pci_device * pci_dev, uint_t reg_num, int length);
+ int (*config_update)(uint_t reg_num, void * src, uint_t length, void * priv_data);
+
+ int (*cmd_update)(struct pci_device * pci_dev, uchar_t io_enabled, uchar_t mem_enabled);
+ int (*ext_rom_update)(struct pci_device * pci_dev);
+
+ int (*config_write)(uint_t reg_num, void * src, uint_t length, void * private_data);
+ int (*config_read)(uint_t reg_num, void * dst, uint_t length, void * private_data);
- int (*cmd_update)(struct pci_device *pci_dev, uchar_t io_enabled, uchar_t mem_enabled);
- int (*ext_rom_update)(struct pci_device *pci_dev);
int ext_rom_update_flag;
int bar_update_flag;
int fn_num,
const char * name,
struct v3_pci_bar * bars,
- int (*config_update)(struct pci_device * pci_dev, uint_t reg_num, int length),
+ int (*config_update)(uint_t reg_num, void * src, uint_t length, void * private_data),
int (*cmd_update)(struct pci_device *pci_dev, uchar_t io_enabled, uchar_t mem_enabled),
int (*ext_rom_update)(struct pci_device *pci_dev),
- struct vm_device * dev);
+ struct vm_device * dev, void * priv_data);
+
+
+struct pci_device *
+v3_pci_register_passthrough_device(struct vm_device * pci,
+ int bus_num,
+ int dev_num,
+ int fn_num,
+ const char * name,
+ int (*config_write)(uint_t reg_num, void * src, uint_t length, void * private_data),
+ int (*config_read)(uint_t reg_num, void * dst, uint_t length, void * private_data),
+ struct vm_device * dev,
+ void * private_data);
#endif
--- /dev/null
+/*
+ * 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, Jack Lange <jarusl@cs.northwestern.edu>
+ * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
+ * All rights reserved.
+ *
+ * Author: 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".
+ */
+
+#ifndef __DEVICES_PCI_PASSTHROUGH_H__
+#define __DEVICES_PCI_PASSTHROUGH_H__
+
+#ifdef __V3VEE__
+
+struct pci_passthrough_cfg {
+ char pci_bus_name[32];
+
+ char name[32];
+ uint16_t vendor_id;
+ uint16_t device_id;
+};
+
+
+#endif
+
+#endif
/* External API */
-int v3_hook_io_port(struct guest_info * info, uint_t port,
+int v3_hook_io_port(struct guest_info * info, uint16_t port,
int (*read)(uint16_t port, void * dst, uint_t length, void * priv_data),
int (*write)(uint16_t port, void * src, uint_t length, void * priv_data),
void * priv_data);
-int v3_unhook_io_port(struct guest_info * info, uint_t port);
+int v3_unhook_io_port(struct guest_info * info, uint16_t port);
void * arch_data;
};
-struct v3_io_hook * v3_get_io_hook(struct guest_info * info, uint_t port);
+struct v3_io_hook * v3_get_io_hook(struct guest_info * info, uint16_t port);
void v3_print_io_map(struct guest_info * info);
-void print_shadow_map(struct guest_info * info);
+void v3_print_mem_map(struct guest_info * info);
struct hashtable * shdw_ptr_ht;
};
-
-
+// Present = 0 and Dirty = 0
+// fixme
static inline int is_swapped_pte32(pte32_t * pte) {
return ((pte->present == 0) && (*(uint32_t *)pte != 0));
}
help
Includes the Virtual PCI Bus
+config PASSTHROUGH_PCI
+ bool "Passthrough PCI"
+ default y
+ depends on PCI && EXPERIMENTAL
+ help
+ Enables hardware devices to be passed through to the VM
+
config DEBUG_PCI
bool "PCI debugging"
depends on PCI && DEBUG_ON
obj-$(CONFIG_CGA) += cga.o
obj-$(CONFIG_TELNET_CONSOLE) += telnet_cons.o
+
+obj-$(CONFIG_PASSTHROUGH_PCI) += pci_passthrough.o
\ No newline at end of file
v3_outw(port,((uint16_t*)src)[0]);
break;
case 4:
- v3_outdw(port,((uint_t*)src)[0]);
+ v3_outdw(port,((uint32_t *)src)[0]);
break;
default:
for (i = 0; i < length; i++) {
- v3_outb(port, ((uchar_t*)src)[i]);
+ v3_outb(port, ((uchar_t *)src)[i]);
}
}
pci_dev = v3_pci_register_device(state->pci, PCI_STD_DEVICE,
0, 0, 0, "i440FX", bars,
- NULL, NULL, NULL, dev);
+ NULL, NULL, NULL, dev, NULL);
if (!pci_dev) {
return -1;
}
-static int pci_config_update(struct pci_device * pci_dev, uint_t reg_num, int length) {
+static int pci_config_update(uint_t reg_num, void * src, uint_t length, void * private_data) {
PrintDebug("PCI Config Update\n");
PrintDebug("\t\tInterupt register (Dev=%s), irq=%d\n", pci_dev->name, pci_dev->config_header.intr_line);
pci_dev = v3_pci_register_device(ide->pci_bus, PCI_STD_DEVICE, 0, sb_pci->dev_num, 1,
"PIIX3_IDE", bars,
- pci_config_update, NULL, NULL, dev);
+ pci_config_update, NULL, NULL, dev, dev);
if (pci_dev == NULL) {
PrintError("Failed to register IDE BUS %d with PCI\n", i);
pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE,
0, PCI_AUTO_DEV_NUM, 0,
"LNX_VIRTIO_BALLOON", bars,
- NULL, NULL, NULL, dev);
+ NULL, NULL, NULL, dev, NULL);
if (!pci_dev) {
PrintError("Could not register PCI Device\n");
pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE,
0, PCI_AUTO_DEV_NUM, 0,
"LNX_VIRTIO_BLK", bars,
- NULL, NULL, NULL, dev);
+ NULL, NULL, NULL, dev, NULL);
if (!pci_dev) {
PrintError("Could not register PCI Device\n");
pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE,
0, PCI_AUTO_DEV_NUM, 0,
"LNX_VIRTIO_SYM", bars,
- NULL, NULL, NULL, dev);
+ NULL, NULL, NULL, dev, NULL);
if (!pci_dev) {
PrintError("Could not register PCI Device\n");
return length;
}
+ if (pci_dev->type == PCI_PASSTHROUGH) {
+ if (pci_dev->config_read(reg_num, dst, length, pci_dev->priv_data) == -1) {
+ PrintError("Failed to handle configuration update for passthrough pci_device\n");
+ return -1;
+ }
+
+ return 0;
+ }
+
for (i = 0; i < length; i++) {
*(uint8_t *)((uint8_t *)dst + i) = pci_dev->config_space[reg_num + i];
}
PrintError("Could not hook PCI IO port (old port=%u) (new port=%u)\n",
PCI_IO_BASE(bar->val) + i, PCI_IO_BASE(new_val) + i);
+ v3_print_io_map(pci->vm_dev->vm);
return -1;
}
}
return length;
}
- PrintDebug("Writing PCI Data register. bus = %d, dev = %d, fn = %d, reg = %d (%x) addr_reg = %x (val=%x, len=%d)\n",
+ PrintDebug("Writing PCI Data register. bus = %d, dev = %d, fn = %d, reg = %d (0x%x) addr_reg = 0x%x (val=0x%x, len=%d)\n",
pci_state->addr_reg.bus_num,
pci_state->addr_reg.dev_num,
pci_state->addr_reg.fn_num,
return -1;
}
+ if (pci_dev->type == PCI_PASSTHROUGH) {
+ if (pci_dev->config_write(reg_num, src, length, pci_dev->priv_data) == -1) {
+ PrintError("Failed to handle configuration update for passthrough pci_device\n");
+ return -1;
+ }
+
+ return 0;
+ }
+
for (i = 0; i < length; i++) {
uint_t cur_reg = reg_num + i;
}
if (pci_dev->config_update) {
- pci_dev->config_update(pci_dev, reg_num, length);
+ pci_dev->config_update(reg_num, src, length, pci_dev->priv_data);
}
// Scan for BAR updated
if (pci_dev->bar[i].updated) {
int bar_offset = 0x10 + 4 * i;
- *(uint32_t *)(pci_dev->config_space + bar_offset) &= pci_dev->bar[i].mask;
- // check special flags....
- // bar_update
- if (bar_update(pci_dev, i, *(uint32_t *)(pci_dev->config_space + bar_offset)) == -1) {
- PrintError("PCI Device %s: Bar update Error Bar=%d\n", pci_dev->name, i);
- return -1;
+ if (pci_dev->bar[i].type == PCI_BAR_PASSTHROUGH) {
+ if (pci_dev->bar[i].bar_write(i, (uint32_t *)(pci_dev->config_space + bar_offset), pci_dev->bar[i].private_data) == -1) {
+ PrintError("Error in passthrough bar write operation\n");
+ return -1;
+ }
+ } else {
+
+ *(uint32_t *)(pci_dev->config_space + bar_offset) &= pci_dev->bar[i].mask;
+ // check special flags....
+
+ // bar_update
+ if (bar_update(pci_dev, i, *(uint32_t *)(pci_dev->config_space + bar_offset)) == -1) {
+ PrintError("PCI Device %s: Bar update Error Bar=%d\n", pci_dev->name, i);
+ return -1;
+ }
}
pci_dev->bar[i].updated = 0;
*(uint32_t *)(pci_dev->config_space + bar_offset) = pci_dev->bar[i].val;
- } else if (pci_dev->bar[i].type == PCI_BAR_MEM16) {
+ } else if (pci_dev->bar[i].type == PCI_BAR_MEM24) {
PrintError("16 Bit memory ranges not supported (reg: %d)\n", i);
return -1;
} else if (pci_dev->bar[i].type == PCI_BAR_NONE) {
pci_dev->bar[i].val = 0x00000000;
pci_dev->bar[i].mask = 0x00000000; // This ensures that all updates will be dropped
*(uint32_t *)(pci_dev->config_space + bar_offset) = pci_dev->bar[i].val;
+ } else if (pci_dev->bar[i].type == PCI_BAR_PASSTHROUGH) {
+
+ // Call the bar init function to get the local cached value
+ pci_dev->bar[i].bar_init(i, &(pci_dev->bar[i].val), pci_dev->bar[i].private_data);
+
} else {
PrintError("Invalid BAR type for bar #%d\n", i);
return -1;
int fn_num,
const char * name,
struct v3_pci_bar * bars,
- int (*config_update)(struct pci_device * pci_dev, uint_t reg_num, int length),
+ int (*config_update)(uint_t reg_num, void * src, uint_t length, void * priv_data),
int (*cmd_update)(struct pci_device *pci_dev, uchar_t io_enabled, uchar_t mem_enabled),
int (*ext_rom_update)(struct pci_device * pci_dev),
- struct vm_device * dev) {
+ struct vm_device * dev, void * priv_data) {
struct pci_internal * pci_state = (struct pci_internal *)pci->private_data;
struct pci_bus * bus = &(pci_state->bus_list[bus_num]);
memset(pci_dev, 0, sizeof(struct pci_device));
+
+ pci_dev->type = dev_type;
- switch (dev_type) {
+ switch (pci_dev->type) {
case PCI_STD_DEVICE:
pci_dev->config_header.header_type = 0x00;
break;
return NULL;
}
+
+
pci_dev->bus_num = bus_num;
pci_dev->dev_num = dev_num;
pci_dev->fn_num = fn_num;
strncpy(pci_dev->name, name, sizeof(pci_dev->name));
pci_dev->vm_dev = dev;
+ pci_dev->priv_data = priv_data;
// register update callbacks
pci_dev->config_update = config_update;
pci_dev->bar[i].default_base_addr = bars[i].default_base_addr;
pci_dev->bar[i].mem_read = bars[i].mem_read;
pci_dev->bar[i].mem_write = bars[i].mem_write;
+ } else if (pci_dev->bar[i].type == PCI_BAR_PASSTHROUGH) {
+ pci_dev->bar[i].bar_init = bars[i].bar_init;
+ pci_dev->bar[i].bar_write = bars[i].bar_write;
} else {
pci_dev->bar[i].num_pages = 0;
pci_dev->bar[i].default_base_addr = 0;
return pci_dev;
}
+
+
+
+// if dev_num == -1, auto assign
+struct pci_device * v3_pci_register_passthrough_device(struct vm_device * pci,
+ int bus_num,
+ int dev_num,
+ int fn_num,
+ const char * name,
+ int (*config_write)(uint_t reg_num, void * src, uint_t length, void * private_data),
+ int (*config_read)(uint_t reg_num, void * dst, uint_t length, void * private_data),
+ struct vm_device * dev,
+ void * private_data) {
+
+ struct pci_internal * pci_state = (struct pci_internal *)pci->private_data;
+ struct pci_bus * bus = &(pci_state->bus_list[bus_num]);
+ struct pci_device * pci_dev = NULL;
+
+ if (dev_num > MAX_BUS_DEVICES) {
+ PrintError("Requested Invalid device number (%d)\n", dev_num);
+ return NULL;
+ }
+
+ if (dev_num == PCI_AUTO_DEV_NUM) {
+ PrintDebug("Searching for free device number\n");
+ if ((dev_num = get_free_dev_num(bus)) == -1) {
+ PrintError("No more available PCI slots on bus %d\n", bus->bus_num);
+ return NULL;
+ }
+ }
+
+ PrintDebug("Checking for PCI Device\n");
+
+ if (get_device(bus, dev_num, fn_num) != NULL) {
+ PrintError("PCI Device already registered at slot %d on bus %d\n",
+ dev_num, bus->bus_num);
+ return NULL;
+ }
+
+
+ pci_dev = (struct pci_device *)V3_Malloc(sizeof(struct pci_device));
+
+ if (pci_dev == NULL) {
+ PrintError("Could not allocate pci device\n");
+ return NULL;
+ }
+
+ memset(pci_dev, 0, sizeof(struct pci_device));
+
+ pci_dev->bus_num = bus_num;
+ pci_dev->dev_num = dev_num;
+ pci_dev->fn_num = fn_num;
+
+ strncpy(pci_dev->name, name, sizeof(pci_dev->name));
+ pci_dev->vm_dev = dev;
+ pci_dev->priv_data = private_data;
+
+ // register update callbacks
+ pci_dev->config_write = config_write;
+ pci_dev->config_read = config_read;
+
+ // add the device
+ add_device_to_bus(bus, pci_dev);
+
+#ifdef CONFIG_DEBUG_PCI
+ pci_dump_state(pci_state);
+#endif
+
+ return pci_dev;
+}
--- /dev/null
+/*
+ * 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, Jack Lange <jarusl@cs.northwestern.edu>
+ * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
+ * All rights reserved.
+ *
+ * Author: 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".
+ */
+
+
+/* This is the generic passthrough PCI virtual device */
+
+/*
+ * The basic idea is that we do not change the hardware PCI configuration
+ * Instead we modify the guest environment to map onto the physical configuration
+ *
+ * The pci subsystem handles most of the configuration space, except for the bar registers.
+ * We handle them here, by either letting them go directly to hardware or remapping through virtual hooks
+ *
+ * Memory Bars are always remapped via the shadow map,
+ * IO Bars are selectively remapped through hooks if the guest changes them
+ */
+
+#include <palacios/vmm.h>
+#include <palacios/vmm_dev_mgr.h>
+#include <palacios/vmm_sprintf.h>
+#include <palacios/vmm_lowlevel.h>
+
+
+#include <devices/pci.h>
+#include <devices/pci_types.h>
+#include <devices/pci_passthrough.h>
+
+
+// Hardcoded... Are these standard??
+#define PCI_CFG_ADDR 0xcf8
+#define PCI_CFG_DATA 0xcfc
+
+#define PCI_BUS_MAX 4
+#define PCI_DEV_MAX 32
+#define PCI_FN_MAX 7
+
+#define PCI_DEVICE 0x0
+#define PCI_PCI_BRIDGE 0x1
+#define PCI_CARDBUS_BRIDGE 0x2
+
+#define PCI_HDR_SIZE 256
+
+
+union pci_addr_reg {
+ uint32_t value;
+ struct {
+ uint_t rsvd1 : 2;
+ uint_t reg : 6;
+ uint_t func : 3;
+ uint_t dev : 5;
+ uint_t bus : 8;
+ uint_t rsvd2 : 7;
+ uint_t enable : 1;
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+typedef enum { PT_BAR_NONE,
+ PT_BAR_IO,
+ PT_BAR_MEM32,
+ PT_BAR_MEM24,
+ PT_BAR_MEM64_LOW,
+ PT_BAR_MEM64_HIGH } pt_bar_type_t;
+
+struct pt_bar {
+ uint32_t size;
+ pt_bar_type_t type;
+ uint32_t addr;
+
+ uint32_t val;
+};
+
+struct pt_dev_state {
+ union {
+ uint8_t config_space[256];
+ struct pci_config_header real_hdr;
+ } __attribute__((packed));
+
+ struct pt_bar phys_bars[6];
+ struct pt_bar virt_bars[6];
+
+
+ struct vm_device * pci_bus;
+ struct pci_device * pci_dev;
+
+ union pci_addr_reg phys_pci_addr;
+
+ char name[32];
+};
+
+
+static inline uint32_t pci_cfg_read32(uint32_t addr) {
+ v3_outdw(PCI_CFG_ADDR, addr);
+ return v3_indw(PCI_CFG_DATA);
+}
+
+
+
+static inline void pci_cfg_write32(uint32_t addr, uint32_t val) {
+ v3_outdw(PCI_CFG_ADDR, addr);
+ v3_outdw(PCI_CFG_DATA, val);
+}
+
+
+
+static inline uint16_t pci_cfg_read16(uint32_t addr) {
+ v3_outw(PCI_CFG_ADDR, addr);
+ return v3_inw(PCI_CFG_DATA);
+}
+
+
+
+static inline void pci_cfg_write16(uint32_t addr, uint16_t val) {
+ v3_outw(PCI_CFG_ADDR, addr);
+ v3_outw(PCI_CFG_DATA, val);
+}
+
+
+
+static inline uint8_t pci_cfg_read8(uint32_t addr) {
+ v3_outb(PCI_CFG_ADDR, addr);
+ return v3_inb(PCI_CFG_DATA);
+}
+
+
+
+static inline void pci_cfg_write8(uint32_t addr, uint8_t val) {
+ v3_outb(PCI_CFG_ADDR, addr);
+ v3_outb(PCI_CFG_DATA, val);
+}
+
+
+// We initialize this
+static int pci_bar_init(int bar_num, uint32_t * dst,void * private_data) {
+ struct vm_device * dev = (struct vm_device *)private_data;
+ struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
+ const uint32_t bar_base_reg = 4;
+ union pci_addr_reg pci_addr = {state->phys_pci_addr.value};
+ uint32_t bar_val = 0;
+ uint32_t max_val = 0;
+ //addr_t irq_state = 0;
+ struct pt_bar * pbar = &(state->phys_bars[bar_num]);
+
+
+ // should read from cached header
+ pci_addr.reg = bar_base_reg + bar_num;
+
+ PrintDebug("PCI Address = 0x%x\n", pci_addr.value);
+
+ bar_val = pci_cfg_read32(pci_addr.value);
+ pbar->val = bar_val;
+
+ if ((bar_val & 0x3) == 0x1) {
+ int i = 0;
+
+ // IO bar
+ pbar->type = PT_BAR_IO;
+ pbar->addr = PCI_IO_BASE(bar_val);
+
+ max_val = bar_val | PCI_IO_MASK;
+
+ // Cycle the physical bar, to determine the actual size
+ // Disable irqs, to try to prevent accesses to the space via a interrupt handler
+ // This is not SMP safe!!
+ // What we probably want to do is write a 0 to the command register
+ //irq_state = v3_irq_save();
+
+ pci_cfg_write32(pci_addr.value, max_val);
+ max_val = pci_cfg_read32(pci_addr.value);
+ pci_cfg_write32(pci_addr.value, bar_val);
+
+ //v3_irq_restore(irq_state);
+
+ pbar->size = ~PCI_IO_BASE(max_val) + 1;
+
+
+ // setup a set of null io hooks
+ // This allows the guest to do passthrough IO to these ports
+ // While still reserving them in the IO map
+ for (i = 0; i < pbar->size; i++) {
+ v3_hook_io_port(dev->vm, pbar->addr + i, NULL, NULL, NULL);
+ }
+
+ } else {
+
+ // might be memory, might be nothing
+
+ max_val = bar_val | PCI_MEM_MASK;
+
+ // Cycle the physical bar, to determine the actual size
+ // Disable irqs, to try to prevent accesses to the space via a interrupt handler
+ // This is not SMP safe!!
+ // What we probably want to do is write a 0 to the command register
+ //irq_state = v3_irq_save();
+
+ pci_cfg_write32(pci_addr.value, max_val);
+ max_val = pci_cfg_read32(pci_addr.value);
+ pci_cfg_write32(pci_addr.value, bar_val);
+
+ //v3_irq_restore(irq_state);
+
+
+ if (max_val == 0) {
+ pbar->type = PT_BAR_NONE;
+ } else {
+
+ // if its a memory region, setup passthrough mem mapping
+
+ if ((bar_val & 0x6) == 0x0) {
+ // MEM 32
+ pbar->type = PT_BAR_MEM32;
+ pbar->addr = PCI_MEM32_BASE(bar_val);
+ pbar->size = ~PCI_MEM32_BASE(max_val) + 1;
+
+ PrintDebug("Adding 32 bit PCI mem region: start=0x%x, end=0x%x\n",
+ pbar->addr, pbar->addr + pbar->size);
+
+ v3_add_shadow_mem(dev->vm,
+ pbar->addr,
+ pbar->addr + pbar->size - 1,
+ pbar->addr);
+
+ } else if ((bar_val & 0x6) == 0x2) {
+ // Mem 24
+ pbar->type = PT_BAR_MEM24;
+ pbar->addr = PCI_MEM24_BASE(bar_val);
+ pbar->size = ~PCI_MEM24_BASE(max_val) + 1;
+
+ v3_add_shadow_mem(dev->vm,
+ pbar->addr,
+ pbar->addr + pbar->size - 1,
+ pbar->addr);
+
+ } else if ((bar_val & 0x6) == 0x4) {
+ // Mem 64
+ PrintError("64 Bit PCI bars not supported\n");
+ return -1;
+ } else {
+ PrintError("Invalid Memory bar type\n");
+ return -1;
+ }
+
+ }
+ }
+
+
+
+
+ // Initially the virtual bars match the physical ones
+ memcpy(&(state->virt_bars[bar_num]), &(state->phys_bars[bar_num]), sizeof(struct pt_bar));
+
+ PrintDebug("bar_num=%d, bar_val=0x%x\n", bar_num, bar_val);
+
+ PrintDebug("phys bar type=%d, addr=0x%x, size=%d\n",
+ pbar->type, pbar->addr,
+ pbar->size);
+
+ PrintDebug("virt bar type=%d, addr=0x%x, size=%d\n",
+ state->virt_bars[bar_num].type, state->virt_bars[bar_num].addr,
+ state->virt_bars[bar_num].size);
+
+
+
+ // Update the pci subsystem versions
+ *dst = bar_val;
+
+ return 0;
+}
+
+static int pt_io_read(uint16_t port, void * dst, uint_t length, void * priv_data) {
+ struct pt_bar * pbar = (struct pt_bar *)priv_data;
+ int port_offset = port % pbar->size;
+
+ if (length == 1) {
+ *(uint8_t *)dst = v3_inb(pbar->addr + port_offset);
+ } else if (length == 2) {
+ *(uint16_t *)dst = v3_inw(pbar->addr + port_offset);
+ } else if (length == 4) {
+ *(uint32_t *)dst = v3_indw(pbar->addr + port_offset);
+ } else {
+ PrintError("Invalid PCI passthrough IO Redirection size read\n");
+ return -1;
+ }
+
+ return length;
+}
+
+
+static int pt_io_write(uint16_t port, void * src, uint_t length, void * priv_data) {
+ struct pt_bar * pbar = (struct pt_bar *)priv_data;
+ int port_offset = port % pbar->size;
+
+ if (length == 1) {
+ v3_outb(pbar->addr + port_offset, *(uint8_t *)src);
+ } else if (length == 2) {
+ v3_outw(pbar->addr + port_offset, *(uint16_t *)src);
+ } else if (length == 4) {
+ v3_outdw(pbar->addr + port_offset, *(uint32_t *)src);
+ } else {
+ PrintError("Invalid PCI passthrough IO Redirection size write\n");
+ return -1;
+ }
+
+ return length;
+
+}
+
+
+
+
+
+static int pci_bar_write(int bar_num, uint32_t * src, void * private_data) {
+ struct vm_device * dev = (struct vm_device *)private_data;
+ struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
+
+ struct pt_bar * pbar = &(state->phys_bars[bar_num]);
+ struct pt_bar * vbar = &(state->virt_bars[bar_num]);
+
+ PrintDebug("Bar update src=0x%x\n", *src);
+
+ if (vbar->type == PT_BAR_NONE) {
+ return 0;
+ } else if (vbar->type == PT_BAR_IO) {
+ int i = 0;
+
+ // unhook old ports
+ for (i = 0; i < vbar->size; i++) {
+ if (v3_unhook_io_port(dev->vm, vbar->addr + i) == -1) {
+ PrintError("Could not unhook previously hooked port.... %d (0x%x)\n",
+ vbar->addr + i, vbar->addr + i);
+ return -1;
+ }
+ }
+
+ PrintDebug("Setting IO Port range size=%d\n", pbar->size);
+
+ // clear the low bits to match the size
+ *src &= ~(pbar->size - 1);
+
+ // Set reserved bits
+ *src |= (pbar->val & ~PCI_IO_MASK);
+
+ vbar->addr = PCI_IO_BASE(*src);
+
+ PrintDebug("Cooked src=0x%x\n", *src);
+
+ PrintDebug("Rehooking passthrough IO ports starting at %d (0x%x)\n",
+ vbar->addr, vbar->addr);
+
+ if (vbar->addr == pbar->addr) {
+ // Map the io ports as passthrough
+ for (i = 0; i < pbar->size; i++) {
+ v3_hook_io_port(dev->vm, pbar->addr + i, NULL, NULL, NULL);
+ }
+ } else {
+ // We have to manually handle the io redirection
+ for (i = 0; i < vbar->size; i++) {
+ v3_hook_io_port(dev->vm, vbar->addr + i, pt_io_read, pt_io_write, pbar);
+ }
+ }
+ } else if (vbar->type == PT_BAR_MEM32) {
+ // remove old mapping
+ struct v3_shadow_region * old_reg = v3_get_shadow_region(dev->vm, vbar->addr);
+
+ if (old_reg == NULL) {
+ // uh oh...
+ PrintError("Could not find PCI Passthrough memory redirection region (addr=0x%x)\n", vbar->addr);
+ return -1;
+ }
+
+ v3_delete_shadow_region(dev->vm, old_reg);
+
+ // clear the low bits to match the size
+ *src &= ~(pbar->size - 1);
+
+ // Set reserved bits
+ *src |= (pbar->val & ~PCI_MEM_MASK);
+
+ PrintDebug("Cooked src=0x%x\n", *src);
+
+ vbar->addr = PCI_MEM32_BASE(*src);
+
+ PrintDebug("Adding pci Passthrough remapping: start=0x%x, size=%d, end=0x%x\n",
+ vbar->addr, vbar->size, vbar->addr + vbar->size);
+
+ v3_add_shadow_mem(dev->vm,
+ vbar->addr,
+ vbar->addr + vbar->size - 1,
+ pbar->addr);
+
+ } else {
+ PrintError("Unhandled Pasthrough PCI Bar type %d\n", vbar->type);
+ return -1;
+ }
+
+
+ vbar->val = *src;
+
+
+ return 0;
+}
+
+
+static int pt_config_update(uint_t reg_num, void * src, uint_t length, void * private_data) {
+ struct vm_device * dev = (struct vm_device *)private_data;
+ struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
+ union pci_addr_reg pci_addr = {state->phys_pci_addr.value};
+
+ pci_addr.reg = reg_num;
+
+ if (length == 1) {
+ pci_cfg_write8(pci_addr.value, *(uint8_t *)src);
+ } else if (length == 2) {
+ pci_cfg_write16(pci_addr.value, *(uint16_t *)src);
+ } else if (length == 4) {
+ pci_cfg_write32(pci_addr.value, *(uint32_t *)src);
+ }
+
+
+ return 0;
+}
+
+
+
+static int find_real_pci_dev(uint16_t vendor_id, uint16_t device_id, struct pt_dev_state * state) {
+ union pci_addr_reg pci_addr = {0x80000000};
+ uint_t i, j, k, m;
+
+ union {
+ uint32_t value;
+ struct {
+ uint16_t vendor;
+ uint16_t device;
+ } __attribute__((packed));
+ } __attribute__((packed)) pci_hdr = {0};
+
+
+
+ for (i = 0, pci_addr.bus = 0; i < PCI_BUS_MAX; i++, pci_addr.bus++) {
+ for (j = 0, pci_addr.dev = 0; j < PCI_DEV_MAX; j++, pci_addr.dev++) {
+ for (k = 0, pci_addr.func = 0; k < PCI_FN_MAX; k++, pci_addr.func++) {
+
+ v3_outdw(PCI_CFG_ADDR, pci_addr.value);
+ pci_hdr.value = v3_indw(PCI_CFG_DATA);
+
+ if ((pci_hdr.vendor == vendor_id) && (pci_hdr.device == device_id)) {
+ uint32_t * cfg_space = (uint32_t *)&state->real_hdr;
+
+ state->phys_pci_addr = pci_addr;
+
+ // Copy the configuration space to the local cached version
+ for (m = 0, pci_addr.reg = 0; m < PCI_HDR_SIZE; m += 4, pci_addr.reg++) {
+ cfg_space[pci_addr.reg] = pci_cfg_read32(pci_addr.value);
+ }
+
+
+ PrintDebug("Found device %x:%x (bus=%d, dev=%d, func=%d)\n",
+ vendor_id, device_id,
+ pci_addr.bus, pci_addr.dev, pci_addr.func);
+
+ return 0;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+
+
+static int setup_virt_pci_dev(struct guest_info * info, struct vm_device * dev) {
+ struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
+ struct pci_device * pci_dev = NULL;
+ struct v3_pci_bar bars[6];
+ int bus_num = 0;
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ bars[i].type = PCI_BAR_PASSTHROUGH;
+ bars[i].private_data = dev;
+ bars[i].bar_init = pci_bar_init;
+ bars[i].bar_write = pci_bar_write;
+ }
+
+ pci_dev = v3_pci_register_device(state->pci_bus,
+ PCI_STD_DEVICE,
+ bus_num, -1, 0,
+ state->name, bars,
+ pt_config_update, NULL, NULL,
+ dev, dev);
+
+ // This will overwrite the bar registers.. but that should be ok.
+ memcpy(pci_dev->config_space, (uint8_t *)&(state->real_hdr), sizeof(struct pci_config_header));
+
+ return 0;
+}
+
+
+static struct v3_device_ops dev_ops = {
+ .free = NULL,
+ .reset = NULL,
+ .start = NULL,
+ .stop = NULL,
+};
+
+
+
+static int passthrough_init(struct guest_info * info, void * cfg_data) {
+ struct pci_passthrough_cfg * cfg = (struct pci_passthrough_cfg *)cfg_data;
+ struct pt_dev_state * state = V3_Malloc(sizeof(struct pt_dev_state));
+ struct vm_device * dev = NULL;
+ struct vm_device * pci = v3_find_dev(info, cfg->pci_bus_name);
+
+
+ memset(state, 0, sizeof(struct pt_dev_state));
+
+ if (!pci) {
+ PrintError("Could not find PCI device\n");
+ return -1;
+ }
+
+ state->pci_bus = pci;
+ strncpy(state->name, cfg->name, 32);
+
+
+
+ dev = v3_allocate_device("PCI_PASSTHROUGH", &dev_ops, state);
+
+ if (v3_attach_device(info, dev) == -1) {
+ PrintError("Could not attach device %s\n", "PCI_PASSTHROUGH");
+ return -1;
+ }
+
+
+ if (find_real_pci_dev(cfg->vendor_id, cfg->device_id, state) == -1) {
+ PrintError("Could not find PCI Device %x:%x\n", cfg->vendor_id, cfg->device_id);
+ return 0;
+ }
+
+ setup_virt_pci_dev(info, dev);
+
+ return 0;
+}
+
+
+
+
+device_register("PCI_PASSTHROUGH", passthrough_init)
pci_dev = v3_pci_register_device(piix3->pci_bus, PCI_MULTIFUNCTION,
bus_num, -1, 0,
"PIIX3", bars,
- NULL, NULL, NULL, dev);
+ NULL, NULL, NULL, dev, NULL);
if (pci_dev == NULL) {
PrintError("Could not register PCI Device for PIIX3\n");
return -1;
#include <palacios/vmm_sym_swap.h>
#endif
-
#include <devices/generic.h>
#include <devices/ide.h>
#include <devices/ram_cd.h>
#include <devices/net_hd.h>
#include <devices/telnet_cons.h>
+#include <devices/pci_passthrough.h>
}
#endif
- print_shadow_map(info);
+ v3_print_mem_map(info);
return 0;
}
if (config_ptr->enable_pci == 1) {
struct ide_cfg ide_config = {"PCI", "PIIX3"};
+ struct pci_passthrough_cfg pci_pt_cfg = {"PCI", "E1000", 0x8086, 0x100e};
v3_create_device(info, "PCI", NULL);
v3_create_device(info, "i440FX", "PCI");
v3_create_device(info, "SYM_SWAP", "LNX_VIRTIO_BLK");
v3_create_device(info, "IDE", &ide_config);
+
+ v3_create_device(info, "PCI_PASSTHROUGH", &pci_pt_cfg);
+
} else {
v3_create_device(info, "IDE", NULL);
}
}
-struct v3_io_hook * v3_get_io_hook(struct guest_info * info, uint_t port) {
+struct v3_io_hook * v3_get_io_hook(struct guest_info * info, uint16_t port) {
struct rb_node * n = info->io_map.map.rb_node;
struct v3_io_hook * hook = NULL;
-int v3_hook_io_port(struct guest_info * info, uint_t port,
+int v3_hook_io_port(struct guest_info * info, uint16_t port,
int (*read)(uint16_t port, void * dst, uint_t length, void * priv_data),
int (*write)(uint16_t port, void * src, uint_t length, void * priv_data),
void * priv_data) {
io_hook->priv_data = priv_data;
if (insert_io_hook(info, io_hook)) {
- PrintError("Could not insert IO hook for port %d\n", port);
+ PrintError("Could not insert IO hook for port %u (0x%x)\n", port, port);
V3_Free(io_hook);
return -1;
}
if (info->io_map.update_map(info, port,
((read == NULL) ? 0 : 1),
((write == NULL) ? 0 : 1)) == -1) {
- PrintError("Could not update IO map for port %d\n", port);
+ PrintError("Could not update IO map for port %u (0x%x)\n", port, port);
V3_Free(io_hook);
return -1;
}
return 0;
}
-int v3_unhook_io_port(struct guest_info * info, uint_t port) {
- struct v3_io_hook * hook = v3_get_io_hook(info, port);
+int v3_unhook_io_port(struct guest_info * info, uint16_t port) {
+ struct v3_io_hook * hook = v3_get_io_hook(info, port);
- if (hook == NULL) {
- return -1;
- }
+ if (hook == NULL) {
+ PrintError("Could not find port to unhook %u (0x%x)\n", port, port);
+ return -1;
+ }
- v3_rb_erase(&(hook->tree_node), &(info->io_map.map));
+ v3_rb_erase(&(hook->tree_node), &(info->io_map.map));
- // set the arch map to default (this should be 1, 1)
- info->io_map.update_map(info, port, 0, 0);
+ // set the arch map to default (this should be 1, 1)
+ info->io_map.update_map(info, port, 0, 0);
- V3_Free(hook);
+ V3_Free(hook);
- return 0;
+ return 0;
}
void v3_print_io_map(struct guest_info * info) {
- struct v3_io_hook * tmp_hook = NULL;
- struct rb_node * node = v3_rb_first(&(info->io_map.map));
+ struct v3_io_hook * tmp_hook = NULL;
+ struct rb_node * node = v3_rb_first(&(info->io_map.map));
- PrintDebug("VMM IO Map\n");
+ V3_Print("VMM IO Map\n");
- do {
- tmp_hook = rb_entry(node, struct v3_io_hook, tree_node);
+ do {
+ tmp_hook = rb_entry(node, struct v3_io_hook, tree_node);
- PrintDebug("IO Port: %hu (Read=%p) (Write=%p)\n",
- tmp_hook->port,
- (void *)(tmp_hook->read), (void *)(tmp_hook->write));
- } while ((node = v3_rb_next(node)));
+ V3_Print("IO Port: %hu (Read=%p) (Write=%p)\n",
+ tmp_hook->port,
+ (void *)(tmp_hook->read), (void *)(tmp_hook->write));
+ } while ((node = v3_rb_next(node)));
}
/* FIX ME */
-static int default_write(uint16_t port, void *src, uint_t length, void * priv_data) {
- /*
+static int default_write(uint16_t port, void * src, uint_t length, void * priv_data) {
+ if (length == 1) {
+ v3_outb(port, *(uint8_t *)src);
+ } else if (length == 2) {
+ v3_outw(port, *(uint16_t *)src);
+ } else if (length == 4) {
+ v3_outdw(port, *(uint32_t *)src);
+ } else {
+ return -1;
+ }
- if (length == 1) {
- __asm__ __volatile__ (
- "outb %b0, %w1"
- :
- : "a" (*dst), "Nd" (port)
- );
- } else if (length == 2) {
- __asm__ __volatile__ (
- "outw %b0, %w1"
- :
- : "a" (*dst), "Nd" (port)
- );
- } else if (length == 4) {
- __asm__ __volatile__ (
- "outw %b0, %w1"
- :
- : "a" (*dst), "Nd" (port)
- );
- }
- */
- return 0;
+ return length;
}
static int default_read(uint16_t port, void * dst, uint_t length, void * priv_data) {
+ if (length == 1) {
+ *(uint8_t *)dst = v3_inb(port);
+ } else if (length == 2) {
+ *(uint16_t *)dst = v3_inw(port);
+ } else if (length == 4) {
+ *(uint32_t *)dst = v3_indw(port);
+ } else {
+ return -1;
+ }
- /*
- uint8_t value;
-
- __asm__ __volatile__ (
- "inb %w1, %b0"
- : "=a" (value)
- : "Nd" (port)
- );
-
- return value;
- */
-
- return 0;
+ return length;
}
if (guest_addr > info->mem_map.base_region.guest_end) {
PrintError("Guest Address Exceeds Base Memory Size (ga=%p), (limit=%p)\n",
(void *)guest_addr, (void *)info->mem_map.base_region.guest_end);
+ v3_print_mem_map(info);
+
return NULL;
}
-void print_shadow_map(struct guest_info * info) {
+void v3_print_mem_map(struct guest_info * info) {
struct rb_node * node = v3_rb_first(&(info->mem_map.shdw_regions));
struct v3_shadow_region * reg = &(info->mem_map.base_region);
int i = 0;
- PrintDebug("Memory Layout:\n");
+ V3_Print("Memory Layout:\n");
- PrintDebug("Base Region: 0x%p - 0x%p -> 0x%p\n",
+ V3_Print("Base Region: 0x%p - 0x%p -> 0x%p\n",
(void *)(reg->guest_start),
(void *)(reg->guest_end - 1),
(void *)(reg->host_addr));
do {
reg = rb_entry(node, struct v3_shadow_region, tree_node);
- PrintDebug("%d: 0x%p - 0x%p -> 0x%p\n", i,
+ V3_Print("%d: 0x%p - 0x%p -> 0x%p\n", i,
(void *)(reg->guest_start),
(void *)(reg->guest_end - 1),
(void *)(reg->host_addr));
- PrintDebug("\t(%s) (WriteHook = 0x%p) (ReadHook = 0x%p)\n",
+ V3_Print("\t(%s) (WriteHook = 0x%p) (ReadHook = 0x%p)\n",
v3_shdw_region_type_to_str(reg->host_type),
(void *)(reg->write_hook),
(void *)(reg->read_hook));
cp = ptr;
for (i = 0; i < length; i+= cols) {
if (hdr != NULL)
- PrintDebug("%s", hdr);
+ V3_Print("%s", hdr);
if ((flags & HD_OMIT_COUNT) == 0)
- PrintDebug("%04x ", i);
+ V3_Print("%04x ", i);
if ((flags & HD_OMIT_HEX) == 0) {
for (j = 0; j < cols; j++) {
k = i + j;
if (k < length)
- PrintDebug("%c%02x", delim, cp[k]);
+ V3_Print("%c%02x", delim, cp[k]);
else
- PrintDebug(" ");
+ V3_Print(" ");
}
}
if ((flags & HD_OMIT_CHARS) == 0) {
- PrintDebug(" |");
+ V3_Print(" |");
for (j = 0; j < cols; j++) {
k = i + j;
if (k >= length)
- PrintDebug(" ");
+ V3_Print(" ");
else if (cp[k] >= ' ' && cp[k] <= '~')
- PrintDebug("%c", cp[k]);
+ V3_Print("%c", cp[k]);
else
- PrintDebug(".");
+ V3_Print(".");
}
- PrintDebug("|");
+ V3_Print("|");
}
- PrintDebug("\n");
+ V3_Print("\n");
}
}
V3_Print("%s%s:%sCnt=%u,%sAvg. Time=%u\n",
hdr_buf,
code_str,
- (strlen(code_str) > 14) ? "\t" : "\t\t",
+ (strlen(code_str) > 13) ? "\t" : "\t\t",
evt->cnt,
(evt->cnt >= 100) ? "\t" : "\t\t",
(uint32_t)(evt->handler_time / evt->cnt));