diff options
author | kfraser@localhost.localdomain <kfraser@localhost.localdomain> | 2007-09-14 16:39:27 +0100 |
---|---|---|
committer | kfraser@localhost.localdomain <kfraser@localhost.localdomain> | 2007-09-14 16:39:27 +0100 |
commit | 9e6ca368e23e4d5131f3ec17231c8860f7a2a662 (patch) | |
tree | 7e1ae82138feac94e2e7bbdb696ee5f6aae75cbe /tools | |
parent | 221b43f35776f6397a1e52802d6391488401facb (diff) | |
download | xen-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.target | 6 | ||||
-rw-r--r-- | tools/ioemu/hw/pass-through.c | 454 | ||||
-rw-r--r-- | tools/ioemu/hw/pass-through.h | 89 | ||||
-rw-r--r-- | tools/ioemu/hw/pc.c | 27 | ||||
-rw-r--r-- | tools/ioemu/vl.c | 12 | ||||
-rw-r--r-- | tools/ioemu/vl.h | 2 | ||||
-rw-r--r-- | tools/libxc/xc_domain.c | 108 | ||||
-rw-r--r-- | tools/libxc/xenctrl.h | 39 | ||||
-rw-r--r-- | tools/python/xen/xend/XendConfig.py | 3 | ||||
-rw-r--r-- | tools/python/xen/xend/image.py | 2 | ||||
-rw-r--r-- | tools/python/xen/xm/create.py | 2 |
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]]) |