blob: 81f7f2151b6d28f5d12cc1448456ed1a1506019c [file] [log] [blame]
/* 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 <inttypes.h>
#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;
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: %#010x\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: %#010x\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 = %#010x, length = %#010x)\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);
if (!hwwp || (!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'},
{"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'},
{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-status \tGet the current HW and SW WP state.\n"
" [--ignore-hw] \tGet SW WP state only.\n"
" --wp-enable \tEnable protection for the RO image section.\n"
" --wp-disable \tDisable all write protection.\n"
" --flash-size \tGet flash size.\n"
" --flash-info \tGet flash info.\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);