Hi all, This patch series introduces the PCI passthrough for Xen. First, we have HostPCIDevice that help to access one PCI device of the host. Then, there is an additions in the QEMU code, pci_check_bar_overlap. There are also several change in pci_ids and pci_regs. Last part, but not least, the PCI passthrough device himself. Cut in 3 parts (or file), there is one to take care of the initialisation of a passthrough device. The second one handle everything about the config address space, there are specifics functions for every config register. The third one is to handle MSI. There is a patch series on xen-devel (applied to xen-unstable) that add the support of setting a PCI passthrough device through QMP from libxl (xen tool stack). It is just a call to device_add, with the driver parametter hostaddr="0000:00:1b.0". Change since v4: - return -errno if there is an error in host_pci_get_* - rename internal function get_value to get_hex_value (and return the same error value has get_resource) Change since v3-v4: - host_pci_get_* can now return an error, and take an extra parameter, a pointer to store the wanted value. - The memory_region for the PCI BAR are handled "manualy" because calling pci_default_write_config was not possible, because the XenPT handle the PCIIORegion it self. This make possible to do a device_remove. - Introduction of PT_ERR and PT_WARN macro to print debug and error messages. Also, these macro as well as PT_LOG will always print the short BDF of the device in the guest point of view. - PT_ERR is print by default (for all error messages). - Some debug/error message have been improve and should be a bit more useful. - hw_error have been removed from the code, and have been replaced by either a call to qemu_system_shudown_request() (that lead to a domain destroy) or a failed in the initialisation of the device. - Now, every patchs should compile with no error. Change v2-v3; - in host-pci-device.c: - Return more usefull error code in get_ressource(). - Use macro in host_pci_find_ext_cap_offset instead of raw number. But I still not sure if PCI_MAX_EXT_CAP is right, it''s result is 480 like it was before, so it''s maybe ok. - All use of MSI stuff in two first pci passthrough patch have been removed and move to the last patch. Change v1-v2: - fix style issue (checkpatch.pl) - set the original authors, add some missing copyright headers - HostPCIDevice: - introduce HostPCIIORegions (with base_addr, size, flags) - save all flags from ./resource and store it in a separate field. - fix endianess on write - new host_pci_dev_put function - use pci.c like interface host_pci_get/set_byte/word/long (instead of host_pci_read/write_) - compile HostPCIDevice only on linux (as well as xen_pci_passthrough) - introduce apic-msidef.h file. - no more run_one_timer, if a pci device is in the middle of a power transition, just "return an error" in config read/write - use a global var mapped_machine_irq (local to xen_pci_passthrough.c) - add msitranslate and power-mgmt ad qdev property Allen Kay (2): Introduce Xen PCI Passthrough, qdevice (1/3) Introduce Xen PCI Passthrough, PCI config space helpers (2/3) Anthony PERARD (6): pci_ids: Add INTEL_82599_VF id. pci_regs: Fix value of PCI_EXP_TYPE_RC_EC. pci_regs: Add PCI_EXP_TYPE_PCIE_BRIDGE configure: Introduce --enable-xen-pci-passthrough. Introduce HostPCIDevice to access a pci device on the host. Introduce apic-msidef.h Jiang Yunhong (1): Introduce Xen PCI Passthrough, MSI (3/3) Yuji Shimada (1): pci.c: Add pci_check_bar_overlap Makefile.target | 6 + configure | 25 + hw/apic-msidef.h | 30 + hw/apic.c | 11 +- hw/host-pci-device.c | 278 ++++ hw/host-pci-device.h | 75 + hw/pci.c | 47 + hw/pci.h | 3 + hw/pci_ids.h | 1 + hw/pci_regs.h | 3 +- hw/xen_common.h | 3 + hw/xen_pci_passthrough.c | 902 ++++++++++++ hw/xen_pci_passthrough.h | 337 +++++ hw/xen_pci_passthrough_config_init.c | 2637 ++++++++++++++++++++++++++++++++++ hw/xen_pci_passthrough_msi.c | 678 +++++++++ xen-all.c | 12 + 16 files changed, 5037 insertions(+), 11 deletions(-) create mode 100644 hw/apic-msidef.h create mode 100644 hw/host-pci-device.c create mode 100644 hw/host-pci-device.h create mode 100644 hw/xen_pci_passthrough.c create mode 100644 hw/xen_pci_passthrough.h create mode 100644 hw/xen_pci_passthrough_config_init.c create mode 100644 hw/xen_pci_passthrough_msi.c -- Anthony PERARD
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> --- hw/pci_ids.h | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/hw/pci_ids.h b/hw/pci_ids.h index 83f3893..2ea5ec2 100644 --- a/hw/pci_ids.h +++ b/hw/pci_ids.h @@ -117,6 +117,7 @@ #define PCI_DEVICE_ID_INTEL_82801I_UHCI6 0x2939 #define PCI_DEVICE_ID_INTEL_82801I_EHCI1 0x293a #define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c +#define PCI_DEVICE_ID_INTEL_82599_VF 0x10ed #define PCI_VENDOR_ID_XEN 0x5853 #define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 -- Anthony PERARD
Anthony PERARD
2011-Nov-24 17:44 UTC
[PATCH V5 02/10] pci_regs: Fix value of PCI_EXP_TYPE_RC_EC.
Value check in PCI Express Base Specification rev 1.1 Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> --- hw/pci_regs.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/hw/pci_regs.h b/hw/pci_regs.h index e8357c3..6b42515 100644 --- a/hw/pci_regs.h +++ b/hw/pci_regs.h @@ -393,7 +393,7 @@ #define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ #define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ #define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */ -#define PCI_EXP_TYPE_RC_EC 0x10 /* Root Complex Event Collector */ +#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ #define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ #define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ #define PCI_EXP_DEVCAP 4 /* Device capabilities */ -- Anthony PERARD
Anthony PERARD
2011-Nov-24 17:44 UTC
[PATCH V5 03/10] pci_regs: Add PCI_EXP_TYPE_PCIE_BRIDGE
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> --- hw/pci_regs.h | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/hw/pci_regs.h b/hw/pci_regs.h index 6b42515..56a404b 100644 --- a/hw/pci_regs.h +++ b/hw/pci_regs.h @@ -392,6 +392,7 @@ #define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ #define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ #define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ +#define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIE Bridge */ #define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */ #define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ #define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ -- Anthony PERARD
Anthony PERARD
2011-Nov-24 17:44 UTC
[PATCH V5 04/10] configure: Introduce --enable-xen-pci-passthrough.
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> --- configure | 25 +++++++++++++++++++++++++ 1 files changed, 25 insertions(+), 0 deletions(-) diff --git a/configure b/configure index f033438..8ad6698 100755 --- a/configure +++ b/configure @@ -127,6 +127,7 @@ vnc_png="" vnc_thread="no" xen="" xen_ctrl_version="" +xen_pci_passthrough="" linux_aio="" attr="" libattr="" @@ -644,6 +645,10 @@ for opt do ;; --enable-xen) xen="yes" ;; + --disable-xen-pci-passthrough) xen_pci_passthrough="no" + ;; + --enable-xen-pci-passthrough) xen_pci_passthrough="yes" + ;; --disable-brlapi) brlapi="no" ;; --enable-brlapi) brlapi="yes" @@ -990,6 +995,8 @@ echo " (affects only QEMU, not qemu-img)" echo " --enable-mixemu enable mixer emulation" echo " --disable-xen disable xen backend driver support" echo " --enable-xen enable xen backend driver support" +echo " --disable-xen-pci-passthrough" +echo " --enable-xen-pci-passthrough" echo " --disable-brlapi disable BrlAPI" echo " --enable-brlapi enable BrlAPI" echo " --disable-vnc-tls disable TLS encryption for VNC server" @@ -1398,6 +1405,21 @@ EOF fi fi +if test "$xen_pci_passthrough" != "no"; then + if test "$xen" = "yes" && test "$linux" = "yes"; then + xen_pci_passthrough=yes + else + if test "$xen_pci_passthrough" = "yes"; then + echo "ERROR" + echo "ERROR: User requested feature Xen PCI Passthrough" + echo "ERROR: but this feature require /sys from Linux" + echo "ERROR" + exit 1; + fi + xen_pci_passthrough=no + fi +fi + ########################################## # pkg-config probe @@ -3500,6 +3522,9 @@ case "$target_arch2" in if test "$xen" = "yes" -a "$target_softmmu" = "yes" ; then target_phys_bits=64 echo "CONFIG_XEN=y" >> $config_target_mak + if test "$xen_pci_passthrough" = yes; then + echo "CONFIG_XEN_PCI_PASSTHROUGH=y" >> "$config_target_mak" + fi else echo "CONFIG_NO_XEN=y" >> $config_target_mak fi -- Anthony PERARD
Anthony PERARD
2011-Nov-24 17:44 UTC
[PATCH V5 05/10] Introduce HostPCIDevice to access a pci device on the host.
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> --- Makefile.target | 3 + hw/host-pci-device.c | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/host-pci-device.h | 75 ++++++++++++++ 3 files changed, 356 insertions(+), 0 deletions(-) create mode 100644 hw/host-pci-device.c create mode 100644 hw/host-pci-device.h diff --git a/Makefile.target b/Makefile.target index a111521..e527c1b 100644 --- a/Makefile.target +++ b/Makefile.target @@ -219,6 +219,9 @@ obj-$(CONFIG_NO_XEN) += xen-stub.o obj-i386-$(CONFIG_XEN) += xen_platform.o +# Xen PCI Passthrough +obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += host-pci-device.o + # Inter-VM PCI shared memory CONFIG_IVSHMEM ifeq ($(CONFIG_KVM), y) diff --git a/hw/host-pci-device.c b/hw/host-pci-device.c new file mode 100644 index 0000000..b1e2db3 --- /dev/null +++ b/hw/host-pci-device.c @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2011 Citrix Ltd. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "host-pci-device.h" + +#define PCI_MAX_EXT_CAP \ + ((PCIE_CONFIG_SPACE_SIZE - PCI_CONFIG_SPACE_SIZE) / (PCI_CAP_SIZEOF + 4)) + +enum error_code { + ERROR_SYNTAX = 1, +}; + +static int path_to(const HostPCIDevice *d, + const char *name, char *buf, ssize_t size) +{ + return snprintf(buf, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%x/%s", + d->domain, d->bus, d->dev, d->func, name); +} + +static int get_resource(HostPCIDevice *d) +{ + int i, rc = 0; + FILE *f; + char path[PATH_MAX]; + unsigned long long start, end, flags, size; + + path_to(d, "resource", path, sizeof (path)); + f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "Error: Can''t open %s: %s\n", path, strerror(errno)); + return -errno; + } + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + if (fscanf(f, "%llx %llx %llx", &start, &end, &flags) != 3) { + fprintf(stderr, "Error: Syntax error in %s\n", path); + rc = ERROR_SYNTAX; + break; + } + if (start) { + size = end - start + 1; + } else { + size = 0; + } + + if (i < PCI_ROM_SLOT) { + d->io_regions[i].base_addr = start; + d->io_regions[i].size = size; + d->io_regions[i].flags = flags; + } else { + d->rom.base_addr = start; + d->rom.size = size; + d->rom.flags = flags; + } + } + + fclose(f); + return rc; +} + +static int get_hex_value(HostPCIDevice *d, const char *name, + unsigned long *pvalue) +{ + char path[PATH_MAX]; + FILE *f; + unsigned long value; + + path_to(d, name, path, sizeof (path)); + f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "Error: Can''t open %s: %s\n", path, strerror(errno)); + return -errno; + } + if (fscanf(f, "%lx\n", &value) != 1) { + fprintf(stderr, "Error: Syntax error in %s\n", path); + fclose(f); + return ERROR_SYNTAX; + } + fclose(f); + *pvalue = value; + return 0; +} + +static bool pci_dev_is_virtfn(HostPCIDevice *d) +{ + char path[PATH_MAX]; + struct stat buf; + + path_to(d, "physfn", path, sizeof (path)); + return !stat(path, &buf); +} + +static int host_pci_config_fd(HostPCIDevice *d) +{ + char path[PATH_MAX]; + + if (d->config_fd < 0) { + path_to(d, "config", path, sizeof (path)); + d->config_fd = open(path, O_RDWR); + if (d->config_fd < 0) { + fprintf(stderr, "HostPCIDevice: Can not open ''%s'': %s\n", + path, strerror(errno)); + } + } + return d->config_fd; +} +static int host_pci_config_read(HostPCIDevice *d, int pos, void *buf, int len) +{ + int fd = host_pci_config_fd(d); + int res = 0; + +again: + res = pread(fd, buf, len, pos); + if (res != len) { + if (res < 0 && (errno == EINTR || errno == EAGAIN)) { + goto again; + } + fprintf(stderr, "%s: read failed: %s (fd: %i)\n", + __func__, strerror(errno), fd); + return -errno; + } + return 0; +} +static int host_pci_config_write(HostPCIDevice *d, + int pos, const void *buf, int len) +{ + int fd = host_pci_config_fd(d); + int res = 0; + +again: + res = pwrite(fd, buf, len, pos); + if (res != len) { + if (res < 0 && (errno == EINTR || errno == EAGAIN)) { + goto again; + } + fprintf(stderr, "%s: write failed: %s\n", + __func__, strerror(errno)); + return -errno; + } + return 0; +} + +int host_pci_get_byte(HostPCIDevice *d, int pos, uint8_t *p) +{ + uint8_t buf; + int rc = host_pci_config_read(d, pos, &buf, 1); + if (rc == 0) { + *p = buf; + } + return rc; +} +int host_pci_get_word(HostPCIDevice *d, int pos, uint16_t *p) +{ + uint16_t buf; + int rc = host_pci_config_read(d, pos, &buf, 2); + if (rc == 0) { + *p = le16_to_cpu(buf); + } + return rc; +} +int host_pci_get_long(HostPCIDevice *d, int pos, uint32_t *p) +{ + uint32_t buf; + int rc = host_pci_config_read(d, pos, &buf, 4); + if (rc == 0) { + *p = le32_to_cpu(buf); + } + return rc; +} +int host_pci_get_block(HostPCIDevice *d, int pos, uint8_t *buf, int len) +{ + return host_pci_config_read(d, pos, buf, len); +} + +int host_pci_set_byte(HostPCIDevice *d, int pos, uint8_t data) +{ + return host_pci_config_write(d, pos, &data, 1); +} +int host_pci_set_word(HostPCIDevice *d, int pos, uint16_t data) +{ + data = cpu_to_le16(data); + return host_pci_config_write(d, pos, &data, 2); +} +int host_pci_set_long(HostPCIDevice *d, int pos, uint32_t data) +{ + data = cpu_to_le32(data); + return host_pci_config_write(d, pos, &data, 4); +} +int host_pci_set_block(HostPCIDevice *d, int pos, uint8_t *buf, int len) +{ + return host_pci_config_write(d, pos, buf, len); +} + +uint32_t host_pci_find_ext_cap_offset(HostPCIDevice *d, uint32_t cap) +{ + uint32_t header = 0; + int max_cap = PCI_MAX_EXT_CAP; + int pos = PCI_CONFIG_SPACE_SIZE; + + do { + if (host_pci_get_long(d, pos, &header)) { + break; + } + /* + * If we have no capabilities, this is indicated by cap ID, + * cap version and next pointer all being 0. + */ + if (header == 0) { + break; + } + + if (PCI_EXT_CAP_ID(header) == cap) { + return pos; + } + + pos = PCI_EXT_CAP_NEXT(header); + if (pos < PCI_CONFIG_SPACE_SIZE) { + break; + } + + max_cap--; + } while (max_cap > 0); + + return 0; +} + +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func) +{ + HostPCIDevice *d = NULL; + unsigned long v = 0; + + d = g_new0(HostPCIDevice, 1); + + d->config_fd = -1; + d->domain = 0; + d->bus = bus; + d->dev = dev; + d->func = func; + + if (host_pci_config_fd(d) == -1) { + goto error; + } + if (get_resource(d) != 0) { + goto error; + } + + if (get_hex_value(d, "vendor", &v)) { + goto error; + } + d->vendor_id = v; + if (get_hex_value(d, "device", &v)) { + goto error; + } + d->device_id = v; + d->is_virtfn = pci_dev_is_virtfn(d); + + return d; +error: + if (d->config_fd >= 0) { + close(d->config_fd); + } + g_free(d); + return NULL; +} + +void host_pci_device_put(HostPCIDevice *d) +{ + if (d->config_fd >= 0) { + close(d->config_fd); + } + g_free(d); +} diff --git a/hw/host-pci-device.h b/hw/host-pci-device.h new file mode 100644 index 0000000..c8880eb --- /dev/null +++ b/hw/host-pci-device.h @@ -0,0 +1,75 @@ +#ifndef HW_HOST_PCI_DEVICE +# define HW_HOST_PCI_DEVICE + +#include "pci.h" + +/* + * from linux/ioport.h + * IO resources have these defined flags. + */ +#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */ + +#define IORESOURCE_TYPE_BITS 0x00000f00 /* Resource type */ +#define IORESOURCE_IO 0x00000100 +#define IORESOURCE_MEM 0x00000200 +#define IORESOURCE_IRQ 0x00000400 +#define IORESOURCE_DMA 0x00000800 + +#define IORESOURCE_PREFETCH 0x00001000 /* No side effects */ +#define IORESOURCE_READONLY 0x00002000 +#define IORESOURCE_CACHEABLE 0x00004000 +#define IORESOURCE_RANGELENGTH 0x00008000 +#define IORESOURCE_SHADOWABLE 0x00010000 + +#define IORESOURCE_SIZEALIGN 0x00020000 /* size indicates alignment */ +#define IORESOURCE_STARTALIGN 0x00040000 /* start field is alignment */ + +#define IORESOURCE_MEM_64 0x00100000 + + /* Userland may not map this resource */ +#define IORESOURCE_EXCLUSIVE 0x08000000 +#define IORESOURCE_DISABLED 0x10000000 +#define IORESOURCE_UNSET 0x20000000 +#define IORESOURCE_AUTO 0x40000000 + /* Driver has marked this resource busy */ +#define IORESOURCE_BUSY 0x80000000 + + +typedef struct HostPCIIORegion { + unsigned long flags; + pcibus_t base_addr; + pcibus_t size; +} HostPCIIORegion; + +typedef struct HostPCIDevice { + uint16_t domain; + uint8_t bus; + uint8_t dev; + uint8_t func; + + uint16_t vendor_id; + uint16_t device_id; + + HostPCIIORegion io_regions[PCI_NUM_REGIONS - 1]; + HostPCIIORegion rom; + + bool is_virtfn; + + int config_fd; +} HostPCIDevice; + +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func); +void host_pci_device_put(HostPCIDevice *pci_dev); + +int host_pci_get_byte(HostPCIDevice *d, int pos, uint8_t *p); +int host_pci_get_word(HostPCIDevice *d, int pos, uint16_t *p); +int host_pci_get_long(HostPCIDevice *d, int pos, uint32_t *p); +int host_pci_get_block(HostPCIDevice *d, int pos, uint8_t *buf, int len); +int host_pci_set_byte(HostPCIDevice *d, int pos, uint8_t data); +int host_pci_set_word(HostPCIDevice *d, int pos, uint16_t data); +int host_pci_set_long(HostPCIDevice *d, int pos, uint32_t data); +int host_pci_set_block(HostPCIDevice *d, int pos, uint8_t *buf, int len); + +uint32_t host_pci_find_ext_cap_offset(HostPCIDevice *s, uint32_t cap); + +#endif /* !HW_HOST_PCI_DEVICE */ -- Anthony PERARD
From: Yuji Shimada <shimada-yxb@necst.nec.co.jp> This function help Xen PCI Passthrough device to check for overlap. Signed-off-by: Yuji Shimada <shimada-yxb@necst.nec.co.jp> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> --- hw/pci.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ hw/pci.h | 3 +++ 2 files changed, 50 insertions(+), 0 deletions(-) diff --git a/hw/pci.c b/hw/pci.c index 399227f..563bb37 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -2038,3 +2038,50 @@ MemoryRegion *pci_address_space_io(PCIDevice *dev) { return dev->bus->address_space_io; } + +int pci_check_bar_overlap(PCIDevice *dev, + pcibus_t addr, pcibus_t size, uint8_t type) +{ + PCIBus *bus = dev->bus; + PCIDevice *devices = NULL; + PCIIORegion *r; + int i, j; + int rc = 0; + + /* check Overlapped to Base Address */ + for (i = 0; i < ARRAY_SIZE(bus->devices); i++) { + devices = bus->devices[i]; + if (!devices) { + continue; + } + + /* skip itself */ + if (devices->devfn == dev->devfn) { + continue; + } + + for (j = 0; j < PCI_NUM_REGIONS; j++) { + r = &devices->io_regions[j]; + + /* skip different resource type, but don''t skip when + * prefetch and non-prefetch memory are compared. + */ + if (type != r->type) { + if (type == PCI_BASE_ADDRESS_SPACE_IO || + r->type == PCI_BASE_ADDRESS_SPACE_IO) { + continue; + } + } + + if ((addr < (r->addr + r->size)) && ((addr + size) > r->addr)) { + printf("Overlapped to device[%02x:%02x.%x][Region:%d]" + "[Address:%"PRIx64"h][Size:%"PRIx64"h]\n", + pci_bus_num(bus), PCI_SLOT(devices->devfn), + PCI_FUNC(devices->devfn), j, r->addr, r->size); + rc = 1; + } + } + } + + return rc; +} diff --git a/hw/pci.h b/hw/pci.h index 625e717..9a723d2 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -552,4 +552,7 @@ static inline void pci_dma_sglist_init(QEMUSGList *qsg, PCIDevice *dev, qemu_sglist_init(qsg, alloc_hint); } +int pci_check_bar_overlap(PCIDevice *dev, + pcibus_t addr, pcibus_t size, uint8_t type); + #endif -- Anthony PERARD
Anthony PERARD
2011-Nov-24 17:44 UTC
[PATCH V5 07/10] Introduce Xen PCI Passthrough, qdevice (1/3)
From: Allen Kay <allen.m.kay@intel.com> A more complete history can be found here: git://xenbits.xensource.com/qemu-xen-unstable.git Signed-off-by: Allen Kay <allen.m.kay@intel.com> Signed-off-by: Guy Zana <guy@neocleus.com> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> --- Makefile.target | 2 + hw/xen_common.h | 3 + hw/xen_pci_passthrough.c | 831 ++++++++++++++++++++++++++++++++++ hw/xen_pci_passthrough.h | 282 ++++++++++++ hw/xen_pci_passthrough_config_init.c | 11 + xen-all.c | 12 + 6 files changed, 1141 insertions(+), 0 deletions(-) create mode 100644 hw/xen_pci_passthrough.c create mode 100644 hw/xen_pci_passthrough.h create mode 100644 hw/xen_pci_passthrough_config_init.c diff --git a/Makefile.target b/Makefile.target index e527c1b..33435a3 100644 --- a/Makefile.target +++ b/Makefile.target @@ -221,6 +221,8 @@ obj-i386-$(CONFIG_XEN) += xen_platform.o # Xen PCI Passthrough obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += host-pci-device.o +obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough.o +obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough_config_init.o # Inter-VM PCI shared memory CONFIG_IVSHMEM diff --git a/hw/xen_common.h b/hw/xen_common.h index 0409ac7..48916fd 100644 --- a/hw/xen_common.h +++ b/hw/xen_common.h @@ -135,4 +135,7 @@ static inline int xc_fd(xc_interface *xen_xc) void destroy_hvm_domain(void); +/* shutdown/destroy current domain because of an error */ +void xen_shutdown_fatal_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); + #endif /* QEMU_HW_XEN_COMMON_H */ diff --git a/hw/xen_pci_passthrough.c b/hw/xen_pci_passthrough.c new file mode 100644 index 0000000..998470b --- /dev/null +++ b/hw/xen_pci_passthrough.c @@ -0,0 +1,831 @@ +/* + * Copyright (c) 2007, Neocleus Corporation. + * Copyright (c) 2007, Intel Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * 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 + */ + +/* + * Interrupt Disable policy: + * + * INTx interrupt: + * Initialize(register_real_device) + * Map INTx(xc_physdev_map_pirq): + * <fail> + * - Set real Interrupt Disable bit to ''1''. + * - Set machine_irq and assigned_device->machine_irq to ''0''. + * * Don''t bind INTx. + * + * Bind INTx(xc_domain_bind_pt_pci_irq): + * <fail> + * - Set real Interrupt Disable bit to ''1''. + * - Unmap INTx. + * - Decrement mapped_machine_irq[machine_irq] + * - Set assigned_device->machine_irq to ''0''. + * + * Write to Interrupt Disable bit by guest software(pt_cmd_reg_write) + * Write ''0'' + * <ptdev->msi_trans_en is false> + * - Set real bit to ''0'' if assigned_device->machine_irq isn''t ''0''. + * + * Write ''1'' + * <ptdev->msi_trans_en is false> + * - Set real bit to ''1''. + */ + +#include <sys/ioctl.h> + +#include "pci.h" +#include "xen.h" +#include "xen_backend.h" +#include "xen_pci_passthrough.h" + +#define PCI_BAR_ENTRIES (6) + +#define PT_NR_IRQS (256) +char mapped_machine_irq[PT_NR_IRQS] = {0}; + +void pt_log(const PCIDevice *d, const char *f, ...) +{ + va_list ap; + + va_start(ap, f); + if (d) { + fprintf(stderr, "[%02x:%02x.%x] ", pci_bus_num(d->bus), + PCI_SLOT(d->devfn), PCI_FUNC(d->devfn)); + } + vfprintf(stderr, f, ap); + va_end(ap); +} + + +/* Config Space */ +static int pt_pci_config_access_check(PCIDevice *d, uint32_t address, int len) +{ + /* check offset range */ + if (address >= 0xFF) { + PT_ERR(d, "Failed to access register with offset exceeding 0xFF. " + "(addr: 0x%02x, len: %d)\n", address, len); + return -1; + } + + /* check read size */ + if ((len != 1) && (len != 2) && (len != 4)) { + PT_ERR(d, "Failed to access register with invalid access length. " + "(addr: 0x%02x, len: %d)\n", address, len); + return -1; + } + + /* check offset alignment */ + if (address & (len - 1)) { + PT_ERR(d, "Failed to access register with invalid access size " + "alignment. (addr: 0x%02x, len: %d)\n", address, len); + return -1; + } + + return 0; +} + +int pt_bar_offset_to_index(uint32_t offset) +{ + int index = 0; + + /* check Exp ROM BAR */ + if (offset == PCI_ROM_ADDRESS) { + return PCI_ROM_SLOT; + } + + /* calculate BAR index */ + index = (offset - PCI_BASE_ADDRESS_0) >> 2; + if (index >= PCI_NUM_REGIONS) { + return -1; + } + + return index; +} + +static uint32_t pt_pci_read_config(PCIDevice *d, uint32_t address, int len) +{ + XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); + uint32_t val = 0; + XenPTRegGroup *reg_grp_entry = NULL; + XenPTReg *reg_entry = NULL; + int rc = 0; + int emul_len = 0; + uint32_t find_addr = address; + + if (pt_pci_config_access_check(d, address, len)) { + goto exit; + } + + /* check power state transition flags */ + if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) { + /* can''t accept until previous power state transition is completed. + * so finish previous request here. + */ + PT_WARN(d, "Guest want to write during power state transition\n"); + goto exit; + } + + /* find register group entry */ + reg_grp_entry = pt_find_reg_grp(s, address); + if (reg_grp_entry) { + /* check 0 Hardwired register group */ + if (reg_grp_entry->reg_grp->grp_type == GRP_TYPE_HARDWIRED) { + /* no need to emulate, just return 0 */ + val = 0; + goto exit; + } + } + + /* read I/O device register value */ + rc = host_pci_get_block(s->real_device, address, (uint8_t *)&val, len); + if (rc < 0) { + PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc); + memset(&val, 0xff, len); + } + + /* just return the I/O device register value for + * passthrough type register group */ + if (reg_grp_entry == NULL) { + goto exit; + } + + /* adjust the read value to appropriate CFC-CFF window */ + val <<= (address & 3) << 3; + emul_len = len; + + /* loop around the guest requested size */ + while (emul_len > 0) { + /* find register entry to be emulated */ + reg_entry = pt_find_reg(reg_grp_entry, find_addr); + if (reg_entry) { + XenPTRegInfo *reg = reg_entry->reg; + uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; + uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); + uint8_t *ptr_val = NULL; + + valid_mask <<= (find_addr - real_offset) << 3; + ptr_val = (uint8_t *)&val + (real_offset & 3); + + /* do emulation based on register size */ + switch (reg->size) { + case 1: + if (reg->u.b.read) { + rc = reg->u.b.read(s, reg_entry, ptr_val, valid_mask); + } + break; + case 2: + if (reg->u.w.read) { + rc = reg->u.w.read(s, reg_entry, + (uint16_t *)ptr_val, valid_mask); + } + break; + case 4: + if (reg->u.dw.read) { + rc = reg->u.dw.read(s, reg_entry, + (uint32_t *)ptr_val, valid_mask); + } + break; + } + + if (rc < 0) { + xen_shutdown_fatal_error("Internal error: Invalid read " + "emulation. (%s, rc: %d)\n", + __func__, rc); + return 0; + } + + /* calculate next address to find */ + emul_len -= reg->size; + if (emul_len > 0) { + find_addr = real_offset + reg->size; + } + } else { + /* nothing to do with passthrough type register, + * continue to find next byte */ + emul_len--; + find_addr++; + } + } + + /* need to shift back before returning them to pci bus emulator */ + val >>= ((address & 3) << 3); + +exit: + PT_LOG_CONFIG(d, address, val, len); + return val; +} + +static void pt_pci_write_config(PCIDevice *d, uint32_t address, + uint32_t val, int len) +{ + XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); + int index = 0; + XenPTRegGroup *reg_grp_entry = NULL; + int rc = 0; + uint32_t read_val = 0; + int emul_len = 0; + XenPTReg *reg_entry = NULL; + uint32_t find_addr = address; + XenPTRegInfo *reg = NULL; + + if (pt_pci_config_access_check(d, address, len)) { + return; + } + + PT_LOG_CONFIG(d, address, val, len); + + /* check unused BAR register */ + index = pt_bar_offset_to_index(address); + if ((index >= 0) && (val > 0 && val < PT_BAR_ALLF) && + (s->bases[index].bar_flag == PT_BAR_FLAG_UNUSED)) { + PT_WARN(d, "Guest attempt to set address to unused Base Address " + "Register. (addr: 0x%02x, len: %d)\n", address, len); + } + + /* check power state transition flags */ + if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) { + /* can''t accept until previous power state transition is completed. + * so finish previous request here. + */ + PT_WARN(d, "Guest want to write during power state transition\n"); + return; + } + + /* find register group entry */ + reg_grp_entry = pt_find_reg_grp(s, address); + if (reg_grp_entry) { + /* check 0 Hardwired register group */ + if (reg_grp_entry->reg_grp->grp_type == GRP_TYPE_HARDWIRED) { + /* ignore silently */ + PT_WARN(d, "Access to 0 Hardwired register. " + "(addr: 0x%02x, len: %d)\n", address, len); + return; + } + } + + /* read I/O device register value */ + rc = host_pci_get_block(s->real_device, address, + (uint8_t *)&read_val, len); + if (rc < 0) { + PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc); + memset(&read_val, 0xff, len); + } + + /* pass directly to the real device for passthrough type register group */ + if (reg_grp_entry == NULL) { + goto out; + } + + /* adjust the read and write value to appropriate CFC-CFF window */ + read_val <<= (address & 3) << 3; + val <<= (address & 3) << 3; + emul_len = len; + + /* loop around the guest requested size */ + while (emul_len > 0) { + /* find register entry to be emulated */ + reg_entry = pt_find_reg(reg_grp_entry, find_addr); + if (reg_entry) { + reg = reg_entry->reg; + uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; + uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); + uint8_t *ptr_val = NULL; + + valid_mask <<= (find_addr - real_offset) << 3; + ptr_val = (uint8_t *)&val + (real_offset & 3); + + /* do emulation based on register size */ + switch (reg->size) { + case 1: + if (reg->u.b.write) { + rc = reg->u.b.write(s, reg_entry, ptr_val, + read_val >> ((real_offset & 3) << 3), + valid_mask); + } + break; + case 2: + if (reg->u.w.write) { + rc = reg->u.w.write(s, reg_entry, (uint16_t *)ptr_val, + (read_val >> ((real_offset & 3) << 3)), + valid_mask); + } + break; + case 4: + if (reg->u.dw.write) { + rc = reg->u.dw.write(s, reg_entry, (uint32_t *)ptr_val, + (read_val >> ((real_offset & 3) << 3)), + valid_mask); + } + break; + } + + if (rc < 0) { + xen_shutdown_fatal_error("Internal error: Invalid write" + " emulation. (%s, rc: %d)\n", + __func__, rc); + return; + } + + /* calculate next address to find */ + emul_len -= reg->size; + if (emul_len > 0) { + find_addr = real_offset + reg->size; + } + } else { + /* nothing to do with passthrough type register, + * continue to find next byte */ + emul_len--; + find_addr++; + } + } + + /* need to shift back before passing them to host_pci_device */ + val >>= (address & 3) << 3; + +out: + if (!(reg && reg->no_wb)) { + /* unknown regs are passed through */ + rc = host_pci_set_block(s->real_device, address, (uint8_t *)&val, len); + + if (rc < 0) { + PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc); + } + } +} + +/* ioport/iomem space*/ +static void pt_iomem_map(XenPCIPassthroughState *s, int i, + pcibus_t e_phys, pcibus_t e_size, int type) +{ + PCIIORegion *r = &s->dev.io_regions[i]; + uint32_t old_ebase = s->bases[i].e_physbase; + bool first_map = s->bases[i].e_size == 0; + int ret = 0; + + s->bases[i].e_physbase = e_phys; + s->bases[i].e_size = e_size; + + PT_LOG(&s->dev, "e_phys=%#"PRIx64" maddr=%#"PRIx64" type=%d" + " len=%#"PRIx64" index=%d first_map=%d\n", + e_phys, s->bases[i].access.maddr, type, + e_size, i, first_map); + + if (e_size == 0) { + return; + } + + if (!first_map && old_ebase != PT_PCI_BAR_UNMAPPED) { + /* Remove old mapping */ + memory_region_del_subregion(r->address_space, + r->memory); + ret = xc_domain_memory_mapping(xen_xc, xen_domid, + old_ebase >> XC_PAGE_SHIFT, + s->bases[i].access.maddr >> XC_PAGE_SHIFT, + (e_size + XC_PAGE_SIZE - 1) >> XC_PAGE_SHIFT, + DPCI_REMOVE_MAPPING); + if (ret != 0) { + PT_ERR(&s->dev, "remove old mapping failed!\n"); + return; + } + } + + /* map only valid guest address */ + if (e_phys != PCI_BAR_UNMAPPED) { + /* Create new mapping */ + memory_region_add_subregion_overlap(r->address_space, + e_phys, r->memory, 1); + ret = xc_domain_memory_mapping(xen_xc, xen_domid, + s->bases[i].e_physbase >> XC_PAGE_SHIFT, + s->bases[i].access.maddr >> XC_PAGE_SHIFT, + (e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT, + DPCI_ADD_MAPPING); + + if (ret != 0) { + PT_ERR(&s->dev, "create new mapping failed!\n"); + } + } +} + +static void pt_ioport_map(XenPCIPassthroughState *s, int i, + pcibus_t e_phys, pcibus_t e_size, int type) +{ + PCIIORegion *r = &s->dev.io_regions[i]; + uint32_t old_ebase = s->bases[i].e_physbase; + bool first_map = s->bases[i].e_size == 0; + int ret = 0; + + s->bases[i].e_physbase = e_phys; + s->bases[i].e_size = e_size; + + PT_LOG(&s->dev, "e_phys=%#04"PRIx64" pio_base=%#04"PRIx64" len=%"PRId64 + " index=%d first_map=%d\n", + e_phys, s->bases[i].access.pio_base, e_size, i, first_map); + + if (e_size == 0) { + return; + } + + if (!first_map && old_ebase != PT_PCI_BAR_UNMAPPED) { + /* Remove old mapping */ + memory_region_del_subregion(r->address_space, + r->memory); + ret = xc_domain_ioport_mapping(xen_xc, xen_domid, old_ebase, + s->bases[i].access.pio_base, e_size, + DPCI_REMOVE_MAPPING); + if (ret != 0) { + PT_ERR(&s->dev, "remove old mapping failed!\n"); + return; + } + } + + /* map only valid guest address (include 0) */ + if (e_phys != PCI_BAR_UNMAPPED) { + /* Create new mapping */ + memory_region_add_subregion_overlap(r->address_space, + e_phys, r->memory, 1); + ret = xc_domain_ioport_mapping(xen_xc, xen_domid, e_phys, + s->bases[i].access.pio_base, e_size, + DPCI_ADD_MAPPING); + if (ret != 0) { + PT_ERR(&s->dev, "create new mapping failed!\n"); + } + } + +} + + +/* mapping BAR */ + +void pt_bar_mapping_one(XenPCIPassthroughState *s, int bar, + int io_enable, int mem_enable) +{ + PCIDevice *dev = &s->dev; + PCIIORegion *r; + XenPTRegGroup *reg_grp_entry = NULL; + XenPTReg *reg_entry = NULL; + XenPTRegion *base = NULL; + pcibus_t r_size = 0, r_addr = PCI_BAR_UNMAPPED; + int rc = 0; + + r = &dev->io_regions[bar]; + + /* check valid region */ + if (!r->size) { + return; + } + + base = &s->bases[bar]; + /* skip unused BAR or upper 64bit BAR */ + if ((base->bar_flag == PT_BAR_FLAG_UNUSED) + || (base->bar_flag == PT_BAR_FLAG_UPPER)) { + return; + } + + /* copy region address to temporary */ + r_addr = r->addr; + + /* need unmapping in case I/O Space or Memory Space disable */ + if (((base->bar_flag == PT_BAR_FLAG_IO) && !io_enable) || + ((base->bar_flag == PT_BAR_FLAG_MEM) && !mem_enable)) { + r_addr = PCI_BAR_UNMAPPED; + } + if ((bar == PCI_ROM_SLOT) && (r_addr != PCI_BAR_UNMAPPED)) { + reg_grp_entry = pt_find_reg_grp(s, PCI_ROM_ADDRESS); + if (reg_grp_entry) { + reg_entry = pt_find_reg(reg_grp_entry, PCI_ROM_ADDRESS); + if (reg_entry && !(reg_entry->data & PCI_ROM_ADDRESS_ENABLE)) { + r_addr = PCI_BAR_UNMAPPED; + } + } + } + + /* prevent guest software mapping memory resource to 00000000h */ + if ((base->bar_flag == PT_BAR_FLAG_MEM) && (r_addr == 0)) { + r_addr = PCI_BAR_UNMAPPED; + } + + r_size = pt_get_emul_size(base->bar_flag, r->size); + + rc = pci_check_bar_overlap(dev, r_addr, r_size, r->type); + if (rc) { + PT_WARN(dev, "Region: %d (addr: %#"FMT_PCIBUS + ", len: %#"FMT_PCIBUS") is overlapped.\n", + bar, r_addr, r_size); + } + + /* check whether we need to update the mapping or not */ + if (r_addr != s->bases[bar].e_physbase) { + /* mapping BAR */ + if (base->bar_flag == PT_BAR_FLAG_IO) { + pt_ioport_map(s, bar, r_addr, r_size, r->type); + } else { + pt_iomem_map(s, bar, r_addr, r_size, r->type); + } + } +} + +void pt_bar_mapping(XenPCIPassthroughState *s, int io_enable, int mem_enable) +{ + int i; + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + pt_bar_mapping_one(s, i, io_enable, mem_enable); + } +} + +static uint64_t bar_read(void *o, target_phys_addr_t addr, unsigned size) +{ + PCIDevice *d = o; + PT_ERR(d, "Should not read BAR through QEMU. @0x"TARGET_FMT_plx"\n", addr); + return 0; +} +static void bar_write(void *o, target_phys_addr_t addr, + uint64_t data, unsigned size) +{ + PCIDevice* d = o; + PT_ERR(d, "Should not write BAR through QEMU. @0x"TARGET_FMT_plx"\n", addr); +} + +static const MemoryRegionOps ops = { + .endianness = DEVICE_NATIVE_ENDIAN, + .read = bar_read, + .write = bar_write, +}; + +/* register regions */ +static int pt_register_regions(XenPCIPassthroughState *s) +{ + int i = 0; + uint32_t bar_data = 0; + HostPCIDevice *d = s->real_device; + + /* Register PIO/MMIO BARs */ + for (i = 0; i < PCI_BAR_ENTRIES; i++) { + HostPCIIORegion *r = &d->io_regions[i]; + + if (r->base_addr && r->size) { + s->bases[i].e_physbase = r->base_addr; + s->bases[i].access.u = r->base_addr; + + /* Register current region */ + if (r->flags & IORESOURCE_IO) { + memory_region_init_io(&s->bar[i], &ops, &s->dev, + "xen-pci-pt-bar-io", r->size); + pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_SPACE_IO, + &s->bar[i]); + } else if (r->flags & IORESOURCE_PREFETCH) { + memory_region_init_io(&s->bar[i], &ops, &s->dev, + "xen-pci-pt-bar-mem", r->size); + pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_MEM_PREFETCH, + &s->bar[i]); + } else { + memory_region_init_io(&s->bar[i], &ops, &s->dev, + "xen-pci-pt-bar-mem", r->size); + pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_SPACE_MEMORY, + &s->bar[i]); + } + + PT_LOG(&s->dev, "IO region registered (size=0x%08"PRIx64 + " base_addr=0x%08"PRIx64")\n", + r->size, r->base_addr); + } + } + + /* Register expansion ROM address */ + if (d->rom.base_addr && d->rom.size) { + /* Re-set BAR reported by OS, otherwise ROM can''t be read. */ + if (host_pci_get_long(d, PCI_ROM_ADDRESS, &bar_data)) { + return 0; + } + if ((bar_data & PCI_ROM_ADDRESS_MASK) == 0) { + bar_data |= d->rom.base_addr & PCI_ROM_ADDRESS_MASK; + host_pci_set_long(d, PCI_ROM_ADDRESS, bar_data); + } + + s->bases[PCI_ROM_SLOT].e_physbase = d->rom.base_addr; + s->bases[PCI_ROM_SLOT].access.maddr = d->rom.base_addr; + + memory_region_init_rom_device(&s->rom, NULL, NULL, &s->dev.qdev, + "xen-pci-pt-rom", d->rom.size); + pci_register_bar(&s->dev, PCI_ROM_SLOT, PCI_BASE_ADDRESS_MEM_PREFETCH, + &s->rom); + + PT_LOG(&s->dev, "Expansion ROM registered (size=0x%08"PRIx64 + " base_addr=0x%08"PRIx64")\n", + d->rom.size, d->rom.base_addr); + } + + return 0; +} + +static void pt_unregister_regions(XenPCIPassthroughState *s) +{ + int i, type, rc; + uint32_t e_size; + PCIDevice *d = &s->dev; + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + e_size = s->bases[i].e_size; + if ((e_size == 0) || (s->bases[i].e_physbase == PT_PCI_BAR_UNMAPPED)) { + continue; + } + + type = d->io_regions[i].type; + + if (type == PCI_BASE_ADDRESS_SPACE_MEMORY + || type == PCI_BASE_ADDRESS_MEM_PREFETCH) { + rc = xc_domain_memory_mapping(xen_xc, xen_domid, + s->bases[i].e_physbase >> XC_PAGE_SHIFT, + s->bases[i].access.maddr >> XC_PAGE_SHIFT, + (e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT, + DPCI_REMOVE_MAPPING); + if (rc != 0) { + PT_ERR(d, "remove old mem mapping failed!\n"); + continue; + } + + } else if (type == PCI_BASE_ADDRESS_SPACE_IO) { + rc = xc_domain_ioport_mapping(xen_xc, xen_domid, + s->bases[i].e_physbase, + s->bases[i].access.pio_base, + e_size, + DPCI_REMOVE_MAPPING); + if (rc != 0) { + PT_ERR(d, "remove old io mapping failed!\n"); + continue; + } + } + } +} + +static int pt_initfn(PCIDevice *pcidev) +{ + XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, pcidev); + int dom, bus; + unsigned slot, func; + int rc = 0; + uint8_t machine_irq = 0; + int pirq = PT_UNASSIGNED_PIRQ; + + if (pci_parse_devaddr(s->hostaddr, &dom, &bus, &slot, &func) < 0) { + PT_ERR(pcidev, "Failed to parse BDF: %s\n", s->hostaddr); + return -1; + } + + /* register real device */ + PT_LOG(pcidev, "Assigning real physical device %02x:%02x.%x" + " to devfn %#x\n", bus, slot, func, s->dev.devfn); + + s->real_device = host_pci_device_get(bus, slot, func); + if (!s->real_device) { + return -1; + } + + s->is_virtfn = s->real_device->is_virtfn; + if (s->is_virtfn) { + PT_LOG(pcidev, "%04x:%02x:%02x.%x is a SR-IOV Virtual Function\n", + s->real_device->domain, bus, slot, func); + } + + /* Initialize virtualized PCI configuration (Extended 256 Bytes) */ + if (host_pci_get_block(s->real_device, 0, pcidev->config, + PCI_CONFIG_SPACE_SIZE) == -1) { + host_pci_device_put(s->real_device); + return -1; + } + + /* Handle real device''s MMIO/PIO BARs */ + pt_register_regions(s); + + /* Bind interrupt */ + if (!s->dev.config[PCI_INTERRUPT_PIN]) { + PT_LOG(pcidev, "no pin interrupt\n"); + goto out; + } + + host_pci_get_byte(s->real_device, PCI_INTERRUPT_LINE, &machine_irq); + rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq); + + if (rc < 0) { + PT_ERR(pcidev, "Mapping machine irq %u to pirq %i failed, (rc: %d)\n", + machine_irq, pirq, rc); + + /* Disable PCI intx assertion (turn on bit10 of devctl) */ + host_pci_set_word(s->real_device, + PCI_COMMAND, + pci_get_word(s->dev.config + PCI_COMMAND) + | PCI_COMMAND_INTX_DISABLE); + machine_irq = 0; + s->machine_irq = 0; + } else { + machine_irq = pirq; + s->machine_irq = pirq; + mapped_machine_irq[machine_irq]++; + } + + /* bind machine_irq to device */ + if (rc < 0 && machine_irq != 0) { + uint8_t e_device = PCI_SLOT(s->dev.devfn); + uint8_t e_intx = pci_intx(s); + + rc = xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, machine_irq, 0, + e_device, e_intx); + if (rc < 0) { + PT_ERR(pcidev, "Binding of interrupt %i failed! (rc: %d)\n", + e_intx, rc); + + /* Disable PCI intx assertion (turn on bit10 of devctl) */ + host_pci_set_word(s->real_device, PCI_COMMAND, + *(uint16_t *)(&s->dev.config[PCI_COMMAND]) + | PCI_COMMAND_INTX_DISABLE); + mapped_machine_irq[machine_irq]--; + + if (mapped_machine_irq[machine_irq] == 0) { + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq)) { + PT_ERR(pcidev, "Unmapping of machine interrupt %i failed!" + " (rc: %d)\n", machine_irq, rc); + } + } + s->machine_irq = 0; + } + } + +out: + PT_LOG(pcidev, "Real physical device %02x:%02x.%x registered successfuly!" + "\nIRQ type = %s\n", bus, slot, func, "INTx"); + + return 0; +} + +static int pt_unregister_device(PCIDevice *pcidev) +{ + XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, pcidev); + uint8_t e_device, e_intx; + uint8_t machine_irq; + int rc; + + /* Unbind interrupt */ + e_device = PCI_SLOT(s->dev.devfn); + e_intx = pci_intx(s); + machine_irq = s->machine_irq; + + if (machine_irq) { + rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq, + PT_IRQ_TYPE_PCI, 0, e_device, e_intx, 0); + if (rc < 0) { + PT_ERR(pcidev, "Unbinding of interrupt failed! rc=%d\n", rc); + } + } + + if (machine_irq) { + mapped_machine_irq[machine_irq]--; + + if (mapped_machine_irq[machine_irq] == 0) { + rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq); + + if (rc < 0) { + PT_ERR(pcidev, "Unmaping of interrupt failed! rc=%d\n", rc); + } + } + } + + /* unregister real device''s MMIO/PIO BARs */ + pt_unregister_regions(s); + + host_pci_device_put(s->real_device); + + return 0; +} + +static PCIDeviceInfo xen_pci_passthrough = { + .init = pt_initfn, + .exit = pt_unregister_device, + .qdev.name = "xen-pci-passthrough", + .qdev.desc = "Assign an host pci device with Xen", + .qdev.size = sizeof(XenPCIPassthroughState), + .config_read = pt_pci_read_config, + .config_write = pt_pci_write_config, + .is_express = 0, + .qdev.props = (Property[]) { + DEFINE_PROP_STRING("hostaddr", XenPCIPassthroughState, hostaddr), + DEFINE_PROP_BIT("power-mgmt", XenPCIPassthroughState, power_mgmt, + 0, false), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void xen_passthrough_register(void) +{ + pci_qdev_register(&xen_pci_passthrough); +} + +device_init(xen_passthrough_register); diff --git a/hw/xen_pci_passthrough.h b/hw/xen_pci_passthrough.h new file mode 100644 index 0000000..110325c --- /dev/null +++ b/hw/xen_pci_passthrough.h @@ -0,0 +1,282 @@ +#ifndef QEMU_HW_XEN_PCI_PASSTHROUGH_H +# define QEMU_HW_XEN_PCI_PASSTHROUGH_H + +#include "qemu-common.h" +#include "xen_common.h" +#include "pci.h" +#include "host-pci-device.h" + +/* #define PT_LOGGING_ENABLED */ +/* #define PT_DEBUG_PCI_CONFIG_ACCESS */ + +void pt_log(const PCIDevice *d, const char *f, ...) GCC_FMT_ATTR(2, 3); + +#define PT_ERR(d, _f, _a...) pt_log(d, "%s: Error: " _f, __func__, ##_a) + +#ifdef PT_LOGGING_ENABLED +# define PT_LOG(d, _f, _a...) pt_log(d, "%s: " _f, __func__, ##_a) +# define PT_WARN(d, _f, _a...) pt_log(d, "%s: Warning: " _f, __func__, ##_a) +#else +# define PT_LOG(d, _f, _a...) +# define PT_WARN(d, _f, _a...) +#endif + +#ifdef PT_DEBUG_PCI_CONFIG_ACCESS +# define PT_LOG_CONFIG(d, addr, val, len) \ + pt_log(d, "%s: address=0x%04x val=0x%08x len=%d\n", \ + __func__, addr, val, len) +#else +# define PT_LOG_CONFIG(d, addr, val, len) +#endif + + +typedef struct XenPTRegInfo XenPTRegInfo; +typedef struct XenPTReg XenPTReg; + +typedef struct XenPCIPassthroughState XenPCIPassthroughState; + +/* function type for config reg */ +typedef int (*conf_reg_init) + (XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset, + uint32_t *data); +typedef int (*conf_dword_write) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, + uint32_t *val, uint32_t dev_value, uint32_t valid_mask); +typedef int (*conf_word_write) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, + uint16_t *val, uint16_t dev_value, uint16_t valid_mask); +typedef int (*conf_byte_write) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, + uint8_t *val, uint8_t dev_value, uint8_t valid_mask); +typedef int (*conf_dword_read) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, + uint32_t *val, uint32_t valid_mask); +typedef int (*conf_word_read) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, + uint16_t *val, uint16_t valid_mask); +typedef int (*conf_byte_read) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, + uint8_t *val, uint8_t valid_mask); +typedef int (*conf_dword_restore) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, uint32_t real_offset, + uint32_t dev_value, uint32_t *val); +typedef int (*conf_word_restore) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, uint32_t real_offset, + uint16_t dev_value, uint16_t *val); +typedef int (*conf_byte_restore) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, uint32_t real_offset, + uint8_t dev_value, uint8_t *val); + +/* power state transition */ +#define PT_FLAG_TRANSITING 0x0001 + +#define PT_BAR_ALLF 0xFFFFFFFF /* BAR ALLF value */ +#define PT_PCI_BAR_UNMAPPED (-1) +#define PT_UNASSIGNED_PIRQ (-1) + + +typedef enum { + GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */ + GRP_TYPE_EMU, /* emul reg group */ +} RegisterGroupType; + +typedef enum { + PT_BAR_FLAG_MEM = 0, /* Memory type BAR */ + PT_BAR_FLAG_IO, /* I/O type BAR */ + PT_BAR_FLAG_UPPER, /* upper 64bit BAR */ + PT_BAR_FLAG_UNUSED, /* unused BAR */ +} PTBarFlag; + + +typedef struct XenPTRegion { + /* Virtual phys base & size */ + uint32_t e_physbase; + uint32_t e_size; + /* Index of region in qemu */ + uint32_t memory_index; + /* BAR flag */ + PTBarFlag bar_flag; + /* Translation of the emulated address */ + union { + uint64_t maddr; + uint64_t pio_base; + uint64_t u; + } access; +} XenPTRegion; + +/* XenPTRegInfo declaration + * - only for emulated register (either a part or whole bit). + * - for passthrough register that need special behavior (like interacting with + * other component), set emu_mask to all 0 and specify r/w func properly. + * - do NOT use ALL F for init_val, otherwise the tbl will not be registered. + */ + +/* emulated register infomation */ +struct XenPTRegInfo { + uint32_t offset; + uint32_t size; + uint32_t init_val; + /* reg read only field mask (ON:RO/ROS, OFF:other) */ + uint32_t ro_mask; + /* reg emulate field mask (ON:emu, OFF:passthrough) */ + uint32_t emu_mask; + /* no write back allowed */ + uint32_t no_wb; + conf_reg_init init; + /* read/write/restore function pointer + * for double_word/word/byte size */ + union { + struct { + conf_dword_write write; + conf_dword_read read; + conf_dword_restore restore; + } dw; + struct { + conf_word_write write; + conf_word_read read; + conf_word_restore restore; + } w; + struct { + conf_byte_write write; + conf_byte_read read; + conf_byte_restore restore; + } b; + } u; +}; + +/* emulated register management */ +struct XenPTReg { + QLIST_ENTRY(XenPTReg) entries; + XenPTRegInfo *reg; + uint32_t data; +}; + +typedef struct XenPTRegGroupInfo XenPTRegGroupInfo; + +/* emul reg group size initialize method */ +typedef int (*pt_reg_size_init_fn) + (XenPCIPassthroughState *, const XenPTRegGroupInfo *, + uint32_t base_offset, uint8_t *size); + +/* emulated register group infomation */ +struct XenPTRegGroupInfo { + uint8_t grp_id; + RegisterGroupType grp_type; + uint8_t grp_size; + pt_reg_size_init_fn size_init; + XenPTRegInfo *emu_reg_tbl; +}; + +/* emul register group management table */ +typedef struct XenPTRegGroup { + QLIST_ENTRY(XenPTRegGroup) entries; + const XenPTRegGroupInfo *reg_grp; + uint32_t base_offset; + uint8_t size; + QLIST_HEAD(, XenPTReg) reg_tbl_list; +} XenPTRegGroup; + + +typedef struct XenPTPM { + QEMUTimer *pm_timer; /* QEMUTimer struct */ + int no_soft_reset; /* No Soft Reset flags */ + uint16_t flags; /* power state transition flags */ + uint16_t pmc_field; /* Power Management Capabilities field */ + int pm_delay; /* power state transition delay */ + uint16_t cur_state; /* current power state */ + uint16_t req_state; /* requested power state */ + uint32_t pm_base; /* Power Management Capability reg base offset */ + uint32_t aer_base; /* AER Capability reg base offset */ +} XenPTPM; + +struct XenPCIPassthroughState { + PCIDevice dev; + + char *hostaddr; + bool is_virtfn; + HostPCIDevice *real_device; + XenPTRegion bases[PCI_NUM_REGIONS]; /* Access regions */ + QLIST_HEAD(, XenPTRegGroup) reg_grp_tbl; + + uint32_t machine_irq; + + uint32_t power_mgmt; + XenPTPM *pm_state; + + MemoryRegion bar[PCI_NUM_REGIONS - 1]; + MemoryRegion rom; +}; + +int pt_config_init(XenPCIPassthroughState *s); +void pt_config_delete(XenPCIPassthroughState *s); +void pt_bar_mapping(XenPCIPassthroughState *s, int io_enable, int mem_enable); +void pt_bar_mapping_one(XenPCIPassthroughState *s, int bar, + int io_enable, int mem_enable); +XenPTRegGroup *pt_find_reg_grp(XenPCIPassthroughState *s, uint32_t address); +XenPTReg *pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address); +int pt_bar_offset_to_index(uint32_t offset); + +static inline pcibus_t pt_get_emul_size(PTBarFlag flag, pcibus_t r_size) +{ + /* align resource size (memory type only) */ + if (flag == PT_BAR_FLAG_MEM) { + return (r_size + XC_PAGE_SIZE - 1) & XC_PAGE_MASK; + } else { + return r_size; + } +} + +/* INTx */ +/* The PCI Local Bus Specification, Rev. 3.0, + * Section 6.2.4 Miscellaneous Registers, pp 223 + * outlines 5 valid values for the intertupt pin (intx). + * 0: For devices (or device functions) that don''t use an interrupt in + * 1: INTA# + * 2: INTB# + * 3: INTC# + * 4: INTD# + * + * Xen uses the following 4 values for intx + * 0: INTA# + * 1: INTB# + * 2: INTC# + * 3: INTD# + * + * Observing that these list of values are not the same, pci_read_intx() + * uses the following mapping from hw to xen values. + * This seems to reflect the current usage within Xen. + * + * PCI hardware | Xen | Notes + * ----------------+-----+---------------------------------------------------- + * 0 | 0 | No interrupt + * 1 | 0 | INTA# + * 2 | 1 | INTB# + * 3 | 2 | INTC# + * 4 | 3 | INTD# + * any other value | 0 | This should never happen, log error message + */ + +static inline uint8_t pci_read_intx(XenPCIPassthroughState *s) +{ + uint8_t v = 0; + host_pci_get_byte(s->real_device, PCI_INTERRUPT_PIN, &v); + return v; +} + +static inline uint8_t pci_intx(XenPCIPassthroughState *s) +{ + uint8_t r_val = pci_read_intx(s); + + PT_LOG(&s->dev, "intx=%i\n", r_val); + if (r_val < 1 || r_val > 4) { + PT_LOG(&s->dev, "Interrupt pin read from hardware is out of range:" + " value=%i, acceptable range is 1 - 4\n", r_val); + r_val = 0; + } else { + r_val -= 1; + } + + return r_val; +} + +#endif /* !QEMU_HW_XEN_PCI_PASSTHROUGH_H */ diff --git a/hw/xen_pci_passthrough_config_init.c b/hw/xen_pci_passthrough_config_init.c new file mode 100644 index 0000000..1e9de64 --- /dev/null +++ b/hw/xen_pci_passthrough_config_init.c @@ -0,0 +1,11 @@ +#include "xen_pci_passthrough.h" + +XenPTRegGroup *pt_find_reg_grp(XenPCIPassthroughState *s, uint32_t address) +{ + return NULL; +} + +XenPTReg *pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address) +{ + return NULL; +} diff --git a/xen-all.c b/xen-all.c index b5e28ab..0e3bbcf 100644 --- a/xen-all.c +++ b/xen-all.c @@ -979,3 +979,15 @@ void destroy_hvm_domain(void) xc_interface_close(xc_handle); } } + +void xen_shutdown_fatal_error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "Will destroy the domain.\n"); + /* destroy the domain */ + qemu_system_shutdown_request(); +} -- Anthony PERARD
Anthony PERARD
2011-Nov-24 17:44 UTC
[PATCH V5 08/10] Introduce Xen PCI Passthrough, PCI config space helpers (2/3)
From: Allen Kay <allen.m.kay@intel.com> A more complete history can be found here: git://xenbits.xensource.com/qemu-xen-unstable.git Signed-off-by: Allen Kay <allen.m.kay@intel.com> Signed-off-by: Guy Zana <guy@neocleus.com> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> --- hw/xen_pci_passthrough.c | 15 + hw/xen_pci_passthrough_config_init.c | 2131 ++++++++++++++++++++++++++++++++++ 2 files changed, 2146 insertions(+), 0 deletions(-) diff --git a/hw/xen_pci_passthrough.c b/hw/xen_pci_passthrough.c index 998470b..c816ed5 100644 --- a/hw/xen_pci_passthrough.c +++ b/hw/xen_pci_passthrough.c @@ -360,6 +360,11 @@ out: PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc); } } + + if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) { + qemu_mod_timer(s->pm_state->pm_timer, + qemu_get_clock_ms(rt_clock) + s->pm_state->pm_delay); + } } /* ioport/iomem space*/ @@ -706,6 +711,13 @@ static int pt_initfn(PCIDevice *pcidev) /* Handle real device''s MMIO/PIO BARs */ pt_register_regions(s); + /* reinitialize each config register to be emulated */ + if (pt_config_init(s)) { + PT_ERR(pcidev, "PCI Config space initialisation failed.\n"); + host_pci_device_put(s->real_device); + return -1; + } + /* Bind interrupt */ if (!s->dev.config[PCI_INTERRUPT_PIN]) { PT_LOG(pcidev, "no pin interrupt\n"); @@ -798,6 +810,9 @@ static int pt_unregister_device(PCIDevice *pcidev) } } + /* delete all emulated config registers */ + pt_config_delete(s); + /* unregister real device''s MMIO/PIO BARs */ pt_unregister_regions(s); diff --git a/hw/xen_pci_passthrough_config_init.c b/hw/xen_pci_passthrough_config_init.c index 1e9de64..ae64544 100644 --- a/hw/xen_pci_passthrough_config_init.c +++ b/hw/xen_pci_passthrough_config_init.c @@ -1,11 +1,2142 @@ +/* + * Copyright (c) 2007, Neocleus Corporation. + * Copyright (c) 2007, Intel Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * 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 "qemu-timer.h" +#include "xen_backend.h" #include "xen_pci_passthrough.h" +#define PT_MERGE_VALUE(value, data, val_mask) \ + (((value) & (val_mask)) | ((data) & ~(val_mask))) + +#define PT_INVALID_REG 0xFFFFFFFF /* invalid register value */ + +/* prototype */ + +static int pt_ptr_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, + uint32_t real_offset, uint32_t *data); +static int pt_init_pci_config(XenPCIPassthroughState *s); + + +/* helper */ + +/* A return value of 1 means the capability should NOT be exposed to guest. */ +static int pt_hide_dev_cap(const HostPCIDevice *d, uint8_t grp_id) +{ + switch (grp_id) { + case PCI_CAP_ID_EXP: + /* The PCI Express Capability Structure of the VF of Intel 82599 10GbE + * Controller looks trivial, e.g., the PCI Express Capabilities + * Register is 0. We should not try to expose it to guest. + * + * The datasheet is available at + * http://download.intel.com/design/network/datashts/82599_datasheet.pdf + * + * See ''Table 9.7. VF PCIe Configuration Space'' of the datasheet, the + * PCI Express Capability Structure of the VF of Intel 82599 10GbE + * Controller looks trivial, e.g., the PCI Express Capabilities + * Register is 0, so the Capability Version is 0 and + * pt_pcie_size_init() would fail. + */ + if (d->vendor_id == PCI_VENDOR_ID_INTEL && + d->device_id == PCI_DEVICE_ID_INTEL_82599_VF) { + return 1; + } + break; + } + return 0; +} + +/* find emulate register group entry */ XenPTRegGroup *pt_find_reg_grp(XenPCIPassthroughState *s, uint32_t address) { + XenPTRegGroup *entry = NULL; + + /* find register group entry */ + QLIST_FOREACH(entry, &s->reg_grp_tbl, entries) { + /* check address */ + if ((entry->base_offset <= address) + && ((entry->base_offset + entry->size) > address)) { + return entry; + } + } + + /* group entry not found */ return NULL; } +/* find emulate register entry */ XenPTReg *pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address) { + XenPTReg *reg_entry = NULL; + XenPTRegInfo *reg = NULL; + uint32_t real_offset = 0; + + /* find register entry */ + QLIST_FOREACH(reg_entry, ®_grp->reg_tbl_list, entries) { + reg = reg_entry->reg; + real_offset = reg_grp->base_offset + reg->offset; + /* check address */ + if ((real_offset <= address) + && ((real_offset + reg->size) > address)) { + return reg_entry; + } + } + return NULL; } + +/* parse BAR */ +static PTBarFlag pt_bar_reg_parse(XenPCIPassthroughState *s, XenPTRegInfo *reg) +{ + PCIDevice *d = &s->dev; + XenPTRegion *region = NULL; + PCIIORegion *r; + int index = 0; + + /* check 64bit BAR */ + index = pt_bar_offset_to_index(reg->offset); + if ((0 < index) && (index < PCI_ROM_SLOT)) { + int flags = s->real_device->io_regions[index - 1].flags; + + if ((flags & IORESOURCE_MEM) && (flags & IORESOURCE_MEM_64)) { + region = &s->bases[index - 1]; + if (region->bar_flag != PT_BAR_FLAG_UPPER) { + return PT_BAR_FLAG_UPPER; + } + } + } + + /* check unused BAR */ + r = &d->io_regions[index]; + if (r->size == 0) { + return PT_BAR_FLAG_UNUSED; + } + + /* for ExpROM BAR */ + if (index == PCI_ROM_SLOT) { + return PT_BAR_FLAG_MEM; + } + + /* check BAR I/O indicator */ + if (s->real_device->io_regions[index].flags & IORESOURCE_IO) { + return PT_BAR_FLAG_IO; + } else { + return PT_BAR_FLAG_MEM; + } +} + + +/**************** + * general register functions + */ + +/* register initialization function */ + +static int pt_common_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + *data = reg->init_val; + return 0; +} + +/* Read register functions */ + +static int pt_byte_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint8_t *value, uint8_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint8_t valid_emu_mask = 0; + + /* emulate byte register */ + valid_emu_mask = reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + + return 0; +} +static int pt_word_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *value, uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t valid_emu_mask = 0; + + /* emulate word register */ + valid_emu_mask = reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + + return 0; +} +static int pt_long_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint32_t *value, uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t valid_emu_mask = 0; + + /* emulate long register */ + valid_emu_mask = reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + + return 0; +} + +/* Write register functions */ + +static int pt_byte_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint8_t *value, uint8_t dev_value, + uint8_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint8_t writable_mask = 0; + uint8_t throughable_mask = 0; + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + return 0; +} +static int pt_word_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *value, uint16_t dev_value, + uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + return 0; +} +static int pt_long_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint32_t *value, uint32_t dev_value, + uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t writable_mask = 0; + uint32_t throughable_mask = 0; + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + return 0; +} + +/* common restore register fonctions */ +static int pt_byte_reg_restore(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint32_t real_offset, uint8_t dev_value, + uint8_t *value) +{ + XenPTRegInfo *reg = cfg_entry->reg; + PCIDevice *d = &s->dev; + + /* use I/O device register''s value as restore value */ + *value = pci_get_byte(d->config + real_offset); + + /* create value for restoring to I/O device register */ + *value = PT_MERGE_VALUE(*value, dev_value, reg->emu_mask); + + return 0; +} +static int pt_word_reg_restore(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint32_t real_offset, uint16_t dev_value, + uint16_t *value) +{ + XenPTRegInfo *reg = cfg_entry->reg; + PCIDevice *d = &s->dev; + + /* use I/O device register''s value as restore value */ + *value = pci_get_word(d->config + real_offset); + + /* create value for restoring to I/O device register */ + *value = PT_MERGE_VALUE(*value, dev_value, reg->emu_mask); + + return 0; +} + + +/* XenPTRegInfo declaration + * - only for emulated register (either a part or whole bit). + * - for passthrough register that need special behavior (like interacting with + * other component), set emu_mask to all 0 and specify r/w func properly. + * - do NOT use ALL F for init_val, otherwise the tbl will not be registered. + */ + +/******************** + * Header Type0 + */ + +static int pt_vendor_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + *data = s->real_device->vendor_id; + return 0; +} +static int pt_device_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + *data = s->real_device->device_id; + return 0; +} +static int pt_status_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + XenPTRegGroup *reg_grp_entry = NULL; + XenPTReg *reg_entry = NULL; + uint32_t reg_field = 0; + + /* find Header register group */ + reg_grp_entry = pt_find_reg_grp(s, PCI_CAPABILITY_LIST); + if (reg_grp_entry) { + /* find Capabilities Pointer register */ + reg_entry = pt_find_reg(reg_grp_entry, PCI_CAPABILITY_LIST); + if (reg_entry) { + /* check Capabilities Pointer register */ + if (reg_entry->data) { + reg_field |= PCI_STATUS_CAP_LIST; + } else { + reg_field &= ~PCI_STATUS_CAP_LIST; + } + } else { + xen_shutdown_fatal_error("Internal error: Couldn''t find XenPTReg*" + " for Capabilities Pointer register." + " (%s)\n", __func__); + return -1; + } + } else { + xen_shutdown_fatal_error("Internal error: Couldn''t find XenPTRegGroup" + " for Header. (%s)\n", __func__); + return -1; + } + + *data = reg_field; + return 0; +} +static int pt_header_type_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + /* read PCI_HEADER_TYPE */ + *data = reg->init_val | 0x80; + return 0; +} + +/* initialize Interrupt Pin register */ +static int pt_irqpin_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + *data = pci_read_intx(s); + return 0; +} + +/* Command register */ +static int pt_cmd_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *value, uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t valid_emu_mask = 0; + uint16_t emu_mask = reg->emu_mask; + + if (s->is_virtfn) { + emu_mask |= PCI_COMMAND_MEMORY; + } + + /* emulate word register */ + valid_emu_mask = emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + + return 0; +} +static int pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *value, uint16_t dev_value, + uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + uint16_t wr_value = *value; + uint16_t emu_mask = reg->emu_mask; + + if (s->is_virtfn) { + emu_mask |= PCI_COMMAND_MEMORY; + } + + /* modify emulate register */ + writable_mask = ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ + throughable_mask = ~emu_mask & valid_mask; + + if (*value & PCI_COMMAND_INTX_DISABLE) { + throughable_mask |= PCI_COMMAND_INTX_DISABLE; + } else { + if (s->machine_irq) { + throughable_mask |= PCI_COMMAND_INTX_DISABLE; + } + } + + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + /* mapping BAR */ + pt_bar_mapping(s, wr_value & PCI_COMMAND_IO, + wr_value & PCI_COMMAND_MEMORY); + + return 0; +} +static int pt_cmd_reg_restore(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint32_t real_offset, uint16_t dev_value, + uint16_t *value) +{ + XenPTRegInfo *reg = cfg_entry->reg; + PCIDevice *d = &s->dev; + uint16_t restorable_mask = 0; + + /* use I/O device register''s value as restore value */ + *value = pci_get_word(d->config + real_offset); + + /* create value for restoring to I/O device register + * but do not include Fast Back-to-Back Enable bit. + */ + restorable_mask = reg->emu_mask & ~PCI_COMMAND_FAST_BACK; + *value = PT_MERGE_VALUE(*value, dev_value, restorable_mask); + + if (!s->machine_irq) { + *value |= PCI_COMMAND_INTX_DISABLE; + } else { + *value &= ~PCI_COMMAND_INTX_DISABLE; + } + + return 0; +} + +/* BAR */ +#define PT_BAR_MEM_RO_MASK 0x0000000F /* BAR ReadOnly mask(Memory) */ +#define PT_BAR_MEM_EMU_MASK 0xFFFFFFF0 /* BAR emul mask(Memory) */ +#define PT_BAR_IO_RO_MASK 0x00000003 /* BAR ReadOnly mask(I/O) */ +#define PT_BAR_IO_EMU_MASK 0xFFFFFFFC /* BAR emul mask(I/O) */ + +static inline uint32_t base_address_with_flags(HostPCIIORegion *hr) +{ + if ((hr->flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { + return hr->base_addr | (hr->flags & ~PCI_BASE_ADDRESS_IO_MASK); + } else { + return hr->base_addr | (hr->flags & ~PCI_BASE_ADDRESS_MEM_MASK); + } +} + +static int pt_bar_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, + uint32_t real_offset, uint32_t *data) +{ + uint32_t reg_field = 0; + int index; + + /* get BAR index */ + index = pt_bar_offset_to_index(reg->offset); + if (index < 0) { + PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); + return -1; + } + + /* set initial guest physical base address to -1 */ + s->bases[index].e_physbase = -1; + + /* set BAR flag */ + s->bases[index].bar_flag = pt_bar_reg_parse(s, reg); + if (s->bases[index].bar_flag == PT_BAR_FLAG_UNUSED) { + reg_field = PT_INVALID_REG; + } + + *data = reg_field; + return 0; +} +static int pt_bar_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint32_t *value, uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t valid_emu_mask = 0; + uint32_t bar_emu_mask = 0; + int index; + + /* get BAR index */ + index = pt_bar_offset_to_index(reg->offset); + if (index < 0) { + PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); + return -1; + } + + /* use fixed-up value from kernel sysfs */ + *value = base_address_with_flags(&s->real_device->io_regions[index]); + + /* set emulate mask depend on BAR flag */ + switch (s->bases[index].bar_flag) { + case PT_BAR_FLAG_MEM: + bar_emu_mask = PT_BAR_MEM_EMU_MASK; + break; + case PT_BAR_FLAG_IO: + bar_emu_mask = PT_BAR_IO_EMU_MASK; + break; + case PT_BAR_FLAG_UPPER: + bar_emu_mask = PT_BAR_ALLF; + break; + default: + break; + } + + /* emulate BAR */ + valid_emu_mask = bar_emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + + return 0; +} +static int pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint32_t *value, uint32_t dev_value, + uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + XenPTRegGroup *reg_grp_entry = NULL; + XenPTReg *reg_entry = NULL; + XenPTRegion *base = NULL; + PCIDevice *d = &s->dev; + PCIIORegion *r; + uint32_t writable_mask = 0; + uint32_t throughable_mask = 0; + uint32_t bar_emu_mask = 0; + uint32_t bar_ro_mask = 0; + uint32_t new_addr, last_addr; + uint32_t prev_offset; + uint32_t r_size = 0; + int index = 0; + + /* get BAR index */ + index = pt_bar_offset_to_index(reg->offset); + if (index < 0) { + PT_ERR(d, "Internal error: Invalid BAR index [%d].\n", index); + return -1; + } + + r = &d->io_regions[index]; + base = &s->bases[index]; + r_size = pt_get_emul_size(base->bar_flag, r->size); + + /* set emulate mask and read-only mask depend on BAR flag */ + switch (s->bases[index].bar_flag) { + case PT_BAR_FLAG_MEM: + bar_emu_mask = PT_BAR_MEM_EMU_MASK; + bar_ro_mask = PT_BAR_MEM_RO_MASK | (r_size - 1); + break; + case PT_BAR_FLAG_IO: + bar_emu_mask = PT_BAR_IO_EMU_MASK; + bar_ro_mask = PT_BAR_IO_RO_MASK | (r_size - 1); + break; + case PT_BAR_FLAG_UPPER: + bar_emu_mask = PT_BAR_ALLF; + bar_ro_mask = 0; /* all upper 32bit are R/W */ + break; + default: + break; + } + + /* modify emulate register */ + writable_mask = bar_emu_mask & ~bar_ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* check whether we need to update the virtual region address or not */ + switch (s->bases[index].bar_flag) { + case PT_BAR_FLAG_MEM: + /* nothing to do */ + break; + case PT_BAR_FLAG_IO: + new_addr = cfg_entry->data; + last_addr = new_addr + r_size - 1; + /* check invalid address */ + if (last_addr <= new_addr || !new_addr || last_addr >= UINT16_MAX) { + /* check 64K range */ + if ((last_addr >= UINT16_MAX) && + (cfg_entry->data != (PT_BAR_ALLF & ~bar_ro_mask))) { + PT_WARN(d, "Guest attempt to set Base Address " + "over the 64KB. (offset: 0x%02x," + " addr: 0x%08x, size: 0x%08x)\n", + reg->offset, new_addr, r_size); + } + /* just remove mapping */ + r->addr = PCI_BAR_UNMAPPED; + goto exit; + } + break; + case PT_BAR_FLAG_UPPER: + if (cfg_entry->data) { + if (cfg_entry->data != (PT_BAR_ALLF & ~bar_ro_mask)) { + PT_WARN(d, "Guest attempt to set high MMIO Base Address. " + "Ignore mapping. " + "(offset: 0x%02x, high address: 0x%08x)\n", + reg->offset, cfg_entry->data); + } + /* clear lower address */ + d->io_regions[index-1].addr = -1; + } else { + /* find lower 32bit BAR */ + prev_offset = (reg->offset - 4); + reg_grp_entry = pt_find_reg_grp(s, prev_offset); + if (reg_grp_entry) { + reg_entry = pt_find_reg(reg_grp_entry, prev_offset); + if (reg_entry) { + /* restore lower address */ + d->io_regions[index-1].addr = reg_entry->data; + } else { + return -1; + } + } else { + return -1; + } + } + + /* never mapping the ''empty'' upper region, + * because we''ll do it enough for the lower region. + */ + r->addr = -1; + goto exit; + default: + break; + } + + /* update the corresponding virtual region address */ + /* + * When guest code tries to get block size of mmio, it will write all "1"s + * into pci bar register. In this case, cfg_entry->data == writable_mask. + * Especially for devices with large mmio, the value of writable_mask + * is likely to be a guest physical address that has been mapped to ram + * rather than mmio. Remapping this value to mmio should be prevented. + */ + + if (cfg_entry->data != writable_mask) { + r->addr = cfg_entry->data; + } + +exit: + /* create value for writing to I/O device register */ + throughable_mask = ~bar_emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + /* After BAR reg update, we need to remap BAR */ + reg_grp_entry = pt_find_reg_grp(s, PCI_COMMAND); + if (reg_grp_entry) { + reg_entry = pt_find_reg(reg_grp_entry, PCI_COMMAND); + if (reg_entry) { + pt_bar_mapping_one(s, index, reg_entry->data & PCI_COMMAND_IO, + reg_entry->data & PCI_COMMAND_MEMORY); + } + } + + return 0; +} +static int pt_bar_reg_restore(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint32_t real_offset, uint32_t dev_value, + uint32_t *value) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t bar_emu_mask = 0; + int index = 0; + + /* get BAR index */ + index = pt_bar_offset_to_index(reg->offset); + if (index < 0) { + PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); + return -1; + } + + /* use value from kernel sysfs */ + if (s->bases[index].bar_flag == PT_BAR_FLAG_UPPER) { + *value = s->real_device->io_regions[index - 1].base_addr >> 32; + } else { + *value = base_address_with_flags(&s->real_device->io_regions[index]); + } + + /* set emulate mask depend on BAR flag */ + switch (s->bases[index].bar_flag) { + case PT_BAR_FLAG_MEM: + bar_emu_mask = PT_BAR_MEM_EMU_MASK; + break; + case PT_BAR_FLAG_IO: + bar_emu_mask = PT_BAR_IO_EMU_MASK; + break; + case PT_BAR_FLAG_UPPER: + bar_emu_mask = PT_BAR_ALLF; + break; + default: + break; + } + + /* create value for restoring to I/O device register */ + *value = PT_MERGE_VALUE(*value, dev_value, bar_emu_mask); + + return 0; +} + +/* write Exp ROM BAR */ +static int pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint32_t *value, + uint32_t dev_value, uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + XenPTRegGroup *reg_grp_entry = NULL; + XenPTReg *reg_entry = NULL; + XenPTRegion *base = NULL; + PCIDevice *d = (PCIDevice *)&s->dev; + PCIIORegion *r; + uint32_t writable_mask = 0; + uint32_t throughable_mask = 0; + pcibus_t r_size = 0; + uint32_t bar_emu_mask = 0; + uint32_t bar_ro_mask = 0; + + r = &d->io_regions[PCI_ROM_SLOT]; + r_size = r->size; + base = &s->bases[PCI_ROM_SLOT]; + /* align memory type resource size */ + pt_get_emul_size(base->bar_flag, r_size); + + /* set emulate mask and read-only mask */ + bar_emu_mask = reg->emu_mask; + bar_ro_mask = (reg->ro_mask | (r_size - 1)) & ~PCI_ROM_ADDRESS_ENABLE; + + /* modify emulate register */ + writable_mask = ~bar_ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* update the corresponding virtual region address */ + /* + * When guest code tries to get block size of mmio, it will write all "1"s + * into pci bar register. In this case, cfg_entry->data == writable_mask. + * Especially for devices with large mmio, the value of writable_mask + * is likely to be a guest physical address that has been mapped to ram + * rather than mmio. Remapping this value to mmio should be prevented. + */ + + if (cfg_entry->data != writable_mask) { + r->addr = cfg_entry->data; + } + + /* create value for writing to I/O device register */ + throughable_mask = ~bar_emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + /* After BAR reg update, we need to remap BAR*/ + reg_grp_entry = pt_find_reg_grp(s, PCI_COMMAND); + if (reg_grp_entry) { + reg_entry = pt_find_reg(reg_grp_entry, PCI_COMMAND); + if (reg_entry) { + pt_bar_mapping_one(s, PCI_ROM_SLOT, + reg_entry->data & PCI_COMMAND_IO, + reg_entry->data & PCI_COMMAND_MEMORY); + } + } + + return 0; +} +/* restore ROM BAR */ +static int pt_exp_rom_bar_reg_restore(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, + uint32_t real_offset, + uint32_t dev_value, uint32_t *value) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t v; + + if (host_pci_get_long(s->real_device, PCI_ROM_ADDRESS, &v)) { + return -1; + } + /* use value from kernel sysfs */ + *value = PT_MERGE_VALUE(v, dev_value, reg->emu_mask); + return 0; +} + +/* Header Type0 reg static infomation table */ +static XenPTRegInfo pt_emu_reg_header0_tbl[] = { + /* Vendor ID reg */ + { + .offset = PCI_VENDOR_ID, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFFFF, + .emu_mask = 0xFFFF, + .init = pt_vendor_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_word_reg_write, + .u.w.restore = NULL, + }, + /* Device ID reg */ + { + .offset = PCI_DEVICE_ID, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFFFF, + .emu_mask = 0xFFFF, + .init = pt_device_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_word_reg_write, + .u.w.restore = NULL, + }, + /* Command reg */ + { + .offset = PCI_COMMAND, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xF880, + .emu_mask = 0x0740, + .init = pt_common_reg_init, + .u.w.read = pt_cmd_reg_read, + .u.w.write = pt_cmd_reg_write, + .u.w.restore = pt_cmd_reg_restore, + }, + /* Capabilities Pointer reg */ + { + .offset = PCI_CAPABILITY_LIST, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = pt_ptr_reg_init, + .u.b.read = pt_byte_reg_read, + .u.b.write = pt_byte_reg_write, + .u.b.restore = NULL, + }, + /* Status reg */ + /* use emulated Cap Ptr value to initialize, + * so need to be declared after Cap Ptr reg + */ + { + .offset = PCI_STATUS, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0x06FF, + .emu_mask = 0x0010, + .init = pt_status_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_word_reg_write, + .u.w.restore = NULL, + }, + /* Cache Line Size reg */ + { + .offset = PCI_CACHE_LINE_SIZE, + .size = 1, + .init_val = 0x00, + .ro_mask = 0x00, + .emu_mask = 0xFF, + .init = pt_common_reg_init, + .u.b.read = pt_byte_reg_read, + .u.b.write = pt_byte_reg_write, + .u.b.restore = pt_byte_reg_restore, + }, + /* Latency Timer reg */ + { + .offset = PCI_LATENCY_TIMER, + .size = 1, + .init_val = 0x00, + .ro_mask = 0x00, + .emu_mask = 0xFF, + .init = pt_common_reg_init, + .u.b.read = pt_byte_reg_read, + .u.b.write = pt_byte_reg_write, + .u.b.restore = pt_byte_reg_restore, + }, + /* Header Type reg */ + { + .offset = PCI_HEADER_TYPE, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0x00, + .init = pt_header_type_reg_init, + .u.b.read = pt_byte_reg_read, + .u.b.write = pt_byte_reg_write, + .u.b.restore = NULL, + }, + /* Interrupt Line reg */ + { + .offset = PCI_INTERRUPT_LINE, + .size = 1, + .init_val = 0x00, + .ro_mask = 0x00, + .emu_mask = 0xFF, + .init = pt_common_reg_init, + .u.b.read = pt_byte_reg_read, + .u.b.write = pt_byte_reg_write, + .u.b.restore = NULL, + }, + /* Interrupt Pin reg */ + { + .offset = PCI_INTERRUPT_PIN, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = pt_irqpin_reg_init, + .u.b.read = pt_byte_reg_read, + .u.b.write = pt_byte_reg_write, + .u.b.restore = NULL, + }, + /* BAR 0 reg */ + /* mask of BAR need to be decided later, depends on IO/MEM type */ + { + .offset = PCI_BASE_ADDRESS_0, + .size = 4, + .init_val = 0x00000000, + .init = pt_bar_reg_init, + .u.dw.read = pt_bar_reg_read, + .u.dw.write = pt_bar_reg_write, + .u.dw.restore = pt_bar_reg_restore, + }, + /* BAR 1 reg */ + { + .offset = PCI_BASE_ADDRESS_1, + .size = 4, + .init_val = 0x00000000, + .init = pt_bar_reg_init, + .u.dw.read = pt_bar_reg_read, + .u.dw.write = pt_bar_reg_write, + .u.dw.restore = pt_bar_reg_restore, + }, + /* BAR 2 reg */ + { + .offset = PCI_BASE_ADDRESS_2, + .size = 4, + .init_val = 0x00000000, + .init = pt_bar_reg_init, + .u.dw.read = pt_bar_reg_read, + .u.dw.write = pt_bar_reg_write, + .u.dw.restore = pt_bar_reg_restore, + }, + /* BAR 3 reg */ + { + .offset = PCI_BASE_ADDRESS_3, + .size = 4, + .init_val = 0x00000000, + .init = pt_bar_reg_init, + .u.dw.read = pt_bar_reg_read, + .u.dw.write = pt_bar_reg_write, + .u.dw.restore = pt_bar_reg_restore, + }, + /* BAR 4 reg */ + { + .offset = PCI_BASE_ADDRESS_4, + .size = 4, + .init_val = 0x00000000, + .init = pt_bar_reg_init, + .u.dw.read = pt_bar_reg_read, + .u.dw.write = pt_bar_reg_write, + .u.dw.restore = pt_bar_reg_restore, + }, + /* BAR 5 reg */ + { + .offset = PCI_BASE_ADDRESS_5, + .size = 4, + .init_val = 0x00000000, + .init = pt_bar_reg_init, + .u.dw.read = pt_bar_reg_read, + .u.dw.write = pt_bar_reg_write, + .u.dw.restore = pt_bar_reg_restore, + }, + /* Expansion ROM BAR reg */ + { + .offset = PCI_ROM_ADDRESS, + .size = 4, + .init_val = 0x00000000, + .ro_mask = 0x000007FE, + .emu_mask = 0xFFFFF800, + .init = pt_bar_reg_init, + .u.dw.read = pt_long_reg_read, + .u.dw.write = pt_exp_rom_bar_reg_write, + .u.dw.restore = pt_exp_rom_bar_reg_restore, + }, + { + .size = 0, + }, +}; + + +/********************************* + * Vital Product Data Capability + */ + +/* Vital Product Data Capability Structure reg static infomation table */ +static XenPTRegInfo pt_emu_reg_vpd_tbl[] = { + { + .offset = PCI_CAP_LIST_NEXT, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = pt_ptr_reg_init, + .u.b.read = pt_byte_reg_read, + .u.b.write = pt_byte_reg_write, + .u.b.restore = NULL, + }, + { + .size = 0, + }, +}; + + +/************************************** + * Vendor Specific Capability + */ + +/* Vendor Specific Capability Structure reg static infomation table */ +static XenPTRegInfo pt_emu_reg_vendor_tbl[] = { + { + .offset = PCI_CAP_LIST_NEXT, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = pt_ptr_reg_init, + .u.b.read = pt_byte_reg_read, + .u.b.write = pt_byte_reg_write, + .u.b.restore = NULL, + }, + { + .size = 0, + }, +}; + + +/***************************** + * PCI Express Capability + */ + +/* initialize Link Control register */ +static int pt_linkctrl_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + uint8_t cap_ver = 0; + uint8_t dev_type = 0; + + cap_ver = pci_get_byte(s->dev.config + real_offset - reg->offset + + PCI_EXP_FLAGS) + & PCI_EXP_FLAGS_VERS; + dev_type = (pci_get_byte(s->dev.config + real_offset - reg->offset + + PCI_EXP_FLAGS) + & PCI_EXP_FLAGS_TYPE) >> 4; + + /* no need to initialize in case of Root Complex Integrated Endpoint + * with cap_ver 1.x + */ + if ((dev_type == PCI_EXP_TYPE_RC_END) && (cap_ver == 1)) { + *data = PT_INVALID_REG; + } + + *data = reg->init_val; + return 0; +} +/* initialize Device Control 2 register */ +static int pt_devctrl2_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + uint8_t cap_ver = 0; + + cap_ver = pci_get_byte(s->dev.config + real_offset - reg->offset + + PCI_EXP_FLAGS) + & PCI_EXP_FLAGS_VERS; + + /* no need to initialize in case of cap_ver 1.x */ + if (cap_ver == 1) { + *data = PT_INVALID_REG; + } + + *data = reg->init_val; + return 0; +} +/* initialize Link Control 2 register */ +static int pt_linkctrl2_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + uint32_t reg_field = 0; + uint8_t cap_ver = 0; + + cap_ver = pci_get_byte(s->dev.config + real_offset - reg->offset + + PCI_EXP_FLAGS) + & PCI_EXP_FLAGS_VERS; + + /* no need to initialize in case of cap_ver 1.x */ + if (cap_ver == 1) { + reg_field = PT_INVALID_REG; + } else { + /* set Supported Link Speed */ + uint8_t lnkcap = pci_get_byte(s->dev.config + real_offset - reg->offset + + PCI_EXP_LNKCAP); + reg_field |= PCI_EXP_LNKCAP_SLS & lnkcap; + } + + *data = reg_field; + return 0; +} + +/* PCI Express Capability Structure reg static infomation table */ +static XenPTRegInfo pt_emu_reg_pcie_tbl[] = { + /* Next Pointer reg */ + { + .offset = PCI_CAP_LIST_NEXT, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = pt_ptr_reg_init, + .u.b.read = pt_byte_reg_read, + .u.b.write = pt_byte_reg_write, + .u.b.restore = NULL, + }, + /* Device Capabilities reg */ + { + .offset = PCI_EXP_DEVCAP, + .size = 4, + .init_val = 0x00000000, + .ro_mask = 0x1FFCFFFF, + .emu_mask = 0x10000000, + .init = pt_common_reg_init, + .u.dw.read = pt_long_reg_read, + .u.dw.write = pt_long_reg_write, + .u.dw.restore = NULL, + }, + /* Device Control reg */ + { + .offset = PCI_EXP_DEVCTL, + .size = 2, + .init_val = 0x2810, + .ro_mask = 0x8400, + .emu_mask = 0xFFFF, + .init = pt_common_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_word_reg_write, + .u.w.restore = pt_word_reg_restore, + }, + /* Link Control reg */ + { + .offset = PCI_EXP_LNKCTL, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFC34, + .emu_mask = 0xFFFF, + .init = pt_linkctrl_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_word_reg_write, + .u.w.restore = pt_word_reg_restore, + }, + /* Device Control 2 reg */ + { + .offset = 0x28, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFFE0, + .emu_mask = 0xFFFF, + .init = pt_devctrl2_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_word_reg_write, + .u.w.restore = pt_word_reg_restore, + }, + /* Link Control 2 reg */ + { + .offset = 0x30, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xE040, + .emu_mask = 0xFFFF, + .init = pt_linkctrl2_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_word_reg_write, + .u.w.restore = pt_word_reg_restore, + }, + { + .size = 0, + }, +}; + + +/********************************* + * Power Management Capability + */ + +/* initialize Power Management Capabilities register */ +static int pt_pmc_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + PCIDevice *d = &s->dev; + + if (s->power_mgmt) { + /* set Power Management Capabilities register */ + s->pm_state->pmc_field = pci_get_word(d->config + real_offset); + } + + *data = reg->init_val; + return 0; +} +/* initialize PCI Power Management Control/Status register */ +static int pt_pmcsr_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + PCIDevice *d = &s->dev; + uint16_t cap_ver = 0; + uint16_t v = 0; + + if (!s->power_mgmt) { + *data = reg->init_val; + return 0; + } + + /* check PCI Power Management support version */ + cap_ver = s->pm_state->pmc_field & PCI_PM_CAP_VER_MASK; + + if (cap_ver > 2) { + /* set No Soft Reset */ + s->pm_state->no_soft_reset + pci_get_byte(d->config + real_offset) & PCI_PM_CTRL_NO_SOFT_RESET; + } + + host_pci_get_word(s->real_device, real_offset, &v); + /* wake up real physical device */ + switch (v & PCI_PM_CTRL_STATE_MASK) { + case 0: + break; + case 1: + PT_LOG(d, "Power state transition D1 -> D0active\n"); + host_pci_set_word(s->real_device, real_offset, 0); + break; + case 2: + PT_LOG(d, "Power state transition D2 -> D0active\n"); + host_pci_set_word(s->real_device, real_offset, 0); + usleep(200); + break; + case 3: + PT_LOG(d, "Power state transition D3hot -> D0active\n"); + host_pci_set_word(s->real_device, real_offset, 0); + usleep(10 * 1000); + if (pt_init_pci_config(s)) { + return -1; + } + break; + } + + *data = reg->init_val; + return 0; +} +/* read Power Management Control/Status register */ +static int pt_pmcsr_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *value, uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t valid_emu_mask = reg->emu_mask; + + if (!s->power_mgmt) { + valid_emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; + } + + valid_emu_mask = valid_emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + + return 0; +} +/* reset Interrupt and I/O resource */ +static void pt_reset_interrupt_and_io_mapping(XenPCIPassthroughState *s) +{ + PCIDevice *d = &s->dev; + PCIIORegion *r; + int i = 0; + uint8_t e_device = 0; + uint8_t e_intx = 0; + + /* unbind INTx */ + e_device = PCI_SLOT(s->dev.devfn); + e_intx = pci_intx(s); + + if (s->machine_irq) { + if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, s->machine_irq, + PT_IRQ_TYPE_PCI, 0, e_device, e_intx, 0)) { + PT_ERR(d, "Unbinding of interrupt failed!\n"); + } + } + + /* clear all virtual region address */ + for (i = 0; i < PCI_NUM_REGIONS; i++) { + r = &d->io_regions[i]; + r->addr = -1; + } + + /* unmapping BAR */ + pt_bar_mapping(s, 0, 0); +} +/* check power state transition */ +static int check_power_state(XenPCIPassthroughState *s) +{ + XenPTPM *pm_state = s->pm_state; + PCIDevice *d = &s->dev; + uint16_t read_val = 0; + uint16_t cur_state = 0; + + /* get current power state */ + if (host_pci_get_word(s->real_device, pm_state->pm_base + PCI_PM_CTRL, + &read_val)) { + return -1; + } + cur_state = read_val & PCI_PM_CTRL_STATE_MASK; + + if (pm_state->req_state != cur_state) { + PT_ERR(d, "Failed to change power state. " + "(requested state: %d, current state: %d)\n", + pm_state->req_state, cur_state); + return -1; + } + return 0; +} +/* write Power Management Control/Status register */ +static void pt_from_d3hot_to_d0_with_reset(void *opaque) +{ + XenPCIPassthroughState *s = opaque; + XenPTPM *pm_state = s->pm_state; + int ret = 0; + + /* check power state */ + ret = check_power_state(s); + + if (ret < 0) { + goto out; + } + + pt_init_pci_config(s); + +out: + /* power state transition flags off */ + pm_state->flags &= ~PT_FLAG_TRANSITING; + + qemu_free_timer(pm_state->pm_timer); + pm_state->pm_timer = NULL; +} +static void pt_default_power_transition(void *opaque) +{ + XenPCIPassthroughState *ptdev = opaque; + XenPTPM *pm_state = ptdev->pm_state; + + /* check power state */ + check_power_state(ptdev); + + /* power state transition flags off */ + pm_state->flags &= ~PT_FLAG_TRANSITING; + + qemu_free_timer(pm_state->pm_timer); + pm_state->pm_timer = NULL; +} +static int pt_pmcsr_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *value, uint16_t dev_value, + uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + PCIDevice *d = &s->dev; + uint16_t emu_mask = reg->emu_mask; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + XenPTPM *pm_state = s->pm_state; + + if (!s->power_mgmt) { + emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; + } + + /* modify emulate register */ + writable_mask = emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ + throughable_mask = ~emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + if (!s->power_mgmt) { + return 0; + } + + /* set I/O device power state */ + pm_state->cur_state = dev_value & PCI_PM_CTRL_STATE_MASK; + + /* set Guest requested PowerState */ + pm_state->req_state = *value & PCI_PM_CTRL_STATE_MASK; + + /* check power state transition or not */ + if (pm_state->cur_state == pm_state->req_state) { + /* not power state transition */ + return 0; + } + + /* check enable power state transition */ + if ((pm_state->req_state != 0) && + (pm_state->cur_state > pm_state->req_state)) { + PT_ERR(d, "Invalid power transition. " + "(requested state: %d, current state: %d)\n", + pm_state->req_state, pm_state->cur_state); + + return 0; + } + + /* check if this device supports the requested power state */ + if (((pm_state->req_state == 1) && !(pm_state->pmc_field & PCI_PM_CAP_D1)) + || ((pm_state->req_state == 2) && + !(pm_state->pmc_field & PCI_PM_CAP_D2))) { + PT_ERR(d, "Invalid power transition. " + "(requested state: %d, current state: %d)\n", + pm_state->req_state, pm_state->cur_state); + + return 0; + } + + /* in case of transition related to D3hot, it''s necessary to wait 10 ms. + * But because writing to register will be performed later on actually, + * don''t start QEMUTimer right now, just alloc and init QEMUTimer here. + */ + if ((pm_state->cur_state == 3) || (pm_state->req_state == 3)) { + if (pm_state->req_state == 0) { + /* alloc and init QEMUTimer */ + if (!pm_state->no_soft_reset) { + pm_state->pm_timer = qemu_new_timer_ms(rt_clock, + pt_from_d3hot_to_d0_with_reset, s); + + /* reset Interrupt and I/O resource mapping */ + pt_reset_interrupt_and_io_mapping(s); + } else { + pm_state->pm_timer = qemu_new_timer_ms(rt_clock, + pt_default_power_transition, s); + } + } else { + /* alloc and init QEMUTimer */ + pm_state->pm_timer = qemu_new_timer_ms(rt_clock, + pt_default_power_transition, s); + } + + /* set power state transition delay */ + pm_state->pm_delay = 10; + + /* power state transition flags on */ + pm_state->flags |= PT_FLAG_TRANSITING; + } + /* in case of transition related to D0, D1 and D2, + * no need to use QEMUTimer. + * So, we perfom writing to register here and then read it back. + */ + else { + /* write power state to I/O device register */ + host_pci_set_word(s->real_device, pm_state->pm_base + PCI_PM_CTRL, + *value); + + /* in case of transition related to D2, + * it''s necessary to wait 200 usec. + * But because QEMUTimer do not support microsec unit right now, + * so we do wait ourself here. + */ + if ((pm_state->cur_state == 2) || (pm_state->req_state == 2)) { + usleep(200); + } + + /* check power state */ + check_power_state(s); + + /* recreate value for writing to I/O device register */ + if (host_pci_get_word(s->real_device, pm_state->pm_base + PCI_PM_CTRL, + value)) { + return -1; + } + } + + return 0; +} + +/* restore Power Management Control/Status register */ +static int pt_pmcsr_reg_restore(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint32_t real_offset, uint16_t dev_value, + uint16_t *value) +{ + /* create value for restoring to I/O device register + * No need to restore, just clear PME Enable and PME Status bit + * Note: register type of PME Status bit is RW1C, so clear by writing 1b + */ + *value = (dev_value & ~PCI_PM_CTRL_PME_ENABLE) | PCI_PM_CTRL_PME_STATUS; + + return 0; +} + + +/* Power Management Capability reg static infomation table */ +static XenPTRegInfo pt_emu_reg_pm_tbl[] = { + /* Next Pointer reg */ + { + .offset = PCI_CAP_LIST_NEXT, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = pt_ptr_reg_init, + .u.b.read = pt_byte_reg_read, + .u.b.write = pt_byte_reg_write, + .u.b.restore = NULL, + }, + /* Power Management Capabilities reg */ + { + .offset = PCI_CAP_FLAGS, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFFFF, + .emu_mask = 0xF9C8, + .init = pt_pmc_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_word_reg_write, + .u.w.restore = NULL, + }, + /* PCI Power Management Control/Status reg */ + { + .offset = PCI_PM_CTRL, + .size = 2, + .init_val = 0x0008, + .ro_mask = 0xE1FC, + .emu_mask = 0x8100, + .init = pt_pmcsr_reg_init, + .u.w.read = pt_pmcsr_reg_read, + .u.w.write = pt_pmcsr_reg_write, + .u.w.restore = pt_pmcsr_reg_restore, + }, + { + .size = 0, + }, +}; + + +/**************************** + * Capabilities + */ + +/* AER register operations */ + +static void aer_save_one_register(XenPCIPassthroughState *s, int offset) +{ + PCIDevice *d = &s->dev; + uint32_t aer_base = s->pm_state->aer_base; + uint32_t val = 0; + + if (host_pci_get_long(s->real_device, aer_base + offset, &val)) { + return; + } + pci_set_long(d->config + aer_base + offset, val); +} +static void pt_aer_reg_save(XenPCIPassthroughState *s) +{ + /* after reset, following register values should be restored. + * So, save them. + */ + aer_save_one_register(s, PCI_ERR_UNCOR_MASK); + aer_save_one_register(s, PCI_ERR_UNCOR_SEVER); + aer_save_one_register(s, PCI_ERR_COR_MASK); + aer_save_one_register(s, PCI_ERR_CAP); +} +static void aer_restore_one_register(XenPCIPassthroughState *s, int offset) +{ + PCIDevice *d = &s->dev; + uint32_t aer_base = s->pm_state->aer_base; + uint32_t config = 0; + + config = pci_get_long(d->config + aer_base + offset); + host_pci_set_long(s->real_device, aer_base + offset, config); +} +static void pt_aer_reg_restore(XenPCIPassthroughState *s) +{ + /* the following registers should be reconfigured to correct values + * after reset. restore them. + * other registers should not be reconfigured after reset + * if there is no reason + */ + aer_restore_one_register(s, PCI_ERR_UNCOR_MASK); + aer_restore_one_register(s, PCI_ERR_UNCOR_SEVER); + aer_restore_one_register(s, PCI_ERR_COR_MASK); + aer_restore_one_register(s, PCI_ERR_CAP); +} + +/* capability structure register group size functions */ + +static int pt_reg_grp_size_init(XenPCIPassthroughState *s, + const XenPTRegGroupInfo *grp_reg, + uint32_t base_offset, uint8_t *size) +{ + *size = grp_reg->grp_size; + return 0; +} +/* get Power Management Capability Structure register group size */ +static int pt_pm_size_init(XenPCIPassthroughState *s, + const XenPTRegGroupInfo *grp_reg, + uint32_t base_offset, uint8_t *size) +{ + *size = grp_reg->grp_size; + + if (!s->power_mgmt) { + return 0; + } + + s->pm_state = g_new0(XenPTPM, 1); + + /* set Power Management Capability base offset */ + s->pm_state->pm_base = base_offset; + + /* find AER register and set AER Capability base offset */ + s->pm_state->aer_base = host_pci_find_ext_cap_offset(s->real_device, + PCI_EXT_CAP_ID_ERR); + + /* save AER register */ + if (s->pm_state->aer_base) { + pt_aer_reg_save(s); + } + + return 0; +} +/* get Vendor Specific Capability Structure register group size */ +static int pt_vendor_size_init(XenPCIPassthroughState *s, + const XenPTRegGroupInfo *grp_reg, + uint32_t base_offset, uint8_t *size) +{ + *size = pci_get_byte(s->dev.config + base_offset + 0x02); + return 0; +} +/* get PCI Express Capability Structure register group size */ +static int pt_pcie_size_init(XenPCIPassthroughState *s, + const XenPTRegGroupInfo *grp_reg, + uint32_t base_offset, uint8_t *size) +{ + PCIDevice *d = &s->dev; + uint16_t exp_flag = 0; + uint16_t type = 0; + uint16_t version = 0; + uint8_t pcie_size = 0; + + exp_flag = pci_get_word(d->config + base_offset + PCI_EXP_FLAGS); + type = (exp_flag & PCI_EXP_FLAGS_TYPE) >> 4; + version = exp_flag & PCI_EXP_FLAGS_VERS; + + /* calculate size depend on capability version and device/port type */ + /* in case of PCI Express Base Specification Rev 1.x */ + if (version == 1) { + /* The PCI Express Capabilities, Device Capabilities, and Device + * Status/Control registers are required for all PCI Express devices. + * The Link Capabilities and Link Status/Control are required for all + * Endpoints that are not Root Complex Integrated Endpoints. Endpoints + * are not required to implement registers other than those listed + * above and terminate the capability structure. + */ + switch (type) { + case PCI_EXP_TYPE_ENDPOINT: + case PCI_EXP_TYPE_LEG_END: + pcie_size = 0x14; + break; + case PCI_EXP_TYPE_RC_END: + /* has no link */ + pcie_size = 0x0C; + break; + /* only EndPoint passthrough is supported */ + case PCI_EXP_TYPE_ROOT_PORT: + case PCI_EXP_TYPE_UPSTREAM: + case PCI_EXP_TYPE_DOWNSTREAM: + case PCI_EXP_TYPE_PCI_BRIDGE: + case PCI_EXP_TYPE_PCIE_BRIDGE: + case PCI_EXP_TYPE_RC_EC: + default: + PT_ERR(d, "Internal error: Unsupported device/port type (%d).\n", + type); + return -1; + } + } + /* in case of PCI Express Base Specification Rev 2.0 */ + else if (version == 2) { + switch (type) { + case PCI_EXP_TYPE_ENDPOINT: + case PCI_EXP_TYPE_LEG_END: + case PCI_EXP_TYPE_RC_END: + /* For Functions that do not implement the registers, + * these spaces must be hardwired to 0b. + */ + pcie_size = 0x3C; + break; + /* only EndPoint passthrough is supported */ + case PCI_EXP_TYPE_ROOT_PORT: + case PCI_EXP_TYPE_UPSTREAM: + case PCI_EXP_TYPE_DOWNSTREAM: + case PCI_EXP_TYPE_PCI_BRIDGE: + case PCI_EXP_TYPE_PCIE_BRIDGE: + case PCI_EXP_TYPE_RC_EC: + default: + PT_ERR(d, "Internal error: Unsupported device/port type (%d).\n", + type); + return -1; + } + } else { + PT_ERR(d, "Internal error: Unsupported capability version (%d).\n", + version); + return -1; + } + + *size = pcie_size; + return 0; +} + +static const XenPTRegGroupInfo pt_emu_reg_grp_tbl[] = { + /* Header Type0 reg group */ + { + .grp_id = 0xFF, + .grp_type = GRP_TYPE_EMU, + .grp_size = 0x40, + .size_init = pt_reg_grp_size_init, + .emu_reg_tbl = pt_emu_reg_header0_tbl, + }, + /* PCI PowerManagement Capability reg group */ + { + .grp_id = PCI_CAP_ID_PM, + .grp_type = GRP_TYPE_EMU, + .grp_size = PCI_PM_SIZEOF, + .size_init = pt_pm_size_init, + .emu_reg_tbl = pt_emu_reg_pm_tbl, + }, + /* AGP Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_AGP, + .grp_type = GRP_TYPE_HARDWIRED, + .grp_size = 0x30, + .size_init = pt_reg_grp_size_init, + }, + /* Vital Product Data Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_VPD, + .grp_type = GRP_TYPE_EMU, + .grp_size = 0x08, + .size_init = pt_reg_grp_size_init, + .emu_reg_tbl = pt_emu_reg_vpd_tbl, + }, + /* Slot Identification reg group */ + { + .grp_id = PCI_CAP_ID_SLOTID, + .grp_type = GRP_TYPE_HARDWIRED, + .grp_size = 0x04, + .size_init = pt_reg_grp_size_init, + }, + /* PCI-X Capabilities List Item reg group */ + { + .grp_id = PCI_CAP_ID_PCIX, + .grp_type = GRP_TYPE_HARDWIRED, + .grp_size = 0x18, + .size_init = pt_reg_grp_size_init, + }, + /* Vendor Specific Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_VNDR, + .grp_type = GRP_TYPE_EMU, + .grp_size = 0xFF, + .size_init = pt_vendor_size_init, + .emu_reg_tbl = pt_emu_reg_vendor_tbl, + }, + /* SHPC Capability List Item reg group */ + { + .grp_id = PCI_CAP_ID_SHPC, + .grp_type = GRP_TYPE_HARDWIRED, + .grp_size = 0x08, + .size_init = pt_reg_grp_size_init, + }, + /* Subsystem ID and Subsystem Vendor ID Capability List Item reg group */ + { + .grp_id = PCI_CAP_ID_SSVID, + .grp_type = GRP_TYPE_HARDWIRED, + .grp_size = 0x08, + .size_init = pt_reg_grp_size_init, + }, + /* AGP 8x Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_AGP3, + .grp_type = GRP_TYPE_HARDWIRED, + .grp_size = 0x30, + .size_init = pt_reg_grp_size_init, + }, + /* PCI Express Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_EXP, + .grp_type = GRP_TYPE_EMU, + .grp_size = 0xFF, + .size_init = pt_pcie_size_init, + .emu_reg_tbl = pt_emu_reg_pcie_tbl, + }, + { + .grp_size = 0, + }, +}; + +/* initialize Capabilities Pointer or Next Pointer register */ +static int pt_ptr_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + /* uint32_t reg_field = (uint32_t)s->dev.config[real_offset]; */ + uint32_t reg_field = pci_get_byte(s->dev.config + real_offset); + int i; + + /* find capability offset */ + while (reg_field) { + for (i = 0; pt_emu_reg_grp_tbl[i].grp_size != 0; i++) { + if (pt_hide_dev_cap(s->real_device, + pt_emu_reg_grp_tbl[i].grp_id)) { + continue; + } + if (pt_emu_reg_grp_tbl[i].grp_id == s->dev.config[reg_field]) { + if (pt_emu_reg_grp_tbl[i].grp_type == GRP_TYPE_EMU) { + goto out; + } + /* ignore the 0 hardwired capability, find next one */ + break; + } + } + /* next capability */ + /* reg_field = (uint32_t)s->dev.config[reg_field + 1]; */ + reg_field = pci_get_byte(s->dev.config + reg_field + 1); + } + +out: + *data = reg_field; + return 0; +} + + +/************* + * Main + */ + +/* restore a part of I/O device register */ +static int pt_config_restore(XenPCIPassthroughState *s) +{ + XenPTRegGroup *reg_grp_entry = NULL; + XenPTReg *reg_entry = NULL; + XenPTRegInfo *reg = NULL; + uint32_t real_offset = 0; + uint32_t read_val = 0; + uint32_t val = 0; + int rc = 0; + + /* find emulate register group entry */ + QLIST_FOREACH(reg_grp_entry, &s->reg_grp_tbl, entries) { + /* find emulate register entry */ + QLIST_FOREACH(reg_entry, ®_grp_entry->reg_tbl_list, entries) { + reg = reg_entry->reg; + + /* check whether restoring is needed */ + if (!reg->u.b.restore) { + continue; + } + + real_offset = reg_grp_entry->base_offset + reg->offset; + + /* read I/O device register value */ + rc = host_pci_get_block(s->real_device, real_offset, + (uint8_t *)&read_val, reg->size); + + if (rc < 0) { + PT_ERR(&s->dev, "pci_read_block failed. " + "return value: %d.\n", rc); + memset(&read_val, 0xff, reg->size); + } + + val = 0; + + /* restore based on register size */ + switch (reg->size) { + case 1: + /* byte register */ + rc = reg->u.b.restore(s, reg_entry, real_offset, + (uint8_t)read_val, (uint8_t *)&val); + break; + case 2: + /* word register */ + rc = reg->u.w.restore(s, reg_entry, real_offset, + (uint16_t)read_val, (uint16_t *)&val); + break; + case 4: + /* double word register */ + rc = reg->u.dw.restore(s, reg_entry, real_offset, + (uint32_t)read_val, (uint32_t *)&val); + break; + } + + /* restoring error */ + if (rc < 0) { + xen_shutdown_fatal_error("Internal error: Invalid restoring." + " (%s, rc: %d)\n", __func__, rc); + return -1; + } + + PT_LOG_CONFIG(&s->dev, real_offset, val, reg->size); + + rc = host_pci_set_block(s->real_device, real_offset, + (uint8_t *)&val, reg->size); + + if (rc < 0) { + PT_ERR(&s->dev, "pci_write_block failed. " + "return value: %d.\n", rc); + return -1; + } + } + } + + /* if AER supported, restore it */ + if (s->pm_state->aer_base) { + pt_aer_reg_restore(s); + } + return 0; +} +/* reinitialize all emulate registers */ +static int pt_config_reinit(XenPCIPassthroughState *s) +{ + XenPTRegGroup *reg_grp_entry = NULL; + XenPTReg *reg_entry = NULL; + XenPTRegInfo *reg = NULL; + int rc = 0; + + /* find emulate register group entry */ + QLIST_FOREACH(reg_grp_entry, &s->reg_grp_tbl, entries) { + /* find emulate register entry */ + QLIST_FOREACH(reg_entry, ®_grp_entry->reg_tbl_list, entries) { + reg = reg_entry->reg; + if (reg->init) { + /* initialize emulate register */ + rc = reg->init(s, reg_entry->reg, + reg_grp_entry->base_offset + reg->offset, + ®_entry->data); + if (rc < 0) { + return rc; + } + } + } + } + return 0; +} + +static int pt_init_pci_config(XenPCIPassthroughState *s) +{ + PCIDevice *d = &s->dev; + int rc = 0; + + PT_LOG(d, "Reinitialize PCI configuration registers due to power state" + " transition with internal reset.\n"); + + /* restore a part of I/O device register */ + rc = pt_config_restore(s); + if (rc < 0) { + return rc; + } + + /* reinitialize all emulate register */ + rc = pt_config_reinit(s); + if (rc < 0) { + return rc; + } + + /* rebind machine_irq to device */ + if (s->machine_irq != 0) { + uint8_t e_device = PCI_SLOT(s->dev.devfn); + uint8_t e_intx = pci_intx(s); + + rc = xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, s->machine_irq, 0, + e_device, e_intx); + if (rc < 0) { + PT_ERR(d, "Rebinding of interrupt failed! rc=%d\n", rc); + } + } + + return rc; +} + +static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap) +{ + uint8_t id; + int max_cap = 48; + uint8_t pos = PCI_CAPABILITY_LIST; + uint8_t status = 0; + + if (host_pci_get_byte(s->real_device, PCI_STATUS, &status)) { + return 0; + } + if ((status & PCI_STATUS_CAP_LIST) == 0) { + return 0; + } + + while (max_cap--) { + if (host_pci_get_byte(s->real_device, pos, &pos)) { + break; + } + if (pos < 0x40) { + break; + } + + pos &= ~3; + if (host_pci_get_byte(s->real_device, pos + PCI_CAP_LIST_ID, &id)) { + break; + } + + if (id == 0xff) { + break; + } + if (id == cap) { + return pos; + } + + pos += PCI_CAP_LIST_NEXT; + } + return 0; +} + +static int pt_config_reg_init(XenPCIPassthroughState *s, + XenPTRegGroup *reg_grp, XenPTRegInfo *reg) +{ + XenPTReg *reg_entry; + uint32_t data = 0; + int rc = 0; + + reg_entry = g_new0(XenPTReg, 1); + reg_entry->reg = reg; + + if (reg->init) { + /* initialize emulate register */ + rc = reg->init(s, reg_entry->reg, + reg_grp->base_offset + reg->offset, &data); + if (rc < 0) { + free(reg_entry); + return rc; + } + if (data == PT_INVALID_REG) { + /* free unused BAR register entry */ + free(reg_entry); + return 0; + } + /* set register value */ + reg_entry->data = data; + } + /* list add register entry */ + QLIST_INSERT_HEAD(®_grp->reg_tbl_list, reg_entry, entries); + + return 0; +} + +int pt_config_init(XenPCIPassthroughState *s) +{ + XenPTRegGroup *reg_grp_entry = NULL; + uint32_t reg_grp_offset = 0; + XenPTRegInfo *reg_tbl = NULL; + int i, j, rc; + + QLIST_INIT(&s->reg_grp_tbl); + + for (i = 0; pt_emu_reg_grp_tbl[i].grp_size != 0; i++) { + if (pt_emu_reg_grp_tbl[i].grp_id != 0xFF) { + if (pt_hide_dev_cap(s->real_device, + pt_emu_reg_grp_tbl[i].grp_id)) { + continue; + } + + reg_grp_offset = find_cap_offset(s, pt_emu_reg_grp_tbl[i].grp_id); + + if (!reg_grp_offset) { + continue; + } + } + + reg_grp_entry = g_new0(XenPTRegGroup, 1); + QLIST_INIT(®_grp_entry->reg_tbl_list); + QLIST_INSERT_HEAD(&s->reg_grp_tbl, reg_grp_entry, entries); + + reg_grp_entry->base_offset = reg_grp_offset; + reg_grp_entry->reg_grp = pt_emu_reg_grp_tbl + i; + if (pt_emu_reg_grp_tbl[i].size_init) { + /* get register group size */ + rc = pt_emu_reg_grp_tbl[i].size_init(s, reg_grp_entry->reg_grp, + reg_grp_offset, + ®_grp_entry->size); + if (rc < 0) { + pt_config_delete(s); + return rc; + } + } + + if (pt_emu_reg_grp_tbl[i].grp_type == GRP_TYPE_EMU) { + if (pt_emu_reg_grp_tbl[i].emu_reg_tbl) { + reg_tbl = pt_emu_reg_grp_tbl[i].emu_reg_tbl; + /* initialize capability register */ + for (j = 0; reg_tbl->size != 0; j++, reg_tbl++) { + /* initialize capability register */ + rc = pt_config_reg_init(s, reg_grp_entry, reg_tbl); + if (rc < 0) { + pt_config_delete(s); + return rc; + } + } + } + } + reg_grp_offset = 0; + } + + return 0; +} + +/* delete all emulate register */ +void pt_config_delete(XenPCIPassthroughState *s) +{ + struct XenPTRegGroup *reg_group, *next_grp; + struct XenPTReg *reg, *next_reg; + + /* free Power Management info table */ + if (s->pm_state) { + if (s->pm_state->pm_timer) { + qemu_del_timer(s->pm_state->pm_timer); + qemu_free_timer(s->pm_state->pm_timer); + s->pm_state->pm_timer = NULL; + } + + g_free(s->pm_state); + } + + /* free all register group entry */ + QLIST_FOREACH_SAFE(reg_group, &s->reg_grp_tbl, entries, next_grp) { + /* free all register entry */ + QLIST_FOREACH_SAFE(reg, ®_group->reg_tbl_list, entries, next_reg) { + QLIST_REMOVE(reg, entries); + g_free(reg); + } + + QLIST_REMOVE(reg_group, entries); + g_free(reg_group); + } +} -- Anthony PERARD
This patch move the msi definition from apic.c to apic-msidef.h. So it can be used also by other .c files. Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> Cc: Michael S. Tsirkin <mst@redhat.com> --- hw/apic-msidef.h | 28 ++++++++++++++++++++++++++++ hw/apic.c | 11 +---------- 2 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 hw/apic-msidef.h diff --git a/hw/apic-msidef.h b/hw/apic-msidef.h new file mode 100644 index 0000000..3182f0b --- /dev/null +++ b/hw/apic-msidef.h @@ -0,0 +1,28 @@ +#ifndef HW_APIC_MSIDEF_H +#define HW_APIC_MSIDEF_H + +/* + * Intel APIC constants: from include/asm/msidef.h + */ + +/* + * Shifts for MSI data + */ + +#define MSI_DATA_VECTOR_SHIFT 0 +#define MSI_DATA_VECTOR_MASK 0x000000ff + +#define MSI_DATA_DELIVERY_MODE_SHIFT 8 +#define MSI_DATA_LEVEL_SHIFT 14 +#define MSI_DATA_TRIGGER_SHIFT 15 + +/* + * Shift/mask fields for msi address + */ + +#define MSI_ADDR_DEST_MODE_SHIFT 2 + +#define MSI_ADDR_DEST_ID_SHIFT 12 +#define MSI_ADDR_DEST_ID_MASK 0x00ffff0 + +#endif /* HW_APIC_MSIDEF_H */ diff --git a/hw/apic.c b/hw/apic.c index 8289eef..18c4a87 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -24,6 +24,7 @@ #include "sysbus.h" #include "trace.h" #include "pc.h" +#include "apic-msidef.h" /* APIC Local Vector Table */ #define APIC_LVT_TIMER 0 @@ -65,16 +66,6 @@ #define MAX_APICS 255 #define MAX_APIC_WORDS 8 -/* Intel APIC constants: from include/asm/msidef.h */ -#define MSI_DATA_VECTOR_SHIFT 0 -#define MSI_DATA_VECTOR_MASK 0x000000ff -#define MSI_DATA_DELIVERY_MODE_SHIFT 8 -#define MSI_DATA_TRIGGER_SHIFT 15 -#define MSI_DATA_LEVEL_SHIFT 14 -#define MSI_ADDR_DEST_MODE_SHIFT 2 -#define MSI_ADDR_DEST_ID_SHIFT 12 -#define MSI_ADDR_DEST_ID_MASK 0x00ffff0 - #define MSI_ADDR_SIZE 0x100000 typedef struct APICState APICState; -- Anthony PERARD
Anthony PERARD
2011-Nov-24 17:44 UTC
[PATCH V5 10/10] Introduce Xen PCI Passthrough, MSI (3/3)
From: Jiang Yunhong <yunhong.jiang@intel.com> A more complete history can be found here: git://xenbits.xensource.com/qemu-xen-unstable.git Signed-off-by: Jiang Yunhong <yunhong.jiang@intel.com> Signed-off-by: Shan Haitao <haitao.shan@intel.com> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> --- Makefile.target | 1 + hw/apic-msidef.h | 2 + hw/xen_pci_passthrough.c | 60 +++- hw/xen_pci_passthrough.h | 55 +++ hw/xen_pci_passthrough_config_init.c | 505 +++++++++++++++++++++++++- hw/xen_pci_passthrough_msi.c | 678 ++++++++++++++++++++++++++++++++++ 6 files changed, 1294 insertions(+), 7 deletions(-) create mode 100644 hw/xen_pci_passthrough_msi.c diff --git a/Makefile.target b/Makefile.target index 33435a3..81cff70 100644 --- a/Makefile.target +++ b/Makefile.target @@ -223,6 +223,7 @@ obj-i386-$(CONFIG_XEN) += xen_platform.o obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += host-pci-device.o obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough.o obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough_config_init.o +obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough_msi.o # Inter-VM PCI shared memory CONFIG_IVSHMEM diff --git a/hw/apic-msidef.h b/hw/apic-msidef.h index 3182f0b..6e2eb71 100644 --- a/hw/apic-msidef.h +++ b/hw/apic-msidef.h @@ -22,6 +22,8 @@ #define MSI_ADDR_DEST_MODE_SHIFT 2 +#define MSI_ADDR_REDIRECTION_SHIFT 3 + #define MSI_ADDR_DEST_ID_SHIFT 12 #define MSI_ADDR_DEST_ID_MASK 0x00ffff0 diff --git a/hw/xen_pci_passthrough.c b/hw/xen_pci_passthrough.c index c816ed5..cd7e3c7 100644 --- a/hw/xen_pci_passthrough.c +++ b/hw/xen_pci_passthrough.c @@ -38,6 +38,39 @@ * Write ''1'' * <ptdev->msi_trans_en is false> * - Set real bit to ''1''. + * + * MSI-INTx translation. + * Initialize(xc_physdev_map_pirq_msi/pt_msi_setup) + * Bind MSI-INTx(xc_domain_bind_pt_irq) + * <fail> + * - Unmap MSI. + * <success> + * - Set dev->msi->pirq to ''-1''. + * <fail> + * - Do nothing. + * + * Write to Interrupt Disable bit by guest software(pt_cmd_reg_write) + * Write ''0'' + * <ptdev->msi_trans_en is true> + * - Set MSI Enable bit to ''1''. + * + * Write ''1'' + * <ptdev->msi_trans_en is true> + * - Set MSI Enable bit to ''0''. + * + * MSI interrupt: + * Initialize MSI register(pt_msi_setup, pt_msi_update) + * Bind MSI(xc_domain_update_msi_irq) + * <fail> + * - Unmap MSI. + * - Set dev->msi->pirq to ''-1''. + * + * MSI-X interrupt: + * Initialize MSI-X register(pt_msix_update_one) + * Bind MSI-X(xc_domain_update_msi_irq) + * <fail> + * - Unmap MSI-X. + * - Set entry->pirq to ''-1''. */ #include <sys/ioctl.h> @@ -389,6 +422,7 @@ static void pt_iomem_map(XenPCIPassthroughState *s, int i, } if (!first_map && old_ebase != PT_PCI_BAR_UNMAPPED) { + pt_add_msix_mapping(s, i); /* Remove old mapping */ memory_region_del_subregion(r->address_space, r->memory); @@ -417,6 +451,15 @@ static void pt_iomem_map(XenPCIPassthroughState *s, int i, if (ret != 0) { PT_ERR(&s->dev, "create new mapping failed!\n"); } + + ret = pt_remove_msix_mapping(s, i); + if (ret != 0) { + PT_ERR(&s->dev, "Remove MSI-X MMIO mapping failed!\n"); + } + + if (old_ebase != e_phys && old_ebase != -1) { + pt_msix_update_remap(s, i); + } } } @@ -744,6 +787,9 @@ static int pt_initfn(PCIDevice *pcidev) mapped_machine_irq[machine_irq]++; } + /* setup MSI-INTx translation if support */ + rc = pt_enable_msi_translate(s); + /* bind machine_irq to device */ if (rc < 0 && machine_irq != 0) { uint8_t e_device = PCI_SLOT(s->dev.devfn); @@ -773,7 +819,8 @@ static int pt_initfn(PCIDevice *pcidev) out: PT_LOG(pcidev, "Real physical device %02x:%02x.%x registered successfuly!" - "\nIRQ type = %s\n", bus, slot, func, "INTx"); + "\nIRQ type = %s\n", bus, slot, func, + s->msi_trans_en ? "MSI-INTx" : "INTx"); return 0; } @@ -790,7 +837,7 @@ static int pt_unregister_device(PCIDevice *pcidev) e_intx = pci_intx(s); machine_irq = s->machine_irq; - if (machine_irq) { + if (s->msi_trans_en == 0 && machine_irq) { rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq, PT_IRQ_TYPE_PCI, 0, e_device, e_intx, 0); if (rc < 0) { @@ -798,6 +845,13 @@ static int pt_unregister_device(PCIDevice *pcidev) } } + if (s->msi) { + pt_msi_disable(s); + } + if (s->msix) { + pt_msix_disable(s); + } + if (machine_irq) { mapped_machine_irq[machine_irq]--; @@ -832,6 +886,8 @@ static PCIDeviceInfo xen_pci_passthrough = { .is_express = 0, .qdev.props = (Property[]) { DEFINE_PROP_STRING("hostaddr", XenPCIPassthroughState, hostaddr), + DEFINE_PROP_BIT("msitranslate", XenPCIPassthroughState, msi_trans_cap, + 0, true), DEFINE_PROP_BIT("power-mgmt", XenPCIPassthroughState, power_mgmt, 0, false), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/xen_pci_passthrough.h b/hw/xen_pci_passthrough.h index 110325c..acbbab5 100644 --- a/hw/xen_pci_passthrough.h +++ b/hw/xen_pci_passthrough.h @@ -74,6 +74,10 @@ typedef int (*conf_byte_restore) #define PT_PCI_BAR_UNMAPPED (-1) #define PT_UNASSIGNED_PIRQ (-1) +/* MSI-X */ +#define PT_MSI_FLAG_UNINIT 0x1000 +#define PT_MSI_FLAG_MAPPED 0x2000 + typedef enum { GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */ @@ -177,6 +181,34 @@ typedef struct XenPTRegGroup { } XenPTRegGroup; +typedef struct XenPTMSI { + uint32_t flags; + uint32_t ctrl_offset; /* saved control offset */ + int pirq; /* guest pirq corresponding */ + uint32_t addr_lo; /* guest message address */ + uint32_t addr_hi; /* guest message upper address */ + uint16_t data; /* guest message data */ +} XenPTMSI; + +typedef struct XenMSIXEntry { + int pirq; /* -1 means unmapped */ + int flags; /* flags indicting whether MSI ADDR or DATA is updated */ + uint32_t io_mem[4]; +} XenMSIXEntry; +typedef struct XenPTMSIX { + uint32_t ctrl_offset; + int enabled; + int total_entries; + int bar_index; + uint64_t table_base; + uint32_t table_off; + uint32_t table_offset_adjust; /* page align mmap */ + uint64_t mmio_base_addr; + int mmio_index; + void *phys_iomem_base; + XenMSIXEntry msix_entry[0]; +} XenPTMSIX; + typedef struct XenPTPM { QEMUTimer *pm_timer; /* QEMUTimer struct */ int no_soft_reset; /* No Soft Reset flags */ @@ -200,6 +232,13 @@ struct XenPCIPassthroughState { uint32_t machine_irq; + XenPTMSI *msi; + XenPTMSIX *msix; + + /* Physical MSI to guest INTx translation when possible */ + uint32_t msi_trans_cap; + bool msi_trans_en; + uint32_t power_mgmt; XenPTPM *pm_state; @@ -279,4 +318,20 @@ static inline uint8_t pci_intx(XenPCIPassthroughState *s) return r_val; } +/* MSI/MSI-X */ +void pt_msi_set_enable(XenPCIPassthroughState *s, int en); +int pt_msi_setup(XenPCIPassthroughState *s); +int pt_msi_update(XenPCIPassthroughState *d); +void pt_msi_disable(XenPCIPassthroughState *s); +int pt_enable_msi_translate(XenPCIPassthroughState *s); +void pt_disable_msi_translate(XenPCIPassthroughState *s); + +int pt_msix_init(XenPCIPassthroughState *s, int pos); +void pt_msix_delete(XenPCIPassthroughState *s); +int pt_msix_update(XenPCIPassthroughState *s); +int pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index); +void pt_msix_disable(XenPCIPassthroughState *s); +int pt_add_msix_mapping(XenPCIPassthroughState *s, int bar_index); +int pt_remove_msix_mapping(XenPCIPassthroughState *s, int bar_index); + #endif /* !QEMU_HW_XEN_PCI_PASSTHROUGH_H */ diff --git a/hw/xen_pci_passthrough_config_init.c b/hw/xen_pci_passthrough_config_init.c index ae64544..28523f1 100644 --- a/hw/xen_pci_passthrough_config_init.c +++ b/hw/xen_pci_passthrough_config_init.c @@ -398,11 +398,19 @@ static int pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, throughable_mask = ~emu_mask & valid_mask; if (*value & PCI_COMMAND_INTX_DISABLE) { - throughable_mask |= PCI_COMMAND_INTX_DISABLE; - } else { - if (s->machine_irq) { + if (s->msi_trans_en) { + pt_msi_set_enable(s, 0); + } else { throughable_mask |= PCI_COMMAND_INTX_DISABLE; } + } else { + if (s->msi_trans_en) { + pt_msi_set_enable(s, 1); + } else { + if (s->machine_irq) { + throughable_mask |= PCI_COMMAND_INTX_DISABLE; + } + } } *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); @@ -1282,13 +1290,21 @@ static void pt_reset_interrupt_and_io_mapping(XenPCIPassthroughState *s) e_device = PCI_SLOT(s->dev.devfn); e_intx = pci_intx(s); - if (s->machine_irq) { + if (s->msi_trans_en == 0 && s->machine_irq) { if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, s->machine_irq, PT_IRQ_TYPE_PCI, 0, e_device, e_intx, 0)) { PT_ERR(d, "Unbinding of interrupt failed!\n"); } } + /* disable MSI/MSI-X and MSI-INTx translation */ + if (s->msi) { + pt_msi_disable(s); + } + if (s->msix) { + pt_msix_disable(s); + } + /* clear all virtual region address */ for (i = 0; i < PCI_NUM_REGIONS; i++) { r = &d->io_regions[i]; @@ -1536,6 +1552,415 @@ static XenPTRegInfo pt_emu_reg_pm_tbl[] = { }, }; +/******************************** + * MSI Capability + */ + +/* Message Control register */ +static int pt_msgctrl_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + PCIDevice *d = &s->dev; + uint16_t reg_field = 0; + + /* use I/O device register''s value as initial value */ + reg_field = pci_get_word(d->config + real_offset); + + if (reg_field & PCI_MSI_FLAGS_ENABLE) { + PT_LOG(&s->dev, "MSI already enabled, disable it first\n"); + host_pci_set_word(s->real_device, real_offset, + reg_field & ~PCI_MSI_FLAGS_ENABLE); + } + s->msi->flags |= reg_field | PT_MSI_FLAG_UNINIT; + s->msi->ctrl_offset = real_offset; + + *data = reg->init_val; + return 0; +} +static int pt_msgctrl_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *value, uint16_t dev_value, + uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + uint16_t val; + + /* Currently no support for multi-vector */ + if (*value & PCI_MSI_FLAGS_QSIZE) { + PT_WARN(&s->dev, "try to set more than 1 vector ctrl %x\n", *value); + } + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + /* update the msi_info too */ + s->msi->flags |= cfg_entry->data & + ~(PT_MSI_FLAG_UNINIT | PT_MSI_FLAG_MAPPED | PCI_MSI_FLAGS_ENABLE); + + /* create value for writing to I/O device register */ + val = *value; + throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + /* update MSI */ + if (val & PCI_MSI_FLAGS_ENABLE) { + /* setup MSI pirq for the first time */ + if (s->msi->flags & PT_MSI_FLAG_UNINIT) { + if (s->msi_trans_en) { + PT_LOG(&s->dev, + "guest enabling MSI, disable MSI-INTx translation\n"); + pt_disable_msi_translate(s); + } else { + /* Init physical one */ + PT_LOG(&s->dev, "setup MSI\n"); + if (pt_msi_setup(s)) { + /* We do not broadcast the error to the framework code, so + * that MSI errors are contained in MSI emulation code and + * QEMU can go on running. + * Guest MSI would be actually not working. + */ + *value &= ~PCI_MSI_FLAGS_ENABLE; + PT_WARN(&s->dev, "Can not map MSI.\n"); + return 0; + } + } + if (pt_msi_update(s)) { + *value &= ~PCI_MSI_FLAGS_ENABLE; + PT_WARN(&s->dev, "Can not bind MSI\n"); + return 0; + } + s->msi->flags &= ~PT_MSI_FLAG_UNINIT; + s->msi->flags |= PT_MSI_FLAG_MAPPED; + } + s->msi->flags |= PCI_MSI_FLAGS_ENABLE; + } else { + s->msi->flags &= ~PCI_MSI_FLAGS_ENABLE; + } + + /* pass through MSI_ENABLE bit when no MSI-INTx translation */ + if (!s->msi_trans_en) { + *value &= ~PCI_MSI_FLAGS_ENABLE; + *value |= val & PCI_MSI_FLAGS_ENABLE; + } + + return 0; +} + +/* initialize Message Upper Address register */ +static int pt_msgaddr64_reg_init(XenPCIPassthroughState *ptdev, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + /* no need to initialize in case of 32 bit type */ + if (!(ptdev->msi->flags & PCI_MSI_FLAGS_64BIT)) { + *data = PT_INVALID_REG; + } else { + *data = reg->init_val; + } + + return 0; +} +/* this function will be called twice (for 32 bit and 64 bit type) */ +/* initialize Message Data register */ +static int pt_msgdata_reg_init(XenPCIPassthroughState *ptdev, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + uint32_t flags = ptdev->msi->flags; + uint32_t offset = reg->offset; + + /* check the offset whether matches the type or not */ + if (((offset == PCI_MSI_DATA_64) && (flags & PCI_MSI_FLAGS_64BIT)) || + ((offset == PCI_MSI_DATA_32) && !(flags & PCI_MSI_FLAGS_64BIT))) { + *data = reg->init_val; + } else { + *data = PT_INVALID_REG; + } + return 0; +} + +/* write Message Address register */ +static int pt_msgaddr32_reg_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint32_t *value, + uint32_t dev_value, uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t writable_mask = 0; + uint32_t throughable_mask = 0; + uint32_t old_addr = cfg_entry->data; + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + /* update the msi_info too */ + s->msi->addr_lo = cfg_entry->data; + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + /* update MSI */ + if (cfg_entry->data != old_addr) { + if (s->msi->flags & PT_MSI_FLAG_MAPPED) { + pt_msi_update(s); + } + } + + return 0; +} +/* write Message Upper Address register */ +static int pt_msgaddr64_reg_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint32_t *value, + uint32_t dev_value, uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t writable_mask = 0; + uint32_t throughable_mask = 0; + uint32_t old_addr = cfg_entry->data; + + /* check whether the type is 64 bit or not */ + if (!(s->msi->flags & PCI_MSI_FLAGS_64BIT)) { + PT_ERR(&s->dev, "Write to the Upper Address without 64 bit support\n"); + return -1; + } + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + /* update the msi_info too */ + s->msi->addr_hi = cfg_entry->data; + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + /* update MSI */ + if (cfg_entry->data != old_addr) { + if (s->msi->flags & PT_MSI_FLAG_MAPPED) { + pt_msi_update(s); + } + } + + return 0; +} + + +/* this function will be called twice (for 32 bit and 64 bit type) */ +/* write Message Data register */ +static int pt_msgdata_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *value, uint16_t dev_value, + uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + uint16_t old_data = cfg_entry->data; + uint32_t flags = s->msi->flags; + uint32_t offset = reg->offset; + + /* check the offset whether matches the type or not */ + if (!((offset == PCI_MSI_DATA_64) && (flags & PCI_MSI_FLAGS_64BIT)) && + !((offset == PCI_MSI_DATA_32) && !(flags & PCI_MSI_FLAGS_64BIT))) { + /* exit I/O emulator */ + PT_ERR(&s->dev, "the offset does not match the 32/64 bit type!\n"); + return -1; + } + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + /* update the msi_info too */ + s->msi->data = cfg_entry->data; + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + /* update MSI */ + if (cfg_entry->data != old_data) { + if (flags & PT_MSI_FLAG_MAPPED) { + pt_msi_update(s); + } + } + + return 0; +} + +/* MSI Capability Structure reg static infomation table */ +static XenPTRegInfo pt_emu_reg_msi_tbl[] = { + /* Next Pointer reg */ + { + .offset = PCI_CAP_LIST_NEXT, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = pt_ptr_reg_init, + .u.b.read = pt_byte_reg_read, + .u.b.write = pt_byte_reg_write, + .u.b.restore = NULL, + }, + /* Message Control reg */ + { + .offset = PCI_MSI_FLAGS, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFF8E, + .emu_mask = 0x007F, + .init = pt_msgctrl_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_msgctrl_reg_write, + .u.w.restore = NULL, + }, + /* Message Address reg */ + { + .offset = PCI_MSI_ADDRESS_LO, + .size = 4, + .init_val = 0x00000000, + .ro_mask = 0x00000003, + .emu_mask = 0xFFFFFFFF, + .no_wb = 1, + .init = pt_common_reg_init, + .u.dw.read = pt_long_reg_read, + .u.dw.write = pt_msgaddr32_reg_write, + .u.dw.restore = NULL, + }, + /* Message Upper Address reg (if PCI_MSI_FLAGS_64BIT set) */ + { + .offset = PCI_MSI_ADDRESS_HI, + .size = 4, + .init_val = 0x00000000, + .ro_mask = 0x00000000, + .emu_mask = 0xFFFFFFFF, + .no_wb = 1, + .init = pt_msgaddr64_reg_init, + .u.dw.read = pt_long_reg_read, + .u.dw.write = pt_msgaddr64_reg_write, + .u.dw.restore = NULL, + }, + /* Message Data reg (16 bits of data for 32-bit devices) */ + { + .offset = PCI_MSI_DATA_32, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0x0000, + .emu_mask = 0xFFFF, + .no_wb = 1, + .init = pt_msgdata_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_msgdata_reg_write, + .u.w.restore = NULL, + }, + /* Message Data reg (16 bits of data for 64-bit devices) */ + { + .offset = PCI_MSI_DATA_64, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0x0000, + .emu_mask = 0xFFFF, + .no_wb = 1, + .init = pt_msgdata_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_msgdata_reg_write, + .u.w.restore = NULL, + }, + { + .size = 0, + }, +}; + + +/************************************** + * MSI-X Capability + */ + +/* Message Control register for MSI-X */ +static int pt_msixctrl_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + PCIDevice *d = &s->dev; + uint16_t reg_field = 0; + + /* use I/O device register''s value as initial value */ + reg_field = pci_get_word(d->config + real_offset); + + if (reg_field & PCI_MSIX_FLAGS_ENABLE) { + PT_LOG(d, "MSIX already enabled, disable it first\n"); + host_pci_set_word(s->real_device, real_offset, + reg_field & ~PCI_MSIX_FLAGS_ENABLE); + } + + s->msix->ctrl_offset = real_offset; + + *data = reg->init_val; + return 0; +} +static int pt_msixctrl_reg_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint16_t *value, + uint16_t dev_value, uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + /* update MSI-X */ + if ((*value & PCI_MSIX_FLAGS_ENABLE) + && !(*value & PCI_MSIX_FLAGS_MASKALL)) { + if (s->msi_trans_en) { + PT_LOG(&s->dev, + "guest enabling MSI-X, disable MSI-INTx translation\n"); + pt_disable_msi_translate(s); + } + pt_msix_update(s); + } + + s->msix->enabled = !!(*value & PCI_MSIX_FLAGS_ENABLE); + + return 0; +} + +/* MSI-X Capability Structure reg static infomation table */ +static XenPTRegInfo pt_emu_reg_msix_tbl[] = { + /* Next Pointer reg */ + { + .offset = PCI_CAP_LIST_NEXT, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = pt_ptr_reg_init, + .u.b.read = pt_byte_reg_read, + .u.b.write = pt_byte_reg_write, + .u.b.restore = NULL, + }, + /* Message Control reg */ + { + .offset = PCI_MSI_FLAGS, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0x3FFF, + .emu_mask = 0x0000, + .init = pt_msixctrl_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_msixctrl_reg_write, + .u.w.restore = NULL, + }, + { + .size = 0, + }, +}; + /**************************** * Capabilities @@ -1709,6 +2134,49 @@ static int pt_pcie_size_init(XenPCIPassthroughState *s, *size = pcie_size; return 0; } +/* get MSI Capability Structure register group size */ +static int pt_msi_size_init(XenPCIPassthroughState *s, + const XenPTRegGroupInfo *grp_reg, + uint32_t base_offset, uint8_t *size) +{ + PCIDevice *d = &s->dev; + uint16_t msg_ctrl = 0; + uint8_t msi_size = 0xa; + + msg_ctrl = pci_get_word(d->config + (base_offset + PCI_MSI_FLAGS)); + + /* check 64 bit address capable & Per-vector masking capable */ + if (msg_ctrl & PCI_MSI_FLAGS_64BIT) { + msi_size += 4; + } + if (msg_ctrl & PCI_MSI_FLAGS_MASKBIT) { + msi_size += 10; + } + + s->msi = g_new0(XenPTMSI, 1); + s->msi->pirq = PT_UNASSIGNED_PIRQ; + + *size = msi_size; + return 0; +} +/* get MSI-X Capability Structure register group size */ +static int pt_msix_size_init(XenPCIPassthroughState *s, + const XenPTRegGroupInfo *grp_reg, + uint32_t base_offset, uint8_t *size) +{ + int rc = 0; + + rc = pt_msix_init(s, base_offset); + + if (rc == -1) { + PT_ERR(&s->dev, "Internal error: Invalid pt_msix_init.\n"); + return rc; + } + + *size = grp_reg->grp_size; + return 0; +} + static const XenPTRegGroupInfo pt_emu_reg_grp_tbl[] = { /* Header Type0 reg group */ @@ -1749,6 +2217,14 @@ static const XenPTRegGroupInfo pt_emu_reg_grp_tbl[] = { .grp_size = 0x04, .size_init = pt_reg_grp_size_init, }, + /* MSI Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_MSI, + .grp_type = GRP_TYPE_EMU, + .grp_size = 0xFF, + .size_init = pt_msi_size_init, + .emu_reg_tbl = pt_emu_reg_msi_tbl, + }, /* PCI-X Capabilities List Item reg group */ { .grp_id = PCI_CAP_ID_PCIX, @@ -1793,6 +2269,14 @@ static const XenPTRegGroupInfo pt_emu_reg_grp_tbl[] = { .size_init = pt_pcie_size_init, .emu_reg_tbl = pt_emu_reg_pcie_tbl, }, + /* MSI-X Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_MSIX, + .grp_type = GRP_TYPE_EMU, + .grp_size = 0x0C, + .size_init = pt_msix_size_init, + .emu_reg_tbl = pt_emu_reg_msix_tbl, + }, { .grp_size = 0, }, @@ -1965,8 +2449,11 @@ static int pt_init_pci_config(XenPCIPassthroughState *s) return rc; } + /* setup MSI-INTx translation if support */ + rc = pt_enable_msi_translate(s); + /* rebind machine_irq to device */ - if (s->machine_irq != 0) { + if (rc < 0 && s->machine_irq != 0) { uint8_t e_device = PCI_SLOT(s->dev.devfn); uint8_t e_intx = pci_intx(s); @@ -2117,6 +2604,14 @@ void pt_config_delete(XenPCIPassthroughState *s) struct XenPTRegGroup *reg_group, *next_grp; struct XenPTReg *reg, *next_reg; + /* free MSI/MSI-X info table */ + if (s->msix) { + pt_msix_delete(s); + } + if (s->msi) { + g_free(s->msi); + } + /* free Power Management info table */ if (s->pm_state) { if (s->pm_state->pm_timer) { diff --git a/hw/xen_pci_passthrough_msi.c b/hw/xen_pci_passthrough_msi.c new file mode 100644 index 0000000..73d3d9b --- /dev/null +++ b/hw/xen_pci_passthrough_msi.c @@ -0,0 +1,678 @@ +/* + * Copyright (c) 2007, Intel Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Jiang Yunhong <yunhong.jiang@intel.com> + * + * This file implements direct PCI assignment to a HVM guest + */ + +#include <sys/mman.h> + +#include "xen_backend.h" +#include "xen_pci_passthrough.h" +#include "apic-msidef.h" + + +#define AUTO_ASSIGN -1 + +/* shift count for gflags */ +#define GFLAGS_SHIFT_DEST_ID 0 +#define GFLAGS_SHIFT_RH 8 +#define GFLAGS_SHIFT_DM 9 +#define GLFAGS_SHIFT_DELIV_MODE 12 +#define GLFAGS_SHIFT_TRG_MODE 15 + + +/* + * Helpers + */ + +static uint32_t get_msi_gflags(uint32_t data, uint64_t addr) +{ + uint32_t result = 0; + int rh, dm, dest_id, deliv_mode, trig_mode; + + rh = (addr >> MSI_ADDR_REDIRECTION_SHIFT) & 0x1; + dm = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1; + dest_id = (addr >> MSI_ADDR_DEST_ID_SHIFT) & 0xff; + deliv_mode = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7; + trig_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; + + result = dest_id | (rh << GFLAGS_SHIFT_RH) | (dm << GFLAGS_SHIFT_DM) | + (deliv_mode << GLFAGS_SHIFT_DELIV_MODE) | + (trig_mode << GLFAGS_SHIFT_TRG_MODE); + + return result; +} + + +/* + * MSI virtualization functions + */ + +void pt_msi_set_enable(XenPCIPassthroughState *s, int en) +{ + uint16_t val = 0; + uint32_t address = 0; + + PT_LOG(&s->dev, "enable: %i\n", en); + + if (!s->msi) { + return; + } + + address = s->msi->ctrl_offset; + if (!address) { + return; + } + + host_pci_get_word(s->real_device, address, &val); + val &= ~PCI_MSI_FLAGS_ENABLE; + val |= en & PCI_MSI_FLAGS_ENABLE; + host_pci_set_word(s->real_device, address, val); +} + +/* setup physical msi, but don''t enable it */ +int pt_msi_setup(XenPCIPassthroughState *s) +{ + int pirq = PT_UNASSIGNED_PIRQ; + uint8_t gvec = 0; + int rc = 0; + + if (!(s->msi->flags & PT_MSI_FLAG_UNINIT)) { + PT_ERR(&s->dev, "Setup physical MSI when it''s already initialized.\n"); + return -1; + } + + gvec = s->msi->data & 0xFF; + if (!gvec) { + /* if gvec is 0, the guest is asking for a particular pirq that + * is passed as dest_id */ + pirq = (s->msi->addr_hi & 0xffffff00) | + ((s->msi->addr_lo >> MSI_ADDR_DEST_ID_SHIFT) & 0xff); + if (!pirq) { + /* this probably identifies an misconfiguration of the guest, + * try the emulated path */ + pirq = PT_UNASSIGNED_PIRQ; + } else { + PT_LOG(&s->dev, "requested pirq: %d\n", pirq); + } + } + + rc = xc_physdev_map_pirq_msi(xen_xc, xen_domid, AUTO_ASSIGN, &pirq, + PCI_DEVFN(s->real_device->dev, + s->real_device->func), + s->real_device->bus, 0, 0); + if (rc) { + PT_ERR(&s->dev, "Mapping of MSI failed. (rc: %d," + " assigned device: %02x:%02x.%x)\n", rc, + s->real_device->dev, s->real_device->func, s->real_device->bus); + return -1; + } + + if (pirq < 0) { + PT_ERR(&s->dev, "Invalid pirq number.\n"); + return -1; + } + + s->msi->pirq = pirq; + PT_LOG(&s->dev, "MSI mapped with pirq %d.\n", pirq); + + return 0; +} + +int pt_msi_update(XenPCIPassthroughState *s) +{ + uint8_t gvec = 0; + uint32_t gflags = 0; + uint64_t addr = 0; + int rc = 0; + + /* get vector, address, flags info, etc. */ + gvec = s->msi->data & 0xFF; + addr = (uint64_t)s->msi->addr_hi << 32 | s->msi->addr_lo; + gflags = get_msi_gflags(s->msi->data, addr); + + PT_LOG(&s->dev, "Update MSI with pirq %d gvec 0x%x gflags 0x%x\n", + s->msi->pirq, gvec, gflags); + + rc = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec, + s->msi->pirq, gflags, 0); + if (rc) { + PT_ERR(&s->dev, "Binding of MSI failed. (rc: %d)\n", rc); + + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, s->msi->pirq)) { + PT_ERR(&s->dev, "Unmapping of MSI pirq %d failed.\n", + s->msi->pirq); + } + s->msi->pirq = PT_UNASSIGNED_PIRQ; + } + return rc; +} + +void pt_msi_disable(XenPCIPassthroughState *s) +{ + PCIDevice *d = &s->dev; + uint8_t gvec = 0; + uint32_t gflags = 0; + uint64_t addr = 0; + uint8_t e_device = 0; + uint8_t e_intx = 0; + + pt_msi_set_enable(s, 0); + + e_device = PCI_SLOT(d->devfn); + e_intx = pci_intx(s); + + if (s->msi_trans_en) { + if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, s->msi->pirq, + PT_IRQ_TYPE_MSI_TRANSLATE, 0, + e_device, e_intx, 0)) { + PT_ERR(d, "Unbinding pt irq for MSI-INTx failed!\n"); + goto out; + } + } else if (!(s->msi->flags & PT_MSI_FLAG_UNINIT)) { + /* get vector, address, flags info, etc. */ + gvec = s->msi->data & 0xFF; + addr = (uint64_t)s->msi->addr_hi << 32 | s->msi->addr_lo; + gflags = get_msi_gflags(s->msi->data, addr); + + PT_ERR(&s->dev, "Unbind MSI with pirq %x, gvec %x\n", + s->msi->pirq, gvec); + + if (xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec, + s->msi->pirq, gflags)) { + PT_ERR(&s->dev, "Unbinding of MSI failed. (pirq: %d)\n", + s->msi->pirq); + goto out; + } + } + + if (s->msi->pirq != PT_UNASSIGNED_PIRQ) { + PT_LOG(&s->dev, "Unmap msi with pirq %d\n", s->msi->pirq); + + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, s->msi->pirq)) { + PT_ERR(&s->dev, "Unmapping of MSI failed. (pirq: %d)\n", + s->msi->pirq); + goto out; + } + } + +out: + /* clear msi info */ + s->msi->flags = 0; + s->msi->pirq = -1; + s->msi_trans_en = 0; +} + +/* + * MSI-INTx translation virtualization functions + */ + +int pt_enable_msi_translate(XenPCIPassthroughState *s) +{ + uint8_t e_device = 0; + uint8_t e_intx = 0; + + if (!(s->msi && s->msi_trans_cap)) { + return -1; + } + + pt_msi_set_enable(s, 0); + s->msi_trans_en = 0; + + if (pt_msi_setup(s)) { + PT_ERR(&s->dev, "MSI-INTx translation MSI setup failed, fallback\n"); + return -1; + } + + e_device = PCI_SLOT(s->dev.devfn); + /* fix virtual interrupt pin to INTA# */ + e_intx = pci_intx(s); + + if (xc_domain_bind_pt_irq(xen_xc, xen_domid, s->msi->pirq, + PT_IRQ_TYPE_MSI_TRANSLATE, 0, + e_device, e_intx, 0)) { + PT_ERR(&s->dev, "MSI-INTx translation bind failed, fallback\n"); + + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, s->msi->pirq)) { + PT_ERR(&s->dev, "Unmapping of MSI failed.\n"); + } + s->msi->pirq = PT_UNASSIGNED_PIRQ; + return -1; + } + + pt_msi_set_enable(s, 1); + s->msi_trans_en = 1; + + return 0; +} + +void pt_disable_msi_translate(XenPCIPassthroughState *s) +{ + uint8_t e_device = 0; + uint8_t e_intx = 0; + + /* MSI_ENABLE bit should be disabed until the new handler is set */ + pt_msi_set_enable(s, 0); + + e_device = PCI_SLOT(s->dev.devfn); + e_intx = pci_intx(s); + + if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, s->msi->pirq, + PT_IRQ_TYPE_MSI_TRANSLATE, 0, + e_device, e_intx, 0)) { + PT_ERR(&s->dev, "Unbinding pt irq for MSI-INTx failed!\n"); + } + + if (s->machine_irq) { + if (xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, s->machine_irq, + 0, e_device, e_intx)) { + PT_ERR(&s->dev, "Rebinding of interrupt failed!\n"); + } + } + + s->msi_trans_en = 0; +} + +/* + * MSI-X virtualization functions + */ + +static void msix_set_enable(XenPCIPassthroughState *s, int en) +{ + uint16_t val = 0; + uint32_t address = 0; + + if (!s->msix) { + return; + } + + address = s->msix->ctrl_offset; + if (!address) { + return; + } + + host_pci_get_word(s->real_device, address, &val); + val &= ~PCI_MSIX_FLAGS_ENABLE; + if (en) { + val |= PCI_MSIX_FLAGS_ENABLE; + } + host_pci_set_word(s->real_device, address, val); +} + +static void mask_physical_msix_entry(XenPCIPassthroughState *s, + int entry_nr, int mask) +{ + void *phys_off; + + phys_off = s->msix->phys_iomem_base + 16 * entry_nr + 12; + *(uint32_t *)phys_off = mask; +} + +static int pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr) +{ + XenMSIXEntry *entry = &s->msix->msix_entry[entry_nr]; + int pirq = entry->pirq; + int gvec = entry->io_mem[2] & 0xff; + uint64_t gaddr = *(uint64_t *)&entry->io_mem[0]; + uint32_t gflags = get_msi_gflags(entry->io_mem[2], gaddr); + int rc; + + if (!entry->flags) { + return 0; + } + + if (!gvec) { + /* if gvec is 0, the guest is asking for a particular pirq that + * is passed as dest_id */ + pirq = ((gaddr >> 32) & 0xffffff00) | + (((gaddr & 0xffffffff) >> MSI_ADDR_DEST_ID_SHIFT) & 0xff); + if (!pirq) { + /* this probably identifies an misconfiguration of the guest, + * try the emulated path */ + pirq = PT_UNASSIGNED_PIRQ; + } else { + PT_LOG(&s->dev, "requested pirq: %d\n", pirq); + } + } + + /* Check if this entry is already mapped */ + if (entry->pirq == PT_UNASSIGNED_PIRQ) { + rc = xc_physdev_map_pirq_msi(xen_xc, xen_domid, AUTO_ASSIGN, &pirq, + PCI_DEVFN(s->real_device->dev, + s->real_device->func), + s->real_device->bus, entry_nr, + s->msix->table_base); + if (rc) { + PT_ERR(&s->dev, "Mapping msix entry %x\n", entry_nr); + return rc; + } + entry->pirq = pirq; + } + + PT_LOG(&s->dev, "Update msix entry 0x%x with pirq %d gvec 0x%x\n", + entry_nr, pirq, gvec); + + rc = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec, pirq, gflags, + s->msix->mmio_base_addr); + if (rc) { + PT_ERR(&s->dev, "Updating msix irq %d info for entry %d\n", + pirq, entry_nr); + + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, entry->pirq)) { + PT_ERR(&s->dev, "Unmapping of MSI-X failed. (pirq: %d)\n", + entry->pirq); + } + entry->pirq = PT_UNASSIGNED_PIRQ; + return rc; + } + + entry->flags = 0; + + return 0; +} + +int pt_msix_update(XenPCIPassthroughState *s) +{ + XenPTMSIX *msix = s->msix; + int i; + + for (i = 0; i < msix->total_entries; i++) { + pt_msix_update_one(s, i); + } + + return 0; +} + +void pt_msix_disable(XenPCIPassthroughState *s) +{ + PCIDevice *d = &s->dev; + uint8_t gvec = 0; + uint32_t gflags = 0; + uint64_t addr = 0; + int i = 0; + XenMSIXEntry *entry = NULL; + + msix_set_enable(s, 0); + + for (i = 0; i < s->msix->total_entries; i++) { + entry = &s->msix->msix_entry[i]; + + if (entry->pirq == PT_UNASSIGNED_PIRQ) { + continue; + } + + gvec = entry->io_mem[2] & 0xff; + addr = *(uint64_t *)&entry->io_mem[0]; + gflags = get_msi_gflags(entry->io_mem[2], addr); + + PT_LOG(d, "Unbind msix with pirq %d, gvec 0x%x\n", + entry->pirq, gvec); + + if (xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec, + entry->pirq, gflags)) { + PT_ERR(d, "Unbinding of MSI-X failed.\n"); + } else { + PT_LOG(d, "Unmap msix with pirq %d\n", entry->pirq); + + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, entry->pirq)) { + PT_ERR(d, "Unmapping of MSI-X failed.\n"); + } + } + /* clear msi-x info */ + entry->pirq = PT_UNASSIGNED_PIRQ; + entry->flags = 0; + } +} + +int pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index) +{ + XenMSIXEntry *entry; + int i, ret; + + if (!(s->msix && s->msix->bar_index == bar_index)) { + return 0; + } + + for (i = 0; i < s->msix->total_entries; i++) { + entry = &s->msix->msix_entry[i]; + if (entry->pirq != PT_UNASSIGNED_PIRQ) { + ret = xc_domain_unbind_pt_irq(xen_xc, xen_domid, entry->pirq, + PT_IRQ_TYPE_MSI, 0, 0, 0, 0); + if (ret) { + PT_ERR(&s->dev, "unbind MSI-X entry %d failed\n", entry->pirq); + } + entry->flags = 1; + } + } + pt_msix_update(s); + + return 0; +} + +static void pci_msix_invalid_write(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + XenPCIPassthroughState *s = opaque; + PT_ERR(&s->dev, "Invalid write to MSI-X table," + " only dword access is allowed.\n"); +} + +static void pci_msix_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + XenPCIPassthroughState *s = opaque; + XenPTMSIX *msix = s->msix; + XenMSIXEntry *entry; + int entry_nr, offset; + void *phys_off; + uint32_t vec_ctrl; + + if (addr % 4) { + PT_ERR(&s->dev, "Unaligned dword access to MSI-X table, " + "addr 0x"TARGET_FMT_plx"\n", addr); + return; + } + + entry_nr = addr / 16; + entry = &msix->msix_entry[entry_nr]; + offset = (addr % 16) / 4; + + /* + * If Xen intercepts the mask bit access, io_mem[3] may not be + * up-to-date. Read from hardware directly. + */ + phys_off = s->msix->phys_iomem_base + 16 * entry_nr + 12; + vec_ctrl = *(uint32_t *)phys_off; + + if (offset != 3 && msix->enabled && !(vec_ctrl & 0x1)) { + PT_ERR(&s->dev, "Can''t update msix entry %d since MSI-X is already " + "enabled.\n", entry_nr); + return; + } + + if (offset != 3 && entry->io_mem[offset] != val) { + entry->flags = 1; + } + entry->io_mem[offset] = val; + + if (offset == 3) { + if (msix->enabled && !(val & 0x1)) { + pt_msix_update_one(s, entry_nr); + } + mask_physical_msix_entry(s, entry_nr, entry->io_mem[3] & 0x1); + } +} + +static CPUWriteMemoryFunc *pci_msix_write[] = { + pci_msix_invalid_write, + pci_msix_invalid_write, + pci_msix_writel +}; + +static uint32_t pci_msix_invalid_read(void *opaque, target_phys_addr_t addr) +{ + XenPCIPassthroughState *s = opaque; + PT_ERR(&s->dev, "Invalid read to MSI-X table," + " only dword access is allowed.\n"); + return 0; +} + +static uint32_t pci_msix_readl(void *opaque, target_phys_addr_t addr) +{ + XenPCIPassthroughState *s = opaque; + XenPTMSIX *msix = s->msix; + int entry_nr, offset; + + if (addr % 4) { + PT_ERR(&s->dev, "Unaligned dword access to MSI-X table, " + "addr 0x"TARGET_FMT_plx"\n", addr); + return 0; + } + + entry_nr = addr / 16; + offset = (addr % 16) / 4; + + return msix->msix_entry[entry_nr].io_mem[offset]; +} + +static CPUReadMemoryFunc *pci_msix_read[] = { + pci_msix_invalid_read, + pci_msix_invalid_read, + pci_msix_readl +}; + +int pt_add_msix_mapping(XenPCIPassthroughState *s, int bar_index) +{ + if (!(s->msix && s->msix->bar_index == bar_index)) { + return 0; + } + + return xc_domain_memory_mapping(xen_xc, xen_domid, + s->msix->mmio_base_addr >> XC_PAGE_SHIFT, + (s->bases[bar_index].access.maddr + s->msix->table_off) + >> XC_PAGE_SHIFT, + (s->msix->total_entries * 16 + XC_PAGE_SIZE - 1) >> XC_PAGE_SHIFT, + DPCI_ADD_MAPPING); +} + +int pt_remove_msix_mapping(XenPCIPassthroughState *s, int bar_index) +{ + if (!(s->msix && s->msix->bar_index == bar_index)) { + return 0; + } + + s->msix->mmio_base_addr = s->bases[bar_index].e_physbase + + s->msix->table_off; + + cpu_register_physical_memory(s->msix->mmio_base_addr, + s->msix->total_entries * 16, + s->msix->mmio_index); + + return xc_domain_memory_mapping(xen_xc, xen_domid, + s->msix->mmio_base_addr >> XC_PAGE_SHIFT, + (s->bases[bar_index].access.maddr + s->msix->table_off) + >> XC_PAGE_SHIFT, + (s->msix->total_entries * 16 + XC_PAGE_SIZE - 1) >> XC_PAGE_SHIFT, + DPCI_REMOVE_MAPPING); +} + +int pt_msix_init(XenPCIPassthroughState *s, int base) +{ + uint8_t id = 0; + uint16_t control = 0; + uint32_t table_off = 0; + int i, total_entries, bar_index; + HostPCIDevice *d = s->real_device; + int fd; + + if (host_pci_get_byte(d, base + PCI_CAP_LIST_ID, &id)) { + return -1; + } + + if (id != PCI_CAP_ID_MSIX) { + PT_ERR(&s->dev, "Invalid id %#x base %#x\n", id, base); + return -1; + } + + host_pci_get_word(d, base + 2, &control); + total_entries = control & 0x7ff; + total_entries += 1; + + s->msix = g_malloc0(sizeof (XenPTMSIX) + + total_entries * sizeof (XenMSIXEntry)); + + s->msix->total_entries = total_entries; + for (i = 0; i < total_entries; i++) { + s->msix->msix_entry[i].pirq = PT_UNASSIGNED_PIRQ; + } + + s->msix->mmio_index + cpu_register_io_memory(pci_msix_read, pci_msix_write, + s, DEVICE_NATIVE_ENDIAN); + + host_pci_get_long(d, base + PCI_MSIX_TABLE, &table_off); + bar_index = s->msix->bar_index = table_off & PCI_MSIX_FLAGS_BIRMASK; + table_off = s->msix->table_off = table_off & ~PCI_MSIX_FLAGS_BIRMASK; + s->msix->table_base = s->real_device->io_regions[bar_index].base_addr; + PT_LOG(&s->dev, "get MSI-X table BAR base 0x%"PRIx64"\n", + s->msix->table_base); + + fd = open("/dev/mem", O_RDWR); + if (fd == -1) { + PT_ERR(&s->dev, "Can''t open /dev/mem: %s\n", strerror(errno)); + goto error_out; + } + PT_LOG(&s->dev, "table_off = %#x, total_entries = %d\n", + table_off, total_entries); + s->msix->table_offset_adjust = table_off & 0x0fff; + s->msix->phys_iomem_base + mmap(0, + total_entries * 16 + s->msix->table_offset_adjust, + PROT_WRITE | PROT_READ, + MAP_SHARED | MAP_LOCKED, + fd, + s->msix->table_base + table_off - s->msix->table_offset_adjust); + + if (s->msix->phys_iomem_base == MAP_FAILED) { + PT_ERR(&s->dev, "Can''t map physical MSI-X table: %s\n", + strerror(errno)); + close(fd); + goto error_out; + } + s->msix->phys_iomem_base = (char *)s->msix->phys_iomem_base + + s->msix->table_offset_adjust; + + close(fd); + + PT_LOG(&s->dev, "mapping physical MSI-X table to %p\n", + s->msix->phys_iomem_base); + return 0; + +error_out: + g_free(s->msix); + s->msix = NULL; + return -1; +} + +void pt_msix_delete(XenPCIPassthroughState *s) +{ + /* unmap the MSI-X memory mapped register area */ + if (s->msix->phys_iomem_base) { + PT_LOG(&s->dev, "unmapping physical MSI-X table from %p\n", + s->msix->phys_iomem_base); + munmap(s->msix->phys_iomem_base, s->msix->total_entries * 16 + + s->msix->table_offset_adjust); + } + + if (s->msix->mmio_index > 0) { + cpu_unregister_io_memory(s->msix->mmio_index); + } + + g_free(s->msix); + s->msix = NULL; +} -- Anthony PERARD
Konrad Rzeszutek Wilk
2011-Nov-29 20:53 UTC
Re: [Xen-devel] [PATCH V5 06/10] pci.c: Add pci_check_bar_overlap
On Thu, Nov 24, 2011 at 05:44:35PM +0000, Anthony PERARD wrote:> From: Yuji Shimada <shimada-yxb@necst.nec.co.jp> > > This function help Xen PCI Passthrough device to check for overlap.helps> > Signed-off-by: Yuji Shimada <shimada-yxb@necst.nec.co.jp> > Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> > --- > hw/pci.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ > hw/pci.h | 3 +++ > 2 files changed, 50 insertions(+), 0 deletions(-) > > diff --git a/hw/pci.c b/hw/pci.c > index 399227f..563bb37 100644 > --- a/hw/pci.c > +++ b/hw/pci.c > @@ -2038,3 +2038,50 @@ MemoryRegion *pci_address_space_io(PCIDevice *dev) > { > return dev->bus->address_space_io; > } > + > +int pci_check_bar_overlap(PCIDevice *dev, > + pcibus_t addr, pcibus_t size, uint8_t type) > +{ > + PCIBus *bus = dev->bus; > + PCIDevice *devices = NULL; > + PCIIORegion *r; > + int i, j; > + int rc = 0; > + > + /* check Overlapped to Base Address */ > + for (i = 0; i < ARRAY_SIZE(bus->devices); i++) { > + devices = bus->devices[i]; > + if (!devices) { > + continue; > + } > + > + /* skip itself */ > + if (devices->devfn == dev->devfn) { > + continue; > + } > + > + for (j = 0; j < PCI_NUM_REGIONS; j++) { > + r = &devices->io_regions[j]; > + > + /* skip different resource type, but don''t skip when > + * prefetch and non-prefetch memory are compared. > + */ > + if (type != r->type) { > + if (type == PCI_BASE_ADDRESS_SPACE_IO || > + r->type == PCI_BASE_ADDRESS_SPACE_IO) { > + continue; > + } > + } > + > + if ((addr < (r->addr + r->size)) && ((addr + size) > r->addr)) { > + printf("Overlapped to device[%02x:%02x.%x][Region:%d]" > + "[Address:%"PRIx64"h][Size:%"PRIx64"h]\n", > + pci_bus_num(bus), PCI_SLOT(devices->devfn), > + PCI_FUNC(devices->devfn), j, r->addr, r->size); > + rc = 1; > + } > + } > + } > + > + return rc; > +} > diff --git a/hw/pci.h b/hw/pci.h > index 625e717..9a723d2 100644 > --- a/hw/pci.h > +++ b/hw/pci.h > @@ -552,4 +552,7 @@ static inline void pci_dma_sglist_init(QEMUSGList *qsg, PCIDevice *dev, > qemu_sglist_init(qsg, alloc_hint); > } > > +int pci_check_bar_overlap(PCIDevice *dev, > + pcibus_t addr, pcibus_t size, uint8_t type); > + > #endif > -- > Anthony PERARD > > > _______________________________________________ > Xen-devel mailing list > Xen-devel@lists.xensource.com > http://lists.xensource.com/xen-devel
Konrad Rzeszutek Wilk
2011-Dec-01 23:18 UTC
Re: [Xen-devel] [PATCH V5 07/10] Introduce Xen PCI Passthrough, qdevice (1/3)
On Thu, Nov 24, 2011 at 05:44:36PM +0000, Anthony PERARD wrote:> From: Allen Kay <allen.m.kay@intel.com> > > A more complete history can be found here: > git://xenbits.xensource.com/qemu-xen-unstable.git > > Signed-off-by: Allen Kay <allen.m.kay@intel.com> > Signed-off-by: Guy Zana <guy@neocleus.com> > Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> > --- > Makefile.target | 2 + > hw/xen_common.h | 3 + > hw/xen_pci_passthrough.c | 831 ++++++++++++++++++++++++++++++++++ > hw/xen_pci_passthrough.h | 282 ++++++++++++ > hw/xen_pci_passthrough_config_init.c | 11 + > xen-all.c | 12 + > 6 files changed, 1141 insertions(+), 0 deletions(-) > create mode 100644 hw/xen_pci_passthrough.c > create mode 100644 hw/xen_pci_passthrough.h > create mode 100644 hw/xen_pci_passthrough_config_init.c > > diff --git a/Makefile.target b/Makefile.target > index e527c1b..33435a3 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -221,6 +221,8 @@ obj-i386-$(CONFIG_XEN) += xen_platform.o > > # Xen PCI Passthrough > obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += host-pci-device.o > +obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough.o > +obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough_config_init.o > > # Inter-VM PCI shared memory > CONFIG_IVSHMEM > diff --git a/hw/xen_common.h b/hw/xen_common.h > index 0409ac7..48916fd 100644 > --- a/hw/xen_common.h > +++ b/hw/xen_common.h > @@ -135,4 +135,7 @@ static inline int xc_fd(xc_interface *xen_xc) > > void destroy_hvm_domain(void); > > +/* shutdown/destroy current domain because of an error */ > +void xen_shutdown_fatal_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); > + > #endif /* QEMU_HW_XEN_COMMON_H */ > diff --git a/hw/xen_pci_passthrough.c b/hw/xen_pci_passthrough.c > new file mode 100644 > index 0000000..998470b > --- /dev/null > +++ b/hw/xen_pci_passthrough.c > @@ -0,0 +1,831 @@ > +/* > + * Copyright (c) 2007, Neocleus Corporation. > + * Copyright (c) 2007, Intel Corporation. > + * > + * This work is licensed under the terms of the GNU GPL, version 2. See > + * the COPYING file in the top-level directory. > + * > + * 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 > + */ > + > +/* > + * Interrupt Disable policy: > + * > + * INTx interrupt: > + * Initialize(register_real_device) > + * Map INTx(xc_physdev_map_pirq): > + * <fail> > + * - Set real Interrupt Disable bit to ''1''. > + * - Set machine_irq and assigned_device->machine_irq to ''0''. > + * * Don''t bind INTx. > + * > + * Bind INTx(xc_domain_bind_pt_pci_irq): > + * <fail> > + * - Set real Interrupt Disable bit to ''1''. > + * - Unmap INTx. > + * - Decrement mapped_machine_irq[machine_irq] > + * - Set assigned_device->machine_irq to ''0''. > + * > + * Write to Interrupt Disable bit by guest software(pt_cmd_reg_write) > + * Write ''0'' > + * <ptdev->msi_trans_en is false> > + * - Set real bit to ''0'' if assigned_device->machine_irq isn''t ''0''. > + * > + * Write ''1'' > + * <ptdev->msi_trans_en is false> > + * - Set real bit to ''1''. > + */ > + > +#include <sys/ioctl.h> > + > +#include "pci.h" > +#include "xen.h" > +#include "xen_backend.h" > +#include "xen_pci_passthrough.h" > + > +#define PCI_BAR_ENTRIES (6) > + > +#define PT_NR_IRQS (256) > +char mapped_machine_irq[PT_NR_IRQS] = {0};char? Can it be made uint8_t instead?> + > +void pt_log(const PCIDevice *d, const char *f, ...) > +{ > + va_list ap; > + > + va_start(ap, f); > + if (d) { > + fprintf(stderr, "[%02x:%02x.%x] ", pci_bus_num(d->bus), > + PCI_SLOT(d->devfn), PCI_FUNC(d->devfn)); > + } > + vfprintf(stderr, f, ap); > + va_end(ap); > +} > + > + > +/* Config Space */ > +static int pt_pci_config_access_check(PCIDevice *d, uint32_t address, int len) > +{ > + /* check offset range */ > + if (address >= 0xFF) { > + PT_ERR(d, "Failed to access register with offset exceeding 0xFF. " > + "(addr: 0x%02x, len: %d)\n", address, len); > + return -1; > + } > + > + /* check read size */ > + if ((len != 1) && (len != 2) && (len != 4)) { > + PT_ERR(d, "Failed to access register with invalid access length. " > + "(addr: 0x%02x, len: %d)\n", address, len); > + return -1; > + } > + > + /* check offset alignment */ > + if (address & (len - 1)) { > + PT_ERR(d, "Failed to access register with invalid access size " > + "alignment. (addr: 0x%02x, len: %d)\n", address, len); > + return -1; > + } > + > + return 0; > +} > + > +int pt_bar_offset_to_index(uint32_t offset) > +{ > + int index = 0; > + > + /* check Exp ROM BAR */ > + if (offset == PCI_ROM_ADDRESS) { > + return PCI_ROM_SLOT; > + } > + > + /* calculate BAR index */ > + index = (offset - PCI_BASE_ADDRESS_0) >> 2; > + if (index >= PCI_NUM_REGIONS) { > + return -1; > + } > + > + return index; > +} > + > +static uint32_t pt_pci_read_config(PCIDevice *d, uint32_t address, int len) > +{ > + XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); > + uint32_t val = 0; > + XenPTRegGroup *reg_grp_entry = NULL; > + XenPTReg *reg_entry = NULL; > + int rc = 0; > + int emul_len = 0; > + uint32_t find_addr = address; > + > + if (pt_pci_config_access_check(d, address, len)) { > + goto exit; > + } > + > + /* check power state transition flags */ > + if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) { > + /* can''t accept until previous power state transition is completed. > + * so finish previous request here.Uh, how do we finish previous request here?> + */ > + PT_WARN(d, "Guest want to write during power state transition\n"); > + goto exit; > + } > + > + /* find register group entry */ > + reg_grp_entry = pt_find_reg_grp(s, address); > + if (reg_grp_entry) { > + /* check 0 Hardwired register group */So what does that mean? 0 hardwired register group? It means that the register is always zero?> + if (reg_grp_entry->reg_grp->grp_type == GRP_TYPE_HARDWIRED) { > + /* no need to emulate, just return 0 */ > + val = 0; > + goto exit; > + } > + } > + > + /* read I/O device register value */ > + rc = host_pci_get_block(s->real_device, address, (uint8_t *)&val, len); > + if (rc < 0) { > + PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc); > + memset(&val, 0xff, len); > + } > + > + /* just return the I/O device register value for > + * passthrough type register group */Is another way to say that: "There is no filter for the value, so just return the raw value." ?> + if (reg_grp_entry == NULL) { > + goto exit; > + } > + > + /* adjust the read value to appropriate CFC-CFF window */ > + val <<= (address & 3) << 3; > + emul_len = len; > + > + /* loop around the guest requested size */ > + while (emul_len > 0) { > + /* find register entry to be emulated */ > + reg_entry = pt_find_reg(reg_grp_entry, find_addr); > + if (reg_entry) { > + XenPTRegInfo *reg = reg_entry->reg; > + uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; > + uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); > + uint8_t *ptr_val = NULL; > + > + valid_mask <<= (find_addr - real_offset) << 3; > + ptr_val = (uint8_t *)&val + (real_offset & 3); > + > + /* do emulation based on register size */ > + switch (reg->size) { > + case 1: > + if (reg->u.b.read) { > + rc = reg->u.b.read(s, reg_entry, ptr_val, valid_mask); > + } > + break; > + case 2: > + if (reg->u.w.read) { > + rc = reg->u.w.read(s, reg_entry, > + (uint16_t *)ptr_val, valid_mask); > + } > + break; > + case 4: > + if (reg->u.dw.read) { > + rc = reg->u.dw.read(s, reg_entry, > + (uint32_t *)ptr_val, valid_mask); > + } > + break; > + } > + > + if (rc < 0) { > + xen_shutdown_fatal_error("Internal error: Invalid read " > + "emulation. (%s, rc: %d)\n", > + __func__, rc); > + return 0; > + } > + > + /* calculate next address to find */ > + emul_len -= reg->size; > + if (emul_len > 0) { > + find_addr = real_offset + reg->size; > + } > + } else { > + /* nothing to do with passthrough type register, > + * continue to find next byte */ > + emul_len--; > + find_addr++; > + } > + } > + > + /* need to shift back before returning them to pci bus emulator */ > + val >>= ((address & 3) << 3); > + > +exit: > + PT_LOG_CONFIG(d, address, val, len); > + return val; > +} > + > +static void pt_pci_write_config(PCIDevice *d, uint32_t address, > + uint32_t val, int len) > +{ > + XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); > + int index = 0; > + XenPTRegGroup *reg_grp_entry = NULL; > + int rc = 0; > + uint32_t read_val = 0; > + int emul_len = 0; > + XenPTReg *reg_entry = NULL; > + uint32_t find_addr = address; > + XenPTRegInfo *reg = NULL; > + > + if (pt_pci_config_access_check(d, address, len)) { > + return; > + } > + > + PT_LOG_CONFIG(d, address, val, len); > + > + /* check unused BAR register */ > + index = pt_bar_offset_to_index(address); > + if ((index >= 0) && (val > 0 && val < PT_BAR_ALLF) && > + (s->bases[index].bar_flag == PT_BAR_FLAG_UNUSED)) { > + PT_WARN(d, "Guest attempt to set address to unused Base Address " > + "Register. (addr: 0x%02x, len: %d)\n", address, len); > + } > + > + /* check power state transition flags */ > + if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) { > + /* can''t accept until previous power state transition is completed. > + * so finish previous request here.Ditto. How do we finish the previous request here?> + */ > + PT_WARN(d, "Guest want to write during power state transition\n"); > + return; > + } > + > + /* find register group entry */ > + reg_grp_entry = pt_find_reg_grp(s, address); > + if (reg_grp_entry) { > + /* check 0 Hardwired register group */ > + if (reg_grp_entry->reg_grp->grp_type == GRP_TYPE_HARDWIRED) { > + /* ignore silently */ > + PT_WARN(d, "Access to 0 Hardwired register. " > + "(addr: 0x%02x, len: %d)\n", address, len); > + return; > + } > + } > + > + /* read I/O device register value */ > + rc = host_pci_get_block(s->real_device, address, > + (uint8_t *)&read_val, len); > + if (rc < 0) { > + PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc); > + memset(&read_val, 0xff, len); > + } > + > + /* pass directly to the real device for passthrough type register group */ > + if (reg_grp_entry == NULL) { > + goto out; > + } > + > + /* adjust the read and write value to appropriate CFC-CFF window */ > + read_val <<= (address & 3) << 3; > + val <<= (address & 3) << 3; > + emul_len = len; > + > + /* loop around the guest requested size */ > + while (emul_len > 0) { > + /* find register entry to be emulated */ > + reg_entry = pt_find_reg(reg_grp_entry, find_addr); > + if (reg_entry) { > + reg = reg_entry->reg; > + uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; > + uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); > + uint8_t *ptr_val = NULL; > + > + valid_mask <<= (find_addr - real_offset) << 3; > + ptr_val = (uint8_t *)&val + (real_offset & 3); > + > + /* do emulation based on register size */ > + switch (reg->size) { > + case 1: > + if (reg->u.b.write) { > + rc = reg->u.b.write(s, reg_entry, ptr_val, > + read_val >> ((real_offset & 3) << 3), > + valid_mask); > + } > + break; > + case 2: > + if (reg->u.w.write) { > + rc = reg->u.w.write(s, reg_entry, (uint16_t *)ptr_val, > + (read_val >> ((real_offset & 3) << 3)), > + valid_mask); > + } > + break; > + case 4: > + if (reg->u.dw.write) { > + rc = reg->u.dw.write(s, reg_entry, (uint32_t *)ptr_val, > + (read_val >> ((real_offset & 3) << 3)), > + valid_mask); > + } > + break; > + } > + > + if (rc < 0) { > + xen_shutdown_fatal_error("Internal error: Invalid write" > + " emulation. (%s, rc: %d)\n", > + __func__, rc); > + return; > + } > + > + /* calculate next address to find */ > + emul_len -= reg->size; > + if (emul_len > 0) { > + find_addr = real_offset + reg->size; > + } > + } else { > + /* nothing to do with passthrough type register, > + * continue to find next byte */ > + emul_len--; > + find_addr++; > + } > + } > + > + /* need to shift back before passing them to host_pci_device */ > + val >>= (address & 3) << 3; > + > +out: > + if (!(reg && reg->no_wb)) { > + /* unknown regs are passed through */ > + rc = host_pci_set_block(s->real_device, address, (uint8_t *)&val, len); > + > + if (rc < 0) { > + PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc); > + } > + } > +} > + > +/* ioport/iomem space*/ > +static void pt_iomem_map(XenPCIPassthroughState *s, int i, > + pcibus_t e_phys, pcibus_t e_size, int type) > +{ > + PCIIORegion *r = &s->dev.io_regions[i]; > + uint32_t old_ebase = s->bases[i].e_physbase; > + bool first_map = s->bases[i].e_size == 0; > + int ret = 0; > + > + s->bases[i].e_physbase = e_phys; > + s->bases[i].e_size = e_size; > + > + PT_LOG(&s->dev, "e_phys=%#"PRIx64" maddr=%#"PRIx64" type=%d" > + " len=%#"PRIx64" index=%d first_map=%d\n", > + e_phys, s->bases[i].access.maddr, type, > + e_size, i, first_map); > + > + if (e_size == 0) { > + return; > + } > + > + if (!first_map && old_ebase != PT_PCI_BAR_UNMAPPED) { > + /* Remove old mapping */ > + memory_region_del_subregion(r->address_space, > + r->memory); > + ret = xc_domain_memory_mapping(xen_xc, xen_domid, > + old_ebase >> XC_PAGE_SHIFT, > + s->bases[i].access.maddr >> XC_PAGE_SHIFT, > + (e_size + XC_PAGE_SIZE - 1) >> XC_PAGE_SHIFT, > + DPCI_REMOVE_MAPPING); > + if (ret != 0) { > + PT_ERR(&s->dev, "remove old mapping failed!\n"); > + return; > + } > + } > + > + /* map only valid guest address */ > + if (e_phys != PCI_BAR_UNMAPPED) { > + /* Create new mapping */ > + memory_region_add_subregion_overlap(r->address_space, > + e_phys, r->memory, 1); > + ret = xc_domain_memory_mapping(xen_xc, xen_domid, > + s->bases[i].e_physbase >> XC_PAGE_SHIFT, > + s->bases[i].access.maddr >> XC_PAGE_SHIFT, > + (e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT, > + DPCI_ADD_MAPPING); > + > + if (ret != 0) { > + PT_ERR(&s->dev, "create new mapping failed!\n"); > + } > + } > +} > + > +static void pt_ioport_map(XenPCIPassthroughState *s, int i, > + pcibus_t e_phys, pcibus_t e_size, int type) > +{ > + PCIIORegion *r = &s->dev.io_regions[i]; > + uint32_t old_ebase = s->bases[i].e_physbase; > + bool first_map = s->bases[i].e_size == 0; > + int ret = 0; > + > + s->bases[i].e_physbase = e_phys; > + s->bases[i].e_size = e_size; > + > + PT_LOG(&s->dev, "e_phys=%#04"PRIx64" pio_base=%#04"PRIx64" len=%"PRId64 > + " index=%d first_map=%d\n", > + e_phys, s->bases[i].access.pio_base, e_size, i, first_map); > + > + if (e_size == 0) { > + return; > + } > + > + if (!first_map && old_ebase != PT_PCI_BAR_UNMAPPED) { > + /* Remove old mapping */ > + memory_region_del_subregion(r->address_space, > + r->memory); > + ret = xc_domain_ioport_mapping(xen_xc, xen_domid, old_ebase, > + s->bases[i].access.pio_base, e_size, > + DPCI_REMOVE_MAPPING); > + if (ret != 0) { > + PT_ERR(&s->dev, "remove old mapping failed!\n"); > + return; > + } > + } > + > + /* map only valid guest address (include 0) */ > + if (e_phys != PCI_BAR_UNMAPPED) { > + /* Create new mapping */ > + memory_region_add_subregion_overlap(r->address_space, > + e_phys, r->memory, 1); > + ret = xc_domain_ioport_mapping(xen_xc, xen_domid, e_phys, > + s->bases[i].access.pio_base, e_size, > + DPCI_ADD_MAPPING); > + if (ret != 0) { > + PT_ERR(&s->dev, "create new mapping failed!\n"); > + } > + } > + > +} > + > + > +/* mapping BAR */ > + > +void pt_bar_mapping_one(XenPCIPassthroughState *s, int bar, > + int io_enable, int mem_enable) > +{ > + PCIDevice *dev = &s->dev; > + PCIIORegion *r; > + XenPTRegGroup *reg_grp_entry = NULL; > + XenPTReg *reg_entry = NULL; > + XenPTRegion *base = NULL; > + pcibus_t r_size = 0, r_addr = PCI_BAR_UNMAPPED; > + int rc = 0; > + > + r = &dev->io_regions[bar]; > + > + /* check valid region */ > + if (!r->size) { > + return; > + } > + > + base = &s->bases[bar]; > + /* skip unused BAR or upper 64bit BAR */ > + if ((base->bar_flag == PT_BAR_FLAG_UNUSED) > + || (base->bar_flag == PT_BAR_FLAG_UPPER)) { > + return; > + } > + > + /* copy region address to temporary */ > + r_addr = r->addr; > + > + /* need unmapping in case I/O Space or Memory Space disable */.. is disabled.> + if (((base->bar_flag == PT_BAR_FLAG_IO) && !io_enable) || > + ((base->bar_flag == PT_BAR_FLAG_MEM) && !mem_enable)) { > + r_addr = PCI_BAR_UNMAPPED; > + }Add a comment saying: /* or ROM address is disabled. */> + if ((bar == PCI_ROM_SLOT) && (r_addr != PCI_BAR_UNMAPPED)) { > + reg_grp_entry = pt_find_reg_grp(s, PCI_ROM_ADDRESS); > + if (reg_grp_entry) { > + reg_entry = pt_find_reg(reg_grp_entry, PCI_ROM_ADDRESS); > + if (reg_entry && !(reg_entry->data & PCI_ROM_ADDRESS_ENABLE)) { > + r_addr = PCI_BAR_UNMAPPED; > + } > + } > + } > + > + /* prevent guest software mapping memory resource to 00000000h */ > + if ((base->bar_flag == PT_BAR_FLAG_MEM) && (r_addr == 0)) { > + r_addr = PCI_BAR_UNMAPPED; > + } > + > + r_size = pt_get_emul_size(base->bar_flag, r->size); > + > + rc = pci_check_bar_overlap(dev, r_addr, r_size, r->type); > + if (rc) { > + PT_WARN(dev, "Region: %d (addr: %#"FMT_PCIBUS > + ", len: %#"FMT_PCIBUS") is overlapped.\n",Is the FMT_PCIBUS for len correct?> + bar, r_addr, r_size); > + } > + > + /* check whether we need to update the mapping or not */ > + if (r_addr != s->bases[bar].e_physbase) { > + /* mapping BAR */ > + if (base->bar_flag == PT_BAR_FLAG_IO) { > + pt_ioport_map(s, bar, r_addr, r_size, r->type); > + } else { > + pt_iomem_map(s, bar, r_addr, r_size, r->type); > + } > + } > +} > + > +void pt_bar_mapping(XenPCIPassthroughState *s, int io_enable, int mem_enable) > +{ > + int i; > + > + for (i = 0; i < PCI_NUM_REGIONS; i++) { > + pt_bar_mapping_one(s, i, io_enable, mem_enable); > + } > +} > + > +static uint64_t bar_read(void *o, target_phys_addr_t addr, unsigned size) > +{ > + PCIDevice *d = o; > + PT_ERR(d, "Should not read BAR through QEMU. @0x"TARGET_FMT_plx"\n", addr);Perhaps you can add a comment of how it should read it? Is the reading trapped by the hypervisor? If so, you might want to include a comment stating that, maybe even the name of the file to look in.> + return 0; > +} > +static void bar_write(void *o, target_phys_addr_t addr, > + uint64_t data, unsigned size) > +{ > + PCIDevice* d = o; > + PT_ERR(d, "Should not write BAR through QEMU. @0x"TARGET_FMT_plx"\n", addr); > +} > + > +static const MemoryRegionOps ops = { > + .endianness = DEVICE_NATIVE_ENDIAN, > + .read = bar_read, > + .write = bar_write, > +}; > + > +/* register regions */ > +static int pt_register_regions(XenPCIPassthroughState *s) > +{ > + int i = 0; > + uint32_t bar_data = 0; > + HostPCIDevice *d = s->real_device; > + > + /* Register PIO/MMIO BARs */ > + for (i = 0; i < PCI_BAR_ENTRIES; i++) { > + HostPCIIORegion *r = &d->io_regions[i]; > + > + if (r->base_addr && r->size) { > + s->bases[i].e_physbase = r->base_addr; > + s->bases[i].access.u = r->base_addr; > + > + /* Register current region */ > + if (r->flags & IORESOURCE_IO) { > + memory_region_init_io(&s->bar[i], &ops, &s->dev, > + "xen-pci-pt-bar-io", r->size); > + pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_SPACE_IO, > + &s->bar[i]); > + } else if (r->flags & IORESOURCE_PREFETCH) { > + memory_region_init_io(&s->bar[i], &ops, &s->dev, > + "xen-pci-pt-bar-mem", r->size); > + pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_MEM_PREFETCH, > + &s->bar[i]); > + } else { > + memory_region_init_io(&s->bar[i], &ops, &s->dev, > + "xen-pci-pt-bar-mem", r->size); > + pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_SPACE_MEMORY, > + &s->bar[i]); > + } > + > + PT_LOG(&s->dev, "IO region registered (size=0x%08"PRIx64 > + " base_addr=0x%08"PRIx64")\n", > + r->size, r->base_addr); > + } > + } > + > + /* Register expansion ROM address */ > + if (d->rom.base_addr && d->rom.size) { > + /* Re-set BAR reported by OS, otherwise ROM can''t be read. */ > + if (host_pci_get_long(d, PCI_ROM_ADDRESS, &bar_data)) { > + return 0; > + } > + if ((bar_data & PCI_ROM_ADDRESS_MASK) == 0) { > + bar_data |= d->rom.base_addr & PCI_ROM_ADDRESS_MASK; > + host_pci_set_long(d, PCI_ROM_ADDRESS, bar_data); > + } > + > + s->bases[PCI_ROM_SLOT].e_physbase = d->rom.base_addr; > + s->bases[PCI_ROM_SLOT].access.maddr = d->rom.base_addr; > + > + memory_region_init_rom_device(&s->rom, NULL, NULL, &s->dev.qdev, > + "xen-pci-pt-rom", d->rom.size); > + pci_register_bar(&s->dev, PCI_ROM_SLOT, PCI_BASE_ADDRESS_MEM_PREFETCH, > + &s->rom); > + > + PT_LOG(&s->dev, "Expansion ROM registered (size=0x%08"PRIx64 > + " base_addr=0x%08"PRIx64")\n", > + d->rom.size, d->rom.base_addr); > + } > + > + return 0; > +} > + > +static void pt_unregister_regions(XenPCIPassthroughState *s) > +{ > + int i, type, rc; > + uint32_t e_size; > + PCIDevice *d = &s->dev; > + > + for (i = 0; i < PCI_NUM_REGIONS; i++) { > + e_size = s->bases[i].e_size; > + if ((e_size == 0) || (s->bases[i].e_physbase == PT_PCI_BAR_UNMAPPED)) { > + continue; > + } > + > + type = d->io_regions[i].type; > + > + if (type == PCI_BASE_ADDRESS_SPACE_MEMORY > + || type == PCI_BASE_ADDRESS_MEM_PREFETCH) { > + rc = xc_domain_memory_mapping(xen_xc, xen_domid, > + s->bases[i].e_physbase >> XC_PAGE_SHIFT, > + s->bases[i].access.maddr >> XC_PAGE_SHIFT, > + (e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT, > + DPCI_REMOVE_MAPPING); > + if (rc != 0) { > + PT_ERR(d, "remove old mem mapping failed!\n"); > + continue; > + } > + > + } else if (type == PCI_BASE_ADDRESS_SPACE_IO) { > + rc = xc_domain_ioport_mapping(xen_xc, xen_domid, > + s->bases[i].e_physbase, > + s->bases[i].access.pio_base, > + e_size, > + DPCI_REMOVE_MAPPING); > + if (rc != 0) { > + PT_ERR(d, "remove old io mapping failed!\n"); > + continue; > + } > + } > + } > +} > + > +static int pt_initfn(PCIDevice *pcidev) > +{ > + XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, pcidev); > + int dom, bus; > + unsigned slot, func;So how come bus is ''int'' but slot and func are unsigned?> + int rc = 0; > + uint8_t machine_irq = 0; > + int pirq = PT_UNASSIGNED_PIRQ; > + > + if (pci_parse_devaddr(s->hostaddr, &dom, &bus, &slot, &func) < 0) { > + PT_ERR(pcidev, "Failed to parse BDF: %s\n", s->hostaddr); > + return -1; > + } > + > + /* register real device */ > + PT_LOG(pcidev, "Assigning real physical device %02x:%02x.%x" > + " to devfn %#x\n", bus, slot, func, s->dev.devfn); > + > + s->real_device = host_pci_device_get(bus, slot, func); > + if (!s->real_device) { > + return -1; > + } > + > + s->is_virtfn = s->real_device->is_virtfn; > + if (s->is_virtfn) { > + PT_LOG(pcidev, "%04x:%02x:%02x.%x is a SR-IOV Virtual Function\n", > + s->real_device->domain, bus, slot, func); > + } > + > + /* Initialize virtualized PCI configuration (Extended 256 Bytes) */ > + if (host_pci_get_block(s->real_device, 0, pcidev->config, > + PCI_CONFIG_SPACE_SIZE) == -1) { > + host_pci_device_put(s->real_device); > + return -1; > + } > + > + /* Handle real device''s MMIO/PIO BARs */ > + pt_register_regions(s); > + > + /* Bind interrupt */ > + if (!s->dev.config[PCI_INTERRUPT_PIN]) {Is a ''zero'' value correct? I think that is the value that SR-IOV devices export since they don''t have legacy IRQs? Or maybe they export the MSI vector values instead. Perhaps you should do if (!s->dev.config[PCI_INTERRUPT_PIN] && !s->is_virtfn) {> + PT_LOG(pcidev, "no pin interrupt\n"); > + goto out; > + } > + > + host_pci_get_byte(s->real_device, PCI_INTERRUPT_LINE, &machine_irq);Just to double check:> lspci -s 01:10.0 -v -xx01:10.0 Ethernet controller: Intel Corporation 82576 Virtual Function (rev 01) Subsystem: Intel Corporation Device a01c Flags: bus master, fast devsel, latency 0 [virtual] Memory at b8840000 (64-bit, non-prefetchable) [size=16K] [virtual] Memory at b8860000 (64-bit, non-prefetchable) [size=16K] Capabilities: [70] MSI-X: Enable+ Count=3 Masked- Capabilities: [a0] Express Endpoint, MSI 00 Capabilities: [100] Advanced Error Reporting Capabilities: [150] Alternative Routing-ID Interpretation (ARI) Kernel driver in use: igbvf Kernel modules: igbvf 00: ff ff ff ff 04 00 10 00 01 00 00 02 00 00 00 00 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20: 00 00 00 00 00 00 00 00 00 00 00 00 86 80 1c a0 30: 00 00 00 00 70 00 00 00 00 00 00 00 00 00 00 00 And a normal USB device:> lspci -s 00:1a.0 -v -xx00:1a.0 USB Controller: Intel Corporation 82801JI (ICH10 Family) USB UHCI Controller #4 (prog-if 00 [UHCI]) Subsystem: Intel Corporation Device 4f53 Flags: bus master, medium devsel, latency 0, IRQ 16 I/O ports at 40e0 [size=32] Capabilities: [50] PCI Advanced Features Kernel driver in use: uhci_hcd 00: 86 80 37 3a 05 00 90 02 00 00 03 0c 00 00 80 00 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20: e1 40 00 00 00 00 00 00 00 00 00 00 86 80 53 4f 30: 00 00 00 00 50 00 00 00 00 00 00 00 0b 01 00 00 The USB device has interrupt line 0xB, and pin 1. The SR-IOV one is zero and zero.> + rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq);so perhaps this should be guarded by a check against devices that have no legacy IRQs, like SR-VIO? Perhaps check against is_virtfn?> + > + if (rc < 0) { > + PT_ERR(pcidev, "Mapping machine irq %u to pirq %i failed, (rc: %d)\n", > + machine_irq, pirq, rc); > + > + /* Disable PCI intx assertion (turn on bit10 of devctl) */ > + host_pci_set_word(s->real_device, > + PCI_COMMAND, > + pci_get_word(s->dev.config + PCI_COMMAND) > + | PCI_COMMAND_INTX_DISABLE); > + machine_irq = 0; > + s->machine_irq = 0; > + } else { > + machine_irq = pirq; > + s->machine_irq = pirq; > + mapped_machine_irq[machine_irq]++; > + } > + > + /* bind machine_irq to device */ > + if (rc < 0 && machine_irq != 0) { > + uint8_t e_device = PCI_SLOT(s->dev.devfn); > + uint8_t e_intx = pci_intx(s); > + > + rc = xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, machine_irq, 0,What is the zero at the end for? Should it have a comment?> + e_device, e_intx); > + if (rc < 0) { > + PT_ERR(pcidev, "Binding of interrupt %i failed! (rc: %d)\n", > + e_intx, rc); > + > + /* Disable PCI intx assertion (turn on bit10 of devctl) */ > + host_pci_set_word(s->real_device, PCI_COMMAND, > + *(uint16_t *)(&s->dev.config[PCI_COMMAND]) > + | PCI_COMMAND_INTX_DISABLE); > + mapped_machine_irq[machine_irq]--;Is this code reentrant? What if the user wants to pass in two USB devices where both of them share the same IRQ. And it so happens that we fail (perhaps the device is owned by another guest). We get here, and both of them decrement the mapped_machine_irq.. which means that> + > + if (mapped_machine_irq[machine_irq] == 0) {this might not be executed (as both of them read it as =1).?> + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq)) { > + PT_ERR(pcidev, "Unmapping of machine interrupt %i failed!" > + " (rc: %d)\n", machine_irq, rc);Or is the code actually single-threaded so there is no danger of this?> + } > + } > + s->machine_irq = 0; > + } > + } > + > +out: > + PT_LOG(pcidev, "Real physical device %02x:%02x.%x registered successfuly!" > + "\nIRQ type = %s\n", bus, slot, func, "INTx"); > + > + return 0; > +} > + > +static int pt_unregister_device(PCIDevice *pcidev) > +{ > + XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, pcidev); > + uint8_t e_device, e_intx; > + uint8_t machine_irq; > + int rc; > + > + /* Unbind interrupt */ > + e_device = PCI_SLOT(s->dev.devfn); > + e_intx = pci_intx(s); > + machine_irq = s->machine_irq; > + > + if (machine_irq) { > + rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq, > + PT_IRQ_TYPE_PCI, 0, e_device, e_intx, 0);What are the two ''0'' for? Can you provide a comment within the arguments?> + if (rc < 0) { > + PT_ERR(pcidev, "Unbinding of interrupt failed! rc=%d\n", rc);Might want to mention which interrupt (or pirq?) failed.> + } > + } > + > + if (machine_irq) {Why not continue the logic within this code? As in why the ''if (machine_irq)'' here? You would still continue going even if xc_domain_unbind_pt_irq failed..> + mapped_machine_irq[machine_irq]--; > + > + if (mapped_machine_irq[machine_irq] == 0) { > + rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq); > + > + if (rc < 0) { > + PT_ERR(pcidev, "Unmaping of interrupt failed! rc=%d\n", rc);And provide the interrupt (both the pirq and linux one). You might want to mention in the erorr logs: "But bravely continuing on.."> + } > + } > + } > + > + /* unregister real device''s MMIO/PIO BARs */ > + pt_unregister_regions(s); > + > + host_pci_device_put(s->real_device); > + > + return 0; > +} > + > +static PCIDeviceInfo xen_pci_passthrough = { > + .init = pt_initfn, > + .exit = pt_unregister_device, > + .qdev.name = "xen-pci-passthrough", > + .qdev.desc = "Assign an host pci device with Xen", > + .qdev.size = sizeof(XenPCIPassthroughState), > + .config_read = pt_pci_read_config, > + .config_write = pt_pci_write_config, > + .is_express = 0, > + .qdev.props = (Property[]) { > + DEFINE_PROP_STRING("hostaddr", XenPCIPassthroughState, hostaddr), > + DEFINE_PROP_BIT("power-mgmt", XenPCIPassthroughState, power_mgmt, > + 0, false), > + DEFINE_PROP_END_OF_LIST(), > + } > +}; > + > +static void xen_passthrough_register(void) > +{ > + pci_qdev_register(&xen_pci_passthrough); > +} > + > +device_init(xen_passthrough_register); > diff --git a/hw/xen_pci_passthrough.h b/hw/xen_pci_passthrough.h > new file mode 100644 > index 0000000..110325c > --- /dev/null > +++ b/hw/xen_pci_passthrough.h > @@ -0,0 +1,282 @@ > +#ifndef QEMU_HW_XEN_PCI_PASSTHROUGH_H > +# define QEMU_HW_XEN_PCI_PASSTHROUGH_H > + > +#include "qemu-common.h" > +#include "xen_common.h" > +#include "pci.h" > +#include "host-pci-device.h" > + > +/* #define PT_LOGGING_ENABLED */ > +/* #define PT_DEBUG_PCI_CONFIG_ACCESS */ > + > +void pt_log(const PCIDevice *d, const char *f, ...) GCC_FMT_ATTR(2, 3); > + > +#define PT_ERR(d, _f, _a...) pt_log(d, "%s: Error: " _f, __func__, ##_a) > + > +#ifdef PT_LOGGING_ENABLED > +# define PT_LOG(d, _f, _a...) pt_log(d, "%s: " _f, __func__, ##_a) > +# define PT_WARN(d, _f, _a...) pt_log(d, "%s: Warning: " _f, __func__, ##_a) > +#else > +# define PT_LOG(d, _f, _a...) > +# define PT_WARN(d, _f, _a...) > +#endif > + > +#ifdef PT_DEBUG_PCI_CONFIG_ACCESS > +# define PT_LOG_CONFIG(d, addr, val, len) \ > + pt_log(d, "%s: address=0x%04x val=0x%08x len=%d\n", \ > + __func__, addr, val, len) > +#else > +# define PT_LOG_CONFIG(d, addr, val, len) > +#endifNice. Thanks!> + > + > +typedef struct XenPTRegInfo XenPTRegInfo; > +typedef struct XenPTReg XenPTReg; > + > +typedef struct XenPCIPassthroughState XenPCIPassthroughState; > + > +/* function type for config reg */ > +typedef int (*conf_reg_init) > + (XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset, > + uint32_t *data); > +typedef int (*conf_dword_write) > + (XenPCIPassthroughState *, XenPTReg *cfg_entry, > + uint32_t *val, uint32_t dev_value, uint32_t valid_mask); > +typedef int (*conf_word_write) > + (XenPCIPassthroughState *, XenPTReg *cfg_entry, > + uint16_t *val, uint16_t dev_value, uint16_t valid_mask); > +typedef int (*conf_byte_write) > + (XenPCIPassthroughState *, XenPTReg *cfg_entry, > + uint8_t *val, uint8_t dev_value, uint8_t valid_mask); > +typedef int (*conf_dword_read) > + (XenPCIPassthroughState *, XenPTReg *cfg_entry, > + uint32_t *val, uint32_t valid_mask); > +typedef int (*conf_word_read) > + (XenPCIPassthroughState *, XenPTReg *cfg_entry, > + uint16_t *val, uint16_t valid_mask); > +typedef int (*conf_byte_read) > + (XenPCIPassthroughState *, XenPTReg *cfg_entry, > + uint8_t *val, uint8_t valid_mask); > +typedef int (*conf_dword_restore) > + (XenPCIPassthroughState *, XenPTReg *cfg_entry, uint32_t real_offset, > + uint32_t dev_value, uint32_t *val); > +typedef int (*conf_word_restore) > + (XenPCIPassthroughState *, XenPTReg *cfg_entry, uint32_t real_offset, > + uint16_t dev_value, uint16_t *val); > +typedef int (*conf_byte_restore) > + (XenPCIPassthroughState *, XenPTReg *cfg_entry, uint32_t real_offset, > + uint8_t dev_value, uint8_t *val); > + > +/* power state transition */ > +#define PT_FLAG_TRANSITING 0x0001 > + > +#define PT_BAR_ALLF 0xFFFFFFFF /* BAR ALLF value */ > +#define PT_PCI_BAR_UNMAPPED (-1) > +#define PT_UNASSIGNED_PIRQ (-1) > + > + > +typedef enum { > + GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */ > + GRP_TYPE_EMU, /* emul reg group */ > +} RegisterGroupType; > + > +typedef enum { > + PT_BAR_FLAG_MEM = 0, /* Memory type BAR */ > + PT_BAR_FLAG_IO, /* I/O type BAR */ > + PT_BAR_FLAG_UPPER, /* upper 64bit BAR */ > + PT_BAR_FLAG_UNUSED, /* unused BAR */ > +} PTBarFlag; > + > + > +typedef struct XenPTRegion { > + /* Virtual phys base & size */ > + uint32_t e_physbase; > + uint32_t e_size; > + /* Index of region in qemu */ > + uint32_t memory_index; > + /* BAR flag */ > + PTBarFlag bar_flag; > + /* Translation of the emulated address */ > + union { > + uint64_t maddr; > + uint64_t pio_base; > + uint64_t u; > + } access; > +} XenPTRegion; > + > +/* XenPTRegInfo declaration > + * - only for emulated register (either a part or whole bit). > + * - for passthrough register that need special behavior (like interacting with > + * other component), set emu_mask to all 0 and specify r/w func properly. > + * - do NOT use ALL F for init_val, otherwise the tbl will not be registered. > + */ > + > +/* emulated register infomation */ > +struct XenPTRegInfo { > + uint32_t offset; > + uint32_t size; > + uint32_t init_val; > + /* reg read only field mask (ON:RO/ROS, OFF:other) */ > + uint32_t ro_mask; > + /* reg emulate field mask (ON:emu, OFF:passthrough) */ > + uint32_t emu_mask; > + /* no write back allowed */ > + uint32_t no_wb; > + conf_reg_init init; > + /* read/write/restore function pointer > + * for double_word/word/byte size */ > + union { > + struct { > + conf_dword_write write; > + conf_dword_read read; > + conf_dword_restore restore; > + } dw; > + struct { > + conf_word_write write; > + conf_word_read read; > + conf_word_restore restore; > + } w; > + struct { > + conf_byte_write write; > + conf_byte_read read; > + conf_byte_restore restore; > + } b; > + } u; > +}; > + > +/* emulated register management */ > +struct XenPTReg { > + QLIST_ENTRY(XenPTReg) entries; > + XenPTRegInfo *reg; > + uint32_t data; > +}; > + > +typedef struct XenPTRegGroupInfo XenPTRegGroupInfo; > + > +/* emul reg group size initialize method */ > +typedef int (*pt_reg_size_init_fn) > + (XenPCIPassthroughState *, const XenPTRegGroupInfo *, > + uint32_t base_offset, uint8_t *size); > + > +/* emulated register group infomation */ > +struct XenPTRegGroupInfo { > + uint8_t grp_id; > + RegisterGroupType grp_type; > + uint8_t grp_size; > + pt_reg_size_init_fn size_init; > + XenPTRegInfo *emu_reg_tbl; > +}; > + > +/* emul register group management table */ > +typedef struct XenPTRegGroup { > + QLIST_ENTRY(XenPTRegGroup) entries; > + const XenPTRegGroupInfo *reg_grp; > + uint32_t base_offset; > + uint8_t size; > + QLIST_HEAD(, XenPTReg) reg_tbl_list; > +} XenPTRegGroup; > + > + > +typedef struct XenPTPM { > + QEMUTimer *pm_timer; /* QEMUTimer struct */ > + int no_soft_reset; /* No Soft Reset flags */ > + uint16_t flags; /* power state transition flags */ > + uint16_t pmc_field; /* Power Management Capabilities field */ > + int pm_delay; /* power state transition delay */ > + uint16_t cur_state; /* current power state */ > + uint16_t req_state; /* requested power state */ > + uint32_t pm_base; /* Power Management Capability reg base offset */ > + uint32_t aer_base; /* AER Capability reg base offset */ > +} XenPTPM; > + > +struct XenPCIPassthroughState { > + PCIDevice dev; > + > + char *hostaddr; > + bool is_virtfn; > + HostPCIDevice *real_device; > + XenPTRegion bases[PCI_NUM_REGIONS]; /* Access regions */ > + QLIST_HEAD(, XenPTRegGroup) reg_grp_tbl; > + > + uint32_t machine_irq; > + > + uint32_t power_mgmt; > + XenPTPM *pm_state; > + > + MemoryRegion bar[PCI_NUM_REGIONS - 1]; > + MemoryRegion rom; > +}; > + > +int pt_config_init(XenPCIPassthroughState *s); > +void pt_config_delete(XenPCIPassthroughState *s); > +void pt_bar_mapping(XenPCIPassthroughState *s, int io_enable, int mem_enable); > +void pt_bar_mapping_one(XenPCIPassthroughState *s, int bar, > + int io_enable, int mem_enable); > +XenPTRegGroup *pt_find_reg_grp(XenPCIPassthroughState *s, uint32_t address); > +XenPTReg *pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address); > +int pt_bar_offset_to_index(uint32_t offset); > + > +static inline pcibus_t pt_get_emul_size(PTBarFlag flag, pcibus_t r_size) > +{ > + /* align resource size (memory type only) */ > + if (flag == PT_BAR_FLAG_MEM) { > + return (r_size + XC_PAGE_SIZE - 1) & XC_PAGE_MASK; > + } else { > + return r_size; > + } > +} > + > +/* INTx */ > +/* The PCI Local Bus Specification, Rev. 3.0, > + * Section 6.2.4 Miscellaneous Registers, pp 223 > + * outlines 5 valid values for the intertupt pin (intx). > + * 0: For devices (or device functions) that don''t use an interrupt in > + * 1: INTA# > + * 2: INTB# > + * 3: INTC# > + * 4: INTD# > + * > + * Xen uses the following 4 values for intx > + * 0: INTA# > + * 1: INTB# > + * 2: INTC# > + * 3: INTD# > + * > + * Observing that these list of values are not the same, pci_read_intx() > + * uses the following mapping from hw to xen values.Might want to add : "(from /sys/../config contents)."> + * This seems to reflect the current usage within Xen. > + * > + * PCI hardware | Xen | Notes > + * ----------------+-----+---------------------------------------------------- > + * 0 | 0 | No interrupt > + * 1 | 0 | INTA# > + * 2 | 1 | INTB# > + * 3 | 2 | INTC# > + * 4 | 3 | INTD# > + * any other value | 0 | This should never happen, log error message > + */ > + > +static inline uint8_t pci_read_intx(XenPCIPassthroughState *s) > +{ > + uint8_t v = 0; > + host_pci_get_byte(s->real_device, PCI_INTERRUPT_PIN, &v); > + return v; > +} > + > +static inline uint8_t pci_intx(XenPCIPassthroughState *s) > +{ > + uint8_t r_val = pci_read_intx(s); > + > + PT_LOG(&s->dev, "intx=%i\n", r_val); > + if (r_val < 1 || r_val > 4) { > + PT_LOG(&s->dev, "Interrupt pin read from hardware is out of range:" > + " value=%i, acceptable range is 1 - 4\n", r_val); > + r_val = 0; > + } else { > + r_val -= 1; > + } > + > + return r_val; > +} > + > +#endif /* !QEMU_HW_XEN_PCI_PASSTHROUGH_H */ > diff --git a/hw/xen_pci_passthrough_config_init.c b/hw/xen_pci_passthrough_config_init.c > new file mode 100644 > index 0000000..1e9de64 > --- /dev/null > +++ b/hw/xen_pci_passthrough_config_init.c > @@ -0,0 +1,11 @@ > +#include "xen_pci_passthrough.h" > + > +XenPTRegGroup *pt_find_reg_grp(XenPCIPassthroughState *s, uint32_t address) > +{ > + return NULL; > +} > + > +XenPTReg *pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address) > +{ > + return NULL; > +} > diff --git a/xen-all.c b/xen-all.c > index b5e28ab..0e3bbcf 100644 > --- a/xen-all.c > +++ b/xen-all.c > @@ -979,3 +979,15 @@ void destroy_hvm_domain(void) > xc_interface_close(xc_handle); > } > } > + > +void xen_shutdown_fatal_error(const char *fmt, ...) > +{ > + va_list ap; > + > + va_start(ap, fmt); > + vfprintf(stderr, fmt, ap); > + va_end(ap); > + fprintf(stderr, "Will destroy the domain.\n"); > + /* destroy the domain */ > + qemu_system_shutdown_request(); > +} > --
Konrad Rzeszutek Wilk
2011-Dec-05 23:00 UTC
Re: [Xen-devel] [PATCH V5 08/10] Introduce Xen PCI Passthrough, PCI config space helpers (2/3)
On Thu, Nov 24, 2011 at 05:44:37PM +0000, Anthony PERARD wrote:> From: Allen Kay <allen.m.kay@intel.com> > > A more complete history can be found here: > git://xenbits.xensource.com/qemu-xen-unstable.git > > Signed-off-by: Allen Kay <allen.m.kay@intel.com> > Signed-off-by: Guy Zana <guy@neocleus.com> > Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> > --- > hw/xen_pci_passthrough.c | 15 + > hw/xen_pci_passthrough_config_init.c | 2131 ++++++++++++++++++++++++++++++++++ > 2 files changed, 2146 insertions(+), 0 deletions(-) > > diff --git a/hw/xen_pci_passthrough.c b/hw/xen_pci_passthrough.c > index 998470b..c816ed5 100644 > --- a/hw/xen_pci_passthrough.c > +++ b/hw/xen_pci_passthrough.c > @@ -360,6 +360,11 @@ out: > PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc); > } > } > + > + if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) { > + qemu_mod_timer(s->pm_state->pm_timer, > + qemu_get_clock_ms(rt_clock) + s->pm_state->pm_delay); > + }Is this in the right place? There is code before in this function that does: if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) return; so we would never get here, I think?> } > > /* ioport/iomem space*/ > @@ -706,6 +711,13 @@ static int pt_initfn(PCIDevice *pcidev) > /* Handle real device''s MMIO/PIO BARs */ > pt_register_regions(s); > > + /* reinitialize each config register to be emulated */ > + if (pt_config_init(s)) { > + PT_ERR(pcidev, "PCI Config space initialisation failed.\n"); > + host_pci_device_put(s->real_device); > + return -1; > + } > + > /* Bind interrupt */ > if (!s->dev.config[PCI_INTERRUPT_PIN]) { > PT_LOG(pcidev, "no pin interrupt\n"); > @@ -798,6 +810,9 @@ static int pt_unregister_device(PCIDevice *pcidev) > } > } > > + /* delete all emulated config registers */ > + pt_config_delete(s); > + > /* unregister real device''s MMIO/PIO BARs */ > pt_unregister_regions(s); > > diff --git a/hw/xen_pci_passthrough_config_init.c b/hw/xen_pci_passthrough_config_init.c > index 1e9de64..ae64544 100644 > --- a/hw/xen_pci_passthrough_config_init.c > +++ b/hw/xen_pci_passthrough_config_init.c > @@ -1,11 +1,2142 @@ > +/* > + * Copyright (c) 2007, Neocleus Corporation. > + * Copyright (c) 2007, Intel Corporation. > + * > + * This work is licensed under the terms of the GNU GPL, version 2. See > + * the COPYING file in the top-level directory. > + * > + * 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 "qemu-timer.h" > +#include "xen_backend.h" > #include "xen_pci_passthrough.h" > > +#define PT_MERGE_VALUE(value, data, val_mask) \ > + (((value) & (val_mask)) | ((data) & ~(val_mask))) > + > +#define PT_INVALID_REG 0xFFFFFFFF /* invalid register value */ > + > +/* prototype */ > + > +static int pt_ptr_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, > + uint32_t real_offset, uint32_t *data); > +static int pt_init_pci_config(XenPCIPassthroughState *s); > + > + > +/* helper */ > + > +/* A return value of 1 means the capability should NOT be exposed to guest. */ > +static int pt_hide_dev_cap(const HostPCIDevice *d, uint8_t grp_id) > +{ > + switch (grp_id) { > + case PCI_CAP_ID_EXP: > + /* The PCI Express Capability Structure of the VF of Intel 82599 10GbE > + * Controller looks trivial, e.g., the PCI Express Capabilities > + * Register is 0. We should not try to expose it to guest. > + * > + * The datasheet is available at > + * http://download.intel.com/design/network/datashts/82599_datasheet.pdf > + * > + * See ''Table 9.7. VF PCIe Configuration Space'' of the datasheet, the > + * PCI Express Capability Structure of the VF of Intel 82599 10GbE > + * Controller looks trivial, e.g., the PCI Express Capabilities > + * Register is 0, so the Capability Version is 0 and > + * pt_pcie_size_init() would fail. > + */ > + if (d->vendor_id == PCI_VENDOR_ID_INTEL && > + d->device_id == PCI_DEVICE_ID_INTEL_82599_VF) { > + return 1; > + } > + break; > + } > + return 0; > +} > + > +/* find emulate register group entry */ > XenPTRegGroup *pt_find_reg_grp(XenPCIPassthroughState *s, uint32_t address) > { > + XenPTRegGroup *entry = NULL; > + > + /* find register group entry */ > + QLIST_FOREACH(entry, &s->reg_grp_tbl, entries) { > + /* check address */ > + if ((entry->base_offset <= address) > + && ((entry->base_offset + entry->size) > address)) { > + return entry; > + } > + } > + > + /* group entry not found */ > return NULL; > } > > +/* find emulate register entry */ > XenPTReg *pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address) > { > + XenPTReg *reg_entry = NULL; > + XenPTRegInfo *reg = NULL; > + uint32_t real_offset = 0; > + > + /* find register entry */ > + QLIST_FOREACH(reg_entry, ®_grp->reg_tbl_list, entries) { > + reg = reg_entry->reg; > + real_offset = reg_grp->base_offset + reg->offset; > + /* check address */ > + if ((real_offset <= address) > + && ((real_offset + reg->size) > address)) { > + return reg_entry; > + } > + } > + > return NULL; > } > + > +/* parse BAR */ > +static PTBarFlag pt_bar_reg_parse(XenPCIPassthroughState *s, XenPTRegInfo *reg) > +{ > + PCIDevice *d = &s->dev; > + XenPTRegion *region = NULL; > + PCIIORegion *r; > + int index = 0; > + > + /* check 64bit BAR */ > + index = pt_bar_offset_to_index(reg->offset); > + if ((0 < index) && (index < PCI_ROM_SLOT)) { > + int flags = s->real_device->io_regions[index - 1].flags;Can you put a comment explaining why you need to shift it? I presume its b/c the ROM slot is its own variable, but it would useful to have that explicitly stated.> + > + if ((flags & IORESOURCE_MEM) && (flags & IORESOURCE_MEM_64)) {You could collapse these two I think? (flags & (IORESOURCE_MEM | IORESOURCE_MEM_64))> + region = &s->bases[index - 1]; > + if (region->bar_flag != PT_BAR_FLAG_UPPER) { > + return PT_BAR_FLAG_UPPER; > + } > + } > + } > + > + /* check unused BAR */ > + r = &d->io_regions[index]; > + if (r->size == 0) { > + return PT_BAR_FLAG_UNUSED; > + } > + > + /* for ExpROM BAR */ > + if (index == PCI_ROM_SLOT) { > + return PT_BAR_FLAG_MEM; > + } > + > + /* check BAR I/O indicator */ > + if (s->real_device->io_regions[index].flags & IORESOURCE_IO) { > + return PT_BAR_FLAG_IO; > + } else { > + return PT_BAR_FLAG_MEM; > + } > +} > + > + > +/**************** > + * general register functions > + */ > + > +/* register initialization function */ > + > +static int pt_common_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + *data = reg->init_val; > + return 0; > +} > + > +/* Read register functions */ > + > +static int pt_byte_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint8_t *value, uint8_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint8_t valid_emu_mask = 0; > + > + /* emulate byte register */ > + valid_emu_mask = reg->emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); > + > + return 0; > +} > +static int pt_word_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint16_t *value, uint16_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint16_t valid_emu_mask = 0; > + > + /* emulate word register */ > + valid_emu_mask = reg->emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); > + > + return 0; > +} > +static int pt_long_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint32_t *value, uint32_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint32_t valid_emu_mask = 0; > + > + /* emulate long register */ > + valid_emu_mask = reg->emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); > + > + return 0; > +} > + > +/* Write register functions */ > + > +static int pt_byte_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint8_t *value, uint8_t dev_value, > + uint8_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint8_t writable_mask = 0; > + uint8_t throughable_mask = 0; > + > + /* modify emulate register */ > + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; > + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); > + > + /* create value for writing to I/O device register */ > + throughable_mask = ~reg->emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); > + > + return 0; > +} > +static int pt_word_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint16_t *value, uint16_t dev_value, > + uint16_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint16_t writable_mask = 0; > + uint16_t throughable_mask = 0; > + > + /* modify emulate register */ > + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; > + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); > + > + /* create value for writing to I/O device register */ > + throughable_mask = ~reg->emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); > + > + return 0; > +} > +static int pt_long_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint32_t *value, uint32_t dev_value, > + uint32_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint32_t writable_mask = 0; > + uint32_t throughable_mask = 0; > + > + /* modify emulate register */ > + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; > + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); > + > + /* create value for writing to I/O device register */ > + throughable_mask = ~reg->emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); > + > + return 0; > +} > + > +/* common restore register fonctions */ > +static int pt_byte_reg_restore(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint32_t real_offset, uint8_t dev_value, > + uint8_t *value) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + PCIDevice *d = &s->dev; > + > + /* use I/O device register''s value as restore value */ > + *value = pci_get_byte(d->config + real_offset); > + > + /* create value for restoring to I/O device register */ > + *value = PT_MERGE_VALUE(*value, dev_value, reg->emu_mask); > + > + return 0; > +} > +static int pt_word_reg_restore(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint32_t real_offset, uint16_t dev_value, > + uint16_t *value) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + PCIDevice *d = &s->dev; > + > + /* use I/O device register''s value as restore value */ > + *value = pci_get_word(d->config + real_offset); > + > + /* create value for restoring to I/O device register */ > + *value = PT_MERGE_VALUE(*value, dev_value, reg->emu_mask); > + > + return 0; > +} > + > + > +/* XenPTRegInfo declaration > + * - only for emulated register (either a part or whole bit). > + * - for passthrough register that need special behavior (like interacting with > + * other component), set emu_mask to all 0 and specify r/w func properly. > + * - do NOT use ALL F for init_val, otherwise the tbl will not be registered. > + */ > + > +/******************** > + * Header Type0 > + */ > + > +static int pt_vendor_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + *data = s->real_device->vendor_id; > + return 0; > +} > +static int pt_device_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + *data = s->real_device->device_id; > + return 0; > +} > +static int pt_status_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + XenPTRegGroup *reg_grp_entry = NULL; > + XenPTReg *reg_entry = NULL; > + uint32_t reg_field = 0; > + > + /* find Header register group */ > + reg_grp_entry = pt_find_reg_grp(s, PCI_CAPABILITY_LIST); > + if (reg_grp_entry) { > + /* find Capabilities Pointer register */ > + reg_entry = pt_find_reg(reg_grp_entry, PCI_CAPABILITY_LIST); > + if (reg_entry) { > + /* check Capabilities Pointer register */ > + if (reg_entry->data) { > + reg_field |= PCI_STATUS_CAP_LIST; > + } else { > + reg_field &= ~PCI_STATUS_CAP_LIST; > + } > + } else { > + xen_shutdown_fatal_error("Internal error: Couldn''t find XenPTReg*" > + " for Capabilities Pointer register." > + " (%s)\n", __func__); > + return -1; > + } > + } else { > + xen_shutdown_fatal_error("Internal error: Couldn''t find XenPTRegGroup" > + " for Header. (%s)\n", __func__); > + return -1; > + } > + > + *data = reg_field; > + return 0; > +} > +static int pt_header_type_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + /* read PCI_HEADER_TYPE */ > + *data = reg->init_val | 0x80; > + return 0; > +} > + > +/* initialize Interrupt Pin register */ > +static int pt_irqpin_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + *data = pci_read_intx(s); > + return 0; > +} > + > +/* Command register */ > +static int pt_cmd_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint16_t *value, uint16_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint16_t valid_emu_mask = 0; > + uint16_t emu_mask = reg->emu_mask; > + > + if (s->is_virtfn) { > + emu_mask |= PCI_COMMAND_MEMORY; > + } > + > + /* emulate word register */ > + valid_emu_mask = emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); > + > + return 0; > +} > +static int pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint16_t *value, uint16_t dev_value,So the *value here is the return value right? How about *response ?> + uint16_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint16_t writable_mask = 0; > + uint16_t throughable_mask = 0; > + uint16_t wr_value = *value; > + uint16_t emu_mask = reg->emu_mask; > + > + if (s->is_virtfn) { > + emu_mask |= PCI_COMMAND_MEMORY; > + } > + > + /* modify emulate register */ > + writable_mask = ~reg->ro_mask & valid_mask; > + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); > + > + /* create value for writing to I/O device register */ > + throughable_mask = ~emu_mask & valid_mask;What does ''throughable'' mean? Is there a better term for it? Say filtered? emulated? cleaned?> + > + if (*value & PCI_COMMAND_INTX_DISABLE) { > + throughable_mask |= PCI_COMMAND_INTX_DISABLE; > + } else { > + if (s->machine_irq) { > + throughable_mask |= PCI_COMMAND_INTX_DISABLE; > + } > + } > + > + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); > + > + /* mapping BAR */ > + pt_bar_mapping(s, wr_value & PCI_COMMAND_IO, > + wr_value & PCI_COMMAND_MEMORY);Ah, here we kick of the real memory mapping with the hypervisor. You might want to expand the comment to say that.> + > + return 0; > +} > +static int pt_cmd_reg_restore(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint32_t real_offset, uint16_t dev_value, > + uint16_t *value) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + PCIDevice *d = &s->dev; > + uint16_t restorable_mask = 0; > + > + /* use I/O device register''s value as restore value */ > + *value = pci_get_word(d->config + real_offset); > + > + /* create value for restoring to I/O device registers/to/the/> + * but do not include Fast Back-to-Back Enable bit. > + */ > + restorable_mask = reg->emu_mask & ~PCI_COMMAND_FAST_BACK; > + *value = PT_MERGE_VALUE(*value, dev_value, restorable_mask); > + > + if (!s->machine_irq) { > + *value |= PCI_COMMAND_INTX_DISABLE; > + } else { > + *value &= ~PCI_COMMAND_INTX_DISABLE; > + } > + > + return 0; > +} > + > +/* BAR */ > +#define PT_BAR_MEM_RO_MASK 0x0000000F /* BAR ReadOnly mask(Memory) */ > +#define PT_BAR_MEM_EMU_MASK 0xFFFFFFF0 /* BAR emul mask(Memory) */ > +#define PT_BAR_IO_RO_MASK 0x00000003 /* BAR ReadOnly mask(I/O) */ > +#define PT_BAR_IO_EMU_MASK 0xFFFFFFFC /* BAR emul mask(I/O) */ > + > +static inline uint32_t base_address_with_flags(HostPCIIORegion *hr) > +{ > + if ((hr->flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { > + return hr->base_addr | (hr->flags & ~PCI_BASE_ADDRESS_IO_MASK); > + } else { > + return hr->base_addr | (hr->flags & ~PCI_BASE_ADDRESS_MEM_MASK); > + } > +} > + > +static int pt_bar_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, > + uint32_t real_offset, uint32_t *data) > +{ > + uint32_t reg_field = 0; > + int index; > + > + /* get BAR index */ > + index = pt_bar_offset_to_index(reg->offset); > + if (index < 0) {Should we check for index > PCI_ROM_BASE as well?> + PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); > + return -1; > + } > + > + /* set initial guest physical base address to -1 */ > + s->bases[index].e_physbase = -1; > + > + /* set BAR flag */ > + s->bases[index].bar_flag = pt_bar_reg_parse(s, reg); > + if (s->bases[index].bar_flag == PT_BAR_FLAG_UNUSED) { > + reg_field = PT_INVALID_REG; > + } > + > + *data = reg_field; > + return 0; > +} > +static int pt_bar_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint32_t *value, uint32_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint32_t valid_emu_mask = 0; > + uint32_t bar_emu_mask = 0; > + int index; > + > + /* get BAR index */ > + index = pt_bar_offset_to_index(reg->offset); > + if (index < 0) {Should we check for index > PCI_ROM_BASE?> + PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); > + return -1; > + } > + > + /* use fixed-up value from kernel sysfs */ > + *value = base_address_with_flags(&s->real_device->io_regions[index]); > + > + /* set emulate mask depend on BAR flag */ > + switch (s->bases[index].bar_flag) { > + case PT_BAR_FLAG_MEM: > + bar_emu_mask = PT_BAR_MEM_EMU_MASK; > + break; > + case PT_BAR_FLAG_IO: > + bar_emu_mask = PT_BAR_IO_EMU_MASK; > + break; > + case PT_BAR_FLAG_UPPER: > + bar_emu_mask = PT_BAR_ALLF; > + break; > + default: > + break; > + } > + > + /* emulate BAR */ > + valid_emu_mask = bar_emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); > + > + return 0; > +} > +static int pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint32_t *value, uint32_t dev_value, > + uint32_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + XenPTRegGroup *reg_grp_entry = NULL; > + XenPTReg *reg_entry = NULL; > + XenPTRegion *base = NULL; > + PCIDevice *d = &s->dev; > + PCIIORegion *r; > + uint32_t writable_mask = 0; > + uint32_t throughable_mask = 0; > + uint32_t bar_emu_mask = 0; > + uint32_t bar_ro_mask = 0; > + uint32_t new_addr, last_addr; > + uint32_t prev_offset; > + uint32_t r_size = 0; > + int index = 0; > + > + /* get BAR index */ > + index = pt_bar_offset_to_index(reg->offset); > + if (index < 0) { > + PT_ERR(d, "Internal error: Invalid BAR index [%d].\n", index); > + return -1; > + } > + > + r = &d->io_regions[index]; > + base = &s->bases[index]; > + r_size = pt_get_emul_size(base->bar_flag, r->size); > + > + /* set emulate mask and read-only mask depend on BAR flag */^^values ^^ the> + switch (s->bases[index].bar_flag) { > + case PT_BAR_FLAG_MEM: > + bar_emu_mask = PT_BAR_MEM_EMU_MASK; > + bar_ro_mask = PT_BAR_MEM_RO_MASK | (r_size - 1); > + break; > + case PT_BAR_FLAG_IO: > + bar_emu_mask = PT_BAR_IO_EMU_MASK; > + bar_ro_mask = PT_BAR_IO_RO_MASK | (r_size - 1); > + break; > + case PT_BAR_FLAG_UPPER: > + bar_emu_mask = PT_BAR_ALLF; > + bar_ro_mask = 0; /* all upper 32bit are R/W */ > + break; > + default: > + break; > + } > + > + /* modify emulate register */ > + writable_mask = bar_emu_mask & ~bar_ro_mask & valid_mask; > + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); > + > + /* check whether we need to update the virtual region address or not */ > + switch (s->bases[index].bar_flag) { > + case PT_BAR_FLAG_MEM: > + /* nothing to do */ > + break; > + case PT_BAR_FLAG_IO: > + new_addr = cfg_entry->data; > + last_addr = new_addr + r_size - 1; > + /* check invalid address */ > + if (last_addr <= new_addr || !new_addr || last_addr >= UINT16_MAX) { > + /* check 64K range */ > + if ((last_addr >= UINT16_MAX) && > + (cfg_entry->data != (PT_BAR_ALLF & ~bar_ro_mask))) {You seem to be using ''PT_BAR_ALLF & ~bar_ro_mask'' this function and never actually use the bar_ro_mask by itself. Perhaps you can (before this switch statement) do: bar_ro_mask = PT_BAR_ALLF & ~bar_ro_mask;> + PT_WARN(d, "Guest attempt to set Base Address " > + "over the 64KB. (offset: 0x%02x," > + " addr: 0x%08x, size: 0x%08x)\n", > + reg->offset, new_addr, r_size); > + } > + /* just remove mapping */ > + r->addr = PCI_BAR_UNMAPPED; > + goto exit; > + } > + break; > + case PT_BAR_FLAG_UPPER: > + if (cfg_entry->data) { > + if (cfg_entry->data != (PT_BAR_ALLF & ~bar_ro_mask)) { > + PT_WARN(d, "Guest attempt to set high MMIO Base Address. " > + "Ignore mapping. " > + "(offset: 0x%02x, high address: 0x%08x)\n", > + reg->offset, cfg_entry->data); > + } > + /* clear lower address */ > + d->io_regions[index-1].addr = -1; > + } else { > + /* find lower 32bit BAR */ > + prev_offset = (reg->offset - 4); > + reg_grp_entry = pt_find_reg_grp(s, prev_offset); > + if (reg_grp_entry) { > + reg_entry = pt_find_reg(reg_grp_entry, prev_offset); > + if (reg_entry) { > + /* restore lower address */ > + d->io_regions[index-1].addr = reg_entry->data; > + } else { > + return -1; > + } > + } else { > + return -1; > + } > + } > + > + /* never mapping the ''empty'' upper region, > + * because we''ll do it enough for the lower region.umm, not sure what you mean here. Do you mean to say: "Do not map the ''empty'' upper region b/c we do not need to. As we only need the lower region."?> + */ > + r->addr = -1; > + goto exit; > + default: > + break; > + } > + > + /* update the corresponding virtual region address */ > + /* > + * When guest code tries to get block size of mmio, it will write all "1"s > + * into pci bar register. In this case, cfg_entry->data == writable_mask. > + * Especially for devices with large mmio, the value of writable_mask > + * is likely to be a guest physical address that has been mapped to ram > + * rather than mmio. Remapping this value to mmio should be prevented. > + */ > + > + if (cfg_entry->data != writable_mask) { > + r->addr = cfg_entry->data; > + } > + > +exit: > + /* create value for writing to I/O device register */ > + throughable_mask = ~bar_emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); > + > + /* After BAR reg update, we need to remap BAR */ > + reg_grp_entry = pt_find_reg_grp(s, PCI_COMMAND); > + if (reg_grp_entry) { > + reg_entry = pt_find_reg(reg_grp_entry, PCI_COMMAND); > + if (reg_entry) { > + pt_bar_mapping_one(s, index, reg_entry->data & PCI_COMMAND_IO, > + reg_entry->data & PCI_COMMAND_MEMORY); > + } > + } > + > + return 0; > +} > +static int pt_bar_reg_restore(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint32_t real_offset, uint32_t dev_value, > + uint32_t *value) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint32_t bar_emu_mask = 0; > + int index = 0; > + > + /* get BAR index */ > + index = pt_bar_offset_to_index(reg->offset); > + if (index < 0) { > + PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); > + return -1; > + } > + > + /* use value from kernel sysfs */ > + if (s->bases[index].bar_flag == PT_BAR_FLAG_UPPER) { > + *value = s->real_device->io_regions[index - 1].base_addr >> 32; > + } else { > + *value = base_address_with_flags(&s->real_device->io_regions[index]); > + } > + > + /* set emulate mask depend on BAR flag */ > + switch (s->bases[index].bar_flag) { > + case PT_BAR_FLAG_MEM: > + bar_emu_mask = PT_BAR_MEM_EMU_MASK; > + break; > + case PT_BAR_FLAG_IO: > + bar_emu_mask = PT_BAR_IO_EMU_MASK; > + break; > + case PT_BAR_FLAG_UPPER: > + bar_emu_mask = PT_BAR_ALLF; > + break; > + default: > + break; > + } > + > + /* create value for restoring to I/O device register */ > + *value = PT_MERGE_VALUE(*value, dev_value, bar_emu_mask); > + > + return 0; > +} > + > +/* write Exp ROM BAR */ > +static int pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, > + XenPTReg *cfg_entry, uint32_t *value, > + uint32_t dev_value, uint32_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + XenPTRegGroup *reg_grp_entry = NULL; > + XenPTReg *reg_entry = NULL; > + XenPTRegion *base = NULL; > + PCIDevice *d = (PCIDevice *)&s->dev; > + PCIIORegion *r; > + uint32_t writable_mask = 0; > + uint32_t throughable_mask = 0; > + pcibus_t r_size = 0; > + uint32_t bar_emu_mask = 0; > + uint32_t bar_ro_mask = 0; > + > + r = &d->io_regions[PCI_ROM_SLOT]; > + r_size = r->size; > + base = &s->bases[PCI_ROM_SLOT]; > + /* align memory type resource size */ > + pt_get_emul_size(base->bar_flag, r_size); > + > + /* set emulate mask and read-only mask */ > + bar_emu_mask = reg->emu_mask; > + bar_ro_mask = (reg->ro_mask | (r_size - 1)) & ~PCI_ROM_ADDRESS_ENABLE; > + > + /* modify emulate register */ > + writable_mask = ~bar_ro_mask & valid_mask; > + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); > + > + /* update the corresponding virtual region address */ > + /* > + * When guest code tries to get block size of mmio, it will write all "1"s > + * into pci bar register. In this case, cfg_entry->data == writable_mask. > + * Especially for devices with large mmio, the value of writable_mask > + * is likely to be a guest physical address that has been mapped to ram > + * rather than mmio. Remapping this value to mmio should be prevented. > + */ > + > + if (cfg_entry->data != writable_mask) { > + r->addr = cfg_entry->data; > + } > + > + /* create value for writing to I/O device register */ > + throughable_mask = ~bar_emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); > + > + /* After BAR reg update, we need to remap BAR*/ > + reg_grp_entry = pt_find_reg_grp(s, PCI_COMMAND); > + if (reg_grp_entry) { > + reg_entry = pt_find_reg(reg_grp_entry, PCI_COMMAND); > + if (reg_entry) { > + pt_bar_mapping_one(s, PCI_ROM_SLOT, > + reg_entry->data & PCI_COMMAND_IO, > + reg_entry->data & PCI_COMMAND_MEMORY); > + } > + } > + > + return 0; > +} > +/* restore ROM BAR */ > +static int pt_exp_rom_bar_reg_restore(XenPCIPassthroughState *s, > + XenPTReg *cfg_entry, > + uint32_t real_offset, > + uint32_t dev_value, uint32_t *value) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint32_t v; > + > + if (host_pci_get_long(s->real_device, PCI_ROM_ADDRESS, &v)) { > + return -1; > + } > + /* use value from kernel sysfs */ > + *value = PT_MERGE_VALUE(v, dev_value, reg->emu_mask); > + return 0; > +} > + > +/* Header Type0 reg static infomation table */ > +static XenPTRegInfo pt_emu_reg_header0_tbl[] = { > + /* Vendor ID reg */ > + { > + .offset = PCI_VENDOR_ID, > + .size = 2, > + .init_val = 0x0000, > + .ro_mask = 0xFFFF, > + .emu_mask = 0xFFFF, > + .init = pt_vendor_reg_init, > + .u.w.read = pt_word_reg_read, > + .u.w.write = pt_word_reg_write, > + .u.w.restore = NULL, > + }, > + /* Device ID reg */ > + { > + .offset = PCI_DEVICE_ID, > + .size = 2, > + .init_val = 0x0000, > + .ro_mask = 0xFFFF, > + .emu_mask = 0xFFFF, > + .init = pt_device_reg_init, > + .u.w.read = pt_word_reg_read, > + .u.w.write = pt_word_reg_write, > + .u.w.restore = NULL, > + }, > + /* Command reg */ > + { > + .offset = PCI_COMMAND, > + .size = 2, > + .init_val = 0x0000, > + .ro_mask = 0xF880, > + .emu_mask = 0x0740, > + .init = pt_common_reg_init, > + .u.w.read = pt_cmd_reg_read, > + .u.w.write = pt_cmd_reg_write, > + .u.w.restore = pt_cmd_reg_restore, > + }, > + /* Capabilities Pointer reg */ > + { > + .offset = PCI_CAPABILITY_LIST, > + .size = 1, > + .init_val = 0x00, > + .ro_mask = 0xFF, > + .emu_mask = 0xFF, > + .init = pt_ptr_reg_init, > + .u.b.read = pt_byte_reg_read, > + .u.b.write = pt_byte_reg_write, > + .u.b.restore = NULL, > + }, > + /* Status reg */ > + /* use emulated Cap Ptr value to initialize, > + * so need to be declared after Cap Ptr reg > + */ > + { > + .offset = PCI_STATUS, > + .size = 2, > + .init_val = 0x0000, > + .ro_mask = 0x06FF, > + .emu_mask = 0x0010, > + .init = pt_status_reg_init, > + .u.w.read = pt_word_reg_read, > + .u.w.write = pt_word_reg_write, > + .u.w.restore = NULL, > + }, > + /* Cache Line Size reg */ > + { > + .offset = PCI_CACHE_LINE_SIZE, > + .size = 1, > + .init_val = 0x00, > + .ro_mask = 0x00, > + .emu_mask = 0xFF, > + .init = pt_common_reg_init, > + .u.b.read = pt_byte_reg_read, > + .u.b.write = pt_byte_reg_write, > + .u.b.restore = pt_byte_reg_restore, > + }, > + /* Latency Timer reg */ > + { > + .offset = PCI_LATENCY_TIMER, > + .size = 1, > + .init_val = 0x00, > + .ro_mask = 0x00, > + .emu_mask = 0xFF, > + .init = pt_common_reg_init, > + .u.b.read = pt_byte_reg_read, > + .u.b.write = pt_byte_reg_write, > + .u.b.restore = pt_byte_reg_restore, > + }, > + /* Header Type reg */ > + { > + .offset = PCI_HEADER_TYPE, > + .size = 1, > + .init_val = 0x00, > + .ro_mask = 0xFF, > + .emu_mask = 0x00, > + .init = pt_header_type_reg_init, > + .u.b.read = pt_byte_reg_read, > + .u.b.write = pt_byte_reg_write, > + .u.b.restore = NULL, > + }, > + /* Interrupt Line reg */ > + { > + .offset = PCI_INTERRUPT_LINE, > + .size = 1, > + .init_val = 0x00, > + .ro_mask = 0x00, > + .emu_mask = 0xFF, > + .init = pt_common_reg_init, > + .u.b.read = pt_byte_reg_read, > + .u.b.write = pt_byte_reg_write, > + .u.b.restore = NULL, > + }, > + /* Interrupt Pin reg */ > + { > + .offset = PCI_INTERRUPT_PIN, > + .size = 1, > + .init_val = 0x00, > + .ro_mask = 0xFF, > + .emu_mask = 0xFF, > + .init = pt_irqpin_reg_init, > + .u.b.read = pt_byte_reg_read, > + .u.b.write = pt_byte_reg_write, > + .u.b.restore = NULL, > + }, > + /* BAR 0 reg */ > + /* mask of BAR need to be decided later, depends on IO/MEM type */ > + { > + .offset = PCI_BASE_ADDRESS_0, > + .size = 4, > + .init_val = 0x00000000, > + .init = pt_bar_reg_init, > + .u.dw.read = pt_bar_reg_read, > + .u.dw.write = pt_bar_reg_write, > + .u.dw.restore = pt_bar_reg_restore, > + }, > + /* BAR 1 reg */ > + { > + .offset = PCI_BASE_ADDRESS_1, > + .size = 4, > + .init_val = 0x00000000, > + .init = pt_bar_reg_init, > + .u.dw.read = pt_bar_reg_read, > + .u.dw.write = pt_bar_reg_write, > + .u.dw.restore = pt_bar_reg_restore, > + }, > + /* BAR 2 reg */ > + { > + .offset = PCI_BASE_ADDRESS_2, > + .size = 4, > + .init_val = 0x00000000, > + .init = pt_bar_reg_init, > + .u.dw.read = pt_bar_reg_read, > + .u.dw.write = pt_bar_reg_write, > + .u.dw.restore = pt_bar_reg_restore, > + }, > + /* BAR 3 reg */ > + { > + .offset = PCI_BASE_ADDRESS_3, > + .size = 4, > + .init_val = 0x00000000, > + .init = pt_bar_reg_init, > + .u.dw.read = pt_bar_reg_read, > + .u.dw.write = pt_bar_reg_write, > + .u.dw.restore = pt_bar_reg_restore, > + }, > + /* BAR 4 reg */ > + { > + .offset = PCI_BASE_ADDRESS_4, > + .size = 4, > + .init_val = 0x00000000, > + .init = pt_bar_reg_init, > + .u.dw.read = pt_bar_reg_read, > + .u.dw.write = pt_bar_reg_write, > + .u.dw.restore = pt_bar_reg_restore, > + }, > + /* BAR 5 reg */ > + { > + .offset = PCI_BASE_ADDRESS_5, > + .size = 4, > + .init_val = 0x00000000, > + .init = pt_bar_reg_init, > + .u.dw.read = pt_bar_reg_read, > + .u.dw.write = pt_bar_reg_write, > + .u.dw.restore = pt_bar_reg_restore, > + }, > + /* Expansion ROM BAR reg */ > + { > + .offset = PCI_ROM_ADDRESS, > + .size = 4, > + .init_val = 0x00000000, > + .ro_mask = 0x000007FE, > + .emu_mask = 0xFFFFF800, > + .init = pt_bar_reg_init, > + .u.dw.read = pt_long_reg_read, > + .u.dw.write = pt_exp_rom_bar_reg_write, > + .u.dw.restore = pt_exp_rom_bar_reg_restore, > + }, > + { > + .size = 0, > + }, > +}; > + > + > +/********************************* > + * Vital Product Data Capability > + */ > + > +/* Vital Product Data Capability Structure reg static infomation table */ > +static XenPTRegInfo pt_emu_reg_vpd_tbl[] = { > + { > + .offset = PCI_CAP_LIST_NEXT, > + .size = 1, > + .init_val = 0x00, > + .ro_mask = 0xFF, > + .emu_mask = 0xFF, > + .init = pt_ptr_reg_init, > + .u.b.read = pt_byte_reg_read, > + .u.b.write = pt_byte_reg_write, > + .u.b.restore = NULL, > + }, > + { > + .size = 0, > + }, > +}; > + > + > +/************************************** > + * Vendor Specific Capability > + */ > + > +/* Vendor Specific Capability Structure reg static infomation table */ > +static XenPTRegInfo pt_emu_reg_vendor_tbl[] = { > + { > + .offset = PCI_CAP_LIST_NEXT, > + .size = 1, > + .init_val = 0x00, > + .ro_mask = 0xFF, > + .emu_mask = 0xFF, > + .init = pt_ptr_reg_init, > + .u.b.read = pt_byte_reg_read, > + .u.b.write = pt_byte_reg_write, > + .u.b.restore = NULL, > + }, > + { > + .size = 0, > + }, > +}; > + > + > +/***************************** > + * PCI Express Capability > + */ > + > +/* initialize Link Control register */ > +static int pt_linkctrl_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + uint8_t cap_ver = 0; > + uint8_t dev_type = 0; > + > + cap_ver = pci_get_byte(s->dev.config + real_offset - reg->offset > + + PCI_EXP_FLAGS) > + & PCI_EXP_FLAGS_VERS;Hmm. Weird tabbing? Or maybe it is my mailer?> + dev_type = (pci_get_byte(s->dev.config + real_offset - reg->offset > + + PCI_EXP_FLAGS) > + & PCI_EXP_FLAGS_TYPE) >> 4;Here it looks better.. but still not sure about the "+ PCI_EXP_FLAGS"?> + > + /* no need to initialize in case of Root Complex Integrated Endpoint > + * with cap_ver 1.x > + */ > + if ((dev_type == PCI_EXP_TYPE_RC_END) && (cap_ver == 1)) { > + *data = PT_INVALID_REG; > + } > + > + *data = reg->init_val; > + return 0; > +} > +/* initialize Device Control 2 register */ > +static int pt_devctrl2_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + uint8_t cap_ver = 0; > + > + cap_ver = pci_get_byte(s->dev.config + real_offset - reg->offset > + + PCI_EXP_FLAGS) > + & PCI_EXP_FLAGS_VERS;Tab-mayhem!> + > + /* no need to initialize in case of cap_ver 1.x */ > + if (cap_ver == 1) { > + *data = PT_INVALID_REG; > + } > + > + *data = reg->init_val; > + return 0; > +} > +/* initialize Link Control 2 register */ > +static int pt_linkctrl2_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + uint32_t reg_field = 0; > + uint8_t cap_ver = 0; > + > + cap_ver = pci_get_byte(s->dev.config + real_offset - reg->offset > + + PCI_EXP_FLAGS) > + & PCI_EXP_FLAGS_VERS;.. strikes again!> + > + /* no need to initialize in case of cap_ver 1.x */ > + if (cap_ver == 1) { > + reg_field = PT_INVALID_REG; > + } else { > + /* set Supported Link Speed */ > + uint8_t lnkcap = pci_get_byte(s->dev.config + real_offset - reg->offset > + + PCI_EXP_LNKCAP); > + reg_field |= PCI_EXP_LNKCAP_SLS & lnkcap; > + } > + > + *data = reg_field; > + return 0; > +} > + > +/* PCI Express Capability Structure reg static infomation table */ > +static XenPTRegInfo pt_emu_reg_pcie_tbl[] = { > + /* Next Pointer reg */ > + { > + .offset = PCI_CAP_LIST_NEXT, > + .size = 1, > + .init_val = 0x00, > + .ro_mask = 0xFF, > + .emu_mask = 0xFF, > + .init = pt_ptr_reg_init, > + .u.b.read = pt_byte_reg_read, > + .u.b.write = pt_byte_reg_write, > + .u.b.restore = NULL, > + }, > + /* Device Capabilities reg */ > + { > + .offset = PCI_EXP_DEVCAP, > + .size = 4, > + .init_val = 0x00000000, > + .ro_mask = 0x1FFCFFFF, > + .emu_mask = 0x10000000, > + .init = pt_common_reg_init, > + .u.dw.read = pt_long_reg_read, > + .u.dw.write = pt_long_reg_write, > + .u.dw.restore = NULL, > + }, > + /* Device Control reg */ > + { > + .offset = PCI_EXP_DEVCTL, > + .size = 2, > + .init_val = 0x2810, > + .ro_mask = 0x8400, > + .emu_mask = 0xFFFF, > + .init = pt_common_reg_init, > + .u.w.read = pt_word_reg_read, > + .u.w.write = pt_word_reg_write, > + .u.w.restore = pt_word_reg_restore, > + }, > + /* Link Control reg */ > + { > + .offset = PCI_EXP_LNKCTL, > + .size = 2, > + .init_val = 0x0000, > + .ro_mask = 0xFC34, > + .emu_mask = 0xFFFF, > + .init = pt_linkctrl_reg_init, > + .u.w.read = pt_word_reg_read, > + .u.w.write = pt_word_reg_write, > + .u.w.restore = pt_word_reg_restore, > + }, > + /* Device Control 2 reg */ > + { > + .offset = 0x28, > + .size = 2, > + .init_val = 0x0000, > + .ro_mask = 0xFFE0, > + .emu_mask = 0xFFFF, > + .init = pt_devctrl2_reg_init, > + .u.w.read = pt_word_reg_read, > + .u.w.write = pt_word_reg_write, > + .u.w.restore = pt_word_reg_restore, > + }, > + /* Link Control 2 reg */ > + { > + .offset = 0x30, > + .size = 2, > + .init_val = 0x0000, > + .ro_mask = 0xE040, > + .emu_mask = 0xFFFF, > + .init = pt_linkctrl2_reg_init, > + .u.w.read = pt_word_reg_read, > + .u.w.write = pt_word_reg_write, > + .u.w.restore = pt_word_reg_restore, > + }, > + { > + .size = 0, > + }, > +}; > + > + > +/********************************* > + * Power Management Capability > + */ > + > +/* initialize Power Management Capabilities register */ > +static int pt_pmc_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + PCIDevice *d = &s->dev; > + > + if (s->power_mgmt) { > + /* set Power Management Capabilities register */ > + s->pm_state->pmc_field = pci_get_word(d->config + real_offset); > + } > + > + *data = reg->init_val; > + return 0; > +} > +/* initialize PCI Power Management Control/Status register */ > +static int pt_pmcsr_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + PCIDevice *d = &s->dev; > + uint16_t cap_ver = 0; > + uint16_t v = 0; > + > + if (!s->power_mgmt) { > + *data = reg->init_val; > + return 0; > + } > + > + /* check PCI Power Management support version */ > + cap_ver = s->pm_state->pmc_field & PCI_PM_CAP_VER_MASK; > + > + if (cap_ver > 2) { > + /* set No Soft Reset */ > + s->pm_state->no_soft_reset > + pci_get_byte(d->config + real_offset) & PCI_PM_CTRL_NO_SOFT_RESET; > + } > + > + host_pci_get_word(s->real_device, real_offset, &v); > + /* wake up real physical device */ > + switch (v & PCI_PM_CTRL_STATE_MASK) { > + case 0: > + break; > + case 1: > + PT_LOG(d, "Power state transition D1 -> D0active\n"); > + host_pci_set_word(s->real_device, real_offset, 0); > + break; > + case 2: > + PT_LOG(d, "Power state transition D2 -> D0active\n"); > + host_pci_set_word(s->real_device, real_offset, 0); > + usleep(200); > + break; > + case 3: > + PT_LOG(d, "Power state transition D3hot -> D0active\n"); > + host_pci_set_word(s->real_device, real_offset, 0); > + usleep(10 * 1000); > + if (pt_init_pci_config(s)) { > + return -1; > + } > + break; > + } > + > + *data = reg->init_val; > + return 0; > +} > +/* read Power Management Control/Status register */ > +static int pt_pmcsr_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint16_t *value, uint16_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint16_t valid_emu_mask = reg->emu_mask; > + > + if (!s->power_mgmt) { > + valid_emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; > + } > + > + valid_emu_mask = valid_emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); > + > + return 0; > +} > +/* reset Interrupt and I/O resource */ > +static void pt_reset_interrupt_and_io_mapping(XenPCIPassthroughState *s) > +{ > + PCIDevice *d = &s->dev; > + PCIIORegion *r; > + int i = 0; > + uint8_t e_device = 0; > + uint8_t e_intx = 0; > + > + /* unbind INTx */ > + e_device = PCI_SLOT(s->dev.devfn); > + e_intx = pci_intx(s); > + > + if (s->machine_irq) { > + if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, s->machine_irq, > + PT_IRQ_TYPE_PCI, 0, e_device, e_intx, 0)) { > + PT_ERR(d, "Unbinding of interrupt failed!\n"); > + } > + } > + > + /* clear all virtual region address */ > + for (i = 0; i < PCI_NUM_REGIONS; i++) { > + r = &d->io_regions[i]; > + r->addr = -1; > + } > + > + /* unmapping BAR */ > + pt_bar_mapping(s, 0, 0); > +} > +/* check power state transition */ > +static int check_power_state(XenPCIPassthroughState *s) > +{ > + XenPTPM *pm_state = s->pm_state; > + PCIDevice *d = &s->dev; > + uint16_t read_val = 0; > + uint16_t cur_state = 0; > + > + /* get current power state */ > + if (host_pci_get_word(s->real_device, pm_state->pm_base + PCI_PM_CTRL, > + &read_val)) { > + return -1; > + } > + cur_state = read_val & PCI_PM_CTRL_STATE_MASK; > + > + if (pm_state->req_state != cur_state) { > + PT_ERR(d, "Failed to change power state. " > + "(requested state: %d, current state: %d)\n", > + pm_state->req_state, cur_state); > + return -1; > + } > + return 0; > +} > +/* write Power Management Control/Status register */ > +static void pt_from_d3hot_to_d0_with_reset(void *opaque) > +{ > + XenPCIPassthroughState *s = opaque; > + XenPTPM *pm_state = s->pm_state; > + int ret = 0; > + > + /* check power state */ > + ret = check_power_state(s); > + > + if (ret < 0) { > + goto out; > + } > + > + pt_init_pci_config(s); > + > +out: > + /* power state transition flags off */ > + pm_state->flags &= ~PT_FLAG_TRANSITING; > + > + qemu_free_timer(pm_state->pm_timer); > + pm_state->pm_timer = NULL; > +} > +static void pt_default_power_transition(void *opaque) > +{ > + XenPCIPassthroughState *ptdev = opaque; > + XenPTPM *pm_state = ptdev->pm_state; > + > + /* check power state */ > + check_power_state(ptdev); > + > + /* power state transition flags off */ > + pm_state->flags &= ~PT_FLAG_TRANSITING; > + > + qemu_free_timer(pm_state->pm_timer); > + pm_state->pm_timer = NULL; > +} > +static int pt_pmcsr_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint16_t *value, uint16_t dev_value, > + uint16_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + PCIDevice *d = &s->dev; > + uint16_t emu_mask = reg->emu_mask; > + uint16_t writable_mask = 0; > + uint16_t throughable_mask = 0; > + XenPTPM *pm_state = s->pm_state; > + > + if (!s->power_mgmt) { > + emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; > + } > + > + /* modify emulate register */ > + writable_mask = emu_mask & ~reg->ro_mask & valid_mask; > + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); > + > + /* create value for writing to I/O device register */ > + throughable_mask = ~emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); > + > + if (!s->power_mgmt) { > + return 0; > + } > + > + /* set I/O device power state */ > + pm_state->cur_state = dev_value & PCI_PM_CTRL_STATE_MASK; > + > + /* set Guest requested PowerState */ > + pm_state->req_state = *value & PCI_PM_CTRL_STATE_MASK; > + > + /* check power state transition or not */ > + if (pm_state->cur_state == pm_state->req_state) { > + /* not power state transition */.. No power state transition.> + return 0; > + } > + > + /* check enable power state transition */ > + if ((pm_state->req_state != 0) && > + (pm_state->cur_state > pm_state->req_state)) { > + PT_ERR(d, "Invalid power transition. " > + "(requested state: %d, current state: %d)\n", > + pm_state->req_state, pm_state->cur_state); > + > + return 0; > + } > + > + /* check if this device supports the requested power state */ > + if (((pm_state->req_state == 1) && !(pm_state->pmc_field & PCI_PM_CAP_D1)) > + || ((pm_state->req_state == 2) && > + !(pm_state->pmc_field & PCI_PM_CAP_D2))) { > + PT_ERR(d, "Invalid power transition. " > + "(requested state: %d, current state: %d)\n", > + pm_state->req_state, pm_state->cur_state); > + > + return 0; > + } > + > + /* in case of transition related to D3hot, it''s necessary to wait 10 ms. > + * But because writing to register will be performed later on actually,''performed later on,''> + * don''t start QEMUTimer right now, just alloc and init QEMUTimer here.You might want to say where it will be started.> + */ > + if ((pm_state->cur_state == 3) || (pm_state->req_state == 3)) { > + if (pm_state->req_state == 0) { > + /* alloc and init QEMUTimer */ > + if (!pm_state->no_soft_reset) { > + pm_state->pm_timer = qemu_new_timer_ms(rt_clock, > + pt_from_d3hot_to_d0_with_reset, s); > + > + /* reset Interrupt and I/O resource mapping */ > + pt_reset_interrupt_and_io_mapping(s); > + } else { > + pm_state->pm_timer = qemu_new_timer_ms(rt_clock, > + pt_default_power_transition, s); > + } > + } else { > + /* alloc and init QEMUTimer */ > + pm_state->pm_timer = qemu_new_timer_ms(rt_clock, > + pt_default_power_transition, s); > + } > + > + /* set power state transition delay */ > + pm_state->pm_delay = 10; > + > + /* power state transition flags on */ > + pm_state->flags |= PT_FLAG_TRANSITING; > + } > + /* in case of transition related to D0, D1 and D2, > + * no need to use QEMUTimer. > + * So, we perfom writing to register here and then read it back. > + */ > + else { > + /* write power state to I/O device register */ > + host_pci_set_word(s->real_device, pm_state->pm_base + PCI_PM_CTRL, > + *value); > + > + /* in case of transition related to D2, > + * it''s necessary to wait 200 usec. > + * But because QEMUTimer do not support microsec unit right now, > + * so we do wait ourself here.''.. QEMUTimer does not support microsec resolution, we will wait here.''> + */ > + if ((pm_state->cur_state == 2) || (pm_state->req_state == 2)) { > + usleep(200); > + } > + > + /* check power state */ > + check_power_state(s); > + > + /* recreate value for writing to I/O device register */ > + if (host_pci_get_word(s->real_device, pm_state->pm_base + PCI_PM_CTRL, > + value)) { > + return -1; > + } > + } > + > + return 0; > +} > + > +/* restore Power Management Control/Status register */ > +static int pt_pmcsr_reg_restore(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint32_t real_offset, uint16_t dev_value, > + uint16_t *value) > +{ > + /* create value for restoring to I/O device register > + * No need to restore, just clear PME Enable and PME Status bit > + * Note: register type of PME Status bit is RW1C, so clear by writing 1b > + */ > + *value = (dev_value & ~PCI_PM_CTRL_PME_ENABLE) | PCI_PM_CTRL_PME_STATUS; > + > + return 0; > +} > + > + > +/* Power Management Capability reg static infomation table */ > +static XenPTRegInfo pt_emu_reg_pm_tbl[] = { > + /* Next Pointer reg */ > + { > + .offset = PCI_CAP_LIST_NEXT, > + .size = 1, > + .init_val = 0x00, > + .ro_mask = 0xFF, > + .emu_mask = 0xFF, > + .init = pt_ptr_reg_init, > + .u.b.read = pt_byte_reg_read, > + .u.b.write = pt_byte_reg_write, > + .u.b.restore = NULL, > + }, > + /* Power Management Capabilities reg */ > + { > + .offset = PCI_CAP_FLAGS, > + .size = 2, > + .init_val = 0x0000, > + .ro_mask = 0xFFFF, > + .emu_mask = 0xF9C8, > + .init = pt_pmc_reg_init, > + .u.w.read = pt_word_reg_read, > + .u.w.write = pt_word_reg_write, > + .u.w.restore = NULL, > + }, > + /* PCI Power Management Control/Status reg */ > + { > + .offset = PCI_PM_CTRL, > + .size = 2, > + .init_val = 0x0008, > + .ro_mask = 0xE1FC, > + .emu_mask = 0x8100, > + .init = pt_pmcsr_reg_init, > + .u.w.read = pt_pmcsr_reg_read, > + .u.w.write = pt_pmcsr_reg_write, > + .u.w.restore = pt_pmcsr_reg_restore, > + }, > + { > + .size = 0, > + }, > +}; > + > + > +/**************************** > + * Capabilities > + */ > + > +/* AER register operations */ > + > +static void aer_save_one_register(XenPCIPassthroughState *s, int offset) > +{ > + PCIDevice *d = &s->dev; > + uint32_t aer_base = s->pm_state->aer_base; > + uint32_t val = 0; > + > + if (host_pci_get_long(s->real_device, aer_base + offset, &val)) { > + return; > + } > + pci_set_long(d->config + aer_base + offset, val); > +} > +static void pt_aer_reg_save(XenPCIPassthroughState *s) > +{ > + /* after reset, following register values should be restored. > + * So, save them. > + */ > + aer_save_one_register(s, PCI_ERR_UNCOR_MASK); > + aer_save_one_register(s, PCI_ERR_UNCOR_SEVER); > + aer_save_one_register(s, PCI_ERR_COR_MASK); > + aer_save_one_register(s, PCI_ERR_CAP); > +} > +static void aer_restore_one_register(XenPCIPassthroughState *s, int offset) > +{ > + PCIDevice *d = &s->dev; > + uint32_t aer_base = s->pm_state->aer_base; > + uint32_t config = 0; > + > + config = pci_get_long(d->config + aer_base + offset); > + host_pci_set_long(s->real_device, aer_base + offset, config); > +} > +static void pt_aer_reg_restore(XenPCIPassthroughState *s) > +{ > + /* the following registers should be reconfigured to correct valuesthe -> The> + * after reset. restore them.reset. -> reset, hence> + * other registers should not be reconfigured after reset''Other''> + * if there is no reasonSo what are the other registers? Every other register _but_ the ones that are enumerated here? If so, please state that explicitly.> + */ > + aer_restore_one_register(s, PCI_ERR_UNCOR_MASK); > + aer_restore_one_register(s, PCI_ERR_UNCOR_SEVER); > + aer_restore_one_register(s, PCI_ERR_COR_MASK); > + aer_restore_one_register(s, PCI_ERR_CAP); > +} > + > +/* capability structure register group size functions */ > + > +static int pt_reg_grp_size_init(XenPCIPassthroughState *s, > + const XenPTRegGroupInfo *grp_reg, > + uint32_t base_offset, uint8_t *size) > +{ > + *size = grp_reg->grp_size; > + return 0; > +} > +/* get Power Management Capability Structure register group size */ > +static int pt_pm_size_init(XenPCIPassthroughState *s, > + const XenPTRegGroupInfo *grp_reg, > + uint32_t base_offset, uint8_t *size) > +{ > + *size = grp_reg->grp_size; > + > + if (!s->power_mgmt) { > + return 0; > + } > + > + s->pm_state = g_new0(XenPTPM, 1); > + > + /* set Power Management Capability base offset */ > + s->pm_state->pm_base = base_offset; > + > + /* find AER register and set AER Capability base offset */ > + s->pm_state->aer_base = host_pci_find_ext_cap_offset(s->real_device, > + PCI_EXT_CAP_ID_ERR); > + > + /* save AER register */ > + if (s->pm_state->aer_base) { > + pt_aer_reg_save(s); > + } > + > + return 0; > +} > +/* get Vendor Specific Capability Structure register group size */ > +static int pt_vendor_size_init(XenPCIPassthroughState *s, > + const XenPTRegGroupInfo *grp_reg, > + uint32_t base_offset, uint8_t *size) > +{ > + *size = pci_get_byte(s->dev.config + base_offset + 0x02); > + return 0; > +} > +/* get PCI Express Capability Structure register group size */ > +static int pt_pcie_size_init(XenPCIPassthroughState *s, > + const XenPTRegGroupInfo *grp_reg, > + uint32_t base_offset, uint8_t *size) > +{ > + PCIDevice *d = &s->dev; > + uint16_t exp_flag = 0; > + uint16_t type = 0; > + uint16_t version = 0; > + uint8_t pcie_size = 0; > + > + exp_flag = pci_get_word(d->config + base_offset + PCI_EXP_FLAGS); > + type = (exp_flag & PCI_EXP_FLAGS_TYPE) >> 4; > + version = exp_flag & PCI_EXP_FLAGS_VERS; > + > + /* calculate size depend on capability version and device/port type */ > + /* in case of PCI Express Base Specification Rev 1.x */ > + if (version == 1) { > + /* The PCI Express Capabilities, Device Capabilities, and Device > + * Status/Control registers are required for all PCI Express devices. > + * The Link Capabilities and Link Status/Control are required for all > + * Endpoints that are not Root Complex Integrated Endpoints. Endpoints > + * are not required to implement registers other than those listed > + * above and terminate the capability structure. > + */ > + switch (type) { > + case PCI_EXP_TYPE_ENDPOINT: > + case PCI_EXP_TYPE_LEG_END: > + pcie_size = 0x14; > + break; > + case PCI_EXP_TYPE_RC_END: > + /* has no link */ > + pcie_size = 0x0C; > + break; > + /* only EndPoint passthrough is supported */ > + case PCI_EXP_TYPE_ROOT_PORT: > + case PCI_EXP_TYPE_UPSTREAM: > + case PCI_EXP_TYPE_DOWNSTREAM: > + case PCI_EXP_TYPE_PCI_BRIDGE: > + case PCI_EXP_TYPE_PCIE_BRIDGE: > + case PCI_EXP_TYPE_RC_EC: > + default: > + PT_ERR(d, "Internal error: Unsupported device/port type (%d).\n", > + type); > + return -1; > + } > + } > + /* in case of PCI Express Base Specification Rev 2.0 */ > + else if (version == 2) { > + switch (type) { > + case PCI_EXP_TYPE_ENDPOINT: > + case PCI_EXP_TYPE_LEG_END: > + case PCI_EXP_TYPE_RC_END: > + /* For Functions that do not implement the registers, > + * these spaces must be hardwired to 0b.So this comment applies to the functions below the ''break'' right? Should you move it? But that doesn''t make sense - we end up hitting return -1 so it bails out and does not return 0b. Hmmm.. So what is this comment referring to?> + */ > + pcie_size = 0x3C; > + break; > + /* only EndPoint passthrough is supported */ > + case PCI_EXP_TYPE_ROOT_PORT: > + case PCI_EXP_TYPE_UPSTREAM: > + case PCI_EXP_TYPE_DOWNSTREAM: > + case PCI_EXP_TYPE_PCI_BRIDGE: > + case PCI_EXP_TYPE_PCIE_BRIDGE: > + case PCI_EXP_TYPE_RC_EC: > + default: > + PT_ERR(d, "Internal error: Unsupported device/port type (%d).\n", > + type); > + return -1; > + } > + } else { > + PT_ERR(d, "Internal error: Unsupported capability version (%d).\n", > + version); > + return -1; > + } > + > + *size = pcie_size; > + return 0; > +} > + > +static const XenPTRegGroupInfo pt_emu_reg_grp_tbl[] = { > + /* Header Type0 reg group */ > + { > + .grp_id = 0xFF, > + .grp_type = GRP_TYPE_EMU, > + .grp_size = 0x40, > + .size_init = pt_reg_grp_size_init, > + .emu_reg_tbl = pt_emu_reg_header0_tbl, > + }, > + /* PCI PowerManagement Capability reg group */ > + { > + .grp_id = PCI_CAP_ID_PM, > + .grp_type = GRP_TYPE_EMU, > + .grp_size = PCI_PM_SIZEOF, > + .size_init = pt_pm_size_init, > + .emu_reg_tbl = pt_emu_reg_pm_tbl, > + }, > + /* AGP Capability Structure reg group */ > + { > + .grp_id = PCI_CAP_ID_AGP, > + .grp_type = GRP_TYPE_HARDWIRED, > + .grp_size = 0x30, > + .size_init = pt_reg_grp_size_init, > + }, > + /* Vital Product Data Capability Structure reg group */ > + { > + .grp_id = PCI_CAP_ID_VPD, > + .grp_type = GRP_TYPE_EMU, > + .grp_size = 0x08, > + .size_init = pt_reg_grp_size_init, > + .emu_reg_tbl = pt_emu_reg_vpd_tbl, > + }, > + /* Slot Identification reg group */ > + { > + .grp_id = PCI_CAP_ID_SLOTID, > + .grp_type = GRP_TYPE_HARDWIRED, > + .grp_size = 0x04, > + .size_init = pt_reg_grp_size_init, > + }, > + /* PCI-X Capabilities List Item reg group */ > + { > + .grp_id = PCI_CAP_ID_PCIX, > + .grp_type = GRP_TYPE_HARDWIRED, > + .grp_size = 0x18, > + .size_init = pt_reg_grp_size_init, > + }, > + /* Vendor Specific Capability Structure reg group */ > + { > + .grp_id = PCI_CAP_ID_VNDR, > + .grp_type = GRP_TYPE_EMU, > + .grp_size = 0xFF, > + .size_init = pt_vendor_size_init, > + .emu_reg_tbl = pt_emu_reg_vendor_tbl, > + }, > + /* SHPC Capability List Item reg group */ > + { > + .grp_id = PCI_CAP_ID_SHPC, > + .grp_type = GRP_TYPE_HARDWIRED, > + .grp_size = 0x08, > + .size_init = pt_reg_grp_size_init, > + }, > + /* Subsystem ID and Subsystem Vendor ID Capability List Item reg group */ > + { > + .grp_id = PCI_CAP_ID_SSVID, > + .grp_type = GRP_TYPE_HARDWIRED, > + .grp_size = 0x08, > + .size_init = pt_reg_grp_size_init, > + }, > + /* AGP 8x Capability Structure reg group */ > + { > + .grp_id = PCI_CAP_ID_AGP3, > + .grp_type = GRP_TYPE_HARDWIRED, > + .grp_size = 0x30, > + .size_init = pt_reg_grp_size_init, > + }, > + /* PCI Express Capability Structure reg group */ > + { > + .grp_id = PCI_CAP_ID_EXP, > + .grp_type = GRP_TYPE_EMU, > + .grp_size = 0xFF, > + .size_init = pt_pcie_size_init, > + .emu_reg_tbl = pt_emu_reg_pcie_tbl, > + }, > + { > + .grp_size = 0, > + }, > +}; > + > +/* initialize Capabilities Pointer or Next Pointer register */ > +static int pt_ptr_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + /* uint32_t reg_field = (uint32_t)s->dev.config[real_offset]; */ > + uint32_t reg_field = pci_get_byte(s->dev.config + real_offset);There is no check whether real_offset > than the size of what is in dev.config. Perhaps that should be done?> + int i; > + > + /* find capability offset */ > + while (reg_field) { > + for (i = 0; pt_emu_reg_grp_tbl[i].grp_size != 0; i++) { > + if (pt_hide_dev_cap(s->real_device, > + pt_emu_reg_grp_tbl[i].grp_id)) { > + continue; > + } > + if (pt_emu_reg_grp_tbl[i].grp_id == s->dev.config[reg_field]) { > + if (pt_emu_reg_grp_tbl[i].grp_type == GRP_TYPE_EMU) { > + goto out; > + } > + /* ignore the 0 hardwired capability, find next one */ > + break; > + } > + } > + /* next capability */ > + /* reg_field = (uint32_t)s->dev.config[reg_field + 1]; */ > + reg_field = pci_get_byte(s->dev.config + reg_field + 1); > + } > + > +out: > + *data = reg_field; > + return 0; > +} > + > + > +/************* > + * Main > + */ > + > +/* restore a part of I/O device register */ > +static int pt_config_restore(XenPCIPassthroughState *s) > +{ > + XenPTRegGroup *reg_grp_entry = NULL; > + XenPTReg *reg_entry = NULL; > + XenPTRegInfo *reg = NULL; > + uint32_t real_offset = 0; > + uint32_t read_val = 0; > + uint32_t val = 0; > + int rc = 0; > + > + /* find emulate register group entry */ > + QLIST_FOREACH(reg_grp_entry, &s->reg_grp_tbl, entries) { > + /* find emulate register entry */ > + QLIST_FOREACH(reg_entry, ®_grp_entry->reg_tbl_list, entries) { > + reg = reg_entry->reg; > + > + /* check whether restoring is needed */ > + if (!reg->u.b.restore) { > + continue; > + } > + > + real_offset = reg_grp_entry->base_offset + reg->offset; > + > + /* read I/O device register value */ > + rc = host_pci_get_block(s->real_device, real_offset, > + (uint8_t *)&read_val, reg->size); > + > + if (rc < 0) { > + PT_ERR(&s->dev, "pci_read_block failed. " > + "return value: %d.\n", rc); > + memset(&read_val, 0xff, reg->size); > + } > + > + val = 0; > + > + /* restore based on register size */ > + switch (reg->size) { > + case 1: > + /* byte register */ > + rc = reg->u.b.restore(s, reg_entry, real_offset, > + (uint8_t)read_val, (uint8_t *)&val); > + break; > + case 2: > + /* word register */ > + rc = reg->u.w.restore(s, reg_entry, real_offset, > + (uint16_t)read_val, (uint16_t *)&val); > + break; > + case 4: > + /* double word register */ > + rc = reg->u.dw.restore(s, reg_entry, real_offset, > + (uint32_t)read_val, (uint32_t *)&val); > + break; > + } > + > + /* restoring error */ > + if (rc < 0) { > + xen_shutdown_fatal_error("Internal error: Invalid restoring." > + " (%s, rc: %d)\n", __func__, rc); > + return -1; > + } > + > + PT_LOG_CONFIG(&s->dev, real_offset, val, reg->size); > + > + rc = host_pci_set_block(s->real_device, real_offset, > + (uint8_t *)&val, reg->size); > + > + if (rc < 0) { > + PT_ERR(&s->dev, "pci_write_block failed. " > + "return value: %d.\n", rc); > + return -1; > + } > + } > + } > + > + /* if AER supported, restore it */ > + if (s->pm_state->aer_base) { > + pt_aer_reg_restore(s); > + } > + return 0; > +} > +/* reinitialize all emulate registers */ > +static int pt_config_reinit(XenPCIPassthroughState *s) > +{ > + XenPTRegGroup *reg_grp_entry = NULL; > + XenPTReg *reg_entry = NULL; > + XenPTRegInfo *reg = NULL; > + int rc = 0; > + > + /* find emulate register group entry */ > + QLIST_FOREACH(reg_grp_entry, &s->reg_grp_tbl, entries) { > + /* find emulate register entry */ > + QLIST_FOREACH(reg_entry, ®_grp_entry->reg_tbl_list, entries) { > + reg = reg_entry->reg; > + if (reg->init) { > + /* initialize emulate register */ > + rc = reg->init(s, reg_entry->reg, > + reg_grp_entry->base_offset + reg->offset, > + ®_entry->data); > + if (rc < 0) { > + return rc; > + } > + } > + } > + } > + return 0; > +} > + > +static int pt_init_pci_config(XenPCIPassthroughState *s) > +{ > + PCIDevice *d = &s->dev; > + int rc = 0; > + > + PT_LOG(d, "Reinitialize PCI configuration registers due to power state" > + " transition with internal reset.\n"); > + > + /* restore a part of I/O device register */ > + rc = pt_config_restore(s); > + if (rc < 0) { > + return rc; > + } > + > + /* reinitialize all emulate register */ > + rc = pt_config_reinit(s); > + if (rc < 0) { > + return rc; > + } > + > + /* rebind machine_irq to device */ > + if (s->machine_irq != 0) { > + uint8_t e_device = PCI_SLOT(s->dev.devfn); > + uint8_t e_intx = pci_intx(s); > + > + rc = xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, s->machine_irq, 0, > + e_device, e_intx); > + if (rc < 0) { > + PT_ERR(d, "Rebinding of interrupt failed! rc=%d\n", rc); > + } > + } > + > + return rc; > +} > + > +static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap) > +{ > + uint8_t id; > + int max_cap = 48;You need to use #define for that. Also you might want just make it an unsigned.> + uint8_t pos = PCI_CAPABILITY_LIST; > + uint8_t status = 0; > + > + if (host_pci_get_byte(s->real_device, PCI_STATUS, &status)) { > + return 0; > + } > + if ((status & PCI_STATUS_CAP_LIST) == 0) { > + return 0; > + } > + > + while (max_cap--) { > + if (host_pci_get_byte(s->real_device, pos, &pos)) { > + break; > + } > + if (pos < 0x40) {Can you explain why? (I presume it is b/c the capabilities entries do not exist before 0x40 offset?)> + break; > + } > + > + pos &= ~3; > + if (host_pci_get_byte(s->real_device, pos + PCI_CAP_LIST_ID, &id)) { > + break; > + } > + > + if (id == 0xff) { > + break; > + } > + if (id == cap) { > + return pos; > + } > + > + pos += PCI_CAP_LIST_NEXT; > + } > + return 0; > +} > + > +static int pt_config_reg_init(XenPCIPassthroughState *s, > + XenPTRegGroup *reg_grp, XenPTRegInfo *reg) > +{ > + XenPTReg *reg_entry; > + uint32_t data = 0; > + int rc = 0; > + > + reg_entry = g_new0(XenPTReg, 1); > + reg_entry->reg = reg; > + > + if (reg->init) { > + /* initialize emulate register */ > + rc = reg->init(s, reg_entry->reg, > + reg_grp->base_offset + reg->offset, &data); > + if (rc < 0) { > + free(reg_entry); > + return rc; > + } > + if (data == PT_INVALID_REG) { > + /* free unused BAR register entry */ > + free(reg_entry); > + return 0; > + } > + /* set register value */ > + reg_entry->data = data; > + } > + /* list add register entry */ > + QLIST_INSERT_HEAD(®_grp->reg_tbl_list, reg_entry, entries); > + > + return 0; > +} > + > +int pt_config_init(XenPCIPassthroughState *s) > +{ > + XenPTRegGroup *reg_grp_entry = NULL; > + uint32_t reg_grp_offset = 0; > + XenPTRegInfo *reg_tbl = NULL; > + int i, j, rc; > + > + QLIST_INIT(&s->reg_grp_tbl); > + > + for (i = 0; pt_emu_reg_grp_tbl[i].grp_size != 0; i++) { > + if (pt_emu_reg_grp_tbl[i].grp_id != 0xFF) { > + if (pt_hide_dev_cap(s->real_device, > + pt_emu_reg_grp_tbl[i].grp_id)) { > + continue; > + } > + > + reg_grp_offset = find_cap_offset(s, pt_emu_reg_grp_tbl[i].grp_id); > + > + if (!reg_grp_offset) { > + continue; > + } > + } > + > + reg_grp_entry = g_new0(XenPTRegGroup, 1); > + QLIST_INIT(®_grp_entry->reg_tbl_list); > + QLIST_INSERT_HEAD(&s->reg_grp_tbl, reg_grp_entry, entries); > + > + reg_grp_entry->base_offset = reg_grp_offset; > + reg_grp_entry->reg_grp = pt_emu_reg_grp_tbl + i; > + if (pt_emu_reg_grp_tbl[i].size_init) { > + /* get register group size */ > + rc = pt_emu_reg_grp_tbl[i].size_init(s, reg_grp_entry->reg_grp, > + reg_grp_offset, > + ®_grp_entry->size); > + if (rc < 0) { > + pt_config_delete(s); > + return rc; > + } > + } > + > + if (pt_emu_reg_grp_tbl[i].grp_type == GRP_TYPE_EMU) { > + if (pt_emu_reg_grp_tbl[i].emu_reg_tbl) { > + reg_tbl = pt_emu_reg_grp_tbl[i].emu_reg_tbl; > + /* initialize capability register */ > + for (j = 0; reg_tbl->size != 0; j++, reg_tbl++) { > + /* initialize capability register */ > + rc = pt_config_reg_init(s, reg_grp_entry, reg_tbl); > + if (rc < 0) { > + pt_config_delete(s); > + return rc; > + } > + } > + } > + } > + reg_grp_offset = 0;Can you move this to the top of the loop?> + } > + > + return 0; > +} > + > +/* delete all emulate register */ > +void pt_config_delete(XenPCIPassthroughState *s) > +{ > + struct XenPTRegGroup *reg_group, *next_grp; > + struct XenPTReg *reg, *next_reg; > + > + /* free Power Management info table */ > + if (s->pm_state) { > + if (s->pm_state->pm_timer) { > + qemu_del_timer(s->pm_state->pm_timer); > + qemu_free_timer(s->pm_state->pm_timer); > + s->pm_state->pm_timer = NULL; > + } > + > + g_free(s->pm_state); > + } > + > + /* free all register group entry */ > + QLIST_FOREACH_SAFE(reg_group, &s->reg_grp_tbl, entries, next_grp) { > + /* free all register entry */ > + QLIST_FOREACH_SAFE(reg, ®_group->reg_tbl_list, entries, next_reg) { > + QLIST_REMOVE(reg, entries); > + g_free(reg); > + } > + > + QLIST_REMOVE(reg_group, entries); > + g_free(reg_group); > + } > +} > -- > Anthony PERARD > > > _______________________________________________ > Xen-devel mailing list > Xen-devel@lists.xensource.com > http://lists.xensource.com/xen-devel
Konrad Rzeszutek Wilk
2011-Dec-05 23:01 UTC
Re: [Xen-devel] [PATCH V5 07/10] Introduce Xen PCI Passthrough, qdevice (1/3)
> > +/* emulated register management */ > > +struct XenPTReg { > > + QLIST_ENTRY(XenPTReg) entries; > > + XenPTRegInfo *reg; > > + uint32_t data;There should be a comment saying what ''data'' is actually. Say the register value... But I wonder - what if the register is more than 32-bytes? Where do we keep the "data" then?
Konrad Rzeszutek Wilk
2011-Dec-06 00:02 UTC
Re: [Xen-devel] [PATCH V5 10/10] Introduce Xen PCI Passthrough, MSI (3/3)
On Thu, Nov 24, 2011 at 05:44:39PM +0000, Anthony PERARD wrote:> From: Jiang Yunhong <yunhong.jiang@intel.com> > > A more complete history can be found here: > git://xenbits.xensource.com/qemu-xen-unstable.git > > Signed-off-by: Jiang Yunhong <yunhong.jiang@intel.com> > Signed-off-by: Shan Haitao <haitao.shan@intel.com> > Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> > --- > Makefile.target | 1 + > hw/apic-msidef.h | 2 + > hw/xen_pci_passthrough.c | 60 +++- > hw/xen_pci_passthrough.h | 55 +++ > hw/xen_pci_passthrough_config_init.c | 505 +++++++++++++++++++++++++- > hw/xen_pci_passthrough_msi.c | 678 ++++++++++++++++++++++++++++++++++ > 6 files changed, 1294 insertions(+), 7 deletions(-) > create mode 100644 hw/xen_pci_passthrough_msi.c > > diff --git a/Makefile.target b/Makefile.target > index 33435a3..81cff70 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -223,6 +223,7 @@ obj-i386-$(CONFIG_XEN) += xen_platform.o > obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += host-pci-device.o > obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough.o > obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough_config_init.o > +obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough_msi.o > > # Inter-VM PCI shared memory > CONFIG_IVSHMEM > diff --git a/hw/apic-msidef.h b/hw/apic-msidef.h > index 3182f0b..6e2eb71 100644 > --- a/hw/apic-msidef.h > +++ b/hw/apic-msidef.h > @@ -22,6 +22,8 @@ > > #define MSI_ADDR_DEST_MODE_SHIFT 2 > > +#define MSI_ADDR_REDIRECTION_SHIFT 3 > + > #define MSI_ADDR_DEST_ID_SHIFT 12 > #define MSI_ADDR_DEST_ID_MASK 0x00ffff0 > > diff --git a/hw/xen_pci_passthrough.c b/hw/xen_pci_passthrough.c > index c816ed5..cd7e3c7 100644 > --- a/hw/xen_pci_passthrough.c > +++ b/hw/xen_pci_passthrough.c > @@ -38,6 +38,39 @@ > * Write ''1'' > * <ptdev->msi_trans_en is false> > * - Set real bit to ''1''. > + * > + * MSI-INTx translation. > + * Initialize(xc_physdev_map_pirq_msi/pt_msi_setup) > + * Bind MSI-INTx(xc_domain_bind_pt_irq) > + * <fail> > + * - Unmap MSI. > + * <success> > + * - Set dev->msi->pirq to ''-1''. > + * <fail> > + * - Do nothing. > + * > + * Write to Interrupt Disable bit by guest software(pt_cmd_reg_write) > + * Write ''0'' > + * <ptdev->msi_trans_en is true> > + * - Set MSI Enable bit to ''1''. > + * > + * Write ''1'' > + * <ptdev->msi_trans_en is true> > + * - Set MSI Enable bit to ''0''. > + * > + * MSI interrupt: > + * Initialize MSI register(pt_msi_setup, pt_msi_update) > + * Bind MSI(xc_domain_update_msi_irq) > + * <fail> > + * - Unmap MSI. > + * - Set dev->msi->pirq to ''-1''. > + * > + * MSI-X interrupt: > + * Initialize MSI-X register(pt_msix_update_one) > + * Bind MSI-X(xc_domain_update_msi_irq) > + * <fail> > + * - Unmap MSI-X. > + * - Set entry->pirq to ''-1''. > */ > > #include <sys/ioctl.h> > @@ -389,6 +422,7 @@ static void pt_iomem_map(XenPCIPassthroughState *s, int i, > } > > if (!first_map && old_ebase != PT_PCI_BAR_UNMAPPED) { > + pt_add_msix_mapping(s, i); > /* Remove old mapping */ > memory_region_del_subregion(r->address_space, > r->memory); > @@ -417,6 +451,15 @@ static void pt_iomem_map(XenPCIPassthroughState *s, int i, > if (ret != 0) { > PT_ERR(&s->dev, "create new mapping failed!\n"); > } > + > + ret = pt_remove_msix_mapping(s, i); > + if (ret != 0) { > + PT_ERR(&s->dev, "Remove MSI-X MMIO mapping failed!\n"); > + } > + > + if (old_ebase != e_phys && old_ebase != -1) { > + pt_msix_update_remap(s, i); > + } > } > } > > @@ -744,6 +787,9 @@ static int pt_initfn(PCIDevice *pcidev) > mapped_machine_irq[machine_irq]++; > } > > + /* setup MSI-INTx translation if support */ > + rc = pt_enable_msi_translate(s); > + > /* bind machine_irq to device */ > if (rc < 0 && machine_irq != 0) { > uint8_t e_device = PCI_SLOT(s->dev.devfn); > @@ -773,7 +819,8 @@ static int pt_initfn(PCIDevice *pcidev) > > out: > PT_LOG(pcidev, "Real physical device %02x:%02x.%x registered successfuly!" > - "\nIRQ type = %s\n", bus, slot, func, "INTx"); > + "\nIRQ type = %s\n", bus, slot, func, > + s->msi_trans_en ? "MSI-INTx" : "INTx"); > > return 0; > } > @@ -790,7 +837,7 @@ static int pt_unregister_device(PCIDevice *pcidev) > e_intx = pci_intx(s); > machine_irq = s->machine_irq; > > - if (machine_irq) { > + if (s->msi_trans_en == 0 && machine_irq) {It is defined as ''bool'' so you could do !s->msi_trans_en instead?> rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq, > PT_IRQ_TYPE_PCI, 0, e_device, e_intx, 0); > if (rc < 0) { > @@ -798,6 +845,13 @@ static int pt_unregister_device(PCIDevice *pcidev) > } > } > > + if (s->msi) { > + pt_msi_disable(s); > + } > + if (s->msix) { > + pt_msix_disable(s); > + } > + > if (machine_irq) { > mapped_machine_irq[machine_irq]--; > > @@ -832,6 +886,8 @@ static PCIDeviceInfo xen_pci_passthrough = { > .is_express = 0, > .qdev.props = (Property[]) { > DEFINE_PROP_STRING("hostaddr", XenPCIPassthroughState, hostaddr), > + DEFINE_PROP_BIT("msitranslate", XenPCIPassthroughState, msi_trans_cap, > + 0, true), > DEFINE_PROP_BIT("power-mgmt", XenPCIPassthroughState, power_mgmt, > 0, false), > DEFINE_PROP_END_OF_LIST(), > diff --git a/hw/xen_pci_passthrough.h b/hw/xen_pci_passthrough.h > index 110325c..acbbab5 100644 > --- a/hw/xen_pci_passthrough.h > +++ b/hw/xen_pci_passthrough.h > @@ -74,6 +74,10 @@ typedef int (*conf_byte_restore) > #define PT_PCI_BAR_UNMAPPED (-1) > #define PT_UNASSIGNED_PIRQ (-1) > > +/* MSI-X */ > +#define PT_MSI_FLAG_UNINIT 0x1000 > +#define PT_MSI_FLAG_MAPPED 0x2000Can you explain what those two defines do please?> + > > typedef enum { > GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */ > @@ -177,6 +181,34 @@ typedef struct XenPTRegGroup { > } XenPTRegGroup; > > > +typedef struct XenPTMSI { > + uint32_t flags; > + uint32_t ctrl_offset; /* saved control offset */ > + int pirq; /* guest pirq corresponding */ > + uint32_t addr_lo; /* guest message address */ > + uint32_t addr_hi; /* guest message upper address */ > + uint16_t data; /* guest message data */ > +} XenPTMSI; > + > +typedef struct XenMSIXEntry { > + int pirq; /* -1 means unmapped */ > + int flags; /* flags indicting whether MSI ADDR or DATA is updated */ > + uint32_t io_mem[4];Can you document what is in each position? Perhaps some enums or #defines for them?> +} XenMSIXEntry; > +typedef struct XenPTMSIX { > + uint32_t ctrl_offset; > + int enabled; > + int total_entries; > + int bar_index; > + uint64_t table_base; > + uint32_t table_off; > + uint32_t table_offset_adjust; /* page align mmap */ > + uint64_t mmio_base_addr; > + int mmio_index; > + void *phys_iomem_base; > + XenMSIXEntry msix_entry[0]; > +} XenPTMSIX; > + > typedef struct XenPTPM { > QEMUTimer *pm_timer; /* QEMUTimer struct */ > int no_soft_reset; /* No Soft Reset flags */ > @@ -200,6 +232,13 @@ struct XenPCIPassthroughState { > > uint32_t machine_irq; > > + XenPTMSI *msi; > + XenPTMSIX *msix; > + > + /* Physical MSI to guest INTx translation when possible */Meaning that the MSI vector is going to show up as INTx in the guest? This is by default enabled, right? At least that is what txt/misc/vtd.txt tells me. So would it be better than to have ''msi_trans_off'' as flag as by default we would enable it? So would it be better than to have ''msi_trans_off'' as flag?> + uint32_t msi_trans_cap; > + bool msi_trans_en; > + > uint32_t power_mgmt; > XenPTPM *pm_state; > > @@ -279,4 +318,20 @@ static inline uint8_t pci_intx(XenPCIPassthroughState *s) > return r_val; > } > > +/* MSI/MSI-X */ > +void pt_msi_set_enable(XenPCIPassthroughState *s, int en);You could make it a bool. So "bool en" instead?> +int pt_msi_setup(XenPCIPassthroughState *s); > +int pt_msi_update(XenPCIPassthroughState *d); > +void pt_msi_disable(XenPCIPassthroughState *s); > +int pt_enable_msi_translate(XenPCIPassthroughState *s); > +void pt_disable_msi_translate(XenPCIPassthroughState *s); > + > +int pt_msix_init(XenPCIPassthroughState *s, int pos);uint32_t as pos?> +void pt_msix_delete(XenPCIPassthroughState *s); > +int pt_msix_update(XenPCIPassthroughState *s); > +int pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index);uint32_t as bar_index?> +void pt_msix_disable(XenPCIPassthroughState *s); > +int pt_add_msix_mapping(XenPCIPassthroughState *s, int bar_index); > +int pt_remove_msix_mapping(XenPCIPassthroughState *s, int bar_index); > + > #endif /* !QEMU_HW_XEN_PCI_PASSTHROUGH_H */ > diff --git a/hw/xen_pci_passthrough_config_init.c b/hw/xen_pci_passthrough_config_init.c > index ae64544..28523f1 100644 > --- a/hw/xen_pci_passthrough_config_init.c > +++ b/hw/xen_pci_passthrough_config_init.c > @@ -398,11 +398,19 @@ static int pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > throughable_mask = ~emu_mask & valid_mask; > > if (*value & PCI_COMMAND_INTX_DISABLE) { > - throughable_mask |= PCI_COMMAND_INTX_DISABLE; > - } else { > - if (s->machine_irq) { > + if (s->msi_trans_en) { > + pt_msi_set_enable(s, 0);Can you put a comment explaining the ''0'' argument?> + } else { > throughable_mask |= PCI_COMMAND_INTX_DISABLE; > } > + } else { > + if (s->msi_trans_en) { > + pt_msi_set_enable(s, 1); > + } else { > + if (s->machine_irq) { > + throughable_mask |= PCI_COMMAND_INTX_DISABLE; > + } > + } > } > > *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); > @@ -1282,13 +1290,21 @@ static void pt_reset_interrupt_and_io_mapping(XenPCIPassthroughState *s) > e_device = PCI_SLOT(s->dev.devfn); > e_intx = pci_intx(s); > > - if (s->machine_irq) { > + if (s->msi_trans_en == 0 && s->machine_irq) { > if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, s->machine_irq, > PT_IRQ_TYPE_PCI, 0, e_device, e_intx, 0)) { > PT_ERR(d, "Unbinding of interrupt failed!\n"); > } > } > > + /* disable MSI/MSI-X and MSI-INTx translation */ > + if (s->msi) { > + pt_msi_disable(s); > + } > + if (s->msix) { > + pt_msix_disable(s); > + } > + > /* clear all virtual region address */ > for (i = 0; i < PCI_NUM_REGIONS; i++) { > r = &d->io_regions[i]; > @@ -1536,6 +1552,415 @@ static XenPTRegInfo pt_emu_reg_pm_tbl[] = { > }, > }; > > +/******************************** > + * MSI Capability > + */ > + > +/* Message Control register */ > +static int pt_msgctrl_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + PCIDevice *d = &s->dev; > + uint16_t reg_field = 0; > + > + /* use I/O device register''s value as initial value */ > + reg_field = pci_get_word(d->config + real_offset);There is no ''pci_get_halfword'' operations?> + > + if (reg_field & PCI_MSI_FLAGS_ENABLE) { > + PT_LOG(&s->dev, "MSI already enabled, disable it first\n");.. "disabling it first."> + host_pci_set_word(s->real_device, real_offset, > + reg_field & ~PCI_MSI_FLAGS_ENABLE); > + } > + s->msi->flags |= reg_field | PT_MSI_FLAG_UNINIT;There is no chance that you PT_MSI_FLAG_UNINIT would shadow what is in the register? You are reading a word after all.. ah, the reg_field is a 16-bit, so you are populating the unused fields. Perhaps you could use bit-fields?> + s->msi->ctrl_offset = real_offset; > + > + *data = reg->init_val; > + return 0; > +} > +static int pt_msgctrl_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint16_t *value, uint16_t dev_value, > + uint16_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint16_t writable_mask = 0; > + uint16_t throughable_mask = 0; > + uint16_t val; > + > + /* Currently no support for multi-vector */ > + if (*value & PCI_MSI_FLAGS_QSIZE) { > + PT_WARN(&s->dev, "try to set more than 1 vector ctrl %x\n", *value);But no return -ENOSPC? The message should probably read: "Tries to set more.."> + } > + > + /* modify emulate register */ > + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; > + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); > + /* update the msi_info too */err, msi_info -> msi->flags?> + s->msi->flags |= cfg_entry->data & > + ~(PT_MSI_FLAG_UNINIT | PT_MSI_FLAG_MAPPED | PCI_MSI_FLAGS_ENABLE); > + > + /* create value for writing to I/O device register */ > + val = *value; > + throughable_mask = ~reg->emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); > + > + /* update MSI */ > + if (val & PCI_MSI_FLAGS_ENABLE) { > + /* setup MSI pirq for the first time */ > + if (s->msi->flags & PT_MSI_FLAG_UNINIT) { > + if (s->msi_trans_en) { > + PT_LOG(&s->dev, > + "guest enabling MSI, disable MSI-INTx translation\n"); > + pt_disable_msi_translate(s); > + } else { > + /* Init physical one */ > + PT_LOG(&s->dev, "setup MSI\n"); > + if (pt_msi_setup(s)) { > + /* We do not broadcast the error to the framework code, so > + * that MSI errors are contained in MSI emulation code and > + * QEMU can go on running. > + * Guest MSI would be actually not working.Not sure if the last sentence is needed?> + */ > + *value &= ~PCI_MSI_FLAGS_ENABLE; > + PT_WARN(&s->dev, "Can not map MSI.\n"); > + return 0; > + } > + } > + if (pt_msi_update(s)) { > + *value &= ~PCI_MSI_FLAGS_ENABLE; > + PT_WARN(&s->dev, "Can not bind MSI\n"); > + return 0; > + } > + s->msi->flags &= ~PT_MSI_FLAG_UNINIT; > + s->msi->flags |= PT_MSI_FLAG_MAPPED; > + } > + s->msi->flags |= PCI_MSI_FLAGS_ENABLE; > + } else { > + s->msi->flags &= ~PCI_MSI_FLAGS_ENABLE; > + } > + > + /* pass through MSI_ENABLE bit when no MSI-INTx translation */^- "there is"> + if (!s->msi_trans_en) { > + *value &= ~PCI_MSI_FLAGS_ENABLE; > + *value |= val & PCI_MSI_FLAGS_ENABLE; > + } > + > + return 0; > +} > + > +/* initialize Message Upper Address register */ > +static int pt_msgaddr64_reg_init(XenPCIPassthroughState *ptdev, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + /* no need to initialize in case of 32 bit type */ > + if (!(ptdev->msi->flags & PCI_MSI_FLAGS_64BIT)) { > + *data = PT_INVALID_REG; > + } else { > + *data = reg->init_val; > + } > + > + return 0; > +} > +/* this function will be called twice (for 32 bit and 64 bit type) */ > +/* initialize Message Data register */ > +static int pt_msgdata_reg_init(XenPCIPassthroughState *ptdev, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + uint32_t flags = ptdev->msi->flags; > + uint32_t offset = reg->offset; > + > + /* check the offset whether matches the type or not */ > + if (((offset == PCI_MSI_DATA_64) && (flags & PCI_MSI_FLAGS_64BIT)) || > + ((offset == PCI_MSI_DATA_32) && !(flags & PCI_MSI_FLAGS_64BIT))) {This looks like it could be made in a helper function..> + *data = reg->init_val; > + } else { > + *data = PT_INVALID_REG; > + } > + return 0; > +} > + > +/* write Message Address register */ > +static int pt_msgaddr32_reg_write(XenPCIPassthroughState *s, > + XenPTReg *cfg_entry, uint32_t *value, > + uint32_t dev_value, uint32_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint32_t writable_mask = 0; > + uint32_t throughable_mask = 0; > + uint32_t old_addr = cfg_entry->data; > + > + /* modify emulate register */ > + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; > + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); > + /* update the msi_info too */ > + s->msi->addr_lo = cfg_entry->data; > + > + /* create value for writing to I/O device register */ > + throughable_mask = ~reg->emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); > + > + /* update MSI */ > + if (cfg_entry->data != old_addr) { > + if (s->msi->flags & PT_MSI_FLAG_MAPPED) { > + pt_msi_update(s); > + } > + } > + > + return 0; > +} > +/* write Message Upper Address register */ > +static int pt_msgaddr64_reg_write(XenPCIPassthroughState *s, > + XenPTReg *cfg_entry, uint32_t *value, > + uint32_t dev_value, uint32_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint32_t writable_mask = 0; > + uint32_t throughable_mask = 0; > + uint32_t old_addr = cfg_entry->data; > + > + /* check whether the type is 64 bit or not */ > + if (!(s->msi->flags & PCI_MSI_FLAGS_64BIT)) { > + PT_ERR(&s->dev, "Write to the Upper Address without 64 bit support\n");"Can''t write to the ..."> + return -1;-ENOSPC?> + } > + > + /* modify emulate register */ > + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; > + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); > + /* update the msi_info too */ > + s->msi->addr_hi = cfg_entry->data; > + > + /* create value for writing to I/O device register */ > + throughable_mask = ~reg->emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); > + > + /* update MSI */ > + if (cfg_entry->data != old_addr) { > + if (s->msi->flags & PT_MSI_FLAG_MAPPED) { > + pt_msi_update(s); > + } > + } > + > + return 0; > +} > + > + > +/* this function will be called twice (for 32 bit and 64 bit type) */ > +/* write Message Data register */ > +static int pt_msgdata_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, > + uint16_t *value, uint16_t dev_value, > + uint16_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint16_t writable_mask = 0; > + uint16_t throughable_mask = 0; > + uint16_t old_data = cfg_entry->data; > + uint32_t flags = s->msi->flags; > + uint32_t offset = reg->offset; > + > + /* check the offset whether matches the type or not */ > + if (!((offset == PCI_MSI_DATA_64) && (flags & PCI_MSI_FLAGS_64BIT)) && > + !((offset == PCI_MSI_DATA_32) && !(flags & PCI_MSI_FLAGS_64BIT))) { > + /* exit I/O emulator */ > + PT_ERR(&s->dev, "the offset does not match the 32/64 bit type!\n"); > + return -1;-ENOSPC> + } > + > + /* modify emulate register */ > + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; > + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); > + /* update the msi_info too */ > + s->msi->data = cfg_entry->data; > + > + /* create value for writing to I/O device register */ > + throughable_mask = ~reg->emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); > + > + /* update MSI */ > + if (cfg_entry->data != old_data) { > + if (flags & PT_MSI_FLAG_MAPPED) { > + pt_msi_update(s); > + } > + } > + > + return 0; > +} > + > +/* MSI Capability Structure reg static infomation table */ > +static XenPTRegInfo pt_emu_reg_msi_tbl[] = { > + /* Next Pointer reg */ > + { > + .offset = PCI_CAP_LIST_NEXT, > + .size = 1, > + .init_val = 0x00, > + .ro_mask = 0xFF, > + .emu_mask = 0xFF, > + .init = pt_ptr_reg_init, > + .u.b.read = pt_byte_reg_read, > + .u.b.write = pt_byte_reg_write, > + .u.b.restore = NULL, > + }, > + /* Message Control reg */ > + { > + .offset = PCI_MSI_FLAGS, > + .size = 2, > + .init_val = 0x0000, > + .ro_mask = 0xFF8E, > + .emu_mask = 0x007F, > + .init = pt_msgctrl_reg_init, > + .u.w.read = pt_word_reg_read, > + .u.w.write = pt_msgctrl_reg_write, > + .u.w.restore = NULL, > + }, > + /* Message Address reg */ > + { > + .offset = PCI_MSI_ADDRESS_LO, > + .size = 4, > + .init_val = 0x00000000, > + .ro_mask = 0x00000003, > + .emu_mask = 0xFFFFFFFF, > + .no_wb = 1, > + .init = pt_common_reg_init, > + .u.dw.read = pt_long_reg_read, > + .u.dw.write = pt_msgaddr32_reg_write, > + .u.dw.restore = NULL, > + }, > + /* Message Upper Address reg (if PCI_MSI_FLAGS_64BIT set) */ > + { > + .offset = PCI_MSI_ADDRESS_HI, > + .size = 4, > + .init_val = 0x00000000, > + .ro_mask = 0x00000000, > + .emu_mask = 0xFFFFFFFF, > + .no_wb = 1, > + .init = pt_msgaddr64_reg_init, > + .u.dw.read = pt_long_reg_read, > + .u.dw.write = pt_msgaddr64_reg_write, > + .u.dw.restore = NULL, > + }, > + /* Message Data reg (16 bits of data for 32-bit devices) */ > + { > + .offset = PCI_MSI_DATA_32, > + .size = 2, > + .init_val = 0x0000, > + .ro_mask = 0x0000, > + .emu_mask = 0xFFFF, > + .no_wb = 1, > + .init = pt_msgdata_reg_init, > + .u.w.read = pt_word_reg_read, > + .u.w.write = pt_msgdata_reg_write, > + .u.w.restore = NULL, > + }, > + /* Message Data reg (16 bits of data for 64-bit devices) */ > + { > + .offset = PCI_MSI_DATA_64, > + .size = 2, > + .init_val = 0x0000, > + .ro_mask = 0x0000, > + .emu_mask = 0xFFFF, > + .no_wb = 1, > + .init = pt_msgdata_reg_init, > + .u.w.read = pt_word_reg_read, > + .u.w.write = pt_msgdata_reg_write, > + .u.w.restore = NULL, > + }, > + { > + .size = 0, > + }, > +}; > + > + > +/************************************** > + * MSI-X Capability > + */ > + > +/* Message Control register for MSI-X */ > +static int pt_msixctrl_reg_init(XenPCIPassthroughState *s, > + XenPTRegInfo *reg, uint32_t real_offset, > + uint32_t *data) > +{ > + PCIDevice *d = &s->dev; > + uint16_t reg_field = 0; > + > + /* use I/O device register''s value as initial value */ > + reg_field = pci_get_word(d->config + real_offset); > + > + if (reg_field & PCI_MSIX_FLAGS_ENABLE) { > + PT_LOG(d, "MSIX already enabled, disable it first\n");.. disabling it first..> + host_pci_set_word(s->real_device, real_offset, > + reg_field & ~PCI_MSIX_FLAGS_ENABLE); > + } > + > + s->msix->ctrl_offset = real_offset; > + > + *data = reg->init_val; > + return 0; > +} > +static int pt_msixctrl_reg_write(XenPCIPassthroughState *s, > + XenPTReg *cfg_entry, uint16_t *value, > + uint16_t dev_value, uint16_t valid_mask) > +{ > + XenPTRegInfo *reg = cfg_entry->reg; > + uint16_t writable_mask = 0; > + uint16_t throughable_mask = 0; > + > + /* modify emulate register */ > + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; > + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); > + > + /* create value for writing to I/O device register */ > + throughable_mask = ~reg->emu_mask & valid_mask; > + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); > + > + /* update MSI-X */ > + if ((*value & PCI_MSIX_FLAGS_ENABLE) > + && !(*value & PCI_MSIX_FLAGS_MASKALL)) { > + if (s->msi_trans_en) { > + PT_LOG(&s->dev, > + "guest enabling MSI-X, disable MSI-INTx translation\n");... disabling> + pt_disable_msi_translate(s); > + } > + pt_msix_update(s); > + } > + > + s->msix->enabled = !!(*value & PCI_MSIX_FLAGS_ENABLE); > + > + return 0; > +} > + > +/* MSI-X Capability Structure reg static infomation table */ > +static XenPTRegInfo pt_emu_reg_msix_tbl[] = { > + /* Next Pointer reg */ > + { > + .offset = PCI_CAP_LIST_NEXT, > + .size = 1, > + .init_val = 0x00, > + .ro_mask = 0xFF, > + .emu_mask = 0xFF, > + .init = pt_ptr_reg_init, > + .u.b.read = pt_byte_reg_read, > + .u.b.write = pt_byte_reg_write, > + .u.b.restore = NULL, > + }, > + /* Message Control reg */ > + { > + .offset = PCI_MSI_FLAGS, > + .size = 2, > + .init_val = 0x0000, > + .ro_mask = 0x3FFF, > + .emu_mask = 0x0000, > + .init = pt_msixctrl_reg_init, > + .u.w.read = pt_word_reg_read, > + .u.w.write = pt_msixctrl_reg_write, > + .u.w.restore = NULL, > + }, > + { > + .size = 0, > + }, > +}; > + > > /**************************** > * Capabilities > @@ -1709,6 +2134,49 @@ static int pt_pcie_size_init(XenPCIPassthroughState *s, > *size = pcie_size; > return 0; > } > +/* get MSI Capability Structure register group size */ > +static int pt_msi_size_init(XenPCIPassthroughState *s, > + const XenPTRegGroupInfo *grp_reg, > + uint32_t base_offset, uint8_t *size) > +{ > + PCIDevice *d = &s->dev; > + uint16_t msg_ctrl = 0; > + uint8_t msi_size = 0xa; > + > + msg_ctrl = pci_get_word(d->config + (base_offset + PCI_MSI_FLAGS)); > + > + /* check 64 bit address capable & Per-vector masking capable */.. ''check if 64-bit address is capable of per-vector masking.''> + if (msg_ctrl & PCI_MSI_FLAGS_64BIT) { > + msi_size += 4; > + } > + if (msg_ctrl & PCI_MSI_FLAGS_MASKBIT) { > + msi_size += 10; > + } > + > + s->msi = g_new0(XenPTMSI, 1); > + s->msi->pirq = PT_UNASSIGNED_PIRQ; > + > + *size = msi_size; > + return 0; > +} > +/* get MSI-X Capability Structure register group size */ > +static int pt_msix_size_init(XenPCIPassthroughState *s, > + const XenPTRegGroupInfo *grp_reg, > + uint32_t base_offset, uint8_t *size) > +{ > + int rc = 0; > + > + rc = pt_msix_init(s, base_offset); > + > + if (rc == -1) {if (rc < 0) ..> + PT_ERR(&s->dev, "Internal error: Invalid pt_msix_init.\n"); > + return rc; > + } > + > + *size = grp_reg->grp_size; > + return 0; > +} > + > > static const XenPTRegGroupInfo pt_emu_reg_grp_tbl[] = { > /* Header Type0 reg group */ > @@ -1749,6 +2217,14 @@ static const XenPTRegGroupInfo pt_emu_reg_grp_tbl[] = { > .grp_size = 0x04, > .size_init = pt_reg_grp_size_init, > }, > + /* MSI Capability Structure reg group */ > + { > + .grp_id = PCI_CAP_ID_MSI, > + .grp_type = GRP_TYPE_EMU, > + .grp_size = 0xFF, > + .size_init = pt_msi_size_init, > + .emu_reg_tbl = pt_emu_reg_msi_tbl, > + }, > /* PCI-X Capabilities List Item reg group */ > { > .grp_id = PCI_CAP_ID_PCIX, > @@ -1793,6 +2269,14 @@ static const XenPTRegGroupInfo pt_emu_reg_grp_tbl[] = { > .size_init = pt_pcie_size_init, > .emu_reg_tbl = pt_emu_reg_pcie_tbl, > }, > + /* MSI-X Capability Structure reg group */ > + { > + .grp_id = PCI_CAP_ID_MSIX, > + .grp_type = GRP_TYPE_EMU, > + .grp_size = 0x0C, > + .size_init = pt_msix_size_init, > + .emu_reg_tbl = pt_emu_reg_msix_tbl, > + }, > { > .grp_size = 0, > }, > @@ -1965,8 +2449,11 @@ static int pt_init_pci_config(XenPCIPassthroughState *s) > return rc; > } > > + /* setup MSI-INTx translation if support */.. if supported.> + rc = pt_enable_msi_translate(s); > + > /* rebind machine_irq to device */ > - if (s->machine_irq != 0) { > + if (rc < 0 && s->machine_irq != 0) { > uint8_t e_device = PCI_SLOT(s->dev.devfn); > uint8_t e_intx = pci_intx(s); > > @@ -2117,6 +2604,14 @@ void pt_config_delete(XenPCIPassthroughState *s) > struct XenPTRegGroup *reg_group, *next_grp; > struct XenPTReg *reg, *next_reg; > > + /* free MSI/MSI-X info table */ > + if (s->msix) { > + pt_msix_delete(s); > + } > + if (s->msi) { > + g_free(s->msi); > + } > + > /* free Power Management info table */ > if (s->pm_state) { > if (s->pm_state->pm_timer) { > diff --git a/hw/xen_pci_passthrough_msi.c b/hw/xen_pci_passthrough_msi.c > new file mode 100644 > index 0000000..73d3d9b > --- /dev/null > +++ b/hw/xen_pci_passthrough_msi.c > @@ -0,0 +1,678 @@ > +/* > + * Copyright (c) 2007, Intel Corporation. > + * > + * This work is licensed under the terms of the GNU GPL, version 2. See > + * the COPYING file in the top-level directory. > + * > + * Jiang Yunhong <yunhong.jiang@intel.com> > + * > + * This file implements direct PCI assignment to a HVM guest > + */ > + > +#include <sys/mman.h> > + > +#include "xen_backend.h" > +#include "xen_pci_passthrough.h" > +#include "apic-msidef.h" > + > + > +#define AUTO_ASSIGN -1 > + > +/* shift count for gflags */ > +#define GFLAGS_SHIFT_DEST_ID 0 > +#define GFLAGS_SHIFT_RH 8 > +#define GFLAGS_SHIFT_DM 9 > +#define GLFAGS_SHIFT_DELIV_MODE 12 > +#define GLFAGS_SHIFT_TRG_MODE 15 > + > + > +/* > + * Helpers > + */ > + > +static uint32_t get_msi_gflags(uint32_t data, uint64_t addr) > +{ > + uint32_t result = 0; > + int rh, dm, dest_id, deliv_mode, trig_mode; > + > + rh = (addr >> MSI_ADDR_REDIRECTION_SHIFT) & 0x1; > + dm = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1; > + dest_id = (addr >> MSI_ADDR_DEST_ID_SHIFT) & 0xff; > + deliv_mode = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7; > + trig_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; > + > + result = dest_id | (rh << GFLAGS_SHIFT_RH) | (dm << GFLAGS_SHIFT_DM) | > + (deliv_mode << GLFAGS_SHIFT_DELIV_MODE) | > + (trig_mode << GLFAGS_SHIFT_TRG_MODE); > + > + return result; > +} > + > + > +/* > + * MSI virtualization functions > + */ > + > +void pt_msi_set_enable(XenPCIPassthroughState *s, int en)Usually these functions that ''enable'' something return a value. Perhaps this function should do that?> +{ > + uint16_t val = 0; > + uint32_t address = 0; > + > + PT_LOG(&s->dev, "enable: %i\n", en);Uh, why not: "PT_LOG(&s->dev,"%s.\n", en ? "enabling" : "disabling");> + > + if (!s->msi) { > + return; > + } > + > + address = s->msi->ctrl_offset; > + if (!address) { > + return; > + } > + > + host_pci_get_word(s->real_device, address, &val); > + val &= ~PCI_MSI_FLAGS_ENABLE; > + val |= en & PCI_MSI_FLAGS_ENABLE; > + host_pci_set_word(s->real_device, address, val); > +} > + > +/* setup physical msi, but don''t enable it */ > +int pt_msi_setup(XenPCIPassthroughState *s) > +{ > + int pirq = PT_UNASSIGNED_PIRQ; > + uint8_t gvec = 0; > + int rc = 0; > + > + if (!(s->msi->flags & PT_MSI_FLAG_UNINIT)) { > + PT_ERR(&s->dev, "Setup physical MSI when it''s already initialized.\n");Eh? I think you mean "Setup physical MSI when it has been properly initialized." ?> + return -1; > + } > + > + gvec = s->msi->data & 0xFF; > + if (!gvec) { > + /* if gvec is 0, the guest is asking for a particular pirq that > + * is passed as dest_id */ > + pirq = (s->msi->addr_hi & 0xffffff00) | > + ((s->msi->addr_lo >> MSI_ADDR_DEST_ID_SHIFT) & 0xff);Please macrofy this.> + if (!pirq) { > + /* this probably identifies an misconfiguration of the guest, > + * try the emulated path */ > + pirq = PT_UNASSIGNED_PIRQ; > + } else { > + PT_LOG(&s->dev, "requested pirq: %d\n", pirq); > + } > + } > + > + rc = xc_physdev_map_pirq_msi(xen_xc, xen_domid, AUTO_ASSIGN, &pirq, > + PCI_DEVFN(s->real_device->dev, > + s->real_device->func), > + s->real_device->bus, 0, 0);What are the two ''0'' at the end for? Can you provide a comment in there?> + if (rc) { > + PT_ERR(&s->dev, "Mapping of MSI failed. (rc: %d," > + " assigned device: %02x:%02x.%x)\n", rc, > + s->real_device->dev, s->real_device->func, s->real_device->bus);You might want to include the ''pirq'' value that was requested.> + return -1; > + } > + > + if (pirq < 0) { > + PT_ERR(&s->dev, "Invalid pirq number.\n"); > + return -1; > + } > + > + s->msi->pirq = pirq; > + PT_LOG(&s->dev, "MSI mapped with pirq %d.\n", pirq); > + > + return 0; > +} > + > +int pt_msi_update(XenPCIPassthroughState *s) > +{ > + uint8_t gvec = 0; > + uint32_t gflags = 0; > + uint64_t addr = 0; > + int rc = 0; > + > + /* get vector, address, flags info, etc. */ > + gvec = s->msi->data & 0xFF; > + addr = (uint64_t)s->msi->addr_hi << 32 | s->msi->addr_lo; > + gflags = get_msi_gflags(s->msi->data, addr); > + > + PT_LOG(&s->dev, "Update MSI with pirq %d gvec 0x%x gflags 0x%x\n",Updating> + s->msi->pirq, gvec, gflags); > + > + rc = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec, > + s->msi->pirq, gflags, 0); > + if (rc) { > + PT_ERR(&s->dev, "Binding of MSI failed. (rc: %d)\n", rc);Not binding.. Updating> + > + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, s->msi->pirq)) { > + PT_ERR(&s->dev, "Unmapping of MSI pirq %d failed.\n", > + s->msi->pirq); > + } > + s->msi->pirq = PT_UNASSIGNED_PIRQ; > + } > + return rc; > +} > + > +void pt_msi_disable(XenPCIPassthroughState *s) > +{ > + PCIDevice *d = &s->dev; > + uint8_t gvec = 0; > + uint32_t gflags = 0; > + uint64_t addr = 0; > + uint8_t e_device = 0; > + uint8_t e_intx = 0; > + > + pt_msi_set_enable(s, 0); > + > + e_device = PCI_SLOT(d->devfn); > + e_intx = pci_intx(s); > + > + if (s->msi_trans_en) { > + if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, s->msi->pirq, > + PT_IRQ_TYPE_MSI_TRANSLATE, 0, > + e_device, e_intx, 0)) { > + PT_ERR(d, "Unbinding pt irq for MSI-INTx failed!\n");Please include the e_intx and pirq that was being unbinded.> + goto out; > + } > + } else if (!(s->msi->flags & PT_MSI_FLAG_UNINIT)) { > + /* get vector, address, flags info, etc. */ > + gvec = s->msi->data & 0xFF; > + addr = (uint64_t)s->msi->addr_hi << 32 | s->msi->addr_lo; > + gflags = get_msi_gflags(s->msi->data, addr); > + > + PT_ERR(&s->dev, "Unbind MSI with pirq %x, gvec %x\n", > + s->msi->pirq, gvec); > + > + if (xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec, > + s->msi->pirq, gflags)) { > + PT_ERR(&s->dev, "Unbinding of MSI failed. (pirq: %d)\n", > + s->msi->pirq); > + goto out; > + } > + } > + > + if (s->msi->pirq != PT_UNASSIGNED_PIRQ) { > + PT_LOG(&s->dev, "Unmap msi with pirq %d\n", s->msi->pirq); > + > + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, s->msi->pirq)) { > + PT_ERR(&s->dev, "Unmapping of MSI failed. (pirq: %d)\n", > + s->msi->pirq); > + goto out; > + } > + } > + > +out: > + /* clear msi info */ > + s->msi->flags = 0; > + s->msi->pirq = -1; > + s->msi_trans_en = 0; > +} > + > +/* > + * MSI-INTx translation virtualization functions > + */ > + > +int pt_enable_msi_translate(XenPCIPassthroughState *s) > +{ > + uint8_t e_device = 0; > + uint8_t e_intx = 0; > + > + if (!(s->msi && s->msi_trans_cap)) { > + return -1;-ENODEV ?> + } > + > + pt_msi_set_enable(s, 0); > + s->msi_trans_en = 0; > + > + if (pt_msi_setup(s)) {You can save the return value from pt_msi_setup..> + PT_ERR(&s->dev, "MSI-INTx translation MSI setup failed, fallback\n");..falling back. But not sure what it is actually falling back to? Just to normal INTx injecting? If so, you might want to say that. .. and the return value from pt_msi_setup could be returned here.> + return -1; > + } > + > + e_device = PCI_SLOT(s->dev.devfn); > + /* fix virtual interrupt pin to INTA# */ > + e_intx = pci_intx(s); > + > + if (xc_domain_bind_pt_irq(xen_xc, xen_domid, s->msi->pirq, > + PT_IRQ_TYPE_MSI_TRANSLATE, 0, > + e_device, e_intx, 0)) {Please document those ''0''.> + PT_ERR(&s->dev, "MSI-INTx translation bind failed, fallback\n"); > + > + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, s->msi->pirq)) { > + PT_ERR(&s->dev, "Unmapping of MSI failed.\n");Mention which PIRQ failed.> + } > + s->msi->pirq = PT_UNASSIGNED_PIRQ; > + return -1;You can return the error value returned from xc_domain_bind_pt_irq..> + } > + > + pt_msi_set_enable(s, 1);Should really have pt_msi_set_enable return a value and then check it. Or do we really don''t care about its value? If so, please mention that in the pt_msi_set_enable.> + s->msi_trans_en = 1; > + > + return 0; > +} > + > +void pt_disable_msi_translate(XenPCIPassthroughState *s) > +{ > + uint8_t e_device = 0; > + uint8_t e_intx = 0; > + > + /* MSI_ENABLE bit should be disabed until the new handler is set */disabled.> + pt_msi_set_enable(s, 0); > + > + e_device = PCI_SLOT(s->dev.devfn); > + e_intx = pci_intx(s); > + > + if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, s->msi->pirq, > + PT_IRQ_TYPE_MSI_TRANSLATE, 0, > + e_device, e_intx, 0)) { > + PT_ERR(&s->dev, "Unbinding pt irq for MSI-INTx failed!\n");Can you include the error code please?> + } > + > + if (s->machine_irq) { > + if (xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, s->machine_irq, > + 0, e_device, e_intx)) { > + PT_ERR(&s->dev, "Rebinding of interrupt failed!\n");And here as well.. And also the machine_irq and e_intx value.> + } > + } > + > + s->msi_trans_en = 0; > +} > + > +/* > + * MSI-X virtualization functions > + */ > + > +static void msix_set_enable(XenPCIPassthroughState *s, int en)No return value? The ''int en'' can also be a bool.> +{ > + uint16_t val = 0; > + uint32_t address = 0; > + > + if (!s->msix) { > + return; > + } > + > + address = s->msix->ctrl_offset; > + if (!address) { > + return; > + } > + > + host_pci_get_word(s->real_device, address, &val); > + val &= ~PCI_MSIX_FLAGS_ENABLE; > + if (en) { > + val |= PCI_MSIX_FLAGS_ENABLE; > + } > + host_pci_set_word(s->real_device, address, val); > +} > + > +static void mask_physical_msix_entry(XenPCIPassthroughState *s, > + int entry_nr, int mask) > +{ > + void *phys_off; > + > + phys_off = s->msix->phys_iomem_base + 16 * entry_nr + 12;Can you provide a comment explaining this math please?> + *(uint32_t *)phys_off = mask; > +} > + > +static int pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr) > +{ > + XenMSIXEntry *entry = &s->msix->msix_entry[entry_nr];You should be checking whether entry_nr is outside the bounds of msix_entry just in case.> + int pirq = entry->pirq; > + int gvec = entry->io_mem[2] & 0xff; > + uint64_t gaddr = *(uint64_t *)&entry->io_mem[0]; > + uint32_t gflags = get_msi_gflags(entry->io_mem[2], gaddr); > + int rc; > + > + if (!entry->flags) { > + return 0; > + } > + > + if (!gvec) { > + /* if gvec is 0, the guest is asking for a particular pirq that > + * is passed as dest_id */ > + pirq = ((gaddr >> 32) & 0xffffff00) | > + (((gaddr & 0xffffffff) >> MSI_ADDR_DEST_ID_SHIFT) & 0xff); > + if (!pirq) { > + /* this probably identifies an misconfiguration of the guest, > + * try the emulated path */ > + pirq = PT_UNASSIGNED_PIRQ; > + } else { > + PT_LOG(&s->dev, "requested pirq: %d\n", pirq); > + } > + } > + > + /* Check if this entry is already mapped */ > + if (entry->pirq == PT_UNASSIGNED_PIRQ) { > + rc = xc_physdev_map_pirq_msi(xen_xc, xen_domid, AUTO_ASSIGN, &pirq, > + PCI_DEVFN(s->real_device->dev, > + s->real_device->func), > + s->real_device->bus, entry_nr, > + s->msix->table_base); > + if (rc) { > + PT_ERR(&s->dev, "Mapping msix entry %x\n", entry_nr); > + return rc; > + } > + entry->pirq = pirq; > + } > + > + PT_LOG(&s->dev, "Update msix entry 0x%x with pirq %d gvec 0x%x\n", > + entry_nr, pirq, gvec); > + > + rc = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec, pirq, gflags, > + s->msix->mmio_base_addr); > + if (rc) { > + PT_ERR(&s->dev, "Updating msix irq %d info for entry %d\n", > + pirq, entry_nr); > + > + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, entry->pirq)) { > + PT_ERR(&s->dev, "Unmapping of MSI-X failed. (pirq: %d)\n", > + entry->pirq); > + } > + entry->pirq = PT_UNASSIGNED_PIRQ; > + return rc; > + } > + > + entry->flags = 0;This whole function looks so similar to msi one.. Is there no chance of collapsing them together? And perhaps having an extra argument to check if it is MSI or MSI-X and where to get the gflags and such.> + > + return 0; > +} > + > +int pt_msix_update(XenPCIPassthroughState *s) > +{ > + XenPTMSIX *msix = s->msix; > + int i; > + > + for (i = 0; i < msix->total_entries; i++) { > + pt_msix_update_one(s, i); > + } > + > + return 0; > +} > + > +void pt_msix_disable(XenPCIPassthroughState *s) > +{ > + PCIDevice *d = &s->dev; > + uint8_t gvec = 0; > + uint32_t gflags = 0; > + uint64_t addr = 0; > + int i = 0; > + XenMSIXEntry *entry = NULL; > + > + msix_set_enable(s, 0); > + > + for (i = 0; i < s->msix->total_entries; i++) { > + entry = &s->msix->msix_entry[i]; > + > + if (entry->pirq == PT_UNASSIGNED_PIRQ) { > + continue; > + } > + > + gvec = entry->io_mem[2] & 0xff; > + addr = *(uint64_t *)&entry->io_mem[0]; > + gflags = get_msi_gflags(entry->io_mem[2], addr); > + > + PT_LOG(d, "Unbind msix with pirq %d, gvec 0x%x\n", > + entry->pirq, gvec); > + > + if (xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec, > + entry->pirq, gflags)) { > + PT_ERR(d, "Unbinding of MSI-X failed.\n"); > + } else { > + PT_LOG(d, "Unmap msix with pirq %d\n", entry->pirq); > + > + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, entry->pirq)) { > + PT_ERR(d, "Unmapping of MSI-X failed.\n"); > + } > + } > + /* clear msi-x info */ > + entry->pirq = PT_UNASSIGNED_PIRQ; > + entry->flags = 0; > + }This looks like a candidate for squashing with the MSI as well. If you can manage to abstract the gflags, addr, etc.> +} > + > +int pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index) > +{ > + XenMSIXEntry *entry; > + int i, ret; > + > + if (!(s->msix && s->msix->bar_index == bar_index)) { > + return 0; > + } > + > + for (i = 0; i < s->msix->total_entries; i++) { > + entry = &s->msix->msix_entry[i]; > + if (entry->pirq != PT_UNASSIGNED_PIRQ) { > + ret = xc_domain_unbind_pt_irq(xen_xc, xen_domid, entry->pirq, > + PT_IRQ_TYPE_MSI, 0, 0, 0, 0);Lots of zeros. Can you document those please?> + if (ret) { > + PT_ERR(&s->dev, "unbind MSI-X entry %d failed\n", entry->pirq); > + } > + entry->flags = 1;Could use a #define for the ''1'' value.> + } > + } > + pt_msix_update(s); > + > + return 0; > +} > + > +static void pci_msix_invalid_write(void *opaque, target_phys_addr_t addr, > + uint32_t val) > +{ > + XenPCIPassthroughState *s = opaque; > + PT_ERR(&s->dev, "Invalid write to MSI-X table," > + " only dword access is allowed.\n"); > +} > + > +static void pci_msix_writel(void *opaque, target_phys_addr_t addr, > + uint32_t val) > +{ > + XenPCIPassthroughState *s = opaque; > + XenPTMSIX *msix = s->msix; > + XenMSIXEntry *entry; > + int entry_nr, offset; > + void *phys_off; > + uint32_t vec_ctrl; > + > + if (addr % 4) { > + PT_ERR(&s->dev, "Unaligned dword access to MSI-X table, " > + "addr 0x"TARGET_FMT_plx"\n", addr); > + return; > + } > + > + entry_nr = addr / 16; > + entry = &msix->msix_entry[entry_nr];You should double-check that that entry_nr is actually allocated.> + offset = (addr % 16) / 4; > + > + /* > + * If Xen intercepts the mask bit access, io_mem[3] may not be > + * up-to-date. Read from hardware directly. > + */ > + phys_off = s->msix->phys_iomem_base + 16 * entry_nr + 12; > + vec_ctrl = *(uint32_t *)phys_off; > + > + if (offset != 3 && msix->enabled && !(vec_ctrl & 0x1)) { > + PT_ERR(&s->dev, "Can''t update msix entry %d since MSI-X is already " > + "enabled.\n", entry_nr);Well, and a whole bunch of other things are set. Would it make sense to provide ''offset'' and ''vec_ctrl'' contents?> + return; > + } > + > + if (offset != 3 && entry->io_mem[offset] != val) {What is the magical ''3'' value? Can you document it a bit please? Perhaps along with the io_mem[x] definition?> + entry->flags = 1; > + } > + entry->io_mem[offset] = val; > + > + if (offset == 3) { > + if (msix->enabled && !(val & 0x1)) { > + pt_msix_update_one(s, entry_nr); > + } > + mask_physical_msix_entry(s, entry_nr, entry->io_mem[3] & 0x1); > + } > +} > + > +static CPUWriteMemoryFunc *pci_msix_write[] = { > + pci_msix_invalid_write, > + pci_msix_invalid_write, > + pci_msix_writel > +}; > + > +static uint32_t pci_msix_invalid_read(void *opaque, target_phys_addr_t addr) > +{ > + XenPCIPassthroughState *s = opaque; > + PT_ERR(&s->dev, "Invalid read to MSI-X table," > + " only dword access is allowed.\n"); > + return 0; > +} > + > +static uint32_t pci_msix_readl(void *opaque, target_phys_addr_t addr) > +{ > + XenPCIPassthroughState *s = opaque; > + XenPTMSIX *msix = s->msix; > + int entry_nr, offset; > + > + if (addr % 4) { > + PT_ERR(&s->dev, "Unaligned dword access to MSI-X table, " > + "addr 0x"TARGET_FMT_plx"\n", addr); > + return 0; > + } > + > + entry_nr = addr / 16; > + offset = (addr % 16) / 4; > + > + return msix->msix_entry[entry_nr].io_mem[offset]; > +} > + > +static CPUReadMemoryFunc *pci_msix_read[] = { > + pci_msix_invalid_read, > + pci_msix_invalid_read, > + pci_msix_readl > +}; > + > +int pt_add_msix_mapping(XenPCIPassthroughState *s, int bar_index) > +{ > + if (!(s->msix && s->msix->bar_index == bar_index)) { > + return 0; > + } > + > + return xc_domain_memory_mapping(xen_xc, xen_domid, > + s->msix->mmio_base_addr >> XC_PAGE_SHIFT, > + (s->bases[bar_index].access.maddr + s->msix->table_off) > + >> XC_PAGE_SHIFT, > + (s->msix->total_entries * 16 + XC_PAGE_SIZE - 1) >> XC_PAGE_SHIFT, > + DPCI_ADD_MAPPING); > +} > + > +int pt_remove_msix_mapping(XenPCIPassthroughState *s, int bar_index) > +{ > + if (!(s->msix && s->msix->bar_index == bar_index)) { > + return 0; > + } > + > + s->msix->mmio_base_addr = s->bases[bar_index].e_physbase > + + s->msix->table_off; > + > + cpu_register_physical_memory(s->msix->mmio_base_addr, > + s->msix->total_entries * 16, > + s->msix->mmio_index); > + > + return xc_domain_memory_mapping(xen_xc, xen_domid, > + s->msix->mmio_base_addr >> XC_PAGE_SHIFT, > + (s->bases[bar_index].access.maddr + s->msix->table_off) > + >> XC_PAGE_SHIFT, > + (s->msix->total_entries * 16 + XC_PAGE_SIZE - 1) >> XC_PAGE_SHIFT, > + DPCI_REMOVE_MAPPING); > +} > + > +int pt_msix_init(XenPCIPassthroughState *s, int base) > +{ > + uint8_t id = 0; > + uint16_t control = 0; > + uint32_t table_off = 0; > + int i, total_entries, bar_index; > + HostPCIDevice *d = s->real_device; > + int fd; > + > + if (host_pci_get_byte(d, base + PCI_CAP_LIST_ID, &id)) { > + return -1;-ENOSPC?> + } > + > + if (id != PCI_CAP_ID_MSIX) { > + PT_ERR(&s->dev, "Invalid id %#x base %#x\n", id, base); > + return -1; > + } > + > + host_pci_get_word(d, base + 2, &control); > + total_entries = control & 0x7ff; > + total_entries += 1; > + > + s->msix = g_malloc0(sizeof (XenPTMSIX) > + + total_entries * sizeof (XenMSIXEntry)); > + > + s->msix->total_entries = total_entries; > + for (i = 0; i < total_entries; i++) { > + s->msix->msix_entry[i].pirq = PT_UNASSIGNED_PIRQ; > + } > + > + s->msix->mmio_index > + cpu_register_io_memory(pci_msix_read, pci_msix_write, > + s, DEVICE_NATIVE_ENDIAN); > + > + host_pci_get_long(d, base + PCI_MSIX_TABLE, &table_off); > + bar_index = s->msix->bar_index = table_off & PCI_MSIX_FLAGS_BIRMASK; > + table_off = s->msix->table_off = table_off & ~PCI_MSIX_FLAGS_BIRMASK; > + s->msix->table_base = s->real_device->io_regions[bar_index].base_addr; > + PT_LOG(&s->dev, "get MSI-X table BAR base 0x%"PRIx64"\n", > + s->msix->table_base);Can you also include the amount of entries it can have?> + > + fd = open("/dev/mem", O_RDWR); > + if (fd == -1) { > + PT_ERR(&s->dev, "Can''t open /dev/mem: %s\n", strerror(errno)); > + goto error_out; > + } > + PT_LOG(&s->dev, "table_off = %#x, total_entries = %d\n", > + table_off, total_entries); > + s->msix->table_offset_adjust = table_off & 0x0fff; > + s->msix->phys_iomem_base > + mmap(0, > + total_entries * 16 + s->msix->table_offset_adjust, > + PROT_WRITE | PROT_READ, > + MAP_SHARED | MAP_LOCKED, > + fd, > + s->msix->table_base + table_off - s->msix->table_offset_adjust); > + > + if (s->msix->phys_iomem_base == MAP_FAILED) { > + PT_ERR(&s->dev, "Can''t map physical MSI-X table: %s\n", > + strerror(errno)); > + close(fd); > + goto error_out; > + } > + s->msix->phys_iomem_base = (char *)s->msix->phys_iomem_base > + + s->msix->table_offset_adjust; > + > + close(fd); > + > + PT_LOG(&s->dev, "mapping physical MSI-X table to %p\n", > + s->msix->phys_iomem_base); > + return 0; > + > +error_out: > + g_free(s->msix); > + s->msix = NULL; > + return -1; > +} > + > +void pt_msix_delete(XenPCIPassthroughState *s) > +{ > + /* unmap the MSI-X memory mapped register area */ > + if (s->msix->phys_iomem_base) { > + PT_LOG(&s->dev, "unmapping physical MSI-X table from %p\n", > + s->msix->phys_iomem_base); > + munmap(s->msix->phys_iomem_base, s->msix->total_entries * 16 + > + s->msix->table_offset_adjust); > + } > + > + if (s->msix->mmio_index > 0) { > + cpu_unregister_io_memory(s->msix->mmio_index); > + } > + > + g_free(s->msix); > + s->msix = NULL; > +} > -- > Anthony PERARD > > > _______________________________________________ > Xen-devel mailing list > Xen-devel@lists.xensource.com > http://lists.xensource.com/xen-devel