summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikolai Artemiev <nartemiev@google.com>2023-02-21 13:52:32 +1100
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-03-16 02:40:04 +0000
commit4976c1a60ca660d530e6dcaeb6dbd6fe4403fed7 (patch)
treef3dc4223f38823b051e8ca6c29fcf9c6a142f126
parente29eac41dce1a5e423d739fc47abfd2b2bda22df (diff)
downloadvboot-stabilize-15393.48.B.tar.gz
Add a new subcommand for getting/setting flash properties such as the flash size and writeprotect configuration. The operations provided by `futility flash` require less information from the user and are less error prone than the equivalents provided by `flashrom`. For example, --wp-enable automatically choses the protection range based on the firmware image and --wp-status gives a warning if the protection range does not match the RO firmware region. BUG=b:268574030 BRANCH=none TEST=`futility flash --{flash-size,wp-enable,wp-disable,wp-status}` Co-authored-by: Edward O'Callaghan <quasisec@google.com> Signed-off-by: Edward O'Callaghan <quasisec@google.com> Signed-off-by: Nikolai Artemiev <nartemiev@google.com> Change-Id: I36d7468616a5bcdf3c4542d48652bd24c3377a61 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/4279661 Reviewed-by: Edward O'Callaghan <quasisec@chromium.org> Commit-Queue: Edward O'Callaghan <quasisec@chromium.org>
-rw-r--r--Android.mk1
-rw-r--r--Makefile1
-rw-r--r--futility/cmd_flash_util.c260
-rw-r--r--futility/updater.h3
-rw-r--r--host/lib/flashrom_drv.c92
-rw-r--r--host/lib/include/flashrom.h25
-rwxr-xr-xtests/futility/run_test_scripts.sh1
-rwxr-xr-xtests/futility/test_flash_util.sh26
8 files changed, 408 insertions, 1 deletions
diff --git a/Android.mk b/Android.mk
index 8708c26e..08176cb1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -120,6 +120,7 @@ LOCAL_CFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
FUTIL_STATIC_SRCS = \
futility/futility.c \
futility/cmd_dump_fmap.c \
+ futility/cmd_flash_util.c \
futility/cmd_gbb_utility.c \
futility/misc.c
diff --git a/Makefile b/Makefile
index 8cc68e3c..2219efd0 100644
--- a/Makefile
+++ b/Makefile
@@ -651,6 +651,7 @@ FUTIL_SRCS = \
futility/cmd_create.c \
futility/cmd_dump_fmap.c \
futility/cmd_dump_kernel_config.c \
+ futility/cmd_flash_util.c \
futility/cmd_gbb_utility.c \
futility/cmd_gscvd.c \
futility/cmd_load_fmap.c \
diff --git a/futility/cmd_flash_util.c b/futility/cmd_flash_util.c
new file mode 100644
index 00000000..783a595e
--- /dev/null
+++ b/futility/cmd_flash_util.c
@@ -0,0 +1,260 @@
+/* Copyright 2023 The ChromiumOS Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include "fmap.h"
+#include "futility.h"
+#include "updater.h"
+
+#ifdef USE_FLASHROM
+
+static int print_flash_size(struct updater_config *cfg)
+{
+ uint32_t flash_size;
+ if (flashrom_get_size(cfg->image.programmer, &flash_size,
+ cfg->verbosity + 1)) {
+ ERROR("%s failed.\n", __func__);
+ return -1;
+ }
+
+ printf("Flash size: %#010x\n", flash_size);
+ return 0;
+}
+
+static int get_ro_range(struct updater_config *cfg,
+ uint32_t *start, uint32_t *len)
+{
+ int ret = 0;
+
+ /* Read fmap */
+ const char *const regions[] = {FMAP_RO_FMAP, NULL};
+ if (flashrom_read_image(&cfg->image_current, regions,
+ cfg->verbosity + 1))
+ return -1;
+
+ FmapAreaHeader *wp_ro = NULL;
+ uint8_t *r = fmap_find_by_name(cfg->image_current.data,
+ cfg->image_current.size,
+ NULL, FMAP_RO, &wp_ro);
+ if (!r || !wp_ro) {
+ ERROR("Could not find WP_RO in the FMAP\n");
+ ret = -1;
+ goto err;
+ }
+
+ *start = wp_ro->area_offset;
+ *len = wp_ro->area_size;
+
+err:
+ free(cfg->image_current.data);
+ cfg->image_current.data = NULL;
+ cfg->image_current.size = 0;
+
+ return ret;
+}
+
+static int print_wp_status(struct updater_config *cfg)
+{
+ /* Get WP_RO region start and length from image */
+ uint32_t ro_start, ro_len;
+ if (get_ro_range(cfg, &ro_start, &ro_len))
+ return -1;
+
+ /* Get current WP region and mode from SPI flash */
+ bool wp_mode;
+ uint32_t wp_start, wp_len;
+ if (flashrom_get_wp(cfg->image.programmer, &wp_mode,
+ &wp_start, &wp_len, cfg->verbosity + 1)) {
+ ERROR("Failed to get WP status\n");
+ return -1;
+ }
+
+ if (!wp_mode && wp_start == 0 && wp_len == 0) {
+ printf("WP status: disabled\n");
+ } else if (wp_mode && wp_start == ro_start && wp_len == ro_len) {
+ printf("WP status: enabled\n");
+ } else {
+ printf("WP status: misconfigured (srp = %d, start = %#010x, length = %#010x)\n",
+ wp_mode, wp_start, wp_len);
+ }
+
+ return 0;
+}
+
+
+static int set_flash_wp(struct updater_config *cfg, bool enable)
+{
+ uint32_t wp_start = 0;
+ uint32_t wp_len = 0;
+
+ if (enable) {
+ /* Use the WP_RO region as the protection range */
+ if (get_ro_range(cfg, &wp_start, &wp_len))
+ return -1;
+ }
+
+ if (flashrom_set_wp(cfg->image.programmer, enable,
+ wp_start, wp_len, cfg->verbosity + 1)) {
+ ERROR("Failed to modify WP configuration.\n");
+ return -1;
+ }
+
+ printf("%s WP\n", enable ? "Enabled" : "Disabled");
+
+ return 0;
+}
+
+/* Command line options */
+static struct option const long_opts[] = {
+ SHARED_FLASH_ARGS_LONGOPTS
+ /* name has_arg *flag val */
+ {"help", 0, NULL, 'h'},
+ {"wp-status", 0, NULL, 's'},
+ {"wp-enable", 0, NULL, 'e'},
+ {"wp-disable", 0, NULL, 'd'},
+ {"flash-size", 0, NULL, 'z'},
+ {NULL, 0, NULL, 0},
+};
+
+static const char *const short_opts = "h" SHARED_FLASH_ARGS_SHORTOPTS;
+
+static void print_help(int argc, char *argv[])
+{
+ printf("\n"
+ "Allows for the management of AP SPI flash configuration.\n"
+ "\n"
+ "Usage: " MYNAME " %s [OPTIONS] \n"
+ "\n"
+ " --wp-status \tGet the current flash WP state.\n"
+ " --wp-enable \tEnable protection for the RO image section.\n"
+ " --wp-disable \tDisable all write protection.\n"
+ " --flash-size \tGet flash size.\n"
+ "\n"
+ SHARED_FLASH_ARGS_HELP,
+ argv[0]);
+}
+
+static int do_flash(int argc, char *argv[])
+{
+ int ret = 0;
+ struct updater_config_arguments args = {0};
+ const char *prepare_ctrl_name = NULL;
+ char *servo_programmer = NULL;
+ bool enable_wp = false;
+ bool disable_wp = false;
+ bool get_wp_status = false;
+ bool get_size = false;
+
+ struct updater_config *cfg = updater_new_config();
+ assert(cfg);
+
+ opterr = 0;
+ int i;
+ while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
+ if (handle_flash_argument(&args, i, optarg))
+ continue;
+ switch (i) {
+ case 'h':
+ print_help(argc, argv);
+ goto out_free;
+ case 's':
+ get_wp_status = true;
+ break;
+ case 'e':
+ enable_wp = true;
+ break;
+ case 'd':
+ disable_wp = true;
+ break;
+ case 'z':
+ get_size = true;
+ break;
+ case 'v':
+ args.verbosity++;
+ break;
+ case '?':
+ ret = -1;
+ if (optopt)
+ ERROR("Unrecognized option: -%c\n", optopt);
+ else if (argv[optind - 1])
+ ERROR("Unrecognized option (possibly '%s')\n",
+ argv[optind - 1]);
+ else
+ ERROR("Unrecognized option.\n");
+ break;
+ default:
+ ret = -1;
+ ERROR("Failed parsing options.\n");
+ }
+ }
+ if (optind < argc) {
+ ret = -1;
+ ERROR("Unexpected arguments.\n");
+ }
+
+ if (!get_size && !enable_wp && !disable_wp && !get_wp_status) {
+ print_help(argc, argv);
+ goto out_free;
+ }
+
+ if (enable_wp && disable_wp) {
+ ret = -1;
+ ERROR("--wp-enable and --wp-disable cannot be used together.\n");
+ goto out_free;
+ }
+
+ if (args.detect_servo) {
+ servo_programmer = host_detect_servo(&prepare_ctrl_name);
+
+ if (!servo_programmer) {
+ ret = -1;
+ ERROR("No servo detected.\n");
+ goto out_free;
+ }
+ if (!args.programmer)
+ args.programmer = servo_programmer;
+ }
+
+ int update_needed;
+ ret = updater_setup_config(cfg, &args, &update_needed);
+ prepare_servo_control(prepare_ctrl_name, 1);
+
+ if (!ret && get_size)
+ ret = print_flash_size(cfg);
+
+ if (!ret && enable_wp)
+ ret = set_flash_wp(cfg, true);
+
+ if (!ret && disable_wp)
+ ret = set_flash_wp(cfg, false);
+
+ if (!ret && get_wp_status)
+ ret = print_wp_status(cfg);
+
+out_free:
+ prepare_servo_control(prepare_ctrl_name, 0);
+ free(servo_programmer);
+ updater_delete_config(cfg);
+
+ return ret;
+}
+#define CMD_HELP_STR "Manage AP SPI flash properties and writeprotect configuration"
+
+#else /* USE_FLASHROM */
+
+static int do_flash(int argc, char *argv[])
+{
+ FATAL(MYNAME " was built without flashrom support, `flash` command unavailable!\n");
+ return -1;
+}
+#define CMD_HELP_STR "Manage AP SPI flash properties and writeprotect configuration (unavailable in this build)"
+
+#endif /* !USE_FLASHROM */
+
+DECLARE_FUTIL_COMMAND(flash, do_flash, VBOOT_VERSION_ALL, CMD_HELP_STR);
diff --git a/futility/updater.h b/futility/updater.h
index 3d04cb4e..1b371f9c 100644
--- a/futility/updater.h
+++ b/futility/updater.h
@@ -12,7 +12,8 @@
#include "updater_utils.h"
/* FMAP section names. */
-static const char * const FMAP_RO_FMAP = "FMAP",
+static const char * const FMAP_RO = "WP_RO",
+ * const FMAP_RO_FMAP = "FMAP",
* const FMAP_RO_FRID = "RO_FRID",
* const FMAP_RO_SECTION = "RO_SECTION",
* const FMAP_RO_CBFS = "COREBOOT",
diff --git a/host/lib/flashrom_drv.c b/host/lib/flashrom_drv.c
index 09e7f468..6eb0de8d 100644
--- a/host/lib/flashrom_drv.c
+++ b/host/lib/flashrom_drv.c
@@ -311,3 +311,95 @@ err_init:
return ret;
}
+
+int flashrom_set_wp(const char *prog_with_params, bool wp_mode,
+ uint32_t wp_start, uint32_t wp_len, int verbosity)
+{
+ int ret = 1;
+
+ g_verbose_screen = (verbosity == -1) ? FLASHROM_MSG_INFO : verbosity;
+
+ struct flashrom_programmer *prog = NULL;
+ struct flashrom_flashctx *flashctx = NULL;
+
+ struct flashrom_wp_cfg *cfg = NULL;
+
+ char *programmer, *params;
+ char *tmp = flashrom_extract_params(prog_with_params, &programmer,
+ &params);
+
+ flashrom_set_log_callback((flashrom_log_callback *)&flashrom_print_cb);
+
+ if (flashrom_init(1)
+ || flashrom_programmer_init(&prog, programmer, params))
+ goto err_init;
+
+ if (flashrom_flash_probe(&flashctx, prog, NULL))
+ goto err_probe;
+
+ if (flashrom_wp_cfg_new(&cfg) != FLASHROM_WP_OK)
+ goto err_cleanup;
+
+ enum flashrom_wp_mode mode = wp_mode ?
+ FLASHROM_WP_MODE_HARDWARE : FLASHROM_WP_MODE_DISABLED;
+ flashrom_wp_set_mode(cfg, mode);
+ flashrom_wp_set_range(cfg, wp_start, wp_len);
+
+ if (flashrom_wp_write_cfg(flashctx, cfg) != FLASHROM_WP_OK)
+ goto err_write_cfg;
+
+ ret = 0;
+
+err_write_cfg:
+ flashrom_wp_cfg_release(cfg);
+
+err_cleanup:
+ flashrom_flash_release(flashctx);
+
+err_probe:
+ if (flashrom_programmer_shutdown(prog))
+ ret = 1;
+
+err_init:
+ free(tmp);
+
+ return ret;
+}
+
+int flashrom_get_size(const char *prog_with_params,
+ uint32_t *flash_len, int verbosity)
+{
+ int r = 0;
+
+ g_verbose_screen = (verbosity == -1) ? FLASHROM_MSG_INFO : verbosity;
+
+ char *programmer, *params;
+ char *tmp = flashrom_extract_params(prog_with_params,
+ &programmer, &params);
+
+ struct flashrom_programmer *prog = NULL;
+ struct flashrom_flashctx *flashctx = NULL;
+
+ flashrom_set_log_callback((flashrom_log_callback *)&flashrom_print_cb);
+
+ if (flashrom_init(1) ||
+ flashrom_programmer_init(&prog, programmer, params)) {
+ r = -1;
+ goto err_init;
+ }
+ if (flashrom_flash_probe(&flashctx, prog, NULL)) {
+ r = -1;
+ goto err_probe;
+ }
+
+ *flash_len = flashrom_flash_getsize(flashctx);
+
+ flashrom_flash_release(flashctx);
+
+err_probe:
+ r |= flashrom_programmer_shutdown(prog);
+
+err_init:
+ free(tmp);
+ return r;
+}
diff --git a/host/lib/include/flashrom.h b/host/lib/include/flashrom.h
index 5fb7a03c..4b61d199 100644
--- a/host/lib/include/flashrom.h
+++ b/host/lib/include/flashrom.h
@@ -88,3 +88,28 @@ int flashrom_write_image(const struct firmware_image *image,
*/
int flashrom_get_wp(const char *programmer, bool *wp_mode,
uint32_t *wp_start, uint32_t *wp_len, int verbosity);
+
+/**
+ * Set wp state using flashrom.
+ *
+ * @param programmer The name of the programmer to use for writing the
+ * writeprotect state.
+ * @param wp_mode WP mode to set. true to enable, false disable WP.
+ * @param wp_start WP start addr to set
+ * @param wp_len WP region length set
+ *
+ * @return 0 on success, or a relevant error.
+ */
+int flashrom_set_wp(const char *programmer, bool wp_mode,
+ uint32_t wp_start, uint32_t wp_len, int verbosity);
+
+/**
+ * Get flash size using flashrom.
+ *
+ * @param programmer The name of the programmer to use.
+ * @param flash_len Pointer to a uint32_t to store chip length.
+ *
+ * @return 0 on success, or a relevant error.
+ */
+int flashrom_get_size(const char *programmer, uint32_t *flash_len,
+ int verbosity);
diff --git a/tests/futility/run_test_scripts.sh b/tests/futility/run_test_scripts.sh
index 662304da..2f9fbfce 100755
--- a/tests/futility/run_test_scripts.sh
+++ b/tests/futility/run_test_scripts.sh
@@ -20,6 +20,7 @@ export OUTDIR
TESTS="
${SCRIPT_DIR}/futility/test_create.sh
${SCRIPT_DIR}/futility/test_dump_fmap.sh
+${SCRIPT_DIR}/futility/test_flash_util.sh
${SCRIPT_DIR}/futility/test_gbb_utility.sh
${SCRIPT_DIR}/futility/test_load_fmap.sh
${SCRIPT_DIR}/futility/test_main.sh
diff --git a/tests/futility/test_flash_util.sh b/tests/futility/test_flash_util.sh
new file mode 100755
index 00000000..e18a35cb
--- /dev/null
+++ b/tests/futility/test_flash_util.sh
@@ -0,0 +1,26 @@
+#!/bin/bash -eux
+# Copyright 2023 The ChromiumOS Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+me=${0##*/}
+TMP="${me}.tmp"
+
+# Work in scratch directory
+cd "${OUTDIR}"
+
+# 8MB test image
+TEST_BIOS="${SCRIPT_DIR}/futility/data/bios_link_mp.bin"
+TEST_PROG="dummy:image=${TEST_BIOS},emulate=VARIABLE_SIZE,size=8388608"
+
+# Test flash size
+flash_size=$("${FUTILITY}" flash --flash-size -p "${TEST_PROG}")
+[ "${flash_size}" = "Flash size: 0x00800000" ]
+
+# Test WP status (VARIABLE_SIZE always has WP disabled)
+wp_status=$("${FUTILITY}" flash --wp-status -p "${TEST_PROG}")
+[ "${wp_status}" = "WP status: disabled" ]
+
+# Cleanup
+rm -f "${TMP}"*
+exit 0