On 09/05/2013 09:47 AM, Tomasz Nowicki wrote:
Similar to mab and cab, bfapei "Blob For APEI" create blob which can be passed to the kernel by special u-boot commands for debugging purposes. It enable testing of APEI without any h/w interaction. The idea is to set physical region aside so kernel could reference to it. Once blob is filling with appropriate value we can pretend registers, storage etc.
Currently bfapei is able to create blob to test tables like: o EINJ - kernel need to trigger error, related error info is ready to read
root@localhost:~# echo 1 > /sys/kernel/debug/apei/einj/error_inject {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
o BERT - kernel check list of errors (boot process) pointed from BERT, any errors with status flag set is printed out
[...] [Hardware Error]: Error record from previous boot: {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 [...]
O ERST - appropriate registers are set so when there is kernel panic ERST driver dump log to region reserved within the blob, once kernel back to life it can mount pstore file system and read logs (files) for more details
panic! [...] root@localhost:~# mount -t pstore - /sys/fs/pstore root@localhost:~# ll /sys/fs/pstore/ total 0 drwxr-xr-x 2 root root 0 Sep 5 15:19 ./ drwxr-xr-x 4 root root 0 Jan 1 1970 ../ -r--r--r-- 1 root root 29374 Sep 5 14:01 dmesg-erst-0
HOWTO: Add appropriate flag to make command: make PADDR=0x42010000 APEI=<einj|bert|erst> exynos5250-arndale.acpi
Is the APEI flag required? If it is, I think we need to make it optional for those cases where it may not be used or needed.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org
Makefile | 14 ++- platforms/Makefile | 1 + tools/bfapei/Makefile | 11 ++ tools/bfapei/bfapei.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++++ tools/bfapei/bfapei.h | 235 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 530 insertions(+), 2 deletions(-) create mode 100644 tools/bfapei/Makefile create mode 100644 tools/bfapei/bfapei.c create mode 100644 tools/bfapei/bfapei.h
diff --git a/Makefile b/Makefile index e897c69..076cce0 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,16 @@ MABDIR := tools/mab/ CABDIR := tools/cab/ +BFAPEI_DIR := tools/bfapei/ COMMON_DIR := tools/common/ PLATDIR := platforms/
-%.acpi : mab cab
- make MAB=$(CURDIR)/$(MABDIR)/mab PADDR=$(PADDR) -C $(PLATDIR) $@
+ARGS := MAB=$(CURDIR)/$(MABDIR)/mab PADDR=$(PADDR) +ifdef APEI
- ARGS += BFAPEI=$(CURDIR)/$(BFAPEI_DIR)/bfapei APEI=$(APEI)
+endif
+%.acpi : mab cab bfapei
make $(ARGS) -C $(PLATDIR) $@
mab : FORCE make -C $(MABDIR) PROG=mab
@@ -12,9 +18,13 @@ mab : FORCE cab : FORCE make -C $(CABDIR) PROG=cab
+bfapei : FORCE
- make -C $(BFAPEI_DIR) PROG=bfapei
- clean : make -C $(MABDIR) clean PROG=mab make -C $(CABDIR) clean PROG=cab
- make -C $(BFAPEI_DIR) clean PROG=bfapei make -C $(COMMON_DIR) clean make -C $(PLATDIR) clean
diff --git a/platforms/Makefile b/platforms/Makefile index caff1c8..ebb1e5d 100644 --- a/platforms/Makefile +++ b/platforms/Makefile @@ -4,6 +4,7 @@ endif
%.acpi : FORCE $(MAB) -d $@ $(PADDR_ARG) -i $(basename $@).manifest -o $@
- $(BFAPEI) -d $@ -i $(basename $@).manifest -t $(APEI) -o apei.acpi
If I don't use APEI=foo on the make line, this fails because BFAPEI doesn't get set.
clean : -rm -f *.acpi/*.aml diff --git a/tools/bfapei/Makefile b/tools/bfapei/Makefile new file mode 100644 index 0000000..f12aace --- /dev/null +++ b/tools/bfapei/Makefile @@ -0,0 +1,11 @@ +CFLAGS := -g -Wall -I../common/include
+HEADERS = $(wildcard *.h) $(wildcard ../common/include/*.h)
+SOURCES = $(wildcard *.c) $(wildcard ../common/*.c)
+$(PROG) : $(patsubst %.c,%.o, $(SOURCES)) $(HEADERS)
- $(CC) $(CFLAGS) $(patsubst %.c,%.o, $(SOURCES)) -o $@
+clean :
- -rm -f $(patsubst %.c,%.o, $(wildcard *.c)) $(PROG) *.acpi
diff --git a/tools/bfapei/bfapei.c b/tools/bfapei/bfapei.c new file mode 100644 index 0000000..2eacf65 --- /dev/null +++ b/tools/bfapei/bfapei.c @@ -0,0 +1,271 @@ +/*
- bfapei.c: main program for the tool to make a Blob of APEI tables
- This file is subject to the terms and conditions of the GNU General
- Public License. See the file "COPYING" in the main directory of this
- archive for more details.
- Copyright (c) 2013, Tomasz Nowicki tomasz.nowicki@linaro.org
- NB: all values are assumed to be little-endian in the blob.
- */
+#include <ctype.h> +#include <endian.h> +#include <errno.h> +#include <libgen.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h>
+#include <sys/queue.h> +#include <sys/types.h> +#include <sys/stat.h>
+#include <build_aml.h> +#include <check_aml.h>
+#include "bfapei.h"
+/*
- Overview of blob for APEI testing
+--------------------+ 0x0 base address, need to be obtained from HEST.
| |
| place for status | HEST and BERT reference to this region
| block error info | during error servicing.
| |
+--------------------+ +0x100
| |
| place for ERST | ERST driver reference to this region during
| registers | persistent error handling.
| |
+--------------------+ +0x200
| |
~~~~~~~~~~~~~~~~~~~~~~~~
| |
+--------------------+ +0x2000
| |
| buffer for dmesg | kernel log is dumping here during panic
| log | when there is a kernel oops or panic
| |
+--------------------+ +0x12000
- */
+static void usage(void) {
- printf("%s %s\n", PROGNAME, VERSION);
- printf("usage: %s -d <dir> -o <blob> -i <manifest>\n", PROGNAME);
- printf("-t [einj|bert|erst]\n");
- printf(" -d <dir> => directory of ASL files\n");
- printf(" -o <blob> => file name for resulting APEI blob\n");
- printf(" -i <manifest> => list of AML files needed\n");
- printf(" -t => blob type for APEI\n");
- printf(" -q => if given, supress output\n");
+}
+static int obtain_esb(char *homedir, char *manifest_name) +{
- int err, table_size, i, error_source_count;
- struct acpi_hest_generic *generic;
- struct acpi_table_hest *hest_hdr;
- struct table *p;
- char *blob;
- FILE *fp;
- /* What tables can we handle with */
- err = read_manifest(homedir, manifest_name);
- if (err) {
printf("? could not read manifest: %s\n", manifest_name);
return 0;
- }
- /* HEST table would be candidate */
- p = find_table("hest");
- if (!p) {
printf("Not found hest table!\n");
return 0;
- }
- if (!p->aml_name) {
printf("No aml file created for %s table!\n", p->asl_name);
return 0;
- }
- table_size = get_file_size(p->aml_name);
- blob = (char *)malloc(table_size);
- if (!blob) {
printf("Memory allocation error!\n");
return 0;
- }
- fp = fopen(p->aml_name, "r");
- if (!fp) {
printf("File %s can not be opened!\n", p->aml_name);
return 0;
- }
- table_size = fread(blob, 1, table_size, fp);
- fclose(fp);
- /* How many error sources we should traverse looking for GHES ? */
- hest_hdr = (struct acpi_table_hest *)blob;
- error_source_count = hest_hdr->error_source_count;
- blob = (char *)(hest_hdr + 1);
- generic = (struct acpi_hest_generic *)blob;
- for (i = 0; i < error_source_count; i++) {
if (generic->header.type == ACPI_HEST_TYPE_GENERIC_ERROR)
return generic->error_status_address.address;
- }
- return 0;
+}
+static int bfapei_einj(char **buf, int *size, uint64_t paddr, int status) +{
- struct acpi_hest_generic_status *block_ptr;
- struct acpi_hest_generic_data *gdata;
- struct cper_sec_mem_err *mem_err;
- uint64_t *add_ptr;
- int reqr_size = sizeof(uint64_t) +
sizeof(struct acpi_hest_generic_status) +
sizeof(struct acpi_hest_generic_data) +
sizeof (struct cper_sec_mem_err);
- if (*size < reqr_size) {
*buf = realloc(*buf, *size + reqr_size);
if (!(*buf)) {
printf("Error during memory allocation!\n");
return BFAPEI_FAIL;
}
memset(*buf + *size, 0, reqr_size - *size);
*size += reqr_size;
- }
- /*
* Fill in blob which will be parsed by EINJ driver.
*/
- /* First point to generic error status block */
- add_ptr = (uint64_t *) *buf;
- *add_ptr = paddr + sizeof(uint64_t);
- /* Fill in generic error status block */
- block_ptr = (struct acpi_hest_generic_status *) (++add_ptr);
- block_ptr->block_status = status;
- block_ptr->data_length = sizeof(struct acpi_hest_generic_data);
- block_ptr->error_severity = GHES_SEV_CORRECTED;
- /* Fill in generic error data entry */
- gdata = (struct acpi_hest_generic_data *) (block_ptr + 1);
- memcpy(gdata->section_type, (void *) &CPER_SEC_PLATFORM_MEM,
sizeof(uuid_le));
- gdata->error_data_length = sizeof(struct cper_sec_mem_err);
- block_ptr->data_length += gdata->error_data_length;
- mem_err = (struct cper_sec_mem_err *) (gdata + 1);
- /* Place for more specific err info */
- return BFAPEI_OK;
+}
+static int bfapei_erst(char **buf, int *size, uint64_t paddr) +{
- int reqr_size = 0x400;
- if (*size < reqr_size) {
*buf = realloc(*buf, *size + reqr_size);
if (!(*buf)) {
printf("Error during memory allocation!\n");
return BFAPEI_FAIL;
}
memset(*buf + *size, 0, reqr_size - *size);
*size += reqr_size;
- }
- /*
* Fill in ERST registers.
*/
- WRITE_ERST_REG(*buf, ACPI_ERST_GET_COMMAND_STATUS, ERST_STATUS_SUCCESS);
- WRITE_ERST_REG(*buf, ACPI_ERST_CHECK_BUSY_STATUS, 1);
- WRITE_ERST_REG(*buf, ACPI_ERST_GET_ERROR_RANGE, paddr + 0x2000);
- WRITE_ERST_REG(*buf, ACPI_ERST_GET_ERROR_LENGTH, 0x10000);
- WRITE_ERST_REG(*buf, ACPI_ERST_GET_ERROR_ATTRIBUTES, ERST_RANGE_SLOW);
- return BFAPEI_OK;
+}
+int main(int argc, char *argv[]) {
- char *homedir, *apei_blob_name, *manifest_name, *blob_type;
- int opt, size = 0, quiet = 0;
- uint64_t paddr;
- char *buf = NULL;;
- while ((opt = getopt(argc, argv, "d:o:i:t:q")) != EOF) {
switch (opt) {
case 'd':
homedir = optarg;
break;
case 'o':
apei_blob_name = optarg;
break;
case 'i':
manifest_name = optarg;
break;
case 't':
blob_type = optarg;
break;
case 'q':
quiet = 1;
break;
default:
usage();
return BFAPEI_FAIL;
}
- }
- if ((argc > (optind + 1)) || (apei_blob_name == NULL) ||
(homedir == NULL) || (manifest_name == NULL) ||
(blob_type == NULL)) {
printf("Missing a parameter value\n");
usage();
return BFAPEI_FAIL;
- }
- paddr = obtain_esb(homedir, manifest_name);
- if (!paddr) {
printf("No Error Status Block address obtained !\n");
return BFAPEI_FAIL;
- }
- if (strncmp(blob_type, "einj", 4) == 0) {
if (bfapei_einj(&buf, &size, paddr, 0))
return BFAPEI_FAIL;
- }
- /* Set status flag so error will be visible for BERT driver */
- if (strncmp(blob_type, "bert", 4) == 0) {
if (bfapei_einj(&buf, &size, paddr, 1))
return BFAPEI_FAIL;
- }
- if (strncmp(blob_type, "erst", 4) == 0) {
if (bfapei_erst(&buf, &size, paddr))
return BFAPEI_FAIL;
- }
- /* Save buffer to specified file. */
- write_blob(homedir, apei_blob_name, buf, size);
- if (!quiet) {
printf("%s blob for %s APEI table testing created\n", apei_blob_name, blob_type);
printf("Physical address: 0x%llx\n", paddr);
- }
- return BFAPEI_OK;
+} diff --git a/tools/bfapei/bfapei.h b/tools/bfapei/bfapei.h new file mode 100644 index 0000000..63b08c5 --- /dev/null +++ b/tools/bfapei/bfapei.h @@ -0,0 +1,235 @@ +/*
- bfapei.h: tool to make a Blob For APEI testing
- This file is subject to the terms and conditions of the GNU General
- Public License. See the file "COPYING" in the main directory of this
- archive for more details.
- Copyright (c) 2013, Tomasz Nowicki tomasz.nowicki@linaro.org
- NB: all values are assumed to be little-endian in the blob.
- */
+#ifndef _APEI_H +#define _APEI_H
+/* VERSION a.b.c = <public release>.<functionality changes>.<bug fixes> */ +const char VERSION[] = { "0.1.0" }; +const char PROGNAME[] = { "bfapei" };
+#define ACPI_NAME_SIZE 4 +#define ACPI_OEM_ID_SIZE 6 +#define ACPI_OEM_TABLE_ID_SIZE 8
The defines above (ACPI_*) look like good candidates for one of the common include files. I think I'd prefer them there.
+/* Generic ACPI table header */
+struct acpi_table_header {
- char signature[ACPI_NAME_SIZE]; /* ASCII table signature */
- uint32_t length; /* Length of table in bytes, including this header */
- uint8_t revision; /* ACPI Specification minor version number */
- uint8_t checksum; /* To make sum of entire table == 0 */
- char oem_id[ACPI_OEM_ID_SIZE]; /* ASCII OEM identification */
- char oem_table_id[ACPI_OEM_TABLE_ID_SIZE]; /* ASCII OEM table identification */
- uint32_t oem_revision; /* OEM revision number */
- char asl_compiler_id[ACPI_NAME_SIZE]; /* ASCII ASL compiler vendor ID */
- uint32_t asl_compiler_revision; /* ASL compiler version */
+};
Hrm. I should have thought of this, too. I think the the acpi_table_header struct needs to be in one of the common include files. If it is, we can make mab/cab a bit cleaner.
+/* HEST - Hardware Error Source Table */
+struct acpi_table_hest {
- struct acpi_table_header header; /* Common ACPI table header */
- uint32_t error_source_count;
+};
+/* HEST subtable header */
+struct acpi_hest_header {
- uint16_t type;
- uint16_t source_id;
+};
+/* Values for Type field above for subtables */
+enum acpi_hest_types {
- ACPI_HEST_TYPE_IA32_CHECK = 0,
- ACPI_HEST_TYPE_IA32_CORRECTED_CHECK = 1,
- ACPI_HEST_TYPE_IA32_NMI = 2,
- ACPI_HEST_TYPE_NOT_USED3 = 3,
- ACPI_HEST_TYPE_NOT_USED4 = 4,
- ACPI_HEST_TYPE_NOT_USED5 = 5,
- ACPI_HEST_TYPE_AER_ROOT_PORT = 6,
- ACPI_HEST_TYPE_AER_ENDPOINT = 7,
- ACPI_HEST_TYPE_AER_BRIDGE = 8,
- ACPI_HEST_TYPE_GENERIC_ERROR = 9,
- ACPI_HEST_TYPE_RESERVED = 10 /* 10 and greater are reserved */
+};
+/* GAS - Generic Address Structure */ +struct acpi_generic_address {
- uint8_t space_id; /* Address space where struct or register exists */
- uint8_t bit_width; /* Size in bits of given register */
- uint8_t bit_offset; /* Bit offset within the register */
- uint8_t access_width; /* Minimum Access size (ACPI 3.0) */
- uint64_t address; /* 64-bit address of struct or register */
+} __attribute__ ((packed));
For now, only bfapei uses the acpi_generic_address struct so it's probably okay here, but it may make sense in the common files -- I'll leave that to your best judgment because I can go either way. If we start getting a lot more tools here, I think we need to contemplate just pulling in all of the existing Linux acpi includes so we're not duplicating all of those header files.
+/* Hardware Error Notification */
+struct acpi_hest_notify {
- uint8_t type;
- uint8_t length;
- uint16_t config_write_enable;
- uint32_t poll_interval;
- uint32_t vector;
- uint32_t polling_threshold_value;
- uint32_t polling_threshold_window;
- uint32_t error_threshold_value;
- uint32_t error_threshold_window;
+};
+/* 9: Generic Hardware Error Source */
+struct acpi_hest_generic {
- struct acpi_hest_header header;
- uint16_t related_source_id;
- uint8_t reserved;
- uint8_t enabled;
- uint32_t records_to_preallocate;
- uint32_t max_sections_per_record;
- uint32_t max_raw_data_length;
- struct acpi_generic_address error_status_address;
- struct acpi_hest_notify notify;
- uint32_t error_block_length;
+};
+enum {
- GHES_SEV_NO = 0x0,
- GHES_SEV_CORRECTED = 0x1,
- GHES_SEV_RECOVERABLE = 0x2,
- GHES_SEV_PANIC = 0x3,
+};
+/* Generic Error Status block */
+struct acpi_hest_generic_status {
- uint32_t block_status;
- uint32_t raw_data_offset;
- uint32_t raw_data_length;
- uint32_t data_length;
- uint32_t error_severity;
+};
+/* Generic Error Data entry */
+struct acpi_hest_generic_data {
- uint8_t section_type[16];
- uint32_t error_severity;
- uint16_t revision;
- uint8_t validation_bits;
- uint8_t flags;
- uint32_t error_data_length;
- uint8_t fru_id[16];
- uint8_t fru_text[20];
+};
+typedef struct {
- uint8_t b[16];
+} uuid_le;
+#define UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ +((uuid_le) \ +{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
- (b) & 0xff, ((b) >> 8) & 0xff, \
- (c) & 0xff, ((c) >> 8) & 0xff, \
- (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }})
+/* Platform Memory */ +#define CPER_SEC_PLATFORM_MEM \
- UUID_LE(0xA5BC1114, 0x6F64, 0x4EDE, 0xB8, 0x63, 0x3E, 0x83, \
0xED, 0x7C, 0x83, 0xB1)
+/* Memory Error Section */ +struct cper_sec_mem_err {
- uint64_t validation_bits;
- uint64_t error_status;
- uint64_t physical_addr;
- uint64_t physical_addr_mask;
- uint16_t node;
- uint16_t card;
- uint16_t module;
- uint16_t bank;
- uint16_t device;
- uint16_t row;
- uint16_t column;
- uint16_t bit_pos;
- uint64_t requestor_id;
- uint64_t responder_id;
- uint64_t target_id;
- uint8_t error_type;
+} __attribute__ ((packed));
+/* ERST Error Log Address Range atrributes */ +#define ERST_RANGE_RESERVED 0x0001 +#define ERST_RANGE_NVRAM 0x0002 +#define ERST_RANGE_SLOW 0x0004
+/* ERST command status */ +#define ERST_STATUS_SUCCESS 0x0 +#define ERST_STATUS_NOT_ENOUGH_SPACE 0x1 +#define ERST_STATUS_HARDWARE_NOT_AVAILABLE 0x2 +#define ERST_STATUS_FAILED 0x3 +#define ERST_STATUS_RECORD_STORE_EMPTY 0x4 +#define ERST_STATUS_RECORD_NOT_FOUND 0x5
+#define MAX_LINE 1024 +#define BFAPEI_OK 0 +#define BFAPEI_FAIL 1
+#define SIZE sizeof(uint64_t) + sizeof(struct acpi_hest_generic_status) + \
sizeof(struct acpi_hest_generic_data) + \
sizeof (struct cper_sec_mem_err)
+/* Values for Action field above */
+enum acpi_erst_actions {
- ACPI_ERST_BEGIN_WRITE = 0,
- ACPI_ERST_BEGIN_READ = 1,
- ACPI_ERST_BEGIN_CLEAR = 2,
- ACPI_ERST_END = 3,
- ACPI_ERST_SET_RECORD_OFFSET = 4,
- ACPI_ERST_EXECUTE_OPERATION = 5,
- ACPI_ERST_CHECK_BUSY_STATUS = 6,
- ACPI_ERST_GET_COMMAND_STATUS = 7,
- ACPI_ERST_GET_RECORD_ID = 8,
- ACPI_ERST_SET_RECORD_ID = 9,
- ACPI_ERST_GET_RECORD_COUNT = 10,
- ACPI_ERST_BEGIN_DUMMY_WRIITE = 11,
- ACPI_ERST_NOT_USED = 12,
- ACPI_ERST_GET_ERROR_RANGE = 13,
- ACPI_ERST_GET_ERROR_LENGTH = 14,
- ACPI_ERST_GET_ERROR_ATTRIBUTES = 15,
- ACPI_ERST_ACTION_RESERVED = 16 /* 16 and greater are reserved */
+};
+/* ERST generic registers offset */ +uint64_t erst_reg_map[] = {
- [ACPI_ERST_CHECK_BUSY_STATUS] = 0x130,
- [ACPI_ERST_GET_COMMAND_STATUS] = 0x138,
- [ACPI_ERST_GET_ERROR_RANGE] = 0x168,
- [ACPI_ERST_GET_ERROR_LENGTH] = 0x170,
- [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)
+#endif