All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 00/14] pcie port switch emulators
@ 2010-09-06  7:46 Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 01/14] RESEND apb: fix typo Isaku Yamahata
                   ` (15 more replies)
  0 siblings, 16 replies; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

This patch series implements pcie port switch emulators
which is basic part for pcie/q35 support.
This is for mst/pci tree.

some random comments
- pci bus reset
  As Anthony is cleaning up qdev reset stuff, so pci bus reset code
  is commented out. Once the qdev clean up is done, the patch that
  enabled pci bus reset will be sent.

- vid/did
  there are arbitrariness for pcie port switch. I just choose
  Intel and IT one to model them.

thanks,

Isaku Yamahata (14):
  RESEND apb: fix typo.
  pci: consolidate pci_add_capability_at_offset() into
    pci_add_capability().
  pci bridge: add helper function for ssvid capability.
  pci: call hotplug callback even when not hotplug case for later use.
  pci: make pci_parse_devfn() aware of func.
  pci_ids.h: add vendor id of Texus Intesruments.
  msi: implemented msi.
  pcie: helper functions for pcie extended capability.
  pcie port: define struct PCIEPort/PCIESlot and helper functions
  pcie root port: implement pcie root port.
  pcie upstream port: pci express switch upstream port.
  pcie downstream port: pci express switch downstream port.
  pcie/hotplug: glue pushing attention button command. pcie_abp
  pcie/aer: glue aer error injection into qemu monitor.

 Makefile.objs        |    6 +-
 hw/acpi_piix4.c      |    3 +
 hw/apb_pci.c         |    6 +-
 hw/eepro100.c        |    4 +-
 hw/msi.c             |  362 +++++++++++
 hw/msi.h             |   41 ++
 hw/msix.c            |    3 +-
 hw/pci.c             |   70 ++-
 hw/pci.h             |   36 +-
 hw/pci_bridge.c      |   19 +
 hw/pci_bridge.h      |    3 +
 hw/pci_ids.h         |    2 +
 hw/pcie.c            | 1753 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pcie.h            |  186 ++++++
 hw/pcie_downstream.c |  224 +++++++
 hw/pcie_downstream.h |   33 +
 hw/pcie_port.c       |  188 ++++++
 hw/pcie_port.h       |   51 ++
 hw/pcie_root.c       |  247 +++++++
 hw/pcie_root.h       |   32 +
 hw/pcie_upstream.c   |  206 ++++++
 hw/pcie_upstream.h   |   32 +
 qemu-common.h        |    3 +
 qemu-monitor.hx      |   36 +
 sysemu.h             |    9 +
 25 files changed, 3520 insertions(+), 35 deletions(-)
 create mode 100644 hw/msi.c
 create mode 100644 hw/msi.h
 create mode 100644 hw/pcie.c
 create mode 100644 hw/pcie.h
 create mode 100644 hw/pcie_downstream.c
 create mode 100644 hw/pcie_downstream.h
 create mode 100644 hw/pcie_port.c
 create mode 100644 hw/pcie_port.h
 create mode 100644 hw/pcie_root.c
 create mode 100644 hw/pcie_root.h
 create mode 100644 hw/pcie_upstream.c
 create mode 100644 hw/pcie_upstream.h

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

* [Qemu-devel] [PATCH 01/14] RESEND apb: fix typo.
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06  9:46   ` [Qemu-devel] " Michael S. Tsirkin
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 02/14] pci: consolidate pci_add_capability_at_offset() into pci_add_capability() Isaku Yamahata
                   ` (14 subsequent siblings)
  15 siblings, 1 reply; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

fix typo.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 hw/apb_pci.c |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/hw/apb_pci.c b/hw/apb_pci.c
index 10a5baa..c619112 100644
--- a/hw/apb_pci.c
+++ b/hw/apb_pci.c
@@ -362,7 +362,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
     /* APB secondary busses */
     pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 0), true,
                                    "pbm-bridge");
-    br = DO_UPCAST(PCIBridge, dev, dev);
+    br = DO_UPCAST(PCIBridge, dev, pci_dev);
     pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 1",
                        pci_apb_map_irq);
     qdev_init_nofail(&pci_dev->qdev);
@@ -370,7 +370,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
 
     pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 1), true,
                                    "pbm-bridge");
-    br = DO_UPCAST(PCIBridge, dev, dev);
+    br = DO_UPCAST(PCIBridge, dev, pci_dev);
     pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 2",
                        pci_apb_map_irq);
     qdev_init_nofail(&pci_dev->qdev);
@@ -462,7 +462,7 @@ static PCIDeviceInfo pbm_pci_bridge_info = {
     .qdev.name = "pbm-bridge",
     .qdev.size = sizeof(PCIBridge),
     .qdev.vmsd = &vmstate_pci_device,
-    .qdev.reset = pci_brdige_reset,
+    .qdev.reset = pci_bridge_reset,
     .init = apb_pci_bridge_initfn,
     .exit = pci_bridge_exitfn,
     .config_write = pci_bridge_write_config,
-- 
1.7.1.1

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

* [Qemu-devel] [PATCH 02/14] pci: consolidate pci_add_capability_at_offset() into pci_add_capability().
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 01/14] RESEND apb: fix typo Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06  8:10   ` [Qemu-devel] " Michael S. Tsirkin
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 03/14] pci bridge: add helper function for ssvid capability Isaku Yamahata
                   ` (13 subsequent siblings)
  15 siblings, 1 reply; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

By making pci_add_capability() the special case of
pci_add_capability_at_offset() of offset = 0,
consolidate pci_add_capability_at_offset() into pci_add_capability().

Cc: Stefan Weil <weil@mail.berlios.de>
Cc: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 hw/eepro100.c |    4 ++--
 hw/msix.c     |    3 ++-
 hw/pci.c      |   33 ++++++++++++++++++---------------
 hw/pci.h      |    5 ++---
 4 files changed, 24 insertions(+), 21 deletions(-)

diff --git a/hw/eepro100.c b/hw/eepro100.c
index 2b75c8f..8cbc3aa 100644
--- a/hw/eepro100.c
+++ b/hw/eepro100.c
@@ -539,8 +539,8 @@ static void e100_pci_reset(EEPRO100State * s, E100PCIDeviceInfo *e100_device)
     if (e100_device->power_management) {
         /* Power Management Capabilities */
         int cfg_offset = 0xdc;
-        int r = pci_add_capability_at_offset(&s->dev, PCI_CAP_ID_PM,
-                                             cfg_offset, PCI_PM_SIZEOF);
+        int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM,
+                                   cfg_offset, PCI_PM_SIZEOF);
         assert(r >= 0);
         pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21);
 #if 0 /* TODO: replace dummy code for power management emulation. */
diff --git a/hw/msix.c b/hw/msix.c
index d99403a..7ce63eb 100644
--- a/hw/msix.c
+++ b/hw/msix.c
@@ -73,7 +73,8 @@ static int msix_add_config(struct PCIDevice *pdev, unsigned short nentries,
     }
 
     pdev->msix_bar_size = new_size;
-    config_offset = pci_add_capability(pdev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH);
+    config_offset = pci_add_capability(pdev, PCI_CAP_ID_MSIX,
+                                       0, MSIX_CAP_LENGTH);
     if (config_offset < 0)
         return config_offset;
     config = pdev->config + config_offset;
diff --git a/hw/pci.c b/hw/pci.c
index 2dc1577..754ffb3 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -1682,11 +1682,25 @@ static void pci_del_option_rom(PCIDevice *pdev)
     pdev->rom_offset = 0;
 }
 
-/* Reserve space and add capability to the linked list in pci config space */
-int pci_add_capability_at_offset(PCIDevice *pdev, uint8_t cap_id,
-                                 uint8_t offset, uint8_t size)
+/*
+ * if !offset
+ * Reserve space and add capability to the linked list in pci config space
+ *
+ * if offset = 0,
+ * Find and reserve space and add capability to the linked list
+ * in pci config space */
+int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
+                       uint8_t offset, uint8_t size)
 {
-    uint8_t *config = pdev->config + offset;
+    uint8_t *config;
+    if (!offset) {
+        offset = pci_find_space(pdev, size);
+        if (!offset) {
+            return -ENOSPC;
+        }
+    }
+
+    config = pdev->config + offset;
     config[PCI_CAP_LIST_ID] = cap_id;
     config[PCI_CAP_LIST_NEXT] = pdev->config[PCI_CAPABILITY_LIST];
     pdev->config[PCI_CAPABILITY_LIST] = offset;
@@ -1699,17 +1713,6 @@ int pci_add_capability_at_offset(PCIDevice *pdev, uint8_t cap_id,
     return offset;
 }
 
-/* Find and reserve space and add capability to the linked list
- * in pci config space */
-int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
-{
-    uint8_t offset = pci_find_space(pdev, size);
-    if (!offset) {
-        return -ENOSPC;
-    }
-    return pci_add_capability_at_offset(pdev, cap_id, offset, size);
-}
-
 /* Unlink capability from the pci config space. */
 void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
 {
diff --git a/hw/pci.h b/hw/pci.h
index c551f96..2ddba59 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -183,9 +183,8 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
                             pcibus_t size, int type,
                             PCIMapIORegionFunc *map_func);
 
-int pci_add_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size);
-int pci_add_capability_at_offset(PCIDevice *pci_dev, uint8_t cap_id,
-                                 uint8_t cap_offset, uint8_t cap_size);
+int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
+                       uint8_t offset, uint8_t size);
 
 void pci_del_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size);
 
-- 
1.7.1.1

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

* [Qemu-devel] [PATCH 03/14] pci bridge: add helper function for ssvid capability.
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 01/14] RESEND apb: fix typo Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 02/14] pci: consolidate pci_add_capability_at_offset() into pci_add_capability() Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 04/14] pci: call hotplug callback even when not hotplug case for later use Isaku Yamahata
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

helper function to add ssvid capability.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 hw/pci_bridge.c |   19 +++++++++++++++++++
 hw/pci_bridge.h |    3 +++
 2 files changed, 22 insertions(+), 0 deletions(-)

diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c
index 198c3c7..638e3b3 100644
--- a/hw/pci_bridge.c
+++ b/hw/pci_bridge.c
@@ -32,6 +32,25 @@
 #include "pci_bridge.h"
 #include "pci_internals.h"
 
+/* PCI bridge subsystem vendor ID helper functions */
+#define PCI_SSVID_SIZEOF        8
+#define PCI_SSVID_SVID          4
+#define PCI_SSVID_SSID          6
+
+int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset,
+                          uint16_t svid, uint16_t ssid)
+{
+    int pos;
+    pos = pci_add_capability(dev, PCI_CAP_ID_SSVID, offset, PCI_SSVID_SIZEOF);
+    if (pos < 0) {
+        return pos;
+    }
+
+    pci_set_word(dev->config + pos + PCI_SSVID_SVID, svid);
+    pci_set_word(dev->config + pos + PCI_SSVID_SSID, ssid);
+    return pos;
+}
+
 /* Accessor function to get parent bridge device from pci bus. */
 PCIDevice *pci_bridge_get_device(PCIBus *bus)
 {
diff --git a/hw/pci_bridge.h b/hw/pci_bridge.h
index 63ada19..f6fade0 100644
--- a/hw/pci_bridge.h
+++ b/hw/pci_bridge.h
@@ -28,6 +28,9 @@
 
 #include "pci.h"
 
+int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset,
+                          uint16_t svid, uint16_t ssid);
+
 PCIDevice *pci_bridge_get_device(PCIBus *bus);
 PCIBus *pci_bridge_get_sec_bus(PCIBridge *br);
 
-- 
1.7.1.1

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

* [Qemu-devel] [PATCH 04/14] pci: call hotplug callback even when not hotplug case for later use.
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
                   ` (2 preceding siblings ...)
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 03/14] pci bridge: add helper function for ssvid capability Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 05/14] pci: make pci_parse_devfn() aware of func Isaku Yamahata
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

call hotplug callback even when not hotplug case for later use.
And move hotplug check into hotplug callback.
PCIE slot needs this for card presence detection.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 hw/acpi_piix4.c |    3 +++
 hw/pci.c        |    3 ++-
 2 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index bfa1d9a..24dfcf2 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -611,6 +611,9 @@ static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, int state)
     PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev,
                                 DO_UPCAST(PCIDevice, qdev, qdev));
 
+    if (!dev->qdev.hotplugged)
+        return 0;
+
     s->pci0_status.up = 0;
     s->pci0_status.down = 0;
     if (state) {
diff --git a/hw/pci.c b/hw/pci.c
index 754ffb3..bb9ddea 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -1514,7 +1514,8 @@ static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
         pci_dev->romfile = qemu_strdup(info->romfile);
     pci_add_option_rom(pci_dev);
 
-    if (qdev->hotplugged) {
+    if (bus->hotplug) {
+        /* lower layer must check qdev->hotplugged */
         rc = bus->hotplug(bus->hotplug_qdev, pci_dev, 1);
         if (rc != 0) {
             int r = pci_unregister_device(&pci_dev->qdev);
-- 
1.7.1.1

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

* [Qemu-devel] [PATCH 05/14] pci: make pci_parse_devfn() aware of func.
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
                   ` (3 preceding siblings ...)
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 04/14] pci: call hotplug callback even when not hotplug case for later use Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 06/14] pci_ids.h: add vendor id of Texus Intesruments Isaku Yamahata
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

make pci_parse_devfn() aware of func. With func = NULL it behave as before.
This will be used later.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 hw/pci.c |   34 ++++++++++++++++++++++++++--------
 hw/pci.h |    2 ++
 2 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/hw/pci.c b/hw/pci.c
index bb9ddea..f03b83e 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -424,15 +424,18 @@ static void pci_set_default_subsystem_id(PCIDevice *pci_dev)
 }
 
 /*
- * Parse [[<domain>:]<bus>:]<slot>, return -1 on error
+ * Parse [[<domain>:]<bus>:]<slot>, return -1 on error if funcp == NULL
+ *       [[<domain>:]<bus>:]<slot>.<func>, return -1 on error
  */
-static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *slotp)
+int pci_parse_devaddr(const char *addr, int *domp, int *busp,
+                      unsigned int *slotp, unsigned int *funcp)
 {
     const char *p;
     char *e;
     unsigned long val;
     unsigned long dom = 0, bus = 0;
-    unsigned slot = 0;
+    unsigned int slot = 0;
+    unsigned int func = 0;
 
     p = addr;
     val = strtoul(p, &e, 16);
@@ -454,11 +457,24 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *s
 	}
     }
 
-    if (dom > 0xffff || bus > 0xff || val > 0x1f)
-	return -1;
-
     slot = val;
 
+    if (funcp != NULL) {
+        if (*e != '.')
+            return -1;
+
+        p = e + 1;
+        val = strtoul(p, &e, 16);
+        if (e == p)
+            return -1;
+
+        func = val;
+    }
+
+    /* if funcp == NULL func is 0 */
+    if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7)
+	return -1;
+
     if (*e)
 	return -1;
 
@@ -469,6 +485,8 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *s
     *domp = dom;
     *busp = bus;
     *slotp = slot;
+    if (funcp != NULL)
+        *funcp = func;
     return 0;
 }
 
@@ -479,7 +497,7 @@ int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
     if (!strncmp(addr, "pci_addr=", 9)) {
         addr += 9;
     }
-    if (pci_parse_devaddr(addr, domp, busp, slotp)) {
+    if (pci_parse_devaddr(addr, domp, busp, slotp, NULL)) {
         monitor_printf(mon, "Invalid pci address\n");
         return -1;
     }
@@ -496,7 +514,7 @@ PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr)
         return pci_find_bus(pci_find_root_bus(0), 0);
     }
 
-    if (pci_parse_devaddr(devaddr, &dom, &bus, &slot) < 0) {
+    if (pci_parse_devaddr(devaddr, &dom, &bus, &slot, NULL) < 0) {
         return NULL;
     }
 
diff --git a/hw/pci.h b/hw/pci.h
index 2ddba59..2b4c318 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -227,6 +227,8 @@ PCIBus *pci_find_bus(PCIBus *bus, int bus_num);
 PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function);
 PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr);
 
+int pci_parse_devaddr(const char *addr, int *domp, int *busp,
+                      unsigned int *slotp, unsigned int *funcp);
 int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
                      unsigned *slotp);
 
-- 
1.7.1.1

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

* [Qemu-devel] [PATCH 06/14] pci_ids.h: add vendor id of Texus Intesruments.
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
                   ` (4 preceding siblings ...)
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 05/14] pci: make pci_parse_devfn() aware of func Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 07/14] msi: implemented msi Isaku Yamahata
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

add vendor id of Texus Intesruments.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 hw/pci_ids.h |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index 39e9f1d..82cba7e 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -57,6 +57,8 @@
 #define PCI_VENDOR_ID_AMD                0x1022
 #define PCI_DEVICE_ID_AMD_LANCE          0x2000
 
+#define PCI_VENDOR_ID_TI                 0x104c
+
 #define PCI_VENDOR_ID_MOTOROLA           0x1057
 #define PCI_DEVICE_ID_MOTOROLA_MPC106    0x0002
 #define PCI_DEVICE_ID_MOTOROLA_RAVEN     0x4801
-- 
1.7.1.1

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

* [Qemu-devel] [PATCH 07/14] msi: implemented msi.
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
                   ` (5 preceding siblings ...)
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 06/14] pci_ids.h: add vendor id of Texus Intesruments Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06  9:44   ` [Qemu-devel] " Michael S. Tsirkin
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 08/14] pcie: helper functions for pcie extended capability Isaku Yamahata
                   ` (8 subsequent siblings)
  15 siblings, 1 reply; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

implemented msi support functions.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 Makefile.objs |    2 +-
 hw/msi.c      |  362 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/msi.h      |   41 +++++++
 hw/pci.h      |    5 +
 4 files changed, 409 insertions(+), 1 deletions(-)
 create mode 100644 hw/msi.c
 create mode 100644 hw/msi.h

diff --git a/Makefile.objs b/Makefile.objs
index 594894b..5f5a4c5 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -186,7 +186,7 @@ hw-obj-$(CONFIG_PIIX4) += piix4.o
 # PCI watchdog devices
 hw-obj-y += wdt_i6300esb.o
 
-hw-obj-y += msix.o
+hw-obj-y += msix.o msi.o
 
 # PCI network cards
 hw-obj-y += ne2000.o
diff --git a/hw/msi.c b/hw/msi.c
new file mode 100644
index 0000000..dbe89ee
--- /dev/null
+++ b/hw/msi.c
@@ -0,0 +1,362 @@
+/*
+ * msi.c
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "msi.h"
+
+#define PCI_MSI_PENDING_32      0x10
+#define PCI_MSI_PENDING_64      0x14
+
+/* PCI_MSI_FLAGS */
+#define PCI_MSI_FLAGS_QSIZE_SHIFT       4
+#define PCI_MSI_FLAGS_QMASK_SHIFT       1
+
+/* PCI_MSI_ADDRESS_LO */
+#define PCI_MSI_ADDRESS_LO_RESERVED_MASK        0x3
+
+#define PCI_MSI_32_SIZEOF       0x0a
+#define PCI_MSI_64_SIZEOF       0x0e
+#define PCI_MSI_32M_SIZEOF      0x14
+#define PCI_MSI_64M_SIZEOF      0x18
+
+#define MSI_DEBUG
+#ifdef MSI_DEBUG
+# define MSI_DPRINTF(fmt, ...)                                          \
+    fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__)
+#else
+# define MSI_DPRINTF(fmt, ...)  do { } while (0)
+#endif
+#define MSI_DEV_PRINTF(dev, fmt, ...)                                   \
+    MSI_DPRINTF("%s:%x " fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__)
+
+static inline bool msi_test_bit(uint32_t bitmap, uint8_t bit)
+{
+    return bitmap & (1 << bit);
+}
+
+static inline void msi_set_bit(uint32_t *bitmap, uint8_t bit)
+{
+    *bitmap |= (1 << bit);
+}
+
+static inline void msi_clear_bit(uint32_t *bitmap, uint8_t bit)
+{
+    *bitmap &= ~(1 << bit);
+}
+
+
+/* multiple vector only suport power of 2 and up to 32 */
+static inline uint8_t ilog2(uint8_t nr_vector)
+{
+    switch (nr_vector) {
+    case 1:
+        return 0;
+    case 2:
+        return 1;
+    case 4:
+        return 2;
+    case 8:
+        return 3;
+    case 16:
+        return 4;
+    case 32:
+        return 5;
+    default:
+        abort();
+        break;
+    }
+    return 0;
+}
+
+static inline bool is_mask_bit_support(uint16_t control)
+{
+    return control & PCI_MSI_FLAGS_MASKBIT;
+}
+
+static inline bool is_64bit_address(uint16_t control)
+{
+    return control & PCI_MSI_FLAGS_64BIT;
+}
+
+static inline uint8_t msi_vector_order(uint16_t control)
+{
+    return (control & PCI_MSI_FLAGS_QSIZE) >> PCI_MSI_FLAGS_QSIZE_SHIFT;
+}
+
+static inline uint8_t msi_nr_vector(uint16_t control)
+{
+    return 1 << msi_vector_order(control);
+}
+
+static inline bool msi_control_enabled(uint16_t control)
+{
+    return control & PCI_MSI_FLAGS_ENABLE;
+}
+
+static inline uint8_t msi_cap_sizeof(uint16_t control)
+{
+    switch (control & (PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT)) {
+    case PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT:
+        return PCI_MSI_64M_SIZEOF;
+    case PCI_MSI_FLAGS_64BIT:
+        return PCI_MSI_64_SIZEOF;
+    case PCI_MSI_FLAGS_MASKBIT:
+        return PCI_MSI_32M_SIZEOF;
+    case 0:
+        return PCI_MSI_32_SIZEOF;
+    default:
+        abort();
+        break;
+    }
+    return 0;
+}
+
+static inline uint16_t msi_control_reg(const PCIDevice* dev)
+{
+    return dev->msi_cap + PCI_MSI_FLAGS;
+}
+
+static inline uint32_t msi_lower_address_reg(const PCIDevice* dev)
+{
+    return dev->msi_cap + PCI_MSI_ADDRESS_LO;
+}
+
+static inline uint32_t msi_upper_address_reg(const PCIDevice* dev)
+{
+    return dev->msi_cap + PCI_MSI_ADDRESS_HI;
+}
+
+static inline uint16_t msi_data_reg(const PCIDevice* dev, bool is64bit)
+{
+    return dev->msi_cap + (is64bit ?  PCI_MSI_DATA_64 : PCI_MSI_DATA_32);
+}
+
+static inline uint32_t msi_mask_reg(const PCIDevice* dev, bool is64bit)
+{
+    return dev->msi_cap + (is64bit ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32);
+}
+
+static inline uint32_t msi_pending_reg(const PCIDevice* dev, bool is64bit)
+{
+    return dev->msi_cap + (is64bit ? PCI_MSI_PENDING_64 : PCI_MSI_PENDING_32);
+}
+
+bool msi_enabled(const PCIDevice *dev)
+{
+    return msi_present(dev) &&
+        msi_control_enabled(pci_get_word(dev->config + msi_control_reg(dev)));
+}
+
+int msi_init(struct PCIDevice *dev, uint8_t offset,
+             uint8_t nr_vector, uint16_t flags)
+{
+    uint8_t vector_order = ilog2(nr_vector);
+    bool is64bit = is_64bit_address(flags);
+    uint8_t cap_size;
+    int config_offset;
+    MSI_DEV_PRINTF(dev,
+                   "init offset: 0x%"PRIx8" vector: %"PRId8
+                   " flags 0x%"PRIx16"\n", offset, nr_vector, flags);
+
+    flags &= PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT;
+    flags |= (vector_order << PCI_MSI_FLAGS_QMASK_SHIFT) & PCI_MSI_FLAGS_QMASK;
+
+    cap_size = msi_cap_sizeof(flags);
+    config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size);
+    if (config_offset < 0) {
+        return config_offset;
+    }
+
+    dev->msi_cap = config_offset;
+    dev->cap_present |= QEMU_PCI_CAP_MSI;
+
+    pci_set_word(dev->config + msi_control_reg(dev), flags);
+    pci_set_word(dev->wmask + msi_control_reg(dev),
+                 PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
+    pci_set_long(dev->wmask + msi_lower_address_reg(dev),
+                 ~PCI_MSI_ADDRESS_LO_RESERVED_MASK);
+    if (is64bit) {
+        pci_set_long(dev->wmask + msi_upper_address_reg(dev), 0xffffffff);
+    }
+    pci_set_word(dev->wmask + msi_data_reg(dev, is64bit), 0xffff);
+
+    if (is_mask_bit_support(flags)) {
+        pci_set_long(dev->wmask + msi_mask_reg(dev, is64bit),
+                     (1 << nr_vector) - 1);
+    }
+    return config_offset;
+}
+
+void msi_uninit(struct PCIDevice *dev)
+{
+    uint16_t control = pci_get_word(dev->config + msi_control_reg(dev));
+    uint8_t cap_size = msi_cap_sizeof(control);
+    pci_del_capability(dev, PCI_CAP_ID_MSIX, cap_size);
+    MSI_DEV_PRINTF(dev, "uninit\n");
+}
+
+void msi_reset(PCIDevice *dev)
+{
+    uint16_t control;
+    bool is64bit;
+
+    control = pci_get_word(dev->config + msi_control_reg(dev));
+    control &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
+    is64bit = is_64bit_address(control);
+
+    pci_set_word(dev->config + msi_control_reg(dev), control);
+    pci_set_long(dev->config + msi_lower_address_reg(dev), 0);
+    if (is64bit) {
+        pci_set_long(dev->config + msi_upper_address_reg(dev), 0);
+    }
+    pci_set_word(dev->config + msi_data_reg(dev, is64bit), 0);
+    if (is_mask_bit_support(control)) {
+        pci_set_long(dev->config + msi_mask_reg(dev, is64bit), 0);
+        pci_set_long(dev->config + msi_pending_reg(dev, is64bit), 0);
+    }
+    MSI_DEV_PRINTF(dev, "reset\n");
+}
+
+static bool msi_is_masked(const PCIDevice *dev, uint8_t vector)
+{
+    uint16_t control = pci_get_word(dev->config + msi_control_reg(dev));
+    uint32_t mask;
+
+    if (!is_mask_bit_support(control)) {
+        return false;
+    }
+
+    mask = pci_get_long(dev->config +
+                        msi_mask_reg(dev, is_64bit_address(control)));
+    return msi_test_bit(mask, vector);
+}
+
+static void msi_set_pending(PCIDevice *dev, uint8_t vector)
+{
+    uint16_t control = pci_get_word(dev->config + msi_control_reg(dev));
+    bool is64bit = is_64bit_address(control);
+    uint32_t pending;
+
+    assert(is_mask_bit_support(control));
+
+    pending = pci_get_long(dev->config + msi_pending_reg(dev, is64bit));
+    msi_set_bit(&pending, vector);
+    pci_set_long(dev->config + msi_pending_reg(dev, is64bit), pending);
+    MSI_DEV_PRINTF(dev, "pending vector 0x%"PRIx8"\n", vector);
+}
+
+void msi_notify(PCIDevice *dev, uint8_t vector)
+{
+    uint16_t control = pci_get_word(dev->config + msi_control_reg(dev));
+    bool is64bit = is_64bit_address(control);
+    uint8_t nr_vector = msi_nr_vector(control);
+    uint64_t address;
+    uint32_t data;
+
+    assert(vector < nr_vector);
+    if (msi_is_masked(dev, vector)) {
+        msi_set_pending(dev, vector);
+        return;
+    }
+
+    if (is64bit){
+        address = pci_get_quad(dev->config + msi_lower_address_reg(dev));
+    } else {
+        address = pci_get_long(dev->config + msi_lower_address_reg(dev));
+    }
+
+    /* upper bit 31:16 is zero */
+    data = pci_get_word(dev->config + msi_data_reg(dev, is64bit));
+    if (nr_vector > 1) {
+        data &= ~(nr_vector - 1);
+        data |= vector;
+    }
+
+    MSI_DEV_PRINTF(dev,
+                   "notify vector 0x%"PRIx8
+                   " address: 0x%"PRIx64" data: 0x%"PRIx32"\n",
+                   vector, address, data);
+    stl_phys(address, data);
+}
+
+/* call this function after updating configs by pci_default_write_config() */
+void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len)
+{
+    uint16_t control = pci_get_word(dev->config + msi_control_reg(dev));
+    bool is64bit = is_64bit_address(control);
+    uint8_t nr_vector = msi_nr_vector(control);
+
+#ifdef MSI_DEBUG
+    uint8_t cap_size = msi_cap_sizeof(control & (PCI_MSI_FLAGS_MASKBIT |
+                                                 PCI_MSI_FLAGS_64BIT));
+    if (ranges_overlap(addr, len, dev->msi_cap, cap_size)) {
+        MSI_DEV_PRINTF(dev, "addr 0x%"PRIx32" val 0x%"PRIx32" len %d\n",
+                       addr, val, len);
+        MSI_DEV_PRINTF(dev, "ctrl: 0x%"PRIx16" address: 0x%"PRIx32,
+                       control,
+                       pci_get_long(dev->config + msi_lower_address_reg(dev)));
+        if (is64bit) {
+            fprintf(stderr, " addrss-hi: 0x%"PRIx32,
+                    pci_get_long(dev->config + msi_upper_address_reg(dev)));
+        }
+        fprintf(stderr, " data: 0x%"PRIx16,
+                pci_get_word(dev->config + msi_data_reg(dev, is64bit)));
+        if (is_mask_bit_support(control)) {
+            fprintf(stderr, " mask 0x%"PRIx32" pending 0x%"PRIx32,
+                    pci_get_long(dev->config + msi_mask_reg(dev, is64bit)),
+                    pci_get_long(dev->config + msi_pending_reg(dev, is64bit)));
+        }
+        fprintf(stderr, "\n");
+    }
+#endif
+
+    if (!msi_control_enabled(control)) {
+        return;
+    }
+
+    if (!is_mask_bit_support(control)) {
+        /* if per vector masking isn't support,
+           there is no pending interrupt. */
+        return;
+    }
+
+    if (range_covers_byte(addr, len, msi_control_reg(dev)) || /* MSI enable */
+        ranges_overlap(addr, len, msi_mask_reg(dev, is64bit), 4)) {
+        uint32_t pending =
+            pci_get_long(dev->config + msi_pending_reg(dev, is64bit));
+        uint8_t vector;
+
+        /* deliver pending interrupts which are unmasked */
+        for (vector = 0; vector < nr_vector; ++vector) {
+            if (msi_is_masked(dev, vector) || !msi_test_bit(pending, vector)) {
+                continue;
+            }
+
+            msi_clear_bit(&pending, vector);
+            pci_set_long(dev->config + msi_pending_reg(dev, is64bit), pending);
+            msi_notify(dev, vector);
+        }
+    }
+}
+
+uint8_t msi_nr_allocated_vector(const PCIDevice *dev)
+{
+    uint16_t control = pci_get_word(dev->config + msi_control_reg(dev));
+    return msi_nr_vector(control);
+}
diff --git a/hw/msi.h b/hw/msi.h
new file mode 100644
index 0000000..1131c6e
--- /dev/null
+++ b/hw/msi.h
@@ -0,0 +1,41 @@
+/*
+ * msi.h
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QEMU_MSI_H
+#define QEMU_MSI_H
+
+#include "qemu-common.h"
+#include "pci.h"
+
+bool msi_enabled(const PCIDevice *dev);
+int msi_init(struct PCIDevice *dev, uint8_t offset,
+             uint8_t nr_vector, uint16_t flags);
+void msi_uninit(struct PCIDevice *dev);
+void msi_reset(PCIDevice *dev);
+void msi_notify(PCIDevice *dev, uint8_t vector);
+void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len);
+uint8_t msi_nr_allocated_vector(const PCIDevice *dev);
+
+static inline bool msi_present(const PCIDevice *dev)
+{
+    return dev->cap_present & QEMU_PCI_CAP_MSI;
+}
+
+#endif /* QEMU_MSI_H */
diff --git a/hw/pci.h b/hw/pci.h
index 2b4c318..9387a03 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -115,6 +115,8 @@ enum {
     /* multifunction capable device */
 #define QEMU_PCI_CAP_MULTIFUNCTION_BITNR        2
     QEMU_PCI_CAP_MULTIFUNCTION = (1 << QEMU_PCI_CAP_MULTIFUNCTION_BITNR),
+
+    QEMU_PCI_CAP_MSI = 0x4,
 };
 
 struct PCIDevice {
@@ -168,6 +170,9 @@ struct PCIDevice {
     /* Version id needed for VMState */
     int32_t version_id;
 
+    /* Offset of MSI capability in config space */
+    uint8_t msi_cap;
+
     /* Location of option rom */
     char *romfile;
     ram_addr_t rom_offset;
-- 
1.7.1.1

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

* [Qemu-devel] [PATCH 08/14] pcie: helper functions for pcie extended capability.
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
                   ` (6 preceding siblings ...)
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 07/14] msi: implemented msi Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 09/14] pcie port: define struct PCIEPort/PCIESlot and helper functions Isaku Yamahata
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

