summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Nowicki <tomasz.nowicki@linaro.org>2013-09-19 17:51:59 +0200
committerTomasz Nowicki <tomasz.nowicki@linaro.org>2013-09-23 11:42:54 +0200
commit5aebae7f179e41649b2b93996e882ace444a736f (patch)
treee53397cb3553ba73530f7817174077384b40b5d0
parent157bbfd13948abdd860d46a05ec13dc174a9d41f (diff)
bfapei: Expand bfapei tool to EINJ table testing.
bfapei now can create blob for EINJ driver as well. Blob for EINJ driver need to be combined with blob for HEST driver since EINJ is designed to set status flag for Error Status Block pointed from HEST table. This way EINJ driver can be tested in easy way. Note that SCI must be triggered in the next step so GHES driver start to parse error. This could be done using SCI emulate mechanism. Logic path: 1. Create Error Status Block (ESB information related to given error, pointed by HEST) 2. Create pretended registers (blob for EINJ) that instruct kernel to set status flag in ESB. 3. Trigger error using debugfs 4. Emulate SCI HOWTO: Enter to acpi tool repository: # make PADDR=0x<physical address> APEI=einj <platform>.acpi Boot target: Print available error list: # cat /sys/kernel/debug/apei/einj/available_error_type 0x00000008 Memory Correctable Set given error type: # echo 0x00000008 > /sys/kernel/debug/apei/einj/error_type Confirm error was really set: # cat /sys/kernel/debug/apei/einj/error_type 0x8 Inject error (here status flag for ESB is set) # echo 0x00000008 > /sys/kernel/debug/apei/einj/error_inject Emulate SCI (notification of HED device with 0x80 value) # echo "HED 128" > /sys/kernel/debug/acpi/sci_notify ACPI: ACPI device name is <HED>, event code is <128> ACPI: Notify event is queued {1}[Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 2 {1}[Hardware Error]: APEI generic hardware error status {1}[Hardware Error]: severity: 1, fatal {1}[Hardware Error]: section: 0, severity: 0, recoverable {1}[Hardware Error]: flags: 0x00 {1}[Hardware Error]: section_type: memory error Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
-rw-r--r--tools/bfapei/bfapei.c61
-rw-r--r--tools/bfapei/bfapei.h30
-rw-r--r--tools/common/include/acpi.h57
3 files changed, 141 insertions, 7 deletions
diff --git a/tools/bfapei/bfapei.c b/tools/bfapei/bfapei.c
index 73a421c..ff99953 100644
--- a/tools/bfapei/bfapei.c
+++ b/tools/bfapei/bfapei.c
@@ -45,6 +45,10 @@
* | |
* +--------------------+ +0x200
* | |
+ * | place for EINJ | EINJ driver reference to this region during
+ * | registers | error injection.
+ * | |
+ * +--------------------+ +0x300
*
* ~~~~~~~~~~~~~~~~~~~~~~~~
*
@@ -182,6 +186,55 @@ static int bfapei_hest(char **buf, int *size, uint64_t paddr, int status)
return BFAPEI_OK;
}
+static int bfapei_einj(char **buf, int *size, uint64_t paddr)
+{
+ struct acpi_hest_generic_status *block_ptr;
+ struct acpi_einj_trigger *trigger_tab;
+ struct acpi_whea_header *trigger_entry;
+ int reqr_size = 0x300;
+ uint64_t *add_ptr;
+
+ if (bfapei_expand_buf(buf, size, reqr_size))
+ return BFAPEI_FAIL;
+
+ /* Available error type: Memory Correctable */
+ WRITE_EINJ_REG(*buf, ACPI_EINJ_GET_ERROR_TYPE, 0x00000008);
+
+ WRITE_EINJ_REG(*buf, ACPI_EINJ_GET_COMMAND_STATUS, ACPI_EINJ_SUCCESS);
+ WRITE_EINJ_REG(*buf, ACPI_EINJ_CHECK_BUSY_STATUS, 1);
+
+ /*
+ * Nothing to do more than just set status flag for previous prepared
+ * ESB (error status block)
+ */
+ WRITE_EINJ_REG(*buf, ACPI_EINJ_GET_TRIGGER_TABLE, paddr + 0x280);
+ trigger_tab = (struct acpi_einj_trigger *) (*buf + 0x280);
+ trigger_tab->header_size = sizeof(struct acpi_einj_trigger);
+ trigger_tab->table_size = sizeof(struct acpi_einj_trigger) +
+ sizeof(struct acpi_whea_header);
+ trigger_tab->entry_count = 1;
+
+ trigger_entry = (struct acpi_whea_header *) ++trigger_tab;
+ trigger_entry->action = ACPI_EINJ_TRIGGER_ERROR;
+ trigger_entry->instruction = ACPI_EINJ_WRITE_REGISTER_VALUE;
+
+ trigger_entry->register_region.space_id = ACPI_ADR_SPACE_SYSTEM_MEMORY;
+ trigger_entry->register_region.bit_width = 40;
+ trigger_entry->register_region.bit_offset = 0;
+ trigger_entry->register_region.access_width = 4;
+
+ /* Obtain address of status block field */
+ add_ptr = (uint64_t *) *buf;
+ block_ptr = (struct acpi_hest_generic_status *) (++add_ptr);
+ trigger_entry->register_region.address =
+ paddr + ((char *)&(block_ptr->block_status) - *buf);
+
+ trigger_entry->value = 1;
+ trigger_entry->mask = 0xFFFFFFFFFFFFFFFFUL;
+
+ return BFAPEI_OK;
+}
+
static int bfapei_erst(char **buf, int *size, uint64_t paddr)
{
int reqr_size = 0x200;
@@ -250,6 +303,14 @@ int main(int argc, char *argv[]) {
return BFAPEI_FAIL;
}
+ if (strncmp(blob_type, "einj", 4) == 0) {
+ if (bfapei_hest(&buf, &size, paddr, 0))
+ return BFAPEI_FAIL;
+
+ if (bfapei_einj(&buf, &size, paddr))
+ return BFAPEI_FAIL;
+ }
+
/* Set status flag so error will be visible for BERT driver */
if (strncmp(blob_type, "bert", 4) == 0) {
if (bfapei_hest(&buf, &size, paddr, 1))
diff --git a/tools/bfapei/bfapei.h b/tools/bfapei/bfapei.h
index 16b6b3c..20678ce 100644
--- a/tools/bfapei/bfapei.h
+++ b/tools/bfapei/bfapei.h
@@ -27,6 +27,13 @@ const char PROGNAME[] = { "bfapei" };
sizeof(struct acpi_hest_generic_data) + \
sizeof (struct cper_sec_mem_err)
+/* Register write macro helper */
+#define WRITE_LONG(blob, offset, value) { \
+ uint64_t *add_ptr; \
+ add_ptr = (uint64_t *) (blob + offset); \
+ *add_ptr = value; \
+}
+
/* ERST generic registers offset */
uint64_t erst_reg_map[] = {
[ACPI_ERST_CHECK_BUSY_STATUS] = 0x130,
@@ -36,14 +43,23 @@ uint64_t erst_reg_map[] = {
[ACPI_ERST_GET_ERROR_ATTRIBUTES] = 0x178,
};
-/* ERST macro helper */
-#define WRITE_LONG(blob, offset, value) { \
- uint64_t *add_ptr; \
- add_ptr = (uint64_t *) (blob + offset); \
- *add_ptr = value; \
-}
-
#define WRITE_ERST_REG(blob, reg, value) \
WRITE_LONG(blob, erst_reg_map[reg], value)
+/* EINJ generic registers offset */
+uint64_t einj_reg_map[] = {
+ [ACPI_EINJ_BEGIN_OPERATION] = 0x200,
+ [ACPI_EINJ_GET_TRIGGER_TABLE] = 0x208,
+ [ACPI_EINJ_SET_ERROR_TYPE] = 0x210,
+ [ACPI_EINJ_GET_ERROR_TYPE] = 0x218,
+ [ACPI_EINJ_END_OPERATION] = 0x220,
+ [ACPI_EINJ_EXECUTE_OPERATION] = 0x228,
+ [ACPI_EINJ_CHECK_BUSY_STATUS] = 0x230,
+ [ACPI_EINJ_GET_COMMAND_STATUS] = 0x238,
+
+};
+
+#define WRITE_EINJ_REG(blob, reg, value) \
+ WRITE_LONG(blob, einj_reg_map[reg], value)
+
#endif
diff --git a/tools/common/include/acpi.h b/tools/common/include/acpi.h
index c9380de..9ba29fb 100644
--- a/tools/common/include/acpi.h
+++ b/tools/common/include/acpi.h
@@ -201,4 +201,61 @@ enum acpi_erst_actions {
ACPI_ERST_ACTION_RESERVED = 16 /* 16 and greater are reserved */
};
+#define ACPI_ADR_SPACE_SYSTEM_MEMORY 0
+
+enum acpi_einj_actions {
+ ACPI_EINJ_BEGIN_OPERATION = 0,
+ ACPI_EINJ_GET_TRIGGER_TABLE = 1,
+ ACPI_EINJ_SET_ERROR_TYPE = 2,
+ ACPI_EINJ_GET_ERROR_TYPE = 3,
+ ACPI_EINJ_END_OPERATION = 4,
+ ACPI_EINJ_EXECUTE_OPERATION = 5,
+ ACPI_EINJ_CHECK_BUSY_STATUS = 6,
+ ACPI_EINJ_GET_COMMAND_STATUS = 7,
+ ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS = 8,
+ ACPI_EINJ_ACTION_RESERVED = 9, /* 9 and greater are reserved */
+ ACPI_EINJ_TRIGGER_ERROR = 0xFF /* Except for this value */
+};
+
+/* Values for Instruction field above */
+
+enum acpi_einj_instructions {
+ ACPI_EINJ_READ_REGISTER = 0,
+ ACPI_EINJ_READ_REGISTER_VALUE = 1,
+ ACPI_EINJ_WRITE_REGISTER = 2,
+ ACPI_EINJ_WRITE_REGISTER_VALUE = 3,
+ ACPI_EINJ_NOOP = 4,
+ ACPI_EINJ_INSTRUCTION_RESERVED = 5 /* 5 and greater are reserved */
+};
+
+/* Command status return values */
+
+enum acpi_einj_command_status {
+ ACPI_EINJ_SUCCESS = 0,
+ ACPI_EINJ_FAILURE = 1,
+ ACPI_EINJ_INVALID_ACCESS = 2,
+ ACPI_EINJ_STATUS_RESERVED = 3 /* 3 and greater are reserved */
+};
+
+/* EINJ Trigger Error Action Table */
+
+struct acpi_einj_trigger {
+ uint32_t header_size;
+ uint32_t revision;
+ uint32_t table_size;
+ uint32_t entry_count;
+};
+
+/* Subtable header for WHEA tables (EINJ, ERST, WDAT) */
+
+struct acpi_whea_header {
+ uint8_t action;
+ uint8_t instruction;
+ uint8_t flags;
+ uint8_t reserved;
+ struct acpi_generic_address register_region;
+ uint64_t value; /* Value used with Read/Write register */
+ uint64_t mask; /* Bitmask required for this register instruction */
+};
+
#endif /* ACPI_H_ */