aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/external/gard
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/external/gard')
-rw-r--r--roms/skiboot/external/gard/.gitignore11
-rw-r--r--roms/skiboot/external/gard/Makefile43
-rw-r--r--roms/skiboot/external/gard/Makefile.dist17
-rw-r--r--roms/skiboot/external/gard/config.h33
-rw-r--r--roms/skiboot/external/gard/gard.c1017
-rw-r--r--roms/skiboot/external/gard/gard.h74
-rw-r--r--roms/skiboot/external/gard/opal-gard.130
-rw-r--r--roms/skiboot/external/gard/rules.mk51
-rwxr-xr-xroms/skiboot/external/gard/test/add_test.sh67
-rw-r--r--roms/skiboot/external/gard/test/files/blank.binbin0 -> 20480 bytes
-rw-r--r--roms/skiboot/external/gard/test/files/data-p9.binbin0 -> 20480 bytes
-rw-r--r--roms/skiboot/external/gard/test/files/data1.binbin0 -> 20480 bytes
-rwxr-xr-xroms/skiboot/external/gard/test/make-check-test1
-rw-r--r--roms/skiboot/external/gard/test/results/00-list.err0
-rw-r--r--roms/skiboot/external/gard/test/results/00-list.out5
-rw-r--r--roms/skiboot/external/gard/test/results/01-show_1.err0
-rw-r--r--roms/skiboot/external/gard/test/results/01-show_1.out8
-rw-r--r--roms/skiboot/external/gard/test/results/02-usage.err24
-rw-r--r--roms/skiboot/external/gard/test/results/02-usage.out1
-rw-r--r--roms/skiboot/external/gard/test/results/03-show_1-p9.err0
-rw-r--r--roms/skiboot/external/gard/test/results/03-show_1-p9.out10
-rw-r--r--roms/skiboot/external/gard/test/results/04-create-bad-instance.err2
-rw-r--r--roms/skiboot/external/gard/test/results/04-create-bad-instance.out0
-rw-r--r--roms/skiboot/external/gard/test/results/05-create-bad-unit.err2
-rw-r--r--roms/skiboot/external/gard/test/results/05-create-bad-unit.out0
-rw-r--r--roms/skiboot/external/gard/test/results/06-create-long-path.err2
-rw-r--r--roms/skiboot/external/gard/test/results/06-create-long-path.out0
-rw-r--r--roms/skiboot/external/gard/test/results/07-create-slash.err2
-rw-r--r--roms/skiboot/external/gard/test/results/07-create-slash.out0
-rw-r--r--roms/skiboot/external/gard/test/results/08-create-duplicate.err1
-rw-r--r--roms/skiboot/external/gard/test/results/08-create-duplicate.out0
-rw-r--r--roms/skiboot/external/gard/test/results/09-create-last-unit.err0
-rw-r--r--roms/skiboot/external/gard/test/results/09-create-last-unit.out4
-rw-r--r--roms/skiboot/external/gard/test/results/10-clear-single.err0
-rw-r--r--roms/skiboot/external/gard/test/results/10-clear-single.out7
-rw-r--r--roms/skiboot/external/gard/test/results/11-clear-first.err0
-rw-r--r--roms/skiboot/external/gard/test/results/11-clear-first.out13
-rwxr-xr-xroms/skiboot/external/gard/test/test-gard5
-rw-r--r--roms/skiboot/external/gard/test/tests/00-list10
-rw-r--r--roms/skiboot/external/gard/test/tests/01-show_110
-rw-r--r--roms/skiboot/external/gard/test/tests/02-usage17
-rw-r--r--roms/skiboot/external/gard/test/tests/03-show_1-p910
-rw-r--r--roms/skiboot/external/gard/test/tests/04-create-bad-instance10
-rw-r--r--roms/skiboot/external/gard/test/tests/05-create-bad-unit10
-rw-r--r--roms/skiboot/external/gard/test/tests/06-create-long-path10
-rw-r--r--roms/skiboot/external/gard/test/tests/07-create-slash10
-rw-r--r--roms/skiboot/external/gard/test/tests/08-create-duplicate10
-rw-r--r--roms/skiboot/external/gard/test/tests/09-create-last-unit16
-rw-r--r--roms/skiboot/external/gard/test/tests/10-clear-single23
-rw-r--r--roms/skiboot/external/gard/test/tests/11-clear-first26
-rw-r--r--roms/skiboot/external/gard/units.c242
51 files changed, 1834 insertions, 0 deletions
diff --git a/roms/skiboot/external/gard/.gitignore b/roms/skiboot/external/gard/.gitignore
new file mode 100644
index 000000000..7e8f83f20
--- /dev/null
+++ b/roms/skiboot/external/gard/.gitignore
@@ -0,0 +1,11 @@
+o
+*.d
+ccan
+common
+common-ast-sf-ctrl.c
+common-io.h
+gard
+test/test.sh
+libflash
+make_version.sh
+gard-*.tar
diff --git a/roms/skiboot/external/gard/Makefile b/roms/skiboot/external/gard/Makefile
new file mode 100644
index 000000000..e249cf846
--- /dev/null
+++ b/roms/skiboot/external/gard/Makefile
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: Apache-2.0
+# This tool is a linux userland tool and should be completely stand alone
+include rules.mk
+GET_ARCH = ../../external/common/get_arch.sh
+include ../../external/common/rules.mk
+
+all: links arch_links $(EXE)
+
+.PHONY: coverage
+coverage: CFLAGS += -fprofile-arcs -ftest-coverage
+coverage: check
+
+#Rebuild version.o so that the the version always matches
+#what the test suite will get from ./make_version.sh
+check: version.o all
+ @ln -sf ../../make_version.sh make_version.sh
+ @ln -sf ../../test/test.sh test/test.sh
+ @test/test-gard
+
+.PHONY: VERSION-always
+.version: VERSION-always
+ @echo $(GARD_VERSION) > $@.tmp
+ @cmp -s $@ $@.tmp || cp $@.tmp $@
+ @rm -f $@.tmp
+
+.PHONY: dist
+#File is named $(GARD_VERSION).tar because the expectation is that gard-
+#is always at the start of the verion. This remains consistent with skiboot
+#version strings
+dist: arch_links links .version
+ @find -L ../gard/ -iname '*.[ch]' -print0 | xargs -0 tar -rhf $(GARD_VERSION).tar
+ @tar --transform 's/Makefile.dist/Makefile/' -rhf $(GARD_VERSION).tar \
+ ../gard/Makefile.dist ../gard/rules.mk \
+ ../gard/.version ../gard/common/*
+
+clean: arch_clean
+ rm -f $(OBJS) $(EXE) *.o *.d .version .version.tmp
+
+distclean: clean
+ rm -f *.c~ *.h~ *.sh~ Makefile~ config.mk~ libflash/*.c~ libflash/*.h~
+ rm -f libflash ccan common io.h version.c make_version.sh
+ rm -f gard-*.tar
+
diff --git a/roms/skiboot/external/gard/Makefile.dist b/roms/skiboot/external/gard/Makefile.dist
new file mode 100644
index 000000000..e03ad230a
--- /dev/null
+++ b/roms/skiboot/external/gard/Makefile.dist
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#Prevent make from trying to remake arch links symlinks
+#which are fixed files now
+ARCH_LINKS :=
+
+GARD_VERSION := $(shell cat .version)
+
+include rules.mk
+GET_ARCH = common/get_arch.sh
+include common/rules.mk
+
+all: $(EXE)
+
+clean:
+ rm -f $(OBJS) *.o
+distclean: clean
+ rm -f $(EXE)
diff --git a/roms/skiboot/external/gard/config.h b/roms/skiboot/external/gard/config.h
new file mode 100644
index 000000000..42aef6ddb
--- /dev/null
+++ b/roms/skiboot/external/gard/config.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * For CCAN
+ *
+ * Copyright 2015-2017 IBM Corp
+ */
+
+#include <endian.h>
+#include <byteswap.h>
+
+#define HAVE_TYPEOF 1
+#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1
+
+#ifndef HAVE_LITTLE_ENDIAN
+#ifndef HAVE_BIG_ENDIAN
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define HAVE_LITTLE_ENDIAN 1
+#else
+#define HAVE_BIG_ENDIAN 1
+#endif
+#endif
+#endif
+
+/* Keep -Wundef happy by defining whatever isn't on commandline to 0 */
+#if defined(HAVE_LITTLE_ENDIAN) && HAVE_LITTLE_ENDIAN
+#define HAVE_BIG_ENDIAN 0
+#endif
+#if defined(HAVE_BIG_ENDIAN) && HAVE_BIG_ENDIAN
+#define HAVE_LITTLE_ENDIAN 0
+#endif
+
+#define HAVE_BYTESWAP_H 1
+#define HAVE_BSWAP_64 1
diff --git a/roms/skiboot/external/gard/gard.c b/roms/skiboot/external/gard/gard.c
new file mode 100644
index 000000000..53a26d0e9
--- /dev/null
+++ b/roms/skiboot/external/gard/gard.c
@@ -0,0 +1,1017 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Manipulate GARD records in the GARD partition
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <ctype.h>
+
+#include <ccan/array_size/array_size.h>
+
+#include <mtd/mtd-abi.h>
+
+#include <getopt.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+#include <libflash/file.h>
+#include <libflash/blocklevel.h>
+#include <common/arch_flash.h>
+
+#include "gard.h"
+
+#define FDT_PATH "/proc/device-tree"
+#define FDT_FSP_NODE FDT_PATH"/fsps"
+#define FDT_ACTIVE_FLASH_PATH FDT_PATH"/chosen/ibm,system-flash"
+#define SYSFS_MTD_PATH "/sys/class/mtd/"
+#define FLASH_GARD_PART "GUARD"
+
+#define VPNOR_GARD_DIR "/media/pnor-prsv"
+#define VPNOR_GARD_FILE VPNOR_GARD_DIR"/GUARD"
+
+/* Full gard version number (possibly includes gitid). */
+extern const char version[];
+
+
+#define __unused __attribute__((unused))
+
+struct gard_ctx {
+ uint32_t f_size;
+ uint32_t f_pos;
+
+ uint32_t gard_part_idx;
+ uint32_t gard_data_pos;
+ uint32_t gard_data_len;
+
+ struct blocklevel_device *bl;
+ struct ffs_handle *ffs;
+};
+
+static void show_flash_err(int rc)
+{
+ switch (rc) {
+ case FFS_ERR_BAD_MAGIC:
+ fprintf(stderr, "libffs bad magic\n");
+ break;
+ case FFS_ERR_BAD_VERSION:
+ fprintf(stderr, "libffs bad version\n");
+ break;
+ case FFS_ERR_BAD_CKSUM:
+ fprintf(stderr, "libffs bad check sum\n");
+ break;
+ case FFS_ERR_PART_NOT_FOUND:
+ fprintf(stderr, "libffs flash partition not found\n");
+ break;
+ /* ------- */
+ case FLASH_ERR_MALLOC_FAILED:
+ fprintf(stderr, "libflash malloc failed\n");
+ break;
+ case FLASH_ERR_CHIP_UNKNOWN:
+ fprintf(stderr, "libflash unknown flash chip\n");
+ break;
+ case FLASH_ERR_PARM_ERROR:
+ fprintf(stderr, "libflash parameter error\n");
+ break;
+ case FLASH_ERR_ERASE_BOUNDARY:
+ fprintf(stderr, "libflash erase boundary error\n");
+ break;
+ case FLASH_ERR_WREN_TIMEOUT:
+ fprintf(stderr, "libflash WREN timeout\n");
+ break;
+ case FLASH_ERR_WIP_TIMEOUT:
+ fprintf(stderr, "libflash WIP timeout\n");
+ break;
+ case FLASH_ERR_VERIFY_FAILURE:
+ fprintf(stderr, "libflash verification failure\n");
+ break;
+ case FLASH_ERR_4B_NOT_SUPPORTED:
+ fprintf(stderr, "libflash 4byte mode not supported\n");
+ break;
+ case FLASH_ERR_CTRL_CONFIG_MISMATCH:
+ fprintf(stderr, "libflash control config mismatch\n");
+ break;
+ case FLASH_ERR_CHIP_ER_NOT_SUPPORTED:
+ fprintf(stderr, "libflash chip not supported\n");
+ break;
+ case FLASH_ERR_CTRL_CMD_UNSUPPORTED:
+ fprintf(stderr, "libflash unsupported control command\n");
+ break;
+ case FLASH_ERR_CTRL_TIMEOUT:
+ fprintf(stderr, "libflash control timeout\n");
+ break;
+ case FLASH_ERR_ECC_INVALID:
+ fprintf(stderr, "libflash ecc invalid\n");
+ break;
+ default:
+ fprintf(stderr, "A libflash/libffs error has occurred %d\n", rc);
+ }
+}
+
+const struct chip_unit_desc *chip_units;
+int chip_unit_count;
+
+static void set_chip_gen(const struct chip_unit_desc *c)
+{
+ chip_units = c;
+ chip_unit_count = 0;
+
+ while (strcmp("LAST_IN_RANGE", c->desc)) {
+ chip_unit_count++;
+ c++;
+ }
+}
+
+#ifdef __powerpc64__
+static void guess_chip_gen(void)
+{
+ /*
+ * Guesstimate what chip generation based on the PVR if we're running
+ * on ppc64.
+ */
+ uint32_t pvr;
+
+ /* grab the chip type from the PVR SPR */
+ asm ("mfspr %0,0x11f" : "=r" (pvr));
+
+ switch (pvr >> 16) {
+ case 0x004b: /* murano */
+ case 0x004c: /* naples */
+ case 0x004d: /* venice */
+ set_chip_gen(p8_chip_units);
+ return;
+
+ case 0x004e: /* nimbus */
+ case 0x004f: /* axone */
+ set_chip_gen(p9_chip_units);
+ return;
+
+ case 0x0080: /* power10 */
+ set_chip_gen(p10_chip_units);
+ return;
+
+ default:
+ fprintf(stderr, "Unsupported processor (pvr %#x)! Set the processor generation manually with -8, -9 or -0\n", pvr);
+ exit(1);
+ }
+}
+#else
+static void guess_chip_gen(void)
+{
+#ifdef ASSUME_P8
+ set_chip_gen(p8_chip_units);
+#else
+ set_chip_gen(p9_chip_units);
+#endif
+}
+#endif
+
+static const char *target_type_to_str(int type)
+{
+ int i;
+
+ for (i = 0; i < chip_unit_count; i++)
+ if (chip_units[i].type == type)
+ return chip_units[i].desc;
+
+ return "UNKNOWN";
+}
+
+static int str_to_target_type(const char *path)
+{
+ int i, len;
+
+ for (i = 0; i < chip_unit_count; i++) {
+ len = strlen(chip_units[i].desc);
+
+ if (!strncasecmp(chip_units[i].desc, path, len))
+ return chip_units[i].type; /* match! */
+ }
+
+ return -1;
+}
+
+static const char *deconfig_reason_str(enum gard_reason reason)
+{
+ switch (reason) {
+ case GARD_NO_REASON:
+ return "None";
+ case GARD_MANUAL:
+ return "Manual";
+ case GARD_UNRECOVERABLE:
+ return "Unrecoverable";
+ case GARD_FATAL:
+ return "Fatal";
+ case GARD_PREDICTIVE:
+ return "Predictive";
+ case GARD_POWER:
+ return "Power"; // What does this even mean?
+ case GARD_HYP:
+ return "Hypervisor";
+ case GARD_RECONFIG:
+ return "Reconfig";
+ default:
+ return "Unknown";
+ }
+};
+
+static const char *path_type_to_str(enum path_type t)
+{
+ switch (t) {
+ case PATH_NA:
+ return "not applicable";
+ case PATH_AFFINITY:
+ return "affinity";
+ case PATH_PHYSICAL:
+ return "physical";
+ case PATH_DEVICE:
+ return "device";
+ case PATH_POWER:
+ return "power";
+ }
+ return "Unknown";
+}
+
+/*
+ * NB: buffer is assumped to be MAX_PATH_SIZE
+ */
+static char *format_path(struct entity_path *path, char *buffer)
+{
+ int elements = path->type_size & PATH_ELEMENTS_MASK;
+ int i, offset = 0;
+
+ for (i = 0; i < elements; i++) {
+ const struct path_element *e = &path->path_elements[i];
+
+ offset += sprintf(buffer + offset, "/%s%d",
+ target_type_to_str(e->target_type),
+ e->instance);
+ }
+
+ return buffer;
+}
+
+/*
+ * parses a Path string into the entity_path structured provided.
+ *
+ * str - In param, String to parse
+ * parsed - Out param, resultant entity_path
+ *
+ * e.g.
+ *
+ * "/Sys0/Node0/Proc1" -> {
+ * type_size = 0x23,
+ *
+ * path_element[0] = {0, 0}
+ * path_element[1] = {1, 0}
+ * path_element[2] = {2, 1}
+ * }
+ */
+static int parse_path(const char *str, struct entity_path *parsed)
+{
+ int unit_count = 0;
+
+ memset(parsed, 0, sizeof(*parsed));
+
+ while (*str != '\0') {
+ int unit_id = str_to_target_type(++str); /* ++ skips the '/' */
+ long instance;
+ char *end;
+ size_t len;
+
+ if (unit_count > MAX_PATH_ELEMENTS - 1) {
+ fprintf(stderr, "Path has more than 10 components!\n");
+ return -1;
+ }
+
+ /* find the type Id of this component */
+ if (unit_id < 0) { /* unknown unit, bail out */
+ fprintf(stderr, "Unknown unit at: '%s'\n", str);
+ return -1;
+ }
+
+ parsed->path_elements[unit_count].target_type = unit_id;
+
+ /* now parse the instance # */
+ len = strlen(target_type_to_str(unit_id));
+ instance = strtol(str + len, &end, 10);
+
+ if (!isdigit(*(str + len))) {
+ fprintf(stderr, "Missing instance number after '%s'\n",
+ str);
+ return -1;
+ }
+
+ if (*end != '\0' && *end != '/') {
+ fprintf(stderr, "Unable to parse instance after '%s'\n",
+ str);
+ return -1;
+ }
+
+ if (instance > 255 || instance < 0) {
+ fprintf(stderr,
+ "Instance %ld is invalid. Must be 0 to 255\n",
+ instance);
+ return -1;
+ }
+ parsed->path_elements[unit_count].instance = instance;
+
+ str = end;
+ unit_count++;
+ }
+
+ /*
+ * We assume the path is a physical path because every gard record I've
+ * seen so far uses them. We might need to fix this later on, but lets
+ * cross the bridge when we have to.
+ */
+ parsed->type_size = (unit_count & 0xf) |
+ (PATH_PHYSICAL << PATH_TYPE_SHIFT);
+
+ return 0;
+}
+
+static struct gard_record blank_record;
+
+static bool is_valid_record(struct gard_record *g)
+{
+ return memcmp(&blank_record, g, sizeof(*g));
+}
+
+static int do_iterate(struct gard_ctx *ctx,
+ int (*func)(struct gard_ctx *ctx, int pos,
+ struct gard_record *gard, void *priv),
+ void *priv)
+{
+ int rc = 0;
+ unsigned int i;
+ struct gard_record gard, null_gard;
+
+ memset(&null_gard, UINT_MAX, sizeof(gard));
+ for (i = 0; i * sizeof(gard) < ctx->gard_data_len && rc == 0; i++) {
+ memset(&gard, 0, sizeof(gard));
+
+ rc = blocklevel_read(ctx->bl, ctx->gard_data_pos + (i * sizeof(gard)),
+ &gard, sizeof(gard));
+ /* It isn't super clear what constitutes the end, this should do */
+ if (rc || memcmp(&gard, &null_gard, sizeof(gard)) == 0)
+ break;
+
+ rc = func(ctx, i, &gard, priv);
+ }
+
+ return rc;
+}
+
+/*
+ * read the next guard record into the supplied buffer (gard)
+ *
+ * returns the record id (nb: 1 based not zero)
+ *
+ */
+static int __gard_next(struct gard_ctx *ctx, int pos, struct gard_record *gard, int *rc)
+{
+ uint32_t offset = pos * sizeof(*gard);
+
+ if (offset > ctx->gard_data_len) /* too big */
+ return -1;
+
+ /* you lose error handling information, *gruble* */
+ memset(gard, 0, sizeof(*gard));
+ *rc = blocklevel_read(ctx->bl, ctx->gard_data_pos + offset,
+ gard, sizeof(*gard));
+
+ if (!is_valid_record(gard))
+ return -1;
+
+ if (*rc)
+ return -1;
+
+ return pos;
+}
+
+#define for_each_gard(ctx, pos, gard, rc) \
+ for (pos = __gard_next(ctx, 0, gard, rc); \
+ pos >= 0; pos = __gard_next(ctx, ++pos, gard, rc))
+
+static int count_records(struct gard_ctx *ctx)
+{
+ struct gard_record record;
+ int rc, pos, count = 0;
+
+ for_each_gard(ctx, pos, &record, &rc)
+ count++;
+
+ return rc ? rc : count;
+}
+
+static int count_valid_records(struct gard_ctx *ctx)
+{
+ struct gard_record record;
+ int rc, pos, count = 0;
+
+ for_each_gard(ctx, pos, &record, &rc)
+ count++;
+
+ return rc ? rc : count;
+}
+
+static size_t find_longest_path(struct gard_ctx *ctx)
+{
+ char scratch[MAX_PATH_SIZE];
+ struct gard_record gard;
+ size_t len, longest = 0;
+ int rc, pos;
+
+ for_each_gard(ctx, pos, &gard, &rc) {
+ len = strlen(format_path(&gard.target_id, scratch));
+ if (len > longest)
+ longest = len;
+ }
+
+ return longest;
+}
+
+static void draw_ruler(char c, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ putchar(c);
+ putchar('\n');
+}
+
+static int do_list(struct gard_ctx *ctx, int argc __attribute__((unused)),
+ char **argv __attribute__((unused)))
+{
+ /* This header matches the line formatting above in do_list_i() */
+ const char *header = " ID | Error | Type | Path";
+ size_t ruler_size;
+ char scratch[MAX_PATH_SIZE];
+ struct gard_record gard;
+ int rc = 0, pos;
+
+ /* No entries */
+ if (count_valid_records(ctx) == 0) {
+ printf("No GARD entries to display\n");
+ return 0;
+ }
+
+ puts(header);
+
+ ruler_size = strlen(header) + find_longest_path(ctx);
+ draw_ruler('-', ruler_size);
+
+ for_each_gard(ctx, pos, &gard, &rc) {
+ printf(" %08x | %08x | %-10s | %s%s\n",
+ be32toh(gard.record_id),
+ be32toh(gard.errlog_eid),
+ deconfig_reason_str(gard.error_type),
+ format_path(&gard.target_id, scratch),
+ gard.record_id == 0xffffffff ? " *CLEARED*" : "");
+ }
+
+ draw_ruler('=', ruler_size);
+
+ return rc;
+}
+
+static int do_show_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv)
+{
+ uint32_t id;
+
+ (void)ctx;
+ (void)pos;
+
+ if (!priv || !gard)
+ return -1;
+
+ id = *(uint32_t *)priv;
+
+ if (be32toh(gard->record_id) == id) {
+ unsigned int count, i;
+
+ printf("Record ID: 0x%08x%s\n", id, id == 0xffffffff ? " *CLEARED*" : "");
+ printf("========================\n");
+ printf("Error ID: 0x%08x\n", be32toh(gard->errlog_eid));
+ printf("Error Type: %s (0x%02x)\n",
+ deconfig_reason_str(gard->error_type),
+ gard->error_type);
+ printf("Path Type: %s\n", path_type_to_str(gard->target_id.type_size >> PATH_TYPE_SHIFT));
+ count = gard->target_id.type_size & PATH_ELEMENTS_MASK;
+ for (i = 0; i < count && i < MAX_PATH_ELEMENTS; i++)
+ printf("%*c%s, Instance #%d\n", i + 1, '>', target_type_to_str(gard->target_id.path_elements[i].target_type),
+ gard->target_id.path_elements[i].instance);
+ }
+
+ return 0;
+}
+
+static int do_show(struct gard_ctx *ctx, int argc, char **argv)
+{
+ uint32_t id;
+ int rc;
+
+ if (argc != 2) {
+ fprintf(stderr, "%s option requires a GARD record\n", argv[0]);
+ return -1;
+ }
+
+ id = strtoul(argv[1], NULL, 16);
+
+ rc = do_iterate(ctx, &do_show_i, &id);
+
+ return rc;
+}
+
+static int do_clear_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv)
+{
+ int largest, rc = 0;
+ char *buf;
+ struct gard_record null_gard;
+
+ if (!gard || !ctx || !priv)
+ return -1;
+
+ /* Not this one */
+ if (be32toh(gard->record_id) != *(uint32_t *)priv)
+ return 0;
+
+ memset(&null_gard, 0xFF, sizeof(null_gard));
+
+ largest = count_records(ctx);
+
+ printf("Clearing gard record 0x%08x...", be32toh(gard->record_id));
+
+ if (largest < 0 || pos > largest) {
+ /* Something went horribly wrong */
+ fprintf(stderr, "largest index out of range %d\n", largest);
+ return -1;
+ }
+
+ if (pos < largest) {
+ /* We're not clearing the last record, shift all the records up */
+ int buf_len = ((largest - pos) * sizeof(struct gard_record));
+ int buf_pos = ctx->gard_data_pos + ((pos + 1) * sizeof(struct gard_record));
+ buf = malloc(buf_len);
+ if (!buf)
+ return -ENOMEM;
+
+ rc = blocklevel_read(ctx->bl, buf_pos, buf, buf_len);
+ if (rc) {
+ free(buf);
+ fprintf(stderr, "Couldn't read from flash at 0x%08x for len 0x%08x\n", buf_pos, buf_len);
+ return rc;
+ }
+
+ rc = blocklevel_smart_write(ctx->bl, buf_pos - sizeof(*gard), buf, buf_len);
+ free(buf);
+ if (rc) {
+ fprintf(stderr, "Couldn't write to flash at 0x%08x for len 0x%08x\n",
+ buf_pos - (int) sizeof(struct gard_record), buf_len);
+ return rc;
+ }
+ }
+
+ /* Now wipe the last record */
+ rc = blocklevel_smart_write(ctx->bl, ctx->gard_data_pos + (largest * sizeof(null_gard)),
+ &null_gard, sizeof(null_gard));
+ printf("done\n");
+
+ return rc;
+}
+
+static int reset_partition(struct gard_ctx *ctx)
+{
+ int no_ecc_len = (ctx->gard_data_len / 9) * 8;
+ struct gard_record *gard;
+ int rc = 0;
+
+ gard = malloc(ctx->gard_data_len);
+ if (!gard) {
+ return FLASH_ERR_MALLOC_FAILED;
+ }
+ memset(gard, 0xFF, ctx->gard_data_len);
+
+ rc = blocklevel_smart_erase(ctx->bl, ctx->gard_data_pos, ctx->gard_data_len);
+ if (rc) {
+ fprintf(stderr, "Couldn't erase the gard partition. Bailing out\n");
+ goto out;
+ }
+
+ rc = blocklevel_write(ctx->bl, ctx->gard_data_pos, gard, no_ecc_len);
+ if (rc)
+ fprintf(stderr, "Couldn't reset the entire gard partition. Bailing out\n");
+
+out:
+ free(gard);
+ return rc;
+}
+
+static int do_clear(struct gard_ctx *ctx, int argc, char **argv)
+{
+ int rc;
+ uint32_t id;
+
+ if (argc != 2) {
+ fprintf(stderr, "%s option requires a GARD record or 'all'\n", argv[0]);
+ return -1;
+ }
+
+ if (strncmp(argv[1], "all", strlen("all")) == 0) {
+ printf("Clearing the entire gard partition...");
+ fflush(stdout);
+ rc = reset_partition(ctx);
+ printf("done\n");
+ } else {
+ id = strtoul(argv[1], NULL, 16);
+ rc = do_iterate(ctx, do_clear_i, &id);
+ }
+
+ return rc;
+}
+
+static int do_create(struct gard_ctx *ctx, int argc, char **argv)
+{
+ int rc, pos, max_id = 0, last_pos = 0;
+ struct gard_record gard;
+ struct entity_path path;
+
+ if (argc < 2) {
+ fprintf(stderr, "create requires path to gard\n");
+ fprintf(stderr, "e.g.\n");
+ fprintf(stderr, " /Sys0/Node0/Proc0\n");
+ fprintf(stderr, " /Sys0/Node0/DIMM15\n");
+ return -1;
+ }
+
+ if (parse_path(argv[1], &path)) {
+ fprintf(stderr, "Unable to parse path\n");
+ return -1;
+ }
+
+ /* check if we already have a gard record applied to this path */
+ for_each_gard(ctx, pos, &gard, &rc) {
+ if (!memcmp(&path, &gard.target_id, sizeof(path))) {
+ fprintf(stderr,
+ "Unit %s is already GARDed by record %#08x\n",
+ argv[1], be32toh(gard.record_id));
+ return -1;
+ }
+
+ /*
+ * Keep track of the largest record ID seen so far,
+ * we'll give the new record the max + 1 to ensure
+ * that it's unique
+ */
+ if (be32toh(gard.record_id) > max_id)
+ max_id = be32toh(gard.record_id);
+
+ last_pos++;
+ }
+
+ /* do we have an empty record to write into? */
+ if (!rc && !is_valid_record(&gard)) {
+ int offset = last_pos * sizeof(gard);
+
+ memset(&gard, 0xff, sizeof(gard));
+
+ gard.record_id = be32toh(max_id + 1);
+ gard.error_type = GARD_MANUAL;
+ gard.target_id = path;
+ gard.errlog_eid = 0x0;
+
+ if (offset > ctx->gard_data_len - sizeof(gard)) {
+ fprintf(stderr, "No space in GUARD for a new record\n");
+ return -1;
+ }
+
+ rc = blocklevel_smart_write(ctx->bl,
+ ctx->gard_data_pos + offset, &gard, sizeof(gard));
+ }
+
+ return rc;
+}
+
+static int check_gard_partition(struct gard_ctx *ctx)
+{
+ int rc;
+ struct gard_record gard;
+ char msg[2];
+
+ if (ctx->gard_data_len == 0 || ctx->gard_data_len % sizeof(struct gard_record) != 0)
+ /* Just warn for now */
+ fprintf(stderr, "The %s partition doesn't appear to be an exact multiple of"
+ "gard records in size: %zd vs %u (or partition is zero in length)\n",
+ FLASH_GARD_PART, sizeof(struct gard_record), ctx->gard_data_len);
+
+ /*
+ * Attempt to read the first record, nothing can really operate if the
+ * first record is dead. There (currently) isn't a way to validate more
+ * than ECC correctness.
+ */
+ rc = blocklevel_read(ctx->bl, ctx->gard_data_pos, &gard, sizeof(gard));
+ if (rc == FLASH_ERR_ECC_INVALID) {
+ fprintf(stderr, "The data at the GUARD partition does not appear to be valid gard data\n");
+ fprintf(stderr, "Clear the entire GUARD partition? [y/N]\n");
+ if (fgets(msg, sizeof(msg), stdin) == NULL) {
+ fprintf(stderr, "Couldn't read from standard input\n");
+ return -1;
+ }
+ if (msg[0] == 'y') {
+ rc = reset_partition(ctx);
+ if (rc) {
+ fprintf(stderr, "Couldn't reset the GUARD partition. Bailing out\n");
+ return rc;
+ }
+ }
+ /*
+ * else leave rc as is so that the main bails out, not going to be
+ * able to do sensible anyway
+ */
+ }
+ return rc;
+}
+
+__attribute__ ((unused))
+static int do_nop(struct gard_ctx *ctx, int argc, char **argv)
+{
+ (void)ctx;
+ (void)argc;
+ fprintf(stderr, "Unimplemented action '%s'\n", argv[0]);
+ return EXIT_SUCCESS;
+}
+
+struct {
+ const char *name;
+ const char *desc;
+ int (*fn)(struct gard_ctx *, int, char **);
+} actions[] = {
+ { "list", "List current GARD records", do_list },
+ { "show", "Show details of a GARD record", do_show },
+ { "clear", "Clear GARD records", do_clear },
+ { "create", "Create a GARD record", do_create },
+};
+
+static void print_version(void)
+{
+ printf("Open-Power GARD tool %s\n", version);
+}
+
+static void usage(const char *progname)
+{
+ unsigned int i;
+
+ print_version();
+ fprintf(stderr, "Usage: %s [-a -e -f <file> -p] <command> [<args>]\n\n",
+ progname);
+ fprintf(stderr, "-8 --p8\n");
+ fprintf(stderr, "-9 --p9\n");
+ fprintf(stderr, "-0 --p10\n\tSet the processor generation\n\n");
+ fprintf(stderr, "-e --ecc\n\tForce reading/writing with ECC bytes.\n\n");
+ fprintf(stderr, "-f --file <file>\n\tDon't search for MTD device,"
+ " read from <file>.\n\n");
+ fprintf(stderr, "-p --part\n\tUsed in conjunction with -f to specify"
+ " that just\n");
+ fprintf(stderr, "\tthe GUARD partition is in <file> and libffs\n");
+ fprintf(stderr, "\tshouldn't be used.\n\n");
+
+
+ fprintf(stderr, "Where <command> is one of:\n\n");
+
+ for (i = 0; i < ARRAY_SIZE(actions); i++) {
+ fprintf(stderr, "\t%-7s\t%s\n",
+ actions[i].name, actions[i].desc);
+ }
+}
+
+static bool is_fsp(void)
+{
+ return access(FDT_FSP_NODE, F_OK) == 0;
+}
+
+static struct option global_options[] = {
+ { "file", required_argument, 0, 'f' },
+ { "part", no_argument, 0, 'p' },
+ { "ecc", no_argument, 0, 'e' },
+ { "p8", no_argument, 0, '8' },
+ { "p9", no_argument, 0, '9' },
+ { "p10", no_argument, 0, '0' },
+ { 0 },
+};
+static const char *global_optstring = "+ef:p890";
+
+int main(int argc, char **argv)
+{
+ const char *action, *progname;
+ char *filename = NULL;
+ struct gard_ctx _ctx, *ctx;
+ uint64_t bl_size;
+ int rc, i = 0;
+ bool part = 0;
+ bool ecc = 0;
+
+ progname = argv[0];
+
+ ctx = &_ctx;
+ memset(ctx, 0, sizeof(*ctx));
+ memset(&blank_record, 0xff, sizeof(blank_record));
+
+ /* process global options */
+ for (;;) {
+ int c;
+
+ c = getopt_long(argc, argv, global_optstring, global_options,
+ NULL);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'e':
+ ecc = true;
+ break;
+ case 'f':
+ /* If they specify -f twice */
+ free(filename);
+
+ filename = strdup(optarg);
+ if (!filename) {
+ fprintf(stderr, "Out of memory\n");
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'p':
+ part = true;
+ break;
+ case '8':
+ set_chip_gen(p8_chip_units);
+ break;
+ case '9':
+ set_chip_gen(p9_chip_units);
+ break;
+ case '0':
+ set_chip_gen(p10_chip_units);
+ break;
+ case '?':
+ usage(progname);
+ rc = EXIT_FAILURE;
+ goto out_free;
+ }
+ }
+
+
+ if (is_fsp() && !filename) {
+ fprintf(stderr, "This is the OpenPower gard tool which does "
+ "not support FSP systems\n");
+ return EXIT_FAILURE;
+ }
+
+
+ /*
+ * It doesn't make sense to specify that we have the gard partition but
+ * read from flash
+ */
+ if (part && !filename) {
+ usage(progname);
+ fprintf(stderr, "-p only makes sense when used with -f!\n");
+ return EXIT_FAILURE;
+ }
+
+ /* do we have a command? */
+ if (optind == argc) {
+ usage(progname);
+ rc = EXIT_FAILURE;
+ goto out_free;
+ }
+
+ argc -= optind;
+ argv += optind;
+ action = argv[0];
+
+#ifdef __arm__
+ /*
+ * HACK: Look for a vPNOR GUARD file if we haven't been given anything
+ * explitly. If it exists then we can safely assume that:
+ * a) The host is a P9
+ * b) The file is ECC protected
+ * c) The file is a bare partition.
+ *
+ * This is a stupid hack, but there's not other sane place for it.
+ * arch_init_flash() always looks for a FFS formatted PNOR when
+ * filename is NULL
+ */
+ if (!filename) {
+ struct stat buf;
+
+ if (!stat(VPNOR_GARD_FILE, &buf)) {
+ filename = strdup(VPNOR_GARD_FILE);
+ /* BUG: This ignores the command line settings */
+ part = true;
+ ecc = true;
+ } else if (!stat(VPNOR_GARD_DIR, &buf)) {
+ printf(VPNOR_GARD_FILE" is missing. Nothing to do\n");
+ return 0;
+ }
+ }
+#endif
+
+ if (!chip_units)
+ guess_chip_gen();
+
+ /*
+ * Force libflash to do flash accesses via the MTD. Direct mode is
+ * generally unsafe since it fiddles with the flash controller state
+ * underneath the kernel. Anyone who needs direct mode can use pflash
+ * instead.
+ */
+ arch_flash_access(ctx->bl, PNOR_MTD);
+
+ if (arch_flash_init(&(ctx->bl), filename, true)) {
+ /* Can fail for a few ways, most likely couldn't open MTD device */
+ fprintf(stderr, "Can't open %s\n", filename ? filename : "MTD Device. Are you root?");
+ rc = EXIT_FAILURE;
+ goto out_free;
+ }
+
+ rc = blocklevel_get_info(ctx->bl, NULL, &bl_size, NULL);
+ if (rc)
+ goto out;
+
+ if (bl_size > UINT_MAX) {
+ fprintf(stderr, "MTD device bigger than %i: size: %" PRIu64 "\n",
+ UINT_MAX, bl_size);
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+ ctx->f_size = bl_size;
+
+ if (!part) {
+ rc = ffs_init(0, ctx->f_size, ctx->bl, &ctx->ffs, 1);
+ if (rc)
+ goto out;
+
+ rc = ffs_lookup_part(ctx->ffs, FLASH_GARD_PART, &ctx->gard_part_idx);
+ if (rc)
+ goto out;
+
+ rc = ffs_part_info(ctx->ffs, ctx->gard_part_idx, NULL, &(ctx->gard_data_pos),
+ &(ctx->gard_data_len), NULL, NULL);
+ if (rc)
+ goto out;
+ } else {
+ if (ecc) {
+ rc = blocklevel_ecc_protect(ctx->bl, 0, ctx->f_size);
+ if (rc)
+ goto out;
+ }
+
+ ctx->gard_data_pos = 0;
+ ctx->gard_data_len = ctx->f_size;
+ }
+
+ rc = check_gard_partition(ctx);
+ if (rc) {
+ fprintf(stderr, "Does not appear to be sane gard data\n");
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(actions); i++) {
+ if (!strcmp(actions[i].name, action)) {
+ rc = actions[i].fn(ctx, argc, argv);
+ break;
+ }
+ }
+
+out:
+ if (ctx->ffs)
+ ffs_close(ctx->ffs);
+
+ file_exit_close(ctx->bl);
+
+ if (i == ARRAY_SIZE(actions)) {
+ fprintf(stderr, "%s: '%s' isn't a valid command\n", progname, action);
+ usage(progname);
+ rc = EXIT_FAILURE;
+ goto out_free;
+ }
+
+ if (rc > 0) {
+ show_flash_err(rc);
+ if (filename && rc == FFS_ERR_BAD_MAGIC)
+ fprintf(stderr, "Maybe you didn't give a full flash image file?\nDid you mean '--part'?\n");
+ }
+
+out_free:
+ free(filename);
+ return rc;
+}
diff --git a/roms/skiboot/external/gard/gard.h b/roms/skiboot/external/gard/gard.h
new file mode 100644
index 000000000..d59c2a0de
--- /dev/null
+++ b/roms/skiboot/external/gard/gard.h
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2017 IBM Corp.
+ */
+
+#include <stdint.h>
+
+#define MAX_PATH_ELEMENTS 10
+#define PATH_TYPE_SHIFT 4
+#define PATH_ELEMENTS_MASK (0x0F)
+
+/*
+ * Sourced from hostboot: src/include/usr/hwas/common/hwasCallout.H
+ */
+enum gard_reason {
+ GARD_NO_REASON = 0x0,
+ GARD_MANUAL = 0xD2,
+ GARD_UNRECOVERABLE = 0xE2,
+ GARD_FATAL = 0xE3,
+ GARD_PREDICTIVE = 0xE6,
+ GARD_POWER = 0xE9,
+ GARD_HYP = 0xEA,
+ GARD_RECONFIG = 0xEB,
+
+ /*
+ * This should only occur if the GUARD partition isn't correctly
+ * programmed with ECC bits.
+ */
+ GARD_VOID = 0xFF,
+};
+
+/* see src/include/usr/targeting/common/entitypath.H */
+enum path_type {
+ PATH_NA = 0x00,
+ PATH_AFFINITY = 0x01,
+ PATH_PHYSICAL = 0x02,
+ PATH_DEVICE = 0x03,
+ PATH_POWER = 0x04,
+};
+
+struct path_element {
+ uint8_t target_type;
+ uint8_t instance;
+} __attribute__((packed));
+
+struct entity_path {
+ /* First 4 bits are a path_type enum */
+ /* Second 4 bits are the amount of path_elements */
+ uint8_t type_size;
+ struct path_element path_elements[MAX_PATH_ELEMENTS];
+
+} __attribute__((packed));
+
+/* From hostboot: src/include/usr/hwas/common/deconfigGard.H:GardRecord */
+struct gard_record {
+ uint32_t record_id;
+ struct entity_path target_id;
+ uint8_t pad0[3]; /* compiler dependent padding */
+ uint32_t errlog_eid;
+ uint8_t error_type;
+ uint8_t resource_recovery;
+ uint8_t pad1[6];
+} __attribute__((packed));
+
+#define MAX_PATH_SIZE 420
+
+struct chip_unit_desc {
+ int type;
+ const char *desc;
+};
+
+extern const struct chip_unit_desc *chip_units;
+extern const struct chip_unit_desc p8_chip_units[];
+extern const struct chip_unit_desc p9_chip_units[];
+extern const struct chip_unit_desc p10_chip_units[];
diff --git a/roms/skiboot/external/gard/opal-gard.1 b/roms/skiboot/external/gard/opal-gard.1
new file mode 100644
index 000000000..65b214470
--- /dev/null
+++ b/roms/skiboot/external/gard/opal-gard.1
@@ -0,0 +1,30 @@
+.TH opal-gard 1 "19 June 2015"
+.SH NAME
+opal-gard \- GUARD Partition manipulation tool for OpenPower hardware
+.SH SYNOPSIS
+\fBopal-gard\fP [ \-e | \-f \fIfile\fP | \-p ]
+\fIcommand\fP
+.SH DESCRIPTION
+\fBopal-gard\fP allows reading of the GUARD partition on OpenPower hardware though the exposed mtd flash interface. The actual device (usually \fB/dev/mtd0\fR) is automatically detected.
+.SS Options
+\fB\-e, \-\-ecc\fP
+Force reading/writing with ECC bytes.
+.TP
+\fB\-f, \-\-file\fP \fIfile\fR
+Don't search for the \fB/dev/mtd\fR device, use \fIfile\fP instead.
+.TP
+.TP
+\fB\-p, \-\-part\fP
+Used in conjunction with \-f to specify that just the GUARD partition is in the \fIfile\fR and that libffs shouldn't be used.
+.SS Commands
+\fIcommand\fP
+may be one of the following
+.TP
+\fBlist\fP
+List current GARD records
+.TP
+\fBshow\fP
+Show details of a GARD record
+.TP
+\fBclear\fP
+Clear GARD records
diff --git a/roms/skiboot/external/gard/rules.mk b/roms/skiboot/external/gard/rules.mk
new file mode 100644
index 000000000..92ceb9bc0
--- /dev/null
+++ b/roms/skiboot/external/gard/rules.mk
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+.DEFAULT_GOAL := all
+
+override CFLAGS += -O2 -Wall -Werror -Wno-stringop-truncation -I.
+OBJS = version.o gard.o units.o
+LIBFLASH_FILES := libflash.c libffs.c ecc.c blocklevel.c file.c
+LIBFLASH_OBJS := $(addprefix libflash-, $(LIBFLASH_FILES:.c=.o))
+LIBFLASH_SRC := $(addprefix libflash/,$(LIBFLASH_FILES))
+CCAN_FILES := list.c
+CCAN_OBJS := $(addprefix ccan-list-, $(CCAN_FILES:.c=.o))
+CCAN_SRC := $(addprefix ccan/list/,$(CCAN_FILES))
+OBJS += $(LIBFLASH_OBJS) $(CCAN_OBJS)
+OBJS += common-arch_flash.o
+EXE = opal-gard
+
+prefix = /usr/local/
+sbindir = $(prefix)/sbin
+datadir = $(prefix)/share
+mandir = $(datadir)/man
+
+#This will only be unset if we're running out of git tree,
+#../../make_version.sh is garanteed to exist that way
+GARD_VERSION ?= $(shell ../../make_version.sh $(EXE))
+
+version.c: .version
+ @(if [ "a$(GARD_VERSION)" = "a" ]; then \
+ echo "#error You need to set GARD_VERSION environment variable" > $@ ;\
+ else \
+ echo "const char version[] = \"$(GARD_VERSION)\";" ;\
+ fi) > $@
+
+%.o : %.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+$(LIBFLASH_SRC): | links
+$(CCAN_SRC): | links
+
+$(LIBFLASH_OBJS): libflash-%.o : libflash/%.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+$(CCAN_OBJS): ccan-list-%.o: ccan/list/%.c
+ $(Q_CC)$(CC) $(CFLAGS) -c $< -o $@
+
+$(EXE): $(OBJS)
+ $(CC) $(LDFLAGS) $(CFLAGS) $^ -o $@
+
+install: all
+ install -D $(EXE) $(DESTDIR)$(sbindir)/$(EXE)
+ install -D -m 0644 $(EXE).1 $(DESTDIR)$(mandir)/man1/$(EXE).1
+
+
diff --git a/roms/skiboot/external/gard/test/add_test.sh b/roms/skiboot/external/gard/test/add_test.sh
new file mode 100755
index 000000000..6523dffc6
--- /dev/null
+++ b/roms/skiboot/external/gard/test/add_test.sh
@@ -0,0 +1,67 @@
+#!/bin/bash -uex
+#
+# this is a really dumb script for auto-generating test cases from known good-data
+#
+# usage: ./add_test <pass|fail> <inputfile> <testname> [opal-gard subcommand]
+#
+# e.g.
+# ./add_test.sh fail blank.bin create-bad-instance create /sys256
+# ./add_test.sh pass blank.bin create-normal create /sys0/node0/proc0
+#
+# this will generate a test script file and writes the stdout/stderr of the command
+# to the respective files.
+#
+
+cd $(dirname $(realpath $0))/../
+echo $PWD
+
+if [ "$1" = "pass" ]; then
+ check='if [ "$?" -ne 0 ]; then'
+ test_type="pass"
+else
+ check='if [ "$?" -eq 0 ]; then'
+ test_type="fails"
+fi
+shift
+
+file="test/files/$1"
+if [ ! -f "$file" ]; then
+ echo "test file not found!"
+ exit 1;
+fi
+shift
+
+name="$1"
+shift
+
+max="$(ls test/tests/ -1|sort -n | sed 's@\(..\).*@\1@' | tail -1 | sed s@^0*@@)"
+num="$(printf %02d $((max + 1)))"
+
+echo "Adding: $num-$name"
+
+# where we will write the script file
+script_file="test/tests/$num-$name"
+
+echo "making $num-$name: f=$script_file, normally $test_type, cmd='$*'"
+
+cat > $script_file <<EOF
+#! /bin/sh
+
+run_binary "./opal-gard" "-9 -p -e -f $file $*"
+$check
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
+EOF
+
+# generate the .out and .err files
+stdout_file="test/results/$num-$name.out"
+stderr_file="test/results/$num-$name.err"
+
+test_input="$name-$num-input"
+cp $file $test_input
+./opal-gard -f $test_input -p -e $* 2>$stderr_file >$stdout_file
+rm -f $test_input
diff --git a/roms/skiboot/external/gard/test/files/blank.bin b/roms/skiboot/external/gard/test/files/blank.bin
new file mode 100644
index 000000000..c6cc6b8c2
--- /dev/null
+++ b/roms/skiboot/external/gard/test/files/blank.bin
Binary files differ
diff --git a/roms/skiboot/external/gard/test/files/data-p9.bin b/roms/skiboot/external/gard/test/files/data-p9.bin
new file mode 100644
index 000000000..f2141657a
--- /dev/null
+++ b/roms/skiboot/external/gard/test/files/data-p9.bin
Binary files differ
diff --git a/roms/skiboot/external/gard/test/files/data1.bin b/roms/skiboot/external/gard/test/files/data1.bin
new file mode 100644
index 000000000..aa5bf1437
--- /dev/null
+++ b/roms/skiboot/external/gard/test/files/data1.bin
Binary files differ
diff --git a/roms/skiboot/external/gard/test/make-check-test b/roms/skiboot/external/gard/test/make-check-test
new file mode 100755
index 000000000..6b9e5db88
--- /dev/null
+++ b/roms/skiboot/external/gard/test/make-check-test
@@ -0,0 +1 @@
+make -C external/gard/ check
diff --git a/roms/skiboot/external/gard/test/results/00-list.err b/roms/skiboot/external/gard/test/results/00-list.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/00-list.err
diff --git a/roms/skiboot/external/gard/test/results/00-list.out b/roms/skiboot/external/gard/test/results/00-list.out
new file mode 100644
index 000000000..0e20b4aef
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/00-list.out
@@ -0,0 +1,5 @@
+ ID | Error | Type | Path
+-----------------------------------------------------------
+ 00000001 | 90000015 | Predictive | /Sys0/Node0/Proc0
+ 00000002 | 90000016 | Predictive | /Sys0/Node0/Membuf0
+===========================================================
diff --git a/roms/skiboot/external/gard/test/results/01-show_1.err b/roms/skiboot/external/gard/test/results/01-show_1.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/01-show_1.err
diff --git a/roms/skiboot/external/gard/test/results/01-show_1.out b/roms/skiboot/external/gard/test/results/01-show_1.out
new file mode 100644
index 000000000..60fcea264
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/01-show_1.out
@@ -0,0 +1,8 @@
+Record ID: 0x00000001
+========================
+Error ID: 0x90000015
+Error Type: Predictive (0xe6)
+Path Type: physical
+>Sys, Instance #0
+ >Node, Instance #0
+ >Proc, Instance #0
diff --git a/roms/skiboot/external/gard/test/results/02-usage.err b/roms/skiboot/external/gard/test/results/02-usage.err
new file mode 100644
index 000000000..453fcf52f
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/02-usage.err
@@ -0,0 +1,24 @@
+Usage: ./opal-gard [-a -e -f <file> -p] <command> [<args>]
+
+-8 --p8
+-9 --p9
+-0 --p10
+ Set the processor generation
+
+-e --ecc
+ Force reading/writing with ECC bytes.
+
+-f --file <file>
+ Don't search for MTD device, read from <file>.
+
+-p --part
+ Used in conjunction with -f to specify that just
+ the GUARD partition is in <file> and libffs
+ shouldn't be used.
+
+Where <command> is one of:
+
+ list List current GARD records
+ show Show details of a GARD record
+ clear Clear GARD records
+ create Create a GARD record
diff --git a/roms/skiboot/external/gard/test/results/02-usage.out b/roms/skiboot/external/gard/test/results/02-usage.out
new file mode 100644
index 000000000..c862167b4
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/02-usage.out
@@ -0,0 +1 @@
+Open-Power GARD tool VERSION
diff --git a/roms/skiboot/external/gard/test/results/03-show_1-p9.err b/roms/skiboot/external/gard/test/results/03-show_1-p9.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/03-show_1-p9.err
diff --git a/roms/skiboot/external/gard/test/results/03-show_1-p9.out b/roms/skiboot/external/gard/test/results/03-show_1-p9.out
new file mode 100644
index 000000000..c9ae5b21c
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/03-show_1-p9.out
@@ -0,0 +1,10 @@
+Record ID: 0x00000001
+========================
+Error ID: 0x90000007
+Error Type: Fatal (0xe3)
+Path Type: physical
+>Sys, Instance #0
+ >Node, Instance #0
+ >Proc, Instance #0
+ >EQ, Instance #1
+ >EX, Instance #0
diff --git a/roms/skiboot/external/gard/test/results/04-create-bad-instance.err b/roms/skiboot/external/gard/test/results/04-create-bad-instance.err
new file mode 100644
index 000000000..2a8da501a
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/04-create-bad-instance.err
@@ -0,0 +1,2 @@
+Instance 256 is invalid. Must be 0 to 255
+Unable to parse path
diff --git a/roms/skiboot/external/gard/test/results/04-create-bad-instance.out b/roms/skiboot/external/gard/test/results/04-create-bad-instance.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/04-create-bad-instance.out
diff --git a/roms/skiboot/external/gard/test/results/05-create-bad-unit.err b/roms/skiboot/external/gard/test/results/05-create-bad-unit.err
new file mode 100644
index 000000000..aa1af82f5
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/05-create-bad-unit.err
@@ -0,0 +1,2 @@
+Unknown unit at: 'doesnt_exist0'
+Unable to parse path
diff --git a/roms/skiboot/external/gard/test/results/05-create-bad-unit.out b/roms/skiboot/external/gard/test/results/05-create-bad-unit.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/05-create-bad-unit.out
diff --git a/roms/skiboot/external/gard/test/results/06-create-long-path.err b/roms/skiboot/external/gard/test/results/06-create-long-path.err
new file mode 100644
index 000000000..f79a3bda9
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/06-create-long-path.err
@@ -0,0 +1,2 @@
+Path has more than 10 components!
+Unable to parse path
diff --git a/roms/skiboot/external/gard/test/results/06-create-long-path.out b/roms/skiboot/external/gard/test/results/06-create-long-path.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/06-create-long-path.out
diff --git a/roms/skiboot/external/gard/test/results/07-create-slash.err b/roms/skiboot/external/gard/test/results/07-create-slash.err
new file mode 100644
index 000000000..6fd687604
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/07-create-slash.err
@@ -0,0 +1,2 @@
+Unknown unit at: ''
+Unable to parse path
diff --git a/roms/skiboot/external/gard/test/results/07-create-slash.out b/roms/skiboot/external/gard/test/results/07-create-slash.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/07-create-slash.out
diff --git a/roms/skiboot/external/gard/test/results/08-create-duplicate.err b/roms/skiboot/external/gard/test/results/08-create-duplicate.err
new file mode 100644
index 000000000..4c4073953
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/08-create-duplicate.err
@@ -0,0 +1 @@
+Unit /Sys0/Node0/Membuf0 is already GARDed by record 0x000002
diff --git a/roms/skiboot/external/gard/test/results/08-create-duplicate.out b/roms/skiboot/external/gard/test/results/08-create-duplicate.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/08-create-duplicate.out
diff --git a/roms/skiboot/external/gard/test/results/09-create-last-unit.err b/roms/skiboot/external/gard/test/results/09-create-last-unit.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/09-create-last-unit.err
diff --git a/roms/skiboot/external/gard/test/results/09-create-last-unit.out b/roms/skiboot/external/gard/test/results/09-create-last-unit.out
new file mode 100644
index 000000000..62cfcbbd0
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/09-create-last-unit.out
@@ -0,0 +1,4 @@
+ ID | Error | Type | Path
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 00000001 | 00000000 | Manual | /MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255
+================================================================================================================================================================
diff --git a/roms/skiboot/external/gard/test/results/10-clear-single.err b/roms/skiboot/external/gard/test/results/10-clear-single.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/10-clear-single.err
diff --git a/roms/skiboot/external/gard/test/results/10-clear-single.out b/roms/skiboot/external/gard/test/results/10-clear-single.out
new file mode 100644
index 000000000..904e61a56
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/10-clear-single.out
@@ -0,0 +1,7 @@
+Clearing the entire gard partition...done
+ ID | Error | Type | Path
+---------------------------------------------------------
+ 00000001 | 00000000 | Manual | /Sys0/Node0/Proc1
+=========================================================
+Clearing gard record 0x00000001...done
+No GARD entries to display
diff --git a/roms/skiboot/external/gard/test/results/11-clear-first.err b/roms/skiboot/external/gard/test/results/11-clear-first.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/11-clear-first.err
diff --git a/roms/skiboot/external/gard/test/results/11-clear-first.out b/roms/skiboot/external/gard/test/results/11-clear-first.out
new file mode 100644
index 000000000..3b67689ce
--- /dev/null
+++ b/roms/skiboot/external/gard/test/results/11-clear-first.out
@@ -0,0 +1,13 @@
+Clearing the entire gard partition...done
+ ID | Error | Type | Path
+---------------------------------------------------------
+ 00000001 | 00000000 | Manual | /Sys0/Node0/Proc0
+ 00000002 | 00000000 | Manual | /Sys0/Node0/Proc1
+=========================================================
+Clearing gard record 0x00000001...done
+ ID | Error | Type | Path
+---------------------------------------------------------
+ 00000002 | 00000000 | Manual | /Sys0/Node0/Proc1
+=========================================================
+Clearing gard record 0x00000002...done
+No GARD entries to display
diff --git a/roms/skiboot/external/gard/test/test-gard b/roms/skiboot/external/gard/test/test-gard
new file mode 100755
index 000000000..10da35156
--- /dev/null
+++ b/roms/skiboot/external/gard/test/test-gard
@@ -0,0 +1,5 @@
+#! /bin/sh
+
+. test/test.sh
+
+run_tests "test/tests/*" "test/results"
diff --git a/roms/skiboot/external/gard/test/tests/00-list b/roms/skiboot/external/gard/test/tests/00-list
new file mode 100644
index 000000000..982acb3d3
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/00-list
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -8 -f test/files/data1.bin list"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/01-show_1 b/roms/skiboot/external/gard/test/tests/01-show_1
new file mode 100644
index 000000000..48b779eb9
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/01-show_1
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -8 -f test/files/data1.bin show 1"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/02-usage b/roms/skiboot/external/gard/test/tests/02-usage
new file mode 100644
index 000000000..7ef3c04e6
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/02-usage
@@ -0,0 +1,17 @@
+#! /bin/sh
+
+# This test fails on FSP based system. Hence skip this test.
+if [ -d "/proc/device-tree/fsps" ] ; then
+ return 0
+fi
+
+run_binary "./opal-gard"
+if [ "$?" -ne 1 ] ; then
+ fail_test
+fi
+
+strip_version_from_result "gard"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/03-show_1-p9 b/roms/skiboot/external/gard/test/tests/03-show_1-p9
new file mode 100644
index 000000000..2a7b91b7d
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/03-show_1-p9
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -f test/files/data-p9.bin --p9 show 1"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/04-create-bad-instance b/roms/skiboot/external/gard/test/tests/04-create-bad-instance
new file mode 100644
index 000000000..f9070a3dc
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/04-create-bad-instance
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -8 -f test/files/blank.bin create /sys256"
+if [ "$?" -eq 0 ]; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/05-create-bad-unit b/roms/skiboot/external/gard/test/tests/05-create-bad-unit
new file mode 100644
index 000000000..be60aed04
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/05-create-bad-unit
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -f test/files/blank.bin create /doesnt_exist0"
+if [ "$?" -eq 0 ]; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/06-create-long-path b/roms/skiboot/external/gard/test/tests/06-create-long-path
new file mode 100644
index 000000000..f3ffaabf6
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/06-create-long-path
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -8 -f test/files/blank.bin create /sys0/sys0/sys0/sys0/sys0/sys0/sys0/sys0/sys0/sys0/sys0/sys0/"
+if [ "$?" -eq 0 ]; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/07-create-slash b/roms/skiboot/external/gard/test/tests/07-create-slash
new file mode 100644
index 000000000..0e597e346
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/07-create-slash
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -f test/files/blank.bin create /"
+if [ "$?" -eq 0 ]; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/08-create-duplicate b/roms/skiboot/external/gard/test/tests/08-create-duplicate
new file mode 100644
index 000000000..fad5d3499
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/08-create-duplicate
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+run_binary "./opal-gard" "-p -e -8 -f test/files/data1.bin create /Sys0/Node0/Membuf0"
+if [ "$?" -eq 0 ]; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/09-create-last-unit b/roms/skiboot/external/gard/test/tests/09-create-last-unit
new file mode 100644
index 000000000..4d9b319cc
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/09-create-last-unit
@@ -0,0 +1,16 @@
+#! /bin/sh
+
+cp test/files/blank.bin $DATA_DIR/input
+run_binary "./opal-gard" "-9 -p -e -f $DATA_DIR/input create /MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255/MFREFCLK255"
+if [ "$?" -ne 0 ]; then
+ fail_test
+fi
+
+run_binary "./opal-gard" "-9 -p -e -f $DATA_DIR/input list"
+if [ "$?" -ne 0 ]; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/10-clear-single b/roms/skiboot/external/gard/test/tests/10-clear-single
new file mode 100644
index 000000000..3a5f962ec
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/10-clear-single
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+set -e
+
+DATA=$(mktemp)
+
+cleanup() {
+ rm -f $DATA
+}
+
+trap cleanup EXIT
+
+dd if=/dev/zero of=$DATA bs=$((0x1000)) count=5 2>/dev/null
+
+run_binary "./opal-gard" "-p -e -f $DATA clear all"
+run_binary "./opal-gard" "-p -e -f $DATA create /sys0/node0/proc1"
+run_binary "./opal-gard" "-p -e -f $DATA list"
+run_binary "./opal-gard" "-p -e -f $DATA clear 00000001"
+run_binary "./opal-gard" "-p -e -f $DATA list"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/test/tests/11-clear-first b/roms/skiboot/external/gard/test/tests/11-clear-first
new file mode 100644
index 000000000..c59fcc8f0
--- /dev/null
+++ b/roms/skiboot/external/gard/test/tests/11-clear-first
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+set -e
+
+DATA=$(mktemp)
+
+cleanup() {
+ rm -f $DATA
+}
+
+trap cleanup EXIT
+
+dd if=/dev/zero of=$DATA bs=$((0x1000)) count=5 2>/dev/null
+
+run_binary "./opal-gard" "-p -e -f $DATA clear all"
+run_binary "./opal-gard" "-p -e -f $DATA create /sys0/node0/proc0"
+run_binary "./opal-gard" "-p -e -f $DATA create /sys0/node0/proc1"
+run_binary "./opal-gard" "-p -e -f $DATA list"
+run_binary "./opal-gard" "-p -e -f $DATA clear 00000001"
+run_binary "./opal-gard" "-p -e -f $DATA list"
+run_binary "./opal-gard" "-p -e -f $DATA clear 00000002"
+run_binary "./opal-gard" "-p -e -f $DATA list"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/gard/units.c b/roms/skiboot/external/gard/units.c
new file mode 100644
index 000000000..f3b435a3a
--- /dev/null
+++ b/roms/skiboot/external/gard/units.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Each chip has a set of "units" that are detailed by the System MRW. Granted
+ * they're pretty much fixed for a given chip generation so hardcoding them here
+ * isn't a big deal.
+ *
+ * These tables we generated from obj/genfiles/errl/errludtarget.H. Which is
+ * generated as a part of the hostboot build process. Yeah that's about as
+ * dumb as it sounds, but whatever.
+ *
+ * Copyright 2017 IBM Corp
+ */
+
+#include "gard.h"
+
+/*
+ * Notes:
+ *
+ * When formatting these as strings we print them into a buffer of MAX_PATH_SIZE
+ * Given there is a max path length of ten units MAX_PATH_SIZE might need a
+ * bump is we start using very detailed unit description strings.
+ */
+
+const struct chip_unit_desc p8_chip_units[] = {
+ {0x00, "NA"},
+ {0x01, "Sys"},
+ {0x02, "Node"},
+ {0x03, "DIMM"},
+ {0x04, "Membuf"},
+ {0x05, "Proc"},
+ {0x06, "EX"},
+ {0x07, "Core"},
+ {0x08, "L2"},
+ {0x09, "L3"},
+ {0x0A, "L4"},
+ {0x0B, "MCS"},
+ {0x0D, "MBA"},
+ {0x0E, "XBUS"},
+ {0x0F, "ABUS"},
+ {0x10, "PCI"},
+ {0x11, "DPSS"},
+ {0x12, "APSS"},
+ {0x13, "OCC"},
+ {0x14, "PSI"},
+ {0x15, "FSP"},
+ {0x16, "PNOR"},
+ {0x17, "OSC"},
+ {0x18, "TODCLK"},
+ {0x19, "CONTROL_NODE"},
+ {0x1A, "OSCREFCLK"},
+ {0x1B, "OSCPCICLK"},
+ {0x1C, "REFCLKENDPT"},
+ {0x1D, "PCICLKENDPT"},
+ {0x1E, "NX"},
+ {0x1F, "PORE"},
+ {0x20, "PCIESWITCH"},
+ {0x21, "CAPP"},
+ {0x22, "FSI"},
+ {0x23, "TPM"},
+ {0x24, "SP"},
+ {0x25, "UART"},
+ {0x26, "PS"},
+ {0x27, "FAN"},
+ {0x28, "VRM"},
+ {0x29, "USB"},
+ {0x2A, "ETH"},
+ {0x2B, "PANEL"},
+ {0x2C, "TEST_FAIL"},
+ {0x2D, "LAST_IN_RANGE"}
+};
+
+const struct chip_unit_desc p9_chip_units[] = {
+ {0x00, "NA"},
+ {0x01, "Sys"},
+ {0x02, "Node"},
+ {0x03, "DIMM"},
+ {0x04, "Membuf"},
+ {0x05, "Proc"},
+ {0x06, "EX"},
+ {0x07, "Core"},
+ {0x08, "L2"},
+ {0x09, "L3"},
+ {0x0A, "L4"},
+ {0x0B, "MCS"},
+ /* a hole! */
+ {0x0D, "MBA"},
+ {0x0E, "XBUS"},
+ {0x0F, "ABUS"},
+ {0x10, "PCI"},
+ {0x11, "DPSS"},
+ {0x12, "APSS"},
+ {0x13, "OCC"},
+ {0x14, "PSI"},
+ {0x15, "FSP"},
+ {0x16, "PNOR"},
+ {0x17, "OSC"},
+ {0x18, "TODCLK"},
+ {0x19, "CONTROL_NODE"},
+ {0x1A, "OSCREFCLK"},
+ {0x1B, "OSCPCICLK"},
+ {0x1C, "REFCLKENDPT"},
+ {0x1D, "PCICLKENDPT"},
+ {0x1E, "NX"},
+ {0x1F, "PORE"},
+ {0x20, "PCIESWITCH"},
+ {0x21, "CAPP"},
+ {0x22, "FSI"},
+ {0x23, "EQ"},
+ {0x24, "MCA"},
+ {0x25, "MCBIST"},
+ {0x26, "MI"},
+ {0x27, "DMI"},
+ {0x28, "OBUS"},
+ {0x2A, "SBE"},
+ {0x2B, "PPE"},
+ {0x2C, "PERV"},
+ {0x2D, "PEC"},
+ {0x2E, "PHB"},
+ {0x2F, "SYSREFCLKENDPT"},
+ {0x30, "MFREFCLKENDPT"},
+ {0x31, "TPM"},
+ {0x32, "SP"},
+ {0x33, "UART"},
+ {0x34, "PS"},
+ {0x35, "FAN"},
+ {0x36, "VRM"},
+ {0x37, "USB"},
+ {0x38, "ETH"},
+ {0x39, "PANEL"},
+ {0x3A, "BMC"},
+ {0x3B, "FLASH"},
+ {0x3C, "SEEPROM"},
+ {0x3D, "TMP"},
+ {0x3E, "GPIO_EXPANDER"},
+ {0x3F, "POWER_SEQUENCER"},
+ {0x40, "RTC"},
+ {0x41, "FANCTLR"},
+ {0x42, "OBUS_BRICK"},
+ {0x43, "NPU"},
+ {0x44, "MC"},
+ {0x45, "TEST_FAIL"},
+ {0x46, "MFREFCLK"},
+ {0x47, "SMPGROUP"},
+ {0x48, "OMI"},
+ {0x49, "MCC"},
+ {0x4A, "OMIC"},
+ {0x4B, "OCMB_CHIP"},
+ {0x4C, "MEM_PORT"},
+ {0x4D, "I2C_MUX"},
+ {0x4E, "PMIC"},
+ {0x4F, "LAST_IN_RANGE"},
+};
+
+const struct chip_unit_desc p10_chip_units[] = {
+ {0x00, "NA"},
+ {0x01, "Sys"},
+ {0x02, "Node"},
+ {0x03, "DIMM"},
+ {0x04, "Membuf"},
+ {0x05, "Proc"},
+ {0x06, "EX"},
+ {0x07, "Core"},
+ {0x08, "L2"},
+ {0x09, "L3"},
+ {0x0A, "L4"},
+ {0x0B, "MCS"},
+ /* a hole! */
+ {0x0D, "MBA"},
+ {0x0E, "XBUS"},
+ {0x0F, "ABUS"},
+ {0x10, "PCI"},
+ {0x11, "DPSS"},
+ {0x12, "APSS"},
+ {0x13, "OCC"},
+ {0x14, "PSI"},
+ {0x15, "FSP"},
+ {0x16, "PNOR"},
+ {0x17, "OSC"},
+ {0x18, "TODCLK"},
+ {0x19, "CONTROL_NODE"},
+ {0x1A, "OSCREFCLK"},
+ {0x1B, "OSCPCICLK"},
+ {0x1C, "REFCLKENDPT"},
+ {0x1D, "PCICLKENDPT"},
+ {0x1E, "NX"},
+ {0x1F, "PORE"},
+ {0x20, "PCIESWITCH"},
+ {0x21, "CAPP"},
+ {0x22, "FSI"},
+ {0x23, "EQ"},
+ {0x24, "MCA"},
+ {0x25, "MCBIST"},
+ {0x26, "MI"},
+ {0x27, "DMI"},
+ {0x28, "OBUS"},
+ {0x2A, "SBE"},
+ {0x2B, "PPE"},
+ {0x2C, "PERV"},
+ {0x2D, "PEC"},
+ {0x2E, "PHB"},
+ {0x2F, "SYSREFCLKENDPT"},
+ {0x30, "MFREFCLKENDPT"},
+ {0x31, "TPM"},
+ {0x32, "SP"},
+ {0x33, "UART"},
+ {0x34, "PS"},
+ {0x35, "FAN"},
+ {0x36, "VRM"},
+ {0x37, "USB"},
+ {0x38, "ETH"},
+ {0x39, "PANEL"},
+ {0x3A, "BMC"},
+ {0x3B, "FLASH"},
+ {0x3C, "SEEPROM"},
+ {0x3D, "TMP"},
+ {0x3E, "GPIO_EXPANDER"},
+ {0x3F, "POWER_SEQUENCER"},
+ {0x40, "RTC"},
+ {0x41, "FANCTLR"},
+ {0x42, "OBUS_BRICK"},
+ {0x43, "NPU"},
+ {0x44, "MC"},
+ {0x45, "TEST_FAIL"},
+ {0x46, "MFREFCLK"},
+ {0x47, "SMPGROUP"},
+ {0x48, "OMI"},
+ {0x49, "MCC"},
+ {0x4A, "OMIC"},
+ {0x4B, "OCMB_CHIP"},
+ {0x4C, "MEM_PORT"},
+ {0x4D, "I2C_MUX"},
+ {0x4E, "PMIC"},
+ {0x4F, "NMMU"},
+ {0x50, "PAU"},
+ {0x51, "IOHS"},
+ {0x52, "PAUC"},
+ {0x53, "FC"},
+ {0x54, "LPCREFCLKENDPT"},
+ {0x55, "GENERIC_I2C_DEVICE"},
+ {0x56, "LAST_IN_RANGE"},
+};
+