static void lookup_partition(const char *name) { int rc; if (flash_side == 1) { uint32_t other_side_offset; rc = open_partition("OTHER_SIDE"); if (rc == FFS_ERR_PART_NOT_FOUND) fprintf(stderr, "side 1 was specified but there doesn't appear" " to be a second side to this flash\n"); if (rc) exit(1); /* Just need to know where it starts */ rc = ffs_part_info(ffsh, ffs_index, NULL, &other_side_offset, NULL, NULL, NULL); if (rc) exit(1); ffs_close(ffsh); ffsh = NULL; ffs_toc = other_side_offset; } rc = open_partition(name); if (rc) exit(1); }
static void print_ffs_info(uint32_t toc_offset) { struct ffs_handle *ffs_handle; uint32_t other_side_offset = 0; int rc; uint32_t i; rc = ffs_init(toc_offset, fl_total_size, bl, &ffs_handle, 0); if (rc) { fprintf(stderr, "Error %d opening ffs !\n", rc); return; } printf("\n"); printf("TOC@0x%08x Partitions:\n", toc_offset); printf("-----------\n"); for (i = 0;; i++) { uint32_t start, size, act, end; bool ecc; char *name; rc = ffs_part_info(ffs_handle, i, &name, &start, &size, &act, &ecc); if (rc == FFS_ERR_PART_NOT_FOUND) break; if (rc) { fprintf(stderr, "Error %d scanning partitions\n", rc); break; } end = start + size; printf("ID=%02d %15s %08x..%08x (actual=%08x) %s\n", i, name, start, end, act, ecc ? "[ECC]" : ""); if (strcmp(name, "OTHER_SIDE") == 0) other_side_offset = start; free(name); } ffs_close(ffs_handle); if (other_side_offset) print_ffs_info(other_side_offset); }
int ffs_next_side(struct ffs_handle *ffs, struct ffs_handle **new_ffs, bool mark_ecc) { int rc; uint32_t index, offset, max_size; if (!ffs || !new_ffs) return FLASH_ERR_PARM_ERROR; *new_ffs = NULL; rc = ffs_lookup_part(ffs, "OTHER_SIDE", &index); if (rc) return rc; rc = ffs_part_info(ffs, index, NULL, &offset, &max_size, NULL, NULL); if (rc) return rc; return ffs_init(offset, max_size, ffs->bl, new_ffs, mark_ecc); }
static int flash_nvram_probe(struct flash *flash, struct ffs_handle *ffs) { uint32_t start, size, part; bool ecc; int rc; prlog(PR_INFO, "FLASH: probing for NVRAM\n"); rc = ffs_lookup_part(ffs, "NVRAM", &part); if (rc) { prlog(PR_WARNING, "FLASH: no NVRAM partition found\n"); return OPAL_HARDWARE; } rc = ffs_part_info(ffs, part, NULL, &start, &size, NULL, &ecc); if (rc) { /** * @fwts-label NVRAMNoPartition * @fwts-advice OPAL could not find an NVRAM partition * on the system flash. Check that the system flash * has a valid partition table, and that the firmware * build process has added a NVRAM partition. */ prlog(PR_ERR, "FLASH: Can't parse ffs info for NVRAM\n"); return OPAL_HARDWARE; } nvram_flash = flash; nvram_offset = start; nvram_size = ecc ? ecc_buffer_size_minus_ecc(size) : size; platform.nvram_info = flash_nvram_info; platform.nvram_start_read = flash_nvram_start_read; platform.nvram_write = flash_nvram_write; return 0; }
/* * load a resource from FLASH * buf and len shouldn't account for ECC even if partition is ECCed. * * The API here is a bit strange. * If resource has a STB container, buf will contain it * If loading subpartition with STB container, buff will *NOT* contain it * For trusted boot, the whole partition containing the subpart is measured. * * Additionally, the logic to work out how much to read from flash is insane. */ static int flash_load_resource(enum resource_id id, uint32_t subid, void *buf, size_t *len) { int i; int rc = OPAL_RESOURCE; struct ffs_handle *ffs; struct flash *flash; const char *name; bool status = false; bool ecc; bool part_signed = false; void *bufp = buf; size_t bufsz = *len; int ffs_part_num, ffs_part_start, ffs_part_size; int content_size = 0; int offset = 0; lock(&flash_lock); if (!system_flash) { /** * @fwts-label SystemFlashNotFound * @fwts-advice No system flash was found. Check for missing * calls flash_register(...). */ prlog(PR_WARNING, "FLASH: Can't load resource id:%i. " "No system flash found\n", id); goto out_unlock; } flash = system_flash; if (flash->busy) goto out_unlock; for (i = 0, name = NULL; i < ARRAY_SIZE(part_name_map); i++) { if (part_name_map[i].id == id) { name = part_name_map[i].name; break; } } if (!name) { prerror("FLASH: Couldn't find partition for id %d\n", id); goto out_unlock; } /* * If partition doesn't have a subindex but the caller specifies one, * we fail. eg. kernel partition doesn't have a subindex */ if ((part_name_map[i].subid == RESOURCE_SUBID_NONE) && (subid != RESOURCE_SUBID_NONE)) { prerror("PLAT: Partition %s doesn't have subindex\n", name); goto out_unlock; } rc = ffs_init(0, flash->size, flash->bl, &ffs, 1); if (rc) { prerror("FLASH: Can't open ffs handle: %d\n", rc); goto out_unlock; } rc = ffs_lookup_part(ffs, name, &ffs_part_num); if (rc) { /* This is not an error per-se, some partitions * are purposefully absent, don't spam the logs */ prlog(PR_DEBUG, "FLASH: No %s partition\n", name); goto out_free_ffs; } rc = ffs_part_info(ffs, ffs_part_num, NULL, &ffs_part_start, NULL, &ffs_part_size, &ecc); if (rc) { prerror("FLASH: Failed to get %s partition info\n", name); goto out_free_ffs; } prlog(PR_DEBUG,"FLASH: %s partition %s ECC\n", name, ecc ? "has" : "doesn't have"); if (ffs_part_size < SECURE_BOOT_HEADERS_SIZE) { prerror("FLASH: secboot headers bigger than " "partition size 0x%x\n", ffs_part_size); goto out_free_ffs; } rc = blocklevel_read(flash->bl, ffs_part_start, bufp, SECURE_BOOT_HEADERS_SIZE); if (rc) { prerror("FLASH: failed to read the first 0x%x from " "%s partition, rc %d\n", SECURE_BOOT_HEADERS_SIZE, name, rc); goto out_free_ffs; } part_signed = stb_is_container(bufp, SECURE_BOOT_HEADERS_SIZE); prlog(PR_DEBUG, "FLASH: %s partition %s signed\n", name, part_signed ? "is" : "isn't"); /* * part_start/size are raw pointers into the partition. * ie. they will account for ECC if included. */ if (part_signed) { bufp += SECURE_BOOT_HEADERS_SIZE; bufsz -= SECURE_BOOT_HEADERS_SIZE; content_size = stb_sw_payload_size(buf, SECURE_BOOT_HEADERS_SIZE); *len = content_size + SECURE_BOOT_HEADERS_SIZE; if (content_size > bufsz) { prerror("FLASH: content size > buffer size\n"); rc = OPAL_PARAMETER; goto out_free_ffs; } ffs_part_start += SECURE_BOOT_HEADERS_SIZE; rc = blocklevel_read(flash->bl, ffs_part_start, bufp, content_size); if (rc) { prerror("FLASH: failed to read content size %d" " %s partition, rc %d\n", content_size, name, rc); goto out_free_ffs; } if (subid == RESOURCE_SUBID_NONE) goto done_reading; rc = flash_subpart_info(bufp, content_size, ffs_part_size, NULL, subid, &offset, &content_size); if (rc) { prerror("FLASH: Failed to parse subpart info for %s\n", name); goto out_free_ffs; } bufp += offset; goto done_reading; } else /* stb_signed */ { /* * Back to the old way of doing things, no STB header. */ if (subid == RESOURCE_SUBID_NONE) { if (id == RESOURCE_ID_KERNEL || id == RESOURCE_ID_INITRAMFS) { /* * Because actualSize is a lie, we compute the * size of the BOOTKERNEL based on what the ELF * headers say. Otherwise we end up reading more * than we should */ content_size = sizeof_elf_from_hdr(buf); if (!content_size) { prerror("FLASH: Invalid ELF header part" " %s\n", name); rc = OPAL_RESOURCE; goto out_free_ffs; } } else { content_size = ffs_part_size; } if (content_size > bufsz) { prerror("FLASH: %s content size %d > " " buffer size %lu\n", name, content_size, bufsz); rc = OPAL_PARAMETER; goto out_free_ffs; } prlog(PR_DEBUG, "FLASH: computed %s size %u\n", name, content_size); rc = blocklevel_read(flash->bl, ffs_part_start, buf, content_size); if (rc) { prerror("FLASH: failed to read content size %d" " %s partition, rc %d\n", content_size, name, rc); goto out_free_ffs; } *len = content_size; goto done_reading; } BUILD_ASSERT(FLASH_SUBPART_HEADER_SIZE <= SECURE_BOOT_HEADERS_SIZE); rc = flash_subpart_info(bufp, SECURE_BOOT_HEADERS_SIZE, ffs_part_size, &ffs_part_size, subid, &offset, &content_size); if (rc) { prerror("FLASH: FAILED reading subpart info. rc=%d\n", rc); goto out_free_ffs; } *len = ffs_part_size; prlog(PR_DEBUG, "FLASH: Computed %s partition size: %u " "(subpart %u size %u offset %u)\n", name, ffs_part_size, subid, content_size, offset); /* * For a sub partition, we read the whole (computed) * partition, and then measure that. * Afterwards, we memmove() things back into place for * the caller. */ rc = blocklevel_read(flash->bl, ffs_part_start, buf, ffs_part_size); bufp += offset; } done_reading: /* * Verify and measure the retrieved PNOR partition as part of the * secure boot and trusted boot requirements */ secureboot_verify(id, buf, *len); trustedboot_measure(id, buf, *len); /* Find subpartition */ if (subid != RESOURCE_SUBID_NONE) { memmove(buf, bufp, content_size); *len = content_size; } status = true; out_free_ffs: ffs_close(ffs); out_unlock: unlock(&flash_lock); return status ? OPAL_SUCCESS : rc; }
static struct dt_node *flash_add_dt_node(struct flash *flash, int id) { int i; int rc; const char *name; bool ecc; struct ffs_handle *ffs; int ffs_part_num, ffs_part_start, ffs_part_size; struct dt_node *flash_node; struct dt_node *partition_container_node; struct dt_node *partition_node; flash_node = dt_new_addr(opal_node, "flash", id); dt_add_property_strings(flash_node, "compatible", "ibm,opal-flash"); dt_add_property_cells(flash_node, "ibm,opal-id", id); dt_add_property_u64(flash_node, "reg", flash->size); dt_add_property_cells(flash_node, "ibm,flash-block-size", flash->block_size); if (flash->no_erase) dt_add_property(flash_node, "no-erase", NULL, 0); /* we fix to 32-bits */ dt_add_property_cells(flash_node, "#address-cells", 1); dt_add_property_cells(flash_node, "#size-cells", 1); /* Add partition container node */ partition_container_node = dt_new(flash_node, "partitions"); dt_add_property_strings(partition_container_node, "compatible", "fixed-partitions"); /* we fix to 32-bits */ dt_add_property_cells(partition_container_node, "#address-cells", 1); dt_add_property_cells(partition_container_node, "#size-cells", 1); /* Add partitions */ for (i = 0, name = NULL; i < ARRAY_SIZE(part_name_map); i++) { name = part_name_map[i].name; rc = ffs_init(0, flash->size, flash->bl, &ffs, 1); if (rc) { prerror("FLASH: Can't open ffs handle\n"); continue; } rc = ffs_lookup_part(ffs, name, &ffs_part_num); if (rc) { /* This is not an error per-se, some partitions * are purposefully absent, don't spam the logs */ prlog(PR_DEBUG, "FLASH: No %s partition\n", name); continue; } rc = ffs_part_info(ffs, ffs_part_num, NULL, &ffs_part_start, NULL, &ffs_part_size, &ecc); if (rc) { prerror("FLASH: Failed to get %s partition info\n", name); continue; } partition_node = dt_new_addr(partition_container_node, "partition", ffs_part_start); dt_add_property_strings(partition_node, "label", name); dt_add_property_cells(partition_node, "reg", ffs_part_start, ffs_part_size); if (part_name_map[i].id != RESOURCE_ID_KERNEL_FW) { /* Mark all partitions other than the full PNOR and the boot kernel * firmware as read only. These two partitions are the only partitions * that are properly erase block aligned at this time. */ dt_add_property(partition_node, "read-only", NULL, 0); } } partition_node = dt_new_addr(partition_container_node, "partition", 0); dt_add_property_strings(partition_node, "label", "PNOR"); dt_add_property_cells(partition_node, "reg", 0, flash->size); return flash_node; }
int main(int argc, char *argv[]) { const char *pname = argv[0]; uint32_t address = 0, read_size = 0, write_size = 0; uint32_t erase_start = 0, erase_size = 0; bool erase = false, do_clear = false; bool program = false, erase_all = false, info = false, do_read = false; bool enable_4B = false, disable_4B = false; bool show_help = false, show_version = false; bool no_action = false, tune = false; char *write_file = NULL, *read_file = NULL, *part_name = NULL; bool ffs_toc_seen = false; int rc; while(1) { static struct option long_opts[] = { {"address", required_argument, NULL, 'a'}, {"size", required_argument, NULL, 's'}, {"partition", required_argument, NULL, 'P'}, {"bmc", no_argument, NULL, 'b'}, {"enable-4B", no_argument, NULL, '4'}, {"disable-4B", no_argument, NULL, '3'}, {"read", required_argument, NULL, 'r'}, {"erase-all", no_argument, NULL, 'E'}, {"erase", no_argument, NULL, 'e'}, {"program", required_argument, NULL, 'p'}, {"force", no_argument, NULL, 'f'}, {"info", no_argument, NULL, 'i'}, {"tune", no_argument, NULL, 't'}, {"dummy", no_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {"debug", no_argument, NULL, 'g'}, {"side", required_argument, NULL, 'S'}, {"toc", required_argument, NULL, 'T'}, {"clear", no_argument, NULL, 'c'} }; int c, oidx = 0; c = getopt_long(argc, argv, "a:s:P:r:43Eep:fdihvbtgS:T:c", long_opts, &oidx); if (c == EOF) break; switch(c) { case 'a': address = strtoul(optarg, NULL, 0); break; case 's': read_size = write_size = strtoul(optarg, NULL, 0); break; case 'P': part_name = strdup(optarg); break; case '4': enable_4B = true; break; case '3': disable_4B = true; break; case 'r': do_read = true; read_file = strdup(optarg); break; case 'E': erase_all = erase = true; break; case 'e': erase = true; break; case 'p': program = true; write_file = strdup(optarg); break; case 'f': must_confirm = false; break; case 'd': must_confirm = false; dummy_run = true; break; case 'i': info = true; break; case 'b': bmc_flash = true; break; case 't': tune = true; break; case 'v': show_version = true; break; case 'h': show_help = show_version = true; break; case 'g': libflash_debug = true; break; case 'S': flash_side = atoi(optarg); break; case 'T': ffs_toc_seen = true; ffs_toc = strtoul(optarg, NULL, 0); break; case 'c': do_clear = true; break; default: exit(1); } } /* Check if we need to access the flash at all (which will * also tune them as a side effect */ no_action = !erase && !program && !info && !do_read && !enable_4B && !disable_4B && !tune && !do_clear; /* Nothing to do, if we didn't already, print usage */ if (no_action && !show_version) show_help = show_version = true; if (show_version) print_version(); if (show_help) print_help(pname); if (no_action) return 0; /* --enable-4B and --disable-4B are mutually exclusive */ if (enable_4B && disable_4B) { fprintf(stderr, "--enable-4B and --disable-4B are mutually" " exclusive !\n"); exit(1); } /* 4B not supported on BMC flash */ if (enable_4B && bmc_flash) { fprintf(stderr, "--enable-4B not supported on BMC flash !\n"); exit(1); } /* partitions not supported on BMC flash */ if (part_name && bmc_flash) { fprintf(stderr, "--partition not supported on BMC flash !\n"); exit(1); } /* part-name and erase-all make no sense together */ if (part_name && erase_all) { fprintf(stderr, "--partition and --erase-all are mutually" " exclusive !\n"); exit(1); } /* Read command should always come with a file */ if (do_read && !read_file) { fprintf(stderr, "Read with no file specified !\n"); exit(1); } /* Program command should always come with a file */ if (program && !write_file) { fprintf(stderr, "Program with no file specified !\n"); exit(1); } /* If both partition and address specified, error out */ if (address && part_name) { fprintf(stderr, "Specify partition or address, not both !\n"); exit(1); } if (do_clear && !part_name) { fprintf(stderr, "--clear only supported on a partition name\n"); exit(1); } /* Explicitly only support two sides */ if (flash_side != 0 && flash_side != 1) { fprintf(stderr, "Unexpected value for --side '%d'\n", flash_side); exit(1); } if (ffs_toc_seen && flash_side) { fprintf(stderr, "--toc and --side are exclusive"); exit(1); } /* If file specified but not size, get size from file */ if (write_file && !write_size) { struct stat stbuf; if (stat(write_file, &stbuf)) { perror("Failed to get file size"); exit(1); } write_size = stbuf.st_size; } if (bmc_flash) { if (arch_flash_bmc(NULL, 1) == -1) { fprintf(stderr, "Can't switch to BMC flash on this architecture\n"); exit(1); } } if (arch_flash_init(&bl, NULL, true)) exit(1); on_exit(exiting, NULL); rc = blocklevel_get_info(bl, &fl_name, &fl_total_size, &fl_erase_granule); if (rc) { fprintf(stderr, "Error %d getting flash info\n", rc); exit(1); } /* If -t is passed, then print a nice message */ if (tune) printf("Flash and controller tuned\n"); /* If read specified and no read_size, use flash size */ if (do_read && !read_size && !part_name) read_size = fl_total_size; /* We have a partition specified, grab the details */ if (part_name) lookup_partition(part_name); /* We have a partition, adjust read/write size if needed */ if (ffsh && ffs_index >= 0) { uint32_t pstart, pmaxsz, pactsize; bool ecc; int rc; rc = ffs_part_info(ffsh, ffs_index, NULL, &pstart, &pmaxsz, &pactsize, &ecc); if (rc) { fprintf(stderr,"Failed to get partition info\n"); exit(1); } if (!ecc && do_clear) { fprintf(stderr, "The partition on which to do --clear " "does not have ECC, are you sure?\n"); check_confirm(); /* Still confirm later on */ must_confirm = true; } /* Read size is obtained from partition "actual" size */ if (!read_size) read_size = pactsize; /* Write size is max size of partition */ if (!write_size) write_size = pmaxsz; /* Crop write size to partition size */ if (write_size > pmaxsz) { printf("WARNING: Size (%d bytes) larger than partition" " (%d bytes), cropping to fit\n", write_size, pmaxsz); write_size = pmaxsz; } /* If erasing, check partition alignment */ if (erase && ((pstart | pmaxsz) & 0xfff)) { fprintf(stderr,"Partition not aligned properly\n"); exit(1); } /* Set address */ address = pstart; } /* Align erase boundaries */ if (erase && !erase_all) { uint32_t mask = 0xfff; uint32_t erase_end; /* Dummy size for erase, will be adjusted later */ if (!write_size) write_size = 1; erase_start = address & ~mask; erase_end = ((address + write_size) + mask) & ~mask; erase_size = erase_end - erase_start; if (erase_start != address || erase_size != write_size) fprintf(stderr, "WARNING: Erase region adjusted" " to 0x%08x..0x%08x\n", erase_start, erase_end); } /* Process commands */ if (enable_4B) enable_4B_addresses(); if (disable_4B) disable_4B_addresses(); if (info) { /* * Don't pass through modfied TOC value if the modification was done * because of --size, but still respect if it came from --toc (we * assume the user knows what they're doing in that case) */ print_flash_info(flash_side ? 0 : ffs_toc); } /* Unlock flash (PNOR only) */ if ((erase || program || do_clear) && !bmc_flash) { need_relock = arch_flash_set_wrprotect(bl, false); if (need_relock == -1) { fprintf(stderr, "Architecture doesn't support write protection on flash\n"); need_relock = 0; exit (1); } } if (do_read) do_read_file(read_file, address, read_size); if (erase_all) erase_chip(); else if (erase) erase_range(erase_start, erase_size, program); if (program) program_file(write_file, address, write_size); if (do_clear) set_ecc(address, write_size); return 0; }
int ffs_init(uint32_t offset, uint32_t max_size, struct blocklevel_device *bl, struct ffs_handle **ffs, bool mark_ecc) { struct ffs_hdr hdr; struct ffs_hdr blank_hdr; struct ffs_handle *f; uint64_t total_size; int rc, i; if (!ffs || !bl) return FLASH_ERR_PARM_ERROR; *ffs = NULL; rc = blocklevel_get_info(bl, NULL, &total_size, NULL); if (rc) { FL_ERR("FFS: Error %d retrieving flash info\n", rc); return rc; } if (total_size > UINT_MAX) return FLASH_ERR_VERIFY_FAILURE; if ((offset + max_size) < offset) return FLASH_ERR_PARM_ERROR; if ((max_size > total_size)) return FLASH_ERR_PARM_ERROR; /* Read flash header */ rc = blocklevel_read(bl, offset, &hdr, sizeof(hdr)); if (rc) { FL_ERR("FFS: Error %d reading flash header\n", rc); return rc; } /* * Flash controllers can get deconfigured or otherwise upset, when this * happens they return all 0xFF bytes. * An ffs_hdr consisting of all 0xFF cannot be valid and it would be * nice to drop a hint to the user to help with debugging. This will * help quickly differentiate between flash corruption and standard * type 'reading from the wrong place' errors vs controller errors or * reading erased data. */ memset(&blank_hdr, UINT_MAX, sizeof(struct ffs_hdr)); if (memcmp(&blank_hdr, &hdr, sizeof(struct ffs_hdr)) == 0) { FL_ERR("FFS: Reading the flash has returned all 0xFF.\n"); FL_ERR("Are you reading erased flash?\n"); FL_ERR("Is something else using the flash controller?\n"); return FLASH_ERR_BAD_READ; } /* Allocate ffs_handle structure and start populating */ f = malloc(sizeof(*f)); if (!f) return FLASH_ERR_MALLOC_FAILED; memset(f, 0, sizeof(*f)); f->toc_offset = offset; f->max_size = max_size; f->bl = bl; /* Convert and check flash header */ rc = ffs_check_convert_header(&f->hdr, &hdr); if (rc) { FL_INF("FFS: Flash header not found. Code: %d\n", rc); goto out; } /* Check header is sane */ if ((f->hdr.block_size * f->hdr.size) > max_size) { rc = FLASH_ERR_PARM_ERROR; FL_ERR("FFS: Flash header exceeds max flash size\n"); goto out; } if ((f->hdr.entry_size * f->hdr.entry_count) > (f->hdr.block_size * f->hdr.size)) { rc = FLASH_ERR_PARM_ERROR; FL_ERR("FFS: Flash header entries exceeds available blocks\n"); goto out; } /* * Decide how much of the image to grab to get the whole * partition map. */ f->cached_size = f->hdr.block_size * f->hdr.size; /* Check for overflow or a silly size */ if (!f->hdr.size || f->cached_size / f->hdr.size != f->hdr.block_size) { rc= FLASH_ERR_MALLOC_FAILED; FL_ERR("FFS: Cache size overflow (0x%x * 0x%x)\n", f->hdr.block_size, f->hdr.size); goto out; } FL_DBG("FFS: Partition map size: 0x%x\n", f->cached_size); /* Allocate cache */ f->cache = malloc(f->cached_size); if (!f->cache) { rc = FLASH_ERR_MALLOC_FAILED; goto out; } /* Read the cached map */ rc = blocklevel_read(bl, offset, f->cache, f->cached_size); if (rc) { FL_ERR("FFS: Error %d reading flash partition map\n", rc); goto out; } if (mark_ecc) { uint32_t start, total_size; bool ecc; for (i = 0; i < f->hdr.entry_count; i++) { rc = ffs_part_info(f, i, NULL, &start, &total_size, NULL, &ecc); if (rc) { FL_ERR("FFS: Failed to read ffs partition %d\n", i); goto out; } if (ecc) { rc = blocklevel_ecc_protect(bl, start, total_size); if (rc) { FL_ERR("FFS: Failed to blocklevel_ecc_protect(0x%08x, 0x%08x)\n", start, total_size); goto out; } } /* ecc */ } /* for */ } out: if (rc == 0) *ffs = f; else free(f); return rc; }