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