aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>2007-09-14 16:39:27 +0100
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>2007-09-14 16:39:27 +0100
commit9e6ca368e23e4d5131f3ec17231c8860f7a2a662 (patch)
tree7e1ae82138feac94e2e7bbdb696ee5f6aae75cbe /tools
parent221b43f35776f6397a1e52802d6391488401facb (diff)
downloadxen-9e6ca368e23e4d5131f3ec17231c8860f7a2a662.tar.gz
xen-9e6ca368e23e4d5131f3ec17231c8860f7a2a662.tar.bz2
xen-9e6ca368e23e4d5131f3ec17231c8860f7a2a662.zip
PCI passthru: tools changes (generic and vt-d)
I have added CONFIG_PASSTHROUGH in ioemu/Makefile.target and ioemu/hw/pc.c in attached vtd_tools2.patch. This should turn off libpci usage by default until user specifically enables it. This can be safely check-in without breaking builds for people who do not care about pass-through devices. I will try to think of a better way to enable this. Signed-off-by: Allen Kay <allen.m.kay@intel.com> Signed-off-by: Guy Zana <guy@neocleus.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/ioemu/Makefile.target6
-rw-r--r--tools/ioemu/hw/pass-through.c454
-rw-r--r--tools/ioemu/hw/pass-through.h89
-rw-r--r--tools/ioemu/hw/pc.c27
-rw-r--r--tools/ioemu/vl.c12
-rw-r--r--tools/ioemu/vl.h2
-rw-r--r--tools/libxc/xc_domain.c108
-rw-r--r--tools/libxc/xenctrl.h39
-rw-r--r--tools/python/xen/xend/XendConfig.py3
-rw-r--r--tools/python/xen/xend/image.py2
-rw-r--r--tools/python/xen/xm/create.py2
11 files changed, 732 insertions, 12 deletions
diff --git a/tools/ioemu/Makefile.target b/tools/ioemu/Makefile.target
index 33773a4929..d0a8162507 100644
--- a/tools/ioemu/Makefile.target
+++ b/tools/ioemu/Makefile.target
@@ -197,6 +197,9 @@ CPPFLAGS+=-D_GNU_SOURCE
LIBS+=-lm
LIBS+=-L../../libxc -lxenctrl -lxenguest
LIBS+=-L../../xenstore -lxenstore
+ifdef CONFIG_PASSTHROUGH
+LIBS+=-lpci
+endif
ifndef CONFIG_USER_ONLY
LIBS+=-lz
endif
@@ -400,6 +403,9 @@ VL_OBJS+= piix4acpi.o
VL_OBJS+= xenstore.o
VL_OBJS+= xen_platform.o
VL_OBJS+= tpm_tis.o
+ifdef CONFIG_PASSTHROUGH
+VL_OBJS+= pass-through.o
+endif
CPPFLAGS += -DHAS_AUDIO
endif
ifeq ($(TARGET_BASE_ARCH), ppc)
diff --git a/tools/ioemu/hw/pass-through.c b/tools/ioemu/hw/pass-through.c
new file mode 100644
index 0000000000..fcf3617cfa
--- /dev/null
+++ b/tools/ioemu/hw/pass-through.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (c) 2007, Neocleus Corporation.
+ * Copyright (c) 2007, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Alex Novik <alex@neocleus.com>
+ * Allen Kay <allen.m.kay@intel.com>
+ * Guy Zana <guy@neocleus.com>
+ *
+ * This file implements direct PCI assignment to a HVM guest
+ *
+ */
+#include "vl.h"
+#include "pass-through.h"
+#include "pci/header.h"
+#include "pci/pci.h"
+
+extern FILE *logfile;
+char *token;
+
+int pci_devs(const char *direct_pci)
+{
+ int count = 0;
+ const char *c;
+
+ /* skip first "[" character */
+ c = direct_pci + 1;
+ while ((c = strchr(c, '[')) != NULL) {
+ c++;
+ count++;
+ }
+ return (count);
+}
+
+int next_token(char *direct_pci)
+{
+ if (token == NULL)
+ token = strtok(direct_pci, ",");
+ else
+ token = strtok(NULL, ",");
+ token = strchr(token, 'x');
+ token = token + 1;
+ return ((int) strtol(token, NULL, 16));
+}
+
+void next_bdf(char *direct_pci, int *seg,
+ int *bus, int *dev, int *func)
+{
+ *seg = next_token(direct_pci);
+ *bus = next_token(direct_pci);
+ *dev = next_token(direct_pci);
+ *func = next_token(direct_pci);
+}
+
+uint8_t find_cap_offset(struct pci_dev *pci_dev, uint8_t cap)
+{
+ int id;
+ int max_cap = 48;
+ int pos = PCI_CAPABILITY_LIST;
+ int status;
+
+ status = pci_read_byte(pci_dev, PCI_STATUS);
+ if ( (status & PCI_STATUS_CAP_LIST) == 0 )
+ return 0;
+
+ while ( max_cap-- )
+ {
+ pos = pci_read_byte(pci_dev, pos);
+ if ( pos < 0x40 )
+ break;
+
+ pos &= ~3;
+ id = pci_read_byte(pci_dev, pos + PCI_CAP_LIST_ID);
+
+ if ( id == 0xff )
+ break;
+ if ( id == cap )
+ return pos;
+
+ pos += PCI_CAP_LIST_NEXT;
+ }
+ return 0;
+}
+
+void pdev_flr(struct pci_dev *pci_dev)
+{
+ int pos;
+ int dev_cap;
+ int dev_status;
+
+ pos = find_cap_offset(pci_dev, PCI_CAP_ID_EXP);
+ if ( pos )
+ {
+ dev_cap = pci_read_long(pci_dev, pos + PCI_EXP_DEVCAP);
+ if ( dev_cap & PCI_EXP_DEVCAP_FLR )
+ {
+ pci_write_word(pci_dev, pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_FLR);
+ do {
+ dev_status = pci_read_long(pci_dev, pos + PCI_EXP_DEVSTA);
+ } while (dev_status & PCI_EXP_DEVSTA_TRPND);
+ }
+ }
+}
+
+/* Being called each time a mmio region has been updated */
+void pt_iomem_map(PCIDevice *d, int i, uint32_t e_phys, uint32_t e_size,
+ int type)
+{
+ struct pt_dev *assigned_device = (struct pt_dev *)d;
+ uint32_t old_ebase = assigned_device->bases[i].e_physbase;
+ int first_map = ( assigned_device->bases[i].e_size == 0 );
+ int ret = 0;
+
+ assigned_device->bases[i].e_physbase = e_phys;
+ assigned_device->bases[i].e_size= e_size;
+
+ PT_LOG("e_phys=%08x maddr=%08x type=%d len=%08x index=%d\n",
+ e_phys, assigned_device->bases[i].access.maddr, type, e_size, i);
+
+ if ( e_size == 0 )
+ return;
+
+ if ( !first_map )
+ {
+ /* Remove old mapping */
+ ret = xc_domain_memory_mapping(xc_handle, domid, old_ebase >> 12,
+ assigned_device->bases[i].access.maddr >> 12,
+ (e_size+0xFFF) >> 12,
+ DPCI_REMOVE_MAPPING);
+ if ( ret != 0 )
+ {
+ PT_LOG("Error: remove old mapping failed!\n");
+ return;
+ }
+ }
+
+ /* Create new mapping */
+ ret = xc_domain_memory_mapping(xc_handle, domid,
+ assigned_device->bases[i].e_physbase >> 12,
+ assigned_device->bases[i].access.maddr >> 12,
+ (e_size+0xFFF) >> 12,
+ DPCI_ADD_MAPPING);
+ if ( ret != 0 )
+ PT_LOG("Error: create new mapping failed!\n");
+
+}
+
+/* Being called each time a pio region has been updated */
+void pt_ioport_map(PCIDevice *d, int i,
+ uint32_t e_phys, uint32_t e_size, int type)
+{
+ struct pt_dev *assigned_device = (struct pt_dev *)d;
+ uint32_t old_ebase = assigned_device->bases[i].e_physbase;
+ int first_map = ( assigned_device->bases[i].e_size == 0 );
+ int ret = 0;
+
+ assigned_device->bases[i].e_physbase = e_phys;
+ assigned_device->bases[i].e_size= e_size;
+
+ PT_LOG("e_phys=%04x pio_base=%04x len=%04x index=%d\n",
+ (uint16_t)e_phys, (uint16_t)assigned_device->bases[i].access.pio_base,
+ (uint16_t)e_size, i);
+
+ if ( e_size == 0 )
+ return;
+
+ if ( !first_map )
+ {
+ /* Remove old mapping */
+ ret = xc_domain_ioport_mapping(xc_handle, domid, old_ebase,
+ assigned_device->bases[i].access.pio_base, e_size,
+ DPCI_REMOVE_MAPPING);
+ if ( ret != 0 )
+ {
+ PT_LOG("Error: remove old mapping failed!\n");
+ return;
+ }
+ }
+
+ /* Create new mapping */
+ ret = xc_domain_ioport_mapping(xc_handle, domid, e_phys,
+ assigned_device->bases[i].access.pio_base, e_size,
+ DPCI_ADD_MAPPING);
+ if ( ret != 0 )
+ PT_LOG("Error: create new mapping failed!\n");
+
+}
+
+static void pt_pci_write_config(PCIDevice *d, uint32_t address, uint32_t val,
+ int len)
+{
+ struct pt_dev *assigned_device = (struct pt_dev *)d;
+ struct pci_dev *pci_dev = assigned_device->pci_dev;
+
+#ifdef PT_DEBUG_PCI_CONFIG_ACCESS
+ PT_LOG("(%x.%x): address=%04x val=0x%08x len=%d\n",
+ (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len);
+#endif
+
+ /* Pre-write hooking */
+ switch ( address ) {
+ case 0x0C ... 0x3F:
+ pci_default_write_config(d, address, val, len);
+ return;
+ }
+
+ /* PCI config pass-through */
+ if (address == 0x4) {
+ switch (len){
+ case 1:
+ pci_write_byte(pci_dev, address, val);
+ break;
+ case 2:
+ pci_write_word(pci_dev, address, val);
+ break;
+ case 4:
+ pci_write_long(pci_dev, address, val);
+ break;
+ }
+ }
+
+ if (address == 0x4) {
+ /* Post-write hooking */
+ pci_default_write_config(d, address, val, len);
+ }
+}
+
+static uint32_t pt_pci_read_config(PCIDevice *d, uint32_t address, int len)
+{
+ struct pt_dev *assigned_device = (struct pt_dev *)d;
+ struct pci_dev *pci_dev = assigned_device->pci_dev;
+ uint32_t val = 0xFF;
+
+ /* Pre-hooking */
+ switch ( address ) {
+ case 0x0C ... 0x3F:
+ val = pci_default_read_config(d, address, len);
+ goto exit;
+ }
+
+ switch ( len ) {
+ case 1:
+ val = pci_read_byte(pci_dev, address);
+ break;
+ case 2:
+ val = pci_read_word(pci_dev, address);
+ break;
+ case 4:
+ val = pci_read_long(pci_dev, address);
+ break;
+ }
+
+exit:
+
+#ifdef PT_DEBUG_PCI_CONFIG_ACCESS
+ PT_LOG("(%x.%x): address=%04x val=0x%08x len=%d\n",
+ (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len);
+#endif
+
+ return val;
+}
+
+static int pt_register_regions(struct pt_dev *assigned_device)
+{
+ int i = 0;
+ uint32_t bar_data = 0;
+ struct pci_dev *pci_dev = assigned_device->pci_dev;
+ PCIDevice *d = &assigned_device->dev;
+
+ /* Register PIO/MMIO BARs */
+ for ( i=0; i < PCI_BAR_ENTRIES; i++ )
+ {
+ if ( pci_dev->base_addr[i] )
+ {
+ assigned_device->bases[i].e_physbase = pci_dev->base_addr[i];
+ assigned_device->bases[i].access.u = pci_dev->base_addr[i];
+
+ /* Register current region */
+ bar_data = *((uint32_t*)(d->config + PCI_BASE_ADDRESS_0) + i);
+ if ( bar_data & PCI_ADDRESS_SPACE_IO )
+ pci_register_io_region((PCIDevice *)assigned_device, i,
+ (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_IO,
+ pt_ioport_map);
+ else if ( bar_data & PCI_ADDRESS_SPACE_MEM_PREFETCH )
+ pci_register_io_region((PCIDevice *)assigned_device, i,
+ (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_MEM_PREFETCH,
+ pt_iomem_map);
+ else
+ pci_register_io_region((PCIDevice *)assigned_device, i,
+ (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_MEM,
+ pt_iomem_map);
+
+ PT_LOG("IO region registered (size=0x%08x base_addr=0x%08x)\n",
+ (uint32_t)(pci_dev->size[i]),
+ (uint32_t)(pci_dev->base_addr[i]));
+ }
+ }
+
+ /* Register expansion ROM address */
+ if ( pci_dev->rom_base_addr && pci_dev->rom_size )
+ {
+ assigned_device->bases[PCI_ROM_SLOT].e_physbase =
+ pci_dev->rom_base_addr;
+ assigned_device->bases[PCI_ROM_SLOT].access.maddr =
+ pci_dev->rom_base_addr;
+ pci_register_io_region((PCIDevice *)assigned_device, PCI_ROM_SLOT,
+ pci_dev->rom_size, PCI_ADDRESS_SPACE_MEM_PREFETCH,
+ pt_iomem_map);
+
+ PT_LOG("Expansion ROM registered (size=0x%08x base_addr=0x%08x)\n",
+ (uint32_t)(pci_dev->rom_size), (uint32_t)(pci_dev->rom_base_addr));
+ }
+
+ return 0;
+}
+
+struct pt_dev * register_real_device(PCIBus *e_bus,
+ const char *e_dev_name, int e_devfn, uint8_t r_bus, uint8_t r_dev,
+ uint8_t r_func, uint32_t machine_irq, struct pci_access *pci_access)
+{
+ int rc, i;
+ struct pt_dev *assigned_device = NULL;
+ struct pci_dev *pci_dev;
+ struct pci_config_cf8 machine_bdf;
+ uint8_t e_device, e_intx;
+
+ PT_LOG("Assigning real physical device %02x:%02x.%x ...\n",
+ r_bus, r_dev, r_func);
+
+ /* Find real device structure */
+ for (pci_dev = pci_access->devices; pci_dev != NULL;
+ pci_dev = pci_dev->next)
+ {
+ if ((r_bus == pci_dev->bus) && (r_dev == pci_dev->dev)
+ && (r_func == pci_dev->func))
+ break;
+ }
+ if ( pci_dev == NULL )
+ {
+ PT_LOG("Error: couldn't locate device in libpci structures\n");
+ return NULL;
+ }
+
+ /* Register device */
+ assigned_device = (struct pt_dev *) pci_register_device(e_bus, e_dev_name,
+ sizeof(struct pt_dev), e_devfn,
+ pt_pci_read_config, pt_pci_write_config);
+ if ( assigned_device == NULL )
+ {
+ PT_LOG("Error: couldn't register real device\n");
+ return NULL;
+ }
+
+ assigned_device->pci_dev = pci_dev;
+
+ /* Issue PCIe FLR */
+ pdev_flr(pci_dev);
+
+ /* Tell XEN vmm to change iommu settings */
+ machine_bdf.reg = 0;
+ machine_bdf.bus = r_bus;
+ machine_bdf.dev = r_dev;
+ machine_bdf.func = r_func;
+ rc = xc_assign_device(xc_handle, domid, machine_bdf.value);
+ if ( rc < 0 )
+ PT_LOG("Error: xc_domain_assign_device error %d\n", rc);
+
+ /* Initialize virtualized PCI configuration (Extended 256 Bytes) */
+ for ( i = 0; i < PCI_CONFIG_SIZE; i++ )
+ assigned_device->dev.config[i] = pci_read_byte(pci_dev, i);
+
+ /* Handle real device's MMIO/PIO BARs */
+ pt_register_regions(assigned_device);
+
+ /* Bind interrupt */
+ e_device = (assigned_device->dev.devfn >> 3) & 0x1f;
+ e_intx = assigned_device->dev.config[0x3d]-1;
+
+ if ( PT_MACHINE_IRQ_AUTO == machine_irq )
+ machine_irq = pci_dev->irq;
+
+ /* bind machine_irq to device */
+ if ( 0 != machine_irq )
+ {
+ rc = xc_domain_bind_pt_pci_irq(xc_handle, domid, machine_irq, 0,
+ e_device, e_intx);
+ if ( rc < 0 )
+ {
+ /* TBD: unregister device in case of an error */
+ PT_LOG("Error: Binding of interrupt failed! rc=%d\n", rc);
+ }
+ }
+ else {
+ /* Disable PCI intx assertion (turn on bit10 of devctl) */
+ assigned_device->dev.config[0x05] |= 0x04;
+ pci_write_word(pci_dev, 0x04,
+ *(uint16_t *)(&assigned_device->dev.config[0x04]));
+ }
+
+ PT_LOG("Real physical device %02x:%02x.%x registered successfuly!\n",
+ r_bus, r_dev, r_func);
+
+ return assigned_device;
+}
+
+int pt_init(PCIBus *e_bus, char *direct_pci)
+{
+ int i;
+ int seg, b, d, f;
+ struct pt_dev *pt_dev;
+ struct pci_access *pci_access;
+ int dev_count = pci_devs(direct_pci);
+
+ /* Initialize libpci */
+ pci_access = pci_alloc();
+ if ( pci_access == NULL )
+ {
+ PT_LOG("pci_access is NULL\n");
+ return -1;
+ }
+ pci_init(pci_access);
+ pci_scan_bus(pci_access);
+
+ /* Assign given devices to guest */
+ for ( i = 0; i < dev_count; i++ )
+ {
+ /* Get next device bdf (bus, device, function) */
+ next_bdf(direct_pci, &seg, &b, &d, &f);
+
+ /* Register real device with the emulated bus */
+ pt_dev = register_real_device(e_bus, "DIRECT PCI", PT_VIRT_DEVFN_AUTO,
+ b, d, f, PT_MACHINE_IRQ_AUTO, pci_access);
+ if ( pt_dev == NULL )
+ {
+ PT_LOG("Error: Registration failed (%02x:%02x.%x)\n", b, d, f);
+ return -1;
+ }
+ }
+
+ /* Success */
+ return 0;
+}
diff --git a/tools/ioemu/hw/pass-through.h b/tools/ioemu/hw/pass-through.h
new file mode 100644
index 0000000000..42f7b52d74
--- /dev/null
+++ b/tools/ioemu/hw/pass-through.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2007, Neocleus Corporation.
+ * Copyright (c) 2007, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+#ifndef __PASSTHROUGH_H__
+#define __PASSTHROUGH_H__
+
+#include "vl.h"
+#include "pci/header.h"
+#include "pci/pci.h"
+
+/* Log acesss */
+#define PT_LOGGING_ENABLED
+
+#ifdef PT_LOGGING_ENABLED
+#define PT_LOG(_f, _a...) fprintf(logfile, "%s: " _f, __func__, ##_a)
+#else
+#define PT_LOG(_f, _a...)
+#endif
+
+/* Some compilation flags */
+// #define PT_DEBUG_PCI_CONFIG_ACCESS
+
+#define PT_MACHINE_IRQ_AUTO (0xFFFFFFFF)
+#define PT_VIRT_DEVFN_AUTO (-1)
+
+/* Misc PCI constants that should be moved to a separate library :) */
+#define PCI_CONFIG_SIZE (256)
+#define PCI_EXP_DEVCAP_FLR (1 << 28)
+#define PCI_EXP_DEVCTL_FLR (0x1b)
+#define PCI_BAR_ENTRIES (6)
+
+struct pt_region {
+ /* Virtual phys base & size */
+ uint32_t e_physbase;
+ uint32_t e_size;
+ /* Index of region in qemu */
+ uint32_t memory_index;
+ /* Translation of the emulated address */
+ union {
+ uint32_t maddr;
+ uint32_t pio_base;
+ uint32_t u;
+ } access;
+};
+
+/*
+ This structure holds the context of the mapping functions
+ and data that is relevant for qemu device management.
+*/
+struct pt_dev {
+ PCIDevice dev;
+ struct pci_dev *pci_dev; /* libpci struct */
+ struct pt_region bases[PCI_NUM_REGIONS]; /* Access regions */
+};
+
+/* Used for formatting PCI BDF into cf8 format */
+struct pci_config_cf8 {
+ union {
+ unsigned int value;
+ struct {
+ unsigned int reserved1:2;
+ unsigned int reg:6;
+ unsigned int func:3;
+ unsigned int dev:5;
+ unsigned int bus:8;
+ unsigned int reserved2:7;
+ unsigned int enable:1;
+ };
+ };
+};
+
+int pt_init(PCIBus * e_bus, char * direct_pci);
+
+#endif /* __PASSTHROUGH_H__ */
+
diff --git a/tools/ioemu/hw/pc.c b/tools/ioemu/hw/pc.c
index 1c33cdc8da..8c17c25477 100644
--- a/tools/ioemu/hw/pc.c
+++ b/tools/ioemu/hw/pc.c
@@ -465,7 +465,7 @@ static void pc_init1(uint64_t ram_size, int vga_ram_size, char *boot_device,
DisplayState *ds, const char **fd_filename, int snapshot,
const char *kernel_filename, const char *kernel_cmdline,
const char *initrd_filename,
- int pci_enabled)
+ int pci_enabled, const char *direct_pci)
{
#ifndef NOBIOS
char buf[1024];
@@ -480,6 +480,7 @@ static void pc_init1(uint64_t ram_size, int vga_ram_size, char *boot_device,
int piix3_devfn = -1;
CPUState *env;
NICInfo *nd;
+ int rc;
linux_boot = (kernel_filename != NULL);
@@ -665,6 +666,19 @@ static void pc_init1(uint64_t ram_size, int vga_ram_size, char *boot_device,
}
}
+#ifdef CONFIG_PASSTHROUGH
+ /* Pass-through Initialization */
+ if ( pci_enabled && direct_pci )
+ {
+ rc = pt_init(pci_bus, direct_pci);
+ if ( rc < 0 )
+ {
+ fprintf(logfile, "Error: Initialization failed for pass-through devices\n");
+ exit(1);
+ }
+ }
+#endif
+
rtc_state = rtc_init(0x70, 8);
register_ioport_read(0x92, 1, 1, ioport92_read, NULL);
@@ -801,12 +815,14 @@ static void pc_init_pci(uint64_t ram_size, int vga_ram_size, char *boot_device,
int snapshot,
const char *kernel_filename,
const char *kernel_cmdline,
- const char *initrd_filename)
+ const char *initrd_filename,
+ const char *direct_pci)
{
pc_init1(ram_size, vga_ram_size, boot_device,
ds, fd_filename, snapshot,
kernel_filename, kernel_cmdline,
- initrd_filename, 1);
+ initrd_filename, 1,
+ direct_pci);
}
static void pc_init_isa(uint64_t ram_size, int vga_ram_size, char *boot_device,
@@ -814,12 +830,13 @@ static void pc_init_isa(uint64_t ram_size, int vga_ram_size, char *boot_device,
int snapshot,
const char *kernel_filename,
const char *kernel_cmdline,
- const char *initrd_filename)
+ const char *initrd_filename,
+ const char *unused)
{
pc_init1(ram_size, vga_ram_size, boot_device,
ds, fd_filename, snapshot,
kernel_filename, kernel_cmdline,
- initrd_filename, 0);
+ initrd_filename, 0, NULL);
}
QEMUMachine pc_machine = {
diff --git a/tools/ioemu/vl.c b/tools/ioemu/vl.c
index 6c300fa3d3..a6d0b948f2 100644
--- a/tools/ioemu/vl.c
+++ b/tools/ioemu/vl.c
@@ -6513,6 +6513,7 @@ enum {
QEMU_OPTION_acpi,
QEMU_OPTION_vncviewer,
QEMU_OPTION_vncunused,
+ QEMU_OPTION_pci,
};
typedef struct QEMUOption {
@@ -6610,6 +6611,7 @@ const QEMUOption qemu_options[] = {
{ "d", HAS_ARG, QEMU_OPTION_d },
{ "vcpus", 1, QEMU_OPTION_vcpus },
{ "acpi", 0, QEMU_OPTION_acpi },
+ { "pci", HAS_ARG, QEMU_OPTION_pci},
{ NULL },
};
@@ -7065,9 +7067,9 @@ int main(int argc, char **argv)
extern void *buffered_pio_page;
#endif
sigset_t set;
-
char qemu_dm_logfilename[128];
-
+ const char *direct_pci = NULL;
+
/* Ensure that SIGUSR2 is blocked by default when a new thread is created,
then only the threads that use the signal unblock it -- this fixes a
race condition in Qcow support where the AIO signal is misdelivered. */
@@ -7560,6 +7562,9 @@ int main(int argc, char **argv)
case QEMU_OPTION_vncunused:
vncunused++;
break;
+ case QEMU_OPTION_pci:
+ direct_pci = optarg;
+ break;
}
}
}
@@ -7926,7 +7931,8 @@ int main(int argc, char **argv)
machine->init(ram_size, vga_ram_size, boot_device,
ds, fd_filename, snapshot,
- kernel_filename, kernel_cmdline, initrd_filename);
+ kernel_filename, kernel_cmdline, initrd_filename,
+ direct_pci);
free(boot_device);
/* init USB devices */
diff --git a/tools/ioemu/vl.h b/tools/ioemu/vl.h
index dda595b480..7b4cff58a9 100644
--- a/tools/ioemu/vl.h
+++ b/tools/ioemu/vl.h
@@ -717,7 +717,7 @@ typedef void QEMUMachineInitFunc(uint64_t ram_size, int vga_ram_size,
char *boot_device,
DisplayState *ds, const char **fd_filename, int snapshot,
const char *kernel_filename, const char *kernel_cmdline,
- const char *initrd_filename);
+ const char *initrd_filename, const char *direct_pci);
typedef struct QEMUMachine {
const char *name;
diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c
index fdb87f0bd1..3c3bb351f6 100644
--- a/tools/libxc/xc_domain.c
+++ b/tools/libxc/xc_domain.c
@@ -734,6 +734,114 @@ int xc_domain_setdebugging(int xc_handle,
return do_domctl(xc_handle, &domctl);
}
+int xc_assign_device(
+ int xc_handle,
+ uint32_t domid,
+ uint32_t machine_bdf)
+{
+ DECLARE_DOMCTL;
+
+ domctl.cmd = XEN_DOMCTL_assign_device;
+ domctl.domain = domid;
+ domctl.u.assign_device.machine_bdf = machine_bdf;
+
+ return do_domctl(xc_handle, &domctl);
+}
+
+/* Pass-through: binds machine irq to guests irq */
+int xc_domain_bind_pt_irq(
+ int xc_handle,
+ uint32_t domid,
+ uint8_t machine_irq,
+ uint8_t irq_type,
+ uint8_t bus,
+ uint8_t device,
+ uint8_t intx,
+ uint8_t isa_irq)
+{
+ int rc;
+ xen_domctl_bind_pt_irq_t * bind;
+ DECLARE_DOMCTL;
+
+ domctl.cmd = XEN_DOMCTL_bind_pt_irq;
+ domctl.domain = (domid_t)domid;
+
+ bind = &(domctl.u.bind_pt_irq);
+ bind->hvm_domid = domid;
+ bind->irq_type = irq_type;
+ bind->machine_irq = machine_irq;
+ bind->u.pci.bus = bus;
+ bind->u.pci.device = device;
+ bind->u.pci.intx = intx;
+ bind->u.isa.isa_irq = isa_irq;
+
+ rc = do_domctl(xc_handle, &domctl);
+ return rc;
+}
+
+int xc_domain_bind_pt_pci_irq(
+ int xc_handle,
+ uint32_t domid,
+ uint8_t machine_irq,
+ uint8_t bus,
+ uint8_t device,
+ uint8_t intx)
+{
+
+ return (xc_domain_bind_pt_irq(xc_handle, domid, machine_irq,
+ PT_IRQ_TYPE_PCI, bus, device, intx, 0));
+}
+
+int xc_domain_bind_pt_isa_irq(
+ int xc_handle,
+ uint32_t domid,
+ uint8_t machine_irq)
+{
+
+ return (xc_domain_bind_pt_irq(xc_handle, domid, machine_irq,
+ PT_IRQ_TYPE_ISA, 0, 0, 0, machine_irq));
+}
+
+int xc_domain_memory_mapping(
+ int xc_handle,
+ uint32_t domid,
+ unsigned long first_gfn,
+ unsigned long first_mfn,
+ unsigned long nr_mfns,
+ uint32_t add_mapping)
+{
+ DECLARE_DOMCTL;
+
+ domctl.cmd = XEN_DOMCTL_memory_mapping;
+ domctl.domain = domid;
+ domctl.u.memory_mapping.first_gfn = first_gfn;
+ domctl.u.memory_mapping.first_mfn = first_mfn;
+ domctl.u.memory_mapping.nr_mfns = nr_mfns;
+ domctl.u.memory_mapping.add_mapping = add_mapping;
+
+ return do_domctl(xc_handle, &domctl);
+}
+
+int xc_domain_ioport_mapping(
+ int xc_handle,
+ uint32_t domid,
+ uint32_t first_gport,
+ uint32_t first_mport,
+ uint32_t nr_ports,
+ uint32_t add_mapping)
+{
+ DECLARE_DOMCTL;
+
+ domctl.cmd = XEN_DOMCTL_ioport_mapping;
+ domctl.domain = domid;
+ domctl.u.ioport_mapping.first_gport = first_gport;
+ domctl.u.ioport_mapping.first_mport = first_mport;
+ domctl.u.ioport_mapping.nr_ports = nr_ports;
+ domctl.u.ioport_mapping.add_mapping = add_mapping;
+
+ return do_domctl(xc_handle, &domctl);
+}
+
/*
* Local variables:
* mode: C
diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h
index 8566b518f0..6b056b9e9d 100644
--- a/tools/libxc/xenctrl.h
+++ b/tools/libxc/xenctrl.h
@@ -897,4 +897,43 @@ int xc_ia64_save_to_nvram(int xc_handle, uint32_t dom);
/* IA64 specific, nvram init */
int xc_ia64_nvram_init(int xc_handle, char *dom_name, uint32_t dom);
+/* HVM guest pass-through */
+int xc_assign_device(int xc_handle,
+ uint32_t domid,
+ uint32_t machine_bdf);
+
+int xc_domain_memory_mapping(int xc_handle,
+ uint32_t domid,
+ unsigned long first_gfn,
+ unsigned long first_mfn,
+ unsigned long nr_mfns,
+ uint32_t add_mapping);
+
+int xc_domain_ioport_mapping(int xc_handle,
+ uint32_t domid,
+ uint32_t first_gport,
+ uint32_t first_mport,
+ uint32_t nr_ports,
+ uint32_t add_mapping);
+
+int xc_domain_bind_pt_irq(int xc_handle,
+ uint32_t domid,
+ uint8_t machine_irq,
+ uint8_t irq_type,
+ uint8_t bus,
+ uint8_t device,
+ uint8_t intx,
+ uint8_t isa_irq);
+
+int xc_domain_bind_pt_pci_irq(int xc_handle,
+ uint32_t domid,
+ uint8_t machine_irq,
+ uint8_t bus,
+ uint8_t device,
+ uint8_t intx);
+
+int xc_domain_bind_pt_isa_irq(int xc_handle,
+ uint32_t domid,
+ uint8_t machine_irq);
+
#endif /* XENCTRL_H */
diff --git a/tools/python/xen/xend/XendConfig.py b/tools/python/xen/xend/XendConfig.py
index 7ec493a059..d0e62b9b6b 100644
--- a/tools/python/xen/xend/XendConfig.py
+++ b/tools/python/xen/xend/XendConfig.py
@@ -127,7 +127,7 @@ XENAPI_PLATFORM_CFG = [ 'acpi', 'apic', 'boot', 'device_model', 'display',
'nographic', 'pae', 'rtc_timeoffset', 'serial', 'sdl',
'soundhw','stdvga', 'usb', 'usbdevice', 'vnc',
'vncconsole', 'vncdisplay', 'vnclisten',
- 'vncpasswd', 'vncunused', 'xauthority']
+ 'vncpasswd', 'vncunused', 'xauthority', 'pci']
# Xen API console 'other_config' keys.
XENAPI_CONSOLE_OTHER_CFG = ['vncunused', 'vncdisplay', 'vnclisten',
@@ -168,6 +168,7 @@ XENAPI_CFG_TYPES = {
'tools_version': dict,
'other_config': dict,
'security_label': str,
+ 'pci': str,
}
# List of legacy configuration keys that have no equivalent in the
diff --git a/tools/python/xen/xend/image.py b/tools/python/xen/xend/image.py
index bd5e6b2cf4..95366820b0 100644
--- a/tools/python/xen/xend/image.py
+++ b/tools/python/xen/xend/image.py
@@ -309,7 +309,7 @@ class HVMImageHandler(ImageHandler):
def parseDeviceModelArgs(self, vmConfig):
dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
'localtime', 'serial', 'stdvga', 'isa',
- 'acpi', 'usb', 'usbdevice', 'keymap' ]
+ 'acpi', 'usb', 'usbdevice', 'keymap', 'pci' ]
ret = ['-vcpus', str(self.vm.getVCpuCount())]
diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py
index a6d24b8b69..bcfd0c73ec 100644
--- a/tools/python/xen/xm/create.py
+++ b/tools/python/xen/xm/create.py
@@ -721,7 +721,7 @@ def configure_hvm(config_image, vals):
'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'soundhw',
'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
'sdl', 'display', 'xauthority', 'rtc_timeoffset', 'monitor',
- 'acpi', 'apic', 'usb', 'usbdevice', 'keymap' ]
+ 'acpi', 'apic', 'usb', 'usbdevice', 'keymap', 'pci' ]
for a in args:
if a in vals.__dict__ and vals.__dict__[a] is not None:
config_image.append([a, vals.__dict__[a]])