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, ¬if_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, ¬if_source[i], i, num_sources); | ||
210 | + build_ghes_v2_entry(table_data, linker, ¬if_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 |