aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/external/ffspart/ffspart.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/external/ffspart/ffspart.c')
-rw-r--r--roms/skiboot/external/ffspart/ffspart.c548
1 files changed, 548 insertions, 0 deletions
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;
+}