/* 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 #include "flash_helpers.h" #include "fmap.h" #include "futility.h" #include "updater.h" #ifdef USE_FLASHROM 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}; if (flashrom_read_image(&cfg->image_current, regions, ARRAY_SIZE(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; VB2_DEBUG("start=0x%08" PRIx32 ", length=0x%08" PRIx32 "\n", *start, *len); err: free(cfg->image_current.data); cfg->image_current.data = NULL; cfg->image_current.size = 0; return ret; } 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: 0x%08" PRIx32 "\n", flash_size); return 0; } static int print_flash_info(struct updater_config *cfg) { char *vendor; char *name; uint32_t vid; uint32_t pid; uint32_t flash_size; if (flashrom_get_info(cfg->image.programmer, &vendor, &name, &vid, &pid, &flash_size, cfg->verbosity + 1)) { ERROR("%s failed.\n", __func__); return -1; } printf("Flash vendor: %s\n", vendor); free(vendor); printf("Flash name: %s\n", name); free(name); const uint64_t vidpid = (uint64_t) vid << 32 | pid; printf("Flash vid-pid: 0x%" PRIx64 "\n", vidpid); printf("Flash size: 0x%08" PRIx32 "\n", flash_size); /* 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; printf("Expected WP SR configuration by FW image: (start = 0x%08" PRIx32 ", length = 0x%08" PRIx32 ")\n", ro_start, ro_len); return 0; } static int print_wp_status(struct updater_config *cfg, bool ignore_hw) { /* 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; } /* A 1 implies HWWP is enabled. */ int hwwp = ignore_hw ? 1 : dut_get_property(DUT_PROP_WP_HW, cfg); /* SWWP could be disabled, enabled, or misconfigured. */ bool is_swwp_disabled = !wp_mode && wp_start == 0 && wp_len == 0; bool is_swwp_enabled = wp_mode && wp_start == ro_start && wp_len == ro_len; if (!is_swwp_disabled && !is_swwp_enabled) WARN("SWWP misconfigured (srp = %d, start = 0x%08" PRIx32 ", length = 0x%08" PRIx32 "), WP_RO start = 0x%08" PRIx32 ", length = 0x%08" PRIx32 "\n", wp_mode, wp_start, wp_len, ro_start, ro_len); if (!hwwp || is_swwp_disabled) { if (!ignore_hw && !is_swwp_disabled) { WARN("HWWP disabled. Result does not reflect the SWWP status.\n"); WARN("Use --ignore-hw instead to see the SWWP status specifically.\n"); } printf("WP status: disabled\n"); } else if (is_swwp_enabled) { printf("WP status: enabled\n"); } else { printf("WP status: misconfigured\n"); } 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'}, {"ignore-hw", 0, NULL, 'o'}, {"wp-enable", 0, NULL, 'e'}, {"wp-disable", 0, NULL, 'd'}, {"flash-info", 0, NULL, 'i'}, {"flash-size", 0, NULL, 'z'}, {"verbose", 0, NULL, 'v'}, {NULL, 0, NULL, 0}, }; static const char *const short_opts = "hsoedizv" 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" "-s, --wp-status \tGet the current flash WP state.\n" " --wp-status \tGet the current HW and SW WP state.\n" "-o, [--ignore-hw] \tGet SW WP state only.\n" "-e, --wp-enable \tEnable protection for the RO image section.\n" "-d, --wp-disable \tDisable all write protection.\n" "-i, --flash-info \tGet flash info.\n" "-z, --flash-size \tGet flash size.\n" "-v, --verbose \tPrint verbose messages\n" "\n" SHARED_FLASH_ARGS_HELP, argv[0]); } static int do_flash(int argc, char *argv[]) { struct updater_config *cfg = NULL; struct updater_config_arguments args = {0}; bool enable_wp = false; bool disable_wp = false; bool get_wp_status = false; bool ignore_hw_wp = false; bool get_size = false; bool get_info = false; int ret = 0; 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); return 0; case 's': get_wp_status = true; break; case 'o': ignore_hw_wp = true; break; case 'e': enable_wp = true; break; case 'd': disable_wp = true; break; case 'i': get_info = true; break; case 'z': get_size = true; break; case 'v': args.verbosity++; break; case '?': 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"); return 1; default: ERROR("Failed parsing options.\n"); return 1; } } if (optind < argc) { ERROR("Unexpected arguments.\n"); return 1; } if (!get_size && !get_info && !enable_wp && !disable_wp && !get_wp_status) { print_help(argc, argv); return 0; } if (!get_wp_status && ignore_hw_wp) { ERROR("--ignore-hw must be used with --wp-status.\n"); return 1; } if (enable_wp && disable_wp) { ERROR("--wp-enable and --wp-disable cannot be used together.\n"); return 1; } if (setup_flash(&cfg, &args)) { ERROR("While preparing flash\n"); return 1; } if (get_info) ret = print_flash_info(cfg); 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, ignore_hw_wp); teardown_flash(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);