This patch implements helper functions for pci express extended capability.
NOTE: presence detection depends on pci_qdev_init() change.
      PCIExpressDevice::aer_log_max is in PCIDevice for device property.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 Makefile.objs |    1 +
 hw/pci.h      |   24 +
 hw/pcie.c     | 1668 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pcie.h     |  186 +++++++
 qemu-common.h |    1 +
 5 files changed, 1880 insertions(+), 0 deletions(-)
 create mode 100644 hw/pcie.c
 create mode 100644 hw/pcie.h

diff --git a/Makefile.objs b/Makefile.objs
index 5f5a4c5..eeb5134 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -186,6 +186,7 @@ hw-obj-$(CONFIG_PIIX4) += piix4.o
 # PCI watchdog devices
 hw-obj-y += wdt_i6300esb.o
 
+hw-obj-y += pcie.o
 hw-obj-y += msix.o msi.o
 
 # PCI network cards
diff --git a/hw/pci.h b/hw/pci.h
index 9387a03..3f912b8 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -9,6 +9,8 @@
 /* PCI includes legacy ISA access.  */
 #include "isa.h"
 
+#include "pcie.h"
+
 /* PCI bus */
 
 #define PCI_DEVFN(slot, func)   ((((slot) & 0x1f) << 3) | ((func) & 0x07))
@@ -173,6 +175,12 @@ struct PCIDevice {
     /* Offset of MSI capability in config space */
     uint8_t msi_cap;
 
+    /* PCI Express */
+    PCIExpressDevice *exp;
+    /* Theoretically this belongs to  PCIExpressDevice.
+       However it is here for property and save/load */
+    struct pcie_aer_log aer_log;
+
     /* Location of option rom */
     char *romfile;
     ram_addr_t rom_offset;
@@ -368,6 +376,22 @@ static inline uint32_t pci_config_size(const PCIDevice *d)
     return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE;
 }
 
+
+/* These are pci express specific, so should belong to pcie.h.
+   they're here to avoid header inclusion error. */
+static inline uint8_t pci_pcie_cap(const PCIDevice *d)
+{
+    return d->exp ? d->exp->exp_cap : 0;
+}
+
+/* AER */
+static inline uint16_t pcie_aer_cap(const PCIDevice *d)
+{
+    assert(d->exp);
+    return d->exp->aer_cap;
+}
+
+
 /* These are not pci specific. Should move into a separate header.
  * Only pci.c uses them, so keep them here for now.
  */
