Added needed bits at x86 architecture to allow both pb and q32 machines to support GHESv2 via HEST tables.
To do that, add needed _OSC capability bits to enable APEI support. Signed-off-by: Mauro Carvalho Chehab <[email protected]> --- hw/i386/Kconfig | 1 + hw/i386/acpi-build.c | 99 ++++++++++++++++++++++++++++++++++++++++++++ hw/i386/pc.c | 41 ++++++++++++++++++ include/hw/i386/pc.h | 5 +++ 4 files changed, 146 insertions(+) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index e27d8816e5bb..b1fbfaf33f9b 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -47,6 +47,7 @@ config PC select MC146818RTC # For ACPI builder: select SERIAL_ISA + select ACPI_APEI select ACPI_PCI select ACPI_VMGENID select ACPI_VMCLOCK diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 8837b69687d1..78ff78bd8200 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -71,6 +71,7 @@ #include "hw/acpi/utils.h" #include "hw/acpi/pci.h" #include "hw/acpi/cxl.h" +#include "hw/acpi/ghes.h" #include "qom/qom-qobject.h" #include "hw/i386/amd_iommu.h" @@ -852,6 +853,53 @@ static void build_acpi0017(Aml *table) aml_append(table, scope); } +/* ACPI 5.0, 6.2.10.2: Platform-Wide OSPM Capabilities. */ +#define ACPI_OSC_CAP_APEI (1U << 4) + +static Aml *build_apei_osc(uint32_t mask, uint32_t bits) +{ + Aml *method = aml_method("_OSC", 4, AML_SERIALIZED); + uint8_t failure[8] = { 2 }; /* _OSC failure */ + uint32_t disabled = mask & ~bits; + Aml *support = aml_name("CDW2"); + Aml *status = aml_name("CDW1"); + Aml *if_ctx; + + if_ctx = aml_if(aml_lless(aml_sizeof(aml_arg(3)), aml_int(8))); + aml_append(if_ctx, aml_return(aml_buffer(sizeof(failure), failure))); + aml_append(method, if_ctx); + aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1")); + aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2")); + + /* capacity buffer */ + if_ctx = aml_if(aml_lnot(aml_equal(aml_arg(0), + aml_touuid("0811B06E-4A27-44F9-8D60-3CBBC22E7B48")))); + aml_append(if_ctx, aml_or(status, aml_int(4), status)); + aml_append(if_ctx, aml_return(aml_arg(3))); + aml_append(method, if_ctx); + + /* Revision ID: 1 */ + if_ctx = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1)))); + aml_append(if_ctx, aml_or(status, aml_int(8), status)); + aml_append(if_ctx, aml_return(aml_arg(3))); + aml_append(method, if_ctx); + + /* Count of entries in arg 3: 2 */ + if_ctx = aml_if(aml_lnot(aml_equal(aml_arg(2), aml_int(2)))); + aml_append(if_ctx, aml_or(status, aml_int(2), status)); + aml_append(if_ctx, aml_return(aml_arg(3))); + aml_append(method, if_ctx); + + /* Update needed Platform-Wide _OSC Capabilities */ + aml_append(method, aml_and(support, aml_int(~disabled), aml_local(0))); + if_ctx = aml_if(aml_lnot(aml_equal(support, aml_local(0)))); + aml_append(if_ctx, aml_or(status, aml_int(0x10), status)); + aml_append(method, if_ctx); + aml_append(method, aml_store(aml_local(0), support)); + aml_append(method, aml_return(aml_arg(3))); + return method; +} + static void build_dsdt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm, AcpiMiscInfo *misc, @@ -1281,6 +1329,21 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, scope); } + if (pcms->ras) { + sb_scope = aml_scope("_SB"); + aml_append(sb_scope, build_apei_osc(ACPI_OSC_CAP_APEI, + ACPI_OSC_CAP_APEI)); + aml_append(sb_scope, aml_error_device()); + aml_append(dsdt, sb_scope); + + scope = aml_scope("_GPE"); + method = aml_method("_E07", 0, AML_NOTSERIALIZED); + aml_append(method, aml_notify(aml_name("\\_SB." ACPI_APEI_ERROR_DEVICE), + aml_int(0x80))); + aml_append(scope, method); + aml_append(dsdt, scope); + } + /* copy AML table into ACPI tables blob and patch header there */ g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); acpi_table_end(linker, &table); @@ -2011,6 +2074,10 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg) return true; } +static const AcpiNotificationSourceId hest_ghes_notify[] = { + {ACPI_HEST_SRC_ID_QMP, ACPI_GHES_NOTIFY_SCI}, +}; + static void acpi_build(AcpiBuildTables *tables, MachineState *machine) { @@ -2167,6 +2234,16 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) cxl_build_cedt(table_offsets, tables_blob, tables->linker, x86ms->oem_id, x86ms->oem_table_id, &pcms->cxl_devices_state); } + if (pcms->ras) { + AcpiGhesState *ags = + acpi_device_get_ghes_state(ACPI_DEVICE_IF(x86ms->acpi_dev)); + + acpi_add_table(table_offsets, tables_blob); + acpi_build_hest(ags, tables_blob, tables->hardware_errors, + tables->linker, hest_ghes_notify, + ARRAY_SIZE(hest_ghes_notify), + x86ms->oem_id, x86ms->oem_table_id); + } acpi_add_table(table_offsets, tables_blob); build_waet(tables_blob, tables->linker, x86ms->oem_id, x86ms->oem_table_id); @@ -2283,6 +2360,21 @@ void acpi_setup(void) static FwCfgTPMConfig tpm_config; #endif + if (pcms->ras) { + AcpiGhesState *ags = NULL; + + if (x86ms->acpi_dev) { + ags = acpi_device_get_ghes_state(ACPI_DEVICE_IF(x86ms->acpi_dev)); + } + if (!x86ms->fw_cfg || !pcms->acpi_build_enabled || + !x86_machine_is_acpi_enabled(x86ms) || !ags) { + error_report("ras=on requires ACPI table generation and a PC " + "ACPI controller"); + exit(EXIT_FAILURE); + } + ags->use_hest_addr = true; + } + if (!x86ms->fw_cfg) { ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); return; @@ -2329,6 +2421,13 @@ void acpi_setup(void) } #endif + if (pcms->ras) { + AcpiGhesState *ags = + acpi_device_get_ghes_state(ACPI_DEVICE_IF(x86ms->acpi_dev)); + + acpi_ghes_add_fw_cfg(ags, x86ms->fw_cfg, tables.hardware_errors); + } + vmgenid_dev = find_vmgenid_dev(); if (vmgenid_dev) { vmgenid_add_fw_cfg(VMGENID(vmgenid_dev), x86ms->fw_cfg, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f064aa2b3e32..5906152ed31e 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -50,6 +50,7 @@ #include "qemu/error-report.h" #include "hw/acpi/acpi.h" #include "hw/acpi/pc-hotplug.h" +#include "hw/acpi/ghes.h" #include "acpi-build.h" #include "hw/mem/nvdimm.h" #include "hw/cxl/cxl_host.h" @@ -1611,6 +1612,34 @@ static void pc_machine_set_max_fw_size(Object *obj, Visitor *v, pcms->max_fw_size = value; } +static bool pc_machine_get_ras(Object *obj, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + return pcms->ras; +} + +static void pc_machine_set_ras(Object *obj, bool value, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + pcms->ras = value; +} + +static void pc_sci_error(Notifier *n, void *opaque) +{ + PCMachineState *pcms = container_of(n, PCMachineState, ghes_sci_notifier); + X86MachineState *x86ms = X86_MACHINE(pcms); + uint16_t *source_id = opaque; + + /* Currently, only QMP injection is supported */ + if (!pcms->ras || !x86ms->acpi_dev || + *source_id != ACPI_HEST_SRC_ID_QMP) { + return; + } + + acpi_send_event(DEVICE(x86ms->acpi_dev), ACPI_GENERIC_ERROR); +} static void pc_machine_initfn(Object *obj) { @@ -1645,12 +1674,18 @@ static void pc_machine_initfn(Object *obj) if (pcmc->pci_enabled) { cxl_machine_init(obj, &pcms->cxl_devices_state); } + + pcms->ghes_sci_notifier.notify = pc_sci_error; + notifier_list_add(&acpi_generic_error_notifiers, + &pcms->ghes_sci_notifier); } static void pc_machine_finalize(Object *obj) { PCMachineState *pcms = PC_MACHINE(obj); + notifier_remove(&pcms->ghes_sci_notifier); + if (pcms->pcspk && !qdev_is_realized(DEVICE(pcms->pcspk))) { object_unref(OBJECT(pcms->pcspk)); } @@ -1769,6 +1804,12 @@ static void pc_machine_class_init(ObjectClass *oc, const void *data) object_class_property_set_description(oc, PC_MACHINE_SMBIOS_EP, "SMBIOS Entry Point type [32, 64]"); + object_class_property_add_bool(oc, "ras", pc_machine_get_ras, + pc_machine_set_ras); + object_class_property_set_description(oc, "ras", + "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, "fd-bootchk", pc_machine_get_fd_bootchk, pc_machine_set_fd_bootchk); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index d4b6d3ed57fa..52b4e18ab7db 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -30,6 +30,9 @@ typedef struct PCMachineState { /* State for other subsystems/APIs: */ Notifier machine_done; + /* Triggered when a new SCI GHES error raises */ + Notifier ghes_sci_notifier; + /* Pointers to devices and objects: */ PCIBus *pcibus; I2CBus *smbus; @@ -52,6 +55,8 @@ typedef struct PCMachineState { bool i8042_enabled; bool default_bus_bypass_iommu; bool fd_bootchk; + bool ras; + uint64_t max_fw_size; /* ACPI Memory hotplug IO base address */ -- 2.55.0
