From b55469123b1adbdf3adedbfbb270a17897513f7a Mon Sep 17 00:00:00 2001 From: Matti Finder Date: Wed, 30 Oct 2024 17:58:05 +0100 Subject: [PATCH] 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 Reviewed-on: https://review.coreboot.org/c/flashrom/+/84840 Reviewed-by: Peter Marheine Tested-by: build bot (Jenkins) Reviewed-by: Anastasia Klimchuk --- cli_classic.c | 213 ++++++++++++++++++++++++- doc/classic_cli_manpage.rst | 83 +++++++++- doc/dev_guide/building_from_source.rst | 8 +- doc/release_notes/devel.rst | 10 ++ 4 files changed, 307 insertions(+), 7 deletions(-) diff --git a/cli_classic.c b/cli_classic.c index 896c1670a..2fe9324b4 100644 --- a/cli_classic.c +++ b/cli_classic.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #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 @@ enum { 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 @@ struct cli_options { 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 @@ static void cli_classic_usage(const char *name) " 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 \n" + " increment rpmc counter\n" + " --get-counter get rpmc counter\n" + "RPMC OPTIONS\n" + " --counter-address
counter address (default: 0)\n" + " --rpmc-root-key rpmc root key file\n" + " --key-data hex number to use as key data (default: 0)\n" +#endif /* CONFIG_RPMC_ENABLED */ + "PROGRAMMER SELECTION OPTIONS\n" " -p | --programmer [:] 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 @@ static int wp_cli( 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 @@ static void parse_options(int argc, char **argv, const char *optstring, /* 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 @@ int main(int argc, char *argv[]) {"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 @@ int main(int argc, char *argv[]) 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 @@ int main(int argc, char *argv[]) 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 f0879e472..47df6c795 100644 --- a/doc/classic_cli_manpage.rst +++ b/doc/classic_cli_manpage.rst @@ -19,7 +19,11 @@ SYNOPSIS | [-i [:]]] | [--wp-status] [--wp-list] [--wp-enable|--wp-disable] | [--wp-range ,|--wp-region ] -| [-n] [-N] [-f])] +| [-n] [-N] [-f] +| [--rpmc-root-key ] [--key-data ] +| [--counter-address
] +| [--get-rpmc-status] [--write-root-key] [--update-hmac-key] +| [--increment-counter ] [--get-counter])] | [-V[V[V]]] [-o ] [--progress] [--sacrifice-ratio ] @@ -39,7 +43,7 @@ which use various protocols such as LPC, FWH, parallel flash, or SPI. 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 @@ All operations involving any chip access (probe/read/write/...) require the ``-p **-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 --counter-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 --counter-address
--key-data --update-hmac-key + +**--increment-counter ** + Increments the counter at **--counter-address** by one above the ****. + Requires previously updated **--key-data** and valid **--rpmc-root-key**. + + Examples:: + + flashrom -p prog --rpmc-root-key --counter-address
--key-data --increment-counter 12 + flashrom -p prog --rpmc-root-key --counter-address
--key-data -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 --counter-address
--key-data --get-counter + flashrom -p prog --rpmc-root-key --counter-address
--key-data --update-hmac-key --get-counter + +RPMC options +^^^^^^^^^^^^ + +**--counter-address
** + Target the counter at **
** for any RPMC operations. + Addressing starts at 0. + Defaults to 0. + + Example:: + + flashrom --counter-address 2 + +**--rpmc-root-key ** + Use **** as location of 32-byte root key. + + Example:: + + flashrom --rpmc-root-key /home/user/some_key.bin + +**--key-data ** + Hexadecimal **** 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 b360b6033..73f5c2bfb 100644 --- a/doc/dev_guide/building_from_source.rst +++ b/doc/dev_guide/building_from_source.rst @@ -20,10 +20,12 @@ And the following dependencies: * 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 @@ Linux 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 @@ Linux 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 e0c42a503..02b4e24d6 100644 --- a/doc/release_notes/devel.rst +++ b/doc/release_notes/devel.rst @@ -67,6 +67,16 @@ the entire area with one block. 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 ===============