[PATCH v3 0/6] Reconfigure TPM when active hash algorithms dismatch

This patch set implements PCR allocate command to handle the algorithm dismatches among TPM device, eventlog from previous boot stage and what U-Boot supports.
It re-configurates TPM device if any active algorithms are not supported by U-Boot or does not exist in the eventlog passed in.
To re-configurate, a PCR allocate command will be sent with new algorithm configurations, followed by a shutdown command and a hardware reset to activate those new configurations.
If any of the algorithms from the eventlog does not supported by U-Boot or TPM device does not support all U-Boot algorithms, exit with error.
This new feature is under control by a new introduced kconfig TPM_PCR_ALLOCATE.
Raymond Mao (6): tpm: add TPM2_Shutdown command tpm: add TPM2_PCR_Allocate command tpm: add wrapper and helper APIs for PCR allocate tpm: add PCR allocate into the eventlog handling tpm: PCR allocate during PCR extend to disable the unsupported algorithms board: qemu-arm: select TPM_PCR_ALLOCATE
cmd/tpm-v2.c | 128 ++++++++++++++++- configs/qemu_arm64_defconfig | 1 + include/tpm-v2.h | 53 ++++++- lib/Kconfig | 12 ++ lib/tpm-v2.c | 259 ++++++++++++++++++++++++++++++++++- lib/tpm_api.c | 4 +- lib/tpm_tcg2.c | 52 +++---- 7 files changed, 463 insertions(+), 46 deletions(-)

TPM2_shutdown command is sharing same structure and logics with TPM2_startup, thus this patch extends the existing startup APIs and cmd functions to support shutdown instead of created new ones.
Signed-off-by: Raymond Mao raymond.mao@linaro.org Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org --- changes in v2 - None. changes in v3 - None.
cmd/tpm-v2.c | 14 +++++++++++--- include/tpm-v2.h | 3 ++- lib/tpm-v2.c | 9 +++++---- lib/tpm_api.c | 4 ++-- 4 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/cmd/tpm-v2.c b/cmd/tpm-v2.c index 8517833f86..a6d57ee7c4 100644 --- a/cmd/tpm-v2.c +++ b/cmd/tpm-v2.c @@ -18,11 +18,14 @@ static int do_tpm2_startup(struct cmd_tbl *cmdtp, int flag, int argc, enum tpm2_startup_types mode; struct udevice *dev; int ret; + bool bon = true;
ret = get_tpm(&dev); if (ret) return ret; - if (argc != 2) + + /* argv[2] is optional to perform a TPM2_CC_SHUTDOWN */ + if (argc > 3 || (argc == 3 && strcasecmp("off", argv[2]))) return CMD_RET_USAGE;
if (!strcasecmp("TPM2_SU_CLEAR", argv[1])) { @@ -34,7 +37,10 @@ static int do_tpm2_startup(struct cmd_tbl *cmdtp, int flag, int argc, return CMD_RET_FAILURE; }
- return report_return_code(tpm2_startup(dev, mode)); + if (argv[2]) + bon = false; + + return report_return_code(tpm2_startup(dev, bon, mode)); }
static int do_tpm2_self_test(struct cmd_tbl *cmdtp, int flag, int argc, @@ -420,11 +426,13 @@ U_BOOT_CMD(tpm2, CONFIG_SYS_MAXARGS, 1, do_tpm, "Issue a TPMv2.x command", " Initialize the software stack. Always the first command to issue.\n" " 'tpm startup' is the only acceptable command after a 'tpm init' has been\n" " issued\n" -"startup <mode>\n" +"startup <mode> [<op>]\n" " Issue a TPM2_Startup command.\n" " <mode> is one of:\n" " * TPM2_SU_CLEAR (reset state)\n" " * TPM2_SU_STATE (preserved state)\n" +" <op>:\n" +" * off - To shutdown the TPM\n" "self_test <type>\n" " Test the TPM capabilities.\n" " <type> is one of:\n" diff --git a/include/tpm-v2.h b/include/tpm-v2.h index 65681464b3..f66a8e1bf4 100644 --- a/include/tpm-v2.h +++ b/include/tpm-v2.h @@ -230,6 +230,7 @@ enum tpm2_command_codes { TPM2_CC_PCR_READ = 0x017E, TPM2_CC_PCR_EXTEND = 0x0182, TPM2_CC_PCR_SETAUTHVAL = 0x0183, + TPM2_CC_SHUTDOWN = 0x0145, };
/** @@ -430,7 +431,7 @@ enum { * * Return: code of the operation */ -u32 tpm2_startup(struct udevice *dev, enum tpm2_startup_types mode); +u32 tpm2_startup(struct udevice *dev, bool onoff, enum tpm2_startup_types mode);
/** * Issue a TPM2_SelfTest command. diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c index bc750b7ca1..3946ac2dbb 100644 --- a/lib/tpm-v2.c +++ b/lib/tpm-v2.c @@ -44,12 +44,13 @@ static int tpm2_update_active_banks(struct udevice *dev) return 0; }
-u32 tpm2_startup(struct udevice *dev, enum tpm2_startup_types mode) +u32 tpm2_startup(struct udevice *dev, bool bon, enum tpm2_startup_types mode) { + int op = bon ? TPM2_CC_STARTUP : TPM2_CC_SHUTDOWN; const u8 command_v2[12] = { tpm_u16(TPM2_ST_NO_SESSIONS), tpm_u32(12), - tpm_u32(TPM2_CC_STARTUP), + tpm_u32(op), tpm_u16(mode), }; int ret; @@ -59,7 +60,7 @@ u32 tpm2_startup(struct udevice *dev, enum tpm2_startup_types mode) * but will return RC_INITIALIZE otherwise. */ ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL); - if (ret && ret != TPM2_RC_INITIALIZE) + if ((ret && ret != TPM2_RC_INITIALIZE) || !bon) return ret;
return tpm2_update_active_banks(dev); @@ -84,7 +85,7 @@ u32 tpm2_auto_start(struct udevice *dev) rc = tpm2_self_test(dev, TPMI_YES);
if (rc == TPM2_RC_INITIALIZE) { - rc = tpm2_startup(dev, TPM2_SU_CLEAR); + rc = tpm2_startup(dev, true, TPM2_SU_CLEAR); if (rc) return rc;
diff --git a/lib/tpm_api.c b/lib/tpm_api.c index 39a5121e30..576d601e5f 100644 --- a/lib/tpm_api.c +++ b/lib/tpm_api.c @@ -28,7 +28,7 @@ u32 tpm_startup(struct udevice *dev, enum tpm_startup_type mode) case TPM_ST_DEACTIVATED: return -EINVAL; } - return tpm2_startup(dev, type); + return tpm2_startup(dev, true, type); } else { return -ENOSYS; } @@ -60,7 +60,7 @@ u32 tpm_resume(struct udevice *dev) if (tpm_is_v1(dev)) return tpm1_startup(dev, TPM_ST_STATE); else if (tpm_is_v2(dev)) - return tpm2_startup(dev, TPM2_SU_STATE); + return tpm2_startup(dev, true, TPM2_SU_STATE); else return -ENOSYS; }