diff --git a/hw/pcie.c b/hw/pcie.c
new file mode 100644
index 0000000..1f24c2a
--- /dev/null
+++ b/hw/pcie.c
@@ -0,0 +1,1668 @@
+/*
+ * pcie.c
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sysemu.h"
+#include "pci_bridge.h"
+#include "pcie.h"
+#include "msix.h"
+#include "msi.h"
+#include "pci_internals.h"
+
+//#define DEBUG_PCIE
+#ifdef DEBUG_PCIE
+# define PCIE_DPRINTF(fmt, ...)                                         \
+    fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__)
+#else
+# define PCIE_DPRINTF(fmt, ...) do {} while (0)
+#endif
+#define PCIE_DEV_PRINTF(dev, fmt, ...)                                  \
+    PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__)
+
+static inline const char *pcie_hp_event_name(enum PCIExpressHotPlugEvent event)
+{
+    switch (event) {
+    case PCI_EXP_HP_EV_ABP:
+        return "attention button pushed";
+    case PCI_EXP_HP_EV_PDC:
+        return "present detection changed";
+    case PCI_EXP_HP_EV_CCI:
+        return "command completed";
+    default:
+        break;
+    }
+    return "Unknown event";
+}
+
+static void pcie_aer_clear_error(PCIDevice *dev);
+static void pcie_aer_root_notify(PCIDevice *dev, bool trigger, int level);
+static AER_ERR_MSG_RESULT
+pcie_aer_errmsg_alldev(PCIDevice *dev, const struct pcie_aer_err_msg *msg);
+static AER_ERR_MSG_RESULT
+pcie_aer_errmsg_vbridge(PCIDevice *dev, const struct pcie_aer_err_msg *msg);
+
+/***************************************************************************
+ * pci express capability helper functions
+ */
+
+#define PCI_EXP_VER2_SIZEOF     0x3c    /* express capability of version 2 */
+
+/* PCI_EXP_FLAGS */
+#define PCI_EXP_FLAGS_VER2      2       /* for now, supports only version 2 */
+#define PCI_EXP_FLAGS_IRQ_SHIFT 9
+#define PCI_EXP_FLAGS_IRQ_REG(irq)      (((irq) << PCI_EXP_FLAGS_IRQ_SHIFT) & PCI_EXP_FLAGS_IRQ)
+#define PCI_EXP_FLAGS_TYPE_SHIFT        4
+
+/* PCI_EXP_LINK{CAP, STA} */
+/* link speed */
+#define PCI_EXP_LNK_LS_25               1
+
+#define PCI_EXP_LNK_MLW_SHIFT           4
+#define PCI_EXP_LNK_MLW_1               (1 << PCI_EXP_LNK_MLW_SHIFT)
+
+/* PCI_EXP_LINKCAP */
+#define PCI_EXP_LNKCAP_ASPMS_SHIFT      10
+#define PCI_EXP_LNKCAP_ASPMS_0S         (1 << PCI_EXP_LNKCAP_ASPMS_SHIFT)
+
+#define PCI_EXP_LNKCAP_PN_SHIFT         24
+#define PCI_EXP_LNKCAP_PN_REG(pn)       (((pn) << PCI_EXP_LNKCAP_PN_SHIFT) & PCI_EXP_LNKCAP_PN)
+
+#define PCI_EXP_SLTCAP_PSN_SHIFT        19
+#define PCI_EXP_SLTCAP_PSN_REG(slot)    (((slot) << PCI_EXP_SLTCAP_PSN_SHIFT) & PCI_EXP_SLTCAP_PSN)
+
+#define PCI_EXP_SLTCTL_AIC_SHIFT        6
+#define PCI_EXP_SLTCTL_AIC_ON           (PCI_EXP_HP_IND_ON << PCI_EXP_SLTCTL_AIC_SHIFT)
+#define PCI_EXP_SLTCTL_AIC_BLINK        (PCI_EXP_HP_IND_BLINK << PCI_EXP_SLTCTL_AIC_SHIFT)
+#define PCI_EXP_SLTCTL_AIC_OFF          (PCI_EXP_HP_IND_OFF << PCI_EXP_SLTCTL_AIC_SHIFT)
+
+#define PCI_EXP_SLTCTL_PIC_SHIFT        8
+#define PCI_EXP_SLTCTL_PIC_ON           (PCI_EXP_HP_IND_ON << PCI_EXP_SLTCTL_PIC_SHIFT)
+#define PCI_EXP_SLTCTL_PIC_BLINK        (PCI_EXP_HP_IND_BLINK << PCI_EXP_SLTCTL_PIC_SHIFT)
+#define PCI_EXP_SLTCTL_PIC_OFF          (PCI_EXP_HP_IND_OFF << PCI_EXP_SLTCTL_PIC_SHIFT)
+
+#define PCI_EXP_DEVCAP2_EFF             0x100000
+#define PCI_EXP_DEVCAP2_EETLPP          0x200000
+
+#define PCI_EXP_DEVCTL2_EETLPPB         0x80
+
+static void pcie_notify(PCIDevice *dev, uint16_t vector,
+                        bool trigger, int level)
+{
+    /* masking/masking interrupt is handled by upper layer.
+     * i.e. msix_notify() for MSI-X
+     *      msi_notify()  for MSI
+     *      pci_set_irq() for INTx
+     */
+    PCIE_DEV_PRINTF(dev, "noitfy vector %d tirgger:%d level:%d\n",
+                    vector, trigger, level);
+    if (msix_enabled(dev)) {
+        if (trigger) {
+            msix_notify(dev, vector);
+        }
+    } else if (msi_enabled(dev)) {
+        if (trigger){
+            msi_notify(dev, vector);
+        }
+    } else  {
+        qemu_set_irq(dev->irq[0], level);
+    }
+}
+
+static inline uint32_t pcie_written_val_long(uint32_t addr, uint32_t val,
+                                             uint32_t pos)
+{
+    if (addr >= pos) {
+        val <<= addr - pos;
+    } else {
+        val >>= pos - addr;
+    }
+    return val;
+}
+
+static inline uint16_t pcie_written_val_word(uint32_t addr, uint32_t val,
+                                             uint32_t pos)
+{
+    return pcie_written_val_long(addr, val, pos) & 0xffff;
+}
+
+/*
+ * RW1C: Write-1-to-clear
+ * regiger      written val        result
+ * 0            0               => 0
+ * 1            0               => 1
+ * 0            1               => 0
+ * 1            1               => 0
+ */
+static inline void pcie_w1c_long(PCIDevice *d, uint32_t pos, uint32_t mask,
+                                 uint32_t addr, uint32_t val)
+{
+    uint32_t written = pcie_written_val_long(addr, val, pos) & mask;
+    uint32_t reg = pci_get_long(d->config + pos);
+    reg &= ~written;
+    pci_set_long(d->config + pos, reg);
+}
+
+static inline void pcie_w1c_word(PCIDevice *d, uint32_t pos, uint16_t mask,
+                                 uint32_t addr, uint32_t val)
+{
+    uint16_t written = pcie_written_val_word(addr, val, pos) & mask;
+    uint16_t reg = pci_get_word(d->config + pos);
+    reg &= ~written;
+    pci_set_word(d->config + pos, reg);
+}
+
+int pci_pcie_cap_init(PCIDevice *dev,
+                      uint8_t offset, uint8_t type, uint8_t port)
+{
+    int exp_cap;
+    uint8_t *pcie_cap;
+
+    assert(pci_is_express(dev));
+    dev->exp = qemu_mallocz(sizeof(*dev->exp));
+
+    exp_cap = pci_add_capability(dev, PCI_CAP_ID_EXP, offset,
+                                 PCI_EXP_VER2_SIZEOF);
+    if (exp_cap < 0) {
+        qemu_free(dev->exp);
+        dev->exp = NULL;
+        return exp_cap;
+    }
+    dev->exp->exp_cap = exp_cap;
+    /* dev->cap_present |= QEMU_PCI_CAP_EXPRESS; */ /* already done in pci_qdev_init() */
+
+    pcie_cap = dev->config + pci_pcie_cap(dev);
+
+    /* capability register
+       interrupt message number defaults to 0 */
+    pci_set_word(pcie_cap + PCI_EXP_FLAGS,
+                 ((type << PCI_EXP_FLAGS_TYPE_SHIFT) & PCI_EXP_FLAGS_TYPE) |
+                 PCI_EXP_FLAGS_VER2);
+
+    /* device capability register
+     * table 7-12:
+     * roll based error reporting bit must be set by all
+     * Functions conforming to the ECN, PCI Express Base
+     * Specification, Revision 1.1., or subsequent PCI Express Base
+     * Specification revisions.
+     */
+    pci_set_long(pcie_cap + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER);
+
+    pci_set_long(pcie_cap + PCI_EXP_LNKCAP,
+                 PCI_EXP_LNKCAP_PN_REG(port) |
+                 PCI_EXP_LNKCAP_ASPMS_0S |
+                 PCI_EXP_LNK_MLW_1 |
+                 PCI_EXP_LNK_LS_25);
+
+    pci_set_word(pcie_cap + PCI_EXP_LNKSTA,
+                 PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25);
+
+    pci_set_long(pcie_cap + PCI_EXP_DEVCAP2,
+                 PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP);
+
+    pci_set_word(dev->wmask + exp_cap, PCI_EXP_DEVCTL2_EETLPPB);
+    return exp_cap;
+}
+
+int pci_pcie_cap_exit(PCIDevice *dev)
+{
+    /* pci_del_capability(dev, PCI_CAP_ID_EXP, PCI_EXP_VER2_SIZEOF); */
+    qemu_free(dev->exp);
+    return 0;
+}
+
+uint8_t pcie_cap_get_type(const PCIDevice *dev)
+{
+    uint32_t pos = pci_pcie_cap(dev);
+    assert(pos > 0);
+    return (pci_get_word(dev->config + pos + PCI_EXP_FLAGS) &
+            PCI_EXP_FLAGS_TYPE) >> PCI_EXP_FLAGS_TYPE_SHIFT;
+}
+
+/* MSI/MSI-X */
+/* pci express interrupt message number */
+void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector)
+{
+    uint8_t *pcie_cap = dev->config + pci_pcie_cap(dev);
+    uint16_t tmp;
+
+    assert(vector <= 32);
+    tmp = pci_get_word(pcie_cap + PCI_EXP_FLAGS);
+    tmp &= ~PCI_EXP_FLAGS_IRQ;
+    tmp |= PCI_EXP_FLAGS_IRQ_REG(vector);
+    pci_set_word(pcie_cap + PCI_EXP_FLAGS, tmp);
+}
+
+uint8_t pcie_cap_flags_get_vector(PCIDevice *dev)
+{
+    return (pci_get_word(dev->config + pci_pcie_cap(dev) + PCI_EXP_FLAGS) &
+            PCI_EXP_FLAGS_IRQ) >> PCI_EXP_FLAGS_IRQ_SHIFT;
+}
+
+static void pcie_cap_notify(PCIDevice *dev, bool trigger, int level)
+{
+    pcie_notify(dev, pcie_cap_flags_get_vector(dev), trigger, level);
+}
+
+void pcie_cap_deverr_init(PCIDevice *dev)
+{
+    uint32_t pos = pci_pcie_cap(dev);
+    uint8_t *pcie_cap = dev->config + pos;
+    uint8_t *pcie_wmask = dev->wmask + pos;
+
+    pci_set_long(pcie_cap + PCI_EXP_DEVCAP,
+                 pci_get_long(pcie_cap + PCI_EXP_DEVCAP) |
+                 PCI_EXP_DEVCAP_RBER);
+
+    pci_set_long(pcie_wmask + PCI_EXP_DEVCTL,
+                 pci_get_long(pcie_wmask + PCI_EXP_DEVCTL) |
+                 PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
+                 PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
+}
+
+void pcie_cap_deverr_reset(PCIDevice *dev)
+{
+    uint8_t *pcie_cap = dev->config + pci_pcie_cap(dev);
+    pci_set_long(pcie_cap + PCI_EXP_DEVCTL,
+                 pci_get_long(pcie_cap + PCI_EXP_DEVCTL) &
+                 ~(PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
+                   PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE));
+}
+
+void pcie_cap_deverr_write_config(PCIDevice *dev,
+                                  uint32_t addr, uint32_t val, int len)
+{
+    uint32_t pos = pci_pcie_cap(dev);
+    if (ranges_overlap(addr, len, pos + PCI_EXP_DEVSTA, 4)) {
+        /* RW1C */
+        pcie_w1c_long(dev, pos + PCI_EXP_DEVSTA,
+                      PCI_EXP_DEVSTA_CED | PCI_EXP_DEVSTA_NFED |
+                      PCI_EXP_DEVSTA_URD | PCI_EXP_DEVSTA_URD,
+                      addr, val);
+    }
+}
+
+/*
+ * events: PCI_EXP_HP_EV_xxx
+ * status: bit or of PCI_EXP_SLTSTA_xxx
+ */
+static void pcie_cap_slot_event(PCIDevice *dev,
+                                enum PCIExpressHotPlugEvent events,
+                                uint16_t status)
+{
+    bool trigger = false;
+    int level = 0;
+    uint8_t *pcie_cap = dev->config + pci_pcie_cap(dev);
+    uint16_t sltctl = pci_get_word(pcie_cap + PCI_EXP_SLTCTL);
+    uint16_t sltsta = pci_get_word(pcie_cap + PCI_EXP_SLTSTA);
+
+    PCIE_DEV_PRINTF(dev,
+                    "sltctl: 0x%0x2 sltsta: 0x%02x event:%x %s status:%d\n",
+                    sltctl, sltsta,
+                    events, pcie_hp_event_name(events), status);
+    events &= PCI_EXP_HP_EV_SUPPORTED;
+    if ((sltctl & PCI_EXP_SLTCTL_HPIE) && (sltctl & events) &&
+        ((sltsta ^ events) & events) /* 0 -> 1 */) {
+        trigger = true;
+    }
+
+    if (events & PCI_EXP_HP_EV_PDC) {
+        sltsta &= ~PCI_EXP_SLTSTA_PDS;
+        sltsta |= (status & PCI_EXP_SLTSTA_PDS);
+    }
+    sltsta |= events;
+    pci_set_word(pcie_cap + PCI_EXP_SLTSTA, sltsta);
+    PCIE_DEV_PRINTF(dev, "sltsta -> %02xn", sltsta);
+
+    if ((sltctl & PCI_EXP_SLTCTL_HPIE) && (sltsta & PCI_EXP_HP_EV_SUPPORTED)) {
+        level = 1;
+    }
+
+    pcie_cap_notify(dev, trigger, level);
+}
+
+static int pcie_cap_slot_hotplug(DeviceState *qdev,
+                                 PCIDevice *pci_dev, int state)
+{
+    PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
+    uint8_t *pcie_cap = d->config + pci_pcie_cap(d);
+    uint16_t sltsta = pci_get_word(pcie_cap + PCI_EXP_SLTSTA);
+
+    if (!pci_dev->qdev.hotplugged) {
+        assert(state); /* this case only happens machine creation. */
+        sltsta |= PCI_EXP_SLTSTA_PDS;
+        pci_set_word(pcie_cap + PCI_EXP_SLTSTA, sltsta);
+        return 0;
+    }
+
+    PCIE_DEV_PRINTF(pci_dev, "hotplug state: %d\n", state);
+    if (sltsta & PCI_EXP_SLTSTA_EIS) {
+        /* the slot is electromechanically locked. */
+        return -EBUSY;
+    }
+
+    if (state) {
+        if (PCI_FUNC(pci_dev->devfn) == 0) {
+            /* event is per slot. Not per function
+             * only generates event for function = 0.
+             * When hot plug, populate functions > 0
+             * and then add function = 0 last.
+             */
+            pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC, PCI_EXP_SLTSTA_PDS);
+        }
+    } else {
+        PCIBridge *br;
+        PCIBus *bus;
+        DeviceState *next;
+        if (PCI_FUNC(pci_dev->devfn) != 0) {
+            /* event is per slot. Not per function.
+               accepts function = 0 only. */
+            return -EINVAL;
+        }
+
+        /* zap all functions. */
+        br = DO_UPCAST(PCIBridge, dev, d);
+        bus = pci_bridge_get_sec_bus(br);
+        QLIST_FOREACH_SAFE(qdev, &bus->qbus.children, sibling, next) {
+            qdev_free(qdev);
+        }
+
+        pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC, 0);
+    }
+    return 0;
+}
+
+/* pci express slot for pci express root/downstream port
+   PCI express capability slot registers */
+void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot)
+{
+    uint8_t *pcie_cap = dev->config + pci_pcie_cap(dev);
+    uint8_t *pcie_wmask = dev->wmask + pci_pcie_cap(dev);
+    uint32_t tmp;
+
+    pci_set_word(pcie_cap + PCI_EXP_FLAGS,
+                 pci_get_word(pcie_cap + PCI_EXP_FLAGS) | PCI_EXP_FLAGS_SLOT);
+
+    tmp = pci_get_long(pcie_cap + PCI_EXP_SLTCAP);
+    tmp &= PCI_EXP_SLTCAP_PSN;
+    tmp |=
+        PCI_EXP_SLTCAP_PSN_REG(slot) |
+        PCI_EXP_SLTCAP_EIP |
+        PCI_EXP_SLTCAP_HPS |
+        PCI_EXP_SLTCAP_HPC |
+        PCI_EXP_SLTCAP_PIP |
+        PCI_EXP_SLTCAP_AIP |
+        PCI_EXP_SLTCAP_ABP;
+    pci_set_long(pcie_cap + PCI_EXP_SLTCAP, tmp);
+
+    tmp = pci_get_word(pcie_cap + PCI_EXP_SLTCTL);
+    tmp &= ~(PCI_EXP_SLTCTL_PIC | PCI_EXP_SLTCTL_AIC);
+    tmp |= PCI_EXP_SLTCTL_PIC_OFF | PCI_EXP_SLTCTL_AIC_OFF;
+    pci_set_word(pcie_cap + PCI_EXP_SLTCTL, tmp);
+    pci_set_word(pcie_wmask + PCI_EXP_SLTCTL,
+                 pci_get_word(pcie_wmask + PCI_EXP_SLTCTL) |
+                 PCI_EXP_SLTCTL_PIC |
+                 PCI_EXP_SLTCTL_AIC |
+                 PCI_EXP_SLTCTL_HPIE |
+                 PCI_EXP_SLTCTL_CCIE |
+                 PCI_EXP_SLTCTL_PDCE |
+                 PCI_EXP_SLTCTL_ABPE);
+
+    pci_bus_hotplug(pci_bridge_get_sec_bus(DO_UPCAST(PCIBridge, dev, dev)),
+                    pcie_cap_slot_hotplug, &dev->qdev);
+}
+
+void pcie_cap_slot_reset(PCIDevice *dev)
+{
+    uint8_t *pcie_cap = dev->config + pci_pcie_cap(dev);
+    uint32_t tmp;
+
+    PCIE_DEV_PRINTF(dev, "reset\n");
+
+    tmp = pci_get_word(pcie_cap + PCI_EXP_SLTCTL);
+    tmp &= ~(PCI_EXP_SLTCTL_EIC |
+             PCI_EXP_SLTCTL_PIC |
+             PCI_EXP_SLTCTL_AIC |
+             PCI_EXP_SLTCTL_HPIE |
+             PCI_EXP_SLTCTL_CCIE |
+             PCI_EXP_SLTCTL_PDCE |
+             PCI_EXP_SLTCTL_ABPE);
+    tmp |= PCI_EXP_SLTCTL_PIC_OFF | PCI_EXP_SLTCTL_AIC_OFF;
+    pci_set_word(pcie_cap + PCI_EXP_SLTCTL, tmp);
+
+    tmp = pci_get_word(pcie_cap + PCI_EXP_SLTSTA);
+    tmp &= ~(PCI_EXP_SLTSTA_EIS | /* by reset, the lock is released */
+             PCI_EXP_SLTSTA_CC |
+             PCI_EXP_SLTSTA_PDC |
+             PCI_EXP_SLTSTA_ABP);
+    pci_set_word(pcie_cap + PCI_EXP_SLTSTA, tmp);
+}
+
+void pcie_cap_slot_write_config(PCIDevice *dev,
+                                uint32_t addr, uint32_t val, int len,
+                                uint16_t sltctl_prev)
+{
+    uint32_t pos = pci_pcie_cap(dev);
+    uint8_t *pcie_cap = dev->config + pos;
+    uint16_t sltctl = pci_get_word(pcie_cap + PCI_EXP_SLTCTL);
+    uint16_t sltsta = pci_get_word(pcie_cap + PCI_EXP_SLTSTA);
+
+    PCIE_DEV_PRINTF(dev,
+                    "addr: 0x%x val: 0x%x len: %d\n"
+                    "\tsltctl_prev: 0x%02x sltctl: 0x%02x sltsta 0x%02x\n",
+                    addr, val, len, sltctl_prev, sltctl, sltsta);
+    /* SLTSTA: process SLTSTA before SLTCTL to avoid spurious interrupt */
+    if (ranges_overlap(addr, len, pos + PCI_EXP_SLTSTA, 2)) {
+        /* RW1C */
+        pcie_w1c_word(dev, pos + PCI_EXP_SLTSTA, PCI_EXP_HP_EV_SUPPORTED,
+                      addr, val);
+        sltsta = pci_get_word(pcie_cap + PCI_EXP_SLTSTA);
+
+        /* write to stlsta results in clearing bits,
+           so new interrupts won't be generated. */
+        PCIE_DEV_PRINTF(dev, "sltsta -> 0x%02x\n", sltsta);
+    }
+
+    /* SLTCTL */
+    if (ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) {
+        PCIE_DEV_PRINTF(dev, "sltctl: 0x%02x -> 0x%02x\n",
+                        sltctl_prev, sltctl);
+        if (pcie_written_val_word(addr, val, pos + PCI_EXP_SLTCTL) &
+            PCI_EXP_SLTCTL_EIC) {
+            /* toggle PCI_EXP_SLTSTA_EIS */
+            sltsta = (sltsta & ~PCI_EXP_SLTSTA_EIS) |
+                ((sltsta ^ PCI_EXP_SLTSTA_EIS) & PCI_EXP_SLTSTA_EIS);
+            pci_set_word(pcie_cap + PCI_EXP_SLTSTA, sltsta);
+            PCIE_DEV_PRINTF(dev, "PCI_EXP_SLTCTL_EIC: sltsta -> 0x%02x\n",
+                            sltsta);
+        }
+
+        if (sltctl & PCI_EXP_SLTCTL_HPIE) {
+            bool trigger = false;
+            int level = 0;
+
+            if (((sltctl_prev ^ sltctl) & sltctl) & PCI_EXP_HP_EV_SUPPORTED) {
+                /* 0 -> 1 */
+                trigger = true;
+            }
+            if ((sltctl & sltsta) & PCI_EXP_HP_EV_SUPPORTED) {
+                level = 1;
+            }
+            pcie_cap_notify(dev, trigger, level);
+        }
+
+        /* command completed.
+           unlike real hardware, command completes instantaneously */
+#define PCI_EXP_SLTCTL_SUPPORTED        \
+            (PCI_EXP_SLTCTL_ABPE |      \
+             PCI_EXP_SLTCTL_PDCE |      \
+             PCI_EXP_SLTCTL_CCIE |      \
+             PCI_EXP_SLTCTL_HPIE |      \
+             PCI_EXP_SLTCTL_AIC |       \
+             PCI_EXP_SLTCTL_PCC |       \
+             PCI_EXP_SLTCTL_EIC)
+        if ( 1 /* (sltctl_prev ^ sltctl) & PCI_EXP_SLTCTL_SUPPORTED */ ) {
+            /* set command completed bit */
+            pcie_cap_slot_event(dev, PCI_EXP_HP_EV_CCI, 0);
+        }
+    }
+}
+
+void pcie_cap_slot_push_attention_button(PCIDevice *dev)
+{
+    pcie_cap_slot_event(dev, PCI_EXP_HP_EV_ABP, 0);
+}
+
+/* root control/capabilities/status. PME isn't emulated for now */
+void pcie_cap_root_init(PCIDevice *dev)
+{
+    uint8_t pos = pci_pcie_cap(dev);
+    pci_set_word(dev->wmask + pos + PCI_EXP_RTCTL,
+                 PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE |
+                 PCI_EXP_RTCTL_SEFEE);
+}
+
+void pcie_cap_root_reset(PCIDevice *dev)
+{
+    uint8_t *pcie_cap = dev->config + pci_pcie_cap(dev);
+    pci_set_word(pcie_cap + PCI_EXP_RTCTL, 0);
+}
+
+/* function level reset(FLR) */
+void pcie_cap_flr_init(PCIDevice *dev, pcie_flr_fn flr)
+{
+    uint8_t *pcie_cap = dev->config + pci_pcie_cap(dev);
+    pci_set_word(pcie_cap + PCI_EXP_DEVCAP,
+                 pci_get_word(pcie_cap + PCI_EXP_DEVCAP) | PCI_EXP_DEVCAP_FLR);
+    dev->exp->flr = flr;
+}
+
+void pcie_cap_flr_write_config(PCIDevice *dev,
+                               uint32_t addr, uint32_t val, int len)
+{
+    uint32_t pos = pci_pcie_cap(dev);
+    if (ranges_overlap(addr, len, pos + PCI_EXP_DEVCTL, 2)) {
+        uint16_t val16 = pcie_written_val_word(addr, val,
+                                               pos + PCI_EXP_DEVCTL);
+        if ((val16 & PCI_EXP_DEVCTL_BCR_FLR) && dev->exp->flr) {
+            dev->exp->flr(dev);
+        }
+    }
+}
+
+
+/* Alternative Routing-ID Interpretation (ARI) */
+/* ari forwarding support for down stream port */
+void pcie_cap_ari_init(PCIDevice *dev)
+{
+    uint8_t *pcie_cap = dev->config + pci_pcie_cap(dev);
+    uint8_t *pcie_wmask = dev->wmask + pci_pcie_cap(dev);
+
+    pci_set_long(pcie_cap + PCI_EXP_DEVCAP2,
+                 pci_get_long(pcie_cap + PCI_EXP_DEVCAP2) |
+                 PCI_EXP_DEVCAP2_ARI);
+
+    pci_set_long(pcie_wmask + PCI_EXP_DEVCTL2,
+                 pci_get_long(pcie_wmask + PCI_EXP_DEVCTL2) |
+                 PCI_EXP_DEVCTL2_ARI);
+}
+
+void pcie_cap_ari_reset(PCIDevice *dev)
+{
+    uint8_t *pcie_cap = dev->config + pci_pcie_cap(dev);
+
+    pci_set_long(pcie_cap + PCI_EXP_DEVCTL2,
+                 pci_get_long(pcie_cap + PCI_EXP_DEVCTL2) &
+                 ~PCI_EXP_DEVCTL2_ARI);
+}
+
+bool pcie_cap_is_ari_enabled(const PCIDevice *dev)
+{
+    if (!pci_is_express(dev)) {
+        return false;
+    }
+    if (!pci_pcie_cap(dev)) {
+        return false;
+    }
+
+    return pci_get_long(dev->config + pci_pcie_cap(dev) + PCI_EXP_DEVCTL2) &
+        PCI_EXP_DEVCTL2_ARI;
+}
+
+/**************************************************************************
+ * pci express extended capability allocation functions
+ * uint16_t ext_cap_id (16 bit)
+ * uint8_t cap_ver (4 bit)
+ * uint16_t cap_offset (12 bit)
+ * uint16_t ext_cap_size
+ */
+
+#define PCI_EXT_CAP_VER_SHIFT   16
+#define PCI_EXT_CAP_NEXT_MASK   0xfff00000
+#define PCI_EXT_CAP_NEXT_SHIFT  20
+
+#define PCI_EXT_CAP(id, ver, next) ((id) | ((ver) << PCI_EXT_CAP_VER_SHIFT) | ((next) << PCI_EXT_CAP_NEXT_SHIFT))
+
+#define PCI_EXT_CAP_ALIGN       4
+#define PCI_EXT_CAP_ALIGNUP(x)  (((x) + PCI_EXT_CAP_ALIGN - 1) & ~(PCI_EXT_CAP_ALIGN - 1))
+
+static int16_t pcie_ext_cap_find_space(PCIDevice *dev, uint16_t size)
+{
+    uint16_t offset = PCI_CONFIG_SPACE_SIZE;
+    uint16_t i = offset;
+
+    while (i < PCIE_CONFIG_SPACE_SIZE - size) {
+        if (dev->used[i]) {
+            offset = PCI_EXT_CAP_ALIGNUP(i + 1);
+            i = offset;
+            continue;
+        } else if (i - offset + 1 == size) {
+            return offset;
+        }
+
+        ++i;
+    }
+
+    return 0;
+}
+
+static uint16_t pcie_find_ext_capability_list(PCIDevice *dev, uint16_t cap_id,
+                                              uint16_t *prev_p)
+{
+    int ttl;
+
+    uint16_t prev = 0;
+    uint16_t next = PCI_CONFIG_SPACE_SIZE;
+    uint32_t header = pci_get_long(dev->config + next);
+
+    if (!header) {
+        return 0;
+    }
+
+    /* minimum 8 bytes per capability */
+    ttl = (PCIE_CONFIG_SPACE_SIZE - PCI_CONFIG_SPACE_SIZE) / 8;
+
+    while (ttl-- > 0) {
+        if (PCI_EXT_CAP_ID(header) == cap_id) {
+            break;
+        }
+
+        prev = next;
+        next = PCI_EXT_CAP_NEXT(header);
+        if (next < PCI_CONFIG_SPACE_SIZE) {
+            return 0;
+        }
+        header = pci_get_long(dev->config + prev);
+    }
+
+    if (!ttl) {
+        return 0;
+    }
+    if (prev_p) {
+        *prev_p = prev;
+    }
+    return next;
+}
+
+uint16_t pcie_find_ext_capability(PCIDevice *dev, uint16_t cap_id)
+{
+    return pcie_find_ext_capability_list(dev, cap_id, NULL);
+}
+
+static void pcie_ext_cap_set_next(PCIDevice *dev, uint16_t pos, uint16_t next)
+{
+    uint16_t header = pci_get_long(dev->config + pos);
+    assert(!(next & (PCI_EXT_CAP_ALIGN - 1)));
+    header = (header & ~PCI_EXT_CAP_NEXT_MASK) |
+        ((next << PCI_EXT_CAP_NEXT_SHIFT) & PCI_EXT_CAP_NEXT_MASK);
+    pci_set_long(dev->config + pos, header);
+}
+
+static void pcie_allocate_ext_capability(PCIDevice *dev,
+                                         uint16_t cap_id, uint8_t cap_ver,
+                                         uint16_t offset, uint16_t size)
+{
+    uint32_t header;
+    uint16_t next;
+
+    assert(offset < offset + size);
+    assert(offset + size < PCIE_CONFIG_SPACE_SIZE);
+    assert(size >= 8);
+
+    if (offset == PCI_CONFIG_SPACE_SIZE) {
+        header = pci_get_long(dev->config + offset);
+        next = PCI_EXT_CAP_NEXT(header);
+    } else {
+        /* find last ext cap */
+        int ttl = (PCIE_CONFIG_SPACE_SIZE - PCI_CONFIG_SPACE_SIZE) / 8;
+        uint16_t pos = PCI_CONFIG_SPACE_SIZE;
+        while (ttl-- > 0) {
+            header = pci_get_long(dev->config + pos);
+            if (PCI_EXT_CAP_NEXT(header) < PCI_CONFIG_SPACE_SIZE) {
+                break;
+            }
+
+            pos = PCI_EXT_CAP_NEXT(header);
+        }
+
+        assert(ttl > 0); /* since it is known that [offset, offset + size]
+                            is unused, so ttl shouldn't be zero */
+        pcie_ext_cap_set_next(dev, pos, offset);
+        next = 0;
+    }
+    pci_set_long(dev->config + offset, PCI_EXT_CAP(cap_id, cap_ver, next));
+
+    memset(dev->used + offset, 0xFF, size);
+    /* Make capability read-only by default */
+    memset(dev->wmask + offset, 0, size);
+    /* Check capability by default */
+    memset(dev->cmask + offset, 0xFF, size);
+}
+
+int pcie_add_ext_capability(PCIDevice *dev,
+                            uint16_t cap_id, uint8_t cap_ver, uint16_t size)
+{
+    uint16_t offset = pcie_ext_cap_find_space(dev, size);
+
+    if (!offset) {
+        return -ENOSPC;
+    }
+
+    pcie_allocate_ext_capability(dev, cap_id, cap_ver, offset, size);
+    return offset;
+}
+
+int pcie_append_ext_capability(PCIDevice *dev,
+                               uint16_t cap_id, uint8_t cap_ver,
+                               uint16_t offset, uint16_t size)
+{
+    uint16_t i;
+
+    if (!offset) {
+        return pcie_add_ext_capability(dev, cap_id, cap_ver, size);
+    }
+
+    assert(offset < offset + size);
+    assert(offset + size < PCIE_CONFIG_SPACE_SIZE);
+    assert(size >= 8);
+
+    for (i = offset; i < offset + size; ++i) {
+        if (dev->used[i]) {
+            return -EBUSY;
+        }
+    }
+
+    pcie_allocate_ext_capability(dev, cap_id, cap_ver, offset, size);
+    return offset;
+}
+
+void pcie_del_ext_capability(PCIDevice *dev, uint16_t cap_id, uint16_t size)
+{
+    uint16_t prev;
+    uint16_t offset = pcie_find_ext_capability_list(dev, cap_id, &prev);
+    uint32_t header;
+
+    if (!offset) {
+        return;
+    }
+
+    header = pci_get_long(dev->config + offset);
+    if (prev) {
+        pcie_ext_cap_set_next(dev, prev, PCI_EXT_CAP_NEXT(header));
+    } else {
+        /* move up next ext cap to PCI_CONFIG_SPACE_SIZE? */
+        assert(offset == PCI_CONFIG_SPACE_SIZE);
+        pci_set_long(dev->config + offset,
+                     PCI_EXT_CAP(0, 0, PCI_EXT_CAP_NEXT(header)));
+    }
+
+    /* Make capability writeable again */
+    memset(dev->wmask + offset, 0xff, size);
+    /* Clear cmask as device-specific registers can't be checked */
+    memset(dev->cmask + offset, 0, size);
+    memset(dev->used + offset, 0, size);
+}
+
+void pcie_reserve_ext_capability(PCIDevice *dev,
+                                 uint16_t offset, uint16_t size)
+{
+    memset(dev->used + offset, 0xff, size);
+}
+
+/**************************************************************************
+ * pci express extended capability helper functions
+ */
+
+/* ARI */
+#define PCI_ARI_VER     1
+#define PCI_ARI_SIZEOF  8
+
+int pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn)
+{
+    int pos;
+    pos = pcie_append_ext_capability(dev, PCI_EXT_CAP_ID_ARI, PCI_ARI_VER,
+                                     offset, PCI_ARI_SIZEOF);
+    if (pos < 0) {
+        return pos;
+    }
+
+    pci_set_long(dev->config + pos + PCI_ARI_CAP, PCI_ARI_CAP_NFN(nextfn));
+    return pos;
+}
+
+/* AER */
+#define PCI_ERR_VER                     2
+#define PCI_ERR_SIZEOF                  0x48
+
+#define PCI_ERR_UNC_SDN                 0x00000020      /* surprise down */
+#define PCI_ERR_UNC_ACSV                0x00200000      /* ACS Violation */
+#define PCI_ERR_UNC_INTN                0x00400000      /* Internal Error */
+#define PCI_ERR_UNC_MCBTLP              0x00800000      /* MC Blcoked TLP */
+#define PCI_ERR_UNC_ATOP_EBLOCKED       0x01000000      /* atomic op egress blocked */
+#define PCI_ERR_UNC_TLP_PRF_BLOCKED     0x02000000      /* TLP Prefix Blocked */
+#define PCI_ERR_UNC_SUPPORTED           (PCI_ERR_UNC_DLP |              \
+                                         PCI_ERR_UNC_SDN |              \
+                                         PCI_ERR_UNC_POISON_TLP |       \
+                                         PCI_ERR_UNC_FCP |              \
+                                         PCI_ERR_UNC_COMP_TIME |        \
+                                         PCI_ERR_UNC_COMP_ABORT |       \
+                                         PCI_ERR_UNC_UNX_COMP |         \
+                                         PCI_ERR_UNC_RX_OVER |          \
+                                         PCI_ERR_UNC_MALF_TLP |         \
+                                         PCI_ERR_UNC_ECRC |             \
+                                         PCI_ERR_UNC_UNSUP |            \
+                                         PCI_ERR_UNC_ACSV |             \
+                                         PCI_ERR_UNC_INTN |             \
+                                         PCI_ERR_UNC_MCBTLP |           \
+                                         PCI_ERR_UNC_ATOP_EBLOCKED |    \
+                                         PCI_ERR_UNC_TLP_PRF_BLOCKED)
+
+#define PCI_ERR_UNC_SEVERITY_DEFAULT    (PCI_ERR_UNC_DLP |              \
+                                         PCI_ERR_UNC_SDN |              \
+                                         PCI_ERR_UNC_FCP |              \
+                                         PCI_ERR_UNC_RX_OVER |          \
+                                         PCI_ERR_UNC_MALF_TLP |         \
+                                         PCI_ERR_UNC_INTN)
+
+#define PCI_ERR_COR_ADV_NONFATAL        0x00002000      /* Advisory Non-Fatal */
+#define PCI_ERR_COR_INTERNAL            0x00004000      /* Corrected Internal */
+#define PCI_ERR_COR_HL_OVERFLOW         0x00008000      /* Header Long Overflow */
+#define PCI_ERR_COR_SUPPORTED           (PCI_ERR_COR_RCVR |             \
+                                         PCI_ERR_COR_BAD_TLP |          \
+                                         PCI_ERR_COR_BAD_DLLP |         \
+                                         PCI_ERR_COR_REP_ROLL |         \
+                                         PCI_ERR_COR_REP_TIMER |        \
+                                         PCI_ERR_COR_ADV_NONFATAL |     \
+                                         PCI_ERR_COR_INTERNAL |         \
+                                         PCI_ERR_COR_HL_OVERFLOW)
+#define PCI_ERR_COR_MASK_DEFAULT        (PCI_ERR_COR_ADV_NONFATAL |     \
+                                         PCI_ERR_COR_INTERNAL |         \
+                                         PCI_ERR_COR_HL_OVERFLOW)
+
+
+#define PCI_ERR_CAP_FEP_MASK            0x0000001f
+#define PCI_ERR_CAP_MHRC                0x00000200
+#define PCI_ERR_CAP_MHRE                0x00000400
+#define PCI_ERR_CAP_TLP                 0x00000800
+
+#define PCI_ERR_TLP_PREFIX_LOG          0x38
+
+/* From 6.2.7 Error Listing and Rules. Table 6-2, 6-3 and 6-4 */
+static enum PCIE_AER_SEVERITY pcie_aer_uncor_default_severity(uint32_t status)
+{
+    switch (status) {
+    case PCI_ERR_UNC_INTN:
+    case PCI_ERR_UNC_DLP:
+    case PCI_ERR_UNC_SDN:
+    case PCI_ERR_UNC_RX_OVER:
+    case PCI_ERR_UNC_FCP:
+    case PCI_ERR_UNC_MALF_TLP:
+        return AER_ERR_FATAL;
+    case PCI_ERR_UNC_POISON_TLP:
+    case PCI_ERR_UNC_ECRC:
+    case PCI_ERR_UNC_UNSUP:
+    case PCI_ERR_UNC_COMP_TIME:
+    case PCI_ERR_UNC_COMP_ABORT:
+    case PCI_ERR_UNC_UNX_COMP:
+    case PCI_ERR_UNC_ACSV:
+    case PCI_ERR_UNC_MCBTLP:
+    case PCI_ERR_UNC_ATOP_EBLOCKED:
+    case PCI_ERR_UNC_TLP_PRF_BLOCKED:
+        return AER_ERR_NONFATAL;
+    default:
+        break;
+    }
+    abort();
+    return AER_ERR_FATAL;
+}
+
+static uint32_t pcie_aer_log_next(uint32_t i, uint32_t max)
+{
+    return (i + 1) % max;
+}
+
+static bool pcie_aer_log_empty_index(uint32_t producer, uint32_t consumer)
+{
+    return producer == consumer;
+}
+
+static bool pcie_aer_log_empty(struct pcie_aer_log *aer_log)
+{
+    return pcie_aer_log_empty_index(aer_log->producer, aer_log->consumer);
+}
+
+static bool pcie_aer_log_full(struct pcie_aer_log *aer_log)
+{
+    return pcie_aer_log_next(aer_log->producer, aer_log->log_max) ==
+        aer_log->consumer;
+}
+
+static uint32_t pcie_aer_log_add(struct pcie_aer_log *aer_log)
+{
+    uint32_t i = aer_log->producer;
+    aer_log->producer = pcie_aer_log_next(aer_log->producer, aer_log->log_max);
+    return i;
+}
+
+static uint32_t pcie_aer_log_del(struct pcie_aer_log *aer_log)
+{
+    uint32_t i = aer_log->consumer;
+    aer_log->consumer = pcie_aer_log_next(aer_log->consumer, aer_log->log_max);
+    return i;
+}
+
+static int pcie_aer_log_add_err(struct pcie_aer_log *aer_log,
+                                const struct pcie_aer_err *err)
+{
+    uint32_t i;
+    if (pcie_aer_log_full(aer_log)) {
+        return -1;
+    }
+    i = pcie_aer_log_add(aer_log);
+    memcpy(&aer_log->log[i], err, sizeof(*err));
+    return 0;
+}
+
+static const struct pcie_aer_err*
+pcie_aer_log_del_err(struct pcie_aer_log *aer_log)
+{
+    uint32_t i;
+    assert(!pcie_aer_log_empty(aer_log));
+    i = pcie_aer_log_del(aer_log);
+    return &aer_log->log[i];
+}
+
+static void pcie_aer_log_clear_all_err(struct pcie_aer_log *aer_log)
+{
+    aer_log->producer = 0;
+    aer_log->consumer = 0;
+}
+
+int pcie_aer_init(PCIDevice *dev, uint16_t offset)
+{
+    int pos;
+    PCIExpressDevice *exp;
+
+    pci_set_word(dev->wmask + PCI_COMMAND,
+                 pci_get_word(dev->wmask + PCI_COMMAND) | PCI_COMMAND_SERR);
+
+    pos = pcie_append_ext_capability(dev, PCI_EXT_CAP_ID_ERR, PCI_ERR_VER,
+                                     offset, PCI_ERR_SIZEOF);
+    if (pos < 0) {
+        return pos;
+    }
+    exp = dev->exp;
+    exp->aer_cap = pos;
+    if (dev->aer_log.log_max == PCIE_AER_LOG_MAX_UNSET) {
+        dev->aer_log.log_max = PCIE_AER_LOG_MAX_DEFAULT;
+    }
+    if (dev->aer_log.log_max > PCIE_AER_LOG_MAX_MAX) {
+        dev->aer_log.log_max = PCIE_AER_LOG_MAX_MAX;
+    }
+    dev->aer_log.log =
+        qemu_mallocz(sizeof(dev->aer_log.log[0]) * dev->aer_log.log_max);
+
+    pci_set_long(dev->wmask + pos + PCI_ERR_UNCOR_MASK,
+                 PCI_ERR_UNC_SUPPORTED);
+
+    pci_set_long(dev->config + pos + PCI_ERR_UNCOR_SEVER,
+                 PCI_ERR_UNC_SEVERITY_DEFAULT);
+    pci_set_long(dev->wmask + pos + PCI_ERR_UNCOR_SEVER,
+                 PCI_ERR_UNC_SUPPORTED);
+
+    pci_set_long(dev->config + pos + PCI_ERR_COR_MASK,
+                 PCI_ERR_COR_MASK_DEFAULT);
+    pci_set_long(dev->wmask + pos + PCI_ERR_COR_MASK,
+                 PCI_ERR_COR_SUPPORTED);
+
+    /* capabilities and control. multiple header logging is supported */
+    if (dev->aer_log.log_max > 0) {
+        pci_set_long(dev->config + pos + PCI_ERR_CAP,
+                     PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC |
+                     PCI_ERR_CAP_MHRC);
+        pci_set_long(dev->wmask + pos + PCI_ERR_CAP,
+                     PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE |
+                     PCI_ERR_CAP_MHRE);
+    } else {
+        pci_set_long(dev->config + pos + PCI_ERR_CAP,
+                     PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC);
+        pci_set_long(dev->wmask + pos + PCI_ERR_CAP,
+                     PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE);
+    }
+
+    switch (pcie_cap_get_type(dev)) {
+    case PCI_EXP_TYPE_ROOT_PORT:
+        /* this case will be set by pcie_aer_root_init() */
+        /* fallthrough */
+    case PCI_EXP_TYPE_DOWNSTREAM:
+    case PCI_EXP_TYPE_UPSTREAM:
+        pci_set_word(dev->wmask + PCI_BRIDGE_CONTROL,
+                     pci_get_word(dev->wmask + PCI_BRIDGE_CONTROL) |
+                     PCI_BRIDGE_CTL_SERR);
+        exp->aer_errmsg = pcie_aer_errmsg_vbridge;
+        break;
+    default:
+        exp->aer_errmsg = pcie_aer_errmsg_alldev;
+        break;
+    }
+    return pos;
+}
+
+void pcie_aer_exit(PCIDevice *dev)
+{
+    pci_del_capability(dev, PCI_EXT_CAP_ID_ERR, PCI_ERR_SIZEOF);
+    qemu_free(dev->aer_log.log);
+}
+
+/* Multiple Header recording isn't implemented. Is it wanted? */
+void pcie_aer_write_config(PCIDevice *dev,
+                           uint32_t addr, uint32_t val, int len)
+{
+    uint32_t pos = dev->exp->aer_cap;
+
+    /* PCI_STATUS_SIG_SYSTEM_ERROR */
+    if (ranges_overlap(addr, len, PCI_STATUS, 2)) {
+        pcie_w1c_word(dev, PCI_STATUS, PCI_STATUS_SIG_SYSTEM_ERROR, addr, val);
+    }
+
+    /* uncorrectable */
+    if (ranges_overlap(addr, len, pos + PCI_ERR_UNCOR_STATUS, 4)) {
+        uint32_t written =
+            pcie_written_val_long(addr, val, pos + PCI_ERR_UNCOR_STATUS) &
+            PCI_ERR_UNC_SUPPORTED;
+        uint32_t uncorsta =
+            pci_get_long(dev->config + pos + PCI_ERR_UNCOR_STATUS);
+        uint32_t errcap = pci_get_long(dev->config + pos + PCI_ERR_CAP);
+        uint32_t first_error = (1 << PCI_ERR_CAP_FEP(errcap));
+
+        if ((uncorsta & first_error) && (written & first_error)) {
+            pcie_aer_clear_error(dev);
+        }
+        if (!(errcap & PCI_ERR_CAP_MHRE)) {
+            /* RW1CS */
+            pcie_w1c_long(dev, pos + PCI_ERR_UNCOR_STATUS,
+                          PCI_ERR_UNC_SUPPORTED, addr, val);
+        }
+    }
+
+    /* correctable */
+    if (ranges_overlap(addr, len, pos + PCI_ERR_COR_STATUS, 4)) {
+        /* RW1CS */
+        pcie_w1c_long(dev, pos + PCI_ERR_COR_STATUS, PCI_ERR_COR_SUPPORTED,
+                      addr, val);
+    }
+
+    /* capability & control */
+    if (ranges_overlap(addr, len, pos + PCI_ERR_CAP, 4)) {
+        uint32_t err_cap = pci_get_long(dev->config + pos + PCI_ERR_CAP);
+        if (!(err_cap & PCI_ERR_CAP_MHRE)) {
+            pcie_aer_log_clear_all_err(&dev->aer_log);
+        }
+    }
+}
+
+#define PCI_SEC_STATUS_RCV_SYSTEM_ERROR         0x4000
+
+void pcie_aer_write_config_vbridge(PCIDevice *dev,
+                                   uint32_t addr, uint32_t val, int len)
+{
+    /* PCI_SEC_STATUS_RCV_SYSTEM_ERROR */
+    if (ranges_overlap(addr, len, PCI_STATUS, 2)) {
+        pcie_w1c_word(dev, PCI_SEC_STATUS, PCI_SEC_STATUS_RCV_SYSTEM_ERROR,
+                      addr, val);
+    }
+}
+
+static inline void pcie_aer_errmsg(PCIDevice *dev,
+                                   const struct pcie_aer_err_msg *msg)
+{
+    assert(dev->exp);
+    assert(dev->exp->aer_errmsg);
+    dev->exp->aer_errmsg(dev, msg);
+}
+
+static AER_ERR_MSG_RESULT
+pcie_aer_errmsg_alldev(PCIDevice *dev, const struct pcie_aer_err_msg *msg)
+{
+    uint16_t cmd = pci_get_word(dev->config + PCI_COMMAND);
+    bool transmit1 =
+        pcie_aer_err_msg_is_uncor(msg) && (cmd & PCI_COMMAND_SERR);
+    uint32_t pos = pci_pcie_cap(dev);
+    uint32_t devctl = pci_get_word(dev->config + pos + PCI_EXP_DEVCTL);
+    bool transmit2 = msg->severity & devctl;
+    PCIDevice *parent_port;
+
+    if (transmit1) {
+        if (pcie_aer_err_msg_is_uncor(msg)) {
+            /* Signaled System Error */
+            uint8_t *status = dev->config + PCI_STATUS;
+            pci_set_word(status,
+                         pci_get_word(status) | PCI_STATUS_SIG_SYSTEM_ERROR);
+        }
+    }
+
+    if (!(transmit1 || transmit2)) {
+        return AER_ERR_MSG_MASKED;
+    }
+
+    /* send up error message */
+    if (pci_is_express(dev) &&
+        pcie_cap_get_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
+        /* Root port notify system itself,
+           or send the error message to root complex event collector. */
+        /*
+         * if root port is associated to event collector, set
+         * parent_port = root complex event collector
+         * For now root complex event collector isn't supported.
+         */
+        parent_port = NULL;
+    } else {
+        parent_port = pci_bridge_get_device(dev->bus);
+    }
+    if (parent_port) {
+        if (!pci_is_express(parent_port)) {
+            /* What to do? */
+            return AER_ERR_MSG_MASKED;
+        }
+        pcie_aer_errmsg(parent_port, msg);
+    }
+    return AER_ERR_MSG_SENT;
+}
+
+static AER_ERR_MSG_RESULT
+pcie_aer_errmsg_vbridge(PCIDevice *dev, const struct pcie_aer_err_msg *msg)
+{
+    uint16_t bridge_control = pci_get_word(dev->config + PCI_BRIDGE_CONTROL);
+
+    if (pcie_aer_err_msg_is_uncor(msg)) {
+        /* Received System Error */
+        uint8_t *sec_status = dev->config + PCI_SEC_STATUS;
+        pci_set_word(sec_status,
+                     pci_get_word(sec_status) |
+                     PCI_SEC_STATUS_RCV_SYSTEM_ERROR);
+    }
+
+    if (!(bridge_control & PCI_BRIDGE_CTL_SERR)) {
+        return AER_ERR_MSG_MASKED;
+    }
+    return pcie_aer_errmsg_alldev(dev, msg);
+}
+
+static AER_ERR_MSG_RESULT
+pcie_aer_errmsg_root_port(PCIDevice *dev, const struct pcie_aer_err_msg *msg)
+{
+    AER_ERR_MSG_RESULT ret;
+    uint16_t cmd;
+    uint8_t *aer_cap;
+    uint32_t root_cmd;
+    uint32_t root_sta;
+    bool trigger;
+
+    ret = pcie_aer_errmsg_vbridge(dev, msg);
+    if (ret != AER_ERR_MSG_SENT) {
+        return ret;
+    }
+
+    ret = AER_ERR_MSG_MASKED;
+    cmd = pci_get_word(dev->config + PCI_COMMAND);
+    aer_cap = dev->config + pcie_aer_cap(dev);
+    root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND);
+    root_sta = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS);
+    trigger = false;
+
+    if (cmd & PCI_COMMAND_SERR) {
+        /* System Error. Platform Specific */
+        /* ret = AER_ERR_MSG_SENT; */
+    }
+
+    /* Errro Message Received: Root Error Status register */
+    switch (msg->severity) {
+    case AER_ERR_COR:
+        if (root_sta & PCI_ERR_ROOT_COR_RCV) {
+            root_sta |= PCI_ERR_ROOT_MULTI_COR_RCV;
+        } else {
+            if (root_cmd & PCI_ERR_ROOT_CMD_COR_EN) {
+                trigger = true;
+            }
+            pci_set_word(aer_cap + PCI_ERR_ROOT_COR_SRC, msg->source_id);
+        }
+        root_sta |= PCI_ERR_ROOT_COR_RCV;
+        break;
+    case AER_ERR_NONFATAL:
+        if (!(root_sta & PCI_ERR_ROOT_NONFATAL_RCV) &&
+            root_cmd & PCI_ERR_ROOT_CMD_NONFATAL_EN) {
+            trigger = true;
+        }
+        root_sta |= PCI_ERR_ROOT_NONFATAL_RCV;
+        break;
+    case AER_ERR_FATAL:
+        if (!(root_sta & PCI_ERR_ROOT_FATAL_RCV) &&
+            root_cmd & PCI_ERR_ROOT_CMD_FATAL_EN) {
+            trigger = true;
+        }
+        if (!(root_sta & PCI_ERR_ROOT_UNCOR_RCV)) {
+            root_sta |= PCI_ERR_ROOT_FIRST_FATAL;
+        }
+        root_sta |= PCI_ERR_ROOT_FATAL_RCV;
+        break;
+    }
+    if (pcie_aer_err_msg_is_uncor(msg)) {
+        if (root_sta & PCI_ERR_ROOT_UNCOR_RCV) {
+            root_sta |= PCI_ERR_ROOT_MULTI_UNCOR_RCV;
+        } else {
+            pci_set_word(aer_cap + PCI_ERR_ROOT_SRC, msg->source_id);
+        }
+        root_sta |= PCI_ERR_ROOT_UNCOR_RCV;
+    }
+    pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_sta);
+
+    if (root_cmd & msg->severity) {
+        /* Error Interrupt(INTx or MSI) */
+        pcie_aer_root_notify(dev, trigger, 1);
+        ret = AER_ERR_MSG_SENT;
+    }
+    return ret;
+}
+
+static void pcie_aer_update_log(PCIDevice *dev, const struct pcie_aer_err *err)
+{
+    uint8_t *aer_cap = dev->config + pcie_aer_cap(dev);
+    uint8_t first_bit = ffsl(err->status) - 1;
+    uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP);
+    int i;
+    uint32_t dw;
+
+    errcap &= ~(PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP);
+    errcap |= PCI_ERR_CAP_FEP(first_bit);
+
+    if (err->flags & PCIE_AER_ERR_HEADER_VALID) {
+        for (i = 0; i < ARRAY_SIZE(err->header); ++i) {
+            /* 7.10.8 Header Log Register */
+            cpu_to_be32wu(&dw, err->header[i]);
+            memcpy(aer_cap + PCI_ERR_HEADER_LOG + sizeof(err->header[0]) * i,
+                   &dw, sizeof(dw));
+        }
+    } else {
+        assert(!(err->flags & PCIE_AER_ERR_TLP_PRESENT));
+        memset(aer_cap + PCI_ERR_HEADER_LOG, 0, sizeof(err->header));
+    }
+
+    if ((err->flags & PCIE_AER_ERR_TLP_PRESENT) &&
+        (pci_get_long(dev->config + pci_pcie_cap(dev) + PCI_EXP_DEVCTL2) &
+         PCI_EXP_DEVCAP2_EETLPP)) {
+        for (i = 0; i < ARRAY_SIZE(err->prefix); ++i) {
+            /* 7.10.12 tlp prefix log register */
+            cpu_to_be32wu(&dw, err->prefix[i]);
+            memcpy(aer_cap + PCI_ERR_TLP_PREFIX_LOG +
+                   sizeof(err->prefix[0]) * i, &dw, sizeof(dw));
+        }
+        errcap |= PCI_ERR_CAP_TLP;
+    } else {
+        memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0, sizeof(err->prefix));
+    }
+    pci_set_long(aer_cap + PCI_ERR_CAP, errcap);
+}
+
+static void pcie_aer_clear_log(PCIDevice *dev)
+{
+    struct pcie_aer_err *err;
+    uint8_t *aer_cap = dev->config + pcie_aer_cap(dev);
+    uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP);
+
+    errcap &= ~(PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP);
+    pci_set_long(aer_cap + PCI_ERR_CAP, errcap);
+
+    memset(aer_cap + PCI_ERR_HEADER_LOG, 0, sizeof(err->header));
+    memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0, sizeof(err->prefix));
+}
+
+static int pcie_aer_record_error(PCIDevice *dev,
+                                 const struct pcie_aer_err *err)
+{
+    uint8_t *aer_cap = dev->config + pcie_aer_cap(dev);
+    uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP);
+    int fep = PCI_ERR_CAP_FEP(errcap);
+
+    if (errcap & PCI_ERR_CAP_MHRE &&
+        (pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) & (1ULL << fep))) {
+        /*  Not first error. queue error */
+        if (pcie_aer_log_add_err(&dev->aer_log, err) < 0) {
+            /* overflow */
+            return -1;
+        }
+        return 0;
+    }
+
+    pcie_aer_update_log(dev, err);
+    return 0;
+}
+
+static void pcie_aer_clear_error(PCIDevice *dev)
+{
+    uint8_t *aer_cap = dev->config + pcie_aer_cap(dev);
+    uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP);
+    uint32_t old_err = (1UL << PCI_ERR_CAP_FEP(errcap));
+    struct pcie_aer_log *aer_log = &dev->aer_log;
+    const struct pcie_aer_err *err;
+    uint32_t consumer;
+
+    if (!(errcap & PCI_ERR_CAP_MHRE) || pcie_aer_log_empty(aer_log)) {
+        pcie_aer_clear_log(dev);
+        pci_set_long(aer_cap + PCI_ERR_UNCOR_STATUS,
+                     pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) & ~old_err);
+        return;
+    }
+
+    /* if no same error is queued, clear bit in uncorrectable error status */
+    for (consumer = dev->aer_log.consumer;
+         !pcie_aer_log_empty_index(dev->aer_log.producer, consumer);
+         consumer = pcie_aer_log_next(consumer, dev->aer_log.log_max)) {
+        if (dev->aer_log.log[consumer].status & old_err) {
+            old_err = 0;
+            break;
+        }
+    }
+    if (old_err) {
+        pci_set_long(aer_cap + PCI_ERR_UNCOR_STATUS,
+                     pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) & ~old_err);
+    }
+
+    err = pcie_aer_log_del_err(aer_log);
+    pcie_aer_update_log(dev, err);
+}
+
+/*
+ * non-Function specific error must be recorded in all functions.
+ * It is the responsibility of the caller of this function.
+ * It is also caller's responsiblity to determine which function should
+ * report the rerror.
+ *
+ * 6.2.4 Error Logging
+ * 6.2.5 Sqeucne of Device Error Signaling and Logging Operations
+ * table 6-2: Flowchard Showing Sequence of Device Error Signaling and Logging
+ *            Operations
+ *
+ * Although this implementation can be shortened/optimized, this is kept
+ * parallel to table 6-2.
+ */
+void pcie_aer_inject_error(PCIDevice *dev, const struct pcie_aer_err *err)
+{
+    uint8_t *exp_cap;
+    uint8_t *aer_cap = NULL;
+    uint32_t devctl = 0;
+    uint32_t devsta = 0;
+    uint32_t status = err->status;
+    uint32_t mask;
+    bool is_unsupported_request =
+        (!(err->flags & PCIE_AER_ERR_IS_CORRECTABLE) &&
+         err->status == PCI_ERR_UNC_UNSUP);
+    bool is_advisory_nonfatal = false;  /* for advisory non-fatal error */
+    uint32_t uncor_status = 0;          /* for advisory non-fatal error */
+    struct pcie_aer_err_msg msg;
+    int is_header_log_overflowed = 0;
+
+    if (!pci_is_express(dev)) {
+        /* What to do? */
+        return;
+    }
+
+    if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) {
+        status &= PCI_ERR_COR_SUPPORTED;
+    } else {
+        status &= PCI_ERR_UNC_SUPPORTED;
+    }
+    if (!status || status & (status - 1)) {
+        /* invalid status bit. one and only one bit must be set */
+        return;
+    }
+
+    exp_cap = dev->config + pci_pcie_cap(dev);
+    if (dev->exp->aer_cap) {
+        aer_cap = dev->config + pcie_aer_cap(dev);
+        devctl = pci_get_long(exp_cap + PCI_EXP_DEVCTL);
+        devsta = pci_get_long(exp_cap + PCI_EXP_DEVSTA);
+    }
+    if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) {
+    correctable_error:
+        devsta |= PCI_EXP_DEVSTA_CED;
+        if (is_unsupported_request) {
+            devsta |= PCI_EXP_DEVSTA_URD;
+        }
+        pci_set_word(exp_cap + PCI_EXP_DEVSTA, devsta);
+
+        if (aer_cap) {
+            pci_set_long(aer_cap + PCI_ERR_COR_STATUS,
+                         pci_get_long(aer_cap + PCI_ERR_COR_STATUS) | status);
+            mask = pci_get_long(aer_cap + PCI_ERR_COR_MASK);
+            if (mask & status) {
+                return;
+            }
+            if (is_advisory_nonfatal) {
+                uint32_t uncor_mask =
+                    pci_get_long(aer_cap + PCI_ERR_UNCOR_MASK);
+                if (!(uncor_mask & uncor_status)) {
+                    is_header_log_overflowed = pcie_aer_record_error(dev, err);
+                }
+                pci_set_long(aer_cap + PCI_ERR_UNCOR_STATUS,
+                             pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) |
+                             uncor_status);
+            }
+        }
+
+        if (is_unsupported_request && !(devctl & PCI_EXP_DEVCTL_URRE)) {
+            return;
+        }
+        if (!(devctl & PCI_EXP_DEVCTL_CERE)) {
+            return;
+        }
+        msg.severity = AER_ERR_COR;
+    } else {
+        bool is_fatal =
+            (pcie_aer_uncor_default_severity(status) == AER_ERR_FATAL);
+        uint16_t cmd;
+
+        if (aer_cap) {
+            is_fatal = status & pci_get_long(aer_cap + PCI_ERR_UNCOR_SEVER);
+        }
+        if (!is_fatal && (err->flags & PCIE_AER_ERR_MAYBE_ADVISORY)) {
+            is_advisory_nonfatal = true;
+            uncor_status = status;
+            status = PCI_ERR_COR_ADV_NONFATAL;
+            goto correctable_error;
+        }
+        if (is_fatal) {
+            devsta |= PCI_EXP_DEVSTA_FED;
+        } else {
+            devsta |= PCI_EXP_DEVSTA_NFED;
+        }
+        if (is_unsupported_request) {
+            devsta |= PCI_EXP_DEVSTA_URD;
+        }
+        pci_set_long(exp_cap + PCI_EXP_DEVSTA, devsta);
+
+        if (aer_cap) {
+            mask = pci_get_long(aer_cap + PCI_ERR_UNCOR_MASK);
+            if (mask & status) {
+                pci_set_long(aer_cap + PCI_ERR_UNCOR_STATUS,
+                             pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) |
+                             status);
+                return;
+            }
+
+            is_header_log_overflowed = pcie_aer_record_error(dev, err);
+            pci_set_long(aer_cap + PCI_ERR_UNCOR_STATUS,
+                         pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) |
+                         status);
+        }
+
+        cmd = pci_get_word(dev->config + PCI_COMMAND);
+        if (is_unsupported_request &&
+            !(devctl & PCI_EXP_DEVCTL_URRE) && !(cmd & PCI_COMMAND_SERR)) {
+            return;
+        }
+        if (is_fatal) {
+            if (!((cmd & PCI_COMMAND_SERR) ||
+                  (devctl & PCI_EXP_DEVCTL_FERE))) {
+                return;
+            }
+            msg.severity = AER_ERR_FATAL;
+        } else {
+            if (!((cmd & PCI_COMMAND_SERR) ||
+                  (devctl & PCI_EXP_DEVCTL_NFERE))) {
+                return;
+            }
+            msg.severity = AER_ERR_NONFATAL;
+        }
+    }
+
+    /* send up error message */
+    msg.source_id = err->source_id;
+    pcie_aer_errmsg(dev, &msg);
+
+    if (is_header_log_overflowed) {
+        struct pcie_aer_err header_log_overflow = {
+            .status = PCI_ERR_COR_HL_OVERFLOW,
+            .flags = PCIE_AER_ERR_IS_CORRECTABLE,
+            .header = {0, 0, 0, 0},
+            .prefix = {0, 0, 0, 0},
+        };
+        pcie_aer_inject_error(dev, &header_log_overflow);
+    }
+}
+
+/* aer root error command/status */
+#define PCI_ERR_ROOT_CMD_EN_MASK        (PCI_ERR_ROOT_CMD_COR_EN |      \
+                                         PCI_ERR_ROOT_CMD_NONFATAL_EN | \
+                                         PCI_ERR_ROOT_CMD_FATAL_EN)
+
+#define PCI_ERR_ROOT_IRQ_SHIFT          26
+#define PCI_ERR_ROOT_IRQ                0xf8000000
+#define PCI_ERR_ROOT_STATUS_REPORT_MASK (PCI_ERR_ROOT_COR_RCV |         \
+                                         PCI_ERR_ROOT_MULTI_COR_RCV |   \
+                                         PCI_ERR_ROOT_UNCOR_RCV |       \
+                                         PCI_ERR_ROOT_MULTI_UNCOR_RCV | \
+                                         PCI_ERR_ROOT_FIRST_FATAL |     \
+                                         PCI_ERR_ROOT_NONFATAL_RCV |    \
+                                         PCI_ERR_ROOT_FATAL_RCV)
+
+void pcie_aer_root_set_vector(PCIDevice *dev, uint8_t vector)
+{
+    uint8_t *aer_cap = dev->config + pcie_aer_cap(dev);
+    uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS);
+    root_status &= ~PCI_ERR_ROOT_IRQ;
+    root_status |=
+        (((uint32_t)vector) << PCI_ERR_ROOT_IRQ_SHIFT) & PCI_ERR_ROOT_IRQ;
+    pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_status);
+}
+
+static uint8_t pcie_aer_root_get_vector(PCIDevice *dev)
+{
+    uint8_t *aer_cap = dev->config + pcie_aer_cap(dev);
+    uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS);
+    return (root_status & PCI_ERR_ROOT_IRQ) >> PCI_ERR_ROOT_IRQ_SHIFT;
+}
+
+static void pcie_aer_root_notify(PCIDevice *dev, bool trigger, int level)
+{
+    pcie_notify(dev, pcie_aer_root_get_vector(dev), trigger, level);
+}
+
+void pcie_aer_root_init(PCIDevice *dev)
+{
+    uint16_t pos = pcie_aer_cap(dev);
+
+    pci_set_long(dev->wmask + pos + PCI_ERR_ROOT_COMMAND,
+                 PCI_ERR_ROOT_CMD_EN_MASK);
+    dev->exp->aer_errmsg = pcie_aer_errmsg_root_port;
+}
+
+void pcie_aer_root_reset(PCIDevice *dev)
+{
+    uint8_t* aer_cap = dev->config + pcie_aer_cap(dev);
+
+    pci_set_long(aer_cap + PCI_ERR_ROOT_COMMAND, 0);
+
+    /*
+     * Advanced Error Interrupt Message Number in Root Error Status Register
+     * must be updated by chip dependent code.
+     */
+}
+
+static bool pcie_aer_root_does_trigger(uint32_t cmd, uint32_t sta)
+{
+    return
+        ((cmd & PCI_ERR_ROOT_CMD_COR_EN) && (sta & PCI_ERR_ROOT_COR_RCV)) ||
+        ((cmd & PCI_ERR_ROOT_CMD_NONFATAL_EN) &&
+         (sta & PCI_ERR_ROOT_NONFATAL_RCV)) ||
+        ((cmd & PCI_ERR_ROOT_CMD_FATAL_EN) && (sta & PCI_ERR_ROOT_FATAL_RCV));
+}
+
+void pcie_aer_root_write_config(PCIDevice *dev,
+                                uint32_t addr, uint32_t val, int len,
+                                uint32_t root_cmd_prev)
+{
+    uint16_t pos = pcie_aer_cap(dev);
+    uint8_t *aer_cap = dev->config + pos;
+    uint32_t root_status;
+
+    if (ranges_overlap(addr, len, pos + PCI_ERR_ROOT_STATUS, 4)) {
+        /* RW1CS */
+        pcie_w1c_long(dev, pos + PCI_ERR_ROOT_STATUS,
+                      PCI_ERR_ROOT_STATUS_REPORT_MASK, addr, val);
+    }
+
+    /* root command */
+    if (ranges_overlap(addr, len, pos + PCI_ERR_ROOT_COMMAND, 4)) {
+        uint32_t root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND);
+        if (root_cmd & PCI_ERR_ROOT_CMD_EN_MASK) {
+            bool trigger = false;
+            int level = 0;
+            uint32_t root_cmd_set = (root_cmd_prev ^ root_cmd) & root_cmd;
+
+            /* 0 -> 1 */
+            root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS);
+            if (pcie_aer_root_does_trigger(root_cmd_set, root_status)) {
+                trigger = true;
+            }
+            if (pcie_aer_root_does_trigger(root_cmd, root_status)) {
+                level = 1;
+            }
+            pcie_aer_root_notify(dev, trigger, level);
+        }
+    }
+}
+
+static const VMStateDescription vmstate_pcie_aer_err = {
+    .name = "PCIE_AER_ERROR",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields     = (VMStateField[]) {
+        VMSTATE_UINT32(status, struct pcie_aer_err),
+        VMSTATE_UINT16(source_id, struct pcie_aer_err),
+        VMSTATE_UINT16(flags, struct pcie_aer_err),
+        VMSTATE_UINT32_ARRAY(header, struct pcie_aer_err, 4),
+        VMSTATE_UINT32_ARRAY(prefix, struct pcie_aer_err, 4),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+#define VMSTATE_PCIE_AER_ERRS(_field, _state, _field_num, _vmsd, _type) { \
+    .name       = (stringify(_field)),                                    \
+    .version_id = 0,                                                      \
+    .num_offset = vmstate_offset_value(_state, _field_num, uint16_t),     \
+    .size       = sizeof(_type),                                          \
+    .vmsd       = &(_vmsd),                                               \
+    .flags      = VMS_POINTER | VMS_VARRAY_UINT16 | VMS_STRUCT,           \
+    .offset     = vmstate_offset_pointer(_state, _field, _type),          \
+}
+
+const VMStateDescription vmstate_pcie_aer_log = {
+    .name = "PCIE_AER_ERROR_LOG",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields     = (VMStateField[]) {
+        VMSTATE_UINT32(producer, struct pcie_aer_log),
+        VMSTATE_UINT32(consumer, struct pcie_aer_log),
+        VMSTATE_UINT16(log_max, struct pcie_aer_log),
+        VMSTATE_PCIE_AER_ERRS(log, struct pcie_aer_log, log_max,
+                              vmstate_pcie_aer_err, struct pcie_aer_err),
+        VMSTATE_END_OF_LIST()
+    }
+};
diff --git a/hw/pcie.h b/hw/pcie.h
new file mode 100644
index 0000000..07f42c6
--- /dev/null
+++ b/hw/pcie.h
@@ -0,0 +1,186 @@
+/*
+ * pcie.h
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QEMU_PCIE_H
+#define QEMU_PCIE_H
+
+#include "hw.h"
+
+enum PCIExpressIndicator {
+    /* for attention and power indicator */
+    PCI_EXP_HP_IND_RESERVED     = 0b00,
+    PCI_EXP_HP_IND_ON           = 0b01,
+    PCI_EXP_HP_IND_BLINK        = 0b10,
+    PCI_EXP_HP_IND_OFF          = 0b11,
+};
+
+enum PCIExpressHotPlugEvent {
+    /* the bits match the bits in Slot Control/Status registers.
+     * PCI_EXP_HP_EV_xxx = PCI_EXP_SLTCTL_xxxE = PCI_EXP_SLTSTA_xxx
+     */
+    PCI_EXP_HP_EV_ABP   = 0b00001,      /* attention button preseed */
+    PCI_EXP_HP_EV_PDC   = 0b01000,      /* presence detect changed */
+    PCI_EXP_HP_EV_CCI   = 0b10000,      /* command completed */
+
+    PCI_EXP_HP_EV_SUPPORTED     = 0b11001,       /* supported event mask  */
+    /* events not listed aren't supported */
+};
+
+typedef void (*pcie_flr_fn)(PCIDevice *dev);
+
+struct pcie_aer_err_msg;
+enum AER_ERR_MSG_RESULT {
+    AER_ERR_MSG_MASKED,
+    AER_ERR_MSG_SENT,
+};
+typedef enum AER_ERR_MSG_RESULT AER_ERR_MSG_RESULT;
+typedef AER_ERR_MSG_RESULT (*pcie_aer_errmsg_fn)(PCIDevice *dev, const struct pcie_aer_err_msg *msg);
+
+struct PCIExpressDevice {
+    /* Offset of express capability in config space */
+    uint8_t exp_cap;
+
+    /* FLR */
+    pcie_flr_fn flr;
+
+    /* AER */
+    uint16_t aer_cap;
+    pcie_aer_errmsg_fn aer_errmsg;
+};
+
+struct pcie_aer_log {
+    uint32_t producer;
+    uint32_t consumer;
+
+#define PCIE_AER_LOG_MAX_DEFAULT        8
+#define PCIE_AER_LOG_MAX_MAX            128 /* what is appropriate? */
+#define PCIE_AER_LOG_MAX_UNSET          (~(uint16_t)0)
+    uint16_t log_max;
+
+    struct pcie_aer_err *log;
+};
+
+extern const VMStateDescription vmstate_pcie_aer_log;
+
+/* PCI express capability helper functions */
+int pci_pcie_cap_init(PCIDevice *dev,
+                      uint8_t offset, uint8_t type, uint8_t port);
+int pci_pcie_cap_exit(PCIDevice *dev);
+uint8_t pcie_cap_get_type(const PCIDevice *dev);
+void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector);
+uint8_t pcie_cap_flags_get_vector(PCIDevice *dev);
+
+void pcie_cap_deverr_init(PCIDevice *dev);
+void pcie_cap_deverr_reset(PCIDevice *dev);
+void pcie_cap_deverr_write_config(PCIDevice *dev,
+                                  uint32_t addr, uint32_t val, int len);
+
+void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot);
+void pcie_cap_slot_reset(PCIDevice *dev);
+void pcie_cap_slot_write_config(PCIDevice *dev,
+                                uint32_t addr, uint32_t val, int len,
+                                uint16_t sltctl_prev);
+void pcie_cap_slot_push_attention_button(PCIDevice *dev);
+
+void pcie_cap_root_init(PCIDevice *dev);
+void pcie_cap_root_reset(PCIDevice *dev);
+
+void pcie_cap_flr_init(PCIDevice *dev, pcie_flr_fn flr);
+void pcie_cap_flr_write_config(PCIDevice *dev,
+                           uint32_t addr, uint32_t val, int len);
+
+void pcie_cap_ari_init(PCIDevice *dev);
+void pcie_cap_ari_reset(PCIDevice *dev);
+bool pcie_cap_is_ari_enabled(const PCIDevice *dev);
+
+/* PCI express extended capability helper functions */
+uint16_t pcie_find_ext_capability(PCIDevice *dev, uint16_t cap_id);
+int pcie_add_ext_capability(PCIDevice *dev,
+                            uint16_t cap_id, uint8_t cap_ver, uint16_t size);
+int pcie_append_ext_capability(PCIDevice *dev,
+                               uint16_t cap_id, uint8_t cap_ver,
+                               uint16_t offset, uint16_t size);
+void pcie_del_ext_capability(PCIDevice *dev, uint16_t cap_id, uint16_t size);
+void pcie_reserve_ext_capability(PCIDevice *dev,
+                                 uint16_t offset, uint16_t size);
+
+int pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn);
+
+/* PCI express extended capabilities */
+
+/* AER */
+/* aer error severity */
+enum PCIE_AER_SEVERITY {
+    /* those value are same as
+     * Root error command register in aer extended cap and
+     * root control register in pci express cap.
+     */
+    AER_ERR_COR         = 0x1,
+    AER_ERR_NONFATAL    = 0x2,
+    AER_ERR_FATAL       = 0x4,
+};
+
+/* aer error message: error signaling message has only error sevirity and
+   source id. See 2.2.8.3 error signaling messages */
+struct pcie_aer_err_msg {
+    enum PCIE_AER_SEVERITY severity;
+    uint16_t source_id; /* bdf */
+};
+
+static inline bool
+pcie_aer_err_msg_is_uncor(const struct pcie_aer_err_msg *msg)
+{
+    return msg->severity == AER_ERR_NONFATAL || msg->severity == AER_ERR_FATAL;
+}
+
+/* error */
+struct pcie_aer_err {
+    uint32_t status;    /* error status bits */
+    uint16_t source_id; /* bdf */
+
+#define PCIE_AER_ERR_IS_CORRECTABLE     0x1     /* correctable/uncorrectable */
+#define PCIE_AER_ERR_MAYBE_ADVISORY     0x2     /* maybe advisory non-fatal */
+#define PCIE_AER_ERR_HEADER_VALID       0x4     /* TLP header is logged */
+#define PCIE_AER_ERR_TLP_PRESENT        0x8     /* TLP Prefix is logged */
+    uint16_t flags;
+
+    uint32_t header[4]; /* TLP header */
+    uint32_t prefix[4]; /* TLP header prefix */
+};
+
+int pcie_aer_init(PCIDevice *dev, uint16_t offset);
+void pcie_aer_exit(PCIDevice *dev);
+void pcie_aer_write_config(PCIDevice *dev,
+                           uint32_t addr, uint32_t val, int len);
+void pcie_aer_write_config_vbridge(PCIDevice *dev,
+                                   uint32_t addr, uint32_t val, int len);
+
+/* aer root port */
+void pcie_aer_root_set_vector(PCIDevice *dev, uint8_t vector);
+void pcie_aer_root_init(PCIDevice *dev);
+void pcie_aer_root_reset(PCIDevice *dev);
+void pcie_aer_root_write_config(PCIDevice *dev,
+                                uint32_t addr, uint32_t val, int len,
+                                uint32_t root_cmd_prev);
+
+/* error injection */
+void pcie_aer_inject_error(PCIDevice *dev, const struct pcie_aer_err *err);
+
+#endif /* QEMU_PCIE_H */
diff --git a/qemu-common.h b/qemu-common.h
index d735235..6d9ee26 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -219,6 +219,7 @@ typedef struct PCIHostState PCIHostState;
 typedef struct PCIExpressHost PCIExpressHost;
 typedef struct PCIBus PCIBus;
 typedef struct PCIDevice PCIDevice;
