All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
@ 2011-10-04 14:51 ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Hi all,

This patch series introduce the PCI passthrough for Xen.

First, we have HostPCIDevice that help to access one PCI device of the host.

Then, there are several additions in the QEMU code. One is qemu_run_one_timer
to run a specific timer. It is used by PCI passthrough to run a timer about
power management. Another is 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.

I'm still working on 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".

There is some minor things missing:
 - copyright header
 - PCI IO space multiplexer

Regards,


Anthony PERARD (11):
  Introduce HostPCIDevice to access a pci device on the host.
  qemu-timer: Introduce qemu_run_one_timer
  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
  pci.c: Add pci_check_bar_overlap
  host-pci-device: Add host_pci_find_ext_cap_offset
  Introduce Xen PCI Passthrough, qdevice (1/3)
  Introduce Xen PCI Passthrough, PCI config space helpers (2/3)
  Introduce Xen PCI Passthrough, MSI (3/3)
  config/make: Introduce --enable-xen-pci-passthrough, built it.

 Makefile.target                      |    7 +
 configure                            |   21 +
 hw/host-pci-device.c                 |  223 +++
 hw/host-pci-device.h                 |   38 +
 hw/pci.c                             |   46 +
 hw/pci.h                             |    3 +
 hw/pci_ids.h                         |    1 +
 hw/pci_regs.h                        |    3 +-
 hw/xen_pci_passthrough.c             |  763 +++++++++++
 hw/xen_pci_passthrough.h             |  335 +++++
 hw/xen_pci_passthrough_config_init.c | 2489 ++++++++++++++++++++++++++++++++++
 hw/xen_pci_passthrough_helpers.c     |   46 +
 hw/xen_pci_passthrough_msi.c         |  674 +++++++++
 qemu-timer.c                         |   15 +
 qemu-timer.h                         |    3 +
 15 files changed, 4666 insertions(+), 1 deletions(-)
 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_helpers.c
 create mode 100644 hw/xen_pci_passthrough_msi.c

-- 
Anthony PERARD

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCH RFC V1 00/11] Xen PCI Passthrough
@ 2011-10-04 14:51 ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Hi all,

This patch series introduce the PCI passthrough for Xen.

First, we have HostPCIDevice that help to access one PCI device of the host.

Then, there are several additions in the QEMU code. One is qemu_run_one_timer
to run a specific timer. It is used by PCI passthrough to run a timer about
power management. Another is 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.

I'm still working on 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".

There is some minor things missing:
 - copyright header
 - PCI IO space multiplexer

Regards,


Anthony PERARD (11):
  Introduce HostPCIDevice to access a pci device on the host.
  qemu-timer: Introduce qemu_run_one_timer
  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
  pci.c: Add pci_check_bar_overlap
  host-pci-device: Add host_pci_find_ext_cap_offset
  Introduce Xen PCI Passthrough, qdevice (1/3)
  Introduce Xen PCI Passthrough, PCI config space helpers (2/3)
  Introduce Xen PCI Passthrough, MSI (3/3)
  config/make: Introduce --enable-xen-pci-passthrough, built it.

 Makefile.target                      |    7 +
 configure                            |   21 +
 hw/host-pci-device.c                 |  223 +++
 hw/host-pci-device.h                 |   38 +
 hw/pci.c                             |   46 +
 hw/pci.h                             |    3 +
 hw/pci_ids.h                         |    1 +
 hw/pci_regs.h                        |    3 +-
 hw/xen_pci_passthrough.c             |  763 +++++++++++
 hw/xen_pci_passthrough.h             |  335 +++++
 hw/xen_pci_passthrough_config_init.c | 2489 ++++++++++++++++++++++++++++++++++
 hw/xen_pci_passthrough_helpers.c     |   46 +
 hw/xen_pci_passthrough_msi.c         |  674 +++++++++
 qemu-timer.c                         |   15 +
 qemu-timer.h                         |    3 +
 15 files changed, 4666 insertions(+), 1 deletions(-)
 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_helpers.c
 create mode 100644 hw/xen_pci_passthrough_msi.c

-- 
Anthony PERARD

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [Qemu-devel] [PATCH RFC V1 01/11] Introduce HostPCIDevice to access a pci device on the host.
  2011-10-04 14:51 ` Anthony PERARD
@ 2011-10-04 14:51   ` Anthony PERARD
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 hw/host-pci-device.c |  192 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/host-pci-device.h |   36 +++++++++
 2 files changed, 228 insertions(+), 0 deletions(-)
 create mode 100644 hw/host-pci-device.c
 create mode 100644 hw/host-pci-device.h

diff --git a/hw/host-pci-device.c b/hw/host-pci-device.c
new file mode 100644
index 0000000..b3f2899
--- /dev/null
+++ b/hw/host-pci-device.c
@@ -0,0 +1,192 @@
+#include "qemu-common.h"
+#include "host-pci-device.h"
+
+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;
+    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 -1;
+    }
+
+    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);
+            break;
+        }
+        if (start) {
+            size = end - start + 1;
+        } else {
+            size = 0;
+        }
+
+        flags &= 0xf;
+
+        if (i < PCI_ROM_SLOT) {
+            d->base_addr[i] = start | flags;
+            d->size[i] = size;
+        } else {
+            d->rom_base_addr = start | flags;
+            d->rom_size = size;
+        }
+    }
+
+    fclose(f);
+    return 0;
+}
+
+static unsigned long get_value(HostPCIDevice *d, const char *name)
+{
+    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 -1;
+    }
+    if (fscanf(f, "%lx\n", &value) != 1) {
+        fprintf(stderr, "Error: Syntax error in %s\n", path);
+        value = -1;
+    }
+    fclose(f);
+    return value;
+}
+
+static int pci_dev_is_virtfn(HostPCIDevice *d)
+{
+    int rc;
+    char path[PATH_MAX];
+    struct stat buf;
+
+    path_to(d, "physfn", path, sizeof (path));
+    rc = !stat(path, &buf);
+
+    return rc;
+}
+
+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;
+
+    res = pread(fd, buf, len, pos);
+    if (res < 0) {
+        fprintf(stderr, "host_pci_config: read failed: %s (fd: %i)\n",
+                strerror(errno), fd);
+        return -1;
+    }
+    return res;
+}
+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;
+
+    res = pwrite(fd, buf, len, pos);
+    if (res < 0) {
+        fprintf(stderr, "host_pci_config: write failed: %s\n",
+                strerror(errno));
+        return -1;
+    }
+    return res;
+}
+
+uint8_t host_pci_read_byte(HostPCIDevice *d, int pos)
+{
+  uint8_t buf;
+  host_pci_config_read(d, pos, &buf, 1);
+  return buf;
+}
+uint16_t host_pci_read_word(HostPCIDevice *d, int pos)
+{
+  uint16_t buf;
+  host_pci_config_read(d, pos, &buf, 2);
+  return le16_to_cpu(buf);
+}
+uint32_t host_pci_read_long(HostPCIDevice *d, int pos)
+{
+  uint32_t buf;
+  host_pci_config_read(d, pos, &buf, 4);
+  return le32_to_cpu(buf);
+}
+int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
+{
+  return host_pci_config_read(d, pos, buf, len);
+}
+
+int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data)
+{
+  return host_pci_config_write(d, pos, &data, 1);
+}
+int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data)
+{
+  return host_pci_config_write(d, pos, &data, 2);
+}
+int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data)
+{
+  return host_pci_config_write(d, pos, &data, 4);
+}
+int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
+{
+  return host_pci_config_write(d, pos, buf, len);
+}
+
+HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func)
+{
+    HostPCIDevice *d = NULL;
+
+    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) == -1)
+        goto error;
+
+    d->vendor_id = get_value(d, "vendor");
+    d->device_id = get_value(d, "device");
+    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;
+}
diff --git a/hw/host-pci-device.h b/hw/host-pci-device.h
new file mode 100644
index 0000000..0137507
--- /dev/null
+++ b/hw/host-pci-device.h
@@ -0,0 +1,36 @@
+#ifndef HW_HOST_PCI_DEVICE
+#  define HW_HOST_PCI_DEVICE
+
+#include "pci.h"
+
+typedef struct HostPCIDevice {
+    uint16_t domain;
+    uint8_t bus;
+    uint8_t dev;
+    uint8_t func;
+
+    uint16_t vendor_id;
+    uint16_t device_id;
+
+    pcibus_t base_addr[PCI_NUM_REGIONS - 1];
+    pcibus_t size[PCI_NUM_REGIONS - 1];
+    pcibus_t rom_base_addr;
+    pcibus_t rom_size;
+
+    bool is_virtfn;
+
+    int config_fd;
+} HostPCIDevice;
+
+HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func);
+
+uint8_t host_pci_read_byte(HostPCIDevice *d, int pos);
+uint16_t host_pci_read_word(HostPCIDevice *d, int pos);
+uint32_t host_pci_read_long(HostPCIDevice *d, int pos);
+int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
+int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data);
+int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data);
+int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data);
+int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
+
+#endif /* !HW_HOST_PCI_DEVICE */
-- 
Anthony PERARD

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCH RFC V1 01/11] Introduce HostPCIDevice to access a pci device on the host.
@ 2011-10-04 14:51   ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 hw/host-pci-device.c |  192 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/host-pci-device.h |   36 +++++++++
 2 files changed, 228 insertions(+), 0 deletions(-)
 create mode 100644 hw/host-pci-device.c
 create mode 100644 hw/host-pci-device.h

diff --git a/hw/host-pci-device.c b/hw/host-pci-device.c
new file mode 100644
index 0000000..b3f2899
--- /dev/null
+++ b/hw/host-pci-device.c
@@ -0,0 +1,192 @@
+#include "qemu-common.h"
+#include "host-pci-device.h"
+
+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;
+    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 -1;
+    }
+
+    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);
+            break;
+        }
+        if (start) {
+            size = end - start + 1;
+        } else {
+            size = 0;
+        }
+
+        flags &= 0xf;
+
+        if (i < PCI_ROM_SLOT) {
+            d->base_addr[i] = start | flags;
+            d->size[i] = size;
+        } else {
+            d->rom_base_addr = start | flags;
+            d->rom_size = size;
+        }
+    }
+
+    fclose(f);
+    return 0;
+}
+
+static unsigned long get_value(HostPCIDevice *d, const char *name)
+{
+    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 -1;
+    }
+    if (fscanf(f, "%lx\n", &value) != 1) {
+        fprintf(stderr, "Error: Syntax error in %s\n", path);
+        value = -1;
+    }
+    fclose(f);
+    return value;
+}
+
+static int pci_dev_is_virtfn(HostPCIDevice *d)
+{
+    int rc;
+    char path[PATH_MAX];
+    struct stat buf;
+
+    path_to(d, "physfn", path, sizeof (path));
+    rc = !stat(path, &buf);
+
+    return rc;
+}
+
+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;
+
+    res = pread(fd, buf, len, pos);
+    if (res < 0) {
+        fprintf(stderr, "host_pci_config: read failed: %s (fd: %i)\n",
+                strerror(errno), fd);
+        return -1;
+    }
+    return res;
+}
+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;
+
+    res = pwrite(fd, buf, len, pos);
+    if (res < 0) {
+        fprintf(stderr, "host_pci_config: write failed: %s\n",
+                strerror(errno));
+        return -1;
+    }
+    return res;
+}
+
+uint8_t host_pci_read_byte(HostPCIDevice *d, int pos)
+{
+  uint8_t buf;
+  host_pci_config_read(d, pos, &buf, 1);
+  return buf;
+}
+uint16_t host_pci_read_word(HostPCIDevice *d, int pos)
+{
+  uint16_t buf;
+  host_pci_config_read(d, pos, &buf, 2);
+  return le16_to_cpu(buf);
+}
+uint32_t host_pci_read_long(HostPCIDevice *d, int pos)
+{
+  uint32_t buf;
+  host_pci_config_read(d, pos, &buf, 4);
+  return le32_to_cpu(buf);
+}
+int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
+{
+  return host_pci_config_read(d, pos, buf, len);
+}
+
+int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data)
+{
+  return host_pci_config_write(d, pos, &data, 1);
+}
+int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data)
+{
+  return host_pci_config_write(d, pos, &data, 2);
+}
+int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data)
+{
+  return host_pci_config_write(d, pos, &data, 4);
+}
+int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
+{
+  return host_pci_config_write(d, pos, buf, len);
+}
+
+HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func)
+{
+    HostPCIDevice *d = NULL;
+
+    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) == -1)
+        goto error;
+
+    d->vendor_id = get_value(d, "vendor");
+    d->device_id = get_value(d, "device");
+    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;
+}
diff --git a/hw/host-pci-device.h b/hw/host-pci-device.h
new file mode 100644
index 0000000..0137507
--- /dev/null
+++ b/hw/host-pci-device.h
@@ -0,0 +1,36 @@
+#ifndef HW_HOST_PCI_DEVICE
+#  define HW_HOST_PCI_DEVICE
+
+#include "pci.h"
+
+typedef struct HostPCIDevice {
+    uint16_t domain;
+    uint8_t bus;
+    uint8_t dev;
+    uint8_t func;
+
+    uint16_t vendor_id;
+    uint16_t device_id;
+
+    pcibus_t base_addr[PCI_NUM_REGIONS - 1];
+    pcibus_t size[PCI_NUM_REGIONS - 1];
+    pcibus_t rom_base_addr;
+    pcibus_t rom_size;
+
+    bool is_virtfn;
+
+    int config_fd;
+} HostPCIDevice;
+
+HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func);
+
+uint8_t host_pci_read_byte(HostPCIDevice *d, int pos);
+uint16_t host_pci_read_word(HostPCIDevice *d, int pos);
+uint32_t host_pci_read_long(HostPCIDevice *d, int pos);
+int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
+int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data);
+int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data);
+int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data);
+int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
+
+#endif /* !HW_HOST_PCI_DEVICE */
-- 
Anthony PERARD

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [Qemu-devel] [PATCH RFC V1 02/11] qemu-timer: Introduce qemu_run_one_timer
  2011-10-04 14:51 ` Anthony PERARD
@ 2011-10-04 14:51   ` Anthony PERARD
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Used by the Xen PCI Passthrough code to run the timer about the power
state transition.

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 qemu-timer.c |   15 +++++++++++++++
 qemu-timer.h |    3 +++
 2 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/qemu-timer.c b/qemu-timer.c
index 46dd483..15e659b 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -1163,3 +1163,18 @@ int qemu_calculate_timeout(void)
     return 1000;
 }
 
+/* run the specified timer */
+void qemu_run_one_timer(QEMUTimer *ts)
+{
+    uint64_t current_time;
+
+    /* remove timer from the list before calling the callback */
+    qemu_del_timer(ts);
+
+    while ((current_time = qemu_get_clock_ms(rt_clock)) < ts->expire_time)
+        /* sleep until the expire time */
+        usleep((ts->expire_time - current_time) * 1000);
+
+    /* run the callback */
+    ts->cb(ts->opaque);
+}
diff --git a/qemu-timer.h b/qemu-timer.h
index 0a43469..b7b907b 100644
--- a/qemu-timer.h
+++ b/qemu-timer.h
@@ -338,4 +338,7 @@ extern int64_t tlb_flush_time;
 extern int64_t dev_time;
 #endif
 
+/* run the specified timer */
+void qemu_run_one_timer(QEMUTimer *ts);
+
 #endif
-- 
Anthony PERARD

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCH RFC V1 02/11] qemu-timer: Introduce qemu_run_one_timer
@ 2011-10-04 14:51   ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Used by the Xen PCI Passthrough code to run the timer about the power
state transition.

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 qemu-timer.c |   15 +++++++++++++++
 qemu-timer.h |    3 +++
 2 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/qemu-timer.c b/qemu-timer.c
index 46dd483..15e659b 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -1163,3 +1163,18 @@ int qemu_calculate_timeout(void)
     return 1000;
 }
 
+/* run the specified timer */
+void qemu_run_one_timer(QEMUTimer *ts)
+{
+    uint64_t current_time;
+
+    /* remove timer from the list before calling the callback */
+    qemu_del_timer(ts);
+
+    while ((current_time = qemu_get_clock_ms(rt_clock)) < ts->expire_time)
+        /* sleep until the expire time */
+        usleep((ts->expire_time - current_time) * 1000);
+
+    /* run the callback */
+    ts->cb(ts->opaque);
+}
diff --git a/qemu-timer.h b/qemu-timer.h
index 0a43469..b7b907b 100644
--- a/qemu-timer.h
+++ b/qemu-timer.h
@@ -338,4 +338,7 @@ extern int64_t tlb_flush_time;
 extern int64_t dev_time;
 #endif
 
+/* run the specified timer */
+void qemu_run_one_timer(QEMUTimer *ts);
+
 #endif
-- 
Anthony PERARD

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [Qemu-devel] [PATCH RFC V1 03/11] pci_ids: Add INTEL_82599_VF id.
  2011-10-04 14:51 ` Anthony PERARD
@ 2011-10-04 14:51   ` Anthony PERARD
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

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

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCH RFC V1 03/11] pci_ids: Add INTEL_82599_VF id.
@ 2011-10-04 14:51   ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

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

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [Qemu-devel] [PATCH RFC V1 04/11] pci_regs: Fix value of PCI_EXP_TYPE_RC_EC.
  2011-10-04 14:51 ` Anthony PERARD
@ 2011-10-04 14:51   ` Anthony PERARD
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

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 e884096..dad7d9a 100644
--- a/hw/pci_regs.h
+++ b/hw/pci_regs.h
@@ -392,7 +392,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

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCH RFC V1 04/11] pci_regs: Fix value of PCI_EXP_TYPE_RC_EC.
@ 2011-10-04 14:51   ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

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 e884096..dad7d9a 100644
--- a/hw/pci_regs.h
+++ b/hw/pci_regs.h
@@ -392,7 +392,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

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [Qemu-devel] [PATCH RFC V1 05/11] pci_regs: Add PCI_EXP_TYPE_PCIE_BRIDGE
  2011-10-04 14:51 ` Anthony PERARD
@ 2011-10-04 14:51   ` Anthony PERARD
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

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 dad7d9a..091a749 100644
--- a/hw/pci_regs.h
+++ b/hw/pci_regs.h
@@ -391,6 +391,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

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCH RFC V1 05/11] pci_regs: Add PCI_EXP_TYPE_PCIE_BRIDGE
@ 2011-10-04 14:51   ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

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 dad7d9a..091a749 100644
--- a/hw/pci_regs.h
+++ b/hw/pci_regs.h
@@ -391,6 +391,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

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [Qemu-devel] [PATCH RFC V1 06/11] pci.c: Add pci_check_bar_overlap
  2011-10-04 14:51 ` Anthony PERARD
@ 2011-10-04 14:51   ` Anthony PERARD
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

This function help Xen PCI Passthrough device to check for overlap.

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 hw/pci.c |   46 ++++++++++++++++++++++++++++++++++++++++++++++
 hw/pci.h |    3 +++
 2 files changed, 49 insertions(+), 0 deletions(-)

diff --git a/hw/pci.c b/hw/pci.c
index af74003..56e94d1 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -2196,3 +2196,49 @@ MemoryRegion *pci_address_space(PCIDevice *dev)
 {
     return dev->bus->address_space_mem;
 }
+
+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++) {
+        if (!(devices = bus->devices[i])) {
+            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 c04b169..97996b5 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -490,4 +490,7 @@ static inline uint32_t pci_config_size(const PCIDevice *d)
     return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE;
 }
 
+int pci_check_bar_overlap(PCIDevice *dev,
+                          pcibus_t addr, pcibus_t size, uint8_t type);
+
 #endif
-- 
Anthony PERARD

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCH RFC V1 06/11] pci.c: Add pci_check_bar_overlap
@ 2011-10-04 14:51   ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

This function help Xen PCI Passthrough device to check for overlap.

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 hw/pci.c |   46 ++++++++++++++++++++++++++++++++++++++++++++++
 hw/pci.h |    3 +++
 2 files changed, 49 insertions(+), 0 deletions(-)

diff --git a/hw/pci.c b/hw/pci.c
index af74003..56e94d1 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -2196,3 +2196,49 @@ MemoryRegion *pci_address_space(PCIDevice *dev)
 {
     return dev->bus->address_space_mem;
 }
+
+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++) {
+        if (!(devices = bus->devices[i])) {
+            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 c04b169..97996b5 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -490,4 +490,7 @@ static inline uint32_t pci_config_size(const PCIDevice *d)
     return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE;
 }
 
+int pci_check_bar_overlap(PCIDevice *dev,
+                          pcibus_t addr, pcibus_t size, uint8_t type);
+
 #endif
-- 
Anthony PERARD

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [Qemu-devel] [PATCH RFC V1 07/11] host-pci-device: Add host_pci_find_ext_cap_offset
  2011-10-04 14:51 ` Anthony PERARD
@ 2011-10-04 14:51   ` Anthony PERARD
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 hw/host-pci-device.c |   31 +++++++++++++++++++++++++++++++
 hw/host-pci-device.h |    2 ++
 2 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/hw/host-pci-device.c b/hw/host-pci-device.c
index b3f2899..2a889d5 100644
--- a/hw/host-pci-device.c
+++ b/hw/host-pci-device.c
@@ -162,6 +162,37 @@ int host_pci_write_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 = 480;
+    int pos = 0x100;
+
+    do {
+        header = host_pci_read_long(d, pos);
+        /*
+         * 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 < 0x100) {
+            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;
diff --git a/hw/host-pci-device.h b/hw/host-pci-device.h
index 0137507..2734eb3 100644
--- a/hw/host-pci-device.h
+++ b/hw/host-pci-device.h
@@ -33,4 +33,6 @@ int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data);
 int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data);
 int host_pci_write_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

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCH RFC V1 07/11] host-pci-device: Add host_pci_find_ext_cap_offset
@ 2011-10-04 14:51   ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 hw/host-pci-device.c |   31 +++++++++++++++++++++++++++++++
 hw/host-pci-device.h |    2 ++
 2 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/hw/host-pci-device.c b/hw/host-pci-device.c
index b3f2899..2a889d5 100644
--- a/hw/host-pci-device.c
+++ b/hw/host-pci-device.c
@@ -162,6 +162,37 @@ int host_pci_write_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 = 480;
+    int pos = 0x100;
+
+    do {
+        header = host_pci_read_long(d, pos);
+        /*
+         * 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 < 0x100) {
+            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;
diff --git a/hw/host-pci-device.h b/hw/host-pci-device.h
index 0137507..2734eb3 100644
--- a/hw/host-pci-device.h
+++ b/hw/host-pci-device.h
@@ -33,4 +33,6 @@ int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data);
 int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data);
 int host_pci_write_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

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [Qemu-devel] [PATCH RFC V1 08/11] Introduce Xen PCI Passthrough, qdevice (1/3)
  2011-10-04 14:51 ` Anthony PERARD
@ 2011-10-04 14:51   ` Anthony PERARD
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 hw/xen_pci_passthrough.c         |  763 ++++++++++++++++++++++++++++++++++++++
 hw/xen_pci_passthrough.h         |  335 +++++++++++++++++
 hw/xen_pci_passthrough_helpers.c |   46 +++
 3 files changed, 1144 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_helpers.c

diff --git a/hw/xen_pci_passthrough.c b/hw/xen_pci_passthrough.c
new file mode 100644
index 0000000..bfbe042
--- /dev/null
+++ b/hw/xen_pci_passthrough.c
@@ -0,0 +1,763 @@
+#include <sys/ioctl.h>
+
+#include "pci.h"
+#include "xen.h"
+#include "xen_backend.h"
+#include "xen_pci_passthrough.h"
+
+#define PCI_BAR_ENTRIES (6)
+
+typedef struct PlugDevice {
+    uint8_t r_bus;
+    uint8_t r_slot;
+    uint8_t r_func;
+    int bus;
+    int slot;
+    int func;
+} PlugDevice;
+
+QLIST_HEAD(php_dev_list, PlugDevice) php_dev_list = 
+    QLIST_HEAD_INITIALIZER(php_dev_list);
+
+#define PT_BAR_ALLF             0xFFFFFFFF      /* BAR ALLF value */
+
+/* #define PT_NR_IRQS          (256) */
+/* char mapped_machine_irq[PT_NR_IRQS] = {0}; */
+
+/* Config Space */
+static int pt_pci_config_access_check(PCIDevice *d, uint32_t address, int len)
+{
+    /* check offset range */
+    if (address >= 0xFF) {
+        PT_LOG("Error: Failed to access register with offset exceeding FFh. "
+               "[%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
+               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+               address, len);
+        return -1;
+    }
+
+    /* check read size */
+    if ((len != 1) && (len != 2) && (len != 4)) {
+        PT_LOG("Error: Failed to access register with invalid access length. "
+               "[%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
+               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+               address, len);
+        return -1;
+    }
+
+    /* check offset alignment */
+    if (address & (len - 1)) {
+        PT_LOG("Error: Failed to access register with invalid access size "
+            "alignment. [%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
+            pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+            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 untill previous power state transition is completed.
+         * so finished previous request here.
+         */
+        qemu_run_one_timer(s->pm_state->pm_timer);
+    }
+
+    /* 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_read_block(s->real_device, address, (uint8_t *)&val, len);
+    if (!rc) {
+        PT_LOG("Error: 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 Guest request 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 depend 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) {
+                hw_error("Internal error: Invalid read emulation "
+                         "return value[%d]. I/O emulator exit.\n", rc);
+            }
+
+            /* 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("[%02x:%02x.%x]: address=%04x val=0x%08x len=%d\n",
+                  pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+                  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("[%02x:%02x.%x]: address=%04x val=0x%08x len=%d\n",
+                  pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+                  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_LOG("Warning: Guest attempt to set address to unused Base Address "
+               "Register. [%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
+               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+               address, len);
+    }
+
+    /* check power state transition flags */
+    if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) {
+        /* can't accept untill previous power state transition is completed.
+         * so finished previous request here.
+         */
+        qemu_run_one_timer(s->pm_state->pm_timer);
+    }
+
+    /* 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_LOG("Warning: Access to 0 Hardwired register. "
+                   "[%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
+                   pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+                   address, len);
+            return;
+        }
+    }
+
+    /* read I/O device register value */
+    rc = host_pci_read_block(s->real_device, address,
+                             (uint8_t *)&read_val, len);
+    if (!rc) {
+        PT_LOG("Error: pci_read_block failed. return value[%d].\n", rc);
+        memset(&read_val, 0xff, len);
+    }
+
+    /* pass directly to libpci 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 Guest request 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 depend 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) {
+                hw_error("Internal error: Invalid write emulation "
+                         "return value[%d]. I/O emulator exit.\n", rc);
+            }
+
+            /* 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 libpci */
+    val >>= (address & 3) << 3;
+
+out:
+    if (!(reg && reg->no_wb)) {
+        /* unknown regs are passed through */
+        rc = host_pci_write_block(s->real_device, address,
+                                  (uint8_t *)&val, len);
+
+        if (!rc) {
+            PT_LOG("Error: pci_write_block failed. return value[%d].\n", rc);
+        }
+    }
+
+    if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) {
+        /* set QEMUTimer */
+        qemu_mod_timer(s->pm_state->pm_timer,
+                       qemu_get_clock_ms(rt_clock) + s->pm_state->pm_delay);
+    }
+}
+
+/* ioport/iomem space*/
+static void pt_iomem_map(XenPCIPassthroughState *s, int i,
+                         pcibus_t e_phys, pcibus_t e_size, int type)
+{
+    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("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 != -1) {
+        add_msix_mapping(s, i);
+        /* Remove old mapping */
+        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_LOG("Error: remove old mapping failed!\n");
+            return;
+        }
+    }
+
+    /* map only valid guest address */
+    if (e_phys != -1) {
+        /* Create new mapping */
+        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_LOG("Error: create new mapping failed!\n");
+        }
+
+        ret = remove_msix_mapping(s, i);
+        if (ret != 0) {
+            PT_LOG("Error: remove MSI-X mmio mapping failed!\n");
+        }
+
+        if (old_ebase != e_phys && old_ebase != -1) {
+            pt_msix_update_remap(s, i);
+        }
+    }
+}
+
+static void pt_ioport_map(XenPCIPassthroughState *s, int i,
+                          pcibus_t e_phys, pcibus_t e_size, int type)
+{
+    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("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 != -1) {
+        /* Remove old mapping */
+        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_LOG("Error: remove old mapping failed!\n");
+            return;
+        }
+    }
+
+    /* map only valid guest address (include 0) */
+    if (e_phys != -1) {
+        /* Create new mapping */
+        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_LOG("Error: 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 = -1;
+    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 = -1;
+    }
+    if ((bar == PCI_ROM_SLOT) && (r_addr != -1)) {
+        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 = -1;
+            }
+        }
+    }
+
+    /* prevent guest software mapping memory resource to 00000000h */
+    if ((base->bar_flag == PT_BAR_FLAG_MEM) && (r_addr == 0)) {
+        r_addr = -1;
+    }
+
+    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 > 0) {
+        PT_LOG("Warning: s[%02x:%02x.%x][Region:%d][Address:%"FMT_PCIBUS"h]"
+               "[Size:%"FMT_PCIBUS"h] is overlapped.\n", pci_bus_num(dev->bus),
+               PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), 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);
+    }
+}
+
+/* register regions */
+int qemu_pci_register_regions(XenPCIPassthroughState *s)
+{
+    /* but no mapping done anymore (see in reset) */
+    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++) {
+        if (pt_pci_base_addr(d->base_addr[i])) {
+            s->bases[i].e_physbase = pt_pci_base_addr(d->base_addr[i]);
+            s->bases[i].access.u = pt_pci_base_addr(d->base_addr[i]);
+
+            /* Register current region */
+            if (d->base_addr[i] & PCI_BASE_ADDRESS_SPACE_IO) {
+                memory_region_init_io(&s->bar[i], NULL, NULL,
+                                      "xen-pci-pt-bar", d->size[i]);
+                pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_SPACE_IO,
+                                 &s->bar[i]);
+            } else if (d->base_addr[i] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+                memory_region_init_io(&s->bar[i], NULL, NULL,
+                                      "xen-pci-pt-bar", d->size[i]);
+                pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_MEM_PREFETCH,
+                                 &s->bar[i]);
+            } else {
+                memory_region_init_io(&s->bar[i], NULL, NULL,
+                                      "xen-pci-pt-bar", d->size[i]);
+                pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_SPACE_MEMORY,
+                                 &s->bar[i]);
+            }
+
+            PT_LOG("IO region registered (size=0x%08"PRIx64
+                   " base_addr=0x%08"PRIx64")\n",
+                   d->size[i], d->base_addr[i]);
+        }
+    }
+
+    /* Register expansion ROM address */
+    if ((d->rom_base_addr & PCI_ROM_ADDRESS_MASK) && d->rom_size) {
+        /* Re-set BAR reported by OS, otherwise ROM can't be read. */
+        bar_data = host_pci_read_long(d, PCI_ROM_ADDRESS);
+        if ((bar_data & PCI_ROM_ADDRESS_MASK) == 0) {
+            bar_data |= d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
+            host_pci_write_long(d, PCI_ROM_ADDRESS, bar_data);
+        }
+
+        s->bases[PCI_ROM_SLOT].e_physbase = d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
+        s->bases[PCI_ROM_SLOT].access.maddr = d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
+
+        // memory_region_init_rom_device
+        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("Expansion ROM registered (size=0x%08"PRIx64
+               " base_addr=0x%08"PRIx64")\n",
+               d->rom_size, d->rom_base_addr);
+    }
+
+    return 0;
+}
+
+static int pci_parse_host_devaddr(char **str, int *seg, int *bus, int *dev,
+                                  int *func, char **opt, int *vdevfn)
+{
+    char *token;
+    const char *delim = ":.";
+
+    if (!(*str) || (!strchr(*str, ':') && !strchr(*str, '.'))) {
+        return 0;
+    }
+
+    token  = strsep(str, delim);
+    *seg = strtoul(token, NULL, 16);
+
+    token  = strsep(str, delim);
+    *bus  = strtoul(token, NULL, 16);
+
+    token  = strsep(str, delim);
+    *dev  = strtoul(token, NULL, 16);
+
+    token  = strsep(str, delim);
+
+    *opt = strchr(token, '@');
+    if (*opt) {
+        *(*opt)++ = '\0';
+        *vdevfn = strtoul(*opt, NULL, 16);
+    } else {
+        *vdevfn = -1; //AUTO_PHP_SLOT;
+        *opt = token;
+    }
+
+    *opt = strchr(*opt, ',');
+    if (*opt) {
+        *(*opt)++ = '\0';
+    }
+
+    *func = strtoul(token, NULL, 16);
+
+    return 1;
+}
+
+// TODO add to PlugDevice list php_dev_list;
+static int pt_initfn(PCIDevice *pci_dev)
+{
+    XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, pci_dev);
+    int devfn_ignored = -1;
+    int dom, bus;
+    int slot, func;
+    char *opt;
+    int rc = 0;
+    uint32_t machine_irq;
+
+    /* todo: use local/stack buf, or better: change pci_parse_host_devaddr */
+    char *addr = g_strdup(s->hostaddr);
+
+    PT_LOG("initialiase passthrough for %s (devfn: %i)\n", addr, s->dev.devfn);
+
+    if (pci_parse_host_devaddr(&addr, &dom, &bus, &slot, &func, &opt,
+                               &devfn_ignored) < 0) {
+        fprintf(stderr, "erro parse bdf: %s\n", addr);
+        return -1;
+    }
+
+    s->php_dev.r_bus = bus;
+    s->php_dev.r_dev = slot;
+    s->php_dev.r_func = func;
+
+    /* register real device */
+    PT_LOG("Assigning real physical device %02x:%02x.%x ...\n",
+           bus, slot, func);
+
+    s->real_device = host_pci_device_get(bus, slot, func);
+    if (!s->real_device) {
+        return -1;
+    }
+
+    s->msi_trans_cap = 1; // default enable, later, read from param (xenstore)
+    /* s->power_mgmt = power_mgmt; */
+    s->is_virtfn = s->real_device->is_virtfn;
+    if (s->is_virtfn) {
+        PT_LOG("%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_read_block(s->real_device, 0, pci_dev->config,
+                            PCI_CONFIG_SPACE_SIZE) == -1) {
+        return -1;
+    }
+
+    /* Handle real device's MMIO/PIO BARs */
+    qemu_pci_register_regions(s);
+
+    /* reinitialize each config register to be emulated */
+    pt_config_init(s);
+
+    /* Bind interrupt */
+    if (!s->dev.config[PCI_INTERRUPT_PIN]) {
+        PT_LOG("no pin interrupt\n");
+        goto out;
+    }
+
+    /* pt machine irq auto */
+    {
+        int pirq = -1;
+
+        machine_irq = host_pci_read_byte(s->real_device, PCI_INTERRUPT_LINE);
+        rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq);
+
+        if (rc) {
+            PT_LOG("Error: Mapping irq failed, rc = %d\n", rc);
+
+            /* Disable PCI intx assertion (turn on bit10 of devctl) */
+            host_pci_write_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]++; */
+        }
+    }
+
+    /* 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);
+        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_LOG("Error: Binding of interrupt failed! rc=%d\n", rc);
+
+            /* Disable PCI intx assertion (turn on bit10 of devctl) */
+            host_pci_write_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_LOG("Error: Unmapping of interrupt failed! rc=%d\n",
+                           rc);
+                }
+            /* } */
+            s->machine_irq = 0;
+        }
+    }
+
+out:
+    PT_LOG("Real physical device %02x:%02x.%x registered successfuly!\n"
+           "IRQ type = %s\n", bus, slot, func,
+           s->msi_trans_en ? "MSI-INTx" : "INTx");
+
+    return 0;
+}
+
+static PCIDeviceInfo xen_pci_passthrough = {
+    .init = pt_initfn,
+    .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_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..10e8189
--- /dev/null
+++ b/hw/xen_pci_passthrough.h
@@ -0,0 +1,335 @@
+#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
+
+#ifdef PT_LOGGING_ENABLED
+#  define PT_LOG(_f, _a...)   fprintf(stderr, "%s: " _f, __func__, ##_a)
+#else
+#  define PT_LOG(_f, _a...)
+#endif
+
+#ifdef PT_DEBUG_PCI_CONFIG_ACCESS
+#  define PT_LOG_CONFIG(_f, _a...) PT_LOG(_f, ##_a)
+#else
+#  define PT_LOG_CONFIG(_f, _a...)
+#endif
+
+
+typedef struct XenPTRegion XenPTRegion;
+typedef struct XenPTReg XenPTReg;
+typedef struct XenPTRegInfo XenPTRegInfo;
+typedef struct XenPTRegGroup XenPTRegGroup;
+typedef struct XenPTRegGroupInfo XenPTRegGroupInfo;
+
+typedef struct XenPCIPassthroughState XenPCIPassthroughState;
+
+// fonction type for config reg
+typedef uint32_t (*conf_reg_init)
+    (XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset);
+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);
+
+struct PHPDev {
+    /* uint8_t valid; */
+    uint8_t r_bus;
+    uint8_t r_dev;
+    uint8_t r_func;
+    /* char *opt; */
+};
+
+/* power state transition */
+#define PT_FLAG_TRANSITING 0x0001
+
+#define PT_INVALID_REG          0xFFFFFFFF      /* invalid register value */
+#define PT_BAR_ALLF             0xFFFFFFFF      /* BAR ALLF value */
+#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) */
+
+/* MSI-X */
+#define MSI_FLAG_UNINIT 0x1000
+#define PT_MSI_MAPPED   0x2000
+
+#define MSI_DATA_VECTOR_SHIFT          0
+#define     MSI_DATA_VECTOR(v)         (((u8)v) << MSI_DATA_VECTOR_SHIFT)
+
+#define MSI_DATA_DELIVERY_SHIFT        8
+#define     MSI_DATA_DELIVERY_FIXED    (0 << MSI_DATA_DELIVERY_SHIFT)
+#define     MSI_DATA_DELIVERY_LOWPRI   (1 << MSI_DATA_DELIVERY_SHIFT)
+
+#define MSI_DATA_LEVEL_SHIFT           14
+#define     MSI_DATA_LEVEL_DEASSERT    (0 << MSI_DATA_LEVEL_SHIFT)
+#define     MSI_DATA_LEVEL_ASSERT      (1 << MSI_DATA_LEVEL_SHIFT)
+
+#define MSI_DATA_TRIGGER_SHIFT         15
+#define     MSI_DATA_TRIGGER_EDGE      (0 << MSI_DATA_TRIGGER_SHIFT)
+#define     MSI_DATA_TRIGGER_LEVEL     (1 << MSI_DATA_TRIGGER_SHIFT)
+
+/*
+ * Shift/mask fields for APIC-based bus address
+ */
+#define MSI_ADDR_HEADER                0xfee00000
+#define MSI_TARGET_CPU_SHIFT           12
+
+#define MSI_ADDR_DESTID_MASK           0xfff0000f
+#define   MSI_ADDR_DESTID_CPU(cpu)     ((cpu) << MSI_TARGET_CPU_SHIFT)
+
+#define MSI_ADDR_DESTMODE_SHIFT        2
+#define   MSI_ADDR_DESTMODE_PHYS       (0 << MSI_ADDR_DESTMODE_SHIFT)
+#define     MSI_ADDR_DESTMODE_LOGIC    (1 << MSI_ADDR_DESTMODE_SHIFT)
+
+#define MSI_ADDR_REDIRECTION_SHIFT     3
+#define   MSI_ADDR_REDIRECTION_CPU     (0 << MSI_ADDR_REDIRECTION_SHIFT)
+#define   MSI_ADDR_REDIRECTION_LOWPRI  (1 << MSI_ADDR_REDIRECTION_SHIFT)
+
+#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
+
+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;
+
+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;
+};
+
+/* emulated register management */
+struct XenPTReg {
+    QLIST_ENTRY(XenPTReg) entries;
+    XenPTRegInfo *reg;
+    uint32_t data;
+};
+
+/* emul register group management table */
+struct XenPTRegGroup {
+    QLIST_ENTRY (XenPTRegGroup) entries;
+    const XenPTRegGroupInfo *reg_grp;
+    uint32_t base_offset;
+    uint8_t size;
+    QLIST_HEAD(, XenPTReg) reg_tbl_list;
+};
+
+/* emul reg group size initialize method */
+typedef uint8_t (*pt_reg_size_init_fn)
+    (XenPCIPassthroughState *, const XenPTRegGroupInfo *,
+     uint32_t base_offset);
+
+/* 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;
+};
+
+
+/* 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;
+};
+
+typedef struct pt_msi_info {
+    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 msix_entry_info {
+    int pirq;        /* -1 means unmapped */
+    int flags;       /* flags indicting whether MSI ADDR or DATA is updated */
+    uint32_t io_mem[4];
+} XenMSIXEntry;
+typedef struct pt_msix_info {
+    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;
+    struct msix_entry_info msix_entry[0];
+} XenPTMSIX;
+
+typedef struct pt_pm_info {
+    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;
+    struct PHPDev php_dev;
+    bool is_virtfn;
+    HostPCIDevice *real_device;
+    XenPTRegion bases[PCI_NUM_REGIONS]; /* Access regions */
+    QLIST_HEAD(, XenPTRegGroup) reg_grp_tbl;
+
+    uint32_t machine_irq;
+
+    XenPTMSI *msi;
+    XenPTMSIX *msix;
+
+    /* Physical MSI to guest INTx translation when possible */
+    int msi_trans_cap;
+    bool msi_trans_en;
+
+    bool power_mgmt;
+    XenPTPM *pm_state;
+
+    MemoryRegion bar[PCI_NUM_REGIONS - 1];
+    MemoryRegion rom;
+};
+
+void pt_config_init(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);
+
+int qemu_pci_register_regions(XenPCIPassthroughState *);
+
+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;
+    }
+}
+
+static inline pcibus_t pt_pci_base_addr(pcibus_t base)
+{
+    if (base & PCI_BASE_ADDRESS_SPACE_IO)
+        return base & PCI_BASE_ADDRESS_IO_MASK;
+
+    return base & PCI_BASE_ADDRESS_MEM_MASK;
+}
+
+/* INTx */
+static inline uint8_t pci_read_intx(XenPCIPassthroughState *s)
+{
+    return host_pci_read_byte(s->real_device, PCI_INTERRUPT_PIN);
+}
+uint8_t pci_intx(XenPCIPassthroughState *ptdev);
+
+/* MSI/MSI-X */
+void msi_set_enable(XenPCIPassthroughState *dev, int en);
+int pt_msi_setup(XenPCIPassthroughState *dev);
+int pt_msi_update(XenPCIPassthroughState *d);
+void pt_msi_disable(XenPCIPassthroughState *dev);
+int pt_enable_msi_translate(XenPCIPassthroughState* dev);
+void pt_disable_msi_translate(XenPCIPassthroughState *dev);
+
+int pt_msix_init(XenPCIPassthroughState *dev, int pos);
+int pt_msix_update(XenPCIPassthroughState *dev);
+int pt_msix_update_remap(XenPCIPassthroughState *dev, int bar_index);
+void pt_msix_disable(XenPCIPassthroughState *dev);
+int add_msix_mapping(XenPCIPassthroughState *s, int bar_index);
+int remove_msix_mapping(XenPCIPassthroughState *s, int bar_index);
+
+#endif /* !QEMU_HW_XEN_PCI_PASSTHROUGH_H */
diff --git a/hw/xen_pci_passthrough_helpers.c b/hw/xen_pci_passthrough_helpers.c
new file mode 100644
index 0000000..192e918
--- /dev/null
+++ b/hw/xen_pci_passthrough_helpers.c
@@ -0,0 +1,46 @@
+#include "xen_pci_passthrough.h"
+
+/* 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
+}
+ */
+uint8_t pci_intx(XenPCIPassthroughState *ptdev)
+{
+    uint8_t r_val = pci_read_intx(ptdev);
+
+    PT_LOG("intx=%i\n", r_val);
+    if (r_val < 1 || r_val > 4) {
+        PT_LOG("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;
+}
-- 
Anthony PERARD

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCH RFC V1 08/11] Introduce Xen PCI Passthrough, qdevice (1/3)
@ 2011-10-04 14:51   ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 hw/xen_pci_passthrough.c         |  763 ++++++++++++++++++++++++++++++++++++++
 hw/xen_pci_passthrough.h         |  335 +++++++++++++++++
 hw/xen_pci_passthrough_helpers.c |   46 +++
 3 files changed, 1144 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_helpers.c

diff --git a/hw/xen_pci_passthrough.c b/hw/xen_pci_passthrough.c
new file mode 100644
index 0000000..bfbe042
--- /dev/null
+++ b/hw/xen_pci_passthrough.c
@@ -0,0 +1,763 @@
+#include <sys/ioctl.h>
+
+#include "pci.h"
+#include "xen.h"
+#include "xen_backend.h"
+#include "xen_pci_passthrough.h"
+
+#define PCI_BAR_ENTRIES (6)
+
+typedef struct PlugDevice {
+    uint8_t r_bus;
+    uint8_t r_slot;
+    uint8_t r_func;
+    int bus;
+    int slot;
+    int func;
+} PlugDevice;
+
+QLIST_HEAD(php_dev_list, PlugDevice) php_dev_list = 
+    QLIST_HEAD_INITIALIZER(php_dev_list);
+
+#define PT_BAR_ALLF             0xFFFFFFFF      /* BAR ALLF value */
+
+/* #define PT_NR_IRQS          (256) */
+/* char mapped_machine_irq[PT_NR_IRQS] = {0}; */
+
+/* Config Space */
+static int pt_pci_config_access_check(PCIDevice *d, uint32_t address, int len)
+{
+    /* check offset range */
+    if (address >= 0xFF) {
+        PT_LOG("Error: Failed to access register with offset exceeding FFh. "
+               "[%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
+               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+               address, len);
+        return -1;
+    }
+
+    /* check read size */
+    if ((len != 1) && (len != 2) && (len != 4)) {
+        PT_LOG("Error: Failed to access register with invalid access length. "
+               "[%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
+               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+               address, len);
+        return -1;
+    }
+
+    /* check offset alignment */
+    if (address & (len - 1)) {
+        PT_LOG("Error: Failed to access register with invalid access size "
+            "alignment. [%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
+            pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+            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 untill previous power state transition is completed.
+         * so finished previous request here.
+         */
+        qemu_run_one_timer(s->pm_state->pm_timer);
+    }
+
+    /* 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_read_block(s->real_device, address, (uint8_t *)&val, len);
+    if (!rc) {
+        PT_LOG("Error: 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 Guest request 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 depend 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) {
+                hw_error("Internal error: Invalid read emulation "
+                         "return value[%d]. I/O emulator exit.\n", rc);
+            }
+
+            /* 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("[%02x:%02x.%x]: address=%04x val=0x%08x len=%d\n",
+                  pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+                  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("[%02x:%02x.%x]: address=%04x val=0x%08x len=%d\n",
+                  pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+                  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_LOG("Warning: Guest attempt to set address to unused Base Address "
+               "Register. [%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
+               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+               address, len);
+    }
+
+    /* check power state transition flags */
+    if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) {
+        /* can't accept untill previous power state transition is completed.
+         * so finished previous request here.
+         */
+        qemu_run_one_timer(s->pm_state->pm_timer);
+    }
+
+    /* 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_LOG("Warning: Access to 0 Hardwired register. "
+                   "[%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
+                   pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+                   address, len);
+            return;
+        }
+    }
+
+    /* read I/O device register value */
+    rc = host_pci_read_block(s->real_device, address,
+                             (uint8_t *)&read_val, len);
+    if (!rc) {
+        PT_LOG("Error: pci_read_block failed. return value[%d].\n", rc);
+        memset(&read_val, 0xff, len);
+    }
+
+    /* pass directly to libpci 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 Guest request 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 depend 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) {
+                hw_error("Internal error: Invalid write emulation "
+                         "return value[%d]. I/O emulator exit.\n", rc);
+            }
+
+            /* 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 libpci */
+    val >>= (address & 3) << 3;
+
+out:
+    if (!(reg && reg->no_wb)) {
+        /* unknown regs are passed through */
+        rc = host_pci_write_block(s->real_device, address,
+                                  (uint8_t *)&val, len);
+
+        if (!rc) {
+            PT_LOG("Error: pci_write_block failed. return value[%d].\n", rc);
+        }
+    }
+
+    if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) {
+        /* set QEMUTimer */
+        qemu_mod_timer(s->pm_state->pm_timer,
+                       qemu_get_clock_ms(rt_clock) + s->pm_state->pm_delay);
+    }
+}
+
+/* ioport/iomem space*/
+static void pt_iomem_map(XenPCIPassthroughState *s, int i,
+                         pcibus_t e_phys, pcibus_t e_size, int type)
+{
+    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("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 != -1) {
+        add_msix_mapping(s, i);
+        /* Remove old mapping */
+        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_LOG("Error: remove old mapping failed!\n");
+            return;
+        }
+    }
+
+    /* map only valid guest address */
+    if (e_phys != -1) {
+        /* Create new mapping */
+        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_LOG("Error: create new mapping failed!\n");
+        }
+
+        ret = remove_msix_mapping(s, i);
+        if (ret != 0) {
+            PT_LOG("Error: remove MSI-X mmio mapping failed!\n");
+        }
+
+        if (old_ebase != e_phys && old_ebase != -1) {
+            pt_msix_update_remap(s, i);
+        }
+    }
+}
+
+static void pt_ioport_map(XenPCIPassthroughState *s, int i,
+                          pcibus_t e_phys, pcibus_t e_size, int type)
+{
+    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("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 != -1) {
+        /* Remove old mapping */
+        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_LOG("Error: remove old mapping failed!\n");
+            return;
+        }
+    }
+
+    /* map only valid guest address (include 0) */
+    if (e_phys != -1) {
+        /* Create new mapping */
+        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_LOG("Error: 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 = -1;
+    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 = -1;
+    }
+    if ((bar == PCI_ROM_SLOT) && (r_addr != -1)) {
+        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 = -1;
+            }
+        }
+    }
+
+    /* prevent guest software mapping memory resource to 00000000h */
+    if ((base->bar_flag == PT_BAR_FLAG_MEM) && (r_addr == 0)) {
+        r_addr = -1;
+    }
+
+    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 > 0) {
+        PT_LOG("Warning: s[%02x:%02x.%x][Region:%d][Address:%"FMT_PCIBUS"h]"
+               "[Size:%"FMT_PCIBUS"h] is overlapped.\n", pci_bus_num(dev->bus),
+               PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), 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);
+    }
+}
+
+/* register regions */
+int qemu_pci_register_regions(XenPCIPassthroughState *s)
+{
+    /* but no mapping done anymore (see in reset) */
+    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++) {
+        if (pt_pci_base_addr(d->base_addr[i])) {
+            s->bases[i].e_physbase = pt_pci_base_addr(d->base_addr[i]);
+            s->bases[i].access.u = pt_pci_base_addr(d->base_addr[i]);
+
+            /* Register current region */
+            if (d->base_addr[i] & PCI_BASE_ADDRESS_SPACE_IO) {
+                memory_region_init_io(&s->bar[i], NULL, NULL,
+                                      "xen-pci-pt-bar", d->size[i]);
+                pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_SPACE_IO,
+                                 &s->bar[i]);
+            } else if (d->base_addr[i] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+                memory_region_init_io(&s->bar[i], NULL, NULL,
+                                      "xen-pci-pt-bar", d->size[i]);
+                pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_MEM_PREFETCH,
+                                 &s->bar[i]);
+            } else {
+                memory_region_init_io(&s->bar[i], NULL, NULL,
+                                      "xen-pci-pt-bar", d->size[i]);
+                pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_SPACE_MEMORY,
+                                 &s->bar[i]);
+            }
+
+            PT_LOG("IO region registered (size=0x%08"PRIx64
+                   " base_addr=0x%08"PRIx64")\n",
+                   d->size[i], d->base_addr[i]);
+        }
+    }
+
+    /* Register expansion ROM address */
+    if ((d->rom_base_addr & PCI_ROM_ADDRESS_MASK) && d->rom_size) {
+        /* Re-set BAR reported by OS, otherwise ROM can't be read. */
+        bar_data = host_pci_read_long(d, PCI_ROM_ADDRESS);
+        if ((bar_data & PCI_ROM_ADDRESS_MASK) == 0) {
+            bar_data |= d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
+            host_pci_write_long(d, PCI_ROM_ADDRESS, bar_data);
+        }
+
+        s->bases[PCI_ROM_SLOT].e_physbase = d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
+        s->bases[PCI_ROM_SLOT].access.maddr = d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
+
+        // memory_region_init_rom_device
+        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("Expansion ROM registered (size=0x%08"PRIx64
+               " base_addr=0x%08"PRIx64")\n",
+               d->rom_size, d->rom_base_addr);
+    }
+
+    return 0;
+}
+
+static int pci_parse_host_devaddr(char **str, int *seg, int *bus, int *dev,
+                                  int *func, char **opt, int *vdevfn)
+{
+    char *token;
+    const char *delim = ":.";
+
+    if (!(*str) || (!strchr(*str, ':') && !strchr(*str, '.'))) {
+        return 0;
+    }
+
+    token  = strsep(str, delim);
+    *seg = strtoul(token, NULL, 16);
+
+    token  = strsep(str, delim);
+    *bus  = strtoul(token, NULL, 16);
+
+    token  = strsep(str, delim);
+    *dev  = strtoul(token, NULL, 16);
+
+    token  = strsep(str, delim);
+
+    *opt = strchr(token, '@');
+    if (*opt) {
+        *(*opt)++ = '\0';
+        *vdevfn = strtoul(*opt, NULL, 16);
+    } else {
+        *vdevfn = -1; //AUTO_PHP_SLOT;
+        *opt = token;
+    }
+
+    *opt = strchr(*opt, ',');
+    if (*opt) {
+        *(*opt)++ = '\0';
+    }
+
+    *func = strtoul(token, NULL, 16);
+
+    return 1;
+}
+
+// TODO add to PlugDevice list php_dev_list;
+static int pt_initfn(PCIDevice *pci_dev)
+{
+    XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, pci_dev);
+    int devfn_ignored = -1;
+    int dom, bus;
+    int slot, func;
+    char *opt;
+    int rc = 0;
+    uint32_t machine_irq;
+
+    /* todo: use local/stack buf, or better: change pci_parse_host_devaddr */
+    char *addr = g_strdup(s->hostaddr);
+
+    PT_LOG("initialiase passthrough for %s (devfn: %i)\n", addr, s->dev.devfn);
+
+    if (pci_parse_host_devaddr(&addr, &dom, &bus, &slot, &func, &opt,
+                               &devfn_ignored) < 0) {
+        fprintf(stderr, "erro parse bdf: %s\n", addr);
+        return -1;
+    }
+
+    s->php_dev.r_bus = bus;
+    s->php_dev.r_dev = slot;
+    s->php_dev.r_func = func;
+
+    /* register real device */
+    PT_LOG("Assigning real physical device %02x:%02x.%x ...\n",
+           bus, slot, func);
+
+    s->real_device = host_pci_device_get(bus, slot, func);
+    if (!s->real_device) {
+        return -1;
+    }
+
+    s->msi_trans_cap = 1; // default enable, later, read from param (xenstore)
+    /* s->power_mgmt = power_mgmt; */
+    s->is_virtfn = s->real_device->is_virtfn;
+    if (s->is_virtfn) {
+        PT_LOG("%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_read_block(s->real_device, 0, pci_dev->config,
+                            PCI_CONFIG_SPACE_SIZE) == -1) {
+        return -1;
+    }
+
+    /* Handle real device's MMIO/PIO BARs */
+    qemu_pci_register_regions(s);
+
+    /* reinitialize each config register to be emulated */
+    pt_config_init(s);
+
+    /* Bind interrupt */
+    if (!s->dev.config[PCI_INTERRUPT_PIN]) {
+        PT_LOG("no pin interrupt\n");
+        goto out;
+    }
+
+    /* pt machine irq auto */
+    {
+        int pirq = -1;
+
+        machine_irq = host_pci_read_byte(s->real_device, PCI_INTERRUPT_LINE);
+        rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq);
+
+        if (rc) {
+            PT_LOG("Error: Mapping irq failed, rc = %d\n", rc);
+
+            /* Disable PCI intx assertion (turn on bit10 of devctl) */
+            host_pci_write_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]++; */
+        }
+    }
+
+    /* 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);
+        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_LOG("Error: Binding of interrupt failed! rc=%d\n", rc);
+
+            /* Disable PCI intx assertion (turn on bit10 of devctl) */
+            host_pci_write_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_LOG("Error: Unmapping of interrupt failed! rc=%d\n",
+                           rc);
+                }
+            /* } */
+            s->machine_irq = 0;
+        }
+    }
+
+out:
+    PT_LOG("Real physical device %02x:%02x.%x registered successfuly!\n"
+           "IRQ type = %s\n", bus, slot, func,
+           s->msi_trans_en ? "MSI-INTx" : "INTx");
+
+    return 0;
+}
+
+static PCIDeviceInfo xen_pci_passthrough = {
+    .init = pt_initfn,
+    .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_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..10e8189
--- /dev/null
+++ b/hw/xen_pci_passthrough.h
@@ -0,0 +1,335 @@
+#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
+
+#ifdef PT_LOGGING_ENABLED
+#  define PT_LOG(_f, _a...)   fprintf(stderr, "%s: " _f, __func__, ##_a)
+#else
+#  define PT_LOG(_f, _a...)
+#endif
+
+#ifdef PT_DEBUG_PCI_CONFIG_ACCESS
+#  define PT_LOG_CONFIG(_f, _a...) PT_LOG(_f, ##_a)
+#else
+#  define PT_LOG_CONFIG(_f, _a...)
+#endif
+
+
+typedef struct XenPTRegion XenPTRegion;
+typedef struct XenPTReg XenPTReg;
+typedef struct XenPTRegInfo XenPTRegInfo;
+typedef struct XenPTRegGroup XenPTRegGroup;
+typedef struct XenPTRegGroupInfo XenPTRegGroupInfo;
+
+typedef struct XenPCIPassthroughState XenPCIPassthroughState;
+
+// fonction type for config reg
+typedef uint32_t (*conf_reg_init)
+    (XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset);
+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);
+
+struct PHPDev {
+    /* uint8_t valid; */
+    uint8_t r_bus;
+    uint8_t r_dev;
+    uint8_t r_func;
+    /* char *opt; */
+};
+
+/* power state transition */
+#define PT_FLAG_TRANSITING 0x0001
+
+#define PT_INVALID_REG          0xFFFFFFFF      /* invalid register value */
+#define PT_BAR_ALLF             0xFFFFFFFF      /* BAR ALLF value */
+#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) */
+
+/* MSI-X */
+#define MSI_FLAG_UNINIT 0x1000
+#define PT_MSI_MAPPED   0x2000
+
+#define MSI_DATA_VECTOR_SHIFT          0
+#define     MSI_DATA_VECTOR(v)         (((u8)v) << MSI_DATA_VECTOR_SHIFT)
+
+#define MSI_DATA_DELIVERY_SHIFT        8
+#define     MSI_DATA_DELIVERY_FIXED    (0 << MSI_DATA_DELIVERY_SHIFT)
+#define     MSI_DATA_DELIVERY_LOWPRI   (1 << MSI_DATA_DELIVERY_SHIFT)
+
+#define MSI_DATA_LEVEL_SHIFT           14
+#define     MSI_DATA_LEVEL_DEASSERT    (0 << MSI_DATA_LEVEL_SHIFT)
+#define     MSI_DATA_LEVEL_ASSERT      (1 << MSI_DATA_LEVEL_SHIFT)
+
+#define MSI_DATA_TRIGGER_SHIFT         15
+#define     MSI_DATA_TRIGGER_EDGE      (0 << MSI_DATA_TRIGGER_SHIFT)
+#define     MSI_DATA_TRIGGER_LEVEL     (1 << MSI_DATA_TRIGGER_SHIFT)
+
+/*
+ * Shift/mask fields for APIC-based bus address
+ */
+#define MSI_ADDR_HEADER                0xfee00000
+#define MSI_TARGET_CPU_SHIFT           12
+
+#define MSI_ADDR_DESTID_MASK           0xfff0000f
+#define   MSI_ADDR_DESTID_CPU(cpu)     ((cpu) << MSI_TARGET_CPU_SHIFT)
+
+#define MSI_ADDR_DESTMODE_SHIFT        2
+#define   MSI_ADDR_DESTMODE_PHYS       (0 << MSI_ADDR_DESTMODE_SHIFT)
+#define     MSI_ADDR_DESTMODE_LOGIC    (1 << MSI_ADDR_DESTMODE_SHIFT)
+
+#define MSI_ADDR_REDIRECTION_SHIFT     3
+#define   MSI_ADDR_REDIRECTION_CPU     (0 << MSI_ADDR_REDIRECTION_SHIFT)
+#define   MSI_ADDR_REDIRECTION_LOWPRI  (1 << MSI_ADDR_REDIRECTION_SHIFT)
+
+#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
+
+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;
+
+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;
+};
+
+/* emulated register management */
+struct XenPTReg {
+    QLIST_ENTRY(XenPTReg) entries;
+    XenPTRegInfo *reg;
+    uint32_t data;
+};
+
+/* emul register group management table */
+struct XenPTRegGroup {
+    QLIST_ENTRY (XenPTRegGroup) entries;
+    const XenPTRegGroupInfo *reg_grp;
+    uint32_t base_offset;
+    uint8_t size;
+    QLIST_HEAD(, XenPTReg) reg_tbl_list;
+};
+
+/* emul reg group size initialize method */
+typedef uint8_t (*pt_reg_size_init_fn)
+    (XenPCIPassthroughState *, const XenPTRegGroupInfo *,
+     uint32_t base_offset);
+
+/* 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;
+};
+
+
+/* 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;
+};
+
+typedef struct pt_msi_info {
+    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 msix_entry_info {
+    int pirq;        /* -1 means unmapped */
+    int flags;       /* flags indicting whether MSI ADDR or DATA is updated */
+    uint32_t io_mem[4];
+} XenMSIXEntry;
+typedef struct pt_msix_info {
+    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;
+    struct msix_entry_info msix_entry[0];
+} XenPTMSIX;
+
+typedef struct pt_pm_info {
+    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;
+    struct PHPDev php_dev;
+    bool is_virtfn;
+    HostPCIDevice *real_device;
+    XenPTRegion bases[PCI_NUM_REGIONS]; /* Access regions */
+    QLIST_HEAD(, XenPTRegGroup) reg_grp_tbl;
+
+    uint32_t machine_irq;
+
+    XenPTMSI *msi;
+    XenPTMSIX *msix;
+
+    /* Physical MSI to guest INTx translation when possible */
+    int msi_trans_cap;
+    bool msi_trans_en;
+
+    bool power_mgmt;
+    XenPTPM *pm_state;
+
+    MemoryRegion bar[PCI_NUM_REGIONS - 1];
+    MemoryRegion rom;
+};
+
+void pt_config_init(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);
+
+int qemu_pci_register_regions(XenPCIPassthroughState *);
+
+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;
+    }
+}
+
+static inline pcibus_t pt_pci_base_addr(pcibus_t base)
+{
+    if (base & PCI_BASE_ADDRESS_SPACE_IO)
+        return base & PCI_BASE_ADDRESS_IO_MASK;
+
+    return base & PCI_BASE_ADDRESS_MEM_MASK;
+}
+
+/* INTx */
+static inline uint8_t pci_read_intx(XenPCIPassthroughState *s)
+{
+    return host_pci_read_byte(s->real_device, PCI_INTERRUPT_PIN);
+}
+uint8_t pci_intx(XenPCIPassthroughState *ptdev);
+
+/* MSI/MSI-X */
+void msi_set_enable(XenPCIPassthroughState *dev, int en);
+int pt_msi_setup(XenPCIPassthroughState *dev);
+int pt_msi_update(XenPCIPassthroughState *d);
+void pt_msi_disable(XenPCIPassthroughState *dev);
+int pt_enable_msi_translate(XenPCIPassthroughState* dev);
+void pt_disable_msi_translate(XenPCIPassthroughState *dev);
+
+int pt_msix_init(XenPCIPassthroughState *dev, int pos);
+int pt_msix_update(XenPCIPassthroughState *dev);
+int pt_msix_update_remap(XenPCIPassthroughState *dev, int bar_index);
+void pt_msix_disable(XenPCIPassthroughState *dev);
+int add_msix_mapping(XenPCIPassthroughState *s, int bar_index);
+int remove_msix_mapping(XenPCIPassthroughState *s, int bar_index);
+
+#endif /* !QEMU_HW_XEN_PCI_PASSTHROUGH_H */
diff --git a/hw/xen_pci_passthrough_helpers.c b/hw/xen_pci_passthrough_helpers.c
new file mode 100644
index 0000000..192e918
--- /dev/null
+++ b/hw/xen_pci_passthrough_helpers.c
@@ -0,0 +1,46 @@
+#include "xen_pci_passthrough.h"
+
+/* 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
+}
+ */
+uint8_t pci_intx(XenPCIPassthroughState *ptdev)
+{
+    uint8_t r_val = pci_read_intx(ptdev);
+
+    PT_LOG("intx=%i\n", r_val);
+    if (r_val < 1 || r_val > 4) {
+        PT_LOG("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;
+}
-- 
Anthony PERARD

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [Qemu-devel] [PATCH RFC V1 09/11] Introduce Xen PCI Passthrough, PCI config space helpers (2/3)
  2011-10-04 14:51 ` Anthony PERARD
@ 2011-10-04 14:51   ` Anthony PERARD
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 hw/xen_pci_passthrough_config_init.c | 2489 ++++++++++++++++++++++++++++++++++
 1 files changed, 2489 insertions(+), 0 deletions(-)
 create mode 100644 hw/xen_pci_passthrough_config_init.c

diff --git a/hw/xen_pci_passthrough_config_init.c b/hw/xen_pci_passthrough_config_init.c
new file mode 100644
index 0000000..4bf9c15
--- /dev/null
+++ b/hw/xen_pci_passthrough_config_init.c
@@ -0,0 +1,2489 @@
+#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)))
+
+/* prototype */
+
+static uint32_t pt_ptr_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg,
+                                uint32_t real_offset);
+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.
+         */
+        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, &reg_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 ((index > 0) && (index < PCI_ROM_SLOT) &&
+        ((s->real_device->base_addr[index - 1]
+          & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK))
+         == (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_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->base_addr[index] & PCI_BASE_ADDRESS_SPACE_IO) {
+        return PT_BAR_FLAG_IO;
+    } else {
+        return PT_BAR_FLAG_MEM;
+    }
+}
+
+
+/****************
+ * general register functions
+ */
+
+/* register initialization function */
+
+static uint32_t pt_common_reg_init(XenPCIPassthroughState *s,
+                                   XenPTRegInfo *reg, uint32_t real_offset)
+{
+    return reg->init_val;
+}
+
+/* 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 uint32_t pt_vendor_reg_init(XenPCIPassthroughState *s,
+                                   XenPTRegInfo *reg, uint32_t real_offset)
+{
+    return s->real_device->vendor_id;
+}
+static uint32_t pt_device_reg_init(XenPCIPassthroughState *s,
+                                   XenPTRegInfo *reg, uint32_t real_offset)
+{
+    return s->real_device->device_id;
+}
+static uint32_t pt_status_reg_init(XenPCIPassthroughState *s,
+                                   XenPTRegInfo *reg, uint32_t real_offset)
+{
+    XenPTRegGroup *reg_grp_entry = NULL;
+    XenPTReg *reg_entry = NULL;
+    int 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 {
+            hw_error("Internal error: Couldn't find pt_reg_tbl for "
+                     "Capabilities Pointer register. I/O emulator exit.\n");
+        }
+    } else {
+        hw_error("Internal error: Couldn't find pt_reg_grp_tbl for Header. "
+                 "I/O emulator exit.\n");
+    }
+
+    return reg_field;
+}
+static uint32_t pt_header_type_reg_init(XenPCIPassthroughState *s,
+                                        XenPTRegInfo *reg,
+                                        uint32_t real_offset)
+{
+    /* read PCI_HEADER_TYPE */
+    return reg->init_val | 0x80;
+}
+
+/* initialize Interrupt Pin register */
+static uint32_t pt_irqpin_reg_init(XenPCIPassthroughState *s,
+                                   XenPTRegInfo *reg, uint32_t real_offset)
+{
+    return pci_read_intx(s);
+}
+
+/* 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) {
+        if (s->msi_trans_en) {
+            msi_set_enable(s, 0);
+        } else {
+            throughable_mask |= PCI_COMMAND_INTX_DISABLE;
+        }
+    } else {
+        if (s->msi_trans_en) {
+            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);
+
+    /* 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 */
+static uint32_t pt_bar_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg,
+                                uint32_t real_offset)
+{
+    int reg_field = 0;
+    int index;
+
+    /* get BAR index */
+    index = pt_bar_offset_to_index(reg->offset);
+    if (index < 0) {
+        hw_error("Internal error: Invalid BAR index[%d]. "
+                 "I/O emulator exit.\n", index);
+    }
+
+    /* 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;
+    }
+
+    return reg_field;
+}
+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) {
+        hw_error("Internal error: Invalid BAR index[%d]. "
+                 "I/O emulator exit.\n", index);
+    }
+
+    /* use fixed-up value from kernel sysfs */
+    *value = s->real_device->base_addr[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) {
+        hw_error("Internal error: Invalid BAR index[%d]. "
+                 "I/O emulator exit.\n", index);
+    }
+
+    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 >= 0x10000) {
+            /* check 64K range */
+            if ((last_addr >= 0x10000) &&
+                (cfg_entry->data != (PT_BAR_ALLF & ~bar_ro_mask))) {
+                PT_LOG("Warning: Guest attempt to set Base Address "
+                       "over the 64KB. [%02x:%02x.%x][Offset:%02xh]"
+                       "[Address:%08xh][Size:%08xh]\n",
+                       pci_bus_num(d->bus), PCI_SLOT(d->devfn),
+                       PCI_FUNC(d->devfn),
+                       reg->offset, new_addr, r_size);
+            }
+            /* just remove mapping */
+            r->addr = -1;
+            goto exit;
+        }
+        break;
+    case PT_BAR_FLAG_UPPER:
+        if (cfg_entry->data) {
+            if (cfg_entry->data != (PT_BAR_ALLF & ~bar_ro_mask)) {
+                PT_LOG("Warning: Guest attempt to set high MMIO Base Address. "
+                       "Ignore mapping. "
+                       "[%02x:%02x.%x][Offset:%02xh][High Address:%08xh]\n",
+                       pci_bus_num(d->bus), PCI_SLOT(d->devfn),
+                       PCI_FUNC(d->devfn), 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) {
+        hw_error("Internal error: Invalid BAR index[%d]. "
+                 "I/O emulator exit.\n", index);
+    }
+
+    /* use value from kernel sysfs */
+    if (s->bases[index].bar_flag == PT_BAR_FLAG_UPPER) {
+        *value = s->real_device->base_addr[index-1]; // >> 32;
+    } else {
+        *value = s->real_device->base_addr[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;
+
+    /* use value from kernel sysfs */
+    *value = 
+        PT_MERGE_VALUE(host_pci_read_long(s->real_device, PCI_ROM_ADDRESS),
+                       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 uint32_t pt_linkctrl_reg_init(XenPCIPassthroughState *s,
+                                     XenPTRegInfo *reg, uint32_t real_offset)
+{
+    uint8_t cap_ver = 0;
+    uint8_t dev_type = 0;
+
+    /* TODO maybe better to use fonction from hw/pcie.c */
+    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)) {
+        return PT_INVALID_REG;
+    }
+
+    return reg->init_val;
+}
+/* initialize Device Control 2 register */
+static uint32_t pt_devctrl2_reg_init(XenPCIPassthroughState *s,
+                                     XenPTRegInfo *reg, uint32_t real_offset)
+{
+    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) {
+        return PT_INVALID_REG;
+    }
+
+    return reg->init_val;
+}
+/* initialize Link Control 2 register */
+static uint32_t pt_linkctrl2_reg_init(XenPCIPassthroughState *s,
+                                      XenPTRegInfo *reg, uint32_t real_offset)
+{
+    int 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) {
+        return PT_INVALID_REG;
+    }
+
+    /* set Supported Link Speed */
+    reg_field |= PCI_EXP_LNKCAP_SLS &
+        pci_get_byte(s->dev.config + real_offset - reg->offset
+                     + PCI_EXP_LNKCAP);
+
+    return reg_field;
+}
+
+/* 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 uint32_t pt_pmc_reg_init(XenPCIPassthroughState *s,
+                                XenPTRegInfo *reg, uint32_t real_offset)
+{
+    PCIDevice *d = &s->dev;
+
+    if (!s->power_mgmt) {
+        return reg->init_val;
+    }
+
+    /* set Power Management Capabilities register */
+    s->pm_state->pmc_field = pci_get_word(d->config + real_offset);
+
+    return reg->init_val;
+}
+/* initialize PCI Power Management Control/Status register */
+static uint32_t pt_pmcsr_reg_init(XenPCIPassthroughState *s,
+                                  XenPTRegInfo *reg, uint32_t real_offset)
+{
+    PCIDevice *d = &s->dev;
+    uint16_t cap_ver  = 0;
+
+    if (!s->power_mgmt) {
+        return reg->init_val;
+    }
+
+    /* 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;
+    }
+
+    /* wake up real physical device */
+    switch (host_pci_read_word(s->real_device, real_offset)
+            & PCI_PM_CTRL_STATE_MASK) {
+    case 0:
+        break;
+    case 1:
+        PT_LOG("Power state transition D1 -> D0active\n");
+        host_pci_write_word(s->real_device, real_offset, 0);
+        break;
+    case 2:
+        PT_LOG("Power state transition D2 -> D0active\n");
+        host_pci_write_word(s->real_device, real_offset, 0);
+        usleep(200);
+        break;
+    case 3:
+        PT_LOG("Power state transition D3hot -> D0active\n");
+        host_pci_write_word(s->real_device, real_offset, 0);
+        usleep(10 * 1000);
+        pt_init_pci_config(s);
+        break;
+    }
+
+    return reg->init_val;
+}
+/* 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->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_LOG("Error: 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];
+        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 */
+    read_val = host_pci_read_word(s->real_device,
+                                  pm_state->pm_base + PCI_PM_CTRL);
+    cur_state = read_val & PCI_PM_CTRL_STATE_MASK;
+
+    if (pm_state->req_state != cur_state) {
+        PT_LOG("Error: Failed to change power state. "
+               "[%02x:%02x.%x][requested state:%d][current state:%d]\n",
+               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+               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;
+    struct pt_pm_info *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_LOG("Error: Invalid power transition. "
+               "[%02x:%02x.%x][requested state:%d][current state:%d]\n",
+               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+               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_LOG("Error: Invalid power transition. "
+               "[%02x:%02x.%x][requested state:%d][current state:%d]\n",
+               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+               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_write_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 */
+        *value = host_pci_read_word(s->real_device,
+                                    pm_state->pm_base + PCI_PM_CTRL);
+    }
+
+    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,
+    },
+};
+
+/********************************
+ * MSI Capability
+ */
+
+/* Message Control register */
+static uint32_t pt_msgctrl_reg_init(XenPCIPassthroughState *s,
+                                    XenPTRegInfo *reg, uint32_t real_offset)
+{
+    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("MSI enabled already, disable first\n");
+        host_pci_write_word(s->real_device, real_offset,
+                            reg_field & ~PCI_MSI_FLAGS_ENABLE);
+    }
+    s->msi->flags |= reg_field | MSI_FLAG_UNINIT;
+    s->msi->ctrl_offset = real_offset;
+
+    return reg->init_val;
+}
+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;
+    PCIDevice *pd = (PCIDevice *)s;
+    uint16_t val;
+
+    /* Currently no support for multi-vector */
+    if (*value & PCI_MSI_FLAGS_QSIZE) {
+        PT_LOG("Warning: 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 &
+        ~(MSI_FLAG_UNINIT | PT_MSI_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 & MSI_FLAG_UNINIT) {
+            if (s->msi_trans_en) {
+                PT_LOG("guest enabling MSI, disable MSI-INTx translation\n");
+                pt_disable_msi_translate(s);
+            } else {
+                /* Init physical one */
+                PT_LOG("setup msi for dev %x\n", pd->devfn);
+                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_LOG("Warning: Can not map MSI for dev %x\n", pd->devfn);
+		    return 0;
+                }
+            }
+            if (pt_msi_update(s)) {
+                *value &= ~PCI_MSI_FLAGS_ENABLE;
+                PT_LOG("Warning: Can not bind MSI for dev %x\n", pd->devfn);
+                return 0;
+            }
+            s->msi->flags &= ~MSI_FLAG_UNINIT;
+            s->msi->flags |= PT_MSI_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 uint32_t pt_msgaddr64_reg_init(XenPCIPassthroughState *ptdev,
+                                      XenPTRegInfo *reg, uint32_t real_offset)
+{
+    /* no need to initialize in case of 32 bit type */
+    if (!(ptdev->msi->flags & PCI_MSI_FLAGS_64BIT)) {
+        return PT_INVALID_REG;
+    }
+
+    return reg->init_val;
+}
+/* this function will be called twice (for 32 bit and 64 bit type) */
+/* initialize Message Data register */
+static uint32_t pt_msgdata_reg_init(XenPCIPassthroughState *ptdev,
+                                    XenPTRegInfo *reg, uint32_t real_offset)
+{
+    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))) {
+        return reg->init_val;
+    } else {
+        return PT_INVALID_REG;
+    }
+}
+
+/* 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_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)) {
+        /* exit I/O emulator */
+        PT_LOG("Error: why comes to 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_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_LOG("Error: the offset is not match with 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_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 uint32_t pt_msixctrl_reg_init(XenPCIPassthroughState *s,
+                                     XenPTRegInfo *reg, uint32_t real_offset)
+{
+    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("MSIX enabled already, disable first\n");
+        host_pci_write_word(s->real_device, real_offset,
+                            reg_field & ~PCI_MSIX_FLAGS_ENABLE);
+    }
+
+    s->msix->ctrl_offset = real_offset;
+
+    return reg->init_val;
+}
+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("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
+ */
+
+/* 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;
+
+    val = host_pci_read_long(s->real_device, aer_base + offset);
+    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_write_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 uint8_t pt_reg_grp_size_init(XenPCIPassthroughState *s,
+                                    const XenPTRegGroupInfo *grp_reg,
+                                    uint32_t base_offset)
+{
+    return grp_reg->grp_size;
+}
+/* get Power Management Capability Structure register group size */
+static uint8_t pt_pm_size_init(XenPCIPassthroughState *s,
+                               const XenPTRegGroupInfo *grp_reg,
+                               uint32_t base_offset)
+{
+    if (!s->power_mgmt) {
+        return grp_reg->grp_size;
+    }
+
+    s->pm_state = g_malloc0(sizeof (XenPTPM));
+
+    /* 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 grp_reg->grp_size;
+}
+/* get Vendor Specific Capability Structure register group size */
+static uint8_t pt_vendor_size_init(XenPCIPassthroughState *s,
+                                   const XenPTRegGroupInfo *grp_reg,
+                                   uint32_t base_offset)
+{
+    return pci_get_byte(s->dev.config + base_offset + 0x02);
+}
+/* get PCI Express Capability Structure register group size */
+static uint8_t pt_pcie_size_init(XenPCIPassthroughState *s,
+                                 const XenPTRegGroupInfo *grp_reg,
+                                 uint32_t base_offset)
+{
+    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:
+            hw_error("Internal error: Unsupported device/port type[%d]. "
+                     "I/O emulator exit.\n", type);
+        }
+    }
+    /* 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:
+            hw_error("Internal error: Unsupported device/port type[%d]. "
+                     "I/O emulator exit.\n", type);
+        }
+    } else {
+        hw_error("Internal error: Unsupported capability version[%d]. "
+                 "I/O emulator exit.\n", version);
+    }
+
+    return pcie_size;
+}
+/* get MSI Capability Structure register group size */
+static uint8_t pt_msi_size_init(XenPCIPassthroughState *s,
+                                const XenPTRegGroupInfo *grp_reg,
+                                uint32_t base_offset)
+{
+    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_malloc0(sizeof (XenPTMSI));
+    s->msi->pirq = -1;
+    PT_LOG("done\n");
+
+    return msi_size;
+}
+/* get MSI-X Capability Structure register group size */
+static uint8_t pt_msix_size_init(XenPCIPassthroughState *s,
+                                 const XenPTRegGroupInfo *grp_reg,
+                                 uint32_t base_offset)
+{
+    int ret = 0;
+
+    ret = pt_msix_init(s, base_offset);
+
+    if (ret == -1) {
+        hw_error("Internal error: Invalid pt_msix_init return value[%d]. "
+                 "I/O emulator exit.\n", ret);
+    }
+
+    return grp_reg->grp_size;
+}
+
+
+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,
+    },
+    /* 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,
+        .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,
+    },
+    /* 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,
+    },
+};
+
+/* initialize Capabilities Pointer or Next Pointer register */
+static uint32_t pt_ptr_reg_init(XenPCIPassthroughState *s,
+                                XenPTRegInfo *reg, uint32_t real_offset)
+{
+    /* 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:
+    return reg_field;
+}
+
+
+/*************
+ * Main
+ */
+
+/* restore a part of I/O device register */
+static void 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 ret = 0;
+
+    /* find emulate register group entry */
+    QLIST_FOREACH(reg_grp_entry, &s->reg_grp_tbl, entries) {
+        /* find emulate register entry */
+        QLIST_FOREACH(reg_entry, &reg_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 */
+            ret = host_pci_read_block(s->real_device, real_offset,
+                                      (uint8_t *)&read_val, reg->size);
+
+            if (!ret) {
+                PT_LOG("Error: pci_read_block failed. "
+                       "return value[%d].\n", ret);
+                memset(&read_val, 0xff, reg->size);
+            }
+
+            val = 0;
+
+            /* restore based on register size */
+            switch (reg->size) {
+            case 1:
+                /* byte register */
+                ret = reg->u.b.restore(s, reg_entry, real_offset,
+                                       (uint8_t)read_val, (uint8_t *)&val);
+                break;
+            case 2:
+                /* word register */
+                ret = reg->u.w.restore(s, reg_entry, real_offset,
+                                       (uint16_t)read_val, (uint16_t *)&val);
+                break;
+            case 4:
+                /* double word register */
+                ret = reg->u.dw.restore(s, reg_entry, real_offset,
+                                        (uint32_t)read_val, (uint32_t *)&val);
+                break;
+            }
+
+            /* restoring error */
+            if (ret < 0) {
+                hw_error("Internal error: Invalid restoring "
+                         "return value[%d]. I/O emulator exit.\n", ret);
+            }
+
+            PT_LOG_CONFIG("[%02x:%02x.%x]: address=%04x val=0x%08x len=%d\n",
+                          pci_bus_num(s->dev.bus), PCI_SLOT(s->dev.devfn),
+                          PCI_FUNC(s->dev.devfn),
+                          real_offset, val, reg->size);
+
+            ret = host_pci_write_block(s->real_device, real_offset,
+                                       (uint8_t *)&val, reg->size);
+
+            if (!ret) {
+                PT_LOG("Error: pci_write_block failed. "
+                       "return value[%d].\n", ret);
+            }
+        }
+    }
+
+    /* if AER supported, restore it */
+    if (s->pm_state->aer_base) {
+        pt_aer_reg_restore(s);
+    }
+}
+/* reinitialize all emulate registers */
+static void pt_config_reinit(XenPCIPassthroughState *s)
+{
+    XenPTRegGroup *reg_grp_entry = NULL;
+    XenPTReg *reg_entry = NULL;
+    XenPTRegInfo *reg = NULL;
+
+    /* find emulate register group entry */
+    QLIST_FOREACH(reg_grp_entry, &s->reg_grp_tbl, entries) {
+        /* find emulate register entry */
+        QLIST_FOREACH(reg_entry, &reg_grp_entry->reg_tbl_list, entries) {
+            reg = reg_entry->reg;
+            if (reg->init) {
+                /* initialize emulate register */
+                reg_entry->data = 
+                    reg->init(s, reg_entry->reg,
+                              reg_grp_entry->base_offset + reg->offset);
+            }
+        }
+    }
+}
+
+static int pt_init_pci_config(XenPCIPassthroughState *s)
+{
+    PCIDevice *d = &s->dev;
+    int ret = 0;
+
+    PT_LOG("Reinitialize PCI configuration registers due to power state"
+           " transition with internal reset. [%02x:%02x.%x]\n",
+           pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
+
+    /* restore a part of I/O device register */
+    pt_config_restore(s);
+
+    /* reinitialize all emulate register */
+    pt_config_reinit(s);
+
+    /* setup MSI-INTx translation if support */
+    ret = pt_enable_msi_translate(s);
+
+    /* rebind machine_irq to device */
+    if (ret < 0 && s->machine_irq != 0) {
+        uint8_t e_device = PCI_SLOT(s->dev.devfn);
+        uint8_t e_intx = pci_intx(s);
+
+        ret = xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, s->machine_irq, 0,
+                                        e_device, e_intx);
+        if (ret < 0) {
+            PT_LOG("Error: Rebinding of interrupt failed! ret=%d\n", ret);
+        }
+    }
+
+    return ret;
+}
+
+static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap)
+{
+    int id;
+    int max_cap = 48;
+    int pos = PCI_CAPABILITY_LIST;
+    int status;
+
+    status = host_pci_read_byte(s->real_device, PCI_STATUS);
+    if ((status & PCI_STATUS_CAP_LIST) == 0) {
+        return 0;
+    }
+
+    while (max_cap--) {
+        pos = host_pci_read_byte(s->real_device, pos);
+        if (pos < 0x40) {
+            break;
+        }
+
+        pos &= ~3;
+        id = host_pci_read_byte(s->real_device, pos + PCI_CAP_LIST_ID);
+
+        if (id == 0xff) {
+            break;
+        }
+        if (id == cap) {
+            return pos;
+        }
+
+        pos += PCI_CAP_LIST_NEXT;
+    }
+    return 0;
+}
+
+static void pt_config_reg_init(XenPCIPassthroughState *s,
+                               XenPTRegGroup *reg_grp, XenPTRegInfo *reg)
+{
+    XenPTReg *reg_entry;
+    uint32_t data = 0;
+
+    reg_entry = g_malloc0(sizeof (XenPTReg));
+
+    reg_entry->reg = reg;
+    reg_entry->data = 0;
+
+    if (reg->init) {
+        /* initialize emulate register */
+        data = reg->init(s, reg_entry->reg,
+                         reg_grp->base_offset + reg->offset);
+        if (data == PT_INVALID_REG) {
+            /* free unused BAR register entry */
+            free(reg_entry);
+            return;
+        }
+        /* set register value */
+        reg_entry->data = data;
+    }
+    /* list add register entry */
+    QLIST_INSERT_HEAD(&reg_grp->reg_tbl_list, reg_entry, entries);
+
+    return;
+}
+
+void pt_config_init(XenPCIPassthroughState *s)
+{
+    XenPTRegGroup *reg_grp_entry = NULL;
+    uint32_t reg_grp_offset = 0;
+    XenPTRegInfo *reg_tbl = NULL;
+    int i, j;
+
+    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_malloc0(sizeof (XenPTRegGroup));
+        QLIST_INIT(&reg_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 */
+            reg_grp_entry->size = 
+                pt_emu_reg_grp_tbl[i].size_init(s, reg_grp_entry->reg_grp,
+                                                reg_grp_offset);
+        }
+
+        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 */
+                    pt_config_reg_init(s, reg_grp_entry, reg_tbl);
+                }
+            }
+        }
+        reg_grp_offset = 0;
+    }
+
+    return;
+}
-- 
Anthony PERARD

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCH RFC V1 09/11] Introduce Xen PCI Passthrough, PCI config space helpers (2/3)
@ 2011-10-04 14:51   ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 hw/xen_pci_passthrough_config_init.c | 2489 ++++++++++++++++++++++++++++++++++
 1 files changed, 2489 insertions(+), 0 deletions(-)
 create mode 100644 hw/xen_pci_passthrough_config_init.c

diff --git a/hw/xen_pci_passthrough_config_init.c b/hw/xen_pci_passthrough_config_init.c
new file mode 100644
index 0000000..4bf9c15
--- /dev/null
+++ b/hw/xen_pci_passthrough_config_init.c
@@ -0,0 +1,2489 @@
+#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)))
+
+/* prototype */
+
+static uint32_t pt_ptr_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg,
+                                uint32_t real_offset);
+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.
+         */
+        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, &reg_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 ((index > 0) && (index < PCI_ROM_SLOT) &&
+        ((s->real_device->base_addr[index - 1]
+          & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK))
+         == (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_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->base_addr[index] & PCI_BASE_ADDRESS_SPACE_IO) {
+        return PT_BAR_FLAG_IO;
+    } else {
+        return PT_BAR_FLAG_MEM;
+    }
+}
+
+
+/****************
+ * general register functions
+ */
+
+/* register initialization function */
+
+static uint32_t pt_common_reg_init(XenPCIPassthroughState *s,
+                                   XenPTRegInfo *reg, uint32_t real_offset)
+{
+    return reg->init_val;
+}
+
+/* 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 uint32_t pt_vendor_reg_init(XenPCIPassthroughState *s,
+                                   XenPTRegInfo *reg, uint32_t real_offset)
+{
+    return s->real_device->vendor_id;
+}
+static uint32_t pt_device_reg_init(XenPCIPassthroughState *s,
+                                   XenPTRegInfo *reg, uint32_t real_offset)
+{
+    return s->real_device->device_id;
+}
+static uint32_t pt_status_reg_init(XenPCIPassthroughState *s,
+                                   XenPTRegInfo *reg, uint32_t real_offset)
+{
+    XenPTRegGroup *reg_grp_entry = NULL;
+    XenPTReg *reg_entry = NULL;
+    int 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 {
+            hw_error("Internal error: Couldn't find pt_reg_tbl for "
+                     "Capabilities Pointer register. I/O emulator exit.\n");
+        }
+    } else {
+        hw_error("Internal error: Couldn't find pt_reg_grp_tbl for Header. "
+                 "I/O emulator exit.\n");
+    }
+
+    return reg_field;
+}
+static uint32_t pt_header_type_reg_init(XenPCIPassthroughState *s,
+                                        XenPTRegInfo *reg,
+                                        uint32_t real_offset)
+{
+    /* read PCI_HEADER_TYPE */
+    return reg->init_val | 0x80;
+}
+
+/* initialize Interrupt Pin register */
+static uint32_t pt_irqpin_reg_init(XenPCIPassthroughState *s,
+                                   XenPTRegInfo *reg, uint32_t real_offset)
+{
+    return pci_read_intx(s);
+}
+
+/* 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) {
+        if (s->msi_trans_en) {
+            msi_set_enable(s, 0);
+        } else {
+            throughable_mask |= PCI_COMMAND_INTX_DISABLE;
+        }
+    } else {
+        if (s->msi_trans_en) {
+            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);
+
+    /* 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 */
+static uint32_t pt_bar_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg,
+                                uint32_t real_offset)
+{
+    int reg_field = 0;
+    int index;
+
+    /* get BAR index */
+    index = pt_bar_offset_to_index(reg->offset);
+    if (index < 0) {
+        hw_error("Internal error: Invalid BAR index[%d]. "
+                 "I/O emulator exit.\n", index);
+    }
+
+    /* 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;
+    }
+
+    return reg_field;
+}
+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) {
+        hw_error("Internal error: Invalid BAR index[%d]. "
+                 "I/O emulator exit.\n", index);
+    }
+
+    /* use fixed-up value from kernel sysfs */
+    *value = s->real_device->base_addr[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) {
+        hw_error("Internal error: Invalid BAR index[%d]. "
+                 "I/O emulator exit.\n", index);
+    }
+
+    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 >= 0x10000) {
+            /* check 64K range */
+            if ((last_addr >= 0x10000) &&
+                (cfg_entry->data != (PT_BAR_ALLF & ~bar_ro_mask))) {
+                PT_LOG("Warning: Guest attempt to set Base Address "
+                       "over the 64KB. [%02x:%02x.%x][Offset:%02xh]"
+                       "[Address:%08xh][Size:%08xh]\n",
+                       pci_bus_num(d->bus), PCI_SLOT(d->devfn),
+                       PCI_FUNC(d->devfn),
+                       reg->offset, new_addr, r_size);
+            }
+            /* just remove mapping */
+            r->addr = -1;
+            goto exit;
+        }
+        break;
+    case PT_BAR_FLAG_UPPER:
+        if (cfg_entry->data) {
+            if (cfg_entry->data != (PT_BAR_ALLF & ~bar_ro_mask)) {
+                PT_LOG("Warning: Guest attempt to set high MMIO Base Address. "
+                       "Ignore mapping. "
+                       "[%02x:%02x.%x][Offset:%02xh][High Address:%08xh]\n",
+                       pci_bus_num(d->bus), PCI_SLOT(d->devfn),
+                       PCI_FUNC(d->devfn), 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) {
+        hw_error("Internal error: Invalid BAR index[%d]. "
+                 "I/O emulator exit.\n", index);
+    }
+
+    /* use value from kernel sysfs */
+    if (s->bases[index].bar_flag == PT_BAR_FLAG_UPPER) {
+        *value = s->real_device->base_addr[index-1]; // >> 32;
+    } else {
+        *value = s->real_device->base_addr[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;
+
+    /* use value from kernel sysfs */
+    *value = 
+        PT_MERGE_VALUE(host_pci_read_long(s->real_device, PCI_ROM_ADDRESS),
+                       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 uint32_t pt_linkctrl_reg_init(XenPCIPassthroughState *s,
+                                     XenPTRegInfo *reg, uint32_t real_offset)
+{
+    uint8_t cap_ver = 0;
+    uint8_t dev_type = 0;
+
+    /* TODO maybe better to use fonction from hw/pcie.c */
+    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)) {
+        return PT_INVALID_REG;
+    }
+
+    return reg->init_val;
+}
+/* initialize Device Control 2 register */
+static uint32_t pt_devctrl2_reg_init(XenPCIPassthroughState *s,
+                                     XenPTRegInfo *reg, uint32_t real_offset)
+{
+    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) {
+        return PT_INVALID_REG;
+    }
+
+    return reg->init_val;
+}
+/* initialize Link Control 2 register */
+static uint32_t pt_linkctrl2_reg_init(XenPCIPassthroughState *s,
+                                      XenPTRegInfo *reg, uint32_t real_offset)
+{
+    int 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) {
+        return PT_INVALID_REG;
+    }
+
+    /* set Supported Link Speed */
+    reg_field |= PCI_EXP_LNKCAP_SLS &
+        pci_get_byte(s->dev.config + real_offset - reg->offset
+                     + PCI_EXP_LNKCAP);
+
+    return reg_field;
+}
+
+/* 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 uint32_t pt_pmc_reg_init(XenPCIPassthroughState *s,
+                                XenPTRegInfo *reg, uint32_t real_offset)
+{
+    PCIDevice *d = &s->dev;
+
+    if (!s->power_mgmt) {
+        return reg->init_val;
+    }
+
+    /* set Power Management Capabilities register */
+    s->pm_state->pmc_field = pci_get_word(d->config + real_offset);
+
+    return reg->init_val;
+}
+/* initialize PCI Power Management Control/Status register */
+static uint32_t pt_pmcsr_reg_init(XenPCIPassthroughState *s,
+                                  XenPTRegInfo *reg, uint32_t real_offset)
+{
+    PCIDevice *d = &s->dev;
+    uint16_t cap_ver  = 0;
+
+    if (!s->power_mgmt) {
+        return reg->init_val;
+    }
+
+    /* 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;
+    }
+
+    /* wake up real physical device */
+    switch (host_pci_read_word(s->real_device, real_offset)
+            & PCI_PM_CTRL_STATE_MASK) {
+    case 0:
+        break;
+    case 1:
+        PT_LOG("Power state transition D1 -> D0active\n");
+        host_pci_write_word(s->real_device, real_offset, 0);
+        break;
+    case 2:
+        PT_LOG("Power state transition D2 -> D0active\n");
+        host_pci_write_word(s->real_device, real_offset, 0);
+        usleep(200);
+        break;
+    case 3:
+        PT_LOG("Power state transition D3hot -> D0active\n");
+        host_pci_write_word(s->real_device, real_offset, 0);
+        usleep(10 * 1000);
+        pt_init_pci_config(s);
+        break;
+    }
+
+    return reg->init_val;
+}
+/* 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->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_LOG("Error: 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];
+        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 */
+    read_val = host_pci_read_word(s->real_device,
+                                  pm_state->pm_base + PCI_PM_CTRL);
+    cur_state = read_val & PCI_PM_CTRL_STATE_MASK;
+
+    if (pm_state->req_state != cur_state) {
+        PT_LOG("Error: Failed to change power state. "
+               "[%02x:%02x.%x][requested state:%d][current state:%d]\n",
+               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+               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;
+    struct pt_pm_info *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_LOG("Error: Invalid power transition. "
+               "[%02x:%02x.%x][requested state:%d][current state:%d]\n",
+               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+               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_LOG("Error: Invalid power transition. "
+               "[%02x:%02x.%x][requested state:%d][current state:%d]\n",
+               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+               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_write_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 */
+        *value = host_pci_read_word(s->real_device,
+                                    pm_state->pm_base + PCI_PM_CTRL);
+    }
+
+    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,
+    },
+};
+
+/********************************
+ * MSI Capability
+ */
+
+/* Message Control register */
+static uint32_t pt_msgctrl_reg_init(XenPCIPassthroughState *s,
+                                    XenPTRegInfo *reg, uint32_t real_offset)
+{
+    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("MSI enabled already, disable first\n");
+        host_pci_write_word(s->real_device, real_offset,
+                            reg_field & ~PCI_MSI_FLAGS_ENABLE);
+    }
+    s->msi->flags |= reg_field | MSI_FLAG_UNINIT;
+    s->msi->ctrl_offset = real_offset;
+
+    return reg->init_val;
+}
+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;
+    PCIDevice *pd = (PCIDevice *)s;
+    uint16_t val;
+
+    /* Currently no support for multi-vector */
+    if (*value & PCI_MSI_FLAGS_QSIZE) {
+        PT_LOG("Warning: 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 &
+        ~(MSI_FLAG_UNINIT | PT_MSI_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 & MSI_FLAG_UNINIT) {
+            if (s->msi_trans_en) {
+                PT_LOG("guest enabling MSI, disable MSI-INTx translation\n");
+                pt_disable_msi_translate(s);
+            } else {
+                /* Init physical one */
+                PT_LOG("setup msi for dev %x\n", pd->devfn);
+                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_LOG("Warning: Can not map MSI for dev %x\n", pd->devfn);
+		    return 0;
+                }
+            }
+            if (pt_msi_update(s)) {
+                *value &= ~PCI_MSI_FLAGS_ENABLE;
+                PT_LOG("Warning: Can not bind MSI for dev %x\n", pd->devfn);
+                return 0;
+            }
+            s->msi->flags &= ~MSI_FLAG_UNINIT;
+            s->msi->flags |= PT_MSI_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 uint32_t pt_msgaddr64_reg_init(XenPCIPassthroughState *ptdev,
+                                      XenPTRegInfo *reg, uint32_t real_offset)
+{
+    /* no need to initialize in case of 32 bit type */
+    if (!(ptdev->msi->flags & PCI_MSI_FLAGS_64BIT)) {
+        return PT_INVALID_REG;
+    }
+
+    return reg->init_val;
+}
+/* this function will be called twice (for 32 bit and 64 bit type) */
+/* initialize Message Data register */
+static uint32_t pt_msgdata_reg_init(XenPCIPassthroughState *ptdev,
+                                    XenPTRegInfo *reg, uint32_t real_offset)
+{
+    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))) {
+        return reg->init_val;
+    } else {
+        return PT_INVALID_REG;
+    }
+}
+
+/* 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_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)) {
+        /* exit I/O emulator */
+        PT_LOG("Error: why comes to 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_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_LOG("Error: the offset is not match with 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_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 uint32_t pt_msixctrl_reg_init(XenPCIPassthroughState *s,
+                                     XenPTRegInfo *reg, uint32_t real_offset)
+{
+    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("MSIX enabled already, disable first\n");
+        host_pci_write_word(s->real_device, real_offset,
+                            reg_field & ~PCI_MSIX_FLAGS_ENABLE);
+    }
+
+    s->msix->ctrl_offset = real_offset;
+
+    return reg->init_val;
+}
+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("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
+ */
+
+/* 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;
+
+    val = host_pci_read_long(s->real_device, aer_base + offset);
+    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_write_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 uint8_t pt_reg_grp_size_init(XenPCIPassthroughState *s,
+                                    const XenPTRegGroupInfo *grp_reg,
+                                    uint32_t base_offset)
+{
+    return grp_reg->grp_size;
+}
+/* get Power Management Capability Structure register group size */
+static uint8_t pt_pm_size_init(XenPCIPassthroughState *s,
+                               const XenPTRegGroupInfo *grp_reg,
+                               uint32_t base_offset)
+{
+    if (!s->power_mgmt) {
+        return grp_reg->grp_size;
+    }
+
+    s->pm_state = g_malloc0(sizeof (XenPTPM));
+
+    /* 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 grp_reg->grp_size;
+}
+/* get Vendor Specific Capability Structure register group size */
+static uint8_t pt_vendor_size_init(XenPCIPassthroughState *s,
+                                   const XenPTRegGroupInfo *grp_reg,
+                                   uint32_t base_offset)
+{
+    return pci_get_byte(s->dev.config + base_offset + 0x02);
+}
+/* get PCI Express Capability Structure register group size */
+static uint8_t pt_pcie_size_init(XenPCIPassthroughState *s,
+                                 const XenPTRegGroupInfo *grp_reg,
+                                 uint32_t base_offset)
+{
+    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:
+            hw_error("Internal error: Unsupported device/port type[%d]. "
+                     "I/O emulator exit.\n", type);
+        }
+    }
+    /* 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:
+            hw_error("Internal error: Unsupported device/port type[%d]. "
+                     "I/O emulator exit.\n", type);
+        }
+    } else {
+        hw_error("Internal error: Unsupported capability version[%d]. "
+                 "I/O emulator exit.\n", version);
+    }
+
+    return pcie_size;
+}
+/* get MSI Capability Structure register group size */
+static uint8_t pt_msi_size_init(XenPCIPassthroughState *s,
+                                const XenPTRegGroupInfo *grp_reg,
+                                uint32_t base_offset)
+{
+    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_malloc0(sizeof (XenPTMSI));
+    s->msi->pirq = -1;
+    PT_LOG("done\n");
+
+    return msi_size;
+}
+/* get MSI-X Capability Structure register group size */
+static uint8_t pt_msix_size_init(XenPCIPassthroughState *s,
+                                 const XenPTRegGroupInfo *grp_reg,
+                                 uint32_t base_offset)
+{
+    int ret = 0;
+
+    ret = pt_msix_init(s, base_offset);
+
+    if (ret == -1) {
+        hw_error("Internal error: Invalid pt_msix_init return value[%d]. "
+                 "I/O emulator exit.\n", ret);
+    }
+
+    return grp_reg->grp_size;
+}
+
+
+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,
+    },
+    /* 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,
+        .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,
+    },
+    /* 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,
+    },
+};
+
+/* initialize Capabilities Pointer or Next Pointer register */
+static uint32_t pt_ptr_reg_init(XenPCIPassthroughState *s,
+                                XenPTRegInfo *reg, uint32_t real_offset)
+{
+    /* 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:
+    return reg_field;
+}
+
+
+/*************
+ * Main
+ */
+
+/* restore a part of I/O device register */
+static void 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 ret = 0;
+
+    /* find emulate register group entry */
+    QLIST_FOREACH(reg_grp_entry, &s->reg_grp_tbl, entries) {
+        /* find emulate register entry */
+        QLIST_FOREACH(reg_entry, &reg_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 */
+            ret = host_pci_read_block(s->real_device, real_offset,
+                                      (uint8_t *)&read_val, reg->size);
+
+            if (!ret) {
+                PT_LOG("Error: pci_read_block failed. "
+                       "return value[%d].\n", ret);
+                memset(&read_val, 0xff, reg->size);
+            }
+
+            val = 0;
+
+            /* restore based on register size */
+            switch (reg->size) {
+            case 1:
+                /* byte register */
+                ret = reg->u.b.restore(s, reg_entry, real_offset,
+                                       (uint8_t)read_val, (uint8_t *)&val);
+                break;
+            case 2:
+                /* word register */
+                ret = reg->u.w.restore(s, reg_entry, real_offset,
+                                       (uint16_t)read_val, (uint16_t *)&val);
+                break;
+            case 4:
+                /* double word register */
+                ret = reg->u.dw.restore(s, reg_entry, real_offset,
+                                        (uint32_t)read_val, (uint32_t *)&val);
+                break;
+            }
+
+            /* restoring error */
+            if (ret < 0) {
+                hw_error("Internal error: Invalid restoring "
+                         "return value[%d]. I/O emulator exit.\n", ret);
+            }
+
+            PT_LOG_CONFIG("[%02x:%02x.%x]: address=%04x val=0x%08x len=%d\n",
+                          pci_bus_num(s->dev.bus), PCI_SLOT(s->dev.devfn),
+                          PCI_FUNC(s->dev.devfn),
+                          real_offset, val, reg->size);
+
+            ret = host_pci_write_block(s->real_device, real_offset,
+                                       (uint8_t *)&val, reg->size);
+
+            if (!ret) {
+                PT_LOG("Error: pci_write_block failed. "
+                       "return value[%d].\n", ret);
+            }
+        }
+    }
+
+    /* if AER supported, restore it */
+    if (s->pm_state->aer_base) {
+        pt_aer_reg_restore(s);
+    }
+}
+/* reinitialize all emulate registers */
+static void pt_config_reinit(XenPCIPassthroughState *s)
+{
+    XenPTRegGroup *reg_grp_entry = NULL;
+    XenPTReg *reg_entry = NULL;
+    XenPTRegInfo *reg = NULL;
+
+    /* find emulate register group entry */
+    QLIST_FOREACH(reg_grp_entry, &s->reg_grp_tbl, entries) {
+        /* find emulate register entry */
+        QLIST_FOREACH(reg_entry, &reg_grp_entry->reg_tbl_list, entries) {
+            reg = reg_entry->reg;
+            if (reg->init) {
+                /* initialize emulate register */
+                reg_entry->data = 
+                    reg->init(s, reg_entry->reg,
+                              reg_grp_entry->base_offset + reg->offset);
+            }
+        }
+    }
+}
+
+static int pt_init_pci_config(XenPCIPassthroughState *s)
+{
+    PCIDevice *d = &s->dev;
+    int ret = 0;
+
+    PT_LOG("Reinitialize PCI configuration registers due to power state"
+           " transition with internal reset. [%02x:%02x.%x]\n",
+           pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
+
+    /* restore a part of I/O device register */
+    pt_config_restore(s);
+
+    /* reinitialize all emulate register */
+    pt_config_reinit(s);
+
+    /* setup MSI-INTx translation if support */
+    ret = pt_enable_msi_translate(s);
+
+    /* rebind machine_irq to device */
+    if (ret < 0 && s->machine_irq != 0) {
+        uint8_t e_device = PCI_SLOT(s->dev.devfn);
+        uint8_t e_intx = pci_intx(s);
+
+        ret = xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, s->machine_irq, 0,
+                                        e_device, e_intx);
+        if (ret < 0) {
+            PT_LOG("Error: Rebinding of interrupt failed! ret=%d\n", ret);
+        }
+    }
+
+    return ret;
+}
+
+static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap)
+{
+    int id;
+    int max_cap = 48;
+    int pos = PCI_CAPABILITY_LIST;
+    int status;
+
+    status = host_pci_read_byte(s->real_device, PCI_STATUS);
+    if ((status & PCI_STATUS_CAP_LIST) == 0) {
+        return 0;
+    }
+
+    while (max_cap--) {
+        pos = host_pci_read_byte(s->real_device, pos);
+        if (pos < 0x40) {
+            break;
+        }
+
+        pos &= ~3;
+        id = host_pci_read_byte(s->real_device, pos + PCI_CAP_LIST_ID);
+
+        if (id == 0xff) {
+            break;
+        }
+        if (id == cap) {
+            return pos;
+        }
+
+        pos += PCI_CAP_LIST_NEXT;
+    }
+    return 0;
+}
+
+static void pt_config_reg_init(XenPCIPassthroughState *s,
+                               XenPTRegGroup *reg_grp, XenPTRegInfo *reg)
+{
+    XenPTReg *reg_entry;
+    uint32_t data = 0;
+
+    reg_entry = g_malloc0(sizeof (XenPTReg));
+
+    reg_entry->reg = reg;
+    reg_entry->data = 0;
+
+    if (reg->init) {
+        /* initialize emulate register */
+        data = reg->init(s, reg_entry->reg,
+                         reg_grp->base_offset + reg->offset);
+        if (data == PT_INVALID_REG) {
+            /* free unused BAR register entry */
+            free(reg_entry);
+            return;
+        }
+        /* set register value */
+        reg_entry->data = data;
+    }
+    /* list add register entry */
+    QLIST_INSERT_HEAD(&reg_grp->reg_tbl_list, reg_entry, entries);
+
+    return;
+}
+
+void pt_config_init(XenPCIPassthroughState *s)
+{
+    XenPTRegGroup *reg_grp_entry = NULL;
+    uint32_t reg_grp_offset = 0;
+    XenPTRegInfo *reg_tbl = NULL;
+    int i, j;
+
+    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_malloc0(sizeof (XenPTRegGroup));
+        QLIST_INIT(&reg_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 */
+            reg_grp_entry->size = 
+                pt_emu_reg_grp_tbl[i].size_init(s, reg_grp_entry->reg_grp,
+                                                reg_grp_offset);
+        }
+
+        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 */
+                    pt_config_reg_init(s, reg_grp_entry, reg_tbl);
+                }
+            }
+        }
+        reg_grp_offset = 0;
+    }
+
+    return;
+}
-- 
Anthony PERARD

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [Qemu-devel] [PATCH RFC V1 10/11] Introduce Xen PCI Passthrough, MSI (3/3)
  2011-10-04 14:51 ` Anthony PERARD
@ 2011-10-04 14:51   ` Anthony PERARD
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 hw/xen_pci_passthrough_msi.c |  674 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 674 insertions(+), 0 deletions(-)
 create mode 100644 hw/xen_pci_passthrough_msi.c

diff --git a/hw/xen_pci_passthrough_msi.c b/hw/xen_pci_passthrough_msi.c
new file mode 100644
index 0000000..be18ff1
--- /dev/null
+++ b/hw/xen_pci_passthrough_msi.c
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 2007, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * 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"
+
+void msi_set_enable(XenPCIPassthroughState *dev, int en)
+{
+    uint16_t val = 0;
+    uint32_t address = 0;
+    PT_LOG("enable: %i\n", en);
+
+    if (!dev->msi) {
+        return;
+    }
+
+    address = dev->msi->ctrl_offset;
+    if (!address) {
+        return;
+    }
+
+    val = host_pci_read_word(dev->real_device, address);
+    val &= ~PCI_MSI_FLAGS_ENABLE;
+    val |= en & PCI_MSI_FLAGS_ENABLE;
+    host_pci_write_word(dev->real_device, address, val);
+
+    PT_LOG("done, address: %#x, val: %#x\n", address, val);
+}
+
+static void msix_set_enable(XenPCIPassthroughState *dev, int en)
+{
+    uint16_t val = 0;
+    uint32_t address = 0;
+
+    if (!dev->msix) {
+        return;
+    }
+
+    address = dev->msix->ctrl_offset;
+    if (!address) {
+        return;
+    }
+
+    val = host_pci_read_word(dev->real_device, address);
+    val &= ~PCI_MSIX_FLAGS_ENABLE;
+    if (en) {
+        val |= PCI_MSIX_FLAGS_ENABLE;
+    }
+    host_pci_write_word(dev->real_device, address, val);
+}
+
+/*********************************/
+/* MSI virtuailization functions */
+
+/*
+ * setup physical msi, but didn't enable it
+ */
+int pt_msi_setup(XenPCIPassthroughState *dev)
+{
+    int pirq = -1;
+    uint8_t gvec = 0;
+
+    if (!(dev->msi->flags & MSI_FLAG_UNINIT)) {
+        PT_LOG("Error: setup physical after initialized?? \n");
+        return -1;
+    }
+
+    gvec = dev->msi->data & 0xFF;
+    if (!gvec) {
+        /* if gvec is 0, the guest is asking for a particular pirq that
+         * is passed as dest_id */
+        pirq = (dev->msi->addr_hi & 0xffffff00) |
+               ((dev->msi->addr_lo >> MSI_TARGET_CPU_SHIFT) & 0xff);
+        if (!pirq) {
+            /* this probably identifies an misconfiguration of the guest,
+             * try the emulated path */
+            pirq = -1;
+        } else {
+            PT_LOG("pt_msi_setup requested pirq = %d\n", pirq);
+        }
+    }
+
+    if (xc_physdev_map_pirq_msi(xen_xc, xen_domid, AUTO_ASSIGN, &pirq,
+                                PCI_DEVFN(dev->real_device->dev,
+                                          dev->real_device->func),
+                                dev->real_device->bus, 0, 0)) {
+        PT_LOG("Error: Mapping of MSI failed.\n");
+        return -1;
+    }
+
+    if (pirq < 0) {
+        PT_LOG("Error: Invalid pirq number\n");
+        return -1;
+    }
+
+    dev->msi->pirq = pirq;
+    PT_LOG("msi mapped with pirq %x\n", pirq);
+
+    return 0;
+}
+
+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_DESTMODE_SHIFT) & 0x1;
+    dest_id = (addr >> MSI_TARGET_CPU_SHIFT) & 0xff;
+    deliv_mode = (data >> MSI_DATA_DELIVERY_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;
+}
+
+int pt_msi_update(XenPCIPassthroughState *d)
+{
+    uint8_t gvec = 0;
+    uint32_t gflags = 0;
+    uint64_t addr = 0;
+    int ret = 0;
+
+    /* get vector, address, flags info, etc. */
+    gvec = d->msi->data & 0xFF;
+    addr = (uint64_t)d->msi->addr_hi << 32 | d->msi->addr_lo;
+    gflags = __get_msi_gflags(d->msi->data, addr);
+
+    PT_LOG("Update msi with pirq %x gvec %x gflags %x\n",
+           d->msi->pirq, gvec, gflags);
+
+    ret = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec,
+                                   d->msi->pirq, gflags, 0);
+
+    if (ret) {
+        PT_LOG("Error: Binding of MSI failed.\n");
+
+        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, d->msi->pirq)) {
+            PT_LOG("Error: Unmapping of MSI failed.\n");
+        }
+        d->msi->pirq = -1;
+        return ret;
+    }
+    return 0;
+}
+
+void pt_msi_disable(XenPCIPassthroughState *dev)
+{
+    PCIDevice *d = &dev->dev;
+    uint8_t gvec = 0;
+    uint32_t gflags = 0;
+    uint64_t addr = 0;
+    uint8_t e_device = 0;
+    uint8_t e_intx = 0;
+
+    msi_set_enable(dev, 0);
+
+    e_device = PCI_SLOT(dev->dev.devfn);
+    e_intx = pci_intx(dev);
+
+    if (dev->msi_trans_en) {
+        if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, dev->msi->pirq,
+                                    PT_IRQ_TYPE_MSI_TRANSLATE, 0,
+                                    e_device, e_intx, 0)) {
+            PT_LOG("Error: Unbinding pt irq for MSI-INTx failed!\n");
+            goto out;
+        }
+    } else if (!(dev->msi->flags & MSI_FLAG_UNINIT)) {
+        /* get vector, address, flags info, etc. */
+        gvec = dev->msi->data & 0xFF;
+        addr = (uint64_t)dev->msi->addr_hi << 32 | dev->msi->addr_lo;
+        gflags = __get_msi_gflags(dev->msi->data, addr);
+
+        PT_LOG("Unbind msi with pirq %x, gvec %x\n",
+                dev->msi->pirq, gvec);
+
+        if (xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec,
+                                        dev->msi->pirq, gflags)) {
+            PT_LOG("Error: Unbinding of MSI failed. [%02x:%02x.%x]\n",
+                   pci_bus_num(d->bus), PCI_SLOT(d->devfn),
+                   PCI_FUNC(d->devfn));
+            goto out;
+        }
+    }
+
+    if (dev->msi->pirq != -1) {
+        PT_LOG("Unmap msi with pirq %x\n", dev->msi->pirq);
+
+        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, dev->msi->pirq)) {
+            PT_LOG("Error: Unmapping of MSI failed. [%02x:%02x.%x]\n",
+                   pci_bus_num(d->bus), PCI_SLOT(d->devfn),
+                   PCI_FUNC(d->devfn));
+            goto out;
+        }
+    }
+
+out:
+    /* clear msi info */
+    dev->msi->flags = 0;
+    dev->msi->pirq = -1;
+    dev->msi_trans_en = 0;
+}
+
+/* MSI-INTx translation virtulization functions */
+int pt_enable_msi_translate(XenPCIPassthroughState* dev)
+{
+    uint8_t e_device = 0;
+    uint8_t e_intx = 0;
+
+    if (!(dev->msi && dev->msi_trans_cap)) {
+        return -1;
+    }
+
+    msi_set_enable(dev, 0);
+    dev->msi_trans_en = 0;
+
+    if (pt_msi_setup(dev)) {
+        PT_LOG("Error: MSI-INTx translation MSI setup failed, fallback\n");
+        return -1;
+    }
+
+    e_device = PCI_SLOT(dev->dev.devfn);
+    /* fix virtual interrupt pin to INTA# */
+    e_intx = pci_intx(dev);
+
+    if (xc_domain_bind_pt_irq(xen_xc, xen_domid, dev->msi->pirq,
+                              PT_IRQ_TYPE_MSI_TRANSLATE, 0,
+                              e_device, e_intx, 0)) {
+        PT_LOG("Error: MSI-INTx translation bind failed, fallback\n");
+
+        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, dev->msi->pirq)) {
+            PT_LOG("Error: Unmapping of MSI failed.\n");
+        }
+        dev->msi->pirq = -1;
+        return -1;
+    }
+
+    msi_set_enable(dev, 1);
+    dev->msi_trans_en = 1;
+
+    return 0;
+}
+
+void pt_disable_msi_translate(XenPCIPassthroughState *dev)
+{
+    uint8_t e_device = 0;
+    uint8_t e_intx = 0;
+
+    /* MSI_ENABLE bit should be disabed until the new handler is set */
+    msi_set_enable(dev, 0);
+
+    e_device = PCI_SLOT(dev->dev.devfn);
+    e_intx = pci_intx(dev);
+
+    if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, dev->msi->pirq,
+                                 PT_IRQ_TYPE_MSI_TRANSLATE, 0,
+                                 e_device, e_intx, 0)) {
+        PT_LOG("Error: Unbinding pt irq for MSI-INTx failed!\n");
+    }
+
+    if (dev->machine_irq) {
+        if (xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, dev->machine_irq,
+                                       0, e_device, e_intx)) {
+            PT_LOG("Error: Rebinding of interrupt failed!\n");
+        }
+    }
+
+    dev->msi_trans_en = 0;
+}
+
+/*********************************/
+/* MSI-X virtulization functions */
+
+static void mask_physical_msix_entry(XenPCIPassthroughState *dev,
+                                     int entry_nr, int mask)
+{
+    void *phys_off;
+
+    phys_off = dev->msix->phys_iomem_base + 16 * entry_nr + 12;
+    *(uint32_t *)phys_off = mask;
+}
+
+static int pt_msix_update_one(XenPCIPassthroughState *dev, int entry_nr)
+{
+    struct msix_entry_info *entry = &dev->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 ret;
+
+    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_TARGET_CPU_SHIFT) & 0xff);
+        if (!pirq) {
+            /* this probably identifies an misconfiguration of the guest,
+             * try the emulated path */
+            pirq = -1;
+        } else {
+            PT_LOG("pt_msix_update_one requested pirq = %d\n", pirq);
+        }
+    }
+
+    /* Check if this entry is already mapped */
+    if (entry->pirq == -1) {
+        ret = xc_physdev_map_pirq_msi(xen_xc, xen_domid, AUTO_ASSIGN, &pirq,
+                                      PCI_DEVFN(dev->real_device->dev,
+                                                dev->real_device->func),
+                                      dev->real_device->bus, entry_nr,
+                                      dev->msix->table_base);
+        if (ret) {
+            PT_LOG("Error: Mapping msix entry %x\n", entry_nr);
+            return ret;
+        }
+        entry->pirq = pirq;
+    }
+
+    PT_LOG("Update msix entry %x with pirq %x gvec %x\n",
+            entry_nr, pirq, gvec);
+
+    ret = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec, pirq, gflags,
+                                   dev->msix->mmio_base_addr);
+    if (ret) {
+        PT_LOG("Error: Updating msix irq info for entry %d\n", entry_nr);
+
+        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, entry->pirq)) {
+            PT_LOG("Error: Unmapping of MSI-X failed.\n");
+        }
+        entry->pirq = -1;
+        return ret;
+    }
+
+    entry->flags = 0;
+
+    return 0;
+}
+
+int pt_msix_update(XenPCIPassthroughState *dev)
+{
+    struct pt_msix_info *msix = dev->msix;
+    int i;
+
+    for (i = 0; i < msix->total_entries; i++) {
+        pt_msix_update_one(dev, i);
+    }
+
+    return 0;
+}
+
+void pt_msix_disable(XenPCIPassthroughState *dev)
+{
+    PCIDevice *d = &dev->dev;
+    uint8_t gvec = 0;
+    uint32_t gflags = 0;
+    uint64_t addr = 0;
+    int i = 0;
+    struct msix_entry_info *entry = NULL;
+
+    msix_set_enable(dev, 0);
+
+    for ( i = 0; i < dev->msix->total_entries; i++ ) {
+        entry = &dev->msix->msix_entry[i];
+
+        if (entry->pirq == -1) {
+            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("Unbind msix with pirq %x, gvec %x\n",
+                entry->pirq, gvec);
+
+        if (xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec,
+                                        entry->pirq, gflags)) {
+            PT_LOG("Error: Unbinding of MSI-X failed. [%02x:%02x.%x]\n",
+                   pci_bus_num(d->bus), PCI_SLOT(d->devfn),
+                   PCI_FUNC(d->devfn));
+        } else {
+            PT_LOG("Unmap msix with pirq %x\n", entry->pirq);
+
+            if (xc_physdev_unmap_pirq(xen_xc, xen_domid, entry->pirq)) {
+                PT_LOG("Error: Unmapping of MSI-X failed. [%02x:%02x.%x]\n",
+                       pci_bus_num(d->bus),
+                       PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
+            }
+        }
+        /* clear msi-x info */
+        entry->pirq = -1;
+        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 != -1) {
+            ret = xc_domain_unbind_pt_irq(xen_xc, xen_domid, entry->pirq,
+                                          PT_IRQ_TYPE_MSI, 0, 0, 0, 0);
+            if (ret) {
+                PT_LOG("Error: 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)
+{
+    PT_LOG("Error: 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 *dev = (XenPCIPassthroughState *)opaque;
+    XenPTMSIX *msix = dev->msix;
+    XenMSIXEntry *entry;
+    int entry_nr, offset;
+    void *phys_off;
+    uint32_t vec_ctrl;
+
+    if (addr % 4) {
+        PT_LOG("Error: Unaligned dword access to MSI-X table, "
+                "addr %016"PRIx64"\n", addr);
+        return;
+    }
+
+    PT_LOG("addr: "TARGET_FMT_plx", val: %#x\n", addr, val);
+
+    // FIXME/TODO I maybe have to do the same in readl !
+    /* entry_nr = (addr - msix->mmio_base_addr) / 16; */
+    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 = dev->msix->phys_iomem_base + 16 * entry_nr + 12;
+    vec_ctrl = *(uint32_t *)phys_off;
+
+    if (offset != 3 && msix->enabled && !(vec_ctrl & 0x1)) {
+        PT_LOG("Error: Can't update msix entry %d since MSI-X is already "
+                "function.\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(dev, entry_nr);
+        }
+        mask_physical_msix_entry(dev, 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)
+{
+    PT_LOG("Error: 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 *dev = (XenPCIPassthroughState *)opaque;
+    struct pt_msix_info *msix = dev->msix;
+    int entry_nr, offset;
+
+    if (addr % 4) {
+        PT_LOG("Error: Unaligned dword access to MSI-X table, "
+                "addr %016"PRIx64"\n", addr);
+        return 0;
+    }
+
+    PT_LOG("addr: "TARGET_FMT_plx"\n", addr);
+
+    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 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 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 *dev, int pos)
+{
+    uint8_t id;
+    uint16_t control;
+    int i, total_entries, table_off, bar_index;
+    HostPCIDevice *pd = dev->real_device;
+    int fd;
+
+    id = host_pci_read_byte(pd, pos + PCI_CAP_LIST_ID);
+
+    if (id != PCI_CAP_ID_MSIX) {
+        PT_LOG("Error: Invalid id %#x pos %#x\n", id, pos);
+        return -1;
+    }
+
+    control = host_pci_read_word(pd, pos + 2);
+    total_entries = control & 0x7ff;
+    total_entries += 1;
+
+    dev->msix = g_malloc0(sizeof (struct pt_msix_info)
+                          + total_entries * sizeof (struct msix_entry_info));
+
+    dev->msix->total_entries = total_entries;
+    for (i = 0; i < total_entries; i++) {
+        dev->msix->msix_entry[i].pirq = -1;
+    }
+
+    dev->msix->mmio_index =
+        cpu_register_io_memory(pci_msix_read, pci_msix_write,
+                               dev, DEVICE_NATIVE_ENDIAN);
+
+    table_off = host_pci_read_long(pd, pos + PCI_MSIX_TABLE);
+    bar_index = dev->msix->bar_index = table_off & PCI_MSIX_FLAGS_BIRMASK;
+    table_off = dev->msix->table_off = table_off & ~PCI_MSIX_FLAGS_BIRMASK;
+    dev->msix->table_base =
+        pt_pci_base_addr(dev->real_device->base_addr[bar_index]);
+    PT_LOG("get MSI-X table bar base %#"PRIx64"\n", dev->msix->table_base);
+
+    fd = open("/dev/mem", O_RDWR);
+    if (fd == -1) {
+        PT_LOG("Error: Can't open /dev/mem: %s\n", strerror(errno));
+        goto error_out;
+    }
+    PT_LOG("table_off = %#x, total_entries = %d\n", table_off, total_entries);
+    dev->msix->table_offset_adjust = table_off & 0x0fff;
+    dev->msix->phys_iomem_base = mmap(0,
+                                      total_entries * 16
+                                      + dev->msix->table_offset_adjust,
+                                      PROT_WRITE | PROT_READ,
+                                      MAP_SHARED | MAP_LOCKED,
+                                      fd,
+                                      dev->msix->table_base + table_off
+                                      - dev->msix->table_offset_adjust);
+    if (dev->msix->phys_iomem_base == MAP_FAILED) {
+        PT_LOG("Error: Can't map physical MSI-X table: %s\n", strerror(errno));
+        close(fd);
+        goto error_out;
+    }
+    dev->msix->phys_iomem_base = ((char *)dev->msix->phys_iomem_base +
+                                  dev->msix->table_offset_adjust);
+
+    close(fd);
+
+    PT_LOG("mapping physical MSI-X table to %p\n", dev->msix->phys_iomem_base);
+    return 0;
+
+error_out:
+    g_free(dev->msix);
+    dev->msix = NULL;
+    return -1;
+}
+
+#if 0
+static void pt_msix_delete(XenPCIPassthroughState *dev)
+{
+    /* unmap the MSI-X memory mapped register area */
+    if (dev->msix->phys_iomem_base)
+    {
+        PT_LOG("unmapping physical MSI-X table from %lx\n",
+           (unsigned long)dev->msix->phys_iomem_base);
+        munmap(dev->msix->phys_iomem_base, dev->msix->total_entries * 16 +
+           dev->msix->table_offset_adjust);
+    }
+
+    if (dev->msix->mmio_index > 0)
+    {
+        cpu_unregister_io_memory(dev->msix->mmio_index);
+    }
+
+
+    free(dev->msix);
+}
+#endif
-- 
Anthony PERARD

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCH RFC V1 10/11] Introduce Xen PCI Passthrough, MSI (3/3)
@ 2011-10-04 14:51   ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 hw/xen_pci_passthrough_msi.c |  674 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 674 insertions(+), 0 deletions(-)
 create mode 100644 hw/xen_pci_passthrough_msi.c

diff --git a/hw/xen_pci_passthrough_msi.c b/hw/xen_pci_passthrough_msi.c
new file mode 100644
index 0000000..be18ff1
--- /dev/null
+++ b/hw/xen_pci_passthrough_msi.c
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 2007, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * 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"
+
+void msi_set_enable(XenPCIPassthroughState *dev, int en)
+{
+    uint16_t val = 0;
+    uint32_t address = 0;
+    PT_LOG("enable: %i\n", en);
+
+    if (!dev->msi) {
+        return;
+    }
+
+    address = dev->msi->ctrl_offset;
+    if (!address) {
+        return;
+    }
+
+    val = host_pci_read_word(dev->real_device, address);
+    val &= ~PCI_MSI_FLAGS_ENABLE;
+    val |= en & PCI_MSI_FLAGS_ENABLE;
+    host_pci_write_word(dev->real_device, address, val);
+
+    PT_LOG("done, address: %#x, val: %#x\n", address, val);
+}
+
+static void msix_set_enable(XenPCIPassthroughState *dev, int en)
+{
+    uint16_t val = 0;
+    uint32_t address = 0;
+
+    if (!dev->msix) {
+        return;
+    }
+
+    address = dev->msix->ctrl_offset;
+    if (!address) {
+        return;
+    }
+
+    val = host_pci_read_word(dev->real_device, address);
+    val &= ~PCI_MSIX_FLAGS_ENABLE;
+    if (en) {
+        val |= PCI_MSIX_FLAGS_ENABLE;
+    }
+    host_pci_write_word(dev->real_device, address, val);
+}
+
+/*********************************/
+/* MSI virtuailization functions */
+
+/*
+ * setup physical msi, but didn't enable it
+ */
+int pt_msi_setup(XenPCIPassthroughState *dev)
+{
+    int pirq = -1;
+    uint8_t gvec = 0;
+
+    if (!(dev->msi->flags & MSI_FLAG_UNINIT)) {
+        PT_LOG("Error: setup physical after initialized?? \n");
+        return -1;
+    }
+
+    gvec = dev->msi->data & 0xFF;
+    if (!gvec) {
+        /* if gvec is 0, the guest is asking for a particular pirq that
+         * is passed as dest_id */
+        pirq = (dev->msi->addr_hi & 0xffffff00) |
+               ((dev->msi->addr_lo >> MSI_TARGET_CPU_SHIFT) & 0xff);
+        if (!pirq) {
+            /* this probably identifies an misconfiguration of the guest,
+             * try the emulated path */
+            pirq = -1;
+        } else {
+            PT_LOG("pt_msi_setup requested pirq = %d\n", pirq);
+        }
+    }
+
+    if (xc_physdev_map_pirq_msi(xen_xc, xen_domid, AUTO_ASSIGN, &pirq,
+                                PCI_DEVFN(dev->real_device->dev,
+                                          dev->real_device->func),
+                                dev->real_device->bus, 0, 0)) {
+        PT_LOG("Error: Mapping of MSI failed.\n");
+        return -1;
+    }
+
+    if (pirq < 0) {
+        PT_LOG("Error: Invalid pirq number\n");
+        return -1;
+    }
+
+    dev->msi->pirq = pirq;
+    PT_LOG("msi mapped with pirq %x\n", pirq);
+
+    return 0;
+}
+
+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_DESTMODE_SHIFT) & 0x1;
+    dest_id = (addr >> MSI_TARGET_CPU_SHIFT) & 0xff;
+    deliv_mode = (data >> MSI_DATA_DELIVERY_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;
+}
+
+int pt_msi_update(XenPCIPassthroughState *d)
+{
+    uint8_t gvec = 0;
+    uint32_t gflags = 0;
+    uint64_t addr = 0;
+    int ret = 0;
+
+    /* get vector, address, flags info, etc. */
+    gvec = d->msi->data & 0xFF;
+    addr = (uint64_t)d->msi->addr_hi << 32 | d->msi->addr_lo;
+    gflags = __get_msi_gflags(d->msi->data, addr);
+
+    PT_LOG("Update msi with pirq %x gvec %x gflags %x\n",
+           d->msi->pirq, gvec, gflags);
+
+    ret = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec,
+                                   d->msi->pirq, gflags, 0);
+
+    if (ret) {
+        PT_LOG("Error: Binding of MSI failed.\n");
+
+        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, d->msi->pirq)) {
+            PT_LOG("Error: Unmapping of MSI failed.\n");
+        }
+        d->msi->pirq = -1;
+        return ret;
+    }
+    return 0;
+}
+
+void pt_msi_disable(XenPCIPassthroughState *dev)
+{
+    PCIDevice *d = &dev->dev;
+    uint8_t gvec = 0;
+    uint32_t gflags = 0;
+    uint64_t addr = 0;
+    uint8_t e_device = 0;
+    uint8_t e_intx = 0;
+
+    msi_set_enable(dev, 0);
+
+    e_device = PCI_SLOT(dev->dev.devfn);
+    e_intx = pci_intx(dev);
+
+    if (dev->msi_trans_en) {
+        if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, dev->msi->pirq,
+                                    PT_IRQ_TYPE_MSI_TRANSLATE, 0,
+                                    e_device, e_intx, 0)) {
+            PT_LOG("Error: Unbinding pt irq for MSI-INTx failed!\n");
+            goto out;
+        }
+    } else if (!(dev->msi->flags & MSI_FLAG_UNINIT)) {
+        /* get vector, address, flags info, etc. */
+        gvec = dev->msi->data & 0xFF;
+        addr = (uint64_t)dev->msi->addr_hi << 32 | dev->msi->addr_lo;
+        gflags = __get_msi_gflags(dev->msi->data, addr);
+
+        PT_LOG("Unbind msi with pirq %x, gvec %x\n",
+                dev->msi->pirq, gvec);
+
+        if (xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec,
+                                        dev->msi->pirq, gflags)) {
+            PT_LOG("Error: Unbinding of MSI failed. [%02x:%02x.%x]\n",
+                   pci_bus_num(d->bus), PCI_SLOT(d->devfn),
+                   PCI_FUNC(d->devfn));
+            goto out;
+        }
+    }
+
+    if (dev->msi->pirq != -1) {
+        PT_LOG("Unmap msi with pirq %x\n", dev->msi->pirq);
+
+        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, dev->msi->pirq)) {
+            PT_LOG("Error: Unmapping of MSI failed. [%02x:%02x.%x]\n",
+                   pci_bus_num(d->bus), PCI_SLOT(d->devfn),
+                   PCI_FUNC(d->devfn));
+            goto out;
+        }
+    }
+
+out:
+    /* clear msi info */
+    dev->msi->flags = 0;
+    dev->msi->pirq = -1;
+    dev->msi_trans_en = 0;
+}
+
+/* MSI-INTx translation virtulization functions */
+int pt_enable_msi_translate(XenPCIPassthroughState* dev)
+{
+    uint8_t e_device = 0;
+    uint8_t e_intx = 0;
+
+    if (!(dev->msi && dev->msi_trans_cap)) {
+        return -1;
+    }
+
+    msi_set_enable(dev, 0);
+    dev->msi_trans_en = 0;
+
+    if (pt_msi_setup(dev)) {
+        PT_LOG("Error: MSI-INTx translation MSI setup failed, fallback\n");
+        return -1;
+    }
+
+    e_device = PCI_SLOT(dev->dev.devfn);
+    /* fix virtual interrupt pin to INTA# */
+    e_intx = pci_intx(dev);
+
+    if (xc_domain_bind_pt_irq(xen_xc, xen_domid, dev->msi->pirq,
+                              PT_IRQ_TYPE_MSI_TRANSLATE, 0,
+                              e_device, e_intx, 0)) {
+        PT_LOG("Error: MSI-INTx translation bind failed, fallback\n");
+
+        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, dev->msi->pirq)) {
+            PT_LOG("Error: Unmapping of MSI failed.\n");
+        }
+        dev->msi->pirq = -1;
+        return -1;
+    }
+
+    msi_set_enable(dev, 1);
+    dev->msi_trans_en = 1;
+
+    return 0;
+}
+
+void pt_disable_msi_translate(XenPCIPassthroughState *dev)
+{
+    uint8_t e_device = 0;
+    uint8_t e_intx = 0;
+
+    /* MSI_ENABLE bit should be disabed until the new handler is set */
+    msi_set_enable(dev, 0);
+
+    e_device = PCI_SLOT(dev->dev.devfn);
+    e_intx = pci_intx(dev);
+
+    if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, dev->msi->pirq,
+                                 PT_IRQ_TYPE_MSI_TRANSLATE, 0,
+                                 e_device, e_intx, 0)) {
+        PT_LOG("Error: Unbinding pt irq for MSI-INTx failed!\n");
+    }
+
+    if (dev->machine_irq) {
+        if (xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, dev->machine_irq,
+                                       0, e_device, e_intx)) {
+            PT_LOG("Error: Rebinding of interrupt failed!\n");
+        }
+    }
+
+    dev->msi_trans_en = 0;
+}
+
+/*********************************/
+/* MSI-X virtulization functions */
+
+static void mask_physical_msix_entry(XenPCIPassthroughState *dev,
+                                     int entry_nr, int mask)
+{
+    void *phys_off;
+
+    phys_off = dev->msix->phys_iomem_base + 16 * entry_nr + 12;
+    *(uint32_t *)phys_off = mask;
+}
+
+static int pt_msix_update_one(XenPCIPassthroughState *dev, int entry_nr)
+{
+    struct msix_entry_info *entry = &dev->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 ret;
+
+    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_TARGET_CPU_SHIFT) & 0xff);
+        if (!pirq) {
+            /* this probably identifies an misconfiguration of the guest,
+             * try the emulated path */
+            pirq = -1;
+        } else {
+            PT_LOG("pt_msix_update_one requested pirq = %d\n", pirq);
+        }
+    }
+
+    /* Check if this entry is already mapped */
+    if (entry->pirq == -1) {
+        ret = xc_physdev_map_pirq_msi(xen_xc, xen_domid, AUTO_ASSIGN, &pirq,
+                                      PCI_DEVFN(dev->real_device->dev,
+                                                dev->real_device->func),
+                                      dev->real_device->bus, entry_nr,
+                                      dev->msix->table_base);
+        if (ret) {
+            PT_LOG("Error: Mapping msix entry %x\n", entry_nr);
+            return ret;
+        }
+        entry->pirq = pirq;
+    }
+
+    PT_LOG("Update msix entry %x with pirq %x gvec %x\n",
+            entry_nr, pirq, gvec);
+
+    ret = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec, pirq, gflags,
+                                   dev->msix->mmio_base_addr);
+    if (ret) {
+        PT_LOG("Error: Updating msix irq info for entry %d\n", entry_nr);
+
+        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, entry->pirq)) {
+            PT_LOG("Error: Unmapping of MSI-X failed.\n");
+        }
+        entry->pirq = -1;
+        return ret;
+    }
+
+    entry->flags = 0;
+
+    return 0;
+}
+
+int pt_msix_update(XenPCIPassthroughState *dev)
+{
+    struct pt_msix_info *msix = dev->msix;
+    int i;
+
+    for (i = 0; i < msix->total_entries; i++) {
+        pt_msix_update_one(dev, i);
+    }
+
+    return 0;
+}
+
+void pt_msix_disable(XenPCIPassthroughState *dev)
+{
+    PCIDevice *d = &dev->dev;
+    uint8_t gvec = 0;
+    uint32_t gflags = 0;
+    uint64_t addr = 0;
+    int i = 0;
+    struct msix_entry_info *entry = NULL;
+
+    msix_set_enable(dev, 0);
+
+    for ( i = 0; i < dev->msix->total_entries; i++ ) {
+        entry = &dev->msix->msix_entry[i];
+
+        if (entry->pirq == -1) {
+            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("Unbind msix with pirq %x, gvec %x\n",
+                entry->pirq, gvec);
+
+        if (xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec,
+                                        entry->pirq, gflags)) {
+            PT_LOG("Error: Unbinding of MSI-X failed. [%02x:%02x.%x]\n",
+                   pci_bus_num(d->bus), PCI_SLOT(d->devfn),
+                   PCI_FUNC(d->devfn));
+        } else {
+            PT_LOG("Unmap msix with pirq %x\n", entry->pirq);
+
+            if (xc_physdev_unmap_pirq(xen_xc, xen_domid, entry->pirq)) {
+                PT_LOG("Error: Unmapping of MSI-X failed. [%02x:%02x.%x]\n",
+                       pci_bus_num(d->bus),
+                       PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
+            }
+        }
+        /* clear msi-x info */
+        entry->pirq = -1;
+        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 != -1) {
+            ret = xc_domain_unbind_pt_irq(xen_xc, xen_domid, entry->pirq,
+                                          PT_IRQ_TYPE_MSI, 0, 0, 0, 0);
+            if (ret) {
+                PT_LOG("Error: 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)
+{
+    PT_LOG("Error: 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 *dev = (XenPCIPassthroughState *)opaque;
+    XenPTMSIX *msix = dev->msix;
+    XenMSIXEntry *entry;
+    int entry_nr, offset;
+    void *phys_off;
+    uint32_t vec_ctrl;
+
+    if (addr % 4) {
+        PT_LOG("Error: Unaligned dword access to MSI-X table, "
+                "addr %016"PRIx64"\n", addr);
+        return;
+    }
+
+    PT_LOG("addr: "TARGET_FMT_plx", val: %#x\n", addr, val);
+
+    // FIXME/TODO I maybe have to do the same in readl !
+    /* entry_nr = (addr - msix->mmio_base_addr) / 16; */
+    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 = dev->msix->phys_iomem_base + 16 * entry_nr + 12;
+    vec_ctrl = *(uint32_t *)phys_off;
+
+    if (offset != 3 && msix->enabled && !(vec_ctrl & 0x1)) {
+        PT_LOG("Error: Can't update msix entry %d since MSI-X is already "
+                "function.\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(dev, entry_nr);
+        }
+        mask_physical_msix_entry(dev, 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)
+{
+    PT_LOG("Error: 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 *dev = (XenPCIPassthroughState *)opaque;
+    struct pt_msix_info *msix = dev->msix;
+    int entry_nr, offset;
+
+    if (addr % 4) {
+        PT_LOG("Error: Unaligned dword access to MSI-X table, "
+                "addr %016"PRIx64"\n", addr);
+        return 0;
+    }
+
+    PT_LOG("addr: "TARGET_FMT_plx"\n", addr);
+
+    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 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 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 *dev, int pos)
+{
+    uint8_t id;
+    uint16_t control;
+    int i, total_entries, table_off, bar_index;
+    HostPCIDevice *pd = dev->real_device;
+    int fd;
+
+    id = host_pci_read_byte(pd, pos + PCI_CAP_LIST_ID);
+
+    if (id != PCI_CAP_ID_MSIX) {
+        PT_LOG("Error: Invalid id %#x pos %#x\n", id, pos);
+        return -1;
+    }
+
+    control = host_pci_read_word(pd, pos + 2);
+    total_entries = control & 0x7ff;
+    total_entries += 1;
+
+    dev->msix = g_malloc0(sizeof (struct pt_msix_info)
+                          + total_entries * sizeof (struct msix_entry_info));
+
+    dev->msix->total_entries = total_entries;
+    for (i = 0; i < total_entries; i++) {
+        dev->msix->msix_entry[i].pirq = -1;
+    }
+
+    dev->msix->mmio_index =
+        cpu_register_io_memory(pci_msix_read, pci_msix_write,
+                               dev, DEVICE_NATIVE_ENDIAN);
+
+    table_off = host_pci_read_long(pd, pos + PCI_MSIX_TABLE);
+    bar_index = dev->msix->bar_index = table_off & PCI_MSIX_FLAGS_BIRMASK;
+    table_off = dev->msix->table_off = table_off & ~PCI_MSIX_FLAGS_BIRMASK;
+    dev->msix->table_base =
+        pt_pci_base_addr(dev->real_device->base_addr[bar_index]);
+    PT_LOG("get MSI-X table bar base %#"PRIx64"\n", dev->msix->table_base);
+
+    fd = open("/dev/mem", O_RDWR);
+    if (fd == -1) {
+        PT_LOG("Error: Can't open /dev/mem: %s\n", strerror(errno));
+        goto error_out;
+    }
+    PT_LOG("table_off = %#x, total_entries = %d\n", table_off, total_entries);
+    dev->msix->table_offset_adjust = table_off & 0x0fff;
+    dev->msix->phys_iomem_base = mmap(0,
+                                      total_entries * 16
+                                      + dev->msix->table_offset_adjust,
+                                      PROT_WRITE | PROT_READ,
+                                      MAP_SHARED | MAP_LOCKED,
+                                      fd,
+                                      dev->msix->table_base + table_off
+                                      - dev->msix->table_offset_adjust);
+    if (dev->msix->phys_iomem_base == MAP_FAILED) {
+        PT_LOG("Error: Can't map physical MSI-X table: %s\n", strerror(errno));
+        close(fd);
+        goto error_out;
+    }
+    dev->msix->phys_iomem_base = ((char *)dev->msix->phys_iomem_base +
+                                  dev->msix->table_offset_adjust);
+
+    close(fd);
+
+    PT_LOG("mapping physical MSI-X table to %p\n", dev->msix->phys_iomem_base);
+    return 0;
+
+error_out:
+    g_free(dev->msix);
+    dev->msix = NULL;
+    return -1;
+}
+
+#if 0
+static void pt_msix_delete(XenPCIPassthroughState *dev)
+{
+    /* unmap the MSI-X memory mapped register area */
+    if (dev->msix->phys_iomem_base)
+    {
+        PT_LOG("unmapping physical MSI-X table from %lx\n",
+           (unsigned long)dev->msix->phys_iomem_base);
+        munmap(dev->msix->phys_iomem_base, dev->msix->total_entries * 16 +
+           dev->msix->table_offset_adjust);
+    }
+
+    if (dev->msix->mmio_index > 0)
+    {
+        cpu_unregister_io_memory(dev->msix->mmio_index);
+    }
+
+
+    free(dev->msix);
+}
+#endif
-- 
Anthony PERARD

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [Qemu-devel] [PATCH RFC V1 11/11] config/make: Introduce --enable-xen-pci-passthrough, built it.
  2011-10-04 14:51 ` Anthony PERARD
@ 2011-10-04 14:51   ` Anthony PERARD
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 Makefile.target |    7 +++++++
 configure       |   21 +++++++++++++++++++++
 2 files changed, 28 insertions(+), 0 deletions(-)

diff --git a/Makefile.target b/Makefile.target
index f708453..b5fbc18 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -208,6 +208,13 @@ 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
+obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough_helpers.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 =
 ifeq ($(CONFIG_KVM), y)
diff --git a/configure b/configure
index 0875f95..b90cfe1 100755
--- a/configure
+++ b/configure
@@ -127,6 +127,7 @@ vnc_png=""
 vnc_thread="no"
 xen=""
 xen_ctrl_version=""
+xen_pci_passthrough=""
 linux_aio=""
 attr=""
 xfs=""
@@ -635,6 +636,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"
@@ -972,6 +977,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"
@@ -1335,6 +1342,17 @@ EOF
   fi
 fi
 
+if test "$xen_pci_passthrough" != "no"; then
+  if test "$xen" = "yes"; then
+    xen_pci_passthrough=yes
+  else
+    if test "$xen_pci_passthrough" = "yes"; then
+      feature_not_found "Xen PCI Passthrough without Xen"
+    fi
+    xen_pci_passthrough=no
+  fi
+fi
+
 ##########################################
 # pkg-config probe
 
@@ -3378,6 +3396,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

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCH RFC V1 11/11] config/make: Introduce --enable-xen-pci-passthrough, built it.
@ 2011-10-04 14:51   ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-04 14:51 UTC (permalink / raw)
  To: QEMU-devel; +Cc: Anthony PERARD, Alex Williamson, Xen Devel, Stefano Stabellini

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
 Makefile.target |    7 +++++++
 configure       |   21 +++++++++++++++++++++
 2 files changed, 28 insertions(+), 0 deletions(-)

diff --git a/Makefile.target b/Makefile.target
index f708453..b5fbc18 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -208,6 +208,13 @@ 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
+obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough_helpers.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 =
 ifeq ($(CONFIG_KVM), y)
diff --git a/configure b/configure
index 0875f95..b90cfe1 100755
--- a/configure
+++ b/configure
@@ -127,6 +127,7 @@ vnc_png=""
 vnc_thread="no"
 xen=""
 xen_ctrl_version=""
+xen_pci_passthrough=""
 linux_aio=""
 attr=""
 xfs=""
@@ -635,6 +636,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"
@@ -972,6 +977,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"
@@ -1335,6 +1342,17 @@ EOF
   fi
 fi
 
+if test "$xen_pci_passthrough" != "no"; then
+  if test "$xen" = "yes"; then
+    xen_pci_passthrough=yes
+  else
+    if test "$xen_pci_passthrough" = "yes"; then
+      feature_not_found "Xen PCI Passthrough without Xen"
+    fi
+    xen_pci_passthrough=no
+  fi
+fi
+
 ##########################################
 # pkg-config probe
 
@@ -3378,6 +3396,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

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
  2011-10-04 14:51 ` Anthony PERARD
@ 2011-10-04 14:58   ` Avi Kivity
  -1 siblings, 0 replies; 66+ messages in thread
From: Avi Kivity @ 2011-10-04 14:58 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On 10/04/2011 04:51 PM, Anthony PERARD wrote:
> Hi all,
>
> This patch series introduce the PCI passthrough for Xen.
>
> First, we have HostPCIDevice that help to access one PCI device of the host.
>
> Then, there are several additions in the QEMU code. One is qemu_run_one_timer
> to run a specific timer. It is used by PCI passthrough to run a timer about
> power management. Another is 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.
>
> I'm still working on 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".
>
> There is some minor things missing:
>   - copyright header
>   - PCI IO space multiplexer
>
>

We also have pci passthrough in qemu-kvm (I think based on the same 
Neocleus code).  Rather than having two pci assignment implementations, 
I think we should have just one, with the differences (programming the 
hypervisor) abstracted at that level.

-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
@ 2011-10-04 14:58   ` Avi Kivity
  0 siblings, 0 replies; 66+ messages in thread
From: Avi Kivity @ 2011-10-04 14:58 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On 10/04/2011 04:51 PM, Anthony PERARD wrote:
> Hi all,
>
> This patch series introduce the PCI passthrough for Xen.
>
> First, we have HostPCIDevice that help to access one PCI device of the host.
>
> Then, there are several additions in the QEMU code. One is qemu_run_one_timer
> to run a specific timer. It is used by PCI passthrough to run a timer about
> power management. Another is 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.
>
> I'm still working on 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".
>
> There is some minor things missing:
>   - copyright header
>   - PCI IO space multiplexer
>
>

We also have pci passthrough in qemu-kvm (I think based on the same 
Neocleus code).  Rather than having two pci assignment implementations, 
I think we should have just one, with the differences (programming the 
hypervisor) abstracted at that level.

-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
  2011-10-04 14:58   ` Avi Kivity
@ 2011-10-04 15:01     ` Anthony Liguori
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony Liguori @ 2011-10-04 15:01 UTC (permalink / raw)
  To: Avi Kivity
  Cc: Anthony PERARD, Alex Williamson, Xen Devel, QEMU-devel,
	Stefano Stabellini

On 10/04/2011 09:58 AM, Avi Kivity wrote:
> On 10/04/2011 04:51 PM, Anthony PERARD wrote:
>> Hi all,
>>
>> This patch series introduce the PCI passthrough for Xen.
>>
>> First, we have HostPCIDevice that help to access one PCI device of the host.
>>
>> Then, there are several additions in the QEMU code. One is qemu_run_one_timer
>> to run a specific timer. It is used by PCI passthrough to run a timer about
>> power management. Another is 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.
>>
>> I'm still working on 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".
>>
>> There is some minor things missing:
>> - copyright header
>> - PCI IO space multiplexer
>>
>>
>
> We also have pci passthrough in qemu-kvm (I think based on the same Neocleus
> code). Rather than having two pci assignment implementations, I think we should
> have just one, with the differences (programming the hypervisor) abstracted at
> that level.

I agree in principle but how close is qemu-kvm pci passthrough to a mergable 
state?  Would it make sense to merge the Xen code first and then abstract it?

Regards,

Anthony Liguori

>

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
@ 2011-10-04 15:01     ` Anthony Liguori
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony Liguori @ 2011-10-04 15:01 UTC (permalink / raw)
  To: Avi Kivity
  Cc: Anthony PERARD, Alex Williamson, Xen Devel, QEMU-devel,
	Stefano Stabellini

On 10/04/2011 09:58 AM, Avi Kivity wrote:
> On 10/04/2011 04:51 PM, Anthony PERARD wrote:
>> Hi all,
>>
>> This patch series introduce the PCI passthrough for Xen.
>>
>> First, we have HostPCIDevice that help to access one PCI device of the host.
>>
>> Then, there are several additions in the QEMU code. One is qemu_run_one_timer
>> to run a specific timer. It is used by PCI passthrough to run a timer about
>> power management. Another is 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.
>>
>> I'm still working on 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".
>>
>> There is some minor things missing:
>> - copyright header
>> - PCI IO space multiplexer
>>
>>
>
> We also have pci passthrough in qemu-kvm (I think based on the same Neocleus
> code). Rather than having two pci assignment implementations, I think we should
> have just one, with the differences (programming the hypervisor) abstracted at
> that level.

I agree in principle but how close is qemu-kvm pci passthrough to a mergable 
state?  Would it make sense to merge the Xen code first and then abstract it?

Regards,

Anthony Liguori

>

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
  2011-10-04 15:01     ` Anthony Liguori
@ 2011-10-04 15:05       ` Stefano Stabellini
  -1 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-04 15:05 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: Xen Devel, Stefano Stabellini, QEMU-devel, Alex Williamson,
	Avi Kivity, Anthony Perard

On Tue, 4 Oct 2011, Anthony Liguori wrote:
> On 10/04/2011 09:58 AM, Avi Kivity wrote:
> > On 10/04/2011 04:51 PM, Anthony PERARD wrote:
> >> Hi all,
> >>
> >> This patch series introduce the PCI passthrough for Xen.
> >>
> >> First, we have HostPCIDevice that help to access one PCI device of the host.
> >>
> >> Then, there are several additions in the QEMU code. One is qemu_run_one_timer
> >> to run a specific timer. It is used by PCI passthrough to run a timer about
> >> power management. Another is 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.
> >>
> >> I'm still working on 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".
> >>
> >> There is some minor things missing:
> >> - copyright header
> >> - PCI IO space multiplexer
> >>
> >>
> >
> > We also have pci passthrough in qemu-kvm (I think based on the same Neocleus
> > code). Rather than having two pci assignment implementations, I think we should
> > have just one, with the differences (programming the hypervisor) abstracted at
> > that level.
> 
> I agree in principle but how close is qemu-kvm pci passthrough to a mergable 
> state?  Would it make sense to merge the Xen code first and then abstract it?

I think it should be fairly easy to abstract the current xen code: just
a matter of providing memory, ioport and interrupt mapping functions.

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
@ 2011-10-04 15:05       ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-04 15:05 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: Xen Devel, Stefano Stabellini, QEMU-devel, Alex Williamson,
	Avi Kivity, Anthony Perard

On Tue, 4 Oct 2011, Anthony Liguori wrote:
> On 10/04/2011 09:58 AM, Avi Kivity wrote:
> > On 10/04/2011 04:51 PM, Anthony PERARD wrote:
> >> Hi all,
> >>
> >> This patch series introduce the PCI passthrough for Xen.
> >>
> >> First, we have HostPCIDevice that help to access one PCI device of the host.
> >>
> >> Then, there are several additions in the QEMU code. One is qemu_run_one_timer
> >> to run a specific timer. It is used by PCI passthrough to run a timer about
> >> power management. Another is 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.
> >>
> >> I'm still working on 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".
> >>
> >> There is some minor things missing:
> >> - copyright header
> >> - PCI IO space multiplexer
> >>
> >>
> >
> > We also have pci passthrough in qemu-kvm (I think based on the same Neocleus
> > code). Rather than having two pci assignment implementations, I think we should
> > have just one, with the differences (programming the hypervisor) abstracted at
> > that level.
> 
> I agree in principle but how close is qemu-kvm pci passthrough to a mergable 
> state?  Would it make sense to merge the Xen code first and then abstract it?

I think it should be fairly easy to abstract the current xen code: just
a matter of providing memory, ioport and interrupt mapping functions.

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
  2011-10-04 15:05       ` Stefano Stabellini
@ 2011-10-04 16:33         ` Alex Williamson
  -1 siblings, 0 replies; 66+ messages in thread
From: Alex Williamson @ 2011-10-04 16:33 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: Anthony Perard, Xen Devel, Avi Kivity, QEMU-devel

On Tue, 2011-10-04 at 16:05 +0100, Stefano Stabellini wrote:
> On Tue, 4 Oct 2011, Anthony Liguori wrote:
> > On 10/04/2011 09:58 AM, Avi Kivity wrote:
> > > On 10/04/2011 04:51 PM, Anthony PERARD wrote:
> > >> Hi all,
> > >>
> > >> This patch series introduce the PCI passthrough for Xen.
> > >>
> > >> First, we have HostPCIDevice that help to access one PCI device of the host.
> > >>
> > >> Then, there are several additions in the QEMU code. One is qemu_run_one_timer
> > >> to run a specific timer. It is used by PCI passthrough to run a timer about
> > >> power management. Another is 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.
> > >>
> > >> I'm still working on 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".
> > >>
> > >> There is some minor things missing:
> > >> - copyright header
> > >> - PCI IO space multiplexer
> > >>
> > >>
> > >
> > > We also have pci passthrough in qemu-kvm (I think based on the same Neocleus
> > > code). Rather than having two pci assignment implementations, I think we should
> > > have just one, with the differences (programming the hypervisor) abstracted at
> > > that level.
> > 
> > I agree in principle but how close is qemu-kvm pci passthrough to a mergable 
> > state?  Would it make sense to merge the Xen code first and then abstract it?
> 
> I think it should be fairly easy to abstract the current xen code: just
> a matter of providing memory, ioport and interrupt mapping functions.

I thought we were potentially looking at vfio as a convergence point.
I'm still a bit off from having a vfio re-write ready to submit, but is
this still a possibility?  Thanks,

Alex

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
@ 2011-10-04 16:33         ` Alex Williamson
  0 siblings, 0 replies; 66+ messages in thread
From: Alex Williamson @ 2011-10-04 16:33 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: Anthony Perard, Xen Devel, Avi Kivity, QEMU-devel

On Tue, 2011-10-04 at 16:05 +0100, Stefano Stabellini wrote:
> On Tue, 4 Oct 2011, Anthony Liguori wrote:
> > On 10/04/2011 09:58 AM, Avi Kivity wrote:
> > > On 10/04/2011 04:51 PM, Anthony PERARD wrote:
> > >> Hi all,
> > >>
> > >> This patch series introduce the PCI passthrough for Xen.
> > >>
> > >> First, we have HostPCIDevice that help to access one PCI device of the host.
> > >>
> > >> Then, there are several additions in the QEMU code. One is qemu_run_one_timer
> > >> to run a specific timer. It is used by PCI passthrough to run a timer about
> > >> power management. Another is 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.
> > >>
> > >> I'm still working on 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".
> > >>
> > >> There is some minor things missing:
> > >> - copyright header
> > >> - PCI IO space multiplexer
> > >>
> > >>
> > >
> > > We also have pci passthrough in qemu-kvm (I think based on the same Neocleus
> > > code). Rather than having two pci assignment implementations, I think we should
> > > have just one, with the differences (programming the hypervisor) abstracted at
> > > that level.
> > 
> > I agree in principle but how close is qemu-kvm pci passthrough to a mergable 
> > state?  Would it make sense to merge the Xen code first and then abstract it?
> 
> I think it should be fairly easy to abstract the current xen code: just
> a matter of providing memory, ioport and interrupt mapping functions.

I thought we were potentially looking at vfio as a convergence point.
I'm still a bit off from having a vfio re-write ready to submit, but is
this still a possibility?  Thanks,

Alex

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
  2011-10-04 16:33         ` Alex Williamson
@ 2011-10-04 16:56           ` Stefano Stabellini
  -1 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-04 16:56 UTC (permalink / raw)
  To: Alex Williamson
  Cc: Xen Devel, Stefano Stabellini, QEMU-devel, Avi Kivity, Anthony Perard

On Tue, 4 Oct 2011, Alex Williamson wrote:
> On Tue, 2011-10-04 at 16:05 +0100, Stefano Stabellini wrote:
> > On Tue, 4 Oct 2011, Anthony Liguori wrote:
> > > On 10/04/2011 09:58 AM, Avi Kivity wrote:
> > > > On 10/04/2011 04:51 PM, Anthony PERARD wrote:
> > > >> Hi all,
> > > >>
> > > >> This patch series introduce the PCI passthrough for Xen.
> > > >>
> > > >> First, we have HostPCIDevice that help to access one PCI device of the host.
> > > >>
> > > >> Then, there are several additions in the QEMU code. One is qemu_run_one_timer
> > > >> to run a specific timer. It is used by PCI passthrough to run a timer about
> > > >> power management. Another is 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.
> > > >>
> > > >> I'm still working on 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".
> > > >>
> > > >> There is some minor things missing:
> > > >> - copyright header
> > > >> - PCI IO space multiplexer
> > > >>
> > > >>
> > > >
> > > > We also have pci passthrough in qemu-kvm (I think based on the same Neocleus
> > > > code). Rather than having two pci assignment implementations, I think we should
> > > > have just one, with the differences (programming the hypervisor) abstracted at
> > > > that level.
> > > 
> > > I agree in principle but how close is qemu-kvm pci passthrough to a mergable 
> > > state?  Would it make sense to merge the Xen code first and then abstract it?
> > 
> > I think it should be fairly easy to abstract the current xen code: just
> > a matter of providing memory, ioport and interrupt mapping functions.
> 
> I thought we were potentially looking at vfio as a convergence point.
> I'm still a bit off from having a vfio re-write ready to submit, but is
> this still a possibility?  Thanks,
 

As I said at KVM Forum 2011, I am OK with vfio: it is a nice thing to
have in the long term.
However having seen how much time it took to make this code work
reliably, I think is going to take some time before we have a stable
vfio implementation.
Considering that we have just introduced upstream qemu to xen-unstable,
I would like to reach feature parity with the old qemu-xen sooner rather
than later, so that we can kill the qemu-xen fork once and for all. In
order to reach feature parity we need PCI passthrough.  Once that is
done, we could gradually modify this code to introduce vfio support,
throwing away what is not necessary and keeping what can be reused.
If you are interested, we can easily make this code work for KVM too,
otherwise we can keep it Xen specific and wait for vfio to be ready.

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
@ 2011-10-04 16:56           ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-04 16:56 UTC (permalink / raw)
  To: Alex Williamson
  Cc: Xen Devel, Stefano Stabellini, QEMU-devel, Avi Kivity, Anthony Perard

On Tue, 4 Oct 2011, Alex Williamson wrote:
> On Tue, 2011-10-04 at 16:05 +0100, Stefano Stabellini wrote:
> > On Tue, 4 Oct 2011, Anthony Liguori wrote:
> > > On 10/04/2011 09:58 AM, Avi Kivity wrote:
> > > > On 10/04/2011 04:51 PM, Anthony PERARD wrote:
> > > >> Hi all,
> > > >>
> > > >> This patch series introduce the PCI passthrough for Xen.
> > > >>
> > > >> First, we have HostPCIDevice that help to access one PCI device of the host.
> > > >>
> > > >> Then, there are several additions in the QEMU code. One is qemu_run_one_timer
> > > >> to run a specific timer. It is used by PCI passthrough to run a timer about
> > > >> power management. Another is 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.
> > > >>
> > > >> I'm still working on 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".
> > > >>
> > > >> There is some minor things missing:
> > > >> - copyright header
> > > >> - PCI IO space multiplexer
> > > >>
> > > >>
> > > >
> > > > We also have pci passthrough in qemu-kvm (I think based on the same Neocleus
> > > > code). Rather than having two pci assignment implementations, I think we should
> > > > have just one, with the differences (programming the hypervisor) abstracted at
> > > > that level.
> > > 
> > > I agree in principle but how close is qemu-kvm pci passthrough to a mergable 
> > > state?  Would it make sense to merge the Xen code first and then abstract it?
> > 
> > I think it should be fairly easy to abstract the current xen code: just
> > a matter of providing memory, ioport and interrupt mapping functions.
> 
> I thought we were potentially looking at vfio as a convergence point.
> I'm still a bit off from having a vfio re-write ready to submit, but is
> this still a possibility?  Thanks,
 

As I said at KVM Forum 2011, I am OK with vfio: it is a nice thing to
have in the long term.
However having seen how much time it took to make this code work
reliably, I think is going to take some time before we have a stable
vfio implementation.
Considering that we have just introduced upstream qemu to xen-unstable,
I would like to reach feature parity with the old qemu-xen sooner rather
than later, so that we can kill the qemu-xen fork once and for all. In
order to reach feature parity we need PCI passthrough.  Once that is
done, we could gradually modify this code to introduce vfio support,
throwing away what is not necessary and keeping what can be reused.
If you are interested, we can easily make this code work for KVM too,
otherwise we can keep it Xen specific and wait for vfio to be ready.

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
  2011-10-04 16:33         ` Alex Williamson
@ 2011-10-04 17:01           ` Avi Kivity
  -1 siblings, 0 replies; 66+ messages in thread
From: Avi Kivity @ 2011-10-04 17:01 UTC (permalink / raw)
  To: Alex Williamson; +Cc: Anthony Perard, Xen Devel, QEMU-devel, Stefano Stabellini

On 10/04/2011 06:33 PM, Alex Williamson wrote:
> I thought we were potentially looking at vfio as a convergence point.
> I'm still a bit off from having a vfio re-write ready to submit, but is
> this still a possibility?  Thanks,
>

vfio leaves out users of current and past kernels; relying on it would 
force me to maintain qemu-kvm for a while.

-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
@ 2011-10-04 17:01           ` Avi Kivity
  0 siblings, 0 replies; 66+ messages in thread
From: Avi Kivity @ 2011-10-04 17:01 UTC (permalink / raw)
  To: Alex Williamson; +Cc: Anthony Perard, Xen Devel, QEMU-devel, Stefano Stabellini

On 10/04/2011 06:33 PM, Alex Williamson wrote:
> I thought we were potentially looking at vfio as a convergence point.
> I'm still a bit off from having a vfio re-write ready to submit, but is
> this still a possibility?  Thanks,
>

vfio leaves out users of current and past kernels; relying on it would 
force me to maintain qemu-kvm for a while.

-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
  2011-10-04 15:01     ` Anthony Liguori
@ 2011-10-04 17:03       ` Avi Kivity
  -1 siblings, 0 replies; 66+ messages in thread
From: Avi Kivity @ 2011-10-04 17:03 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: Anthony PERARD, Alex Williamson, Xen Devel, QEMU-devel,
	Stefano Stabellini

On 10/04/2011 05:01 PM, Anthony Liguori wrote:
>> We also have pci passthrough in qemu-kvm (I think based on the same 
>> Neocleus
>> code). Rather than having two pci assignment implementations, I think 
>> we should
>> have just one, with the differences (programming the hypervisor) 
>> abstracted at
>> that level.
>
>
> I agree in principle but how close is qemu-kvm pci passthrough to a 
> mergable state?  Would it make sense to merge the Xen code first and 
> then abstract it?

Merging either implementation and abstracting it would risk regressions 
in the other.

How about merging both, with the ABIs (command line and qmp) tagged as 
experimental, and then doing a merge in the same style as 
i386+x86_64->x86 or the two kvm implementations in qemu?  We can pick 
one implementation as the merge target and port fixes from the other.

-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
@ 2011-10-04 17:03       ` Avi Kivity
  0 siblings, 0 replies; 66+ messages in thread
From: Avi Kivity @ 2011-10-04 17:03 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: Anthony PERARD, Alex Williamson, Xen Devel, QEMU-devel,
	Stefano Stabellini

On 10/04/2011 05:01 PM, Anthony Liguori wrote:
>> We also have pci passthrough in qemu-kvm (I think based on the same 
>> Neocleus
>> code). Rather than having two pci assignment implementations, I think 
>> we should
>> have just one, with the differences (programming the hypervisor) 
>> abstracted at
>> that level.
>
>
> I agree in principle but how close is qemu-kvm pci passthrough to a 
> mergable state?  Would it make sense to merge the Xen code first and 
> then abstract it?

Merging either implementation and abstracting it would risk regressions 
in the other.

How about merging both, with the ABIs (command line and qmp) tagged as 
experimental, and then doing a merge in the same style as 
i386+x86_64->x86 or the two kvm implementations in qemu?  We can pick 
one implementation as the merge target and port fixes from the other.

-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
  2011-10-04 15:01     ` Anthony Liguori
@ 2011-10-04 17:21       ` Jan Kiszka
  -1 siblings, 0 replies; 66+ messages in thread
From: Jan Kiszka @ 2011-10-04 17:21 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: Xen Devel, Stefano Stabellini, QEMU-devel, Alex Williamson,
	Avi Kivity, Anthony PERARD

[-- Attachment #1: Type: text/plain, Size: 2436 bytes --]

On 2011-10-04 17:01, Anthony Liguori wrote:
> On 10/04/2011 09:58 AM, Avi Kivity wrote:
>> On 10/04/2011 04:51 PM, Anthony PERARD wrote:
>>> Hi all,
>>>
>>> This patch series introduce the PCI passthrough for Xen.
>>>
>>> First, we have HostPCIDevice that help to access one PCI device of
>>> the host.
>>>
>>> Then, there are several additions in the QEMU code. One is
>>> qemu_run_one_timer
>>> to run a specific timer. It is used by PCI passthrough to run a timer
>>> about
>>> power management. Another is 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.
>>>
>>> I'm still working on 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".
>>>
>>> There is some minor things missing:
>>> - copyright header
>>> - PCI IO space multiplexer
>>>
>>>
>>
>> We also have pci passthrough in qemu-kvm (I think based on the same
>> Neocleus
>> code). Rather than having two pci assignment implementations, I think
>> we should
>> have just one, with the differences (programming the hypervisor)
>> abstracted at
>> that level.
> 
> I agree in principle but how close is qemu-kvm pci passthrough to a
> mergable state?  Would it make sense to merge the Xen code first and
> then abstract it?

What is missing to get qemu-kvm device assignment ready:
 - MSI/MSI-X refactoring in QEMU (specifically config notifiers, also
   relevant for virtio)
 - switch device assignment to generic MSI/MSI-X
 - get in-kernel irqchip support upstream
 - minor code cleanups

I was hoping to complete this for 1.0, but it looks unrealistic now.
Maybe I'll find the time for the MSI stuff at least.

From a first glance at these patches, I think there are already some
synergies in the host-pci access layer. And I bet the Xen bits could
also make use of our MSI/MSI-X code once it's generalized. But that will
be a bit work for them (based on my experience with qemu-kvm).

Jan


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
@ 2011-10-04 17:21       ` Jan Kiszka
  0 siblings, 0 replies; 66+ messages in thread
From: Jan Kiszka @ 2011-10-04 17:21 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: Xen Devel, Stefano Stabellini, QEMU-devel, Alex Williamson,
	Avi Kivity, Anthony PERARD

[-- Attachment #1: Type: text/plain, Size: 2436 bytes --]

On 2011-10-04 17:01, Anthony Liguori wrote:
> On 10/04/2011 09:58 AM, Avi Kivity wrote:
>> On 10/04/2011 04:51 PM, Anthony PERARD wrote:
>>> Hi all,
>>>
>>> This patch series introduce the PCI passthrough for Xen.
>>>
>>> First, we have HostPCIDevice that help to access one PCI device of
>>> the host.
>>>
>>> Then, there are several additions in the QEMU code. One is
>>> qemu_run_one_timer
>>> to run a specific timer. It is used by PCI passthrough to run a timer
>>> about
>>> power management. Another is 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.
>>>
>>> I'm still working on 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".
>>>
>>> There is some minor things missing:
>>> - copyright header
>>> - PCI IO space multiplexer
>>>
>>>
>>
>> We also have pci passthrough in qemu-kvm (I think based on the same
>> Neocleus
>> code). Rather than having two pci assignment implementations, I think
>> we should
>> have just one, with the differences (programming the hypervisor)
>> abstracted at
>> that level.
> 
> I agree in principle but how close is qemu-kvm pci passthrough to a
> mergable state?  Would it make sense to merge the Xen code first and
> then abstract it?

What is missing to get qemu-kvm device assignment ready:
 - MSI/MSI-X refactoring in QEMU (specifically config notifiers, also
   relevant for virtio)
 - switch device assignment to generic MSI/MSI-X
 - get in-kernel irqchip support upstream
 - minor code cleanups

I was hoping to complete this for 1.0, but it looks unrealistic now.
Maybe I'll find the time for the MSI stuff at least.

From a first glance at these patches, I think there are already some
synergies in the host-pci access layer. And I bet the Xen bits could
also make use of our MSI/MSI-X code once it's generalized. But that will
be a bit work for them (based on my experience with qemu-kvm).

Jan


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [PATCH RFC V1 02/11] qemu-timer: Introduce qemu_run_one_timer
  2011-10-04 14:51   ` Anthony PERARD
@ 2011-10-04 17:27     ` Jan Kiszka
  -1 siblings, 0 replies; 66+ messages in thread
From: Jan Kiszka @ 2011-10-04 17:27 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

[-- Attachment #1: Type: text/plain, Size: 1185 bytes --]

On 2011-10-04 16:51, Anthony PERARD wrote:
> Used by the Xen PCI Passthrough code to run the timer about the power
> state transition.
> 
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
> ---
>  qemu-timer.c |   15 +++++++++++++++
>  qemu-timer.h |    3 +++
>  2 files changed, 18 insertions(+), 0 deletions(-)
> 
> diff --git a/qemu-timer.c b/qemu-timer.c
> index 46dd483..15e659b 100644
> --- a/qemu-timer.c
> +++ b/qemu-timer.c
> @@ -1163,3 +1163,18 @@ int qemu_calculate_timeout(void)
>      return 1000;
>  }
>  
> +/* run the specified timer */
> +void qemu_run_one_timer(QEMUTimer *ts)
> +{
> +    uint64_t current_time;
> +
> +    /* remove timer from the list before calling the callback */
> +    qemu_del_timer(ts);
> +
> +    while ((current_time = qemu_get_clock_ms(rt_clock)) < ts->expire_time)
> +        /* sleep until the expire time */
> +        usleep((ts->expire_time - current_time) * 1000);
> +
> +    /* run the callback */
> +    ts->cb(ts->opaque);
> +}

This looks funny. I can't imagine that this could ever fit into the
standard (asynchronous) QEMU execution model for I/O. Keep it private to
Xen?

Jan


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCH RFC V1 02/11] qemu-timer: Introduce qemu_run_one_timer
@ 2011-10-04 17:27     ` Jan Kiszka
  0 siblings, 0 replies; 66+ messages in thread
From: Jan Kiszka @ 2011-10-04 17:27 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

[-- Attachment #1: Type: text/plain, Size: 1185 bytes --]

On 2011-10-04 16:51, Anthony PERARD wrote:
> Used by the Xen PCI Passthrough code to run the timer about the power
> state transition.
> 
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
> ---
>  qemu-timer.c |   15 +++++++++++++++
>  qemu-timer.h |    3 +++
>  2 files changed, 18 insertions(+), 0 deletions(-)
> 
> diff --git a/qemu-timer.c b/qemu-timer.c
> index 46dd483..15e659b 100644
> --- a/qemu-timer.c
> +++ b/qemu-timer.c
> @@ -1163,3 +1163,18 @@ int qemu_calculate_timeout(void)
>      return 1000;
>  }
>  
> +/* run the specified timer */
> +void qemu_run_one_timer(QEMUTimer *ts)
> +{
> +    uint64_t current_time;
> +
> +    /* remove timer from the list before calling the callback */
> +    qemu_del_timer(ts);
> +
> +    while ((current_time = qemu_get_clock_ms(rt_clock)) < ts->expire_time)
> +        /* sleep until the expire time */
> +        usleep((ts->expire_time - current_time) * 1000);
> +
> +    /* run the callback */
> +    ts->cb(ts->opaque);
> +}

This looks funny. I can't imagine that this could ever fit into the
standard (asynchronous) QEMU execution model for I/O. Keep it private to
Xen?

Jan


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
  2011-10-04 17:01           ` Avi Kivity
@ 2011-10-04 17:41             ` Jan Kiszka
  -1 siblings, 0 replies; 66+ messages in thread
From: Jan Kiszka @ 2011-10-04 17:41 UTC (permalink / raw)
  To: Avi Kivity
  Cc: Anthony Perard, Alex Williamson, Xen Devel, QEMU-devel,
	Stefano Stabellini

[-- Attachment #1: Type: text/plain, Size: 769 bytes --]

On 2011-10-04 19:01, Avi Kivity wrote:
> On 10/04/2011 06:33 PM, Alex Williamson wrote:
>> I thought we were potentially looking at vfio as a convergence point.
>> I'm still a bit off from having a vfio re-write ready to submit, but is
>> this still a possibility?  Thanks,
>>
> 
> vfio leaves out users of current and past kernels; relying on it would
> force me to maintain qemu-kvm for a while.

VFIO is a great thing, and we all want it. But it's also a big project
now as it has to please more and more use cases. I think it will require
some more time to settle its interfaces, and it should likely take this
time. So we need KVM device assignment upstream as well until comparable
VFIO support is in every recent distro kernel out there.

Jan


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
@ 2011-10-04 17:41             ` Jan Kiszka
  0 siblings, 0 replies; 66+ messages in thread
From: Jan Kiszka @ 2011-10-04 17:41 UTC (permalink / raw)
  To: Avi Kivity
  Cc: Anthony Perard, Alex Williamson, Xen Devel, QEMU-devel,
	Stefano Stabellini

[-- Attachment #1: Type: text/plain, Size: 769 bytes --]

On 2011-10-04 19:01, Avi Kivity wrote:
> On 10/04/2011 06:33 PM, Alex Williamson wrote:
>> I thought we were potentially looking at vfio as a convergence point.
>> I'm still a bit off from having a vfio re-write ready to submit, but is
>> this still a possibility?  Thanks,
>>
> 
> vfio leaves out users of current and past kernels; relying on it would
> force me to maintain qemu-kvm for a while.

VFIO is a great thing, and we all want it. But it's also a big project
now as it has to please more and more use cases. I think it will require
some more time to settle its interfaces, and it should likely take this
time. So we need KVM device assignment upstream as well until comparable
VFIO support is in every recent distro kernel out there.

Jan


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [PATCH RFC V1 02/11] qemu-timer: Introduce qemu_run_one_timer
  2011-10-04 17:27     ` Jan Kiszka
@ 2011-10-04 17:52       ` Stefano Stabellini
  -1 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-04 17:52 UTC (permalink / raw)
  To: Jan Kiszka
  Cc: Anthony Perard, Alex Williamson, Xen Devel, QEMU-devel,
	Stefano Stabellini

On Tue, 4 Oct 2011, Jan Kiszka wrote:
> On 2011-10-04 16:51, Anthony PERARD wrote:
> > Used by the Xen PCI Passthrough code to run the timer about the power
> > state transition.
> > 
> > Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
> > ---
> >  qemu-timer.c |   15 +++++++++++++++
> >  qemu-timer.h |    3 +++
> >  2 files changed, 18 insertions(+), 0 deletions(-)
> > 
> > diff --git a/qemu-timer.c b/qemu-timer.c
> > index 46dd483..15e659b 100644
> > --- a/qemu-timer.c
> > +++ b/qemu-timer.c
> > @@ -1163,3 +1163,18 @@ int qemu_calculate_timeout(void)
> >      return 1000;
> >  }
> >  
> > +/* run the specified timer */
> > +void qemu_run_one_timer(QEMUTimer *ts)
> > +{
> > +    uint64_t current_time;
> > +
> > +    /* remove timer from the list before calling the callback */
> > +    qemu_del_timer(ts);
> > +
> > +    while ((current_time = qemu_get_clock_ms(rt_clock)) < ts->expire_time)
> > +        /* sleep until the expire time */
> > +        usleep((ts->expire_time - current_time) * 1000);
> > +
> > +    /* run the callback */
> > +    ts->cb(ts->opaque);
> > +}
> 
> This looks funny. I can't imagine that this could ever fit into the
> standard (asynchronous) QEMU execution model for I/O. Keep it private to
> Xen?

I haven't finished reading the series yet, but this caught my eye
because it is particularly ugly; we should get rid of it.

Considering that on a real device if you try to write to the PCI config
space before a power transition is completed the write just fails, maybe
we should do the same here and remove the timer.

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCH RFC V1 02/11] qemu-timer: Introduce qemu_run_one_timer
@ 2011-10-04 17:52       ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-04 17:52 UTC (permalink / raw)
  To: Jan Kiszka
  Cc: Anthony Perard, Alex Williamson, Xen Devel, QEMU-devel,
	Stefano Stabellini

On Tue, 4 Oct 2011, Jan Kiszka wrote:
> On 2011-10-04 16:51, Anthony PERARD wrote:
> > Used by the Xen PCI Passthrough code to run the timer about the power
> > state transition.
> > 
> > Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
> > ---
> >  qemu-timer.c |   15 +++++++++++++++
> >  qemu-timer.h |    3 +++
> >  2 files changed, 18 insertions(+), 0 deletions(-)
> > 
> > diff --git a/qemu-timer.c b/qemu-timer.c
> > index 46dd483..15e659b 100644
> > --- a/qemu-timer.c
> > +++ b/qemu-timer.c
> > @@ -1163,3 +1163,18 @@ int qemu_calculate_timeout(void)
> >      return 1000;
> >  }
> >  
> > +/* run the specified timer */
> > +void qemu_run_one_timer(QEMUTimer *ts)
> > +{
> > +    uint64_t current_time;
> > +
> > +    /* remove timer from the list before calling the callback */
> > +    qemu_del_timer(ts);
> > +
> > +    while ((current_time = qemu_get_clock_ms(rt_clock)) < ts->expire_time)
> > +        /* sleep until the expire time */
> > +        usleep((ts->expire_time - current_time) * 1000);
> > +
> > +    /* run the callback */
> > +    ts->cb(ts->opaque);
> > +}
> 
> This looks funny. I can't imagine that this could ever fit into the
> standard (asynchronous) QEMU execution model for I/O. Keep it private to
> Xen?

I haven't finished reading the series yet, but this caught my eye
because it is particularly ugly; we should get rid of it.

Considering that on a real device if you try to write to the PCI config
space before a power transition is completed the write just fails, maybe
we should do the same here and remove the timer.

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
  2011-10-04 17:03       ` Avi Kivity
@ 2011-10-04 18:19         ` Stefano Stabellini
  -1 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-04 18:19 UTC (permalink / raw)
  To: Avi Kivity
  Cc: Xen Devel, Stefano Stabellini, QEMU-devel, Alex Williamson,
	Anthony Perard

On Tue, 4 Oct 2011, Avi Kivity wrote:
> On 10/04/2011 05:01 PM, Anthony Liguori wrote:
> >> We also have pci passthrough in qemu-kvm (I think based on the same 
> >> Neocleus
> >> code). Rather than having two pci assignment implementations, I think 
> >> we should
> >> have just one, with the differences (programming the hypervisor) 
> >> abstracted at
> >> that level.
> >
> >
> > I agree in principle but how close is qemu-kvm pci passthrough to a 
> > mergable state?  Would it make sense to merge the Xen code first and 
> > then abstract it?
> 
> Merging either implementation and abstracting it would risk regressions 
> in the other.

Honestly the last time I looked at the kvm passthrough code (admittedly
a while ago), it looked very similar to the xen passthrough code, so I
don't think we would risk much merging either one first and then
abstracting it.


> How about merging both, with the ABIs (command line and qmp) tagged as 
> experimental, and then doing a merge in the same style as 
> i386+x86_64->x86 or the two kvm implementations in qemu?  We can pick 
> one implementation as the merge target and port fixes from the other.

I am OK with this too: it is probably more work but it doesn't risk
loosing any bug fixes.
If you think that kvm passthrough might have several bug fixes that xen
passthrough does not have is probably the right way to go.

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
@ 2011-10-04 18:19         ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-04 18:19 UTC (permalink / raw)
  To: Avi Kivity
  Cc: Xen Devel, Stefano Stabellini, QEMU-devel, Alex Williamson,
	Anthony Perard

On Tue, 4 Oct 2011, Avi Kivity wrote:
> On 10/04/2011 05:01 PM, Anthony Liguori wrote:
> >> We also have pci passthrough in qemu-kvm (I think based on the same 
> >> Neocleus
> >> code). Rather than having two pci assignment implementations, I think 
> >> we should
> >> have just one, with the differences (programming the hypervisor) 
> >> abstracted at
> >> that level.
> >
> >
> > I agree in principle but how close is qemu-kvm pci passthrough to a 
> > mergable state?  Would it make sense to merge the Xen code first and 
> > then abstract it?
> 
> Merging either implementation and abstracting it would risk regressions 
> in the other.

Honestly the last time I looked at the kvm passthrough code (admittedly
a while ago), it looked very similar to the xen passthrough code, so I
don't think we would risk much merging either one first and then
abstracting it.


> How about merging both, with the ABIs (command line and qmp) tagged as 
> experimental, and then doing a merge in the same style as 
> i386+x86_64->x86 or the two kvm implementations in qemu?  We can pick 
> one implementation as the merge target and port fixes from the other.

I am OK with this too: it is probably more work but it doesn't risk
loosing any bug fixes.
If you think that kvm passthrough might have several bug fixes that xen
passthrough does not have is probably the right way to go.

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [PATCH RFC V1 02/11] qemu-timer: Introduce qemu_run_one_timer
  2011-10-04 17:27     ` Jan Kiszka
@ 2011-10-04 18:20       ` Anthony Liguori
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony Liguori @ 2011-10-04 18:20 UTC (permalink / raw)
  To: Jan Kiszka
  Cc: Anthony PERARD, Alex Williamson, Xen Devel, QEMU-devel,
	Stefano Stabellini

On 10/04/2011 12:27 PM, Jan Kiszka wrote:
> On 2011-10-04 16:51, Anthony PERARD wrote:
>> Used by the Xen PCI Passthrough code to run the timer about the power
>> state transition.
>>
>> Signed-off-by: Anthony PERARD<anthony.perard@citrix.com>
>> ---
>>   qemu-timer.c |   15 +++++++++++++++
>>   qemu-timer.h |    3 +++
>>   2 files changed, 18 insertions(+), 0 deletions(-)
>>
>> diff --git a/qemu-timer.c b/qemu-timer.c
>> index 46dd483..15e659b 100644
>> --- a/qemu-timer.c
>> +++ b/qemu-timer.c
>> @@ -1163,3 +1163,18 @@ int qemu_calculate_timeout(void)
>>       return 1000;
>>   }
>>
>> +/* run the specified timer */
>> +void qemu_run_one_timer(QEMUTimer *ts)
>> +{
>> +    uint64_t current_time;
>> +
>> +    /* remove timer from the list before calling the callback */
>> +    qemu_del_timer(ts);
>> +
>> +    while ((current_time = qemu_get_clock_ms(rt_clock))<  ts->expire_time)
>> +        /* sleep until the expire time */
>> +        usleep((ts->expire_time - current_time) * 1000);
>> +
>> +    /* run the callback */
>> +    ts->cb(ts->opaque);
>> +}
>
> This looks funny. I can't imagine that this could ever fit into the
> standard (asynchronous) QEMU execution model for I/O. Keep it private to
> Xen?

I think it's funny for Xen too.  Why in the world would you need this?

Regards,

Anthony Liguroi

> Jan
>

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCH RFC V1 02/11] qemu-timer: Introduce qemu_run_one_timer
@ 2011-10-04 18:20       ` Anthony Liguori
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony Liguori @ 2011-10-04 18:20 UTC (permalink / raw)
  To: Jan Kiszka
  Cc: Anthony PERARD, Alex Williamson, Xen Devel, QEMU-devel,
	Stefano Stabellini

On 10/04/2011 12:27 PM, Jan Kiszka wrote:
> On 2011-10-04 16:51, Anthony PERARD wrote:
>> Used by the Xen PCI Passthrough code to run the timer about the power
>> state transition.
>>
>> Signed-off-by: Anthony PERARD<anthony.perard@citrix.com>
>> ---
>>   qemu-timer.c |   15 +++++++++++++++
>>   qemu-timer.h |    3 +++
>>   2 files changed, 18 insertions(+), 0 deletions(-)
>>
>> diff --git a/qemu-timer.c b/qemu-timer.c
>> index 46dd483..15e659b 100644
>> --- a/qemu-timer.c
>> +++ b/qemu-timer.c
>> @@ -1163,3 +1163,18 @@ int qemu_calculate_timeout(void)
>>       return 1000;
>>   }
>>
>> +/* run the specified timer */
>> +void qemu_run_one_timer(QEMUTimer *ts)
>> +{
>> +    uint64_t current_time;
>> +
>> +    /* remove timer from the list before calling the callback */
>> +    qemu_del_timer(ts);
>> +
>> +    while ((current_time = qemu_get_clock_ms(rt_clock))<  ts->expire_time)
>> +        /* sleep until the expire time */
>> +        usleep((ts->expire_time - current_time) * 1000);
>> +
>> +    /* run the callback */
>> +    ts->cb(ts->opaque);
>> +}
>
> This looks funny. I can't imagine that this could ever fit into the
> standard (asynchronous) QEMU execution model for I/O. Keep it private to
> Xen?

I think it's funny for Xen too.  Why in the world would you need this?

Regards,

Anthony Liguroi

> Jan
>

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [PATCH RFC V1 01/11] Introduce HostPCIDevice to access a pci device on the host.
  2011-10-04 14:51   ` Anthony PERARD
@ 2011-10-04 18:21     ` Jan Kiszka
  -1 siblings, 0 replies; 66+ messages in thread
From: Jan Kiszka @ 2011-10-04 18:21 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

[-- Attachment #1: Type: text/plain, Size: 8262 bytes --]

This wasn't run through checkpatch.pl, I bet.

On 2011-10-04 16:51, Anthony PERARD wrote:
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
> ---
>  hw/host-pci-device.c |  192 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/host-pci-device.h |   36 +++++++++
>  2 files changed, 228 insertions(+), 0 deletions(-)
>  create mode 100644 hw/host-pci-device.c
>  create mode 100644 hw/host-pci-device.h
> 
> diff --git a/hw/host-pci-device.c b/hw/host-pci-device.c
> new file mode 100644
> index 0000000..b3f2899
> --- /dev/null
> +++ b/hw/host-pci-device.c
> @@ -0,0 +1,192 @@
> +#include "qemu-common.h"
> +#include "host-pci-device.h"
> +
> +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;
> +    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 -1;
> +    }
> +
> +    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);
> +            break;
> +        }
> +        if (start) {
> +            size = end - start + 1;
> +        } else {
> +            size = 0;
> +        }
> +
> +        flags &= 0xf;

No magic numbers please.

It also looks a bit strange to me: It's the resource type encoded in the
second byte? Aren't you interested in it?

> +
> +        if (i < PCI_ROM_SLOT) {
> +            d->base_addr[i] = start | flags;
> +            d->size[i] = size;
> +        } else {
> +            d->rom_base_addr = start | flags;
> +            d->rom_size = size;
> +        }
> +    }
> +
> +    fclose(f);
> +    return 0;
> +}
> +
> +static unsigned long get_value(HostPCIDevice *d, const char *name)
> +{
> +    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 -1;
> +    }
> +    if (fscanf(f, "%lx\n", &value) != 1) {
> +        fprintf(stderr, "Error: Syntax error in %s\n", path);
> +        value = -1;
> +    }
> +    fclose(f);
> +    return value;
> +}
> +
> +static int pci_dev_is_virtfn(HostPCIDevice *d)
> +{
> +    int rc;
> +    char path[PATH_MAX];
> +    struct stat buf;
> +
> +    path_to(d, "physfn", path, sizeof (path));
> +    rc = !stat(path, &buf);
> +
> +    return rc;
> +}
> +
> +static int host_pci_config_fd(HostPCIDevice *d)

[ We will also need the reverse: pass in open file descriptors that
HostPCIDevice should use. Can be added later. ]

> +{
> +    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;
> +
> +    res = pread(fd, buf, len, pos);
> +    if (res < 0) {
> +        fprintf(stderr, "host_pci_config: read failed: %s (fd: %i)\n",
> +                strerror(errno), fd);
> +        return -1;
> +    }
> +    return res;
> +}
> +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;
> +
> +    res = pwrite(fd, buf, len, pos);
> +    if (res < 0) {
> +        fprintf(stderr, "host_pci_config: write failed: %s\n",
> +                strerror(errno));
> +        return -1;
> +    }
> +    return res;
> +}
> +
> +uint8_t host_pci_read_byte(HostPCIDevice *d, int pos)
> +{
> +  uint8_t buf;
> +  host_pci_config_read(d, pos, &buf, 1);
> +  return buf;
> +}
> +uint16_t host_pci_read_word(HostPCIDevice *d, int pos)
> +{
> +  uint16_t buf;
> +  host_pci_config_read(d, pos, &buf, 2);
> +  return le16_to_cpu(buf);
> +}
> +uint32_t host_pci_read_long(HostPCIDevice *d, int pos)
> +{
> +  uint32_t buf;
> +  host_pci_config_read(d, pos, &buf, 4);
> +  return le32_to_cpu(buf);
> +}
> +int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
> +{
> +  return host_pci_config_read(d, pos, buf, len);
> +}
> +
> +int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data)
> +{
> +  return host_pci_config_write(d, pos, &data, 1);
> +}
> +int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data)
> +{
> +  return host_pci_config_write(d, pos, &data, 2);

You adjust endianess on read, but not on write.

> +}
> +int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data)
> +{
> +  return host_pci_config_write(d, pos, &data, 4);
> +}
> +int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
> +{
> +  return host_pci_config_write(d, pos, buf, len);
> +}
> +
> +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func)
> +{
> +    HostPCIDevice *d = NULL;
> +
> +    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) == -1)
> +        goto error;
> +
> +    d->vendor_id = get_value(d, "vendor");
> +    d->device_id = get_value(d, "device");
> +    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;
> +}
> diff --git a/hw/host-pci-device.h b/hw/host-pci-device.h
> new file mode 100644
> index 0000000..0137507
> --- /dev/null
> +++ b/hw/host-pci-device.h
> @@ -0,0 +1,36 @@
> +#ifndef HW_HOST_PCI_DEVICE
> +#  define HW_HOST_PCI_DEVICE
> +
> +#include "pci.h"
> +
> +typedef struct HostPCIDevice {
> +    uint16_t domain;
> +    uint8_t bus;
> +    uint8_t dev;
> +    uint8_t func;
> +
> +    uint16_t vendor_id;
> +    uint16_t device_id;
> +
> +    pcibus_t base_addr[PCI_NUM_REGIONS - 1];
> +    pcibus_t size[PCI_NUM_REGIONS - 1];
> +    pcibus_t rom_base_addr;
> +    pcibus_t rom_size;

Regions deserve their own type IMHO. In KVM we have

typedef struct {
    int type;           /* Memory or port I/O */
    int valid;
    uint32_t base_addr;
    uint32_t size;    /* size of the region */
    int resource_fd;
} PCIRegion;

Should probably become HostPCIIORegion (vs. virtual PCIIORegion), and
our field types need some cleanups.

> +
> +    bool is_virtfn;
> +
> +    int config_fd;
> +} HostPCIDevice;
> +
> +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func);

And what about some host_pci_device_put when we're done with it?

> +
> +uint8_t host_pci_read_byte(HostPCIDevice *d, int pos);
> +uint16_t host_pci_read_word(HostPCIDevice *d, int pos);
> +uint32_t host_pci_read_long(HostPCIDevice *d, int pos);
> +int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
> +int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data);
> +int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data);
> +int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data);
> +int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);

I think these should be analogous to our pci layer:
host_pci_get/set_byte/word/long/quad.

Looks like it's generally useful for KVM as well.

Jan


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCH RFC V1 01/11] Introduce HostPCIDevice to access a pci device on the host.
@ 2011-10-04 18:21     ` Jan Kiszka
  0 siblings, 0 replies; 66+ messages in thread
From: Jan Kiszka @ 2011-10-04 18:21 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

[-- Attachment #1: Type: text/plain, Size: 8262 bytes --]

This wasn't run through checkpatch.pl, I bet.

On 2011-10-04 16:51, Anthony PERARD wrote:
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
> ---
>  hw/host-pci-device.c |  192 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/host-pci-device.h |   36 +++++++++
>  2 files changed, 228 insertions(+), 0 deletions(-)
>  create mode 100644 hw/host-pci-device.c
>  create mode 100644 hw/host-pci-device.h
> 
> diff --git a/hw/host-pci-device.c b/hw/host-pci-device.c
> new file mode 100644
> index 0000000..b3f2899
> --- /dev/null
> +++ b/hw/host-pci-device.c
> @@ -0,0 +1,192 @@
> +#include "qemu-common.h"
> +#include "host-pci-device.h"
> +
> +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;
> +    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 -1;
> +    }
> +
> +    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);
> +            break;
> +        }
> +        if (start) {
> +            size = end - start + 1;
> +        } else {
> +            size = 0;
> +        }
> +
> +        flags &= 0xf;

No magic numbers please.

It also looks a bit strange to me: It's the resource type encoded in the
second byte? Aren't you interested in it?

> +
> +        if (i < PCI_ROM_SLOT) {
> +            d->base_addr[i] = start | flags;
> +            d->size[i] = size;
> +        } else {
> +            d->rom_base_addr = start | flags;
> +            d->rom_size = size;
> +        }
> +    }
> +
> +    fclose(f);
> +    return 0;
> +}
> +
> +static unsigned long get_value(HostPCIDevice *d, const char *name)
> +{
> +    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 -1;
> +    }
> +    if (fscanf(f, "%lx\n", &value) != 1) {
> +        fprintf(stderr, "Error: Syntax error in %s\n", path);
> +        value = -1;
> +    }
> +    fclose(f);
> +    return value;
> +}
> +
> +static int pci_dev_is_virtfn(HostPCIDevice *d)
> +{
> +    int rc;
> +    char path[PATH_MAX];
> +    struct stat buf;
> +
> +    path_to(d, "physfn", path, sizeof (path));
> +    rc = !stat(path, &buf);
> +
> +    return rc;
> +}
> +
> +static int host_pci_config_fd(HostPCIDevice *d)

[ We will also need the reverse: pass in open file descriptors that
HostPCIDevice should use. Can be added later. ]

> +{
> +    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;
> +
> +    res = pread(fd, buf, len, pos);
> +    if (res < 0) {
> +        fprintf(stderr, "host_pci_config: read failed: %s (fd: %i)\n",
> +                strerror(errno), fd);
> +        return -1;
> +    }
> +    return res;
> +}
> +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;
> +
> +    res = pwrite(fd, buf, len, pos);
> +    if (res < 0) {
> +        fprintf(stderr, "host_pci_config: write failed: %s\n",
> +                strerror(errno));
> +        return -1;
> +    }
> +    return res;
> +}
> +
> +uint8_t host_pci_read_byte(HostPCIDevice *d, int pos)
> +{
> +  uint8_t buf;
> +  host_pci_config_read(d, pos, &buf, 1);
> +  return buf;
> +}
> +uint16_t host_pci_read_word(HostPCIDevice *d, int pos)
> +{
> +  uint16_t buf;
> +  host_pci_config_read(d, pos, &buf, 2);
> +  return le16_to_cpu(buf);
> +}
> +uint32_t host_pci_read_long(HostPCIDevice *d, int pos)
> +{
> +  uint32_t buf;
> +  host_pci_config_read(d, pos, &buf, 4);
> +  return le32_to_cpu(buf);
> +}
> +int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
> +{
> +  return host_pci_config_read(d, pos, buf, len);
> +}
> +
> +int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data)
> +{
> +  return host_pci_config_write(d, pos, &data, 1);
> +}
> +int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data)
> +{
> +  return host_pci_config_write(d, pos, &data, 2);

You adjust endianess on read, but not on write.

> +}
> +int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data)
> +{
> +  return host_pci_config_write(d, pos, &data, 4);
> +}
> +int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
> +{
> +  return host_pci_config_write(d, pos, buf, len);
> +}
> +
> +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func)
> +{
> +    HostPCIDevice *d = NULL;
> +
> +    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) == -1)
> +        goto error;
> +
> +    d->vendor_id = get_value(d, "vendor");
> +    d->device_id = get_value(d, "device");
> +    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;
> +}
> diff --git a/hw/host-pci-device.h b/hw/host-pci-device.h
> new file mode 100644
> index 0000000..0137507
> --- /dev/null
> +++ b/hw/host-pci-device.h
> @@ -0,0 +1,36 @@
> +#ifndef HW_HOST_PCI_DEVICE
> +#  define HW_HOST_PCI_DEVICE
> +
> +#include "pci.h"
> +
> +typedef struct HostPCIDevice {
> +    uint16_t domain;
> +    uint8_t bus;
> +    uint8_t dev;
> +    uint8_t func;
> +
> +    uint16_t vendor_id;
> +    uint16_t device_id;
> +
> +    pcibus_t base_addr[PCI_NUM_REGIONS - 1];
> +    pcibus_t size[PCI_NUM_REGIONS - 1];
> +    pcibus_t rom_base_addr;
> +    pcibus_t rom_size;

Regions deserve their own type IMHO. In KVM we have

typedef struct {
    int type;           /* Memory or port I/O */
    int valid;
    uint32_t base_addr;
    uint32_t size;    /* size of the region */
    int resource_fd;
} PCIRegion;

Should probably become HostPCIIORegion (vs. virtual PCIIORegion), and
our field types need some cleanups.

> +
> +    bool is_virtfn;
> +
> +    int config_fd;
> +} HostPCIDevice;
> +
> +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func);

And what about some host_pci_device_put when we're done with it?

> +
> +uint8_t host_pci_read_byte(HostPCIDevice *d, int pos);
> +uint16_t host_pci_read_word(HostPCIDevice *d, int pos);
> +uint32_t host_pci_read_long(HostPCIDevice *d, int pos);
> +int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
> +int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data);
> +int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data);
> +int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data);
> +int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);

I think these should be analogous to our pci layer:
host_pci_get/set_byte/word/long/quad.

Looks like it's generally useful for KVM as well.

Jan


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
  2011-10-04 18:19         ` Stefano Stabellini
@ 2011-10-04 18:24           ` Avi Kivity
  -1 siblings, 0 replies; 66+ messages in thread
From: Avi Kivity @ 2011-10-04 18:24 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: Anthony Perard, Alex Williamson, Xen Devel, QEMU-devel

On 10/04/2011 08:19 PM, Stefano Stabellini wrote:
> On Tue, 4 Oct 2011, Avi Kivity wrote:
> >  On 10/04/2011 05:01 PM, Anthony Liguori wrote:
> >  >>  We also have pci passthrough in qemu-kvm (I think based on the same
> >  >>  Neocleus
> >  >>  code). Rather than having two pci assignment implementations, I think
> >  >>  we should
> >  >>  have just one, with the differences (programming the hypervisor)
> >  >>  abstracted at
> >  >>  that level.
> >  >
> >  >
> >  >  I agree in principle but how close is qemu-kvm pci passthrough to a
> >  >  mergable state?  Would it make sense to merge the Xen code first and
> >  >  then abstract it?
> >
> >  Merging either implementation and abstracting it would risk regressions
> >  in the other.
>
> Honestly the last time I looked at the kvm passthrough code (admittedly
> a while ago), it looked very similar to the xen passthrough code, so I
> don't think we would risk much merging either one first and then
> abstracting it.

There were 59 commits in the past year to hw/device-assignment.c, so the 
risk is real IMO.

> >  How about merging both, with the ABIs (command line and qmp) tagged as
> >  experimental, and then doing a merge in the same style as
> >  i386+x86_64->x86 or the two kvm implementations in qemu?  We can pick
> >  one implementation as the merge target and port fixes from the other.
>
> I am OK with this too: it is probably more work but it doesn't risk
> loosing any bug fixes.
> If you think that kvm passthrough might have several bug fixes that xen
> passthrough does not have is probably the right way to go.

and the other way round.

-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough
@ 2011-10-04 18:24           ` Avi Kivity
  0 siblings, 0 replies; 66+ messages in thread
From: Avi Kivity @ 2011-10-04 18:24 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: Anthony Perard, Alex Williamson, Xen Devel, QEMU-devel

On 10/04/2011 08:19 PM, Stefano Stabellini wrote:
> On Tue, 4 Oct 2011, Avi Kivity wrote:
> >  On 10/04/2011 05:01 PM, Anthony Liguori wrote:
> >  >>  We also have pci passthrough in qemu-kvm (I think based on the same
> >  >>  Neocleus
> >  >>  code). Rather than having two pci assignment implementations, I think
> >  >>  we should
> >  >>  have just one, with the differences (programming the hypervisor)
> >  >>  abstracted at
> >  >>  that level.
> >  >
> >  >
> >  >  I agree in principle but how close is qemu-kvm pci passthrough to a
> >  >  mergable state?  Would it make sense to merge the Xen code first and
> >  >  then abstract it?
> >
> >  Merging either implementation and abstracting it would risk regressions
> >  in the other.
>
> Honestly the last time I looked at the kvm passthrough code (admittedly
> a while ago), it looked very similar to the xen passthrough code, so I
> don't think we would risk much merging either one first and then
> abstracting it.

There were 59 commits in the past year to hw/device-assignment.c, so the 
risk is real IMO.

> >  How about merging both, with the ABIs (command line and qmp) tagged as
> >  experimental, and then doing a merge in the same style as
> >  i386+x86_64->x86 or the two kvm implementations in qemu?  We can pick
> >  one implementation as the merge target and port fixes from the other.
>
> I am OK with this too: it is probably more work but it doesn't risk
> loosing any bug fixes.
> If you think that kvm passthrough might have several bug fixes that xen
> passthrough does not have is probably the right way to go.

and the other way round.

-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [PATCH RFC V1 01/11] Introduce HostPCIDevice to access a pci device on the host.
  2011-10-04 14:51   ` Anthony PERARD
@ 2011-10-04 18:36     ` Stefano Stabellini
  -1 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-04 18:36 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On Tue, 4 Oct 2011, Anthony PERARD wrote:
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
> ---
>  hw/host-pci-device.c |  192 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/host-pci-device.h |   36 +++++++++
>  2 files changed, 228 insertions(+), 0 deletions(-)
>  create mode 100644 hw/host-pci-device.c
>  create mode 100644 hw/host-pci-device.h
> 
> diff --git a/hw/host-pci-device.c b/hw/host-pci-device.c
> new file mode 100644
> index 0000000..b3f2899
> --- /dev/null
> +++ b/hw/host-pci-device.c
> @@ -0,0 +1,192 @@
> +#include "qemu-common.h"
> +#include "host-pci-device.h"
> +
> +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);
> +}

This is all very Linux specific so at the very least it should only be
compiled if CONFIG_LINUX.


> +static int get_resource(HostPCIDevice *d)
> +{
> +    int i;
> +    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 -1;
> +    }
> +
> +    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);
> +            break;
> +        }

you should return error in this case


> +        if (start) {
> +            size = end - start + 1;
> +        } else {
> +            size = 0;
> +        }
> +
> +        flags &= 0xf;

please #define what mask is this 0xf


> +        if (i < PCI_ROM_SLOT) {
> +            d->base_addr[i] = start | flags;
> +            d->size[i] = size;
> +        } else {
> +            d->rom_base_addr = start | flags;
> +            d->rom_size = size;
> +        }
> +    }
> +
> +    fclose(f);
> +    return 0;
> +}
> +
> +static unsigned long get_value(HostPCIDevice *d, const char *name)
> +{
> +    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 -1;
> +    }
> +    if (fscanf(f, "%lx\n", &value) != 1) {
> +        fprintf(stderr, "Error: Syntax error in %s\n", path);
> +        value = -1;
> +    }
> +    fclose(f);
> +    return value;
> +}
> +
> +static int pci_dev_is_virtfn(HostPCIDevice *d)
> +{
> +    int rc;
> +    char path[PATH_MAX];
> +    struct stat buf;
> +
> +    path_to(d, "physfn", path, sizeof (path));
> +    rc = !stat(path, &buf);
> +
> +    return rc;
> +}
> +
> +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;
> +
> +    res = pread(fd, buf, len, pos);
> +    if (res < 0) {
> +        fprintf(stderr, "host_pci_config: read failed: %s (fd: %i)\n",
> +                strerror(errno), fd);
> +        return -1;
> +    }
> +    return res;
> +}
> +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;
> +
> +    res = pwrite(fd, buf, len, pos);
> +    if (res < 0) {
> +        fprintf(stderr, "host_pci_config: write failed: %s\n",
> +                strerror(errno));
> +        return -1;
> +    }
> +    return res;
> +}
> +
> +uint8_t host_pci_read_byte(HostPCIDevice *d, int pos)
> +{
> +  uint8_t buf;
> +  host_pci_config_read(d, pos, &buf, 1);
> +  return buf;
> +}
> +uint16_t host_pci_read_word(HostPCIDevice *d, int pos)
> +{
> +  uint16_t buf;
> +  host_pci_config_read(d, pos, &buf, 2);
> +  return le16_to_cpu(buf);
> +}
> +uint32_t host_pci_read_long(HostPCIDevice *d, int pos)
> +{
> +  uint32_t buf;
> +  host_pci_config_read(d, pos, &buf, 4);
> +  return le32_to_cpu(buf);
> +}
> +int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
> +{
> +  return host_pci_config_read(d, pos, buf, len);
> +}
> +
> +int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data)
> +{
> +  return host_pci_config_write(d, pos, &data, 1);
> +}
> +int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data)
> +{
> +  return host_pci_config_write(d, pos, &data, 2);
> +}
> +int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data)
> +{
> +  return host_pci_config_write(d, pos, &data, 4);
> +}
> +int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
> +{
> +  return host_pci_config_write(d, pos, buf, len);
> +}
> +
> +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func)
> +{
> +    HostPCIDevice *d = NULL;
> +
> +    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) == -1)
> +        goto error;
> +
> +    d->vendor_id = get_value(d, "vendor");
> +    d->device_id = get_value(d, "device");
> +    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;
> +}
> diff --git a/hw/host-pci-device.h b/hw/host-pci-device.h
> new file mode 100644
> index 0000000..0137507
> --- /dev/null
> +++ b/hw/host-pci-device.h
> @@ -0,0 +1,36 @@
> +#ifndef HW_HOST_PCI_DEVICE
> +#  define HW_HOST_PCI_DEVICE
> +
> +#include "pci.h"
> +
> +typedef struct HostPCIDevice {
> +    uint16_t domain;
> +    uint8_t bus;
> +    uint8_t dev;
> +    uint8_t func;
> +
> +    uint16_t vendor_id;
> +    uint16_t device_id;
> +
> +    pcibus_t base_addr[PCI_NUM_REGIONS - 1];
> +    pcibus_t size[PCI_NUM_REGIONS - 1];
> +    pcibus_t rom_base_addr;
> +    pcibus_t rom_size;
> +
> +    bool is_virtfn;
> +
> +    int config_fd;
> +} HostPCIDevice;
> +
> +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func);
> +
> +uint8_t host_pci_read_byte(HostPCIDevice *d, int pos);
> +uint16_t host_pci_read_word(HostPCIDevice *d, int pos);
> +uint32_t host_pci_read_long(HostPCIDevice *d, int pos);
> +int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
> +int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data);
> +int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data);
> +int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data);
> +int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
> +
> +#endif /* !HW_HOST_PCI_DEVICE */

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCH RFC V1 01/11] Introduce HostPCIDevice to access a pci device on the host.
@ 2011-10-04 18:36     ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-04 18:36 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On Tue, 4 Oct 2011, Anthony PERARD wrote:
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
> ---
>  hw/host-pci-device.c |  192 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/host-pci-device.h |   36 +++++++++
>  2 files changed, 228 insertions(+), 0 deletions(-)
>  create mode 100644 hw/host-pci-device.c
>  create mode 100644 hw/host-pci-device.h
> 
> diff --git a/hw/host-pci-device.c b/hw/host-pci-device.c
> new file mode 100644
> index 0000000..b3f2899
> --- /dev/null
> +++ b/hw/host-pci-device.c
> @@ -0,0 +1,192 @@
> +#include "qemu-common.h"
> +#include "host-pci-device.h"
> +
> +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);
> +}

This is all very Linux specific so at the very least it should only be
compiled if CONFIG_LINUX.


> +static int get_resource(HostPCIDevice *d)
> +{
> +    int i;
> +    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 -1;
> +    }
> +
> +    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);
> +            break;
> +        }

you should return error in this case


> +        if (start) {
> +            size = end - start + 1;
> +        } else {
> +            size = 0;
> +        }
> +
> +        flags &= 0xf;

please #define what mask is this 0xf


> +        if (i < PCI_ROM_SLOT) {
> +            d->base_addr[i] = start | flags;
> +            d->size[i] = size;
> +        } else {
> +            d->rom_base_addr = start | flags;
> +            d->rom_size = size;
> +        }
> +    }
> +
> +    fclose(f);
> +    return 0;
> +}
> +
> +static unsigned long get_value(HostPCIDevice *d, const char *name)
> +{
> +    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 -1;
> +    }
> +    if (fscanf(f, "%lx\n", &value) != 1) {
> +        fprintf(stderr, "Error: Syntax error in %s\n", path);
> +        value = -1;
> +    }
> +    fclose(f);
> +    return value;
> +}
> +
> +static int pci_dev_is_virtfn(HostPCIDevice *d)
> +{
> +    int rc;
> +    char path[PATH_MAX];
> +    struct stat buf;
> +
> +    path_to(d, "physfn", path, sizeof (path));
> +    rc = !stat(path, &buf);
> +
> +    return rc;
> +}
> +
> +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;
> +
> +    res = pread(fd, buf, len, pos);
> +    if (res < 0) {
> +        fprintf(stderr, "host_pci_config: read failed: %s (fd: %i)\n",
> +                strerror(errno), fd);
> +        return -1;
> +    }
> +    return res;
> +}
> +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;
> +
> +    res = pwrite(fd, buf, len, pos);
> +    if (res < 0) {
> +        fprintf(stderr, "host_pci_config: write failed: %s\n",
> +                strerror(errno));
> +        return -1;
> +    }
> +    return res;
> +}
> +
> +uint8_t host_pci_read_byte(HostPCIDevice *d, int pos)
> +{
> +  uint8_t buf;
> +  host_pci_config_read(d, pos, &buf, 1);
> +  return buf;
> +}
> +uint16_t host_pci_read_word(HostPCIDevice *d, int pos)
> +{
> +  uint16_t buf;
> +  host_pci_config_read(d, pos, &buf, 2);
> +  return le16_to_cpu(buf);
> +}
> +uint32_t host_pci_read_long(HostPCIDevice *d, int pos)
> +{
> +  uint32_t buf;
> +  host_pci_config_read(d, pos, &buf, 4);
> +  return le32_to_cpu(buf);
> +}
> +int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
> +{
> +  return host_pci_config_read(d, pos, buf, len);
> +}
> +
> +int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data)
> +{
> +  return host_pci_config_write(d, pos, &data, 1);
> +}
> +int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data)
> +{
> +  return host_pci_config_write(d, pos, &data, 2);
> +}
> +int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data)
> +{
> +  return host_pci_config_write(d, pos, &data, 4);
> +}
> +int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
> +{
> +  return host_pci_config_write(d, pos, buf, len);
> +}
> +
> +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func)
> +{
> +    HostPCIDevice *d = NULL;
> +
> +    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) == -1)
> +        goto error;
> +
> +    d->vendor_id = get_value(d, "vendor");
> +    d->device_id = get_value(d, "device");
> +    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;
> +}
> diff --git a/hw/host-pci-device.h b/hw/host-pci-device.h
> new file mode 100644
> index 0000000..0137507
> --- /dev/null
> +++ b/hw/host-pci-device.h
> @@ -0,0 +1,36 @@
> +#ifndef HW_HOST_PCI_DEVICE
> +#  define HW_HOST_PCI_DEVICE
> +
> +#include "pci.h"
> +
> +typedef struct HostPCIDevice {
> +    uint16_t domain;
> +    uint8_t bus;
> +    uint8_t dev;
> +    uint8_t func;
> +
> +    uint16_t vendor_id;
> +    uint16_t device_id;
> +
> +    pcibus_t base_addr[PCI_NUM_REGIONS - 1];
> +    pcibus_t size[PCI_NUM_REGIONS - 1];
> +    pcibus_t rom_base_addr;
> +    pcibus_t rom_size;
> +
> +    bool is_virtfn;
> +
> +    int config_fd;
> +} HostPCIDevice;
> +
> +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func);
> +
> +uint8_t host_pci_read_byte(HostPCIDevice *d, int pos);
> +uint16_t host_pci_read_word(HostPCIDevice *d, int pos);
> +uint32_t host_pci_read_long(HostPCIDevice *d, int pos);
> +int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
> +int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data);
> +int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data);
> +int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data);
> +int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
> +
> +#endif /* !HW_HOST_PCI_DEVICE */

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [PATCH RFC V1 07/11] host-pci-device: Add host_pci_find_ext_cap_offset
  2011-10-04 14:51   ` Anthony PERARD
@ 2011-10-05 11:07     ` Stefano Stabellini
  -1 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-05 11:07 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On Tue, 4 Oct 2011, Anthony PERARD wrote:
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>

any reason why we shouldn't merge this patch with patch #1?


> ---
>  hw/host-pci-device.c |   31 +++++++++++++++++++++++++++++++
>  hw/host-pci-device.h |    2 ++
>  2 files changed, 33 insertions(+), 0 deletions(-)
> 
> diff --git a/hw/host-pci-device.c b/hw/host-pci-device.c
> index b3f2899..2a889d5 100644
> --- a/hw/host-pci-device.c
> +++ b/hw/host-pci-device.c
> @@ -162,6 +162,37 @@ int host_pci_write_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 = 480;
> +    int pos = 0x100;
> +
> +    do {
> +        header = host_pci_read_long(d, pos);
> +        /*
> +         * 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 < 0x100) {
> +            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;
> diff --git a/hw/host-pci-device.h b/hw/host-pci-device.h
> index 0137507..2734eb3 100644
> --- a/hw/host-pci-device.h
> +++ b/hw/host-pci-device.h
> @@ -33,4 +33,6 @@ int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data);
>  int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data);
>  int host_pci_write_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
> 

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCH RFC V1 07/11] host-pci-device: Add host_pci_find_ext_cap_offset
@ 2011-10-05 11:07     ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-05 11:07 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On Tue, 4 Oct 2011, Anthony PERARD wrote:
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>

any reason why we shouldn't merge this patch with patch #1?


> ---
>  hw/host-pci-device.c |   31 +++++++++++++++++++++++++++++++
>  hw/host-pci-device.h |    2 ++
>  2 files changed, 33 insertions(+), 0 deletions(-)
> 
> diff --git a/hw/host-pci-device.c b/hw/host-pci-device.c
> index b3f2899..2a889d5 100644
> --- a/hw/host-pci-device.c
> +++ b/hw/host-pci-device.c
> @@ -162,6 +162,37 @@ int host_pci_write_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 = 480;
> +    int pos = 0x100;
> +
> +    do {
> +        header = host_pci_read_long(d, pos);
> +        /*
> +         * 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 < 0x100) {
> +            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;
> diff --git a/hw/host-pci-device.h b/hw/host-pci-device.h
> index 0137507..2734eb3 100644
> --- a/hw/host-pci-device.h
> +++ b/hw/host-pci-device.h
> @@ -33,4 +33,6 @@ int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data);
>  int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data);
>  int host_pci_write_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
> 

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [PATCH RFC V1 08/11] Introduce Xen PCI Passthrough, qdevice (1/3)
  2011-10-04 14:51   ` Anthony PERARD
@ 2011-10-05 11:51     ` Stefano Stabellini
  -1 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-05 11:51 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On Tue, 4 Oct 2011, Anthony PERARD wrote:
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>

You should set the original author correctly and add his signed-off-by.
Remeber to run the patch through checkpatch.pl.

> ---
>  hw/xen_pci_passthrough.c         |  763 ++++++++++++++++++++++++++++++++++++++
>  hw/xen_pci_passthrough.h         |  335 +++++++++++++++++
>  hw/xen_pci_passthrough_helpers.c |   46 +++
>  3 files changed, 1144 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_helpers.c
> 
> diff --git a/hw/xen_pci_passthrough.c b/hw/xen_pci_passthrough.c
> new file mode 100644
> index 0000000..bfbe042
> --- /dev/null
> +++ b/hw/xen_pci_passthrough.c
> @@ -0,0 +1,763 @@
> +#include <sys/ioctl.h>
> +
> +#include "pci.h"
> +#include "xen.h"
> +#include "xen_backend.h"
> +#include "xen_pci_passthrough.h"

it would be nice to keep the comment about PCI-MSI translation we have
in the corresponding source file in qemu-xen


> +#define PCI_BAR_ENTRIES (6)
> +
> +typedef struct PlugDevice {
> +    uint8_t r_bus;
> +    uint8_t r_slot;
> +    uint8_t r_func;
> +    int bus;
> +    int slot;
> +    int func;
> +} PlugDevice;
> +
> +QLIST_HEAD(php_dev_list, PlugDevice) php_dev_list =
> +    QLIST_HEAD_INITIALIZER(php_dev_list);
> +

What is the difference between r_bus,r_slot and r_func and bus, slot and
func? It should be well documented with a comment.
Also do we need both PlugDevice and PHPDev? What is the difference?


> +#define PT_BAR_ALLF             0xFFFFFFFF      /* BAR ALLF value */

already defined in hw/xen_pci_passthrough.h


> +
> +/* #define PT_NR_IRQS          (256) */
> +/* char mapped_machine_irq[PT_NR_IRQS] = {0}; */
> +
> +/* Config Space */
> +static int pt_pci_config_access_check(PCIDevice *d, uint32_t address, int len)
> +{
> +    /* check offset range */
> +    if (address >= 0xFF) {
> +        PT_LOG("Error: Failed to access register with offset exceeding FFh. "
> +               "[%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
> +               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +               address, len);
> +        return -1;
> +    }
> +
> +    /* check read size */
> +    if ((len != 1) && (len != 2) && (len != 4)) {
> +        PT_LOG("Error: Failed to access register with invalid access length. "
> +               "[%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
> +               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +               address, len);
> +        return -1;
> +    }
> +
> +    /* check offset alignment */
> +    if (address & (len - 1)) {
> +        PT_LOG("Error: Failed to access register with invalid access size "
> +            "alignment. [%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
> +            pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +            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 untill previous power state transition is completed.
> +         * so finished previous request here.
> +         */
> +        qemu_run_one_timer(s->pm_state->pm_timer);
> +    }

the timer is not acceptable, we should probably remove it and return an
error instead, unless somebody has any better ideas.


> +    /* 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_read_block(s->real_device, address, (uint8_t *)&val, len);
> +    if (!rc) {
> +        PT_LOG("Error: 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 Guest request 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 depend 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) {
> +                hw_error("Internal error: Invalid read emulation "
> +                         "return value[%d]. I/O emulator exit.\n", rc);
> +            }
> +
> +            /* 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("[%02x:%02x.%x]: address=%04x val=0x%08x len=%d\n",
> +                  pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +                  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("[%02x:%02x.%x]: address=%04x val=0x%08x len=%d\n",
> +                  pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +                  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_LOG("Warning: Guest attempt to set address to unused Base Address "
> +               "Register. [%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
> +               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +               address, len);
> +    }
> +
> +    /* check power state transition flags */
> +    if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) {
> +        /* can't accept untill previous power state transition is completed.
> +         * so finished previous request here.
> +         */
> +        qemu_run_one_timer(s->pm_state->pm_timer);
> +    }

same here

> +    /* 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_LOG("Warning: Access to 0 Hardwired register. "
> +                   "[%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
> +                   pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +                   address, len);
> +            return;
> +        }
> +    }
> +
> +    /* read I/O device register value */
> +    rc = host_pci_read_block(s->real_device, address,
> +                             (uint8_t *)&read_val, len);
> +    if (!rc) {
> +        PT_LOG("Error: pci_read_block failed. return value[%d].\n", rc);
> +        memset(&read_val, 0xff, len);
> +    }
> +
> +    /* pass directly to libpci 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 Guest request 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 depend 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) {
> +                hw_error("Internal error: Invalid write emulation "
> +                         "return value[%d]. I/O emulator exit.\n", rc);
> +            }
> +
> +            /* 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 libpci */
> +    val >>= (address & 3) << 3;
> +
> +out:
> +    if (!(reg && reg->no_wb)) {
> +        /* unknown regs are passed through */
> +        rc = host_pci_write_block(s->real_device, address,
> +                                  (uint8_t *)&val, len);
> +
> +        if (!rc) {
> +            PT_LOG("Error: pci_write_block failed. return value[%d].\n", rc);
> +        }
> +    }
> +
> +    if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) {
> +        /* set QEMUTimer */
> +        qemu_mod_timer(s->pm_state->pm_timer,
> +                       qemu_get_clock_ms(rt_clock) + s->pm_state->pm_delay);
> +    }
> +}

same here

> +
> +/* ioport/iomem space*/
> +static void pt_iomem_map(XenPCIPassthroughState *s, int i,
> +                         pcibus_t e_phys, pcibus_t e_size, int type)
> +{
> +    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("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 != -1) {
> +        add_msix_mapping(s, i);
> +        /* Remove old mapping */
> +        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_LOG("Error: remove old mapping failed!\n");
> +            return;
> +        }
> +    }
> +
> +    /* map only valid guest address */
> +    if (e_phys != -1) {
> +        /* Create new mapping */
> +        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_LOG("Error: create new mapping failed!\n");
> +        }
> +
> +        ret = remove_msix_mapping(s, i);
> +        if (ret != 0) {
> +            PT_LOG("Error: remove MSI-X mmio mapping failed!\n");
> +        }
> +
> +        if (old_ebase != e_phys && old_ebase != -1) {
> +            pt_msix_update_remap(s, i);
> +        }
> +    }
> +}
> +
> +static void pt_ioport_map(XenPCIPassthroughState *s, int i,
> +                          pcibus_t e_phys, pcibus_t e_size, int type)
> +{
> +    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("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 != -1) {
> +        /* Remove old mapping */
> +        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_LOG("Error: remove old mapping failed!\n");
> +            return;
> +        }
> +    }
> +
> +    /* map only valid guest address (include 0) */
> +    if (e_phys != -1) {
> +        /* Create new mapping */
> +        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_LOG("Error: 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 = -1;
> +    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 = -1;
> +    }
> +    if ((bar == PCI_ROM_SLOT) && (r_addr != -1)) {
> +        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 = -1;
> +            }
> +        }
> +    }
> +
> +    /* prevent guest software mapping memory resource to 00000000h */
> +    if ((base->bar_flag == PT_BAR_FLAG_MEM) && (r_addr == 0)) {
> +        r_addr = -1;
> +    }
> +
> +    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 > 0) {
> +        PT_LOG("Warning: s[%02x:%02x.%x][Region:%d][Address:%"FMT_PCIBUS"h]"
> +               "[Size:%"FMT_PCIBUS"h] is overlapped.\n", pci_bus_num(dev->bus),
> +               PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), 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);
> +    }
> +}
> +
> +/* register regions */
> +int qemu_pci_register_regions(XenPCIPassthroughState *s)
> +{
> +    /* but no mapping done anymore (see in reset) */
> +    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++) {
> +        if (pt_pci_base_addr(d->base_addr[i])) {
> +            s->bases[i].e_physbase = pt_pci_base_addr(d->base_addr[i]);
> +            s->bases[i].access.u = pt_pci_base_addr(d->base_addr[i]);
> +
> +            /* Register current region */
> +            if (d->base_addr[i] & PCI_BASE_ADDRESS_SPACE_IO) {
> +                memory_region_init_io(&s->bar[i], NULL, NULL,
> +                                      "xen-pci-pt-bar", d->size[i]);
> +                pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_SPACE_IO,
> +                                 &s->bar[i]);
> +            } else if (d->base_addr[i] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
> +                memory_region_init_io(&s->bar[i], NULL, NULL,
> +                                      "xen-pci-pt-bar", d->size[i]);
> +                pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_MEM_PREFETCH,
> +                                 &s->bar[i]);
> +            } else {
> +                memory_region_init_io(&s->bar[i], NULL, NULL,
> +                                      "xen-pci-pt-bar", d->size[i]);
> +                pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_SPACE_MEMORY,
> +                                 &s->bar[i]);
> +            }
> +
> +            PT_LOG("IO region registered (size=0x%08"PRIx64
> +                   " base_addr=0x%08"PRIx64")\n",
> +                   d->size[i], d->base_addr[i]);
> +        }
> +    }
> +
> +    /* Register expansion ROM address */
> +    if ((d->rom_base_addr & PCI_ROM_ADDRESS_MASK) && d->rom_size) {
> +        /* Re-set BAR reported by OS, otherwise ROM can't be read. */
> +        bar_data = host_pci_read_long(d, PCI_ROM_ADDRESS);
> +        if ((bar_data & PCI_ROM_ADDRESS_MASK) == 0) {
> +            bar_data |= d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
> +            host_pci_write_long(d, PCI_ROM_ADDRESS, bar_data);
> +        }
> +
> +        s->bases[PCI_ROM_SLOT].e_physbase = d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
> +        s->bases[PCI_ROM_SLOT].access.maddr = d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
> +
> +        // memory_region_init_rom_device
> +        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("Expansion ROM registered (size=0x%08"PRIx64
> +               " base_addr=0x%08"PRIx64")\n",
> +               d->rom_size, d->rom_base_addr);
> +    }
> +
> +    return 0;
> +}
> +
> +static int pci_parse_host_devaddr(char **str, int *seg, int *bus, int *dev,
> +                                  int *func, char **opt, int *vdevfn)
> +{
> +    char *token;
> +    const char *delim = ":.";
> +
> +    if (!(*str) || (!strchr(*str, ':') && !strchr(*str, '.'))) {
> +        return 0;
> +    }
> +
> +    token  = strsep(str, delim);
> +    *seg = strtoul(token, NULL, 16);
> +
> +    token  = strsep(str, delim);
> +    *bus  = strtoul(token, NULL, 16);
> +
> +    token  = strsep(str, delim);
> +    *dev  = strtoul(token, NULL, 16);
> +
> +    token  = strsep(str, delim);
> +
> +    *opt = strchr(token, '@');
> +    if (*opt) {
> +        *(*opt)++ = '\0';
> +        *vdevfn = strtoul(*opt, NULL, 16);
> +    } else {
> +        *vdevfn = -1; //AUTO_PHP_SLOT;
> +        *opt = token;
> +    }
> +
> +    *opt = strchr(*opt, ',');
> +    if (*opt) {
> +        *(*opt)++ = '\0';
> +    }
> +
> +    *func = strtoul(token, NULL, 16);
> +
> +    return 1;
> +}

all the other functions return 0 on success and a negative number on
error, we should do the same here


> +// TODO add to PlugDevice list php_dev_list;
> +static int pt_initfn(PCIDevice *pci_dev)
> +{
> +    XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, pci_dev);
> +    int devfn_ignored = -1;
> +    int dom, bus;
> +    int slot, func;
> +    char *opt;
> +    int rc = 0;
> +    uint32_t machine_irq;
> +
> +    /* todo: use local/stack buf, or better: change pci_parse_host_devaddr */
> +    char *addr = g_strdup(s->hostaddr);
> +
> +    PT_LOG("initialiase passthrough for %s (devfn: %i)\n", addr, s->dev.devfn);
> +
> +    if (pci_parse_host_devaddr(&addr, &dom, &bus, &slot, &func, &opt,
> +                               &devfn_ignored) < 0) {
> +        fprintf(stderr, "erro parse bdf: %s\n", addr);
> +        return -1;
> +    }
> +
> +    s->php_dev.r_bus = bus;
> +    s->php_dev.r_dev = slot;
> +    s->php_dev.r_func = func;
> +
> +    /* register real device */
> +    PT_LOG("Assigning real physical device %02x:%02x.%x ...\n",
> +           bus, slot, func);
> +
> +    s->real_device = host_pci_device_get(bus, slot, func);
> +    if (!s->real_device) {
> +        return -1;
> +    }
> +
> +    s->msi_trans_cap = 1; // default enable, later, read from param (xenstore)
> +    /* s->power_mgmt = power_mgmt; */
> +    s->is_virtfn = s->real_device->is_virtfn;
> +    if (s->is_virtfn) {
> +        PT_LOG("%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_read_block(s->real_device, 0, pci_dev->config,
> +                            PCI_CONFIG_SPACE_SIZE) == -1) {
> +        return -1;
> +    }
> +
> +    /* Handle real device's MMIO/PIO BARs */
> +    qemu_pci_register_regions(s);
> +
> +    /* reinitialize each config register to be emulated */
> +    pt_config_init(s);
> +
> +    /* Bind interrupt */
> +    if (!s->dev.config[PCI_INTERRUPT_PIN]) {
> +        PT_LOG("no pin interrupt\n");
> +        goto out;
> +    }
> +
> +    /* pt machine irq auto */
> +    {
> +        int pirq = -1;
> +
> +        machine_irq = host_pci_read_byte(s->real_device, PCI_INTERRUPT_LINE);
> +        rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq);
> +
> +        if (rc) {
> +            PT_LOG("Error: Mapping irq failed, rc = %d\n", rc);
> +
> +            /* Disable PCI intx assertion (turn on bit10 of devctl) */
> +            host_pci_write_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]++; */
> +        }
> +    }
> +
> +    /* 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);
> +        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_LOG("Error: Binding of interrupt failed! rc=%d\n", rc);
> +
> +            /* Disable PCI intx assertion (turn on bit10 of devctl) */
> +            host_pci_write_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_LOG("Error: Unmapping of interrupt failed! rc=%d\n",
> +                           rc);
> +                }
> +            /* } */
> +            s->machine_irq = 0;
> +        }
> +    }
> +
> +out:
> +    PT_LOG("Real physical device %02x:%02x.%x registered successfuly!\n"
> +           "IRQ type = %s\n", bus, slot, func,
> +           s->msi_trans_en ? "MSI-INTx" : "INTx");
> +
> +    return 0;
> +}
> +
> +static PCIDeviceInfo xen_pci_passthrough = {
> +    .init = pt_initfn,
> +    .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_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..10e8189
> --- /dev/null
> +++ b/hw/xen_pci_passthrough.h
> @@ -0,0 +1,335 @@
> +#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
> +
> +#ifdef PT_LOGGING_ENABLED
> +#  define PT_LOG(_f, _a...)   fprintf(stderr, "%s: " _f, __func__, ##_a)
> +#else
> +#  define PT_LOG(_f, _a...)
> +#endif
> +
> +#ifdef PT_DEBUG_PCI_CONFIG_ACCESS
> +#  define PT_LOG_CONFIG(_f, _a...) PT_LOG(_f, ##_a)
> +#else
> +#  define PT_LOG_CONFIG(_f, _a...)
> +#endif
> +
> +
> +typedef struct XenPTRegion XenPTRegion;
> +typedef struct XenPTReg XenPTReg;
> +typedef struct XenPTRegInfo XenPTRegInfo;
> +typedef struct XenPTRegGroup XenPTRegGroup;
> +typedef struct XenPTRegGroupInfo XenPTRegGroupInfo;
> +
> +typedef struct XenPCIPassthroughState XenPCIPassthroughState;
> +
> +// fonction type for config reg

function


> +typedef uint32_t (*conf_reg_init)
> +    (XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset);
> +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);
> +
> +struct PHPDev {
> +    /* uint8_t valid; */
> +    uint8_t r_bus;
> +    uint8_t r_dev;
> +    uint8_t r_func;
> +    /* char *opt; */
> +};
> +
> +/* power state transition */
> +#define PT_FLAG_TRANSITING 0x0001
> +
> +#define PT_INVALID_REG          0xFFFFFFFF      /* invalid register value */
> +#define PT_BAR_ALLF             0xFFFFFFFF      /* BAR ALLF value */
> +#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) */
> +
> +/* MSI-X */
> +#define MSI_FLAG_UNINIT 0x1000
> +#define PT_MSI_MAPPED   0x2000
> +
> +#define MSI_DATA_VECTOR_SHIFT          0
> +#define     MSI_DATA_VECTOR(v)         (((u8)v) << MSI_DATA_VECTOR_SHIFT)
> +
> +#define MSI_DATA_DELIVERY_SHIFT        8
> +#define     MSI_DATA_DELIVERY_FIXED    (0 << MSI_DATA_DELIVERY_SHIFT)
> +#define     MSI_DATA_DELIVERY_LOWPRI   (1 << MSI_DATA_DELIVERY_SHIFT)
> +
> +#define MSI_DATA_LEVEL_SHIFT           14
> +#define     MSI_DATA_LEVEL_DEASSERT    (0 << MSI_DATA_LEVEL_SHIFT)
> +#define     MSI_DATA_LEVEL_ASSERT      (1 << MSI_DATA_LEVEL_SHIFT)
> +
> +#define MSI_DATA_TRIGGER_SHIFT         15
> +#define     MSI_DATA_TRIGGER_EDGE      (0 << MSI_DATA_TRIGGER_SHIFT)
> +#define     MSI_DATA_TRIGGER_LEVEL     (1 << MSI_DATA_TRIGGER_SHIFT)

considering that all these MSI defines are general purpose they should
probably be in a general purpose header


> +/*
> + * Shift/mask fields for APIC-based bus address
> + */
> +#define MSI_ADDR_HEADER                0xfee00000
> +#define MSI_TARGET_CPU_SHIFT           12
> +
> +#define MSI_ADDR_DESTID_MASK           0xfff0000f
> +#define   MSI_ADDR_DESTID_CPU(cpu)     ((cpu) << MSI_TARGET_CPU_SHIFT)
> +
> +#define MSI_ADDR_DESTMODE_SHIFT        2
> +#define   MSI_ADDR_DESTMODE_PHYS       (0 << MSI_ADDR_DESTMODE_SHIFT)
> +#define     MSI_ADDR_DESTMODE_LOGIC    (1 << MSI_ADDR_DESTMODE_SHIFT)
> +
> +#define MSI_ADDR_REDIRECTION_SHIFT     3
> +#define   MSI_ADDR_REDIRECTION_CPU     (0 << MSI_ADDR_REDIRECTION_SHIFT)
> +#define   MSI_ADDR_REDIRECTION_LOWPRI  (1 << MSI_ADDR_REDIRECTION_SHIFT)

same as before


> +#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

There seem to be used only be the next patch, it makes sense to move
them there.


> +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;
> +
> +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;
> +};
> +
> +/* emulated register management */
> +struct XenPTReg {
> +    QLIST_ENTRY(XenPTReg) entries;
> +    XenPTRegInfo *reg;
> +    uint32_t data;
> +};
> +
> +/* emul register group management table */
> +struct XenPTRegGroup {
> +    QLIST_ENTRY (XenPTRegGroup) entries;
> +    const XenPTRegGroupInfo *reg_grp;
> +    uint32_t base_offset;
> +    uint8_t size;
> +    QLIST_HEAD(, XenPTReg) reg_tbl_list;
> +};
> +
> +/* emul reg group size initialize method */
> +typedef uint8_t (*pt_reg_size_init_fn)
> +    (XenPCIPassthroughState *, const XenPTRegGroupInfo *,
> +     uint32_t base_offset);
> +
> +/* 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;
> +};
> +
> +
> +/* 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;
> +};
> +
> +typedef struct pt_msi_info {
> +    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 msix_entry_info {
> +    int pirq;        /* -1 means unmapped */
> +    int flags;       /* flags indicting whether MSI ADDR or DATA is updated */
> +    uint32_t io_mem[4];
> +} XenMSIXEntry;
> +typedef struct pt_msix_info {
> +    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;
> +    struct msix_entry_info msix_entry[0];
> +} XenPTMSIX;
> +
> +typedef struct pt_pm_info {
> +    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;
> +    struct PHPDev php_dev;
> +    bool is_virtfn;
> +    HostPCIDevice *real_device;
> +    XenPTRegion bases[PCI_NUM_REGIONS]; /* Access regions */
> +    QLIST_HEAD(, XenPTRegGroup) reg_grp_tbl;
> +
> +    uint32_t machine_irq;
> +
> +    XenPTMSI *msi;
> +    XenPTMSIX *msix;
> +
> +    /* Physical MSI to guest INTx translation when possible */
> +    int msi_trans_cap;
> +    bool msi_trans_en;
> +
> +    bool power_mgmt;
> +    XenPTPM *pm_state;
> +
> +    MemoryRegion bar[PCI_NUM_REGIONS - 1];
> +    MemoryRegion rom;
> +};
> +
> +void pt_config_init(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);
> +
> +int qemu_pci_register_regions(XenPCIPassthroughState *);
> +
> +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;
> +    }
> +}
> +
> +static inline pcibus_t pt_pci_base_addr(pcibus_t base)
> +{
> +    if (base & PCI_BASE_ADDRESS_SPACE_IO)
> +        return base & PCI_BASE_ADDRESS_IO_MASK;
> +
> +    return base & PCI_BASE_ADDRESS_MEM_MASK;
> +}
> +
> +/* INTx */
> +static inline uint8_t pci_read_intx(XenPCIPassthroughState *s)
> +{
> +    return host_pci_read_byte(s->real_device, PCI_INTERRUPT_PIN);
> +}
> +uint8_t pci_intx(XenPCIPassthroughState *ptdev);
> +
> +/* MSI/MSI-X */
> +void msi_set_enable(XenPCIPassthroughState *dev, int en);
> +int pt_msi_setup(XenPCIPassthroughState *dev);
> +int pt_msi_update(XenPCIPassthroughState *d);
> +void pt_msi_disable(XenPCIPassthroughState *dev);
> +int pt_enable_msi_translate(XenPCIPassthroughState* dev);
> +void pt_disable_msi_translate(XenPCIPassthroughState *dev);
> +
> +int pt_msix_init(XenPCIPassthroughState *dev, int pos);
> +int pt_msix_update(XenPCIPassthroughState *dev);
> +int pt_msix_update_remap(XenPCIPassthroughState *dev, int bar_index);
> +void pt_msix_disable(XenPCIPassthroughState *dev);
> +int add_msix_mapping(XenPCIPassthroughState *s, int bar_index);
> +int remove_msix_mapping(XenPCIPassthroughState *s, int bar_index);

These functions are actually implemented in the next patch, so they
should also be defined there.


> +#endif /* !QEMU_HW_XEN_PCI_PASSTHROUGH_H */
> diff --git a/hw/xen_pci_passthrough_helpers.c b/hw/xen_pci_passthrough_helpers.c
> new file mode 100644
> index 0000000..192e918
> --- /dev/null
> +++ b/hw/xen_pci_passthrough_helpers.c
> @@ -0,0 +1,46 @@
> +#include "xen_pci_passthrough.h"
> +
> +/* 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
> +}
> + */
> +uint8_t pci_intx(XenPCIPassthroughState *ptdev)
> +{
> +    uint8_t r_val = pci_read_intx(ptdev);
> +
> +    PT_LOG("intx=%i\n", r_val);
> +    if (r_val < 1 || r_val > 4) {
> +        PT_LOG("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;
> +}
> --
> Anthony PERARD
> 

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCH RFC V1 08/11] Introduce Xen PCI Passthrough, qdevice (1/3)
@ 2011-10-05 11:51     ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-05 11:51 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On Tue, 4 Oct 2011, Anthony PERARD wrote:
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>

You should set the original author correctly and add his signed-off-by.
Remeber to run the patch through checkpatch.pl.

> ---
>  hw/xen_pci_passthrough.c         |  763 ++++++++++++++++++++++++++++++++++++++
>  hw/xen_pci_passthrough.h         |  335 +++++++++++++++++
>  hw/xen_pci_passthrough_helpers.c |   46 +++
>  3 files changed, 1144 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_helpers.c
> 
> diff --git a/hw/xen_pci_passthrough.c b/hw/xen_pci_passthrough.c
> new file mode 100644
> index 0000000..bfbe042
> --- /dev/null
> +++ b/hw/xen_pci_passthrough.c
> @@ -0,0 +1,763 @@
> +#include <sys/ioctl.h>
> +
> +#include "pci.h"
> +#include "xen.h"
> +#include "xen_backend.h"
> +#include "xen_pci_passthrough.h"

it would be nice to keep the comment about PCI-MSI translation we have
in the corresponding source file in qemu-xen


> +#define PCI_BAR_ENTRIES (6)
> +
> +typedef struct PlugDevice {
> +    uint8_t r_bus;
> +    uint8_t r_slot;
> +    uint8_t r_func;
> +    int bus;
> +    int slot;
> +    int func;
> +} PlugDevice;
> +
> +QLIST_HEAD(php_dev_list, PlugDevice) php_dev_list =
> +    QLIST_HEAD_INITIALIZER(php_dev_list);
> +

What is the difference between r_bus,r_slot and r_func and bus, slot and
func? It should be well documented with a comment.
Also do we need both PlugDevice and PHPDev? What is the difference?


> +#define PT_BAR_ALLF             0xFFFFFFFF      /* BAR ALLF value */

already defined in hw/xen_pci_passthrough.h


> +
> +/* #define PT_NR_IRQS          (256) */
> +/* char mapped_machine_irq[PT_NR_IRQS] = {0}; */
> +
> +/* Config Space */
> +static int pt_pci_config_access_check(PCIDevice *d, uint32_t address, int len)
> +{
> +    /* check offset range */
> +    if (address >= 0xFF) {
> +        PT_LOG("Error: Failed to access register with offset exceeding FFh. "
> +               "[%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
> +               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +               address, len);
> +        return -1;
> +    }
> +
> +    /* check read size */
> +    if ((len != 1) && (len != 2) && (len != 4)) {
> +        PT_LOG("Error: Failed to access register with invalid access length. "
> +               "[%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
> +               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +               address, len);
> +        return -1;
> +    }
> +
> +    /* check offset alignment */
> +    if (address & (len - 1)) {
> +        PT_LOG("Error: Failed to access register with invalid access size "
> +            "alignment. [%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
> +            pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +            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 untill previous power state transition is completed.
> +         * so finished previous request here.
> +         */
> +        qemu_run_one_timer(s->pm_state->pm_timer);
> +    }

the timer is not acceptable, we should probably remove it and return an
error instead, unless somebody has any better ideas.


> +    /* 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_read_block(s->real_device, address, (uint8_t *)&val, len);
> +    if (!rc) {
> +        PT_LOG("Error: 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 Guest request 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 depend 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) {
> +                hw_error("Internal error: Invalid read emulation "
> +                         "return value[%d]. I/O emulator exit.\n", rc);
> +            }
> +
> +            /* 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("[%02x:%02x.%x]: address=%04x val=0x%08x len=%d\n",
> +                  pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +                  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("[%02x:%02x.%x]: address=%04x val=0x%08x len=%d\n",
> +                  pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +                  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_LOG("Warning: Guest attempt to set address to unused Base Address "
> +               "Register. [%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
> +               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +               address, len);
> +    }
> +
> +    /* check power state transition flags */
> +    if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) {
> +        /* can't accept untill previous power state transition is completed.
> +         * so finished previous request here.
> +         */
> +        qemu_run_one_timer(s->pm_state->pm_timer);
> +    }

same here

> +    /* 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_LOG("Warning: Access to 0 Hardwired register. "
> +                   "[%02x:%02x.%x][Offset:%02xh][Length:%d]\n",
> +                   pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
> +                   address, len);
> +            return;
> +        }
> +    }
> +
> +    /* read I/O device register value */
> +    rc = host_pci_read_block(s->real_device, address,
> +                             (uint8_t *)&read_val, len);
> +    if (!rc) {
> +        PT_LOG("Error: pci_read_block failed. return value[%d].\n", rc);
> +        memset(&read_val, 0xff, len);
> +    }
> +
> +    /* pass directly to libpci 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 Guest request 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 depend 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) {
> +                hw_error("Internal error: Invalid write emulation "
> +                         "return value[%d]. I/O emulator exit.\n", rc);
> +            }
> +
> +            /* 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 libpci */
> +    val >>= (address & 3) << 3;
> +
> +out:
> +    if (!(reg && reg->no_wb)) {
> +        /* unknown regs are passed through */
> +        rc = host_pci_write_block(s->real_device, address,
> +                                  (uint8_t *)&val, len);
> +
> +        if (!rc) {
> +            PT_LOG("Error: pci_write_block failed. return value[%d].\n", rc);
> +        }
> +    }
> +
> +    if (s->pm_state != NULL && s->pm_state->flags & PT_FLAG_TRANSITING) {
> +        /* set QEMUTimer */
> +        qemu_mod_timer(s->pm_state->pm_timer,
> +                       qemu_get_clock_ms(rt_clock) + s->pm_state->pm_delay);
> +    }
> +}

same here

> +
> +/* ioport/iomem space*/
> +static void pt_iomem_map(XenPCIPassthroughState *s, int i,
> +                         pcibus_t e_phys, pcibus_t e_size, int type)
> +{
> +    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("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 != -1) {
> +        add_msix_mapping(s, i);
> +        /* Remove old mapping */
> +        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_LOG("Error: remove old mapping failed!\n");
> +            return;
> +        }
> +    }
> +
> +    /* map only valid guest address */
> +    if (e_phys != -1) {
> +        /* Create new mapping */
> +        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_LOG("Error: create new mapping failed!\n");
> +        }
> +
> +        ret = remove_msix_mapping(s, i);
> +        if (ret != 0) {
> +            PT_LOG("Error: remove MSI-X mmio mapping failed!\n");
> +        }
> +
> +        if (old_ebase != e_phys && old_ebase != -1) {
> +            pt_msix_update_remap(s, i);
> +        }
> +    }
> +}
> +
> +static void pt_ioport_map(XenPCIPassthroughState *s, int i,
> +                          pcibus_t e_phys, pcibus_t e_size, int type)
> +{
> +    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("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 != -1) {
> +        /* Remove old mapping */
> +        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_LOG("Error: remove old mapping failed!\n");
> +            return;
> +        }
> +    }
> +
> +    /* map only valid guest address (include 0) */
> +    if (e_phys != -1) {
> +        /* Create new mapping */
> +        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_LOG("Error: 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 = -1;
> +    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 = -1;
> +    }
> +    if ((bar == PCI_ROM_SLOT) && (r_addr != -1)) {
> +        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 = -1;
> +            }
> +        }
> +    }
> +
> +    /* prevent guest software mapping memory resource to 00000000h */
> +    if ((base->bar_flag == PT_BAR_FLAG_MEM) && (r_addr == 0)) {
> +        r_addr = -1;
> +    }
> +
> +    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 > 0) {
> +        PT_LOG("Warning: s[%02x:%02x.%x][Region:%d][Address:%"FMT_PCIBUS"h]"
> +               "[Size:%"FMT_PCIBUS"h] is overlapped.\n", pci_bus_num(dev->bus),
> +               PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), 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);
> +    }
> +}
> +
> +/* register regions */
> +int qemu_pci_register_regions(XenPCIPassthroughState *s)
> +{
> +    /* but no mapping done anymore (see in reset) */
> +    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++) {
> +        if (pt_pci_base_addr(d->base_addr[i])) {
> +            s->bases[i].e_physbase = pt_pci_base_addr(d->base_addr[i]);
> +            s->bases[i].access.u = pt_pci_base_addr(d->base_addr[i]);
> +
> +            /* Register current region */
> +            if (d->base_addr[i] & PCI_BASE_ADDRESS_SPACE_IO) {
> +                memory_region_init_io(&s->bar[i], NULL, NULL,
> +                                      "xen-pci-pt-bar", d->size[i]);
> +                pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_SPACE_IO,
> +                                 &s->bar[i]);
> +            } else if (d->base_addr[i] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
> +                memory_region_init_io(&s->bar[i], NULL, NULL,
> +                                      "xen-pci-pt-bar", d->size[i]);
> +                pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_MEM_PREFETCH,
> +                                 &s->bar[i]);
> +            } else {
> +                memory_region_init_io(&s->bar[i], NULL, NULL,
> +                                      "xen-pci-pt-bar", d->size[i]);
> +                pci_register_bar(&s->dev, i, PCI_BASE_ADDRESS_SPACE_MEMORY,
> +                                 &s->bar[i]);
> +            }
> +
> +            PT_LOG("IO region registered (size=0x%08"PRIx64
> +                   " base_addr=0x%08"PRIx64")\n",
> +                   d->size[i], d->base_addr[i]);
> +        }
> +    }
> +
> +    /* Register expansion ROM address */
> +    if ((d->rom_base_addr & PCI_ROM_ADDRESS_MASK) && d->rom_size) {
> +        /* Re-set BAR reported by OS, otherwise ROM can't be read. */
> +        bar_data = host_pci_read_long(d, PCI_ROM_ADDRESS);
> +        if ((bar_data & PCI_ROM_ADDRESS_MASK) == 0) {
> +            bar_data |= d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
> +            host_pci_write_long(d, PCI_ROM_ADDRESS, bar_data);
> +        }
> +
> +        s->bases[PCI_ROM_SLOT].e_physbase = d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
> +        s->bases[PCI_ROM_SLOT].access.maddr = d->rom_base_addr & PCI_ROM_ADDRESS_MASK;
> +
> +        // memory_region_init_rom_device
> +        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("Expansion ROM registered (size=0x%08"PRIx64
> +               " base_addr=0x%08"PRIx64")\n",
> +               d->rom_size, d->rom_base_addr);
> +    }
> +
> +    return 0;
> +}
> +
> +static int pci_parse_host_devaddr(char **str, int *seg, int *bus, int *dev,
> +                                  int *func, char **opt, int *vdevfn)
> +{
> +    char *token;
> +    const char *delim = ":.";
> +
> +    if (!(*str) || (!strchr(*str, ':') && !strchr(*str, '.'))) {
> +        return 0;
> +    }
> +
> +    token  = strsep(str, delim);
> +    *seg = strtoul(token, NULL, 16);
> +
> +    token  = strsep(str, delim);
> +    *bus  = strtoul(token, NULL, 16);
> +
> +    token  = strsep(str, delim);
> +    *dev  = strtoul(token, NULL, 16);
> +
> +    token  = strsep(str, delim);
> +
> +    *opt = strchr(token, '@');
> +    if (*opt) {
> +        *(*opt)++ = '\0';
> +        *vdevfn = strtoul(*opt, NULL, 16);
> +    } else {
> +        *vdevfn = -1; //AUTO_PHP_SLOT;
> +        *opt = token;
> +    }
> +
> +    *opt = strchr(*opt, ',');
> +    if (*opt) {
> +        *(*opt)++ = '\0';
> +    }
> +
> +    *func = strtoul(token, NULL, 16);
> +
> +    return 1;
> +}

all the other functions return 0 on success and a negative number on
error, we should do the same here


> +// TODO add to PlugDevice list php_dev_list;
> +static int pt_initfn(PCIDevice *pci_dev)
> +{
> +    XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, pci_dev);
> +    int devfn_ignored = -1;
> +    int dom, bus;
> +    int slot, func;
> +    char *opt;
> +    int rc = 0;
> +    uint32_t machine_irq;
> +
> +    /* todo: use local/stack buf, or better: change pci_parse_host_devaddr */
> +    char *addr = g_strdup(s->hostaddr);
> +
> +    PT_LOG("initialiase passthrough for %s (devfn: %i)\n", addr, s->dev.devfn);
> +
> +    if (pci_parse_host_devaddr(&addr, &dom, &bus, &slot, &func, &opt,
> +                               &devfn_ignored) < 0) {
> +        fprintf(stderr, "erro parse bdf: %s\n", addr);
> +        return -1;
> +    }
> +
> +    s->php_dev.r_bus = bus;
> +    s->php_dev.r_dev = slot;
> +    s->php_dev.r_func = func;
> +
> +    /* register real device */
> +    PT_LOG("Assigning real physical device %02x:%02x.%x ...\n",
> +           bus, slot, func);
> +
> +    s->real_device = host_pci_device_get(bus, slot, func);
> +    if (!s->real_device) {
> +        return -1;
> +    }
> +
> +    s->msi_trans_cap = 1; // default enable, later, read from param (xenstore)
> +    /* s->power_mgmt = power_mgmt; */
> +    s->is_virtfn = s->real_device->is_virtfn;
> +    if (s->is_virtfn) {
> +        PT_LOG("%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_read_block(s->real_device, 0, pci_dev->config,
> +                            PCI_CONFIG_SPACE_SIZE) == -1) {
> +        return -1;
> +    }
> +
> +    /* Handle real device's MMIO/PIO BARs */
> +    qemu_pci_register_regions(s);
> +
> +    /* reinitialize each config register to be emulated */
> +    pt_config_init(s);
> +
> +    /* Bind interrupt */
> +    if (!s->dev.config[PCI_INTERRUPT_PIN]) {
> +        PT_LOG("no pin interrupt\n");
> +        goto out;
> +    }
> +
> +    /* pt machine irq auto */
> +    {
> +        int pirq = -1;
> +
> +        machine_irq = host_pci_read_byte(s->real_device, PCI_INTERRUPT_LINE);
> +        rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq);
> +
> +        if (rc) {
> +            PT_LOG("Error: Mapping irq failed, rc = %d\n", rc);
> +
> +            /* Disable PCI intx assertion (turn on bit10 of devctl) */
> +            host_pci_write_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]++; */
> +        }
> +    }
> +
> +    /* 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);
> +        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_LOG("Error: Binding of interrupt failed! rc=%d\n", rc);
> +
> +            /* Disable PCI intx assertion (turn on bit10 of devctl) */
> +            host_pci_write_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_LOG("Error: Unmapping of interrupt failed! rc=%d\n",
> +                           rc);
> +                }
> +            /* } */
> +            s->machine_irq = 0;
> +        }
> +    }
> +
> +out:
> +    PT_LOG("Real physical device %02x:%02x.%x registered successfuly!\n"
> +           "IRQ type = %s\n", bus, slot, func,
> +           s->msi_trans_en ? "MSI-INTx" : "INTx");
> +
> +    return 0;
> +}
> +
> +static PCIDeviceInfo xen_pci_passthrough = {
> +    .init = pt_initfn,
> +    .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_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..10e8189
> --- /dev/null
> +++ b/hw/xen_pci_passthrough.h
> @@ -0,0 +1,335 @@
> +#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
> +
> +#ifdef PT_LOGGING_ENABLED
> +#  define PT_LOG(_f, _a...)   fprintf(stderr, "%s: " _f, __func__, ##_a)
> +#else
> +#  define PT_LOG(_f, _a...)
> +#endif
> +
> +#ifdef PT_DEBUG_PCI_CONFIG_ACCESS
> +#  define PT_LOG_CONFIG(_f, _a...) PT_LOG(_f, ##_a)
> +#else
> +#  define PT_LOG_CONFIG(_f, _a...)
> +#endif
> +
> +
> +typedef struct XenPTRegion XenPTRegion;
> +typedef struct XenPTReg XenPTReg;
> +typedef struct XenPTRegInfo XenPTRegInfo;
> +typedef struct XenPTRegGroup XenPTRegGroup;
> +typedef struct XenPTRegGroupInfo XenPTRegGroupInfo;
> +
> +typedef struct XenPCIPassthroughState XenPCIPassthroughState;
> +
> +// fonction type for config reg

function


> +typedef uint32_t (*conf_reg_init)
> +    (XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset);
> +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);
> +
> +struct PHPDev {
> +    /* uint8_t valid; */
> +    uint8_t r_bus;
> +    uint8_t r_dev;
> +    uint8_t r_func;
> +    /* char *opt; */
> +};
> +
> +/* power state transition */
> +#define PT_FLAG_TRANSITING 0x0001
> +
> +#define PT_INVALID_REG          0xFFFFFFFF      /* invalid register value */
> +#define PT_BAR_ALLF             0xFFFFFFFF      /* BAR ALLF value */
> +#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) */
> +
> +/* MSI-X */
> +#define MSI_FLAG_UNINIT 0x1000
> +#define PT_MSI_MAPPED   0x2000
> +
> +#define MSI_DATA_VECTOR_SHIFT          0
> +#define     MSI_DATA_VECTOR(v)         (((u8)v) << MSI_DATA_VECTOR_SHIFT)
> +
> +#define MSI_DATA_DELIVERY_SHIFT        8
> +#define     MSI_DATA_DELIVERY_FIXED    (0 << MSI_DATA_DELIVERY_SHIFT)
> +#define     MSI_DATA_DELIVERY_LOWPRI   (1 << MSI_DATA_DELIVERY_SHIFT)
> +
> +#define MSI_DATA_LEVEL_SHIFT           14
> +#define     MSI_DATA_LEVEL_DEASSERT    (0 << MSI_DATA_LEVEL_SHIFT)
> +#define     MSI_DATA_LEVEL_ASSERT      (1 << MSI_DATA_LEVEL_SHIFT)
> +
> +#define MSI_DATA_TRIGGER_SHIFT         15
> +#define     MSI_DATA_TRIGGER_EDGE      (0 << MSI_DATA_TRIGGER_SHIFT)
> +#define     MSI_DATA_TRIGGER_LEVEL     (1 << MSI_DATA_TRIGGER_SHIFT)

considering that all these MSI defines are general purpose they should
probably be in a general purpose header


> +/*
> + * Shift/mask fields for APIC-based bus address
> + */
> +#define MSI_ADDR_HEADER                0xfee00000
> +#define MSI_TARGET_CPU_SHIFT           12
> +
> +#define MSI_ADDR_DESTID_MASK           0xfff0000f
> +#define   MSI_ADDR_DESTID_CPU(cpu)     ((cpu) << MSI_TARGET_CPU_SHIFT)
> +
> +#define MSI_ADDR_DESTMODE_SHIFT        2
> +#define   MSI_ADDR_DESTMODE_PHYS       (0 << MSI_ADDR_DESTMODE_SHIFT)
> +#define     MSI_ADDR_DESTMODE_LOGIC    (1 << MSI_ADDR_DESTMODE_SHIFT)
> +
> +#define MSI_ADDR_REDIRECTION_SHIFT     3
> +#define   MSI_ADDR_REDIRECTION_CPU     (0 << MSI_ADDR_REDIRECTION_SHIFT)
> +#define   MSI_ADDR_REDIRECTION_LOWPRI  (1 << MSI_ADDR_REDIRECTION_SHIFT)

same as before


> +#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

There seem to be used only be the next patch, it makes sense to move
them there.


> +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;
> +
> +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;
> +};
> +
> +/* emulated register management */
> +struct XenPTReg {
> +    QLIST_ENTRY(XenPTReg) entries;
> +    XenPTRegInfo *reg;
> +    uint32_t data;
> +};
> +
> +/* emul register group management table */
> +struct XenPTRegGroup {
> +    QLIST_ENTRY (XenPTRegGroup) entries;
> +    const XenPTRegGroupInfo *reg_grp;
> +    uint32_t base_offset;
> +    uint8_t size;
> +    QLIST_HEAD(, XenPTReg) reg_tbl_list;
> +};
> +
> +/* emul reg group size initialize method */
> +typedef uint8_t (*pt_reg_size_init_fn)
> +    (XenPCIPassthroughState *, const XenPTRegGroupInfo *,
> +     uint32_t base_offset);
> +
> +/* 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;
> +};
> +
> +
> +/* 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;
> +};
> +
> +typedef struct pt_msi_info {
> +    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 msix_entry_info {
> +    int pirq;        /* -1 means unmapped */
> +    int flags;       /* flags indicting whether MSI ADDR or DATA is updated */
> +    uint32_t io_mem[4];
> +} XenMSIXEntry;
> +typedef struct pt_msix_info {
> +    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;
> +    struct msix_entry_info msix_entry[0];
> +} XenPTMSIX;
> +
> +typedef struct pt_pm_info {
> +    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;
> +    struct PHPDev php_dev;
> +    bool is_virtfn;
> +    HostPCIDevice *real_device;
> +    XenPTRegion bases[PCI_NUM_REGIONS]; /* Access regions */
> +    QLIST_HEAD(, XenPTRegGroup) reg_grp_tbl;
> +
> +    uint32_t machine_irq;
> +
> +    XenPTMSI *msi;
> +    XenPTMSIX *msix;
> +
> +    /* Physical MSI to guest INTx translation when possible */
> +    int msi_trans_cap;
> +    bool msi_trans_en;
> +
> +    bool power_mgmt;
> +    XenPTPM *pm_state;
> +
> +    MemoryRegion bar[PCI_NUM_REGIONS - 1];
> +    MemoryRegion rom;
> +};
> +
> +void pt_config_init(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);
> +
> +int qemu_pci_register_regions(XenPCIPassthroughState *);
> +
> +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;
> +    }
> +}
> +
> +static inline pcibus_t pt_pci_base_addr(pcibus_t base)
> +{
> +    if (base & PCI_BASE_ADDRESS_SPACE_IO)
> +        return base & PCI_BASE_ADDRESS_IO_MASK;
> +
> +    return base & PCI_BASE_ADDRESS_MEM_MASK;
> +}
> +
> +/* INTx */
> +static inline uint8_t pci_read_intx(XenPCIPassthroughState *s)
> +{
> +    return host_pci_read_byte(s->real_device, PCI_INTERRUPT_PIN);
> +}
> +uint8_t pci_intx(XenPCIPassthroughState *ptdev);
> +
> +/* MSI/MSI-X */
> +void msi_set_enable(XenPCIPassthroughState *dev, int en);
> +int pt_msi_setup(XenPCIPassthroughState *dev);
> +int pt_msi_update(XenPCIPassthroughState *d);
> +void pt_msi_disable(XenPCIPassthroughState *dev);
> +int pt_enable_msi_translate(XenPCIPassthroughState* dev);
> +void pt_disable_msi_translate(XenPCIPassthroughState *dev);
> +
> +int pt_msix_init(XenPCIPassthroughState *dev, int pos);
> +int pt_msix_update(XenPCIPassthroughState *dev);
> +int pt_msix_update_remap(XenPCIPassthroughState *dev, int bar_index);
> +void pt_msix_disable(XenPCIPassthroughState *dev);
> +int add_msix_mapping(XenPCIPassthroughState *s, int bar_index);
> +int remove_msix_mapping(XenPCIPassthroughState *s, int bar_index);

These functions are actually implemented in the next patch, so they
should also be defined there.


> +#endif /* !QEMU_HW_XEN_PCI_PASSTHROUGH_H */
> diff --git a/hw/xen_pci_passthrough_helpers.c b/hw/xen_pci_passthrough_helpers.c
> new file mode 100644
> index 0000000..192e918
> --- /dev/null
> +++ b/hw/xen_pci_passthrough_helpers.c
> @@ -0,0 +1,46 @@
> +#include "xen_pci_passthrough.h"
> +
> +/* 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
> +}
> + */
> +uint8_t pci_intx(XenPCIPassthroughState *ptdev)
> +{
> +    uint8_t r_val = pci_read_intx(ptdev);
> +
> +    PT_LOG("intx=%i\n", r_val);
> +    if (r_val < 1 || r_val > 4) {
> +        PT_LOG("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;
> +}
> --
> Anthony PERARD
> 

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [PATCH RFC V1 10/11] Introduce Xen PCI Passthrough, MSI (3/3)
  2011-10-04 14:51   ` Anthony PERARD
@ 2011-10-05 11:51     ` Stefano Stabellini
  -1 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-05 11:51 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On Tue, 4 Oct 2011, Anthony PERARD wrote:
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>

You should set the original author of this patch correctly and add his
signed-off-by.
Remember to run the patch through checkpatch.pl.

> ---
>  hw/xen_pci_passthrough_msi.c |  674 ++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 674 insertions(+), 0 deletions(-)
>  create mode 100644 hw/xen_pci_passthrough_msi.c
> 
> diff --git a/hw/xen_pci_passthrough_msi.c b/hw/xen_pci_passthrough_msi.c
> new file mode 100644
> index 0000000..be18ff1
> --- /dev/null
> +++ b/hw/xen_pci_passthrough_msi.c
> @@ -0,0 +1,674 @@
> +/*
> + * Copyright (c) 2007, Intel Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
> + * Place - Suite 330, Boston, MA 02111-1307 USA.
> + *
> + * 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"
> +
> +void msi_set_enable(XenPCIPassthroughState *dev, int en)
> +{
> +    uint16_t val = 0;
> +    uint32_t address = 0;
> +    PT_LOG("enable: %i\n", en);
> +
> +    if (!dev->msi) {
> +        return;
> +    }
> +
> +    address = dev->msi->ctrl_offset;
> +    if (!address) {
> +        return;
> +    }
> +
> +    val = host_pci_read_word(dev->real_device, address);
> +    val &= ~PCI_MSI_FLAGS_ENABLE;
> +    val |= en & PCI_MSI_FLAGS_ENABLE;
> +    host_pci_write_word(dev->real_device, address, val);
> +
> +    PT_LOG("done, address: %#x, val: %#x\n", address, val);
> +}
> +
> +static void msix_set_enable(XenPCIPassthroughState *dev, int en)
> +{
> +    uint16_t val = 0;
> +    uint32_t address = 0;
> +
> +    if (!dev->msix) {
> +        return;
> +    }
> +
> +    address = dev->msix->ctrl_offset;
> +    if (!address) {
> +        return;
> +    }
> +
> +    val = host_pci_read_word(dev->real_device, address);
> +    val &= ~PCI_MSIX_FLAGS_ENABLE;
> +    if (en) {
> +        val |= PCI_MSIX_FLAGS_ENABLE;
> +    }
> +    host_pci_write_word(dev->real_device, address, val);
> +}
> +
> +/*********************************/
> +/* MSI virtuailization functions */
> +
> +/*
> + * setup physical msi, but didn't enable it
> + */
> +int pt_msi_setup(XenPCIPassthroughState *dev)
> +{
> +    int pirq = -1;
> +    uint8_t gvec = 0;
> +
> +    if (!(dev->msi->flags & MSI_FLAG_UNINIT)) {
> +        PT_LOG("Error: setup physical after initialized?? \n");
> +        return -1;
> +    }
> +
> +    gvec = dev->msi->data & 0xFF;
> +    if (!gvec) {
> +        /* if gvec is 0, the guest is asking for a particular pirq that
> +         * is passed as dest_id */
> +        pirq = (dev->msi->addr_hi & 0xffffff00) |
> +               ((dev->msi->addr_lo >> MSI_TARGET_CPU_SHIFT) & 0xff);
> +        if (!pirq) {
> +            /* this probably identifies an misconfiguration of the guest,
> +             * try the emulated path */
> +            pirq = -1;
> +        } else {
> +            PT_LOG("pt_msi_setup requested pirq = %d\n", pirq);
> +        }
> +    }
> +
> +    if (xc_physdev_map_pirq_msi(xen_xc, xen_domid, AUTO_ASSIGN, &pirq,
> +                                PCI_DEVFN(dev->real_device->dev,
> +                                          dev->real_device->func),
> +                                dev->real_device->bus, 0, 0)) {
> +        PT_LOG("Error: Mapping of MSI failed.\n");
> +        return -1;
> +    }
> +
> +    if (pirq < 0) {
> +        PT_LOG("Error: Invalid pirq number\n");
> +        return -1;
> +    }
> +
> +    dev->msi->pirq = pirq;
> +    PT_LOG("msi mapped with pirq %x\n", pirq);
> +
> +    return 0;
> +}
> +
> +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_DESTMODE_SHIFT) & 0x1;
> +    dest_id = (addr >> MSI_TARGET_CPU_SHIFT) & 0xff;
> +    deliv_mode = (data >> MSI_DATA_DELIVERY_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;
> +}
> +
> +int pt_msi_update(XenPCIPassthroughState *d)
> +{
> +    uint8_t gvec = 0;
> +    uint32_t gflags = 0;
> +    uint64_t addr = 0;
> +    int ret = 0;
> +
> +    /* get vector, address, flags info, etc. */
> +    gvec = d->msi->data & 0xFF;
> +    addr = (uint64_t)d->msi->addr_hi << 32 | d->msi->addr_lo;
> +    gflags = __get_msi_gflags(d->msi->data, addr);
> +
> +    PT_LOG("Update msi with pirq %x gvec %x gflags %x\n",
> +           d->msi->pirq, gvec, gflags);
> +
> +    ret = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec,
> +                                   d->msi->pirq, gflags, 0);
> +
> +    if (ret) {
> +        PT_LOG("Error: Binding of MSI failed.\n");
> +
> +        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, d->msi->pirq)) {
> +            PT_LOG("Error: Unmapping of MSI failed.\n");
> +        }
> +        d->msi->pirq = -1;
> +        return ret;
> +    }
> +    return 0;
> +}
> +
> +void pt_msi_disable(XenPCIPassthroughState *dev)
> +{
> +    PCIDevice *d = &dev->dev;
> +    uint8_t gvec = 0;
> +    uint32_t gflags = 0;
> +    uint64_t addr = 0;
> +    uint8_t e_device = 0;
> +    uint8_t e_intx = 0;
> +
> +    msi_set_enable(dev, 0);
> +
> +    e_device = PCI_SLOT(dev->dev.devfn);
> +    e_intx = pci_intx(dev);
> +
> +    if (dev->msi_trans_en) {
> +        if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, dev->msi->pirq,
> +                                    PT_IRQ_TYPE_MSI_TRANSLATE, 0,
> +                                    e_device, e_intx, 0)) {
> +            PT_LOG("Error: Unbinding pt irq for MSI-INTx failed!\n");
> +            goto out;
> +        }
> +    } else if (!(dev->msi->flags & MSI_FLAG_UNINIT)) {
> +        /* get vector, address, flags info, etc. */
> +        gvec = dev->msi->data & 0xFF;
> +        addr = (uint64_t)dev->msi->addr_hi << 32 | dev->msi->addr_lo;
> +        gflags = __get_msi_gflags(dev->msi->data, addr);
> +
> +        PT_LOG("Unbind msi with pirq %x, gvec %x\n",
> +                dev->msi->pirq, gvec);
> +
> +        if (xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec,
> +                                        dev->msi->pirq, gflags)) {
> +            PT_LOG("Error: Unbinding of MSI failed. [%02x:%02x.%x]\n",
> +                   pci_bus_num(d->bus), PCI_SLOT(d->devfn),
> +                   PCI_FUNC(d->devfn));
> +            goto out;
> +        }
> +    }
> +
> +    if (dev->msi->pirq != -1) {
> +        PT_LOG("Unmap msi with pirq %x\n", dev->msi->pirq);
> +
> +        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, dev->msi->pirq)) {
> +            PT_LOG("Error: Unmapping of MSI failed. [%02x:%02x.%x]\n",
> +                   pci_bus_num(d->bus), PCI_SLOT(d->devfn),
> +                   PCI_FUNC(d->devfn));
> +            goto out;
> +        }
> +    }
> +
> +out:
> +    /* clear msi info */
> +    dev->msi->flags = 0;
> +    dev->msi->pirq = -1;
> +    dev->msi_trans_en = 0;
> +}
> +
> +/* MSI-INTx translation virtulization functions */
> +int pt_enable_msi_translate(XenPCIPassthroughState* dev)
> +{
> +    uint8_t e_device = 0;
> +    uint8_t e_intx = 0;
> +
> +    if (!(dev->msi && dev->msi_trans_cap)) {
> +        return -1;
> +    }
> +
> +    msi_set_enable(dev, 0);
> +    dev->msi_trans_en = 0;
> +
> +    if (pt_msi_setup(dev)) {
> +        PT_LOG("Error: MSI-INTx translation MSI setup failed, fallback\n");
> +        return -1;
> +    }
> +
> +    e_device = PCI_SLOT(dev->dev.devfn);
> +    /* fix virtual interrupt pin to INTA# */
> +    e_intx = pci_intx(dev);
> +
> +    if (xc_domain_bind_pt_irq(xen_xc, xen_domid, dev->msi->pirq,
> +                              PT_IRQ_TYPE_MSI_TRANSLATE, 0,
> +                              e_device, e_intx, 0)) {
> +        PT_LOG("Error: MSI-INTx translation bind failed, fallback\n");
> +
> +        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, dev->msi->pirq)) {
> +            PT_LOG("Error: Unmapping of MSI failed.\n");
> +        }
> +        dev->msi->pirq = -1;
> +        return -1;
> +    }
> +
> +    msi_set_enable(dev, 1);
> +    dev->msi_trans_en = 1;
> +
> +    return 0;
> +}
> +
> +void pt_disable_msi_translate(XenPCIPassthroughState *dev)
> +{
> +    uint8_t e_device = 0;
> +    uint8_t e_intx = 0;
> +
> +    /* MSI_ENABLE bit should be disabed until the new handler is set */
> +    msi_set_enable(dev, 0);
> +
> +    e_device = PCI_SLOT(dev->dev.devfn);
> +    e_intx = pci_intx(dev);
> +
> +    if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, dev->msi->pirq,
> +                                 PT_IRQ_TYPE_MSI_TRANSLATE, 0,
> +                                 e_device, e_intx, 0)) {
> +        PT_LOG("Error: Unbinding pt irq for MSI-INTx failed!\n");
> +    }
> +
> +    if (dev->machine_irq) {
> +        if (xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, dev->machine_irq,
> +                                       0, e_device, e_intx)) {
> +            PT_LOG("Error: Rebinding of interrupt failed!\n");
> +        }
> +    }
> +
> +    dev->msi_trans_en = 0;
> +}
> +
> +/*********************************/
> +/* MSI-X virtulization functions */
> +
> +static void mask_physical_msix_entry(XenPCIPassthroughState *dev,
> +                                     int entry_nr, int mask)
> +{
> +    void *phys_off;
> +
> +    phys_off = dev->msix->phys_iomem_base + 16 * entry_nr + 12;
> +    *(uint32_t *)phys_off = mask;
> +}
> +
> +static int pt_msix_update_one(XenPCIPassthroughState *dev, int entry_nr)
> +{
> +    struct msix_entry_info *entry = &dev->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 ret;
> +
> +    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_TARGET_CPU_SHIFT) & 0xff);
> +        if (!pirq) {
> +            /* this probably identifies an misconfiguration of the guest,
> +             * try the emulated path */
> +            pirq = -1;
> +        } else {
> +            PT_LOG("pt_msix_update_one requested pirq = %d\n", pirq);
> +        }
> +    }
> +
> +    /* Check if this entry is already mapped */
> +    if (entry->pirq == -1) {
> +        ret = xc_physdev_map_pirq_msi(xen_xc, xen_domid, AUTO_ASSIGN, &pirq,
> +                                      PCI_DEVFN(dev->real_device->dev,
> +                                                dev->real_device->func),
> +                                      dev->real_device->bus, entry_nr,
> +                                      dev->msix->table_base);
> +        if (ret) {
> +            PT_LOG("Error: Mapping msix entry %x\n", entry_nr);
> +            return ret;
> +        }
> +        entry->pirq = pirq;
> +    }
> +
> +    PT_LOG("Update msix entry %x with pirq %x gvec %x\n",
> +            entry_nr, pirq, gvec);
> +
> +    ret = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec, pirq, gflags,
> +                                   dev->msix->mmio_base_addr);
> +    if (ret) {
> +        PT_LOG("Error: Updating msix irq info for entry %d\n", entry_nr);
> +
> +        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, entry->pirq)) {
> +            PT_LOG("Error: Unmapping of MSI-X failed.\n");
> +        }
> +        entry->pirq = -1;
> +        return ret;
> +    }
> +
> +    entry->flags = 0;
> +
> +    return 0;
> +}
> +
> +int pt_msix_update(XenPCIPassthroughState *dev)
> +{
> +    struct pt_msix_info *msix = dev->msix;
> +    int i;
> +
> +    for (i = 0; i < msix->total_entries; i++) {
> +        pt_msix_update_one(dev, i);
> +    }
> +
> +    return 0;
> +}
> +
> +void pt_msix_disable(XenPCIPassthroughState *dev)
> +{
> +    PCIDevice *d = &dev->dev;
> +    uint8_t gvec = 0;
> +    uint32_t gflags = 0;
> +    uint64_t addr = 0;
> +    int i = 0;
> +    struct msix_entry_info *entry = NULL;
> +
> +    msix_set_enable(dev, 0);
> +
> +    for ( i = 0; i < dev->msix->total_entries; i++ ) {
> +        entry = &dev->msix->msix_entry[i];
> +
> +        if (entry->pirq == -1) {
> +            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("Unbind msix with pirq %x, gvec %x\n",
> +                entry->pirq, gvec);
> +
> +        if (xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec,
> +                                        entry->pirq, gflags)) {
> +            PT_LOG("Error: Unbinding of MSI-X failed. [%02x:%02x.%x]\n",
> +                   pci_bus_num(d->bus), PCI_SLOT(d->devfn),
> +                   PCI_FUNC(d->devfn));
> +        } else {
> +            PT_LOG("Unmap msix with pirq %x\n", entry->pirq);
> +
> +            if (xc_physdev_unmap_pirq(xen_xc, xen_domid, entry->pirq)) {
> +                PT_LOG("Error: Unmapping of MSI-X failed. [%02x:%02x.%x]\n",
> +                       pci_bus_num(d->bus),
> +                       PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
> +            }
> +        }
> +        /* clear msi-x info */
> +        entry->pirq = -1;
> +        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 != -1) {
> +            ret = xc_domain_unbind_pt_irq(xen_xc, xen_domid, entry->pirq,
> +                                          PT_IRQ_TYPE_MSI, 0, 0, 0, 0);
> +            if (ret) {
> +                PT_LOG("Error: 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)
> +{
> +    PT_LOG("Error: 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 *dev = (XenPCIPassthroughState *)opaque;
> +    XenPTMSIX *msix = dev->msix;
> +    XenMSIXEntry *entry;
> +    int entry_nr, offset;
> +    void *phys_off;
> +    uint32_t vec_ctrl;
> +
> +    if (addr % 4) {
> +        PT_LOG("Error: Unaligned dword access to MSI-X table, "
> +                "addr %016"PRIx64"\n", addr);
> +        return;
> +    }
> +
> +    PT_LOG("addr: "TARGET_FMT_plx", val: %#x\n", addr, val);
> +
> +    // FIXME/TODO I maybe have to do the same in readl !
> +    /* entry_nr = (addr - msix->mmio_base_addr) / 16; */

what is the problem here?


> +    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 = dev->msix->phys_iomem_base + 16 * entry_nr + 12;
> +    vec_ctrl = *(uint32_t *)phys_off;
> +
> +    if (offset != 3 && msix->enabled && !(vec_ctrl & 0x1)) {
> +        PT_LOG("Error: Can't update msix entry %d since MSI-X is already "
> +                "function.\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(dev, entry_nr);
> +        }
> +        mask_physical_msix_entry(dev, 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)
> +{
> +    PT_LOG("Error: 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 *dev = (XenPCIPassthroughState *)opaque;
> +    struct pt_msix_info *msix = dev->msix;
> +    int entry_nr, offset;
> +
> +    if (addr % 4) {
> +        PT_LOG("Error: Unaligned dword access to MSI-X table, "
> +                "addr %016"PRIx64"\n", addr);
> +        return 0;
> +    }
> +
> +    PT_LOG("addr: "TARGET_FMT_plx"\n", addr);
> +
> +    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 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 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 *dev, int pos)
> +{
> +    uint8_t id;
> +    uint16_t control;
> +    int i, total_entries, table_off, bar_index;
> +    HostPCIDevice *pd = dev->real_device;
> +    int fd;
> +
> +    id = host_pci_read_byte(pd, pos + PCI_CAP_LIST_ID);
> +
> +    if (id != PCI_CAP_ID_MSIX) {
> +        PT_LOG("Error: Invalid id %#x pos %#x\n", id, pos);
> +        return -1;
> +    }
> +
> +    control = host_pci_read_word(pd, pos + 2);
> +    total_entries = control & 0x7ff;
> +    total_entries += 1;
> +
> +    dev->msix = g_malloc0(sizeof (struct pt_msix_info)
> +                          + total_entries * sizeof (struct msix_entry_info));
> +
> +    dev->msix->total_entries = total_entries;
> +    for (i = 0; i < total_entries; i++) {
> +        dev->msix->msix_entry[i].pirq = -1;
> +    }
> +
> +    dev->msix->mmio_index =
> +        cpu_register_io_memory(pci_msix_read, pci_msix_write,
> +                               dev, DEVICE_NATIVE_ENDIAN);
> +
> +    table_off = host_pci_read_long(pd, pos + PCI_MSIX_TABLE);
> +    bar_index = dev->msix->bar_index = table_off & PCI_MSIX_FLAGS_BIRMASK;
> +    table_off = dev->msix->table_off = table_off & ~PCI_MSIX_FLAGS_BIRMASK;
> +    dev->msix->table_base =
> +        pt_pci_base_addr(dev->real_device->base_addr[bar_index]);
> +    PT_LOG("get MSI-X table bar base %#"PRIx64"\n", dev->msix->table_base);
> +
> +    fd = open("/dev/mem", O_RDWR);
> +    if (fd == -1) {
> +        PT_LOG("Error: Can't open /dev/mem: %s\n", strerror(errno));
> +        goto error_out;
> +    }
> +    PT_LOG("table_off = %#x, total_entries = %d\n", table_off, total_entries);
> +    dev->msix->table_offset_adjust = table_off & 0x0fff;
> +    dev->msix->phys_iomem_base = mmap(0,
> +                                      total_entries * 16
> +                                      + dev->msix->table_offset_adjust,
> +                                      PROT_WRITE | PROT_READ,
> +                                      MAP_SHARED | MAP_LOCKED,
> +                                      fd,
> +                                      dev->msix->table_base + table_off
> +                                      - dev->msix->table_offset_adjust);
> +    if (dev->msix->phys_iomem_base == MAP_FAILED) {
> +        PT_LOG("Error: Can't map physical MSI-X table: %s\n", strerror(errno));
> +        close(fd);
> +        goto error_out;
> +    }
> +    dev->msix->phys_iomem_base = ((char *)dev->msix->phys_iomem_base +
> +                                  dev->msix->table_offset_adjust);
> +
> +    close(fd);
> +
> +    PT_LOG("mapping physical MSI-X table to %p\n", dev->msix->phys_iomem_base);
> +    return 0;
> +
> +error_out:
> +    g_free(dev->msix);
> +    dev->msix = NULL;
> +    return -1;
> +}
> +
> +#if 0
> +static void pt_msix_delete(XenPCIPassthroughState *dev)
> +{
> +    /* unmap the MSI-X memory mapped register area */
> +    if (dev->msix->phys_iomem_base)
> +    {
> +        PT_LOG("unmapping physical MSI-X table from %lx\n",
> +           (unsigned long)dev->msix->phys_iomem_base);
> +        munmap(dev->msix->phys_iomem_base, dev->msix->total_entries * 16 +
> +           dev->msix->table_offset_adjust);
> +    }
> +
> +    if (dev->msix->mmio_index > 0)
> +    {
> +        cpu_unregister_io_memory(dev->msix->mmio_index);
> +    }
> +
> +
> +    free(dev->msix);
> +}
> +#endif
> --
> Anthony PERARD
> 

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCH RFC V1 10/11] Introduce Xen PCI Passthrough, MSI (3/3)
@ 2011-10-05 11:51     ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-05 11:51 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On Tue, 4 Oct 2011, Anthony PERARD wrote:
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>

You should set the original author of this patch correctly and add his
signed-off-by.
Remember to run the patch through checkpatch.pl.

> ---
>  hw/xen_pci_passthrough_msi.c |  674 ++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 674 insertions(+), 0 deletions(-)
>  create mode 100644 hw/xen_pci_passthrough_msi.c
> 
> diff --git a/hw/xen_pci_passthrough_msi.c b/hw/xen_pci_passthrough_msi.c
> new file mode 100644
> index 0000000..be18ff1
> --- /dev/null
> +++ b/hw/xen_pci_passthrough_msi.c
> @@ -0,0 +1,674 @@
> +/*
> + * Copyright (c) 2007, Intel Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
> + * Place - Suite 330, Boston, MA 02111-1307 USA.
> + *
> + * 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"
> +
> +void msi_set_enable(XenPCIPassthroughState *dev, int en)
> +{
> +    uint16_t val = 0;
> +    uint32_t address = 0;
> +    PT_LOG("enable: %i\n", en);
> +
> +    if (!dev->msi) {
> +        return;
> +    }
> +
> +    address = dev->msi->ctrl_offset;
> +    if (!address) {
> +        return;
> +    }
> +
> +    val = host_pci_read_word(dev->real_device, address);
> +    val &= ~PCI_MSI_FLAGS_ENABLE;
> +    val |= en & PCI_MSI_FLAGS_ENABLE;
> +    host_pci_write_word(dev->real_device, address, val);
> +
> +    PT_LOG("done, address: %#x, val: %#x\n", address, val);
> +}
> +
> +static void msix_set_enable(XenPCIPassthroughState *dev, int en)
> +{
> +    uint16_t val = 0;
> +    uint32_t address = 0;
> +
> +    if (!dev->msix) {
> +        return;
> +    }
> +
> +    address = dev->msix->ctrl_offset;
> +    if (!address) {
> +        return;
> +    }
> +
> +    val = host_pci_read_word(dev->real_device, address);
> +    val &= ~PCI_MSIX_FLAGS_ENABLE;
> +    if (en) {
> +        val |= PCI_MSIX_FLAGS_ENABLE;
> +    }
> +    host_pci_write_word(dev->real_device, address, val);
> +}
> +
> +/*********************************/
> +/* MSI virtuailization functions */
> +
> +/*
> + * setup physical msi, but didn't enable it
> + */
> +int pt_msi_setup(XenPCIPassthroughState *dev)
> +{
> +    int pirq = -1;
> +    uint8_t gvec = 0;
> +
> +    if (!(dev->msi->flags & MSI_FLAG_UNINIT)) {
> +        PT_LOG("Error: setup physical after initialized?? \n");
> +        return -1;
> +    }
> +
> +    gvec = dev->msi->data & 0xFF;
> +    if (!gvec) {
> +        /* if gvec is 0, the guest is asking for a particular pirq that
> +         * is passed as dest_id */
> +        pirq = (dev->msi->addr_hi & 0xffffff00) |
> +               ((dev->msi->addr_lo >> MSI_TARGET_CPU_SHIFT) & 0xff);
> +        if (!pirq) {
> +            /* this probably identifies an misconfiguration of the guest,
> +             * try the emulated path */
> +            pirq = -1;
> +        } else {
> +            PT_LOG("pt_msi_setup requested pirq = %d\n", pirq);
> +        }
> +    }
> +
> +    if (xc_physdev_map_pirq_msi(xen_xc, xen_domid, AUTO_ASSIGN, &pirq,
> +                                PCI_DEVFN(dev->real_device->dev,
> +                                          dev->real_device->func),
> +                                dev->real_device->bus, 0, 0)) {
> +        PT_LOG("Error: Mapping of MSI failed.\n");
> +        return -1;
> +    }
> +
> +    if (pirq < 0) {
> +        PT_LOG("Error: Invalid pirq number\n");
> +        return -1;
> +    }
> +
> +    dev->msi->pirq = pirq;
> +    PT_LOG("msi mapped with pirq %x\n", pirq);
> +
> +    return 0;
> +}
> +
> +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_DESTMODE_SHIFT) & 0x1;
> +    dest_id = (addr >> MSI_TARGET_CPU_SHIFT) & 0xff;
> +    deliv_mode = (data >> MSI_DATA_DELIVERY_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;
> +}
> +
> +int pt_msi_update(XenPCIPassthroughState *d)
> +{
> +    uint8_t gvec = 0;
> +    uint32_t gflags = 0;
> +    uint64_t addr = 0;
> +    int ret = 0;
> +
> +    /* get vector, address, flags info, etc. */
> +    gvec = d->msi->data & 0xFF;
> +    addr = (uint64_t)d->msi->addr_hi << 32 | d->msi->addr_lo;
> +    gflags = __get_msi_gflags(d->msi->data, addr);
> +
> +    PT_LOG("Update msi with pirq %x gvec %x gflags %x\n",
> +           d->msi->pirq, gvec, gflags);
> +
> +    ret = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec,
> +                                   d->msi->pirq, gflags, 0);
> +
> +    if (ret) {
> +        PT_LOG("Error: Binding of MSI failed.\n");
> +
> +        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, d->msi->pirq)) {
> +            PT_LOG("Error: Unmapping of MSI failed.\n");
> +        }
> +        d->msi->pirq = -1;
> +        return ret;
> +    }
> +    return 0;
> +}
> +
> +void pt_msi_disable(XenPCIPassthroughState *dev)
> +{
> +    PCIDevice *d = &dev->dev;
> +    uint8_t gvec = 0;
> +    uint32_t gflags = 0;
> +    uint64_t addr = 0;
> +    uint8_t e_device = 0;
> +    uint8_t e_intx = 0;
> +
> +    msi_set_enable(dev, 0);
> +
> +    e_device = PCI_SLOT(dev->dev.devfn);
> +    e_intx = pci_intx(dev);
> +
> +    if (dev->msi_trans_en) {
> +        if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, dev->msi->pirq,
> +                                    PT_IRQ_TYPE_MSI_TRANSLATE, 0,
> +                                    e_device, e_intx, 0)) {
> +            PT_LOG("Error: Unbinding pt irq for MSI-INTx failed!\n");
> +            goto out;
> +        }
> +    } else if (!(dev->msi->flags & MSI_FLAG_UNINIT)) {
> +        /* get vector, address, flags info, etc. */
> +        gvec = dev->msi->data & 0xFF;
> +        addr = (uint64_t)dev->msi->addr_hi << 32 | dev->msi->addr_lo;
> +        gflags = __get_msi_gflags(dev->msi->data, addr);
> +
> +        PT_LOG("Unbind msi with pirq %x, gvec %x\n",
> +                dev->msi->pirq, gvec);
> +
> +        if (xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec,
> +                                        dev->msi->pirq, gflags)) {
> +            PT_LOG("Error: Unbinding of MSI failed. [%02x:%02x.%x]\n",
> +                   pci_bus_num(d->bus), PCI_SLOT(d->devfn),
> +                   PCI_FUNC(d->devfn));
> +            goto out;
> +        }
> +    }
> +
> +    if (dev->msi->pirq != -1) {
> +        PT_LOG("Unmap msi with pirq %x\n", dev->msi->pirq);
> +
> +        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, dev->msi->pirq)) {
> +            PT_LOG("Error: Unmapping of MSI failed. [%02x:%02x.%x]\n",
> +                   pci_bus_num(d->bus), PCI_SLOT(d->devfn),
> +                   PCI_FUNC(d->devfn));
> +            goto out;
> +        }
> +    }
> +
> +out:
> +    /* clear msi info */
> +    dev->msi->flags = 0;
> +    dev->msi->pirq = -1;
> +    dev->msi_trans_en = 0;
> +}
> +
> +/* MSI-INTx translation virtulization functions */
> +int pt_enable_msi_translate(XenPCIPassthroughState* dev)
> +{
> +    uint8_t e_device = 0;
> +    uint8_t e_intx = 0;
> +
> +    if (!(dev->msi && dev->msi_trans_cap)) {
> +        return -1;
> +    }
> +
> +    msi_set_enable(dev, 0);
> +    dev->msi_trans_en = 0;
> +
> +    if (pt_msi_setup(dev)) {
> +        PT_LOG("Error: MSI-INTx translation MSI setup failed, fallback\n");
> +        return -1;
> +    }
> +
> +    e_device = PCI_SLOT(dev->dev.devfn);
> +    /* fix virtual interrupt pin to INTA# */
> +    e_intx = pci_intx(dev);
> +
> +    if (xc_domain_bind_pt_irq(xen_xc, xen_domid, dev->msi->pirq,
> +                              PT_IRQ_TYPE_MSI_TRANSLATE, 0,
> +                              e_device, e_intx, 0)) {
> +        PT_LOG("Error: MSI-INTx translation bind failed, fallback\n");
> +
> +        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, dev->msi->pirq)) {
> +            PT_LOG("Error: Unmapping of MSI failed.\n");
> +        }
> +        dev->msi->pirq = -1;
> +        return -1;
> +    }
> +
> +    msi_set_enable(dev, 1);
> +    dev->msi_trans_en = 1;
> +
> +    return 0;
> +}
> +
> +void pt_disable_msi_translate(XenPCIPassthroughState *dev)
> +{
> +    uint8_t e_device = 0;
> +    uint8_t e_intx = 0;
> +
> +    /* MSI_ENABLE bit should be disabed until the new handler is set */
> +    msi_set_enable(dev, 0);
> +
> +    e_device = PCI_SLOT(dev->dev.devfn);
> +    e_intx = pci_intx(dev);
> +
> +    if (xc_domain_unbind_pt_irq(xen_xc, xen_domid, dev->msi->pirq,
> +                                 PT_IRQ_TYPE_MSI_TRANSLATE, 0,
> +                                 e_device, e_intx, 0)) {
> +        PT_LOG("Error: Unbinding pt irq for MSI-INTx failed!\n");
> +    }
> +
> +    if (dev->machine_irq) {
> +        if (xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, dev->machine_irq,
> +                                       0, e_device, e_intx)) {
> +            PT_LOG("Error: Rebinding of interrupt failed!\n");
> +        }
> +    }
> +
> +    dev->msi_trans_en = 0;
> +}
> +
> +/*********************************/
> +/* MSI-X virtulization functions */
> +
> +static void mask_physical_msix_entry(XenPCIPassthroughState *dev,
> +                                     int entry_nr, int mask)
> +{
> +    void *phys_off;
> +
> +    phys_off = dev->msix->phys_iomem_base + 16 * entry_nr + 12;
> +    *(uint32_t *)phys_off = mask;
> +}
> +
> +static int pt_msix_update_one(XenPCIPassthroughState *dev, int entry_nr)
> +{
> +    struct msix_entry_info *entry = &dev->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 ret;
> +
> +    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_TARGET_CPU_SHIFT) & 0xff);
> +        if (!pirq) {
> +            /* this probably identifies an misconfiguration of the guest,
> +             * try the emulated path */
> +            pirq = -1;
> +        } else {
> +            PT_LOG("pt_msix_update_one requested pirq = %d\n", pirq);
> +        }
> +    }
> +
> +    /* Check if this entry is already mapped */
> +    if (entry->pirq == -1) {
> +        ret = xc_physdev_map_pirq_msi(xen_xc, xen_domid, AUTO_ASSIGN, &pirq,
> +                                      PCI_DEVFN(dev->real_device->dev,
> +                                                dev->real_device->func),
> +                                      dev->real_device->bus, entry_nr,
> +                                      dev->msix->table_base);
> +        if (ret) {
> +            PT_LOG("Error: Mapping msix entry %x\n", entry_nr);
> +            return ret;
> +        }
> +        entry->pirq = pirq;
> +    }
> +
> +    PT_LOG("Update msix entry %x with pirq %x gvec %x\n",
> +            entry_nr, pirq, gvec);
> +
> +    ret = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec, pirq, gflags,
> +                                   dev->msix->mmio_base_addr);
> +    if (ret) {
> +        PT_LOG("Error: Updating msix irq info for entry %d\n", entry_nr);
> +
> +        if (xc_physdev_unmap_pirq(xen_xc, xen_domid, entry->pirq)) {
> +            PT_LOG("Error: Unmapping of MSI-X failed.\n");
> +        }
> +        entry->pirq = -1;
> +        return ret;
> +    }
> +
> +    entry->flags = 0;
> +
> +    return 0;
> +}
> +
> +int pt_msix_update(XenPCIPassthroughState *dev)
> +{
> +    struct pt_msix_info *msix = dev->msix;
> +    int i;
> +
> +    for (i = 0; i < msix->total_entries; i++) {
> +        pt_msix_update_one(dev, i);
> +    }
> +
> +    return 0;
> +}
> +
> +void pt_msix_disable(XenPCIPassthroughState *dev)
> +{
> +    PCIDevice *d = &dev->dev;
> +    uint8_t gvec = 0;
> +    uint32_t gflags = 0;
> +    uint64_t addr = 0;
> +    int i = 0;
> +    struct msix_entry_info *entry = NULL;
> +
> +    msix_set_enable(dev, 0);
> +
> +    for ( i = 0; i < dev->msix->total_entries; i++ ) {
> +        entry = &dev->msix->msix_entry[i];
> +
> +        if (entry->pirq == -1) {
> +            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("Unbind msix with pirq %x, gvec %x\n",
> +                entry->pirq, gvec);
> +
> +        if (xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec,
> +                                        entry->pirq, gflags)) {
> +            PT_LOG("Error: Unbinding of MSI-X failed. [%02x:%02x.%x]\n",
> +                   pci_bus_num(d->bus), PCI_SLOT(d->devfn),
> +                   PCI_FUNC(d->devfn));
> +        } else {
> +            PT_LOG("Unmap msix with pirq %x\n", entry->pirq);
> +
> +            if (xc_physdev_unmap_pirq(xen_xc, xen_domid, entry->pirq)) {
> +                PT_LOG("Error: Unmapping of MSI-X failed. [%02x:%02x.%x]\n",
> +                       pci_bus_num(d->bus),
> +                       PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
> +            }
> +        }
> +        /* clear msi-x info */
> +        entry->pirq = -1;
> +        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 != -1) {
> +            ret = xc_domain_unbind_pt_irq(xen_xc, xen_domid, entry->pirq,
> +                                          PT_IRQ_TYPE_MSI, 0, 0, 0, 0);
> +            if (ret) {
> +                PT_LOG("Error: 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)
> +{
> +    PT_LOG("Error: 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 *dev = (XenPCIPassthroughState *)opaque;
> +    XenPTMSIX *msix = dev->msix;
> +    XenMSIXEntry *entry;
> +    int entry_nr, offset;
> +    void *phys_off;
> +    uint32_t vec_ctrl;
> +
> +    if (addr % 4) {
> +        PT_LOG("Error: Unaligned dword access to MSI-X table, "
> +                "addr %016"PRIx64"\n", addr);
> +        return;
> +    }
> +
> +    PT_LOG("addr: "TARGET_FMT_plx", val: %#x\n", addr, val);
> +
> +    // FIXME/TODO I maybe have to do the same in readl !
> +    /* entry_nr = (addr - msix->mmio_base_addr) / 16; */

what is the problem here?


> +    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 = dev->msix->phys_iomem_base + 16 * entry_nr + 12;
> +    vec_ctrl = *(uint32_t *)phys_off;
> +
> +    if (offset != 3 && msix->enabled && !(vec_ctrl & 0x1)) {
> +        PT_LOG("Error: Can't update msix entry %d since MSI-X is already "
> +                "function.\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(dev, entry_nr);
> +        }
> +        mask_physical_msix_entry(dev, 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)
> +{
> +    PT_LOG("Error: 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 *dev = (XenPCIPassthroughState *)opaque;
> +    struct pt_msix_info *msix = dev->msix;
> +    int entry_nr, offset;
> +
> +    if (addr % 4) {
> +        PT_LOG("Error: Unaligned dword access to MSI-X table, "
> +                "addr %016"PRIx64"\n", addr);
> +        return 0;
> +    }
> +
> +    PT_LOG("addr: "TARGET_FMT_plx"\n", addr);
> +
> +    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 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 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 *dev, int pos)
> +{
> +    uint8_t id;
> +    uint16_t control;
> +    int i, total_entries, table_off, bar_index;
> +    HostPCIDevice *pd = dev->real_device;
> +    int fd;
> +
> +    id = host_pci_read_byte(pd, pos + PCI_CAP_LIST_ID);
> +
> +    if (id != PCI_CAP_ID_MSIX) {
> +        PT_LOG("Error: Invalid id %#x pos %#x\n", id, pos);
> +        return -1;
> +    }
> +
> +    control = host_pci_read_word(pd, pos + 2);
> +    total_entries = control & 0x7ff;
> +    total_entries += 1;
> +
> +    dev->msix = g_malloc0(sizeof (struct pt_msix_info)
> +                          + total_entries * sizeof (struct msix_entry_info));
> +
> +    dev->msix->total_entries = total_entries;
> +    for (i = 0; i < total_entries; i++) {
> +        dev->msix->msix_entry[i].pirq = -1;
> +    }
> +
> +    dev->msix->mmio_index =
> +        cpu_register_io_memory(pci_msix_read, pci_msix_write,
> +                               dev, DEVICE_NATIVE_ENDIAN);
> +
> +    table_off = host_pci_read_long(pd, pos + PCI_MSIX_TABLE);
> +    bar_index = dev->msix->bar_index = table_off & PCI_MSIX_FLAGS_BIRMASK;
> +    table_off = dev->msix->table_off = table_off & ~PCI_MSIX_FLAGS_BIRMASK;
> +    dev->msix->table_base =
> +        pt_pci_base_addr(dev->real_device->base_addr[bar_index]);
> +    PT_LOG("get MSI-X table bar base %#"PRIx64"\n", dev->msix->table_base);
> +
> +    fd = open("/dev/mem", O_RDWR);
> +    if (fd == -1) {
> +        PT_LOG("Error: Can't open /dev/mem: %s\n", strerror(errno));
> +        goto error_out;
> +    }
> +    PT_LOG("table_off = %#x, total_entries = %d\n", table_off, total_entries);
> +    dev->msix->table_offset_adjust = table_off & 0x0fff;
> +    dev->msix->phys_iomem_base = mmap(0,
> +                                      total_entries * 16
> +                                      + dev->msix->table_offset_adjust,
> +                                      PROT_WRITE | PROT_READ,
> +                                      MAP_SHARED | MAP_LOCKED,
> +                                      fd,
> +                                      dev->msix->table_base + table_off
> +                                      - dev->msix->table_offset_adjust);
> +    if (dev->msix->phys_iomem_base == MAP_FAILED) {
> +        PT_LOG("Error: Can't map physical MSI-X table: %s\n", strerror(errno));
> +        close(fd);
> +        goto error_out;
> +    }
> +    dev->msix->phys_iomem_base = ((char *)dev->msix->phys_iomem_base +
> +                                  dev->msix->table_offset_adjust);
> +
> +    close(fd);
> +
> +    PT_LOG("mapping physical MSI-X table to %p\n", dev->msix->phys_iomem_base);
> +    return 0;
> +
> +error_out:
> +    g_free(dev->msix);
> +    dev->msix = NULL;
> +    return -1;
> +}
> +
> +#if 0
> +static void pt_msix_delete(XenPCIPassthroughState *dev)
> +{
> +    /* unmap the MSI-X memory mapped register area */
> +    if (dev->msix->phys_iomem_base)
> +    {
> +        PT_LOG("unmapping physical MSI-X table from %lx\n",
> +           (unsigned long)dev->msix->phys_iomem_base);
> +        munmap(dev->msix->phys_iomem_base, dev->msix->total_entries * 16 +
> +           dev->msix->table_offset_adjust);
> +    }
> +
> +    if (dev->msix->mmio_index > 0)
> +    {
> +        cpu_unregister_io_memory(dev->msix->mmio_index);
> +    }
> +
> +
> +    free(dev->msix);
> +}
> +#endif
> --
> Anthony PERARD
> 

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [PATCH RFC V1 11/11] config/make: Introduce --enable-xen-pci-passthrough, built it.
  2011-10-04 14:51   ` Anthony PERARD
@ 2011-10-05 11:55     ` Stefano Stabellini
  -1 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-05 11:55 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On Tue, 4 Oct 2011, Anthony PERARD wrote:
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
> ---
>  Makefile.target |    7 +++++++
>  configure       |   21 +++++++++++++++++++++
>  2 files changed, 28 insertions(+), 0 deletions(-)
> 
> diff --git a/Makefile.target b/Makefile.target
> index f708453..b5fbc18 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -208,6 +208,13 @@ 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
> +obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough_helpers.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 =
>  ifeq ($(CONFIG_KVM), y)
> diff --git a/configure b/configure
> index 0875f95..b90cfe1 100755
> --- a/configure
> +++ b/configure
> @@ -127,6 +127,7 @@ vnc_png=""
>  vnc_thread="no"
>  xen=""
>  xen_ctrl_version=""
> +xen_pci_passthrough=""
>  linux_aio=""
>  attr=""
>  xfs=""
> @@ -635,6 +636,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"
> @@ -972,6 +977,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"
> @@ -1335,6 +1342,17 @@ EOF
>    fi
>  fi
>  
> +if test "$xen_pci_passthrough" != "no"; then
> +  if test "$xen" = "yes"; then
> +    xen_pci_passthrough=yes
> +  else
> +    if test "$xen_pci_passthrough" = "yes"; then
> +      feature_not_found "Xen PCI Passthrough without Xen"
> +    fi
> +    xen_pci_passthrough=no
> +  fi
> +fi
> +

Xen works on many OSes that are not Linux, however host-pci-device only
works on Linux. 
For the moment we can turn off xen_pci_passthrough if we are not running
on Linux, but one day not too far I hope will have a host-pci-device
implementation that works on BSDs and Solaris.


>  ##########################################
>  # pkg-config probe
>  
> @@ -3378,6 +3396,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
> 

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCH RFC V1 11/11] config/make: Introduce --enable-xen-pci-passthrough, built it.
@ 2011-10-05 11:55     ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2011-10-05 11:55 UTC (permalink / raw)
  To: Anthony PERARD; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On Tue, 4 Oct 2011, Anthony PERARD wrote:
> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
> ---
>  Makefile.target |    7 +++++++
>  configure       |   21 +++++++++++++++++++++
>  2 files changed, 28 insertions(+), 0 deletions(-)
> 
> diff --git a/Makefile.target b/Makefile.target
> index f708453..b5fbc18 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -208,6 +208,13 @@ 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
> +obj-i386-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pci_passthrough_helpers.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 =
>  ifeq ($(CONFIG_KVM), y)
> diff --git a/configure b/configure
> index 0875f95..b90cfe1 100755
> --- a/configure
> +++ b/configure
> @@ -127,6 +127,7 @@ vnc_png=""
>  vnc_thread="no"
>  xen=""
>  xen_ctrl_version=""
> +xen_pci_passthrough=""
>  linux_aio=""
>  attr=""
>  xfs=""
> @@ -635,6 +636,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"
> @@ -972,6 +977,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"
> @@ -1335,6 +1342,17 @@ EOF
>    fi
>  fi
>  
> +if test "$xen_pci_passthrough" != "no"; then
> +  if test "$xen" = "yes"; then
> +    xen_pci_passthrough=yes
> +  else
> +    if test "$xen_pci_passthrough" = "yes"; then
> +      feature_not_found "Xen PCI Passthrough without Xen"
> +    fi
> +    xen_pci_passthrough=no
> +  fi
> +fi
> +

Xen works on many OSes that are not Linux, however host-pci-device only
works on Linux. 
For the moment we can turn off xen_pci_passthrough if we are not running
on Linux, but one day not too far I hope will have a host-pci-device
implementation that works on BSDs and Solaris.


>  ##########################################
>  # pkg-config probe
>  
> @@ -3378,6 +3396,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
> 

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [Qemu-devel] [PATCH RFC V1 01/11] Introduce HostPCIDevice to access a pci device on the host.
  2011-10-04 18:21     ` Jan Kiszka
@ 2011-10-12 16:56       ` Anthony PERARD
  -1 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-12 16:56 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On Tue, Oct 4, 2011 at 19:21, Jan Kiszka <jan.kiszka@web.de> wrote:
> This wasn't run through checkpatch.pl, I bet.
>
> On 2011-10-04 16:51, Anthony PERARD wrote:
>> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
>> ---
>>  hw/host-pci-device.c |  192 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>  hw/host-pci-device.h |   36 +++++++++
>>  2 files changed, 228 insertions(+), 0 deletions(-)
>>  create mode 100644 hw/host-pci-device.c
>>  create mode 100644 hw/host-pci-device.h
>>
>> diff --git a/hw/host-pci-device.c b/hw/host-pci-device.c
>> new file mode 100644
>> index 0000000..b3f2899
>> --- /dev/null
>> +++ b/hw/host-pci-device.c
>> @@ -0,0 +1,192 @@
>> +#include "qemu-common.h"
>> +#include "host-pci-device.h"
>> +
>> +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;
>> +    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 -1;
>> +    }
>> +
>> +    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);
>> +            break;
>> +        }
>> +        if (start) {
>> +            size = end - start + 1;
>> +        } else {
>> +            size = 0;
>> +        }
>> +
>> +        flags &= 0xf;
>
> No magic numbers please.
>
> It also looks a bit strange to me: It's the resource type encoded in the
> second byte? Aren't you interested in it?

Actually, we are interessted to have the BAR with the different flags
(IO/MEM, prefetch, size) like the value in the config space. Because
the base_address value will be given to the VM (by the function
pt_bar_reg_read). But I can later merge the values (start | (flags &
~pci_base_address_io/mem_mask)), and have the right value to give
back.

So here, I will keep seperate the base address, and the flags.

>> +
>> +        if (i < PCI_ROM_SLOT) {
>> +            d->base_addr[i] = start | flags;
>> +            d->size[i] = size;
>> +        } else {
>> +            d->rom_base_addr = start | flags;
>> +            d->rom_size = size;
>> +        }
>> +    }
>> +
>> +    fclose(f);
>> +    return 0;
>> +}
>> +
>> +static unsigned long get_value(HostPCIDevice *d, const char *name)
>> +{
>> +    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 -1;
>> +    }
>> +    if (fscanf(f, "%lx\n", &value) != 1) {
>> +        fprintf(stderr, "Error: Syntax error in %s\n", path);
>> +        value = -1;
>> +    }
>> +    fclose(f);
>> +    return value;
>> +}
>> +
>> +static int pci_dev_is_virtfn(HostPCIDevice *d)
>> +{
>> +    int rc;
>> +    char path[PATH_MAX];
>> +    struct stat buf;
>> +
>> +    path_to(d, "physfn", path, sizeof (path));
>> +    rc = !stat(path, &buf);
>> +
>> +    return rc;
>> +}
>> +
>> +static int host_pci_config_fd(HostPCIDevice *d)
>
> [ We will also need the reverse: pass in open file descriptors that
> HostPCIDevice should use. Can be added later. ]
>
>> +{
>> +    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;
>> +
>> +    res = pread(fd, buf, len, pos);
>> +    if (res < 0) {
>> +        fprintf(stderr, "host_pci_config: read failed: %s (fd: %i)\n",
>> +                strerror(errno), fd);
>> +        return -1;
>> +    }
>> +    return res;
>> +}
>> +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;
>> +
>> +    res = pwrite(fd, buf, len, pos);
>> +    if (res < 0) {
>> +        fprintf(stderr, "host_pci_config: write failed: %s\n",
>> +                strerror(errno));
>> +        return -1;
>> +    }
>> +    return res;
>> +}
>> +
>> +uint8_t host_pci_read_byte(HostPCIDevice *d, int pos)
>> +{
>> +  uint8_t buf;
>> +  host_pci_config_read(d, pos, &buf, 1);
>> +  return buf;
>> +}
>> +uint16_t host_pci_read_word(HostPCIDevice *d, int pos)
>> +{
>> +  uint16_t buf;
>> +  host_pci_config_read(d, pos, &buf, 2);
>> +  return le16_to_cpu(buf);
>> +}
>> +uint32_t host_pci_read_long(HostPCIDevice *d, int pos)
>> +{
>> +  uint32_t buf;
>> +  host_pci_config_read(d, pos, &buf, 4);
>> +  return le32_to_cpu(buf);
>> +}
>> +int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
>> +{
>> +  return host_pci_config_read(d, pos, buf, len);
>> +}
>> +
>> +int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data)
>> +{
>> +  return host_pci_config_write(d, pos, &data, 1);
>> +}
>> +int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data)
>> +{
>> +  return host_pci_config_write(d, pos, &data, 2);
>
> You adjust endianess on read, but not on write.

Will fix that.

>> +}
>> +int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data)
>> +{
>> +  return host_pci_config_write(d, pos, &data, 4);
>> +}
>> +int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
>> +{
>> +  return host_pci_config_write(d, pos, buf, len);
>> +}
>> +
>> +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func)
>> +{
>> +    HostPCIDevice *d = NULL;
>> +
>> +    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) == -1)
>> +        goto error;
>> +
>> +    d->vendor_id = get_value(d, "vendor");
>> +    d->device_id = get_value(d, "device");
>> +    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;
>> +}
>> diff --git a/hw/host-pci-device.h b/hw/host-pci-device.h
>> new file mode 100644
>> index 0000000..0137507
>> --- /dev/null
>> +++ b/hw/host-pci-device.h
>> @@ -0,0 +1,36 @@
>> +#ifndef HW_HOST_PCI_DEVICE
>> +#  define HW_HOST_PCI_DEVICE
>> +
>> +#include "pci.h"
>> +
>> +typedef struct HostPCIDevice {
>> +    uint16_t domain;
>> +    uint8_t bus;
>> +    uint8_t dev;
>> +    uint8_t func;
>> +
>> +    uint16_t vendor_id;
>> +    uint16_t device_id;
>> +
>> +    pcibus_t base_addr[PCI_NUM_REGIONS - 1];
>> +    pcibus_t size[PCI_NUM_REGIONS - 1];
>> +    pcibus_t rom_base_addr;
>> +    pcibus_t rom_size;
>
> Regions deserve their own type IMHO. In KVM we have
>
> typedef struct {
>    int type;           /* Memory or port I/O */
>    int valid;
>    uint32_t base_addr;
>    uint32_t size;    /* size of the region */
>    int resource_fd;
> } PCIRegion;
>
> Should probably become HostPCIIORegion (vs. virtual PCIIORegion), and
> our field types need some cleanups.

I will do that, but I think to have only base_addr, size and flags.
flags will be the flags given by the sysfs resource file. Is that OK ?

>> +
>> +    bool is_virtfn;
>> +
>> +    int config_fd;
>> +} HostPCIDevice;
>> +
>> +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func);
>
> And what about some host_pci_device_put when we're done with it?

Will do it.

>> +
>> +uint8_t host_pci_read_byte(HostPCIDevice *d, int pos);
>> +uint16_t host_pci_read_word(HostPCIDevice *d, int pos);
>> +uint32_t host_pci_read_long(HostPCIDevice *d, int pos);
>> +int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
>> +int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data);
>> +int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data);
>> +int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data);
>> +int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
>
> I think these should be analogous to our pci layer:
> host_pci_get/set_byte/word/long/quad.

Yes, I will change that.

> Looks like it's generally useful for KVM as well.

Thanks,

-- 
Anthony PERARD

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCH RFC V1 01/11] Introduce HostPCIDevice to access a pci device on the host.
@ 2011-10-12 16:56       ` Anthony PERARD
  0 siblings, 0 replies; 66+ messages in thread
From: Anthony PERARD @ 2011-10-12 16:56 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: Alex Williamson, Xen Devel, QEMU-devel, Stefano Stabellini

On Tue, Oct 4, 2011 at 19:21, Jan Kiszka <jan.kiszka@web.de> wrote:
> This wasn't run through checkpatch.pl, I bet.
>
> On 2011-10-04 16:51, Anthony PERARD wrote:
>> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
>> ---
>>  hw/host-pci-device.c |  192 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>  hw/host-pci-device.h |   36 +++++++++
>>  2 files changed, 228 insertions(+), 0 deletions(-)
>>  create mode 100644 hw/host-pci-device.c
>>  create mode 100644 hw/host-pci-device.h
>>
>> diff --git a/hw/host-pci-device.c b/hw/host-pci-device.c
>> new file mode 100644
>> index 0000000..b3f2899
>> --- /dev/null
>> +++ b/hw/host-pci-device.c
>> @@ -0,0 +1,192 @@
>> +#include "qemu-common.h"
>> +#include "host-pci-device.h"
>> +
>> +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;
>> +    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 -1;
>> +    }
>> +
>> +    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);
>> +            break;
>> +        }
>> +        if (start) {
>> +            size = end - start + 1;
>> +        } else {
>> +            size = 0;
>> +        }
>> +
>> +        flags &= 0xf;
>
> No magic numbers please.
>
> It also looks a bit strange to me: It's the resource type encoded in the
> second byte? Aren't you interested in it?

Actually, we are interessted to have the BAR with the different flags
(IO/MEM, prefetch, size) like the value in the config space. Because
the base_address value will be given to the VM (by the function
pt_bar_reg_read). But I can later merge the values (start | (flags &
~pci_base_address_io/mem_mask)), and have the right value to give
back.

So here, I will keep seperate the base address, and the flags.

>> +
>> +        if (i < PCI_ROM_SLOT) {
>> +            d->base_addr[i] = start | flags;
>> +            d->size[i] = size;
>> +        } else {
>> +            d->rom_base_addr = start | flags;
>> +            d->rom_size = size;
>> +        }
>> +    }
>> +
>> +    fclose(f);
>> +    return 0;
>> +}
>> +
>> +static unsigned long get_value(HostPCIDevice *d, const char *name)
>> +{
>> +    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 -1;
>> +    }
>> +    if (fscanf(f, "%lx\n", &value) != 1) {
>> +        fprintf(stderr, "Error: Syntax error in %s\n", path);
>> +        value = -1;
>> +    }
>> +    fclose(f);
>> +    return value;
>> +}
>> +
>> +static int pci_dev_is_virtfn(HostPCIDevice *d)
>> +{
>> +    int rc;
>> +    char path[PATH_MAX];
>> +    struct stat buf;
>> +
>> +    path_to(d, "physfn", path, sizeof (path));
>> +    rc = !stat(path, &buf);
>> +
>> +    return rc;
>> +}
>> +
>> +static int host_pci_config_fd(HostPCIDevice *d)
>
> [ We will also need the reverse: pass in open file descriptors that
> HostPCIDevice should use. Can be added later. ]
>
>> +{
>> +    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;
>> +
>> +    res = pread(fd, buf, len, pos);
>> +    if (res < 0) {
>> +        fprintf(stderr, "host_pci_config: read failed: %s (fd: %i)\n",
>> +                strerror(errno), fd);
>> +        return -1;
>> +    }
>> +    return res;
>> +}
>> +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;
>> +
>> +    res = pwrite(fd, buf, len, pos);
>> +    if (res < 0) {
>> +        fprintf(stderr, "host_pci_config: write failed: %s\n",
>> +                strerror(errno));
>> +        return -1;
>> +    }
>> +    return res;
>> +}
>> +
>> +uint8_t host_pci_read_byte(HostPCIDevice *d, int pos)
>> +{
>> +  uint8_t buf;
>> +  host_pci_config_read(d, pos, &buf, 1);
>> +  return buf;
>> +}
>> +uint16_t host_pci_read_word(HostPCIDevice *d, int pos)
>> +{
>> +  uint16_t buf;
>> +  host_pci_config_read(d, pos, &buf, 2);
>> +  return le16_to_cpu(buf);
>> +}
>> +uint32_t host_pci_read_long(HostPCIDevice *d, int pos)
>> +{
>> +  uint32_t buf;
>> +  host_pci_config_read(d, pos, &buf, 4);
>> +  return le32_to_cpu(buf);
>> +}
>> +int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
>> +{
>> +  return host_pci_config_read(d, pos, buf, len);
>> +}
>> +
>> +int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data)
>> +{
>> +  return host_pci_config_write(d, pos, &data, 1);
>> +}
>> +int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data)
>> +{
>> +  return host_pci_config_write(d, pos, &data, 2);
>
> You adjust endianess on read, but not on write.

Will fix that.

>> +}
>> +int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data)
>> +{
>> +  return host_pci_config_write(d, pos, &data, 4);
>> +}
>> +int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len)
>> +{
>> +  return host_pci_config_write(d, pos, buf, len);
>> +}
>> +
>> +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func)
>> +{
>> +    HostPCIDevice *d = NULL;
>> +
>> +    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) == -1)
>> +        goto error;
>> +
>> +    d->vendor_id = get_value(d, "vendor");
>> +    d->device_id = get_value(d, "device");
>> +    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;
>> +}
>> diff --git a/hw/host-pci-device.h b/hw/host-pci-device.h
>> new file mode 100644
>> index 0000000..0137507
>> --- /dev/null
>> +++ b/hw/host-pci-device.h
>> @@ -0,0 +1,36 @@
>> +#ifndef HW_HOST_PCI_DEVICE
>> +#  define HW_HOST_PCI_DEVICE
>> +
>> +#include "pci.h"
>> +
>> +typedef struct HostPCIDevice {
>> +    uint16_t domain;
>> +    uint8_t bus;
>> +    uint8_t dev;
>> +    uint8_t func;
>> +
>> +    uint16_t vendor_id;
>> +    uint16_t device_id;
>> +
>> +    pcibus_t base_addr[PCI_NUM_REGIONS - 1];
>> +    pcibus_t size[PCI_NUM_REGIONS - 1];
>> +    pcibus_t rom_base_addr;
>> +    pcibus_t rom_size;
>
> Regions deserve their own type IMHO. In KVM we have
>
> typedef struct {
>    int type;           /* Memory or port I/O */
>    int valid;
>    uint32_t base_addr;
>    uint32_t size;    /* size of the region */
>    int resource_fd;
> } PCIRegion;
>
> Should probably become HostPCIIORegion (vs. virtual PCIIORegion), and
> our field types need some cleanups.

I will do that, but I think to have only base_addr, size and flags.
flags will be the flags given by the sysfs resource file. Is that OK ?

>> +
>> +    bool is_virtfn;
>> +
>> +    int config_fd;
>> +} HostPCIDevice;
>> +
>> +HostPCIDevice *host_pci_device_get(uint8_t bus, uint8_t dev, uint8_t func);
>
> And what about some host_pci_device_put when we're done with it?

Will do it.

>> +
>> +uint8_t host_pci_read_byte(HostPCIDevice *d, int pos);
>> +uint16_t host_pci_read_word(HostPCIDevice *d, int pos);
>> +uint32_t host_pci_read_long(HostPCIDevice *d, int pos);
>> +int host_pci_read_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
>> +int host_pci_write_byte(HostPCIDevice *d, int pos, uint8_t data);
>> +int host_pci_write_word(HostPCIDevice *d, int pos, uint16_t data);
>> +int host_pci_write_long(HostPCIDevice *d, int pos, uint32_t data);
>> +int host_pci_write_block(HostPCIDevice *d, int pos, uint8_t *buf, int len);
>
> I think these should be analogous to our pci layer:
> host_pci_get/set_byte/word/long/quad.

Yes, I will change that.

> Looks like it's generally useful for KVM as well.

Thanks,

-- 
Anthony PERARD

^ permalink raw reply	[flat|nested] 66+ messages in thread

end of thread, other threads:[~2011-10-12 16:56 UTC | newest]

Thread overview: 66+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-10-04 14:51 [Qemu-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough Anthony PERARD
2011-10-04 14:51 ` Anthony PERARD
2011-10-04 14:51 ` [Qemu-devel] [PATCH RFC V1 01/11] Introduce HostPCIDevice to access a pci device on the host Anthony PERARD
2011-10-04 14:51   ` Anthony PERARD
2011-10-04 18:21   ` [Qemu-devel] " Jan Kiszka
2011-10-04 18:21     ` Jan Kiszka
2011-10-12 16:56     ` [Qemu-devel] " Anthony PERARD
2011-10-12 16:56       ` Anthony PERARD
2011-10-04 18:36   ` [Qemu-devel] " Stefano Stabellini
2011-10-04 18:36     ` Stefano Stabellini
2011-10-04 14:51 ` [Qemu-devel] [PATCH RFC V1 02/11] qemu-timer: Introduce qemu_run_one_timer Anthony PERARD
2011-10-04 14:51   ` Anthony PERARD
2011-10-04 17:27   ` [Qemu-devel] " Jan Kiszka
2011-10-04 17:27     ` Jan Kiszka
2011-10-04 17:52     ` [Qemu-devel] " Stefano Stabellini
2011-10-04 17:52       ` Stefano Stabellini
2011-10-04 18:20     ` [Qemu-devel] " Anthony Liguori
2011-10-04 18:20       ` Anthony Liguori
2011-10-04 14:51 ` [Qemu-devel] [PATCH RFC V1 03/11] pci_ids: Add INTEL_82599_VF id Anthony PERARD
2011-10-04 14:51   ` Anthony PERARD
2011-10-04 14:51 ` [Qemu-devel] [PATCH RFC V1 04/11] pci_regs: Fix value of PCI_EXP_TYPE_RC_EC Anthony PERARD
2011-10-04 14:51   ` Anthony PERARD
2011-10-04 14:51 ` [Qemu-devel] [PATCH RFC V1 05/11] pci_regs: Add PCI_EXP_TYPE_PCIE_BRIDGE Anthony PERARD
2011-10-04 14:51   ` Anthony PERARD
2011-10-04 14:51 ` [Qemu-devel] [PATCH RFC V1 06/11] pci.c: Add pci_check_bar_overlap Anthony PERARD
2011-10-04 14:51   ` Anthony PERARD
2011-10-04 14:51 ` [Qemu-devel] [PATCH RFC V1 07/11] host-pci-device: Add host_pci_find_ext_cap_offset Anthony PERARD
2011-10-04 14:51   ` Anthony PERARD
2011-10-05 11:07   ` [Qemu-devel] " Stefano Stabellini
2011-10-05 11:07     ` Stefano Stabellini
2011-10-04 14:51 ` [Qemu-devel] [PATCH RFC V1 08/11] Introduce Xen PCI Passthrough, qdevice (1/3) Anthony PERARD
2011-10-04 14:51   ` Anthony PERARD
2011-10-05 11:51   ` [Qemu-devel] " Stefano Stabellini
2011-10-05 11:51     ` Stefano Stabellini
2011-10-04 14:51 ` [Qemu-devel] [PATCH RFC V1 09/11] Introduce Xen PCI Passthrough, PCI config space helpers (2/3) Anthony PERARD
2011-10-04 14:51   ` Anthony PERARD
2011-10-04 14:51 ` [Qemu-devel] [PATCH RFC V1 10/11] Introduce Xen PCI Passthrough, MSI (3/3) Anthony PERARD
2011-10-04 14:51   ` Anthony PERARD
2011-10-05 11:51   ` [Qemu-devel] " Stefano Stabellini
2011-10-05 11:51     ` Stefano Stabellini
2011-10-04 14:51 ` [Qemu-devel] [PATCH RFC V1 11/11] config/make: Introduce --enable-xen-pci-passthrough, built it Anthony PERARD
2011-10-04 14:51   ` Anthony PERARD
2011-10-05 11:55   ` [Qemu-devel] " Stefano Stabellini
2011-10-05 11:55     ` Stefano Stabellini
2011-10-04 14:58 ` [Qemu-devel] [Xen-devel] [PATCH RFC V1 00/11] Xen PCI Passthrough Avi Kivity
2011-10-04 14:58   ` Avi Kivity
2011-10-04 15:01   ` [Qemu-devel] " Anthony Liguori
2011-10-04 15:01     ` Anthony Liguori
2011-10-04 15:05     ` [Qemu-devel] " Stefano Stabellini
2011-10-04 15:05       ` Stefano Stabellini
2011-10-04 16:33       ` [Qemu-devel] " Alex Williamson
2011-10-04 16:33         ` Alex Williamson
2011-10-04 16:56         ` [Qemu-devel] " Stefano Stabellini
2011-10-04 16:56           ` Stefano Stabellini
2011-10-04 17:01         ` [Qemu-devel] " Avi Kivity
2011-10-04 17:01           ` Avi Kivity
2011-10-04 17:41           ` [Qemu-devel] " Jan Kiszka
2011-10-04 17:41             ` Jan Kiszka
2011-10-04 17:03     ` [Qemu-devel] " Avi Kivity
2011-10-04 17:03       ` Avi Kivity
2011-10-04 18:19       ` [Qemu-devel] " Stefano Stabellini
2011-10-04 18:19         ` Stefano Stabellini
2011-10-04 18:24         ` [Qemu-devel] " Avi Kivity
2011-10-04 18:24           ` Avi Kivity
2011-10-04 17:21     ` [Qemu-devel] " Jan Kiszka
2011-10-04 17:21       ` Jan Kiszka

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.