TPM2_PCR_Allocate command is required to reconfigure a TPM device to enable or disable algorithms in run-time, thus this patch introduces the implementation of PCR allocate APIs and adds related cmd functions for testing.
To test the feature, ensure that TPM is started up. Run pcr_allocate command to turn on/off an algorithm, multiple calls are supported and all changes will be cached: `tpm2 pcr_allocate <algorithm_name> <on|off>` Run startup command with argument 'off' to shutdown the TPM. `tpm2 startup TPM2_SU_CLEAR off` Reboot the board via `reset` to activate the changes.
Signed-off-by: Raymond Mao raymond.mao@linaro.org Acked-by: Ilias Apalodimas ilias.apalodimas@linaro.org --- changes in v2 - Fixed typos. - Print the algorithm name directly. - Check if the algorithm supported by the TPM device before allocate. changes in v3 - None.
cmd/tpm-v2.c | 114 +++++++++++++++++++++++++++++++++++++++++++ include/tpm-v2.h | 29 +++++++++++ lib/tpm-v2.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+)
diff --git a/cmd/tpm-v2.c b/cmd/tpm-v2.c index a6d57ee7c4..a62862e94f 100644 --- a/cmd/tpm-v2.c +++ b/cmd/tpm-v2.c @@ -232,6 +232,106 @@ unmap_data: return report_return_code(rc); }
+static u32 select_mask(u32 mask, enum tpm2_algorithms algo, bool select) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(hash_algo_list); i++) { + if (hash_algo_list[i].hash_alg != algo) + continue; + + if (select) + mask |= hash_algo_list[i].hash_mask; + else + mask &= ~hash_algo_list[i].hash_mask; + + break; + } + + return mask; +} + +static bool +is_algo_in_pcrs(enum tpm2_algorithms algo, struct tpml_pcr_selection *pcrs) +{ + size_t i; + + for (i = 0; i < pcrs->count; i++) { + if (algo == pcrs->selection[i].hash) + return true; + } + + return false; +} + +static int do_tpm2_pcrallocate(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + int ret; + enum tpm2_algorithms algo; + const char *pw = (argc < 4) ? NULL : argv[3]; + const ssize_t pw_sz = pw ? strlen(pw) : 0; + static struct tpml_pcr_selection pcr = { 0 }; + u32 pcr_len = 0; + bool bon = false; + static u32 mask; + int i; + + /* argv[1]: algorithm (bank), argv[2]: on/off */ + if (argc < 3 || argc > 4) + return CMD_RET_USAGE; + + if (!strcasecmp("on", argv[2])) + bon = true; + else if (strcasecmp("off", argv[2])) + return CMD_RET_USAGE; + + algo = tpm2_name_to_algorithm(argv[1]); + if (algo == -EINVAL) + return CMD_RET_USAGE; + + ret = get_tpm(&dev); + if (ret) + return ret; + + if (!pcr.count) { + /* + * Get current active algorithms (banks), PCRs and mask via the + * first call + */ + ret = tpm2_get_pcr_info(dev, &pcr); + if (ret) + return ret; + + for (i = 0; i < pcr.count; i++) { + struct tpms_pcr_selection *sel = &pcr.selection[i]; + const char *name; + + if (!tpm2_is_active_bank(sel)) + continue; + + mask = select_mask(mask, sel->hash, true); + name = tpm2_algorithm_name(sel->hash); + if (name) + printf("Active bank[%d]: %s\n", i, name); + } + } + + if (!is_algo_in_pcrs(algo, &pcr)) { + printf("%s is not supported by the tpm device\n", argv[1]); + return CMD_RET_USAGE; + } + + mask = select_mask(mask, algo, bon); + ret = tpm2_pcr_config_algo(dev, mask, &pcr, &pcr_len); + if (ret) + return ret; + + return report_return_code(tpm2_send_pcr_allocate(dev, pw, pw_sz, &pcr, + pcr_len)); +} + static int do_tpm_dam_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -401,6 +501,7 @@ static struct cmd_tbl tpm2_commands[] = { do_tpm_pcr_setauthpolicy, "", ""), U_BOOT_CMD_MKENT(pcr_setauthvalue, 0, 1, do_tpm_pcr_setauthvalue, "", ""), + U_BOOT_CMD_MKENT(pcr_allocate, 0, 1, do_tpm2_pcrallocate, "", ""), };
struct cmd_tbl *get_tpm2_commands(unsigned int *size) @@ -481,4 +582,17 @@ U_BOOT_CMD(tpm2, CONFIG_SYS_MAXARGS, 1, do_tpm, "Issue a TPMv2.x command", " <pcr>: index of the PCR\n" " <key>: secret to protect the access of PCR #<pcr>\n" " <password>: optional password of the PLATFORM hierarchy\n" +"pcr_allocate <algorithm> <on/off> [<password>]\n" +" Issue a TPM2_PCR_Allocate Command to reconfig PCR bank algorithm.\n" +" <algorithm> is one of:\n" +" * sha1\n" +" * sha256\n" +" * sha384\n" +" * sha512\n" +" <on|off> is one of:\n" +" * on - Select all available PCRs associated with the specified\n" +" algorithm (bank)\n" +" * off - Clear all available PCRs associated with the specified\n" +" algorithm (bank)\n" +" <password>: optional password\n" ); diff --git a/include/tpm-v2.h b/include/tpm-v2.h index f66a8e1bf4..af3158f6e4 100644 --- a/include/tpm-v2.h +++ b/include/tpm-v2.h @@ -230,6 +230,7 @@ enum tpm2_command_codes { TPM2_CC_PCR_READ = 0x017E, TPM2_CC_PCR_EXTEND = 0x0182, TPM2_CC_PCR_SETAUTHVAL = 0x0183, + TPM2_CC_PCR_ALLOCATE = 0x012B, TPM2_CC_SHUTDOWN = 0x0145, };
@@ -702,6 +703,34 @@ u32 tpm2_report_state(struct udevice *dev, uint vendor_cmd, uint vendor_subcmd, u32 tpm2_enable_nvcommits(struct udevice *dev, uint vendor_cmd, uint vendor_subcmd);
+/** + * tpm2_pcr_config_algo() - Allocate the active PCRs. Requires reboot + * + * @dev TPM device + * @algo_mask Mask of the algorithms + * @pcr PCR structure for allocation + * @pcr_len Actual PCR data length + * + * Return: code of the operation + */ +u32 tpm2_pcr_config_algo(struct udevice *dev, u32 algo_mask, + struct tpml_pcr_selection *pcr, u32 *pcr_len); + +/** + * tpm2_send_pcr_allocate() - Send PCR allocate command. Requires reboot + * + * @dev TPM device + * @pw Platform password + * @pw_sz Length of the password + * @pcr PCR structure for allocation + * @pcr_len Actual PCR data length + * + * Return: code of the operation + */ +u32 tpm2_send_pcr_allocate(struct udevice *dev, const char *pw, + const ssize_t pw_sz, struct tpml_pcr_selection *pcr, + u32 pcr_len); + /** * tpm2_auto_start() - start up the TPM and perform selftests. * If a testable function has not been tested and is diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c index 3946ac2dbb..045b5dd9eb 100644 --- a/lib/tpm-v2.c +++ b/lib/tpm-v2.c @@ -400,6 +400,130 @@ u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, return 0; }
+u32 tpm2_pcr_config_algo(struct udevice *dev, u32 algo_mask, + struct tpml_pcr_selection *pcr, u32 *pcr_len) +{ + int i; + + if (pcr->count > TPM2_NUM_PCR_BANKS) + return TPM_LIB_ERROR; + + *pcr_len = sizeof(pcr->count); + + for (i = 0; i < pcr->count; i++) { + struct tpms_pcr_selection *sel = &pcr->selection[i]; + u8 pad = 0; + int j; + + if (sel->size_of_select > TPM2_PCR_SELECT_MAX) + return TPM_LIB_ERROR; + + /* + * Found the algorithm (bank) that matches, and enable all PCR + * bits. + * TODO: only select the bits needed + */ + for (j = 0; j < ARRAY_SIZE(hash_algo_list); j++) { + if (hash_algo_list[j].hash_alg != sel->hash) + continue; + + if (algo_mask & hash_algo_list[j].hash_mask) + pad = 0xff; + } + + for (j = 0; j < sel->size_of_select; j++) + sel->pcr_select[j] = pad; + + log_info("set bank[%d] %s %s\n", i, + tpm2_algorithm_name(sel->hash), + tpm2_is_active_bank(sel) ? "on" : "off"); + + *pcr_len += sizeof(sel->hash) + sizeof(sel->size_of_select) + + sel->size_of_select; + } + + return 0; +} + +u32 tpm2_send_pcr_allocate(struct udevice *dev, const char *pw, + const ssize_t pw_sz, struct tpml_pcr_selection *pcr, + u32 pcr_len) +{ + /* Length of the message header, up to start of password */ + uint offset = 27; + u8 command_v2[COMMAND_BUFFER_SIZE] = { + tpm_u16(TPM2_ST_SESSIONS), /* TAG */ + tpm_u32(offset + pw_sz + pcr_len), /* Length */ + tpm_u32(TPM2_CC_PCR_ALLOCATE), /* Command code */ + + /* handles 4 bytes */ + tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */ + + /* AUTH_SESSION */ + tpm_u32(9 + pw_sz), /* Authorization size */ + tpm_u32(TPM2_RS_PW), /* Session handle */ + tpm_u16(0), /* Size of <nonce> */ + /* <nonce> (if any) */ + 0, /* Attributes: Cont/Excl/Rst */ + tpm_u16(pw_sz), /* Size of <hmac/password> */ + /* STRING(pw) <hmac/password> (if any) */ + + /* TPML_PCR_SELECTION */ + }; + u8 response[COMMAND_BUFFER_SIZE]; + size_t response_len = COMMAND_BUFFER_SIZE; + u32 i; + int ret; + + /* + * Fill the command structure starting from the first buffer: + * the password (if any) + */ + if (pack_byte_string(command_v2, sizeof(command_v2), "s", offset, pw, + pw_sz)) + return TPM_LIB_ERROR; + + offset += pw_sz; + + /* Pack the count field */ + if (pack_byte_string(command_v2, sizeof(command_v2), "d", offset, pcr->count)) + return TPM_LIB_ERROR; + + offset += sizeof(pcr->count); + + /* Pack each tpms_pcr_selection */ + for (i = 0; i < pcr->count; i++) { + struct tpms_pcr_selection *sel = &pcr->selection[i]; + + /* Pack hash (16-bit) */ + if (pack_byte_string(command_v2, sizeof(command_v2), "w", offset, + sel->hash)) + return TPM_LIB_ERROR; + + offset += sizeof(sel->hash); + + /* Pack size_of_select (8-bit) */ + if (pack_byte_string(command_v2, sizeof(command_v2), "b", offset, + sel->size_of_select)) + return TPM_LIB_ERROR; + + offset += sizeof(sel->size_of_select); + + /* Pack pcr_select array */ + if (pack_byte_string(command_v2, sizeof(command_v2), "s", offset, + sel->pcr_select, sel->size_of_select)) + return TPM_LIB_ERROR; + + offset += sel->size_of_select; + } + + ret = tpm_sendrecv_command(dev, command_v2, response, &response_len); + if (!ret) + tpm_init(dev); + + return ret; +} + static int tpm2_get_num_pcr(struct udevice *dev, u32 *num_pcr) { u8 response[(sizeof(struct tpms_capability_data) -

Add PCR allocate wrapper APIs for using in tcg2 protocol. The wrapper proceeds a PCR allocate command, followed by a shutdown command. A system boot is required after two commands since TPM device needs a HW reset to activate the new algorithms config. Also, a helper function is included to determine the new bank mask for PCR allocation by combining the status of current active, supported and eventlog bank masks. A new kconfig is created. PCR allocate and system reboot only happens when the kconfig is selected, otherwise just exit with errors.
Signed-off-by: Raymond Mao raymond.mao@linaro.org --- changes in v2 - Rename the newly added APIs. changes in v3 - Rename tpm2_set_algorithm_mask to tpm2_scan_masks.
include/tpm-v2.h | 21 ++++++++ lib/Kconfig | 12 +++++ lib/tpm-v2.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+)
diff --git a/include/tpm-v2.h b/include/tpm-v2.h index af3158f6e4..ece422df0c 100644 --- a/include/tpm-v2.h +++ b/include/tpm-v2.h @@ -703,6 +703,18 @@ u32 tpm2_report_state(struct udevice *dev, uint vendor_cmd, uint vendor_subcmd, u32 tpm2_enable_nvcommits(struct udevice *dev, uint vendor_cmd, uint vendor_subcmd);
+/** + * tpm2_scan_masks - Scan the bitmask of algorithms based on the + * active/supported banks and the one from eventlog. + * + * @dev TPM device + * @log_active Active algorithm bitmask + * @mask Bitmask to set + * + * Return: zero on success, negative errno otherwise + */ +int tpm2_scan_masks(struct udevice *dev, u32 log_active, u32 *mask); + /** * tpm2_pcr_config_algo() - Allocate the active PCRs. Requires reboot * @@ -730,6 +742,15 @@ u32 tpm2_pcr_config_algo(struct udevice *dev, u32 algo_mask, u32 tpm2_send_pcr_allocate(struct udevice *dev, const char *pw, const ssize_t pw_sz, struct tpml_pcr_selection *pcr, u32 pcr_len); +/** + * tpm2_activate_banks() - Activate PCR banks + * + * @param dev TPM device + * @log_active Bitmask of eventlog algorithms + * + * Return: code of the operation + */ +int tpm2_activate_banks(struct udevice *dev, u32 log_active);
/** * tpm2_auto_start() - start up the TPM and perform selftests. diff --git a/lib/Kconfig b/lib/Kconfig index baeb615626..0732333849 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -514,6 +514,18 @@ config VPL_TPM for the low-level TPM interface, but only one TPM is supported at a time by the TPM library.
+config TPM_PCR_ALLOCATE + bool "Re-configurate TPM algorithms in run-time (PCR allocate)" + depends on TPM_V2 && (MEASURED_BOOT || EFI_TCG2_PROTOCOL) + help + This enables a detection for the dismatches of algorithms among TPM + device, eventlog from previous boot stage and U-Boot support. + A PCR allocate command will be sent to reconfigurate the TPM device + in run-time to make sure algorithms in TPM device, eventlog and + U-Boot are aligned with each other. + A system reboot will be proceeded after then to activate the new + algorithms. + endmenu
menu "Android Verified Boot" diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c index 045b5dd9eb..aa02a9e961 100644 --- a/lib/tpm-v2.c +++ b/lib/tpm-v2.c @@ -44,6 +44,127 @@ static int tpm2_update_active_banks(struct udevice *dev) return 0; }
+static void tpm2_print_selected_algorithm_name(u32 selected) +{ + size_t i; + const char *str; + + for (i = 0; i < ARRAY_SIZE(hash_algo_list); i++) { + const struct digest_info *algo = &hash_algo_list[i]; + + if (!(selected & algo->hash_mask)) + continue; + + str = tpm2_algorithm_name(algo->hash_alg); + if (str) + log_info("%s\n", str); + } +} + +int tpm2_scan_masks(struct udevice *dev, u32 log_active, u32 *mask) +{ + struct tpml_pcr_selection pcrs; + u32 active = 0; + u32 supported = 0; + int rc, i; + + *mask = 0; + + rc = tpm2_get_pcr_info(dev, &pcrs); + if (rc) + return rc; + + for (i = 0; i < pcrs.count; i++) { + struct tpms_pcr_selection *sel = &pcrs.selection[i]; + size_t j; + u32 hash_mask = 0; + + for (j = 0; j < ARRAY_SIZE(hash_algo_list); j++) { + if (hash_algo_list[j].hash_alg == sel->hash) + hash_mask = hash_algo_list[j].hash_mask; + } + + if (tpm2_algorithm_supported(sel->hash)) + supported |= hash_mask; + + if (tpm2_is_active_bank(sel)) + active |= hash_mask; + } + + /* All eventlog algorithm(s) must be supported */ + if (log_active & ~supported) { + log_err("EventLog contains U-Boot unsupported algorithm(s)\n"); + tpm2_print_selected_algorithm_name(log_active & ~supported); + rc = -1; + } + if (log_active && active & ~log_active) { + log_warning("TPM active algorithm(s) not exist in eventlog\n"); + tpm2_print_selected_algorithm_name(active & ~log_active); + *mask = log_active; + } + + /* Any active algorithm(s) which are not supported must be removed */ + if (active & ~supported) { + log_warning("TPM active algorithm(s) unsupported by u-boot\n"); + tpm2_print_selected_algorithm_name(active & ~supported); + if (*mask) + *mask = active & supported & *mask; + else + *mask = active & supported; + } + + return rc; +} + +static int tpm2_pcr_allocate(struct udevice *dev, u32 algo_mask) +{ + struct tpml_pcr_selection pcr = { 0 }; + u32 pcr_len = 0; + int rc; + + rc = tpm2_get_pcr_info(dev, &pcr); + if (rc) + return rc; + + rc = tpm2_pcr_config_algo(dev, algo_mask, &pcr, &pcr_len); + if (rc) + return rc; + + /* Assume no password */ + rc = tpm2_send_pcr_allocate(dev, NULL, 0, &pcr, pcr_len); + if (rc) + return rc; + + /* Send TPM2_Shutdown, assume mode = TPM2_SU_CLEAR */ + return tpm2_startup(dev, false, TPM2_SU_CLEAR); +} + +int tpm2_activate_banks(struct udevice *dev, u32 log_active) +{ + u32 algo_mask = 0; + int rc; + + rc = tpm2_scan_masks(dev, log_active, &algo_mask); + if (rc) + return rc; + + if (algo_mask) { + if (!IS_ENABLED(CONFIG_TPM_PCR_ALLOCATE)) + return -1; + + rc = tpm2_pcr_allocate(dev, algo_mask); + if (rc) + return rc; + + log_info("PCR allocate done, shutdown TPM and reboot\n"); + do_reset(NULL, 0, 0, NULL); + log_err("reset does not work!\n"); + return -1; + } + + return 0; +} + u32 tpm2_startup(struct udevice *dev, bool bon, enum tpm2_startup_types mode) { int op = bon ? TPM2_CC_STARTUP : TPM2_CC_SHUTDOWN;

On Mon, 27 Jan 2025 at 16:59, Raymond Mao raymond.mao@linaro.org wrote:
Add PCR allocate wrapper APIs for using in tcg2 protocol. The wrapper proceeds a PCR allocate command, followed by a shutdown command. A system boot is required after two commands since TPM device needs a HW reset to activate the new algorithms config. Also, a helper function is included to determine the new bank mask for PCR allocation by combining the status of current active, supported and eventlog bank masks. A new kconfig is created. PCR allocate and system reboot only happens when the kconfig is selected, otherwise just exit with errors.
Signed-off-by: Raymond Mao raymond.mao@linaro.org
changes in v2
- Rename the newly added APIs.
changes in v3
- Rename tpm2_set_algorithm_mask to tpm2_scan_masks.
include/tpm-v2.h | 21 ++++++++ lib/Kconfig | 12 +++++ lib/tpm-v2.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+)
diff --git a/include/tpm-v2.h b/include/tpm-v2.h index af3158f6e4..ece422df0c 100644 --- a/include/tpm-v2.h +++ b/include/tpm-v2.h @@ -703,6 +703,18 @@ u32 tpm2_report_state(struct udevice *dev, uint vendor_cmd, uint vendor_subcmd, u32 tpm2_enable_nvcommits(struct udevice *dev, uint vendor_cmd, uint vendor_subcmd);
+/**
- tpm2_scan_masks - Scan the bitmask of algorithms based on the
active/supported banks and the one from eventlog.
- @dev TPM device
- @log_active Active algorithm bitmask
- @mask Bitmask to set
- Return: zero on success, negative errno otherwise
- */
+int tpm2_scan_masks(struct udevice *dev, u32 log_active, u32 *mask);
/**
- tpm2_pcr_config_algo() - Allocate the active PCRs. Requires reboot
@@ -730,6 +742,15 @@ u32 tpm2_pcr_config_algo(struct udevice *dev, u32 algo_mask, u32 tpm2_send_pcr_allocate(struct udevice *dev, const char *pw, const ssize_t pw_sz, struct tpml_pcr_selection *pcr, u32 pcr_len); +/**
- tpm2_activate_banks() - Activate PCR banks
- @param dev TPM device
- @log_active Bitmask of eventlog algorithms
- Return: code of the operation
- */
+int tpm2_activate_banks(struct udevice *dev, u32 log_active);
/**
- tpm2_auto_start() - start up the TPM and perform selftests.
diff --git a/lib/Kconfig b/lib/Kconfig index baeb615626..0732333849 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -514,6 +514,18 @@ config VPL_TPM for the low-level TPM interface, but only one TPM is supported at a time by the TPM library.
+config TPM_PCR_ALLOCATE
bool "Re-configurate TPM algorithms in run-time (PCR allocate)"
depends on TPM_V2 && (MEASURED_BOOT || EFI_TCG2_PROTOCOL)
help
This enables a detection for the dismatches of algorithms among TPM
device, eventlog from previous boot stage and U-Boot support.
A PCR allocate command will be sent to reconfigurate the TPM device
in run-time to make sure algorithms in TPM device, eventlog and
U-Boot are aligned with each other.
A system reboot will be proceeded after then to activate the new
algorithms.
endmenu
menu "Android Verified Boot" diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c index 045b5dd9eb..aa02a9e961 100644 --- a/lib/tpm-v2.c +++ b/lib/tpm-v2.c @@ -44,6 +44,127 @@ static int tpm2_update_active_banks(struct udevice *dev) return 0; }
+static void tpm2_print_selected_algorithm_name(u32 selected) +{
size_t i;
const char *str;
for (i = 0; i < ARRAY_SIZE(hash_algo_list); i++) {
const struct digest_info *algo = &hash_algo_list[i];
if (!(selected & algo->hash_mask))
continue;
str = tpm2_algorithm_name(algo->hash_alg);
if (str)
log_info("%s\n", str);
}
+}
+int tpm2_scan_masks(struct udevice *dev, u32 log_active, u32 *mask) +{
struct tpml_pcr_selection pcrs;
u32 active = 0;
u32 supported = 0;
int rc, i;
*mask = 0;
rc = tpm2_get_pcr_info(dev, &pcrs);
if (rc)
return rc;
for (i = 0; i < pcrs.count; i++) {
struct tpms_pcr_selection *sel = &pcrs.selection[i];
size_t j;
u32 hash_mask = 0;
for (j = 0; j < ARRAY_SIZE(hash_algo_list); j++) {
if (hash_algo_list[j].hash_alg == sel->hash)
hash_mask = hash_algo_list[j].hash_mask;
}
if (tpm2_algorithm_supported(sel->hash))
supported |= hash_mask;
if (tpm2_is_active_bank(sel))
active |= hash_mask;
}
/* All eventlog algorithm(s) must be supported */
if (log_active & ~supported) {
log_err("EventLog contains U-Boot unsupported algorithm(s)\n");
tpm2_print_selected_algorithm_name(log_active & ~supported);
rc = -1;
}
if (log_active && active & ~log_active) {
log_warning("TPM active algorithm(s) not exist in eventlog\n");
tpm2_print_selected_algorithm_name(active & ~log_active);
*mask = log_active;
}
/* Any active algorithm(s) which are not supported must be removed */
if (active & ~supported) {
log_warning("TPM active algorithm(s) unsupported by u-boot\n");
tpm2_print_selected_algorithm_name(active & ~supported);
if (*mask)
*mask = active & supported & *mask;
else
*mask = active & supported;
}
return rc;
+}
+static int tpm2_pcr_allocate(struct udevice *dev, u32 algo_mask) +{
struct tpml_pcr_selection pcr = { 0 };
u32 pcr_len = 0;
int rc;
rc = tpm2_get_pcr_info(dev, &pcr);
if (rc)
return rc;
rc = tpm2_pcr_config_algo(dev, algo_mask, &pcr, &pcr_len);
if (rc)
return rc;
/* Assume no password */
rc = tpm2_send_pcr_allocate(dev, NULL, 0, &pcr, pcr_len);
if (rc)
return rc;
/* Send TPM2_Shutdown, assume mode = TPM2_SU_CLEAR */
return tpm2_startup(dev, false, TPM2_SU_CLEAR);
+}
+int tpm2_activate_banks(struct udevice *dev, u32 log_active) +{
u32 algo_mask = 0;
int rc;
rc = tpm2_scan_masks(dev, log_active, &algo_mask);
if (rc)
return rc;
if (algo_mask) {
if (!IS_ENABLED(CONFIG_TPM_PCR_ALLOCATE))
return -1;
rc = tpm2_pcr_allocate(dev, algo_mask);
if (rc)
return rc;
log_info("PCR allocate done, shutdown TPM and reboot\n");
do_reset(NULL, 0, 0, NULL);
log_err("reset does not work!\n");
return -1;
}
return 0;
+}
u32 tpm2_startup(struct udevice *dev, bool bon, enum tpm2_startup_types mode) { int op = bon ? TPM2_CC_STARTUP : TPM2_CC_SHUTDOWN; -- 2.25.1
Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org

Get eventlog bank mask when parsing the eventlog from previous boot stage and invoke PCR allocate function based on it. PCR allocate will be proceeded if any dismatches observed among the active, supportted and eventlog bank masks to re-configurate the TPM with the proper algorithms and reboot the system.
Signed-off-by: Raymond Mao raymond.mao@linaro.org Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org --- changes in v2 - Replaced with the renamed API by patch 3/6. changes in v3 - None.
lib/tpm_tcg2.c | 52 +++++++++++++++++--------------------------------- 1 file changed, 17 insertions(+), 35 deletions(-)
diff --git a/lib/tpm_tcg2.c b/lib/tpm_tcg2.c index adb32a14f0..c314b401d0 100644 --- a/lib/tpm_tcg2.c +++ b/lib/tpm_tcg2.c @@ -359,12 +359,12 @@ static int tcg2_replay_eventlog(struct tcg2_event_log *elog, return 0; }
-static int tcg2_log_parse(struct udevice *dev, struct tcg2_event_log *elog) +static int tcg2_log_parse(struct udevice *dev, struct tcg2_event_log *elog, + u32 *log_active) { struct tpml_digest_values digest_list; struct tcg_efi_spec_id_event *event; struct tcg_pcr_event *log; - u32 log_active; u32 calc_size; u32 active; u32 count; @@ -375,6 +375,8 @@ static int tcg2_log_parse(struct udevice *dev, struct tcg2_event_log *elog) int rc; u32 i;
+ *log_active = 0; + if (elog->log_size <= offsetof(struct tcg_pcr_event, event)) return 0;
@@ -420,7 +422,6 @@ static int tcg2_log_parse(struct udevice *dev, struct tcg2_event_log *elog) * algorithms, so just check the EvenLog against the TPM active ones. */ digest_list.count = 0; - log_active = 0; for (i = 0; i < count; ++i) { algo = get_unaligned_le16(&event->digest_sizes[i].algorithm_id); mask = tcg2_algorithm_to_mask(algo); @@ -446,17 +447,15 @@ static int tcg2_log_parse(struct udevice *dev, struct tcg2_event_log *elog) algo); return -1; } - log_active |= mask; + *log_active |= mask; }
rc = tcg2_get_active_pcr_banks(dev, &active); if (rc) return rc; /* If the EventLog and active algorithms don't match exit */ - if (log_active != active) { - log_err("EventLog doesn't contain all active PCR banks\n"); - return -1; - } + if (*log_active != active) + return -ERESTARTSYS;
/* Read PCR0 to check if previous firmware extended the PCRs or not. */ rc = tcg2_pcr_read(dev, 0, &digest_list); @@ -553,43 +552,21 @@ int tcg2_log_prepare_buffer(struct udevice *dev, struct tcg2_event_log *elog, bool ignore_existing_log) { struct tcg2_event_log log; - int rc, i; + int rc; + u32 log_active = 0;
elog->log_position = 0; elog->found = false;
- /* - * Make sure U-Boot is compiled with all the active PCRs - * since we are about to create an EventLog and we won't - * measure anything if the PCR banks don't match - */ - if (!tpm2_check_active_banks(dev)) { - log_err("Cannot create EventLog\n"); - log_err("Mismatch between U-Boot and TPM hash algos\n"); - log_info("TPM:\n"); - tpm2_print_active_banks(dev); - log_info("U-Boot:\n"); - for (i = 0; i < ARRAY_SIZE(hash_algo_list); i++) { - const struct digest_info *algo = &hash_algo_list[i]; - const char *str; - - if (!algo->supported) - continue; - - str = tpm2_algorithm_name(algo->hash_alg); - if (str) - log_info("%s\n", str); - } - return -EINVAL; - } - rc = tcg2_platform_get_log(dev, (void **)&log.log, &log.log_size); if (!rc) { log.log_position = 0; log.found = false;
if (!ignore_existing_log) { - rc = tcg2_log_parse(dev, &log); + rc = tcg2_log_parse(dev, &log, &log_active); + if (rc == -ERESTARTSYS && log_active) + goto pcr_allocate; if (rc) return rc; } @@ -616,6 +593,11 @@ int tcg2_log_prepare_buffer(struct udevice *dev, struct tcg2_event_log *elog, elog->found = log.found; }
+pcr_allocate: + rc = tpm2_activate_banks(dev, log_active); + if (rc) + return rc; + /* * Initialize the log buffer if no log was discovered and the buffer is * valid. User's can pass in their own buffer as a fallback if no

During PCR extend process, if any unsupported algorithms are active, try to use PCR allocate to inactivate them.
Signed-off-by: Raymond Mao raymond.mao@linaro.org Acked-by: Ilias Apalodimas ilias.apalodimas@linaro.org --- changes in v2 - None. changes in v3 - None.
lib/tpm-v2.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c index aa02a9e961..9ca7933c09 100644 --- a/lib/tpm-v2.c +++ b/lib/tpm-v2.c @@ -344,7 +344,10 @@ u32 tpm2_pcr_extend(struct udevice *dev, u32 index, u32 algorithm,
if (!tpm2_check_active_banks(dev)) { log_err("Cannot extend PCRs if all the TPM enabled algorithms are not supported\n"); - return -EINVAL; + + ret = tpm2_pcr_allocate(dev, 0); + if (ret) + return -EINVAL; } /* * Fill the command structure starting from the first buffer:

On Mon, 27 Jan 2025 at 16:59, Raymond Mao raymond.mao@linaro.org wrote:
During PCR extend process, if any unsupported algorithms are active, try to use PCR allocate to inactivate them.
Signed-off-by: Raymond Mao raymond.mao@linaro.org Acked-by: Ilias Apalodimas ilias.apalodimas@linaro.org
changes in v2
- None.
changes in v3
- None.
lib/tpm-v2.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c index aa02a9e961..9ca7933c09 100644 --- a/lib/tpm-v2.c +++ b/lib/tpm-v2.c @@ -344,7 +344,10 @@ u32 tpm2_pcr_extend(struct udevice *dev, u32 index, u32 algorithm,
if (!tpm2_check_active_banks(dev)) { log_err("Cannot extend PCRs if all the TPM enabled algorithms are not supported\n");
return -EINVAL;
ret = tpm2_pcr_allocate(dev, 0);
if (ret)
return -EINVAL; } /* * Fill the command structure starting from the first buffer:
-- 2.25.1
Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org

Enable PCR allocate and system reboot when algorithms don't match among the values from TPM device, U-Boot support and evenlog.
Signed-off-by: Raymond Mao raymond.mao@linaro.org Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org --- changes in v2 - None. changes in v3 - None.
configs/qemu_arm64_defconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/configs/qemu_arm64_defconfig b/configs/qemu_arm64_defconfig index 0c7107c1f4..6ba3cb1bbd 100644 --- a/configs/qemu_arm64_defconfig +++ b/configs/qemu_arm64_defconfig @@ -73,3 +73,4 @@ CONFIG_SEMIHOSTING=y CONFIG_MBEDTLS_LIB=y CONFIG_TPM=y CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE=y +CONFIG_TPM_PCR_ALLOCATE=y
participants (2)
-
Ilias Apalodimas
-
Raymond Mao