Skip to content

Commit d2932a5

Browse files
aeglrafaeljw
authored andcommitted
ACPI: APEI: EINJ: Fix EINJV2 initialization and injection
ACPI 6.6 specification for EINJV2 appends an extra structure to the end of the existing struct set_error_type_with_address. Several issues showed up in testing. 1) Initialization was broken by an earlier fix [1] since is_v2 is only set while performing an injection, not during initialization. 2) A buggy BIOS provided invalid "revision" and "length" for the extension structure. Add several sanity checks. 3) When injecting legacy error types on an EINJV2 capable system, don't copy the component arrays. Fixes: 6c70585 ("ACPI: APEI: EINJ: Check if user asked for EINJV2 injection") # [1] Fixes: b476102 ("ACPI: APEI: EINJ: Enable EINJv2 error injections") Signed-off-by: Tony Luck <[email protected]> [ rjw: Changelog edits ] Cc: 6.17+ <[email protected]> # 6.17+ Link: https://patch.msgid.link/[email protected] Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 6a23ae0 commit d2932a5

File tree

1 file changed

+41
-23
lines changed

1 file changed

+41
-23
lines changed

drivers/acpi/apei/einj-core.c

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ bool einj_initialized __ro_after_init;
182182

183183
static void __iomem *einj_param;
184184
static u32 v5param_size;
185+
static u32 v66param_size;
185186
static bool is_v2;
186187

187188
static void einj_exec_ctx_init(struct apei_exec_context *ctx)
@@ -283,6 +284,24 @@ static void check_vendor_extension(u64 paddr,
283284
acpi_os_unmap_iomem(p, sizeof(v));
284285
}
285286

287+
static u32 einjv2_init(struct einjv2_extension_struct *e)
288+
{
289+
if (e->revision != 1) {
290+
pr_info("Unknown v2 extension revision %u\n", e->revision);
291+
return 0;
292+
}
293+
if (e->length < sizeof(*e) || e->length > PAGE_SIZE) {
294+
pr_info(FW_BUG "Bad1 v2 extension length %u\n", e->length);
295+
return 0;
296+
}
297+
if ((e->length - sizeof(*e)) % sizeof(e->component_arr[0])) {
298+
pr_info(FW_BUG "Bad2 v2 extension length %u\n", e->length);
299+
return 0;
300+
}
301+
302+
return (e->length - sizeof(*e)) / sizeof(e->component_arr[0]);
303+
}
304+
286305
static void __iomem *einj_get_parameter_address(void)
287306
{
288307
int i;
@@ -310,28 +329,21 @@ static void __iomem *einj_get_parameter_address(void)
310329
v5param_size = sizeof(v5param);
311330
p = acpi_os_map_iomem(pa_v5, sizeof(*p));
312331
if (p) {
313-
int offset, len;
314-
315332
memcpy_fromio(&v5param, p, v5param_size);
316333
acpi5 = 1;
317334
check_vendor_extension(pa_v5, &v5param);
318-
if (is_v2 && available_error_type & ACPI65_EINJV2_SUPP) {
319-
len = v5param.einjv2_struct.length;
320-
offset = offsetof(struct einjv2_extension_struct, component_arr);
321-
max_nr_components = (len - offset) /
322-
sizeof(v5param.einjv2_struct.component_arr[0]);
323-
/*
324-
* The first call to acpi_os_map_iomem above does not include the
325-
* component array, instead it is used to read and calculate maximum
326-
* number of components supported by the system. Below, the mapping
327-
* is expanded to include the component array.
328-
*/
335+
if (available_error_type & ACPI65_EINJV2_SUPP) {
336+
struct einjv2_extension_struct *e;
337+
338+
e = &v5param.einjv2_struct;
339+
max_nr_components = einjv2_init(e);
340+
341+
/* remap including einjv2_extension_struct */
329342
acpi_os_unmap_iomem(p, v5param_size);
330-
offset = offsetof(struct set_error_type_with_address, einjv2_struct);
331-
v5param_size = offset + struct_size(&v5param.einjv2_struct,
332-
component_arr, max_nr_components);
333-
p = acpi_os_map_iomem(pa_v5, v5param_size);
343+
v66param_size = v5param_size - sizeof(*e) + e->length;
344+
p = acpi_os_map_iomem(pa_v5, v66param_size);
334345
}
346+
335347
return p;
336348
}
337349
}
@@ -527,6 +539,7 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
527539
u64 param3, u64 param4)
528540
{
529541
struct apei_exec_context ctx;
542+
u32 param_size = is_v2 ? v66param_size : v5param_size;
530543
u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT;
531544
int i, rc;
532545

@@ -539,11 +552,11 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
539552
if (acpi5) {
540553
struct set_error_type_with_address *v5param;
541554

542-
v5param = kmalloc(v5param_size, GFP_KERNEL);
555+
v5param = kmalloc(param_size, GFP_KERNEL);
543556
if (!v5param)
544557
return -ENOMEM;
545558

546-
memcpy_fromio(v5param, einj_param, v5param_size);
559+
memcpy_fromio(v5param, einj_param, param_size);
547560
v5param->type = type;
548561
if (type & ACPI5_VENDOR_BIT) {
549562
switch (vendor_flags) {
@@ -601,7 +614,7 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
601614
break;
602615
}
603616
}
604-
memcpy_toio(einj_param, v5param, v5param_size);
617+
memcpy_toio(einj_param, v5param, param_size);
605618
kfree(v5param);
606619
} else {
607620
rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE);
@@ -1132,9 +1145,14 @@ static void einj_remove(struct faux_device *fdev)
11321145
struct apei_exec_context ctx;
11331146

11341147
if (einj_param) {
1135-
acpi_size size = (acpi5) ?
1136-
v5param_size :
1137-
sizeof(struct einj_parameter);
1148+
acpi_size size;
1149+
1150+
if (v66param_size)
1151+
size = v66param_size;
1152+
else if (acpi5)
1153+
size = v5param_size;
1154+
else
1155+
size = sizeof(struct einj_parameter);
11381156

11391157
acpi_os_unmap_iomem(einj_param, size);
11401158
if (vendor_errors.size)

0 commit comments

Comments
 (0)