* [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators
@ 2010-10-18 3:17 Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 01/15] pci: make pci_del_capability() update for w1cmask Isaku Yamahata
` (15 more replies)
0 siblings, 16 replies; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
Here is v4 of the pcie patch series.
- PCIDevice::written
The abuse of PCIDevice::config to record a written bit of non-modifiable
registers is confusing and bad style. So I introduced PCIDevice::written.
- FLR stuff
I didn't addresse FLR function pointer issue. Anyway in order to
make FLR work, Qdev bus reset must be addresssed.
So Let's discuss it at the next phase.
new patches: 1, 2, 3, 4, 5
Other patches are (almost) same as before except adjustment to compile.
The patches of 1, 2, 3 and 5 can be harmlessly merged, I think.
Patch description:
This patch series implements pcie port switch emulators
which is basic part for pcie/q35 support.
This is for mst/pci tree.
changes v3 -> v4:
- introduced new pci config helper functions.(clear set bit)
- various clean up and some bug fixes.
- dropped pci_shift_xxx().
- dropped function pointerin pcie_aer.h
- dropped pci_exp_cap(), pcie_aer_cap().
- file rename (pcie_{root, upstream, downsatrem} => ioh33420, x3130).
changes v2 -> v3:
- msi: improved commant and simplified shift/ffs dance
- pci w1c config register framework
- split pcie.[ch] into pcie_regs.h, pcie.[ch] and pcie_aer.[ch]
- pcie, aer: many changes by following reviews.
changes v1 -> v2:
- update msi
- dropped already pushed out patches.
- added msix patches.
Isaku Yamahata (15):
pci: make pci_del_capability() update for w1cmask
pci: introduce helper functions to clear/set bits in configuration
space
pci: use pci_clear_bit_word() in pci_device_reset()
pci: record which is written into pci configuration space
pci/bridge: fix pci_bridge_reset()
msi: implements msi
pcie: add pcie constants to pcie_regs.h
pcie: helper functions for pcie capability and extended capability
pcie/aer: helper functions for pcie aer capability
pcie port: define struct PCIEPort/PCIESlot and helper functions
ioh3420: pcie root port in X58 ioh
x3130: pcie upstream port
x3130: pcie downstream port
pcie/hotplug: introduce pushing attention button command
pcie/aer: glue aer error injection into qemu monitor
Makefile.objs | 4 +-
hw/ioh3420.c | 228 ++++++++++++
hw/ioh3420.h | 10 +
hw/msi.c | 352 +++++++++++++++++++
hw/msi.h | 41 +++
hw/pci.c | 16 +-
hw/pci.h | 92 +++++-
hw/pci_bridge.c | 15 +-
hw/pcie.c | 529 ++++++++++++++++++++++++++++
hw/pcie.h | 112 ++++++
hw/pcie_aer.c | 873 +++++++++++++++++++++++++++++++++++++++++++++++
hw/pcie_aer.h | 105 ++++++
hw/pcie_port.c | 197 +++++++++++
hw/pcie_port.h | 51 +++
hw/pcie_regs.h | 154 +++++++++
hw/xio3130_downstream.c | 207 +++++++++++
hw/xio3130_downstream.h | 11 +
hw/xio3130_upstream.c | 188 ++++++++++
hw/xio3130_upstream.h | 10 +
qemu-common.h | 6 +
qemu-monitor.hx | 36 ++
sysemu.h | 9 +
22 files changed, 3233 insertions(+), 13 deletions(-)
create mode 100644 hw/ioh3420.c
create mode 100644 hw/ioh3420.h
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_aer.c
create mode 100644 hw/pcie_aer.h
create mode 100644 hw/pcie_port.c
create mode 100644 hw/pcie_port.h
create mode 100644 hw/pcie_regs.h
create mode 100644 hw/xio3130_downstream.c
create mode 100644 hw/xio3130_downstream.h
create mode 100644 hw/xio3130_upstream.c
create mode 100644 hw/xio3130_upstream.h
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH v4 01/15] pci: make pci_del_capability() update for w1cmask
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 6:06 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 02/15] pci: introduce helper functions to clear/set bits in configuration space Isaku Yamahata
` (14 subsequent siblings)
15 siblings, 1 reply; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
When deleting pci capability, w1cmask should be 0
to make those registers writablein addition to wmask.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
hw/pci.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/hw/pci.c b/hw/pci.c
index abddc6d..e3462a9 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -1745,6 +1745,7 @@ void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT];
/* Make capability writeable again */
memset(pdev->wmask + offset, 0xff, size);
+ memset(pdev->w1cmask + offset, 0, size);
/* Clear cmask as device-specific registers can't be checked */
memset(pdev->cmask + offset, 0, size);
memset(pdev->used + offset, 0, size);
--
1.7.1.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH v4 02/15] pci: introduce helper functions to clear/set bits in configuration space
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 01/15] pci: make pci_del_capability() update for w1cmask Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 5:42 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 6:32 ` Michael S. Tsirkin
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 03/15] pci: use pci_clear_bit_word() in pci_device_reset() Isaku Yamahata
` (13 subsequent siblings)
15 siblings, 2 replies; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
This patch introduces helper functions to clear/set bits in configuration
space. pci_{clear_set, clear, set}_bit_{byte, word, long, quad}().
They will be used later.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
hw/pci.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 72 insertions(+), 0 deletions(-)
diff --git a/hw/pci.h b/hw/pci.h
index d8b399f..eafa9f3 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -323,6 +323,78 @@ pci_config_set_interrupt_pin(uint8_t *pci_config, uint8_t val)
pci_set_byte(&pci_config[PCI_INTERRUPT_PIN], val);
}
+static inline void
+pci_clear_set_bit_byte(uint8_t *config, uint8_t clear, uint8_t set)
+{
+ pci_set_byte(config, (pci_get_byte(config) & ~clear) | set);
+}
+
+static inline void
+pci_clear_bit_byte(uint8_t *config, uint8_t clear)
+{
+ pci_clear_set_bit_byte(config, clear, 0);
+}
+
+static inline void
+pci_set_bit_byte(uint8_t *config, uint8_t set)
+{
+ pci_clear_set_bit_byte(config, 0, set);
+}
+
+static inline void
+pci_clear_set_bit_word(uint8_t *config, uint16_t clear, uint16_t set)
+{
+ pci_set_word(config, (pci_get_word(config) & ~clear) | set);
+}
+
+static inline void
+pci_clear_bit_word(uint8_t *config, uint16_t clear)
+{
+ pci_clear_set_bit_word(config, clear, 0);
+}
+
+static inline void
+pci_set_bit_word(uint8_t *config, uint16_t set)
+{
+ pci_clear_set_bit_word(config, 0, set);
+}
+
+static inline void
+pci_clear_set_bit_long(uint8_t *config, uint32_t clear, uint32_t set)
+{
+ pci_set_long(config, (pci_get_long(config) & ~clear) | set);
+}
+
+static inline void
+pci_clear_bit_long(uint8_t *config, uint32_t clear)
+{
+ pci_clear_set_bit_long(config, clear, 0);
+}
+
+static inline void
+pci_set_bit_long(uint8_t *config, uint32_t set)
+{
+ pci_clear_set_bit_long(config, 0, set);
+}
+
+static inline void
+pci_clear_set_bit_quad(uint8_t *config, uint64_t clear, uint64_t set)
+{
+ pci_set_quad(config, (pci_get_quad(config) & ~clear) | set);
+}
+
+static inline void
+pci_clear_bit_quad(uint8_t *config, uint64_t clear)
+{
+ pci_clear_set_bit_quad(config, clear, 0);
+}
+
+static inline void
+pci_set_bit_quad(uint8_t *config, uint64_t set)
+{
+ pci_clear_set_bit_quad(config, 0, set);
+}
+
typedef int (*pci_qdev_initfn)(PCIDevice *dev);
typedef struct {
DeviceInfo qdev;
--
1.7.1.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH v4 03/15] pci: use pci_clear_bit_word() in pci_device_reset()
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 01/15] pci: make pci_del_capability() update for w1cmask Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 02/15] pci: introduce helper functions to clear/set bits in configuration space Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 04/15] pci: record which is written into pci configuration space Isaku Yamahata
` (12 subsequent siblings)
15 siblings, 0 replies; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
use pci_clear_bit_word() in pci_device_reset() where appropriate.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
hw/pci.c | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/hw/pci.c b/hw/pci.c
index e3462a9..5954476 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -137,9 +137,8 @@ static void pci_device_reset(PCIDevice *dev)
dev->irq_state = 0;
pci_update_irq_status(dev);
/* Clear all writeable bits */
- pci_set_word(dev->config + PCI_COMMAND,
- pci_get_word(dev->config + PCI_COMMAND) &
- ~pci_get_word(dev->wmask + PCI_COMMAND));
+ pci_clear_bit_word(dev->config + PCI_COMMAND,
+ pci_get_word(dev->wmask + PCI_COMMAND));
dev->config[PCI_CACHE_LINE_SIZE] = 0x0;
dev->config[PCI_INTERRUPT_LINE] = 0x0;
for (r = 0; r < PCI_NUM_REGIONS; ++r) {
--
1.7.1.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH v4 04/15] pci: record which is written into pci configuration space
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
` (2 preceding siblings ...)
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 03/15] pci: use pci_clear_bit_word() in pci_device_reset() Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 5:38 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 05/15] pci/bridge: fix pci_bridge_reset() Isaku Yamahata
` (11 subsequent siblings)
15 siblings, 1 reply; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
record which is written into pci configuration space.
introduce helper function to zero PCIDevice::written.
They will be used later.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
hw/pci.c | 10 ++++++++++
hw/pci.h | 5 +++++
2 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/hw/pci.c b/hw/pci.c
index 5954476..eca9324 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -627,6 +627,7 @@ static void pci_config_alloc(PCIDevice *pci_dev)
pci_dev->cmask = qemu_mallocz(config_size);
pci_dev->wmask = qemu_mallocz(config_size);
pci_dev->w1cmask = qemu_mallocz(config_size);
+ pci_dev->written = qemu_mallocz(config_size);
pci_dev->used = qemu_mallocz(config_size);
}
@@ -636,6 +637,7 @@ static void pci_config_free(PCIDevice *pci_dev)
qemu_free(pci_dev->cmask);
qemu_free(pci_dev->wmask);
qemu_free(pci_dev->w1cmask);
+ qemu_free(pci_dev->written);
qemu_free(pci_dev->used);
}
@@ -1002,6 +1004,8 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
assert(!(wmask & w1cmask));
d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask);
d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */
+ d->written[addr + i] = val; /* record what is written for driver
+ specific code */
}
if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) ||
ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) ||
@@ -1013,6 +1017,12 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
pci_update_irq_disabled(d, was_irq_disabled);
}
+void pci_clear_written_write_config(PCIDevice *d,
+ uint32_t addr, uint32_t val, int l)
+{
+ memset(d->written + addr, 0, l);
+}
+
/***********************************************************/
/* generic PCI irq support */
diff --git a/hw/pci.h b/hw/pci.h
index eafa9f3..7097817 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -132,6 +132,9 @@ struct PCIDevice {
/* Used to implement RW1C(Write 1 to Clear) bytes */
uint8_t *w1cmask;
+ /* Used to record what value is written */
+ uint8_t *written;
+
/* Used to allocate config space for capabilities. */
uint8_t *used;
@@ -200,6 +203,8 @@ uint32_t pci_default_read_config(PCIDevice *d,
uint32_t address, int len);
void pci_default_write_config(PCIDevice *d,
uint32_t address, uint32_t val, int len);
+void pci_clear_written_write_config(PCIDevice *d,
+ uint32_t addr, uint32_t val, int l);
void pci_device_save(PCIDevice *s, QEMUFile *f);
int pci_device_load(PCIDevice *s, QEMUFile *f);
--
1.7.1.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH v4 05/15] pci/bridge: fix pci_bridge_reset()
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
` (3 preceding siblings ...)
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 04/15] pci: record which is written into pci configuration space Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 6:22 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 06/15] msi: implements msi Isaku Yamahata
` (10 subsequent siblings)
15 siblings, 1 reply; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
lower 4bits of base/limit register is RO, and
should not be modified on reset.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
hw/pci_bridge.c | 15 +++++++++------
1 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c
index 638e3b3..7229610 100644
--- a/hw/pci_bridge.c
+++ b/hw/pci_bridge.c
@@ -161,12 +161,15 @@ void pci_bridge_reset_reg(PCIDevice *dev)
conf[PCI_SUBORDINATE_BUS] = 0;
conf[PCI_SEC_LATENCY_TIMER] = 0;
- conf[PCI_IO_BASE] = 0;
- conf[PCI_IO_LIMIT] = 0;
- pci_set_word(conf + PCI_MEMORY_BASE, 0);
- pci_set_word(conf + PCI_MEMORY_LIMIT, 0);
- pci_set_word(conf + PCI_PREF_MEMORY_BASE, 0);
- pci_set_word(conf + PCI_PREF_MEMORY_LIMIT, 0);
+ pci_clear_bit_byte(conf + PCI_IO_BASE, PCI_IO_RANGE_MASK & 0xff);
+ pci_clear_bit_byte(conf + PCI_IO_LIMIT, PCI_IO_RANGE_MASK & 0xff);
+ pci_clear_bit_word(conf + PCI_MEMORY_BASE, PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_clear_bit_word(conf + PCI_MEMORY_LIMIT,
+ PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_clear_bit_word(conf + PCI_PREF_MEMORY_BASE,
+ PCI_PREF_RANGE_MASK & 0xffff);
+ pci_clear_bit_word(conf + PCI_PREF_MEMORY_LIMIT,
+ PCI_PREF_RANGE_MASK & 0xffff);
pci_set_word(conf + PCI_PREF_BASE_UPPER32, 0);
pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0);
--
1.7.1.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH v4 06/15] msi: implements msi
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
` (4 preceding siblings ...)
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 05/15] pci/bridge: fix pci_bridge_reset() Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 07/15] pcie: add pcie constants to pcie_regs.h Isaku Yamahata
` (9 subsequent siblings)
15 siblings, 0 replies; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
implements msi related functions.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
Changes v3 -> v4:
- use pci_set_bit_xxx helper function.
- make nr_vectors, vector unsigned int.
- introduce PCI_MSI_VECTORS_MAX.
- fix undefined bit operations.
- eliminate msi_set_pending().
Changes v2 -> v3:
- improved comment wording.
- simplified shift/ffs dance.
Changes v1 -> v2:
- opencode some oneline helper function/macros for readability
- use ffs where appropriate
- rename some functions/variables as suggested.
- added assert()
- 1 -> 1U
- clear INTx# when MSI is enabled
- clear pending bits for freed vectors.
- check the requested number of vectors.
---
Makefile.objs | 2 +-
hw/msi.c | 352 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/msi.h | 41 +++++++
hw/pci.h | 10 +-
4 files changed, 401 insertions(+), 4 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..41b7c01
--- /dev/null
+++ b/hw/msi.c
@@ -0,0 +1,352 @@
+/*
+ * 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"
+
+/* Eventually those constants should go to Linux pci_regs.h */
+#define PCI_MSI_PENDING_32 0x10
+#define PCI_MSI_PENDING_64 0x14
+
+/* PCI_MSI_ADDRESS_LO */
+#define PCI_MSI_ADDRESS_LO_MASK (~0x3)
+
+/* If we get rid of cap allocator, we won't need those. */
+#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 PCI_MSI_VECTORS_MAX 32
+
+/* If we get rid of cap allocator, we won't need this. */
+static inline uint8_t msi_cap_sizeof(uint16_t flags)
+{
+ switch (flags & (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;
+}
+
+//#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 unsigned int msi_nr_vectors(uint16_t flags)
+{
+ return 1U <<
+ ((flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1));
+}
+
+static inline uint8_t msi_flags_off(const PCIDevice* dev)
+{
+ return dev->msi_cap + PCI_MSI_FLAGS;
+}
+
+static inline uint8_t msi_address_lo_off(const PCIDevice* dev)
+{
+ return dev->msi_cap + PCI_MSI_ADDRESS_LO;
+}
+
+static inline uint8_t msi_address_hi_off(const PCIDevice* dev)
+{
+ return dev->msi_cap + PCI_MSI_ADDRESS_HI;
+}
+
+static inline uint8_t msi_data_off(const PCIDevice* dev, bool msi64bit)
+{
+ return dev->msi_cap + (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32);
+}
+
+static inline uint8_t msi_mask_off(const PCIDevice* dev, bool msi64bit)
+{
+ return dev->msi_cap + (msi64bit ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32);
+}
+
+static inline uint8_t msi_pending_off(const PCIDevice* dev, bool msi64bit)
+{
+ return dev->msi_cap + (msi64bit ? PCI_MSI_PENDING_64 : PCI_MSI_PENDING_32);
+}
+
+bool msi_enabled(const PCIDevice *dev)
+{
+ return msi_present(dev) &&
+ (pci_get_word(dev->config + msi_flags_off(dev)) &
+ PCI_MSI_FLAGS_ENABLE);
+}
+
+int msi_init(struct PCIDevice *dev, uint8_t offset,
+ unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask)
+{
+ unsigned int vectors_order;
+ uint16_t flags;
+ uint8_t cap_size;
+ int config_offset;
+ MSI_DEV_PRINTF(dev,
+ "init offset: 0x%"PRIx8" vector: %"PRId8
+ " 64bit %d mask %d\n",
+ offset, nr_vectors, msi64bit, msi_per_vector_mask);
+
+ assert(!(nr_vectors & (nr_vectors - 1))); /* power of 2 */
+ assert(nr_vectors > 0);
+ assert(nr_vectors <= PCI_MSI_VECTORS_MAX);
+ /* the nr of MSI vectors is up to 32 */
+ vectors_order = ffs(nr_vectors) - 1;
+
+ flags = vectors_order << (ffs(PCI_MSI_FLAGS_QMASK) - 1);
+ if (msi64bit) {
+ flags |= PCI_MSI_FLAGS_64BIT;
+ }
+ if (msi_per_vector_mask) {
+ flags |= PCI_MSI_FLAGS_MASKBIT;
+ }
+
+ 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_flags_off(dev), flags);
+ pci_set_word(dev->wmask + msi_flags_off(dev),
+ PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
+ pci_set_long(dev->wmask + msi_address_lo_off(dev),
+ PCI_MSI_ADDRESS_LO_MASK);
+ if (msi64bit) {
+ pci_set_long(dev->wmask + msi_address_hi_off(dev), 0xffffffff);
+ }
+ pci_set_word(dev->wmask + msi_data_off(dev, msi64bit), 0xffff);
+
+ if (msi_per_vector_mask) {
+ pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit),
+ /* (1U << nr_vectors) - 1 is undefined
+ when nr_vectors = 32 */
+ 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors));
+ }
+ return config_offset;
+}
+
+void msi_uninit(struct PCIDevice *dev)
+{
+ uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
+ uint8_t cap_size = msi_cap_sizeof(flags);
+ pci_del_capability(dev, PCI_CAP_ID_MSIX, cap_size);
+ MSI_DEV_PRINTF(dev, "uninit\n");
+}
+
+void msi_reset(PCIDevice *dev)
+{
+ uint16_t flags;
+ bool msi64bit;
+
+ flags = pci_get_word(dev->config + msi_flags_off(dev));
+ flags &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
+ msi64bit = flags & PCI_MSI_FLAGS_64BIT;
+
+ pci_set_word(dev->config + msi_flags_off(dev), flags);
+ pci_set_long(dev->config + msi_address_lo_off(dev), 0);
+ if (msi64bit) {
+ pci_set_long(dev->config + msi_address_hi_off(dev), 0);
+ }
+ pci_set_word(dev->config + msi_data_off(dev, msi64bit), 0);
+ if (flags & PCI_MSI_FLAGS_MASKBIT) {
+ pci_set_long(dev->config + msi_mask_off(dev, msi64bit), 0);
+ pci_set_long(dev->config + msi_pending_off(dev, msi64bit), 0);
+ }
+ MSI_DEV_PRINTF(dev, "reset\n");
+}
+
+static bool msi_is_masked(const PCIDevice *dev, unsigned int vector)
+{
+ uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
+ uint32_t mask;
+ assert(vector < PCI_MSI_VECTORS_MAX);
+
+ if (!(flags & PCI_MSI_FLAGS_MASKBIT)) {
+ return false;
+ }
+
+ mask = pci_get_long(dev->config +
+ msi_mask_off(dev, flags & PCI_MSI_FLAGS_64BIT));
+ return mask & (1U << vector);
+}
+
+void msi_notify(PCIDevice *dev, unsigned int vector)
+{
+ uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
+ bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
+ unsigned int nr_vectors = msi_nr_vectors(flags);
+ uint64_t address;
+ uint32_t data;
+
+ assert(vector < nr_vectors);
+ if (msi_is_masked(dev, vector)) {
+ assert(flags & PCI_MSI_FLAGS_MASKBIT);
+ pci_set_bit_long(dev->config + msi_pending_off(dev, msi64bit),
+ 1U << vector);
+ MSI_DEV_PRINTF(dev, "pending vector 0x%x\n", vector);
+ return;
+ }
+
+ if (msi64bit){
+ address = pci_get_quad(dev->config + msi_address_lo_off(dev));
+ } else {
+ address = pci_get_long(dev->config + msi_address_lo_off(dev));
+ }
+
+ /* upper bit 31:16 is zero */
+ data = pci_get_word(dev->config + msi_data_off(dev, msi64bit));
+ if (nr_vectors > 1) {
+ data &= ~(nr_vectors - 1);
+ data |= vector;
+ }
+
+ MSI_DEV_PRINTF(dev,
+ "notify vector 0x%x"
+ " 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 flags = pci_get_word(dev->config + msi_flags_off(dev));
+ bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
+ bool msi_per_vector_mask = flags & PCI_MSI_FLAGS_MASKBIT;
+ unsigned int nr_vectors;
+ uint8_t log_num_vecs;
+ uint8_t log_max_vecs;
+ unsigned int vector;
+ uint32_t pending;
+ int i;
+
+#ifdef MSI_DEBUG
+ if (ranges_overlap(addr, len, dev->msi_cap, msi_cap_sizeof(flags))) {
+ 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,
+ flags,
+ pci_get_long(dev->config + msi_address_lo_off(dev)));
+ if (msi64bit) {
+ fprintf(stderr, " addrss-hi: 0x%"PRIx32,
+ pci_get_long(dev->config + msi_address_hi_off(dev)));
+ }
+ fprintf(stderr, " data: 0x%"PRIx16,
+ pci_get_word(dev->config + msi_data_off(dev, msi64bit)));
+ if (flags & PCI_MSI_FLAGS_MASKBIT) {
+ fprintf(stderr, " mask 0x%"PRIx32" pending 0x%"PRIx32,
+ pci_get_long(dev->config + msi_mask_off(dev, msi64bit)),
+ pci_get_long(dev->config + msi_pending_off(dev, msi64bit)));
+ }
+ fprintf(stderr, "\n");
+ }
+#endif
+
+ /* Are we modified? */
+ if (!(ranges_overlap(addr, len, msi_flags_off(dev), 2) ||
+ (msi_per_vector_mask &&
+ ranges_overlap(addr, len, msi_mask_off(dev, msi64bit), 4)))) {
+ return;
+ }
+
+ if (!(flags & PCI_MSI_FLAGS_ENABLE)) {
+ return;
+ }
+
+ /*
+ * Now MSI is enabled, clear INTx# interrupts.
+ * the driver is prohibited from writing enable bit to mask
+ * a service request. But the guest OS could do this.
+ * So we just discard the interrupts as moderate fallback.
+ *
+ * 6.8.3.3. Enabling Operation
+ * While enabled for MSI or MSI-X operation, a function is prohibited
+ * from using its INTx# pin (if implemented) to request
+ * service (MSI, MSI-X, and INTx# are mutually exclusive).
+ */
+ for (i = 0; i < PCI_NUM_PINS; ++i) {
+ qemu_set_irq(dev->irq[i], 0);
+ }
+
+ /*
+ * nr_vectors might be set bigger than capable. So clamp it.
+ * This is not legal by spec, so we can do anything we like,
+ * just don't crash the host
+ */
+ log_num_vecs =
+ (flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1);
+ log_max_vecs =
+ (flags & PCI_MSI_FLAGS_QMASK) >> (ffs(PCI_MSI_FLAGS_QMASK) - 1);
+ if (log_num_vecs > log_max_vecs) {
+ flags &= ~PCI_MSI_FLAGS_QSIZE;
+ flags |= log_max_vecs << (ffs(PCI_MSI_FLAGS_QSIZE) - 1);
+ pci_set_word(dev->config + msi_flags_off(dev), flags);
+ }
+
+ if (!msi_per_vector_mask) {
+ /* if per vector masking isn't supported,
+ there is no pending interrupt. */
+ return;
+ }
+
+ nr_vectors = msi_nr_vectors(flags);
+
+ /* This will discard pending interrupts, if any. */
+ pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit));
+ pending &= 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors);
+ pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending);
+
+ /* deliver pending interrupts which are unmasked */
+ for (vector = 0; vector < nr_vectors; ++vector) {
+ if (msi_is_masked(dev, vector) || !(pending & (1U << vector))) {
+ continue;
+ }
+
+ pci_clear_bit_long(dev->config + msi_pending_off(dev, msi64bit),
+ 1U << vector);
+ msi_notify(dev, vector);
+ }
+}
+
+unsigned int msi_nr_vectors_allocated(const PCIDevice *dev)
+{
+ uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
+ return msi_nr_vectors(flags);
+}
diff --git a/hw/msi.h b/hw/msi.h
new file mode 100644
index 0000000..5766018
--- /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,
+ unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask);
+void msi_uninit(struct PCIDevice *dev);
+void msi_reset(PCIDevice *dev);
+void msi_notify(PCIDevice *dev, unsigned int vector);
+void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len);
+unsigned int msi_nr_vectors_allocated(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 7097817..3dc2099 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -109,11 +109,12 @@ typedef struct PCIIORegion {
/* Bits in cap_present field. */
enum {
- QEMU_PCI_CAP_MSIX = 0x1,
- QEMU_PCI_CAP_EXPRESS = 0x2,
+ QEMU_PCI_CAP_MSI = 0x1,
+ QEMU_PCI_CAP_MSIX = 0x2,
+ QEMU_PCI_CAP_EXPRESS = 0x4,
/* multifunction capable device */
-#define QEMU_PCI_CAP_MULTIFUNCTION_BITNR 2
+#define QEMU_PCI_CAP_MULTIFUNCTION_BITNR 3
QEMU_PCI_CAP_MULTIFUNCTION = (1 << QEMU_PCI_CAP_MULTIFUNCTION_BITNR),
};
@@ -174,6 +175,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] 30+ messages in thread
* [Qemu-devel] [PATCH v4 07/15] pcie: add pcie constants to pcie_regs.h
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
` (5 preceding siblings ...)
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 06/15] msi: implements msi Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 08/15] pcie: helper functions for pcie capability and extended capability Isaku Yamahata
` (8 subsequent siblings)
15 siblings, 0 replies; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
add pcie constants to pcie_regs.h.
Those constants should go to Linux pci_regs.h and then the file should
go away eventually.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
Changes v3 -> v4:
- removed copyright notice as requested.
Changes v2 -> v3:
- moved out pcie constants from pcie.c to pcie_regs.h.
- removed unused macros
---
hw/pcie_regs.h | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 154 insertions(+), 0 deletions(-)
create mode 100644 hw/pcie_regs.h
diff --git a/hw/pcie_regs.h b/hw/pcie_regs.h
new file mode 100644
index 0000000..3461a1b
--- /dev/null
+++ b/hw/pcie_regs.h
@@ -0,0 +1,154 @@
+/*
+ * constants for pcie configurations space from pci express spec.
+ *
+ * TODO:
+ * Those constants and macros should go to Linux pci_regs.h
+ * Once they're merged, they will go away.
+ */
+#ifndef QEMU_PCIE_REGS_H
+#define QEMU_PCIE_REGS_H
+
+
+/* express capability */
+
+#define PCI_EXP_VER2_SIZEOF 0x3c /* express capability of ver. 2 */
+#define PCI_EXT_CAP_VER_SHIFT 16
+#define PCI_EXT_CAP_NEXT_SHIFT 20
+#define PCI_EXT_CAP_NEXT_MASK (0xffc << PCI_EXT_CAP_NEXT_SHIFT)
+
+#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))
+
+/* PCI_EXP_FLAGS */
+#define PCI_EXP_FLAGS_VER2 2 /* for now, supports only ver. 2 */
+#define PCI_EXP_FLAGS_IRQ_SHIFT (ffs(PCI_EXP_FLAGS_IRQ) - 1)
+#define PCI_EXP_FLAGS_TYPE_SHIFT (ffs(PCI_EXP_FLAGS_TYPE) - 1)
+
+
+/* PCI_EXP_LINK{CAP, STA} */
+/* link speed */
+#define PCI_EXP_LNK_LS_25 1
+
+#define PCI_EXP_LNK_MLW_SHIFT (ffs(PCI_EXP_LNKCAP_MLW) - 1)
+#define PCI_EXP_LNK_MLW_1 (1 << PCI_EXP_LNK_MLW_SHIFT)
+
+/* PCI_EXP_LINKCAP */
+#define PCI_EXP_LNKCAP_ASPMS_SHIFT (ffs(PCI_EXP_LNKCAP_ASPMS) - 1)
+#define PCI_EXP_LNKCAP_ASPMS_0S (1 << PCI_EXP_LNKCAP_ASPMS_SHIFT)
+
+#define PCI_EXP_LNKCAP_PN_SHIFT (ffs(PCI_EXP_LNKCAP_PN) - 1)
+
+#define PCI_EXP_SLTCAP_PSN_SHIFT (ffs(PCI_EXP_SLTCAP_PSN) - 1)
+
+#define PCI_EXP_SLTCTL_IND_RESERVED 0x0
+#define PCI_EXP_SLTCTL_IND_ON 0x1
+#define PCI_EXP_SLTCTL_IND_BLINK 0x2
+#define PCI_EXP_SLTCTL_IND_OFF 0x3
+#define PCI_EXP_SLTCTL_AIC_SHIFT (ffs(PCI_EXP_SLTCTL_AIC) - 1)
+#define PCI_EXP_SLTCTL_AIC_OFF \
+ (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_AIC_SHIFT)
+
+#define PCI_EXP_SLTCTL_PIC_SHIFT (ffs(PCI_EXP_SLTCTL_PIC) - 1)
+#define PCI_EXP_SLTCTL_PIC_OFF \
+ (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_PIC_SHIFT)
+
+#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)
+
+#define PCI_EXP_DEVCAP2_EFF 0x100000
+#define PCI_EXP_DEVCAP2_EETLPP 0x200000
+
+#define PCI_EXP_DEVCTL2_EETLPPB 0x80
+
+/* ARI */
+#define PCI_ARI_VER 1
+#define PCI_ARI_SIZEOF 8
+
+/* 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_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_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
+
+#define PCI_SEC_STATUS_RCV_SYSTEM_ERROR 0x4000
+
+/* 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_MAX 32
+#define PCI_ERR_ROOT_IRQ 0xf8000000
+#define PCI_ERR_ROOT_IRQ_SHIFT (ffs(PCI_ERR_ROOT_IRQ) - 1)
+#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)
+
+#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_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)
+
+#endif /* QEMU_PCIE_REGS_H */
--
1.7.1.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH v4 08/15] pcie: helper functions for pcie capability and extended capability
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
` (6 preceding siblings ...)
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 07/15] pcie: add pcie constants to pcie_regs.h Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 5:38 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 09/15] pcie/aer: helper functions for pcie aer capability Isaku Yamahata
` (7 subsequent siblings)
15 siblings, 1 reply; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
This patch implements helper functions for pci express capability
and pci express extended capability allocation.
NOTE: presence detection depends on pci_qdev_init() change.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
Changes v3 -> v4:
- various clean up
- dropped pcie_notify(), pcie_del_capability()
- use pci_{clear_set, clear}_bit_xxx() helper functions.
- dropped pci_exp_cap()
---
Makefile.objs | 1 +
hw/pci.h | 5 +
hw/pcie.c | 529 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/pcie.h | 107 ++++++++++++
qemu-common.h | 1 +
5 files changed, 643 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 3dc2099..3c9c228 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))
@@ -178,6 +180,9 @@ struct PCIDevice {
/* Offset of MSI capability in config space */
uint8_t msi_cap;
+ /* PCI Express */
+ PCIExpressDevice exp;
+
/* Location of option rom */
char *romfile;
ram_addr_t rom_offset;
diff --git a/hw/pcie.c b/hw/pcie.c
new file mode 100644
index 0000000..0f2bf50
--- /dev/null
+++ b/hw/pcie.c
@@ -0,0 +1,529 @@
+/*
+ * 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"
+#include "pcie_regs.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__)
+
+
+/***************************************************************************
+ * pci express capability helper functions
+ */
+int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port)
+{
+ int pos;
+ uint8_t *exp_cap;
+
+ assert(pci_is_express(dev));
+
+ pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset,
+ PCI_EXP_VER2_SIZEOF);
+ if (pos < 0) {
+ return pos;
+ }
+ dev->exp.exp_cap = pos;
+ exp_cap = dev->config + pos;
+
+ /* capability register
+ interrupt message number defaults to 0 */
+ pci_set_word(exp_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(exp_cap + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER);
+
+ pci_set_long(exp_cap + PCI_EXP_LNKCAP,
+ (port << PCI_EXP_LNKCAP_PN_SHIFT) |
+ PCI_EXP_LNKCAP_ASPMS_0S |
+ PCI_EXP_LNK_MLW_1 |
+ PCI_EXP_LNK_LS_25);
+
+ pci_set_word(exp_cap + PCI_EXP_LNKSTA,
+ PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25);
+
+ pci_set_long(exp_cap + PCI_EXP_DEVCAP2,
+ PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP);
+
+ pci_set_word(dev->wmask + pos, PCI_EXP_DEVCTL2_EETLPPB);
+ return pos;
+}
+
+void pcie_cap_exit(PCIDevice *dev)
+{
+ pci_del_capability(dev, PCI_CAP_ID_EXP, PCI_EXP_VER2_SIZEOF);
+}
+
+uint8_t pcie_cap_get_type(const PCIDevice *dev)
+{
+ uint32_t pos = dev->exp.exp_cap;
+ 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 */
+/* 7.8.2 PCI Express Capabilities Register: Interrupt Message Number */
+void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector)
+{
+ assert(vector < 32);
+ pci_clear_set_bit_word(dev->config + dev->exp.exp_cap + PCI_EXP_FLAGS,
+ PCI_EXP_FLAGS_IRQ,
+ vector << PCI_EXP_FLAGS_IRQ_SHIFT);
+}
+
+uint8_t pcie_cap_flags_get_vector(PCIDevice *dev)
+{
+ return (pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_FLAGS) &
+ PCI_EXP_FLAGS_IRQ) >> PCI_EXP_FLAGS_IRQ_SHIFT;
+}
+
+void pcie_cap_deverr_init(PCIDevice *dev)
+{
+ uint32_t pos = dev->exp.exp_cap;
+ pci_set_bit_long(dev->config + pos + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER);
+ pci_set_bit_long(dev->wmask + pos + PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
+ pci_set_bit_long(dev->w1cmask + pos + PCI_EXP_DEVSTA,
+ PCI_EXP_DEVSTA_CED | PCI_EXP_DEVSTA_NFED |
+ PCI_EXP_DEVSTA_URD | PCI_EXP_DEVSTA_URD);
+}
+
+void pcie_cap_deverr_reset(PCIDevice *dev)
+{
+ pci_clear_bit_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
+}
+
+static void pcie_cap_slot_notify(PCIDevice *dev,
+ bool msi_trigger, int int_level)
+{
+ unsigned int msi_vector = pcie_cap_flags_get_vector(dev);
+ unsigned int int_irq = 0; /* INTA# is chosen for slot event notification */
+
+ if (msix_enabled(dev)) {
+ if (msi_trigger) {
+ msix_notify(dev, msi_vector);
+ }
+ } else if (msi_enabled(dev)) {
+ if (msi_trigger) {
+ msi_notify(dev, msi_vector);
+ }
+ } else {
+ qemu_set_irq(dev->irq[int_irq], int_level);
+ }
+}
+
+/*
+ * A PCI Express Hot-Plug Event has occured, so update slot status register
+ * and notify OS of the event if necessary.
+ *
+ * 6.7.3 PCI Express Hot-Plug Events
+ * 6.7.3.4 Software Notification of Hot-Plug Events
+ */
+static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event)
+{
+ uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
+ uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL);
+ uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
+ bool msi_trigger;
+ int int_level;
+
+ PCIE_DEV_PRINTF(dev,
+ "sltctl: 0x%02"PRIx16" sltsta: 0x%02"PRIx16" event: %x\n",
+ sltctl, sltsta, event);
+
+ msi_trigger = (sltctl & PCI_EXP_SLTCTL_HPIE) && (sltctl & event) &&
+ ((sltsta ^ event) & event); /* 0 -> 1 */
+
+ sltsta |= event;
+ pci_set_word(exp_cap + PCI_EXP_SLTSTA, sltsta);
+ PCIE_DEV_PRINTF(dev, "sltsta -> %02"PRIx16"\n", sltsta);
+
+ int_level = ((sltctl & PCI_EXP_SLTCTL_HPIE) &&
+ (sltsta & PCI_EXP_HP_EV_SUPPORTED));
+
+ pcie_cap_slot_notify(dev, msi_trigger, int_level);
+}
+
+static int pcie_cap_slot_hotplug(DeviceState *qdev,
+ PCIDevice *pci_dev, int state)
+{
+ PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
+ uint8_t *exp_cap = d->config + d->exp.exp_cap;
+ uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
+
+ if (!pci_dev->qdev.hotplugged) {
+ assert(state); /* this case only happens at machine creation. */
+ pci_set_bit_word(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS);
+ return 0;
+ }
+
+ PCIE_DEV_PRINTF(pci_dev, "hotplug state: %d\n", state);
+ if (sltsta & PCI_EXP_SLTSTA_EIS) {
+ /* the slot is electromechanically locked.
+ * This error is propagated up to qdev and then to HMP/QMP.
+ */
+ return -EBUSY;
+ }
+
+ /* TODO: multifunction hot-plug.
+ * Right now, only a device of function = 0 is allowed to be
+ * hot plugged/unplugged.
+ */
+ assert(PCI_FUNC(pci_dev->devfn) == 0);
+
+ if (state) {
+ pci_set_bit_word(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS);
+ pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC);
+ } else {
+ qdev_free(&pci_dev->qdev);
+ pci_clear_bit_word(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS);
+ pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC);
+ }
+ 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)
+{
+ uint32_t pos = dev->exp.exp_cap;
+
+ pci_set_bit_word(dev->config + pos + PCI_EXP_FLAGS, PCI_EXP_FLAGS_SLOT);
+
+ pci_clear_set_bit_long(dev->config + pos + PCI_EXP_SLTCAP,
+ ~PCI_EXP_SLTCAP_PSN,
+ (slot << PCI_EXP_SLTCAP_PSN_SHIFT) |
+ 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_clear_set_bit_word(dev->config + pos + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PIC | PCI_EXP_SLTCTL_AIC,
+ PCI_EXP_SLTCTL_PIC_OFF | PCI_EXP_SLTCTL_AIC_OFF);
+ pci_set_bit_word(dev->wmask + pos + 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_set_bit_word(dev->w1cmask + pos + PCI_EXP_SLTSTA,
+ PCI_EXP_HP_EV_SUPPORTED);
+
+ 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 *exp_cap = dev->config + dev->exp.exp_cap;
+
+ PCIE_DEV_PRINTF(dev, "reset\n");
+
+ pci_clear_set_bit_word(exp_cap + PCI_EXP_SLTCTL,
+ 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,
+ PCI_EXP_SLTCTL_PIC_OFF | PCI_EXP_SLTCTL_AIC_OFF);
+
+ pci_clear_bit_word(exp_cap + PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_EIS |/* by reset, the lock is released */
+ PCI_EXP_SLTSTA_CC |
+ PCI_EXP_SLTSTA_PDC |
+ PCI_EXP_SLTSTA_ABP);
+}
+
+void pcie_cap_slot_write_config(PCIDevice *dev,
+ uint32_t addr, uint32_t val, int len,
+ uint16_t sltctl_prev)
+{
+ uint32_t pos = dev->exp.exp_cap;
+ uint8_t *exp_cap = dev->config + pos;
+ uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL);
+ uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
+
+ PCIE_DEV_PRINTF(dev,
+ "addr: 0x%"PRIx32" val: 0x%"PRIx32" len: %d\n"
+ "\tsltctl_prev: 0x%02"PRIx16" sltctl: 0x%02"PRIx16
+ " sltsta: 0x%02"PRIx16"\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)) {
+ sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
+
+ /* write to stlsta results in clearing bits,
+ so new interrupts won't be generated. */
+ PCIE_DEV_PRINTF(dev, "sltsta -> 0x%02"PRIx16"\n", sltsta);
+ }
+
+ /* SLTCTL */
+ if (ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) {
+ PCIE_DEV_PRINTF(dev, "sltctl: 0x%02"PRIx16" -> 0x%02"PRIx16"\n",
+ sltctl_prev, sltctl);
+ if (pci_get_word(dev->written + pos + PCI_EXP_SLTCTL) &
+ PCI_EXP_SLTCTL_EIC) {
+ sltsta ^= PCI_EXP_SLTSTA_EIS; /* toggle PCI_EXP_SLTSTA_EIS bit */
+ pci_set_word(exp_cap + PCI_EXP_SLTSTA, sltsta);
+ PCIE_DEV_PRINTF(dev, "PCI_EXP_SLTCTL_EIC: "
+ "sltsta -> 0x%02"PRIx16"\n",
+ sltsta);
+ }
+
+ /*
+ * The events might be enabled or disabled,
+ * Check if the software notificastion condition is satisfied
+ * or disatisfied.
+ *
+ * 6.7.3.4 Software Notification of Hot-plug events
+ */
+ if (sltctl & PCI_EXP_SLTCTL_HPIE) {
+ bool msi_trigger =
+ (sltctl_prev ^ sltctl) & sltctl & /* 0 -> 1 */
+ sltsta & PCI_EXP_HP_EV_SUPPORTED;
+ int int_level = sltctl & sltsta & PCI_EXP_HP_EV_SUPPORTED;
+
+ pcie_cap_slot_notify(dev, msi_trigger, int_level);
+ }
+
+ if (!((sltctl_prev ^ sltctl) & PCI_EXP_SLTCTL_SUPPORTED)) {
+ PCIE_DEV_PRINTF(dev,
+ "sprious command completion slctl "
+ "0x%"PRIx16" -> 0x%"PRIx16"\n",
+ sltctl_prev, sltctl);
+ }
+
+ /* command completion.
+ * Real hardware might take a while to complete
+ * requested command because physical movement would be involved
+ * like locking the electromechanical lock.
+ * However in our case, command is completed instantaneously above,
+ * so send a command completion event right now.
+ *
+ * 6.7.3.2 Command Completed Events
+ */
+ /* set command completed bit */
+ pcie_cap_slot_event(dev, PCI_EXP_HP_EV_CCI);
+ }
+}
+
+void pcie_cap_slot_push_attention_button(PCIDevice *dev)
+{
+ pcie_cap_slot_event(dev, PCI_EXP_HP_EV_ABP);
+}
+
+/* root control/capabilities/status. PME isn't emulated for now */
+void pcie_cap_root_init(PCIDevice *dev)
+{
+ pci_set_word(dev->wmask + dev->exp.exp_cap + PCI_EXP_RTCTL,
+ PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE |
+ PCI_EXP_RTCTL_SEFEE);
+}
+
+void pcie_cap_root_reset(PCIDevice *dev)
+{
+ pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_RTCTL, 0);
+}
+
+/* function level reset(FLR) */
+void pcie_cap_flr_init(PCIDevice *dev, pcie_flr_fn flr)
+{
+ pci_set_bit_long(dev->config + dev->exp.exp_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)
+{
+ /* if ranges_overlap(addr, len, pos + PCI_EXP_DEVCTL, 2) is false,
+ written == 0 */
+ uint16_t written = pci_get_word(dev->written +
+ dev->exp.exp_cap + PCI_EXP_DEVCTL);
+ if ((written & 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)
+{
+ uint32_t pos = dev->exp.exp_cap;
+ pci_set_bit_long(dev->config + pos + PCI_EXP_DEVCAP2, PCI_EXP_DEVCAP2_ARI);
+ pci_set_bit_long(dev->wmask + pos + PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_ARI);
+}
+
+void pcie_cap_ari_reset(PCIDevice *dev)
+{
+ pci_clear_bit_long(dev->config + dev->exp.exp_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 (!dev->exp.exp_cap) {
+ return false;
+ }
+
+ return pci_get_long(dev->config + dev->exp.exp_cap + 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
+ */
+
+static uint16_t pcie_find_capability_list(PCIDevice *dev, uint16_t cap_id,
+ uint16_t *prev_p)
+{
+ uint16_t prev = 0;
+ uint16_t next;
+ uint32_t header = pci_get_long(dev->config + PCI_CONFIG_SPACE_SIZE);
+
+ if (!header) {
+ /* no extended capability */
+ next = 0;
+ goto out;
+ }
+ for (next = PCI_CONFIG_SPACE_SIZE; next;
+ prev = next, next = PCI_EXT_CAP_NEXT(header)) {
+
+ assert(next >= PCI_CONFIG_SPACE_SIZE);
+ assert(next <= PCIE_CONFIG_SPACE_SIZE - 8);
+
+ header = pci_get_long(dev->config + next);
+ if (PCI_EXT_CAP_ID(header) == cap_id) {
+ break;
+ }
+ }
+
+out:
+ if (prev_p) {
+ *prev_p = prev;
+ }
+ return next;
+}
+
+uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id)
+{
+ return pcie_find_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);
+}
+
+/*
+ * caller must supply valid (offset, size) * such that the range shouldn't
+ * overlap with other capability or other registers.
+ * This function doesn't check it.
+ */
+void pcie_add_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 >= PCI_CONFIG_SPACE_SIZE);
+ assert(offset < offset + size);
+ assert(offset + size < PCIE_CONFIG_SPACE_SIZE);
+ assert(size >= 8);
+ assert(pci_is_express(dev));
+
+ if (offset == PCI_CONFIG_SPACE_SIZE) {
+ header = pci_get_long(dev->config + offset);
+ next = PCI_EXT_CAP_NEXT(header);
+ } else {
+ uint16_t prev;
+
+ /* 0 is reserved cap id. use internally to find the last capability
+ in the linked list */
+ next = pcie_find_capability_list(dev, 0, &prev);
+
+ assert(prev >= PCI_CONFIG_SPACE_SIZE);
+ assert(next == 0);
+ pcie_ext_cap_set_next(dev, prev, offset);
+ }
+ pci_set_long(dev->config + offset, PCI_EXT_CAP(cap_id, cap_ver, next));
+
+ /* Make capability read-only by default */
+ memset(dev->wmask + offset, 0, size);
+ memset(dev->w1cmask + offset, 0, size);
+ /* Check capability by default */
+ memset(dev->cmask + offset, 0xFF, size);
+}
+
+/**************************************************************************
+ * pci express extended capability helper functions
+ */
+
+/* ARI */
+void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn)
+{
+ pcie_add_capability(dev, PCI_EXT_CAP_ID_ARI, PCI_ARI_VER,
+ offset, PCI_ARI_SIZEOF);
+ pci_set_long(dev->config + offset + PCI_ARI_CAP, PCI_ARI_CAP_NFN(nextfn));
+}
diff --git a/hw/pcie.h b/hw/pcie.h
new file mode 100644
index 0000000..603bb8b
--- /dev/null
+++ b/hw/pcie.h
@@ -0,0 +1,107 @@
+/*
+ * 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"
+#include "pci_regs.h"
+#include "pcie_regs.h"
+
+typedef enum {
+ /* for attention and power indicator */
+ PCI_EXP_HP_IND_RESERVED = PCI_EXP_SLTCTL_IND_RESERVED,
+ PCI_EXP_HP_IND_ON = PCI_EXP_SLTCTL_IND_ON,
+ PCI_EXP_HP_IND_BLINK = PCI_EXP_SLTCTL_IND_BLINK,
+ PCI_EXP_HP_IND_OFF = PCI_EXP_SLTCTL_IND_OFF,
+} PCIExpressIndicator;
+
+typedef enum {
+ /* these bits must match the bits in Slot Control/Status registers.
+ * PCI_EXP_HP_EV_xxx = PCI_EXP_SLTCTL_xxxE = PCI_EXP_SLTSTA_xxx
+ *
+ * Not all the bits of slot control register match with the ones of
+ * slot status. Not some bits of slot status register is used to
+ * show status, not to report event occurence.
+ * So such bits must be masked out when checking the software
+ * notification condition.
+ */
+ PCI_EXP_HP_EV_ABP = PCI_EXP_SLTCTL_ABPE,
+ /* attention button pressed */
+ PCI_EXP_HP_EV_PDC = PCI_EXP_SLTCTL_PDCE,
+ /* presence detect changed */
+ PCI_EXP_HP_EV_CCI = PCI_EXP_SLTCTL_CCIE,
+ /* command completed */
+
+ PCI_EXP_HP_EV_SUPPORTED = PCI_EXP_HP_EV_ABP |
+ PCI_EXP_HP_EV_PDC |
+ PCI_EXP_HP_EV_CCI,
+ /* supported event mask */
+
+ /* events not listed aren't supported */
+} PCIExpressHotPlugEvent;
+
+typedef void (*pcie_flr_fn)(PCIDevice *dev);
+
+struct PCIExpressDevice {
+ /* Offset of express capability in config space */
+ uint8_t exp_cap;
+
+ /* FLR */
+ pcie_flr_fn flr;
+};
+
+/* PCI express capability helper functions */
+int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port);
+void 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_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_capability(PCIDevice *dev, uint16_t cap_id);
+void pcie_add_capability(PCIDevice *dev,
+ uint16_t cap_id, uint8_t cap_ver,
+ uint16_t offset, uint16_t size);
+
+void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn);
+
+#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] 30+ messages in thread
* [Qemu-devel] [PATCH v4 09/15] pcie/aer: helper functions for pcie aer capability
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
` (7 preceding siblings ...)
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 08/15] pcie: helper functions for pcie capability and extended capability Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 5:45 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 10/15] pcie port: define struct PCIEPort/PCIESlot and helper functions Isaku Yamahata
` (6 subsequent siblings)
15 siblings, 1 reply; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
This patch implements helper functions for pcie aer capability
which will be used later.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
Changes v3 -> v4:
- various naming fixes.
- use pci bit operation helper function
- eliminate errmsg function pointer
- replace pci_shift_xxx() with PCIDevice::written
- uncorrect error status register.
- dropped pcie_aer_cap()
Changes v2 -> v3:
- split out from pcie.[ch] to pcie_aer.[ch] to make the files sorter.
- embeded PCIExpressDevice into PCIDevice.
- CodingStyle fix
---
Makefile.objs | 2 +-
hw/pcie.h | 5 +
hw/pcie_aer.c | 789 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/pcie_aer.h | 105 ++++++++
qemu-common.h | 3 +
5 files changed, 903 insertions(+), 1 deletions(-)
create mode 100644 hw/pcie_aer.c
create mode 100644 hw/pcie_aer.h
diff --git a/Makefile.objs b/Makefile.objs
index eeb5134..68bcc48 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_aer.o
hw-obj-y += msix.o msi.o
# PCI network cards
diff --git a/hw/pcie.h b/hw/pcie.h
index 603bb8b..891e369 100644
--- a/hw/pcie.h
+++ b/hw/pcie.h
@@ -24,6 +24,7 @@
#include "hw.h"
#include "pci_regs.h"
#include "pcie_regs.h"
+#include "pcie_aer.h"
typedef enum {
/* for attention and power indicator */
@@ -66,6 +67,10 @@ struct PCIExpressDevice {
/* FLR */
pcie_flr_fn flr;
+
+ /* AER */
+ uint16_t aer_cap;
+ PCIEAERLog aer_log;
};
/* PCI express capability helper functions */
diff --git a/hw/pcie_aer.c b/hw/pcie_aer.c
new file mode 100644
index 0000000..c86f2b9
--- /dev/null
+++ b/hw/pcie_aer.c
@@ -0,0 +1,789 @@
+/*
+ * pcie_aer.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"
+#include "pcie_regs.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 void pcie_aer_clear_error(PCIDevice *dev);
+static uint8_t pcie_aer_root_get_vector(PCIDevice *dev);
+static AERMsgResult
+pcie_aer_msg_alldev(PCIDevice *dev, const PCIEAERMsg *msg);
+static AERMsgResult
+pcie_aer_msg_vbridge(PCIDevice *dev, const PCIEAERMsg *msg);
+static AERMsgResult
+pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg);
+
+/* From 6.2.7 Error Listing and Rules. Table 6-2, 6-3 and 6-4 */
+static PCIEAERSeverity 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 aer_log_next(uint32_t i, uint32_t max)
+{
+ return (i + 1) % max;
+}
+
+static bool aer_log_empty_index(uint32_t producer, uint32_t consumer)
+{
+ return producer == consumer;
+}
+
+static bool aer_log_empty(PCIEAERLog *aer_log)
+{
+ return aer_log_empty_index(aer_log->producer, aer_log->consumer);
+}
+
+static bool aer_log_full(PCIEAERLog *aer_log)
+{
+ return aer_log_next(aer_log->producer, aer_log->log_max) ==
+ aer_log->consumer;
+}
+
+static uint32_t aer_log_add(PCIEAERLog *aer_log)
+{
+ uint32_t i = aer_log->producer;
+ aer_log->producer = aer_log_next(aer_log->producer, aer_log->log_max);
+ return i;
+}
+
+static uint32_t aer_log_del(PCIEAERLog *aer_log)
+{
+ uint32_t i = aer_log->consumer;
+ aer_log->consumer = aer_log_next(aer_log->consumer, aer_log->log_max);
+ return i;
+}
+
+static int aer_log_add_err(PCIEAERLog *aer_log, const PCIEAERErr *err)
+{
+ uint32_t i;
+ if (aer_log_full(aer_log)) {
+ return -1;
+ }
+ i = aer_log_add(aer_log);
+ memcpy(&aer_log->log[i], err, sizeof(*err));
+ return 0;
+}
+
+static const PCIEAERErr* aer_log_del_err(PCIEAERLog *aer_log)
+{
+ uint32_t i;
+ assert(!aer_log_empty(aer_log));
+ i = aer_log_del(aer_log);
+ return &aer_log->log[i];
+}
+
+static void aer_log_clear_all_err(PCIEAERLog *aer_log)
+{
+ aer_log->producer = 0;
+ aer_log->consumer = 0;
+}
+
+void pcie_aer_init(PCIDevice *dev, uint16_t offset)
+{
+ PCIExpressDevice *exp;
+
+ pci_set_bit_word(dev->wmask + PCI_COMMAND, PCI_COMMAND_SERR);
+ pci_set_bit_word(dev->w1cmask + PCI_STATUS, PCI_STATUS_SIG_SYSTEM_ERROR);
+
+ pcie_add_capability(dev, PCI_EXT_CAP_ID_ERR, PCI_ERR_VER,
+ offset, PCI_ERR_SIZEOF);
+ exp = &dev->exp;
+ exp->aer_cap = offset;
+ if (dev->exp.aer_log.log_max == PCIE_AER_LOG_MAX_UNSET) {
+ dev->exp.aer_log.log_max = PCIE_AER_LOG_MAX_DEFAULT;
+ }
+ if (dev->exp.aer_log.log_max > PCIE_AER_LOG_MAX_MAX) {
+ dev->exp.aer_log.log_max = PCIE_AER_LOG_MAX_MAX;
+ }
+ dev->exp.aer_log.log = qemu_mallocz(sizeof(dev->exp.aer_log.log[0]) *
+ dev->exp.aer_log.log_max);
+
+ /* On reset PCI_ERR_CAP_MHRE is disabled
+ * PCI_ERR_CAP_MHRE is RWS so that reset doesn't affect related
+ * registers
+ */
+ pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS,
+ PCI_ERR_UNC_SUPPORTED);
+
+ pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_MASK,
+ PCI_ERR_UNC_SUPPORTED);
+
+ pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER,
+ PCI_ERR_UNC_SEVERITY_DEFAULT);
+ pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_SEVER,
+ PCI_ERR_UNC_SUPPORTED);
+
+ pci_set_bit_long(dev->w1cmask + offset + PCI_ERR_COR_STATUS,
+ PCI_ERR_COR_STATUS);
+
+ pci_set_long(dev->config + offset + PCI_ERR_COR_MASK,
+ PCI_ERR_COR_MASK_DEFAULT);
+ pci_set_long(dev->wmask + offset + PCI_ERR_COR_MASK,
+ PCI_ERR_COR_SUPPORTED);
+
+ /* capabilities and control. multiple header logging is supported */
+ if (dev->exp.aer_log.log_max > 0) {
+ pci_set_long(dev->config + offset + PCI_ERR_CAP,
+ PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC |
+ PCI_ERR_CAP_MHRC);
+ pci_set_long(dev->wmask + offset + PCI_ERR_CAP,
+ PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE |
+ PCI_ERR_CAP_MHRE);
+ } else {
+ pci_set_long(dev->config + offset + PCI_ERR_CAP,
+ PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC);
+ pci_set_long(dev->wmask + offset + 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_bit_word(dev->wmask + PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_SERR);
+ pci_set_bit_long(dev->w1cmask + PCI_STATUS,
+ PCI_SEC_STATUS_RCV_SYSTEM_ERROR);
+ break;
+ default:
+ /* nothing */
+ break;
+ }
+}
+
+void pcie_aer_exit(PCIDevice *dev)
+{
+ qemu_free(dev->exp.aer_log.log);
+}
+
+void pcie_aer_write_config(PCIDevice *dev,
+ uint32_t addr, uint32_t val, int len,
+ uint32_t uncorsta_old)
+{
+ uint32_t pos = dev->exp.aer_cap;
+
+ /* uncorrectable */
+ uint32_t written =
+ pci_get_long(dev->written + pos + PCI_ERR_UNCOR_STATUS) &
+ PCI_ERR_UNC_SUPPORTED;
+ /* if uncorrectable status register isn't touched, written == 0*/
+ uint32_t errcap = pci_get_long(dev->config + pos + PCI_ERR_CAP);
+ uint32_t first_error = (1U << PCI_ERR_CAP_FEP(errcap));
+ if ((uncorsta_old & first_error) && (written & first_error)) {
+ pcie_aer_clear_error(dev);
+ }
+
+ /* 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)) {
+ aer_log_clear_all_err(&dev->exp.aer_log);
+ }
+ }
+}
+
+static inline void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg)
+{
+ uint8_t type;
+ AERMsgResult result;
+
+ assert(pci_is_express(dev));
+
+ type = pcie_cap_get_type(dev);
+ if (type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_UPSTREAM ||
+ type == PCI_EXP_TYPE_DOWNSTREAM) {
+ result = pcie_aer_msg_vbridge(dev, msg);
+ if (result != AER_MSG_SENT) {
+ return;
+ }
+ }
+ result = pcie_aer_msg_alldev(dev, msg);
+ if (type == PCI_EXP_TYPE_ROOT_PORT && result == AER_MSG_SENT) {
+ pcie_aer_msg_root_port(dev, msg);
+ }
+}
+
+static AERMsgResult
+pcie_aer_msg_alldev(PCIDevice *dev, const PCIEAERMsg *msg)
+{
+ uint16_t cmd = pci_get_word(dev->config + PCI_COMMAND);
+ bool transmit1 =
+ pcie_aer_msg_is_uncor(msg) && (cmd & PCI_COMMAND_SERR);
+ uint32_t devctl = pci_get_word(dev->config +
+ dev->exp.exp_cap + PCI_EXP_DEVCTL);
+ bool transmit2 = msg->severity & devctl;
+ PCIDevice *parent_port;
+
+ if (transmit1) {
+ if (pcie_aer_msg_is_uncor(msg)) {
+ /* Signaled System Error */
+ pci_set_bit_word(dev->config + PCI_STATUS,
+ PCI_STATUS_SIG_SYSTEM_ERROR);
+ }
+ }
+
+ if (!(transmit1 || transmit2)) {
+ return AER_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_MSG_MASKED;
+ }
+ pcie_aer_msg(parent_port, msg);
+ }
+ return AER_MSG_SENT;
+}
+
+static AERMsgResult
+pcie_aer_msg_vbridge(PCIDevice *dev, const PCIEAERMsg *msg)
+{
+ uint16_t bridge_control = pci_get_word(dev->config + PCI_BRIDGE_CONTROL);
+
+ if (pcie_aer_msg_is_uncor(msg)) {
+ /* Received System Error */
+ pci_set_bit_word(dev->config + PCI_SEC_STATUS,
+ PCI_SEC_STATUS_RCV_SYSTEM_ERROR);
+ }
+
+ if (!(bridge_control & PCI_BRIDGE_CTL_SERR)) {
+ return AER_MSG_MASKED;
+ }
+ return AER_MSG_SENT;
+}
+
+static void pcie_aer_root_port_notify(PCIDevice *dev,
+ bool msi_trigger, int int_level)
+{
+ unsigned int msi_vector = pcie_aer_root_get_vector(dev);
+ unsigned int int_irq = 0; /* INTA# is chosen for aer notification */
+
+ if (msix_enabled(dev)) {
+ if (msi_trigger) {
+ msix_notify(dev, msi_vector);
+ }
+ } else if (msi_enabled(dev)) {
+ if (msi_trigger) {
+ msi_notify(dev, msi_vector);
+ }
+ } else {
+ qemu_set_irq(dev->irq[int_irq], int_level);
+ }
+}
+
+static AERMsgResult
+pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg)
+{
+ AERMsgResult ret;
+ uint16_t cmd;
+ uint8_t *aer_cap;
+ uint32_t root_cmd;
+ uint32_t root_sta;
+ bool msi_trigger;
+
+ ret = AER_MSG_MASKED;
+ cmd = pci_get_word(dev->config + PCI_COMMAND);
+ aer_cap = dev->config + dev->exp.aer_cap;
+ root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND);
+ root_sta = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS);
+ msi_trigger = false;
+
+ if (cmd & PCI_COMMAND_SERR) {
+ /* System Error. Platform Specific */
+ /* ret = AER_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) {
+ msi_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) {
+ msi_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) {
+ msi_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_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) {
+ /* 6.2.4.1.2 Interrupt Generation */
+ pcie_aer_root_port_notify(dev, msi_trigger, 1);
+ ret = AER_MSG_SENT;
+ }
+ return ret;
+}
+
+static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err)
+{
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+ 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 + dev->exp.exp_cap + 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)
+{
+ PCIEAERErr *err;
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+
+ pci_clear_bit_long(aer_cap + PCI_ERR_CAP,
+ PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP);
+
+ 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 PCIEAERErr *err)
+{
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+ 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 (aer_log_add_err(&dev->exp.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 + dev->exp.aer_cap;
+ uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP);
+ uint32_t old_err = (1U << PCI_ERR_CAP_FEP(errcap));
+ PCIEAERLog *aer_log = &dev->exp.aer_log;
+ const PCIEAERErr *err;
+ uint32_t consumer;
+
+ if (!(errcap & PCI_ERR_CAP_MHRE) || aer_log_empty(aer_log)) {
+ pcie_aer_clear_log(dev);
+ pci_clear_bit_long(aer_cap + PCI_ERR_UNCOR_STATUS, old_err);
+ return;
+ }
+
+ /*
+ * If more errors are queued, set corresponding bits in uncorrectable
+ * error status.
+ * We emulates uncorrectable error status register as W1CS.
+ * So set bit in uncorrectable error status here again for multiple
+ * error recording support.
+ *
+ * 6.2.4.2 Multiple Error Handling(Advanced Error Reporting Capability)
+ */
+ for (consumer = dev->exp.aer_log.consumer;
+ !aer_log_empty_index(dev->exp.aer_log.producer, consumer);
+ consumer = aer_log_next(consumer, dev->exp.aer_log.log_max)) {
+ pci_set_bit_long(aer_cap + PCI_ERR_UNCOR_STATUS,
+ dev->exp.aer_log.log[consumer].status);
+ }
+
+ err = 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 PCIEAERErr *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 */
+ PCIEAERMsg 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 + dev->exp.exp_cap;
+ if (dev->exp.aer_cap) {
+ aer_cap = dev->config + dev->exp.aer_cap;
+ 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_bit_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_bit_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_bit_long(aer_cap + PCI_ERR_UNCOR_STATUS, status);
+ return;
+ }
+
+ is_header_log_overflowed = pcie_aer_record_error(dev, err);
+ pci_set_bit_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_msg(dev, &msg);
+
+ if (is_header_log_overflowed) {
+ PCIEAERErr 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);
+ }
+}
+
+void pcie_aer_root_set_vector(PCIDevice *dev, uint8_t vector)
+{
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+ assert(vector < PCI_ERR_ROOT_IRQ_MAX);
+ pci_clear_set_bit_long(aer_cap + PCI_ERR_ROOT_STATUS,
+ PCI_ERR_ROOT_IRQ,
+ ((uint32_t)vector) << PCI_ERR_ROOT_IRQ_SHIFT);
+}
+
+static uint8_t pcie_aer_root_get_vector(PCIDevice *dev)
+{
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+ 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;
+}
+
+void pcie_aer_root_init(PCIDevice *dev)
+{
+ uint16_t pos = dev->exp.aer_cap;
+
+ pci_set_long(dev->wmask + pos + PCI_ERR_ROOT_COMMAND,
+ PCI_ERR_ROOT_CMD_EN_MASK);
+ pci_set_long(dev->w1cmask + pos + PCI_ERR_ROOT_STATUS,
+ PCI_ERR_ROOT_STATUS_REPORT_MASK);
+}
+
+void pcie_aer_root_reset(PCIDevice *dev)
+{
+ uint8_t* aer_cap = dev->config + dev->exp.aer_cap;
+
+ 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 because it's chip dependent
+ * which number is used.
+ */
+}
+
+static bool pcie_aer_root_does_trigger(uint32_t cmd, uint32_t status)
+{
+ return
+ ((cmd & PCI_ERR_ROOT_CMD_COR_EN) && (status & PCI_ERR_ROOT_COR_RCV)) ||
+ ((cmd & PCI_ERR_ROOT_CMD_NONFATAL_EN) &&
+ (status & PCI_ERR_ROOT_NONFATAL_RCV)) ||
+ ((cmd & PCI_ERR_ROOT_CMD_FATAL_EN) &&
+ (status & 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 = dev->exp.aer_cap;
+ uint8_t *aer_cap = dev->config + pos;
+
+ /* root command register */
+ uint32_t root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND);
+ if (root_cmd & PCI_ERR_ROOT_CMD_EN_MASK) {
+ /* 6.2.4.1.2 Interrupt Generation */
+
+ /* 0 -> 1 */
+ uint32_t root_cmd_set = (root_cmd_prev ^ root_cmd) & root_cmd;
+ uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS);
+
+ bool msi_trigger = pcie_aer_root_does_trigger(root_cmd_set,
+ root_status);
+ int int_level = pcie_aer_root_does_trigger(root_cmd, root_status);
+
+ pcie_aer_root_port_notify(dev, msi_trigger, int_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, PCIEAERErr),
+ VMSTATE_UINT16(source_id, PCIEAERErr),
+ VMSTATE_UINT16(flags, PCIEAERErr),
+ VMSTATE_UINT32_ARRAY(header, PCIEAERErr, 4),
+ VMSTATE_UINT32_ARRAY(prefix, PCIEAERErr, 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, PCIEAERLog),
+ VMSTATE_UINT32(consumer, PCIEAERLog),
+ VMSTATE_UINT16(log_max, PCIEAERLog),
+ VMSTATE_PCIE_AER_ERRS(log, PCIEAERLog, log_max,
+ vmstate_pcie_aer_err, PCIEAERErr),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
diff --git a/hw/pcie_aer.h b/hw/pcie_aer.h
new file mode 100644
index 0000000..7d1540b
--- /dev/null
+++ b/hw/pcie_aer.h
@@ -0,0 +1,105 @@
+/*
+ * pcie_aer.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_AER_H
+#define QEMU_PCIE_AER_H
+
+#include "hw.h"
+
+/* definitions which PCIExpressDevice uses */
+typedef enum {
+ AER_MSG_MASKED,
+ AER_MSG_SENT,
+} AERMsgResult;
+
+/* AER log */
+struct PCIEAERLog {
+ /* This structure is saved/loaded.
+ So explicitly size them instead of unsigned int */
+ 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 0xffff
+ uint16_t log_max;
+
+ PCIEAERErr *log; /* ringed buffer */
+};
+
+/* aer error severity */
+typedef enum {
+ /* 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,
+} PCIEAERSeverity;
+
+/* aer error message: error signaling message has only error sevirity and
+ source id. See 2.2.8.3 error signaling messages */
+struct PCIEAERMsg {
+ PCIEAERSeverity severity;
+ uint16_t source_id; /* bdf */
+};
+
+static inline bool
+pcie_aer_msg_is_uncor(const PCIEAERMsg *msg)
+{
+ return msg->severity == AER_ERR_NONFATAL || msg->severity == AER_ERR_FATAL;
+}
+
+/* error */
+struct PCIEAERErr {
+ 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 */
+};
+
+extern const VMStateDescription vmstate_pcie_aer_log;
+
+void 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,
+ uint32_t uncorsta_prev);
+
+/* 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 PCIEAERErr *err);
+
+#endif /* QEMU_PCIE_AER_H */
diff --git a/qemu-common.h b/qemu-common.h
index 6d9ee26..94b2c11 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -221,6 +221,9 @@ typedef struct PCIBus PCIBus;
typedef struct PCIDevice PCIDevice;
typedef struct PCIExpressDevice PCIExpressDevice;
typedef struct PCIBridge PCIBridge;
+typedef struct PCIEAERMsg PCIEAERMsg;
+typedef struct PCIEAERLog PCIEAERLog;
+typedef struct PCIEAERErr PCIEAERErr;
typedef struct SerialState SerialState;
typedef struct IRQState *qemu_irq;
typedef struct PCMCIACardState PCMCIACardState;
--
1.7.1.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH v4 10/15] pcie port: define struct PCIEPort/PCIESlot and helper functions
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
` (8 preceding siblings ...)
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 09/15] pcie/aer: helper functions for pcie aer capability Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 11/15] ioh3420: pcie root port in X58 ioh Isaku Yamahata
` (5 subsequent siblings)
15 siblings, 0 replies; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
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>
---
Changes v3 -> v4:
- Initialize prefetchable memory base/limit registers correctly.
They must support 64bit.
- compilation adjustment.
Changes v2 -> v3:
- static'fy chassis.
- compilation adjustment.
---
Makefile.objs | 2 +-
hw/pcie_port.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/pcie_port.h | 51 +++++++++++++++++++++++++
qemu-common.h | 2 +
4 files changed, 169 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 68bcc48..6c3b84a 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_aer.o
+hw-obj-y += pcie.o pcie_aer.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..6ab9261
--- /dev/null
+++ b/hw/pcie_port.c
@@ -0,0 +1,115 @@
+/*
+ * 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);
+
+ /* 7.5.3.5 Prefetchable Memory Base Limit
+ * The Prefetchable Memory Base and Prefetchable Memory Limit registers
+ * must indicate that 64-bit addresses are supported, as defined in
+ * PCI-to-PCI Bridge Architecture Specification, Revision 1.2.
+ */
+ pci_set_bit_word(d->config + PCI_PREF_MEMORY_BASE, PCI_PREF_RANGE_TYPE_64);
+ pci_set_bit_word(d->config + PCI_PREF_MEMORY_LIMIT,
+ PCI_PREF_RANGE_TYPE_64);
+}
+
+/**************************************************************************
+ * (chassis number, pcie physical slot number) -> pcie slot conversion
+ */
+struct PCIEChassis {
+ uint8_t number;
+
+ QLIST_HEAD(, PCIESlot) slots;
+ QLIST_ENTRY(PCIEChassis) next;
+};
+
+static 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 94b2c11..63ee8c3 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -224,6 +224,8 @@ typedef struct PCIBridge PCIBridge;
typedef struct PCIEAERMsg PCIEAERMsg;
typedef struct PCIEAERLog PCIEAERLog;
typedef struct PCIEAERErr PCIEAERErr;
+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] 30+ messages in thread
* [Qemu-devel] [PATCH v4 11/15] ioh3420: pcie root port in X58 ioh
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
` (9 preceding siblings ...)
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 10/15] pcie port: define struct PCIEPort/PCIESlot and helper functions Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 12/15] x3130: pcie upstream port Isaku Yamahata
` (4 subsequent siblings)
15 siblings, 0 replies; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
Implements pcie root port switch in intel X58 ioh
whose device id is 0x3420.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
Changes v3 -> v4:
- rename pcie_root -> ioh3420
- compilation adjustment.
Changes v2 -> v3:
- compilation adjustment.
---
Makefile.objs | 1 +
hw/ioh3420.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/ioh3420.h | 10 +++
3 files changed, 239 insertions(+), 0 deletions(-)
create mode 100644 hw/ioh3420.c
create mode 100644 hw/ioh3420.h
diff --git a/Makefile.objs b/Makefile.objs
index 6c3b84a..cf7d2e9 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
+hw-obj-y += ioh3420.o
hw-obj-y += watchdog.o
hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
hw-obj-$(CONFIG_ECC) += ecc.o
diff --git a/hw/ioh3420.c b/hw/ioh3420.c
new file mode 100644
index 0000000..19330c3
--- /dev/null
+++ b/hw/ioh3420.c
@@ -0,0 +1,228 @@
+/*
+ * ioh3420.c
+ * Intel X58 north bridge IOH
+ * PCI Express root port device id 3420
+ *
+ * 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 "ioh3420.h"
+
+#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
+
+/*
+ * 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 ioh3420_aer_vector(const PCIDevice *d)
+{
+ switch (msi_nr_vectors_allocated(d)) {
+ case 1:
+ return 0;
+ case 2:
+ return 1;
+ case 4:
+ case 8:
+ case 16:
+ case 32:
+ default:
+ break;
+ }
+ abort();
+ return 0;
+}
+
+static void ioh3420_aer_vector_update(PCIDevice *d)
+{
+ pcie_aer_root_set_vector(d, ioh3420_aer_vector(d));
+}
+
+static void ioh3420_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ uint16_t sltctl =
+ pci_get_word(d->config + d->exp.exp_cap + PCI_EXP_SLTCTL);
+ uint32_t uncorsta =
+ pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_UNCOR_STATUS);
+ uint32_t root_cmd =
+ pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND);
+
+ pci_bridge_write_config(d, address, val, len);
+ msi_write_config(d, address, val, len);
+ ioh3420_aer_vector_update(d);
+ pcie_cap_slot_write_config(d, address, val, len, sltctl);
+ pcie_aer_write_config(d, address, val, len, uncorsta);
+ pcie_aer_root_write_config(d, address, val, len, root_cmd);
+ pci_clear_written_write_config(d, address, val, len);
+}
+
+static void ioh3420_reset(DeviceState *qdev)
+{
+ PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
+ msi_reset(d);
+ ioh3420_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 ioh3420_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] = PCI_DEVICE_ID_IOH_REV;
+ pcie_port_init_reg(d);
+
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_IOH_EPORT);
+
+ rc = pci_bridge_ssvid_init(d, IOH_EP_SSVID_OFFSET,
+ IOH_EP_SSVID_SVID, IOH_EP_SSVID_SSID);
+ if (rc < 0) {
+ return rc;
+ }
+ rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR,
+ IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
+ IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
+ if (rc < 0) {
+ return rc;
+ }
+ rc = pcie_cap_init(d, IOH_EP_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);
+ pcie_aer_init(d, IOH_EP_AER_OFFSET);
+ pcie_aer_root_init(d);
+ ioh3420_aer_vector_update(d);
+ return 0;
+}
+
+static int ioh3420_exitfn(PCIDevice *d)
+{
+ pcie_aer_exit(d);
+ msi_uninit(d);
+ pcie_cap_exit(d);
+ return pci_bridge_exitfn(d);
+}
+
+PCIESlot *ioh3420_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, "ioh3420");
+ 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_ioh3420 = {
+ .name = "ioh-3240-express-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.exp.aer_log, PCIESlot, 0,
+ vmstate_pcie_aer_log, PCIEAERLog),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static PCIDeviceInfo ioh3420_info = {
+ .qdev.name = "ioh3420",
+ .qdev.desc = "Intel IOH device id 3420 PCIE Root Port",
+ .qdev.size = sizeof(PCIESlot),
+ .qdev.reset = ioh3420_reset,
+ .qdev.vmsd = &vmstate_ioh3420,
+
+ .is_express = 1,
+ .is_bridge = 1,
+ .config_write = ioh3420_write_config,
+ .init = ioh3420_initfn,
+ .exit = ioh3420_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.exp.aer_log.log_max,
+ PCIE_AER_LOG_MAX_DEFAULT),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void ioh3420_register(void)
+{
+ pci_qdev_register(&ioh3420_info);
+}
+
+device_init(ioh3420_register);
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tab-mode: nil
+ * End:
+ */
diff --git a/hw/ioh3420.h b/hw/ioh3420.h
new file mode 100644
index 0000000..68c523a
--- /dev/null
+++ b/hw/ioh3420.h
@@ -0,0 +1,10 @@
+#ifndef QEMU_IOH3420_H
+#define QEMU_IOH3420_H
+
+#include "pcie_port.h"
+
+PCIESlot *ioh3420_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_IOH3420_H */
--
1.7.1.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH v4 12/15] x3130: pcie upstream port
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
` (10 preceding siblings ...)
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 11/15] ioh3420: pcie root port in X58 ioh Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 4:59 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 13/15] x3130: pcie downstream port Isaku Yamahata
` (3 subsequent siblings)
15 siblings, 1 reply; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
Implement TI x3130 pcie upstream port switch.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
Chnages v3 -> v4:
- rename pcie_upstream -> x3130_upstream.
- compilation adjustment.
Changes v2 -> v3:
- compilation adjustment.
---
Makefile.objs | 2 +-
hw/xio3130_upstream.c | 188 +++++++++++++++++++++++++++++++++++++++++++++++++
hw/xio3130_upstream.h | 10 +++
3 files changed, 199 insertions(+), 1 deletions(-)
create mode 100644 hw/xio3130_upstream.c
create mode 100644 hw/xio3130_upstream.h
diff --git a/Makefile.objs b/Makefile.objs
index cf7d2e9..d61e88a 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -140,7 +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
-hw-obj-y += ioh3420.o
+hw-obj-y += ioh3420.o xio3130_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/xio3130_upstream.c b/hw/xio3130_upstream.c
new file mode 100644
index 0000000..5c5deb2
--- /dev/null
+++ b/hw/xio3130_upstream.c
@@ -0,0 +1,188 @@
+/*
+ * xio3130_upstream.c
+ * TI X3130 pci express upstream port switch
+ *
+ * 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 "xio3130_upstream.h"
+
+#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
+
+static void xio3130_upstream_write_config(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ uint32_t uncorsta =
+ pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_UNCOR_STATUS);
+
+ pci_bridge_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(d, address, val, len, uncorsta);
+ pci_clear_written_write_config(d, address, val, len);
+}
+
+static void xio3130_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 xio3130_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 xio3130_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, PCI_VENDOR_ID_TI);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_TI_XIO3130U);
+ d->config[PCI_REVISION_ID] = XIO3130_REVISION;
+
+ rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
+ XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
+ XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
+ if (rc < 0) {
+ return rc;
+ }
+ rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
+ XIO3130_SSVID_SVID, XIO3130_SSVID_SSID);
+ if (rc < 0) {
+ return rc;
+ }
+ rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM,
+ p->port);
+ if (rc < 0) {
+ return rc;
+ }
+ pcie_cap_flr_init(d, &xio3130_upstream_flr);
+ pcie_cap_deverr_init(d);
+ pcie_aer_init(d, XIO3130_AER_OFFSET);
+
+ return 0;
+}
+
+static int xio3130_upstream_exitfn(PCIDevice *d)
+{
+ pcie_aer_exit(d);
+ msi_uninit(d);
+ pcie_cap_exit(d);
+ return pci_bridge_exitfn(d);
+}
+
+PCIEPort *xio3130_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, "x3130-upstream");
+ 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_xio3130_upstream = {
+ .name = "xio3130-express-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.exp.aer_log, PCIEPort, 0, vmstate_pcie_aer_log,
+ PCIEAERLog),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static PCIDeviceInfo xio3130_upstream_info = {
+ .qdev.name = "x3130-upstream",
+ .qdev.desc = "TI X3130 Upstream Port of PCI Express Switch",
+ .qdev.size = sizeof(PCIEPort),
+ .qdev.reset = xio3130_upstream_reset,
+ .qdev.vmsd = &vmstate_xio3130_upstream,
+
+ .is_express = 1,
+ .is_bridge = 1,
+ .config_write = xio3130_upstream_write_config,
+ .init = xio3130_upstream_initfn,
+ .exit = xio3130_upstream_exitfn,
+
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT8("port", PCIEPort, port, 0),
+ DEFINE_PROP_UINT16("aer_log_max", PCIEPort, br.dev.exp.aer_log.log_max,
+ PCIE_AER_LOG_MAX_DEFAULT),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void xio3130_upstream_register(void)
+{
+ pci_qdev_register(&xio3130_upstream_info);
+}
+
+device_init(xio3130_upstream_register);
+
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tab-mode: nil
+ * End:
+ */
diff --git a/hw/xio3130_upstream.h b/hw/xio3130_upstream.h
new file mode 100644
index 0000000..e996997
--- /dev/null
+++ b/hw/xio3130_upstream.h
@@ -0,0 +1,10 @@
+#ifndef QEMU_XIO3130_UPSTREAM_H
+#define QEMU_XIO3130_UPSTREAM_H
+
+#include "pcie_port.h"
+
+PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction,
+ const char *bus_name, pci_map_irq_fn map_irq,
+ uint8_t port);
+
+#endif /* QEMU_XIO3130_H */
--
1.7.1.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH v4 13/15] x3130: pcie downstream port
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
` (11 preceding siblings ...)
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 12/15] x3130: pcie upstream port Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 14/15] pcie/hotplug: introduce pushing attention button command Isaku Yamahata
` (2 subsequent siblings)
15 siblings, 0 replies; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
Implement TI x3130 pcie downstream port switch.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
Changes v3 -> v4:
- rename: pcie_downstream -> x3130_downstream
- compilation adjustment.
Changes v2 -> v3:
- compilation adjustment.
---
Makefile.objs | 2 +-
hw/xio3130_downstream.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++
hw/xio3130_downstream.h | 11 +++
3 files changed, 219 insertions(+), 1 deletions(-)
create mode 100644 hw/xio3130_downstream.c
create mode 100644 hw/xio3130_downstream.h
diff --git a/Makefile.objs b/Makefile.objs
index d61e88a..48f98f3 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -140,7 +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
-hw-obj-y += ioh3420.o xio3130_upstream.o
+hw-obj-y += ioh3420.o xio3130_upstream.o xio3130_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/xio3130_downstream.c b/hw/xio3130_downstream.c
new file mode 100644
index 0000000..beb37df
--- /dev/null
+++ b/hw/xio3130_downstream.c
@@ -0,0 +1,207 @@
+/*
+ * x3130_downstream.c
+ * TI X3130 pci express downstream port switch
+ *
+ * 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 "xio3130_downstream.h"
+
+#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
+
+static void xio3130_downstream_write_config(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ uint16_t sltctl =
+ pci_get_word(d->config + d->exp.exp_cap + PCI_EXP_SLTCTL);
+ uint32_t uncorsta =
+ pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_UNCOR_STATUS);
+
+ pci_bridge_write_config(d, address, val, len);
+ pcie_cap_flr_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(d, address, val, len, uncorsta);
+ pci_clear_written_write_config(d, address, val, len);
+}
+
+static void xio3130_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 xio3130_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 xio3130_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, PCI_VENDOR_ID_TI);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_TI_XIO3130D);
+ d->config[PCI_REVISION_ID] = XIO3130_REVISION;
+
+ rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
+ XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
+ XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
+ if (rc < 0) {
+ return rc;
+ }
+ rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
+ XIO3130_SSVID_SVID, XIO3130_SSVID_SSID);
+ if (rc < 0) {
+ return rc;
+ }
+ rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM,
+ p->port);
+ if (rc < 0) {
+ return rc;
+ }
+ pcie_cap_flr_init(d, &xio3130_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);
+ pcie_aer_init(d, XIO3130_AER_OFFSET);
+
+ return 0;
+}
+
+static int xio3130_downstream_exitfn(PCIDevice *d)
+{
+ pcie_aer_exit(d);
+ msi_uninit(d);
+ pcie_cap_exit(d);
+ return pci_bridge_exitfn(d);
+}
+
+PCIESlot *xio3130_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,
+ "xio3130-downstream");
+ 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_xio3130_downstream = {
+ .name = "xio3130-express-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.exp.aer_log, PCIESlot, 0,
+ vmstate_pcie_aer_log, PCIEAERLog),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static PCIDeviceInfo xio3130_downstream_info = {
+ .qdev.name = "xio3130-downstream",
+ .qdev.desc = "TI X3130 Downstream Port of PCI Express Switch",
+ .qdev.size = sizeof(PCIESlot),
+ .qdev.reset = xio3130_downstream_reset,
+ .qdev.vmsd = &vmstate_xio3130_downstream,
+
+ .is_express = 1,
+ .is_bridge = 1,
+ .config_write = xio3130_downstream_write_config,
+ .init = xio3130_downstream_initfn,
+ .exit = xio3130_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.exp.aer_log.log_max,
+ PCIE_AER_LOG_MAX_DEFAULT),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void xio3130_downstream_register(void)
+{
+ pci_qdev_register(&xio3130_downstream_info);
+}
+
+device_init(xio3130_downstream_register);
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tab-mode: nil
+ * End:
+ */
diff --git a/hw/xio3130_downstream.h b/hw/xio3130_downstream.h
new file mode 100644
index 0000000..010487f
--- /dev/null
+++ b/hw/xio3130_downstream.h
@@ -0,0 +1,11 @@
+#ifndef QEMU_XIO3130_DOWNSTREAM_H
+#define QEMU_XIO3130_DOWNSTREAM_H
+
+#include "pcie_port.h"
+
+PCIESlot *xio3130_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_XIO3130_DOWNSTREAM_H */
--
1.7.1.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [Qemu-devel] [PATCH v4 14/15] pcie/hotplug: introduce pushing attention button command
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
` (12 preceding siblings ...)
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 13/15] x3130: pcie downstream port Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 15/15] pcie/aer: glue aer error injection into qemu monitor Isaku Yamahata
2010-10-18 6:24 ` [Qemu-devel] Re: [PATCH v4 00/15] pcie port switch emulators Michael S. Tsirkin
15 siblings, 0 replies; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
glue pcie_push_attention_button 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 6ab9261..a7bd206 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)
@@ -113,3 +117,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..965c754 100644
--- a/qemu-monitor.hx
+++ b/qemu-monitor.hx
@@ -1154,6 +1154,20 @@ Hot remove PCI device.
ETEXI
{
+ .name = "pcie_push_attention_button",
+ .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] 30+ messages in thread
* [Qemu-devel] [PATCH v4 15/15] pcie/aer: glue aer error injection into qemu monitor
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
` (13 preceding siblings ...)
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 14/15] pcie/hotplug: introduce pushing attention button command Isaku Yamahata
@ 2010-10-18 3:17 ` Isaku Yamahata
2010-10-18 6:24 ` [Qemu-devel] Re: [PATCH v4 00/15] pcie port switch emulators Michael S. Tsirkin
15 siblings, 0 replies; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 3:17 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
introduce pcie_aer_inject_error command.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
Changes v3 -> v4:
- s/PCIE_AER/PCIEAER/g for structure names.
- compilation adjustment.
Changes v2 -> v3:
- compilation adjustment.
---
hw/pcie_aer.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
qemu-monitor.hx | 22 ++++++++++++++
sysemu.h | 5 +++
3 files changed, 111 insertions(+), 0 deletions(-)
diff --git a/hw/pcie_aer.c b/hw/pcie_aer.c
index c86f2b9..132a0a9 100644
--- a/hw/pcie_aer.c
+++ b/hw/pcie_aer.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"
@@ -787,3 +789,85 @@ const VMStateDescription vmstate_pcie_aer_log = {
}
};
+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;
+ PCIEAERErr 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 965c754..ccb3d0e 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] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 12/15] x3130: pcie upstream port
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 12/15] x3130: pcie upstream port Isaku Yamahata
@ 2010-10-18 4:59 ` Michael S. Tsirkin
0 siblings, 0 replies; 30+ messages in thread
From: Michael S. Tsirkin @ 2010-10-18 4:59 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 12:17:53PM +0900, Isaku Yamahata wrote:
> +static void xio3130_upstream_flr(PCIDevice *d)
> +{
> + /* TODO: not enabled until qdev reset clean up
> + waiting for Anthony's qdev cealn up */
typo
> +#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
> +}
--
MST
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 08/15] pcie: helper functions for pcie capability and extended capability
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 08/15] pcie: helper functions for pcie capability and extended capability Isaku Yamahata
@ 2010-10-18 5:38 ` Michael S. Tsirkin
0 siblings, 0 replies; 30+ messages in thread
From: Michael S. Tsirkin @ 2010-10-18 5:38 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 12:17:49PM +0900, Isaku Yamahata wrote:
> This patch implements helper functions for pci express capability
> and pci express extended capability allocation.
> NOTE: presence detection depends on pci_qdev_init() change.
>
> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
OK, this is getting good.
> ---
> Changes v3 -> v4:
> - various clean up
> - dropped pcie_notify(), pcie_del_capability()
> - use pci_{clear_set, clear}_bit_xxx() helper functions.
> - dropped pci_exp_cap()
> ---
> Makefile.objs | 1 +
> hw/pci.h | 5 +
> hw/pcie.c | 529 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> hw/pcie.h | 107 ++++++++++++
> qemu-common.h | 1 +
> 5 files changed, 643 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 3dc2099..3c9c228 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))
> @@ -178,6 +180,9 @@ struct PCIDevice {
> /* Offset of MSI capability in config space */
> uint8_t msi_cap;
>
> + /* PCI Express */
> + PCIExpressDevice exp;
> +
> /* Location of option rom */
> char *romfile;
> ram_addr_t rom_offset;
> diff --git a/hw/pcie.c b/hw/pcie.c
> new file mode 100644
> index 0000000..0f2bf50
> --- /dev/null
> +++ b/hw/pcie.c
> @@ -0,0 +1,529 @@
> +/*
> + * 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"
> +#include "pcie_regs.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__)
> +
> +
> +/***************************************************************************
> + * pci express capability helper functions
> + */
> +int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port)
> +{
> + int pos;
> + uint8_t *exp_cap;
> +
> + assert(pci_is_express(dev));
> +
> + pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset,
> + PCI_EXP_VER2_SIZEOF);
> + if (pos < 0) {
> + return pos;
> + }
> + dev->exp.exp_cap = pos;
> + exp_cap = dev->config + pos;
> +
> + /* capability register
> + interrupt message number defaults to 0 */
> + pci_set_word(exp_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(exp_cap + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER);
> +
> + pci_set_long(exp_cap + PCI_EXP_LNKCAP,
> + (port << PCI_EXP_LNKCAP_PN_SHIFT) |
> + PCI_EXP_LNKCAP_ASPMS_0S |
> + PCI_EXP_LNK_MLW_1 |
> + PCI_EXP_LNK_LS_25);
> +
> + pci_set_word(exp_cap + PCI_EXP_LNKSTA,
> + PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25);
> +
> + pci_set_long(exp_cap + PCI_EXP_DEVCAP2,
> + PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP);
> +
> + pci_set_word(dev->wmask + pos, PCI_EXP_DEVCTL2_EETLPPB);
> + return pos;
> +}
> +
> +void pcie_cap_exit(PCIDevice *dev)
> +{
> + pci_del_capability(dev, PCI_CAP_ID_EXP, PCI_EXP_VER2_SIZEOF);
> +}
> +
> +uint8_t pcie_cap_get_type(const PCIDevice *dev)
> +{
> + uint32_t pos = dev->exp.exp_cap;
> + 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 */
> +/* 7.8.2 PCI Express Capabilities Register: Interrupt Message Number */
> +void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector)
> +{
> + assert(vector < 32);
> + pci_clear_set_bit_word(dev->config + dev->exp.exp_cap + PCI_EXP_FLAGS,
> + PCI_EXP_FLAGS_IRQ,
> + vector << PCI_EXP_FLAGS_IRQ_SHIFT);
> +}
> +
> +uint8_t pcie_cap_flags_get_vector(PCIDevice *dev)
> +{
> + return (pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_FLAGS) &
> + PCI_EXP_FLAGS_IRQ) >> PCI_EXP_FLAGS_IRQ_SHIFT;
> +}
> +
> +void pcie_cap_deverr_init(PCIDevice *dev)
> +{
> + uint32_t pos = dev->exp.exp_cap;
> + pci_set_bit_long(dev->config + pos + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER);
> + pci_set_bit_long(dev->wmask + pos + PCI_EXP_DEVCTL,
> + PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
> + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
> + pci_set_bit_long(dev->w1cmask + pos + PCI_EXP_DEVSTA,
> + PCI_EXP_DEVSTA_CED | PCI_EXP_DEVSTA_NFED |
> + PCI_EXP_DEVSTA_URD | PCI_EXP_DEVSTA_URD);
> +}
> +
> +void pcie_cap_deverr_reset(PCIDevice *dev)
> +{
> + pci_clear_bit_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL,
> + PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
> + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
> +}
> +
> +static void pcie_cap_slot_notify(PCIDevice *dev,
> + bool msi_trigger, int int_level)
IMO this is not a good API because oly one msi/int level is ever used.
Can this get slot control/event and figure out what to do itself?
> +{
> + unsigned int msi_vector = pcie_cap_flags_get_vector(dev);
> + unsigned int int_irq = 0; /* INTA# is chosen for slot event notification */
For the specific devices we implement, right?
I think this needs a TODO, other devices might
need to use another interrupt.
> +
> + if (msix_enabled(dev)) {
> + if (msi_trigger) {
> + msix_notify(dev, msi_vector);
> + }
> + } else if (msi_enabled(dev)) {
> + if (msi_trigger) {
> + msi_notify(dev, msi_vector);
> + }
> + } else {
> + qemu_set_irq(dev->irq[int_irq], int_level);
> + }
> +}
> +
> +/*
> + * A PCI Express Hot-Plug Event has occured, so update slot status register
> + * and notify OS of the event if necessary.
> + *
> + * 6.7.3 PCI Express Hot-Plug Events
> + * 6.7.3.4 Software Notification of Hot-Plug Events
> + */
> +static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event)
> +{
> + uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
> + uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL);
> + uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
> + bool msi_trigger;
> + int int_level;
> +
> + PCIE_DEV_PRINTF(dev,
> + "sltctl: 0x%02"PRIx16" sltsta: 0x%02"PRIx16" event: %x\n",
> + sltctl, sltsta, event);
> +
> + msi_trigger = (sltctl & PCI_EXP_SLTCTL_HPIE) && (sltctl & event) &&
> + ((sltsta ^ event) & event); /* 0 -> 1 */
> +
> + sltsta |= event;
> + pci_set_word(exp_cap + PCI_EXP_SLTSTA, sltsta);
> + PCIE_DEV_PRINTF(dev, "sltsta -> %02"PRIx16"\n", sltsta);
> +
> + int_level = ((sltctl & PCI_EXP_SLTCTL_HPIE) &&
> + (sltsta & PCI_EXP_HP_EV_SUPPORTED));
int_level ignores whether event is masked in sltctl?
> +
> + pcie_cap_slot_notify(dev, msi_trigger, int_level);
Both places must test HPIE.
I suggest simply moving this test into pcie_cap_slot_notify:
if (!sltctl & PCI_EXP_SLTCTL_HPIE) {
return;
}
> +}
> +
> +static int pcie_cap_slot_hotplug(DeviceState *qdev,
> + PCIDevice *pci_dev, int state)
> +{
> + PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
> + uint8_t *exp_cap = d->config + d->exp.exp_cap;
> + uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
> +
> + if (!pci_dev->qdev.hotplugged) {
> + assert(state); /* this case only happens at machine creation. */
> + pci_set_bit_word(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS);
> + return 0;
> + }
> +
> + PCIE_DEV_PRINTF(pci_dev, "hotplug state: %d\n", state);
> + if (sltsta & PCI_EXP_SLTSTA_EIS) {
> + /* the slot is electromechanically locked.
> + * This error is propagated up to qdev and then to HMP/QMP.
> + */
> + return -EBUSY;
> + }
> +
> + /* TODO: multifunction hot-plug.
> + * Right now, only a device of function = 0 is allowed to be
> + * hot plugged/unplugged.
> + */
> + assert(PCI_FUNC(pci_dev->devfn) == 0);
> +
> + if (state) {
> + pci_set_bit_word(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS);
> + pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC);
> + } else {
> + qdev_free(&pci_dev->qdev);
> + pci_clear_bit_word(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS);
> + pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC);
> + }
> + 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)
> +{
> + uint32_t pos = dev->exp.exp_cap;
> +
> + pci_set_bit_word(dev->config + pos + PCI_EXP_FLAGS, PCI_EXP_FLAGS_SLOT);
> +
> + pci_clear_set_bit_long(dev->config + pos + PCI_EXP_SLTCAP,
> + ~PCI_EXP_SLTCAP_PSN,
> + (slot << PCI_EXP_SLTCAP_PSN_SHIFT) |
> + 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_clear_set_bit_word(dev->config + pos + PCI_EXP_SLTCTL,
> + PCI_EXP_SLTCTL_PIC | PCI_EXP_SLTCTL_AIC,
> + PCI_EXP_SLTCTL_PIC_OFF | PCI_EXP_SLTCTL_AIC_OFF);
> + pci_set_bit_word(dev->wmask + pos + 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_set_bit_word(dev->w1cmask + pos + PCI_EXP_SLTSTA,
> + PCI_EXP_HP_EV_SUPPORTED);
> +
> + 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 *exp_cap = dev->config + dev->exp.exp_cap;
> +
> + PCIE_DEV_PRINTF(dev, "reset\n");
> +
> + pci_clear_set_bit_word(exp_cap + PCI_EXP_SLTCTL,
> + 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,
> + PCI_EXP_SLTCTL_PIC_OFF | PCI_EXP_SLTCTL_AIC_OFF);
> +
> + pci_clear_bit_word(exp_cap + PCI_EXP_SLTSTA,
> + PCI_EXP_SLTSTA_EIS |/* by reset, the lock is released */
> + PCI_EXP_SLTSTA_CC |
> + PCI_EXP_SLTSTA_PDC |
> + PCI_EXP_SLTSTA_ABP);
> +}
> +
> +void pcie_cap_slot_write_config(PCIDevice *dev,
> + uint32_t addr, uint32_t val, int len,
> + uint16_t sltctl_prev)
> +{
> + uint32_t pos = dev->exp.exp_cap;
> + uint8_t *exp_cap = dev->config + pos;
> + uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL);
> + uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
> +
> + PCIE_DEV_PRINTF(dev,
> + "addr: 0x%"PRIx32" val: 0x%"PRIx32" len: %d\n"
> + "\tsltctl_prev: 0x%02"PRIx16" sltctl: 0x%02"PRIx16
> + " sltsta: 0x%02"PRIx16"\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)) {
> + sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
> +
> + /* write to stlsta results in clearing bits,
> + so new interrupts won't be generated. */
> + PCIE_DEV_PRINTF(dev, "sltsta -> 0x%02"PRIx16"\n", sltsta);
> + }
> +
above does not do anything.
> + /* SLTCTL */
> + if (ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) {
> + PCIE_DEV_PRINTF(dev, "sltctl: 0x%02"PRIx16" -> 0x%02"PRIx16"\n",
> + sltctl_prev, sltctl);
> + if (pci_get_word(dev->written + pos + PCI_EXP_SLTCTL) &
> + PCI_EXP_SLTCTL_EIC) {
> + sltsta ^= PCI_EXP_SLTSTA_EIS; /* toggle PCI_EXP_SLTSTA_EIS bit */
> + pci_set_word(exp_cap + PCI_EXP_SLTSTA, sltsta);
> + PCIE_DEV_PRINTF(dev, "PCI_EXP_SLTCTL_EIC: "
> + "sltsta -> 0x%02"PRIx16"\n",
> + sltsta);
> + }
Make EIC writeable then you will not need ranges_overlap
and will not need written register:
w = pci_get_word(dev->config + pos + PCI_EXP_SLTCTL) &
PCI_EXP_SLTCTL_EIC) {
pci_set_word(dev->config + pos + PCI_EXP_SLTCTL,
w & ~PCI_EXP_SLTCTL_EIC);
}
> +
> + /*
> + * The events might be enabled or disabled,
> + * Check if the software notificastion condition is satisfied
> + * or disatisfied.
> + *
> + * 6.7.3.4 Software Notification of Hot-plug events
> + */
> + if (sltctl & PCI_EXP_SLTCTL_HPIE) {
> + bool msi_trigger =
> + (sltctl_prev ^ sltctl) & sltctl & /* 0 -> 1 */
> + sltsta & PCI_EXP_HP_EV_SUPPORTED;
> + int int_level = sltctl & sltsta & PCI_EXP_HP_EV_SUPPORTED;
> +
> + pcie_cap_slot_notify(dev, msi_trigger, int_level);
> + }
What happens if level is 1 (Asserted) and user clears
PCI_EXP_SLTCTL_HPIE?
Why can not this function use pcie_cap_slot_event?
There seems to be duplicated logic here.
> +
> + if (!((sltctl_prev ^ sltctl) & PCI_EXP_SLTCTL_SUPPORTED)) {
> + PCIE_DEV_PRINTF(dev,
> + "sprious command completion slctl "
> + "0x%"PRIx16" -> 0x%"PRIx16"\n",
> + sltctl_prev, sltctl);
> + }
> +
> + /* command completion.
> + * Real hardware might take a while to complete
> + * requested command because physical movement would be involved
> + * like locking the electromechanical lock.
> + * However in our case, command is completed instantaneously above,
> + * so send a command completion event right now.
> + *
> + * 6.7.3.2 Command Completed Events
> + */
> + /* set command completed bit */
> + pcie_cap_slot_event(dev, PCI_EXP_HP_EV_CCI);
> + }
> +}
> +
> +void pcie_cap_slot_push_attention_button(PCIDevice *dev)
> +{
> + pcie_cap_slot_event(dev, PCI_EXP_HP_EV_ABP);
> +}
> +
> +/* root control/capabilities/status. PME isn't emulated for now */
> +void pcie_cap_root_init(PCIDevice *dev)
> +{
> + pci_set_word(dev->wmask + dev->exp.exp_cap + PCI_EXP_RTCTL,
> + PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE |
> + PCI_EXP_RTCTL_SEFEE);
> +}
> +
> +void pcie_cap_root_reset(PCIDevice *dev)
> +{
> + pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_RTCTL, 0);
> +}
> +
> +/* function level reset(FLR) */
> +void pcie_cap_flr_init(PCIDevice *dev, pcie_flr_fn flr)
> +{
> + pci_set_bit_long(dev->config + dev->exp.exp_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)
> +{
> + /* if ranges_overlap(addr, len, pos + PCI_EXP_DEVCTL, 2) is false,
> + written == 0 */
> + uint16_t written = pci_get_word(dev->written +
> + dev->exp.exp_cap + PCI_EXP_DEVCTL);
> + if ((written & PCI_EXP_DEVCTL_BCR_FLR) && dev->exp.flr) {
> + dev->exp.flr(dev);
> + }
> +}
So if flr is NULL you don't do anything on FLR? This is obviously
wrong, isn't it? But we will only see when this is made to really work.
For now, just comment this all out as you are waiting for infrastructure
to be implemented, and make bit simple RO 0.
Long term, make flr bit writeable and
uint16_t written = pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL);
if ((written & PCI_EXP_DEVCTL_BCR_FLR)) {
/* TODO: reset device */
pci_set_word(dev->config + dev->exp.exp_cap,
written & ~PCI_EXP_DEVCTL_BCR_FLR);
}
> +
> +/* Alternative Routing-ID Interpretation (ARI) */
> +/* ari forwarding support for down stream port */
> +void pcie_cap_ari_init(PCIDevice *dev)
> +{
> + uint32_t pos = dev->exp.exp_cap;
> + pci_set_bit_long(dev->config + pos + PCI_EXP_DEVCAP2, PCI_EXP_DEVCAP2_ARI);
> + pci_set_bit_long(dev->wmask + pos + PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_ARI);
> +}
> +
> +void pcie_cap_ari_reset(PCIDevice *dev)
> +{
> + pci_clear_bit_long(dev->config + dev->exp.exp_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 (!dev->exp.exp_cap) {
> + return false;
> + }
> +
> + return pci_get_long(dev->config + dev->exp.exp_cap + 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
> + */
> +
> +static uint16_t pcie_find_capability_list(PCIDevice *dev, uint16_t cap_id,
> + uint16_t *prev_p)
> +{
> + uint16_t prev = 0;
> + uint16_t next;
> + uint32_t header = pci_get_long(dev->config + PCI_CONFIG_SPACE_SIZE);
> +
> + if (!header) {
> + /* no extended capability */
> + next = 0;
> + goto out;
> + }
> + for (next = PCI_CONFIG_SPACE_SIZE; next;
> + prev = next, next = PCI_EXT_CAP_NEXT(header)) {
> +
> + assert(next >= PCI_CONFIG_SPACE_SIZE);
> + assert(next <= PCIE_CONFIG_SPACE_SIZE - 8);
> +
> + header = pci_get_long(dev->config + next);
> + if (PCI_EXT_CAP_ID(header) == cap_id) {
> + break;
> + }
> + }
> +
> +out:
> + if (prev_p) {
> + *prev_p = prev;
> + }
> + return next;
> +}
> +
> +uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id)
> +{
> + return pcie_find_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);
> +}
> +
> +/*
> + * caller must supply valid (offset, size) * such that the range shouldn't
> + * overlap with other capability or other registers.
> + * This function doesn't check it.
> + */
> +void pcie_add_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 >= PCI_CONFIG_SPACE_SIZE);
> + assert(offset < offset + size);
> + assert(offset + size < PCIE_CONFIG_SPACE_SIZE);
> + assert(size >= 8);
> + assert(pci_is_express(dev));
> +
> + if (offset == PCI_CONFIG_SPACE_SIZE) {
> + header = pci_get_long(dev->config + offset);
> + next = PCI_EXT_CAP_NEXT(header);
> + } else {
> + uint16_t prev;
> +
> + /* 0 is reserved cap id. use internally to find the last capability
> + in the linked list */
> + next = pcie_find_capability_list(dev, 0, &prev);
> +
> + assert(prev >= PCI_CONFIG_SPACE_SIZE);
> + assert(next == 0);
> + pcie_ext_cap_set_next(dev, prev, offset);
> + }
> + pci_set_long(dev->config + offset, PCI_EXT_CAP(cap_id, cap_ver, next));
> +
> + /* Make capability read-only by default */
> + memset(dev->wmask + offset, 0, size);
> + memset(dev->w1cmask + offset, 0, size);
> + /* Check capability by default */
> + memset(dev->cmask + offset, 0xFF, size);
> +}
> +
> +/**************************************************************************
> + * pci express extended capability helper functions
> + */
> +
> +/* ARI */
> +void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn)
> +{
> + pcie_add_capability(dev, PCI_EXT_CAP_ID_ARI, PCI_ARI_VER,
> + offset, PCI_ARI_SIZEOF);
> + pci_set_long(dev->config + offset + PCI_ARI_CAP, PCI_ARI_CAP_NFN(nextfn));
> +}
> diff --git a/hw/pcie.h b/hw/pcie.h
> new file mode 100644
> index 0000000..603bb8b
> --- /dev/null
> +++ b/hw/pcie.h
> @@ -0,0 +1,107 @@
> +/*
> + * 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"
> +#include "pci_regs.h"
> +#include "pcie_regs.h"
> +
> +typedef enum {
> + /* for attention and power indicator */
> + PCI_EXP_HP_IND_RESERVED = PCI_EXP_SLTCTL_IND_RESERVED,
> + PCI_EXP_HP_IND_ON = PCI_EXP_SLTCTL_IND_ON,
> + PCI_EXP_HP_IND_BLINK = PCI_EXP_SLTCTL_IND_BLINK,
> + PCI_EXP_HP_IND_OFF = PCI_EXP_SLTCTL_IND_OFF,
> +} PCIExpressIndicator;
> +
> +typedef enum {
> + /* these bits must match the bits in Slot Control/Status registers.
> + * PCI_EXP_HP_EV_xxx = PCI_EXP_SLTCTL_xxxE = PCI_EXP_SLTSTA_xxx
> + *
> + * Not all the bits of slot control register match with the ones of
> + * slot status. Not some bits of slot status register is used to
> + * show status, not to report event occurence.
> + * So such bits must be masked out when checking the software
> + * notification condition.
> + */
> + PCI_EXP_HP_EV_ABP = PCI_EXP_SLTCTL_ABPE,
> + /* attention button pressed */
> + PCI_EXP_HP_EV_PDC = PCI_EXP_SLTCTL_PDCE,
> + /* presence detect changed */
> + PCI_EXP_HP_EV_CCI = PCI_EXP_SLTCTL_CCIE,
> + /* command completed */
> +
> + PCI_EXP_HP_EV_SUPPORTED = PCI_EXP_HP_EV_ABP |
> + PCI_EXP_HP_EV_PDC |
> + PCI_EXP_HP_EV_CCI,
> + /* supported event mask */
> +
> + /* events not listed aren't supported */
> +} PCIExpressHotPlugEvent;
> +
> +typedef void (*pcie_flr_fn)(PCIDevice *dev);
> +
> +struct PCIExpressDevice {
> + /* Offset of express capability in config space */
> + uint8_t exp_cap;
> +
> + /* FLR */
> + pcie_flr_fn flr;
> +};
>From what I saw, sticky registers are mostly standard so
even when implemented they will be common, not per device.
As suggested previously, let's just invoke reset callback,
devices can test FLR bit if they like to do things
differently.
But it's hard to say, as currently this is just dead code
all commneted out.
Let's not have any callback for now, and add it later when
infrastructure is in place and we see there is in fact
a lot of device specific code.
--
MST
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 04/15] pci: record which is written into pci configuration space
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 04/15] pci: record which is written into pci configuration space Isaku Yamahata
@ 2010-10-18 5:38 ` Michael S. Tsirkin
2010-10-18 7:17 ` Isaku Yamahata
0 siblings, 1 reply; 30+ messages in thread
From: Michael S. Tsirkin @ 2010-10-18 5:38 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 12:17:45PM +0900, Isaku Yamahata wrote:
> record which is written into pci configuration space.
> introduce helper function to zero PCIDevice::written.
> They will be used later.
>
> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
This really exposes an internal variable.
I really dislike this, and I don't think it's needed
at all: just make the bit writeable.
Commented on appropriate patches.
> ---
> hw/pci.c | 10 ++++++++++
> hw/pci.h | 5 +++++
> 2 files changed, 15 insertions(+), 0 deletions(-)
>
> diff --git a/hw/pci.c b/hw/pci.c
> index 5954476..eca9324 100644
> --- a/hw/pci.c
> +++ b/hw/pci.c
> @@ -627,6 +627,7 @@ static void pci_config_alloc(PCIDevice *pci_dev)
> pci_dev->cmask = qemu_mallocz(config_size);
> pci_dev->wmask = qemu_mallocz(config_size);
> pci_dev->w1cmask = qemu_mallocz(config_size);
> + pci_dev->written = qemu_mallocz(config_size);
> pci_dev->used = qemu_mallocz(config_size);
> }
>
> @@ -636,6 +637,7 @@ static void pci_config_free(PCIDevice *pci_dev)
> qemu_free(pci_dev->cmask);
> qemu_free(pci_dev->wmask);
> qemu_free(pci_dev->w1cmask);
> + qemu_free(pci_dev->written);
> qemu_free(pci_dev->used);
> }
>
> @@ -1002,6 +1004,8 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
> assert(!(wmask & w1cmask));
> d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask);
> d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */
> + d->written[addr + i] = val; /* record what is written for driver
> + specific code */
> }
> if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) ||
> ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) ||
> @@ -1013,6 +1017,12 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
> pci_update_irq_disabled(d, was_irq_disabled);
> }
>
> +void pci_clear_written_write_config(PCIDevice *d,
> + uint32_t addr, uint32_t val, int l)
> +{
> + memset(d->written + addr, 0, l);
> +}
> +
> /***********************************************************/
> /* generic PCI irq support */
>
> diff --git a/hw/pci.h b/hw/pci.h
> index eafa9f3..7097817 100644
> --- a/hw/pci.h
> +++ b/hw/pci.h
> @@ -132,6 +132,9 @@ struct PCIDevice {
> /* Used to implement RW1C(Write 1 to Clear) bytes */
> uint8_t *w1cmask;
>
> + /* Used to record what value is written */
> + uint8_t *written;
> +
> /* Used to allocate config space for capabilities. */
> uint8_t *used;
>
> @@ -200,6 +203,8 @@ uint32_t pci_default_read_config(PCIDevice *d,
> uint32_t address, int len);
> void pci_default_write_config(PCIDevice *d,
> uint32_t address, uint32_t val, int len);
> +void pci_clear_written_write_config(PCIDevice *d,
> + uint32_t addr, uint32_t val, int l);
> void pci_device_save(PCIDevice *s, QEMUFile *f);
> int pci_device_load(PCIDevice *s, QEMUFile *f);
>
> --
> 1.7.1.1
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 02/15] pci: introduce helper functions to clear/set bits in configuration space
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 02/15] pci: introduce helper functions to clear/set bits in configuration space Isaku Yamahata
@ 2010-10-18 5:42 ` Michael S. Tsirkin
2010-10-18 6:32 ` Michael S. Tsirkin
1 sibling, 0 replies; 30+ messages in thread
From: Michael S. Tsirkin @ 2010-10-18 5:42 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 12:17:43PM +0900, Isaku Yamahata wrote:
> This patch introduces helper functions to clear/set bits in configuration
> space. pci_{clear_set, clear, set}_bit_{byte, word, long, quad}().
> They will be used later.
>
> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
I don't think we want clear_set variant.
It is just confusing, simply calling
clear and then set will be better.
> ---
> hw/pci.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 72 insertions(+), 0 deletions(-)
>
> diff --git a/hw/pci.h b/hw/pci.h
> index d8b399f..eafa9f3 100644
> --- a/hw/pci.h
> +++ b/hw/pci.h
> @@ -323,6 +323,78 @@ pci_config_set_interrupt_pin(uint8_t *pci_config, uint8_t val)
> pci_set_byte(&pci_config[PCI_INTERRUPT_PIN], val);
> }
>
> +static inline void
> +pci_clear_set_bit_byte(uint8_t *config, uint8_t clear, uint8_t set)
> +{
> + pci_set_byte(config, (pci_get_byte(config) & ~clear) | set);
> +}
> +
Let's just open-code this.
> +static inline void
> +pci_clear_bit_byte(uint8_t *config, uint8_t clear)
> +{
> + pci_clear_set_bit_byte(config, clear, 0);
> +}
> +
> +static inline void
> +pci_set_bit_byte(uint8_t *config, uint8_t set)
> +{
> + pci_clear_set_bit_byte(config, 0, set);
> +}
> +
> +static inline void
> +pci_clear_set_bit_word(uint8_t *config, uint16_t clear, uint16_t set)
> +{
> + pci_set_word(config, (pci_get_word(config) & ~clear) | set);
> +}
> +
and this
> +static inline void
> +pci_clear_bit_word(uint8_t *config, uint16_t clear)
> +{
> + pci_clear_set_bit_word(config, clear, 0);
> +}
> +
> +static inline void
> +pci_set_bit_word(uint8_t *config, uint16_t set)
> +{
> + pci_clear_set_bit_word(config, 0, set);
> +}
> +
> +static inline void
> +pci_clear_set_bit_long(uint8_t *config, uint32_t clear, uint32_t set)
> +{
> + pci_set_long(config, (pci_get_long(config) & ~clear) | set);
> +}
> +
and this.
> +static inline void
> +pci_clear_bit_long(uint8_t *config, uint32_t clear)
> +{
> + pci_clear_set_bit_long(config, clear, 0);
> +}
> +
> +static inline void
> +pci_set_bit_long(uint8_t *config, uint32_t set)
> +{
> + pci_clear_set_bit_long(config, 0, set);
> +}
> +
> +static inline void
> +pci_clear_set_bit_quad(uint8_t *config, uint64_t clear, uint64_t set)
> +{
> + pci_set_quad(config, (pci_get_quad(config) & ~clear) | set);
> +}
> +
> +static inline void
> +pci_clear_bit_quad(uint8_t *config, uint64_t clear)
> +{
> + pci_clear_set_bit_quad(config, clear, 0);
> +}
> +
> +static inline void
> +pci_set_bit_quad(uint8_t *config, uint64_t set)
> +{
> + pci_clear_set_bit_quad(config, 0, set);
> +}
> +
> typedef int (*pci_qdev_initfn)(PCIDevice *dev);
> typedef struct {
> DeviceInfo qdev;
> --
> 1.7.1.1
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 09/15] pcie/aer: helper functions for pcie aer capability
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 09/15] pcie/aer: helper functions for pcie aer capability Isaku Yamahata
@ 2010-10-18 5:45 ` Michael S. Tsirkin
0 siblings, 0 replies; 30+ messages in thread
From: Michael S. Tsirkin @ 2010-10-18 5:45 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 12:17:50PM +0900, Isaku Yamahata wrote:
> This patch implements helper functions for pcie aer capability
> which will be used later.
>
> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
> ---
> Changes v3 -> v4:
> - various naming fixes.
> - use pci bit operation helper function
> - eliminate errmsg function pointer
> - replace pci_shift_xxx() with PCIDevice::written
> - uncorrect error status register.
> - dropped pcie_aer_cap()
>
> Changes v2 -> v3:
> - split out from pcie.[ch] to pcie_aer.[ch] to make the files sorter.
> - embeded PCIExpressDevice into PCIDevice.
> - CodingStyle fix
> ---
> Makefile.objs | 2 +-
> hw/pcie.h | 5 +
> hw/pcie_aer.c | 789 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> hw/pcie_aer.h | 105 ++++++++
> qemu-common.h | 3 +
> 5 files changed, 903 insertions(+), 1 deletions(-)
> create mode 100644 hw/pcie_aer.c
> create mode 100644 hw/pcie_aer.h
>
> diff --git a/Makefile.objs b/Makefile.objs
> index eeb5134..68bcc48 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_aer.o
> hw-obj-y += msix.o msi.o
>
> # PCI network cards
> diff --git a/hw/pcie.h b/hw/pcie.h
> index 603bb8b..891e369 100644
> --- a/hw/pcie.h
> +++ b/hw/pcie.h
> @@ -24,6 +24,7 @@
> #include "hw.h"
> #include "pci_regs.h"
> #include "pcie_regs.h"
> +#include "pcie_aer.h"
>
> typedef enum {
> /* for attention and power indicator */
> @@ -66,6 +67,10 @@ struct PCIExpressDevice {
>
> /* FLR */
> pcie_flr_fn flr;
> +
> + /* AER */
> + uint16_t aer_cap;
> + PCIEAERLog aer_log;
> };
>
> /* PCI express capability helper functions */
> diff --git a/hw/pcie_aer.c b/hw/pcie_aer.c
> new file mode 100644
> index 0000000..c86f2b9
> --- /dev/null
> +++ b/hw/pcie_aer.c
> @@ -0,0 +1,789 @@
> +/*
> + * pcie_aer.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"
> +#include "pcie_regs.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 void pcie_aer_clear_error(PCIDevice *dev);
> +static uint8_t pcie_aer_root_get_vector(PCIDevice *dev);
> +static AERMsgResult
> +pcie_aer_msg_alldev(PCIDevice *dev, const PCIEAERMsg *msg);
> +static AERMsgResult
> +pcie_aer_msg_vbridge(PCIDevice *dev, const PCIEAERMsg *msg);
> +static AERMsgResult
> +pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg);
> +
> +/* From 6.2.7 Error Listing and Rules. Table 6-2, 6-3 and 6-4 */
> +static PCIEAERSeverity 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 aer_log_next(uint32_t i, uint32_t max)
> +{
> + return (i + 1) % max;
> +}
> +
> +static bool aer_log_empty_index(uint32_t producer, uint32_t consumer)
> +{
> + return producer == consumer;
> +}
> +
> +static bool aer_log_empty(PCIEAERLog *aer_log)
> +{
> + return aer_log_empty_index(aer_log->producer, aer_log->consumer);
> +}
> +
> +static bool aer_log_full(PCIEAERLog *aer_log)
> +{
> + return aer_log_next(aer_log->producer, aer_log->log_max) ==
> + aer_log->consumer;
> +}
> +
> +static uint32_t aer_log_add(PCIEAERLog *aer_log)
> +{
> + uint32_t i = aer_log->producer;
> + aer_log->producer = aer_log_next(aer_log->producer, aer_log->log_max);
> + return i;
> +}
> +
> +static uint32_t aer_log_del(PCIEAERLog *aer_log)
> +{
> + uint32_t i = aer_log->consumer;
> + aer_log->consumer = aer_log_next(aer_log->consumer, aer_log->log_max);
> + return i;
> +}
> +
> +static int aer_log_add_err(PCIEAERLog *aer_log, const PCIEAERErr *err)
> +{
> + uint32_t i;
> + if (aer_log_full(aer_log)) {
> + return -1;
> + }
> + i = aer_log_add(aer_log);
> + memcpy(&aer_log->log[i], err, sizeof(*err));
> + return 0;
> +}
> +
> +static const PCIEAERErr* aer_log_del_err(PCIEAERLog *aer_log)
> +{
> + uint32_t i;
> + assert(!aer_log_empty(aer_log));
> + i = aer_log_del(aer_log);
> + return &aer_log->log[i];
> +}
> +
> +static void aer_log_clear_all_err(PCIEAERLog *aer_log)
> +{
> + aer_log->producer = 0;
> + aer_log->consumer = 0;
> +}
> +
> +void pcie_aer_init(PCIDevice *dev, uint16_t offset)
> +{
> + PCIExpressDevice *exp;
> +
> + pci_set_bit_word(dev->wmask + PCI_COMMAND, PCI_COMMAND_SERR);
> + pci_set_bit_word(dev->w1cmask + PCI_STATUS, PCI_STATUS_SIG_SYSTEM_ERROR);
> +
> + pcie_add_capability(dev, PCI_EXT_CAP_ID_ERR, PCI_ERR_VER,
> + offset, PCI_ERR_SIZEOF);
> + exp = &dev->exp;
> + exp->aer_cap = offset;
> + if (dev->exp.aer_log.log_max == PCIE_AER_LOG_MAX_UNSET) {
> + dev->exp.aer_log.log_max = PCIE_AER_LOG_MAX_DEFAULT;
> + }
> + if (dev->exp.aer_log.log_max > PCIE_AER_LOG_MAX_MAX) {
> + dev->exp.aer_log.log_max = PCIE_AER_LOG_MAX_MAX;
> + }
> + dev->exp.aer_log.log = qemu_mallocz(sizeof(dev->exp.aer_log.log[0]) *
> + dev->exp.aer_log.log_max);
> +
> + /* On reset PCI_ERR_CAP_MHRE is disabled
> + * PCI_ERR_CAP_MHRE is RWS so that reset doesn't affect related
> + * registers
> + */
> + pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS,
> + PCI_ERR_UNC_SUPPORTED);
> +
> + pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_MASK,
> + PCI_ERR_UNC_SUPPORTED);
> +
> + pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER,
> + PCI_ERR_UNC_SEVERITY_DEFAULT);
> + pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_SEVER,
> + PCI_ERR_UNC_SUPPORTED);
> +
> + pci_set_bit_long(dev->w1cmask + offset + PCI_ERR_COR_STATUS,
> + PCI_ERR_COR_STATUS);
> +
> + pci_set_long(dev->config + offset + PCI_ERR_COR_MASK,
> + PCI_ERR_COR_MASK_DEFAULT);
> + pci_set_long(dev->wmask + offset + PCI_ERR_COR_MASK,
> + PCI_ERR_COR_SUPPORTED);
> +
> + /* capabilities and control. multiple header logging is supported */
> + if (dev->exp.aer_log.log_max > 0) {
> + pci_set_long(dev->config + offset + PCI_ERR_CAP,
> + PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC |
> + PCI_ERR_CAP_MHRC);
> + pci_set_long(dev->wmask + offset + PCI_ERR_CAP,
> + PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE |
> + PCI_ERR_CAP_MHRE);
> + } else {
> + pci_set_long(dev->config + offset + PCI_ERR_CAP,
> + PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC);
> + pci_set_long(dev->wmask + offset + 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_bit_word(dev->wmask + PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_SERR);
> + pci_set_bit_long(dev->w1cmask + PCI_STATUS,
> + PCI_SEC_STATUS_RCV_SYSTEM_ERROR);
> + break;
> + default:
> + /* nothing */
> + break;
> + }
> +}
> +
> +void pcie_aer_exit(PCIDevice *dev)
> +{
> + qemu_free(dev->exp.aer_log.log);
> +}
> +
> +void pcie_aer_write_config(PCIDevice *dev,
> + uint32_t addr, uint32_t val, int len,
> + uint32_t uncorsta_old)
> +{
> + uint32_t pos = dev->exp.aer_cap;
> +
> + /* uncorrectable */
> + uint32_t written =
> + pci_get_long(dev->written + pos + PCI_ERR_UNCOR_STATUS) &
> + PCI_ERR_UNC_SUPPORTED;
> + /* if uncorrectable status register isn't touched, written == 0*/
> + uint32_t errcap = pci_get_long(dev->config + pos + PCI_ERR_CAP);
> + uint32_t first_error = (1U << PCI_ERR_CAP_FEP(errcap));
> + if ((uncorsta_old & first_error) && (written & first_error)) {
> + pcie_aer_clear_error(dev);
> + }
> +
> + /* 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)) {
> + aer_log_clear_all_err(&dev->exp.aer_log);
> + }
> + }
> +}
> +
> +static inline void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg)
> +{
> + uint8_t type;
> + AERMsgResult result;
> +
> + assert(pci_is_express(dev));
> +
> + type = pcie_cap_get_type(dev);
> + if (type == PCI_EXP_TYPE_ROOT_PORT ||
> + type == PCI_EXP_TYPE_UPSTREAM ||
> + type == PCI_EXP_TYPE_DOWNSTREAM) {
> + result = pcie_aer_msg_vbridge(dev, msg);
> + if (result != AER_MSG_SENT) {
> + return;
> + }
> + }
> + result = pcie_aer_msg_alldev(dev, msg);
> + if (type == PCI_EXP_TYPE_ROOT_PORT && result == AER_MSG_SENT) {
> + pcie_aer_msg_root_port(dev, msg);
> + }
> +}
> +
> +static AERMsgResult
> +pcie_aer_msg_alldev(PCIDevice *dev, const PCIEAERMsg *msg)
> +{
> + uint16_t cmd = pci_get_word(dev->config + PCI_COMMAND);
> + bool transmit1 =
> + pcie_aer_msg_is_uncor(msg) && (cmd & PCI_COMMAND_SERR);
> + uint32_t devctl = pci_get_word(dev->config +
> + dev->exp.exp_cap + PCI_EXP_DEVCTL);
> + bool transmit2 = msg->severity & devctl;
> + PCIDevice *parent_port;
> +
> + if (transmit1) {
> + if (pcie_aer_msg_is_uncor(msg)) {
> + /* Signaled System Error */
> + pci_set_bit_word(dev->config + PCI_STATUS,
> + PCI_STATUS_SIG_SYSTEM_ERROR);
> + }
> + }
> +
> + if (!(transmit1 || transmit2)) {
> + return AER_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_MSG_MASKED;
> + }
> + pcie_aer_msg(parent_port, msg);
> + }
> + return AER_MSG_SENT;
> +}
> +
> +static AERMsgResult
> +pcie_aer_msg_vbridge(PCIDevice *dev, const PCIEAERMsg *msg)
> +{
> + uint16_t bridge_control = pci_get_word(dev->config + PCI_BRIDGE_CONTROL);
> +
> + if (pcie_aer_msg_is_uncor(msg)) {
> + /* Received System Error */
> + pci_set_bit_word(dev->config + PCI_SEC_STATUS,
> + PCI_SEC_STATUS_RCV_SYSTEM_ERROR);
> + }
> +
> + if (!(bridge_control & PCI_BRIDGE_CTL_SERR)) {
> + return AER_MSG_MASKED;
> + }
> + return AER_MSG_SENT;
> +}
> +
> +static void pcie_aer_root_port_notify(PCIDevice *dev,
> + bool msi_trigger, int int_level)
> +{
> + unsigned int msi_vector = pcie_aer_root_get_vector(dev);
> + unsigned int int_irq = 0; /* INTA# is chosen for aer notification */
> +
> + if (msix_enabled(dev)) {
> + if (msi_trigger) {
> + msix_notify(dev, msi_vector);
> + }
> + } else if (msi_enabled(dev)) {
> + if (msi_trigger) {
> + msi_notify(dev, msi_vector);
> + }
> + } else {
> + qemu_set_irq(dev->irq[int_irq], int_level);
> + }
> +}
> +
> +static AERMsgResult
> +pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg)
> +{
> + AERMsgResult ret;
> + uint16_t cmd;
> + uint8_t *aer_cap;
> + uint32_t root_cmd;
> + uint32_t root_sta;
> + bool msi_trigger;
> +
> + ret = AER_MSG_MASKED;
> + cmd = pci_get_word(dev->config + PCI_COMMAND);
> + aer_cap = dev->config + dev->exp.aer_cap;
> + root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND);
> + root_sta = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS);
> + msi_trigger = false;
> +
> + if (cmd & PCI_COMMAND_SERR) {
> + /* System Error. Platform Specific */
> + /* ret = AER_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) {
> + msi_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) {
> + msi_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) {
> + msi_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_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) {
> + /* 6.2.4.1.2 Interrupt Generation */
> + pcie_aer_root_port_notify(dev, msi_trigger, 1);
> + ret = AER_MSG_SENT;
> + }
> + return ret;
> +}
> +
> +static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err)
> +{
> + uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
> + 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 + dev->exp.exp_cap + 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)
> +{
> + PCIEAERErr *err;
> + uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
> +
> + pci_clear_bit_long(aer_cap + PCI_ERR_CAP,
> + PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP);
> +
> + 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 PCIEAERErr *err)
> +{
> + uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
> + 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 (aer_log_add_err(&dev->exp.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 + dev->exp.aer_cap;
> + uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP);
> + uint32_t old_err = (1U << PCI_ERR_CAP_FEP(errcap));
> + PCIEAERLog *aer_log = &dev->exp.aer_log;
> + const PCIEAERErr *err;
> + uint32_t consumer;
> +
> + if (!(errcap & PCI_ERR_CAP_MHRE) || aer_log_empty(aer_log)) {
> + pcie_aer_clear_log(dev);
> + pci_clear_bit_long(aer_cap + PCI_ERR_UNCOR_STATUS, old_err);
> + return;
> + }
> +
> + /*
> + * If more errors are queued, set corresponding bits in uncorrectable
> + * error status.
> + * We emulates uncorrectable error status register as W1CS.
> + * So set bit in uncorrectable error status here again for multiple
> + * error recording support.
> + *
> + * 6.2.4.2 Multiple Error Handling(Advanced Error Reporting Capability)
> + */
> + for (consumer = dev->exp.aer_log.consumer;
> + !aer_log_empty_index(dev->exp.aer_log.producer, consumer);
> + consumer = aer_log_next(consumer, dev->exp.aer_log.log_max)) {
> + pci_set_bit_long(aer_cap + PCI_ERR_UNCOR_STATUS,
> + dev->exp.aer_log.log[consumer].status);
> + }
> +
> + err = 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 PCIEAERErr *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 */
> + PCIEAERMsg 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 + dev->exp.exp_cap;
> + if (dev->exp.aer_cap) {
> + aer_cap = dev->config + dev->exp.aer_cap;
> + 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_bit_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_bit_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_bit_long(aer_cap + PCI_ERR_UNCOR_STATUS, status);
> + return;
> + }
> +
> + is_header_log_overflowed = pcie_aer_record_error(dev, err);
> + pci_set_bit_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_msg(dev, &msg);
> +
> + if (is_header_log_overflowed) {
> + PCIEAERErr 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);
> + }
> +}
> +
> +void pcie_aer_root_set_vector(PCIDevice *dev, uint8_t vector)
> +{
> + uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
> + assert(vector < PCI_ERR_ROOT_IRQ_MAX);
> + pci_clear_set_bit_long(aer_cap + PCI_ERR_ROOT_STATUS,
> + PCI_ERR_ROOT_IRQ,
> + ((uint32_t)vector) << PCI_ERR_ROOT_IRQ_SHIFT);
So this is an example why clear_set is not a good API.
Does this clear PCI_ERR_ROOT_IRQ and set vector?
clear vector and set PCI_ERR_ROOT_IRQ?
One can not tell.
Using set and clear separately will make it obvious.
> +}
> +
> +static uint8_t pcie_aer_root_get_vector(PCIDevice *dev)
> +{
> + uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
> + 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;
> +}
> +
> +void pcie_aer_root_init(PCIDevice *dev)
> +{
> + uint16_t pos = dev->exp.aer_cap;
> +
> + pci_set_long(dev->wmask + pos + PCI_ERR_ROOT_COMMAND,
> + PCI_ERR_ROOT_CMD_EN_MASK);
> + pci_set_long(dev->w1cmask + pos + PCI_ERR_ROOT_STATUS,
> + PCI_ERR_ROOT_STATUS_REPORT_MASK);
> +}
> +
> +void pcie_aer_root_reset(PCIDevice *dev)
> +{
> + uint8_t* aer_cap = dev->config + dev->exp.aer_cap;
> +
> + 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 because it's chip dependent
> + * which number is used.
> + */
> +}
> +
> +static bool pcie_aer_root_does_trigger(uint32_t cmd, uint32_t status)
> +{
> + return
> + ((cmd & PCI_ERR_ROOT_CMD_COR_EN) && (status & PCI_ERR_ROOT_COR_RCV)) ||
> + ((cmd & PCI_ERR_ROOT_CMD_NONFATAL_EN) &&
> + (status & PCI_ERR_ROOT_NONFATAL_RCV)) ||
> + ((cmd & PCI_ERR_ROOT_CMD_FATAL_EN) &&
> + (status & 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 = dev->exp.aer_cap;
> + uint8_t *aer_cap = dev->config + pos;
> +
> + /* root command register */
> + uint32_t root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND);
> + if (root_cmd & PCI_ERR_ROOT_CMD_EN_MASK) {
> + /* 6.2.4.1.2 Interrupt Generation */
> +
> + /* 0 -> 1 */
> + uint32_t root_cmd_set = (root_cmd_prev ^ root_cmd) & root_cmd;
> + uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS);
> +
> + bool msi_trigger = pcie_aer_root_does_trigger(root_cmd_set,
> + root_status);
> + int int_level = pcie_aer_root_does_trigger(root_cmd, root_status);
> +
> + pcie_aer_root_port_notify(dev, msi_trigger, int_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, PCIEAERErr),
> + VMSTATE_UINT16(source_id, PCIEAERErr),
> + VMSTATE_UINT16(flags, PCIEAERErr),
> + VMSTATE_UINT32_ARRAY(header, PCIEAERErr, 4),
> + VMSTATE_UINT32_ARRAY(prefix, PCIEAERErr, 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, PCIEAERLog),
> + VMSTATE_UINT32(consumer, PCIEAERLog),
> + VMSTATE_UINT16(log_max, PCIEAERLog),
> + VMSTATE_PCIE_AER_ERRS(log, PCIEAERLog, log_max,
> + vmstate_pcie_aer_err, PCIEAERErr),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> diff --git a/hw/pcie_aer.h b/hw/pcie_aer.h
> new file mode 100644
> index 0000000..7d1540b
> --- /dev/null
> +++ b/hw/pcie_aer.h
> @@ -0,0 +1,105 @@
> +/*
> + * pcie_aer.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_AER_H
> +#define QEMU_PCIE_AER_H
> +
> +#include "hw.h"
> +
> +/* definitions which PCIExpressDevice uses */
> +typedef enum {
> + AER_MSG_MASKED,
> + AER_MSG_SENT,
> +} AERMsgResult;
> +
> +/* AER log */
> +struct PCIEAERLog {
> + /* This structure is saved/loaded.
> + So explicitly size them instead of unsigned int */
> + 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 0xffff
> + uint16_t log_max;
> +
> + PCIEAERErr *log; /* ringed buffer */
> +};
> +
> +/* aer error severity */
> +typedef enum {
> + /* 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,
> +} PCIEAERSeverity;
> +
> +/* aer error message: error signaling message has only error sevirity and
> + source id. See 2.2.8.3 error signaling messages */
> +struct PCIEAERMsg {
> + PCIEAERSeverity severity;
> + uint16_t source_id; /* bdf */
> +};
> +
> +static inline bool
> +pcie_aer_msg_is_uncor(const PCIEAERMsg *msg)
> +{
> + return msg->severity == AER_ERR_NONFATAL || msg->severity == AER_ERR_FATAL;
> +}
> +
> +/* error */
> +struct PCIEAERErr {
> + 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 */
> +};
> +
> +extern const VMStateDescription vmstate_pcie_aer_log;
> +
> +void 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,
> + uint32_t uncorsta_prev);
> +
> +/* 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 PCIEAERErr *err);
> +
> +#endif /* QEMU_PCIE_AER_H */
> diff --git a/qemu-common.h b/qemu-common.h
> index 6d9ee26..94b2c11 100644
> --- a/qemu-common.h
> +++ b/qemu-common.h
> @@ -221,6 +221,9 @@ typedef struct PCIBus PCIBus;
> typedef struct PCIDevice PCIDevice;
> typedef struct PCIExpressDevice PCIExpressDevice;
> typedef struct PCIBridge PCIBridge;
> +typedef struct PCIEAERMsg PCIEAERMsg;
> +typedef struct PCIEAERLog PCIEAERLog;
> +typedef struct PCIEAERErr PCIEAERErr;
> typedef struct SerialState SerialState;
> typedef struct IRQState *qemu_irq;
> typedef struct PCMCIACardState PCMCIACardState;
> --
> 1.7.1.1
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 01/15] pci: make pci_del_capability() update for w1cmask
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 01/15] pci: make pci_del_capability() update for w1cmask Isaku Yamahata
@ 2010-10-18 6:06 ` Michael S. Tsirkin
0 siblings, 0 replies; 30+ messages in thread
From: Michael S. Tsirkin @ 2010-10-18 6:06 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 12:17:42PM +0900, Isaku Yamahata wrote:
> When deleting pci capability, w1cmask should be 0
> to make those registers writablein addition to wmask.
>
> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
Applied (typo in commit message fixed).
> ---
> hw/pci.c | 1 +
> 1 files changed, 1 insertions(+), 0 deletions(-)
>
> diff --git a/hw/pci.c b/hw/pci.c
> index abddc6d..e3462a9 100644
> --- a/hw/pci.c
> +++ b/hw/pci.c
> @@ -1745,6 +1745,7 @@ void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
> pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT];
> /* Make capability writeable again */
> memset(pdev->wmask + offset, 0xff, size);
> + memset(pdev->w1cmask + offset, 0, size);
> /* Clear cmask as device-specific registers can't be checked */
> memset(pdev->cmask + offset, 0, size);
> memset(pdev->used + offset, 0, size);
> --
> 1.7.1.1
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 05/15] pci/bridge: fix pci_bridge_reset()
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 05/15] pci/bridge: fix pci_bridge_reset() Isaku Yamahata
@ 2010-10-18 6:22 ` Michael S. Tsirkin
2010-10-18 7:10 ` Isaku Yamahata
0 siblings, 1 reply; 30+ messages in thread
From: Michael S. Tsirkin @ 2010-10-18 6:22 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 12:17:46PM +0900, Isaku Yamahata wrote:
> lower 4bits of base/limit register is RO, and
> should not be modified on reset.
>
> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
> ---
> hw/pci_bridge.c | 15 +++++++++------
> 1 files changed, 9 insertions(+), 6 deletions(-)
>
> diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c
> index 638e3b3..7229610 100644
> --- a/hw/pci_bridge.c
> +++ b/hw/pci_bridge.c
> @@ -161,12 +161,15 @@ void pci_bridge_reset_reg(PCIDevice *dev)
> conf[PCI_SUBORDINATE_BUS] = 0;
> conf[PCI_SEC_LATENCY_TIMER] = 0;
>
> - conf[PCI_IO_BASE] = 0;
> - conf[PCI_IO_LIMIT] = 0;
> - pci_set_word(conf + PCI_MEMORY_BASE, 0);
> - pci_set_word(conf + PCI_MEMORY_LIMIT, 0);
> - pci_set_word(conf + PCI_PREF_MEMORY_BASE, 0);
> - pci_set_word(conf + PCI_PREF_MEMORY_LIMIT, 0);
> + pci_clear_bit_byte(conf + PCI_IO_BASE, PCI_IO_RANGE_MASK & 0xff);
> + pci_clear_bit_byte(conf + PCI_IO_LIMIT, PCI_IO_RANGE_MASK & 0xff);
No need for & 0xff and & 0xfffff here and below.
also, PCI spec also says that these registers' value
is undefined after reset, so no need to clear them?
If there's a reason, pls put it in comment.
> + pci_clear_bit_word(conf + PCI_MEMORY_BASE, PCI_MEMORY_RANGE_MASK & 0xffff);
> + pci_clear_bit_word(conf + PCI_MEMORY_LIMIT,
> + PCI_MEMORY_RANGE_MASK & 0xffff);
> + pci_clear_bit_word(conf + PCI_PREF_MEMORY_BASE,
> + PCI_PREF_RANGE_MASK & 0xffff);
> + pci_clear_bit_word(conf + PCI_PREF_MEMORY_LIMIT,
> + PCI_PREF_RANGE_MASK & 0xffff);
> pci_set_word(conf + PCI_PREF_BASE_UPPER32, 0);
> pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0);
>
> --
> 1.7.1.1
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 00/15] pcie port switch emulators
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
` (14 preceding siblings ...)
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 15/15] pcie/aer: glue aer error injection into qemu monitor Isaku Yamahata
@ 2010-10-18 6:24 ` Michael S. Tsirkin
15 siblings, 0 replies; 30+ messages in thread
From: Michael S. Tsirkin @ 2010-10-18 6:24 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 12:17:41PM +0900, Isaku Yamahata wrote:
>
> Here is v4 of the pcie patch series.
> - PCIDevice::written
> The abuse of PCIDevice::config to record a written bit of non-modifiable
> registers is confusing and bad style. So I introduced PCIDevice::written.
> - FLR stuff
> I didn't addresse FLR function pointer issue. Anyway in order to
> make FLR work, Qdev bus reset must be addresssed.
> So Let's discuss it at the next phase.
So let's just punt and add FLR as a stub, not call anything from it.
> new patches: 1, 2, 3, 4, 5
> Other patches are (almost) same as before except adjustment to compile.
> The patches of 1, 2, 3 and 5 can be harmlessly merged, I think.
>
> Patch description:
> This patch series implements pcie port switch emulators
> which is basic part for pcie/q35 support.
> This is for mst/pci tree.
>
> changes v3 -> v4:
> - introduced new pci config helper functions.(clear set bit)
> - various clean up and some bug fixes.
> - dropped pci_shift_xxx().
> - dropped function pointerin pcie_aer.h
> - dropped pci_exp_cap(), pcie_aer_cap().
> - file rename (pcie_{root, upstream, downsatrem} => ioh33420, x3130).
>
> changes v2 -> v3:
> - msi: improved commant and simplified shift/ffs dance
> - pci w1c config register framework
> - split pcie.[ch] into pcie_regs.h, pcie.[ch] and pcie_aer.[ch]
> - pcie, aer: many changes by following reviews.
>
> changes v1 -> v2:
> - update msi
> - dropped already pushed out patches.
> - added msix patches.
>
>
> Isaku Yamahata (15):
> pci: make pci_del_capability() update for w1cmask
> pci: introduce helper functions to clear/set bits in configuration
> space
> pci: use pci_clear_bit_word() in pci_device_reset()
> pci: record which is written into pci configuration space
> pci/bridge: fix pci_bridge_reset()
> msi: implements msi
> pcie: add pcie constants to pcie_regs.h
> pcie: helper functions for pcie capability and extended capability
> pcie/aer: helper functions for pcie aer capability
> pcie port: define struct PCIEPort/PCIESlot and helper functions
> ioh3420: pcie root port in X58 ioh
> x3130: pcie upstream port
> x3130: pcie downstream port
> pcie/hotplug: introduce pushing attention button command
> pcie/aer: glue aer error injection into qemu monitor
>
> Makefile.objs | 4 +-
> hw/ioh3420.c | 228 ++++++++++++
> hw/ioh3420.h | 10 +
> hw/msi.c | 352 +++++++++++++++++++
> hw/msi.h | 41 +++
> hw/pci.c | 16 +-
> hw/pci.h | 92 +++++-
> hw/pci_bridge.c | 15 +-
> hw/pcie.c | 529 ++++++++++++++++++++++++++++
> hw/pcie.h | 112 ++++++
> hw/pcie_aer.c | 873 +++++++++++++++++++++++++++++++++++++++++++++++
> hw/pcie_aer.h | 105 ++++++
> hw/pcie_port.c | 197 +++++++++++
> hw/pcie_port.h | 51 +++
> hw/pcie_regs.h | 154 +++++++++
> hw/xio3130_downstream.c | 207 +++++++++++
> hw/xio3130_downstream.h | 11 +
> hw/xio3130_upstream.c | 188 ++++++++++
> hw/xio3130_upstream.h | 10 +
> qemu-common.h | 6 +
> qemu-monitor.hx | 36 ++
> sysemu.h | 9 +
> 22 files changed, 3233 insertions(+), 13 deletions(-)
> create mode 100644 hw/ioh3420.c
> create mode 100644 hw/ioh3420.h
> 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_aer.c
> create mode 100644 hw/pcie_aer.h
> create mode 100644 hw/pcie_port.c
> create mode 100644 hw/pcie_port.h
> create mode 100644 hw/pcie_regs.h
> create mode 100644 hw/xio3130_downstream.c
> create mode 100644 hw/xio3130_downstream.h
> create mode 100644 hw/xio3130_upstream.c
> create mode 100644 hw/xio3130_upstream.h
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 02/15] pci: introduce helper functions to clear/set bits in configuration space
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 02/15] pci: introduce helper functions to clear/set bits in configuration space Isaku Yamahata
2010-10-18 5:42 ` [Qemu-devel] " Michael S. Tsirkin
@ 2010-10-18 6:32 ` Michael S. Tsirkin
1 sibling, 0 replies; 30+ messages in thread
From: Michael S. Tsirkin @ 2010-10-18 6:32 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 12:17:43PM +0900, Isaku Yamahata wrote:
> This patch introduces helper functions to clear/set bits in configuration
> space. pci_{clear_set, clear, set}_bit_{byte, word, long, quad}().
> They will be used later.
>
> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
I am not very happy with the names we came up with.
pci_clear_bit_byte - it sounds like this clears bit *and* byte.
Also, this gets a mask, not a bit number as the name implies.
How about
pci_word_set_mask
pci_word_clear_mask
Other ideas?
> ---
> hw/pci.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 72 insertions(+), 0 deletions(-)
>
> diff --git a/hw/pci.h b/hw/pci.h
> index d8b399f..eafa9f3 100644
> --- a/hw/pci.h
> +++ b/hw/pci.h
> @@ -323,6 +323,78 @@ pci_config_set_interrupt_pin(uint8_t *pci_config, uint8_t val)
> pci_set_byte(&pci_config[PCI_INTERRUPT_PIN], val);
> }
>
> +static inline void
> +pci_clear_set_bit_byte(uint8_t *config, uint8_t clear, uint8_t set)
> +{
> + pci_set_byte(config, (pci_get_byte(config) & ~clear) | set);
> +}
> +
> +static inline void
> +pci_clear_bit_byte(uint8_t *config, uint8_t clear)
> +{
> + pci_clear_set_bit_byte(config, clear, 0);
> +}
> +
> +static inline void
> +pci_set_bit_byte(uint8_t *config, uint8_t set)
> +{
> + pci_clear_set_bit_byte(config, 0, set);
> +}
> +
> +static inline void
> +pci_clear_set_bit_word(uint8_t *config, uint16_t clear, uint16_t set)
> +{
> + pci_set_word(config, (pci_get_word(config) & ~clear) | set);
> +}
> +
> +static inline void
> +pci_clear_bit_word(uint8_t *config, uint16_t clear)
> +{
> + pci_clear_set_bit_word(config, clear, 0);
> +}
> +
> +static inline void
> +pci_set_bit_word(uint8_t *config, uint16_t set)
> +{
> + pci_clear_set_bit_word(config, 0, set);
> +}
> +
> +static inline void
> +pci_clear_set_bit_long(uint8_t *config, uint32_t clear, uint32_t set)
> +{
> + pci_set_long(config, (pci_get_long(config) & ~clear) | set);
> +}
> +
> +static inline void
> +pci_clear_bit_long(uint8_t *config, uint32_t clear)
> +{
> + pci_clear_set_bit_long(config, clear, 0);
> +}
> +
> +static inline void
> +pci_set_bit_long(uint8_t *config, uint32_t set)
> +{
> + pci_clear_set_bit_long(config, 0, set);
> +}
> +
> +static inline void
> +pci_clear_set_bit_quad(uint8_t *config, uint64_t clear, uint64_t set)
> +{
> + pci_set_quad(config, (pci_get_quad(config) & ~clear) | set);
> +}
> +
> +static inline void
> +pci_clear_bit_quad(uint8_t *config, uint64_t clear)
> +{
> + pci_clear_set_bit_quad(config, clear, 0);
> +}
> +
> +static inline void
> +pci_set_bit_quad(uint8_t *config, uint64_t set)
> +{
> + pci_clear_set_bit_quad(config, 0, set);
> +}
> +
> typedef int (*pci_qdev_initfn)(PCIDevice *dev);
> typedef struct {
> DeviceInfo qdev;
> --
> 1.7.1.1
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 05/15] pci/bridge: fix pci_bridge_reset()
2010-10-18 7:10 ` Isaku Yamahata
@ 2010-10-18 7:08 ` Michael S. Tsirkin
2010-10-18 7:44 ` Isaku Yamahata
0 siblings, 1 reply; 30+ messages in thread
From: Michael S. Tsirkin @ 2010-10-18 7:08 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 04:10:17PM +0900, Isaku Yamahata wrote:
> On Mon, Oct 18, 2010 at 08:22:24AM +0200, Michael S. Tsirkin wrote:
> > On Mon, Oct 18, 2010 at 12:17:46PM +0900, Isaku Yamahata wrote:
> > > lower 4bits of base/limit register is RO, and
> > > should not be modified on reset.
> > >
> > > Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
> > > ---
> > > hw/pci_bridge.c | 15 +++++++++------
> > > 1 files changed, 9 insertions(+), 6 deletions(-)
> > >
> > > diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c
> > > index 638e3b3..7229610 100644
> > > --- a/hw/pci_bridge.c
> > > +++ b/hw/pci_bridge.c
> > > @@ -161,12 +161,15 @@ void pci_bridge_reset_reg(PCIDevice *dev)
> > > conf[PCI_SUBORDINATE_BUS] = 0;
> > > conf[PCI_SEC_LATENCY_TIMER] = 0;
> > >
> > > - conf[PCI_IO_BASE] = 0;
> > > - conf[PCI_IO_LIMIT] = 0;
> > > - pci_set_word(conf + PCI_MEMORY_BASE, 0);
> > > - pci_set_word(conf + PCI_MEMORY_LIMIT, 0);
> > > - pci_set_word(conf + PCI_PREF_MEMORY_BASE, 0);
> > > - pci_set_word(conf + PCI_PREF_MEMORY_LIMIT, 0);
> > > + pci_clear_bit_byte(conf + PCI_IO_BASE, PCI_IO_RANGE_MASK & 0xff);
> > > + pci_clear_bit_byte(conf + PCI_IO_LIMIT, PCI_IO_RANGE_MASK & 0xff);
> >
> > No need for & 0xff and & 0xfffff here and below.
>
> gcc complains like this without them.
> hw/pci_bridge.c:165: error: large integer implicitly truncated to unsigned type
I see.
>
> > also, PCI spec also says that these registers' value
> > is undefined after reset, so no need to clear them?
> > If there's a reason, pls put it in comment.
>
> The spec says the lower bits are read-only and they have its meaning.
Yes. but my question is why even touch io base/io limit at all
in this function?
It looks like guest can not rely on these being 0 after reset.
> Anyway I'll add the reference as comment.
>
> For example.
>
> > 3.2.5.6 I/O Base Register and I/O Limit Register
> >
> > If the low four bits of the I/O Base and I/O Limit registers are 01h,
> > then the bridge supports 32-bit I/O address decoding, and the I/O Base
> > Upper 16 Bits and the I/O Limit Upper 16 Bits hold the upper 16 bits,
> > corresponding to AD[31::16], of the 32-bit I/O Base and I/O Limit
> > addresses respectively. In this case, system configuration software is
> > permitted to locate the I/O address range supported by the bridge
> > anywhere in the 4-GB I/O Space. Note that the 4-KB alignment and
> > granularity restrictions still apply when the bridge supports 32-bit
> > I/O addressing.
> > table 3-7
I don't think this is required but up to you.
> >
> >
> > > + pci_clear_bit_word(conf + PCI_MEMORY_BASE, PCI_MEMORY_RANGE_MASK & 0xffff);
> > > + pci_clear_bit_word(conf + PCI_MEMORY_LIMIT,
> > > + PCI_MEMORY_RANGE_MASK & 0xffff);
> > > + pci_clear_bit_word(conf + PCI_PREF_MEMORY_BASE,
> > > + PCI_PREF_RANGE_MASK & 0xffff);
> > > + pci_clear_bit_word(conf + PCI_PREF_MEMORY_LIMIT,
> > > + PCI_PREF_RANGE_MASK & 0xffff);
> > > pci_set_word(conf + PCI_PREF_BASE_UPPER32, 0);
> > > pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0);
> > >
> > > --
> > > 1.7.1.1
> >
>
> --
> yamahata
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 05/15] pci/bridge: fix pci_bridge_reset()
2010-10-18 6:22 ` [Qemu-devel] " Michael S. Tsirkin
@ 2010-10-18 7:10 ` Isaku Yamahata
2010-10-18 7:08 ` Michael S. Tsirkin
0 siblings, 1 reply; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 7:10 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 08:22:24AM +0200, Michael S. Tsirkin wrote:
> On Mon, Oct 18, 2010 at 12:17:46PM +0900, Isaku Yamahata wrote:
> > lower 4bits of base/limit register is RO, and
> > should not be modified on reset.
> >
> > Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
> > ---
> > hw/pci_bridge.c | 15 +++++++++------
> > 1 files changed, 9 insertions(+), 6 deletions(-)
> >
> > diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c
> > index 638e3b3..7229610 100644
> > --- a/hw/pci_bridge.c
> > +++ b/hw/pci_bridge.c
> > @@ -161,12 +161,15 @@ void pci_bridge_reset_reg(PCIDevice *dev)
> > conf[PCI_SUBORDINATE_BUS] = 0;
> > conf[PCI_SEC_LATENCY_TIMER] = 0;
> >
> > - conf[PCI_IO_BASE] = 0;
> > - conf[PCI_IO_LIMIT] = 0;
> > - pci_set_word(conf + PCI_MEMORY_BASE, 0);
> > - pci_set_word(conf + PCI_MEMORY_LIMIT, 0);
> > - pci_set_word(conf + PCI_PREF_MEMORY_BASE, 0);
> > - pci_set_word(conf + PCI_PREF_MEMORY_LIMIT, 0);
> > + pci_clear_bit_byte(conf + PCI_IO_BASE, PCI_IO_RANGE_MASK & 0xff);
> > + pci_clear_bit_byte(conf + PCI_IO_LIMIT, PCI_IO_RANGE_MASK & 0xff);
>
> No need for & 0xff and & 0xfffff here and below.
gcc complains like this without them.
hw/pci_bridge.c:165: error: large integer implicitly truncated to unsigned type
> also, PCI spec also says that these registers' value
> is undefined after reset, so no need to clear them?
> If there's a reason, pls put it in comment.
The spec says the lower bits are read-only and they have its meaning.
Anyway I'll add the reference as comment.
For example.
> 3.2.5.6 I/O Base Register and I/O Limit Register
>
> If the low four bits of the I/O Base and I/O Limit registers are 01h,
> then the bridge supports 32-bit I/O address decoding, and the I/O Base
> Upper 16 Bits and the I/O Limit Upper 16 Bits hold the upper 16 bits,
> corresponding to AD[31::16], of the 32-bit I/O Base and I/O Limit
> addresses respectively. In this case, system configuration software is
> permitted to locate the I/O address range supported by the bridge
> anywhere in the 4-GB I/O Space. Note that the 4-KB alignment and
> granularity restrictions still apply when the bridge supports 32-bit
> I/O addressing.
> table 3-7
>
>
> > + pci_clear_bit_word(conf + PCI_MEMORY_BASE, PCI_MEMORY_RANGE_MASK & 0xffff);
> > + pci_clear_bit_word(conf + PCI_MEMORY_LIMIT,
> > + PCI_MEMORY_RANGE_MASK & 0xffff);
> > + pci_clear_bit_word(conf + PCI_PREF_MEMORY_BASE,
> > + PCI_PREF_RANGE_MASK & 0xffff);
> > + pci_clear_bit_word(conf + PCI_PREF_MEMORY_LIMIT,
> > + PCI_PREF_RANGE_MASK & 0xffff);
> > pci_set_word(conf + PCI_PREF_BASE_UPPER32, 0);
> > pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0);
> >
> > --
> > 1.7.1.1
>
--
yamahata
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 04/15] pci: record which is written into pci configuration space
2010-10-18 5:38 ` [Qemu-devel] " Michael S. Tsirkin
@ 2010-10-18 7:17 ` Isaku Yamahata
2010-10-18 7:32 ` Michael S. Tsirkin
0 siblings, 1 reply; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 7:17 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 07:38:53AM +0200, Michael S. Tsirkin wrote:
> On Mon, Oct 18, 2010 at 12:17:45PM +0900, Isaku Yamahata wrote:
> > record which is written into pci configuration space.
> > introduce helper function to zero PCIDevice::written.
> > They will be used later.
> >
> > Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
>
> This really exposes an internal variable.
> I really dislike this, and I don't think it's needed
> at all: just make the bit writeable.
> Commented on appropriate patches.
I see. So You really want those bit writable.
Then how about introducing pci_{byte, word, long}_test_and_clear_mask()
helper functions?
>
> > ---
> > hw/pci.c | 10 ++++++++++
> > hw/pci.h | 5 +++++
> > 2 files changed, 15 insertions(+), 0 deletions(-)
> >
> > diff --git a/hw/pci.c b/hw/pci.c
> > index 5954476..eca9324 100644
> > --- a/hw/pci.c
> > +++ b/hw/pci.c
> > @@ -627,6 +627,7 @@ static void pci_config_alloc(PCIDevice *pci_dev)
> > pci_dev->cmask = qemu_mallocz(config_size);
> > pci_dev->wmask = qemu_mallocz(config_size);
> > pci_dev->w1cmask = qemu_mallocz(config_size);
> > + pci_dev->written = qemu_mallocz(config_size);
> > pci_dev->used = qemu_mallocz(config_size);
> > }
> >
> > @@ -636,6 +637,7 @@ static void pci_config_free(PCIDevice *pci_dev)
> > qemu_free(pci_dev->cmask);
> > qemu_free(pci_dev->wmask);
> > qemu_free(pci_dev->w1cmask);
> > + qemu_free(pci_dev->written);
> > qemu_free(pci_dev->used);
> > }
> >
> > @@ -1002,6 +1004,8 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
> > assert(!(wmask & w1cmask));
> > d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask);
> > d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */
> > + d->written[addr + i] = val; /* record what is written for driver
> > + specific code */
> > }
> > if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) ||
> > ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) ||
> > @@ -1013,6 +1017,12 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
> > pci_update_irq_disabled(d, was_irq_disabled);
> > }
> >
> > +void pci_clear_written_write_config(PCIDevice *d,
> > + uint32_t addr, uint32_t val, int l)
> > +{
> > + memset(d->written + addr, 0, l);
> > +}
> > +
> > /***********************************************************/
> > /* generic PCI irq support */
> >
> > diff --git a/hw/pci.h b/hw/pci.h
> > index eafa9f3..7097817 100644
> > --- a/hw/pci.h
> > +++ b/hw/pci.h
> > @@ -132,6 +132,9 @@ struct PCIDevice {
> > /* Used to implement RW1C(Write 1 to Clear) bytes */
> > uint8_t *w1cmask;
> >
> > + /* Used to record what value is written */
> > + uint8_t *written;
> > +
> > /* Used to allocate config space for capabilities. */
> > uint8_t *used;
> >
> > @@ -200,6 +203,8 @@ uint32_t pci_default_read_config(PCIDevice *d,
> > uint32_t address, int len);
> > void pci_default_write_config(PCIDevice *d,
> > uint32_t address, uint32_t val, int len);
> > +void pci_clear_written_write_config(PCIDevice *d,
> > + uint32_t addr, uint32_t val, int l);
> > void pci_device_save(PCIDevice *s, QEMUFile *f);
> > int pci_device_load(PCIDevice *s, QEMUFile *f);
> >
> > --
> > 1.7.1.1
>
--
yamahata
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 04/15] pci: record which is written into pci configuration space
2010-10-18 7:17 ` Isaku Yamahata
@ 2010-10-18 7:32 ` Michael S. Tsirkin
0 siblings, 0 replies; 30+ messages in thread
From: Michael S. Tsirkin @ 2010-10-18 7:32 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 04:17:40PM +0900, Isaku Yamahata wrote:
> On Mon, Oct 18, 2010 at 07:38:53AM +0200, Michael S. Tsirkin wrote:
> > On Mon, Oct 18, 2010 at 12:17:45PM +0900, Isaku Yamahata wrote:
> > > record which is written into pci configuration space.
> > > introduce helper function to zero PCIDevice::written.
> > > They will be used later.
> > >
> > > Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
> >
> > This really exposes an internal variable.
> > I really dislike this, and I don't think it's needed
> > at all: just make the bit writeable.
> > Commented on appropriate patches.
>
> I see. So You really want those bit writable.
> Then how about introducing pci_{byte, word, long}_test_and_clear_mask()
> helper functions?
okay, but then let's get rid of simple _clear/_set then: the side effect
or returning old value is harmless. Also pls add a comment noting these
are not atomic.
>
>
> >
> > > ---
> > > hw/pci.c | 10 ++++++++++
> > > hw/pci.h | 5 +++++
> > > 2 files changed, 15 insertions(+), 0 deletions(-)
> > >
> > > diff --git a/hw/pci.c b/hw/pci.c
> > > index 5954476..eca9324 100644
> > > --- a/hw/pci.c
> > > +++ b/hw/pci.c
> > > @@ -627,6 +627,7 @@ static void pci_config_alloc(PCIDevice *pci_dev)
> > > pci_dev->cmask = qemu_mallocz(config_size);
> > > pci_dev->wmask = qemu_mallocz(config_size);
> > > pci_dev->w1cmask = qemu_mallocz(config_size);
> > > + pci_dev->written = qemu_mallocz(config_size);
> > > pci_dev->used = qemu_mallocz(config_size);
> > > }
> > >
> > > @@ -636,6 +637,7 @@ static void pci_config_free(PCIDevice *pci_dev)
> > > qemu_free(pci_dev->cmask);
> > > qemu_free(pci_dev->wmask);
> > > qemu_free(pci_dev->w1cmask);
> > > + qemu_free(pci_dev->written);
> > > qemu_free(pci_dev->used);
> > > }
> > >
> > > @@ -1002,6 +1004,8 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
> > > assert(!(wmask & w1cmask));
> > > d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask);
> > > d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */
> > > + d->written[addr + i] = val; /* record what is written for driver
> > > + specific code */
> > > }
> > > if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) ||
> > > ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) ||
> > > @@ -1013,6 +1017,12 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
> > > pci_update_irq_disabled(d, was_irq_disabled);
> > > }
> > >
> > > +void pci_clear_written_write_config(PCIDevice *d,
> > > + uint32_t addr, uint32_t val, int l)
> > > +{
> > > + memset(d->written + addr, 0, l);
> > > +}
> > > +
> > > /***********************************************************/
> > > /* generic PCI irq support */
> > >
> > > diff --git a/hw/pci.h b/hw/pci.h
> > > index eafa9f3..7097817 100644
> > > --- a/hw/pci.h
> > > +++ b/hw/pci.h
> > > @@ -132,6 +132,9 @@ struct PCIDevice {
> > > /* Used to implement RW1C(Write 1 to Clear) bytes */
> > > uint8_t *w1cmask;
> > >
> > > + /* Used to record what value is written */
> > > + uint8_t *written;
> > > +
> > > /* Used to allocate config space for capabilities. */
> > > uint8_t *used;
> > >
> > > @@ -200,6 +203,8 @@ uint32_t pci_default_read_config(PCIDevice *d,
> > > uint32_t address, int len);
> > > void pci_default_write_config(PCIDevice *d,
> > > uint32_t address, uint32_t val, int len);
> > > +void pci_clear_written_write_config(PCIDevice *d,
> > > + uint32_t addr, uint32_t val, int l);
> > > void pci_device_save(PCIDevice *s, QEMUFile *f);
> > > int pci_device_load(PCIDevice *s, QEMUFile *f);
> > >
> > > --
> > > 1.7.1.1
> >
>
> --
> yamahata
^ permalink raw reply [flat|nested] 30+ messages in thread
* [Qemu-devel] Re: [PATCH v4 05/15] pci/bridge: fix pci_bridge_reset()
2010-10-18 7:08 ` Michael S. Tsirkin
@ 2010-10-18 7:44 ` Isaku Yamahata
0 siblings, 0 replies; 30+ messages in thread
From: Isaku Yamahata @ 2010-10-18 7:44 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Mon, Oct 18, 2010 at 09:08:09AM +0200, Michael S. Tsirkin wrote:
> > The spec says the lower bits are read-only and they have its meaning.
>
> Yes. but my question is why even touch io base/io limit at all
> in this function?
> It looks like guest can not rely on these being 0 after reset.
Oh, now I'm seeing your point.
Then, I'll drop those lines and don't touch those registers
on reset at all.
--
yamahata
^ permalink raw reply [flat|nested] 30+ messages in thread
end of thread, other threads:[~2010-10-18 7:45 UTC | newest]
Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-18 3:17 [Qemu-devel] [PATCH v4 00/15] pcie port switch emulators Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 01/15] pci: make pci_del_capability() update for w1cmask Isaku Yamahata
2010-10-18 6:06 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 02/15] pci: introduce helper functions to clear/set bits in configuration space Isaku Yamahata
2010-10-18 5:42 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 6:32 ` Michael S. Tsirkin
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 03/15] pci: use pci_clear_bit_word() in pci_device_reset() Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 04/15] pci: record which is written into pci configuration space Isaku Yamahata
2010-10-18 5:38 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 7:17 ` Isaku Yamahata
2010-10-18 7:32 ` Michael S. Tsirkin
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 05/15] pci/bridge: fix pci_bridge_reset() Isaku Yamahata
2010-10-18 6:22 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 7:10 ` Isaku Yamahata
2010-10-18 7:08 ` Michael S. Tsirkin
2010-10-18 7:44 ` Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 06/15] msi: implements msi Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 07/15] pcie: add pcie constants to pcie_regs.h Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 08/15] pcie: helper functions for pcie capability and extended capability Isaku Yamahata
2010-10-18 5:38 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 09/15] pcie/aer: helper functions for pcie aer capability Isaku Yamahata
2010-10-18 5:45 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 10/15] pcie port: define struct PCIEPort/PCIESlot and helper functions Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 11/15] ioh3420: pcie root port in X58 ioh Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 12/15] x3130: pcie upstream port Isaku Yamahata
2010-10-18 4:59 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 13/15] x3130: pcie downstream port Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 14/15] pcie/hotplug: introduce pushing attention button command Isaku Yamahata
2010-10-18 3:17 ` [Qemu-devel] [PATCH v4 15/15] pcie/aer: glue aer error injection into qemu monitor Isaku Yamahata
2010-10-18 6:24 ` [Qemu-devel] Re: [PATCH v4 00/15] pcie port switch emulators Michael S. Tsirkin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).