+typedef struct PCIExpressDevice PCIExpressDevice;
 typedef struct PCIBridge PCIBridge;
 typedef struct SerialState SerialState;
 typedef struct IRQState *qemu_irq;
-- 
1.7.1.1

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

* [Qemu-devel] [PATCH 09/14] pcie port: define struct PCIEPort/PCIESlot and helper functions
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
                   ` (7 preceding siblings ...)
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 08/14] pcie: helper functions for pcie extended capability Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 10/14] pcie root port: implement pcie root port Isaku Yamahata
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

define struct PCIEPort which represents common part
of pci express port.(root, upstream and downstream.)
add a helper function for pcie port which can be used commonly by
root/upstream/downstream port.
define struct PCIESlot which represents common part of
pcie slot.(root and downstream.) and helper functions for it.
helper functions for chassis, slot -> PCIESlot conversion.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 Makefile.objs  |    2 +-
 hw/pcie_port.c |  106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pcie_port.h |   51 +++++++++++++++++++++++++++
 qemu-common.h  |    2 +
 4 files changed, 160 insertions(+), 1 deletions(-)
 create mode 100644 hw/pcie_port.c
 create mode 100644 hw/pcie_port.h

diff --git a/Makefile.objs b/Makefile.objs
index eeb5134..c73d12b 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -186,7 +186,7 @@ hw-obj-$(CONFIG_PIIX4) += piix4.o
 # PCI watchdog devices
 hw-obj-y += wdt_i6300esb.o
 
-hw-obj-y += pcie.o
+hw-obj-y += pcie.o pcie_port.o
 hw-obj-y += msix.o msi.o
 
 # PCI network cards
diff --git a/hw/pcie_port.c b/hw/pcie_port.c
new file mode 100644
index 0000000..d2b1f38
--- /dev/null
+++ b/hw/pcie_port.c
@@ -0,0 +1,106 @@
+/*
+ * pcie_port.c
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pcie_port.h"
+
+void pcie_port_init_reg(PCIDevice *d)
+{
+    /* Unlike pci bridge,
+       66MHz and fast back to back don't apply to pci express port. */
+    pci_set_word(d->config + PCI_STATUS, 0);
+    pci_set_word(d->config + PCI_SEC_STATUS, 0);
+}
+
+/**************************************************************************
+ * (chassis number, pcie physical slot number) -> pcie slot conversion
+ */
+struct PCIEChassis {
+    uint8_t     number;
+
+    QLIST_HEAD(, PCIESlot) slots;
+    QLIST_ENTRY(PCIEChassis) next;
+};
+
+QLIST_HEAD(, PCIEChassis) chassis = QLIST_HEAD_INITIALIZER(chassis);
+
+static struct PCIEChassis *pcie_chassis_find(uint8_t chassis_number)
+{
+    struct PCIEChassis *c;
+    QLIST_FOREACH(c, &chassis, next) {
+        if (c->number == chassis_number) {
+            break;
+        }
+    }
+    return c;
+}
+
+void pcie_chassis_create(uint8_t chassis_number)
+{
+    struct PCIEChassis *c;
+    c = pcie_chassis_find(chassis_number);
+    if (c) {
+        return;
+    }
+    c = qemu_mallocz(sizeof(*c));
+    c->number = chassis_number;
+    QLIST_INIT(&c->slots);
+    QLIST_INSERT_HEAD(&chassis, c, next);
+}
+
+static PCIESlot *pcie_chassis_find_slot_with_chassis(struct PCIEChassis *c,
+                                                     uint8_t slot)
+{
+    PCIESlot *s;
+    QLIST_FOREACH(s, &c->slots, next) {
+        if (s->slot == slot) {
+            break;
+        }
+    }
+    return s;
+}
+
+PCIESlot *pcie_chassis_find_slot(uint8_t chassis_number, uint16_t slot)
+{
+    struct PCIEChassis *c;
+    c = pcie_chassis_find(chassis_number);
+    if (!c) {
+        return NULL;
+    }
+    return pcie_chassis_find_slot_with_chassis(c, slot);
+}
+
+int pcie_chassis_add_slot(struct PCIESlot *slot)
+{
+    struct PCIEChassis *c;
+    c = pcie_chassis_find(slot->chassis);
+    if (!c) {
+        return -ENODEV;
+    }
+    if (pcie_chassis_find_slot_with_chassis(c, slot->slot)) {
+        return -EBUSY;
+    }
+    QLIST_INSERT_HEAD(&c->slots, slot, next);
+    return 0;
+}
+
+void pcie_chassis_del_slot(PCIESlot *s)
+{
+    QLIST_REMOVE(s, next);
+}
diff --git a/hw/pcie_port.h b/hw/pcie_port.h
new file mode 100644
index 0000000..3709583
--- /dev/null
+++ b/hw/pcie_port.h
@@ -0,0 +1,51 @@
+/*
+ * pcie_port.h
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QEMU_PCIE_PORT_H
+#define QEMU_PCIE_PORT_H
+
+#include "pci_bridge.h"
+#include "pci_internals.h"
+
+struct PCIEPort {
+    PCIBridge   br;
+
+    /* pci express switch port */
+    uint8_t     port;
+};
+
+void pcie_port_init_reg(PCIDevice *d);
+
+struct PCIESlot {
+    PCIEPort    port;
+
+    /* pci express switch port with slot */
+    uint8_t     chassis;
+    uint16_t    slot;
+    QLIST_ENTRY(PCIESlot) next;
+};
+
+void pcie_chassis_create(uint8_t chassis_number);
+void pcie_main_chassis_create(void);
+PCIESlot *pcie_chassis_find_slot(uint8_t chassis, uint16_t slot);
+int pcie_chassis_add_slot(struct PCIESlot *slot);
+void pcie_chassis_del_slot(PCIESlot *s);
+
+#endif /* QEMU_PCIE_PORT_H */
diff --git a/qemu-common.h b/qemu-common.h
index 6d9ee26..b97b16e 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -221,6 +221,8 @@ typedef struct PCIBus PCIBus;
 typedef struct PCIDevice PCIDevice;
 typedef struct PCIExpressDevice PCIExpressDevice;
 typedef struct PCIBridge PCIBridge;
+typedef struct PCIEPort PCIEPort;
+typedef struct PCIESlot PCIESlot;
 typedef struct SerialState SerialState;
 typedef struct IRQState *qemu_irq;
 typedef struct PCMCIACardState PCMCIACardState;
-- 
1.7.1.1

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

* [Qemu-devel] [PATCH 10/14] pcie root port: implement pcie root port.
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
                   ` (8 preceding siblings ...)
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 09/14] pcie port: define struct PCIEPort/PCIESlot and helper functions Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 11/14] pcie upstream port: pci express switch upstream port Isaku Yamahata
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

