Sergii Dmytruk has uploaded this change for review.

View Change

[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.

To view, visit change 59407. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: If77694e3b1c453b3fc0561da7a9eefdbce4fbb2b
Gerrit-Change-Number: 59407
Gerrit-PatchSet: 1
Gerrit-Owner: Sergii Dmytruk <sergii.dmytruk@3mdeb.com>
Gerrit-MessageType: newchange