summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvan Benn <evanbenn@chromium.org>2022-12-01 19:08:39 +1100
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-01-06 21:42:21 +0000
commit42b3ebdd0233fe23e3a8333af1ce6731d71f5f6b (patch)
tree37f1e41b3e9bccc78a5ddaf69d094eeede04d1a8
parent0b6b689d650f89eaa619ef4207f0737eb8aca34a (diff)
downloadvboot-42b3ebdd0233fe23e3a8333af1ce6731d71f5f6b.tar.gz
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 <evanbenn@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/4075311 Reviewed-by: Yu-Ping Wu <yupingso@chromium.org> Commit-Queue: Edward O'Callaghan <quasisec@chromium.org> Reviewed-by: Edward O'Callaghan <quasisec@chromium.org>
-rw-r--r--futility/cmd_gbb_utility.c240
-rw-r--r--futility/updater.c19
-rw-r--r--futility/updater.h10
-rwxr-xr-xtests/futility/test_gbb_utility.sh6
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 <unistd.h>
#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.