pcie root port.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 Makefile.objs  |    2 +-
 hw/pcie_root.c |  247 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pcie_root.h |   32 +++++++
 3 files changed, 280 insertions(+), 1 deletions(-)
 create mode 100644 hw/pcie_root.c
 create mode 100644 hw/pcie_root.h

diff --git a/Makefile.objs b/Makefile.objs
index c73d12b..658c399 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -186,7 +186,7 @@ hw-obj-$(CONFIG_PIIX4) += piix4.o
 # PCI watchdog devices
 hw-obj-y += wdt_i6300esb.o
 
-hw-obj-y += pcie.o pcie_port.o
+hw-obj-y += pcie.o pcie_port.o pcie_root.o
 hw-obj-y += msix.o msi.o
 
 # PCI network cards
diff --git a/hw/pcie_root.c b/hw/pcie_root.c
new file mode 100644
index 0000000..cecfc6e
--- /dev/null
+++ b/hw/pcie_root.c
@@ -0,0 +1,247 @@
+/*
+ * pcie_root.c
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pci_ids.h"
+#include "msi.h"
+#include "pcie.h"
+#include "pcie_root.h"
+
+/* For now, Intel X58 IOH corporate deice exporess* root port.
+   need to get its own id? */
+#define PCI_DEVICE_ID_IOH_EPORT         0x3420  /* D0:F0 express mode */
+#define PCI_DEVICE_ID_IOH_REV           0x2
+#define IOH_EP_SSVID_OFFSET             0x40
+#define IOH_EP_SSVID_SVID               PCI_VENDOR_ID_INTEL
+#define IOH_EP_SSVID_SSID               0
+#define IOH_EP_MSI_OFFSET               0x60
+#define IOH_EP_MSI_SUPPORTED_FLAGS      PCI_MSI_FLAGS_MASKBIT
+#define IOH_EP_MSI_NR_VECTOR            2
+#define IOH_EP_EXP_OFFSET               0x90
+#define IOH_EP_AER_OFFSET               0x100
+
+#define PCIE_ROOT_VID                   PCI_VENDOR_ID_INTEL
+#define PCIE_ROOT_DID                   PCI_DEVICE_ID_IOH_EPORT
+#define PCIE_ROOT_REV                   PCI_DEVICE_ID_IOH_REV
+#define PCIE_ROOT_SSVID_OFFSET          IOH_EP_SSVID_OFFSET
+#define PCIE_ROOT_SVID                  IOH_EP_SSVID_SVID
+#define PCIE_ROOT_SSID                  IOH_EP_SSVID_SSID
+#define PCIE_ROOT_MSI_SUPPORTED_FLAGS   IOH_EP_MSI_SUPPORTED_FLAGS
+#define PCIE_ROOT_MSI_NR_VECTOR         IOH_EP_MSI_NR_VECTOR
+#define PCIE_ROOT_MSI_OFFSET            IOH_EP_MSI_OFFSET
+#define PCIE_ROOT_EXP_OFFSET            IOH_EP_EXP_OFFSET
+#define PCIE_ROOT_AER_OFFSET            IOH_EP_AER_OFFSET
+
+/*
+ * If two MSI vector are allocated, Advanced Error Interrupt Message Number
+ * is 1. otherwise 0.
+ * 17.12.5.10 RPERRSTS,  32:27 bit Advanced Error Interrupt Message Number.
+ */
+static uint8_t pcie_root_aer_vector(const PCIDevice *d)
+{
+    switch (msi_nr_allocated_vector(d)) {
+    case 1:
+        return 0;
+    case 2:
+        return 1;
+    case 4:
+    case 8:
+    case 16:
+    case 32:
+    default:
+        break;
+    }
+    abort();
+    return 0;
+}
+
+static void pcie_root_aer_vector_update(PCIDevice *d)
+{
+    pcie_aer_root_set_vector(d, pcie_root_aer_vector(d));
+}
+
+static void pcie_root_write_config(PCIDevice *d,
+                                   uint32_t address, uint32_t val, int len)
+{
+    uint16_t sltctl =
+        pci_get_word(d->config + pci_pcie_cap(d) + PCI_EXP_SLTCTL);
+    uint32_t root_cmd =
+        pci_get_long(d->config + pcie_aer_cap(d) + PCI_ERR_ROOT_COMMAND);
+
+    pci_bridge_write_config(d, address, val, len);
+    msi_write_config(d, address, val, len);
+    pcie_root_aer_vector_update(d);
+    pcie_cap_deverr_write_config(d, address, val, len);
+    pcie_cap_slot_write_config(d, address, val, len, sltctl);
+    pcie_aer_write_config_vbridge(d, address, val, len);
+    pcie_aer_write_config(d, address, val, len);
+    pcie_aer_root_write_config(d, address, val, len, root_cmd);
+}
+
+static void pcie_root_reset(DeviceState *qdev)
+{
+    PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
+    msi_reset(d);
+    pcie_root_aer_vector_update(d);
+    pcie_cap_root_reset(d);
+    pcie_cap_deverr_reset(d);
+    pcie_cap_slot_reset(d);
+    pcie_aer_root_reset(d);
+    pci_bridge_reset(qdev);
+}
+
+static int pcie_root_initfn(PCIDevice *d)
+{
+    PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
+    PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
+    PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
+    int rc;
+
+    rc = pci_bridge_initfn(d);
+    if (rc < 0) {
+        return rc;
+    }
+
+    d->config[PCI_REVISION_ID] = PCIE_ROOT_REV;
+    pcie_port_init_reg(d);
+
+    pci_config_set_vendor_id(d->config, PCIE_ROOT_VID);
+    pci_config_set_device_id(d->config, PCIE_ROOT_DID);
+
+    rc = pci_bridge_ssvid_init(d, PCIE_ROOT_SSVID_OFFSET,
+                               PCIE_ROOT_SVID, PCIE_ROOT_SSID);
+    if (rc < 0) {
+        return rc;
+    }
+    rc = msi_init(d, PCIE_ROOT_MSI_OFFSET,
+                  PCIE_ROOT_MSI_NR_VECTOR, PCIE_ROOT_MSI_SUPPORTED_FLAGS);
+    if (rc < 0) {
+        return rc;
+    }
+    rc = pci_pcie_cap_init(d, PCIE_ROOT_EXP_OFFSET, PCI_EXP_TYPE_ROOT_PORT,
+                           p->port);
+    if (rc < 0) {
+        return rc;
+    }
+    pcie_cap_deverr_init(d);
+    pcie_cap_slot_init(d, s->slot);
+    pcie_chassis_create(s->chassis);
+    rc = pcie_chassis_add_slot(s);
+    if (rc < 0) {
+        return rc;
+    }
+    pcie_cap_root_init(d);
+    rc = pcie_aer_init(d, PCIE_ROOT_AER_OFFSET);
+    if (rc < 0) {
+        return rc;
+    }
+    pcie_aer_root_init(d);
+    pcie_root_aer_vector_update(d);
+    return 0;
+}
+
+static int pcie_root_exitfn(PCIDevice *d)
+{
+    int rc;
+
+    pcie_aer_exit(d);
+    msi_uninit(d);
+    rc = pci_pcie_cap_exit(d);
+    if (rc < 0) {
+        return rc;
+    }
+    return pci_bridge_exitfn(d);
+}
+
+PCIESlot *pcie_root_init(PCIBus *bus, int devfn, bool multifunction,
+                         const char *bus_name, pci_map_irq_fn map_irq,
+                         uint8_t port, uint8_t chassis, uint16_t slot)
+{
+    PCIDevice *d;
+    PCIBridge *br;
+    DeviceState *qdev;
+
+    d = pci_create_multifunction(bus, devfn, multifunction, PCIE_ROOT_PORT);
+    if (!d) {
+        return NULL;
+    }
+    br = DO_UPCAST(PCIBridge, dev, d);
+
+    qdev = &br->dev.qdev;
+    pci_bridge_map_irq(br, bus_name, map_irq);
+    qdev_prop_set_uint8(qdev, "port", port);
+    qdev_prop_set_uint8(qdev, "chassis", chassis);
+    qdev_prop_set_uint16(qdev, "slot", slot);
+    qdev_init_nofail(qdev);
+
+    return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br));
+}
+
+static const VMStateDescription vmstate_pcie_root = {
+    .name = "pcie-root-port",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot),
+        VMSTATE_STRUCT(port.br.dev.aer_log, PCIESlot, 0, vmstate_pcie_aer_log,
+                       struct pcie_aer_log),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static PCIDeviceInfo pcie_root_info = {
+    .qdev.name = PCIE_ROOT_PORT,
+    .qdev.desc = "Root Port of PCI Express Switch",
+    .qdev.size = sizeof(PCIESlot),
+    .qdev.reset = pcie_root_reset,
+    .qdev.vmsd = &vmstate_pcie_root,
+
+    .is_express = 1,
+    .is_bridge = 1,
+    .config_write = pcie_root_write_config,
+    .init = pcie_root_initfn,
+    .exit = pcie_root_exitfn,
+
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0),
+        DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0),
+        DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0),
+        DEFINE_PROP_UINT16("aer_log_max", PCIESlot,
+                           port.br.dev.aer_log.log_max,
+                           PCIE_AER_LOG_MAX_DEFAULT),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void pcie_root_register(void)
+{
+    pci_qdev_register(&pcie_root_info);
+}
+
+device_init(pcie_root_register);
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 8
+ *  indent-tab-mode: nil
+ * End:
+ */
diff --git a/hw/pcie_root.h b/hw/pcie_root.h
new file mode 100644
index 0000000..9c5d4d0
--- /dev/null
+++ b/hw/pcie_root.h
@@ -0,0 +1,32 @@
+/*
+ * pcie_root.h
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QEMU_PCIE_ROOT_H
+#define QEMU_PCIE_ROOT_H
+
+#include "pcie_port.h"
+
+#define PCIE_ROOT_PORT    "pcie-root-port"
+
+PCIESlot *pcie_root_init(PCIBus *bus, int devfn, bool multifunction,
+                         const char *bus_name, pci_map_irq_fn map_irq,
+                         uint8_t port, uint8_t chassis, uint16_t slot);
+
+#endif /* QEMU_PCIE_ROOT_H */
-- 
1.7.1.1

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

