From patchwork Sun Apr 30 05:35:03 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dongjiu Geng X-Patchwork-Id: 9706015 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E8F6960245 for ; Sun, 30 Apr 2017 05:29:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DA72526E46 for ; Sun, 30 Apr 2017 05:29:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CEDCB27FA3; Sun, 30 Apr 2017 05:29:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id B6FF026E46 for ; Sun, 30 Apr 2017 05:29:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-ID:Date:Subject:To :From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=xayebmaEa+rQVLrxKzgfoP22AFIIXx83/qV/kBaLT7w=; b=a4sTlf1I4ELgtH +v/tqyitKRgAJRuEjfHRMYhajfNi1HQa90jHGnTxD1EVLtWq+98SP+nU8r20LWw06Nik/bT/Royig gevF5GcccrwY8EZ9jYxvtsBfXv/MbR/Z5bs5BuwLZAEZJ18lFZGVyDBmSAPCEXQorNoQwXvyl8nGc XZ9cPOxZf/71cnWKiz3haiqxSA7yStwsz8AnZA6Neyhv9jtdg+ZwJa3899RPRImy6wk595wz7pEpy qoZY90/x1xBiYdihHIex26mteWSwsVM98vvmNevd/uq6l778ALOjwlPHh6xzSk50rd8zpF2iVqxUI Egv0c66vzFgoikHrIzDQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1d4hQV-0000LP-4a; Sun, 30 Apr 2017 05:29:43 +0000 Received: from szxga01-in.huawei.com ([45.249.212.187] helo=dggrg01-dlp.huawei.com) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1d4hPp-0007vE-J7 for linux-arm-kernel@lists.infradead.org; Sun, 30 Apr 2017 05:29:05 +0000 Received: from 172.30.72.55 (EHLO DGGEML404-HUB.china.huawei.com) ([172.30.72.55]) by dggrg01-dlp.huawei.com (MOS 4.4.6-GA FastPath queued) with ESMTP id ANP74828; Sun, 30 Apr 2017 13:24:40 +0800 (CST) Received: from linux.huawei.com (10.67.187.203) by DGGEML404-HUB.china.huawei.com (10.3.17.39) with Microsoft SMTP Server id 14.3.301.0; Sun, 30 Apr 2017 13:24:26 +0800 From: Dongjiu Geng To: , , , , , , , , , , , , , , , , , , , Subject: [PATCH v3 1/4] ACPI: Add APEI GHES Table Generation support Date: Sun, 30 Apr 2017 13:35:03 +0800 Message-ID: <1493530506-26833-1-git-send-email-gengdongjiu@huawei.com> X-Mailer: git-send-email 1.7.7 MIME-Version: 1.0 X-Originating-IP: [10.67.187.203] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A020204.5905751C.0088, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0, ip=0.0.0.0, so=2014-11-16 11:51:01, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: 38da79738201de80ffb626dee5fcd528 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170429_222903_069106_A6CB28C5 X-CRM114-Status: GOOD ( 19.71 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: songwenjun@huawei.com, wuquanming@huawei.com, xiexiuqi@huawei.com, gengdongjiu@huawei.com, huangshaoyu@huawei.com, wangxiongfeng2@huawei.com Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP This implements APEI GHES Table by passing the error cper info to the guest via a fw_cfg_blob. After a CPER info is added, an SEA/SEI exception will be injected into the guest OS. Below is the table layout, the max number of error soure is 11, which is classified by notification type. etc/acpi/tables etc/hardware_errors ================ ========================================== +-----------+ +--------------+ | address | +-> +--------------+ | HEST + | registers | | | Error Status | + +------------+ | +---------+ | | Data Block 1 | | | GHES1 | --> | |address1 | --------+ | +------------+ | | GHES2 | --> | |address2 | ------+ | | CPER | | | GHES3 | --> | |address3 | ----+ | | | CPER | | | .... | --> | | ....... | | | | | CPER | | | GHES10 | --> | |address10| -+ | | | | CPER | +-+------------+ +-+---------+ | | | +-+------------+ | | | | | +---> +--------------+ | | | Error Status | | | | Data Block 2 | | | | +------------+ | | | | CPER | | | | | CPER | | | +-+------------+ | | | +-----> +--------------+ | | Error Status | | | Data Block 3 | | | +------------+ | | | CPER | | +-+------------+ | ........... +--------> +--------------+ | Error Status | | Data Block 10| | +------------+ | | CPER | | | CPER | | | CPER | +-+------------+ Signed-off-by: Dongjiu Geng --- default-configs/arm-softmmu.mak | 1 + hw/acpi/Makefile.objs | 1 + hw/acpi/aml-build.c | 2 + hw/acpi/hest_ghes.c | 203 +++++++++++++++++++++++++++++++++++ hw/arm/virt-acpi-build.c | 6 ++ include/hw/acpi/acpi-defs.h | 227 ++++++++++++++++++++++++++++++++++++++++ include/hw/acpi/aml-build.h | 1 + include/hw/acpi/hest_ghes.h | 43 ++++++++ 8 files changed, 484 insertions(+) create mode 100644 hw/acpi/hest_ghes.c create mode 100644 include/hw/acpi/hest_ghes.h diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 1e3bd2b..d5f1552 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -121,3 +121,4 @@ CONFIG_ACPI=y CONFIG_SMBIOS=y CONFIG_ASPEED_SOC=y CONFIG_GPIO_KEY=y +CONFIG_ACPI_APEI_GENERATION=y diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index 11c35bc..776b46e 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o +common-obj-$(CONFIG_ACPI_APEI_GENERATION) += hest_ghes.o common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o common-obj-y += acpi_interface.o diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index c6f2032..802b98d 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1560,6 +1560,7 @@ void acpi_build_tables_init(AcpiBuildTables *tables) tables->table_data = g_array_new(false, true /* clear */, 1); tables->tcpalog = g_array_new(false, true /* clear */, 1); tables->vmgenid = g_array_new(false, true /* clear */, 1); + tables->hardware_errors = g_array_new(false, true /* clear */, 1); tables->linker = bios_linker_loader_init(); } @@ -1570,6 +1571,7 @@ void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre) g_array_free(tables->table_data, true); g_array_free(tables->tcpalog, mfre); g_array_free(tables->vmgenid, mfre); + g_array_free(tables->hardware_errors, mfre); } /* Build rsdt table */ diff --git a/hw/acpi/hest_ghes.c b/hw/acpi/hest_ghes.c new file mode 100644 index 0000000..91d382e --- /dev/null +++ b/hw/acpi/hest_ghes.c @@ -0,0 +1,203 @@ +/* + * APEI GHES table Generation + * + * Copyright (C) 2017 huawei. + * + * Author: Dongjiu Geng + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qmp-commands.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/aml-build.h" +#include "hw/acpi/hest_ghes.h" +#include "hw/nvram/fw_cfg.h" +#include "sysemu/sysemu.h" + +static int ghes_generate_cper_record(uint64_t block_error_address, + uint64_t error_physical_addr) +{ + AcpiGenericErrorStatus block; + AcpiGenericErrorData *gdata; + struct cper_sec_mem_err *mem_err; + uint64_t block_data_length; + unsigned char *buffer; + + cpu_physical_memory_read(block_error_address, &block, + sizeof(AcpiGenericErrorStatus)); + + block_data_length = sizeof(AcpiGenericErrorStatus) + block.data_length; + + /* If the Generic Error Status Block is NULL, update + * the block header + */ + if (!block.block_status) { + block.block_status = ACPI_BERT_UNCORRECTABLE; + block.error_severity = CPER_SEV_FATAL; + } + + block.data_length += sizeof(AcpiGenericErrorData); + block.data_length += sizeof(struct cper_sec_mem_err); + + /* Write back the Generic Error Status Block to guest memory */ + cpu_physical_memory_write(block_error_address, &block, + sizeof(AcpiGenericErrorStatus)); + + /* Fill in Generic Error Data Entry */ + buffer = g_malloc(sizeof(AcpiGenericErrorData) + sizeof(cper_sec_mem_err)); + memset(buffer, 0, sizeof(AcpiGenericErrorData) + sizeof(cper_sec_mem_err)); + gdata = (AcpiGenericErrorData *)buffer; + + memcpy(gdata->section_type, (void *) &CPER_SEC_PLATFORM_MEM, + sizeof(uuid_le)); + gdata->error_data_length = sizeof(struct cper_sec_mem_err); + + mem_err = (struct cper_sec_mem_err *) (gdata + 1); + + /* In order to simplify simulation, hardcode the CPER section to memory + * section. + */ + mem_err->validation_bits |= CPER_MEM_VALID_ERROR_TYPE; + mem_err->error_type = 3; + + mem_err->validation_bits |= CPER_MEM_VALID_PA; + mem_err->physical_addr = error_physical_addr; + + mem_err->validation_bits |= CPER_MEM_VALID_CARD | CPER_MEM_VALID_MODULE | + CPER_MEM_VALID_BANK | CPER_MEM_VALID_ROW | + CPER_MEM_VALID_COLUMN | CPER_MEM_VALID_BIT_POSITION; + mem_err->card = 1; + mem_err->module = 2; + mem_err->bank = 3; + mem_err->row = 1; + mem_err->column = 2; + mem_err->bit_pos = 5; + + mem_err->validation_bits |= CPER_MEM_VALID_ERROR_STATUS; + mem_err->error_status = 4 << 8; + + /* Write back the Generic Error Data Entry to guest memory */ + cpu_physical_memory_write(block_error_address + block_data_length, buffer, + sizeof(AcpiGenericErrorData) + sizeof(cper_sec_mem_err)); + + g_free(buffer); + return BFAPEI_OK; +} + +void ghes_build_acpi(GArray *table_data, GArray *hardware_error, + BIOSLinker *linker) +{ + Aml *hest; + uint32_t address_registers_offset; + AcpiTableHeader *header; + AcpiGenericHardwareErrorSource *error_source; + int i; + + int block_reqr_size = sizeof(uint64_t) + MAX_RAW_DATA_LENGTH; + + /* New address register and Error status block table size*/ + g_array_set_size(hardware_error, MAX_ERROR_SOURCE_COUNT_V6 + * block_reqr_size); + + /* Put this in a HEST table */ + hest = init_aml_allocator(); + address_registers_offset = table_data->len + + sizeof(AcpiHardwareErrorSourceTable) + + ERROR_STATUS_ADDRESS_OFFSET + + GAS_ADDRESS_OFFSET; + /* Reserve space for HEST table size*/ + acpi_data_push(hest->buf, sizeof(AcpiHardwareErrorSourceTable) + + MAX_ERROR_SOURCE_COUNT_V6 + * sizeof(AcpiGenericHardwareErrorSource)); + + g_array_append_vals(table_data, hest->buf->data, hest->buf->len); + /* Allocate guest memory for the Data fw_cfg blob */ + bios_linker_loader_alloc(linker, GHES_ERRORS_FW_CFG_FILE, + hardware_error, 4096, + false /* page boundary, high memory */); + header = (AcpiTableHeader *)(table_data->data + + table_data->len - hest->buf->len); + *(uint32_t *)(header + 1) = MAX_ERROR_SOURCE_COUNT_V6; + error_source = (AcpiGenericHardwareErrorSource *)((char *)header + + sizeof(AcpiHardwareErrorSourceTable)); + + for (i = 0; i < MAX_ERROR_SOURCE_COUNT_V6; i++) { + error_source->type = ACPI_HEST_TYPE_GENERIC_ERROR; + error_source->source_id = 0; + error_source->related_source_id = 0xffff; + error_source->flags = 0; + error_source->enabled = 1; + error_source->number_of_records = 1; + error_source->max_sections_per_record = 1; + error_source->max_raw_data_length = MAX_RAW_DATA_LENGTH; + error_source->error_status_address.space_id = + ACPI_ADR_SPACE_SYSTEM_MEMORY; + error_source->error_status_address.bit_width = 64; + error_source->error_status_address.bit_offset = 0; + error_source->error_status_address.access_width = 4; + error_source->notify.type = i; + error_source->notify.length = sizeof(AcpiGenericHardwareErrorSource); + + bios_linker_loader_add_pointer(linker, GHES_ERRORS_FW_CFG_FILE, + sizeof(uint64_t) * i, sizeof(uint64_t), + GHES_ERRORS_FW_CFG_FILE, + MAX_ERROR_SOURCE_COUNT_V6 * sizeof(uint64_t) + + i * MAX_RAW_DATA_LENGTH); + bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, + address_registers_offset + + i * sizeof(AcpiGenericHardwareErrorSource), + sizeof(uint32_t), GHES_ERRORS_FW_CFG_FILE, + i * sizeof(uint64_t)); + + bios_linker_loader_write_pointer(linker, GHES_DATA_ADDR_FW_CFG_FILE, + i * sizeof(uint64_t), sizeof(uint64_t), + GHES_ERRORS_FW_CFG_FILE, + MAX_ERROR_SOURCE_COUNT_V6 * sizeof(uint64_t) + + i * MAX_RAW_DATA_LENGTH); + error_source++; + } + + build_header(linker, table_data, + (void *)header, "HEST", hest->buf->len, 1, NULL, "GHES"); + + free_aml_allocator(); +} + +static GhesErrorState ges; +void ghes_add_fw_cfg(FWCfgState *s, GArray *hardware_error) +{ + + int block_reqr_size = sizeof(uint64_t) + MAX_RAW_DATA_LENGTH; + int size = MAX_ERROR_SOURCE_COUNT_V6 * block_reqr_size; + + /* Create a read-only fw_cfg file for GHES */ + fw_cfg_add_file(s, GHES_ERRORS_FW_CFG_FILE, hardware_error->data, + size); + /* Create a read-write fw_cfg file for Address */ + fw_cfg_add_file_callback(s, GHES_DATA_ADDR_FW_CFG_FILE, NULL, NULL, + &(ges.ghes_addr_le[0]), + sizeof(uint64_t) * MAX_ERROR_SOURCE_COUNT_V6, + false); +} + +void ghes_update_guest(uint32_t notify, uint64_t physical_address) +{ + uint64_t block_error_addr; + + if (physical_address) { + ges.physical_addr = physical_address; + block_error_addr = ges.ghes_addr_le[notify]; + block_error_addr = le32_to_cpu(block_error_addr); + + /* A zero value in ghes_addr means that BIOS has not yet written + * the address + */ + if (block_error_addr) { + ghes_generate_cper_record(block_error_addr, physical_address); + } + } +} diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 0835e59..e7ab5dc 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -45,6 +45,8 @@ #include "hw/arm/virt.h" #include "sysemu/numa.h" #include "kvm_arm.h" +#include "hw/acpi/vmgenid.h" +#include "hw/acpi/hest_ghes.h" #define ARM_SPI_BASE 32 #define ACPI_POWER_BUTTON_DEVICE "PWRB" @@ -778,6 +780,9 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) acpi_add_table(table_offsets, tables_blob); build_spcr(tables_blob, tables->linker, vms); + acpi_add_table(table_offsets, tables_blob); + ghes_build_acpi(tables_blob, tables->hardware_errors, tables->linker); + if (nb_numa_nodes > 0) { acpi_add_table(table_offsets, tables_blob); build_srat(tables_blob, tables->linker, vms); @@ -892,6 +897,7 @@ void virt_acpi_setup(VirtMachineState *vms) build_state->rsdp_mr = acpi_add_rom_blob(build_state, tables.rsdp, ACPI_BUILD_RSDP_FILE, 0); + ghes_add_fw_cfg(vms->fw_cfg, tables.hardware_errors); qemu_register_reset(virt_acpi_build_reset, build_state); virt_acpi_build_reset(build_state); diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 4cc3630..27adede 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -295,6 +295,58 @@ typedef struct AcpiMultipleApicTable AcpiMultipleApicTable; #define ACPI_APIC_GENERIC_TRANSLATOR 15 #define ACPI_APIC_RESERVED 16 /* 16 and greater are reserved */ +#define CPER_MEM_VALID_ERROR_STATUS 0x0001 +#define CPER_MEM_VALID_PA 0x0002 +#define CPER_MEM_VALID_PA_MASK 0x0004 +#define CPER_MEM_VALID_NODE 0x0008 +#define CPER_MEM_VALID_CARD 0x0010 +#define CPER_MEM_VALID_MODULE 0x0020 +#define CPER_MEM_VALID_BANK 0x0040 +#define CPER_MEM_VALID_DEVICE 0x0080 +#define CPER_MEM_VALID_ROW 0x0100 +#define CPER_MEM_VALID_COLUMN 0x0200 +#define CPER_MEM_VALID_BIT_POSITION 0x0400 +#define CPER_MEM_VALID_REQUESTOR_ID 0x0800 +#define CPER_MEM_VALID_RESPONDER_ID 0x1000 +#define CPER_MEM_VALID_TARGET_ID 0x2000 +#define CPER_MEM_VALID_ERROR_TYPE 0x4000 +#define CPER_MEM_VALID_RANK_NUMBER 0x8000 +#define CPER_MEM_VALID_CARD_HANDLE 0x10000 +#define CPER_MEM_VALID_MODULE_HANDLE 0x20000 + +typedef struct { + uint8_t b[16]; +} uuid_le; + +#define UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ +((uuid_le) \ +{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, \ + (c) & 0xff, ((c) >> 8) & 0xff, \ + (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) } }) + +/* Platform Memory */ +#define CPER_SEC_PLATFORM_MEM \ + UUID_LE(0xA5BC1114, 0x6F64, 0x4EDE, 0xB8, 0x63, 0x3E, 0x83, \ + 0xED, 0x7C, 0x83, 0xB1) + +/* Values for Notify Type field above */ + +enum acpi_hest_notify_types { + ACPI_HEST_NOTIFY_POLLED = 0, + ACPI_HEST_NOTIFY_EXTERNAL = 1, + ACPI_HEST_NOTIFY_LOCAL = 2, + ACPI_HEST_NOTIFY_SCI = 3, + ACPI_HEST_NOTIFY_NMI = 4, + ACPI_HEST_NOTIFY_CMCI = 5, /* ACPI 5.0 */ + ACPI_HEST_NOTIFY_MCE = 6, /* ACPI 5.0 */ + ACPI_HEST_NOTIFY_GPIO = 7, /* ACPI 6.0 */ + ACPI_HEST_NOTIFY_SEA = 8, /* ACPI 6.1 */ + ACPI_HEST_NOTIFY_SEI = 9, /* ACPI 6.1 */ + ACPI_HEST_NOTIFY_GSIV = 10, /* ACPI 6.1 */ + ACPI_HEST_NOTIFY_RESERVED = 11 /* 11 and greater are reserved */ +}; + /* * MADT sub-structures (Follow MULTIPLE_APIC_DESCRIPTION_TABLE) */ @@ -475,6 +527,181 @@ struct AcpiSystemResourceAffinityTable } QEMU_PACKED; typedef struct AcpiSystemResourceAffinityTable AcpiSystemResourceAffinityTable; +#define ACPI_ADR_SPACE_SYSTEM_MEMORY (uint8_t) 0 +#define ACPI_ADR_SPACE_SYSTEM_IO (uint8_t) 1 +#define ACPI_ADR_SPACE_PCI_CONFIG (uint8_t) 2 +#define ACPI_ADR_SPACE_EC (uint8_t) 3 +#define ACPI_ADR_SPACE_SMBUS (uint8_t) 4 +#define ACPI_ADR_SPACE_CMOS (uint8_t) 5 +#define ACPI_ADR_SPACE_PCI_BAR_TARGET (uint8_t) 6 +#define ACPI_ADR_SPACE_IPMI (uint8_t) 7 +#define ACPI_ADR_SPACE_GPIO (uint8_t) 8 +#define ACPI_ADR_SPACE_GSBUS (uint8_t) 9 +#define ACPI_ADR_SPACE_PLATFORM_COMM (uint8_t) 10 + +/* GAS - Generic Address Structure */ +struct acpi_generic_address { + uint8_t space_id; /* Address space where + *struct or register exists + */ + uint8_t bit_width; /* Size in bits of given register */ + uint8_t bit_offset; /* Bit offset within the register */ + uint8_t access_width; /* Minimum Access size (ACPI 3.0) */ + uint64_t address; /* 64-bit address of struct or register */ +} __attribute__ ((packed)); + +/* Hardware Error Notification */ +struct acpi_hest_notify { + uint8_t type; + uint8_t length; + uint16_t config_write_enable; + uint32_t poll_interval; + uint32_t vector; + uint32_t polling_threshold_value; + uint32_t polling_threshold_window; + uint32_t error_threshold_value; + uint32_t error_threshold_window; +}; + +enum acpi_hest_types { + ACPI_HEST_TYPE_IA32_CHECK = 0, + ACPI_HEST_TYPE_IA32_CORRECTED_CHECK = 1, + ACPI_HEST_TYPE_IA32_NMI = 2, + ACPI_HEST_TYPE_NOT_USED3 = 3, + ACPI_HEST_TYPE_NOT_USED4 = 4, + ACPI_HEST_TYPE_NOT_USED5 = 5, + ACPI_HEST_TYPE_AER_ROOT_PORT = 6, + ACPI_HEST_TYPE_AER_ENDPOINT = 7, + ACPI_HEST_TYPE_AER_BRIDGE = 8, + ACPI_HEST_TYPE_GENERIC_ERROR = 9, + ACPI_HEST_TYPE_GENERIC_ERROR_V2 = 10, + ACPI_HEST_TYPE_RESERVED = 11 /* 11 and greater are reserved */ +}; + +/* Values for block_status flags above */ +#define ACPI_BERT_UNCORRECTABLE (1) +#define ACPI_BERT_CORRECTABLE (1 << 1) +#define ACPI_BERT_MULTIPLE_UNCORRECTABLE (1 << 2) +#define ACPI_BERT_MULTIPLE_CORRECTABLE (1 << 3) +/* 8 bits, error count */ +#define ACPI_BERT_ERROR_ENTRY_COUNT (0xFF << 4) + +/* Generic Hardware Error Source Structure */ +struct AcpiGenericHardwareErrorSource { + uint16_t type; + uint16_t source_id; + uint16_t related_source_id; + uint8_t flags; + uint8_t enabled; + uint32_t number_of_records; + uint32_t max_sections_per_record; + uint32_t max_raw_data_length; + struct acpi_generic_address error_status_address; + struct acpi_hest_notify notify; + uint32_t error_status_block_length; +} QEMU_PACKED; +typedef struct AcpiGenericHardwareErrorSource AcpiGenericHardwareErrorSource; + +/* Generic Hardware Error Source , version 2 */ +struct AcpiGenericHardwareErrorSourceV2 { + uint16_t type; + uint16_t source_id; + uint16_t related_source_id; + uint8_t flags; + uint8_t enabled; + uint32_t number_of_records; + uint32_t max_sections_per_record; + uint32_t max_raw_data_length; + struct acpi_generic_address error_status_address; + struct acpi_hest_notify notify; + uint32_t error_status_block_length; + struct acpi_generic_address read_ack_register; + uint64_t read_ack_preserve; + uint64_t read_ack_write; +} QEMU_PACKED; +typedef struct AcpiGenericHardwareErrorSourceV2 + AcpiGenericHardwareErrorSourceV2; + +/* Generic Error Status block */ + +struct AcpiGenericErrorStatus { + uint32_t block_status; + uint32_t raw_data_offset; + uint32_t raw_data_length; + uint32_t data_length; + uint32_t error_severity; +}; +typedef struct AcpiGenericErrorStatus AcpiGenericErrorStatus; + +/* Generic Error Data entry */ + +struct AcpiGenericErrorData { + uint8_t section_type[16]; + uint32_t error_severity; + uint16_t revision; + uint8_t validation_bits; + uint8_t flags; + uint32_t error_data_length; + uint8_t fru_id[16]; + uint8_t fru_text[20]; +}; +typedef struct AcpiGenericErrorData AcpiGenericErrorData; + +/* Extension for revision 0x0300 */ +struct AcpiGenericErrorDataV300 { + uint8_t section_type[16]; + uint32_t error_severity; + uint16_t revision; + uint8_t validation_bits; + uint8_t flags; + uint32_t error_data_length; + uint8_t fru_id[16]; + uint8_t fru_text[20]; + uint64_t time_stamp; +}; +typedef struct AcpiGenericErrorDataV300 AcpiGenericErrorDataV300; + +enum { + CPER_SEV_RECOVERABLE, + CPER_SEV_FATAL, + CPER_SEV_CORRECTED, + CPER_SEV_INFORMATIONAL, +}; + +/* Memory Error Section */ +struct cper_sec_mem_err { + uint64_t validation_bits; + uint64_t error_status; + uint64_t physical_addr; + uint64_t physical_addr_mask; + uint16_t node; + uint16_t card; + uint16_t module; + uint16_t bank; + uint16_t device; + uint16_t row; + uint16_t column; + uint16_t bit_pos; + uint64_t requestor_id; + uint64_t responder_id; + uint64_t target_id; + uint8_t error_type; + uint8_t reserved; + uint16_t rank; + uint16_t mem_array_handle; /* card handle in UEFI 2.4 */ + uint16_t mem_dev_handle; /* module handle in UEFI 2.4 */ +}; + typedef struct cper_sec_mem_err cper_sec_mem_err; + +/* + * HEST Description Table + */ +struct AcpiHardwareErrorSourceTable { + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + uint32_t error_source_count; +} QEMU_PACKED; +typedef struct AcpiHardwareErrorSourceTable AcpiHardwareErrorSourceTable; + #define ACPI_SRAT_PROCESSOR_APIC 0 #define ACPI_SRAT_MEMORY 1 #define ACPI_SRAT_PROCESSOR_x2APIC 2 diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 00c21f1..c1d15b3 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -211,6 +211,7 @@ struct AcpiBuildTables { GArray *rsdp; GArray *tcpalog; GArray *vmgenid; + GArray *hardware_errors; BIOSLinker *linker; } AcpiBuildTables; diff --git a/include/hw/acpi/hest_ghes.h b/include/hw/acpi/hest_ghes.h new file mode 100644 index 0000000..0cadc2b --- /dev/null +++ b/include/hw/acpi/hest_ghes.h @@ -0,0 +1,43 @@ +#ifndef ACPI_GHES_H +#define ACPI_GHES_H + +#include "hw/acpi/bios-linker-loader.h" + +#define GHES_ERRORS_FW_CFG_FILE "etc/hardware_errors" +#define GHES_DATA_ADDR_FW_CFG_FILE "etc/hardware_errors_addr" + +#define GAS_ADDRESS_OFFSET 4 +#define ERROR_STATUS_ADDRESS_OFFSET 20 +#define NOTIFICATION_STRUCTURE 32 + +#define BFAPEI_OK 0 +#define BFAPEI_FAIL 1 + +/* The max number of error source, the error sources + * are classified by notification type, below is the definition + * 0 - Polled + * 1 - External Interrupt + * 2 - Local Interrupt + * 3 - SCI + * 4 - NMI + * 5 - CMCI + * 6 - MCE + * 7 - GPIO-Signal + * 8 - ARMv8 SEA + * 9 - ARMv8 SEI + * 10 - External Interrupt - GSIV + */ +#define MAX_ERROR_SOURCE_COUNT_V6 11 +/* The max size in Bytes for one error block */ +#define MAX_RAW_DATA_LENGTH 0x1000 + +typedef struct GhesErrorState { + uint64_t physical_addr; + uint64_t ghes_addr_le[8]; +} GhesErrorState; + +void ghes_build_acpi(GArray *table_data, GArray *hardware_error, + BIOSLinker *linker); +void ghes_add_fw_cfg(FWCfgState *s, GArray *guid); +void ghes_update_guest(uint32_t notify, uint64_t physical_address); +#endif