|
@@ -34,6 +34,7 @@
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/mm.h>
|
|
|
#include <acpi/acpi.h>
|
|
|
+#include <asm/unaligned.h>
|
|
|
|
|
|
#include "apei-internal.h"
|
|
|
|
|
@@ -216,7 +217,7 @@ static void check_vendor_extension(u64 paddr,
|
|
|
static void *einj_get_parameter_address(void)
|
|
|
{
|
|
|
int i;
|
|
|
- u64 paddrv4 = 0, paddrv5 = 0;
|
|
|
+ u64 pa_v4 = 0, pa_v5 = 0;
|
|
|
struct acpi_whea_header *entry;
|
|
|
|
|
|
entry = EINJ_TAB_ENTRY(einj_tab);
|
|
@@ -225,30 +226,28 @@ static void *einj_get_parameter_address(void)
|
|
|
entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
|
|
|
entry->register_region.space_id ==
|
|
|
ACPI_ADR_SPACE_SYSTEM_MEMORY)
|
|
|
- memcpy(&paddrv4, &entry->register_region.address,
|
|
|
- sizeof(paddrv4));
|
|
|
+ pa_v4 = get_unaligned(&entry->register_region.address);
|
|
|
if (entry->action == ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS &&
|
|
|
entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
|
|
|
entry->register_region.space_id ==
|
|
|
ACPI_ADR_SPACE_SYSTEM_MEMORY)
|
|
|
- memcpy(&paddrv5, &entry->register_region.address,
|
|
|
- sizeof(paddrv5));
|
|
|
+ pa_v5 = get_unaligned(&entry->register_region.address);
|
|
|
entry++;
|
|
|
}
|
|
|
- if (paddrv5) {
|
|
|
+ if (pa_v5) {
|
|
|
struct set_error_type_with_address *v5param;
|
|
|
|
|
|
- v5param = acpi_os_map_memory(paddrv5, sizeof(*v5param));
|
|
|
+ v5param = acpi_os_map_memory(pa_v5, sizeof(*v5param));
|
|
|
if (v5param) {
|
|
|
acpi5 = 1;
|
|
|
- check_vendor_extension(paddrv5, v5param);
|
|
|
+ check_vendor_extension(pa_v5, v5param);
|
|
|
return v5param;
|
|
|
}
|
|
|
}
|
|
|
- if (param_extension && paddrv4) {
|
|
|
+ if (param_extension && pa_v4) {
|
|
|
struct einj_parameter *v4param;
|
|
|
|
|
|
- v4param = acpi_os_map_memory(paddrv4, sizeof(*v4param));
|
|
|
+ v4param = acpi_os_map_memory(pa_v4, sizeof(*v4param));
|
|
|
if (!v4param)
|
|
|
return NULL;
|
|
|
if (v4param->reserved1 || v4param->reserved2) {
|
|
@@ -416,7 +415,8 @@ out:
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
-static int __einj_error_inject(u32 type, u64 param1, u64 param2)
|
|
|
+static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
|
|
|
+ u64 param3, u64 param4)
|
|
|
{
|
|
|
struct apei_exec_context ctx;
|
|
|
u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT;
|
|
@@ -446,6 +446,12 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
|
|
|
break;
|
|
|
}
|
|
|
v5param->flags = vendor_flags;
|
|
|
+ } else if (flags) {
|
|
|
+ v5param->flags = flags;
|
|
|
+ v5param->memory_address = param1;
|
|
|
+ v5param->memory_address_range = param2;
|
|
|
+ v5param->apicid = param3;
|
|
|
+ v5param->pcie_sbdf = param4;
|
|
|
} else {
|
|
|
switch (type) {
|
|
|
case ACPI_EINJ_PROCESSOR_CORRECTABLE:
|
|
@@ -514,11 +520,17 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
|
|
|
}
|
|
|
|
|
|
/* Inject the specified hardware error */
|
|
|
-static int einj_error_inject(u32 type, u64 param1, u64 param2)
|
|
|
+static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
|
|
|
+ u64 param3, u64 param4)
|
|
|
{
|
|
|
int rc;
|
|
|
unsigned long pfn;
|
|
|
|
|
|
+ /* If user manually set "flags", make sure it is legal */
|
|
|
+ if (flags && (flags &
|
|
|
+ ~(SETWA_FLAGS_APICID|SETWA_FLAGS_MEM|SETWA_FLAGS_PCIE_SBDF)))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
/*
|
|
|
* We need extra sanity checks for memory errors.
|
|
|
* Other types leap directly to injection.
|
|
@@ -532,7 +544,7 @@ static int einj_error_inject(u32 type, u64 param1, u64 param2)
|
|
|
if (type & ACPI5_VENDOR_BIT) {
|
|
|
if (vendor_flags != SETWA_FLAGS_MEM)
|
|
|
goto inject;
|
|
|
- } else if (!(type & MEM_ERROR_MASK))
|
|
|
+ } else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM))
|
|
|
goto inject;
|
|
|
|
|
|
/*
|
|
@@ -546,15 +558,18 @@ static int einj_error_inject(u32 type, u64 param1, u64 param2)
|
|
|
|
|
|
inject:
|
|
|
mutex_lock(&einj_mutex);
|
|
|
- rc = __einj_error_inject(type, param1, param2);
|
|
|
+ rc = __einj_error_inject(type, flags, param1, param2, param3, param4);
|
|
|
mutex_unlock(&einj_mutex);
|
|
|
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
static u32 error_type;
|
|
|
+static u32 error_flags;
|
|
|
static u64 error_param1;
|
|
|
static u64 error_param2;
|
|
|
+static u64 error_param3;
|
|
|
+static u64 error_param4;
|
|
|
static struct dentry *einj_debug_dir;
|
|
|
|
|
|
static int available_error_type_show(struct seq_file *m, void *v)
|
|
@@ -648,7 +663,8 @@ static int error_inject_set(void *data, u64 val)
|
|
|
if (!error_type)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- return einj_error_inject(error_type, error_param1, error_param2);
|
|
|
+ return einj_error_inject(error_type, error_flags, error_param1, error_param2,
|
|
|
+ error_param3, error_param4);
|
|
|
}
|
|
|
|
|
|
DEFINE_SIMPLE_ATTRIBUTE(error_inject_fops, NULL,
|
|
@@ -729,6 +745,10 @@ static int __init einj_init(void)
|
|
|
rc = -ENOMEM;
|
|
|
einj_param = einj_get_parameter_address();
|
|
|
if ((param_extension || acpi5) && einj_param) {
|
|
|
+ fentry = debugfs_create_x32("flags", S_IRUSR | S_IWUSR,
|
|
|
+ einj_debug_dir, &error_flags);
|
|
|
+ if (!fentry)
|
|
|
+ goto err_unmap;
|
|
|
fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR,
|
|
|
einj_debug_dir, &error_param1);
|
|
|
if (!fentry)
|
|
@@ -737,6 +757,14 @@ static int __init einj_init(void)
|
|
|
einj_debug_dir, &error_param2);
|
|
|
if (!fentry)
|
|
|
goto err_unmap;
|
|
|
+ fentry = debugfs_create_x64("param3", S_IRUSR | S_IWUSR,
|
|
|
+ einj_debug_dir, &error_param3);
|
|
|
+ if (!fentry)
|
|
|
+ goto err_unmap;
|
|
|
+ fentry = debugfs_create_x64("param4", S_IRUSR | S_IWUSR,
|
|
|
+ einj_debug_dir, &error_param4);
|
|
|
+ if (!fentry)
|
|
|
+ goto err_unmap;
|
|
|
|
|
|
fentry = debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR,
|
|
|
einj_debug_dir, ¬rigger);
|