* [Qemu-devel] [PATCH 11/14] pcie upstream port: pci express switch upstream port.
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
                   ` (9 preceding siblings ...)
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 10/14] pcie root port: implement pcie root port Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 12/14] pcie downstream port: pci express switch downstream port Isaku Yamahata
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

pci express switch upstream port.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 Makefile.objs      |    2 +-
 hw/pcie_upstream.c |  206 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pcie_upstream.h |   32 ++++++++
 3 files changed, 239 insertions(+), 1 deletions(-)
 create mode 100644 hw/pcie_upstream.c
 create mode 100644 hw/pcie_upstream.h

diff --git a/Makefile.objs b/Makefile.objs
index 658c399..e7a08dc 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -139,7 +139,7 @@ user-obj-y += cutils.o cache-utils.o
 hw-obj-y =
 hw-obj-y += vl.o loader.o
 hw-obj-y += virtio.o virtio-console.o
-hw-obj-y += fw_cfg.o pci.o pci_host.o pcie_host.o pci_bridge.o
+hw-obj-y += fw_cfg.o pci.o pci_host.o pcie_host.o pci_bridge.o pcie_upstream.o
 hw-obj-y += watchdog.o
 hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
 hw-obj-$(CONFIG_ECC) += ecc.o
diff --git a/hw/pcie_upstream.c b/hw/pcie_upstream.c
new file mode 100644
index 0000000..d1f0920
--- /dev/null
+++ b/hw/pcie_upstream.c
@@ -0,0 +1,206 @@
+/*
+ * pcie_upstream.c
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pci_ids.h"
+#include "msi.h"
+#include "pcie.h"
+#include "pcie_upstream.h"
+
+/* For now, TI XIO3130 is borrowed. need to get its own id? */
+#define PCI_DEVICE_ID_TI_XIO3130U       0x8232  /* upstream port */
+#define XIO3130_REVISION                0x2
+#define XIO3130_MSI_OFFSET              0x70
+#define XIO3130_MSI_SUPPORTED_FLAGS     PCI_MSI_FLAGS_64BIT
+#define XIO3130_MSI_NR_VECTOR           1
+#define XIO3130_SSVID_OFFSET            0x80
+#define XIO3130_SSVID_SVID              0
+#define XIO3130_SSVID_SSID              0
+#define XIO3130_EXP_OFFSET              0x90
+#define XIO3130_AER_OFFSET              0x100
+
+#define PCIE_UPSTREAM_VID               PCI_VENDOR_ID_TI
+#define PCIE_UPSTREAM_DID               PCI_DEVICE_ID_TI_XIO3130U
+#define PCIE_UPSTREAM_REVISION          XIO3130_REVISION
+#define PCIE_UPSTREAM_MSI_SUPPORTED_FLAGS       XIO3130_MSI_SUPPORTED_FLAGS
+#define PCIE_UPSTREAM_MSI_NR_VECTOR     XIO3130_MSI_NR_VECTOR
+#define PCIE_UPSTREAM_MSI_OFFSET        XIO3130_MSI_OFFSET
+#define PCIE_UPSTREAM_SSVID_OFFSET      XIO3130_SSVID_OFFSET
+#define PCIE_UPSTREAM_SVID              XIO3130_SSVID_SVID
+#define PCIE_UPSTREAM_SSID              XIO3130_SSVID_SSID
+#define PCIE_UPSTREAM_EXP_OFFSET        XIO3130_EXP_OFFSET
+#define PCIE_UPSTREAM_AER_OFFSET        XIO3130_AER_OFFSET
+
+static void pcie_upstream_write_config(PCIDevice *d,
+                                       uint32_t address, uint32_t val, int len)
+{
+    pci_bridge_write_config(d, address, val, len);
+    pcie_cap_deverr_write_config(d, address, val, len);
+    pcie_cap_flr_write_config(d, address, val, len);
+    msi_write_config(d, address, val, len);
+    pcie_aer_write_config_vbridge(d, address, val, len);
+    pcie_aer_write_config(d, address, val, len);
+}
+
+static void pcie_upstream_reset(DeviceState *qdev)
+{
+    PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
+    msi_reset(d);
+    pci_bridge_reset(qdev);
+    pcie_cap_deverr_reset(d);
+}
+
+static void pcie_upstream_flr(PCIDevice *d)
+{
+    /* TODO: not enabled until qdev reset clean up
+       waiting for Anthony's qdev cealn up */
+#if 0
+    /* So far, sticky bit registers or register which must be preserved
+       over FLR aren't emulated. So just reset this device. */
+    pci_device_reset(d);
+#endif
+}
+
+static int pcie_upstream_initfn(PCIDevice *d)
+{
+    PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
+    PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
+    int rc;
+
+    rc = pci_bridge_initfn(d);
+    if (rc < 0) {
+        return rc;
+    }
+
+    pcie_port_init_reg(d);
+    pci_config_set_vendor_id(d->config, PCIE_UPSTREAM_VID);
+    pci_config_set_device_id(d->config, PCIE_UPSTREAM_DID);
+    d->config[PCI_REVISION_ID] = PCIE_UPSTREAM_REVISION;
+
+    rc = msi_init(d, PCIE_UPSTREAM_MSI_OFFSET, PCIE_UPSTREAM_MSI_NR_VECTOR,
+                  PCIE_UPSTREAM_MSI_SUPPORTED_FLAGS);
+    if (rc < 0) {
+        return rc;
+    }
+    rc = pci_bridge_ssvid_init(d, PCIE_UPSTREAM_SSVID_OFFSET,
+                               PCIE_UPSTREAM_SVID, PCIE_UPSTREAM_SSID);
+    if (rc < 0) {
+        return rc;
+    }
+    rc = pci_pcie_cap_init(d, PCIE_UPSTREAM_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM,
+                           p->port);
+    if (rc < 0) {
+        return rc;
+    }
+    pcie_cap_flr_init(d, &pcie_upstream_flr);
+    pcie_cap_deverr_init(d);
+    rc = pcie_aer_init(d, PCIE_UPSTREAM_AER_OFFSET);
+    if (rc < 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+static int pcie_upstream_exitfn(PCIDevice *d)
+{
+    int rc;
+
+    pcie_aer_exit(d);
+    msi_uninit(d);
+    rc = pci_pcie_cap_exit(d);
+    if (rc < 0) {
+        return rc;
+    }
+    return pci_bridge_exitfn(d);
+}
+
+PCIEPort *pcie_upstream_init(PCIBus *bus, int devfn, bool multifunction,
+                             const char *bus_name, pci_map_irq_fn map_irq,
+                             uint8_t port)
+{
+    PCIDevice *d;
+    PCIBridge *br;
+    DeviceState *qdev;
+
+    d = pci_create_multifunction(bus, devfn, multifunction,
+                                 PCIE_UPSTREAM_PORT);
+    if (!d) {
+        return NULL;
+    }
+    br = DO_UPCAST(PCIBridge, dev, d);
+
+    qdev = &br->dev.qdev;
+    pci_bridge_map_irq(br, bus_name, map_irq);
+    qdev_prop_set_uint8(qdev, "port", port);
+    qdev_init_nofail(qdev);
+
+    return DO_UPCAST(PCIEPort, br, br);
+}
+
+static const VMStateDescription vmstate_pcie_upstream = {
+    .name = "pcie-upstream-port",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCIE_DEVICE(br.dev, PCIEPort),
+        VMSTATE_STRUCT(br.dev.aer_log, PCIEPort, 0, vmstate_pcie_aer_log,
+                       struct pcie_aer_log),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static PCIDeviceInfo pcie_upstream_info = {
+    .qdev.name = PCIE_UPSTREAM_PORT,
+    .qdev.desc = "Upstream Port of PCI Express Switch",
+    .qdev.size = sizeof(PCIEPort),
+    .qdev.reset = pcie_upstream_reset,
+    .qdev.vmsd = &vmstate_pcie_upstream,
+
+    .is_express = 1,
+    .is_bridge = 1,
+    .config_write = pcie_upstream_write_config,
+    .init = pcie_upstream_initfn,
+    .exit = pcie_upstream_exitfn,
+
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT8("port", PCIEPort, port, 0),
+        DEFINE_PROP_UINT16("aer_log_max", PCIEPort,
+                           br.dev.aer_log.log_max, PCIE_AER_LOG_MAX_DEFAULT),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void pcie_upstream_register(void)
+{
+    pci_qdev_register(&pcie_upstream_info);
+}
+
+device_init(pcie_upstream_register);
+
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 8
+ *  indent-tab-mode: nil
+ * End:
+ */
diff --git a/hw/pcie_upstream.h b/hw/pcie_upstream.h
new file mode 100644
index 0000000..1d36317
--- /dev/null
+++ b/hw/pcie_upstream.h
@@ -0,0 +1,32 @@
+/*
+ * pcie_upstream.h
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QEMU_PCIE_UPSTREAM_H
+#define QEMU_PCIE_UPSTREAM_H
+
+#include "pcie_port.h"
+
+#define PCIE_UPSTREAM_PORT      "pcie-upstream-port"
+
+PCIEPort *pcie_upstream_init(PCIBus *bus, int devfn, bool multifunction,
+                             const char *bus_name, pci_map_irq_fn map_irq,
+                             uint8_t port);
+
+#endif /* QEMU_PCIE_UPSTREAM_H */
-- 
1.7.1.1

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

* [Qemu-devel] [PATCH 12/14] pcie downstream port: pci express switch downstream port.
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
                   ` (10 preceding siblings ...)
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 11/14] pcie upstream port: pci express switch upstream port Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 13/14] pcie/hotplug: glue pushing attention button command. pcie_abp Isaku Yamahata
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

pcie switch downstream port.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 Makefile.objs        |    1 +
 hw/pcie_downstream.c |  224 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pcie_downstream.h |   33 ++++++++
 3 files changed, 258 insertions(+), 0 deletions(-)
 create mode 100644 hw/pcie_downstream.c
 create mode 100644 hw/pcie_downstream.h

diff --git a/Makefile.objs b/Makefile.objs
index e7a08dc..6904b02 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -140,6 +140,7 @@ hw-obj-y =
 hw-obj-y += vl.o loader.o
 hw-obj-y += virtio.o virtio-console.o
 hw-obj-y += fw_cfg.o pci.o pci_host.o pcie_host.o pci_bridge.o pcie_upstream.o
+hw-obj-y += pcie_downstream.o
 hw-obj-y += watchdog.o
 hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
 hw-obj-$(CONFIG_ECC) += ecc.o
diff --git a/hw/pcie_downstream.c b/hw/pcie_downstream.c
new file mode 100644
index 0000000..a04efc9
--- /dev/null
+++ b/hw/pcie_downstream.c
@@ -0,0 +1,224 @@
+/*
+ * pcie_downstream.c
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pci_ids.h"
+#include "msi.h"
+#include "pcie.h"
+#include "pcie_downstream.h"
+
+/* For now, TI XIO3130 is borrowed. need to get its own id? */
+#define PCI_DEVICE_ID_TI_XIO3130D       0x8233  /* downstream port */
+#define XIO3130_REVISION                0x1
+#define XIO3130_MSI_OFFSET              0x70
+#define XIO3130_MSI_SUPPORTED_FLAGS     PCI_MSI_FLAGS_64BIT
+#define XIO3130_MSI_NR_VECTOR           1
+#define XIO3130_SSVID_OFFSET            0x80
+#define XIO3130_SSVID_SVID              0
+#define XIO3130_SSVID_SSID              0
+#define XIO3130_EXP_OFFSET              0x90
+#define XIO3130_AER_OFFSET              0x100
+
+#define PCIE_DOWNSTREAM_VID             PCI_VENDOR_ID_TI
+#define PCIE_DOWNSTREAM_DID             PCI_DEVICE_ID_TI_XIO3130D
+#define PCIE_DOWNSTREAM_REVISION        XIO3130_REVISION
+#define PCIE_DOWNSTREAM_MSI_SUPPORTED_FLAGS     XIO3130_MSI_SUPPORTED_FLAGS
+#define PCIE_DOWNSTREAM_MSI_NR_VECTOR   XIO3130_MSI_NR_VECTOR
+#define PCIE_DOWNSTREAM_MSI_OFFSET      XIO3130_MSI_OFFSET
+#define PCIE_DOWNSTREAM_SSVID_OFFSET    XIO3130_SSVID_OFFSET
+#define PCIE_DOWNSTREAM_SVID            XIO3130_SSVID_SVID
+#define PCIE_DOWNSTREAM_SSID            XIO3130_SSVID_SSID
+#define PCIE_DOWNSTREAM_EXP_OFFSET      XIO3130_EXP_OFFSET
+#define PCIE_DOWNSTREAM_AER_OFFSET      XIO3130_AER_OFFSET
+
+static void pcie_downstream_write_config(PCIDevice *d, uint32_t address,
+                                         uint32_t val, int len)
+{
+    uint16_t sltctl =
+        pci_get_word(d->config + pci_pcie_cap(d) + PCI_EXP_SLTCTL);
+    pci_bridge_write_config(d, address, val, len);
+    pcie_cap_flr_write_config(d, address, val, len);
+    pcie_cap_deverr_write_config(d, address, val, len);
+    pcie_cap_slot_write_config(d, address, val, len, sltctl);
+    msi_write_config(d, address, val, len);
+    pcie_aer_write_config_vbridge(d, address, val, len);
+    pcie_aer_write_config(d, address, val, len);
+}
+
+static void pcie_downstream_reset(DeviceState *qdev)
+{
+    PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
+    msi_reset(d);
+    pcie_cap_deverr_reset(d);
+    pcie_cap_slot_reset(d);
+    pcie_cap_ari_reset(d);
+    pci_bridge_reset(qdev);
+}
+
+static void pcie_downstream_flr(PCIDevice *d)
+{
+    /* TODO: not enabled until qdev reset clean up
+       waiting for Anthony's qdev cealn up */
+#if 0
+    /* So far, sticky bit registers or register which must be preserved
+       over FLR aren't emulated. So just reset this device. */
+    pci_device_reset(d);
+#endif
+}
+
+static int pcie_downstream_initfn(PCIDevice *d)
+{
+    PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
+    PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
+    PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
+    int rc;
+
+    rc = pci_bridge_initfn(d);
+    if (rc < 0) {
+        return rc;
+    }
+
+    pcie_port_init_reg(d);
+    pci_config_set_vendor_id(d->config, PCIE_DOWNSTREAM_VID);
+    pci_config_set_device_id(d->config, PCIE_DOWNSTREAM_DID);
+    d->config[PCI_REVISION_ID] = PCIE_DOWNSTREAM_REVISION;
+
+    rc = msi_init(d, PCIE_DOWNSTREAM_MSI_OFFSET, PCIE_DOWNSTREAM_MSI_NR_VECTOR,
+                  PCIE_DOWNSTREAM_MSI_SUPPORTED_FLAGS);
+    if (rc < 0) {
+        return rc;
+    }
+    rc = pci_bridge_ssvid_init(d, PCIE_DOWNSTREAM_SSVID_OFFSET,
+                               PCIE_DOWNSTREAM_SVID, PCIE_DOWNSTREAM_SSID);
+    if (rc < 0) {
+        return rc;
+    }
+    rc = pci_pcie_cap_init(d, PCIE_DOWNSTREAM_EXP_OFFSET,
+                           PCI_EXP_TYPE_DOWNSTREAM, p->port);
+    if (rc < 0) {
+        return rc;
+    }
+    pcie_cap_flr_init(d, &pcie_downstream_flr);
+    pcie_cap_deverr_init(d);
+    pcie_cap_slot_init(d, s->slot);
+    pcie_chassis_create(s->chassis);
+    rc = pcie_chassis_add_slot(s);
+    if (rc < 0) {
+        return rc;
+    }
+    pcie_cap_ari_init(d);
+    rc = pcie_aer_init(d, PCIE_DOWNSTREAM_AER_OFFSET);
+    if (rc < 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+static int pcie_downstream_exitfn(PCIDevice *d)
+{
+    int rc;
+
+    pcie_aer_exit(d);
+    msi_uninit(d);
+    rc = pci_pcie_cap_exit(d);
+    if (rc < 0) {
+        return rc;
+    }
+    return pci_bridge_exitfn(d);
+}
+
+PCIESlot *pcie_downstream_init(PCIBus *bus,
+                               int devfn, bool multifunction,
+                               const char *bus_name, pci_map_irq_fn map_irq,
+                               uint8_t port, uint8_t chassis, uint16_t slot)
+{
+    PCIDevice *d;
+    PCIBridge *br;
+    DeviceState *qdev;
+
+    d = pci_create_multifunction(bus, devfn, multifunction,
+                                 PCIE_DOWNSTREAM_PORT);
+    if (!d) {
+        return NULL;
+    }
+    br = DO_UPCAST(PCIBridge, dev, d);
+
+    qdev = &br->dev.qdev;
+    pci_bridge_map_irq(br, bus_name, map_irq);
+    qdev_prop_set_uint8(qdev, "port", port);
+    qdev_prop_set_uint8(qdev, "chassis", chassis);
+    qdev_prop_set_uint16(qdev, "slot", slot);
+    qdev_init_nofail(qdev);
+
+    return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br));
+}
+
+static const VMStateDescription vmstate_pcie_downstream = {
+    .name = "pcie-downstream-port",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot),
+        VMSTATE_STRUCT(port.br.dev.aer_log, PCIESlot, 0, vmstate_pcie_aer_log,
+                       struct pcie_aer_log),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static PCIDeviceInfo pcie_downstream_info = {
+    .qdev.name = PCIE_DOWNSTREAM_PORT,
+    .qdev.desc = "Downstream Port of PCI Express Switch",
+    .qdev.size = sizeof(PCIESlot),
+    .qdev.reset = pcie_downstream_reset,
+    .qdev.vmsd = &vmstate_pcie_downstream,
+
+    .is_express = 1,
+    .is_bridge = 1,
+    .config_write = pcie_downstream_write_config,
+    .init = pcie_downstream_initfn,
+    .exit = pcie_downstream_exitfn,
+
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0),
+        DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0),
+        DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0),
+        DEFINE_PROP_UINT16("aer_log_max", PCIESlot,
+                           port.br.dev.aer_log.log_max,
+                           PCIE_AER_LOG_MAX_DEFAULT),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void pcie_downstream_register(void)
+{
+    pci_qdev_register(&pcie_downstream_info);
+}
+
+device_init(pcie_downstream_register);
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 8
+ *  indent-tab-mode: nil
+ * End:
+ */
diff --git a/hw/pcie_downstream.h b/hw/pcie_downstream.h
new file mode 100644
index 0000000..686fdac
--- /dev/null
+++ b/hw/pcie_downstream.h
@@ -0,0 +1,33 @@
+/*
+ * pcie_downstream.h
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QEMU_PCIE_DOWNSTREAM_H
+#define QEMU_PCIE_DOWNSTREAM_H
+
+#include "pcie_port.h"
+
+#define PCIE_DOWNSTREAM_PORT    "pcie-downstream-port"
+
+PCIESlot *pcie_downstream_init(PCIBus *bus,
+                               int devfn, bool multifunction,
+                               const char *bus_name, pci_map_irq_fn map_irq,
+                               uint8_t port, uint8_t chassis, uint16_t slot);
+
+#endif /* QEMU_PCIE_DOWNSTREAM_H */
-- 
1.7.1.1

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

* [Qemu-devel] [PATCH 13/14] pcie/hotplug: glue pushing attention button command. pcie_abp
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
                   ` (11 preceding siblings ...)
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 12/14] pcie downstream port: pci express switch downstream port Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 14/14] pcie/aer: glue aer error injection into qemu monitor Isaku Yamahata
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

glue to pcie_abp monitor command.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
 hw/pcie_port.c  |   82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-monitor.hx |   14 +++++++++
 sysemu.h        |    4 +++
 3 files changed, 100 insertions(+), 0 deletions(-)

diff --git a/hw/pcie_port.c b/hw/pcie_port.c
index d2b1f38..0aa8c53 100644
--- a/hw/pcie_port.c
+++ b/hw/pcie_port.c
@@ -18,6 +18,10 @@
  * with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "qemu-objects.h"
+#include "sysemu.h"
+#include "monitor.h"
+#include "pcie.h"
 #include "pcie_port.h"
 
 void pcie_port_init_reg(PCIDevice *d)
@@ -104,3 +108,81 @@ void pcie_chassis_del_slot(PCIESlot *s)
 {
     QLIST_REMOVE(s, next);
 }
+
+/**************************************************************************
+ * glue for qemu monitor
+ */
+
+/* Parse [<chassis>.]<slot>, return -1 on error */
+static int pcie_parse_slot_addr(const char* slot_addr,
+                                uint8_t *chassisp, uint16_t *slotp)
+{
+    const char *p;
+    char *e;
+    unsigned long val;
+    unsigned long chassis = 0;
+    unsigned long slot;
+
+    p = slot_addr;
+    val = strtoul(p, &e, 0);
+    if (e == p) {
+        return -1;
+    }
+    if (*e == '.') {
+        chassis = val;
+        p = e + 1;
+        val = strtoul(p, &e, 0);
+        if (e == p) {
+            return -1;
+        }
+    }
+    slot = val;
+
+    if (*e) {
+        return -1;
+    }
+
+    if (chassis > 0xff || slot > 0xffff) {
+        return -1;
+    }
+
+    *chassisp = chassis;
+    *slotp = slot;
+    return 0;
+}
+
+void pcie_attention_button_push_print(Monitor *mon, const QObject *data)
+{
+    QDict *qdict;
+
+    assert(qobject_type(data) == QTYPE_QDICT);
+    qdict = qobject_to_qdict(data);
+
+    monitor_printf(mon, "OK chassis %d, slot %d\n",
+                   (int) qdict_get_int(qdict, "chassis"),
+                   (int) qdict_get_int(qdict, "slot"));
+}
+
+int pcie_attention_button_push(Monitor *mon, const QDict *qdict,
+                               QObject **ret_data)
+{
+    const char* pcie_slot = qdict_get_str(qdict, "pcie_slot");
+    uint8_t chassis;
+    uint16_t slot;
+    PCIESlot *s;
+
+    if (pcie_parse_slot_addr(pcie_slot, &chassis, &slot) < 0) {
+        monitor_printf(mon, "invalid pcie slot address %s\n", pcie_slot);
+        return -1;
+    }
+    s = pcie_chassis_find_slot(chassis, slot);
+    if (!s) {
+        monitor_printf(mon, "slot is not found. %s\n", pcie_slot);
+        return -1;
+    }
+    pcie_cap_slot_push_attention_button(&s->port.br.dev);
+    *ret_data = qobject_from_jsonf("{ 'chassis': %d, 'slot': %d}",
+                                   chassis, slot);
+    assert(*ret_data);
+    return 0;
+}
diff --git a/qemu-monitor.hx b/qemu-monitor.hx
index 2af3de6..02fbda1 100644
--- a/qemu-monitor.hx
+++ b/qemu-monitor.hx
@@ -1154,6 +1154,20 @@ Hot remove PCI device.
 ETEXI
 
     {
+        .name       = "pcie_abp",
+        .args_type  = "pcie_slot:s",
+        .params     = "[<chassis>.]<slot>",
+        .help       = "push pci express attention button",
+        .user_print  = pcie_attention_button_push_print,
+        .mhandler.cmd_new = pcie_attention_button_push,
+    },
+
+STEXI
+@item pcie_abp
+Push PCI express attention button
+ETEXI
+
+    {
         .name       = "host_net_add",
         .args_type  = "device:s,opts:s?",
         .params     = "tap|user|socket|vde|dump [options]",
diff --git a/sysemu.h b/sysemu.h
index 9c988bb..cca411d 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -150,6 +150,10 @@ extern unsigned int nb_prom_envs;
 void pci_device_hot_add(Monitor *mon, const QDict *qdict);
 void drive_hot_add(Monitor *mon, const QDict *qdict);
 void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict);
+/* pcie hotplug */
+void pcie_attention_button_push_print(Monitor *mon, const QObject *data);
+int pcie_attention_button_push(Monitor *mon, const QDict *qdict,
+                               QObject **ret_data);
 
 /* serial ports */
 
-- 
1.7.1.1

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

* [Qemu-devel] [PATCH 14/14] pcie/aer: glue aer error injection into qemu monitor.
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
                   ` (12 preceding siblings ...)
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 13/14] pcie/hotplug: glue pushing attention button command. pcie_abp Isaku Yamahata
@ 2010-09-06  7:46 ` Isaku Yamahata
  2010-09-06 17:02 ` [Qemu-devel] Re: [PATCH 00/14] pcie port switch emulators Wei Xu
  2010-09-07 17:14 ` Michael S. Tsirkin
  15 siblings, 0 replies; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06  7:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: skandasa, etmartin, wexu2, mst, adhyas, yamahata

glue aer error injection into qemu monitor.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>

Conflicts:

	hw/pcie.c
---
 hw/pcie.c       |   85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-monitor.hx |   22 ++++++++++++++
 sysemu.h        |    5 +++
 3 files changed, 112 insertions(+), 0 deletions(-)

diff --git a/hw/pcie.c b/hw/pcie.c
index 1f24c2a..f02a868 100644
--- a/hw/pcie.c
+++ b/hw/pcie.c
@@ -19,6 +19,8 @@
  */
 
 #include "sysemu.h"
+#include "qemu-objects.h"
+#include "monitor.h"
 #include "pci_bridge.h"
 #include "pcie.h"
 #include "msix.h"
@@ -1666,3 +1668,86 @@ const VMStateDescription vmstate_pcie_aer_log = {
         VMSTATE_END_OF_LIST()
     }
 };
+
+void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
+{
+    QDict *qdict;
+    int devfn;
+    assert(qobject_type(data) == QTYPE_QDICT);
+    qdict = qobject_to_qdict(data);
+
+    devfn = (int)qdict_get_int(qdict, "devfn");
+    monitor_printf(mon, "OK domain: %x, bus: %x devfn: %x.%x\n",
+                   (int) qdict_get_int(qdict, "domain"),
+                   (int) qdict_get_int(qdict, "bus"),
+                   PCI_SLOT(devfn), PCI_FUNC(devfn));
+}
+
+int do_pcie_aer_inejct_error(Monitor *mon,
+                             const QDict *qdict, QObject **ret_data)
+{
+    const char *pci_addr = qdict_get_str(qdict, "pci_addr");
+    int dom;
+    int bus;
+    unsigned int slot;
+    unsigned int func;
+    PCIDevice *dev;
+    struct pcie_aer_err err;
+
+    /* Ideally qdev device path should be used.
+     * However at the moment there is no reliable way to determine
+     * wheher a given qdev is pci device or not.
+     * so pci_addr is used.
+     */
+    if (pci_parse_devaddr(pci_addr, &dom, &bus, &slot, &func)) {
+        monitor_printf(mon, "invalid pci address %s\n", pci_addr);
+        return -1;
+    }
+    dev = pci_find_device(pci_find_root_bus(dom), bus, slot, func);
+    if (!dev) {
+        monitor_printf(mon, "device is not found. 0x%x:0x%x.0x%x\n",
+                       bus, slot, func);
+        return -1;
+    }
+    if (!pci_is_express(dev)) {
+        monitor_printf(mon, "the device doesn't support pci express. "
+                       "0x%x:0x%x.0x%x\n",
+                       bus, slot, func);
+        return -1;
+    }
+
+    err.status = qdict_get_int(qdict, "error_status");
+    err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn;
+
+    err.flags = 0;
+    if (qdict_get_int(qdict, "is_correctable")) {
+        err.flags |= PCIE_AER_ERR_IS_CORRECTABLE;
+    }
+    if (qdict_get_int(qdict, "advisory_non_fatal")) {
+        err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY;
+    }
+    if (qdict_haskey(qdict, "tlph0")) {
+        err.flags |= PCIE_AER_ERR_HEADER_VALID;
+    }
+    if (qdict_haskey(qdict, "hpfx0")) {
+        err.flags |= PCIE_AER_ERR_TLP_PRESENT;
+    }
+
+    err.header[0] = qdict_get_try_int(qdict, "tlph0", 0);
+    err.header[1] = qdict_get_try_int(qdict, "tlph1", 0);
+    err.header[2] = qdict_get_try_int(qdict, "tlph2", 0);
+    err.header[3] = qdict_get_try_int(qdict, "tlph3", 0);
+
+    err.prefix[0] = qdict_get_try_int(qdict, "hpfx0", 0);
+    err.prefix[1] = qdict_get_try_int(qdict, "hpfx1", 0);
+    err.prefix[2] = qdict_get_try_int(qdict, "hpfx2", 0);
+    err.prefix[3] = qdict_get_try_int(qdict, "hpfx3", 0);
+
+    pcie_aer_inject_error(dev, &err);
+    *ret_data = qobject_from_jsonf("{ 'domain': %d, 'bus': %d, 'devfn': %d }",
+                                   pci_find_domain(dev->bus),
+                                   pci_bus_num(dev->bus), dev->devfn);
+    assert(*ret_data);
+
+    return 0;
+}
diff --git a/qemu-monitor.hx b/qemu-monitor.hx
index 02fbda1..080c90e 100644
--- a/qemu-monitor.hx
+++ b/qemu-monitor.hx
@@ -1168,6 +1168,28 @@ Push PCI express attention button
 ETEXI
 
     {
+        .name       = "pcie_aer_inject_error",
+        .args_type  = "advisory_non_fatal:-a,is_correctable:-c,"
+	              "pci_addr:s,error_status:i,"
+	              "tlph0:i?,tlph1:i?,tlph2:i?,tlph3:i?,"
+	              "hpfx0:i?,hpfx1:i?,hpfx2:i?,hpfx3:i?",
+        .params     = "[-a] [-c] [[<domain>:]<bus>:]<slot>.<func> "
+	              "<error status:32bit> "
+	              "[<tlp header:(32bit x 4)>] "
+	              "[<tlp header prefix:(32bit x 4)>]",
+        .help       = "inject pcie aer error "
+	               "(use -a for advisory non fatal error) "
+	               "(use -c for correctrable error)",
+        .user_print  = pcie_aer_inject_error_print,
+        .mhandler.cmd_new = do_pcie_aer_inejct_error,
+    },
+
+STEXI
+@item pcie_abp
+Push PCI express attention button
+ETEXI
+
+    {
         .name       = "host_net_add",
         .args_type  = "device:s,opts:s?",
         .params     = "tap|user|socket|vde|dump [options]",
diff --git a/sysemu.h b/sysemu.h
index cca411d..2f7157c 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -155,6 +155,11 @@ void pcie_attention_button_push_print(Monitor *mon, const QObject *data);
 int pcie_attention_button_push(Monitor *mon, const QDict *qdict,
                                QObject **ret_data);
 
+/* pcie aer error injection */
+void pcie_aer_inject_error_print(Monitor *mon, const QObject *data);
+int do_pcie_aer_inejct_error(Monitor *mon,
+                             const QDict *qdict, QObject **ret_data);
+
 /* serial ports */
 
 #define MAX_SERIAL_PORTS 4
-- 
1.7.1.1

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

* [Qemu-devel] Re: [PATCH 02/14] pci: consolidate pci_add_capability_at_offset() into pci_add_capability().
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 02/14] pci: consolidate pci_add_capability_at_offset() into pci_add_capability() Isaku Yamahata
@ 2010-09-06  8:10   ` Michael S. Tsirkin
  0 siblings, 0 replies; 25+ messages in thread
