[3/5] external/ffspart: Simple C program to be able to make an FFS partition

Submitted by Cyril Bur on March 17, 2017, 5:13 a.m.

Details

Message ID 20170317051309.16899-4-cyril.bur@au1.ibm.com
State Accepted
Headers show

Commit Message

Cyril Bur March 17, 2017, 5:13 a.m.
A typical input file to generate something that current op-build would create:
HBI,0x00010000,0x05a0000,EV,./bins/HBI.bin
MVPD,0x05b0000,0x0090000,EF,./bins/MVPD.bin
CVPD,0x0640000,0x0048000,EF,./bins/CVPD.bin
DJVPD,0x688000,0x0048000,EF,./bins/DJVPD.bin
HBD,0x006d0000,0x0060000,E,./bins/HBD.bin
SBEC,0x0730000,0x0090000,EI,./bins/SBEC.bin
SBE,0x007c0000,0x0048000,EI,./bins/SBE.bin
ATTR_TMP,0x808000,0x8000,F,./bins/ATTR_TMP.bin
ATTR_PERM,0x810000,0x8000,EF,./bins/ATTR_PERM.bin
WINK,0x00818000,0x0120000,EV,./bins/WINK.bin
GUARD,0x00938000,0x005000,EPF,./bins/GUARD.bin
HBEL,0x0093d000,0x0024000,EF,./bins/HBEL.bin
PAYLOAD,0x961000,0x100000,,./bins/skiboot.lid
BOOTKERNEL,0xa61000,0xf00000,,./bins/petitboot.zImage
NVRAM,0x01961000,0x90000,EPF,./bins/NVRAM.bin
HBRT,0x019f1000,0x360000,EV,./bins/HBRT.bin
OCC,0x001d51000,0x120000,E,./bins/OCC.bin
FIRDATA,0x1e71000,0x3000,EF,./bins/FIRDATA.bin
CAPP,0x001e74000,0x24000,E,./bins/CAPP.bin
HBB,0x0001f67000,0x90000,EV,./bins/HBB.bin
VERSION,0x1ff7000,0x1000,,./bins/VERSION2.bin

Signed-off-by: Cyril Bur <cyril.bur@au1.ibm.com>
---
 external/ffspart/Makefile  |  37 ++++
 external/ffspart/config.h  |  19 ++
 external/ffspart/ffspart.c | 438 +++++++++++++++++++++++++++++++++++++++++++++
 external/ffspart/rules.mk  |  33 ++++
 4 files changed, 527 insertions(+)
 create mode 100644 external/ffspart/Makefile
 create mode 100644 external/ffspart/config.h
 create mode 100644 external/ffspart/ffspart.c
 create mode 100644 external/ffspart/rules.mk

Patch hide | download patch | download mbox

