aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/external/ffspart
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/external/ffspart')
-rw-r--r--roms/skiboot/external/ffspart/.gitignore7
-rw-r--r--roms/skiboot/external/ffspart/Makefile44
-rw-r--r--roms/skiboot/external/ffspart/config.h24
-rw-r--r--roms/skiboot/external/ffspart/ffspart.c548
-rw-r--r--roms/skiboot/external/ffspart/rules.mk37
-rw-r--r--roms/skiboot/external/ffspart/test/files/03-tiny-pnor.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/03-tiny-pnor.outbin0 -> 2560 bytes
-rw-r--r--roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.outbin0 -> 2560 bytes
-rw-r--r--roms/skiboot/external/ffspart/test/files/05-hdr-overlap.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/06-small-flash.in5
-rw-r--r--roms/skiboot/external/ffspart/test/files/07-big-files.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/08-small-files.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/10-bad-input.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/11-long-name.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/12-bad-numbers-base.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/13-bad-numbers-size.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/14-bad-input-flags.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/15-overlapping-partitions.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/16-create-blank.in4
-rw-r--r--roms/skiboot/external/ffspart/test/files/16-create-blank.outbin0 -> 2560 bytes
-rw-r--r--roms/skiboot/external/ffspart/test/files/17-toc.in5
-rw-r--r--roms/skiboot/external/ffspart/test/files/17-toc.outbin0 -> 2560 bytes
-rw-r--r--roms/skiboot/external/ffspart/test/files/18-eraseblock-gt-first-partition.in2
-rwxr-xr-xroms/skiboot/external/ffspart/test/make-check-test1
-rw-r--r--roms/skiboot/external/ffspart/test/results/00-usage.err0
-rw-r--r--roms/skiboot/external/ffspart/test/results/00-usage.out19
-rw-r--r--roms/skiboot/external/ffspart/test/results/01-param-sanity.err0
-rw-r--r--roms/skiboot/external/ffspart/test/results/01-param-sanity.out19
-rw-r--r--roms/skiboot/external/ffspart/test/results/01.1-param-sanity.err0
-rw-r--r--roms/skiboot/external/ffspart/test/results/01.1-param-sanity.out19
-rw-r--r--roms/skiboot/external/ffspart/test/results/05-hdr-overlap.err5
-rw-r--r--roms/skiboot/external/ffspart/test/results/05-hdr-overlap.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/06-small-flash.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/06-small-flash.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/07-big-files.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/07-big-files.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/08-small-files.err2
-rw-r--r--roms/skiboot/external/ffspart/test/results/08-small-files.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/10-bad-input.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/10-bad-input.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/11-long-name.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/11-long-name.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/14-bad-input-flags.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/14-bad-input-flags.out0
-rw-r--r--roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.err4
-rw-r--r--roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.out0
-rwxr-xr-xroms/skiboot/external/ffspart/test/test-ffspart5
-rw-r--r--roms/skiboot/external/ffspart/test/tests/00-usage12
-rw-r--r--roms/skiboot/external/ffspart/test/tests/01-param-sanity12
-rw-r--r--roms/skiboot/external/ffspart/test/tests/01.1-param-sanity12
-rw-r--r--roms/skiboot/external/ffspart/test/tests/03-tiny-pnor15
-rw-r--r--roms/skiboot/external/ffspart/test/tests/04-tiny-pnor225
-rw-r--r--roms/skiboot/external/ffspart/test/tests/05-hdr-overlap14
-rw-r--r--roms/skiboot/external/ffspart/test/tests/06-small-flash13
-rw-r--r--roms/skiboot/external/ffspart/test/tests/07-big-files26
-rw-r--r--roms/skiboot/external/ffspart/test/tests/08-small-files22
-rw-r--r--roms/skiboot/external/ffspart/test/tests/10-bad-input13
-rw-r--r--roms/skiboot/external/ffspart/test/tests/11-long-name11
-rw-r--r--roms/skiboot/external/ffspart/test/tests/12-bad-numbers-base13
-rw-r--r--roms/skiboot/external/ffspart/test/tests/13-bad-numbers-size13
-rw-r--r--roms/skiboot/external/ffspart/test/tests/14-bad-input-flags13
-rw-r--r--roms/skiboot/external/ffspart/test/tests/15-overlapping-partitions13
-rw-r--r--roms/skiboot/external/ffspart/test/tests/16-create-blank15
-rw-r--r--roms/skiboot/external/ffspart/test/tests/17-toc15
-rw-r--r--roms/skiboot/external/ffspart/test/tests/18-eraseblock-gt-first-partition45
70 files changed, 1124 insertions, 0 deletions
diff --git a/roms/skiboot/external/ffspart/.gitignore b/roms/skiboot/external/ffspart/.gitignore
new file mode 100644
index 000000000..ed76f20a0
--- /dev/null
+++ b/roms/skiboot/external/ffspart/.gitignore
@@ -0,0 +1,7 @@
+ccan
+common
+ffspart
+libflash
+make_version.sh
+test/test.sh
+
diff --git a/roms/skiboot/external/ffspart/Makefile b/roms/skiboot/external/ffspart/Makefile
new file mode 100644
index 000000000..0a8d910fd
--- /dev/null
+++ b/roms/skiboot/external/ffspart/Makefile
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+# Use make V=1 for a verbose build.
+include rules.mk
+GET_ARCH = ../../external/common/get_arch.sh
+include ../../external/common/rules.mk
+
+all: links arch_links $(EXE)
+
+#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 ../../test/test.sh test/test.sh
+ @test/test-ffspart
+
+$(OBJS): | links arch_links
+
+.PHONY: VERSION-always
+.version: VERSION-always
+ @echo $(FFSPART_VERSION) > $@.tmp
+ @cmp -s $@ $@.tmp || cp $@.tmp $@
+ @rm -f $@.tmp
+
+install: all
+ install -D ffspart $(DESTDIR)$(sbindir)/ffspart
+
+.PHONY: dist
+#File is named $(FFSPART_VERSION).tar because the expectation is that ffspart-
+#is always at the start of the verion. This remains consistent with skiboot
+#version strings
+dist: links .version
+ find -L ../ffspart/ -iname '*.[ch]' -print0 | xargs -0 tar -rhf $(FFSPART_VERSION).tar
+ tar --transform 's/Makefile.dist/Makefile/' -rhf $(FFSPART_VERSION).tar \
+ ../ffspart/Makefile.dist ../ffspart/rules.mk \
+ ../ffspart/.version ../ffspart/make_version.sh \
+ ../ffspart/common/*
+
+.PHONY: clean
+clean: arch_clean
+ rm -f $(OBJS) $(EXE) *.o *.d
+.PHONY: distclean
+distclean: clean
+ rm -f *.c~ *.h~ *.sh~ Makefile~ config.mk~ libflash/*.c~ libflash/*.h~
+ rm -f libflash ccan .version .version.tmp
+ rm -f common io.h
diff --git a/roms/skiboot/external/ffspart/config.h b/roms/skiboot/external/ffspart/config.h
new file mode 100644
index 000000000..7894afb6b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/config.h
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * For CCAN
+ *
+ * Copyright 2017 IBM Corp.
+ */
+
+#include <endian.h>
+#include <byteswap.h>
+
+#define HAVE_TYPEOF 1
+#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define HAVE_BIG_ENDIAN 0
+#define HAVE_LITTLE_ENDIAN 1
+#else
+#define HAVE_BIG_ENDIAN 1
+#define HAVE_LITTLE_ENDIAN 0
+#endif
+
+#define HAVE_BYTESWAP_H 1
+#define HAVE_BSWAP_64 1
diff --git a/roms/skiboot/external/ffspart/ffspart.c b/roms/skiboot/external/ffspart/ffspart.c
new file mode 100644
index 000000000..98e790b23
--- /dev/null
+++ b/roms/skiboot/external/ffspart/ffspart.c
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Assemble a FFS Image (no, not that FFS, this FFS)
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+#include <libflash/blocklevel.h>
+#include <libflash/ecc.h>
+#include <common/arch_flash.h>
+
+/*
+ * Flags:
+ * - E: ECC for this part
+ */
+
+/*
+ * TODO FIXME
+ * Max line theoretical max size:
+ * - name: 15 chars = 15
+ * - base: 0xffffffff = 10
+ * - size: 0xffffffff = 10
+ * - flag: E = 1
+ *
+ * 36 + 3 separators = 39
+ * Plus \n 40
+ * Lets do 50.
+ */
+#define MAX_LINE (PATH_MAX+255)
+#define MAX_TOCS 10
+#define SEPARATOR ','
+
+/* Full version number (possibly includes gitid). */
+extern const char version[];
+
+static int read_u32(const char *input, uint32_t *val)
+{
+ char *endptr;
+ *val = strtoul(input, &endptr, 0);
+ return (*endptr == SEPARATOR) ? 0 : 1;
+}
+
+static const char *advance_line(const char *input)
+{
+ char *pos = strchr(input, SEPARATOR);
+ if (!pos)
+ return NULL;
+ return pos + 1;
+}
+
+static struct ffs_hdr *parse_toc(const char *line, uint32_t block_size,
+ uint32_t block_count)
+{
+ struct ffs_entry_user user;
+ struct ffs_entry *ent;
+ struct ffs_hdr *hdr;
+ uint32_t tbase;
+ int rc;
+
+ if (read_u32(line, &tbase)) {
+ fprintf(stderr, "Couldn't parse TOC base address\n");
+ return NULL;
+ }
+
+ line = advance_line(line);
+ if (!line) {
+ fprintf(stderr, "Couldn't find TOC flags\n");
+ return NULL;
+ }
+
+ rc = ffs_string_to_entry_user(line, strlen(line), &user);
+ if (rc) {
+ fprintf(stderr, "Couldn't parse TOC flags\n");
+ return NULL;
+ }
+
+ rc = ffs_entry_new("part", tbase, 0, &ent);
+ if (rc) {
+ fprintf(stderr, "Couldn't make entry for TOC@0x%08x\n", tbase);
+ return NULL;
+ }
+
+ rc = ffs_entry_user_set(ent, &user);
+ if (rc) {
+ fprintf(stderr, "Invalid TOC flag\n");
+ ffs_entry_put(ent);
+ return NULL;
+ }
+
+ rc = ffs_hdr_new(block_size, block_count, &ent, &hdr);
+ if (rc) {
+ hdr = NULL;
+ fprintf(stderr, "Couldn't make header for TOC@0x%08x\n", tbase);
+ }
+
+ ffs_entry_put(ent);
+ return hdr;
+}
+
+static int parse_entry(struct blocklevel_device *bl,
+ struct ffs_hdr **tocs, const char *line, bool allow_empty)
+{
+ char name[FFS_PART_NAME_MAX + 2] = { 0 };
+ struct ffs_entry_user user = { 0 };
+ uint32_t pbase, psize, pactual, i;
+ struct ffs_entry *new_entry;
+ struct stat data_stat;
+ const char *filename;
+ bool added = false;
+ uint8_t *data_ptr, ecc = 0;
+ int data_fd, rc;
+ char *pos;
+
+ memcpy(name, line, FFS_PART_NAME_MAX + 1);
+ pos = strchr(name, SEPARATOR);
+ /* There is discussion to be had as to if we should bail here */
+ if (!pos) {
+ fprintf(stderr, "WARNING: Long partition name will get truncated to '%s'\n",
+ name);
+ name[FFS_PART_NAME_MAX] = '\0';
+ } else {
+ *pos = '\0';
+ }
+
+ line = advance_line(line);
+ if (!line || read_u32(line, &pbase)) {
+ fprintf(stderr, "Couldn't parse '%s' partition base address\n",
+ name);
+ return -1;
+ }
+
+ line = advance_line(line);
+ if (!line || read_u32(line, &psize)) {
+ fprintf(stderr, "Couldn't parse '%s' partition length\n",
+ name);
+ return -1;
+ }
+
+ line = advance_line(line);
+ if (!line || !advance_line(line)) {
+ fprintf(stderr, "Couldn't find '%s' partition flags\n",
+ name);
+ return -1;
+ }
+
+ rc = ffs_string_to_entry_user(line, advance_line(line) - 1 - line, &user);
+ if (rc) {
+ fprintf(stderr, "Couldn't parse '%s' partition flags\n",
+ name);
+ return -1;
+ }
+ line = advance_line(line);
+ /* Already checked return value */
+
+ rc = ffs_entry_new(name, pbase, psize, &new_entry);
+ if (rc) {
+ fprintf(stderr, "Invalid entry '%s' 0x%08x for 0x%08x\n",
+ name, pbase, psize);
+ return -1;
+ }
+
+ rc = ffs_entry_user_set(new_entry, &user);
+ if (rc) {
+ fprintf(stderr, "Couldn't set '%s' partition flags\n",
+ name);
+ ffs_entry_put(new_entry);
+ return -1;
+ }
+
+ if (has_flag(new_entry, FFS_MISCFLAGS_BACKUP)) {
+ rc = ffs_entry_set_act_size(new_entry, 0);
+ if (rc) {
+ fprintf(stderr, "Couldn't set '%s' partition actual size\n",
+ name);
+ ffs_entry_put(new_entry);
+ return -1;
+ }
+ }
+
+ if (!advance_line(line)) {
+ fprintf(stderr, "Missing TOC field for '%s' partition\n",
+ name);
+ ffs_entry_put(new_entry);
+ return -1;
+ }
+
+ while (*line != SEPARATOR) {
+ int toc = *(line++);
+
+ if (!isdigit(toc)) {
+ fprintf(stderr, "Bad TOC value %d (%c) for '%s' partition\n",
+ toc, toc, name);
+ ffs_entry_put(new_entry);
+ return -1;
+ }
+ toc -= '0';
+ if (!tocs[toc]) {
+ fprintf(stderr, "No TOC with ID %d for '%s' partition\n",
+ toc, name);
+ ffs_entry_put(new_entry);
+ return -1;
+ }
+ rc = ffs_entry_add(tocs[toc], new_entry);
+ if (rc) {
+ fprintf(stderr, "Couldn't add '%s' partition to TOC %d: %d\n",
+ name, toc, rc);
+ ffs_entry_put(new_entry);
+ return rc;
+ }
+ added = true;
+ }
+ if (!added) {
+ /*
+ * They didn't specify a TOC in the TOC field, use
+ * TOC@0 as the default
+ */
+ rc = ffs_entry_add(tocs[0], new_entry);
+ if (rc) {
+ fprintf(stderr, "Couldn't add '%s' partition to default TOC: %d\n",
+ name, rc);
+ ffs_entry_put(new_entry);
+ return rc;
+ }
+ }
+ ffs_entry_put(new_entry);
+
+ if (*line != '\0' && *(line + 1) != '\0') {
+ size_t data_len;
+
+ filename = line + 1;
+
+ /*
+ * Support flashing already ecc'd data as this is the case
+ * for POWER8 SBE image binary.
+ */
+ if (has_ecc(new_entry) && !strstr(filename, ".ecc"))
+ blocklevel_ecc_protect(bl, pbase, psize);
+
+ data_fd = open(filename, O_RDONLY);
+ if (data_fd == -1) {
+ fprintf(stderr, "Couldn't open file '%s' for '%s' partition "
+ "(%m)\n", filename, name);
+ return -1;
+ }
+
+ if (fstat(data_fd, &data_stat) == -1) {
+ fprintf(stderr, "Couldn't stat file '%s' for '%s' partition "
+ "(%m)\n", filename, name);
+ close(data_fd);
+ return -1;
+ }
+
+ data_ptr = calloc(1, psize);
+ if (!data_ptr) {
+ return -1;
+ }
+
+ pactual = data_stat.st_size;
+
+ /*
+ * There's two char device inputs we care about: /dev/zero and
+ * /dev/urandom. Both have a stat.st_size of zero so read in
+ * a full partition worth, accounting for ECC overhead.
+ */
+ if (!pactual && S_ISCHR(data_stat.st_mode)) {
+ pactual = psize;
+
+ if (has_ecc(new_entry)) {
+ pactual = ecc_buffer_size_minus_ecc(pactual);
+
+ /* ECC input size needs to be a multiple of 8 */
+ pactual = pactual & ~0x7;
+ }
+ }
+ /*
+ * Sanity check that the file isn't too large for
+ * partition
+ */
+ if (has_ecc(new_entry) && !strstr(filename, ".ecc"))
+ psize = ecc_buffer_size_minus_ecc(psize);
+ if (pactual > psize) {
+ fprintf(stderr, "File '%s' for partition '%s' is too large,"
+ " %u > %u\n",
+ filename, name, pactual, psize);
+ close(data_fd);
+ return -1;
+ }
+
+ for (data_len = 0; data_len < pactual; data_len += rc) {
+ rc = read(data_fd, &data_ptr[data_len], pactual - data_len);
+ if (rc == -1) {
+ fprintf(stderr, "error reading from '%s'", filename);
+ exit(1);
+ }
+ }
+
+ rc = blocklevel_write(bl, pbase, data_ptr, pactual);
+ if (rc) {
+ fprintf(stderr, "Couldn't write file '%s' for '%s' partition to PNOR "
+ "(%m)\n", filename, name);
+ exit(1);
+ }
+
+ free(data_ptr);
+ close(data_fd);
+ } else {
+ if (!allow_empty) {
+ fprintf(stderr, "Filename missing for partition %s!\n",
+ name);
+ return -1;
+ }
+ if (has_ecc(new_entry)) {
+ i = pbase + 8;
+ while (i < pbase + psize) {
+ rc = blocklevel_write(bl, i, &ecc, sizeof(ecc));
+ if (rc) {
+ fprintf(stderr, "\nError setting ECC byte at 0x%08x\n",
+ i);
+ return rc;
+ }
+ i += 9;
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+static void print_version(void)
+{
+ printf("Open-Power FFS format tool %s\n", version);
+}
+
+static void print_help(const char *pname)
+{
+ print_version();
+ printf("Usage: %s [options] -e -s size -c num -i layout_file -p pnor_file ...\n\n", pname);
+ printf(" Options:\n");
+ printf("\t-e, --allow_empty\n");
+ printf("\t\tCreate partition as blank if not specified (sets ECC if flag set)\n\n");
+ printf("\t-s, --block_size=size\n");
+ printf("\t\tSize (in hex with leading 0x) of the blocks on the flash in bytes\n\n");
+ printf("\t-c, --block_count=num\n");
+ printf("\t\tNumber of blocks on the flash\n\n");
+ printf("\t-i, --input=file\n");
+ printf("\t\tFile containing the required partition data\n\n");
+ printf("\t-p, --pnor=file\n");
+ printf("\t\tOutput file to write data\n\n");
+}
+
+int main(int argc, char *argv[])
+{
+ static char line[MAX_LINE];
+
+ char *pnor = NULL, *input = NULL;
+ bool toc_created = false, bad_input = false, allow_empty = false;
+ uint32_t block_size = 0, block_count = 0;
+ struct ffs_hdr *tocs[MAX_TOCS] = { 0 };
+ struct blocklevel_device *bl = NULL;
+ const char *pname = argv[0];
+ int line_number, rc, i;
+ FILE *in_file;
+
+ while(1) {
+ struct option long_opts[] = {
+ {"allow_empty", no_argument, NULL, 'e'},
+ {"block_count", required_argument, NULL, 'c'},
+ {"block_size", required_argument, NULL, 's'},
+ {"debug", no_argument, NULL, 'g'},
+ {"input", required_argument, NULL, 'i'},
+ {"pnor", required_argument, NULL, 'p'},
+ {NULL, 0, 0, 0}
+ };
+ int c, oidx = 0;
+
+ c = getopt_long(argc, argv, "+:ec:gi:p:s:", long_opts, &oidx);
+ if (c == EOF)
+ break;
+ switch(c) {
+ case 'e':
+ allow_empty = true;
+ break;
+ case 'c':
+ block_count = strtoul(optarg, NULL, 0);
+ break;
+ case 'g':
+ libflash_debug = true;
+ break;
+ case 'i':
+ free(input);
+ input = strdup(optarg);
+ if (!input)
+ fprintf(stderr, "Out of memory!\n");
+ break;
+ case 'p':
+ free(pnor);
+ pnor = strdup(optarg);
+ if (!pnor)
+ fprintf(stderr, "Out of memory!\n");
+ break;
+ case 's':
+ block_size = strtoul(optarg, NULL, 0);
+ break;
+ case ':':
+ fprintf(stderr, "Unrecognised option \"%s\" to '%c'\n",
+ optarg, optopt);
+ bad_input = true;
+ break;
+ case '?':
+ fprintf(stderr, "Unrecognised option '%c'\n", optopt);
+ bad_input = true;
+ break;
+ default:
+ fprintf(stderr , "Encountered unknown error parsing options\n");
+ bad_input = true;
+ }
+ }
+
+ if (bad_input || !block_size || !block_count || !input || !pnor) {
+ print_help(pname);
+ return 1;
+ }
+
+ in_file = fopen(input, "r");
+ if (!in_file) {
+ fprintf(stderr, "Couldn't open your input file %s: %m\n", input);
+ return 2;
+ }
+
+ /*
+ * TODO: This won't create the file.
+ * We should do this
+ */
+ rc = arch_flash_init(&bl, pnor, true);
+ if (rc) {
+ fprintf(stderr, "Couldn't initialise architecture flash structures\n");
+ fclose(in_file);
+ return 3;
+ }
+
+ /*
+ * 'Erase' the file, make it all 0xFF
+ * TODO: Add sparse option and don't do this.
+ */
+ rc = blocklevel_erase(bl, 0, block_size * block_count);
+ if (rc) {
+ fprintf(stderr, "Couldn't erase '%s' pnor file\n", pnor);
+ fclose(in_file);
+ return 4;
+ }
+
+ line_number = 0;
+ while (fgets(line, MAX_LINE, in_file) != NULL) {
+ line_number++;
+
+ /* Inline comments in input file */
+ if (line[0] == '#')
+ continue;
+
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ if (line[0] == '@') {
+ int toc_num = line[1];
+ rc = 5;
+
+ if (!isdigit(toc_num)) {
+ fprintf(stderr, "Invalid TOC ID %d (%c)\n",
+ toc_num, toc_num);
+ goto parse_out;
+ }
+
+ toc_num -= '0';
+
+ if (line[2] != SEPARATOR) {
+ fprintf(stderr, "TOC ID too long\n");
+ goto parse_out;
+ }
+
+ if (tocs[toc_num]) {
+ fprintf(stderr, "Duplicate TOC ID %d\n", toc_num);
+ goto parse_out;
+ }
+
+ tocs[toc_num] = parse_toc(&line[3], block_size, block_count);
+ if (!tocs[toc_num])
+ goto parse_out;
+ toc_created = true;
+ } else {
+ if (!toc_created) {
+ fprintf(stderr, "WARNING: Attempting to parse a partition line without any TOCs created.\n");
+ fprintf(stderr, " Generating a default TOC at zero\n");
+ rc = ffs_hdr_new(block_size, block_count, NULL, &tocs[0]);
+ if (rc) {
+ rc = 7;
+ fprintf(stderr, "Couldn't generate a default TOC at zero\n");
+ goto parse_out;
+ }
+ toc_created = true;
+ }
+ rc = parse_entry(bl, tocs, line, allow_empty);
+ if (rc) {
+ rc = 6;
+ goto parse_out;
+ }
+ }
+ }
+
+ for(i = 0; i < MAX_TOCS; i++) {
+ if (tocs[i]) {
+ rc = ffs_hdr_finalise(bl, tocs[i]);
+ if (rc) {
+ rc = 7;
+ fprintf(stderr, "Failed to write out TOC values\n");
+ break;
+ }
+ }
+ }
+
+parse_out:
+ if (rc == 5 || rc == 6)
+ fprintf(stderr, "Failed to parse input file '%s' at line %d\n",
+ input, line_number);
+ arch_flash_close(bl, pnor);
+ fclose(in_file);
+ for(i = 0; i < MAX_TOCS; i++)
+ ffs_hdr_free(tocs[i]);
+ free(input);
+ free(pnor);
+ return rc;
+}
diff --git a/roms/skiboot/external/ffspart/rules.mk b/roms/skiboot/external/ffspart/rules.mk
new file mode 100644
index 000000000..e006dc5b7
--- /dev/null
+++ b/roms/skiboot/external/ffspart/rules.mk
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+.DEFAULT_GOAL := all
+
+override CFLAGS += -O2 -Wall -g -I.
+EXE = ffspart
+OBJS = $(EXE).o version.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))
+OBJS += $(LIBFLASH_OBJS)
+OBJS += common-arch_flash.o
+
+prefix = /usr/local/
+sbindir = $(prefix)/sbin
+
+CC = $(CROSS_COMPILE)gcc
+
+FFSPART_VERSION ?= $(shell ./make_version.sh $(EXE))
+
+version.c: make_version.sh .version
+ @(if [ "a$(FFSPART_VERSION)" = "a" ]; then \
+ echo "#error You need to set FFSPART_VERSION environment variable" > $@ ;\
+ else \
+ echo "const char version[] = \"$(FFSPART_VERSION)\";" ;\
+ fi) > $@
+
+%.o : %.c
+ $(Q_CC)$(CC) $(CFLAGS) -c $< -o $@
+
+$(LIBFLASH_SRC): | links
+
+$(LIBFLASH_OBJS): libflash-%.o : libflash/%.c
+ $(Q_CC)$(CC) $(CFLAGS) -c $< -o $@
+
+$(EXE): $(OBJS)
+ $(Q_CC)$(CC) $(CFLAGS) $^ -lrt -o $@
+
diff --git a/roms/skiboot/external/ffspart/test/files/03-tiny-pnor.in b/roms/skiboot/external/ffspart/test/files/03-tiny-pnor.in
new file mode 100644
index 000000000..961c3fefa
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/03-tiny-pnor.in
@@ -0,0 +1,4 @@
+ONE,0x00400,0x00000100,EL,,/dev/zero
+TWO,0x00500,0x00000100,EF,,/dev/zero
+THREE,0x600,0x00000100,EF,,/dev/zero
+FOUR,0x0700,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/03-tiny-pnor.out b/roms/skiboot/external/ffspart/test/files/03-tiny-pnor.out
new file mode 100644
index 000000000..e00fa5c0e
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/03-tiny-pnor.out
Binary files differ
diff --git a/roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.in b/roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.in
new file mode 100644
index 000000000..7fc12ba4f
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.in
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EL,,SEDCATCH_1
+TWO,0x00000400,0x00000100,EF,,SEDCATCH_2
+THREE,0x00000500,0x00000100,EF,,SEDCATCH_3
+FOUR,0x00000600,0x00000100,EF,,SEDCATCH_4
diff --git a/roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.out b/roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.out
new file mode 100644
index 000000000..617c4ef44
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/04-tiny-pnor2.out
Binary files differ
diff --git a/roms/skiboot/external/ffspart/test/files/05-hdr-overlap.in b/roms/skiboot/external/ffspart/test/files/05-hdr-overlap.in
new file mode 100644
index 000000000..8af94d2d7
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/05-hdr-overlap.in
@@ -0,0 +1,4 @@
+ONE,0x00000200,0x00000100,EV,,/dev/zero
+TWO,0x00000300,0x00000100,EF,,/dev/zero
+THREE,0x00000400,0x00000100,EF,,/dev/zero
+FOUR,0x00000500,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/06-small-flash.in b/roms/skiboot/external/ffspart/test/files/06-small-flash.in
new file mode 100644
index 000000000..a717888b3
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/06-small-flash.in
@@ -0,0 +1,5 @@
+ONE,0x00000300,0x00000100,EV,,/dev/zero
+TWO,0x00000400,0x00000100,EF,,/dev/zero
+THREE,0x00000500,0x00000100,EF,,/dev/zero
+FOUR,0x00000600,0x00000100,EF,,/dev/zero
+FIVE,0x00000700,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/07-big-files.in b/roms/skiboot/external/ffspart/test/files/07-big-files.in
new file mode 100644
index 000000000..0369bf665
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/07-big-files.in
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EV,,SEDCATCH_1
+TWO,0x00000400,0x00000100,EF,,SEDCATCH_2
+THREE,0x00000500,0x00000100,EF,,SEDCATCH_3
+FOUR,0x00000600,0x00000100,EF,,SEDCATCH_4
diff --git a/roms/skiboot/external/ffspart/test/files/08-small-files.in b/roms/skiboot/external/ffspart/test/files/08-small-files.in
new file mode 100644
index 000000000..0369bf665
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/08-small-files.in
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EV,,SEDCATCH_1
+TWO,0x00000400,0x00000100,EF,,SEDCATCH_2
+THREE,0x00000500,0x00000100,EF,,SEDCATCH_3
+FOUR,0x00000600,0x00000100,EF,,SEDCATCH_4
diff --git a/roms/skiboot/external/ffspart/test/files/10-bad-input.in b/roms/skiboot/external/ffspart/test/files/10-bad-input.in
new file mode 100644
index 000000000..5c4c90ee6
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/10-bad-input.in
@@ -0,0 +1,4 @@
+ONE0x00000300,0x00000100,EV,,/dev/zero
+TWO,0x00000400,0x00000100,EF,,/dev/zero
+THREE,0x00000500,0x00000100,EF,,/dev/zero
+FOUR,0x00000600,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/11-long-name.in b/roms/skiboot/external/ffspart/test/files/11-long-name.in
new file mode 100644
index 000000000..bd32ea89a
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/11-long-name.in
@@ -0,0 +1,4 @@
+This_is_more_than_15_characters,0x00000300,0x00000100,EV,,/dev/zero
+This_is_exactly,0x00000400,0x00000100,EF,,/dev/zero
+This_is_one_le,0x00000500,0x00000100,EF,,/dev/zero
+This_is_one_more,0x00000600,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/12-bad-numbers-base.in b/roms/skiboot/external/ffspart/test/files/12-bad-numbers-base.in
new file mode 100644
index 000000000..8f6fd27a9
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/12-bad-numbers-base.in
@@ -0,0 +1,4 @@
+ONE,0xg0000300,0x00000100,EV,,/dev/zero
+TWO,0x00000400,0x00000100,EF,,/dev/zero
+THREE,0x00000500,0x00000100,EF,,/dev/zero
+FOUR,0x00000600,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/13-bad-numbers-size.in b/roms/skiboot/external/ffspart/test/files/13-bad-numbers-size.in
new file mode 100644
index 000000000..b1dacc738
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/13-bad-numbers-size.in
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00001g00,EV,,/dev/zero
+TWO,0x00000400,0x00000100,EF,,/dev/zero
+THREE,0x00000500,0x00000100,EF,,/dev/zero
+FOUR,0x00000600,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/14-bad-input-flags.in b/roms/skiboot/external/ffspart/test/files/14-bad-input-flags.in
new file mode 100644
index 000000000..dcf18f248
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/14-bad-input-flags.in
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EVZ,,/dev/zero
+TWO,0x00000400,0x00000100,EF,,/dev/zero
+THREE,0x00000500,0x00000100,EF,,/dev/zero
+FOUR,0x00000600,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/15-overlapping-partitions.in b/roms/skiboot/external/ffspart/test/files/15-overlapping-partitions.in
new file mode 100644
index 000000000..e496afa17
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/15-overlapping-partitions.in
@@ -0,0 +1,4 @@
+ONE,0x00000300,0x00000100,EV,,/dev/zero
+TWO,0x00000350,0x00000100,EF,,/dev/zero
+THREE,0x00000500,0x00000100,EF,,/dev/zero
+FOUR,0x00000600,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/16-create-blank.in b/roms/skiboot/external/ffspart/test/files/16-create-blank.in
new file mode 100644
index 000000000..7cff6bf01
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/16-create-blank.in
@@ -0,0 +1,4 @@
+ONE,0x00400,0x00000100,EL,,
+TWO,0x00500,0x00000100,EF,,
+THREE,0x600,0x00000100,EF,,
+FOUR,0x0700,0x00000100,EF,,
diff --git a/roms/skiboot/external/ffspart/test/files/16-create-blank.out b/roms/skiboot/external/ffspart/test/files/16-create-blank.out
new file mode 100644
index 000000000..43b9583b3
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/16-create-blank.out
Binary files differ
diff --git a/roms/skiboot/external/ffspart/test/files/17-toc.in b/roms/skiboot/external/ffspart/test/files/17-toc.in
new file mode 100644
index 000000000..6020e0fe2
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/17-toc.in
@@ -0,0 +1,5 @@
+@0,0x0,
+ONE,0x00400,0x00000100,EL,,/dev/zero
+TWO,0x00500,0x00000100,EF,,/dev/zero
+THREE,0x600,0x00000100,EF,,/dev/zero
+FOUR,0x0700,0x00000100,EF,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/files/17-toc.out b/roms/skiboot/external/ffspart/test/files/17-toc.out
new file mode 100644
index 000000000..e00fa5c0e
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/17-toc.out
Binary files differ
diff --git a/roms/skiboot/external/ffspart/test/files/18-eraseblock-gt-first-partition.in b/roms/skiboot/external/ffspart/test/files/18-eraseblock-gt-first-partition.in
new file mode 100644
index 000000000..bed913682
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/files/18-eraseblock-gt-first-partition.in
@@ -0,0 +1,2 @@
+@0,0x0,
+FIRST,0x400,0x100,,,/dev/zero
diff --git a/roms/skiboot/external/ffspart/test/make-check-test b/roms/skiboot/external/ffspart/test/make-check-test
new file mode 100755
index 000000000..943528678
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/make-check-test
@@ -0,0 +1 @@
+make -C external/ffspart/ check
diff --git a/roms/skiboot/external/ffspart/test/results/00-usage.err b/roms/skiboot/external/ffspart/test/results/00-usage.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/00-usage.err
diff --git a/roms/skiboot/external/ffspart/test/results/00-usage.out b/roms/skiboot/external/ffspart/test/results/00-usage.out
new file mode 100644
index 000000000..cf7213d7b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/00-usage.out
@@ -0,0 +1,19 @@
+Open-Power FFS format tool VERSION
+Usage: ./ffspart [options] -e -s size -c num -i layout_file -p pnor_file ...
+
+ Options:
+ -e, --allow_empty
+ Create partition as blank if not specified (sets ECC if flag set)
+
+ -s, --block_size=size
+ Size (in hex with leading 0x) of the blocks on the flash in bytes
+
+ -c, --block_count=num
+ Number of blocks on the flash
+
+ -i, --input=file
+ File containing the required partition data
+
+ -p, --pnor=file
+ Output file to write data
+
diff --git a/roms/skiboot/external/ffspart/test/results/01-param-sanity.err b/roms/skiboot/external/ffspart/test/results/01-param-sanity.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/01-param-sanity.err
diff --git a/roms/skiboot/external/ffspart/test/results/01-param-sanity.out b/roms/skiboot/external/ffspart/test/results/01-param-sanity.out
new file mode 100644
index 000000000..cf7213d7b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/01-param-sanity.out
@@ -0,0 +1,19 @@
+Open-Power FFS format tool VERSION
+Usage: ./ffspart [options] -e -s size -c num -i layout_file -p pnor_file ...
+
+ Options:
+ -e, --allow_empty
+ Create partition as blank if not specified (sets ECC if flag set)
+
+ -s, --block_size=size
+ Size (in hex with leading 0x) of the blocks on the flash in bytes
+
+ -c, --block_count=num
+ Number of blocks on the flash
+
+ -i, --input=file
+ File containing the required partition data
+
+ -p, --pnor=file
+ Output file to write data
+
diff --git a/roms/skiboot/external/ffspart/test/results/01.1-param-sanity.err b/roms/skiboot/external/ffspart/test/results/01.1-param-sanity.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/01.1-param-sanity.err
diff --git a/roms/skiboot/external/ffspart/test/results/01.1-param-sanity.out b/roms/skiboot/external/ffspart/test/results/01.1-param-sanity.out
new file mode 100644
index 000000000..cf7213d7b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/01.1-param-sanity.out
@@ -0,0 +1,19 @@
+Open-Power FFS format tool VERSION
+Usage: ./ffspart [options] -e -s size -c num -i layout_file -p pnor_file ...
+
+ Options:
+ -e, --allow_empty
+ Create partition as blank if not specified (sets ECC if flag set)
+
+ -s, --block_size=size
+ Size (in hex with leading 0x) of the blocks on the flash in bytes
+
+ -c, --block_count=num
+ Number of blocks on the flash
+
+ -i, --input=file
+ File containing the required partition data
+
+ -p, --pnor=file
+ Output file to write data
+
diff --git a/roms/skiboot/external/ffspart/test/results/05-hdr-overlap.err b/roms/skiboot/external/ffspart/test/results/05-hdr-overlap.err
new file mode 100644
index 000000000..ff8aca201
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/05-hdr-overlap.err
@@ -0,0 +1,5 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Adding partition 'THREE' would cause partition 'ONE' at 0x00000200 to overlap with the header
+Couldn't add 'THREE' partition to default TOC: 107
+Failed to parse input file 'FILE' at line 3
diff --git a/roms/skiboot/external/ffspart/test/results/05-hdr-overlap.out b/roms/skiboot/external/ffspart/test/results/05-hdr-overlap.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/05-hdr-overlap.out
diff --git a/roms/skiboot/external/ffspart/test/results/06-small-flash.err b/roms/skiboot/external/ffspart/test/results/06-small-flash.err
new file mode 100644
index 000000000..a5316a90e
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/06-small-flash.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Couldn't add 'TWO' partition to default TOC: 108
+Failed to parse input file 'FILE' at line 2
diff --git a/roms/skiboot/external/ffspart/test/results/06-small-flash.out b/roms/skiboot/external/ffspart/test/results/06-small-flash.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/06-small-flash.out
diff --git a/roms/skiboot/external/ffspart/test/results/07-big-files.err b/roms/skiboot/external/ffspart/test/results/07-big-files.err
new file mode 100644
index 000000000..bfb6fa022
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/07-big-files.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+File 'FILE_ONE' for partition 'ONE' is too large, 257 > 227
+Failed to parse input file 'FILE' at line 1
diff --git a/roms/skiboot/external/ffspart/test/results/07-big-files.out b/roms/skiboot/external/ffspart/test/results/07-big-files.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/07-big-files.out
diff --git a/roms/skiboot/external/ffspart/test/results/08-small-files.err b/roms/skiboot/external/ffspart/test/results/08-small-files.err
new file mode 100644
index 000000000..c902b642b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/08-small-files.err
@@ -0,0 +1,2 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
diff --git a/roms/skiboot/external/ffspart/test/results/08-small-files.out b/roms/skiboot/external/ffspart/test/results/08-small-files.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/08-small-files.out
diff --git a/roms/skiboot/external/ffspart/test/results/10-bad-input.err b/roms/skiboot/external/ffspart/test/results/10-bad-input.err
new file mode 100644
index 000000000..01c43d81a
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/10-bad-input.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Couldn't parse 'ONE0x00000300' partition length
+Failed to parse input file 'FILE' at line 1
diff --git a/roms/skiboot/external/ffspart/test/results/10-bad-input.out b/roms/skiboot/external/ffspart/test/results/10-bad-input.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/10-bad-input.out
diff --git a/roms/skiboot/external/ffspart/test/results/11-long-name.err b/roms/skiboot/external/ffspart/test/results/11-long-name.err
new file mode 100644
index 000000000..cf8dcc325
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/11-long-name.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+WARNING: Long partition name will get truncated to 'This_is_more_tha'
+WARNING: Long partition name will get truncated to 'This_is_one_more'
diff --git a/roms/skiboot/external/ffspart/test/results/11-long-name.out b/roms/skiboot/external/ffspart/test/results/11-long-name.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/11-long-name.out
diff --git a/roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.err b/roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.err
new file mode 100644
index 000000000..cdd836eac
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Couldn't parse 'ONE' partition base address
+Failed to parse input file 'FILE' at line 1
diff --git a/roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.out b/roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/12-bad-numbers-base.out
diff --git a/roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.err b/roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.err
new file mode 100644
index 000000000..19078d5db
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Couldn't parse 'ONE' partition length
+Failed to parse input file 'FILE' at line 1
diff --git a/roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.out b/roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/13-bad-numbers-size.out
diff --git a/roms/skiboot/external/ffspart/test/results/14-bad-input-flags.err b/roms/skiboot/external/ffspart/test/results/14-bad-input-flags.err
new file mode 100644
index 000000000..613525c3d
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/14-bad-input-flags.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Couldn't parse 'ONE' partition flags
+Failed to parse input file 'FILE' at line 1
diff --git a/roms/skiboot/external/ffspart/test/results/14-bad-input-flags.out b/roms/skiboot/external/ffspart/test/results/14-bad-input-flags.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/14-bad-input-flags.out
diff --git a/roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.err b/roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.err
new file mode 100644
index 000000000..c81d3c0e4
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.err
@@ -0,0 +1,4 @@
+WARNING: Attempting to parse a partition line without any TOCs created.
+ Generating a default TOC at zero
+Couldn't add 'TWO' partition to default TOC: 107
+Failed to parse input file 'FILE' at line 2
diff --git a/roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.out b/roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/results/15-overlapping-partitions.out
diff --git a/roms/skiboot/external/ffspart/test/test-ffspart b/roms/skiboot/external/ffspart/test/test-ffspart
new file mode 100755
index 000000000..7e38a4e37
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/test-ffspart
@@ -0,0 +1,5 @@
+#! /bin/sh
+
+. test/test.sh
+
+run_tests "test/tests/*" "test/results" "test/files"
diff --git a/roms/skiboot/external/ffspart/test/tests/00-usage b/roms/skiboot/external/ffspart/test/tests/00-usage
new file mode 100644
index 000000000..0ca453f4d
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/00-usage
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+run_binary "./ffspart"
+if [ "$?" -ne 1 ] ; then
+ fail_test
+fi
+
+strip_version_from_result "ffspart"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/01-param-sanity b/roms/skiboot/external/ffspart/test/tests/01-param-sanity
new file mode 100644
index 000000000..d63736006
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/01-param-sanity
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+run_binary "./ffspart" "-c 3 -i /dev/null -p /dev/null"
+if [ "$?" -ne 1 ] ; then
+ fail_test
+fi
+
+strip_version_from_result "ffspart"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/01.1-param-sanity b/roms/skiboot/external/ffspart/test/tests/01.1-param-sanity
new file mode 100644
index 000000000..1ce1f906c
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/01.1-param-sanity
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+run_binary "./ffspart" "-s 1 -i /dev/null -p /dev/null"
+if [ "$?" -ne 1 ] ; then
+ fail_test
+fi
+
+strip_version_from_result "ffspart"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/03-tiny-pnor b/roms/skiboot/external/ffspart/test/tests/03-tiny-pnor
new file mode 100644
index 000000000..e7783394b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/03-tiny-pnor
@@ -0,0 +1,15 @@
+#! /bin/sh
+
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+if ! cmp -n $((0x100)) $DATA_DIR/$CUR_TEST.out $DATA_DIR/$CUR_TEST.gen ; then
+ echo "Output differs"
+ fail_test
+fi
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/04-tiny-pnor2 b/roms/skiboot/external/ffspart/test/tests/04-tiny-pnor2
new file mode 100644
index 000000000..3db5f1e29
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/04-tiny-pnor2
@@ -0,0 +1,25 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+i=1;
+while [ $i -lt 5 ] ; do
+ j=0;
+ while [ $j -lt $((0xe0)) ] ; do
+ echo -n "$i" >> $DATA_DIR/$CUR_TEST.$i;
+ j=$(expr $j + 1);
+ done
+ sed -i "s|SEDCATCH_$i|$DATA_DIR\/$CUR_TEST.$i|" $DATA_DIR/$CUR_TEST.in
+ i=$(expr $i + 1);
+done
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+if ! cmp -n $((0x500)) $DATA_DIR/$CUR_TEST.out $DATA_DIR/$CUR_TEST.gen ; then
+ echo "Output differs"
+ fail_test
+fi
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/05-hdr-overlap b/roms/skiboot/external/ffspart/test/tests/05-hdr-overlap
new file mode 100644
index 000000000..39a1a1406
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/05-hdr-overlap
@@ -0,0 +1,14 @@
+#! /bin/sh
+
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 6 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/06-small-flash b/roms/skiboot/external/ffspart/test/tests/06-small-flash
new file mode 100644
index 000000000..7c41e33f0
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/06-small-flash
@@ -0,0 +1,13 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 4 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 6 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/07-big-files b/roms/skiboot/external/ffspart/test/tests/07-big-files
new file mode 100644
index 000000000..f7a0064d9
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/07-big-files
@@ -0,0 +1,26 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+i=1;
+while [ $i -lt 5 ] ; do
+ j=0;
+ while [ $j -lt $((0x101)) ] ; do
+ echo -n "$i" >> $DATA_DIR/$CUR_TEST.$i;
+ j=$(expr $j + 1);
+ done
+ sed -i "s|SEDCATCH_$i|$DATA_DIR\/$CUR_TEST.$i|" $DATA_DIR/$CUR_TEST.in
+ i=$(expr $i + 1);
+done
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -eq 0 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.1|FILE_ONE|" "$STDERR_OUT"
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/08-small-files b/roms/skiboot/external/ffspart/test/tests/08-small-files
new file mode 100644
index 000000000..1e4f3b3be
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/08-small-files
@@ -0,0 +1,22 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+i=1;
+while [ $i -lt 5 ] ; do
+ j=0;
+ while [ $j -lt $((0xe0)) ] ; do
+ echo -n "$i" >> $DATA_DIR/$CUR_TEST.$i;
+ j=$(expr $j + 1);
+ done
+ sed -i "s|SEDCATCH_$i|$DATA_DIR\/$CUR_TEST.$i|" $DATA_DIR/$CUR_TEST.in
+ i=$(expr $i + 1);
+done
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/10-bad-input b/roms/skiboot/external/ffspart/test/tests/10-bad-input
new file mode 100644
index 000000000..d7c433262
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/10-bad-input
@@ -0,0 +1,13 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -eq 0 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/11-long-name b/roms/skiboot/external/ffspart/test/tests/11-long-name
new file mode 100644
index 000000000..893aad478
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/11-long-name
@@ -0,0 +1,11 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/12-bad-numbers-base b/roms/skiboot/external/ffspart/test/tests/12-bad-numbers-base
new file mode 100644
index 000000000..d7c433262
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/12-bad-numbers-base
@@ -0,0 +1,13 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -eq 0 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/13-bad-numbers-size b/roms/skiboot/external/ffspart/test/tests/13-bad-numbers-size
new file mode 100644
index 000000000..d7c433262
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/13-bad-numbers-size
@@ -0,0 +1,13 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -eq 0 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/14-bad-input-flags b/roms/skiboot/external/ffspart/test/tests/14-bad-input-flags
new file mode 100644
index 000000000..d7c433262
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/14-bad-input-flags
@@ -0,0 +1,13 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -eq 0 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/15-overlapping-partitions b/roms/skiboot/external/ffspart/test/tests/15-overlapping-partitions
new file mode 100644
index 000000000..6d85748d0
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/15-overlapping-partitions
@@ -0,0 +1,13 @@
+#! /bin/sh
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 6 ] ; then
+ fail_test
+fi
+
+sed -i "s|$DATA_DIR/$CUR_TEST.in|FILE|" "$STDERR_OUT"
+
+diff_with_result
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/16-create-blank b/roms/skiboot/external/ffspart/test/tests/16-create-blank
new file mode 100644
index 000000000..02640e4e6
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/16-create-blank
@@ -0,0 +1,15 @@
+#! /bin/sh
+
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen --allow_empty"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+if ! cmp $DATA_DIR/$CUR_TEST.out $DATA_DIR/$CUR_TEST.gen ; then
+ echo "Output differs"
+ fail_test
+fi
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/17-toc b/roms/skiboot/external/ffspart/test/tests/17-toc
new file mode 100644
index 000000000..e7783394b
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/17-toc
@@ -0,0 +1,15 @@
+#! /bin/sh
+
+touch $DATA_DIR/$CUR_TEST.gen
+
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $DATA_DIR/$CUR_TEST.gen"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+if ! cmp -n $((0x100)) $DATA_DIR/$CUR_TEST.out $DATA_DIR/$CUR_TEST.gen ; then
+ echo "Output differs"
+ fail_test
+fi
+
+pass_test
diff --git a/roms/skiboot/external/ffspart/test/tests/18-eraseblock-gt-first-partition b/roms/skiboot/external/ffspart/test/tests/18-eraseblock-gt-first-partition
new file mode 100644
index 000000000..662665172
--- /dev/null
+++ b/roms/skiboot/external/ffspart/test/tests/18-eraseblock-gt-first-partition
@@ -0,0 +1,45 @@
+#! /bin/sh
+
+EXPECTED="ID=01 FIRST 0x00000400..0x00000500 (actual=0x00000100) [----------]"
+FFSIMG=$DATA_DIR/$CUR_TEST.gen
+
+command -v pflash > /dev/null || echo "skipping test: pflash required but not found in PATH" && exit 0
+
+
+# https://github.com/open-power/skiboot/issues/205
+touch $FFSIMG
+
+# Use a block size that works with the existing codebase
+run_binary "./ffspart" "-s 0x100 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $FFSIMG"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+CONTENTS=$(pflash -i -F $FFSIMG | grep FIRST)
+
+if [ "$CONTENTS" != "$EXPECTED" ]; then
+ echo "Actual: $CONTENTS"
+ echo "Expected: $EXPECTED"
+ fail_test
+fi
+
+# Use a block size that fails with the existing codebase. This test is expected
+# to fail; change it to expect pass when the issue is fixed.
+# https://github.com/open-power/skiboot/issues/202
+run_binary "./ffspart" "-s 0x1000 -c 10 -i $DATA_DIR/$CUR_TEST.in -p $FFSIMG"
+if [ "$?" -ne 0 ] ; then
+ fail_test
+fi
+
+CONTENTS=$(pflash -i -F $FFSIMG | grep FIRST)
+
+if [ "$CONTENTS" != "$EXPECTED" ]; then
+ echo "This case should be marked as fail but ffspart has bugs"
+ echo "https://github.com/open-power/skiboot/issues/202"
+ echo "Actual: $CONTENTS"
+ echo "Expected: $EXPECTED"
+ pass_test
+fi
+
+
+pass_test