From: Michael S. Tsirkin @ 2010-09-06  8:10 UTC (permalink / raw)
  To: Isaku Yamahata; +Cc: skandasa, etmartin, wexu2, adhyas, qemu-devel

On Mon, Sep 06, 2010 at 04:46:16PM +0900, Isaku Yamahata wrote:
> By making pci_add_capability() the special case of
> pci_add_capability_at_offset() of offset = 0,
> consolidate pci_add_capability_at_offset() into pci_add_capability().
> 
> Cc: Stefan Weil <weil@mail.berlios.de>
> Cc: Michael S. Tsirkin <mst@redhat.com>
> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>

Good stuff.

Here's an idea: Long term I think we should just give up on the ability
to allocate capabilities dynamically, and always pass a valid offset.
In particular, this would help us get rid of the used mask, and we won't
have to pass in capability size.

virtio would then have
#define VIRTIO_PCI_MSIX_CAP_OFF 0x40
and msix.h would be changed to get the offset.

The reason is that we need to preserve the offsets for migration
to work, anyway.

Have said all this, this is just an idea, it is not a must
and can be a separate patch, or I might do this change myself.

> ---
>  hw/eepro100.c |    4 ++--
>  hw/msix.c     |    3 ++-
>  hw/pci.c      |   33 ++++++++++++++++++---------------
>  hw/pci.h      |    5 ++---
>  4 files changed, 24 insertions(+), 21 deletions(-)
> 
> diff --git a/hw/eepro100.c b/hw/eepro100.c
> index 2b75c8f..8cbc3aa 100644
> --- a/hw/eepro100.c
> +++ b/hw/eepro100.c
> @@ -539,8 +539,8 @@ static void e100_pci_reset(EEPRO100State * s, E100PCIDeviceInfo *e100_device)
>      if (e100_device->power_management) {
>          /* Power Management Capabilities */
>          int cfg_offset = 0xdc;
> -        int r = pci_add_capability_at_offset(&s->dev, PCI_CAP_ID_PM,
> -                                             cfg_offset, PCI_PM_SIZEOF);
> +        int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM,
> +                                   cfg_offset, PCI_PM_SIZEOF);
>          assert(r >= 0);
>          pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21);
>  #if 0 /* TODO: replace dummy code for power management emulation. */
> diff --git a/hw/msix.c b/hw/msix.c
> index d99403a..7ce63eb 100644
> --- a/hw/msix.c
> +++ b/hw/msix.c
> @@ -73,7 +73,8 @@ static int msix_add_config(struct PCIDevice *pdev, unsigned short nentries,
>      }
>  
>      pdev->msix_bar_size = new_size;
> -    config_offset = pci_add_capability(pdev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH);
> +    config_offset = pci_add_capability(pdev, PCI_CAP_ID_MSIX,
> +                                       0, MSIX_CAP_LENGTH);
>      if (config_offset < 0)
>          return config_offset;
>      config = pdev->config + config_offset;
> diff --git a/hw/pci.c b/hw/pci.c
> index 2dc1577..754ffb3 100644
> --- a/hw/pci.c
> +++ b/hw/pci.c
> @@ -1682,11 +1682,25 @@ static void pci_del_option_rom(PCIDevice *pdev)
>      pdev->rom_offset = 0;
>  }
>  
> -/* Reserve space and add capability to the linked list in pci config space */
> -int pci_add_capability_at_offset(PCIDevice *pdev, uint8_t cap_id,
> -                                 uint8_t offset, uint8_t size)
> +/*
> + * if !offset
> + * Reserve space and add capability to the linked list in pci config space
> + *
> + * if offset = 0,
> + * Find and reserve space and add capability to the linked list
> + * in pci config space */
> +int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
> +                       uint8_t offset, uint8_t size)
>  {
> -    uint8_t *config = pdev->config + offset;
> +    uint8_t *config;
> +    if (!offset) {
> +        offset = pci_find_space(pdev, size);
> +        if (!offset) {
> +            return -ENOSPC;
> +        }
> +    }
> +
> +    config = pdev->config + offset;
>      config[PCI_CAP_LIST_ID] = cap_id;
>      config[PCI_CAP_LIST_NEXT] = pdev->config[PCI_CAPABILITY_LIST];
>      pdev->config[PCI_CAPABILITY_LIST] = offset;
> @@ -1699,17 +1713,6 @@ int pci_add_capability_at_offset(PCIDevice *pdev, uint8_t cap_id,
>      return offset;
>  }
>  
> -/* Find and reserve space and add capability to the linked list
> - * in pci config space */
> -int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
> -{
> -    uint8_t offset = pci_find_space(pdev, size);
> -    if (!offset) {
> -        return -ENOSPC;
> -    }
> -    return pci_add_capability_at_offset(pdev, cap_id, offset, size);
> -}
> -
>  /* Unlink capability from the pci config space. */
>  void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
>  {
> diff --git a/hw/pci.h b/hw/pci.h
> index c551f96..2ddba59 100644
> --- a/hw/pci.h
> +++ b/hw/pci.h
> @@ -183,9 +183,8 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
>                              pcibus_t size, int type,
>                              PCIMapIORegionFunc *map_func);
>  
> -int pci_add_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size);
> -int pci_add_capability_at_offset(PCIDevice *pci_dev, uint8_t cap_id,
> -                                 uint8_t cap_offset, uint8_t cap_size);
> +int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
> +                       uint8_t offset, uint8_t size);
>  
>  void pci_del_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size);
>  
> -- 
> 1.7.1.1

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

* [Qemu-devel] Re: [PATCH 07/14] msi: implemented msi.
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 07/14] msi: implemented msi Isaku Yamahata
@ 2010-09-06  9:44   ` Michael S. Tsirkin
  2010-09-08  7:43     ` Isaku Yamahata
  0 siblings, 1 reply; 25+ messages in thread
From: Michael S. Tsirkin @ 2010-09-06  9:44 UTC (permalink / raw)
  To: Isaku Yamahata; +Cc: skandasa, adhyas, etmartin, qemu-devel, wexu2

On Mon, Sep 06, 2010 at 04:46:21PM +0900, Isaku Yamahata wrote:
> implemented msi support functions.
> 
> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>


Good stuff. Below I suggest a couple of minor correctness issues,
and a couple of API changes.  Below are also some suggestions to improve
readability a bit: mostly there are too many one-liners used
only once or twice, with names not informative enough to guess what they
do, so I suggest we opencode them as one ends up reading the source
anyway.

Thanks!

> ---
>  Makefile.objs |    2 +-
>  hw/msi.c      |  362 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/msi.h      |   41 +++++++
>  hw/pci.h      |    5 +
>  4 files changed, 409 insertions(+), 1 deletions(-)
>  create mode 100644 hw/msi.c
>  create mode 100644 hw/msi.h
> 
> diff --git a/Makefile.objs b/Makefile.objs
> index 594894b..5f5a4c5 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -186,7 +186,7 @@ hw-obj-$(CONFIG_PIIX4) += piix4.o
>  # PCI watchdog devices
>  hw-obj-y += wdt_i6300esb.o
>  
> -hw-obj-y += msix.o
> +hw-obj-y += msix.o msi.o
>  
>  # PCI network cards
>  hw-obj-y += ne2000.o
> diff --git a/hw/msi.c b/hw/msi.c
> new file mode 100644
> index 0000000..dbe89ee
> --- /dev/null
> +++ b/hw/msi.c
> @@ -0,0 +1,362 @@
> +/*
> + * msi.c
> + *
> + * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
> + *                    VA Linux Systems Japan K.K.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> +
> + * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "msi.h"
> +
> +#define PCI_MSI_PENDING_32      0x10
> +#define PCI_MSI_PENDING_64      0x14
> +

Longer term, pls try to add this to linux, btw.
If you manage to we'll then move this to pci_regs.h

> +/* PCI_MSI_FLAGS */
> +#define PCI_MSI_FLAGS_QSIZE_SHIFT       4
> +#define PCI_MSI_FLAGS_QMASK_SHIFT       1

Use ffs on macros from pci_regs.h :
PCI_MSI_FLAGS_QSIZE_SHIFT ->
(ffs(PCI_MSI_FLAGS_QSIZE) - 1)
as there's a single user, we don't need a macro then.

> +
> +/* PCI_MSI_ADDRESS_LO */
> +#define PCI_MSI_ADDRESS_LO_RESERVED_MASK        0x3

Better to have the valid bits than invalid bits:
#define PCI_MSI_ADDRESS_LO_MASK        (~0x3)
And then use directly.
again, we can try adding this to linux long term.


> +
> +#define PCI_MSI_32_SIZEOF       0x0a
> +#define PCI_MSI_64_SIZEOF       0x0e
> +#define PCI_MSI_32M_SIZEOF      0x14
> +#define PCI_MSI_64M_SIZEOF      0x18

Note to self: if we get rid of cap allocator, we won't need the above.

> +
> +#define MSI_DEBUG
> +#ifdef MSI_DEBUG
> +# define MSI_DPRINTF(fmt, ...)                                          \
> +    fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__)
> +#else
> +# define MSI_DPRINTF(fmt, ...)  do { } while (0)
> +#endif
> +#define MSI_DEV_PRINTF(dev, fmt, ...)                                   \
> +    MSI_DPRINTF("%s:%x " fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__)
> +
> +static inline bool msi_test_bit(uint32_t bitmap, uint8_t bit)
> +{
> +    return bitmap & (1 << bit);
> +}
> +
> +static inline void msi_set_bit(uint32_t *bitmap, uint8_t bit)
> +{
> +    *bitmap |= (1 << bit);
> +}
> +
> +static inline void msi_clear_bit(uint32_t *bitmap, uint8_t bit)
> +{
> +    *bitmap &= ~(1 << bit);
> +}


The above 3 are much better open-coded - all they do is simple math.
Also, I think we should use 1U << - the above is signed and so it
will cause integer overflow when bit is 31.

> +
> +

Two empty lines -> one

> +/* multiple vector only suport power of 2 and up to 32 */
> +static inline uint8_t ilog2(uint8_t nr_vector)
> +{
> +    switch (nr_vector) {
> +    case 1:
> +        return 0;
> +    case 2:
> +        return 1;
> +    case 4:
> +        return 2;
> +    case 8:
> +        return 3;
> +    case 16:
> +        return 4;
> +    case 32:
> +        return 5;
> +    default:
> +        abort();

Not intuitive to abort in a mathematical function IMO.

> +        break;
> +    }
> +    return 0;
> +}
> +

Please just use ffs opencoded, there's a single user.
ilog -> ffs(n) - 1;
and do this after range-checking as suggested below.

> +static inline bool is_mask_bit_support(uint16_t control)
> +{
> +    return control & PCI_MSI_FLAGS_MASKBIT;
> +}
> +
> +static inline bool is_64bit_address(uint16_t control)
> +{
> +    return control & PCI_MSI_FLAGS_64BIT;
> +}
> +

These 2 function names are especially confusing.
The above 2 are better open-coded.

> +static inline uint8_t msi_vector_order(uint16_t control)
> +{
> +    return (control & PCI_MSI_FLAGS_QSIZE) >> PCI_MSI_FLAGS_QSIZE_SHIFT;
> +}
> +

above only used once? better opencoded

> +static inline uint8_t msi_nr_vector(uint16_t control)
> +{
> +    return 1 << msi_vector_order(control);
> +}
> +

Rename msi_nr_vectors.


> +static inline bool msi_control_enabled(uint16_t control)
> +{
> +    return control & PCI_MSI_FLAGS_ENABLE;
> +}
> +

above is better opencoded.

> +static inline uint8_t msi_cap_sizeof(uint16_t control)
> +{
> +    switch (control & (PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT)) {
> +    case PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT:
> +        return PCI_MSI_64M_SIZEOF;
> +    case PCI_MSI_FLAGS_64BIT:
> +        return PCI_MSI_64_SIZEOF;
> +    case PCI_MSI_FLAGS_MASKBIT:
> +        return PCI_MSI_32M_SIZEOF;
> +    case 0:
> +        return PCI_MSI_32_SIZEOF;
> +    default:
> +        abort();
> +        break;
> +    }
> +    return 0;
> +}
> +

Note to self: this will go away too if we get rid of cap allocator.

> +static inline uint16_t msi_control_reg(const PCIDevice* dev)
> +{
> +    return dev->msi_cap + PCI_MSI_FLAGS;
> +}
> +
> +static inline uint32_t msi_lower_address_reg(const PCIDevice* dev)
> +{
> +    return dev->msi_cap + PCI_MSI_ADDRESS_LO;
> +}
> +
> +static inline uint32_t msi_upper_address_reg(const PCIDevice* dev)
> +{
> +    return dev->msi_cap + PCI_MSI_ADDRESS_HI;
> +}
> +

I think above are better open-coded - a bit less sure here as I
understand you want consistency between registers.
In any case, I think names should be consistent with macros
and be explicit in that they return an offset:
e.g. msi_lower_address_reg -> msi_address_lo_off
Also, return uint8_t and not uint32_t: these registers
always live in a traditional config space.


> +static inline uint16_t msi_data_reg(const PCIDevice* dev, bool is64bit)

msi64bit would be a better name than is64bit

> +{
> +    return dev->msi_cap + (is64bit ?  PCI_MSI_DATA_64 : PCI_MSI_DATA_32);
> +}
> +
> +static inline uint32_t msi_mask_reg(const PCIDevice* dev, bool is64bit)
> +{
> +    return dev->msi_cap + (is64bit ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32);
> +}
> +
> +static inline uint32_t msi_pending_reg(const PCIDevice* dev, bool is64bit)
> +{
> +    return dev->msi_cap + (is64bit ? PCI_MSI_PENDING_64 : PCI_MSI_PENDING_32);
> +}
> +
> +bool msi_enabled(const PCIDevice *dev)
> +{
> +    return msi_present(dev) &&
> +        msi_control_enabled(pci_get_word(dev->config + msi_control_reg(dev)));
> +}
> +
> +int msi_init(struct PCIDevice *dev, uint8_t offset,
> +             uint8_t nr_vector, uint16_t flags)

rename nr_vector -> nr_vectors here and elsewhere

> +{
> +    uint8_t vector_order = ilog2(nr_vector);
> +    bool is64bit = is_64bit_address(flags);
> +    uint8_t cap_size;
> +    int config_offset;


maybe add assert on nr_vector: should be
	assert(!((nr_vector - 1) & nr_vector));
	assert(nr_vector > 0);
	assert(nr_vector <= 32);


> +    MSI_DEV_PRINTF(dev,
> +                   "init offset: 0x%"PRIx8" vector: %"PRId8
> +                   " flags 0x%"PRIx16"\n", offset, nr_vector, flags);
> +
> +    flags &= PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT;
> +    flags |= (vector_order << PCI_MSI_FLAGS_QMASK_SHIFT) & PCI_MSI_FLAGS_QMASK;
> +
> +    cap_size = msi_cap_sizeof(flags);
> +    config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size);
> +    if (config_offset < 0) {
> +        return config_offset;
> +    }
> +
> +    dev->msi_cap = config_offset;
> +    dev->cap_present |= QEMU_PCI_CAP_MSI;
> +
> +    pci_set_word(dev->config + msi_control_reg(dev), flags);
> +    pci_set_word(dev->wmask + msi_control_reg(dev),
> +                 PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
> +    pci_set_long(dev->wmask + msi_lower_address_reg(dev),
> +                 ~PCI_MSI_ADDRESS_LO_RESERVED_MASK);
> +    if (is64bit) {
> +        pci_set_long(dev->wmask + msi_upper_address_reg(dev), 0xffffffff);
> +    }
> +    pci_set_word(dev->wmask + msi_data_reg(dev, is64bit), 0xffff);
> +
> +    if (is_mask_bit_support(flags)) {
> +        pci_set_long(dev->wmask + msi_mask_reg(dev, is64bit),
> +                     (1 << nr_vector) - 1);

1 is signed. I think this should be 1U << nr_vector, otherwise
you get signed integer overflow for nr >= 31.

> +    }
> +    return config_offset;
> +}
> +
> +void msi_uninit(struct PCIDevice *dev)
> +{
> +    uint16_t control = pci_get_word(dev->config + msi_control_reg(dev));
> +    uint8_t cap_size = msi_cap_sizeof(control);
> +    pci_del_capability(dev, PCI_CAP_ID_MSIX, cap_size);
> +    MSI_DEV_PRINTF(dev, "uninit\n");
> +}
> +
> +void msi_reset(PCIDevice *dev)
> +{
> +    uint16_t control;
> +    bool is64bit;
> +
> +    control = pci_get_word(dev->config + msi_control_reg(dev));
> +    control &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
> +    is64bit = is_64bit_address(control);
> +
> +    pci_set_word(dev->config + msi_control_reg(dev), control);
> +    pci_set_long(dev->config + msi_lower_address_reg(dev), 0);
> +    if (is64bit) {
> +        pci_set_long(dev->config + msi_upper_address_reg(dev), 0);
> +    }
> +    pci_set_word(dev->config + msi_data_reg(dev, is64bit), 0);
> +    if (is_mask_bit_support(control)) {
> +        pci_set_long(dev->config + msi_mask_reg(dev, is64bit), 0);
> +        pci_set_long(dev->config + msi_pending_reg(dev, is64bit), 0);
> +    }
> +    MSI_DEV_PRINTF(dev, "reset\n");
> +}
> +
> +static bool msi_is_masked(const PCIDevice *dev, uint8_t vector)
> +{
> +    uint16_t control = pci_get_word(dev->config + msi_control_reg(dev));
> +    uint32_t mask;
> +
> +    if (!is_mask_bit_support(control)) {
> +        return false;
> +    }
> +
> +    mask = pci_get_long(dev->config +
> +                        msi_mask_reg(dev, is_64bit_address(control)));
> +    return msi_test_bit(mask, vector);
> +}
> +
> +static void msi_set_pending(PCIDevice *dev, uint8_t vector)
> +{
> +    uint16_t control = pci_get_word(dev->config + msi_control_reg(dev));
> +    bool is64bit = is_64bit_address(control);
> +    uint32_t pending;
> +
> +    assert(is_mask_bit_support(control));
> +
> +    pending = pci_get_long(dev->config + msi_pending_reg(dev, is64bit));
> +    msi_set_bit(&pending, vector);
> +    pci_set_long(dev->config + msi_pending_reg(dev, is64bit), pending);
> +    MSI_DEV_PRINTF(dev, "pending vector 0x%"PRIx8"\n", vector);
> +}
> +
> +void msi_notify(PCIDevice *dev, uint8_t vector)
> +{
> +    uint16_t control = pci_get_word(dev->config + msi_control_reg(dev));
> +    bool is64bit = is_64bit_address(control);
> +    uint8_t nr_vector = msi_nr_vector(control);
> +    uint64_t address;
> +    uint32_t data;
> +
> +    assert(vector < nr_vector);
> +    if (msi_is_masked(dev, vector)) {
> +        msi_set_pending(dev, vector);
> +        return;
> +    }
> +
> +    if (is64bit){
> +        address = pci_get_quad(dev->config + msi_lower_address_reg(dev));
> +    } else {
> +        address = pci_get_long(dev->config + msi_lower_address_reg(dev));
> +    }
> +
> +    /* upper bit 31:16 is zero */
> +    data = pci_get_word(dev->config + msi_data_reg(dev, is64bit));
> +    if (nr_vector > 1) {
> +        data &= ~(nr_vector - 1);
> +        data |= vector;
> +    }
> +
> +    MSI_DEV_PRINTF(dev,
> +                   "notify vector 0x%"PRIx8
> +                   " address: 0x%"PRIx64" data: 0x%"PRIx32"\n",
> +                   vector, address, data);
> +    stl_phys(address, data);
> +}
> +
> +/* call this function after updating configs by pci_default_write_config() */
> +void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len)
> +{
> +    uint16_t control = pci_get_word(dev->config + msi_control_reg(dev));
> +    bool is64bit = is_64bit_address(control);
> +    uint8_t nr_vector = msi_nr_vector(control);
> +
> +#ifdef MSI_DEBUG
> +    uint8_t cap_size = msi_cap_sizeof(control & (PCI_MSI_FLAGS_MASKBIT |
> +                                                 PCI_MSI_FLAGS_64BIT));
> +    if (ranges_overlap(addr, len, dev->msi_cap, cap_size)) {
> +        MSI_DEV_PRINTF(dev, "addr 0x%"PRIx32" val 0x%"PRIx32" len %d\n",
> +                       addr, val, len);
> +        MSI_DEV_PRINTF(dev, "ctrl: 0x%"PRIx16" address: 0x%"PRIx32,
> +                       control,
> +                       pci_get_long(dev->config + msi_lower_address_reg(dev)));
> +        if (is64bit) {
> +            fprintf(stderr, " addrss-hi: 0x%"PRIx32,
> +                    pci_get_long(dev->config + msi_upper_address_reg(dev)));
> +        }
> +        fprintf(stderr, " data: 0x%"PRIx16,
> +                pci_get_word(dev->config + msi_data_reg(dev, is64bit)));
> +        if (is_mask_bit_support(control)) {
> +            fprintf(stderr, " mask 0x%"PRIx32" pending 0x%"PRIx32,
> +                    pci_get_long(dev->config + msi_mask_reg(dev, is64bit)),
> +                    pci_get_long(dev->config + msi_pending_reg(dev, is64bit)));
> +        }
> +        fprintf(stderr, "\n");
> +    }
> +#endif
> +
> +    if (!msi_control_enabled(control)) {

Should not we clear pending bits here?

> +        return;
> +    }

We must also clear INTx# interrupts when msi is enabled.

> +
> +    if (!is_mask_bit_support(control)) {
> +        /* if per vector masking isn't support,
> +           there is no pending interrupt. */
> +        return;
> +    }
> +

I think we also need to clear pending bits if # of vectors
is reduced.

We need to also decide what to do if software tries to
enable more vectors than supported. I suggest setting
allocated vectors to nr_vectors in this case.


> +    if (range_covers_byte(addr, len, msi_control_reg(dev)) || /* MSI enable */
> +        ranges_overlap(addr, len, msi_mask_reg(dev, is64bit), 4)) {

I think it is better to reduce nesting.
Reverse the condition and return if this is not an msi write.

> +        uint32_t pending =
> +            pci_get_long(dev->config + msi_pending_reg(dev, is64bit));
> +        uint8_t vector;
> +
> +        /* deliver pending interrupts which are unmasked */
> +        for (vector = 0; vector < nr_vector; ++vector) {
> +            if (msi_is_masked(dev, vector) || !msi_test_bit(pending, vector)) {

I am confused. This is called after mask is updated, right?
So msi_is_masked means vector was masked, not unmasked?
I think the logic is reversed here.

> +                continue;
> +            }
> +
> +            msi_clear_bit(&pending, vector);
> +            pci_set_long(dev->config + msi_pending_reg(dev, is64bit), pending);
> +            msi_notify(dev, vector);
> +        }
> +    }
> +}
> +
> +uint8_t msi_nr_allocated_vector(const PCIDevice *dev)

rename msi_nr_vectors_allocated to match spec.

