/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 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; }