* [RFC PATCH 01/11] hw/pci: Add pcie_find_dvsec() utility.
2024-02-05 14:19 [RFC PATCH 00/11 qemu] arm/acpi/pci/cxl: ACPI based FW First error injection Jonathan Cameron
@ 2024-02-05 14:19 ` Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 02/11] hw/acpi: Allow GPEX _OSC to keep fw first control of AER and CXL errors Jonathan Cameron
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2024-02-05 14:19 UTC (permalink / raw)
To: linux-cxl, qemu-devel
Cc: Igor Mammedov, Ani Sinha, Shannon Zhao, Dongjiu Geng, linuxarm,
Michael S . Tsirkin, Ira Weiny, Peter Maydell, Fan Ni,
Marcel Apfelbaum
Simple search code used to find first instance of a PCIe
Designated Vendor-Specific Extended Capability.
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
include/hw/pci/pcie.h | 1 +
hw/pci/pcie.c | 24 ++++++++++++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h
index 11f5a91bbb..ff559a6653 100644
--- a/include/hw/pci/pcie.h
+++ b/include/hw/pci/pcie.h
@@ -127,6 +127,7 @@ bool pcie_cap_is_arifwd_enabled(const PCIDevice *dev);
/* PCI express extended capability helper functions */
uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id);
+uint16_t pcie_find_dvsec(PCIDevice *dev, uint16_t vid, uint16_t id);
void pcie_add_capability(PCIDevice *dev,
uint16_t cap_id, uint8_t cap_ver,
uint16_t offset, uint16_t size);
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 6db0cf69cd..9f1ca718b5 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -944,6 +944,30 @@ uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id)
return pcie_find_capability_list(dev, cap_id, NULL);
}
+uint16_t pcie_find_dvsec(PCIDevice *dev, uint16_t vid, uint16_t id)
+{
+ uint16_t prev = 0;
+ uint16_t next;
+
+ while (1) {
+ uint32_t head1;
+
+ next = pcie_find_capability_list(dev, 0x23, &prev);
+ if (!next) {
+ break;
+ }
+ head1 = pci_get_long(dev->config + next + 4);
+ if ((head1 & 0xFFFF) == vid) {
+ uint16_t head2 = pci_get_word(dev->config + next + 8);
+ if (head2 == id) {
+ return next;
+ }
+ }
+ prev = next;
+ }
+ return 0;
+}
+
static void pcie_ext_cap_set_next(PCIDevice *dev, uint16_t pos, uint16_t next)
{
uint32_t header = pci_get_long(dev->config + pos);
--
2.39.2
^ permalink raw reply related [flat|nested] 12+ messages in thread* [RFC PATCH 02/11] hw/acpi: Allow GPEX _OSC to keep fw first control of AER and CXL errors.
2024-02-05 14:19 [RFC PATCH 00/11 qemu] arm/acpi/pci/cxl: ACPI based FW First error injection Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 01/11] hw/pci: Add pcie_find_dvsec() utility Jonathan Cameron
@ 2024-02-05 14:19 ` Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 03/11] arm/virt: Add fw-first-ras property Jonathan Cameron
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2024-02-05 14:19 UTC (permalink / raw)
To: linux-cxl, qemu-devel
Cc: Igor Mammedov, Ani Sinha, Shannon Zhao, Dongjiu Geng, linuxarm,
Michael S . Tsirkin, Ira Weiny, Peter Maydell, Fan Ni,
Marcel Apfelbaum
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
include/hw/acpi/cxl.h | 2 +-
include/hw/pci-host/gpex.h | 1 +
hw/acpi/cxl-stub.c | 2 +-
hw/acpi/cxl.c | 31 +++++++++++++++++++++++++++----
hw/i386/acpi-build.c | 2 +-
hw/pci-host/gpex-acpi.c | 17 +++++++++++------
hw/pci-host/gpex.c | 1 +
7 files changed, 43 insertions(+), 13 deletions(-)
diff --git a/include/hw/acpi/cxl.h b/include/hw/acpi/cxl.h
index 8f22c71530..38714147ec 100644
--- a/include/hw/acpi/cxl.h
+++ b/include/hw/acpi/cxl.h
@@ -24,7 +24,7 @@
void cxl_build_cedt(GArray *table_offsets, GArray *table_data,
BIOSLinker *linker, const char *oem_id,
const char *oem_table_id, CXLState *cxl_state);
-void build_cxl_osc_method(Aml *dev);
+void build_cxl_osc_method(Aml *dev, bool fw_first);
void build_cxl_dsm_method(Aml *dev);
#endif
diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h
index dce883573b..866ae71ba0 100644
--- a/include/hw/pci-host/gpex.h
+++ b/include/hw/pci-host/gpex.h
@@ -47,6 +47,7 @@ struct GPEXConfig {
MemMapEntry pio;
int irq;
PCIBus *bus;
+ bool fw_first_ras;
};
struct GPEXHost {
diff --git a/hw/acpi/cxl-stub.c b/hw/acpi/cxl-stub.c
index 15bc21076b..0ec5c48850 100644
--- a/hw/acpi/cxl-stub.c
+++ b/hw/acpi/cxl-stub.c
@@ -6,7 +6,7 @@
#include "hw/acpi/aml-build.h"
#include "hw/acpi/cxl.h"
-void build_cxl_osc_method(Aml *dev)
+void build_cxl_osc_method(Aml *dev, bool fw_first)
{
g_assert_not_reached();
}
diff --git a/hw/acpi/cxl.c b/hw/acpi/cxl.c
index d0e6a4b45e..526cfe961a 100644
--- a/hw/acpi/cxl.c
+++ b/hw/acpi/cxl.c
@@ -27,6 +27,7 @@
#include "hw/acpi/aml-build.h"
#include "hw/acpi/bios-linker-loader.h"
#include "hw/acpi/cxl.h"
+#include "hw/acpi/ghes.h"
#include "qapi/error.h"
#include "qemu/uuid.h"
@@ -222,11 +223,12 @@ void cxl_build_cedt(GArray *table_offsets, GArray *table_data,
acpi_table_end(linker, &table);
}
-static Aml *__build_cxl_osc_method(void)
+static Aml *__build_cxl_osc_method(bool fw_first)
{
Aml *method, *if_uuid, *else_uuid, *if_arg1_not_1, *if_cxl, *if_caps_masked;
Aml *a_ctrl = aml_local(0);
Aml *a_cdw1 = aml_name("CDW1");
+ Aml *cxl_ctrl = aml_local(2);
method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
/* CDW1 is used for the return value so is present whether or not a match occurs */
@@ -260,7 +262,11 @@ static Aml *__build_cxl_osc_method(void)
* Allows OS control for all 5 features:
* PCIeHotplug SHPCHotplug PME AER PCIeCapability
*/
- aml_append(if_uuid, aml_and(a_ctrl, aml_int(0x1F), a_ctrl));
+ if (fw_first) {
+ aml_append(if_uuid, aml_and(a_ctrl, aml_int(0x17), a_ctrl));
+ } else {
+ aml_append(if_uuid, aml_and(a_ctrl, aml_int(0x1F), a_ctrl));
+ }
/*
* Check _OSC revision.
@@ -290,6 +296,23 @@ static Aml *__build_cxl_osc_method(void)
aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(12), "CDW4"));
/* CXL capabilities */
aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(16), "CDW5"));
+
+ aml_append(if_cxl, aml_store(aml_name("CDW5"), cxl_ctrl));
+ if (fw_first) {
+ aml_append(if_cxl, aml_and(cxl_ctrl, aml_int(0x0), cxl_ctrl));
+ } else {
+ /* Only allow CXL Memory Error Reporting */
+ aml_append(if_cxl, aml_and(cxl_ctrl, aml_int(0x1), cxl_ctrl));
+ }
+
+ if_caps_masked = aml_if(aml_lnot(aml_equal(aml_name("CDW5"), cxl_ctrl)));
+
+ /* Capability bits were masked */
+ aml_append(if_caps_masked, aml_or(a_cdw1, aml_int(0x10), a_cdw1));
+ aml_append(if_cxl, if_caps_masked);
+
+ aml_append(if_cxl, aml_store(cxl_ctrl, aml_name("CDW5")));
+
aml_append(if_cxl, aml_store(aml_name("CDW4"), aml_name("SUPC")));
aml_append(if_cxl, aml_store(aml_name("CDW5"), aml_name("CTRC")));
@@ -316,11 +339,11 @@ static Aml *__build_cxl_osc_method(void)
return method;
}
-void build_cxl_osc_method(Aml *dev)
+void build_cxl_osc_method(Aml *dev, bool fw_first)
{
aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
aml_append(dev, aml_name_decl("SUPC", aml_int(0)));
aml_append(dev, aml_name_decl("CTRC", aml_int(0)));
- aml_append(dev, __build_cxl_osc_method());
+ aml_append(dev, __build_cxl_osc_method(fw_first));
}
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 58e4c54f31..2fbac1d826 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -1655,7 +1655,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
aml_append(aml_pkg, aml_eisaid("PNP0A08"));
aml_append(aml_pkg, aml_eisaid("PNP0A03"));
aml_append(dev, aml_name_decl("_CID", aml_pkg));
- build_cxl_osc_method(dev);
+ build_cxl_osc_method(dev, false);
} else if (pci_bus_is_express(bus)) {
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08")));
aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03")));
diff --git a/hw/pci-host/gpex-acpi.c b/hw/pci-host/gpex-acpi.c
index f69413ea2c..f003669975 100644
--- a/hw/pci-host/gpex-acpi.c
+++ b/hw/pci-host/gpex-acpi.c
@@ -49,7 +49,7 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq)
}
}
-static void acpi_dsdt_add_pci_osc(Aml *dev)
+static void acpi_dsdt_add_pci_osc(Aml *dev, bool fw_first_aer)
{
Aml *method, *UUID, *ifctx, *ifctx1, *elsectx, *buf;
@@ -79,8 +79,13 @@ static void acpi_dsdt_add_pci_osc(Aml *dev)
* Allow OS control for all 5 features:
* PCIeHotplug SHPCHotplug PME AER PCIeCapability.
*/
- aml_append(ifctx, aml_and(aml_name("CTRL"), aml_int(0x1F),
- aml_name("CTRL")));
+ if (fw_first_aer) {
+ aml_append(ifctx, aml_and(aml_name("CTRL"), aml_int(0x17),
+ aml_name("CTRL")));
+ } else {
+ aml_append(ifctx, aml_and(aml_name("CTRL"), aml_int(0x1F),
+ aml_name("CTRL")));
+ }
ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1))));
aml_append(ifctx1, aml_or(aml_name("CDW1"), aml_int(0x08),
@@ -186,9 +191,9 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg)
aml_append(dev, aml_name_decl("_CRS", crs));
if (is_cxl) {
- build_cxl_osc_method(dev);
+ build_cxl_osc_method(dev, cfg->fw_first_ras);
} else {
- acpi_dsdt_add_pci_osc(dev);
+ acpi_dsdt_add_pci_osc(dev, cfg->fw_first_ras);
}
aml_append(scope, dev);
@@ -263,7 +268,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg)
}
aml_append(dev, aml_name_decl("_CRS", rbuf));
- acpi_dsdt_add_pci_osc(dev);
+ acpi_dsdt_add_pci_osc(dev, cfg->fw_first_ras);
Aml *dev_res0 = aml_device("%s", "RES0");
aml_append(dev_res0, aml_name_decl("_HID", aml_string("PNP0C02")));
diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c
index e9cf455bf5..49fc69eec6 100644
--- a/hw/pci-host/gpex.c
+++ b/hw/pci-host/gpex.c
@@ -166,6 +166,7 @@ static Property gpex_host_properties[] = {
gpex_cfg.mmio64.base, 0),
DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MMIO_SIZE, GPEXHost,
gpex_cfg.mmio64.size, 0),
+ DEFINE_PROP_BOOL("fw_first_ras", GPEXHost, gpex_cfg.fw_first_ras, false),
DEFINE_PROP_END_OF_LIST(),
};
--
2.39.2
^ permalink raw reply related [flat|nested] 12+ messages in thread* [RFC PATCH 03/11] arm/virt: Add fw-first-ras property.
2024-02-05 14:19 [RFC PATCH 00/11 qemu] arm/acpi/pci/cxl: ACPI based FW First error injection Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 01/11] hw/pci: Add pcie_find_dvsec() utility Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 02/11] hw/acpi: Allow GPEX _OSC to keep fw first control of AER and CXL errors Jonathan Cameron
@ 2024-02-05 14:19 ` Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 04/11] acpi/ghes: Support GPIO error source Jonathan Cameron
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2024-02-05 14:19 UTC (permalink / raw)
To: linux-cxl, qemu-devel
Cc: Igor Mammedov, Ani Sinha, Shannon Zhao, Dongjiu Geng, linuxarm,
Michael S . Tsirkin, Ira Weiny, Peter Maydell, Fan Ni,
Marcel Apfelbaum
Provide a machine parameter to request firmware first RAS handling
and no hand over to the OS via _OSC.
Includes a bug fix as register access is not in CDW5 but only
in CXW4 (OS support field).
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
include/hw/arm/virt.h | 1 +
hw/acpi/cxl.c | 3 ---
hw/arm/virt-acpi-build.c | 1 +
hw/arm/virt.c | 20 ++++++++++++++++++++
4 files changed, 22 insertions(+), 3 deletions(-)
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 0a14551f19..84323ccb32 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -153,6 +153,7 @@ struct VirtMachineState {
bool tcg_its;
bool virt;
bool ras;
+ bool fw_first_ras;
bool mte;
bool dtb_randomness;
OnOffAuto acpi;
diff --git a/hw/acpi/cxl.c b/hw/acpi/cxl.c
index 526cfe961a..1d6dadbddd 100644
--- a/hw/acpi/cxl.c
+++ b/hw/acpi/cxl.c
@@ -316,9 +316,6 @@ static Aml *__build_cxl_osc_method(bool fw_first)
aml_append(if_cxl, aml_store(aml_name("CDW4"), aml_name("SUPC")));
aml_append(if_cxl, aml_store(aml_name("CDW5"), aml_name("CTRC")));
- /* CXL 2.0 Port/Device Register access */
- aml_append(if_cxl,
- aml_or(aml_name("CDW5"), aml_int(0x1), aml_name("CDW5")));
aml_append(if_uuid, if_cxl);
aml_append(if_uuid, aml_return(aml_arg(3)));
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index e5f6996111..cdc0bca729 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -208,6 +208,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
.ecam = memmap[ecam_id],
.irq = irq,
.bus = vms->bus,
+ .fw_first_ras = vms->fw_first_ras,
};
if (vms->highmem_mmio) {
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 14d3b66657..c1c8a514d7 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2864,6 +2864,20 @@ static void virt_set_ras(Object *obj, bool value, Error **errp)
vms->ras = value;
}
+static bool virt_get_fw_first_ras(Object *obj, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+
+ return vms->fw_first_ras;
+}
+
+static void virt_set_fw_first_ras(Object *obj, bool value, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+
+ vms->fw_first_ras = value;
+}
+
static bool virt_get_mte(Object *obj, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
@@ -3400,6 +3414,12 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
"Set on/off to enable/disable reporting host memory errors "
"to a KVM guest using ACPI and guest external abort exceptions");
+ object_class_property_add_bool(oc, "fw-first-ras", virt_get_fw_first_ras,
+ virt_set_fw_first_ras);
+ object_class_property_set_description(oc, "fw-first-ras",
+ "Set on/off to control PCI/CXL _OSC allow the guest to"
+ "obtain permission to do native handling of AER and CXL errors");
+
object_class_property_add_bool(oc, "mte", virt_get_mte, virt_set_mte);
object_class_property_set_description(oc, "mte",
"Set on/off to enable/disable emulating a "
--
2.39.2
^ permalink raw reply related [flat|nested] 12+ messages in thread* [RFC PATCH 04/11] acpi/ghes: Support GPIO error source.
2024-02-05 14:19 [RFC PATCH 00/11 qemu] arm/acpi/pci/cxl: ACPI based FW First error injection Jonathan Cameron
` (2 preceding siblings ...)
2024-02-05 14:19 ` [RFC PATCH 03/11] arm/virt: Add fw-first-ras property Jonathan Cameron
@ 2024-02-05 14:19 ` Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 05/11] arm/virt: Wire up GPIO error source for ACPI / GHES Jonathan Cameron
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2024-02-05 14:19 UTC (permalink / raw)
To: linux-cxl, qemu-devel
Cc: Igor Mammedov, Ani Sinha, Shannon Zhao, Dongjiu Geng, linuxarm,
Michael S . Tsirkin, Ira Weiny, Peter Maydell, Fan Ni,
Marcel Apfelbaum
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
include/hw/acpi/ghes.h | 1 +
hw/acpi/ghes.c | 8 ++++++--
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h
index 674f6958e9..4f1ab1a73a 100644
--- a/include/hw/acpi/ghes.h
+++ b/include/hw/acpi/ghes.h
@@ -58,6 +58,7 @@ enum AcpiGhesNotifyType {
enum {
ACPI_HEST_SRC_ID_SEA = 0,
+ ACPI_HEST_SRC_ID_GPIO = 1,
/* future ids go here */
ACPI_HEST_SRC_ID_RESERVED,
};
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
index e9511d9b8f..5b8bc6eeb4 100644
--- a/hw/acpi/ghes.c
+++ b/hw/acpi/ghes.c
@@ -34,8 +34,8 @@
/* The max size in bytes for one error block */
#define ACPI_GHES_MAX_RAW_DATA_LENGTH (1 * KiB)
-/* Now only support ARMv8 SEA notification type error source */
-#define ACPI_GHES_ERROR_SOURCE_COUNT 1
+/* Support ARMv8 SEA notification type error source and GPIO interrupt. */
+#define ACPI_GHES_ERROR_SOURCE_COUNT 2
/* Generic Hardware Error Source version 2 */
#define ACPI_GHES_SOURCE_GENERIC_ERROR_V2 10
@@ -327,6 +327,9 @@ static void build_ghes_v2(GArray *table_data, int source_id, BIOSLinker *linker)
*/
build_ghes_hw_error_notification(table_data, ACPI_GHES_NOTIFY_SEA);
break;
+ case ACPI_HEST_SRC_ID_GPIO:
+ build_ghes_hw_error_notification(table_data, ACPI_GHES_NOTIFY_GPIO);
+ break;
default:
error_report("Not support this error source");
abort();
@@ -370,6 +373,7 @@ void acpi_build_hest(GArray *table_data, BIOSLinker *linker,
/* Error Source Count */
build_append_int_noprefix(table_data, ACPI_GHES_ERROR_SOURCE_COUNT, 4);
build_ghes_v2(table_data, ACPI_HEST_SRC_ID_SEA, linker);
+ build_ghes_v2(table_data, ACPI_HEST_SRC_ID_GPIO, linker);
acpi_table_end(linker, &table);
}
--
2.39.2
^ permalink raw reply related [flat|nested] 12+ messages in thread* [RFC PATCH 05/11] arm/virt: Wire up GPIO error source for ACPI / GHES
2024-02-05 14:19 [RFC PATCH 00/11 qemu] arm/acpi/pci/cxl: ACPI based FW First error injection Jonathan Cameron
` (3 preceding siblings ...)
2024-02-05 14:19 ` [RFC PATCH 04/11] acpi/ghes: Support GPIO error source Jonathan Cameron
@ 2024-02-05 14:19 ` Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 06/11] acpi: pci/cxl: Stash the OSC control parameters Jonathan Cameron
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2024-02-05 14:19 UTC (permalink / raw)
To: linux-cxl, qemu-devel
Cc: Igor Mammedov, Ani Sinha, Shannon Zhao, Dongjiu Geng, linuxarm,
Michael S . Tsirkin, Ira Weiny, Peter Maydell, Fan Ni,
Marcel Apfelbaum
Includes creation of a GED - Generic Event Device
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
include/hw/boards.h | 1 +
hw/arm/virt-acpi-build.c | 29 +++++++++++++++++++++++++----
hw/arm/virt.c | 12 +++++++++++-
3 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/include/hw/boards.h b/include/hw/boards.h
index bcfde8a84d..a9badd9fd2 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -301,6 +301,7 @@ struct MachineClass {
const CPUArchIdList *(*possible_cpu_arch_ids)(MachineState *machine);
int64_t (*get_default_cpu_node_id)(const MachineState *ms, int idx);
ram_addr_t (*fixup_ram_size)(ram_addr_t size);
+ void (*set_error)(void);
};
/**
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index cdc0bca729..297fa5f8b2 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -64,6 +64,7 @@
#define ARM_SPI_BASE 32
+#define ACPI_GENERIC_EVENT_DEVICE "GEDD"
#define ACPI_BUILD_TABLE_SIZE 0x20000
static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms)
@@ -242,9 +243,14 @@ static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap,
Aml *aei = aml_resource_template();
/* Pin 3 for power button */
- const uint32_t pin_list[1] = {3};
+ uint32_t pin = 3;
aml_append(aei, aml_gpio_int(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
- AML_EXCLUSIVE, AML_PULL_UP, 0, pin_list, 1,
+ AML_EXCLUSIVE, AML_PULL_UP, 0, &pin, 1,
+ "GPO0", NULL, 0));
+ pin = 6;
+ /* Pin 8 for generic error */
+ aml_append(aei, aml_gpio_int(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, AML_PULL_UP, 0, &pin, 1,
"GPO0", NULL, 0));
aml_append(dev, aml_name_decl("_AEI", aei));
@@ -253,6 +259,11 @@ static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap,
aml_append(method, aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
aml_int(0x80)));
aml_append(dev, method);
+ method = aml_method("_E06", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_notify(aml_name(ACPI_GENERIC_EVENT_DEVICE),
+ aml_int(0x80)));
+ aml_append(dev, method);
+
aml_append(scope, dev);
}
@@ -885,6 +896,15 @@ static void build_fadt_rev6(GArray *table_data, BIOSLinker *linker,
build_fadt(table_data, linker, &fadt, vms->oem_id, vms->oem_table_id);
}
+static void acpi_dsdt_add_generic_event_device(Aml *scope)
+{
+ Aml *dev = aml_device(ACPI_GENERIC_EVENT_DEVICE);
+ aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C33")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
+ aml_append(scope, dev);
+}
+
/* DSDT */
static void
build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
@@ -926,9 +946,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
irqmap[VIRT_ACPI_GED] + ARM_SPI_BASE, AML_SYSTEM_MEMORY,
memmap[VIRT_ACPI_GED].base);
} else {
- acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO],
- (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
}
+ acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO],
+ (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
if (vms->acpi_dev) {
uint32_t event = object_property_get_uint(OBJECT(vms->acpi_dev),
@@ -942,6 +962,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
}
acpi_dsdt_add_power_button(scope);
+ acpi_dsdt_add_generic_event_device(scope);
#ifdef CONFIG_TPM
acpi_dsdt_add_tpm(scope, vms);
#endif
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index c1c8a514d7..c87dc5acce 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -914,6 +914,13 @@ static void create_rtc(const VirtMachineState *vms)
}
static DeviceState *gpio_key_dev;
+
+static DeviceState *gpio_error_dev;
+static void virt_set_error(void)
+{
+ qemu_set_irq(qdev_get_gpio_in(gpio_error_dev, 0), 1);
+}
+
static void virt_powerdown_req(Notifier *n, void *opaque)
{
VirtMachineState *s = container_of(n, VirtMachineState, powerdown_notifier);
@@ -931,6 +938,8 @@ static void create_gpio_keys(char *fdt, DeviceState *pl061_dev,
{
gpio_key_dev = sysbus_create_simple("gpio-key", -1,
qdev_get_gpio_in(pl061_dev, 3));
+ gpio_error_dev = sysbus_create_simple("gpio-key", -1,
+ qdev_get_gpio_in(pl061_dev, 6));
qemu_fdt_add_subnode(fdt, "/gpio-keys");
qemu_fdt_setprop_string(fdt, "/gpio-keys", "compatible", "gpio-keys");
@@ -2606,8 +2615,8 @@ static void machvirt_init(MachineState *machine)
if (has_ged && aarch64 && firmware_loaded && virt_is_acpi_enabled(vms)) {
vms->acpi_dev = create_acpi_ged(vms);
} else {
- create_gpio_devices(vms, VIRT_GPIO, sysmem);
}
+ create_gpio_devices(vms, VIRT_GPIO, sysmem);
if (vms->secure && !vmc->no_secure_gpio) {
create_gpio_devices(vms, VIRT_SECURE_GPIO, secure_sysmem);
@@ -3337,6 +3346,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
mc->default_ram_id = "mach-virt.ram";
mc->default_nic = "virtio-net-pci";
+ mc->set_error = virt_set_error;
object_class_property_add(oc, "acpi", "OnOffAuto",
virt_get_acpi, virt_set_acpi,
NULL, NULL);
--
2.39.2
^ permalink raw reply related [flat|nested] 12+ messages in thread* [RFC PATCH 06/11] acpi: pci/cxl: Stash the OSC control parameters.
2024-02-05 14:19 [RFC PATCH 00/11 qemu] arm/acpi/pci/cxl: ACPI based FW First error injection Jonathan Cameron
` (4 preceding siblings ...)
2024-02-05 14:19 ` [RFC PATCH 05/11] arm/virt: Wire up GPIO error source for ACPI / GHES Jonathan Cameron
@ 2024-02-05 14:19 ` Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 07/11] pci/aer: Support firmware first error injection via GHESv2 Jonathan Cameron
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2024-02-05 14:19 UTC (permalink / raw)
To: linux-cxl, qemu-devel
Cc: Igor Mammedov, Ani Sinha, Shannon Zhao, Dongjiu Geng, linuxarm,
Michael S . Tsirkin, Ira Weiny, Peter Maydell, Fan Ni,
Marcel Apfelbaum
Allow QEMU to know what was successfully requested by the OS
via _OSC. Note this handling is very minimal and assumes last
written Control parameters were accepted (which they should be
if the OS is obeying the rules for negotiating this stuff).
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
include/hw/acpi/ghes.h | 3 +++
hw/acpi/cxl.c | 16 +++++++++++++++
hw/acpi/ghes-stub.c | 10 +++++++++
hw/acpi/ghes.c | 44 ++++++++++++++++++++++++++++++++++++++++
hw/arm/virt-acpi-build.c | 41 ++++++++++++++++++++++++++++++++++++-
5 files changed, 113 insertions(+), 1 deletion(-)
diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h
index 4f1ab1a73a..3210c19c14 100644
--- a/include/hw/acpi/ghes.h
+++ b/include/hw/acpi/ghes.h
@@ -66,6 +66,7 @@ enum {
typedef struct AcpiGhesState {
uint64_t ghes_addr_le;
bool present; /* True if GHES is present at all on this board */
+ uint64_t pci_osc_addr_le;
} AcpiGhesState;
void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker);
@@ -82,4 +83,6 @@ int acpi_ghes_record_errors(uint8_t notify, uint64_t error_physical_addr);
* safe to call acpi_ghes_record_errors() to record a memory error.
*/
bool acpi_ghes_present(void);
+bool acpi_fw_first_pci(void);
+bool acpi_fw_first_cxl_mem(void);
#endif
diff --git a/hw/acpi/cxl.c b/hw/acpi/cxl.c
index 1d6dadbddd..2ce3488943 100644
--- a/hw/acpi/cxl.c
+++ b/hw/acpi/cxl.c
@@ -228,11 +228,27 @@ static Aml *__build_cxl_osc_method(bool fw_first)
Aml *method, *if_uuid, *else_uuid, *if_arg1_not_1, *if_cxl, *if_caps_masked;
Aml *a_ctrl = aml_local(0);
Aml *a_cdw1 = aml_name("CDW1");
+ Aml *cxl_osc_mem = aml_local(1);
Aml *cxl_ctrl = aml_local(2);
+ Aml *field;
+
method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
/* CDW1 is used for the return value so is present whether or not a match occurs */
aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
+ if (acpi_ghes_present()) {
+ aml_append(method, aml_store(aml_name("COSC"), cxl_osc_mem));
+ aml_append(method, aml_operation_region("CXLA", AML_SYSTEM_MEMORY,
+ cxl_osc_mem, 64));
+
+ field = aml_field("CXLA", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("ODW1", 32));
+ aml_append(field, aml_named_field("ODW2", 32));
+ aml_append(method, field);
+ /* Store the value for querying later */
+ aml_append(method, aml_store(aml_name("CTRL"), aml_name("ODW1")));
+ aml_append(method, aml_store(aml_name("CTRC"), aml_name("ODW2")));
+ }
/*
* Generate shared section between:
diff --git a/hw/acpi/ghes-stub.c b/hw/acpi/ghes-stub.c
index c315de1802..1ad7b9f776 100644
--- a/hw/acpi/ghes-stub.c
+++ b/hw/acpi/ghes-stub.c
@@ -20,3 +20,13 @@ bool acpi_ghes_present(void)
{
return false;
}
+
+bool acpi_fw_first_pci(void)
+{
+ return false;
+}
+
+bool acpi_fw_first_cxl_mem(void)
+{
+ return false;
+}
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
index 5b8bc6eeb4..9f99202e1f 100644
--- a/hw/acpi/ghes.c
+++ b/hw/acpi/ghes.c
@@ -462,3 +462,47 @@ bool acpi_ghes_present(void)
ags = &acpi_ged_state->ghes_state;
return ags->present;
}
+
+bool acpi_fw_first_pci(void)
+{
+ if (acpi_ghes_present()) {
+ AcpiGhesState *ags =
+ &ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED,
+ NULL))->ghes_state;
+ uint32_t pci_osc;
+
+ cpu_physical_memory_read(le64_to_cpu(ags->pci_osc_addr_le),
+ &pci_osc, sizeof(pci_osc));
+ if (pci_osc == 0) {
+ printf("OSC not called yet\n");
+ return true; /* OSC not run yet */
+ }
+ printf("OSC has been called %x\n", pci_osc);
+ return !(pci_osc & (1 << 3));
+ }
+ return false;
+}
+
+bool acpi_fw_first_cxl_mem(void)
+{
+ if (!acpi_fw_first_pci()) {
+ return false;
+ }
+ if (acpi_ghes_present()) {
+ AcpiGhesState *ags =
+ &ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED,
+ NULL))->ghes_state;
+ uint32_t cxl_osc;
+
+ cpu_physical_memory_read(le64_to_cpu(ags->pci_osc_addr_le) +
+ sizeof(uint32_t),
+ &cxl_osc, sizeof(cxl_osc));
+ if (cxl_osc == 0) {
+ printf("CXL OSC not called yet or memory error not requested\n");
+ return true; /* OSC not run yet */
+ }
+ printf("OSC has been called %x\n", cxl_osc);
+ return !(cxl_osc & (1 << 0));
+ }
+ return false;
+}
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 297fa5f8b2..93ec095b0f 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -916,6 +916,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
const int *irqmap = vms->irqmap;
AcpiTable table = { .sig = "DSDT", .rev = 2, .oem_id = vms->oem_id,
.oem_table_id = vms->oem_table_id };
+ int mem_addr_offset;
acpi_table_begin(&table, table_data);
dsdt = init_aml_allocator();
@@ -972,6 +973,16 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
/* copy AML table into ACPI tables blob */
g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
+ /* Outside of the DSDT creation because we need the final address */
+ mem_addr_offset = build_append_named_dword(table_data, "COSC");
+ /* Patch COSC to point to the cxl-osc FW_CFG file */
+ bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE,
+ mem_addr_offset, sizeof(uint32_t),
+ "etc/acpi/cxl-osc", 0);
+ /* Store address of cxl-osc FW_CFG file in cxl-osc-addr FW_CFG file */
+ bios_linker_loader_write_pointer(linker, "etc/acpi/cxl-osc-addr", 0,
+ sizeof(uint64_t), "etc/acpi/cxl-osc", 0);
+
acpi_table_end(linker, &table);
free_aml_allocator();
}
@@ -995,6 +1006,8 @@ static void acpi_align_size(GArray *blob, unsigned align)
g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
}
+static GArray *test;
+
static
void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
{
@@ -1004,6 +1017,10 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
GArray *tables_blob = tables->table_data;
MachineState *ms = MACHINE(vms);
+ /* Load the cxl-osc FW_CFG file into guest memory */
+ bios_linker_loader_alloc(tables->linker, "etc/acpi/cxl-osc",
+ test, 64, false);
+
table_offsets = g_array_new(false, true /* clear */,
sizeof(uint32_t));
@@ -1202,6 +1219,10 @@ void virt_acpi_setup(VirtMachineState *vms)
build_state = g_malloc0(sizeof *build_state);
+ test = g_array_new(false, true, 4);
+ acpi_data_push(test, sizeof(uint64_t));
+ *((uint64_t *)test->data) = 0xdeadbeefdeadbeef;
+
acpi_build_tables_init(&tables);
virt_acpi_build(vms, &tables);
@@ -1234,7 +1255,25 @@ void virt_acpi_setup(VirtMachineState *vms)
virt_acpi_build_reset(build_state);
vmstate_register(NULL, 0, &vmstate_virt_acpi_build, build_state);
- /* Cleanup tables but don't free the memory: we track it
+ if (acpi_ghes_present()) {
+ AcpiGhesState *ags =
+ &ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED,
+ NULL))->ghes_state;
+
+ /* Add a cxl-osc FW_CFG file that will be used to stash osc outcomes */
+ fw_cfg_add_file(vms->fw_cfg, "etc/acpi/cxl-osc",
+ test->data, test->len);
+ /*
+ * Add a cxl-osc-addr FW_CFG file that will be used to get to the
+ * address of cxl-osc FW_CFG file. Can be written by FW.
+ */
+ fw_cfg_add_file_callback(vms->fw_cfg, "etc/acpi/cxl-osc-addr",
+ NULL, NULL, NULL,
+ &ags->pci_osc_addr_le, sizeof(uint64_t),
+ false);
+ }
+ /*
+ * Cleanup tables but don't free the memory: we track it
* in build_state.
*/
acpi_build_tables_cleanup(&tables, false);
--
2.39.2
^ permalink raw reply related [flat|nested] 12+ messages in thread* [RFC PATCH 07/11] pci/aer: Support firmware first error injection via GHESv2
2024-02-05 14:19 [RFC PATCH 00/11 qemu] arm/acpi/pci/cxl: ACPI based FW First error injection Jonathan Cameron
` (5 preceding siblings ...)
2024-02-05 14:19 ` [RFC PATCH 06/11] acpi: pci/cxl: Stash the OSC control parameters Jonathan Cameron
@ 2024-02-05 14:19 ` Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 08/11] hw/pci/aer: Default to error handling on Jonathan Cameron
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2024-02-05 14:19 UTC (permalink / raw)
To: linux-cxl, qemu-devel
Cc: Igor Mammedov, Ani Sinha, Shannon Zhao, Dongjiu Geng, linuxarm,
Michael S . Tsirkin, Ira Weiny, Peter Maydell, Fan Ni,
Marcel Apfelbaum
If the machine supports firmware first error injection
enable those flows.
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
include/hw/acpi/ghes.h | 3 +
hw/acpi/ghes-stub.c | 4 +
hw/acpi/ghes.c | 250 +++++++++++++++++++++++++++++++++++++++--
hw/pci/pcie_aer.c | 35 ++++--
4 files changed, 271 insertions(+), 21 deletions(-)
diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h
index 3210c19c14..437aeae7f6 100644
--- a/include/hw/acpi/ghes.h
+++ b/include/hw/acpi/ghes.h
@@ -76,6 +76,9 @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s,
GArray *hardware_errors);
int acpi_ghes_record_errors(uint8_t notify, uint64_t error_physical_addr);
+typedef struct PCIDevice PCIDevice;
+bool ghes_record_aer_errors(PCIDevice *dev, uint32_t notify);
+
/**
* acpi_ghes_present: Report whether ACPI GHES table is present
*
diff --git a/hw/acpi/ghes-stub.c b/hw/acpi/ghes-stub.c
index 1ad7b9f776..bd208af4ec 100644
--- a/hw/acpi/ghes-stub.c
+++ b/hw/acpi/ghes-stub.c
@@ -11,6 +11,10 @@
#include "qemu/osdep.h"
#include "hw/acpi/ghes.h"
+bool ghes_record_aer_errors(PCIDevice *dev, uint32_t notify)
+{
+ return true;
+}
int acpi_ghes_record_errors(uint8_t source_id, uint64_t physical_address)
{
return -1;
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
index 9f99202e1f..d0103c0a6a 100644
--- a/hw/acpi/ghes.c
+++ b/hw/acpi/ghes.c
@@ -26,6 +26,8 @@
#include "qemu/error-report.h"
#include "hw/acpi/generic_event_device.h"
#include "hw/nvram/fw_cfg.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_device.h"
#include "qemu/uuid.h"
#define ACPI_GHES_ERRORS_FW_CFG_FILE "etc/hardware_errors"
@@ -52,6 +54,7 @@
/* The memory section CPER size, UEFI 2.6: N.2.5 Memory Error Section */
#define ACPI_GHES_MEM_CPER_LENGTH 80
+#define ACPI_GHES_PCIE_CPER_LENGTH 208
/* Masks for block_status flags */
#define ACPI_GEBS_UNCORRECTABLE 1
@@ -184,6 +187,98 @@ static void acpi_ghes_build_append_mem_cper(GArray *table,
build_append_int_noprefix(table, 0, 7);
}
+static void build_append_aer_cper(PCIDevice *dev, GArray *table)
+{
+ PCIDeviceClass *pci_class = PCI_DEVICE_GET_CLASS(dev);
+ uint16_t pcie_cap_offset = pci_find_capability(dev, 0x10);
+ uint16_t sn_cap_offset = pcie_find_capability(dev, 0x3);
+ uint16_t aer_cap_offset = pcie_find_capability(dev, 0x1);
+ int i;
+
+ build_append_int_noprefix(table,
+ /* Port Type */
+ ((pcie_cap_offset ? 1UL : 0UL) << 0) |
+ /* PCI Express Version */
+ (1UL << 1) |
+ /* Command Status */
+ (1UL << 2) |
+ /* Device ID valid */
+ (1UL << 3) |
+ /* Serial Number */
+ ((sn_cap_offset ? 1UL : 0UL) << 4) |
+ /* Whole PCIe Capability */
+ ((pcie_cap_offset ? 1UL : 0UL) << 6) |
+ /* AER capability */
+ ((aer_cap_offset ? 1UL : 0UL) << 7),
+ 8);
+ if (pcie_cap_offset) {
+ uint16_t cap_reg = pci_get_word(dev->config + pcie_cap_offset
+ + PCI_EXP_FLAGS);
+ uint16_t port_type = (cap_reg & PCI_EXP_FLAGS_TYPE) >>
+ PCI_EXP_FLAGS_TYPE_SHIFT;
+
+ build_append_int_noprefix(table, port_type, 4);
+ }
+ build_append_int_noprefix(table, 1, 1); /* Version PCIE r6.1 */
+ build_append_int_noprefix(table, 6, 1);
+ build_append_int_noprefix(table, 0, 2); /* Reserved */
+
+ build_append_int_noprefix(table,
+ pci_get_word(dev->config + PCI_COMMAND), 2);
+ build_append_int_noprefix(table, pci_get_word(dev->config + PCI_STATUS), 2);
+ build_append_int_noprefix(table, 0, 4); /* 20-23 reserved */
+
+ build_append_int_noprefix(table, pci_class->vendor_id, 2);
+ build_append_int_noprefix(table, pci_class->device_id, 2);
+ build_append_int_noprefix(table, pci_class->class_id, 3);
+ build_append_int_noprefix(table, PCI_FUNC(dev->devfn), 1);
+ build_append_int_noprefix(table, PCI_SLOT(dev->devfn), 1);
+ build_append_int_noprefix(table, 0, 2); /* Segment number */
+
+ /* RP/B primary bus number / device bus number */
+ build_append_int_noprefix(table, pci_dev_bus_num(dev), 1);
+ build_append_int_noprefix(table, 0, 1);
+ /*
+ * TODO: Figure out where to get the slot number from.
+ * The slot number capability is deprecated so it only really
+ * exists via the _DSM which is not easily available from here.
+ */
+ build_append_int_noprefix(table, 0, 2);
+ build_append_int_noprefix(table, 0, 1); /* reserved */
+
+ /* Serial number */
+ if (sn_cap_offset) {
+ uint32_t dw = pci_get_long(dev->config + sn_cap_offset + 4);
+
+ build_append_int_noprefix(table, dw, 4);
+ dw = pci_get_long(dev->config + sn_cap_offset + 8);
+ build_append_int_noprefix(table, dw, 4);
+ } else {
+ build_append_int_noprefix(table, 0, 8);
+ }
+
+ /* Bridge control status */
+ build_append_int_noprefix(table, 0, 4);
+
+ if (pcie_cap_offset) {
+ uint32_t *pcie_cap = (uint32_t *)(dev->config + pcie_cap_offset);
+ for (i = 0; i < 60 / sizeof(uint32_t); i++) {
+ build_append_int_noprefix(table, pcie_cap[i], sizeof(uint32_t));
+ }
+ } else { /* Odd if we don't have one of these! */
+ build_append_int_noprefix(table, 0, 60);
+ }
+
+ if (aer_cap_offset) {
+ uint32_t *aer_cap = (uint32_t *)(dev->config + aer_cap_offset);
+ for (i = 0; i < 96 / sizeof(uint32_t); i++) {
+ build_append_int_noprefix(table, aer_cap[i], sizeof(uint32_t));
+ }
+ } else {
+ build_append_int_noprefix(table, 0, 96);
+ }
+}
+
static int acpi_ghes_record_mem_error(uint64_t error_block_address,
uint64_t error_physical_addr)
{
@@ -231,6 +326,45 @@ static int acpi_ghes_record_mem_error(uint64_t error_block_address,
return 0;
}
+static int ghes_record_aer_error(PCIDevice *dev, uint64_t error_block_address)
+{
+ const uint8_t aer_section_id_le[] = {
+ 0x54, 0xE9, 0x95, 0xD9, 0xC1, 0xBB, 0x0F,
+ 0x43, 0xAD, 0x91, 0xB4, 0x4D, 0xCB, 0x3C,
+ 0x6F, 0x35 };
+ QemuUUID fru_id = { 0 };
+ GArray *block = g_array_new(false, true, 1);
+ uint32_t data_length;
+
+ /* Read the current length in bytes of the generic error data */
+ cpu_physical_memory_read(error_block_address + 8, &data_length, 4);
+
+ /* Add a new generic error data entry*/
+ data_length += ACPI_GHES_DATA_LENGTH;
+ data_length += ACPI_GHES_PCIE_CPER_LENGTH;
+
+ /*
+ * Check whether it will run out of the preallocated memory if adding a new
+ * generic error data entry
+ */
+ if ((data_length + ACPI_GHES_GESB_SIZE) > ACPI_GHES_MAX_RAW_DATA_LENGTH) {
+ error_report("Record CPER out of boundary!!!");
+ return false;
+ }
+
+ acpi_ghes_generic_error_status(block, ACPI_GEBS_UNCORRECTABLE, 0, 0,
+ data_length, ACPI_CPER_SEV_RECOVERABLE);
+ acpi_ghes_generic_error_data(block, aer_section_id_le,
+ ACPI_CPER_SEV_RECOVERABLE, 0, 0,
+ ACPI_GHES_PCIE_CPER_LENGTH, fru_id, 0);
+
+ build_append_aer_cper(dev, block);
+ cpu_physical_memory_write(error_block_address, block->data, block->len);
+ g_array_free(block, true);
+
+ return true;
+}
+
/*
* Build table for the hardware error fw_cfg blob.
* Initialize "etc/hardware_errors" and "etc/hardware_errors_addr" fw_cfg blobs.
@@ -392,23 +526,22 @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s,
ags->present = true;
}
+static uint64_t ghes_get_state_start_address(void)
+{
+ AcpiGedState *acpi_ged_state =
+ ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED, NULL));
+ AcpiGhesState *ags = &acpi_ged_state->ghes_state;
+
+ return le64_to_cpu(ags->ghes_addr_le);
+}
+
int acpi_ghes_record_errors(uint8_t source_id, uint64_t physical_address)
{
uint64_t error_block_addr, read_ack_register_addr, read_ack_register = 0;
- uint64_t start_addr;
+ uint64_t start_addr = ghes_get_state_start_address();
bool ret = -1;
- AcpiGedState *acpi_ged_state;
- AcpiGhesState *ags;
-
assert(source_id < ACPI_HEST_SRC_ID_RESERVED);
- acpi_ged_state = ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED,
- NULL));
- g_assert(acpi_ged_state);
- ags = &acpi_ged_state->ghes_state;
-
- start_addr = le64_to_cpu(ags->ghes_addr_le);
-
if (physical_address) {
if (source_id < ACPI_HEST_SRC_ID_RESERVED) {
@@ -448,6 +581,101 @@ int acpi_ghes_record_errors(uint8_t source_id, uint64_t physical_address)
return ret;
}
+/*
+ * Error register block data layout
+ *
+ * | +---------------------+ ges.ghes_addr_le
+ * | |error_block_address0 |
+ * | +---------------------+
+ * | |error_block_address1 |
+ * | +---------------------+ --+--
+ * | | ............. | GHES_ADDRESS_SIZE
+ * | +---------------------+ --+--
+ * | |error_block_addressN |
+ * | +---------------------+
+ * | | read_ack_register0 |
+ * | +---------------------+ --+--
+ * | | read_ack_register1 | GHES_ADDRESS_SIZE
+ * | +---------------------+ --+--
+ * | | ............. |
+ * | +---------------------+
+ * | | read_ack_registerN |
+ * | +---------------------+ --+--
+ * | | CPER | |
+ * | | .... | GHES_MAX_RAW_DATA_LENGT
+ * | | CPER | |
+ * | +---------------------+ --+--
+ * | | .......... |
+ * | +---------------------+
+ * | | CPER |
+ * | | .... |
+ * | | CPER |
+ * | +---------------------+
+ */
+
+/* Map from uint32_t notify to entry offset in GHES */
+static const uint8_t error_source_to_index[] = { 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 1, 0};
+
+static bool ghes_get_addr(uint32_t notify, uint64_t *error_block_addr,
+ uint64_t *read_ack_register_addr)
+{
+ uint64_t base;
+
+ if (notify >= ACPI_GHES_NOTIFY_RESERVED) {
+ return false;
+ }
+
+ /* Find and check the source id for this new CPER */
+ if (error_source_to_index[notify] == 0xff) {
+ return false;
+ }
+
+ base = ghes_get_state_start_address();
+
+ *read_ack_register_addr = base +
+ ACPI_GHES_ERROR_SOURCE_COUNT * sizeof(uint64_t) +
+ error_source_to_index[notify] * sizeof(uint64_t);
+
+ /* Could also be read back from the error_block_address register */
+ *error_block_addr = base +
+ ACPI_GHES_ERROR_SOURCE_COUNT * sizeof(uint64_t) +
+ ACPI_GHES_ERROR_SOURCE_COUNT * sizeof(uint64_t) +
+ error_source_to_index[notify] * ACPI_GHES_MAX_RAW_DATA_LENGTH;
+
+ return true;
+}
+
+bool ghes_record_aer_errors(PCIDevice *dev, uint32_t notify)
+{
+ int read_ack_register = 0;
+ uint64_t read_ack_register_addr = 0;
+ uint64_t error_block_addr = 0;
+
+ if (!ghes_get_addr(notify, &error_block_addr, &read_ack_register_addr)) {
+ return false;
+ }
+
+ cpu_physical_memory_read(read_ack_register_addr, &read_ack_register,
+ sizeof(uint64_t));
+ /* zero means OSPM does not acknowledge the error */
+ if (!read_ack_register) {
+ error_report("Last time OSPM does not acknowledge the error,"
+ " record CPER failed this time, set the ack value to"
+ " avoid blocking next time CPER record! exit");
+ read_ack_register = 1;
+ cpu_physical_memory_write(read_ack_register_addr, &read_ack_register,
+ sizeof(uint64_t));
+ return false;
+ }
+
+ read_ack_register = cpu_to_le64(0);
+ cpu_physical_memory_write(read_ack_register_addr, &read_ack_register,
+ sizeof(uint64_t));
+
+ return ghes_record_aer_error(dev, error_block_addr);
+}
+
bool acpi_ghes_present(void)
{
AcpiGedState *acpi_ged_state;
diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c
index 2c85a78fcd..414a9564c2 100644
--- a/hw/pci/pcie_aer.c
+++ b/hw/pci/pcie_aer.c
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "migration/vmstate.h"
+#include "hw/boards.h"
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pcie.h"
#include "hw/pci/msix.h"
@@ -27,6 +28,7 @@
#include "hw/pci/pci_bus.h"
#include "hw/pci/pcie_regs.h"
#include "pci-internal.h"
+#include "hw/acpi/ghes.h"
//#define DEBUG_PCIE
#ifdef DEBUG_PCIE
@@ -638,6 +640,8 @@ static bool pcie_aer_inject_uncor_error(PCIEAERInject *inj, bool is_fatal)
*/
int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err)
{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
uint8_t *aer_cap = NULL;
uint16_t devctl = 0;
uint16_t devsta = 0;
@@ -701,16 +705,27 @@ int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err)
}
/* send up error message */
- inj.msg.source_id = err->source_id;
- pcie_aer_msg(dev, &inj.msg);
-
- if (inj.log_overflow) {
- PCIEAERErr header_log_overflow = {
- .status = PCI_ERR_COR_HL_OVERFLOW,
- .flags = PCIE_AER_ERR_IS_CORRECTABLE,
- };
- int ret = pcie_aer_inject_error(dev, &header_log_overflow);
- assert(!ret);
+ if (!acpi_fw_first_pci()) {
+ inj.msg.source_id = err->source_id;
+ pcie_aer_msg(dev, &inj.msg);
+
+ if (inj.log_overflow) {
+ PCIEAERErr header_log_overflow = {
+ .status = PCI_ERR_COR_HL_OVERFLOW,
+ .flags = PCIE_AER_ERR_IS_CORRECTABLE,
+ };
+ int ret = pcie_aer_inject_error(dev, &header_log_overflow);
+ assert(!ret);
+ }
+ } else {
+ ghes_record_aer_errors(dev, ACPI_GHES_NOTIFY_GPIO);
+ if (mc->set_error) {
+ mc->set_error();
+ }
+ /* Simulation a firmware clearing status */
+ /* Bit hacky but we only injected one error so this should be fine */
+ pci_set_long(aer_cap + PCI_ERR_UNCOR_STATUS, 0);
+ pci_set_long(aer_cap + PCI_ERR_COR_STATUS, 0);
}
return 0;
}
--
2.39.2
^ permalink raw reply related [flat|nested] 12+ messages in thread* [RFC PATCH 08/11] hw/pci/aer: Default to error handling on.
2024-02-05 14:19 [RFC PATCH 00/11 qemu] arm/acpi/pci/cxl: ACPI based FW First error injection Jonathan Cameron
` (6 preceding siblings ...)
2024-02-05 14:19 ` [RFC PATCH 07/11] pci/aer: Support firmware first error injection via GHESv2 Jonathan Cameron
@ 2024-02-05 14:19 ` Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 09/11] cxl/ras: Set registers to sensible state for FW first ras Jonathan Cameron
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2024-02-05 14:19 UTC (permalink / raw)
To: linux-cxl, qemu-devel
Cc: Igor Mammedov, Ani Sinha, Shannon Zhao, Dongjiu Geng, linuxarm,
Michael S . Tsirkin, Ira Weiny, Peter Maydell, Fan Ni,
Marcel Apfelbaum
This should be dependent on the platform supporting FW first.
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
hw/pci/pcie.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 9f1ca718b5..4f04a1702a 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -304,6 +304,12 @@ 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);
+
+ /* HACK - FW first settings - how to do this cleanly? */
+ pci_long_test_and_set_mask(dev->config + 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->wmask + pos + PCI_EXP_DEVCTL,
PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
--
2.39.2
^ permalink raw reply related [flat|nested] 12+ messages in thread* [RFC PATCH 09/11] cxl/ras: Set registers to sensible state for FW first ras
2024-02-05 14:19 [RFC PATCH 00/11 qemu] arm/acpi/pci/cxl: ACPI based FW First error injection Jonathan Cameron
` (7 preceding siblings ...)
2024-02-05 14:19 ` [RFC PATCH 08/11] hw/pci/aer: Default to error handling on Jonathan Cameron
@ 2024-02-05 14:19 ` Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 10/11] cxl/type3: FW first protocol error injection Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 11/11] cxl/type3: Add firmware first error reporting for general media events Jonathan Cameron
10 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2024-02-05 14:19 UTC (permalink / raw)
To: linux-cxl, qemu-devel
Cc: Igor Mammedov, Ani Sinha, Shannon Zhao, Dongjiu Geng, linuxarm,
Michael S . Tsirkin, Ira Weiny, Peter Maydell, Fan Ni,
Marcel Apfelbaum
Even if we are doing native RAS, until the point where the OS
requests it via an _OSC the firmware may well be handling any
errors from CXL devices. As such configure them as if a firmware
has been doing so.
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
hw/cxl/cxl-component-utils.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c
index a0ff7d4396..e869c482a7 100644
--- a/hw/cxl/cxl-component-utils.c
+++ b/hw/cxl/cxl-component-utils.c
@@ -217,13 +217,13 @@ static void ras_init_common(uint32_t *reg_state, uint32_t *write_msk)
stl_le_p(reg_state + R_CXL_RAS_UNC_ERR_STATUS, 0);
stl_le_p(write_msk + R_CXL_RAS_UNC_ERR_STATUS, 0x1cfff);
/* Bits 12-13 and 17-31 reserved in CXL 2.0 */
- stl_le_p(reg_state + R_CXL_RAS_UNC_ERR_MASK, 0x1cfff);
+ stl_le_p(reg_state + R_CXL_RAS_UNC_ERR_MASK, 0/*0x1cfff*/);
stl_le_p(write_msk + R_CXL_RAS_UNC_ERR_MASK, 0x1cfff);
stl_le_p(reg_state + R_CXL_RAS_UNC_ERR_SEVERITY, 0x1cfff);
stl_le_p(write_msk + R_CXL_RAS_UNC_ERR_SEVERITY, 0x1cfff);
stl_le_p(reg_state + R_CXL_RAS_COR_ERR_STATUS, 0);
stl_le_p(write_msk + R_CXL_RAS_COR_ERR_STATUS, 0x7f);
- stl_le_p(reg_state + R_CXL_RAS_COR_ERR_MASK, 0x7f);
+ stl_le_p(reg_state + R_CXL_RAS_COR_ERR_MASK, 0/*0x7f*/);
stl_le_p(write_msk + R_CXL_RAS_COR_ERR_MASK, 0x7f);
/* CXL switches and devices must set */
stl_le_p(reg_state + R_CXL_RAS_ERR_CAP_CTRL, 0x200);
--
2.39.2
^ permalink raw reply related [flat|nested] 12+ messages in thread* [RFC PATCH 10/11] cxl/type3: FW first protocol error injection.
2024-02-05 14:19 [RFC PATCH 00/11 qemu] arm/acpi/pci/cxl: ACPI based FW First error injection Jonathan Cameron
` (8 preceding siblings ...)
2024-02-05 14:19 ` [RFC PATCH 09/11] cxl/ras: Set registers to sensible state for FW first ras Jonathan Cameron
@ 2024-02-05 14:19 ` Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 11/11] cxl/type3: Add firmware first error reporting for general media events Jonathan Cameron
10 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2024-02-05 14:19 UTC (permalink / raw)
To: linux-cxl, qemu-devel
Cc: Igor Mammedov, Ani Sinha, Shannon Zhao, Dongjiu Geng, linuxarm,
Michael S . Tsirkin, Ira Weiny, Peter Maydell, Fan Ni,
Marcel Apfelbaum
Note this is only hooked up to type 3 device so far.
Injection via the same interface as for native errors.
e.g.
{ "execute": "cxl-inject-uncorrectable-errors",
"arguments": {
"path": "/machine/peripheral/cxl-pmem2",
"errors": [
{
"type": "cache-address-parity",
"header": [ 3, 4]
}
]
}}
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
include/hw/acpi/ghes.h | 4 +
include/hw/cxl/cxl.h | 2 +
hw/acpi/ghes-stub.c | 5 +
hw/acpi/ghes.c | 212 ++++++++++++++++++++++++++++++++++
hw/mem/cxl_type3.c | 28 ++++-
hw/pci-bridge/cxl_root_port.c | 1 -
6 files changed, 248 insertions(+), 4 deletions(-)
diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h
index 437aeae7f6..3426697ecd 100644
--- a/include/hw/acpi/ghes.h
+++ b/include/hw/acpi/ghes.h
@@ -79,6 +79,10 @@ int acpi_ghes_record_errors(uint8_t notify, uint64_t error_physical_addr);
typedef struct PCIDevice PCIDevice;
bool ghes_record_aer_errors(PCIDevice *dev, uint32_t notify);
+typedef struct CXLError CXLError;
+bool ghes_record_cxl_errors(PCIDevice *dev, PCIEAERErr *err,
+ CXLError *cxl_err, uint32_t notify);
+
/**
* acpi_ghes_present: Report whether ACPI GHES table is present
*
diff --git a/include/hw/cxl/cxl.h b/include/hw/cxl/cxl.h
index 857fa61898..24b2166431 100644
--- a/include/hw/cxl/cxl.h
+++ b/include/hw/cxl/cxl.h
@@ -57,6 +57,8 @@ struct CXLHost {
bool passthrough;
};
+#define TYPE_CXL_ROOT_PORT "cxl-rp"
+
#define TYPE_PXB_CXL_HOST "pxb-cxl-host"
OBJECT_DECLARE_SIMPLE_TYPE(CXLHost, PXB_CXL_HOST)
diff --git a/hw/acpi/ghes-stub.c b/hw/acpi/ghes-stub.c
index bd208af4ec..cbc7d57465 100644
--- a/hw/acpi/ghes-stub.c
+++ b/hw/acpi/ghes-stub.c
@@ -19,6 +19,11 @@ int acpi_ghes_record_errors(uint8_t source_id, uint64_t physical_address)
{
return -1;
}
+bool ghes_record_cxl_errors(PCIDevice *dev, PCIEAERErr *err,
+ CXLError *cxl_err, uint32_t notify)
+{
+ return false;
+}
bool acpi_ghes_present(void)
{
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
index d0103c0a6a..c6e863d375 100644
--- a/hw/acpi/ghes.c
+++ b/hw/acpi/ghes.c
@@ -29,6 +29,8 @@
#include "hw/pci/pci.h"
#include "hw/pci/pci_device.h"
#include "qemu/uuid.h"
+#include "hw/cxl/cxl_device.h"
+#include "hw/cxl/cxl.h"
#define ACPI_GHES_ERRORS_FW_CFG_FILE "etc/hardware_errors"
#define ACPI_GHES_DATA_ADDR_FW_CFG_FILE "etc/hardware_errors_addr"
@@ -279,6 +281,140 @@ static void build_append_aer_cper(PCIDevice *dev, GArray *table)
}
}
+static void build_append_cxl_cper(PCIDevice *dev, CXLError *cxl_err,
+ GArray *table)
+{
+ PCIDeviceClass *pci_class = PCI_DEVICE_GET_CLASS(dev);
+ uint16_t sn_cap_offset = pcie_find_capability(dev, 0x3);
+ uint16_t pcie_cap_offset = pci_find_capability(dev, 0x10);
+ uint16_t cxl_dvsec_offset;
+ uint16_t cxl_dvsec_len = 0;
+ uint8_t type = 0xff;
+ int i;
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_CXL_TYPE3)) {
+ type = 2;
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CXL_USP)) {
+ type = 7;
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CXL_DSP)) {
+ type = 6;
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CXL_ROOT_PORT)) {
+ type = 5;
+ }
+
+ /* Only device or port dvsec should exist */
+ cxl_dvsec_offset = pcie_find_dvsec(dev, 0x1e98, 0);
+ if (cxl_dvsec_offset == 0) {
+ cxl_dvsec_offset = pcie_find_dvsec(dev, 0x1e98, 3);
+ }
+
+ if (cxl_dvsec_offset) {
+ cxl_dvsec_len = pci_get_long(dev->config + cxl_dvsec_offset + 4) >> 20;
+ }
+
+ /* CXL Protocol error record */
+ build_append_int_noprefix(table,
+ (type != 0xff ? 1UL << 0 : 0) |
+ (1UL << 1) | /* Agent address valid */
+ (1UL << 2) | /* Device ID */
+ ((sn_cap_offset ? 1UL : 0UL) << 3) |
+ (1UL << 4) | /* Capability structure */
+ ((cxl_dvsec_offset ? 1UL : 0UL) << 5) |
+ (1UL << 6), /* Error Log */
+ 8);
+ /* Agent Type */
+ build_append_int_noprefix(table, type, 1); /* CXL 2.0 device */
+
+ /* Reserved */
+ build_append_int_noprefix(table, 0, 7);
+ /* Agent Address */
+ build_append_int_noprefix(table, PCI_FUNC(dev->devfn), 1);
+ build_append_int_noprefix(table, PCI_SLOT(dev->devfn), 1);
+ build_append_int_noprefix(table, pci_dev_bus_num(dev), 1);
+ build_append_int_noprefix(table, 0 /* Seg */, 2);
+ /* Reserved */
+ build_append_int_noprefix(table, 0, 3);
+ /* Device id */
+ build_append_int_noprefix(table, pci_class->vendor_id, 2);
+ build_append_int_noprefix(table, pci_class->device_id, 2);
+ build_append_int_noprefix(table, pci_class->subsystem_vendor_id, 2);
+ build_append_int_noprefix(table, pci_class->subsystem_id, 2);
+ build_append_int_noprefix(table, pci_class->class_id, 2);
+ /*
+ * TODO: figure out how to get the slot number as the slot number
+ * capabiltiy is deprecated so it only really exists via _DSM
+ */
+ build_append_int_noprefix(table, 0, 2);
+ /* Reserved */
+ build_append_int_noprefix(table, 0, 4);
+
+ if (sn_cap_offset) {
+ uint32_t dw = pci_get_long(dev->config + sn_cap_offset + 4);
+
+ build_append_int_noprefix(table, dw, 4);
+ dw = pci_get_long(dev->config + sn_cap_offset + 8);
+ build_append_int_noprefix(table, dw, 4);
+ } else {
+ build_append_int_noprefix(table, 0, 8);
+ }
+
+ if (pcie_cap_offset) {
+ uint32_t *pcie_cap = (uint32_t *)(dev->config + pcie_cap_offset);
+ for (i = 0; i < 60 / sizeof(uint32_t); i++) {
+ build_append_int_noprefix(table, pcie_cap[i], sizeof(uint32_t));
+ }
+ } else { /* Odd if we don't have one of these! */
+ build_append_int_noprefix(table, 0, 60);
+ }
+
+ /* CXL DVSEC Length */
+ build_append_int_noprefix(table, cxl_dvsec_len, 2);
+
+ /* Error log length */
+ build_append_int_noprefix(table, 0x18, 2); /* No head log as I'm lazy */
+ /* Reserved */
+ build_append_int_noprefix(table, 0, 4);
+ /* DVSEC */
+ for (i = 0; i < cxl_dvsec_len; i += sizeof(uint32_t)) {
+ uint32_t dw = pci_get_long(dev->config + cxl_dvsec_offset + i);
+
+ build_append_int_noprefix(table, dw, sizeof(dw));
+ }
+
+ /* error log */
+ if (object_dynamic_cast(OBJECT(dev), TYPE_CXL_TYPE3)) {
+ CXLType3Dev *ct3d = CXL_TYPE3(dev);
+ uint32_t *rs = ct3d->cxl_cstate.crb.cache_mem_registers;
+
+ /*
+ * TODO: Possibly move this to caller to gather up - or work out
+ * generic way to get to it.
+ */
+ build_append_int_noprefix(table,
+ ldl_le_p(rs + R_CXL_RAS_UNC_ERR_STATUS), 4);
+ build_append_int_noprefix(table,
+ ldl_le_p(rs + R_CXL_RAS_UNC_ERR_MASK), 4);
+ build_append_int_noprefix(table,
+ ldl_le_p(rs + R_CXL_RAS_UNC_ERR_SEVERITY), 4);
+ build_append_int_noprefix(table,
+ ldl_le_p(rs + R_CXL_RAS_COR_ERR_STATUS), 4);
+ build_append_int_noprefix(table,
+ ldl_le_p(rs + R_CXL_RAS_COR_ERR_MASK), 4);
+ build_append_int_noprefix(table,
+ ldl_le_p(rs + R_CXL_RAS_ERR_CAP_CTRL), 4);
+ if (cxl_err) {
+ for (i = 0; i < CXL_RAS_ERR_HEADER_NUM; i++) {
+ build_append_int_noprefix(table, cxl_err->header[i], 4);
+ }
+ } else {
+ build_append_int_noprefix(table, 0, 4 * CXL_RAS_ERR_HEADER_NUM);
+ }
+ } else {
+ /* TODO: Add support for ports etc */
+ build_append_int_noprefix(table, 0, 0x18 + 512);
+ }
+}
+
static int acpi_ghes_record_mem_error(uint64_t error_block_address,
uint64_t error_physical_addr)
{
@@ -365,6 +501,52 @@ static int ghes_record_aer_error(PCIDevice *dev, uint64_t error_block_address)
return true;
}
+static int ghes_record_cxl_error(PCIDevice *dev, CXLError *cxl_err,
+ uint64_t error_block_address)
+{
+ GArray *block;
+ uint32_t data_length;
+ const uint8_t aer_section_id_le[] = {
+ 0xB4, 0xEF, 0xB9, 0x80,
+ 0xB5, 0x52,
+ 0xE3, 0x4D,
+ 0xA7, 0x77, 0x68, 0x78, 0x4B, 0x77, 0x10, 0x48 };
+ QemuUUID fru_id = {0};
+
+ block = g_array_new(false, true /* clear */, 1);
+ /* Read the current length in bytes of the generic error data */
+ cpu_physical_memory_read(error_block_address + 8,
+ &data_length, 4);
+
+ /* Add a new generic error data entry */
+ data_length += ACPI_GHES_DATA_LENGTH;
+ /* TO FIX: Error record dependent */
+ data_length += ACPI_GHES_PCIE_CPER_LENGTH;
+
+ /*
+ * Check whether it will run out of the preallocated memory if adding a new
+ * generic error data entry
+ */
+ if ((data_length + ACPI_GHES_GESB_SIZE) > ACPI_GHES_MAX_RAW_DATA_LENGTH) {
+ error_report("Record CPER out of boundary!!!");
+ return false;
+ }
+ /* Build the new generic error status block header */
+ acpi_ghes_generic_error_status(block, ACPI_GEBS_UNCORRECTABLE, 0, 0,
+ data_length, ACPI_CPER_SEV_RECOVERABLE);
+
+ /* Build the new generic error data entry header */
+ acpi_ghes_generic_error_data(block, aer_section_id_le,
+ ACPI_CPER_SEV_RECOVERABLE, 0, 0,
+ ACPI_GHES_PCIE_CPER_LENGTH, fru_id, 0);
+ /* Build the CXL CPER */
+ build_append_cxl_cper(dev, cxl_err, block);
+ /* Write back above whole new generic error data entry to guest memory */
+ cpu_physical_memory_write(error_block_address, block->data, block->len);
+ g_array_free(block, true);
+ return true;
+}
+
/*
* Build table for the hardware error fw_cfg blob.
* Initialize "etc/hardware_errors" and "etc/hardware_errors_addr" fw_cfg blobs.
@@ -676,6 +858,36 @@ bool ghes_record_aer_errors(PCIDevice *dev, uint32_t notify)
return ghes_record_aer_error(dev, error_block_addr);
}
+bool ghes_record_cxl_errors(PCIDevice *dev, PCIEAERErr *aer_err,
+ CXLError *cxl_err, uint32_t notify)
+{
+ int read_ack_register = 0;
+ uint64_t read_ack_register_addr = 0;
+ uint64_t error_block_addr = 0;
+
+ if (!ghes_get_addr(notify, &error_block_addr, &read_ack_register_addr)) {
+ return false;
+ }
+
+ cpu_physical_memory_read(read_ack_register_addr,
+ &read_ack_register, sizeof(uint64_t));
+ /* zero means OSPM does not acknowledge the error */
+ if (!read_ack_register) {
+ error_report("Last time OSPM does not acknowledge the error,"
+ " record CPER failed this time, set the ack value to"
+ " avoid blocking next time CPER record! exit");
+ read_ack_register = 1;
+ cpu_physical_memory_write(read_ack_register_addr,
+ &read_ack_register, sizeof(uint64_t));
+ return false;
+ }
+
+ read_ack_register = cpu_to_le64(0);
+ cpu_physical_memory_write(read_ack_register_addr,
+ &read_ack_register, sizeof(uint64_t));
+ return ghes_record_cxl_error(dev, cxl_err, error_block_addr);
+}
+
bool acpi_ghes_present(void)
{
AcpiGedState *acpi_ged_state;
diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
index db950d10dc..3a7881118a 100644
--- a/hw/mem/cxl_type3.c
+++ b/hw/mem/cxl_type3.c
@@ -29,6 +29,8 @@
#include "hw/cxl/cxl.h"
#include "hw/pci/msix.h"
#include "hw/pci/spdm.h"
+#include "hw/boards.h"
+#include "hw/acpi/ghes.h"
#define DWORD_BYTE 4
#define CXL_CAPACITY_MULTIPLIER (256 * MiB)
@@ -1520,6 +1522,8 @@ void qmp_cxl_inject_uncorrectable_errors(const char *path,
CXLUncorErrorRecordList *errors,
Error **errp)
{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
Object *obj = object_resolve_path(path, NULL);
static PCIEAERErr err = {};
CXLType3Dev *ct3d;
@@ -1605,7 +1609,16 @@ void qmp_cxl_inject_uncorrectable_errors(const char *path,
}
stl_le_p(reg_state + R_CXL_RAS_UNC_ERR_STATUS, unc_err);
- pcie_aer_inject_error(PCI_DEVICE(obj), &err);
+ if (!acpi_fw_first_pci()) {
+ pcie_aer_inject_error(PCI_DEVICE(obj), &err);
+ } else {
+ ghes_record_cxl_errors(PCI_DEVICE(obj), &err,
+ QTAILQ_FIRST(&ct3d->error_list),
+ ACPI_GHES_NOTIFY_GPIO);
+ if (mc->set_error) {
+ mc->set_error();
+ }
+ }
return;
}
@@ -1613,6 +1626,8 @@ void qmp_cxl_inject_uncorrectable_errors(const char *path,
void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type,
Error **errp)
{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
static PCIEAERErr err = {};
Object *obj = object_resolve_path(path, NULL);
CXLType3Dev *ct3d;
@@ -1650,8 +1665,15 @@ void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type,
cor_err |= (1 << cxl_err_type);
stl_le_p(reg_state + R_CXL_RAS_COR_ERR_STATUS, cor_err);
-
- pcie_aer_inject_error(PCI_DEVICE(obj), &err);
+ if (!acpi_fw_first_pci()) {
+ pcie_aer_inject_error(PCI_DEVICE(obj), &err);
+ } else {
+ ghes_record_cxl_errors(PCI_DEVICE(obj), &err, NULL,
+ ACPI_GHES_NOTIFY_GPIO);
+ if (mc->set_error) {
+ mc->set_error();
+ }
+ }
}
static void cxl_assign_event_header(CXLEventRecordHdr *hdr,
diff --git a/hw/pci-bridge/cxl_root_port.c b/hw/pci-bridge/cxl_root_port.c
index deee8cdb37..15d783913d 100644
--- a/hw/pci-bridge/cxl_root_port.c
+++ b/hw/pci-bridge/cxl_root_port.c
@@ -54,7 +54,6 @@ typedef struct CXLRootPort {
PCIResReserve res_reserve;
} CXLRootPort;
-#define TYPE_CXL_ROOT_PORT "cxl-rp"
DECLARE_INSTANCE_CHECKER(CXLRootPort, CXL_ROOT_PORT, TYPE_CXL_ROOT_PORT)
/*
--
2.39.2
^ permalink raw reply related [flat|nested] 12+ messages in thread* [RFC PATCH 11/11] cxl/type3: Add firmware first error reporting for general media events.
2024-02-05 14:19 [RFC PATCH 00/11 qemu] arm/acpi/pci/cxl: ACPI based FW First error injection Jonathan Cameron
` (9 preceding siblings ...)
2024-02-05 14:19 ` [RFC PATCH 10/11] cxl/type3: FW first protocol error injection Jonathan Cameron
@ 2024-02-05 14:19 ` Jonathan Cameron
10 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2024-02-05 14:19 UTC (permalink / raw)
To: linux-cxl, qemu-devel
Cc: Igor Mammedov, Ani Sinha, Shannon Zhao, Dongjiu Geng, linuxarm,
Michael S . Tsirkin, Ira Weiny, Peter Maydell, Fan Ni,
Marcel Apfelbaum
Initial code for Firmware First injection of general media events.
PoC level only - issue to be solved include:
* Mapping to CPER error types (recoverable etc).
* Some record details are tricky to establish so for now are not
provided.
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
include/hw/acpi/ghes.h | 3 ++
hw/acpi/ghes-stub.c | 6 +++
hw/acpi/ghes.c | 120 +++++++++++++++++++++++++++++++++++++++++
hw/mem/cxl_type3.c | 14 +++--
4 files changed, 140 insertions(+), 3 deletions(-)
diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h
index 3426697ecd..171c3e9dad 100644
--- a/include/hw/acpi/ghes.h
+++ b/include/hw/acpi/ghes.h
@@ -83,6 +83,9 @@ typedef struct CXLError CXLError;
bool ghes_record_cxl_errors(PCIDevice *dev, PCIEAERErr *err,
CXLError *cxl_err, uint32_t notify);
+typedef struct CXLEventGenMedia CXLEventGenMedia;
+bool ghes_record_cxl_event_gm(PCIDevice *dev,
+ CXLEventGenMedia *gem, uint32_t notify);
/**
* acpi_ghes_present: Report whether ACPI GHES table is present
*
diff --git a/hw/acpi/ghes-stub.c b/hw/acpi/ghes-stub.c
index cbc7d57465..34940c6441 100644
--- a/hw/acpi/ghes-stub.c
+++ b/hw/acpi/ghes-stub.c
@@ -25,6 +25,12 @@ bool ghes_record_cxl_errors(PCIDevice *dev, PCIEAERErr *err,
return false;
}
+bool ghes_record_cxl_event_gm(PCIDevice *dev, CXLEventGenMedia *gen,
+ uint32_t notify)
+{
+ return false;
+}
+
bool acpi_ghes_present(void)
{
return false;
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
index c6e863d375..34d8b8a518 100644
--- a/hw/acpi/ghes.c
+++ b/hw/acpi/ghes.c
@@ -281,6 +281,49 @@ static void build_append_aer_cper(PCIDevice *dev, GArray *table)
}
}
+static void build_append_cxl_event_cper(PCIDevice *dev, CXLEventGenMedia *gen,
+ GArray *table)
+{
+ PCIDeviceClass *pci_class = PCI_DEVICE_GET_CLASS(dev);
+ uint16_t sn_cap_offset = pcie_find_capability(dev, 0x3);
+ int i;
+
+ build_append_int_noprefix(table, 0x90, 4); /* Length */
+ build_append_int_noprefix(table,
+ (1UL << 0) | /* Device ID */
+ ((sn_cap_offset ? 1UL : 0UL) << 1) |
+ (1UL << 2), /* Event Log entry */
+ 8);
+ /* Device id - differnet syntax from protocol error - sigh */
+ build_append_int_noprefix(table, pci_class->vendor_id, 2);
+ build_append_int_noprefix(table, pci_class->device_id, 2);
+ build_append_int_noprefix(table, PCI_FUNC(dev->devfn), 1);
+ build_append_int_noprefix(table, PCI_SLOT(dev->devfn), 1);
+ build_append_int_noprefix(table, pci_dev_bus_num(dev), 1);
+ build_append_int_noprefix(table, 0 /* Seg */, 2);
+ /*
+ * TODO: figure out how to get the slot number as the slot number
+ * capabiltiy is deprecated so it only really exists via _DSM
+ */
+ build_append_int_noprefix(table, 0, 2);
+
+ /* Reserved */
+ build_append_int_noprefix(table, 0, 1);
+
+ if (sn_cap_offset) {
+ uint32_t dw = pci_get_long(dev->config + sn_cap_offset + 4);
+
+ build_append_int_noprefix(table, dw, 4);
+ dw = pci_get_long(dev->config + sn_cap_offset + 8);
+ build_append_int_noprefix(table, dw, 4);
+ } else {
+ build_append_int_noprefix(table, 0, 8);
+ }
+ for (i = offsetof(typeof(*gen), hdr.length); i < sizeof(*gen); i++) {
+ build_append_int_noprefix(table, ((uint8_t *)gen)[i], 1);
+ }
+}
+
static void build_append_cxl_cper(PCIDevice *dev, CXLError *cxl_err,
GArray *table)
{
@@ -501,6 +544,52 @@ static int ghes_record_aer_error(PCIDevice *dev, uint64_t error_block_address)
return true;
}
+static int ghes_record_cxl_gen_media(PCIDevice *dev, CXLEventGenMedia *gem,
+ uint64_t error_block_address)
+{
+ QemuUUID fru_id = {0};
+ GArray *block;
+ uint32_t data_length;
+ uint32_t event_length = 0x90;
+ const uint8_t section_id_le[] = { 0x77, 0x0a, 0xcd, 0xfb,
+ 0x60, 0xc2,
+ 0x7f, 0x41,
+ 0x85, 0xa9,
+ 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6 };
+ block = g_array_new(false, true, 1);
+ /* Read the current length in bytes of the generic error data */
+ cpu_physical_memory_read(error_block_address + 8, &data_length, 4);
+
+ /* Add a new generic error data entry*/
+ data_length += ACPI_GHES_DATA_LENGTH;
+ data_length += event_length;
+
+ /*
+ * Check whether it will run out of the preallocated memory if adding a new
+ * generic error data entry
+ */
+ if ((data_length + ACPI_GHES_GESB_SIZE) > ACPI_GHES_MAX_RAW_DATA_LENGTH) {
+ error_report("Record CPER out of boundary!!!");
+ return false;
+ }
+ /* Build the new generic error status block header */
+ acpi_ghes_generic_error_status(block, ACPI_GEBS_UNCORRECTABLE, 0, 0,
+ data_length, ACPI_CPER_SEV_RECOVERABLE);
+
+ /* Build the new generic error data entry header */
+ acpi_ghes_generic_error_data(block, section_id_le,
+ ACPI_CPER_SEV_RECOVERABLE, 0, 0,
+ 0x90, fru_id, 0);
+
+ /* Build the CXL CPER */
+ build_append_cxl_event_cper(dev, gem, block); /* 0x90 long */
+ /* Write back above whole new generic error data entry to guest memory */
+ cpu_physical_memory_write(error_block_address, block->data, block->len);
+ g_array_free(block, true);
+
+ return 0;
+}
+
static int ghes_record_cxl_error(PCIDevice *dev, CXLError *cxl_err,
uint64_t error_block_address)
{
@@ -858,6 +947,37 @@ bool ghes_record_aer_errors(PCIDevice *dev, uint32_t notify)
return ghes_record_aer_error(dev, error_block_addr);
}
+bool ghes_record_cxl_event_gm(PCIDevice *dev, CXLEventGenMedia *gem,
+ uint32_t notify)
+{
+ int read_ack_register = 0;
+ uint64_t read_ack_register_addr = 0;
+ uint64_t error_block_addr = 0;
+
+ if (!ghes_get_addr(notify, &error_block_addr, &read_ack_register_addr)) {
+ return false;
+ }
+
+ cpu_physical_memory_read(read_ack_register_addr,
+ &read_ack_register, sizeof(uint64_t));
+ /* zero means OSPM does not acknowledge the error */
+ if (!read_ack_register) {
+ error_report("Last time OSPM does not acknowledge the error,"
+ " record CPER failed this time, set the ack value to"
+ " avoid blocking next time CPER record! exit");
+ read_ack_register = 1;
+ cpu_physical_memory_write(read_ack_register_addr,
+ &read_ack_register, sizeof(uint64_t));
+ return false;
+ }
+
+ read_ack_register = cpu_to_le64(0);
+ cpu_physical_memory_write(read_ack_register_addr,
+ &read_ack_register, sizeof(uint64_t));
+
+ return ghes_record_cxl_gen_media(dev, gem, error_block_addr);
+}
+
bool ghes_record_cxl_errors(PCIDevice *dev, PCIEAERErr *aer_err,
CXLError *cxl_err, uint32_t notify)
{
diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
index 3a7881118a..1cc58293a2 100644
--- a/hw/mem/cxl_type3.c
+++ b/hw/mem/cxl_type3.c
@@ -1734,6 +1734,8 @@ void qmp_cxl_inject_general_media_event(const char *path, CxlEventLog log,
const char *component_id,
Error **errp)
{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
Object *obj = object_resolve_path(path, NULL);
CXLEventGenMedia gem;
CXLEventRecordHdr *hdr = &gem.hdr;
@@ -1792,9 +1794,15 @@ void qmp_cxl_inject_general_media_event(const char *path, CxlEventLog log,
}
stw_le_p(&gem.validity_flags, valid_flags);
-
- if (cxl_event_insert(cxlds, enc_log, (CXLEventRecordRaw *)&gem)) {
- cxl_event_irq_assert(ct3d);
+ if (!acpi_fw_first_pci()) {
+ if (cxl_event_insert(cxlds, enc_log, (CXLEventRecordRaw *)&gem)) {
+ cxl_event_irq_assert(ct3d);
+ }
+ } else {
+ ghes_record_cxl_event_gm(PCI_DEVICE(ct3d), &gem, ACPI_GHES_NOTIFY_GPIO);
+ if (mc->set_error) {
+ mc->set_error();
+ }
}
}
--
2.39.2
^ permalink raw reply related [flat|nested] 12+ messages in thread