Sergii Dmytruk has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/59407 )
Change subject: [RFC] cli_classic: add options for managing OTP regions ......................................................................
[RFC] cli_classic: add options for managing OTP regions
Change-Id: If77694e3b1c453b3fc0561da7a9eefdbce4fbb2b Signed-off-by: Hatim Kanchwala <hatim at hatimak.me> Signed-off-by: Sergii Dmytruk sergii.dmytruk@3mdeb.com --- M cli_classic.c M flashrom.8.tmpl 2 files changed, 290 insertions(+), 7 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/07/59407/1
diff --git a/cli_classic.c b/cli_classic.c index d69b798..3888581 100644 --- a/cli_classic.c +++ b/cli_classic.c @@ -28,6 +28,7 @@ #include "flash.h" #include "flashchips.h" #include "fmap.h" +#include "otp.h" #include "programmer.h" #include "writeprotect.h" #include "libflashrom.h" @@ -66,6 +67,12 @@ " --wp-status show write protect status\n" " --wp-range=<start>,<len> set write protect range\n" " --wp-region <region> set write protect region\n" + " --otp-status print information about OTP regions\n" + " --otp-region <otp-region> OTP region number (base 1) to operate on\n" + " --otp-read <file> read OTP region and save it to <file>\n" + " --otp-write <file> write <file> to OTP region\n" + " --otp-erase erase OTP region\n" + " --otp-lock lock OTP region\n" " --flash-name read out the detected flash name\n" " --flash-size read out the detected flash size\n" " --fmap read ROM layout from fmap embedded in ROM\n" @@ -154,6 +161,193 @@ return 0; }
+/* Parses OTP region number from an option value. Returns negative number on + * parsing error. */ +static int parse_otp_region(const char *value, int nregions) +{ + if (!value) { + msg_gdbg("OTP region not specified, using default region 1.\n"); + return 0; + } + + char *endptr = NULL; + int region = strtoul(value, &endptr, 0); + if (*endptr != '\0') { + msg_gerr("Error: Invalid otp-region argument format.\n"); + return -1; + } + + if (region < 1 || region > nregions) { + msg_gerr("Error: Value of otp-region must in the range [1, %d].\n", + nregions); + return -1; + } + + return region - 1; +} + +static int print_otp_status(struct flashctx *flash) +{ + size_t nregions; + struct flashrom_otp_region regions[FLASHROM_OTP_MAX_REGIONS]; + if (flashrom_otp_get_regions(flash, regions, &nregions)) { + msg_cerr("Failed to list OTP regions of the chip.\n"); + return 1; + } + + msg_cinfo("%s contains %d OTP region%s:\n", + flash->chip->name, (int)nregions, (nregions == 0) ? "" : "s"); + + for (unsigned int i = 0; i < nregions; i++) { + msg_cinfo(" %d. %d bytes\n", i + 1, regions[i].size); + + if (regions[i].locked) + msg_cinfo(" Permanently locked and cannot be erased or written to.\n"); + else + msg_cinfo(" Not yet locked.\n"); + } + + return 0; +} + +static int otp_cli(struct flashctx *flash, int any_otp_op, + int otp_status, int otp_read, int otp_write, int otp_erase, int otp_lock, + const char *otp_region_opt, const char *filename) +{ + int ret; + + size_t nregions; + struct flashrom_otp_region regions[FLASHROM_OTP_MAX_REGIONS]; + + if (!any_otp_op) + return 0; + + if (!flashrom_otp_chip_supported(flash)) { + msg_gerr("Error: OTP support is not implemented for this flash chip.\n"); + return 1; + } + + int region = -1; + if (otp_read || otp_write || otp_erase || otp_lock) { + if (flashrom_otp_get_regions(flash, regions, &nregions)) { + msg_cerr("Error: Failed to list OTP regions of the chip.\n"); + return 1; + } + + if (nregions > 1 && !otp_region_opt) { + msg_cerr("Error: Chip contains several OTP regions, but no --otp-region given.\n"); + return 1; + } + + region = parse_otp_region(otp_region_opt, nregions); + if (region < 0) + return 1; + } + + if (otp_read) { + const uint32_t len = regions[region].size; + uint8_t *buf = calloc(len, sizeof(*buf)); + if (!buf) { + msg_gerr("Error: Memory allocation failed.\n"); + return 1; + } + msg_gdbg("Reading OTP region...\n"); + if (flashrom_otp_read(flash, region, buf, len)) { + msg_cerr("Error: Reading OTP region failed.\n"); + free(buf); + return 1; + } + + ret = write_buf_to_file(buf, len, filename); + free(buf); + if (ret) + return 1; + } + + if (otp_write) { + const uint32_t len = regions[region].size; + uint8_t *buf = calloc(len, sizeof(*buf)); + if (!buf) { + msg_gerr("Error: Memory allocation failed.\n"); + return 1; + } + + ret = read_buf_from_file(buf, len, filename); + if (ret) { + msg_gerr("Error: Reading from file "%s" failed.\n", filename); + free(buf); + return 1; + } + msg_gdbg("Reading form file "%s" complete.\n", filename); + + msg_cinfo("Erasing OTP region...\n"); + if (flashrom_otp_erase(flash, region)) { + msg_cerr("Error: Erasing OTP region failed.\n"); + free(buf); + return 1; + } + msg_cinfo("Erasing OTP region done.\n"); + + msg_cinfo("Writing OTP region...\n"); + ret = flashrom_otp_write(flash, region, buf, len); + if (ret) { + free(buf); + msg_cerr("Error: Writing OTP region failed.\n"); + return 1; + } + msg_cinfo("Writing OTP region done.\n"); + + uint8_t *verify_buf = calloc(len, sizeof(*verify_buf)); + if (!verify_buf) { + free(buf); + msg_gerr("Error: Memory allocation failed.\n"); + return 1; + } + + msg_cinfo("Verifying written OTP region...\n"); + ret = flashrom_otp_read(flash, region, verify_buf, len); + + const bool ok = (memcmp(buf, verify_buf, len) == 0); + free(verify_buf); + free(buf); + + if (ret) { + msg_gerr("Error: Reading OTP region failed.\n"); + return 1; + } + + if (!ok) { + msg_gerr("Error: Writing OTP region failed verification.\n"); + return 1; + } + + msg_cinfo("Verifying written OTP region done.\n"); + } + + if (otp_erase) { + msg_cinfo("Erasing OTP region ...\n"); + if (flashrom_otp_erase(flash, region)) { + msg_cerr("Error: Erasing OTP region failed.\n"); + return 1; + } + msg_cinfo("Erasing OTP region done.\n"); + } + + if (otp_lock) { + msg_gdbg("Trying to lock OTP region...\n"); + if (flashrom_otp_lock(flash, region)) { + msg_cerr("Error: Failed to lock OTP region %d.\n", region + 1); + return 1; + } + msg_cinfo("Successfully locked OTP region %d.\n", region + 1); + } + + if (otp_status) + return print_otp_status(flash); + + return 0; +} + int main(int argc, char *argv[]) { const struct flashchip *chip = NULL; @@ -169,6 +363,7 @@ int flash_name = 0, flash_size = 0; int set_wp_enable = 0, set_wp_disable = 0, wp_status = 0; int set_wp_range = 0, set_wp_region = 0, wp_list = 0; + int otp_status = 0, otp_read = 0, otp_write = 0, otp_erase = 0, otp_lock = 0; int read_it = 0, extract_it = 0, write_it = 0, erase_it = 0, verify_it = 0; int dont_verify_it = 0, dont_verify_all = 0, list_supported = 0, operation_specified = 0; struct flashrom_layout *layout = NULL; @@ -186,6 +381,12 @@ OPTION_WP_ENABLE, OPTION_WP_DISABLE, OPTION_WP_LIST, + OPTION_OTP_STATUS, + OPTION_OTP_REGION, + OPTION_OTP_READ, + OPTION_OTP_WRITE, + OPTION_OTP_ERASE, + OPTION_OTP_LOCK, }; int ret = 0; unsigned int wp_start = 0, wp_len = 0; @@ -217,6 +418,12 @@ {"wp-enable", optional_argument, 0, OPTION_WP_ENABLE}, {"wp-disable", 0, 0, OPTION_WP_DISABLE}, {"wp-list", 0, 0, OPTION_WP_LIST}, + {"otp-status", 0, NULL, OPTION_OTP_STATUS}, + {"otp-region", 1, NULL, OPTION_OTP_REGION}, + {"otp-read", 1, NULL, OPTION_OTP_READ}, + {"otp-write", 1, NULL, OPTION_OTP_WRITE}, + {"otp-erase", 0, NULL, OPTION_OTP_ERASE}, + {"otp-lock", 0, NULL, OPTION_OTP_LOCK}, {"list-supported", 0, NULL, 'L'}, {"list-supported-wiki", 0, NULL, 'z'}, {"programmer", 1, NULL, 'p'}, @@ -238,6 +445,7 @@ struct layout_include_args *include_args = NULL; char *wp_mode_opt = NULL; char *wp_region = NULL; + char *otp_region_opt = NULL;
/* * Safety-guard against a user who has (mistakenly) closed @@ -385,6 +593,30 @@ case OPTION_WP_DISABLE: set_wp_disable = 1; break; + case OPTION_OTP_STATUS: + otp_status = 1; + break; + case OPTION_OTP_REGION: + otp_region_opt = strdup(optarg); + break; + case OPTION_OTP_READ: + cli_classic_validate_singleop(&operation_specified); + filename = strdup(optarg); + otp_read = 1; + break; + case OPTION_OTP_WRITE: + cli_classic_validate_singleop(&operation_specified); + filename = strdup(optarg); + otp_write = 1; + break; + case OPTION_OTP_ERASE: + cli_classic_validate_singleop(&operation_specified); + otp_erase = 1; + break; + case OPTION_OTP_LOCK: + cli_classic_validate_singleop(&operation_specified); + otp_lock = 1; + break; case 'L': cli_classic_validate_singleop(&operation_specified); list_supported = 1; @@ -673,9 +905,11 @@ goto out_shutdown; }
+ const bool any_otp_op = + otp_status || otp_read || otp_write || otp_erase || otp_lock; + if (!(read_it | write_it | verify_it | erase_it | flash_name | flash_size - | set_wp_range | set_wp_region | set_wp_enable | - set_wp_disable | wp_status | wp_list | extract_it)) { + | any_otp)) { msg_ginfo("No operations were specified.\n"); goto out_shutdown; } @@ -824,6 +1058,11 @@ goto out_release; }
+ ret = otp_cli(fill_flash, any_otp_op, otp_status, otp_read, otp_write, otp_erase, otp_lock, + otp_region_opt, filename); + if (ret) + goto out_release; + flashrom_flag_set(fill_flash, FLASHROM_FLAG_FORCE, !!force); #if CONFIG_INTERNAL == 1 flashrom_flag_set(fill_flash, FLASHROM_FLAG_FORCE_BOARDMISMATCH, !!force_boardmismatch); diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl index 126e50c..3187ab8 100644 --- a/flashrom.8.tmpl +++ b/flashrom.8.tmpl @@ -50,7 +50,12 @@ [\fB-E\fR|\fB-x\fR|\fB-r\fR <file>|\fB-w\fR <file>|\fB-v\fR <file>] [(\fB-l\fR <file>|\fB--ifd|\fB --fmap\fR|\fB--fmap-file\fR <file>) [\fB-i\fR <image>[:<file>]]] - [\fB-n\fR] [\fB-N\fR] [\fB-f\fR])] + [\fB-n\fR] [\fB-N\fR] [\fB-f\fR] + [\fB--otp-status\fR] [\fB--otp-region\fR <otp-region>] + [\fB--otp-read\fR <file>| + \fB--otp-write\fR <file>| + \fB--otp-erase\fR| + \fB--otp-lock\fR]) [\fB-V\fR[\fBV\fR[\fBV\fR]]] [\fB-o\fR <logfile>] .SH DESCRIPTION .B flashrom @@ -140,6 +145,45 @@ Extract every region defined on the layout from flash ROM chip to a file with the same name as the extracted region. .TP +.B "--otp-status" +Print details about OTP region(s) for the chip. +.TP +.B "--otp-region <region>" +Specify OTP region number (base 1) to operation on. Can be omitted if selected +chip has only one region. +.TP +.B "--otp-read <file>" +Read OTP region +.B "<otp-region>" +of the chip and save to +.B "<file>." +.sp +Example: +.sp +.B " flashrom -p prog -c chip --otp-region 2 --otp-read read_otp.bin --otp-status" +.sp +will read the the 2nd OTP region, save it to +.B "read_otp.bin" +and print information about all OTP regions at the end. +.TP +.B "--otp-write <file>" +Write +.B "<file>" +to OTP region +.B "<otp-region>" +of the chip. +.TP +.B "--otp-erase" +Erase OTP region +.B "<otp-region>" +of the chip. +.TP +.B "--otp-lock" +Lock OTP region +.B "<otp-region>" +of the chip against any future writes (be cautious, this action is +almost always irreversible!). +.TP .B "-V, --verbose" More verbose output. This option can be supplied multiple times (max. 3 times, i.e. @@ -1551,10 +1595,10 @@ .sp Some flash chips contain OTP memory often denoted as "security registers". They usually have a capacity in the range of some bytes to a few hundred -bytes and can be used to give devices unique IDs etc. flashrom is not able -to read or write these memories and may therefore not be able to duplicate a -chip completely. For chip types known to include OTP memories a warning is -printed when they are detected. +bytes and can be used to give devices unique IDs etc. flashrom can read and +write these memories for some chips with --otp-* operations, but writing is +possible only if they weren't yet locked. For chip types with unsupported OTP +memories a warning is printed when they are detected. .sp Similar to OTP memories are unique, factory programmed, unforgeable IDs. They are not modifiable by the user at all.