New patch
1
Now that the ghes preparation patches were merged, let's add support
2
for error injection.
1
3
4
On this series, the first 6 patches chang to the math used to calculate offsets at HEST
5
table and hardware_error firmware file, together with its migration code. Migration tested
6
with both latest QEMU released kernel and upstream, on both directions.
7
8
The next patches add a new QAPI to allow injecting GHESv2 errors, and a script using such QAPI
9
to inject ARM Processor Error records.
10
11
---
12
v4:
13
- added an extra comment for AcpiGhesState structure;
14
- patches reordered;
15
- no functional changes, just code shift between the patches in this series.
16
17
v3:
18
- addressed more nits;
19
- hest_add_le now points to the beginning of HEST table;
20
- removed HEST from tests/data/acpi;
21
- added an extra patch to not use fw_cfg with virt-10.0 for hw_error_le
22
23
v2:
24
- address some nits;
25
- improved ags cleanup patch and removed ags.present field;
26
- added some missing le*_to_cpu() calls;
27
- update date at copyright for new files to 2024-2025;
28
- qmp command changed to: inject-ghes-v2-error ans since updated to 10.0;
29
- added HEST and DSDT tables after the changes to make check target happy.
30
(two patches: first one whitelisting such tables; second one removing from
31
whitelist and updating/adding such tables to tests/data/acpi)
32
33
34
35
Mauro Carvalho Chehab (14):
36
acpi/ghes: prepare to change the way HEST offsets are calculated
37
acpi/ghes: add a firmware file with HEST address
38
acpi/ghes: Use HEST table offsets when preparing GHES records
39
acpi/ghes: don't hard-code the number of sources for HEST table
40
acpi/ghes: add a notifier to notify when error data is ready
41
acpi/ghes: create an ancillary acpi_ghes_get_state() function
42
acpi/generic_event_device: Update GHES migration to cover hest addr
43
acpi/generic_event_device: add logic to detect if HEST addr is
44
available
45
acpi/generic_event_device: add an APEI error device
46
tests/acpi: virt: allow acpi table changes for a new table: HEST
47
arm/virt: Wire up a GED error device for ACPI / GHES
48
tests/acpi: virt: add a HEST table to aarch64 virt and update DSDT
49
qapi/acpi-hest: add an interface to do generic CPER error injection
50
scripts/ghes_inject: add a script to generate GHES error inject
51
52
MAINTAINERS | 10 +
53
hw/acpi/Kconfig | 5 +
54
hw/acpi/aml-build.c | 10 +
55
hw/acpi/generic_event_device.c | 43 ++
56
hw/acpi/ghes-stub.c | 7 +-
57
hw/acpi/ghes.c | 231 ++++--
58
hw/acpi/ghes_cper.c | 38 +
59
hw/acpi/ghes_cper_stub.c | 19 +
60
hw/acpi/meson.build | 2 +
61
hw/arm/virt-acpi-build.c | 37 +-
62
hw/arm/virt.c | 19 +-
63
hw/core/machine.c | 2 +
64
include/hw/acpi/acpi_dev_interface.h | 1 +
65
include/hw/acpi/aml-build.h | 2 +
66
include/hw/acpi/generic_event_device.h | 1 +
67
include/hw/acpi/ghes.h | 54 +-
68
include/hw/arm/virt.h | 2 +
69
qapi/acpi-hest.json | 35 +
70
qapi/meson.build | 1 +
71
qapi/qapi-schema.json | 1 +
72
scripts/arm_processor_error.py | 476 ++++++++++++
73
scripts/ghes_inject.py | 51 ++
74
scripts/qmp_helper.py | 702 ++++++++++++++++++
75
target/arm/kvm.c | 7 +-
76
tests/data/acpi/aarch64/virt/DSDT | Bin 5196 -> 5240 bytes
77
.../data/acpi/aarch64/virt/DSDT.acpihmatvirt | Bin 5282 -> 5326 bytes
78
tests/data/acpi/aarch64/virt/DSDT.memhp | Bin 6557 -> 6601 bytes
79
tests/data/acpi/aarch64/virt/DSDT.pxb | Bin 7679 -> 7723 bytes
80
tests/data/acpi/aarch64/virt/DSDT.topology | Bin 5398 -> 5442 bytes
81
29 files changed, 1677 insertions(+), 79 deletions(-)
82
create mode 100644 hw/acpi/ghes_cper.c
83
create mode 100644 hw/acpi/ghes_cper_stub.c
84
create mode 100644 qapi/acpi-hest.json
85
create mode 100644 scripts/arm_processor_error.py
86
create mode 100755 scripts/ghes_inject.py
87
create mode 100755 scripts/qmp_helper.py
88
89
--
90
2.48.1
diff view generated by jsdifflib
New patch
1
Add a new ags flag to change the way HEST offsets are calculated.
2
Currently, offsets needed to store ACPI HEST offsets and read ack
3
are calculated based on a previous knowledge from the logic
4
which creates the HEST table.
1
5
6
Such logic is not generic, not allowing to easily add more HEST
7
entries nor replicates what OSPM does.
8
9
As the next patches will be adding a more generic logic, add a
10
new use_hest_addr, set to false, in preparation for such changes.
11
12
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
13
---
14
hw/acpi/ghes.c | 46 ++++++++++++++++++++++++----------------
15
hw/arm/virt-acpi-build.c | 15 ++++++++++---
16
include/hw/acpi/ghes.h | 14 ++++++++++--
17
3 files changed, 52 insertions(+), 23 deletions(-)
18
19
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/hw/acpi/ghes.c
22
+++ b/hw/acpi/ghes.c
23
@@ -XXX,XX +XXX,XX @@ ghes_gen_err_data_uncorrectable_recoverable(GArray *block,
24
* Initialize "etc/hardware_errors" and "etc/hardware_errors_addr" fw_cfg blobs.
25
* See docs/specs/acpi_hest_ghes.rst for blobs format.
26
*/
27
-static void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker)
28
+static void build_ghes_error_table(AcpiGhesState *ags, GArray *hardware_errors,
29
+ BIOSLinker *linker)
30
{
31
int i, error_status_block_offset;
32
33
@@ -XXX,XX +XXX,XX @@ static void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker)
34
i * ACPI_GHES_MAX_RAW_DATA_LENGTH);
35
}
36
37
- /*
38
- * tell firmware to write hardware_errors GPA into
39
- * hardware_errors_addr fw_cfg, once the former has been initialized.
40
- */
41
- bios_linker_loader_write_pointer(linker, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, 0,
42
- sizeof(uint64_t),
43
- ACPI_HW_ERROR_FW_CFG_FILE, 0);
44
+ if (!ags->use_hest_addr) {
45
+ /*
46
+ * Tell firmware to write hardware_errors GPA into
47
+ * hardware_errors_addr fw_cfg, once the former has been initialized.
48
+ */
49
+ bios_linker_loader_write_pointer(linker, ACPI_HW_ERROR_ADDR_FW_CFG_FILE,
50
+ 0, sizeof(uint64_t),
51
+ ACPI_HW_ERROR_FW_CFG_FILE, 0);
52
+ }
53
}
54
55
/* Build Generic Hardware Error Source version 2 (GHESv2) */
56
@@ -XXX,XX +XXX,XX @@ static void build_ghes_v2(GArray *table_data,
57
}
58
59
/* Build Hardware Error Source Table */
60
-void acpi_build_hest(GArray *table_data, GArray *hardware_errors,
61
+void acpi_build_hest(AcpiGhesState *ags, GArray *table_data,
62
+ GArray *hardware_errors,
63
BIOSLinker *linker,
64
const char *oem_id, const char *oem_table_id)
65
{
66
AcpiTable table = { .sig = "HEST", .rev = 1,
67
.oem_id = oem_id, .oem_table_id = oem_table_id };
68
69
- build_ghes_error_table(hardware_errors, linker);
70
+ build_ghes_error_table(ags, hardware_errors, linker);
71
72
acpi_table_begin(&table, table_data);
73
74
@@ -XXX,XX +XXX,XX @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s,
75
fw_cfg_add_file(s, ACPI_HW_ERROR_FW_CFG_FILE, hardware_error->data,
76
hardware_error->len);
77
78
- /* Create a read-write fw_cfg file for Address */
79
- fw_cfg_add_file_callback(s, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, NULL, NULL,
80
- NULL, &(ags->hw_error_le), sizeof(ags->hw_error_le), false);
81
-
82
- ags->present = true;
83
+ if (!ags->use_hest_addr) {
84
+ /* Create a read-write fw_cfg file for Address */
85
+ fw_cfg_add_file_callback(s, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, NULL, NULL,
86
+ NULL, &(ags->hw_error_le), sizeof(ags->hw_error_le), false);
87
+ }
88
}
89
90
static void get_hw_error_offsets(uint64_t ghes_addr,
91
@@ -XXX,XX +XXX,XX @@ void ghes_record_cper_errors(const void *cper, size_t len,
92
ags = &acpi_ged_state->ghes_state;
93
94
assert(ACPI_GHES_ERROR_SOURCE_COUNT == 1);
95
- get_hw_error_offsets(le64_to_cpu(ags->hw_error_le),
96
- &cper_addr, &read_ack_register_addr);
97
+
98
+ if (!ags->use_hest_addr) {
99
+ get_hw_error_offsets(le64_to_cpu(ags->hw_error_le),
100
+ &cper_addr, &read_ack_register_addr);
101
+ }
102
103
if (!cper_addr) {
104
error_setg(errp, "can not find Generic Error Status Block");
105
@@ -XXX,XX +XXX,XX @@ bool acpi_ghes_present(void)
106
return false;
107
}
108
ags = &acpi_ged_state->ghes_state;
109
- return ags->present;
110
+ if (!ags->hw_error_le)
111
+ return false;
112
+
113
+ return true;
114
}
115
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
116
index XXXXXXX..XXXXXXX 100644
117
--- a/hw/arm/virt-acpi-build.c
118
+++ b/hw/arm/virt-acpi-build.c
119
@@ -XXX,XX +XXX,XX @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
120
build_dbg2(tables_blob, tables->linker, vms);
121
122
if (vms->ras) {
123
- acpi_add_table(table_offsets, tables_blob);
124
- acpi_build_hest(tables_blob, tables->hardware_errors, tables->linker,
125
- vms->oem_id, vms->oem_table_id);
126
+ AcpiGedState *acpi_ged_state;
127
+ AcpiGhesState *ags;
128
+
129
+ acpi_ged_state = ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED,
130
+ NULL));
131
+ if (acpi_ged_state) {
132
+ ags = &acpi_ged_state->ghes_state;
133
+
134
+ acpi_add_table(table_offsets, tables_blob);
135
+ acpi_build_hest(ags, tables_blob, tables->hardware_errors,
136
+ tables->linker, vms->oem_id, vms->oem_table_id);
137
+ }
138
}
139
140
if (ms->numa_state->num_nodes > 0) {
141
diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h
142
index XXXXXXX..XXXXXXX 100644
143
--- a/include/hw/acpi/ghes.h
144
+++ b/include/hw/acpi/ghes.h
145
@@ -XXX,XX +XXX,XX @@ enum {
146
ACPI_GHES_ERROR_SOURCE_COUNT
147
};
148
149
+/*
150
+ * AcpiGhesState stores an offset that will be used to fill HEST entries.
151
+ *
152
+ * When use_hest_addr is false, the stored offset is placed at hw_error_le,
153
+ * meaning an offset from the etc/hardware_errors firmware address. This
154
+ * is the default on QEMU 9.x.
155
+ *
156
+ * An offset value equal to zero means that GHES is not present.
157
+ */
158
typedef struct AcpiGhesState {
159
uint64_t hw_error_le;
160
- bool present; /* True if GHES is present at all on this board */
161
+ bool use_hest_addr; /* Currently, always false */
162
} AcpiGhesState;
163
164
-void acpi_build_hest(GArray *table_data, GArray *hardware_errors,
165
+void acpi_build_hest(AcpiGhesState *ags, GArray *table_data,
166
+ GArray *hardware_errors,
167
BIOSLinker *linker,
168
const char *oem_id, const char *oem_table_id);
169
void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s,
170
--
171
2.48.1
diff view generated by jsdifflib
New patch
1
Store HEST table address at GPA, placing its the start of the table at
2
hest_addr_le variable.
1
3
4
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
5
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
6
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
7
---
8
hw/acpi/ghes.c | 22 ++++++++++++++++++++--
9
include/hw/acpi/ghes.h | 7 ++++++-
10
2 files changed, 26 insertions(+), 3 deletions(-)
11
12
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
13
index XXXXXXX..XXXXXXX 100644
14
--- a/hw/acpi/ghes.c
15
+++ b/hw/acpi/ghes.c
16
@@ -XXX,XX +XXX,XX @@
17
18
#define ACPI_HW_ERROR_FW_CFG_FILE "etc/hardware_errors"
19
#define ACPI_HW_ERROR_ADDR_FW_CFG_FILE "etc/hardware_errors_addr"
20
+#define ACPI_HEST_ADDR_FW_CFG_FILE "etc/acpi_table_hest_addr"
21
22
/* The max size in bytes for one error block */
23
#define ACPI_GHES_MAX_RAW_DATA_LENGTH (1 * KiB)
24
@@ -XXX,XX +XXX,XX @@ void acpi_build_hest(AcpiGhesState *ags, GArray *table_data,
25
{
26
AcpiTable table = { .sig = "HEST", .rev = 1,
27
.oem_id = oem_id, .oem_table_id = oem_table_id };
28
+ uint32_t hest_offset;
29
+
30
+ hest_offset = table_data->len;
31
32
build_ghes_error_table(ags, hardware_errors, linker);
33
34
@@ -XXX,XX +XXX,XX @@ void acpi_build_hest(AcpiGhesState *ags, GArray *table_data,
35
ACPI_GHES_NOTIFY_SEA, ACPI_HEST_SRC_ID_SEA);
36
37
acpi_table_end(linker, &table);
38
+
39
+ if (ags->use_hest_addr) {
40
+ /*
41
+ * Tell firmware to write into GPA the address of HEST via fw_cfg,
42
+ * once initialized.
43
+ */
44
+ bios_linker_loader_write_pointer(linker,
45
+ ACPI_HEST_ADDR_FW_CFG_FILE, 0,
46
+ sizeof(uint64_t),
47
+ ACPI_BUILD_TABLE_FILE, hest_offset);
48
+ }
49
}
50
51
void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s,
52
@@ -XXX,XX +XXX,XX @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s,
53
fw_cfg_add_file(s, ACPI_HW_ERROR_FW_CFG_FILE, hardware_error->data,
54
hardware_error->len);
55
56
- if (!ags->use_hest_addr) {
57
+ if (ags->use_hest_addr) {
58
+ fw_cfg_add_file_callback(s, ACPI_HEST_ADDR_FW_CFG_FILE, NULL, NULL,
59
+ NULL, &(ags->hest_addr_le), sizeof(ags->hest_addr_le), false);
60
+ } else {
61
/* Create a read-write fw_cfg file for Address */
62
fw_cfg_add_file_callback(s, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, NULL, NULL,
63
NULL, &(ags->hw_error_le), sizeof(ags->hw_error_le), false);
64
@@ -XXX,XX +XXX,XX @@ bool acpi_ghes_present(void)
65
return false;
66
}
67
ags = &acpi_ged_state->ghes_state;
68
- if (!ags->hw_error_le)
69
+ if (!ags->hw_error_le && !ags->hest_addr_le)
70
return false;
71
72
return true;
73
diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h
74
index XXXXXXX..XXXXXXX 100644
75
--- a/include/hw/acpi/ghes.h
76
+++ b/include/hw/acpi/ghes.h
77
@@ -XXX,XX +XXX,XX @@ enum {
78
* meaning an offset from the etc/hardware_errors firmware address. This
79
* is the default on QEMU 9.x.
80
*
81
- * An offset value equal to zero means that GHES is not present.
82
+ * When use_hest_addr is true, the stored offset is placed at hest_addr_le,
83
+ * meaning an offset from theHEST table address from etc/acpi/tables firmware.
84
+ * This is the default for QEMU 10.x and above.
85
+ *
86
+ * If both offset values are equal to zero, it means that GHES is not present.
87
*/
88
typedef struct AcpiGhesState {
89
+ uint64_t hest_addr_le;
90
uint64_t hw_error_le;
91
bool use_hest_addr; /* Currently, always false */
92
} AcpiGhesState;
93
--
94
2.48.1
diff view generated by jsdifflib
New patch
1
There are two pointers that are needed during error injection:
1
2
3
1. The start address of the CPER block to be stored;
4
2. The address of the ack.
5
6
It is preferable to calculate them from the HEST table. This allows
7
checking the source ID, the size of the table and the type of the
8
HEST error block structures.
9
10
Yet, keep the old code, as this is needed for migration purposes.
11
12
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
13
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
14
---
15
hw/acpi/ghes.c | 100 +++++++++++++++++++++++++++++++++++++++++
16
include/hw/acpi/ghes.h | 2 +-
17
2 files changed, 101 insertions(+), 1 deletion(-)
18
19
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/hw/acpi/ghes.c
22
+++ b/hw/acpi/ghes.c
23
@@ -XXX,XX +XXX,XX @@
24
/* Address offset in Generic Address Structure(GAS) */
25
#define GAS_ADDR_OFFSET 4
26
27
+/*
28
+ * ACPI spec 1.0b
29
+ * 5.2.3 System Description Table Header
30
+ */
31
+#define ACPI_DESC_HEADER_OFFSET 36
32
+
33
/*
34
* The total size of Generic Error Data Entry
35
* ACPI 6.1/6.2: 18.3.2.7.1 Generic Error Data,
36
@@ -XXX,XX +XXX,XX @@
37
*/
38
#define ACPI_GHES_GESB_SIZE 20
39
40
+/*
41
+ * See the memory layout map at docs/specs/acpi_hest_ghes.rst.
42
+ */
43
+
44
+/*
45
+ * ACPI 6.1: 18.3.2.8 Generic Hardware Error Source version 2
46
+ * Table 18-344 Generic Hardware Error Source version 2 (GHESv2) Structure
47
+ */
48
+#define HEST_GHES_V2_ENTRY_SIZE 92
49
+
50
+/*
51
+ * ACPI 6.1: 18.3.2.7: Generic Hardware Error Source
52
+ * Table 18-344 Generic Hardware Error Source version 2 (GHESv2) Structure
53
+ * Read Ack Register
54
+ */
55
+#define GHES_READ_ACK_ADDR_OFF 64
56
+
57
+/*
58
+ * ACPI 6.1: 18.3.2.7: Generic Hardware Error Source
59
+ * Table 18-341 Generic Hardware Error Source Structure
60
+ * Error Status Address
61
+ */
62
+#define GHES_ERR_STATUS_ADDR_OFF 20
63
+
64
/*
65
* Values for error_severity field
66
*/
67
@@ -XXX,XX +XXX,XX @@ static void get_hw_error_offsets(uint64_t ghes_addr,
68
*read_ack_register_addr = ghes_addr + sizeof(uint64_t);
69
}
70
71
+static void get_ghes_source_offsets(uint16_t source_id,
72
+ uint64_t hest_addr,
73
+ uint64_t *cper_addr,
74
+ uint64_t *read_ack_start_addr,
75
+ Error **errp)
76
+{
77
+ uint64_t hest_err_block_addr, hest_read_ack_addr;
78
+ uint64_t err_source_entry, error_block_addr;
79
+ uint32_t num_sources, i;
80
+
81
+ hest_addr += ACPI_DESC_HEADER_OFFSET;
82
+
83
+ cpu_physical_memory_read(hest_addr, &num_sources,
84
+ sizeof(num_sources));
85
+ num_sources = le32_to_cpu(num_sources);
86
+
87
+ err_source_entry = hest_addr + sizeof(num_sources);
88
+
89
+ /*
90
+ * Currently, HEST Error source navigates only for GHESv2 tables
91
+ */
92
+ for (i = 0; i < num_sources; i++) {
93
+ uint64_t addr = err_source_entry;
94
+ uint16_t type, src_id;
95
+
96
+ cpu_physical_memory_read(addr, &type, sizeof(type));
97
+ type = le16_to_cpu(type);
98
+
99
+ /* For now, we only know the size of GHESv2 table */
100
+ if (type != ACPI_GHES_SOURCE_GENERIC_ERROR_V2) {
101
+ error_setg(errp, "HEST: type %d not supported.", type);
102
+ return;
103
+ }
104
+
105
+ /* Compare CPER source address at the GHESv2 structure */
106
+ addr += sizeof(type);
107
+ cpu_physical_memory_read(addr, &src_id, sizeof(src_id));
108
+ if (le16_to_cpu(src_id) == source_id) {
109
+ break;
110
+ }
111
+
112
+ err_source_entry += HEST_GHES_V2_ENTRY_SIZE;
113
+ }
114
+ if (i == num_sources) {
115
+ error_setg(errp, "HEST: Source %d not found.", source_id);
116
+ return;
117
+ }
118
+
119
+ /* Navigate though table address pointers */
120
+ hest_err_block_addr = err_source_entry + GHES_ERR_STATUS_ADDR_OFF +
121
+ GAS_ADDR_OFFSET;
122
+
123
+ cpu_physical_memory_read(hest_err_block_addr, &error_block_addr,
124
+ sizeof(error_block_addr));
125
+ error_block_addr = le64_to_cpu(error_block_addr);
126
+
127
+ cpu_physical_memory_read(error_block_addr, cper_addr,
128
+ sizeof(*cper_addr));
129
+ *cper_addr = le64_to_cpu(*cper_addr);
130
+
131
+ hest_read_ack_addr = err_source_entry + GHES_READ_ACK_ADDR_OFF +
132
+ GAS_ADDR_OFFSET;
133
+ cpu_physical_memory_read(hest_read_ack_addr, read_ack_start_addr,
134
+ sizeof(*read_ack_start_addr));
135
+ *read_ack_start_addr = le64_to_cpu(*read_ack_start_addr);
136
+}
137
+
138
void ghes_record_cper_errors(const void *cper, size_t len,
139
uint16_t source_id, Error **errp)
140
{
141
@@ -XXX,XX +XXX,XX @@ void ghes_record_cper_errors(const void *cper, size_t len,
142
if (!ags->use_hest_addr) {
143
get_hw_error_offsets(le64_to_cpu(ags->hw_error_le),
144
&cper_addr, &read_ack_register_addr);
145
+ } else {
146
+ get_ghes_source_offsets(source_id, le64_to_cpu(ags->hest_addr_le),
147
+ &cper_addr, &read_ack_register_addr, errp);
148
}
149
150
if (!cper_addr) {
151
diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h
152
index XXXXXXX..XXXXXXX 100644
153
--- a/include/hw/acpi/ghes.h
154
+++ b/include/hw/acpi/ghes.h
155
@@ -XXX,XX +XXX,XX @@ enum {
156
typedef struct AcpiGhesState {
157
uint64_t hest_addr_le;
158
uint64_t hw_error_le;
159
- bool use_hest_addr; /* Currently, always false */
160
+ bool use_hest_addr; /* True if HEST address is present */
161
} AcpiGhesState;
162
163
void acpi_build_hest(AcpiGhesState *ags, GArray *table_data,
164
--
165
2.48.1
diff view generated by jsdifflib
New patch
1
The current code is actually dependent on having just one error
2
structure with a single source, as any change there would cause
3
migration issues.
1
4
5
As the number of sources should be arch-dependent, as it will depend on
6
what kind of notifications will exist, and how many errors can be
7
reported at the same time, change the logic to be more flexible,
8
allowing the number of sources to be defined when building the
9
HEST table by the caller.
10
11
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
12
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
13
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
14
---
15
hw/acpi/ghes.c | 38 +++++++++++++++++++++-----------------
16
hw/arm/virt-acpi-build.c | 8 +++++++-
17
include/hw/acpi/ghes.h | 17 ++++++++++++-----
18
3 files changed, 40 insertions(+), 23 deletions(-)
19
20
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
21
index XXXXXXX..XXXXXXX 100644
22
--- a/hw/acpi/ghes.c
23
+++ b/hw/acpi/ghes.c
24
@@ -XXX,XX +XXX,XX @@ ghes_gen_err_data_uncorrectable_recoverable(GArray *block,
25
* See docs/specs/acpi_hest_ghes.rst for blobs format.
26
*/
27
static void build_ghes_error_table(AcpiGhesState *ags, GArray *hardware_errors,
28
- BIOSLinker *linker)
29
+ BIOSLinker *linker, int num_sources)
30
{
31
int i, error_status_block_offset;
32
33
/* Build error_block_address */
34
- for (i = 0; i < ACPI_GHES_ERROR_SOURCE_COUNT; i++) {
35
+ for (i = 0; i < num_sources; i++) {
36
build_append_int_noprefix(hardware_errors, 0, sizeof(uint64_t));
37
}
38
39
/* Build read_ack_register */
40
- for (i = 0; i < ACPI_GHES_ERROR_SOURCE_COUNT; i++) {
41
+ for (i = 0; i < num_sources; i++) {
42
/*
43
* Initialize the value of read_ack_register to 1, so GHES can be
44
* writable after (re)boot.
45
@@ -XXX,XX +XXX,XX @@ static void build_ghes_error_table(AcpiGhesState *ags, GArray *hardware_errors,
46
47
/* Reserve space for Error Status Data Block */
48
acpi_data_push(hardware_errors,
49
- ACPI_GHES_MAX_RAW_DATA_LENGTH * ACPI_GHES_ERROR_SOURCE_COUNT);
50
+ ACPI_GHES_MAX_RAW_DATA_LENGTH * num_sources);
51
52
/* Tell guest firmware to place hardware_errors blob into RAM */
53
bios_linker_loader_alloc(linker, ACPI_HW_ERROR_FW_CFG_FILE,
54
hardware_errors, sizeof(uint64_t), false);
55
56
- for (i = 0; i < ACPI_GHES_ERROR_SOURCE_COUNT; i++) {
57
+ for (i = 0; i < num_sources; i++) {
58
/*
59
* Tell firmware to patch error_block_address entries to point to
60
* corresponding "Generic Error Status Block"
61
@@ -XXX,XX +XXX,XX @@ static void build_ghes_error_table(AcpiGhesState *ags, GArray *hardware_errors,
62
}
63
64
/* Build Generic Hardware Error Source version 2 (GHESv2) */
65
-static void build_ghes_v2(GArray *table_data,
66
- BIOSLinker *linker,
67
- enum AcpiGhesNotifyType notify,
68
- uint16_t source_id)
69
+static void build_ghes_v2_entry(GArray *table_data,
70
+ BIOSLinker *linker,
71
+ const AcpiNotificationSourceId *notif_src,
72
+ uint16_t index, int num_sources)
73
{
74
uint64_t address_offset;
75
+ const uint16_t notify = notif_src->notify;
76
+ const uint16_t source_id = notif_src->source_id;
77
78
/*
79
* Type:
80
@@ -XXX,XX +XXX,XX @@ static void build_ghes_v2(GArray *table_data,
81
address_offset + GAS_ADDR_OFFSET,
82
sizeof(uint64_t),
83
ACPI_HW_ERROR_FW_CFG_FILE,
84
- source_id * sizeof(uint64_t));
85
+ index * sizeof(uint64_t));
86
87
/* Notification Structure */
88
build_ghes_hw_error_notification(table_data, notify);
89
@@ -XXX,XX +XXX,XX @@ static void build_ghes_v2(GArray *table_data,
90
address_offset + GAS_ADDR_OFFSET,
91
sizeof(uint64_t),
92
ACPI_HW_ERROR_FW_CFG_FILE,
93
- (ACPI_GHES_ERROR_SOURCE_COUNT + source_id)
94
- * sizeof(uint64_t));
95
+ (num_sources + index) * sizeof(uint64_t));
96
97
/*
98
* Read Ack Preserve field
99
@@ -XXX,XX +XXX,XX @@ static void build_ghes_v2(GArray *table_data,
100
void acpi_build_hest(AcpiGhesState *ags, GArray *table_data,
101
GArray *hardware_errors,
102
BIOSLinker *linker,
103
+ const AcpiNotificationSourceId *notif_source,
104
+ int num_sources,
105
const char *oem_id, const char *oem_table_id)
106
{
107
AcpiTable table = { .sig = "HEST", .rev = 1,
108
.oem_id = oem_id, .oem_table_id = oem_table_id };
109
uint32_t hest_offset;
110
+ int i;
111
112
hest_offset = table_data->len;
113
114
- build_ghes_error_table(ags, hardware_errors, linker);
115
+ build_ghes_error_table(ags, hardware_errors, linker, num_sources);
116
117
acpi_table_begin(&table, table_data);
118
119
/* Error Source Count */
120
- build_append_int_noprefix(table_data, ACPI_GHES_ERROR_SOURCE_COUNT, 4);
121
- build_ghes_v2(table_data, linker,
122
- ACPI_GHES_NOTIFY_SEA, ACPI_HEST_SRC_ID_SEA);
123
+ build_append_int_noprefix(table_data, num_sources, 4);
124
+ for (i = 0; i < num_sources; i++) {
125
+ build_ghes_v2_entry(table_data, linker, &notif_source[i], i, num_sources);
126
+ }
127
128
acpi_table_end(linker, &table);
129
130
@@ -XXX,XX +XXX,XX @@ void ghes_record_cper_errors(const void *cper, size_t len,
131
}
132
ags = &acpi_ged_state->ghes_state;
133
134
- assert(ACPI_GHES_ERROR_SOURCE_COUNT == 1);
135
136
if (!ags->use_hest_addr) {
137
get_hw_error_offsets(le64_to_cpu(ags->hw_error_le),
138
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
139
index XXXXXXX..XXXXXXX 100644
140
--- a/hw/arm/virt-acpi-build.c
141
+++ b/hw/arm/virt-acpi-build.c
142
@@ -XXX,XX +XXX,XX @@ static void acpi_align_size(GArray *blob, unsigned align)
143
g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
144
}
145
146
+static const AcpiNotificationSourceId hest_ghes_notify[] = {
147
+ { ACPI_HEST_SRC_ID_SYNC, ACPI_GHES_NOTIFY_SEA },
148
+};
149
+
150
static
151
void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
152
{
153
@@ -XXX,XX +XXX,XX @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
154
155
acpi_add_table(table_offsets, tables_blob);
156
acpi_build_hest(ags, tables_blob, tables->hardware_errors,
157
- tables->linker, vms->oem_id, vms->oem_table_id);
158
+ tables->linker, hest_ghes_notify,
159
+ ARRAY_SIZE(hest_ghes_notify),
160
+ vms->oem_id, vms->oem_table_id);
161
}
162
}
163
164
diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h
165
index XXXXXXX..XXXXXXX 100644
166
--- a/include/hw/acpi/ghes.h
167
+++ b/include/hw/acpi/ghes.h
168
@@ -XXX,XX +XXX,XX @@ enum AcpiGhesNotifyType {
169
ACPI_GHES_NOTIFY_RESERVED = 12
170
};
171
172
-enum {
173
- ACPI_HEST_SRC_ID_SEA = 0,
174
- /* future ids go here */
175
-
176
- ACPI_GHES_ERROR_SOURCE_COUNT
177
+/*
178
+ * ID numbers used to fill HEST source ID field
179
+ */
180
+enum AcpiGhesSourceID {
181
+ ACPI_HEST_SRC_ID_SYNC,
182
};
183
184
+typedef struct AcpiNotificationSourceId {
185
+ enum AcpiGhesSourceID source_id;
186
+ enum AcpiGhesNotifyType notify;
187
+} AcpiNotificationSourceId;
188
+
189
/*
190
* AcpiGhesState stores an offset that will be used to fill HEST entries.
191
*
192
@@ -XXX,XX +XXX,XX @@ typedef struct AcpiGhesState {
193
void acpi_build_hest(AcpiGhesState *ags, GArray *table_data,
194
GArray *hardware_errors,
195
BIOSLinker *linker,
196
+ const AcpiNotificationSourceId * const notif_source,
197
+ int num_sources,
198
const char *oem_id, const char *oem_table_id);
199
void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s,
200
GArray *hardware_errors);
201
--
202
2.48.1
diff view generated by jsdifflib
New patch
1
Some error injection notify methods are async, like GPIO
2
notify. Add a notifier to be used when the error record is
3
ready to be sent to the guest OS.
1
4
5
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
6
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
7
---
8
hw/acpi/ghes.c | 5 ++++-
9
include/hw/acpi/ghes.h | 3 +++
10
2 files changed, 7 insertions(+), 1 deletion(-)
11
12
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
13
index XXXXXXX..XXXXXXX 100644
14
--- a/hw/acpi/ghes.c
15
+++ b/hw/acpi/ghes.c
16
@@ -XXX,XX +XXX,XX @@ static void get_ghes_source_offsets(uint16_t source_id,
17
*read_ack_start_addr = le64_to_cpu(*read_ack_start_addr);
18
}
19
20
+NotifierList acpi_generic_error_notifiers =
21
+ NOTIFIER_LIST_INITIALIZER(error_device_notifiers);
22
+
23
void ghes_record_cper_errors(const void *cper, size_t len,
24
uint16_t source_id, Error **errp)
25
{
26
@@ -XXX,XX +XXX,XX @@ void ghes_record_cper_errors(const void *cper, size_t len,
27
/* Write the generic error data entry into guest memory */
28
cpu_physical_memory_write(cper_addr, cper, len);
29
30
- return;
31
+ notifier_list_notify(&acpi_generic_error_notifiers, NULL);
32
}
33
34
int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address)
35
diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h
36
index XXXXXXX..XXXXXXX 100644
37
--- a/include/hw/acpi/ghes.h
38
+++ b/include/hw/acpi/ghes.h
39
@@ -XXX,XX +XXX,XX @@
40
41
#include "hw/acpi/bios-linker-loader.h"
42
#include "qapi/error.h"
43
+#include "qemu/notify.h"
44
+
45
+extern NotifierList acpi_generic_error_notifiers;
46
47
/*
48
* Values for Hardware Error Notification Type field
49
--
50
2.48.1
diff view generated by jsdifflib
1
Now that the ghes preparation patches were merged, let's add support
1
Instead of having a function to check if ACPI is enabled
2
for error injection.
2
(acpi_ghes_present), change its logic to be more generic,
3
returing a pointed to AcpiGhesState.
3
4
4
On this series, the first 6 patches chang to the math used to calculate offsets at HEST
5
Such change allows cleanup the ghes GED state code, avoiding
5
table and hardware_error firmware file, together with its migration code. Migration tested
6
to read it multiple times, and simplifying the code.
6
with both latest QEMU released kernel and upstream, on both directions.
7
7
8
The next patches add a new QAPI to allow injecting GHESv2 errors, and a script using such QAPI
8
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
9
to inject ARM Processor Error records.
9
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
10
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
11
---
12
hw/acpi/ghes-stub.c | 7 ++++---
13
hw/acpi/ghes.c | 38 ++++++++++----------------------------
14
include/hw/acpi/ghes.h | 14 ++++++++------
15
target/arm/kvm.c | 7 +++++--
16
4 files changed, 27 insertions(+), 39 deletions(-)
10
17
11
If I'm counting well, this is the 19th submission of my error inject patches.
12
13
---
14
15
v2:
16
- address some nits;
17
- improved ags cleanup patch and removed ags.present field;
18
- added some missing le*_to_cpu() calls;
19
- update date at copyright for new files to 2024-2025;
20
- qmp command changed to: inject-ghes-v2-error ans since updated to 10.0;
21
- added HEST and DSDT tables after the changes to make check target happy.
22
(two patches: first one whitelisting such tables; second one removing from
23
whitelist and updating/adding such tables to tests/data/acpi)
24
25
I'm enclosing a diff from v1 at the end, as it may help check the differences.
26
27
Mauro Carvalho Chehab (13):
28
acpi/ghes: Prepare to support multiple sources on ghes
29
tests/acpi: virt: allow acpi table changes for a new table: HEST
30
acpi/ghes: add a firmware file with HEST address
31
acpi/ghes: Use HEST table offsets when preparing GHES records
32
acpi/generic_event_device: Update GHES migration to cover hest addr
33
acpi/generic_event_device: add logic to detect if HEST addr is
34
available
35
acpi/ghes: add a notifier to notify when error data is ready
36
acpi/ghes: Cleanup the code which gets ghes ged state
37
acpi/generic_event_device: add an APEI error device
38
arm/virt: Wire up a GED error device for ACPI / GHES
39
qapi/acpi-hest: add an interface to do generic CPER error injection
40
tests/acpi: virt: add a HEST table to aarch64 virt and update DSDT
41
scripts/ghes_inject: add a script to generate GHES error inject
42
43
MAINTAINERS | 10 +
44
hw/acpi/Kconfig | 5 +
45
hw/acpi/aml-build.c | 10 +
46
hw/acpi/generic_event_device.c | 44 ++
47
hw/acpi/ghes-stub.c | 7 +-
48
hw/acpi/ghes.c | 204 +++--
49
hw/acpi/ghes_cper.c | 38 +
50
hw/acpi/ghes_cper_stub.c | 19 +
51
hw/acpi/meson.build | 2 +
52
hw/arm/virt-acpi-build.c | 35 +-
53
hw/arm/virt.c | 19 +-
54
hw/core/machine.c | 2 +
55
include/hw/acpi/acpi_dev_interface.h | 1 +
56
include/hw/acpi/aml-build.h | 2 +
57
include/hw/acpi/generic_event_device.h | 1 +
58
include/hw/acpi/ghes.h | 45 +-
59
include/hw/arm/virt.h | 2 +
60
qapi/acpi-hest.json | 35 +
61
qapi/meson.build | 1 +
62
qapi/qapi-schema.json | 1 +
63
scripts/arm_processor_error.py | 377 ++++++++++
64
scripts/ghes_inject.py | 51 ++
65
scripts/qmp_helper.py | 702 ++++++++++++++++++
66
target/arm/kvm.c | 8 +-
67
tests/data/acpi/aarch64/virt/DSDT | Bin 5196 -> 5240 bytes
68
.../data/acpi/aarch64/virt/DSDT.acpihmatvirt | Bin 5282 -> 5326 bytes
69
tests/data/acpi/aarch64/virt/DSDT.memhp | Bin 6557 -> 6601 bytes
70
tests/data/acpi/aarch64/virt/DSDT.pxb | Bin 7679 -> 7723 bytes
71
tests/data/acpi/aarch64/virt/DSDT.topology | Bin 5398 -> 5442 bytes
72
tests/data/acpi/aarch64/virt/HEST | Bin 0 -> 224 bytes
73
30 files changed, 1550 insertions(+), 71 deletions(-)
74
create mode 100644 hw/acpi/ghes_cper.c
75
create mode 100644 hw/acpi/ghes_cper_stub.c
76
create mode 100644 qapi/acpi-hest.json
77
create mode 100644 scripts/arm_processor_error.py
78
create mode 100755 scripts/ghes_inject.py
79
create mode 100644 scripts/qmp_helper.py
80
create mode 100644 tests/data/acpi/aarch64/virt/HEST
81
82
----
83
84
Diff against previous version:
85
Instead of placing a v2 here, it is probably more useful to add the diffs
86
against the past version:
87
88
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
89
index XXXXXXX..XXXXXXX 100644
90
--- a/hw/acpi/generic_event_device.c
91
+++ b/hw/acpi/generic_event_device.c
92
@@ -XXX,XX +XXX,XX @@ static const uint32_t ged_supported_events[] = {
93
ACPI_GED_ERROR_EVT,
94
};
95
96
+/*
97
+ * ACPI 5.0b: 5.6.6 Device Object Notifications
98
+ * Table 5-135 Error Device Notification Values
99
+ */
100
+#define ERROR_DEVICE_NOTIFICATION 0x80
101
+
102
/*
103
* The ACPI Generic Event Device (GED) is a hardware-reduced specific
104
* device[ACPI v6.1 Section 5.6.9] that handles all platform events,
105
@@ -XXX,XX +XXX,XX @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
106
case ACPI_GED_ERROR_EVT:
107
aml_append(if_ctx,
108
aml_notify(aml_name(ACPI_APEI_ERROR_DEVICE),
109
- aml_int(0x80)));
110
+ aml_int(ERROR_DEVICE_NOTIFICATION)));
111
break;
112
case ACPI_GED_NVDIMM_HOTPLUG_EVT:
113
aml_append(if_ctx,
114
@@ -XXX,XX +XXX,XX @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
115
116
static const Property acpi_ged_properties[] = {
117
DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0),
118
- DEFINE_PROP_BOOL("x-has-hest-addr", AcpiGedState, ghes_state.hest_lookup, true),
119
+ DEFINE_PROP_BOOL("x-has-hest-addr", AcpiGedState, ghes_state.use_hest_addr, true),
120
};
121
122
static const VMStateDescription vmstate_memhp_state = {
123
diff --git a/hw/acpi/ghes-stub.c b/hw/acpi/ghes-stub.c
18
diff --git a/hw/acpi/ghes-stub.c b/hw/acpi/ghes-stub.c
124
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
125
--- a/hw/acpi/ghes-stub.c
20
--- a/hw/acpi/ghes-stub.c
126
+++ b/hw/acpi/ghes-stub.c
21
+++ b/hw/acpi/ghes-stub.c
127
@@ -XXX,XX +XXX,XX @@
22
@@ -XXX,XX +XXX,XX @@
...
...
132
+int acpi_ghes_memory_errors(AcpiGhesState *ags, uint16_t source_id,
27
+int acpi_ghes_memory_errors(AcpiGhesState *ags, uint16_t source_id,
133
+ uint64_t physical_address)
28
+ uint64_t physical_address)
134
{
29
{
135
return -1;
30
return -1;
136
}
31
}
32
33
-bool acpi_ghes_present(void)
34
+AcpiGhesState *acpi_ghes_get_state(void)
35
{
36
- return false;
37
+ return NULL;
38
}
137
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
39
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
138
index XXXXXXX..XXXXXXX 100644
40
index XXXXXXX..XXXXXXX 100644
139
--- a/hw/acpi/ghes.c
41
--- a/hw/acpi/ghes.c
140
+++ b/hw/acpi/ghes.c
42
+++ b/hw/acpi/ghes.c
141
@@ -XXX,XX +XXX,XX @@
43
@@ -XXX,XX +XXX,XX @@ static void get_hw_error_offsets(uint64_t ghes_addr,
142
* docs/specs/acpi_hest_ghes.rst.
44
uint64_t *cper_addr,
143
*/
45
uint64_t *read_ack_register_addr)
144
145
-/* ACPI 6.2: 18.3.2.8 Generic Hardware Error Source version 2
146
+/*
147
+ * ACPI 6.2: 18.3.2.8 Generic Hardware Error Source version 2
148
* Table 18-382 Generic Hardware Error Source version 2 (GHESv2) Structure
149
*/
150
#define HEST_GHES_V2_TABLE_SIZE 92
151
-#define GHES_ACK_OFFSET (64 + GAS_ADDR_OFFSET)
152
+#define GHES_READ_ACK_ADDR_OFF 64
153
154
-/* ACPI 6.2: 18.3.2.7: Generic Hardware Error Source
155
+/*
156
+ * ACPI 6.2: 18.3.2.7: Generic Hardware Error Source
157
* Table 18-380: 'Error Status Address' field
158
*/
159
-#define GHES_ERR_ST_ADDR_OFFSET (20 + GAS_ADDR_OFFSET)
160
+#define GHES_ERR_STATUS_ADDR_OFF 20
161
162
/*
163
* Values for error_severity field
164
@@ -XXX,XX +XXX,XX @@ static void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker,
165
}
166
167
/* Build Generic Hardware Error Source version 2 (GHESv2) */
168
-static void build_ghes_v2(GArray *table_data,
169
- BIOSLinker *linker,
170
- const AcpiNotificationSourceId *notif_src,
171
- uint16_t index, int num_sources)
172
+static void build_ghes_v2_entry(GArray *table_data,
173
+ BIOSLinker *linker,
174
+ const AcpiNotificationSourceId *notif_src,
175
+ uint16_t index, int num_sources)
176
{
46
{
177
uint64_t address_offset;
47
- if (!ghes_addr) {
178
const uint16_t notify = notif_src->notify;
179
@@ -XXX,XX +XXX,XX @@ static void build_ghes_v2(GArray *table_data,
180
}
181
182
/* Build Hardware Error Source Table */
183
-void acpi_build_hest(GArray *table_data, GArray *hardware_errors,
184
+void acpi_build_hest(AcpiGhesState *ags, GArray *table_data,
185
+ GArray *hardware_errors,
186
BIOSLinker *linker,
187
- const AcpiNotificationSourceId * const notif_source,
188
+ const AcpiNotificationSourceId *notif_source,
189
int num_sources,
190
const char *oem_id, const char *oem_table_id)
191
{
192
AcpiTable table = { .sig = "HEST", .rev = 1,
193
.oem_id = oem_id, .oem_table_id = oem_table_id };
194
- AcpiGedState *acpi_ged_state;
195
- AcpiGhesState *ags = NULL;
196
+ uint32_t hest_offset;
197
int i;
198
199
build_ghes_error_table(hardware_errors, linker, num_sources);
200
201
acpi_table_begin(&table, table_data);
202
203
- int hest_offset = table_data->len;
204
+ hest_offset = table_data->len;
205
206
/* Error Source Count */
207
build_append_int_noprefix(table_data, num_sources, 4);
208
for (i = 0; i < num_sources; i++) {
209
- build_ghes_v2(table_data, linker, &notif_source[i], i, num_sources);
210
+ build_ghes_v2_entry(table_data, linker, &notif_source[i], i, num_sources);
211
}
212
213
acpi_table_end(linker, &table);
214
215
/*
216
- * tell firmware to write into GPA the address of HEST via fw_cfg,
217
+ * Tell firmware to write into GPA the address of HEST via fw_cfg,
218
* once initialized.
219
*/
220
221
- acpi_ged_state = ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED,
222
- NULL));
223
- if (!acpi_ged_state) {
224
- return;
48
- return;
225
- }
49
- }
226
-
50
-
227
- ags = &acpi_ged_state->ghes_state;
228
- if (ags->hest_lookup) {
229
+ if (ags->use_hest_addr) {
230
bios_linker_loader_write_pointer(linker,
231
ACPI_HEST_ADDR_FW_CFG_FILE, 0,
232
sizeof(uint64_t),
233
@@ -XXX,XX +XXX,XX @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s,
234
fw_cfg_add_file_callback(s, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, NULL, NULL,
235
NULL, &(ags->hw_error_le), sizeof(ags->hw_error_le), false);
236
237
- if (ags && ags->hest_lookup) {
238
+ if (ags->use_hest_addr) {
239
fw_cfg_add_file_callback(s, ACPI_HEST_ADDR_FW_CFG_FILE, NULL, NULL,
240
NULL, &(ags->hest_addr_le), sizeof(ags->hest_addr_le), false);
241
}
242
-
243
- ags->present = true;
244
}
245
246
static void get_hw_error_offsets(uint64_t ghes_addr,
247
@@ -XXX,XX +XXX,XX @@ static void get_hw_error_offsets(uint64_t ghes_addr,
248
*read_ack_register_addr = ghes_addr + sizeof(uint64_t);
249
}
250
251
-static void get_ghes_source_offsets(uint16_t source_id, uint64_t hest_addr,
252
+static void get_ghes_source_offsets(uint16_t source_id,
253
+ uint64_t hest_entry_addr,
254
uint64_t *cper_addr,
255
uint64_t *read_ack_start_addr,
256
Error **errp)
257
{
258
uint64_t hest_err_block_addr, hest_read_ack_addr;
259
- uint64_t err_source_struct, error_block_addr;
260
+ uint64_t err_source_entry, error_block_addr;
261
uint32_t num_sources, i;
262
263
- cpu_physical_memory_read(hest_addr, &num_sources, sizeof(num_sources));
264
+
265
+ cpu_physical_memory_read(hest_entry_addr, &num_sources,
266
+ sizeof(num_sources));
267
num_sources = le32_to_cpu(num_sources);
268
269
- err_source_struct = hest_addr + sizeof(num_sources);
270
+ err_source_entry = hest_entry_addr + sizeof(num_sources);
271
272
/*
51
/*
273
* Currently, HEST Error source navigates only for GHESv2 tables
52
* non-HEST version supports only one source, so no need to change
274
*/
53
* the start offset based on the source ID. Also, we can't validate
275
54
@@ -XXX,XX +XXX,XX @@ static void get_ghes_source_offsets(uint16_t source_id,
276
for (i = 0; i < num_sources; i++) {
277
- uint64_t addr = err_source_struct;
278
+ uint64_t addr = err_source_entry;
279
uint16_t type, src_id;
280
281
cpu_physical_memory_read(addr, &type, sizeof(type));
282
@@ -XXX,XX +XXX,XX @@ static void get_ghes_source_offsets(uint16_t source_id, uint64_t hest_addr,
283
addr += sizeof(type);
284
cpu_physical_memory_read(addr, &src_id, sizeof(src_id));
285
286
- if (src_id == source_id) {
287
+ if (le16_to_cpu(src_id) == source_id) {
288
break;
289
}
290
291
- err_source_struct += HEST_GHES_V2_TABLE_SIZE;
292
+ err_source_entry += HEST_GHES_V2_TABLE_SIZE;
293
}
294
if (i == num_sources) {
295
error_setg(errp, "HEST: Source %d not found.", source_id);
296
@@ -XXX,XX +XXX,XX @@ static void get_ghes_source_offsets(uint16_t source_id, uint64_t hest_addr,
297
}
298
299
/* Navigate though table address pointers */
300
- hest_err_block_addr = err_source_struct + GHES_ERR_ST_ADDR_OFFSET;
301
- hest_read_ack_addr = err_source_struct + GHES_ACK_OFFSET;
302
+ hest_err_block_addr = err_source_entry + GHES_ERR_STATUS_ADDR_OFF +
303
+ GAS_ADDR_OFFSET;
304
305
cpu_physical_memory_read(hest_err_block_addr, &error_block_addr,
306
sizeof(error_block_addr));
307
308
+ error_block_addr = le64_to_cpu(error_block_addr);
309
+
310
cpu_physical_memory_read(error_block_addr, cper_addr,
311
sizeof(*cper_addr));
312
313
+ *cper_addr = le64_to_cpu(*cper_addr);
314
+
315
+ hest_read_ack_addr = err_source_entry + GHES_READ_ACK_ADDR_OFF +
316
+ GAS_ADDR_OFFSET;
317
+
318
cpu_physical_memory_read(hest_read_ack_addr, read_ack_start_addr,
319
sizeof(*read_ack_start_addr));
320
+
321
+ *read_ack_start_addr = le64_to_cpu(*read_ack_start_addr);
322
}
323
324
NotifierList acpi_generic_error_notifiers =
55
NotifierList acpi_generic_error_notifiers =
325
NOTIFIER_LIST_INITIALIZER(error_device_notifiers);
56
NOTIFIER_LIST_INITIALIZER(error_device_notifiers);
326
57
327
-void ghes_record_cper_errors(const void *cper, size_t len,
58
-void ghes_record_cper_errors(const void *cper, size_t len,
328
+void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len,
59
+void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len,
329
uint16_t source_id, Error **errp)
60
uint16_t source_id, Error **errp)
330
{
61
{
331
uint64_t cper_addr = 0, read_ack_register_addr = 0, read_ack_register;
62
uint64_t cper_addr = 0, read_ack_register_addr = 0, read_ack_register;
63
- AcpiGedState *acpi_ged_state;
332
- AcpiGhesState *ags;
64
- AcpiGhesState *ags;
333
65
334
if (len > ACPI_GHES_MAX_RAW_DATA_LENGTH) {
66
if (len > ACPI_GHES_MAX_RAW_DATA_LENGTH) {
335
error_setg(errp, "GHES CPER record is too big: %zd", len);
67
error_setg(errp, "GHES CPER record is too big: %zd", len);
336
return;
68
return;
337
}
69
}
338
70
339
- ags = acpi_ghes_get_state();
71
- acpi_ged_state = ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED,
340
- if (!ags) {
72
- NULL));
73
- if (!acpi_ged_state) {
74
- error_setg(errp, "Can't find ACPI_GED object");
75
- return;
76
- }
77
- ags = &acpi_ged_state->ghes_state;
78
-
79
-
80
if (!ags->use_hest_addr) {
81
get_hw_error_offsets(le64_to_cpu(ags->hw_error_le),
82
&cper_addr, &read_ack_register_addr);
83
@@ -XXX,XX +XXX,XX @@ void ghes_record_cper_errors(const void *cper, size_t len,
84
&cper_addr, &read_ack_register_addr, errp);
85
}
86
87
- if (!cper_addr) {
88
- error_setg(errp, "can not find Generic Error Status Block");
341
- return;
89
- return;
342
- }
90
- }
343
-
91
-
344
- if (!ags->hest_lookup) {
92
cpu_physical_memory_read(read_ack_register_addr,
345
+ if (!ags->use_hest_addr) {
93
&read_ack_register, sizeof(read_ack_register));
346
fprintf(stderr,"Using old GHES lookup\n");
94
347
get_hw_error_offsets(le64_to_cpu(ags->hw_error_le),
348
&cper_addr, &read_ack_register_addr);
349
@@ -XXX,XX +XXX,XX @@ void ghes_record_cper_errors(const void *cper, size_t len,
95
@@ -XXX,XX +XXX,XX @@ void ghes_record_cper_errors(const void *cper, size_t len,
350
notifier_list_notify(&acpi_generic_error_notifiers, &source_id);
96
notifier_list_notify(&acpi_generic_error_notifiers, NULL);
351
}
97
}
352
98
353
-int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address)
99
-int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address)
354
+int acpi_ghes_memory_errors(AcpiGhesState *ags, uint16_t source_id,
100
+int acpi_ghes_memory_errors(AcpiGhesState *ags, uint16_t source_id,
355
+ uint64_t physical_address)
101
+ uint64_t physical_address)
...
...
363
- ghes_record_cper_errors(block->data, block->len, source_id, &errp);
109
- ghes_record_cper_errors(block->data, block->len, source_id, &errp);
364
+ ghes_record_cper_errors(ags, block->data, block->len, source_id, &errp);
110
+ ghes_record_cper_errors(ags, block->data, block->len, source_id, &errp);
365
111
366
g_array_free(block, true);
112
g_array_free(block, true);
367
113
368
@@ -XXX,XX +XXX,XX @@ AcpiGhesState *acpi_ghes_get_state(void)
114
@@ -XXX,XX +XXX,XX @@ int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address)
369
return NULL;
115
return 0;
116
}
117
118
-bool acpi_ghes_present(void)
119
+AcpiGhesState *acpi_ghes_get_state(void)
120
{
121
AcpiGedState *acpi_ged_state;
122
AcpiGhesState *ags;
123
@@ -XXX,XX +XXX,XX @@ bool acpi_ghes_present(void)
124
NULL));
125
126
if (!acpi_ged_state) {
127
- return false;
128
+ return NULL;
370
}
129
}
371
ags = &acpi_ged_state->ghes_state;
130
ags = &acpi_ged_state->ghes_state;
372
- if (!ags->present) {
131
- if (!ags->hw_error_le && !ags->hest_addr_le)
373
- return NULL;
132
- return false;
374
- }
133
375
+
134
- return true;
376
if (!ags->hw_error_le && !ags->hest_addr_le) {
135
+ if (!ags->hw_error_le && !ags->hest_addr_le) {
377
return NULL;
136
+ return NULL;
378
}
379
diff --git a/hw/acpi/ghes_cper.c b/hw/acpi/ghes_cper.c
380
index XXXXXXX..XXXXXXX 100644
381
--- a/hw/acpi/ghes_cper.c
382
+++ b/hw/acpi/ghes_cper.c
383
@@ -XXX,XX +XXX,XX @@
384
/*
385
* CPER payload parser for error injection
386
*
387
- * Copyright(C) 2024 Huawei LTD.
388
+ * Copyright(C) 2024-2025 Huawei LTD.
389
*
390
* This code is licensed under the GPL version 2 or later. See the
391
* COPYING file in the top-level directory.
392
@@ -XXX,XX +XXX,XX @@
393
#include "qapi/qapi-commands-acpi-hest.h"
394
#include "hw/acpi/ghes.h"
395
396
-void qmp_inject_ghes_error(const char *qmp_cper, Error **errp)
397
+void qmp_inject_ghes_v2_error(const char *qmp_cper, Error **errp)
398
{
399
+ AcpiGhesState *ags;
400
+
401
+ ags = acpi_ghes_get_state();
402
+ if (!ags) {
403
+ return;
404
+ }
137
+ }
405
138
+ return ags;
406
uint8_t *cper;
407
size_t len;
408
@@ -XXX,XX +XXX,XX @@ void qmp_inject_ghes_error(const char *qmp_cper, Error **errp)
409
return;
410
}
411
412
- ghes_record_cper_errors(cper, len, ACPI_HEST_SRC_ID_QMP, errp);
413
+ ghes_record_cper_errors(ags, cper, len, ACPI_HEST_SRC_ID_QMP, errp);
414
}
139
}
415
diff --git a/hw/acpi/ghes_cper_stub.c b/hw/acpi/ghes_cper_stub.c
416
index XXXXXXX..XXXXXXX 100644
417
--- a/hw/acpi/ghes_cper_stub.c
418
+++ b/hw/acpi/ghes_cper_stub.c
419
@@ -XXX,XX +XXX,XX @@
420
/*
421
* Stub interface for CPER payload parser for error injection
422
*
423
- * Copyright(C) 2024 Huawei LTD.
424
+ * Copyright(C) 2024-2025 Huawei LTD.
425
*
426
* This code is licensed under the GPL version 2 or later. See the
427
* COPYING file in the top-level directory.
428
@@ -XXX,XX +XXX,XX @@
429
#include "qapi/qapi-commands-acpi-hest.h"
430
#include "hw/acpi/ghes.h"
431
432
-void qmp_inject_ghes_error(const char *cper, Error **errp)
433
+void qmp_inject_ghes_v2_error(const char *cper, Error **errp)
434
{
435
error_setg(errp, "GHES QMP error inject is not compiled in");
436
}
437
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
438
index XXXXXXX..XXXXXXX 100644
439
--- a/hw/arm/virt-acpi-build.c
440
+++ b/hw/arm/virt-acpi-build.c
441
@@ -XXX,XX +XXX,XX @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
442
443
acpi_add_table(table_offsets, tables_blob);
444
445
- if (!ags->hest_lookup) {
446
- acpi_build_hest(tables_blob, tables->hardware_errors,
447
+ if (!ags->use_hest_addr) {
448
+ acpi_build_hest(ags, tables_blob, tables->hardware_errors,
449
tables->linker, hest_ghes_notify_9_2,
450
ARRAY_SIZE(hest_ghes_notify_9_2),
451
vms->oem_id, vms->oem_table_id);
452
} else {
453
- acpi_build_hest(tables_blob, tables->hardware_errors,
454
+ acpi_build_hest(ags, tables_blob, tables->hardware_errors,
455
tables->linker, hest_ghes_notify,
456
ARRAY_SIZE(hest_ghes_notify),
457
vms->oem_id, vms->oem_table_id);
458
diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h
140
diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h
459
index XXXXXXX..XXXXXXX 100644
141
index XXXXXXX..XXXXXXX 100644
460
--- a/include/hw/acpi/ghes.h
142
--- a/include/hw/acpi/ghes.h
461
+++ b/include/hw/acpi/ghes.h
143
+++ b/include/hw/acpi/ghes.h
462
@@ -XXX,XX +XXX,XX @@ enum AcpiGhesNotifyType {
144
@@ -XXX,XX +XXX,XX @@ void acpi_build_hest(AcpiGhesState *ags, GArray *table_data,
463
typedef struct AcpiGhesState {
464
uint64_t hest_addr_le;
465
uint64_t hw_error_le;
466
- bool present; /* True if GHES is present at all on this board */
467
- bool hest_lookup; /* True if HEST address is present */
468
+ bool use_hest_addr; /* True if HEST address is present */
469
} AcpiGhesState;
470
471
/*
472
@@ -XXX,XX +XXX,XX @@ typedef struct AcpiNotificationSourceId {
473
enum AcpiGhesNotifyType notify;
474
} AcpiNotificationSourceId;
475
476
-void acpi_build_hest(GArray *table_data, GArray *hardware_errors,
477
+void acpi_build_hest(AcpiGhesState *ags, GArray *table_data,
478
+ GArray *hardware_errors,
479
BIOSLinker *linker,
480
const AcpiNotificationSourceId * const notif_source,
481
int num_sources,
482
const char *oem_id, const char *oem_table_id);
145
const char *oem_id, const char *oem_table_id);
483
void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s,
146
void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s,
484
GArray *hardware_errors);
147
GArray *hardware_errors);
485
-int acpi_ghes_memory_errors(uint16_t source_id, uint64_t error_physical_addr);
148
-int acpi_ghes_memory_errors(uint16_t source_id, uint64_t error_physical_addr);
486
-void ghes_record_cper_errors(const void *cper, size_t len,
149
-void ghes_record_cper_errors(const void *cper, size_t len,
487
+int acpi_ghes_memory_errors(AcpiGhesState *ags, uint16_t source_id,
150
+int acpi_ghes_memory_errors(AcpiGhesState *ags, uint16_t source_id,
488
+ uint64_t error_physical_addr);
151
+ uint64_t error_physical_addr);
489
+void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len,
152
+void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len,
490
uint16_t source_id, Error **errp);
153
uint16_t source_id, Error **errp);
491
154
492
/**
155
/**
493
diff --git a/qapi/acpi-hest.json b/qapi/acpi-hest.json
156
- * acpi_ghes_present: Report whether ACPI GHES table is present
494
index XXXXXXX..XXXXXXX 100644
157
+ * acpi_ghes_get_state: Get a pointer for ACPI ghes state
495
--- a/qapi/acpi-hest.json
158
*
496
+++ b/qapi/acpi-hest.json
159
- * Returns: true if the system has an ACPI GHES table and it is
497
@@ -XXX,XX +XXX,XX @@
160
- * safe to call acpi_ghes_memory_errors() to record a memory error.
498
161
+ * Returns: a pointer to ghes state if the system has an ACPI GHES table,
499
162
+ * it is enabled and it is safe to call acpi_ghes_memory_errors() to record
500
##
163
+ * a memory error. Returns false, otherwise.
501
-# @inject-ghes-error:
164
*/
502
+# @inject-ghes-v2-error:
165
-bool acpi_ghes_present(void);
503
#
166
+AcpiGhesState *acpi_ghes_get_state(void);
504
# Inject an error with additional ACPI 6.1 GHESv2 error information
167
#endif
505
#
506
@@ -XXX,XX +XXX,XX @@
507
#
508
# @unstable: This command is experimental.
509
#
510
-# Since: 9.2
511
+# Since: 10.0
512
##
513
-{ 'command': 'inject-ghes-error',
514
+{ 'command': 'inject-ghes-v2-error',
515
'data': {
516
'cper': 'str'
517
},
518
diff --git a/scripts/arm_processor_error.py b/scripts/arm_processor_error.py
519
index XXXXXXX..XXXXXXX 100644
520
--- a/scripts/arm_processor_error.py
521
+++ b/scripts/arm_processor_error.py
522
@@ -XXX,XX +XXX,XX @@
523
# pylint: disable=C0301,C0114,R0903,R0912,R0913,R0914,R0915,W0511
524
# SPDX-License-Identifier: GPL-2.0
525
#
526
-# Copyright (C) 2024 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
527
+# Copyright (C) 2024-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
528
529
# Note: currently it lacks a method to fill the ARM Processor Error CPER
530
# psci field from emulation. On a real hardware, this is filled only
531
diff --git a/scripts/ghes_inject.py b/scripts/ghes_inject.py
532
index XXXXXXX..XXXXXXX 100755
533
--- a/scripts/ghes_inject.py
534
+++ b/scripts/ghes_inject.py
535
@@ -XXX,XX +XXX,XX @@
536
#
537
# SPDX-License-Identifier: GPL-2.0
538
#
539
-# Copyright (C) 2024 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
540
+# Copyright (C) 2024-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
541
542
"""
543
Handle ACPI GHESv2 error injection logic QEMU QMP interface.
544
diff --git a/scripts/qmp_helper.py b/scripts/qmp_helper.py
545
index XXXXXXX..XXXXXXX 100644
546
--- a/scripts/qmp_helper.py
547
+++ b/scripts/qmp_helper.py
548
@@ -XXX,XX +XXX,XX @@
549
# # pylint: disable=C0103,E0213,E1135,E1136,E1137,R0902,R0903,R0912,R0913
550
# SPDX-License-Identifier: GPL-2.0
551
#
552
-# Copyright (C) 2024 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
553
+# Copyright (C) 2024-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
554
555
"""
556
Helper classes to be used by ghes_inject command classes.
557
@@ -XXX,XX +XXX,XX @@ def send_cper_raw(self, cper_data):
558
559
self._connect()
560
561
- if self.send_cmd("inject-ghes-error", cmd_arg):
562
+ if self.send_cmd("inject-ghes-v2-error", cmd_arg):
563
print("Error injected.")
564
565
def send_cper(self, notif_type, payload):
566
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
168
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
567
index XXXXXXX..XXXXXXX 100644
169
index XXXXXXX..XXXXXXX 100644
568
--- a/target/arm/kvm.c
170
--- a/target/arm/kvm.c
569
+++ b/target/arm/kvm.c
171
+++ b/target/arm/kvm.c
570
@@ -XXX,XX +XXX,XX @@ void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr)
172
@@ -XXX,XX +XXX,XX @@ void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr)
...
...
573
hwaddr paddr;
175
hwaddr paddr;
574
+ AcpiGhesState *ags;
176
+ AcpiGhesState *ags;
575
177
576
assert(code == BUS_MCEERR_AR || code == BUS_MCEERR_AO);
178
assert(code == BUS_MCEERR_AR || code == BUS_MCEERR_AO);
577
179
578
- if (acpi_ghes_get_state() && addr) {
180
- if (acpi_ghes_present() && addr) {
579
+ ags = acpi_ghes_get_state();
181
+ ags = acpi_ghes_get_state();
580
+
581
+ if (ags && addr) {
182
+ if (ags && addr) {
582
ram_addr = qemu_ram_addr_from_host(addr);
183
ram_addr = qemu_ram_addr_from_host(addr);
583
if (ram_addr != RAM_ADDR_INVALID &&
184
if (ram_addr != RAM_ADDR_INVALID &&
584
kvm_physical_memory_addr_from_host(c->kvm_state, addr, &paddr)) {
185
kvm_physical_memory_addr_from_host(c->kvm_state, addr, &paddr)) {
585
@@ -XXX,XX +XXX,XX @@ void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr)
186
@@ -XXX,XX +XXX,XX @@ void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr)
...
...
590
+ if (!acpi_ghes_memory_errors(ags, ACPI_HEST_SRC_ID_SEA,
191
+ if (!acpi_ghes_memory_errors(ags, ACPI_HEST_SRC_ID_SEA,
591
+ paddr)) {
192
+ paddr)) {
592
kvm_inject_arm_sea(c);
193
kvm_inject_arm_sea(c);
593
} else {
194
} else {
594
error_report("failed to record the error");
195
error_report("failed to record the error");
595
diff --git a/tests/data/acpi/aarch64/virt/DSDT b/tests/data/acpi/aarch64/virt/DSDT
196
--
596
index XXXXXXX..XXXXXXX 100644
197
2.48.1
597
Binary files a/tests/data/acpi/aarch64/virt/DSDT and b/tests/data/acpi/aarch64/virt/DSDT differ
598
diff --git a/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt b/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt
599
index XXXXXXX..XXXXXXX 100644
600
Binary files a/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt and b/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt differ
601
diff --git a/tests/data/acpi/aarch64/virt/DSDT.memhp b/tests/data/acpi/aarch64/virt/DSDT.memhp
602
index XXXXXXX..XXXXXXX 100644
603
Binary files a/tests/data/acpi/aarch64/virt/DSDT.memhp and b/tests/data/acpi/aarch64/virt/DSDT.memhp differ
604
diff --git a/tests/data/acpi/aarch64/virt/DSDT.pxb b/tests/data/acpi/aarch64/virt/DSDT.pxb
605
index XXXXXXX..XXXXXXX 100644
606
Binary files a/tests/data/acpi/aarch64/virt/DSDT.pxb and b/tests/data/acpi/aarch64/virt/DSDT.pxb differ
607
diff --git a/tests/data/acpi/aarch64/virt/DSDT.topology b/tests/data/acpi/aarch64/virt/DSDT.topology
608
index XXXXXXX..XXXXXXX 100644
609
Binary files a/tests/data/acpi/aarch64/virt/DSDT.topology and b/tests/data/acpi/aarch64/virt/DSDT.topology differ
610
diff --git a/tests/data/acpi/aarch64/virt/HEST b/tests/data/acpi/aarch64/virt/HEST
611
new file mode 100644
612
index XXXXXXX..XXXXXXX
613
Binary files /dev/null and b/tests/data/acpi/aarch64/virt/HEST differ
diff view generated by jsdifflib
New patch
1
The GHES migration logic should now support HEST table location too.
1
2
3
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
4
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
5
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
6
---
7
hw/acpi/generic_event_device.c | 29 +++++++++++++++++++++++++++++
8
1 file changed, 29 insertions(+)
9
10
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
11
index XXXXXXX..XXXXXXX 100644
12
--- a/hw/acpi/generic_event_device.c
13
+++ b/hw/acpi/generic_event_device.c
14
@@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_ghes_state = {
15
}
16
};
17
18
+static const VMStateDescription vmstate_hest = {
19
+ .name = "acpi-hest",
20
+ .version_id = 1,
21
+ .minimum_version_id = 1,
22
+ .fields = (const VMStateField[]) {
23
+ VMSTATE_UINT64(hest_addr_le, AcpiGhesState),
24
+ VMSTATE_END_OF_LIST()
25
+ },
26
+};
27
+
28
+static bool hest_needed(void *opaque)
29
+{
30
+ AcpiGedState *s = opaque;
31
+ return s->ghes_state.hest_addr_le;
32
+}
33
+
34
+static const VMStateDescription vmstate_hest_state = {
35
+ .name = "acpi-ged/hest",
36
+ .version_id = 1,
37
+ .minimum_version_id = 1,
38
+ .needed = hest_needed,
39
+ .fields = (const VMStateField[]) {
40
+ VMSTATE_STRUCT(ghes_state, AcpiGedState, 1,
41
+ vmstate_hest, AcpiGhesState),
42
+ VMSTATE_END_OF_LIST()
43
+ }
44
+};
45
+
46
static const VMStateDescription vmstate_acpi_ged = {
47
.name = "acpi-ged",
48
.version_id = 1,
49
@@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_acpi_ged = {
50
&vmstate_memhp_state,
51
&vmstate_cpuhp_state,
52
&vmstate_ghes_state,
53
+ &vmstate_hest_state,
54
NULL
55
}
56
};
57
--
58
2.48.1
diff view generated by jsdifflib
New patch
1
Create a new property (x-has-hest-addr) and use it to detect if
2
the GHES table offsets can be calculated from the HEST address
3
(qemu 10.0 and upper) or via the legacy way via an offset obtained
4
from the hardware_errors firmware file.
1
5
6
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
7
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
8
---
9
hw/acpi/generic_event_device.c | 1 +
10
hw/arm/virt-acpi-build.c | 18 ++++++++++++++++--
11
hw/core/machine.c | 2 ++
12
3 files changed, 19 insertions(+), 2 deletions(-)
13
14
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/hw/acpi/generic_event_device.c
17
+++ b/hw/acpi/generic_event_device.c
18
@@ -XXX,XX +XXX,XX @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
19
20
static const Property acpi_ged_properties[] = {
21
DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0),
22
+ DEFINE_PROP_BOOL("x-has-hest-addr", AcpiGedState, ghes_state.use_hest_addr, false),
23
};
24
25
static const VMStateDescription vmstate_memhp_state = {
26
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
27
index XXXXXXX..XXXXXXX 100644
28
--- a/hw/arm/virt-acpi-build.c
29
+++ b/hw/arm/virt-acpi-build.c
30
@@ -XXX,XX +XXX,XX @@ static const AcpiNotificationSourceId hest_ghes_notify[] = {
31
{ ACPI_HEST_SRC_ID_SYNC, ACPI_GHES_NOTIFY_SEA },
32
};
33
34
+static const AcpiNotificationSourceId hest_ghes_notify_9_2[] = {
35
+ { ACPI_HEST_SRC_ID_SYNC, ACPI_GHES_NOTIFY_SEA },
36
+};
37
+
38
static
39
void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
40
{
41
@@ -XXX,XX +XXX,XX @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
42
build_dbg2(tables_blob, tables->linker, vms);
43
44
if (vms->ras) {
45
+ static const AcpiNotificationSourceId *notify;
46
AcpiGedState *acpi_ged_state;
47
+ unsigned int notify_sz;
48
AcpiGhesState *ags;
49
50
acpi_ged_state = ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED,
51
@@ -XXX,XX +XXX,XX @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
52
ags = &acpi_ged_state->ghes_state;
53
54
acpi_add_table(table_offsets, tables_blob);
55
+
56
+ if (!ags->use_hest_addr) {
57
+ notify = hest_ghes_notify_9_2;
58
+ notify_sz = ARRAY_SIZE(hest_ghes_notify_9_2);
59
+ } else {
60
+ notify = hest_ghes_notify;
61
+ notify_sz = ARRAY_SIZE(hest_ghes_notify);
62
+ }
63
+
64
acpi_build_hest(ags, tables_blob, tables->hardware_errors,
65
- tables->linker, hest_ghes_notify,
66
- ARRAY_SIZE(hest_ghes_notify),
67
+ tables->linker, notify, notify_sz,
68
vms->oem_id, vms->oem_table_id);
69
}
70
}
71
diff --git a/hw/core/machine.c b/hw/core/machine.c
72
index XXXXXXX..XXXXXXX 100644
73
--- a/hw/core/machine.c
74
+++ b/hw/core/machine.c
75
@@ -XXX,XX +XXX,XX @@
76
#include "hw/virtio/virtio-pci.h"
77
#include "hw/virtio/virtio-net.h"
78
#include "hw/virtio/virtio-iommu.h"
79
+#include "hw/acpi/generic_event_device.h"
80
#include "audio/audio.h"
81
82
GlobalProperty hw_compat_9_2[] = {
83
@@ -XXX,XX +XXX,XX @@ GlobalProperty hw_compat_9_2[] = {
84
{ "virtio-balloon-pci-non-transitional", "vectors", "0" },
85
{ "virtio-mem-pci", "vectors", "0" },
86
{ "migration", "multifd-clean-tls-termination", "false" },
87
+ { TYPE_ACPI_GED, "x-has-hest-addr", "false" },
88
};
89
const size_t hw_compat_9_2_len = G_N_ELEMENTS(hw_compat_9_2);
90
91
--
92
2.48.1
diff view generated by jsdifflib
New patch
1
Adds a generic error device to handle generic hardware error
2
events as specified at ACPI 6.5 specification at 18.3.2.7.2:
3
https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#event-notification-for-generic-error-sources
4
using HID PNP0C33.
1
5
6
The PNP0C33 device is used to report hardware errors to
7
the guest via ACPI APEI Generic Hardware Error Source (GHES).
8
9
Co-authored-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
10
Co-authored-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
11
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
12
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
13
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
14
---
15
hw/acpi/aml-build.c | 10 ++++++++++
16
hw/acpi/generic_event_device.c | 13 +++++++++++++
17
include/hw/acpi/acpi_dev_interface.h | 1 +
18
include/hw/acpi/aml-build.h | 2 ++
19
include/hw/acpi/generic_event_device.h | 1 +
20
5 files changed, 27 insertions(+)
21
22
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
23
index XXXXXXX..XXXXXXX 100644
24
--- a/hw/acpi/aml-build.c
25
+++ b/hw/acpi/aml-build.c
26
@@ -XXX,XX +XXX,XX @@ Aml *aml_i2c_serial_bus_device(uint16_t address, const char *resource_source)
27
28
return var;
29
}
30
+
31
+/* ACPI 5.0b: 18.3.2.6.2 Event Notification For Generic Error Sources */
32
+Aml *aml_error_device(void)
33
+{
34
+ Aml *dev = aml_device(ACPI_APEI_ERROR_DEVICE);
35
+ aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C33")));
36
+ aml_append(dev, aml_name_decl("_UID", aml_int(0)));
37
+
38
+ return dev;
39
+}
40
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
41
index XXXXXXX..XXXXXXX 100644
42
--- a/hw/acpi/generic_event_device.c
43
+++ b/hw/acpi/generic_event_device.c
44
@@ -XXX,XX +XXX,XX @@ static const uint32_t ged_supported_events[] = {
45
ACPI_GED_PWR_DOWN_EVT,
46
ACPI_GED_NVDIMM_HOTPLUG_EVT,
47
ACPI_GED_CPU_HOTPLUG_EVT,
48
+ ACPI_GED_ERROR_EVT,
49
};
50
51
/*
52
@@ -XXX,XX +XXX,XX @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
53
aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
54
aml_int(0x80)));
55
break;
56
+ case ACPI_GED_ERROR_EVT:
57
+ /*
58
+ * ACPI 5.0b: 5.6.6 Device Object Notifications
59
+ * Table 5-135 Error Device Notification Values
60
+ * Defines 0x80 as the value to be used on notifications
61
+ */
62
+ aml_append(if_ctx,
63
+ aml_notify(aml_name(ACPI_APEI_ERROR_DEVICE),
64
+ aml_int(0x80)));
65
+ break;
66
case ACPI_GED_NVDIMM_HOTPLUG_EVT:
67
aml_append(if_ctx,
68
aml_notify(aml_name("\\_SB.NVDR"),
69
@@ -XXX,XX +XXX,XX @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
70
sel = ACPI_GED_MEM_HOTPLUG_EVT;
71
} else if (ev & ACPI_POWER_DOWN_STATUS) {
72
sel = ACPI_GED_PWR_DOWN_EVT;
73
+ } else if (ev & ACPI_GENERIC_ERROR) {
74
+ sel = ACPI_GED_ERROR_EVT;
75
} else if (ev & ACPI_NVDIMM_HOTPLUG_STATUS) {
76
sel = ACPI_GED_NVDIMM_HOTPLUG_EVT;
77
} else if (ev & ACPI_CPU_HOTPLUG_STATUS) {
78
diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
79
index XXXXXXX..XXXXXXX 100644
80
--- a/include/hw/acpi/acpi_dev_interface.h
81
+++ b/include/hw/acpi/acpi_dev_interface.h
82
@@ -XXX,XX +XXX,XX @@ typedef enum {
83
ACPI_NVDIMM_HOTPLUG_STATUS = 16,
84
ACPI_VMGENID_CHANGE_STATUS = 32,
85
ACPI_POWER_DOWN_STATUS = 64,
86
+ ACPI_GENERIC_ERROR = 128,
87
} AcpiEventStatusBits;
88
89
#define TYPE_ACPI_DEVICE_IF "acpi-device-interface"
90
diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h
91
index XXXXXXX..XXXXXXX 100644
92
--- a/include/hw/acpi/aml-build.h
93
+++ b/include/hw/acpi/aml-build.h
94
@@ -XXX,XX +XXX,XX @@ struct CrsRangeSet {
95
/* Consumer/Producer */
96
#define AML_SERIAL_BUS_FLAG_CONSUME_ONLY (1 << 1)
97
98
+#define ACPI_APEI_ERROR_DEVICE "GEDD"
99
/**
100
* init_aml_allocator:
101
*
102
@@ -XXX,XX +XXX,XX @@ Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz,
103
uint8_t channel);
104
Aml *aml_sleep(uint64_t msec);
105
Aml *aml_i2c_serial_bus_device(uint16_t address, const char *resource_source);
106
+Aml *aml_error_device(void);
107
108
/* Block AML object primitives */
109
Aml *aml_scope(const char *name_format, ...) G_GNUC_PRINTF(1, 2);
110
diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
111
index XXXXXXX..XXXXXXX 100644
112
--- a/include/hw/acpi/generic_event_device.h
113
+++ b/include/hw/acpi/generic_event_device.h
114
@@ -XXX,XX +XXX,XX @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED)
115
#define ACPI_GED_PWR_DOWN_EVT 0x2
116
#define ACPI_GED_NVDIMM_HOTPLUG_EVT 0x4
117
#define ACPI_GED_CPU_HOTPLUG_EVT 0x8
118
+#define ACPI_GED_ERROR_EVT 0x10
119
120
typedef struct GEDState {
121
MemoryRegion evt;
122
--
123
2.48.1
diff view generated by jsdifflib
New patch
1
The DSDT table will also be affected by such change.
1
2
3
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
4
---
5
tests/qtest/bios-tables-test-allowed-diff.h | 1 +
6
1 file changed, 1 insertion(+)
7
8
diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h
9
index XXXXXXX..XXXXXXX 100644
10
--- a/tests/qtest/bios-tables-test-allowed-diff.h
11
+++ b/tests/qtest/bios-tables-test-allowed-diff.h
12
@@ -1 +1,2 @@
13
/* List of comma-separated changed AML files to ignore */
14
+"tests/data/acpi/aarch64/virt/DSDT",
15
--
16
2.48.1
diff view generated by jsdifflib
New patch
1
Adds support to ARM virtualization to allow handling
2
generic error ACPI Event via GED & error source device.
1
3
4
It is aligned with Linux Kernel patch:
5
https://lore.kernel.org/lkml/1272350481-27951-8-git-send-email-ying.huang@intel.com/
6
7
Co-authored-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
8
Co-authored-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
9
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
10
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
11
Acked-by: Igor Mammedov <imammedo@redhat.com>
12
13
---
14
15
Changes from v8:
16
17
- Added a call to the function that produces GHES generic
18
records, as this is now added earlier in this series.
19
20
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
21
---
22
hw/acpi/generic_event_device.c | 2 +-
23
hw/arm/virt-acpi-build.c | 1 +
24
hw/arm/virt.c | 12 +++++++++++-
25
include/hw/arm/virt.h | 1 +
26
4 files changed, 14 insertions(+), 2 deletions(-)
27
28
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
29
index XXXXXXX..XXXXXXX 100644
30
--- a/hw/acpi/generic_event_device.c
31
+++ b/hw/acpi/generic_event_device.c
32
@@ -XXX,XX +XXX,XX @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
33
34
static const Property acpi_ged_properties[] = {
35
DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0),
36
- DEFINE_PROP_BOOL("x-has-hest-addr", AcpiGedState, ghes_state.use_hest_addr, false),
37
+ DEFINE_PROP_BOOL("x-has-hest-addr", AcpiGedState, ghes_state.use_hest_addr, true),
38
};
39
40
static const VMStateDescription vmstate_memhp_state = {
41
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
42
index XXXXXXX..XXXXXXX 100644
43
--- a/hw/arm/virt-acpi-build.c
44
+++ b/hw/arm/virt-acpi-build.c
45
@@ -XXX,XX +XXX,XX @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
46
}
47
48
acpi_dsdt_add_power_button(scope);
49
+ aml_append(scope, aml_error_device());
50
#ifdef CONFIG_TPM
51
acpi_dsdt_add_tpm(scope, vms);
52
#endif
53
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
54
index XXXXXXX..XXXXXXX 100644
55
--- a/hw/arm/virt.c
56
+++ b/hw/arm/virt.c
57
@@ -XXX,XX +XXX,XX @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms)
58
DeviceState *dev;
59
MachineState *ms = MACHINE(vms);
60
int irq = vms->irqmap[VIRT_ACPI_GED];
61
- uint32_t event = ACPI_GED_PWR_DOWN_EVT;
62
+ uint32_t event = ACPI_GED_PWR_DOWN_EVT | ACPI_GED_ERROR_EVT;
63
64
if (ms->ram_slots) {
65
event |= ACPI_GED_MEM_HOTPLUG_EVT;
66
@@ -XXX,XX +XXX,XX @@ static void virt_powerdown_req(Notifier *n, void *opaque)
67
}
68
}
69
70
+static void virt_generic_error_req(Notifier *n, void *opaque)
71
+{
72
+ VirtMachineState *s = container_of(n, VirtMachineState, generic_error_notifier);
73
+
74
+ acpi_send_event(s->acpi_dev, ACPI_GENERIC_ERROR);
75
+}
76
+
77
static void create_gpio_keys(char *fdt, DeviceState *pl061_dev,
78
uint32_t phandle)
79
{
80
@@ -XXX,XX +XXX,XX @@ static void machvirt_init(MachineState *machine)
81
82
if (has_ged && aarch64 && firmware_loaded && virt_is_acpi_enabled(vms)) {
83
vms->acpi_dev = create_acpi_ged(vms);
84
+ vms->generic_error_notifier.notify = virt_generic_error_req;
85
+ notifier_list_add(&acpi_generic_error_notifiers,
86
+ &vms->generic_error_notifier);
87
} else {
88
create_gpio_devices(vms, VIRT_GPIO, sysmem);
89
}
90
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
91
index XXXXXXX..XXXXXXX 100644
92
--- a/include/hw/arm/virt.h
93
+++ b/include/hw/arm/virt.h
94
@@ -XXX,XX +XXX,XX @@ struct VirtMachineState {
95
DeviceState *gic;
96
DeviceState *acpi_dev;
97
Notifier powerdown_notifier;
98
+ Notifier generic_error_notifier;
99
PCIBus *bus;
100
char *oem_id;
101
char *oem_table_id;
102
--
103
2.48.1
diff view generated by jsdifflib
New patch
1
--- a/DSDT.dsl 2025-01-28 09:38:15.155347858 +0100
2
+++ b/DSDT.dsl 2025-01-28 09:39:01.684836954 +0100
3
@@ -XXX,XX +XXX,XX @@
4
*
5
* Original Table Header:
6
* Signature "DSDT"
7
- * Length 0x00001516 (5398)
8
+ * Length 0x00001542 (5442)
9
* Revision 0x02
10
- * Checksum 0x0F
11
+ * Checksum 0xE9
12
* OEM ID "BOCHS "
13
* OEM Table ID "BXPC "
14
* OEM Revision 0x00000001 (1)
15
@@ -XXX,XX +XXX,XX @@
16
{
17
Notify (PWRB, 0x80) // Status Change
18
}
19
+
20
+ If (((Local0 & 0x10) == 0x10))
21
+ {
22
+ Notify (GEDD, 0x80) // Status Change
23
+ }
24
}
25
}
1
26
27
@@ -XXX,XX +XXX,XX @@
28
Name (_HID, "PNP0C0C" /* Power Button Device */) // _HID: Hardware ID
29
Name (_UID, Zero) // _UID: Unique ID
30
}
31
+
32
+ Device (GEDD)
33
+ {
34
+ Name (_HID, "PNP0C33" /* Error Device */) // _HID: Hardware ID
35
+ Name (_UID, Zero) // _UID: Unique ID
36
+ }
37
}
38
}
39
40
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
41
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
42
---
43
tests/data/acpi/aarch64/virt/DSDT | Bin 5196 -> 5240 bytes
44
.../data/acpi/aarch64/virt/DSDT.acpihmatvirt | Bin 5282 -> 5326 bytes
45
tests/data/acpi/aarch64/virt/DSDT.memhp | Bin 6557 -> 6601 bytes
46
tests/data/acpi/aarch64/virt/DSDT.pxb | Bin 7679 -> 7723 bytes
47
tests/data/acpi/aarch64/virt/DSDT.topology | Bin 5398 -> 5442 bytes
48
tests/qtest/bios-tables-test-allowed-diff.h | 1 -
49
6 files changed, 1 deletion(-)
50
51
diff --git a/tests/data/acpi/aarch64/virt/DSDT b/tests/data/acpi/aarch64/virt/DSDT
52
index XXXXXXX..XXXXXXX 100644
53
GIT binary patch
54
delta 109
55
zcmX@3@k4{lCD<jTLWF^ViDe>}G*h$dM)euOOwJsW4+;nC=*7E+g>V+Q2D|zsED)Gn
56
zoxsJ!z{S)S5FX^j)c_F?VBivHb9Z%dnXE4&D;?b=31V}^dw9C=2KWUSI2#)?aKwjt
57
Hx-b9$X;vI^
58
59
delta 64
60
zcmeyNaYlp7CD<jzM}&caNqQoeG*i3NM)euOOit{R4+;lM%f`Egg>V+Q2D|zsED)Gn
61
UoxsJ!z{S)S5FX?-*+E1W06%jPR{#J2
62
63
diff --git a/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt b/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt
64
index XXXXXXX..XXXXXXX 100644
65
GIT binary patch
66
delta 110
67
zcmZ3ac}|ndCD<k8oCpI0)4_>c(oCIR8`a+lGdXii78eO-)SH|wBICY5U~+W=mjDBo
68
yK%2X(iwjpnbdzL2c#soEyoaX?Z-8HbfwO@#14n$Qrwc=LlO#wDl9aJAR0;r(tsHj%
69
70
delta 66
71
zcmX@7xk!`CCD<iokq83=(~XH-(oDVX8`a+lGdZzO78eO-l%1R{A|oB$BpDDM<irv0
72
W;pxH~;1^)vY~akm5g+R5!T<noi4jWx
73
74
diff --git a/tests/data/acpi/aarch64/virt/DSDT.memhp b/tests/data/acpi/aarch64/virt/DSDT.memhp
75
index XXXXXXX..XXXXXXX 100644
76
GIT binary patch
77
delta 88
78
zcmbPheA1Z9CD<k8q$C3algUIbX{MH08`WnBGdXcjJ}4Z_<jXo)OvH<SfxzVI1TFyv
79
qE`c_8R~MJfaU%At($P(lAPz^oho=i~fM0-tv#~J)M|`NK3j+W#;TF9B
80
81
delta 44
82
zcmX?UJlB}ZCD<iot|S8klg&gfX{L_p8`WnBGdXfiJ}4Z_<ij#qOvGz*p@=Oj039?8
83
AE&u=k
84
85
diff --git a/tests/data/acpi/aarch64/virt/DSDT.pxb b/tests/data/acpi/aarch64/virt/DSDT.pxb
86
index XXXXXXX..XXXXXXX 100644
87
GIT binary patch
88
delta 110
89
zcmexwz1oJ$CD<iITaJN&sbC_PG*jDyjq2XAOwJsWOJsu?^(LQ?m2qDnFu6K`OMrn(
90
ypv~RY#f7UOx=Au1JjjV7-ow*{H^48zz}di=fg?WD(}f|rNfM+6Ny^w5Dg^+WYaFrw
91
92
delta 66
93
zcmZ2&^WU1wCD<k8zbpd-Q^!OuX{N5b8`ZsKnVi@sm&gV)%1%BZD<d7<BpDDM<irv0
94
W;pxH~;1^)vY~akm5g+R5!T<oNArgiF
95
96
diff --git a/tests/data/acpi/aarch64/virt/DSDT.topology b/tests/data/acpi/aarch64/virt/DSDT.topology
97
index XXXXXXX..XXXXXXX 100644
98
GIT binary patch
99
delta 86
100
zcmbQHbx4cLCD<jzNtA(s>E%Q&X{O%5jp|7vOwJsWyG4Q-^(NmJk>Ot;Fu6K`OMrn(
101
opv~RY#bxqO5n1WzCP@&RBi_T)g*U)2z`)tqn1Lfc)YF9l01l28<p2Nx
102
103
delta 42
104
ycmX@4HBF1lCD<iIOq79viGL!OG*hGhM)f2SCMWjE-6Fw^vXk$N$V}!Dl?DLb(h64q
105
106
diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h
107
index XXXXXXX..XXXXXXX 100644
108
--- a/tests/qtest/bios-tables-test-allowed-diff.h
109
+++ b/tests/qtest/bios-tables-test-allowed-diff.h
110
@@ -1,2 +1 @@
111
/* List of comma-separated changed AML files to ignore */
112
-"tests/data/acpi/aarch64/virt/DSDT",
113
--
114
2.48.1
diff view generated by jsdifflib
New patch
1
1
Creates a QMP command to be used for generic ACPI APEI hardware error
2
injection (HEST) via GHESv2, and add support for it for ARM guests.
3
4
Error injection uses ACPI_HEST_SRC_ID_QMP source ID to be platform
5
independent. This is mapped at arch virt bindings, depending on the
6
types supported by QEMU and by the BIOS. So, on ARM, this is supported
7
via ACPI_GHES_NOTIFY_GPIO notification type.
8
9
This patch is co-authored:
10
- original ghes logic to inject a simple ARM record by Shiju Jose;
11
- generic logic to handle block addresses by Jonathan Cameron;
12
- generic GHESv2 error inject by Mauro Carvalho Chehab;
13
14
Co-authored-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
15
Co-authored-by: Shiju Jose <shiju.jose@huawei.com>
16
Co-authored-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
17
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
18
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
19
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
20
Acked-by: Igor Mammedov <imammedo@redhat.com>
21
Acked-by: Markus Armbruster <armbru@redhat.com>
22
---
23
24
Changes since v9:
25
- ARM source IDs renamed to reflect SYNC/ASYNC;
26
- command name changed to better reflect what it does;
27
- some improvements at JSON documentation;
28
- add a check for QMP source at the notification logic.
29
30
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
31
---
32
MAINTAINERS | 7 +++++++
33
hw/acpi/Kconfig | 5 +++++
34
hw/acpi/ghes.c | 2 +-
35
hw/acpi/ghes_cper.c | 38 ++++++++++++++++++++++++++++++++++++++
36
hw/acpi/ghes_cper_stub.c | 19 +++++++++++++++++++
37
hw/acpi/meson.build | 2 ++
38
hw/arm/virt-acpi-build.c | 1 +
39
hw/arm/virt.c | 7 +++++++
40
include/hw/acpi/ghes.h | 1 +
41
include/hw/arm/virt.h | 1 +
42
qapi/acpi-hest.json | 35 +++++++++++++++++++++++++++++++++++
43
qapi/meson.build | 1 +
44
qapi/qapi-schema.json | 1 +
45
13 files changed, 119 insertions(+), 1 deletion(-)
46
create mode 100644 hw/acpi/ghes_cper.c
47
create mode 100644 hw/acpi/ghes_cper_stub.c
48
create mode 100644 qapi/acpi-hest.json
49
50
diff --git a/MAINTAINERS b/MAINTAINERS
51
index XXXXXXX..XXXXXXX 100644
52
--- a/MAINTAINERS
53
+++ b/MAINTAINERS
54
@@ -XXX,XX +XXX,XX @@ F: hw/acpi/ghes.c
55
F: include/hw/acpi/ghes.h
56
F: docs/specs/acpi_hest_ghes.rst
57
58
+ACPI/HEST/GHES/ARM processor CPER
59
+R: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
60
+S: Maintained
61
+F: hw/arm/ghes_cper.c
62
+F: hw/acpi/ghes_cper_stub.c
63
+F: qapi/acpi-hest.json
64
+
65
ppc4xx
66
L: qemu-ppc@nongnu.org
67
S: Orphan
68
diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
69
index XXXXXXX..XXXXXXX 100644
70
--- a/hw/acpi/Kconfig
71
+++ b/hw/acpi/Kconfig
72
@@ -XXX,XX +XXX,XX @@ config ACPI_APEI
73
bool
74
depends on ACPI
75
76
+config GHES_CPER
77
+ bool
78
+ depends on ACPI_APEI
79
+ default y
80
+
81
config ACPI_PCI
82
bool
83
depends on ACPI && PCI
84
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
85
index XXXXXXX..XXXXXXX 100644
86
--- a/hw/acpi/ghes.c
87
+++ b/hw/acpi/ghes.c
88
@@ -XXX,XX +XXX,XX @@ void ghes_record_cper_errors(AcpiGhesState *ags, const void *cper, size_t len,
89
/* Write the generic error data entry into guest memory */
90
cpu_physical_memory_write(cper_addr, cper, len);
91
92
- notifier_list_notify(&acpi_generic_error_notifiers, NULL);
93
+ notifier_list_notify(&acpi_generic_error_notifiers, &source_id);
94
}
95
96
int acpi_ghes_memory_errors(AcpiGhesState *ags, uint16_t source_id,
97
diff --git a/hw/acpi/ghes_cper.c b/hw/acpi/ghes_cper.c
98
new file mode 100644
99
index XXXXXXX..XXXXXXX
100
--- /dev/null
101
+++ b/hw/acpi/ghes_cper.c
102
@@ -XXX,XX +XXX,XX @@
103
+/*
104
+ * CPER payload parser for error injection
105
+ *
106
+ * Copyright(C) 2024-2025 Huawei LTD.
107
+ *
108
+ * This code is licensed under the GPL version 2 or later. See the
109
+ * COPYING file in the top-level directory.
110
+ *
111
+ */
112
+
113
+#include "qemu/osdep.h"
114
+
115
+#include "qemu/base64.h"
116
+#include "qemu/error-report.h"
117
+#include "qemu/uuid.h"
118
+#include "qapi/qapi-commands-acpi-hest.h"
119
+#include "hw/acpi/ghes.h"
120
+
121
+void qmp_inject_ghes_v2_error(const char *qmp_cper, Error **errp)
122
+{
123
+ AcpiGhesState *ags;
124
+
125
+ ags = acpi_ghes_get_state();
126
+ if (!ags) {
127
+ return;
128
+ }
129
+
130
+ uint8_t *cper;
131
+ size_t len;
132
+
133
+ cper = qbase64_decode(qmp_cper, -1, &len, errp);
134
+ if (!cper) {
135
+ error_setg(errp, "missing GHES CPER payload");
136
+ return;
137
+ }
138
+
139
+ ghes_record_cper_errors(ags, cper, len, ACPI_HEST_SRC_ID_QMP, errp);
140
+}
141
diff --git a/hw/acpi/ghes_cper_stub.c b/hw/acpi/ghes_cper_stub.c
142
new file mode 100644
143
index XXXXXXX..XXXXXXX
144
--- /dev/null
145
+++ b/hw/acpi/ghes_cper_stub.c
146
@@ -XXX,XX +XXX,XX @@
147
+/*
148
+ * Stub interface for CPER payload parser for error injection
149
+ *
150
+ * Copyright(C) 2024-2025 Huawei LTD.
151
+ *
152
+ * This code is licensed under the GPL version 2 or later. See the
153
+ * COPYING file in the top-level directory.
154
+ *
155
+ */
156
+
157
+#include "qemu/osdep.h"
158
+#include "qapi/error.h"
159
+#include "qapi/qapi-commands-acpi-hest.h"
160
+#include "hw/acpi/ghes.h"
161
+
162
+void qmp_inject_ghes_v2_error(const char *cper, Error **errp)
163
+{
164
+ error_setg(errp, "GHES QMP error inject is not compiled in");
165
+}
166
diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build
167
index XXXXXXX..XXXXXXX 100644
168
--- a/hw/acpi/meson.build
169
+++ b/hw/acpi/meson.build
170
@@ -XXX,XX +XXX,XX @@ endif
171
system_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c', 'acpi_interface.c'))
172
system_ss.add(when: 'CONFIG_ACPI_PCI_BRIDGE', if_false: files('pci-bridge-stub.c'))
173
system_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss)
174
+system_ss.add(when: 'CONFIG_GHES_CPER', if_true: files('ghes_cper.c'))
175
+system_ss.add(when: 'CONFIG_GHES_CPER', if_false: files('ghes_cper_stub.c'))
176
system_ss.add(files('acpi-qmp-cmds.c'))
177
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
178
index XXXXXXX..XXXXXXX 100644
179
--- a/hw/arm/virt-acpi-build.c
180
+++ b/hw/arm/virt-acpi-build.c
181
@@ -XXX,XX +XXX,XX @@ static void acpi_align_size(GArray *blob, unsigned align)
182
183
static const AcpiNotificationSourceId hest_ghes_notify[] = {
184
{ ACPI_HEST_SRC_ID_SYNC, ACPI_GHES_NOTIFY_SEA },
185
+ { ACPI_HEST_SRC_ID_QMP, ACPI_GHES_NOTIFY_GPIO },
186
};
187
188
static const AcpiNotificationSourceId hest_ghes_notify_9_2[] = {
189
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
190
index XXXXXXX..XXXXXXX 100644
191
--- a/hw/arm/virt.c
192
+++ b/hw/arm/virt.c
193
@@ -XXX,XX +XXX,XX @@ static void virt_powerdown_req(Notifier *n, void *opaque)
194
195
static void virt_generic_error_req(Notifier *n, void *opaque)
196
{
197
+ uint16_t *source_id = opaque;
198
+
199
+ /* Currently, only QMP source ID is async */
200
+ if (*source_id != ACPI_HEST_SRC_ID_QMP) {
201
+ return;
202
+ }
203
+
204
VirtMachineState *s = container_of(n, VirtMachineState, generic_error_notifier);
205
206
acpi_send_event(s->acpi_dev, ACPI_GENERIC_ERROR);
207
diff --git a/include/hw/acpi/ghes.h b/include/hw/acpi/ghes.h
208
index XXXXXXX..XXXXXXX 100644
209
--- a/include/hw/acpi/ghes.h
210
+++ b/include/hw/acpi/ghes.h
211
@@ -XXX,XX +XXX,XX @@ enum AcpiGhesNotifyType {
212
*/
213
enum AcpiGhesSourceID {
214
ACPI_HEST_SRC_ID_SYNC,
215
+ ACPI_HEST_SRC_ID_QMP, /* Use it only for QMP injected errors */
216
};
217
218
typedef struct AcpiNotificationSourceId {
219
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
220
index XXXXXXX..XXXXXXX 100644
221
--- a/include/hw/arm/virt.h
222
+++ b/include/hw/arm/virt.h
223
@@ -XXX,XX +XXX,XX @@
224
#include "exec/hwaddr.h"
225
#include "qemu/notify.h"
226
#include "hw/boards.h"
227
+#include "hw/acpi/ghes.h"
228
#include "hw/arm/boot.h"
229
#include "hw/arm/bsa.h"
230
#include "hw/block/flash.h"
231
diff --git a/qapi/acpi-hest.json b/qapi/acpi-hest.json
232
new file mode 100644
233
index XXXXXXX..XXXXXXX
234
--- /dev/null
235
+++ b/qapi/acpi-hest.json
236
@@ -XXX,XX +XXX,XX @@
237
+# -*- Mode: Python -*-
238
+# vim: filetype=python
239
+
240
+##
241
+# == GHESv2 CPER Error Injection
242
+#
243
+# Defined since ACPI Specification 6.1,
244
+# section 18.3.2.8 Generic Hardware Error Source version 2. See:
245
+#
246
+# https://uefi.org/sites/default/files/resources/ACPI_6_1.pdf
247
+##
248
+
249
+
250
+##
251
+# @inject-ghes-v2-error:
252
+#
253
+# Inject an error with additional ACPI 6.1 GHESv2 error information
254
+#
255
+# @cper: contains a base64 encoded string with raw data for a single
256
+# CPER record with Generic Error Status Block, Generic Error Data
257
+# Entry and generic error data payload, as described at
258
+# https://uefi.org/specs/UEFI/2.10/Apx_N_Common_Platform_Error_Record.html#format
259
+#
260
+# Features:
261
+#
262
+# @unstable: This command is experimental.
263
+#
264
+# Since: 10.0
265
+##
266
+{ 'command': 'inject-ghes-v2-error',
267
+ 'data': {
268
+ 'cper': 'str'
269
+ },
270
+ 'features': [ 'unstable' ]
271
+}
272
diff --git a/qapi/meson.build b/qapi/meson.build
273
index XXXXXXX..XXXXXXX 100644
274
--- a/qapi/meson.build
275
+++ b/qapi/meson.build
276
@@ -XXX,XX +XXX,XX @@ qapi_all_modules = [
277
if have_system
278
qapi_all_modules += [
279
'acpi',
280
+ 'acpi-hest',
281
'audio',
282
'cryptodev',
283
'qdev',
284
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
285
index XXXXXXX..XXXXXXX 100644
286
--- a/qapi/qapi-schema.json
287
+++ b/qapi/qapi-schema.json
288
@@ -XXX,XX +XXX,XX @@
289
{ 'include': 'misc-target.json' }
290
{ 'include': 'audio.json' }
291
{ 'include': 'acpi.json' }
292
+{ 'include': 'acpi-hest.json' }
293
{ 'include': 'pci.json' }
294
{ 'include': 'stats.json' }
295
{ 'include': 'virtio.json' }
296
--
297
2.48.1
diff view generated by jsdifflib
New patch
1
Using the QMP GHESv2 API requires preparing a raw data array
2
containing a CPER record.
1
3
4
Add a helper script with subcommands to prepare such data.
5
6
Currently, only ARM Processor error CPER record is supported, by
7
using:
8
    $ ghes_inject.py arm
9
10
which produces those warnings on Linux:
11
12
[ 705.032426] [Firmware Warn]: GHES: Unhandled processor error type 0x02: cache error
13
[ 774.866308] {4}[Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 1
14
[ 774.866583] {4}[Hardware Error]: event severity: recoverable
15
[ 774.866738] {4}[Hardware Error]: Error 0, type: recoverable
16
[ 774.866889] {4}[Hardware Error]: section_type: ARM processor error
17
[ 774.867048] {4}[Hardware Error]: MIDR: 0x00000000000f0510
18
[ 774.867189] {4}[Hardware Error]: running state: 0x0
19
[ 774.867321] {4}[Hardware Error]: Power State Coordination Interface state: 0
20
[ 774.867511] {4}[Hardware Error]: Error info structure 0:
21
[ 774.867679] {4}[Hardware Error]: num errors: 2
22
[ 774.867801] {4}[Hardware Error]: error_type: 0x02: cache error
23
[ 774.867962] {4}[Hardware Error]: error_info: 0x000000000091000f
24
[ 774.868124] {4}[Hardware Error]: transaction type: Data Access
25
[ 774.868280] {4}[Hardware Error]: cache error, operation type: Data write
26
[ 774.868465] {4}[Hardware Error]: cache level: 2
27
[ 774.868592] {4}[Hardware Error]: processor context not corrupted
28
[ 774.868774] [Firmware Warn]: GHES: Unhandled processor error type 0x02: cache error
29
30
Such script allows customizing the error data, allowing to change
31
all fields at the record. Please use:
32
33
    $ ghes_inject.py arm -h
34
35
For more details about its usage.
36
37
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
38
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
39
---
40
MAINTAINERS | 3 +
41
scripts/arm_processor_error.py | 476 ++++++++++++++++++++++
42
scripts/ghes_inject.py | 51 +++
43
scripts/qmp_helper.py | 702 +++++++++++++++++++++++++++++++++
44
4 files changed, 1232 insertions(+)
45
create mode 100644 scripts/arm_processor_error.py
46
create mode 100755 scripts/ghes_inject.py
47
create mode 100755 scripts/qmp_helper.py
48
49
diff --git a/MAINTAINERS b/MAINTAINERS
50
index XXXXXXX..XXXXXXX 100644
51
--- a/MAINTAINERS
52
+++ b/MAINTAINERS
53
@@ -XXX,XX +XXX,XX @@ S: Maintained
54
F: hw/arm/ghes_cper.c
55
F: hw/acpi/ghes_cper_stub.c
56
F: qapi/acpi-hest.json
57
+F: scripts/ghes_inject.py
58
+F: scripts/arm_processor_error.py
59
+F: scripts/qmp_helper.py
60
61
ppc4xx
62
L: qemu-ppc@nongnu.org
63
diff --git a/scripts/arm_processor_error.py b/scripts/arm_processor_error.py
64
new file mode 100644
65
index XXXXXXX..XXXXXXX
66
--- /dev/null
67
+++ b/scripts/arm_processor_error.py
68
@@ -XXX,XX +XXX,XX @@
69
+#!/usr/bin/env python3
70
+#
71
+# pylint: disable=C0301,C0114,R0903,R0912,R0913,R0914,R0915,W0511
72
+# SPDX-License-Identifier: GPL-2.0-or-later
73
+#
74
+# Copyright (C) 2024-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
75
+
76
+# TODO: current implementation has dummy defaults.
77
+#
78
+# For a better implementation, a QMP addition/call is needed to
79
+# retrieve some data for ARM Processor Error injection:
80
+#
81
+# - ARM registers: power_state, mpidr.
82
+
83
+"""
84
+Generates an ARM processor error CPER, compatible with
85
+UEFI 2.9A Errata.
86
+
87
+Injecting such errors can be done using:
88
+
89
+ $ ./scripts/ghes_inject.py arm
90
+ Error injected.
91
+
92
+Produces a simple CPER register, as detected on a Linux guest:
93
+
94
+[Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 1
95
+[Hardware Error]: event severity: recoverable
96
+[Hardware Error]: Error 0, type: recoverable
97
+[Hardware Error]: section_type: ARM processor error
98
+[Hardware Error]: MIDR: 0x0000000000000000
99
+[Hardware Error]: running state: 0x0
100
+[Hardware Error]: Power State Coordination Interface state: 0
101
+[Hardware Error]: Error info structure 0:
102
+[Hardware Error]: num errors: 2
103
+[Hardware Error]: error_type: 0x02: cache error
104
+[Hardware Error]: error_info: 0x000000000091000f
105
+[Hardware Error]: transaction type: Data Access
106
+[Hardware Error]: cache error, operation type: Data write
107
+[Hardware Error]: cache level: 2
108
+[Hardware Error]: processor context not corrupted
109
+[Firmware Warn]: GHES: Unhandled processor error type 0x02: cache error
110
+
111
+The ARM Processor Error message can be customized via command line
112
+parameters. For instance:
113
+
114
+ $ ./scripts/ghes_inject.py arm --mpidr 0x444 --running --affinity 1 \
115
+ --error-info 12345678 --vendor 0x13,123,4,5,1 --ctx-array 0,1,2,3,4,5 \
116
+ -t cache tlb bus micro-arch tlb,micro-arch
117
+ Error injected.
118
+
119
+Injects this error, as detected on a Linux guest:
120
+
121
+[Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 1
122
+[Hardware Error]: event severity: recoverable
123
+[Hardware Error]: Error 0, type: recoverable
124
+[Hardware Error]: section_type: ARM processor error
125
+[Hardware Error]: MIDR: 0x0000000000000000
126
+[Hardware Error]: Multiprocessor Affinity Register (MPIDR): 0x0000000000000000
127
+[Hardware Error]: error affinity level: 0
128
+[Hardware Error]: running state: 0x1
129
+[Hardware Error]: Power State Coordination Interface state: 0
130
+[Hardware Error]: Error info structure 0:
131
+[Hardware Error]: num errors: 2
132
+[Hardware Error]: error_type: 0x02: cache error
133
+[Hardware Error]: error_info: 0x0000000000bc614e
134
+[Hardware Error]: cache level: 2
135
+[Hardware Error]: processor context not corrupted
136
+[Hardware Error]: Error info structure 1:
137
+[Hardware Error]: num errors: 2
138
+[Hardware Error]: error_type: 0x04: TLB error
139
+[Hardware Error]: error_info: 0x000000000054007f
140
+[Hardware Error]: transaction type: Instruction
141
+[Hardware Error]: TLB error, operation type: Instruction fetch
142
+[Hardware Error]: TLB level: 1
143
+[Hardware Error]: processor context not corrupted
144
+[Hardware Error]: the error has not been corrected
145
+[Hardware Error]: PC is imprecise
146
+[Hardware Error]: Error info structure 2:
147
+[Hardware Error]: num errors: 2
148
+[Hardware Error]: error_type: 0x08: bus error
149
+[Hardware Error]: error_info: 0x00000080d6460fff
150
+[Hardware Error]: transaction type: Generic
151
+[Hardware Error]: bus error, operation type: Generic read (type of instruction or data request cannot be determined)
152
+[Hardware Error]: affinity level at which the bus error occurred: 1
153
+[Hardware Error]: processor context corrupted
154
+[Hardware Error]: the error has been corrected
155
+[Hardware Error]: PC is imprecise
156
+[Hardware Error]: Program execution can be restarted reliably at the PC associated with the error.
157
+[Hardware Error]: participation type: Local processor observed
158
+[Hardware Error]: request timed out
159
+[Hardware Error]: address space: External Memory Access
160
+[Hardware Error]: memory access attributes:0x20
161
+[Hardware Error]: access mode: secure
162
+[Hardware Error]: Error info structure 3:
163
+[Hardware Error]: num errors: 2
164
+[Hardware Error]: error_type: 0x10: micro-architectural error
165
+[Hardware Error]: error_info: 0x0000000078da03ff
166
+[Hardware Error]: Error info structure 4:
167
+[Hardware Error]: num errors: 2
168
+[Hardware Error]: error_type: 0x14: TLB error|micro-architectural error
169
+[Hardware Error]: Context info structure 0:
170
+[Hardware Error]: register context type: AArch64 EL1 context registers
171
+[Hardware Error]: 00000000: 00000000 00000000
172
+[Hardware Error]: Vendor specific error info has 5 bytes:
173
+[Hardware Error]: 00000000: 13 7b 04 05 01 .{...
174
+[Firmware Warn]: GHES: Unhandled processor error type 0x02: cache error
175
+[Firmware Warn]: GHES: Unhandled processor error type 0x04: TLB error
176
+[Firmware Warn]: GHES: Unhandled processor error type 0x08: bus error
177
+[Firmware Warn]: GHES: Unhandled processor error type 0x10: micro-architectural error
178
+[Firmware Warn]: GHES: Unhandled processor error type 0x14: TLB error|micro-architectural error
179
+"""
180
+
181
+import argparse
182
+import re
183
+
184
+from qmp_helper import qmp, util, cper_guid
185
+
186
+
187
+class ArmProcessorEinj:
188
+ """
189
+ Implements ARM Processor Error injection via GHES
190
+ """
191
+
192
+ DESC = """
193
+ Generates an ARM processor error CPER, compatible with
194
+ UEFI 2.9A Errata.
195
+ """
196
+
197
+ ACPI_GHES_ARM_CPER_LENGTH = 40
198
+ ACPI_GHES_ARM_CPER_PEI_LENGTH = 32
199
+
200
+ # Context types
201
+ CONTEXT_AARCH32_EL1 = 1
202
+ CONTEXT_AARCH64_EL1 = 5
203
+ CONTEXT_MISC_REG = 8
204
+
205
+ def __init__(self, subparsers):
206
+ """Initialize the error injection class and add subparser"""
207
+
208
+ # Valid choice values
209
+ self.arm_valid_bits = {
210
+ "mpidr": util.bit(0),
211
+ "affinity": util.bit(1),
212
+ "running": util.bit(2),
213
+ "vendor": util.bit(3),
214
+ }
215
+
216
+ self.pei_flags = {
217
+ "first": util.bit(0),
218
+ "last": util.bit(1),
219
+ "propagated": util.bit(2),
220
+ "overflow": util.bit(3),
221
+ }
222
+
223
+ self.pei_error_types = {
224
+ "cache": util.bit(1),
225
+ "tlb": util.bit(2),
226
+ "bus": util.bit(3),
227
+ "micro-arch": util.bit(4),
228
+ }
229
+
230
+ self.pei_valid_bits = {
231
+ "multiple-error": util.bit(0),
232
+ "flags": util.bit(1),
233
+ "error-info": util.bit(2),
234
+ "virt-addr": util.bit(3),
235
+ "phy-addr": util.bit(4),
236
+ }
237
+
238
+ self.data = bytearray()
239
+
240
+ parser = subparsers.add_parser("arm", description=self.DESC)
241
+
242
+ arm_valid_bits = ",".join(self.arm_valid_bits.keys())
243
+ flags = ",".join(self.pei_flags.keys())
244
+ error_types = ",".join(self.pei_error_types.keys())
245
+ pei_valid_bits = ",".join(self.pei_valid_bits.keys())
246
+
247
+ # UEFI N.16 ARM Validation bits
248
+ g_arm = parser.add_argument_group("ARM processor")
249
+ g_arm.add_argument("--arm", "--arm-valid",
250
+ help=f"ARM valid bits: {arm_valid_bits}")
251
+ g_arm.add_argument("-a", "--affinity", "--level", "--affinity-level",
252
+ type=lambda x: int(x, 0),
253
+ help="Affinity level (when multiple levels apply)")
254
+ g_arm.add_argument("-l", "--mpidr", type=lambda x: int(x, 0),
255
+ help="Multiprocessor Affinity Register")
256
+ g_arm.add_argument("-i", "--midr", type=lambda x: int(x, 0),
257
+ help="Main ID Register")
258
+ g_arm.add_argument("-r", "--running",
259
+ action=argparse.BooleanOptionalAction,
260
+ default=None,
261
+ help="Indicates if the processor is running or not")
262
+ g_arm.add_argument("--psci", "--psci-state",
263
+ type=lambda x: int(x, 0),
264
+ help="Power State Coordination Interface - PSCI state")
265
+
266
+ # TODO: Add vendor-specific support
267
+
268
+ # UEFI N.17 bitmaps (type and flags)
269
+ g_pei = parser.add_argument_group("ARM Processor Error Info (PEI)")
270
+ g_pei.add_argument("-t", "--type", nargs="+",
271
+ help=f"one or more error types: {error_types}")
272
+ g_pei.add_argument("-f", "--flags", nargs="*",
273
+ help=f"zero or more error flags: {flags}")
274
+ g_pei.add_argument("-V", "--pei-valid", "--error-valid", nargs="*",
275
+ help=f"zero or more PEI valid bits: {pei_valid_bits}")
276
+
277
+ # UEFI N.17 Integer values
278
+ g_pei.add_argument("-m", "--multiple-error", nargs="+",
279
+ help="Number of errors: 0: Single error, 1: Multiple errors, 2-65535: Error count if known")
280
+ g_pei.add_argument("-e", "--error-info", nargs="+",
281
+ help="Error information (UEFI 2.10 tables N.18 to N.20)")
282
+ g_pei.add_argument("-p", "--physical-address", nargs="+",
283
+ help="Physical address")
284
+ g_pei.add_argument("-v", "--virtual-address", nargs="+",
285
+ help="Virtual address")
286
+
287
+ # UEFI N.21 Context
288
+ g_ctx = parser.add_argument_group("Processor Context")
289
+ g_ctx.add_argument("--ctx-type", "--context-type", nargs="*",
290
+ help="Type of the context (0=ARM32 GPR, 5=ARM64 EL1, other values supported)")
291
+ g_ctx.add_argument("--ctx-size", "--context-size", nargs="*",
292
+ help="Minimal size of the context")
293
+ g_ctx.add_argument("--ctx-array", "--context-array", nargs="*",
294
+ help="Comma-separated arrays for each context")
295
+
296
+ # Vendor-specific data
297
+ g_vendor = parser.add_argument_group("Vendor-specific data")
298
+ g_vendor.add_argument("--vendor", "--vendor-specific", nargs="+",
299
+ help="Vendor-specific byte arrays of data")
300
+
301
+ # Add arguments for Generic Error Data
302
+ qmp.argparse(parser)
303
+
304
+ parser.set_defaults(func=self.send_cper)
305
+
306
+ def send_cper(self, args):
307
+ """Parse subcommand arguments and send a CPER via QMP"""
308
+
309
+ qmp_cmd = qmp(args.host, args.port, args.debug)
310
+
311
+ # Handle Generic Error Data arguments if any
312
+ qmp_cmd.set_args(args)
313
+
314
+ is_cpu_type = re.compile(r"^([\w+]+\-)?arm\-cpu$")
315
+ cpus = qmp_cmd.search_qom("/machine/unattached/device",
316
+ "type", is_cpu_type)
317
+
318
+ cper = {}
319
+ pei = {}
320
+ ctx = {}
321
+ vendor = {}
322
+
323
+ arg = vars(args)
324
+
325
+ # Handle global parameters
326
+ if args.arm:
327
+ arm_valid_init = False
328
+ cper["valid"] = util.get_choice(name="valid",
329
+ value=args.arm,
330
+ choices=self.arm_valid_bits,
331
+ suffixes=["-error", "-err"])
332
+ else:
333
+ cper["valid"] = 0
334
+ arm_valid_init = True
335
+
336
+ if "running" in arg:
337
+ if args.running:
338
+ cper["running-state"] = util.bit(0)
339
+ else:
340
+ cper["running-state"] = 0
341
+ else:
342
+ cper["running-state"] = 0
343
+
344
+ if arm_valid_init:
345
+ if args.affinity:
346
+ cper["valid"] |= self.arm_valid_bits["affinity"]
347
+
348
+ if args.mpidr:
349
+ cper["valid"] |= self.arm_valid_bits["mpidr"]
350
+
351
+ if "running-state" in cper:
352
+ cper["valid"] |= self.arm_valid_bits["running"]
353
+
354
+ if args.psci:
355
+ cper["valid"] |= self.arm_valid_bits["running"]
356
+
357
+ # Handle PEI
358
+ if not args.type:
359
+ args.type = ["cache-error"]
360
+
361
+ util.get_mult_choices(
362
+ pei,
363
+ name="valid",
364
+ values=args.pei_valid,
365
+ choices=self.pei_valid_bits,
366
+ suffixes=["-valid", "--addr"],
367
+ )
368
+ util.get_mult_choices(
369
+ pei,
370
+ name="type",
371
+ values=args.type,
372
+ choices=self.pei_error_types,
373
+ suffixes=["-error", "-err"],
374
+ )
375
+ util.get_mult_choices(
376
+ pei,
377
+ name="flags",
378
+ values=args.flags,
379
+ choices=self.pei_flags,
380
+ suffixes=["-error", "-cap"],
381
+ )
382
+ util.get_mult_int(pei, "error-info", args.error_info)
383
+ util.get_mult_int(pei, "multiple-error", args.multiple_error)
384
+ util.get_mult_int(pei, "phy-addr", args.physical_address)
385
+ util.get_mult_int(pei, "virt-addr", args.virtual_address)
386
+
387
+ # Handle context
388
+ util.get_mult_int(ctx, "type", args.ctx_type, allow_zero=True)
389
+ util.get_mult_int(ctx, "minimal-size", args.ctx_size, allow_zero=True)
390
+ util.get_mult_array(ctx, "register", args.ctx_array, allow_zero=True)
391
+
392
+ util.get_mult_array(vendor, "bytes", args.vendor, max_val=255)
393
+
394
+ # Store PEI
395
+ pei_data = bytearray()
396
+ default_flags = self.pei_flags["first"]
397
+ default_flags |= self.pei_flags["last"]
398
+
399
+ error_info_num = 0
400
+
401
+ for i, p in pei.items(): # pylint: disable=W0612
402
+ error_info_num += 1
403
+
404
+ # UEFI 2.10 doesn't define how to encode error information
405
+ # when multiple types are raised. So, provide a default only
406
+ # if a single type is there
407
+ if "error-info" not in p:
408
+ if p["type"] == util.bit(1):
409
+ p["error-info"] = 0x0091000F
410
+ if p["type"] == util.bit(2):
411
+ p["error-info"] = 0x0054007F
412
+ if p["type"] == util.bit(3):
413
+ p["error-info"] = 0x80D6460FFF
414
+ if p["type"] == util.bit(4):
415
+ p["error-info"] = 0x78DA03FF
416
+
417
+ if "valid" not in p:
418
+ p["valid"] = 0
419
+ if "multiple-error" in p:
420
+ p["valid"] |= self.pei_valid_bits["multiple-error"]
421
+
422
+ if "flags" in p:
423
+ p["valid"] |= self.pei_valid_bits["flags"]
424
+
425
+ if "error-info" in p:
426
+ p["valid"] |= self.pei_valid_bits["error-info"]
427
+
428
+ if "phy-addr" in p:
429
+ p["valid"] |= self.pei_valid_bits["phy-addr"]
430
+
431
+ if "virt-addr" in p:
432
+ p["valid"] |= self.pei_valid_bits["virt-addr"]
433
+
434
+ # Version
435
+ util.data_add(pei_data, 0, 1)
436
+
437
+ util.data_add(pei_data,
438
+ self.ACPI_GHES_ARM_CPER_PEI_LENGTH, 1)
439
+
440
+ util.data_add(pei_data, p["valid"], 2)
441
+ util.data_add(pei_data, p["type"], 1)
442
+ util.data_add(pei_data, p.get("multiple-error", 1), 2)
443
+ util.data_add(pei_data, p.get("flags", default_flags), 1)
444
+ util.data_add(pei_data, p.get("error-info", 0), 8)
445
+ util.data_add(pei_data, p.get("virt-addr", 0xDEADBEEF), 8)
446
+ util.data_add(pei_data, p.get("phy-addr", 0xABBA0BAD), 8)
447
+
448
+ # Store Context
449
+ ctx_data = bytearray()
450
+ context_info_num = 0
451
+
452
+ if ctx:
453
+ ret = qmp_cmd.send_cmd("query-target", may_open=True)
454
+
455
+ default_ctx = self.CONTEXT_MISC_REG
456
+
457
+ if "arch" in ret:
458
+ if ret["arch"] == "aarch64":
459
+ default_ctx = self.CONTEXT_AARCH64_EL1
460
+ elif ret["arch"] == "arm":
461
+ default_ctx = self.CONTEXT_AARCH32_EL1
462
+
463
+ for k in sorted(ctx.keys()):
464
+ context_info_num += 1
465
+
466
+ if "type" not in ctx[k]:
467
+ ctx[k]["type"] = default_ctx
468
+
469
+ if "register" not in ctx[k]:
470
+ ctx[k]["register"] = []
471
+
472
+ reg_size = len(ctx[k]["register"])
473
+ size = 0
474
+
475
+ if "minimal-size" in ctx:
476
+ size = ctx[k]["minimal-size"]
477
+
478
+ size = max(size, reg_size)
479
+
480
+ size = (size + 1) % 0xFFFE
481
+
482
+ # Version
483
+ util.data_add(ctx_data, 0, 2)
484
+
485
+ util.data_add(ctx_data, ctx[k]["type"], 2)
486
+
487
+ util.data_add(ctx_data, 8 * size, 4)
488
+
489
+ for r in ctx[k]["register"]:
490
+ util.data_add(ctx_data, r, 8)
491
+
492
+ for i in range(reg_size, size): # pylint: disable=W0612
493
+ util.data_add(ctx_data, 0, 8)
494
+
495
+ # Vendor-specific bytes are not grouped
496
+ vendor_data = bytearray()
497
+ if vendor:
498
+ for k in sorted(vendor.keys()):
499
+ for b in vendor[k]["bytes"]:
500
+ util.data_add(vendor_data, b, 1)
501
+
502
+ # Encode ARM Processor Error
503
+ data = bytearray()
504
+
505
+ util.data_add(data, cper["valid"], 4)
506
+
507
+ util.data_add(data, error_info_num, 2)
508
+ util.data_add(data, context_info_num, 2)
509
+
510
+ # Calculate the length of the CPER data
511
+ cper_length = self.ACPI_GHES_ARM_CPER_LENGTH
512
+ cper_length += len(pei_data)
513
+ cper_length += len(vendor_data)
514
+ cper_length += len(ctx_data)
515
+ util.data_add(data, cper_length, 4)
516
+
517
+ util.data_add(data, arg.get("affinity-level", 0), 1)
518
+
519
+ # Reserved
520
+ util.data_add(data, 0, 3)
521
+
522
+ if "midr-el1" not in arg:
523
+ if cpus:
524
+ cmd_arg = {
525
+ 'path': cpus[0],
526
+ 'property': "midr"
527
+ }
528
+ ret = qmp_cmd.send_cmd("qom-get", cmd_arg, may_open=True)
529
+ if isinstance(ret, int):
530
+ arg["midr-el1"] = ret
531
+
532
+ util.data_add(data, arg.get("mpidr-el1", 0), 8)
533
+ util.data_add(data, arg.get("midr-el1", 0), 8)
534
+ util.data_add(data, cper["running-state"], 4)
535
+ util.data_add(data, arg.get("psci-state", 0), 4)
536
+
537
+ # Add PEI
538
+ data.extend(pei_data)
539
+ data.extend(ctx_data)
540
+ data.extend(vendor_data)
541
+
542
+ self.data = data
543
+
544
+ qmp_cmd.send_cper(cper_guid.CPER_PROC_ARM, self.data)
545
diff --git a/scripts/ghes_inject.py b/scripts/ghes_inject.py
546
new file mode 100755
547
index XXXXXXX..XXXXXXX
548
--- /dev/null
549
+++ b/scripts/ghes_inject.py
550
@@ -XXX,XX +XXX,XX @@
551
+#!/usr/bin/env python3
552
+#
553
+# SPDX-License-Identifier: GPL-2.0-or-later
554
+#
555
+# Copyright (C) 2024-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
556
+
557
+"""
558
+Handle ACPI GHESv2 error injection logic QEMU QMP interface.
559
+"""
560
+
561
+import argparse
562
+import sys
563
+
564
+from arm_processor_error import ArmProcessorEinj
565
+
566
+EINJ_DESC = """
567
+Handle ACPI GHESv2 error injection logic QEMU QMP interface.
568
+
569
+It allows using UEFI BIOS EINJ features to generate GHES records.
570
+
571
+It helps testing CPER and GHES drivers at the guest OS and how
572
+userspace applications at the guest handle them.
573
+"""
574
+
575
+def main():
576
+ """Main program"""
577
+
578
+ # Main parser - handle generic args like QEMU QMP TCP socket options
579
+ parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter,
580
+ usage="%(prog)s [options]",
581
+ description=EINJ_DESC)
582
+
583
+ g_options = parser.add_argument_group("QEMU QMP socket options")
584
+ g_options.add_argument("-H", "--host", default="localhost", type=str,
585
+ help="host name")
586
+ g_options.add_argument("-P", "--port", default=4445, type=int,
587
+ help="TCP port number")
588
+ g_options.add_argument('-d', '--debug', action='store_true')
589
+
590
+ subparsers = parser.add_subparsers()
591
+
592
+ ArmProcessorEinj(subparsers)
593
+
594
+ args = parser.parse_args()
595
+ if "func" in args:
596
+ args.func(args)
597
+ else:
598
+ sys.exit(f"Please specify a valid command for {sys.argv[0]}")
599
+
600
+if __name__ == "__main__":
601
+ main()
602
diff --git a/scripts/qmp_helper.py b/scripts/qmp_helper.py
603
new file mode 100755
604
index XXXXXXX..XXXXXXX
605
--- /dev/null
606
+++ b/scripts/qmp_helper.py
607
@@ -XXX,XX +XXX,XX @@
608
+#!/usr/bin/env python3
609
+#
610
+# pylint: disable=C0103,E0213,E1135,E1136,E1137,R0902,R0903,R0912,R0913,R0917
611
+# SPDX-License-Identifier: GPL-2.0-or-later
612
+#
613
+# Copyright (C) 2024-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
614
+
615
+"""
616
+Helper classes to be used by ghes_inject command classes.
617
+"""
618
+
619
+import json
620
+import sys
621
+
622
+from datetime import datetime
623
+from os import path as os_path
624
+
625
+try:
626
+ qemu_dir = os_path.abspath(os_path.dirname(os_path.dirname(__file__)))
627
+ sys.path.append(os_path.join(qemu_dir, 'python'))
628
+
629
+ from qemu.qmp.legacy import QEMUMonitorProtocol
630
+
631
+except ModuleNotFoundError as exc:
632
+ print(f"Module '{exc.name}' not found.")
633
+ print("Try export PYTHONPATH=top-qemu-dir/python or run from top-qemu-dir")
634
+ sys.exit(1)
635
+
636
+from base64 import b64encode
637
+
638
+class util:
639
+ """
640
+ Ancillary functions to deal with bitmaps, parse arguments,
641
+ generate GUID and encode data on a bytearray buffer.
642
+ """
643
+
644
+ #
645
+ # Helper routines to handle multiple choice arguments
646
+ #
647
+ def get_choice(name, value, choices, suffixes=None, bitmask=True):
648
+ """Produce a list from multiple choice argument"""
649
+
650
+ new_values = 0
651
+
652
+ if not value:
653
+ return new_values
654
+
655
+ for val in value.split(","):
656
+ val = val.lower()
657
+
658
+ if suffixes:
659
+ for suffix in suffixes:
660
+ val = val.removesuffix(suffix)
661
+
662
+ if val not in choices.keys():
663
+ if suffixes:
664
+ for suffix in suffixes:
665
+ if val + suffix in choices.keys():
666
+ val += suffix
667
+ break
668
+
669
+ if val not in choices.keys():
670
+ sys.exit(f"Error on '{name}': choice '{val}' is invalid.")
671
+
672
+ val = choices[val]
673
+
674
+ if bitmask:
675
+ new_values |= val
676
+ else:
677
+ if new_values:
678
+ sys.exit(f"Error on '{name}': only one value is accepted.")
679
+
680
+ new_values = val
681
+
682
+ return new_values
683
+
684
+ def get_array(name, values, max_val=None):
685
+ """Add numbered hashes from integer lists into an array"""
686
+
687
+ array = []
688
+
689
+ for value in values:
690
+ for val in value.split(","):
691
+ try:
692
+ val = int(val, 0)
693
+ except ValueError:
694
+ sys.exit(f"Error on '{name}': {val} is not an integer")
695
+
696
+ if val < 0:
697
+ sys.exit(f"Error on '{name}': {val} is not unsigned")
698
+
699
+ if max_val and val > max_val:
700
+ sys.exit(f"Error on '{name}': {val} is too little")
701
+
702
+ array.append(val)
703
+
704
+ return array
705
+
706
+ def get_mult_array(mult, name, values, allow_zero=False, max_val=None):
707
+ """Add numbered hashes from integer lists"""
708
+
709
+ if not allow_zero:
710
+ if not values:
711
+ return
712
+ else:
713
+ if values is None:
714
+ return
715
+
716
+ if not values:
717
+ i = 0
718
+ if i not in mult:
719
+ mult[i] = {}
720
+
721
+ mult[i][name] = []
722
+ return
723
+
724
+ i = 0
725
+ for value in values:
726
+ for val in value.split(","):
727
+ try:
728
+ val = int(val, 0)
729
+ except ValueError:
730
+ sys.exit(f"Error on '{name}': {val} is not an integer")
731
+
732
+ if val < 0:
733
+ sys.exit(f"Error on '{name}': {val} is not unsigned")
734
+
735
+ if max_val and val > max_val:
736
+ sys.exit(f"Error on '{name}': {val} is too little")
737
+
738
+ if i not in mult:
739
+ mult[i] = {}
740
+
741
+ if name not in mult[i]:
742
+ mult[i][name] = []
743
+
744
+ mult[i][name].append(val)
745
+
746
+ i += 1
747
+
748
+
749
+ def get_mult_choices(mult, name, values, choices,
750
+ suffixes=None, allow_zero=False):
751
+ """Add numbered hashes from multiple choice arguments"""
752
+
753
+ if not allow_zero:
754
+ if not values:
755
+ return
756
+ else:
757
+ if values is None:
758
+ return
759
+
760
+ i = 0
761
+ for val in values:
762
+ new_values = util.get_choice(name, val, choices, suffixes)
763
+
764
+ if i not in mult:
765
+ mult[i] = {}
766
+
767
+ mult[i][name] = new_values
768
+ i += 1
769
+
770
+
771
+ def get_mult_int(mult, name, values, allow_zero=False):
772
+ """Add numbered hashes from integer arguments"""
773
+ if not allow_zero:
774
+ if not values:
775
+ return
776
+ else:
777
+ if values is None:
778
+ return
779
+
780
+ i = 0
781
+ for val in values:
782
+ try:
783
+ val = int(val, 0)
784
+ except ValueError:
785
+ sys.exit(f"Error on '{name}': {val} is not an integer")
786
+
787
+ if val < 0:
788
+ sys.exit(f"Error on '{name}': {val} is not unsigned")
789
+
790
+ if i not in mult:
791
+ mult[i] = {}
792
+
793
+ mult[i][name] = val
794
+ i += 1
795
+
796
+
797
+ #
798
+ # Data encode helper functions
799
+ #
800
+ def bit(b):
801
+ """Simple macro to define a bit on a bitmask"""
802
+ return 1 << b
803
+
804
+
805
+ def data_add(data, value, num_bytes):
806
+ """Adds bytes from value inside a bitarray"""
807
+
808
+ data.extend(value.to_bytes(num_bytes, byteorder="little")) # pylint: disable=E1101
809
+
810
+ def dump_bytearray(name, data):
811
+ """Does an hexdump of a byte array, grouping in bytes"""
812
+
813
+ print(f"{name} ({len(data)} bytes):")
814
+
815
+ for ln_start in range(0, len(data), 16):
816
+ ln_end = min(ln_start + 16, len(data))
817
+ print(f" {ln_start:08x} ", end="")
818
+ for i in range(ln_start, ln_end):
819
+ print(f"{data[i]:02x} ", end="")
820
+ for i in range(ln_end, ln_start + 16):
821
+ print(" ", end="")
822
+ print(" ", end="")
823
+ for i in range(ln_start, ln_end):
824
+ if data[i] >= 32 and data[i] < 127:
825
+ print(chr(data[i]), end="")
826
+ else:
827
+ print(".", end="")
828
+
829
+ print()
830
+ print()
831
+
832
+ def time(string):
833
+ """Handle BCD timestamps used on Generic Error Data Block"""
834
+
835
+ time = None
836
+
837
+ # Formats to be used when parsing time stamps
838
+ formats = [
839
+ "%Y-%m-%d %H:%M:%S",
840
+ ]
841
+
842
+ if string == "now":
843
+ time = datetime.now()
844
+
845
+ if time is None:
846
+ for fmt in formats:
847
+ try:
848
+ time = datetime.strptime(string, fmt)
849
+ break
850
+ except ValueError:
851
+ pass
852
+
853
+ if time is None:
854
+ raise ValueError("Invalid time format")
855
+
856
+ return time
857
+
858
+class guid:
859
+ """
860
+ Simple class to handle GUID fields.
861
+ """
862
+
863
+ def __init__(self, time_low, time_mid, time_high, nodes):
864
+ """Initialize a GUID value"""
865
+
866
+ assert len(nodes) == 8
867
+
868
+ self.time_low = time_low
869
+ self.time_mid = time_mid
870
+ self.time_high = time_high
871
+ self.nodes = nodes
872
+
873
+ @classmethod
874
+ def UUID(cls, guid_str):
875
+ """Initialize a GUID using a string on its standard format"""
876
+
877
+ if len(guid_str) != 36:
878
+ print("Size not 36")
879
+ raise ValueError('Invalid GUID size')
880
+
881
+ # It is easier to parse without separators. So, drop them
882
+ guid_str = guid_str.replace('-', '')
883
+
884
+ if len(guid_str) != 32:
885
+ print("Size not 32", guid_str, len(guid_str))
886
+ raise ValueError('Invalid GUID hex size')
887
+
888
+ time_low = 0
889
+ time_mid = 0
890
+ time_high = 0
891
+ nodes = []
892
+
893
+ for i in reversed(range(16, 32, 2)):
894
+ h = guid_str[i:i + 2]
895
+ value = int(h, 16)
896
+ nodes.insert(0, value)
897
+
898
+ time_high = int(guid_str[12:16], 16)
899
+ time_mid = int(guid_str[8:12], 16)
900
+ time_low = int(guid_str[0:8], 16)
901
+
902
+ return cls(time_low, time_mid, time_high, nodes)
903
+
904
+ def __str__(self):
905
+ """Output a GUID value on its default string representation"""
906
+
907
+ clock = self.nodes[0] << 8 | self.nodes[1]
908
+
909
+ node = 0
910
+ for i in range(2, len(self.nodes)):
911
+ node = node << 8 | self.nodes[i]
912
+
913
+ s = f"{self.time_low:08x}-{self.time_mid:04x}-"
914
+ s += f"{self.time_high:04x}-{clock:04x}-{node:012x}"
915
+ return s
916
+
917
+ def to_bytes(self):
918
+ """Output a GUID value in bytes"""
919
+
920
+ data = bytearray()
921
+
922
+ util.data_add(data, self.time_low, 4)
923
+ util.data_add(data, self.time_mid, 2)
924
+ util.data_add(data, self.time_high, 2)
925
+ data.extend(bytearray(self.nodes))
926
+
927
+ return data
928
+
929
+class qmp:
930
+ """
931
+ Opens a connection and send/receive QMP commands.
932
+ """
933
+
934
+ def send_cmd(self, command, args=None, may_open=False, return_error=True):
935
+ """Send a command to QMP, optinally opening a connection"""
936
+
937
+ if may_open:
938
+ self._connect()
939
+ elif not self.connected:
940
+ return False
941
+
942
+ msg = { 'execute': command }
943
+ if args:
944
+ msg['arguments'] = args
945
+
946
+ try:
947
+ obj = self.qmp_monitor.cmd_obj(msg)
948
+ # Can we use some other exception class here?
949
+ except Exception as e: # pylint: disable=W0718
950
+ print(f"Command: {command}")
951
+ print(f"Failed to inject error: {e}.")
952
+ return None
953
+
954
+ if "return" in obj:
955
+ if isinstance(obj.get("return"), dict):
956
+ if obj["return"]:
957
+ return obj["return"]
958
+ return "OK"
959
+
960
+ return obj["return"]
961
+
962
+ if isinstance(obj.get("error"), dict):
963
+ error = obj["error"]
964
+ if return_error:
965
+ print(f"Command: {msg}")
966
+ print(f'{error["class"]}: {error["desc"]}')
967
+ else:
968
+ print(json.dumps(obj))
969
+
970
+ return None
971
+
972
+ def _close(self):
973
+ """Shutdown and close the socket, if opened"""
974
+ if not self.connected:
975
+ return
976
+
977
+ self.qmp_monitor.close()
978
+ self.connected = False
979
+
980
+ def _connect(self):
981
+ """Connect to a QMP TCP/IP port, if not connected yet"""
982
+
983
+ if self.connected:
984
+ return True
985
+
986
+ try:
987
+ self.qmp_monitor.connect(negotiate=True)
988
+ except ConnectionError:
989
+ sys.exit(f"Can't connect to QMP host {self.host}:{self.port}")
990
+
991
+ self.connected = True
992
+
993
+ return True
994
+
995
+ BLOCK_STATUS_BITS = {
996
+ "uncorrectable": util.bit(0),
997
+ "correctable": util.bit(1),
998
+ "multi-uncorrectable": util.bit(2),
999
+ "multi-correctable": util.bit(3),
1000
+ }
1001
+
1002
+ ERROR_SEVERITY = {
1003
+ "recoverable": 0,
1004
+ "fatal": 1,
1005
+ "corrected": 2,
1006
+ "none": 3,
1007
+ }
1008
+
1009
+ VALIDATION_BITS = {
1010
+ "fru-id": util.bit(0),
1011
+ "fru-text": util.bit(1),
1012
+ "timestamp": util.bit(2),
1013
+ }
1014
+
1015
+ GEDB_FLAGS_BITS = {
1016
+ "recovered": util.bit(0),
1017
+ "prev-error": util.bit(1),
1018
+ "simulated": util.bit(2),
1019
+ }
1020
+
1021
+ GENERIC_DATA_SIZE = 72
1022
+
1023
+ def argparse(parser):
1024
+ """Prepare a parser group to query generic error data"""
1025
+
1026
+ block_status_bits = ",".join(qmp.BLOCK_STATUS_BITS.keys())
1027
+ error_severity_enum = ",".join(qmp.ERROR_SEVERITY.keys())
1028
+ validation_bits = ",".join(qmp.VALIDATION_BITS.keys())
1029
+ gedb_flags_bits = ",".join(qmp.GEDB_FLAGS_BITS.keys())
1030
+
1031
+ g_gen = parser.add_argument_group("Generic Error Data") # pylint: disable=E1101
1032
+ g_gen.add_argument("--block-status",
1033
+ help=f"block status bits: {block_status_bits}")
1034
+ g_gen.add_argument("--raw-data", nargs="+",
1035
+ help="Raw data inside the Error Status Block")
1036
+ g_gen.add_argument("--error-severity", "--severity",
1037
+ help=f"error severity: {error_severity_enum}")
1038
+ g_gen.add_argument("--gen-err-valid-bits",
1039
+ "--generic-error-validation-bits",
1040
+ help=f"validation bits: {validation_bits}")
1041
+ g_gen.add_argument("--fru-id", type=guid.UUID,
1042
+ help="GUID representing a physical device")
1043
+ g_gen.add_argument("--fru-text",
1044
+ help="ASCII string identifying the FRU hardware")
1045
+ g_gen.add_argument("--timestamp", type=util.time,
1046
+ help="Time when the error info was collected")
1047
+ g_gen.add_argument("--precise", "--precise-timestamp",
1048
+ action='store_true',
1049
+ help="Marks the timestamp as precise if --timestamp is used")
1050
+ g_gen.add_argument("--gedb-flags",
1051
+ help=f"General Error Data Block flags: {gedb_flags_bits}")
1052
+
1053
+ def set_args(self, args):
1054
+ """Set the arguments optionally defined via self.argparse()"""
1055
+
1056
+ if args.block_status:
1057
+ self.block_status = util.get_choice(name="block-status",
1058
+ value=args.block_status,
1059
+ choices=self.BLOCK_STATUS_BITS,
1060
+ bitmask=False)
1061
+ if args.raw_data:
1062
+ self.raw_data = util.get_array("raw-data", args.raw_data,
1063
+ max_val=255)
1064
+ print(self.raw_data)
1065
+
1066
+ if args.error_severity:
1067
+ self.error_severity = util.get_choice(name="error-severity",
1068
+ value=args.error_severity,
1069
+ choices=self.ERROR_SEVERITY,
1070
+ bitmask=False)
1071
+
1072
+ if args.fru_id:
1073
+ self.fru_id = args.fru_id.to_bytes()
1074
+ if not args.gen_err_valid_bits:
1075
+ self.validation_bits |= self.VALIDATION_BITS["fru-id"]
1076
+
1077
+ if args.fru_text:
1078
+ text = bytearray(args.fru_text.encode('ascii'))
1079
+ if len(text) > 20:
1080
+ sys.exit("FRU text is too big to fit")
1081
+
1082
+ self.fru_text = text
1083
+ if not args.gen_err_valid_bits:
1084
+ self.validation_bits |= self.VALIDATION_BITS["fru-text"]
1085
+
1086
+ if args.timestamp:
1087
+ time = args.timestamp
1088
+ century = int(time.year / 100)
1089
+
1090
+ bcd = bytearray()
1091
+ util.data_add(bcd, (time.second // 10) << 4 | (time.second % 10), 1)
1092
+ util.data_add(bcd, (time.minute // 10) << 4 | (time.minute % 10), 1)
1093
+ util.data_add(bcd, (time.hour // 10) << 4 | (time.hour % 10), 1)
1094
+
1095
+ if args.precise:
1096
+ util.data_add(bcd, 1, 1)
1097
+ else:
1098
+ util.data_add(bcd, 0, 1)
1099
+
1100
+ util.data_add(bcd, (time.day // 10) << 4 | (time.day % 10), 1)
1101
+ util.data_add(bcd, (time.month // 10) << 4 | (time.month % 10), 1)
1102
+ util.data_add(bcd,
1103
+ ((time.year % 100) // 10) << 4 | (time.year % 10), 1)
1104
+ util.data_add(bcd, ((century % 100) // 10) << 4 | (century % 10), 1)
1105
+
1106
+ self.timestamp = bcd
1107
+ if not args.gen_err_valid_bits:
1108
+ self.validation_bits |= self.VALIDATION_BITS["timestamp"]
1109
+
1110
+ if args.gen_err_valid_bits:
1111
+ self.validation_bits = util.get_choice(name="validation",
1112
+ value=args.gen_err_valid_bits,
1113
+ choices=self.VALIDATION_BITS)
1114
+
1115
+ def __init__(self, host, port, debug=False):
1116
+ """Initialize variables used by the QMP send logic"""
1117
+
1118
+ self.connected = False
1119
+ self.host = host
1120
+ self.port = port
1121
+ self.debug = debug
1122
+
1123
+ # ACPI 6.1: 18.3.2.7.1 Generic Error Data: Generic Error Status Block
1124
+ self.block_status = self.BLOCK_STATUS_BITS["uncorrectable"]
1125
+ self.raw_data = []
1126
+ self.error_severity = self.ERROR_SEVERITY["recoverable"]
1127
+
1128
+ # ACPI 6.1: 18.3.2.7.1 Generic Error Data: Generic Error Data Entry
1129
+ self.validation_bits = 0
1130
+ self.flags = 0
1131
+ self.fru_id = bytearray(16)
1132
+ self.fru_text = bytearray(20)
1133
+ self.timestamp = bytearray(8)
1134
+
1135
+ self.qmp_monitor = QEMUMonitorProtocol(address=(self.host, self.port))
1136
+
1137
+ #
1138
+ # Socket QMP send command
1139
+ #
1140
+ def send_cper_raw(self, cper_data):
1141
+ """Send a raw CPER data to QEMU though QMP TCP socket"""
1142
+
1143
+ data = b64encode(bytes(cper_data)).decode('ascii')
1144
+
1145
+ cmd_arg = {
1146
+ 'cper': data
1147
+ }
1148
+
1149
+ self._connect()
1150
+
1151
+ if self.send_cmd("inject-ghes-v2-error", cmd_arg):
1152
+ print("Error injected.")
1153
+
1154
+ def send_cper(self, notif_type, payload):
1155
+ """Send commands to QEMU though QMP TCP socket"""
1156
+
1157
+ # Fill CPER record header
1158
+
1159
+ # NOTE: bits 4 to 13 of block status contain the number of
1160
+ # data entries in the data section. This is currently unsupported.
1161
+
1162
+ cper_length = len(payload)
1163
+ data_length = cper_length + len(self.raw_data) + self.GENERIC_DATA_SIZE
1164
+
1165
+ # Generic Error Data Entry
1166
+ gede = bytearray()
1167
+
1168
+ gede.extend(notif_type.to_bytes())
1169
+ util.data_add(gede, self.error_severity, 4)
1170
+ util.data_add(gede, 0x300, 2)
1171
+ util.data_add(gede, self.validation_bits, 1)
1172
+ util.data_add(gede, self.flags, 1)
1173
+ util.data_add(gede, cper_length, 4)
1174
+ gede.extend(self.fru_id)
1175
+ gede.extend(self.fru_text)
1176
+ gede.extend(self.timestamp)
1177
+
1178
+ # Generic Error Status Block
1179
+ gebs = bytearray()
1180
+
1181
+ if self.raw_data:
1182
+ raw_data_offset = len(gebs)
1183
+ else:
1184
+ raw_data_offset = 0
1185
+
1186
+ util.data_add(gebs, self.block_status, 4)
1187
+ util.data_add(gebs, raw_data_offset, 4)
1188
+ util.data_add(gebs, len(self.raw_data), 4)
1189
+ util.data_add(gebs, data_length, 4)
1190
+ util.data_add(gebs, self.error_severity, 4)
1191
+
1192
+ cper_data = bytearray()
1193
+ cper_data.extend(gebs)
1194
+ cper_data.extend(gede)
1195
+ cper_data.extend(bytearray(self.raw_data))
1196
+ cper_data.extend(bytearray(payload))
1197
+
1198
+ if self.debug:
1199
+ print(f"GUID: {notif_type}")
1200
+
1201
+ util.dump_bytearray("Generic Error Status Block", gebs)
1202
+ util.dump_bytearray("Generic Error Data Entry", gede)
1203
+
1204
+ if self.raw_data:
1205
+ util.dump_bytearray("Raw data", bytearray(self.raw_data))
1206
+
1207
+ util.dump_bytearray("Payload", payload)
1208
+
1209
+ self.send_cper_raw(cper_data)
1210
+
1211
+
1212
+ def search_qom(self, path, prop, regex):
1213
+ """
1214
+ Return a list of devices that match path array like:
1215
+
1216
+ /machine/unattached/device
1217
+ /machine/peripheral-anon/device
1218
+ ...
1219
+ """
1220
+
1221
+ found = []
1222
+
1223
+ i = 0
1224
+ while 1:
1225
+ dev = f"{path}[{i}]"
1226
+ args = {
1227
+ 'path': dev,
1228
+ 'property': prop
1229
+ }
1230
+ ret = self.send_cmd("qom-get", args, may_open=True, return_error=False)
1231
+ if not ret:
1232
+ break
1233
+
1234
+ if isinstance(ret, str):
1235
+ if regex.search(ret):
1236
+ found.append(dev)
1237
+
1238
+ i += 1
1239
+ if i > 10000:
1240
+ print("Too many objects returned by qom-get!")
1241
+ break
1242
+
1243
+ return found
1244
+
1245
+class cper_guid:
1246
+ """
1247
+ Contains CPER GUID, as per:
1248
+ https://uefi.org/specs/UEFI/2.10/Apx_N_Common_Platform_Error_Record.html
1249
+ """
1250
+
1251
+ CPER_PROC_GENERIC = guid(0x9876CCAD, 0x47B4, 0x4bdb,
1252
+ [0xB6, 0x5E, 0x16, 0xF1,
1253
+ 0x93, 0xC4, 0xF3, 0xDB])
1254
+
1255
+ CPER_PROC_X86 = guid(0xDC3EA0B0, 0xA144, 0x4797,
1256
+ [0xB9, 0x5B, 0x53, 0xFA,
1257
+ 0x24, 0x2B, 0x6E, 0x1D])
1258
+
1259
+ CPER_PROC_ITANIUM = guid(0xe429faf1, 0x3cb7, 0x11d4,
1260
+ [0xbc, 0xa7, 0x00, 0x80,
1261
+ 0xc7, 0x3c, 0x88, 0x81])
1262
+
1263
+ CPER_PROC_ARM = guid(0xE19E3D16, 0xBC11, 0x11E4,
1264
+ [0x9C, 0xAA, 0xC2, 0x05,
1265
+ 0x1D, 0x5D, 0x46, 0xB0])
1266
+
1267
+ CPER_PLATFORM_MEM = guid(0xA5BC1114, 0x6F64, 0x4EDE,
1268
+ [0xB8, 0x63, 0x3E, 0x83,
1269
+ 0xED, 0x7C, 0x83, 0xB1])
1270
+
1271
+ CPER_PLATFORM_MEM2 = guid(0x61EC04FC, 0x48E6, 0xD813,
1272
+ [0x25, 0xC9, 0x8D, 0xAA,
1273
+ 0x44, 0x75, 0x0B, 0x12])
1274
+
1275
+ CPER_PCIE = guid(0xD995E954, 0xBBC1, 0x430F,
1276
+ [0xAD, 0x91, 0xB4, 0x4D,
1277
+ 0xCB, 0x3C, 0x6F, 0x35])
1278
+
1279
+ CPER_PCI_BUS = guid(0xC5753963, 0x3B84, 0x4095,
1280
+ [0xBF, 0x78, 0xED, 0xDA,
1281
+ 0xD3, 0xF9, 0xC9, 0xDD])
1282
+
1283
+ CPER_PCI_DEV = guid(0xEB5E4685, 0xCA66, 0x4769,
1284
+ [0xB6, 0xA2, 0x26, 0x06,
1285
+ 0x8B, 0x00, 0x13, 0x26])
1286
+
1287
+ CPER_FW_ERROR = guid(0x81212A96, 0x09ED, 0x4996,
1288
+ [0x94, 0x71, 0x8D, 0x72,
1289
+ 0x9C, 0x8E, 0x69, 0xED])
1290
+
1291
+ CPER_DMA_GENERIC = guid(0x5B51FEF7, 0xC79D, 0x4434,
1292
+ [0x8F, 0x1B, 0xAA, 0x62,
1293
+ 0xDE, 0x3E, 0x2C, 0x64])
1294
+
1295
+ CPER_DMA_VT = guid(0x71761D37, 0x32B2, 0x45cd,
1296
+ [0xA7, 0xD0, 0xB0, 0xFE,
1297
+ 0xDD, 0x93, 0xE8, 0xCF])
1298
+
1299
+ CPER_DMA_IOMMU = guid(0x036F84E1, 0x7F37, 0x428c,
1300
+ [0xA7, 0x9E, 0x57, 0x5F,
1301
+ 0xDF, 0xAA, 0x84, 0xEC])
1302
+
1303
+ CPER_CCIX_PER = guid(0x91335EF6, 0xEBFB, 0x4478,
1304
+ [0xA6, 0xA6, 0x88, 0xB7,
1305
+ 0x28, 0xCF, 0x75, 0xD7])
1306
+
1307
+ CPER_CXL_PROT_ERR = guid(0x80B9EFB4, 0x52B5, 0x4DE3,
1308
+ [0xA7, 0x77, 0x68, 0x78,
1309
+ 0x4B, 0x77, 0x10, 0x48])
1310
--
1311
2.48.1
diff view generated by jsdifflib