Anastasia Klimchuk submitted this change.
cli_client: Add rpmc command support
This commit adds uses the new rpmc command implementation to
add them as a new feature to the cli_client.
Also adds the necessary documentation for this new feature.
Tested on the Winbond W25R128JV as a 'SFDP-capable chip'.
This patch was done to add rpmc command support to flashrom.
This enables users to write root keys to their flash chips while they
flash data on the chip. This might become useful in the future as rpmc
support is extended in coreboot.
Also adds debug tools to flashrom, which might be useful in
implementing coreboots rpmc support.
Change-Id: I36c823bbee65f256eb6edabe6f058321c9a0cfa1
Signed-off-by: Matti Finder <matti.finder@gmail.com>
Reviewed-on: https://review.coreboot.org/c/flashrom/+/84840
Reviewed-by: Peter Marheine <pmarheine@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Anastasia Klimchuk <aklm@chromium.org>
---
M cli_classic.c
M doc/classic_cli_manpage.rst
M doc/dev_guide/building_from_source.rst
M doc/release_notes/devel.rst
4 files changed, 307 insertions(+), 7 deletions(-)
diff --git a/cli_classic.c b/cli_classic.c
index 896c167..2fe9324 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -24,6 +24,7 @@
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
+#include <stdint.h>
#include <cli_classic.h>
#include "flash.h"
#include "flashchips.h"
@@ -31,6 +32,10 @@
#include "programmer.h"
#include "libflashrom.h"
+#if CONFIG_RPMC_ENABLED == 1
+#include "rpmc.h"
+#endif /* CONFIG_RPMC_ENABLED */
+
enum {
OPTION_IFD = 0x0100,
OPTION_FMAP,
@@ -46,6 +51,16 @@
OPTION_WP_LIST,
OPTION_PROGRESS,
OPTION_SACRIFICE_RATIO,
+#if CONFIG_RPMC_ENABLED == 1
+ OPTION_RPMC_READ_DATA,
+ OPTION_RPMC_WRITE_ROOT_KEY,
+ OPTION_RPMC_UPDATE_HMAC_KEY,
+ OPTION_RPMC_INCREMENT_COUNTER,
+ OPTION_RPMC_GET_COUNTER,
+ OPTION_RPMC_COUNTER_ADDRESS,
+ OPTION_RPMC_KEY_DATA,
+ OPTION_RPMC_KEY_FILE,
+#endif /* CONFIG_RPMC_ENABLED */
};
struct cli_options {
@@ -75,6 +90,17 @@
char *referencefile;
const char *chip_to_probe;
int sacrifice_ratio;
+
+#if CONFIG_RPMC_ENABLED == 1
+ bool rpmc_read_data;
+ bool rpmc_write_root_key;
+ bool rpmc_update_hmac_key;
+ bool rpmc_increment_counter;
+ bool rpmc_get_counter;
+ unsigned int rpmc_counter_address;
+ uint32_t rpmc_key_data, rpmc_previous_counter_value;
+ const char *rpmc_root_key_file;
+#endif /* CONFIG_RPMC_ENABLED */
};
static void cli_classic_usage(const char *name)
@@ -129,10 +155,27 @@
" operation VS the longevity of the chip. Default is\n"
" longevity.\n"
" DANGEROUS! It wears your chip faster!\n"
+#if CONFIG_RPMC_ENABLED == 1
+ "RPMC COMMANDS\n"
+ " --get-rpmc-status read the extended status\n"
+ " --write-root-key write the root key register\n"
+ " --update-hmac-key update the hmac key register\n"
+ " --increment-counter <current>\n"
+ " increment rpmc counter\n"
+ " --get-counter get rpmc counter\n"
+ "RPMC OPTIONS\n"
+ " --counter-address <address> counter address (default: 0)\n"
+ " --rpmc-root-key <keyfile> rpmc root key file\n"
+ " --key-data <value> hex number to use as key data (default: 0)\n"
+#endif /* CONFIG_RPMC_ENABLED */
+ "PROGRAMMER SELECTION OPTIONS\n"
" -p | --programmer <name>[:<param>] specify the programmer device. One of\n");
list_programmers_linebreak(4, 80, 0);
- printf(".\n\nYou can specify one of -h, -R, -L, "
- "-E, -r, -w, -v or no operation.\n"
+ printf(".\n\nYou can specify one of -h, -R, -L, -E, -r, -w, -v"
+#if CONFIG_RPMC_ENABLED == 1
+ ", a RPMC command"
+#endif /* CONFIG_RPMC_ENABLED */
+ " or no operation.\n"
"If no operation is specified, flashrom will only probe for flash chips.\n");
}
@@ -405,6 +448,110 @@
return 0;
}
+#if CONFIG_RPMC_ENABLED == 1
+static int rpmc_cli(struct flashctx *flash,
+ const char *const key_file,
+ const uint32_t key_data,
+ const unsigned int counter_address,
+ const uint32_t previous_counter,
+ const bool op_read_data,
+ const bool op_write_root_key,
+ const bool op_update_hmac_key,
+ const bool op_increment_counter,
+ const bool op_get_counter)
+{
+ if (op_write_root_key) {
+ enum rpmc_result result = rpmc_write_root_key(flash, key_file, counter_address);
+ if (result != RPMC_SUCCESS) {
+ msg_gerr("Failed to write root key\n%s", rpmc_describe_result(result));
+ return 1;
+ }
+
+ msg_ginfo("Successfully wrote new root key for counter %u.\n", counter_address);
+ }
+
+ if (op_update_hmac_key) {
+ enum rpmc_result result = rpmc_update_hmac_key(flash,
+ key_file,
+ key_data,
+ counter_address);
+ if (result != RPMC_SUCCESS) {
+ msg_gerr("Failed to update hmac key\n%s", rpmc_describe_result(result));
+ return 1;
+ }
+
+ msg_ginfo("Successfully updated hmac key to 0x%08x for counter %u.\n",
+ key_data,
+ counter_address);
+ }
+
+ if (op_increment_counter) {
+ enum rpmc_result result = rpmc_increment_counter(flash,
+ key_file,
+ key_data,
+ counter_address,
+ previous_counter);
+ if (result != RPMC_SUCCESS) {
+ msg_gerr("Failed to increment the counter\n%s", rpmc_describe_result(result));
+ return 1;
+ }
+
+ msg_ginfo("Successfully incremented counter %u.\n", counter_address);
+ }
+
+ if (op_get_counter) {
+ uint32_t counter_value;
+ enum rpmc_result result = rpmc_get_monotonic_counter(flash,
+ key_file,
+ key_data,
+ counter_address,
+ &counter_value);
+ if (result != RPMC_SUCCESS) {
+ msg_gerr("Failed to get the counter value\n%s", rpmc_describe_result(result));
+ return 1;
+ }
+
+ msg_ginfo("Returned counter value %u for counter %u\n", counter_value, counter_address);
+ }
+
+ if (op_read_data) {
+ struct rpmc_status_register status;
+ enum rpmc_result result = rpmc_read_data(flash, &status);
+ if (result != RPMC_SUCCESS) {
+ msg_gerr("Failed to read read rpmc data\n%s", rpmc_describe_result(result));
+ return 1;
+ }
+
+ msg_ginfo("Reading rpmc data returned:\n");
+
+ char bin_buffer[9];
+ uint8_t status_bits = status.status;
+ for (int i = 7; i >= 0; i--){
+ bin_buffer[i] = '0' + (status_bits & 1);
+ status_bits = status_bits >> 1;
+ }
+ bin_buffer[8] = '\0';
+ msg_ginfo("Extended Status: 0b%s\n", bin_buffer);
+
+ msg_ginfo("Tag:\n");
+ for (size_t i = 0; i < RPMC_TAG_LENGTH; i++){
+ msg_ginfo("0x%02x ", status.tag[i]);
+ }
+ msg_ginfo("\n");
+
+ msg_ginfo("Counter: %u\n", status.counter_data);
+
+ msg_ginfo("Signature:\n");
+ for (size_t i = 0; i < RPMC_SIGNATURE_LENGTH; i++){
+ msg_ginfo("0x%02x ", status.signature[i]);
+ }
+ msg_ginfo("\n");
+ }
+
+ return 0;
+}
+#endif /* CONFIG_RPMC_ENABLED */
+
/**
* @brief Reads content to buffer from one or more files.
*
@@ -824,6 +971,33 @@
/* It is okay to convert invalid input to 0. */
options->sacrifice_ratio = atoi(optarg);
break;
+#if CONFIG_RPMC_ENABLED == 1
+ case OPTION_RPMC_READ_DATA:
+ options->rpmc_read_data = true;
+ break;
+ case OPTION_RPMC_WRITE_ROOT_KEY:
+ options->rpmc_write_root_key = true;
+ break;
+ case OPTION_RPMC_UPDATE_HMAC_KEY:
+ options->rpmc_update_hmac_key = true;
+ break;
+ case OPTION_RPMC_INCREMENT_COUNTER:
+ options->rpmc_increment_counter = true;
+ options->rpmc_previous_counter_value = strtoumax(optarg, NULL, 10);
+ break;
+ case OPTION_RPMC_GET_COUNTER:
+ options->rpmc_get_counter = true;
+ break;
+ case OPTION_RPMC_COUNTER_ADDRESS:
+ options->rpmc_counter_address = strtoumax(optarg, NULL, 10);
+ break;
+ case OPTION_RPMC_KEY_DATA:
+ options->rpmc_key_data = strtoumax(optarg, NULL, 16);
+ break;
+ case OPTION_RPMC_KEY_FILE:
+ options->rpmc_root_key_file = strdup(optarg);
+ break;
+#endif /* CONFIG_RPMC_ENABLED */
default:
cli_classic_abort_usage(NULL);
break;
@@ -894,6 +1068,16 @@
{"output", 1, NULL, 'o'},
{"progress", 0, NULL, OPTION_PROGRESS},
{"sacrifice-ratio", 1, NULL, OPTION_SACRIFICE_RATIO},
+#if CONFIG_RPMC_ENABLED == 1
+ {"get-rpmc-status", 0, NULL, OPTION_RPMC_READ_DATA},
+ {"write-root-key", 0, NULL, OPTION_RPMC_WRITE_ROOT_KEY},
+ {"update-hmac-key", 0, NULL, OPTION_RPMC_UPDATE_HMAC_KEY},
+ {"increment-counter", 1, NULL, OPTION_RPMC_INCREMENT_COUNTER},
+ {"get-counter", 0, NULL, OPTION_RPMC_GET_COUNTER},
+ {"counter-address", 1, NULL, OPTION_RPMC_COUNTER_ADDRESS},
+ {"key-data", 1, NULL, OPTION_RPMC_KEY_DATA},
+ {"rpmc-root-key", 1, NULL, OPTION_RPMC_KEY_FILE},
+#endif /* CONFIG_RPMC_ENABLED */
{NULL, 0, NULL, 0},
};
@@ -1104,9 +1288,17 @@
options.set_wp_range || options.set_wp_region || options.enable_wp ||
options.disable_wp || options.print_wp_status || options.print_wp_ranges;
+ const bool any_rpmc_op =
+#if CONFIG_RPMC_ENABLED == 1
+ options.rpmc_read_data || options.rpmc_write_root_key || options.rpmc_update_hmac_key ||
+ options.rpmc_increment_counter || options.rpmc_get_counter;
+#else
+ false;
+#endif /* CONFIG_RPMC_ENABLED */
+
const bool any_op = options.read_it || options.write_it || options.verify_it ||
options.erase_it || options.flash_name || options.flash_size ||
- options.extract_it || any_wp_op;
+ options.extract_it || any_wp_op || any_rpmc_op;
if (!any_op) {
msg_ginfo("No operations were specified.\n");
@@ -1250,6 +1442,21 @@
else if (options.verify_it)
ret = do_verify(fill_flash, options.filename);
+#if CONFIG_RPMC_ENABLED == 1
+ if (any_rpmc_op && ret == 0) {
+ ret = rpmc_cli(fill_flash,
+ options.rpmc_root_key_file,
+ options.rpmc_key_data,
+ options.rpmc_counter_address,
+ options.rpmc_previous_counter_value,
+ options.rpmc_read_data,
+ options.rpmc_write_root_key,
+ options.rpmc_update_hmac_key,
+ options.rpmc_increment_counter,
+ options.rpmc_get_counter);
+ }
+#endif /* CONFIG_RPMC_ENABLED */
+
out_release:
flashrom_layout_release(options.layout);
out_shutdown:
diff --git a/doc/classic_cli_manpage.rst b/doc/classic_cli_manpage.rst
index f0879e4..47df6c7 100644
--- a/doc/classic_cli_manpage.rst
+++ b/doc/classic_cli_manpage.rst
@@ -19,7 +19,11 @@
| [-i <include>[:<file>]]]
| [--wp-status] [--wp-list] [--wp-enable|--wp-disable]
| [--wp-range <start>,<length>|--wp-region <region>]
-| [-n] [-N] [-f])]
+| [-n] [-N] [-f]
+| [--rpmc-root-key <keyfile>] [--key-data <value>]
+| [--counter-address <address>]
+| [--get-rpmc-status] [--write-root-key] [--update-hmac-key]
+| [--increment-counter <current>] [--get-counter])]
| [-V[V[V]]] [-o <logfile>] [--progress] [--sacrifice-ratio <ratio>]
@@ -39,7 +43,7 @@
OPTIONS
-------
-You can specify one of ``-h``, ``-R``, ``-L``, ``-E``, ``-r``, ``-w``, ``-v`` or no operation.
+You can specify one of ``-h``, ``-R``, ``-L``, ``-E``, ``-r``, ``-w``, ``-v``, a RPMC command or no operation.
If no operation is specified, **flashrom** will only probe for flash chips. It is recommended that if you try **flashrom** the
first time on a system, you run it in probe-only mode and check the output.
Also you are advised to make a backup of your current ROM contents with ``-r`` before you try to write a new image.
@@ -334,6 +338,81 @@
**-R, --version**
Show version information and exit.
+RPMC commands
+^^^^^^^^^^^^^
+
+This section describes the commands added in JESD260. They are only supported on specific chip models.
+If the chip is detected correctly but you still get ``Error: RPMC commands are not supported on this device``,
+try using ``-c "SFDP-capable chip"`` for automatic feature detection.
+
+**--get-rpmc-status**
+ Read the extended RPMC status by issuing a OP2 command
+
+ Example::
+
+ flashrom -p prog --get-rpmc-status
+
+
+**--write-root-key**
+ Write new root key from **--rpmc-root-key** file for **--counter-address**.
+
+ Example::
+
+ flashrom -p prog --rpmc-root-key <keyfile> --counter-address <address> --write-root-key
+
+**--update-hmac-key**
+ Update the hmac key register for **--counter-address** with the provided **--key-data**.
+ Requires valid **--rpmc-root-key**.
+
+ Example::
+
+ flashrom -p prog --rpmc-root-key <keyfile> --counter-address <address> --key-data <value> --update-hmac-key
+
+**--increment-counter <current>**
+ Increments the counter at **--counter-address** by one above the **<current>**.
+ Requires previously updated **--key-data** and valid **--rpmc-root-key**.
+
+ Examples::
+
+ flashrom -p prog --rpmc-root-key <keyfile> --counter-address <address> --key-data <value> --increment-counter 12
+ flashrom -p prog --rpmc-root-key <keyfile> --counter-address <address> --key-data <value> -w rom.bin --update-hmac-key --increment-counter 25
+
+**--get-counter**
+ Get the current counter value for **--counter-address**.
+ Requires previously updated **--key-data** and valid **--rpmc-root-key**.
+
+ Examples::
+
+ flashrom -p prog --rpmc-root-key <keyfile> --counter-address <address> --key-data <value> --get-counter
+ flashrom -p prog --rpmc-root-key <keyfile> --counter-address <address> --key-data <value> --update-hmac-key --get-counter
+
+RPMC options
+^^^^^^^^^^^^
+
+**--counter-address <address>**
+ Target the counter at **<address>** for any RPMC operations.
+ Addressing starts at 0.
+ Defaults to 0.
+
+ Example::
+
+ flashrom --counter-address 2
+
+**--rpmc-root-key <keyfile>**
+ Use **<keyfile>** as location of 32-byte root key.
+
+ Example::
+
+ flashrom --rpmc-root-key /home/user/some_key.bin
+
+**--key-data <value>**
+ Hexadecimal **<value>** will be used as 4-byte key data in RPMC operations.
+ Defaults to 0.
+
+ Example::
+
+ flashrom --key-data 12abc
+
.. _programmer-specific information:
PROGRAMMER-SPECIFIC INFORMATION
diff --git a/doc/dev_guide/building_from_source.rst b/doc/dev_guide/building_from_source.rst
index b360b60..73f5c2b 100644
--- a/doc/dev_guide/building_from_source.rst
+++ b/doc/dev_guide/building_from_source.rst
@@ -20,10 +20,12 @@
* libftdi1 [#b2]_
* libjaylink [#b2]_
* NI-845x driver & library package [#b3]_
+* libcrypto [#b4]_
.. [#b1] | optional, for building unit testing
.. [#b2] | optional, depending on the selected programmer
.. [#b3] | optional, proprietary and Windows only. (See Windows build instructions)
+.. [#b4] | optional, to enable RPMC commands
If you are cross compiling, install the dependencies for your target.
@@ -56,7 +58,8 @@
apt-get install -y \
gcc meson ninja-build pkg-config python3-sphinx \
- libcmocka-dev libpci-dev libusb-1.0-0-dev libftdi1-dev libjaylink-dev
+ libcmocka-dev libpci-dev libusb-1.0-0-dev libftdi1-dev libjaylink-dev \
+ libssl-dev
* ArchLinux / Manjaro
@@ -64,7 +67,8 @@
pacman -S --noconfirm \
gcc meson ninja pkg-config python-sphinx cmocka \
- pciutils libusb libftdi libjaylink
+ pciutils libusb libftdi libjaylink \
+ openssl
* openSUSE / SUSE
diff --git a/doc/release_notes/devel.rst b/doc/release_notes/devel.rst
index e0c42a5..02b4e24 100644
--- a/doc/release_notes/devel.rst
+++ b/doc/release_notes/devel.rst
@@ -67,6 +67,16 @@
The tradeoff is the speed of programming operation VS the longevity of
the chip. Default is longevity.
+RPMC support added
+==================
+
+Adding support for RPMC commands as specified by JESD260 to the cli_client. Main
+implementation is in rpmc.c. Also adds new parsing capabilities for the sfdp
+page carrying the necessary information. All the features are optional and
+depend on libcrypto.
+It currently uses automatic feature detection through the corresponding
+sfdp page.
+
Chipset support
===============
To view, visit change 84840. To unsubscribe, or for help writing mail filters, visit settings.