// SPDX-License-Identifier: GPL-2.0-only
/*
* APEI Error INJection support
*
* EINJ provides a hardware error injection mechanism, this is useful
* for debugging and testing of other APEI and RAS features.
*
* For more information about EINJ, please refer to ACPI Specification
* version 4.0, section 17.5.
*
* Copyright 2009-2010 Intel Corp.
* Author: Huang Ying <ying.huang@intel.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/nmi.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/device/faux.h>
#include <linux/unaligned.h>
#include "apei-internal.h"
#undef pr_fmt
#define pr_fmt(fmt) "EINJ: " fmt
#define SLEEP_UNIT_MIN 1000 /* 1ms */
#define SLEEP_UNIT_MAX 5000 /* 5ms */
/* Firmware should respond within 1 seconds */
#define FIRMWARE_TIMEOUT (1 * USEC_PER_SEC)
#define COMPONENT_LEN 16
#define ACPI65_EINJV2_SUPP BIT(30)
#define ACPI5_VENDOR_BIT BIT(31)
#define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \
ACPI_EINJ_MEMORY_UNCORRECTABLE | \
ACPI_EINJ_MEMORY_FATAL)
#define CXL_ERROR_MASK (ACPI_EINJ_CXL_CACHE_CORRECTABLE | \
ACPI_EINJ_CXL_CACHE_UNCORRECTABLE | \
ACPI_EINJ_CXL_CACHE_FATAL | \
ACPI_EINJ_CXL_MEM_CORRECTABLE | \
ACPI_EINJ_CXL_MEM_UNCORRECTABLE | \
ACPI_EINJ_CXL_MEM_FATAL)
/*
* ACPI version 5 provides a SET_ERROR_TYPE_WITH_ADDRESS action.
*/
static int acpi5;
struct syndrome_array {
union {
u8 acpi_id[COMPONENT_LEN];
u8 device_id[COMPONENT_LEN];
u8 pcie_sbdf[COMPONENT_LEN];
u8 vendor_id[COMPONENT_LEN];
} comp_id;
union {
u8 proc_synd[COMPONENT_LEN];
u8 mem_synd[COMPONENT_LEN];
u8 pcie_synd[COMPONENT_LEN];
u8 vendor_synd[COMPONENT_LEN];
} comp_synd;
};
struct einjv2_extension_struct {
u32 length;
u16 revision;
u16 component_arr_count;
struct syndrome_array component_arr[] __counted_by(component_arr_count);
};
struct set_error_type_with_address {
u32 type;
u32 vendor_extension;
u32 flags;
u32 apicid;
u64 memory_address;
u64 memory_address_range;
u32 pcie_sbdf;
struct einjv2_extension_struct einjv2_struct;
};
enum {
SETWA_FLAGS_APICID = 1,
SETWA_FLAGS_MEM = 2,
SETWA_FLAGS_PCIE_SBDF = 4,
SETWA_FLAGS_EINJV2 = 8,
};
/*
* Vendor extensions for platform specific operations
*/
struct vendor_error_type_extension {
u32 length;
u32 pcie_sbdf;
u16 vendor_id;
u16 device_id;
u8 rev_id;
u8 reserved[3];
};
static u32 notrigger;
static u32 vendor_flags;
static struct debugfs_blob_wrapper vendor_blob;
static struct debugfs_blob_wrapper vendor_errors;
static char vendor_dev[64];
static u32 max_nr_components;
static u32 available_error_type;
static u32 available_error_type_v2;
static struct syndrome_array *syndrome_data;
/*
* Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the
* EINJ table through an unpublished extension. Use with caution as
* most will ignore the parameter and make their own choice of address
* for error injection. This extension is used only if
* param_extension module parameter is specified.
*/
struct einj_parameter {
u64 type;
u64 reserved1;
u64 reserved2;
u64 param1;
u64 param2;
};
#define EINJ_OP_BUSY 0x1
#define EINJ_STATUS_SUCCESS 0x0
#define EINJ_STATUS_FAIL 0x1
#define EINJ_STATUS_INVAL 0x2
#define EINJ_TAB_ENTRY(tab) \
((struct acpi_whea_header *)((char *)(tab) + \
sizeof(struct acpi_table_einj)))
static bool param_extension;
module_param(param_extension, bool, 0);
static struct acpi_table_einj *einj_tab;
static struct apei_resources einj_resources;
static struct apei_exec_ins_type einj_ins_type[] = {
[ACPI_EINJ_READ_REGISTER] = {
.flags