/* * This function is shared between the create and update subcommands. * The difference between the two subcommands is that when the FIP file * is created, the parsing of an existing FIP is skipped. This results * in update_fip() creating the new FIP file from scratch because the * internal image table is not populated. */ static void update_fip(void) { toc_entry_t *toc_entry; image_t *image; /* Add or replace images in the FIP file. */ for (toc_entry = toc_entries; toc_entry->cmdline_name != NULL; toc_entry++) { if (toc_entry->action != DO_PACK) continue; image = read_image_from_file(toc_entry, toc_entry->action_arg); if (toc_entry->image != NULL) { if (verbose) log_dbgx("Replacing image %s.bin with %s", toc_entry->cmdline_name, toc_entry->action_arg); replace_image(toc_entry->image, image); } else { if (verbose) log_dbgx("Adding image %s", toc_entry->action_arg); add_image(image); } /* Link backpointer from lookup entry. */ toc_entry->image = image; free(toc_entry->action_arg); toc_entry->action_arg = NULL; } }
/* * This function is shared between the create and update subcommands. * The difference between the two subcommands is that when the FIP file * is created, the parsing of an existing FIP is skipped. This results * in update_fip() creating the new FIP file from scratch because the * internal image table is not populated. */ static void update_fip(void) { image_desc_t *desc; /* Add or replace images in the FIP file. */ for (desc = image_desc_head; desc != NULL; desc = desc->next) { image_t *image; if (desc->action != DO_PACK) continue; image = read_image_from_file(&desc->uuid, desc->action_arg); if (desc->image != NULL) { if (verbose) { log_dbgx("Replacing %s with %s", desc->cmdline_name, desc->action_arg); } free(desc->image); desc->image = image; } else { if (verbose) log_dbgx("Adding image %s", desc->action_arg); desc->image = image; } } }
/* * This function is shared between the create and update subcommands. * The difference between the two subcommands is that when the FIP file * is created, the parsing of an existing FIP is skipped. This results * in update_fip() creating the new FIP file from scratch because the * internal image table is not populated. */ static void update_fip(void) { toc_entry_t *toc_entry; image_t *new_image, *old_image; /* Add or replace images in the FIP file. */ for (toc_entry = toc_entries; toc_entry->cmdline_name != NULL; toc_entry++) { if (toc_entry->action != DO_PACK) continue; new_image = read_image_from_file(&toc_entry->uuid, toc_entry->action_arg); old_image = lookup_image_from_uuid(&toc_entry->uuid); if (old_image != NULL) { if (verbose) log_dbgx("Replacing image %s.bin with %s", toc_entry->cmdline_name, toc_entry->action_arg); replace_image(old_image, new_image); } else { if (verbose) log_dbgx("Adding image %s", toc_entry->action_arg); add_image(new_image); } free(toc_entry->action_arg); toc_entry->action_arg = NULL; } }
static int info_cmd(int argc, char *argv[]) { image_t *image; uint64_t image_offset; uint64_t image_size = 0; fip_toc_header_t toc_header; int i; if (argc != 2) info_usage(); argc--, argv++; parse_fip(argv[0], &toc_header); if (verbose) { log_dbgx("toc_header[name]: 0x%llX", (unsigned long long)toc_header.name); log_dbgx("toc_header[serial_number]: 0x%llX", (unsigned long long)toc_header.serial_number); log_dbgx("toc_header[flags]: 0x%llX", (unsigned long long)toc_header.flags); } image_offset = sizeof(fip_toc_header_t) + (sizeof(fip_toc_entry_t) * (nr_images + 1)); for (i = 0; i < nr_images; i++) { toc_entry_t *toc_entry; image = images[i]; toc_entry = lookup_entry_from_uuid(&image->uuid); if (toc_entry != NULL) printf("%s: ", toc_entry->name); else printf("Unknown entry: "); image_size = image->size; printf("offset=0x%llX, size=0x%llX", (unsigned long long)image_offset, (unsigned long long)image_size); if (toc_entry != NULL) printf(", cmdline=\"--%s\"", toc_entry->cmdline_name); if (verbose) { unsigned char md[SHA256_DIGEST_LENGTH]; SHA256(image->buffer, image_size, md); printf(", sha256="); md_print(md, sizeof(md)); } putchar('\n'); image_offset += image_size; } free_images(); return 0; }
static int info_cmd(int argc, char *argv[]) { image_t *image; uint64_t image_offset; uint64_t image_size = 0; fip_toc_header_t toc_header; int i; if (argc != 2) usage(); argc--, argv++; parse_fip(argv[0], &toc_header); if (verbose) { log_dbgx("toc_header[name]: 0x%llX", (unsigned long long)toc_header.name); log_dbgx("toc_header[serial_number]: 0x%llX", (unsigned long long)toc_header.serial_number); log_dbgx("toc_header[flags]: 0x%llX", (unsigned long long)toc_header.flags); } image_offset = sizeof(fip_toc_header_t) + (sizeof(fip_toc_entry_t) * (nr_images + 1)); for (i = 0; i < nr_images; i++) { image = images[i]; if (image->toc_entry != NULL) printf("%s: ", image->toc_entry->name); else printf("Unknown entry: "); image_size = image->size; printf("offset=0x%llX, size=0x%llX", (unsigned long long)image_offset, (unsigned long long)image_size); if (image->toc_entry != NULL) printf(", cmdline=\"--%s\"\n", image->toc_entry->cmdline_name); else putchar('\n'); image_offset += image_size; } free_images(); return 0; }
static int info_cmd(int argc, char *argv[]) { image_desc_t *desc; fip_toc_header_t toc_header; if (argc != 2) info_usage(); argc--, argv++; parse_fip(argv[0], &toc_header); if (verbose) { log_dbgx("toc_header[name]: 0x%llX", (unsigned long long)toc_header.name); log_dbgx("toc_header[serial_number]: 0x%llX", (unsigned long long)toc_header.serial_number); log_dbgx("toc_header[flags]: 0x%llX", (unsigned long long)toc_header.flags); } for (desc = image_desc_head; desc != NULL; desc = desc->next) { image_t *image = desc->image; if (image == NULL) continue; printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"", desc->name, (unsigned long long)image->toc_e.offset_address, (unsigned long long)image->toc_e.size, desc->cmdline_name); #ifndef _MSC_VER /* We don't have SHA256 for Visual Studio. */ if (verbose) { unsigned char md[SHA256_DIGEST_LENGTH]; SHA256(image->buffer, image->toc_e.size, md); printf(", sha256="); md_print(md, sizeof(md)); } #endif putchar('\n'); } return 0; }
static int remove_cmd(int argc, char *argv[]) { struct option opts[toc_entries_len + 2]; char outfile[FILENAME_MAX] = { 0 }; fip_toc_header_t toc_header; toc_entry_t *toc_entry; int fflag = 0; int i; if (argc < 2) usage(); i = fill_common_opts(opts, no_argument); add_opt(opts, i, "force", no_argument, 'f'); add_opt(opts, ++i, "out", required_argument, 'o'); add_opt(opts, ++i, NULL, 0, 0); while (1) { int c, opt_index; c = getopt_long(argc, argv, "fo:", opts, &opt_index); if (c == -1) break; switch (c) { case OPT_TOC_ENTRY: toc_entry = &toc_entries[opt_index]; toc_entry->action = DO_REMOVE; break; case 'f': fflag = 1; break; case 'o': snprintf(outfile, sizeof(outfile), "%s", optarg); break; default: usage(); } } argc -= optind; argv += optind; if (argc == 0) usage(); if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag) log_errx("File %s already exists, use --force to overwrite it", outfile); if (outfile[0] == '\0') snprintf(outfile, sizeof(outfile), "%s", argv[0]); parse_fip(argv[0], &toc_header); for (toc_entry = toc_entries; toc_entry->cmdline_name != NULL; toc_entry++) { if (toc_entry->action != DO_REMOVE) continue; if (toc_entry->image != NULL) { if (verbose) log_dbgx("Removing %s.bin", toc_entry->cmdline_name); remove_image(toc_entry->image); } else { log_warnx("Requested image %s.bin is not in %s", toc_entry->cmdline_name, argv[0]); } } pack_images(outfile, toc_header.flags); free_images(); return 0; }
static int unpack_cmd(int argc, char *argv[]) { struct option opts[toc_entries_len + 3]; char file[FILENAME_MAX], outdir[PATH_MAX] = { 0 }; toc_entry_t *toc_entry; int fflag = 0; int unpack_all = 1; int i; if (argc < 2) usage(); i = fill_common_opts(opts, required_argument); add_opt(opts, i, "force", no_argument, 'f'); add_opt(opts, ++i, "out", required_argument, 'o'); add_opt(opts, ++i, NULL, 0, 0); while (1) { int c, opt_index; c = getopt_long(argc, argv, "fo:", opts, &opt_index); if (c == -1) break; switch (c) { case OPT_TOC_ENTRY: unpack_all = 0; toc_entry = &toc_entries[opt_index]; toc_entry->action = DO_UNPACK; toc_entry->action_arg = strdup(optarg); if (toc_entry->action_arg == NULL) log_err("strdup"); break; case 'f': fflag = 1; break; case 'o': snprintf(outdir, sizeof(outdir), "%s", optarg); break; default: usage(); } } argc -= optind; argv += optind; if (argc == 0) usage(); parse_fip(argv[0], NULL); if (outdir[0] != '\0') if (chdir(outdir) == -1) log_err("chdir %s", outdir); /* Mark all images to be unpacked. */ if (unpack_all) { for (toc_entry = toc_entries; toc_entry->cmdline_name != NULL; toc_entry++) { if (toc_entry->image != NULL) { toc_entry->action = DO_UNPACK; toc_entry->action_arg = NULL; } } } /* Unpack all specified images. */ for (toc_entry = toc_entries; toc_entry->cmdline_name != NULL; toc_entry++) { if (toc_entry->action != DO_UNPACK) continue; /* Build filename. */ if (toc_entry->action_arg == NULL) snprintf(file, sizeof(file), "%s.bin", toc_entry->cmdline_name); else snprintf(file, sizeof(file), "%s", toc_entry->action_arg); if (toc_entry->image == NULL) { log_warnx("Requested image %s is not in %s", file, argv[0]); free(toc_entry->action_arg); toc_entry->action_arg = NULL; continue; } if (access(file, F_OK) != 0 || fflag) { if (verbose) log_dbgx("Unpacking %s", file); write_image_to_file(toc_entry->image, file); } else { log_warnx("File %s already exists, use --force to overwrite it", file); } free(toc_entry->action_arg); toc_entry->action_arg = NULL; } free_images(); return 0; }
static int pack_images(char *filename, uint64_t toc_flags) { FILE *fp; image_t *image; fip_toc_header_t *toc_header; fip_toc_entry_t *toc_entry; char *buf; uint64_t entry_offset, buf_size, payload_size; int i; /* Calculate total payload size and allocate scratch buffer. */ payload_size = 0; for (i = 0; i < nr_images; i++) payload_size += images[i]->size; buf_size = sizeof(fip_toc_header_t) + sizeof(fip_toc_entry_t) * (nr_images + 1); buf = calloc(1, buf_size); if (buf == NULL) log_err("calloc"); /* Build up header and ToC entries from the image table. */ toc_header = (fip_toc_header_t *)buf; toc_header->name = TOC_HEADER_NAME; toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER; toc_header->flags = toc_flags; toc_entry = (fip_toc_entry_t *)(toc_header + 1); entry_offset = buf_size; for (i = 0; i < nr_images; i++) { image = images[i]; memcpy(&toc_entry->uuid, &image->uuid, sizeof(uuid_t)); toc_entry->offset_address = entry_offset; toc_entry->size = image->size; toc_entry->flags = 0; entry_offset += toc_entry->size; toc_entry++; } /* Append a null uuid entry to mark the end of ToC entries. */ memcpy(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)); toc_entry->offset_address = entry_offset; toc_entry->size = 0; toc_entry->flags = 0; /* Generate the FIP file. */ fp = fopen(filename, "w"); if (fp == NULL) log_err("fopen %s", filename); if (verbose) log_dbgx("Metadata size: %zu bytes", buf_size); if (fwrite(buf, 1, buf_size, fp) != buf_size) log_errx("Failed to write image to %s", filename); free(buf); if (verbose) log_dbgx("Payload size: %zu bytes", payload_size); for (i = 0; i < nr_images; i++) { image = images[i]; if (fwrite(image->buffer, 1, image->size, fp) != image->size) log_errx("Failed to write image to %s", filename); } fclose(fp); return 0; }
static int remove_cmd(int argc, char *argv[]) { struct option *opts = NULL; size_t nr_opts = 0; char outfile[PATH_MAX] = { 0 }; fip_toc_header_t toc_header; image_desc_t *desc; unsigned long align = 1; int fflag = 0; if (argc < 2) remove_usage(); opts = fill_common_opts(opts, &nr_opts, no_argument); opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN); opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); opts = add_opt(opts, &nr_opts, "force", no_argument, 'f'); opts = add_opt(opts, &nr_opts, "out", required_argument, 'o'); opts = add_opt(opts, &nr_opts, NULL, 0, 0); while (1) { int c, opt_index = 0; c = getopt_long(argc, argv, "b:fo:", opts, &opt_index); if (c == -1) break; switch (c) { case OPT_TOC_ENTRY: { image_desc_t *desc; desc = lookup_image_desc_from_opt(opts[opt_index].name); set_image_desc_action(desc, DO_REMOVE, NULL); break; } case OPT_ALIGN: align = get_image_align(optarg); break; case 'b': { char name[_UUID_STR_LEN + 1], filename[PATH_MAX]; uuid_t uuid = { 0 }; image_desc_t *desc; parse_blob_opt(optarg, &uuid, filename, sizeof(filename)); if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0) remove_usage(); desc = lookup_image_desc_from_uuid(&uuid); if (desc == NULL) { uuid_to_str(name, sizeof(name), &uuid); desc = new_image_desc(&uuid, name, "blob"); add_image_desc(desc); } set_image_desc_action(desc, DO_REMOVE, NULL); break; } case 'f': fflag = 1; break; case 'o': snprintf(outfile, sizeof(outfile), "%s", optarg); break; default: remove_usage(); } } argc -= optind; argv += optind; free(opts); if (argc == 0) remove_usage(); if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag) log_errx("File %s already exists, use --force to overwrite it", outfile); if (outfile[0] == '\0') snprintf(outfile, sizeof(outfile), "%s", argv[0]); parse_fip(argv[0], &toc_header); for (desc = image_desc_head; desc != NULL; desc = desc->next) { if (desc->action != DO_REMOVE) continue; if (desc->image != NULL) { if (verbose) log_dbgx("Removing %s", desc->cmdline_name); free(desc->image); desc->image = NULL; } else { log_warnx("%s does not exist in %s", desc->cmdline_name, argv[0]); } } pack_images(outfile, toc_header.flags, align); return 0; }
static int unpack_cmd(int argc, char *argv[]) { struct option *opts = NULL; size_t nr_opts = 0; char outdir[PATH_MAX] = { 0 }; image_desc_t *desc; int fflag = 0; int unpack_all = 1; if (argc < 2) unpack_usage(); opts = fill_common_opts(opts, &nr_opts, required_argument); opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); opts = add_opt(opts, &nr_opts, "force", no_argument, 'f'); opts = add_opt(opts, &nr_opts, "out", required_argument, 'o'); opts = add_opt(opts, &nr_opts, NULL, 0, 0); while (1) { int c, opt_index = 0; c = getopt_long(argc, argv, "b:fo:", opts, &opt_index); if (c == -1) break; switch (c) { case OPT_TOC_ENTRY: { image_desc_t *desc; desc = lookup_image_desc_from_opt(opts[opt_index].name); set_image_desc_action(desc, DO_UNPACK, optarg); unpack_all = 0; break; } case 'b': { char name[_UUID_STR_LEN + 1]; char filename[PATH_MAX] = { 0 }; uuid_t uuid = { 0 }; image_desc_t *desc; parse_blob_opt(optarg, &uuid, filename, sizeof(filename)); if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 || filename[0] == '\0') unpack_usage(); desc = lookup_image_desc_from_uuid(&uuid); if (desc == NULL) { uuid_to_str(name, sizeof(name), &uuid); desc = new_image_desc(&uuid, name, "blob"); add_image_desc(desc); } set_image_desc_action(desc, DO_UNPACK, filename); unpack_all = 0; break; } case 'f': fflag = 1; break; case 'o': snprintf(outdir, sizeof(outdir), "%s", optarg); break; default: unpack_usage(); } } argc -= optind; argv += optind; free(opts); if (argc == 0) unpack_usage(); parse_fip(argv[0], NULL); if (outdir[0] != '\0') if (chdir(outdir) == -1) log_err("chdir %s", outdir); /* Unpack all specified images. */ for (desc = image_desc_head; desc != NULL; desc = desc->next) { char file[PATH_MAX]; image_t *image = desc->image; if (!unpack_all && desc->action != DO_UNPACK) continue; /* Build filename. */ if (desc->action_arg == NULL) snprintf(file, sizeof(file), "%s.bin", desc->cmdline_name); else snprintf(file, sizeof(file), "%s", desc->action_arg); if (image == NULL) { if (!unpack_all) log_warnx("%s does not exist in %s", file, argv[0]); continue; } if (access(file, F_OK) != 0 || fflag) { if (verbose) log_dbgx("Unpacking %s", file); write_image_to_file(image, file); } else { log_warnx("File %s already exists, use --force to overwrite it", file); } } return 0; }
static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align) { FILE *fp; image_desc_t *desc; fip_toc_header_t *toc_header; fip_toc_entry_t *toc_entry; char *buf; uint64_t entry_offset, buf_size, payload_size = 0; size_t nr_images = 0; for (desc = image_desc_head; desc != NULL; desc = desc->next) if (desc->image != NULL) nr_images++; buf_size = sizeof(fip_toc_header_t) + sizeof(fip_toc_entry_t) * (nr_images + 1); buf = calloc(1, buf_size); if (buf == NULL) log_err("calloc"); /* Build up header and ToC entries from the image table. */ toc_header = (fip_toc_header_t *)buf; toc_header->name = TOC_HEADER_NAME; toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER; toc_header->flags = toc_flags; toc_entry = (fip_toc_entry_t *)(toc_header + 1); entry_offset = buf_size; for (desc = image_desc_head; desc != NULL; desc = desc->next) { image_t *image = desc->image; if (image == NULL) continue; payload_size += image->toc_e.size; entry_offset = (entry_offset + align - 1) & ~(align - 1); image->toc_e.offset_address = entry_offset; *toc_entry++ = image->toc_e; entry_offset += image->toc_e.size; } /* Append a null uuid entry to mark the end of ToC entries. */ memset(toc_entry, 0, sizeof(*toc_entry)); toc_entry->offset_address = entry_offset; /* Generate the FIP file. */ fp = fopen(filename, "wb"); if (fp == NULL) log_err("fopen %s", filename); if (verbose) log_dbgx("Metadata size: %zu bytes", buf_size); xfwrite(buf, buf_size, fp, filename); free(buf); if (verbose) log_dbgx("Payload size: %zu bytes", payload_size); for (desc = image_desc_head; desc != NULL; desc = desc->next) { image_t *image = desc->image; if (image == NULL) continue; if (fseek(fp, image->toc_e.offset_address, SEEK_SET)) log_errx("Failed to set file position"); xfwrite(image->buffer, image->toc_e.size, fp, filename); } fclose(fp); return 0; }