xref: /openbmc/qemu/hw/acpi/ghes.c (revision 6b552b9b)
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