diff --git a/external/ffspart/Makefile b/external/ffspart/Makefile
new file mode 100644
index 00000000..922ff75f
--- /dev/null
+++ b/external/ffspart/Makefile
@@ -0,0 +1,37 @@ 
+# 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)
+
+$(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/external/ffspart/config.h b/external/ffspart/config.h
new file mode 100644
index 00000000..a132a01a
--- /dev/null
+++ b/external/ffspart/config.h
@@ -0,0 +1,19 @@ 
+/* For CCAN */
+
+#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/external/ffspart/ffspart.c b/external/ffspart/ffspart.c
new file mode 100644
index 00000000..50b5861e
--- /dev/null
+++ b/external/ffspart/ffspart.c
@@ -0,0 +1,438 @@ 
+/* Copyright 2013-2016 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 	http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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 <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 100
+#define SEPARATOR ','
+
+enum order {
+	ORDER_ADB,
+	ORDER_ABD
+};
+
+/* Full version number (possibly includes gitid). */
+extern const char version[];
+
+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] -s size -c num -i layout_file -p pnor_file ...\n\n", pname);
+	printf(" Options:\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-o, --order=( ADB | ABD )\n");
+	printf("\t\tOrdering of the TOC, Data and Backup TOC. Currently only ADB (default)\n");
+	printf("\t\tis supported\n");
+	printf("\t-p, --pnor=file\n");
+	printf("\t\tOutput file to write data\n\n");
+	printf("\t-t, --sides=( 1 | 2 )\n");
+	printf("\t\tNumber of sides to the flash (Default: 1)\n");
+}
+
+int main(int argc, char *argv[])
+{
+	const char *pname = argv[0];
+	struct blocklevel_device *bl = NULL;
+	unsigned int sides = 1;
+	uint32_t block_size = 0, block_count = 0;
+	enum order order = ORDER_ADB;
+	bool bad_input = false, backup_part = false;
+	char *pnor = NULL, *input = NULL;
+	struct ffs_hdr *new_hdr;
+	FILE *in_file;
+	char line[MAX_LINE];
+	int rc;
+
+	while(1) {
+		struct option long_opts[] = {
+			{"backup",	no_argument, NULL, 'b'},
+			{"block_size",	required_argument,	NULL,	's'},
+			{"block_count",	required_argument,	NULL,	'c'},
+			{"debug",	no_argument,	NULL,	'g'},
+			{"input",	required_argument,	NULL,	'i'},
+			{"order",	required_argument,	NULL,	'o'},
+			{"pnor",	required_argument,	NULL,	'p'},
+			{"tocs",	required_argument,	NULL,	't'},
+			{NULL,	0,	0, 0}
+		};
+		int c, oidx = 0;
+
+		c = getopt_long(argc, argv, "bc:gi:o:p:s:t:", long_opts, &oidx);
+		if (c == EOF)
+			break;
+		switch(c) {
+		case 'b':
+			backup_part = true;
+			break;
+		case 'c':
+			block_count = strtoul(optarg, NULL, 0);
+			break;
+		case 'g':
+			libflash_debug = true;
+			break;
+		case 'i':
+			input = strdup(optarg);
+			break;
+		case 'o':
+			if (strncmp(optarg, "ABD", 3) == 0)
+				order = ORDER_ABD;
+			else if (strncmp(optarg, "ADB", 3) == 0)
+				order = ORDER_ADB;
+			else
+				bad_input = true;
+			break;
+		case 'p':
+			pnor = strdup(optarg);
+			break;
+		case 's':
+			block_size = strtoul(optarg, NULL, 0);
+			break;
+		case 't':
+			sides = strtoul(optarg, NULL, 0);
+			break;
+		default:
+			exit(1);
+		}
+	}
+
+	if (sides == 0)
+		sides = 1;
+
+	if (sides > 2) {
+		fprintf(stderr, "Greater than two sides is not supported\n");
+		bad_input = true;
+	}
+
+	if (!block_size || !block_count || !input || !pnor)
+		bad_input = true;
+
+	/* TODO Check assumption that sides divide the flash in half. */
+	if (block_count % sides) {
+		fprintf(stderr, "Invalid block_count %u for sides %u\n", block_count, sides);
+		bad_input = true;
+	}
+
+	if (bad_input || order == ORDER_ABD) {
+		print_help(pname);
+		rc = 1;
+		goto out;
+	}
+
+	/*
+	 * TODO: Rethink, is ffspart providing a variable TOC size useful?
+	 * Use 1 block for the size of the partition table...
+	 */
+	rc = ffs_hdr_new(block_size, block_size, block_count / sides, &new_hdr);
+	if (rc) {
+		if (rc == FFS_ERR_BAD_SIZE) {
+			/* Well this check is a tad redudant now */
+			fprintf(stderr, "Bad size parametres passed to libffs: "
+					"size must be a multiple of block_size\n"
+					"size (%u), block_size (%u) \n", block_size, block_size);
+		} else {
+			fprintf(stderr, "Error %d initialising new TOC\n", rc);
+		}
+		goto out;
+	}
+
+	if (sides == 2) {
+		rc = ffs_hdr_add_side(new_hdr);
+		if (rc) {
+			fprintf(stderr, "Couldn't add side to header\n");
+			goto out_free_hdr;
+		}
+	}
+
+	in_file = fopen(input, "r");
+	if (!in_file) {
+		rc = errno;
+		fprintf(stderr, "Couldn't open your input file %s because %s\n", input, strerror(errno));
+		goto out_free_hdr;
+	}
+
+	rc = arch_flash_init(&bl, pnor, true);
+	if (rc) {
+		fprintf(stderr, "Couldn't initialise architecture flash structures\n");
+		goto out_close_f;
+	}
+
+	/*
+	 * '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 file\n");
+		goto out_close_bl;
+	}
+
+	while (fgets(line, MAX_LINE, in_file) != NULL) {
+		struct ffs_entry *new_entry;
+		struct ffs_entry_user user = { 0 };
+		char *pos, *old_pos;
+		char *name, *endptr;
+		int side = -1;
+		uint32_t pbase, psize, pactual = 0;
+
+		if (line[strlen(line) - 1] == '\n')
+			line[strlen(line) - 1] = '\0';
+
+		pos = strchr(line, SEPARATOR);
+		if (!pos) {
+			fprintf(stderr, "Invalid input file format: Couldn't find name\n");
+			rc = -1;
+			goto out_close_bl;
+		}
+		*pos = '\0';
+		name = line;
+		/* There is discussion to be had as to if we should bail here */
+		if (pos - line > FFS_PART_NAME_MAX)
+			fprintf(stderr, "WARNING: Long partition '%s' name will get truncated\n",
+					line);
+
+		pos++;
+		old_pos = pos;
+		pos = strchr(pos, SEPARATOR);
+		if (!pos) {
+			fprintf(stderr, "Invalid input file format: Couldn't find base\n");
+			rc = -1;
+			goto out_close_bl;
+		}
+		*pos = '\0';
+		pbase = strtoul(old_pos, &endptr, 0);
+		if (*endptr != '\0') {
+			fprintf(stderr, "Invalid input file format: Couldn't parse "
+					"'%s' partition base address\n", name);
+			rc = -1;
+			goto out_close_bl;
+		}
+
+		pos++;
+		old_pos = pos;
+		pos = strchr(pos, SEPARATOR);
+		if (!pos) {
+			fprintf(stderr, "Invalid input file format: Couldn't find size\n");
+			rc = -1;
+			goto out_close_bl;
+		}
+		*pos = '\0';
+		psize = strtoul(old_pos, &endptr, 0);
+		if (*endptr != '\0') {
+			fprintf(stderr, "Invalid input file format: Couldn't parse "
+					"'%s' partition length\n", name);
+			rc = -1;
+			goto out_close_bl;
+		}
+
+		pos++;
+		while (*pos != '\0' && *pos != SEPARATOR) {
+			switch (*pos) {
+			case 'E':
+				user.datainteg |= FFS_ENRY_INTEG_ECC;
+				break;
+			case 'V':
+				user.vercheck |= FFS_VERCHECK_SHA512V;
+				break;
+			case 'I':
+				user.vercheck |= FFS_VERCHECK_SHA512EC;
+				break;
+			case 'P':
+				user.miscflags |= FFS_MISCFLAGS_PRESERVED;
+				break;
+			case 'R':
+				user.miscflags |= FFS_MISCFLAGS_READONLY;
+				break;
+			case 'F':
+				user.miscflags |= FFS_MISCFLAGS_REPROVISION;
+				break;
+			/* Not sure these are valid */
+			case 'B':
+				user.miscflags |= FFS_MISCFLAGS_BACKUP;
+				break;
+			case '0':
+			case '1':
+			case '2':
+				/*
+				 * There should only be one side specified, fail if
+				 * we've already seen a side
+				 */
+				if (side != -1) {
+					rc = -1;
+					goto out_close_bl;
+				} else {
+					side = *pos - '0';
+				}
+				break;
+			default:
+				fprintf(stderr, "Unknown flag '%c'\n", *pos);
+				rc = -1;
+				goto out_close_bl;
+			}
+			pos++;
+		}
+
+		if (side == -1) /* Default to 0 */
+			side = 0;
+
+		printf("Adding '%s' 0x%08x, 0x%08x\n", name, pbase, psize);
+		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);
+			goto out_close_bl;
+		}
+
+		rc = ffs_entry_user_set(new_entry, &user);
+		if (rc) {
+			fprintf(stderr, "Invalid flag passed to ffs_entry_user_set\n");
+			goto out_while;
+		}
+
+		rc = ffs_entry_add(new_hdr, new_entry, side);
+		if (rc) {
+			fprintf(stderr, "Couldn't add entry '%s' 0x%08x for 0x%08x\n",
+					name, pbase, psize);
+			goto out_while;
+		}
+
+		if (*pos != '\0') {
+			struct stat data_stat;
+			int data_fd;
+			uint8_t *data_ptr;
+			char *data_fname = pos + 1;
+
+			data_fd = open(data_fname, O_RDONLY);
+			if (data_fd == -1) {
+				fprintf(stderr, "Couldn't open data file for partition '%s' (filename: %s)\n",
+						name, data_fname);
+				rc = -1;
+				goto out_while;
+			}
+
+			if (fstat(data_fd, &data_stat) == -1) {
+				fprintf(stderr, "Couldn't stat data file for partition '%s': %s\n",
+						name, strerror(errno));
+				rc = -1;
+				goto out_if;
+			}
+			pactual = data_stat.st_size;
+
+			/*
+			 * Sanity check that the file isn't too large for
+			 * partition
+			 */
+			if (pactual > psize) {
+				fprintf(stderr, "Data file for partition '%s' is too large\n",
+						name);
+				rc = -1;
+				goto out_if;
+			}
+
+			data_ptr = mmap(NULL, pactual, PROT_READ, MAP_SHARED, data_fd, 0);
+			if (!data_ptr) {
+				fprintf(stderr, "Couldn't mmap data file for partition '%s': %s\n",
+						name, strerror(errno));
+				rc = -1;
+				goto out_if;
+			}
+
+			rc = blocklevel_write(bl, pbase, data_ptr, pactual);
+			if (rc)
+				fprintf(stderr, "Couldn't write data file for partition '%s' to pnor file:"
+					    " %s\n", name, strerror(errno));
+
+			munmap(data_ptr, pactual);
+out_if:
+			close(data_fd);
+			if (rc)
+				goto out_while;
+			/*
+			 * TODO: Update the actual size within the partition table.
+			 */
+		}
+
+		continue;
+out_while:
+		free(new_entry);
+		goto out_close_bl;
+	}
+
+	if (backup_part) {
+		rc = ffs_hdr_create_backup(new_hdr);
+		if (rc) {
+			fprintf(stderr, "Failed to create backup part\n");
+			goto out_close_bl;
+		}
+	}
+
+	rc = ffs_hdr_finalise(bl, new_hdr);
+	if (rc)
+		fprintf(stderr, "Failed to write out TOC values\n");
+
+out_close_bl:
+	arch_flash_close(bl, pnor);
+out_close_f:
+	fclose(in_file);
+out_free_hdr:
+	ffs_hdr_free(new_hdr);
+out:
+	free(input);
+	free(pnor);
+	return rc;
+}
diff --git a/external/ffspart/rules.mk b/external/ffspart/rules.mk
new file mode 100644
index 00000000..d0100a27
--- /dev/null
+++ b/external/ffspart/rules.mk
@@ -0,0 +1,33 @@ 
+.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
+
+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 $@
+