From 4976c1a60ca660d530e6dcaeb6dbd6fe4403fed7 Mon Sep 17 00:00:00 2001 From: Nikolai Artemiev Date: Tue, 21 Feb 2023 13:52:32 +1100 Subject: futility: Add `flash` subcommand 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 Signed-off-by: Edward O'Callaghan Signed-off-by: Nikolai Artemiev Change-Id: I36d7468616a5bcdf3c4542d48652bd24c3377a61 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/4279661 Reviewed-by: Edward O'Callaghan Commit-Queue: Edward O'Callaghan --- Android.mk | 1 + Makefile | 1 + futility/cmd_flash_util.c | 260 +++++++++++++++++++++++++++++++++++++ futility/updater.h | 3 +- host/lib/flashrom_drv.c | 92 +++++++++++++ host/lib/include/flashrom.h | 25 ++++ tests/futility/run_test_scripts.sh | 1 + tests/futility/test_flash_util.sh | 26 ++++ 8 files changed, 408 insertions(+), 1 deletion(-) create mode 100644 futility/cmd_flash_util.c create mode 100755 tests/futility/test_flash_util.sh 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 +#include +#include +#include + +#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, + ¶ms); + + 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, ¶ms); + + 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 -- cgit v1.2.1