From 6d8e52a6e350e1dbf450d02fccdb2ac2b0c036e4 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Mon, 27 Jan 2025 06:58:46 -0800 Subject: [PATCH 1/9] tpm: add TPM2_Shutdown command 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 Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- 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 8517833f861..a6d57ee7c49 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 \n" +"startup []\n" " Issue a TPM2_Startup command.\n" " is one of:\n" " * TPM2_SU_CLEAR (reset state)\n" " * TPM2_SU_STATE (preserved state)\n" +" :\n" +" * off - To shutdown the TPM\n" "self_test \n" " Test the TPM capabilities.\n" " is one of:\n" diff --git a/include/tpm-v2.h b/include/tpm-v2.h index 65681464b37..f66a8e1bf41 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 bc750b7ca19..3946ac2dbb7 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 39a5121e302..576d601e5ff 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; } From 9d2bc92ba7bba25bb7827848aa4c51534aff904c Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Mon, 27 Jan 2025 06:58:47 -0800 Subject: [PATCH 2/9] tpm: add TPM2_PCR_Allocate command 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 ` 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 Acked-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- 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 a6d57ee7c49..a62862e94f9 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", " : index of the PCR\n" " : secret to protect the access of PCR #\n" " : optional password of the PLATFORM hierarchy\n" +"pcr_allocate []\n" +" Issue a TPM2_PCR_Allocate Command to reconfig PCR bank algorithm.\n" +" is one of:\n" +" * sha1\n" +" * sha256\n" +" * sha384\n" +" * sha512\n" +" 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" +" : optional password\n" ); diff --git a/include/tpm-v2.h b/include/tpm-v2.h index f66a8e1bf41..af3158f6e4f 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 3946ac2dbb7..045b5dd9ebd 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 */ + /* (if any) */ + 0, /* Attributes: Cont/Excl/Rst */ + tpm_u16(pw_sz), /* Size of */ + /* STRING(pw) (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) - From b6228b2e785df93fa4a1176d374976a9e2205924 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Mon, 27 Jan 2025 06:58:48 -0800 Subject: [PATCH 3/9] tpm: add wrapper and helper APIs for PCR allocate 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 Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- 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 af3158f6e4f..ece422df0c7 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 0a295161385..b27965fc480 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 045b5dd9ebd..aa02a9e9611 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; From d487c3283c83d1314f9736bdfc69391dbc347154 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Mon, 27 Jan 2025 06:58:49 -0800 Subject: [PATCH 4/9] tpm: add PCR allocate into the eventlog handling 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 Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- 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 4134d93a358..1175d08abc5 100644 --- a/lib/tpm_tcg2.c +++ b/lib/tpm_tcg2.c @@ -358,12 +358,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; @@ -374,6 +374,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; @@ -419,7 +421,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); @@ -445,17 +446,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); @@ -552,43 +551,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; } @@ -615,6 +592,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 From e7e166dabf129aae1fc57fe637ffc37d022ea45a Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Mon, 27 Jan 2025 06:58:50 -0800 Subject: [PATCH 5/9] tpm: PCR allocate during PCR extend to disable the unsupported algorithms During PCR extend process, if any unsupported algorithms are active, try to use PCR allocate to inactivate them. Signed-off-by: Raymond Mao Acked-by: Ilias Apalodimas Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- 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 aa02a9e9611..9ca7933c094 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: From b8eaa52612bf34164e7f495a094a5a419903a3ed Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Mon, 27 Jan 2025 06:58:51 -0800 Subject: [PATCH 6/9] board: qemu-arm: select TPM_PCR_ALLOCATE 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 Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- configs/qemu_arm64_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/qemu_arm64_defconfig b/configs/qemu_arm64_defconfig index 6f5a86f7a96..763fc14468a 100644 --- a/configs/qemu_arm64_defconfig +++ b/configs/qemu_arm64_defconfig @@ -75,3 +75,4 @@ CONFIG_SEMIHOSTING=y CONFIG_MBEDTLS_LIB=y CONFIG_TPM=y CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE=y +CONFIG_TPM_PCR_ALLOCATE=y From bb894c5da5151e0858e217407c0cdeb989725a4a Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Mon, 27 Jan 2025 06:49:34 -0800 Subject: [PATCH 7/9] bloblist: add api to get blob with size bloblist_find function only returns the pointer of blob data, which is fine for those self-describing data like FDT. But as a common scenario, an interface is needed to retrieve both the pointer and the size of the blob data. Add a few ut test cases for the new api. Signed-off-by: Raymond Mao Reviewed-by: Simon Glass Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- common/bloblist.c | 17 +++++++++++++++-- include/bloblist.h | 18 ++++++++++++++++++ test/common/bloblist.c | 4 ++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/common/bloblist.c b/common/bloblist.c index 110bb9dc44a..ab48a3cdb98 100644 --- a/common/bloblist.c +++ b/common/bloblist.c @@ -222,14 +222,27 @@ static int bloblist_ensurerec(uint tag, struct bloblist_rec **recp, int size, } void *bloblist_find(uint tag, int size) +{ + void *blob = NULL; + int blob_size; + + blob = bloblist_get_blob(tag, &blob_size); + + if (size && size != blob_size) + return NULL; + + return blob; +} + +void *bloblist_get_blob(uint tag, int *sizep) { struct bloblist_rec *rec; rec = bloblist_findrec(tag); if (!rec) return NULL; - if (size && size != rec->size) - return NULL; + + *sizep = rec->size; return (void *)rec + rec_hdr_size(rec); } diff --git a/include/bloblist.h b/include/bloblist.h index f999391f74b..52ba0ddcf84 100644 --- a/include/bloblist.h +++ b/include/bloblist.h @@ -250,6 +250,24 @@ static inline void *bloblist_check_magic(ulong addr) return ptr; } +#if CONFIG_IS_ENABLED(BLOBLIST) +/** + * bloblist_get_blob() - Find a blob and get the size of it + * + * Searches the bloblist and returns the blob with the matching tag + * + * @tag: Tag to search for (enum bloblist_tag_t) + * @sizep: Size of the blob found + * Return: pointer to bloblist if found, or NULL if not found + */ +void *bloblist_get_blob(uint tag, int *sizep); +#else +static inline void *bloblist_get_blob(uint tag, int *sizep) +{ + return NULL; +} +#endif + /** * bloblist_find() - Find a blob * diff --git a/test/common/bloblist.c b/test/common/bloblist.c index 9467abfa8e1..ab8f41c6808 100644 --- a/test/common/bloblist.c +++ b/test/common/bloblist.c @@ -98,10 +98,12 @@ static int bloblist_test_blob(struct unit_test_state *uts) struct bloblist_hdr *hdr; struct bloblist_rec *rec, *rec2; char *data; + int size = 0; /* At the start there should be no records */ hdr = clear_bloblist(); ut_assertnull(bloblist_find(TEST_TAG, TEST_BLOBLIST_SIZE)); + ut_assertnull(bloblist_get_blob(TEST_TAG, &size)); ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0)); ut_asserteq(sizeof(struct bloblist_hdr), bloblist_get_size()); ut_asserteq(TEST_BLOBLIST_SIZE, bloblist_get_total_size()); @@ -114,6 +116,8 @@ static int bloblist_test_blob(struct unit_test_state *uts) ut_asserteq_addr(rec + 1, data); data = bloblist_find(TEST_TAG, TEST_SIZE); ut_asserteq_addr(rec + 1, data); + ut_asserteq_addr(bloblist_get_blob(TEST_TAG, &size), data); + ut_asserteq(size, TEST_SIZE); /* Check the data is zeroed */ ut_assertok(check_zero(data, TEST_SIZE)); From afe26a74ddfe183b7ea76d5b36d33d2318d02c28 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Mon, 27 Jan 2025 06:49:35 -0800 Subject: [PATCH 8/9] tcg2: decouple eventlog size from efi Move default eventlog size from efi to tpm for using in both efi and measured boot. Signed-off-by: Raymond Mao Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- doc/usage/measured_boot.rst | 1 - drivers/tpm/Kconfig | 9 +++++++++ include/efi_tcg2.h | 2 -- lib/efi_loader/Kconfig | 9 --------- lib/efi_loader/efi_tcg2.c | 15 ++++++++------- 5 files changed, 17 insertions(+), 19 deletions(-) diff --git a/doc/usage/measured_boot.rst b/doc/usage/measured_boot.rst index 05c439e9ac6..488dd546f13 100644 --- a/doc/usage/measured_boot.rst +++ b/doc/usage/measured_boot.rst @@ -24,7 +24,6 @@ Requirements * A hardware TPM 2.0 supported by an enabled U-Boot driver * CONFIG_EFI_TCG2_PROTOCOL=y -* CONFIG_EFI_TCG2_PROTOCOL_EVENTLOG_SIZE=y * optional CONFIG_EFI_TCG2_PROTOCOL_MEASURE_DTB=y will measure the loaded DTB in PCR 1 diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig index d59102d9a6b..01bc686d367 100644 --- a/drivers/tpm/Kconfig +++ b/drivers/tpm/Kconfig @@ -209,6 +209,15 @@ config TPM2_MMIO to the device using the standard TPM Interface Specification (TIS) protocol. +config TPM2_EVENT_LOG_SIZE + int "EventLog size" + depends on TPM_V2 + default 65536 + help + Define the size of the EventLog. Note that this is going to be + allocated twice. One for the eventlog it self and one for the + configuration table that is required from the TCG2 spec + endif # TPM_V2 endmenu diff --git a/include/efi_tcg2.h b/include/efi_tcg2.h index 8dfb1bc9527..7ed88809913 100644 --- a/include/efi_tcg2.h +++ b/include/efi_tcg2.h @@ -28,8 +28,6 @@ #define EFI_TCG2_MAX_PCR_INDEX 23 #define EFI_TCG2_FINAL_EVENTS_TABLE_VERSION 1 -#define TPM2_EVENT_LOG_SIZE CONFIG_EFI_TCG2_PROTOCOL_EVENTLOG_SIZE - typedef u32 efi_tcg_event_log_bitmap; typedef u32 efi_tcg_event_log_format; typedef u32 efi_tcg_event_algorithm_bitmap; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index ad0bbdd8a77..d4f6b56afaa 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -438,15 +438,6 @@ config EFI_TCG2_PROTOCOL Provide a EFI_TCG2_PROTOCOL implementation using the TPM hardware of the platform. -config EFI_TCG2_PROTOCOL_EVENTLOG_SIZE - int "EFI_TCG2_PROTOCOL EventLog size" - depends on EFI_TCG2_PROTOCOL - default 65536 - help - Define the size of the EventLog for EFI_TCG2_PROTOCOL. Note that - this is going to be allocated twice. One for the eventlog it self - and one for the configuration table that is required from the spec - config EFI_TCG2_PROTOCOL_MEASURE_DTB bool "Measure DTB with EFI_TCG2_PROTOCOL" depends on EFI_TCG2_PROTOCOL diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c index c697b53441a..210a846ebc8 100644 --- a/lib/efi_loader/efi_tcg2.c +++ b/lib/efi_loader/efi_tcg2.c @@ -112,7 +112,7 @@ static efi_status_t tcg2_agile_log_append(u32 pcr_index, u32 event_type, /* if ExitBootServices hasn't been called update the normal log */ if (!event_log.ebs_called) { if (event_log.truncated || - event_log.pos + event_size > TPM2_EVENT_LOG_SIZE) { + event_log.pos + event_size > CONFIG_TPM2_EVENT_LOG_SIZE) { event_log.truncated = true; return EFI_VOLUME_FULL; } @@ -125,7 +125,7 @@ static efi_status_t tcg2_agile_log_append(u32 pcr_index, u32 event_type, return ret; /* if GetEventLog has been called update FinalEventLog as well */ - if (event_log.final_pos + event_size > TPM2_EVENT_LOG_SIZE) + if (event_log.final_pos + event_size > CONFIG_TPM2_EVENT_LOG_SIZE) return EFI_VOLUME_FULL; log = (void *)((uintptr_t)event_log.final_buffer + event_log.final_pos); @@ -823,12 +823,12 @@ static efi_status_t create_final_event(void) * EFI_TCG2_GET_EVENT_LOGS need to be stored in an instance of an * EFI_CONFIGURATION_TABLE */ - ret = efi_allocate_pool(EFI_ACPI_MEMORY_NVS, TPM2_EVENT_LOG_SIZE, + ret = efi_allocate_pool(EFI_ACPI_MEMORY_NVS, CONFIG_TPM2_EVENT_LOG_SIZE, &event_log.final_buffer); if (ret != EFI_SUCCESS) goto out; - memset(event_log.final_buffer, 0xff, TPM2_EVENT_LOG_SIZE); + memset(event_log.final_buffer, 0xff, CONFIG_TPM2_EVENT_LOG_SIZE); final_event = event_log.final_buffer; final_event->number_of_events = 0; final_event->version = EFI_TCG2_FINAL_EVENTS_TABLE_VERSION; @@ -914,7 +914,8 @@ static efi_status_t efi_init_event_log(void) if (tcg2_platform_get_tpm2(&dev)) return EFI_DEVICE_ERROR; - ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, TPM2_EVENT_LOG_SIZE, + ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, + CONFIG_TPM2_EVENT_LOG_SIZE, (void **)&event_log.buffer); if (ret != EFI_SUCCESS) return ret; @@ -923,7 +924,7 @@ static efi_status_t efi_init_event_log(void) * initialize log area as 0xff so the OS can easily figure out the * last log entry */ - memset(event_log.buffer, 0xff, TPM2_EVENT_LOG_SIZE); + memset(event_log.buffer, 0xff, CONFIG_TPM2_EVENT_LOG_SIZE); /* * The log header is defined to be in SHA1 event log entry format. @@ -940,7 +941,7 @@ static efi_status_t efi_init_event_log(void) * platforms can use different ways to do so. */ elog.log = event_log.buffer; - elog.log_size = TPM2_EVENT_LOG_SIZE; + elog.log_size = CONFIG_TPM2_EVENT_LOG_SIZE; rc = tcg2_log_prepare_buffer(dev, &elog, false); if (rc) { ret = (rc == -ENOBUFS) ? EFI_BUFFER_TOO_SMALL : EFI_DEVICE_ERROR; From 8895ff8ae2186b53b4a073966ef16b09c12a69b8 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Mon, 27 Jan 2025 06:49:36 -0800 Subject: [PATCH 9/9] tpm: get tpm event log from bloblist Get tpm event log from bloblist instead of FDT when bloblist is enabled and valid from previous boot stage. Signed-off-by: Raymond Mao Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- lib/tpm_tcg2.c | 61 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/lib/tpm_tcg2.c b/lib/tpm_tcg2.c index 1175d08abc5..c314b401d0b 100644 --- a/lib/tpm_tcg2.c +++ b/lib/tpm_tcg2.c @@ -19,6 +19,7 @@ #include #include #include "tpm-utils.h" +#include int tcg2_get_pcr_info(struct udevice *dev, u32 *supported_bank, u32 *active_bank, u32 *bank_num) @@ -654,21 +655,42 @@ void tcg2_measurement_term(struct udevice *dev, struct tcg2_event_log *elog, __weak int tcg2_platform_get_log(struct udevice *dev, void **addr, u32 *size) { - const __be32 *addr_prop; - const __be32 *size_prop; + const __be32 *addr_prop = NULL; + const __be32 *size_prop = NULL; int asize; int ssize; + struct ofnode_phandle_args args; + phys_addr_t a; + fdt_size_t s; *addr = NULL; *size = 0; - addr_prop = dev_read_prop(dev, "tpm_event_log_addr", &asize); - if (!addr_prop) - addr_prop = dev_read_prop(dev, "linux,sml-base", &asize); + *addr = bloblist_get_blob(BLOBLISTT_TPM_EVLOG, size); + if (*addr && *size) { + *addr = map_physmem((uintptr_t)(*addr), *size, MAP_NOCACHE); + return 0; + } - size_prop = dev_read_prop(dev, "tpm_event_log_size", &ssize); - if (!size_prop) + /* + * TODO: + * Replace BLOBLIST with a new kconfig for handoff all components + * (fdt, tpm event log, etc...) from previous boot stage via bloblist + * mandatorily following Firmware Handoff spec. + */ + if (!CONFIG_IS_ENABLED(BLOBLIST)) { + addr_prop = dev_read_prop(dev, "tpm_event_log_addr", &asize); + size_prop = dev_read_prop(dev, "tpm_event_log_size", &ssize); + } + + /* + * If no eventlog was observed, a sml buffer is required for the kernel + * to discover the eventlog. + */ + if (!addr_prop || !size_prop) { + addr_prop = dev_read_prop(dev, "linux,sml-base", &asize); size_prop = dev_read_prop(dev, "linux,sml-size", &ssize); + } if (addr_prop && size_prop) { u64 a = of_read_number(addr_prop, asize / sizeof(__be32)); @@ -676,23 +698,20 @@ __weak int tcg2_platform_get_log(struct udevice *dev, void **addr, u32 *size) *addr = map_physmem(a, s, MAP_NOCACHE); *size = (u32)s; - } else { - struct ofnode_phandle_args args; - phys_addr_t a; - fdt_size_t s; - if (dev_read_phandle_with_args(dev, "memory-region", NULL, 0, - 0, &args)) - return -ENODEV; - - a = ofnode_get_addr_size(args.node, "reg", &s); - if (a == FDT_ADDR_T_NONE) - return -ENOMEM; - - *addr = map_physmem(a, s, MAP_NOCACHE); - *size = (u32)s; + return 0; } + if (dev_read_phandle_with_args(dev, "memory-region", NULL, 0, 0, &args)) + return -ENODEV; + + a = ofnode_get_addr_size(args.node, "reg", &s); + if (a == FDT_ADDR_T_NONE) + return -ENOMEM; + + *addr = map_physmem(a, s, MAP_NOCACHE); + *size = (u32)s; + return 0; }