diff mbox

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

Message ID 20161129003845.23051-4-cyril.bur@au1.ibm.com
State RFC
Headers show

Commit Message

Cyril Bur Nov. 29, 2016, 12:38 a.m. UTC
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  |  40 +++++
 external/ffspart/ffspart.c | 416 +++++++++++++++++++++++++++++++++++++++++++++
 external/ffspart/rules.mk  |  33 ++++
 3 files changed, 489 insertions(+)
 create mode 100644 external/ffspart/Makefile
 create mode 100644 external/ffspart/ffspart.c
 create mode 100644 external/ffspart/rules.mk

Comments

Stewart Smith Dec. 6, 2016, 3:21 a.m. UTC | #1
Cyril Bur <cyril.bur@au1.ibm.com> writes:
> A typical input file to generate something that current op-build would create:
> HBI,0x00010000,0x05a0000,EV,./bins/HBI.bin

Could we have size and offset be set to 0 to auto-size them based on the
file provided?
Cyril Bur Dec. 7, 2016, 11:21 p.m. UTC | #2
On Tue, 2016-12-06 at 14:21 +1100, Stewart Smith wrote:
> Cyril Bur <cyril.bur@au1.ibm.com> writes:
> > A typical input file to generate something that current op-build
> > would create:
> > HBI,0x00010000,0x05a0000,EV,./bins/HBI.bin
> 
> Could we have size and offset be set to 0 to auto-size them based on
> the
> file provided?

I was waiting for someone to mention this - yes. I wonder if another
character, '-' or '*' might be better idea just to disambiguate and
avoid mistakes.

I'll add this feature before I resend.

I also wonder if I might add a 'special' line for the first line, must
contain something like '!' at the start to denote its presence which
would essentially contain the command line args so that the only
information needed to generate a pnor is the input file (with the data
obviously).

Thanks,

Cyril

>
diff mbox

Patch

diff --git a/external/ffspart/Makefile b/external/ffspart/Makefile
new file mode 100644
index 0000000..4b71081
--- /dev/null
+++ b/external/ffspart/Makefile
@@ -0,0 +1,40 @@ 
+# Use make V=1 for a verbose build.
+GET_ARCH = ../../external/common/get_arch.sh
+include ../../external/common/rules.mk
+include rules.mk
+
+all: $(EXE)
+
+.PHONY: links
+links: libflash ccan common
+
+$(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/ffspart.c b/external/ffspart/ffspart.c
new file mode 100644
index 0000000..beb338e
--- /dev/null
+++ b/external/ffspart/ffspart.c
@@ -0,0 +1,416 @@ 
+/* 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] -b size -c num -i layout_file -p pnor_file ...\n\n", pname);
+	printf(" Options:\n");
+	printf("\t-b, --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-s, -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'},
+			{"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:i: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 '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;
+		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;
+
+		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, NULL, 0);
+
+		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, NULL, 0);
+
+		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);
+			}
+			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 0000000..7d028ea
--- /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: .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 $@
+