> +{
> +    uint16_t control = pci_get_word(dev->config + msi_control_reg(dev));
> +    return msi_nr_vector(control);
> +}
> diff --git a/hw/msi.h b/hw/msi.h
> new file mode 100644
> index 0000000..1131c6e
> --- /dev/null
> +++ b/hw/msi.h
> @@ -0,0 +1,41 @@
> +/*
> + * msi.h
> + *
> + * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
> + *                    VA Linux Systems Japan K.K.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> +
> + * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef QEMU_MSI_H
> +#define QEMU_MSI_H
> +
> +#include "qemu-common.h"
> +#include "pci.h"
> +
> +bool msi_enabled(const PCIDevice *dev);
> +int msi_init(struct PCIDevice *dev, uint8_t offset,
> +             uint8_t nr_vector, uint16_t flags);

So users need to encode flags? Where do they get them?
And to encode, we need registers internal to msi.c or duplicate ...
Would not it be better to pass msi_64_bit and msi_per_vector_mask?


> +void msi_uninit(struct PCIDevice *dev);
> +void msi_reset(PCIDevice *dev);
> +void msi_notify(PCIDevice *dev, uint8_t vector);
> +void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len);
> +uint8_t msi_nr_allocated_vector(const PCIDevice *dev);
> +
> +static inline bool msi_present(const PCIDevice *dev)
> +{
> +    return dev->cap_present & QEMU_PCI_CAP_MSI;
> +}
> +
> +#endif /* QEMU_MSI_H */
> diff --git a/hw/pci.h b/hw/pci.h
> index 2b4c318..9387a03 100644
> --- a/hw/pci.h
> +++ b/hw/pci.h
> @@ -115,6 +115,8 @@ enum {
>      /* multifunction capable device */
>  #define QEMU_PCI_CAP_MULTIFUNCTION_BITNR        2
>      QEMU_PCI_CAP_MULTIFUNCTION = (1 << QEMU_PCI_CAP_MULTIFUNCTION_BITNR),
> +
> +    QEMU_PCI_CAP_MSI = 0x4,

These are internal bits so we do not need to preserve any values.
Put this cap right before or right after MSIX please.

>  };
>  
>  struct PCIDevice {
> @@ -168,6 +170,9 @@ struct PCIDevice {
>      /* Version id needed for VMState */
>      int32_t version_id;
>  
> +    /* Offset of MSI capability in config space */
> +    uint8_t msi_cap;
> +
>      /* Location of option rom */
>      char *romfile;
>      ram_addr_t rom_offset;
> -- 
> 1.7.1.1

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

* [Qemu-devel] Re: [PATCH 01/14] RESEND apb: fix typo.
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 01/14] RESEND apb: fix typo Isaku Yamahata
@ 2010-09-06  9:46   ` Michael S. Tsirkin
  2010-09-06 17:54     ` Blue Swirl
  0 siblings, 1 reply; 25+ messages in thread
From: Michael S. Tsirkin @ 2010-09-06  9:46 UTC (permalink / raw)
  To: Isaku Yamahata; +Cc: skandasa, adhyas, etmartin, qemu-devel, wexu2

On Mon, Sep 06, 2010 at 04:46:15PM +0900, Isaku Yamahata wrote:
> fix typo.
> 
> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>

This is separate from the express patches, right?
I'll appply this. Thanks!

> ---
>  hw/apb_pci.c |    6 +++---
>  1 files changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/apb_pci.c b/hw/apb_pci.c
> index 10a5baa..c619112 100644
> --- a/hw/apb_pci.c
> +++ b/hw/apb_pci.c
> @@ -362,7 +362,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
>      /* APB secondary busses */
>      pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 0), true,
>                                     "pbm-bridge");
> -    br = DO_UPCAST(PCIBridge, dev, dev);
> +    br = DO_UPCAST(PCIBridge, dev, pci_dev);
>      pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 1",
>                         pci_apb_map_irq);
>      qdev_init_nofail(&pci_dev->qdev);
> @@ -370,7 +370,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
>  
>      pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 1), true,
>                                     "pbm-bridge");
> -    br = DO_UPCAST(PCIBridge, dev, dev);
> +    br = DO_UPCAST(PCIBridge, dev, pci_dev);
>      pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 2",
>                         pci_apb_map_irq);
>      qdev_init_nofail(&pci_dev->qdev);
> @@ -462,7 +462,7 @@ static PCIDeviceInfo pbm_pci_bridge_info = {
>      .qdev.name = "pbm-bridge",
>      .qdev.size = sizeof(PCIBridge),
>      .qdev.vmsd = &vmstate_pci_device,
> -    .qdev.reset = pci_brdige_reset,
> +    .qdev.reset = pci_bridge_reset,
>      .init = apb_pci_bridge_initfn,
>      .exit = pci_bridge_exitfn,
>      .config_write = pci_bridge_write_config,
> -- 
> 1.7.1.1

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

* [Qemu-devel] Re: [PATCH 00/14] pcie port switch emulators
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
                   ` (13 preceding siblings ...)
  2010-09-06  7:46 ` [Qemu-devel] [PATCH 14/14] pcie/aer: glue aer error injection into qemu monitor Isaku Yamahata
@ 2010-09-06 17:02 ` Wei Xu
  2010-09-07 17:14 ` Michael S. Tsirkin
  15 siblings, 0 replies; 25+ messages in thread
From: Wei Xu @ 2010-09-06 17:02 UTC (permalink / raw)
  To: Isaku Yamahata, qemu-devel, Wei Xu; +Cc: skandasa, adhyas, etmartin, mst

Isaku,

Thanks! Last week I did some merging to qemu-kvm and found similar issues
(qdev reset) and also ioapic/apic needs many changes. I may ask your help
this week...

Wei


On 9/6/10 12:46 AM, "Isaku Yamahata" <yamahata@valinux.co.jp> wrote:

> This patch series implements pcie port switch emulators
> which is basic part for pcie/q35 support.
> This is for mst/pci tree.
> 
> some random comments
> - pci bus reset
>   As Anthony is cleaning up qdev reset stuff, so pci bus reset code
>   is commented out. Once the qdev clean up is done, the patch that
>   enabled pci bus reset will be sent.
> 
> - vid/did
>   there are arbitrariness for pcie port switch. I just choose
>   Intel and IT one to model them.
> 
> thanks,
> 
> Isaku Yamahata (14):
>   RESEND apb: fix typo.
>   pci: consolidate pci_add_capability_at_offset() into
>     pci_add_capability().
>   pci bridge: add helper function for ssvid capability.
>   pci: call hotplug callback even when not hotplug case for later use.
>   pci: make pci_parse_devfn() aware of func.
>   pci_ids.h: add vendor id of Texus Intesruments.
>   msi: implemented msi.
>   pcie: helper functions for pcie extended capability.
>   pcie port: define struct PCIEPort/PCIESlot and helper functions
>   pcie root port: implement pcie root port.
>   pcie upstream port: pci express switch upstream port.
>   pcie downstream port: pci express switch downstream port.
>   pcie/hotplug: glue pushing attention button command. pcie_abp
>   pcie/aer: glue aer error injection into qemu monitor.
> 
>  Makefile.objs        |    6 +-
>  hw/acpi_piix4.c      |    3 +
>  hw/apb_pci.c         |    6 +-
>  hw/eepro100.c        |    4 +-
>  hw/msi.c             |  362 +++++++++++
>  hw/msi.h             |   41 ++
>  hw/msix.c            |    3 +-
>  hw/pci.c             |   70 ++-
>  hw/pci.h             |   36 +-
>  hw/pci_bridge.c      |   19 +
>  hw/pci_bridge.h      |    3 +
>  hw/pci_ids.h         |    2 +
>  hw/pcie.c            | 1753
> ++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/pcie.h            |  186 ++++++
>  hw/pcie_downstream.c |  224 +++++++
>  hw/pcie_downstream.h |   33 +
>  hw/pcie_port.c       |  188 ++++++
>  hw/pcie_port.h       |   51 ++
>  hw/pcie_root.c       |  247 +++++++
>  hw/pcie_root.h       |   32 +
>  hw/pcie_upstream.c   |  206 ++++++
>  hw/pcie_upstream.h   |   32 +
>  qemu-common.h        |    3 +
>  qemu-monitor.hx      |   36 +
>  sysemu.h             |    9 +
>  25 files changed, 3520 insertions(+), 35 deletions(-)
>  create mode 100644 hw/msi.c
>  create mode 100644 hw/msi.h
>  create mode 100644 hw/pcie.c
>  create mode 100644 hw/pcie.h
>  create mode 100644 hw/pcie_downstream.c
>  create mode 100644 hw/pcie_downstream.h
>  create mode 100644 hw/pcie_port.c
>  create mode 100644 hw/pcie_port.h
>  create mode 100644 hw/pcie_root.c
>  create mode 100644 hw/pcie_root.h
>  create mode 100644 hw/pcie_upstream.c
>  create mode 100644 hw/pcie_upstream.h
> 

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

* Re: [Qemu-devel] Re: [PATCH 01/14] RESEND apb: fix typo.
  2010-09-06  9:46   ` [Qemu-devel] " Michael S. Tsirkin
@ 2010-09-06 17:54     ` Blue Swirl
  2010-09-06 19:55       ` Michael S. Tsirkin
  0 siblings, 1 reply; 25+ messages in thread
From: Blue Swirl @ 2010-09-06 17:54 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: skandasa, etmartin, wexu2, adhyas, qemu-devel, Isaku Yamahata

On Mon, Sep 6, 2010 at 9:46 AM, Michael S. Tsirkin <mst@redhat.com> wrote:
> On Mon, Sep 06, 2010 at 04:46:15PM +0900, Isaku Yamahata wrote:
>> fix typo.
>>
>> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
>
> This is separate from the express patches, right?
> I'll appply this. Thanks!

Since the patch that introduces the build breakage (pci_brdige_reset)
has not been committed to master yet, can you instead drop that commit
and introduce a fixed version? This will avoid problems with git
bisect.

>
>> ---
>>  hw/apb_pci.c |    6 +++---
>>  1 files changed, 3 insertions(+), 3 deletions(-)
>>
>> diff --git a/hw/apb_pci.c b/hw/apb_pci.c
>> index 10a5baa..c619112 100644
>> --- a/hw/apb_pci.c
>> +++ b/hw/apb_pci.c
>> @@ -362,7 +362,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
>>      /* APB secondary busses */
>>      pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 0), true,
>>                                     "pbm-bridge");
>> -    br = DO_UPCAST(PCIBridge, dev, dev);
>> +    br = DO_UPCAST(PCIBridge, dev, pci_dev);
>>      pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 1",
>>                         pci_apb_map_irq);
>>      qdev_init_nofail(&pci_dev->qdev);
>> @@ -370,7 +370,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
>>
>>      pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 1), true,
>>                                     "pbm-bridge");
>> -    br = DO_UPCAST(PCIBridge, dev, dev);
>> +    br = DO_UPCAST(PCIBridge, dev, pci_dev);
>>      pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 2",
>>                         pci_apb_map_irq);
>>      qdev_init_nofail(&pci_dev->qdev);
>> @@ -462,7 +462,7 @@ static PCIDeviceInfo pbm_pci_bridge_info = {
>>      .qdev.name = "pbm-bridge",
>>      .qdev.size = sizeof(PCIBridge),
>>      .qdev.vmsd = &vmstate_pci_device,
>> -    .qdev.reset = pci_brdige_reset,
>> +    .qdev.reset = pci_bridge_reset,
>>      .init = apb_pci_bridge_initfn,
>>      .exit = pci_bridge_exitfn,
>>      .config_write = pci_bridge_write_config,
>> --
>> 1.7.1.1
>
>

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

* Re: [Qemu-devel] Re: [PATCH 01/14] RESEND apb: fix typo.
  2010-09-06 17:54     ` Blue Swirl
@ 2010-09-06 19:55       ` Michael S. Tsirkin
  2010-09-06 22:59         ` Isaku Yamahata
  0 siblings, 1 reply; 25+ messages in thread
From: Michael S. Tsirkin @ 2010-09-06 19:55 UTC (permalink / raw)
  To: Blue Swirl; +Cc: skandasa, etmartin, wexu2, adhyas, qemu-devel, Isaku Yamahata

On Mon, Sep 06, 2010 at 05:54:25PM +0000, Blue Swirl wrote:
> On Mon, Sep 6, 2010 at 9:46 AM, Michael S. Tsirkin <mst@redhat.com> wrote:
> > On Mon, Sep 06, 2010 at 04:46:15PM +0900, Isaku Yamahata wrote:
> >> fix typo.
> >>
> >> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
> >
> > This is separate from the express patches, right?
> > I'll appply this. Thanks!
> 
> Since the patch that introduces the build breakage (pci_brdige_reset)
> has not been committed to master yet, can you instead drop that commit
> and introduce a fixed version? This will avoid problems with git
> bisect.

Sure, thanks for pointing this out. Isaku, could
you just repost a fixed up patchset? Working git bisect is nice to have.

> >
> >> ---
> >>  hw/apb_pci.c |    6 +++---
> >>  1 files changed, 3 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/hw/apb_pci.c b/hw/apb_pci.c
> >> index 10a5baa..c619112 100644
> >> --- a/hw/apb_pci.c
> >> +++ b/hw/apb_pci.c
> >> @@ -362,7 +362,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
> >>      /* APB secondary busses */
> >>      pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 0), true,
> >>                                     "pbm-bridge");
> >> -    br = DO_UPCAST(PCIBridge, dev, dev);
> >> +    br = DO_UPCAST(PCIBridge, dev, pci_dev);
> >>      pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 1",
> >>                         pci_apb_map_irq);
> >>      qdev_init_nofail(&pci_dev->qdev);
> >> @@ -370,7 +370,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
> >>
> >>      pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 1), true,
> >>                                     "pbm-bridge");
> >> -    br = DO_UPCAST(PCIBridge, dev, dev);
> >> +    br = DO_UPCAST(PCIBridge, dev, pci_dev);
> >>      pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 2",
> >>                         pci_apb_map_irq);
> >>      qdev_init_nofail(&pci_dev->qdev);
> >> @@ -462,7 +462,7 @@ static PCIDeviceInfo pbm_pci_bridge_info = {
> >>      .qdev.name = "pbm-bridge",
> >>      .qdev.size = sizeof(PCIBridge),
> >>      .qdev.vmsd = &vmstate_pci_device,
> >> -    .qdev.reset = pci_brdige_reset,
> >> +    .qdev.reset = pci_bridge_reset,
> >>      .init = apb_pci_bridge_initfn,
> >>      .exit = pci_bridge_exitfn,
> >>      .config_write = pci_bridge_write_config,
> >> --
> >> 1.7.1.1
> >
> >

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

* Re: [Qemu-devel] Re: [PATCH 01/14] RESEND apb: fix typo.
  2010-09-06 19:55       ` Michael S. Tsirkin
@ 2010-09-06 22:59         ` Isaku Yamahata
  0 siblings, 0 replies; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-06 22:59 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: skandasa, etmartin, wexu2, adhyas, qemu-devel, Blue Swirl

On Mon, Sep 06, 2010 at 10:55:19PM +0300, Michael S. Tsirkin wrote:
> On Mon, Sep 06, 2010 at 05:54:25PM +0000, Blue Swirl wrote:
> > On Mon, Sep 6, 2010 at 9:46 AM, Michael S. Tsirkin <mst@redhat.com> wrote:
> > > On Mon, Sep 06, 2010 at 04:46:15PM +0900, Isaku Yamahata wrote:
> > >> fix typo.
> > >>
> > >> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
> > >
> > > This is separate from the express patches, right?
> > > I'll appply this. Thanks!
> > 
> > Since the patch that introduces the build breakage (pci_brdige_reset)
> > has not been committed to master yet, can you instead drop that commit
> > and introduce a fixed version? This will avoid problems with git
> > bisect.
> 
> Sure, thanks for pointing this out. Isaku, could
> you just repost a fixed up patchset? Working git bisect is nice to have.

I should have included it in the commit log.

12bc74dfc1fe8cc57060a9eb94d0e9884c5c445e
Fortunately it's the head of pci branch.

thanks,

> 
> > >
> > >> ---
> > >> ?hw/apb_pci.c | ? ?6 +++---
> > >> ?1 files changed, 3 insertions(+), 3 deletions(-)
> > >>
> > >> diff --git a/hw/apb_pci.c b/hw/apb_pci.c
> > >> index 10a5baa..c619112 100644
> > >> --- a/hw/apb_pci.c
> > >> +++ b/hw/apb_pci.c
> > >> @@ -362,7 +362,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
> > >> ? ? ?/* APB secondary busses */
> > >> ? ? ?pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 0), true,
> > >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "pbm-bridge");
> > >> - ? ?br = DO_UPCAST(PCIBridge, dev, dev);
> > >> + ? ?br = DO_UPCAST(PCIBridge, dev, pci_dev);
> > >> ? ? ?pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 1",
> > >> ? ? ? ? ? ? ? ? ? ? ? ? pci_apb_map_irq);
> > >> ? ? ?qdev_init_nofail(&pci_dev->qdev);
> > >> @@ -370,7 +370,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
> > >>
> > >> ? ? ?pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 1), true,
> > >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "pbm-bridge");
> > >> - ? ?br = DO_UPCAST(PCIBridge, dev, dev);
> > >> + ? ?br = DO_UPCAST(PCIBridge, dev, pci_dev);
> > >> ? ? ?pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 2",
> > >> ? ? ? ? ? ? ? ? ? ? ? ? pci_apb_map_irq);
> > >> ? ? ?qdev_init_nofail(&pci_dev->qdev);
> > >> @@ -462,7 +462,7 @@ static PCIDeviceInfo pbm_pci_bridge_info = {
> > >> ? ? ?.qdev.name = "pbm-bridge",
> > >> ? ? ?.qdev.size = sizeof(PCIBridge),
> > >> ? ? ?.qdev.vmsd = &vmstate_pci_device,
> > >> - ? ?.qdev.reset = pci_brdige_reset,
> > >> + ? ?.qdev.reset = pci_bridge_reset,
> > >> ? ? ?.init = apb_pci_bridge_initfn,
> > >> ? ? ?.exit = pci_bridge_exitfn,
> > >> ? ? ?.config_write = pci_bridge_write_config,
> > >> --
> > >> 1.7.1.1
> > >
> > >
> 

-- 
yamahata

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

* [Qemu-devel] Re: [PATCH 00/14] pcie port switch emulators
  2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
                   ` (14 preceding siblings ...)
  2010-09-06 17:02 ` [Qemu-devel] Re: [PATCH 00/14] pcie port switch emulators Wei Xu
@ 2010-09-07 17:14 ` Michael S. Tsirkin
  15 siblings, 0 replies; 25+ messages in thread
From: Michael S. Tsirkin @ 2010-09-07 17:14 UTC (permalink / raw)
  To: Isaku Yamahata; +Cc: skandasa, adhyas, etmartin, qemu-devel, wexu2

On Mon, Sep 06, 2010 at 04:46:14PM +0900, Isaku Yamahata wrote:
> This patch series implements pcie port switch emulators
> which is basic part for pcie/q35 support.
> This is for mst/pci tree.
> 
> some random comments
> - pci bus reset
>   As Anthony is cleaning up qdev reset stuff, so pci bus reset code
>   is commented out. Once the qdev clean up is done, the patch that
>   enabled pci bus reset will be sent.
> 
> - vid/did
>   there are arbitrariness for pcie port switch. I just choose
>   Intel and IT one to model them.
> 
> thanks,
> 
> Isaku Yamahata (14):
>   RESEND apb: fix typo.
>   pci: consolidate pci_add_capability_at_offset() into
>     pci_add_capability().
>   pci bridge: add helper function for ssvid capability.
>   pci: call hotplug callback even when not hotplug case for later use.
>   pci: make pci_parse_devfn() aware of func.
>   pci_ids.h: add vendor id of Texus Intesruments.

OK, I pushed the above to the pci branch, and folded
the first patch into the previous one.
Didn't have time to test it yet, but I thought I'd push it out already
to reduce mailing-list churn.

>   msi: implemented msi.

I went over this and posted some comments.

>   pcie: helper functions for pcie extended capability.
>   pcie port: define struct PCIEPort/PCIESlot and helper functions
>   pcie root port: implement pcie root port.
>   pcie upstream port: pci express switch upstream port.
>   pcie downstream port: pci express switch downstream port.
>   pcie/hotplug: glue pushing attention button command. pcie_abp
>   pcie/aer: glue aer error injection into qemu monitor.

Haven't reviewed the above yet. Are there bits that don't depend on msi?

>  Makefile.objs        |    6 +-
>  hw/acpi_piix4.c      |    3 +
>  hw/apb_pci.c         |    6 +-
>  hw/eepro100.c        |    4 +-
>  hw/msi.c             |  362 +++++++++++
>  hw/msi.h             |   41 ++
>  hw/msix.c            |    3 +-
>  hw/pci.c             |   70 ++-
>  hw/pci.h             |   36 +-
>  hw/pci_bridge.c      |   19 +
>  hw/pci_bridge.h      |    3 +
>  hw/pci_ids.h         |    2 +
>  hw/pcie.c            | 1753 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/pcie.h            |  186 ++++++
>  hw/pcie_downstream.c |  224 +++++++
>  hw/pcie_downstream.h |   33 +
>  hw/pcie_port.c       |  188 ++++++
>  hw/pcie_port.h       |   51 ++
>  hw/pcie_root.c       |  247 +++++++
>  hw/pcie_root.h       |   32 +
>  hw/pcie_upstream.c   |  206 ++++++
>  hw/pcie_upstream.h   |   32 +
>  qemu-common.h        |    3 +
>  qemu-monitor.hx      |   36 +
>  sysemu.h             |    9 +
>  25 files changed, 3520 insertions(+), 35 deletions(-)
>  create mode 100644 hw/msi.c
>  create mode 100644 hw/msi.h
>  create mode 100644 hw/pcie.c
>  create mode 100644 hw/pcie.h
>  create mode 100644 hw/pcie_downstream.c
>  create mode 100644 hw/pcie_downstream.h
>  create mode 100644 hw/pcie_port.c
>  create mode 100644 hw/pcie_port.h
>  create mode 100644 hw/pcie_root.c
>  create mode 100644 hw/pcie_root.h
>  create mode 100644 hw/pcie_upstream.c
>  create mode 100644 hw/pcie_upstream.h

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

* [Qemu-devel] Re: [PATCH 07/14] msi: implemented msi.
  2010-09-06  9:44   ` [Qemu-devel] " Michael S. Tsirkin
@ 2010-09-08  7:43     ` Isaku Yamahata
  2010-09-08  7:51       ` Michael S. Tsirkin
  0 siblings, 1 reply; 25+ messages in thread
From: Isaku Yamahata @ 2010-09-08  7:43 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: skandasa, adhyas, etmartin, qemu-devel, wexu2

Thank you for through review.

On Mon, Sep 06, 2010 at 12:44:16PM +0300, Michael S. Tsirkin wrote:
> > +        uint32_t pending =
> > +            pci_get_long(dev->config + msi_pending_reg(dev, is64bit));
> > +        uint8_t vector;
> > +
> > +        /* deliver pending interrupts which are unmasked */
> > +        for (vector = 0; vector < nr_vector; ++vector) {
> > +            if (msi_is_masked(dev, vector) || !msi_test_bit(pending, vector)) {
> 
> I am confused. This is called after mask is updated, right?
> So msi_is_masked means vector was masked, not unmasked?
> I think the logic is reversed here.

I suppose you were missing the following continue.


> > +                continue;
                     ^^^^^^^^^ Here

> > +            }
> > +
> > +            msi_clear_bit(&pending, vector);
> > +            pci_set_long(dev->config + msi_pending_reg(dev, is64bit), pending);
> > +            msi_notify(dev, vector);
> > +        }
> > +    }
> > +}
> > +
> > +uint8_t msi_nr_allocated_vector(const PCIDevice *dev)

-- 
yamahata

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

* [Qemu-devel] Re: [PATCH 07/14] msi: implemented msi.
  2010-09-08  7:43     ` Isaku Yamahata
@ 2010-09-08  7:51       ` Michael S. Tsirkin
  0 siblings, 0 replies; 25+ messages in thread
From: Michael S. Tsirkin @ 2010-09-08  7:51 UTC (permalink / raw)
  To: Isaku Yamahata; +Cc: skandasa, adhyas, etmartin, qemu-devel, wexu2

On Wed, Sep 08, 2010 at 04:43:27PM +0900, Isaku Yamahata wrote:
> Thank you for through review.
> 
> On Mon, Sep 06, 2010 at 12:44:16PM +0300, Michael S. Tsirkin wrote:
> > > +        uint32_t pending =
> > > +            pci_get_long(dev->config + msi_pending_reg(dev, is64bit));
> > > +        uint8_t vector;
> > > +
> > > +        /* deliver pending interrupts which are unmasked */
> > > +        for (vector = 0; vector < nr_vector; ++vector) {
> > > +            if (msi_is_masked(dev, vector) || !msi_test_bit(pending, vector)) {
> > 
> > I am confused. This is called after mask is updated, right?
> > So msi_is_masked means vector was masked, not unmasked?
> > I think the logic is reversed here.
> 
> I suppose you were missing the following continue.
> 
> 
> > > +                continue;
>                      ^^^^^^^^^ Here

I see. You are right. The block is small enough to maybe make it worthwhile
to revert the logic and avoid continue. It's up to you though,
there's no bug here.

> > > +            }
> > > +
> > > +            msi_clear_bit(&pending, vector);
> > > +            pci_set_long(dev->config + msi_pending_reg(dev, is64bit), pending);
> > > +            msi_notify(dev, vector);
> > > +        }
> > > +    }
> > > +}
> > > +
> > > +uint8_t msi_nr_allocated_vector(const PCIDevice *dev)
> 
> -- 
> yamahata

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

end of thread, other threads:[~2010-09-08  7:58 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-06  7:46 [Qemu-devel] [PATCH 00/14] pcie port switch emulators Isaku Yamahata
2010-09-06  7:46 ` [Qemu-devel] [PATCH 01/14] RESEND apb: fix typo Isaku Yamahata
2010-09-06  9:46   ` [Qemu-devel] " Michael S. Tsirkin
2010-09-06 17:54     ` Blue Swirl
2010-09-06 19:55       ` Michael S. Tsirkin
2010-09-06 22:59         ` Isaku Yamahata
2010-09-06  7:46 ` [Qemu-devel] [PATCH 02/14] pci: consolidate pci_add_capability_at_offset() into pci_add_capability() Isaku Yamahata
2010-09-06  8:10   ` [Qemu-devel] " Michael S. Tsirkin
2010-09-06  7:46 ` [Qemu-devel] [PATCH 03/14] pci bridge: add helper function for ssvid capability Isaku Yamahata
2010-09-06  7:46 ` [Qemu-devel] [PATCH 04/14] pci: call hotplug callback even when not hotplug case for later use Isaku Yamahata
2010-09-06  7:46 ` [Qemu-devel] [PATCH 05/14] pci: make pci_parse_devfn() aware of func Isaku Yamahata
2010-09-06  7:46 ` [Qemu-devel] [PATCH 06/14] pci_ids.h: add vendor id of Texus Intesruments Isaku Yamahata
2010-09-06  7:46 ` [Qemu-devel] [PATCH 07/14] msi: implemented msi Isaku Yamahata
2010-09-06  9:44   ` [Qemu-devel] " Michael S. Tsirkin
2010-09-08  7:43     ` Isaku Yamahata
2010-09-08  7:51       ` Michael S. Tsirkin
2010-09-06  7:46 ` [Qemu-devel] [PATCH 08/14] pcie: helper functions for pcie extended capability Isaku Yamahata
2010-09-06  7:46 ` [Qemu-devel] [PATCH 09/14] pcie port: define struct PCIEPort/PCIESlot and helper functions Isaku Yamahata
2010-09-06  7:46 ` [Qemu-devel] [PATCH 10/14] pcie root port: implement pcie root port Isaku Yamahata
2010-09-06  7:46 ` [Qemu-devel] [PATCH 11/14] pcie upstream port: pci express switch upstream port Isaku Yamahata
2010-09-06  7:46 ` [Qemu-devel] [PATCH 12/14] pcie downstream port: pci express switch downstream port Isaku Yamahata
2010-09-06  7:46 ` [Qemu-devel] [PATCH 13/14] pcie/hotplug: glue pushing attention button command. pcie_abp Isaku Yamahata
2010-09-06  7:46 ` [Qemu-devel] [PATCH 14/14] pcie/aer: glue aer error injection into qemu monitor Isaku Yamahata
2010-09-06 17:02 ` [Qemu-devel] Re: [PATCH 00/14] pcie port switch emulators Wei Xu
2010-09-07 17:14 ` Michael S. Tsirkin

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.