From 42b3ebdd0233fe23e3a8333af1ce6731d71f5f6b Mon Sep 17 00:00:00 2001 From: Evan Benn Date: Thu, 1 Dec 2022 19:08:39 +1100 Subject: futility: Add read/write flash capability to gbb command gbb command can read and modify flash in addition to acting on firmware files. BUG=b:260531154 BRANCH=None TEST=FEATURES=test emerge-grunt vboot_reference TEST=futility gbb -s --flags 0x0 /tmp/bios /tmp/bios2 TEST=futility gbb -g --flash TEST=futility gbb --set --flash --flags=0x40b9 --flash TEST=env SERVOD_NAME=grunt futility gbb --get --servo TEST=env SERVOD_NAME=grunt futility gbb --set --servo --flags=0 Change-Id: I66b008ed7325d125eb305e84185e53eccd243898 Signed-off-by: Evan Benn Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/4075311 Reviewed-by: Yu-Ping Wu Commit-Queue: Edward O'Callaghan Reviewed-by: Edward O'Callaghan --- futility/cmd_gbb_utility.c | 240 ++++++++++++++++++++++++++++++------- futility/updater.c | 19 ++- futility/updater.h | 10 ++ tests/futility/test_gbb_utility.sh | 6 + 4 files changed, 220 insertions(+), 55 deletions(-) diff --git a/futility/cmd_gbb_utility.c b/futility/cmd_gbb_utility.c index c0e5fc51..3ca93b94 100644 --- a/futility/cmd_gbb_utility.c +++ b/futility/cmd_gbb_utility.c @@ -16,17 +16,34 @@ #include #include "futility.h" +#include "updater.h" #include "updater_utils.h" +#ifdef USE_FLASHROM +#define FLASH_ARG_HELP \ + " --flash \tRead from and write to flash" \ + ", ignore file arguments.\n" +#define FLASH_MORE_HELP \ + "In GET and SET mode, the following options modify the " \ + "behaviour of flashing. Presence of any of these implies " \ + "--flash.\n" \ + SHARED_FLASH_ARGS_HELP \ + "\n" +#else +#define FLASH_ARG_HELP +#define FLASH_MORE_HELP +#endif /* USE_FLASHROM */ + static void print_help(int argc, char *argv[]) { printf("\n" "Usage: " MYNAME " %s [-g|-s|-c] [OPTIONS] " - "bios_file [output_file]\n" + "[bios_file] [output_file]\n" "\n" "GET MODE:\n" - "-g, --get (default)\tGet (read) from bios_file, " + "-g, --get (default)\tGet (read) from bios_file or flash, " "with following options:\n" + FLASH_ARG_HELP " --hwid \tReport hardware id (default).\n" " --flags \tReport header flags.\n" " --digest \tReport digest of hwid (>= v1.2)\n" @@ -35,8 +52,9 @@ static void print_help(int argc, char *argv[]) " -r --recoverykey=FILE\tFile name to export Recovery Key.\n" "\n" "SET MODE:\n" - "-s, --set \tSet (write) to bios_file, " + "-s, --set \tSet (write) to flash or file, " "with following options:\n" + FLASH_ARG_HELP " -o, --output=FILE \tNew file name for ouptput.\n" " --hwid=HWID \tThe new hardware id to be changed.\n" " --flags=FLAGS \tThe new (numeric) flags value.\n" @@ -47,7 +65,8 @@ static void print_help(int argc, char *argv[]) "CREATE MODE:\n" "-c, --create=hwid_size,rootkey_size,bmpfv_size," "recoverykey_size\n" - " \tCreate a GBB blob by given size list.\n" + " \tCreate a GBB blob by given size list.\n\n" + FLASH_MORE_HELP "SAMPLE:\n" " %s -g bios.bin\n" " %s --set --hwid='New Model' -k key.bin" @@ -57,14 +76,16 @@ static void print_help(int argc, char *argv[]) } enum { - OPT_HWID = 1000, + OPT_HWID = 0x1000, OPT_FLAGS, OPT_DIGEST, + OPT_FLASH, OPT_HELP, }; /* Command line options */ static struct option long_opts[] = { + SHARED_FLASH_ARGS_LONGOPTS /* name has_arg *flag val */ {"get", 0, NULL, 'g'}, {"set", 0, NULL, 's'}, @@ -76,11 +97,12 @@ static struct option long_opts[] = { {"hwid", 0, NULL, OPT_HWID}, {"flags", 0, NULL, OPT_FLAGS}, {"digest", 0, NULL, OPT_DIGEST}, + {"flash", 0, NULL, OPT_FLASH}, {"help", 0, NULL, OPT_HELP}, {NULL, 0, NULL, 0}, }; -static const char *short_opts = ":gsc:o:k:b:r:"; +static const char *short_opts = ":gsc:o:k:b:r:" SHARED_FLASH_ARGS_SHORTOPTS; /* Change the has_arg field of a long_opts entry */ static void opt_has_arg(const char *name, int val) @@ -167,9 +189,9 @@ static uint8_t *create_gbb(const char *desc, off_t *sizeptr) size, strerror(errno)); free(sizes); return NULL; - } else if (sizeptr) { - *sizeptr = size; } + if (sizeptr) + *sizeptr = size; gbb = (struct vb2_gbb_header *) buf; memcpy(gbb->signature, VB2_GBB_SIGNATURE, VB2_GBB_SIGNATURE_SIZE); @@ -317,6 +339,96 @@ done_close: return r; } +/* + * Prepare for flashrom interaction. Setup cfg from args and put servo into + * flash mode if servo is in use. If this succeeds teardown_flash must be + * called. + */ +static int setup_flash(struct updater_config **cfg, + struct updater_config_arguments *args, + const char **prepare_ctrl_name) +{ +#ifdef USE_FLASHROM + *prepare_ctrl_name = NULL; + *cfg = updater_new_config(); + if (!*cfg) { + fprintf(stderr, "\nERROR: Out of memory\n"); + return 1; + } + if (args->detect_servo) { + char *servo_programmer = host_detect_servo(prepare_ctrl_name); + + if (!servo_programmer) { + fprintf(stderr, + "\nERROR: Problem communicating with servo\n"); + goto errdelete; + } + + if (!args->programmer) + args->programmer = servo_programmer; + else + free(servo_programmer); + } + int ignored; + if (updater_setup_config(*cfg, args, &ignored)) { + fprintf(stderr, "\nERROR: Bad servo options\n"); + goto errdelete; + } + prepare_servo_control(*prepare_ctrl_name, 1); + return 0; +errdelete: + updater_delete_config(*cfg); + *cfg = NULL; + return 1; +#else + return 1; +#endif /* USE_FLASHROM */ +} + +/* Cleanup objects created in setup_flash and release servo from flash mode. */ +static void teardown_flash(struct updater_config *cfg, + const char *prepare_ctrl_name, + char *servo_programmer) +{ +#ifdef USE_FLASHROM + prepare_servo_control(prepare_ctrl_name, 0); + free(servo_programmer); + updater_delete_config(cfg); +#endif /* USE_FLASHROM */ +} + +/* Read firmware from flash. */ +static uint8_t *read_from_flash(struct updater_config *cfg, off_t *filesize) +{ +#ifdef USE_FLASHROM + if (load_system_firmware(cfg, &cfg->image_current)) + return NULL; + uint8_t *ret = cfg->image_current.data; + cfg->image_current.data = NULL; + *filesize = cfg->image_current.size; + cfg->image_current.size = 0; + return ret; +#else + return NULL; +#endif /* USE_FLASHROM */ +} + +/* Write firmware to flash. Takes ownership of inbuf and outbuf data. */ +static int write_to_flash(struct updater_config *cfg, uint8_t *outbuf, + off_t filesize) +{ +#ifdef USE_FLASHROM + cfg->image.data = outbuf; + cfg->image.size = filesize; + int ret = write_firmware(cfg, &cfg->image, FMAP_RO_GBB); + cfg->image.data = NULL; + cfg->image.size = 0; + return ret; +#else + return 1; +#endif /* USE_FLASHROM */ +} + static int do_gbb(int argc, char *argv[]) { enum do_what_now { DO_GET, DO_SET, DO_CREATE } mode = DO_GET; @@ -337,9 +449,17 @@ static int do_gbb(int argc, char *argv[]) struct vb2_gbb_header *gbb; uint8_t *gbb_base; int i; + struct updater_config *cfg = NULL; + struct updater_config_arguments args = {0}; + const char *prepare_ctrl_name = NULL; + char *servo_programmer = NULL; opterr = 0; /* quiet, you */ while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) { +#ifdef USE_FLASHROM + if (handle_flash_argument(&args, i, optarg)) + continue; +#endif switch (i) { case 'g': mode = DO_GET; @@ -380,6 +500,14 @@ static int do_gbb(int argc, char *argv[]) case OPT_DIGEST: sel_digest = 1; break; + case OPT_FLASH: +#ifndef USE_FLASHROM + fprintf(stderr, "ERROR: futility was built without " + "flashrom support\n"); + return 1; +#endif + args.use_flash = 1; + break; case OPT_HELP: print_help(argc, argv); return !!errorcnt; @@ -422,16 +550,34 @@ static int do_gbb(int argc, char *argv[]) return 1; } + if (args.use_flash) { + if (setup_flash(&cfg, &args, &prepare_ctrl_name)) { + fprintf(stderr, + "ERROR: error while preparing flash\n"); + return 1; + } + } + /* Now try to do something */ switch (mode) { case DO_GET: - if (argc - optind < 1) { - fprintf(stderr, "\nERROR: missing input filename\n"); - print_help(argc, argv); - errorcnt++; - break; + if (args.use_flash) { + inbuf = read_from_flash(cfg, &filesize); } else { + if (argc - optind < 1) { + fprintf(stderr, + "\nERROR: missing input filename\n"); + print_help(argc, argv); + errorcnt++; + break; + } infile = argv[optind++]; + inbuf = read_entire_file(infile, &filesize); + + } + if (!inbuf) { + errorcnt++; + break; } /* With no args, show the HWID */ @@ -439,12 +585,6 @@ static int do_gbb(int argc, char *argv[]) && !sel_flags && !sel_digest) sel_hwid = 1; - inbuf = read_entire_file(infile, &filesize); - if (!inbuf) { - errorcnt++; - break; - } - gbb = FindGbbHeader(inbuf, filesize); if (!gbb) { fprintf(stderr, "ERROR: No GBB found in %s\n", infile); @@ -491,15 +631,26 @@ static int do_gbb(int argc, char *argv[]) break; case DO_SET: - if (argc - optind < 1) { - fprintf(stderr, "\nERROR: missing input filename\n"); - print_help(argc, argv); + if (args.use_flash) { + inbuf = read_from_flash(cfg, &filesize); + } else { + if (argc - optind < 1) { + fprintf(stderr, + "\nERROR: missing input filename\n"); + print_help(argc, argv); + errorcnt++; + break; + } + infile = argv[optind++]; + inbuf = read_entire_file(infile, &filesize); + if (!outfile) + outfile = (argc - optind < 1) ? infile + : argv[optind++]; + } + if (!inbuf) { errorcnt++; break; } - infile = argv[optind++]; - if (!outfile) - outfile = (argc - optind < 1) ? infile : argv[optind++]; if (sel_hwid && !opt_hwid) { fprintf(stderr, "\nERROR: missing new HWID value\n"); @@ -514,13 +665,6 @@ static int do_gbb(int argc, char *argv[]) break; } - /* With no args, we'll either copy it unchanged or do nothing */ - inbuf = read_entire_file(infile, &filesize); - if (!inbuf) { - errorcnt++; - break; - } - gbb = FindGbbHeader(inbuf, filesize); if (!gbb) { fprintf(stderr, "ERROR: No GBB found in %s\n", infile); @@ -557,14 +701,13 @@ static int do_gbb(int argc, char *argv[]) gbb->hwid_size); errorcnt++; break; - } else { - /* Wipe data before writing new value. */ - memset(gbb_base + gbb->hwid_offset, 0, - gbb->hwid_size); - strcpy((char *)(gbb_base + gbb->hwid_offset), - opt_hwid); - update_hwid_digest(gbb); } + /* Wipe data before writing new value. */ + memset(gbb_base + gbb->hwid_offset, 0, + gbb->hwid_size); + strcpy((char *)(gbb_base + gbb->hwid_offset), + opt_hwid); + update_hwid_digest(gbb); } if (opt_flags) { @@ -577,9 +720,8 @@ static int do_gbb(int argc, char *argv[]) opt_flags); errorcnt++; break; - } else { - gbb->flags = val; } + gbb->flags = val; } if (opt_rootkey) { @@ -606,13 +748,19 @@ static int do_gbb(int argc, char *argv[]) } /* Write it out if there are no problems. */ - if (!errorcnt) - if (write_to_file("successfully saved new image to:", - outfile, outbuf, filesize)) { + if (!errorcnt) { + if (args.use_flash) { + if (write_to_flash(cfg, outbuf, filesize)) { + errorcnt++; + break; + } + } else if (write_to_file( + "successfully saved new image to:", + outfile, outbuf, filesize)) { errorcnt++; break; } - + } break; case DO_CREATE: @@ -645,6 +793,8 @@ static int do_gbb(int argc, char *argv[]) break; } + if (args.use_flash) + teardown_flash(cfg, prepare_ctrl_name, servo_programmer); if (inbuf) free(inbuf); if (outbuf) diff --git a/futility/updater.c b/futility/updater.c index 909339e0..e6ba3443 100644 --- a/futility/updater.c +++ b/futility/updater.c @@ -323,20 +323,14 @@ static int set_try_cookies(struct updater_config *cfg, const char *target, return 0; } -/* - * Writes a single section from the given firmware image to the system. - * Writes the whole firmware image if the section_name is NULL. - * Returns 0 if success, non-zero if error. - */ -static int write_firmware(struct updater_config *cfg, - const struct firmware_image *image, - const char *section_name) +int write_firmware(struct updater_config *cfg, + const struct firmware_image *image, const char *section_name) { const char *sections[2] = {0}; sections[0] = section_name; - return write_system_firmware( - cfg, image, section_name ? sections : NULL); + return write_system_firmware(cfg, image, + section_name ? sections : NULL); } /* @@ -1714,18 +1708,22 @@ int handle_flash_argument(struct updater_config_arguments *args, int opt, { switch (opt) { case 'p': + args->use_flash = 1; args->programmer = optarg; break; case OPT_CCD: + args->use_flash = 1; args->fast_update = 1; args->force_update = 1; args->write_protection = "0"; args->programmer = "raiden_debug_spi:target=AP"; break; case OPT_EMULATE: + args->use_flash = 1; args->emulation = optarg; break; case OPT_SERVO: + args->use_flash = 1; args->detect_servo = 1; args->fast_update = 1; args->force_update = 1; @@ -1734,6 +1732,7 @@ int handle_flash_argument(struct updater_config_arguments *args, int opt, break; case OPT_SERVO_PORT: setenv(ENV_SERVOD_PORT, optarg, 1); + args->use_flash = 1; args->detect_servo = 1; args->fast_update = 1; args->force_update = 1; diff --git a/futility/updater.h b/futility/updater.h index 7e249346..768beece 100644 --- a/futility/updater.h +++ b/futility/updater.h @@ -104,6 +104,7 @@ struct updater_config_arguments { int verbosity; int override_gbb_flags; int detect_servo; + int use_flash; uint32_t gbb_flags; bool detect_model_only; }; @@ -254,6 +255,15 @@ int quirk_override_signature_id(struct updater_config *cfg, struct model_config *model, const char **signature_id); +/* + * Writes a single section from the given firmware image to the system. + * Writes the whole firmware image if the section_name is NULL. + * Returns 0 if success, non-zero if error. + */ +int write_firmware(struct updater_config *cfg, + const struct firmware_image *image, + const char *section_name); + /* Functions from updater_archive.c */ /* diff --git a/tests/futility/test_gbb_utility.sh b/tests/futility/test_gbb_utility.sh index 7ec90d5a..c90ca78f 100755 --- a/tests/futility/test_gbb_utility.sh +++ b/tests/futility/test_gbb_utility.sh @@ -68,6 +68,12 @@ dd if=/dev/urandom bs=16 count=1 of="${TMP}.data3" cmp "${TMP}.data1" "${TMP}.read1" cmp "${TMP}.data2" "${TMP}.read2" +# Basic get and set test using flashrom +# The implementation requires an FMAP so use a full firmware image +PEPPY_BIOS="${SCRIPT_DIR}/futility/data/bios_peppy_mp.bin" +cp "${PEPPY_BIOS}" "${TMP}.full.blob" +"${FUTILITY}" gbb -s --emulate="${TMP}.full.blob" --flags="0xdeadbeef" +"${FUTILITY}" gbb -g --emulate="${TMP}.full.blob" --flags | grep -i "0xdeadbeef" # Okay, creating GBB blobs seems to work. Now let's make sure that corrupted # blobs are rejected. -- cgit v1.2.1