Linux-CXL Archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 00/11 qemu] arm/acpi/pci/cxl: ACPI based FW First error injection.
@ 2024-02-05 14:19 Jonathan Cameron
  2024-02-05 14:19 ` [RFC PATCH 01/11] hw/pci: Add pcie_find_dvsec() utility Jonathan Cameron
                   ` (10 more replies)
  0 siblings, 11 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

I've had a version of this code for many years (and occasionally mention it
as test platform for kernel patches) and it keeps coming in handy, so time
to share the CXL version.

What is this?
- ACPI + UEFI specs define a means of notifying the OS of errors that
  firmware has handled (gathered up data etc, reset the relevant error tracking
  units etc) in a set of standard formats (UEFI spec appendix N).
- ARM virt already supports standard HEST ACPI table description of Synchronous
  External Abort (SEA) for memory errors. This series builds on this to
  add a GHESv2 / Generic Error Device / GPIO interrupt path for asynchronous
  error reporting.
- CXL and PCI AER both already have injection commands (via HMP / QMP)
  These are repurposed to perform FW first injection if the guest OS has not
  negotiated OS first handling (so before the CXL / PCIE _OSC is called or
  when it doesn't negotiate control of AER / CXL Memory Errors).
- The OS normally negotiates for control of error registers via _OSC.
  Previously QEMU unconditionally granted control of these registers.
  This series includes a machine parameter to allow the 'FW' to not let the
  OS take control and tracks whether the OS has asked for control or not.
  Note this code relies on the standard handshake - it's not remotely
  correct if the OS does follow that flow - this can be hardened with some
  more AML magic.

Alternatives:
- In theory we could emulate a management controller running appropriate firmware
  and have that actually handle the errors. It's much easier to instead intercept
  them before the error reporting messages are sent and result logged in the root
  ports error registers. As far as the guest is concerned it doesn't matter if
  these registers are handled via the firmware or never got written in the first
  place (the guest isn't allowed to touch these registers anyway!)
  This is sort of same argument for why we build ACPI tables in general in QEMU
  rather than making that an EDK2 problem.

Why?
- The kernel CXL code supports both firmware first and native RAS.
  As only some vendors have adopted a FW first model and hardware
  availability is limited this code has proven challenging to test.

Why an RFC?
- Small matter that the ARM CXL support isn't upstream.
- I'm assuming adding this support to QEMU will be controversial.
- There are some loose ends, TODOs and Fixme's in the code.
- Only one type of CXL event currently handled - should provide them all
  CXL Protocol and AER error reporting is more complete.
- I should probably figure out how to do this for x86 as apparently people
  also want to use that architecture ;)

Thanks to Shiju Jose for help testing this.

Based on: Random stack of patches on my gitlab.com/jic23/qemu cxl-2024-02-05-draft
branch. Specifically:
https://gitlab.com/jic23/qemu/-/commit/0fa064b9c8eeef468d8a19e87f39f230b4fa4da9

All comments welcome - particularly anyone who can advise on what the HEST
table should look like an x86 machine - too many options!

Jonathan Cameron (11):
  hw/pci: Add pcie_find_dvsec() utility.
  hw/acpi: Allow GPEX _OSC to keep fw first control of AER and CXL
    errors.
  arm/virt: Add fw-first-ras property.
  acpi/ghes: Support GPIO error source.
  arm/virt: Wire up GPIO error source for ACPI / GHES
  acpi: pci/cxl: Stash the OSC control parameters.
  pci/aer: Support firmware first error injection via GHESv2
  hw/pci/aer: Default to error handling on.
  cxl/ras: Set registers to sensible state for FW first ras
  cxl/type3: FW first protocol error injection.
  cxl/type3: Add firmware first error reporting for general media
    events.

 include/hw/acpi/cxl.h         |   2 +-
 include/hw/acpi/ghes.h        |  14 +
 include/hw/arm/virt.h         |   1 +
 include/hw/boards.h           |   1 +
 include/hw/cxl/cxl.h          |   2 +
 include/hw/pci-host/gpex.h    |   1 +
 include/hw/pci/pcie.h         |   1 +
 hw/acpi/cxl-stub.c            |   2 +-
 hw/acpi/cxl.c                 |  50 ++-
 hw/acpi/ghes-stub.c           |  25 ++
 hw/acpi/ghes.c                | 634 +++++++++++++++++++++++++++++++++-
 hw/arm/virt-acpi-build.c      |  71 +++-
 hw/arm/virt.c                 |  32 +-
 hw/cxl/cxl-component-utils.c  |   4 +-
 hw/i386/acpi-build.c          |   2 +-
 hw/mem/cxl_type3.c            |  42 ++-
 hw/pci-bridge/cxl_root_port.c |   1 -
 hw/pci-host/gpex-acpi.c       |  17 +-
 hw/pci-host/gpex.c            |   1 +
 hw/pci/pcie.c                 |  30 ++
 hw/pci/pcie_aer.c             |  35 +-
 21 files changed, 914 insertions(+), 54 deletions(-)

-- 
2.39.2


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

* [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

end of thread, other threads:[~2024-02-05 14:25 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [RFC PATCH 03/11] arm/virt: Add fw-first-ras property Jonathan Cameron
2024-02-05 14:19 ` [RFC PATCH 04/11] acpi/ghes: Support GPIO error source Jonathan Cameron
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 ` [RFC PATCH 06/11] acpi: pci/cxl: Stash the OSC control parameters Jonathan Cameron
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 ` [RFC PATCH 08/11] hw/pci/aer: Default to error handling on Jonathan Cameron
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 ` [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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).