* [Qemu-devel] [PATCH v5 01/14] pci: introduce helper functions to test-and-{clear, set} mask in configuration space
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 02/14] pci: introduce helper function to handle msi-x and msi Isaku Yamahata
` (15 subsequent siblings)
16 siblings, 0 replies; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
This patch introduces helper functions to test-and-{clear, set} mask in configuration
space. pci_{byte, word, long, quad}_test_and_{clear, set}_mask().
They will be used later.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
hw/pci.h | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 70 insertions(+), 0 deletions(-)
diff --git a/hw/pci.h b/hw/pci.h
index d8b399f..752e652 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -323,6 +323,76 @@ pci_config_set_interrupt_pin(uint8_t *pci_config, uint8_t val)
pci_set_byte(&pci_config[PCI_INTERRUPT_PIN], val);
}
+/*
+ * helper functions to do bit mask operation on configuration space.
+ * Just to set bit, use test-and-set and discard returned value.
+ * Just to clear bit, use test-and-clear and discard returned value.
+ * NOTE: They aren't atomic.
+ */
+static inline uint8_t
+pci_byte_test_and_clear_mask(uint8_t *config, uint8_t mask)
+{
+ uint8_t val = pci_get_byte(config);
+ pci_set_byte(config, val & ~mask);
+ return val & mask;
+}
+
+static inline uint8_t
+pci_byte_test_and_set_mask(uint8_t *config, uint8_t mask)
+{
+ uint8_t val = pci_get_byte(config);
+ pci_set_byte(config, val | mask);
+ return val & mask;
+}
+
+static inline uint16_t
+pci_word_test_and_clear_mask(uint8_t *config, uint16_t mask)
+{
+ uint16_t val = pci_get_word(config);
+ pci_set_word(config, val & ~mask);
+ return val & mask;
+}
+
+static inline uint16_t
+pci_word_test_and_set_mask(uint8_t *config, uint16_t mask)
+{
+ uint16_t val = pci_get_word(config);
+ pci_set_word(config, val | mask);
+ return val & mask;
+}
+
+static inline uint32_t
+pci_long_test_and_clear_mask(uint8_t *config, uint32_t mask)
+{
+ uint32_t val = pci_get_long(config);
+ pci_set_long(config, val & ~mask);
+ return val & mask;
+}
+
+static inline uint32_t
+pci_long_test_and_set_mask(uint8_t *config, uint32_t mask)
+{
+ uint32_t val = pci_get_long(config);
+ pci_set_long(config, val | mask);
+ return val & mask;
+}
+
+static inline uint64_t
+pci_quad_test_and_clear_mask(uint8_t *config, uint64_t mask)
+{
+ uint64_t val = pci_get_quad(config);
+ pci_set_quad(config, val & ~mask);
+ return val & mask;
+}
+
+static inline uint64_t
+pci_quad_test_and_set_mask(uint8_t *config, uint64_t mask)
+{
+ uint64_t val = pci_get_quad(config);
+ pci_set_quad(config, val | mask);
+ return val & mask;
+}
+
typedef int (*pci_qdev_initfn)(PCIDevice *dev);
typedef struct {
DeviceInfo qdev;
--
1.7.1.1
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [Qemu-devel] [PATCH v5 02/14] pci: introduce helper function to handle msi-x and msi.
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 01/14] pci: introduce helper functions to test-and-{clear, set} mask in configuration space Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 03/14] pci: use pci_word_test_and_clear_mask() in pci_device_reset() Isaku Yamahata
` (14 subsequent siblings)
16 siblings, 0 replies; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
this patch implements helper functions to handle msi-x and msi
uniformly.
They will be used later.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
hw/pci.c | 19 +++++++++++++++++++
hw/pci.h | 3 +++
2 files changed, 22 insertions(+), 0 deletions(-)
diff --git a/hw/pci.c b/hw/pci.c
index e3462a9..300079f 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -25,6 +25,8 @@
#include "pci.h"
#include "pci_bridge.h"
#include "pci_internals.h"
+#include "msix.h"
+#include "msi.h"
#include "monitor.h"
#include "net.h"
#include "sysemu.h"
@@ -1034,6 +1036,23 @@ static void pci_set_irq(void *opaque, int irq_num, int level)
pci_change_irq_level(pci_dev, irq_num, change);
}
+bool pci_msi_enabled(PCIDevice *dev)
+{
+ return msix_enabled(dev) || msi_enabled(dev);
+}
+
+void pci_msi_notify(PCIDevice *dev, unsigned int vector)
+{
+ if (msix_enabled(dev)) {
+ msix_notify(dev, vector);
+ } else if (msi_enabled(dev)) {
+ msi_notify(dev, vector);
+ } else {
+ /* MSI/MSI-X must be enabled */
+ abort();
+ }
+}
+
/***********************************************************/
/* monitor info on PCI */
diff --git a/hw/pci.h b/hw/pci.h
index 752e652..3072a5f 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -239,6 +239,9 @@ void do_pci_info_print(Monitor *mon, const QObject *data);
void do_pci_info(Monitor *mon, QObject **ret_data);
void pci_bridge_update_mappings(PCIBus *b);
+bool pci_msi_enabled(PCIDevice *dev);
+void pci_msi_notify(PCIDevice *dev, unsigned int vector);
+
static inline void
pci_set_byte(uint8_t *config, uint8_t val)
{
--
1.7.1.1
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [Qemu-devel] [PATCH v5 03/14] pci: use pci_word_test_and_clear_mask() in pci_device_reset()
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 01/14] pci: introduce helper functions to test-and-{clear, set} mask in configuration space Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 02/14] pci: introduce helper function to handle msi-x and msi Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 04/14] pci/bridge: fix pci_bridge_reset() Isaku Yamahata
` (13 subsequent siblings)
16 siblings, 0 replies; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 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>
---
Changes v4 -> v5
- use pci_word_test_and_clear_mask()
---
hw/pci.c | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/hw/pci.c b/hw/pci.c
index 300079f..409e2c0 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -139,9 +139,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_word_test_and_clear_mask(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] 33+ messages in thread
* [Qemu-devel] [PATCH v5 04/14] pci/bridge: fix pci_bridge_reset()
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (2 preceding siblings ...)
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 03/14] pci: use pci_word_test_and_clear_mask() in pci_device_reset() Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 12:07 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 05/14] msi: implements msi Isaku Yamahata
` (12 subsequent siblings)
16 siblings, 1 reply; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 UTC (permalink / raw)
To: qemu-devel; +Cc: skandasa, adnan, wexu2, mst, yamahata, etmartin
The default value of base/limit registers aren't specified in the spec.
So pci_bridge_reset() shouldn't touch them.
Instead, introduced two functions to reset those registers in a way
of typical implementation. zero base/limit registers or disable forwarding.
They will be used later.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
Changes v4 -> v5:
- drop the lines in pci_bridge_reset()
- introduced two functions to reset base/limit registers.
---
hw/pci_bridge.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++-------
hw/pci_bridge.h | 2 +
2 files changed, 51 insertions(+), 8 deletions(-)
diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c
index 638e3b3..de75e6a 100644
--- a/hw/pci_bridge.c
+++ b/hw/pci_bridge.c
@@ -151,6 +151,46 @@ void pci_bridge_write_config(PCIDevice *d,
}
}
+void pci_bridge_reset_zero_base_limit(PCIDevice *dev)
+{
+ uint8_t *conf = dev->config;
+
+ pci_byte_test_and_clear_mask(conf + PCI_IO_BASE,
+ PCI_IO_RANGE_MASK & 0xff);
+ pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT,
+ PCI_IO_RANGE_MASK & 0xff);
+ pci_word_test_and_clear_mask(conf + PCI_MEMORY_BASE,
+ PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT,
+ PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_BASE,
+ PCI_PREF_RANGE_MASK & 0xffff);
+ pci_word_test_and_clear_mask(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);
+}
+
+void pci_bridge_reset_disable_base_limit(PCIDevice *dev)
+{
+ uint8_t *conf = dev->config;
+
+ pci_byte_test_and_set_mask(conf + PCI_IO_BASE,
+ PCI_IO_RANGE_MASK & 0xff);
+ pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT,
+ PCI_IO_RANGE_MASK & 0xff);
+ pci_word_test_and_set_mask(conf + PCI_MEMORY_BASE,
+ PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT,
+ PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_word_test_and_set_mask(conf + PCI_PREF_MEMORY_BASE,
+ PCI_PREF_RANGE_MASK & 0xffff);
+ pci_word_test_and_clear_mask(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);
+}
+
/* reset bridge specific configuration registers */
void pci_bridge_reset_reg(PCIDevice *dev)
{
@@ -161,14 +201,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_set_word(conf + PCI_PREF_BASE_UPPER32, 0);
- pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0);
+ /*
+ * the default values for base/limit registers aren't specified
+ * in the PCI-to-PCI-bridge spec. So we don't thouch them here.
+ * Each implementation can override it.
+ * typical implementation does
+ * - zero registers: pci_bridge_reset_zer_base_limit()
+ * or
+ * - disable forwarding: pci_bridge_reset_disable_base_limit()
+ */
pci_set_word(conf + PCI_BRIDGE_CONTROL, 0);
}
diff --git a/hw/pci_bridge.h b/hw/pci_bridge.h
index f6fade0..2359684 100644
--- a/hw/pci_bridge.h
+++ b/hw/pci_bridge.h
@@ -39,6 +39,8 @@ pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type);
void pci_bridge_write_config(PCIDevice *d,
uint32_t address, uint32_t val, int len);
+void pci_bridge_reset_zero_base_limit(PCIDevice *dev);
+void pci_bridge_reset_disable_base_limit(PCIDevice *dev);
void pci_bridge_reset_reg(PCIDevice *dev);
void pci_bridge_reset(DeviceState *qdev);
--
1.7.1.1
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [Qemu-devel] Re: [PATCH v5 04/14] pci/bridge: fix pci_bridge_reset()
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 04/14] pci/bridge: fix pci_bridge_reset() Isaku Yamahata
@ 2010-10-19 12:07 ` Michael S. Tsirkin
0 siblings, 0 replies; 33+ messages in thread
From: Michael S. Tsirkin @ 2010-10-19 12:07 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Tue, Oct 19, 2010 at 06:06:31PM +0900, Isaku Yamahata wrote:
> The default value of base/limit registers aren't specified in the spec.
> So pci_bridge_reset() shouldn't touch them.
> Instead, introduced two functions to reset those registers in a way
> of typical implementation. zero base/limit registers or disable forwarding.
> They will be used later.
>
> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
> ---
I have some second thoughts:
1. pci_bridge_reset is used in several devices and I have no idea what
the reset change will do there, how do real devices behave or whether
guests depend on a specific behaviour. It seems harmless to leave the
current implementation in place, and simply add
pci_bridge_disable_base_limit which devices can call after
pci_bridge_reset.
2. _zero and _disable describe what function does already,
so we should drop _reset from function name.
Thus we only get 1 new function, pci_bridge_disable_base_limit.
> Changes v4 -> v5:
> - drop the lines in pci_bridge_reset()
> - introduced two functions to reset base/limit registers.
> ---
> hw/pci_bridge.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++-------
> hw/pci_bridge.h | 2 +
> 2 files changed, 51 insertions(+), 8 deletions(-)
>
> diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c
> index 638e3b3..de75e6a 100644
> --- a/hw/pci_bridge.c
> +++ b/hw/pci_bridge.c
> @@ -151,6 +151,46 @@ void pci_bridge_write_config(PCIDevice *d,
> }
> }
>
> +void pci_bridge_reset_zero_base_limit(PCIDevice *dev)
> +{
> + uint8_t *conf = dev->config;
> +
> + pci_byte_test_and_clear_mask(conf + PCI_IO_BASE,
> + PCI_IO_RANGE_MASK & 0xff);
> + pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT,
> + PCI_IO_RANGE_MASK & 0xff);
> + pci_word_test_and_clear_mask(conf + PCI_MEMORY_BASE,
> + PCI_MEMORY_RANGE_MASK & 0xffff);
> + pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT,
> + PCI_MEMORY_RANGE_MASK & 0xffff);
> + pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_BASE,
> + PCI_PREF_RANGE_MASK & 0xffff);
> + pci_word_test_and_clear_mask(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);
> +}
> +
> +void pci_bridge_reset_disable_base_limit(PCIDevice *dev)
> +{
> + uint8_t *conf = dev->config;
> +
> + pci_byte_test_and_set_mask(conf + PCI_IO_BASE,
> + PCI_IO_RANGE_MASK & 0xff);
> + pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT,
> + PCI_IO_RANGE_MASK & 0xff);
> + pci_word_test_and_set_mask(conf + PCI_MEMORY_BASE,
> + PCI_MEMORY_RANGE_MASK & 0xffff);
> + pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT,
> + PCI_MEMORY_RANGE_MASK & 0xffff);
> + pci_word_test_and_set_mask(conf + PCI_PREF_MEMORY_BASE,
> + PCI_PREF_RANGE_MASK & 0xffff);
> + pci_word_test_and_clear_mask(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);
> +}
> +
> /* reset bridge specific configuration registers */
> void pci_bridge_reset_reg(PCIDevice *dev)
> {
> @@ -161,14 +201,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_set_word(conf + PCI_PREF_BASE_UPPER32, 0);
> - pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0);
> + /*
> + * the default values for base/limit registers aren't specified
> + * in the PCI-to-PCI-bridge spec. So we don't thouch them here.
> + * Each implementation can override it.
> + * typical implementation does
> + * - zero registers: pci_bridge_reset_zer_base_limit()
> + * or
> + * - disable forwarding: pci_bridge_reset_disable_base_limit()
> + */
>
> pci_set_word(conf + PCI_BRIDGE_CONTROL, 0);
> }
> diff --git a/hw/pci_bridge.h b/hw/pci_bridge.h
> index f6fade0..2359684 100644
> --- a/hw/pci_bridge.h
> +++ b/hw/pci_bridge.h
> @@ -39,6 +39,8 @@ pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type);
>
> void pci_bridge_write_config(PCIDevice *d,
> uint32_t address, uint32_t val, int len);
> +void pci_bridge_reset_zero_base_limit(PCIDevice *dev);
> +void pci_bridge_reset_disable_base_limit(PCIDevice *dev);
> void pci_bridge_reset_reg(PCIDevice *dev);
> void pci_bridge_reset(DeviceState *qdev);
>
> --
> 1.7.1.1
^ permalink raw reply [flat|nested] 33+ messages in thread
* [Qemu-devel] [PATCH v5 05/14] msi: implements msi
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (3 preceding siblings ...)
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 04/14] pci/bridge: fix pci_bridge_reset() Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 06/14] pcie: add pcie constants to pcie_regs.h Isaku Yamahata
` (11 subsequent siblings)
16 siblings, 0 replies; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 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 v4 -> v5:
- use pci_xxx_test_and_clear/set_mask().
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.
msi: update for helper functions.
update for helper functions.
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
---
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..a949d82
--- /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_long_test_and_set_mask(
+ 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_long_test_and_clear_mask(
+ 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 3072a5f..9e2f27d 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),
};
@@ -171,6 +172,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] 33+ messages in thread
* [Qemu-devel] [PATCH v5 06/14] pcie: add pcie constants to pcie_regs.h
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (4 preceding siblings ...)
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 05/14] msi: implements msi Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 07/14] pcie: helper functions for pcie capability and extended capability Isaku Yamahata
` (10 subsequent siblings)
16 siblings, 0 replies; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 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] 33+ messages in thread
* [Qemu-devel] [PATCH v5 07/14] pcie: helper functions for pcie capability and extended capability
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (5 preceding siblings ...)
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 06/14] pcie: add pcie constants to pcie_regs.h Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 11:53 ` [Qemu-devel] " Michael S. Tsirkin
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 08/14] pcie/aer: helper functions for pcie aer capability Isaku Yamahata
` (9 subsequent siblings)
16 siblings, 1 reply; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 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 v4 -> v5:
- dropped FLR related members. This will be addressed at the next phase.
- use pci_xxx_test_and_xxx_mask().
- drop PCIDevice::written bits. and made related registers writable.
- eliminated pcie_cap_slot_notify()
- introduced PCIExpressDevice::hpev_intx
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 | 540 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/pcie.h | 107 ++++++++++++
qemu-common.h | 1 +
5 files changed, 654 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 9e2f27d..d6c522b 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))
@@ -175,6 +177,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..53d1fce
--- /dev/null
+++ b/hw/pcie.c
@@ -0,0 +1,540 @@
+/*
+ * 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)
+{
+ uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
+ assert(vector < 32);
+ pci_word_test_and_clear_mask(exp_cap + PCI_EXP_FLAGS, PCI_EXP_FLAGS_IRQ);
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_FLAGS,
+ 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_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP,
+ PCI_EXP_DEVCAP_RBER);
+ pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
+ pci_long_test_and_set_mask(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)
+{
+ uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL;
+ pci_long_test_and_clear_mask(devctl,
+ PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
+}
+
+/*
+ * 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);
+
+ PCIE_DEV_PRINTF(dev,
+ "sltctl: 0x%02"PRIx16" sltsta: 0x%02"PRIx16" event: %x\n",
+ sltctl, sltsta, event);
+
+ if (pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, event)) {
+ return;
+ }
+ sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
+ PCIE_DEV_PRINTF(dev, "sltsta -> %02"PRIx16"\n", sltsta);
+
+ if ((sltctl & PCI_EXP_SLTCTL_HPIE) &&
+ (sltctl & event & PCI_EXP_HP_EV_SUPPORTED)) {
+ if (pci_msi_enabled(dev)) {
+ pci_msi_notify(dev, pcie_cap_flags_get_vector(dev));
+ } else {
+ qemu_set_irq(dev->irq[dev->exp.hpev_intx], 1);
+ }
+ }
+}
+
+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_word_test_and_set_mask(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_word_test_and_set_mask(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_word_test_and_clear_mask(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_word_test_and_set_mask(dev->config + pos + PCI_EXP_FLAGS,
+ PCI_EXP_FLAGS_SLOT);
+
+ pci_long_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCAP,
+ ~PCI_EXP_SLTCAP_PSN);
+ pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP,
+ (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_word_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PIC |
+ PCI_EXP_SLTCTL_AIC);
+ pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PIC_OFF |
+ PCI_EXP_SLTCTL_AIC_OFF);
+ pci_word_test_and_set_mask(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);
+ /* Although reading PCI_EXP_SLTCTL_EIC returns always 0,
+ * make the bit writable here in order to detect 1b is written.
+ * pcie_cap_slot_write_config() test-and-clear the bit, so
+ * this bit always returns 0 to the guest.
+ */
+ pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_EIC);
+
+ pci_word_test_and_set_mask(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_word_test_and_clear_mask(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_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PIC_OFF |
+ PCI_EXP_SLTCTL_AIC_OFF);
+
+ pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_EIS |/* on 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);
+
+ /* 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_word_test_and_clear_mask(exp_cap + 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 control bits 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 (pci_msi_enabled(dev)) {
+ bool msi_trigger =
+ (sltctl & PCI_EXP_SLTCTL_HPIE) &&
+ ((sltctl_prev ^ sltctl) & sltctl & /* stlctl: 0 -> 1 */
+ sltsta & PCI_EXP_HP_EV_SUPPORTED);
+ if (msi_trigger) {
+ pci_msi_notify(dev, pcie_cap_flags_get_vector(dev));
+ }
+ } else {
+ int int_level =
+ (sltctl & PCI_EXP_SLTCTL_HPIE) &&
+ (sltctl & sltsta & PCI_EXP_HP_EV_SUPPORTED);
+ qemu_set_irq(dev->irq[dev->exp.hpev_intx], 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);
+}
+
+/*
+ * TODO: implement FLR:
+ * Right now sets the bit which indicates FLR is supported.
+ */
+/* function level reset(FLR) */
+void pcie_cap_flr_init(PCIDevice *dev)
+{
+ pci_long_test_and_set_mask(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCAP,
+ PCI_EXP_DEVCAP_FLR);
+
+ /* Although reading BCR_FLR returns always 0,
+ * the bit is made writable here in order to detect the 1b is written
+ * pcie_cap_flr_write_config() test-and-clear the bit, so
+ * this bit always returns 0 to the guest.
+ */
+ pci_word_test_and_set_mask(dev->wmask + dev->exp.exp_cap + PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_BCR_FLR);
+}
+
+void pcie_cap_flr_write_config(PCIDevice *dev,
+ uint32_t addr, uint32_t val, int len)
+{
+ uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL;
+ if (pci_word_test_and_clear_mask(devctl, PCI_EXP_DEVCTL_BCR_FLR)) {
+ /* TODO: implement 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_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP2,
+ PCI_EXP_DEVCAP2_ARI);
+ pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL2,
+ PCI_EXP_DEVCTL2_ARI);
+}
+
+void pcie_cap_ari_reset(PCIDevice *dev)
+{
+ uint8_t *devctl2 = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2;
+ pci_long_test_and_clear_mask(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..68327d8
--- /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;
+
+struct PCIExpressDevice {
+ /* Offset of express capability in config space */
+ uint8_t exp_cap;
+
+ /* TODO FLR */
+
+ /* SLOT */
+ unsigned int hpev_intx; /* INTx for hot plug event */
+};
+
+/* 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);
+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] 33+ messages in thread
* [Qemu-devel] Re: [PATCH v5 07/14] pcie: helper functions for pcie capability and extended capability
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 07/14] pcie: helper functions for pcie capability and extended capability Isaku Yamahata
@ 2010-10-19 11:53 ` Michael S. Tsirkin
0 siblings, 0 replies; 33+ messages in thread
From: Michael S. Tsirkin @ 2010-10-19 11:53 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Tue, Oct 19, 2010 at 06:06:34PM +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>
> ---
> Changes v4 -> v5:
> - dropped FLR related members. This will be addressed at the next phase.
> - use pci_xxx_test_and_xxx_mask().
> - drop PCIDevice::written bits. and made related registers writable.
> - eliminated pcie_cap_slot_notify()
> - introduced PCIExpressDevice::hpev_intx
Please also add a comment saying that hpev_intx field defaults to 0,
and needs to be changed by devices that want to use another
interrupt for hotplug events.
> 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 | 540 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> hw/pcie.h | 107 ++++++++++++
> qemu-common.h | 1 +
> 5 files changed, 654 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 9e2f27d..d6c522b 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))
> @@ -175,6 +177,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..53d1fce
> --- /dev/null
> +++ b/hw/pcie.c
> @@ -0,0 +1,540 @@
> +/*
> + * 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)
> +{
> + uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
> + assert(vector < 32);
> + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_FLAGS, PCI_EXP_FLAGS_IRQ);
> + pci_word_test_and_set_mask(exp_cap + PCI_EXP_FLAGS,
> + 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_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP,
> + PCI_EXP_DEVCAP_RBER);
> + pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL,
> + PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
> + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
> + pci_long_test_and_set_mask(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)
> +{
> + uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL;
> + pci_long_test_and_clear_mask(devctl,
> + PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
> + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
> +}
> +
> +/*
> + * 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);
> +
> + PCIE_DEV_PRINTF(dev,
> + "sltctl: 0x%02"PRIx16" sltsta: 0x%02"PRIx16" event: %x\n",
> + sltctl, sltsta, event);
> +
> + if (pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, event)) {
> + return;
> + }
> + sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
> + PCIE_DEV_PRINTF(dev, "sltsta -> %02"PRIx16"\n", sltsta);
> +
> + if ((sltctl & PCI_EXP_SLTCTL_HPIE) &&
> + (sltctl & event & PCI_EXP_HP_EV_SUPPORTED)) {
> + if (pci_msi_enabled(dev)) {
> + pci_msi_notify(dev, pcie_cap_flags_get_vector(dev));
> + } else {
> + qemu_set_irq(dev->irq[dev->exp.hpev_intx], 1);
> + }
> + }
> +}
> +
> +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_word_test_and_set_mask(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_word_test_and_set_mask(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_word_test_and_clear_mask(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_word_test_and_set_mask(dev->config + pos + PCI_EXP_FLAGS,
> + PCI_EXP_FLAGS_SLOT);
> +
> + pci_long_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCAP,
> + ~PCI_EXP_SLTCAP_PSN);
> + pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP,
> + (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_word_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCTL,
> + PCI_EXP_SLTCTL_PIC |
> + PCI_EXP_SLTCTL_AIC);
> + pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCTL,
> + PCI_EXP_SLTCTL_PIC_OFF |
> + PCI_EXP_SLTCTL_AIC_OFF);
> + pci_word_test_and_set_mask(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);
> + /* Although reading PCI_EXP_SLTCTL_EIC returns always 0,
> + * make the bit writable here in order to detect 1b is written.
> + * pcie_cap_slot_write_config() test-and-clear the bit, so
> + * this bit always returns 0 to the guest.
> + */
> + pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL,
> + PCI_EXP_SLTCTL_EIC);
> +
> + pci_word_test_and_set_mask(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_word_test_and_clear_mask(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_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL,
> + PCI_EXP_SLTCTL_PIC_OFF |
> + PCI_EXP_SLTCTL_AIC_OFF);
> +
> + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA,
> + PCI_EXP_SLTSTA_EIS |/* on 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);
> +
> + /* 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_word_test_and_clear_mask(exp_cap + 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 control bits 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 (pci_msi_enabled(dev)) {
> + bool msi_trigger =
> + (sltctl & PCI_EXP_SLTCTL_HPIE) &&
> + ((sltctl_prev ^ sltctl) & sltctl & /* stlctl: 0 -> 1 */
> + sltsta & PCI_EXP_HP_EV_SUPPORTED);
> + if (msi_trigger) {
> + pci_msi_notify(dev, pcie_cap_flags_get_vector(dev));
> + }
> + } else {
> + int int_level =
> + (sltctl & PCI_EXP_SLTCTL_HPIE) &&
> + (sltctl & sltsta & PCI_EXP_HP_EV_SUPPORTED);
> + qemu_set_irq(dev->irq[dev->exp.hpev_intx], 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);
> +}
> +
> +/*
> + * TODO: implement FLR:
> + * Right now sets the bit which indicates FLR is supported.
> + */
> +/* function level reset(FLR) */
> +void pcie_cap_flr_init(PCIDevice *dev)
> +{
> + pci_long_test_and_set_mask(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCAP,
> + PCI_EXP_DEVCAP_FLR);
> +
> + /* Although reading BCR_FLR returns always 0,
> + * the bit is made writable here in order to detect the 1b is written
> + * pcie_cap_flr_write_config() test-and-clear the bit, so
> + * this bit always returns 0 to the guest.
> + */
> + pci_word_test_and_set_mask(dev->wmask + dev->exp.exp_cap + PCI_EXP_DEVCTL,
> + PCI_EXP_DEVCTL_BCR_FLR);
> +}
> +
> +void pcie_cap_flr_write_config(PCIDevice *dev,
> + uint32_t addr, uint32_t val, int len)
> +{
> + uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL;
> + if (pci_word_test_and_clear_mask(devctl, PCI_EXP_DEVCTL_BCR_FLR)) {
> + /* TODO: implement 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_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP2,
> + PCI_EXP_DEVCAP2_ARI);
> + pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL2,
> + PCI_EXP_DEVCTL2_ARI);
> +}
> +
> +void pcie_cap_ari_reset(PCIDevice *dev)
> +{
> + uint8_t *devctl2 = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2;
> + pci_long_test_and_clear_mask(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..68327d8
> --- /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;
> +
> +struct PCIExpressDevice {
> + /* Offset of express capability in config space */
> + uint8_t exp_cap;
> +
> + /* TODO FLR */
> +
> + /* SLOT */
> + unsigned int hpev_intx; /* INTx for hot plug event */
> +};
> +
> +/* 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);
> +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 [flat|nested] 33+ messages in thread
* [Qemu-devel] [PATCH v5 08/14] pcie/aer: helper functions for pcie aer capability
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (6 preceding siblings ...)
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 07/14] pcie: helper functions for pcie capability and extended capability Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 09/14] pcie port: define struct PCIEPort/PCIESlot and helper functions Isaku Yamahata
` (8 subsequent siblings)
16 siblings, 0 replies; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 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 v4 -> v5:
- use pci_xxx_test_and_xxx_mask()
- rewrote PCIDevice::written bits.
- eliminated pcie_aer_notify()
- introduced PCIExpressDevice::aer_intx
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 | 6 +
hw/pcie_aer.c | 785 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/pcie_aer.h | 105 ++++++++
qemu-common.h | 3 +
5 files changed, 900 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 68327d8..1b10753 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,11 @@ struct PCIExpressDevice {
/* SLOT */
unsigned int hpev_intx; /* INTx for hot plug event */
+
+ /* AER */
+ uint16_t aer_cap;
+ PCIEAERLog aer_log;
+ unsigned int aer_intx; /* INTx for error reporting */
};
/* PCI express capability helper functions */
diff --git a/hw/pcie_aer.c b/hw/pcie_aer.c
new file mode 100644
index 0000000..1b023b0
--- /dev/null
+++ b/hw/pcie_aer.c
@@ -0,0 +1,785 @@
+/*
+ * 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_word_test_and_set_mask(dev->wmask + PCI_COMMAND, PCI_COMMAND_SERR);
+ pci_word_test_and_set_mask(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->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_long_test_and_set_mask(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_word_test_and_set_mask(dev->wmask + PCI_BRIDGE_CONTROL,
+ PCI_BRIDGE_CTL_SERR);
+ pci_long_test_and_set_mask(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 */
+ if (ranges_overlap(addr, len, pos + PCI_ERR_UNCOR_STATUS, 4)) {
+ uint32_t errcap = pci_get_long(dev->config + pos + PCI_ERR_CAP);
+ uint32_t first_error = 1U << PCI_ERR_CAP_FEP(errcap);
+ uint32_t written = val;
+ if (addr >= pos + PCI_ERR_UNCOR_STATUS) {
+ written <<= (addr - (pos + PCI_ERR_UNCOR_STATUS)) * 8;
+ } else {
+ written >>= (pos + PCI_ERR_UNCOR_STATUS - addr) * 8;
+ }
+ written &= PCI_ERR_UNC_SUPPORTED;
+
+ 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_word_test_and_set_mask(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_word_test_and_set_mask(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 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 */
+ if (pci_msi_enabled(dev)) {
+ pci_msi_notify(dev, pcie_aer_root_get_vector(dev));
+ } else {
+ qemu_set_irq(dev->irq[dev->exp.aer_intx], 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_long_test_and_clear_mask(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_long_test_and_clear_mask(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_long_test_and_set_mask(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_long_test_and_set_mask(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_long_test_and_set_mask(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_long_test_and_set_mask(aer_cap + PCI_ERR_UNCOR_STATUS,
+ status);
+ return;
+ }
+
+ is_header_log_overflowed = pcie_aer_record_error(dev, err);
+ pci_long_test_and_set_mask(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_long_test_and_clear_mask(aer_cap + PCI_ERR_ROOT_STATUS,
+ PCI_ERR_ROOT_IRQ);
+ pci_long_test_and_set_mask(aer_cap + PCI_ERR_ROOT_STATUS,
+ ((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);
+
+ if (pci_msi_enabled(dev)) {
+ if (pcie_aer_root_does_trigger(root_cmd_set, root_status)) {
+ pci_msi_notify(dev, pcie_aer_root_get_vector(dev));
+ }
+ } else {
+ int int_level = pcie_aer_root_does_trigger(root_cmd, root_status);
+ qemu_set_irq(dev->irq[dev->exp.aer_intx], 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] 33+ messages in thread
* [Qemu-devel] [PATCH v5 09/14] pcie port: define struct PCIEPort/PCIESlot and helper functions
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (7 preceding siblings ...)
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 08/14] pcie/aer: helper functions for pcie aer capability Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 10/14] ioh3420: pcie root port in X58 ioh Isaku Yamahata
` (7 subsequent siblings)
16 siblings, 0 replies; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 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 v4 -> v5:
- use pci_xxx_test_and_xxx_mask()
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 | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/pcie_port.h | 51 ++++++++++++++++++++++++
qemu-common.h | 2 +
4 files changed, 170 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..117de61
--- /dev/null
+++ b/hw/pcie_port.c
@@ -0,0 +1,116 @@
+/*
+ * 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_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_BASE,
+ PCI_PREF_RANGE_TYPE_64);
+ pci_word_test_and_set_mask(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] 33+ messages in thread
* [Qemu-devel] [PATCH v5 10/14] ioh3420: pcie root port in X58 ioh
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (8 preceding siblings ...)
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 09/14] pcie port: define struct PCIEPort/PCIESlot and helper functions Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 11/14] x3130: pcie upstream port Isaku Yamahata
` (6 subsequent siblings)
16 siblings, 0 replies; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 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 v4 -> v5:
- use pci_xxx_test_and_xxx_mask()
Changes v3 -> v4:
- rename pcie_root -> ioh3420
- compilation adjustment.
Changes v2 -> v3:
- compilation adjustment.
---
Makefile.objs | 1 +
hw/ioh3420.c | 229 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/ioh3420.h | 10 +++
3 files changed, 240 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..4317ac3
--- /dev/null
+++ b/hw/ioh3420.c
@@ -0,0 +1,229 @@
+/*
+ * 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_disable_base_limit(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] 33+ messages in thread
* [Qemu-devel] [PATCH v5 11/14] x3130: pcie upstream port
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (9 preceding siblings ...)
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 10/14] ioh3420: pcie root port in X58 ioh Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 12/14] x3130: pcie downstream port Isaku Yamahata
` (5 subsequent siblings)
16 siblings, 0 replies; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 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>
---
Changes v4 -> v5:
- remove flr related stuff.
This will be addressed at the next phase.
- use pci_xxx_test_and_xxx_mask().
Chnages v3 -> v4:
- rename pcie_upstream -> x3130_upstream.
- compilation adjustment.
Changes v2 -> v3:
- compilation adjustment.
---
Makefile.objs | 2 +-
hw/xio3130_upstream.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++++
hw/xio3130_upstream.h | 10 +++
3 files changed, 192 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..cba2b09
--- /dev/null
+++ b/hw/xio3130_upstream.c
@@ -0,0 +1,181 @@
+/*
+ * 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_zero_base_limit(d);
+ pci_bridge_reset(qdev);
+ pcie_cap_deverr_reset(d);
+}
+
+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;
+ }
+
+ /* TODO: implement FLR */
+ pcie_cap_flr_init(d);
+
+ 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] 33+ messages in thread
* [Qemu-devel] [PATCH v5 12/14] x3130: pcie downstream port
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (10 preceding siblings ...)
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 11/14] x3130: pcie upstream port Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 13/14] pcie/hotplug: introduce pushing attention button command Isaku Yamahata
` (4 subsequent siblings)
16 siblings, 0 replies; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 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 v4 -> v5:
- use pci_xxx_test_and_xxx_mask().
- removed flr related stuff.
Changes v3 -> v4:
- rename: pcie_downstream -> x3130_downstream
- compilation adjustment.
Changes v2 -> v3:
- compilation adjustment.
---
Makefile.objs | 2 +-
hw/xio3130_downstream.c | 197 +++++++++++++++++++++++++++++++++++++++++++++++
hw/xio3130_downstream.h | 11 +++
3 files changed, 209 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..9801723
--- /dev/null
+++ b/hw/xio3130_downstream.c
@@ -0,0 +1,197 @@
+/*
+ * 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_zero_base_limit(d);
+ pci_bridge_reset(qdev);
+}
+
+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); /* TODO: implement 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] 33+ messages in thread
* [Qemu-devel] [PATCH v5 13/14] pcie/hotplug: introduce pushing attention button command
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (11 preceding siblings ...)
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 12/14] x3130: pcie downstream port Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 14/14] pcie/aer: glue aer error injection into qemu monitor Isaku Yamahata
` (3 subsequent siblings)
16 siblings, 0 replies; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 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 117de61..f43a1c7 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)
@@ -114,3 +118,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] 33+ messages in thread
* [Qemu-devel] [PATCH v5 14/14] pcie/aer: glue aer error injection into qemu monitor
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (12 preceding siblings ...)
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 13/14] pcie/hotplug: introduce pushing attention button command Isaku Yamahata
@ 2010-10-19 9:06 ` Isaku Yamahata
2010-10-19 11:51 ` [Qemu-devel] Re: [PATCH v5 00/14] pcie port switch emulators Michael S. Tsirkin
` (2 subsequent siblings)
16 siblings, 0 replies; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 9:06 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 1b023b0..97d3e2e 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"
@@ -783,3 +785,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] 33+ messages in thread
* [Qemu-devel] Re: [PATCH v5 00/14] pcie port switch emulators
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (13 preceding siblings ...)
2010-10-19 9:06 ` [Qemu-devel] [PATCH v5 14/14] pcie/aer: glue aer error injection into qemu monitor Isaku Yamahata
@ 2010-10-19 11:51 ` Michael S. Tsirkin
2010-10-19 15:19 ` Isaku Yamahata
2010-10-19 11:56 ` Michael S. Tsirkin
2010-10-19 13:00 ` Michael S. Tsirkin
16 siblings, 1 reply; 33+ messages in thread
From: Michael S. Tsirkin @ 2010-10-19 11:51 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, etmartin, qemu-devel, wexu2
On Tue, Oct 19, 2010 at 06:06:27PM +0900, Isaku Yamahata wrote:
> On uncorrectable error status register in pcie_aer_write_config().
> The register is RW1CS, so making it writable and test-and-clear doesn't
> work.
Sure. But isn't this what w1cmask implements?
Also - mail to adnan@khaleel.us seems to bounce. I stripped it
from the Cc list for now.
--
MST
^ permalink raw reply [flat|nested] 33+ messages in thread
* [Qemu-devel] Re: [PATCH v5 00/14] pcie port switch emulators
2010-10-19 11:51 ` [Qemu-devel] Re: [PATCH v5 00/14] pcie port switch emulators Michael S. Tsirkin
@ 2010-10-19 15:19 ` Isaku Yamahata
2010-10-19 17:06 ` Michael S. Tsirkin
0 siblings, 1 reply; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 15:19 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: skandasa, etmartin, qemu-devel, wexu2
On Tue, Oct 19, 2010 at 01:51:31PM +0200, Michael S. Tsirkin wrote:
> On Tue, Oct 19, 2010 at 06:06:27PM +0900, Isaku Yamahata wrote:
> > On uncorrectable error status register in pcie_aer_write_config().
> > The register is RW1CS, so making it writable and test-and-clear doesn't
> > work.
>
> Sure. But isn't this what w1cmask implements?
It's not simple W1C.
Anyway I found the way to code it without the shift operation.
I'll rewrite the function with the next patch series.
--
yamahata
^ permalink raw reply [flat|nested] 33+ messages in thread
* [Qemu-devel] Re: [PATCH v5 00/14] pcie port switch emulators
2010-10-19 15:19 ` Isaku Yamahata
@ 2010-10-19 17:06 ` Michael S. Tsirkin
2010-10-19 22:36 ` Isaku Yamahata
0 siblings, 1 reply; 33+ messages in thread
From: Michael S. Tsirkin @ 2010-10-19 17:06 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, etmartin, qemu-devel, wexu2
On Wed, Oct 20, 2010 at 12:19:47AM +0900, Isaku Yamahata wrote:
> On Tue, Oct 19, 2010 at 01:51:31PM +0200, Michael S. Tsirkin wrote:
> > On Tue, Oct 19, 2010 at 06:06:27PM +0900, Isaku Yamahata wrote:
> > > On uncorrectable error status register in pcie_aer_write_config().
> > > The register is RW1CS, so making it writable and test-and-clear doesn't
> > > work.
> >
> > Sure. But isn't this what w1cmask implements?
>
> It's not simple W1C.
Well it's also sticky, but this only affects reset.
What else is different?
> Anyway I found the way to code it without the shift operation.
> I'll rewrite the function with the next patch series.
> --
> yamahata
^ permalink raw reply [flat|nested] 33+ messages in thread
* [Qemu-devel] Re: [PATCH v5 00/14] pcie port switch emulators
2010-10-19 17:06 ` Michael S. Tsirkin
@ 2010-10-19 22:36 ` Isaku Yamahata
2010-10-19 22:40 ` Michael S. Tsirkin
0 siblings, 1 reply; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 22:36 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: skandasa, etmartin, qemu-devel, wexu2
On Tue, Oct 19, 2010 at 07:06:55PM +0200, Michael S. Tsirkin wrote:
> On Wed, Oct 20, 2010 at 12:19:47AM +0900, Isaku Yamahata wrote:
> > On Tue, Oct 19, 2010 at 01:51:31PM +0200, Michael S. Tsirkin wrote:
> > > On Tue, Oct 19, 2010 at 06:06:27PM +0900, Isaku Yamahata wrote:
> > > > On uncorrectable error status register in pcie_aer_write_config().
> > > > The register is RW1CS, so making it writable and test-and-clear doesn't
> > > > work.
> > >
> > > Sure. But isn't this what w1cmask implements?
> >
> > It's not simple W1C.
>
> Well it's also sticky, but this only affects reset.
> What else is different?
The bit of uncorsta that corresponds to the first error:
If 1b is written, the side effects takes place.
If multiple header recording is disabled, the bit is W1C.
If multiple header recording is enabled, the bit may or may
not be cleared depending on whether recorded errors.
The next error header is pulled out from the internal log.
(the first error bit moves to the one that corresponds to
the pulled-out one.)
The other bits of uncorsta
If multiple header recording is disabled, the bit is W1C.
If multiple header recording is enabled, the value isn't cleared
even when 1b is written. So since we set w1cmask, we need to
restore the old value.
--
yamahata
^ permalink raw reply [flat|nested] 33+ messages in thread
* [Qemu-devel] Re: [PATCH v5 00/14] pcie port switch emulators
2010-10-19 22:36 ` Isaku Yamahata
@ 2010-10-19 22:40 ` Michael S. Tsirkin
2010-10-19 22:55 ` Isaku Yamahata
0 siblings, 1 reply; 33+ messages in thread
From: Michael S. Tsirkin @ 2010-10-19 22:40 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, etmartin, qemu-devel, wexu2
On Wed, Oct 20, 2010 at 07:36:37AM +0900, Isaku Yamahata wrote:
> On Tue, Oct 19, 2010 at 07:06:55PM +0200, Michael S. Tsirkin wrote:
> > On Wed, Oct 20, 2010 at 12:19:47AM +0900, Isaku Yamahata wrote:
> > > On Tue, Oct 19, 2010 at 01:51:31PM +0200, Michael S. Tsirkin wrote:
> > > > On Tue, Oct 19, 2010 at 06:06:27PM +0900, Isaku Yamahata wrote:
> > > > > On uncorrectable error status register in pcie_aer_write_config().
> > > > > The register is RW1CS, so making it writable and test-and-clear doesn't
> > > > > work.
> > > >
> > > > Sure. But isn't this what w1cmask implements?
> > >
> > > It's not simple W1C.
> >
> > Well it's also sticky, but this only affects reset.
> > What else is different?
>
> The bit of uncorsta that corresponds to the first error:
> If 1b is written, the side effects takes place.
> If multiple header recording is disabled, the bit is W1C.
> If multiple header recording is enabled, the bit may or may
> not be cleared depending on whether recorded errors.
> The next error header is pulled out from the internal log.
> (the first error bit moves to the one that corresponds to
> the pulled-out one.)
>
> The other bits of uncorsta
> If multiple header recording is disabled, the bit is W1C.
> If multiple header recording is enabled, the value isn't cleared
> even when 1b is written. So since we set w1cmask, we need to
> restore the old value.
I think I suggested once a good way to implement this:
- always make the bits w1c
- after config write:
if MHR is enabled, and you see that error log is not empty and that bit is 0,
this means that someone has written 1b.
so pop the first error from the log, and set bit to 1 if it's not empty.
This way we only touch w1c mask on setup, no tricky range checks.
Anything wrong with this approach?
--
MST
^ permalink raw reply [flat|nested] 33+ messages in thread
* [Qemu-devel] Re: [PATCH v5 00/14] pcie port switch emulators
2010-10-19 22:40 ` Michael S. Tsirkin
@ 2010-10-19 22:55 ` Isaku Yamahata
2010-10-19 23:02 ` Michael S. Tsirkin
0 siblings, 1 reply; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 22:55 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: skandasa, etmartin, qemu-devel, wexu2
On Wed, Oct 20, 2010 at 12:40:49AM +0200, Michael S. Tsirkin wrote:
> I think I suggested once a good way to implement this:
>
> - always make the bits w1c
> - after config write:
> if MHR is enabled, and you see that error log is not empty and that bit is 0,
> this means that someone has written 1b.
> so pop the first error from the log, and set bit to 1 if it's not empty.
>
> This way we only touch w1c mask on setup, no tricky range checks.
> Anything wrong with this approach?
Basically okay.
Without the range checks, we need to restore the old value to the register
if 1b isn't written to the bit of the first error.
Probably you prefer restoring it mostly always than the range check.
--
yamahata
^ permalink raw reply [flat|nested] 33+ messages in thread
* [Qemu-devel] Re: [PATCH v5 00/14] pcie port switch emulators
2010-10-19 22:55 ` Isaku Yamahata
@ 2010-10-19 23:02 ` Michael S. Tsirkin
0 siblings, 0 replies; 33+ messages in thread
From: Michael S. Tsirkin @ 2010-10-19 23:02 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, etmartin, qemu-devel, wexu2
On Wed, Oct 20, 2010 at 07:55:56AM +0900, Isaku Yamahata wrote:
> On Wed, Oct 20, 2010 at 12:40:49AM +0200, Michael S. Tsirkin wrote:
>
> > I think I suggested once a good way to implement this:
> >
> > - always make the bits w1c
> > - after config write:
> > if MHR is enabled, and you see that error log is not empty and that bit is 0,
> > this means that someone has written 1b.
> > so pop the first error from the log, and set bit to 1 if it's not empty.
> >
> > This way we only touch w1c mask on setup, no tricky range checks.
> > Anything wrong with this approach?
>
> Basically okay.
> Without the range checks, we need to restore the old value to the register
> if 1b isn't written to the bit of the first error.
> Probably you prefer restoring it mostly always than the range check.
Well that would be consistent with how we handle config cycles
in the rest of the code. We had a lot of tricky range checking code
and we killed it all in favor of making operations idempotent so
they are safe to redo on each config cycle.
--
MST
^ permalink raw reply [flat|nested] 33+ messages in thread
* [Qemu-devel] Re: [PATCH v5 00/14] pcie port switch emulators
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (14 preceding siblings ...)
2010-10-19 11:51 ` [Qemu-devel] Re: [PATCH v5 00/14] pcie port switch emulators Michael S. Tsirkin
@ 2010-10-19 11:56 ` Michael S. Tsirkin
2010-10-19 13:55 ` Isaku Yamahata
2010-10-19 13:00 ` Michael S. Tsirkin
16 siblings, 1 reply; 33+ messages in thread
From: Michael S. Tsirkin @ 2010-10-19 11:56 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Tue, Oct 19, 2010 at 06:06:27PM +0900, Isaku Yamahata wrote:
> Here is v5 of the pcie patch series.
> I hope I addressed the blockers.
> On uncorrectable error status register in pcie_aer_write_config().
> The register is RW1CS, so making it writable and test-and-clear doesn't
> work.
>
> new patches: 1, 2,
> updasted patches except trivial change: 4, 7, 8
>
> BTW, as 0.13 is released, any chance to sync pci branch with
> the upstream by requesting pull?
>
> Patch description:
> This patch series implements pcie port switch emulators
> which is basic part for pcie/q35 support.
> This is for mst/pci tree.
>
> change v4 -> v5:
> - introduced pci_xxx_test_and_clear/set_mask
> - eliminated xxx_notify(msi_trigger, int_level)
> - eliminated FLR bits.
> FLR will be addressed at the next phase.
>
> 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 (14):
> pci: introduce helper functions to test-and-{clear, set} mask in
> configuration space
> pci: introduce helper function to handle msi-x and msi.
> pci: use pci_word_test_and_clear_mask() in pci_device_reset()
> 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
I'll apply these.
> pcie/aer: helper functions for pcie aer capability
Maybe move this to the end of the series?
> 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
I think the above can be applied - just remove the dependency
on aer for now.
> pcie/aer: glue aer error injection into qemu monitor
>
> Makefile.objs | 4 +-
> hw/ioh3420.c | 229 +++++++++++++
> hw/ioh3420.h | 10 +
> hw/msi.c | 352 +++++++++++++++++++
> hw/msi.h | 41 +++
> hw/pci.c | 24 ++-
> hw/pci.h | 88 +++++-
> hw/pci_bridge.c | 57 +++-
> hw/pci_bridge.h | 2 +
> hw/pcie.c | 540 +++++++++++++++++++++++++++++
> hw/pcie.h | 113 ++++++
> hw/pcie_aer.c | 869 +++++++++++++++++++++++++++++++++++++++++++++++
> hw/pcie_aer.h | 105 ++++++
> hw/pcie_port.c | 198 +++++++++++
> hw/pcie_port.h | 51 +++
> hw/pcie_regs.h | 154 +++++++++
> hw/xio3130_downstream.c | 197 +++++++++++
> hw/xio3130_downstream.h | 11 +
> hw/xio3130_upstream.c | 181 ++++++++++
> hw/xio3130_upstream.h | 10 +
> qemu-common.h | 6 +
> qemu-monitor.hx | 36 ++
> sysemu.h | 9 +
> 23 files changed, 3272 insertions(+), 15 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] 33+ messages in thread
* [Qemu-devel] Re: [PATCH v5 00/14] pcie port switch emulators
2010-10-19 11:56 ` Michael S. Tsirkin
@ 2010-10-19 13:55 ` Isaku Yamahata
0 siblings, 0 replies; 33+ messages in thread
From: Isaku Yamahata @ 2010-10-19 13:55 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
> > Isaku Yamahata (14):
> > pci: introduce helper functions to test-and-{clear, set} mask in
> > configuration space
> > pci: introduce helper function to handle msi-x and msi.
> > pci: use pci_word_test_and_clear_mask() in pci_device_reset()
> > 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
>
> I'll apply these.
>
> > pcie/aer: helper functions for pcie aer capability
>
> Maybe move this to the end of the series?
>
> > 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
>
> I think the above can be applied - just remove the dependency
> on aer for now.
Okay. I'll update the patch series and send it tomorrow.
--
yamahata
^ permalink raw reply [flat|nested] 33+ messages in thread
* [Qemu-devel] Re: [PATCH v5 00/14] pcie port switch emulators
2010-10-19 9:06 [Qemu-devel] [PATCH v5 00/14] pcie port switch emulators Isaku Yamahata
` (15 preceding siblings ...)
2010-10-19 11:56 ` Michael S. Tsirkin
@ 2010-10-19 13:00 ` Michael S. Tsirkin
16 siblings, 0 replies; 33+ messages in thread
From: Michael S. Tsirkin @ 2010-10-19 13:00 UTC (permalink / raw)
To: Isaku Yamahata; +Cc: skandasa, adnan, etmartin, qemu-devel, wexu2
On Tue, Oct 19, 2010 at 06:06:27PM +0900, Isaku Yamahata wrote:
> Here is v5 of the pcie patch series.
> I hope I addressed the blockers.
> On uncorrectable error status register in pcie_aer_write_config().
> The register is RW1CS, so making it writable and test-and-clear doesn't
> work.
>
> new patches: 1, 2,
> updasted patches except trivial change: 4, 7, 8
Ok, I applied patches 1,2,3 and 5.
> BTW, as 0.13 is released, any chance to sync pci branch with
> the upstream by requesting pull?
>
> Patch description:
> This patch series implements pcie port switch emulators
> which is basic part for pcie/q35 support.
> This is for mst/pci tree.
>
> change v4 -> v5:
> - introduced pci_xxx_test_and_clear/set_mask
> - eliminated xxx_notify(msi_trigger, int_level)
> - eliminated FLR bits.
> FLR will be addressed at the next phase.
>
> 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 (14):
> pci: introduce helper functions to test-and-{clear, set} mask in
> configuration space
> pci: introduce helper function to handle msi-x and msi.
> pci: use pci_word_test_and_clear_mask() in pci_device_reset()
> 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 | 229 +++++++++++++
> hw/ioh3420.h | 10 +
> hw/msi.c | 352 +++++++++++++++++++
> hw/msi.h | 41 +++
> hw/pci.c | 24 ++-
> hw/pci.h | 88 +++++-
> hw/pci_bridge.c | 57 +++-
> hw/pci_bridge.h | 2 +
> hw/pcie.c | 540 +++++++++++++++++++++++++++++
> hw/pcie.h | 113 ++++++
> hw/pcie_aer.c | 869 +++++++++++++++++++++++++++++++++++++++++++++++
> hw/pcie_aer.h | 105 ++++++
> hw/pcie_port.c | 198 +++++++++++
> hw/pcie_port.h | 51 +++
> hw/pcie_regs.h | 154 +++++++++
> hw/xio3130_downstream.c | 197 +++++++++++
> hw/xio3130_downstream.h | 11 +
> hw/xio3130_upstream.c | 181 ++++++++++
> hw/xio3130_upstream.h | 10 +
> qemu-common.h | 6 +
> qemu-monitor.hx | 36 ++
> sysemu.h | 9 +
> 23 files changed, 3272 insertions(+), 15 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] 33+ messages in thread