1 /* 2 * Support for generating APEI tables and recording CPER for Guests 3 * 4 * Copyright (c) 2020 HUAWEI TECHNOLOGIES CO., LTD. 5 * 6 * Author: Dongjiu Geng <gengdongjiu@huawei.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 18 * You should have received a copy of the GNU General Public License along 19 * with this program; if not, see <http://www.gnu.org/licenses/>. 20 */ 21 22 #include "qemu/osdep.h" 23 #include "qemu/units.h" 24 #include "hw/acpi/ghes.h" 25 #include "hw/acpi/aml-build.h" 26 #include "qemu/error-report.h" 27 #include "hw/acpi/generic_event_device.h" 28 #include "hw/nvram/fw_cfg.h" 29 30 #define ACPI_GHES_ERRORS_FW_CFG_FILE "etc/hardware_errors" 31 #define ACPI_GHES_DATA_ADDR_FW_CFG_FILE "etc/hardware_errors_addr" 32 33 /* The max size in bytes for one error block */ 34 #define ACPI_GHES_MAX_RAW_DATA_LENGTH (1 * KiB) 35 36 /* Now only support ARMv8 SEA notification type error source */ 37 #define ACPI_GHES_ERROR_SOURCE_COUNT 1 38 39 /* Generic Hardware Error Source version 2 */ 40 #define ACPI_GHES_SOURCE_GENERIC_ERROR_V2 10 41 42 /* Address offset in Generic Address Structure(GAS) */ 43 #define GAS_ADDR_OFFSET 4 44 45 /* 46 * Hardware Error Notification 47 * ACPI 4.0: 17.3.2.7 Hardware Error Notification 48 * Composes dummy Hardware Error Notification descriptor of specified type 49 */ 50 static void build_ghes_hw_error_notification(GArray *table, const uint8_t type) 51 { 52 /* Type */ 53 build_append_int_noprefix(table, type, 1); 54 /* 55 * Length: 56 * Total length of the structure in bytes 57 */ 58 build_append_int_noprefix(table, 28, 1); 59 /* Configuration Write Enable */ 60 build_append_int_noprefix(table, 0, 2); 61 /* Poll Interval */ 62 build_append_int_noprefix(table, 0, 4); 63 /* Vector */ 64 build_append_int_noprefix(table, 0, 4); 65 /* Switch To Polling Threshold Value */ 66 build_append_int_noprefix(table, 0, 4); 67 /* Switch To Polling Threshold Window */ 68 build_append_int_noprefix(table, 0, 4); 69 /* Error Threshold Value */ 70 build_append_int_noprefix(table, 0, 4); 71 /* Error Threshold Window */ 72 build_append_int_noprefix(table, 0, 4); 73 } 74 75 /* 76 * Build table for the hardware error fw_cfg blob. 77 * Initialize "etc/hardware_errors" and "etc/hardware_errors_addr" fw_cfg blobs. 78 * See docs/specs/acpi_hest_ghes.rst for blobs format. 79 */ 80 void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker) 81 { 82 int i, error_status_block_offset; 83 84 /* Build error_block_address */ 85 for (i = 0; i < ACPI_GHES_ERROR_SOURCE_COUNT; i++) { 86 build_append_int_noprefix(hardware_errors, 0, sizeof(uint64_t)); 87 } 88 89 /* Build read_ack_register */ 90 for (i = 0; i < ACPI_GHES_ERROR_SOURCE_COUNT; i++) { 91 /* 92 * Initialize the value of read_ack_register to 1, so GHES can be 93 * writeable after (re)boot. 94 * ACPI 6.2: 18.3.2.8 Generic Hardware Error Source version 2 95 * (GHESv2 - Type 10) 96 */ 97 build_append_int_noprefix(hardware_errors, 1, sizeof(uint64_t)); 98 } 99 100 /* Generic Error Status Block offset in the hardware error fw_cfg blob */ 101 error_status_block_offset = hardware_errors->len; 102 103 /* Reserve space for Error Status Data Block */ 104 acpi_data_push(hardware_errors, 105 ACPI_GHES_MAX_RAW_DATA_LENGTH * ACPI_GHES_ERROR_SOURCE_COUNT); 106 107 /* Tell guest firmware to place hardware_errors blob into RAM */ 108 bios_linker_loader_alloc(linker, ACPI_GHES_ERRORS_FW_CFG_FILE, 109 hardware_errors, sizeof(uint64_t), false); 110 111 for (i = 0; i < ACPI_GHES_ERROR_SOURCE_COUNT; i++) { 112 /* 113 * Tell firmware to patch error_block_address entries to point to 114 * corresponding "Generic Error Status Block" 115 */ 116 bios_linker_loader_add_pointer(linker, 117 ACPI_GHES_ERRORS_FW_CFG_FILE, sizeof(uint64_t) * i, 118 sizeof(uint64_t), ACPI_GHES_ERRORS_FW_CFG_FILE, 119 error_status_block_offset + i * ACPI_GHES_MAX_RAW_DATA_LENGTH); 120 } 121 122 /* 123 * tell firmware to write hardware_errors GPA into 124 * hardware_errors_addr fw_cfg, once the former has been initialized. 125 */ 126 bios_linker_loader_write_pointer(linker, ACPI_GHES_DATA_ADDR_FW_CFG_FILE, 127 0, sizeof(uint64_t), ACPI_GHES_ERRORS_FW_CFG_FILE, 0); 128 } 129 130 /* Build Generic Hardware Error Source version 2 (GHESv2) */ 131 static void build_ghes_v2(GArray *table_data, int source_id, BIOSLinker *linker) 132 { 133 uint64_t address_offset; 134 /* 135 * Type: 136 * Generic Hardware Error Source version 2(GHESv2 - Type 10) 137 */ 138 build_append_int_noprefix(table_data, ACPI_GHES_SOURCE_GENERIC_ERROR_V2, 2); 139 /* Source Id */ 140 build_append_int_noprefix(table_data, source_id, 2); 141 /* Related Source Id */ 142 build_append_int_noprefix(table_data, 0xffff, 2); 143 /* Flags */ 144 build_append_int_noprefix(table_data, 0, 1); 145 /* Enabled */ 146 build_append_int_noprefix(table_data, 1, 1); 147 148 /* Number of Records To Pre-allocate */ 149 build_append_int_noprefix(table_data, 1, 4); 150 /* Max Sections Per Record */ 151 build_append_int_noprefix(table_data, 1, 4); 152 /* Max Raw Data Length */ 153 build_append_int_noprefix(table_data, ACPI_GHES_MAX_RAW_DATA_LENGTH, 4); 154 155 address_offset = table_data->len; 156 /* Error Status Address */ 157 build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 0x40, 0, 158 4 /* QWord access */, 0); 159 bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, 160 address_offset + GAS_ADDR_OFFSET, sizeof(uint64_t), 161 ACPI_GHES_ERRORS_FW_CFG_FILE, source_id * sizeof(uint64_t)); 162 163 switch (source_id) { 164 case ACPI_HEST_SRC_ID_SEA: 165 /* 166 * Notification Structure 167 * Now only enable ARMv8 SEA notification type 168 */ 169 build_ghes_hw_error_notification(table_data, ACPI_GHES_NOTIFY_SEA); 170 break; 171 default: 172 error_report("Not support this error source"); 173 abort(); 174 } 175 176 /* Error Status Block Length */ 177 build_append_int_noprefix(table_data, ACPI_GHES_MAX_RAW_DATA_LENGTH, 4); 178 179 /* 180 * Read Ack Register 181 * ACPI 6.1: 18.3.2.8 Generic Hardware Error Source 182 * version 2 (GHESv2 - Type 10) 183 */ 184 address_offset = table_data->len; 185 build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 0x40, 0, 186 4 /* QWord access */, 0); 187 bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, 188 address_offset + GAS_ADDR_OFFSET, 189 sizeof(uint64_t), ACPI_GHES_ERRORS_FW_CFG_FILE, 190 (ACPI_GHES_ERROR_SOURCE_COUNT + source_id) * sizeof(uint64_t)); 191 192 /* 193 * Read Ack Preserve field 194 * We only provide the first bit in Read Ack Register to OSPM to write 195 * while the other bits are preserved. 196 */ 197 build_append_int_noprefix(table_data, ~0x1ULL, 8); 198 /* Read Ack Write */ 199 build_append_int_noprefix(table_data, 0x1, 8); 200 } 201 202 /* Build Hardware Error Source Table */ 203 void acpi_build_hest(GArray *table_data, BIOSLinker *linker) 204 { 205 uint64_t hest_start = table_data->len; 206 207 /* Hardware Error Source Table header*/ 208 acpi_data_push(table_data, sizeof(AcpiTableHeader)); 209 210 /* Error Source Count */ 211 build_append_int_noprefix(table_data, ACPI_GHES_ERROR_SOURCE_COUNT, 4); 212 213 build_ghes_v2(table_data, ACPI_HEST_SRC_ID_SEA, linker); 214 215 build_header(linker, table_data, (void *)(table_data->data + hest_start), 216 "HEST", table_data->len - hest_start, 1, NULL, NULL); 217 } 218 219 void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s, 220 GArray *hardware_error) 221 { 222 /* Create a read-only fw_cfg file for GHES */ 223 fw_cfg_add_file(s, ACPI_GHES_ERRORS_FW_CFG_FILE, hardware_error->data, 224 hardware_error->len); 225 226 /* Create a read-write fw_cfg file for Address */ 227 fw_cfg_add_file_callback(s, ACPI_GHES_DATA_ADDR_FW_CFG_FILE, NULL, NULL, 228 NULL, &(ags->ghes_addr_le), sizeof(ags->ghes_addr_le), false); 229 } 230