diff options
-rw-r--r-- | cmd/tpm-v2.c | 128 | ||||
-rw-r--r-- | common/bloblist.c | 17 | ||||
-rw-r--r-- | configs/qemu_arm64_defconfig | 1 | ||||
-rw-r--r-- | doc/usage/measured_boot.rst | 1 | ||||
-rw-r--r-- | drivers/tpm/Kconfig | 9 | ||||
-rw-r--r-- | include/bloblist.h | 18 | ||||
-rw-r--r-- | include/efi_tcg2.h | 2 | ||||
-rw-r--r-- | include/tpm-v2.h | 53 | ||||
-rw-r--r-- | lib/Kconfig | 12 | ||||
-rw-r--r-- | lib/efi_loader/Kconfig | 9 | ||||
-rw-r--r-- | lib/efi_loader/efi_tcg2.c | 15 | ||||
-rw-r--r-- | lib/tpm-v2.c | 259 | ||||
-rw-r--r-- | lib/tpm_api.c | 4 | ||||
-rw-r--r-- | lib/tpm_tcg2.c | 111 | ||||
-rw-r--r-- | test/common/bloblist.c | 4 |
15 files changed, 556 insertions, 87 deletions
diff --git a/cmd/tpm-v2.c b/cmd/tpm-v2.c index 8517833f861..a62862e94f9 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, @@ -226,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[]) { @@ -395,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) @@ -420,11 +527,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" @@ -473,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/common/bloblist.c b/common/bloblist.c index 110bb9dc44a..ab48a3cdb98 100644 --- a/common/bloblist.c +++ b/common/bloblist.c @@ -223,13 +223,26 @@ 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/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 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/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/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/include/tpm-v2.h b/include/tpm-v2.h index 65681464b37..ece422df0c7 100644 --- a/include/tpm-v2.h +++ b/include/tpm-v2.h @@ -230,6 +230,8 @@ 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, }; /** @@ -430,7 +432,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. @@ -702,6 +704,55 @@ 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 + * + * @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_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. * If a testable function has not been tested and is * requested the TPM2 will return TPM_RC_NEEDS_TEST. 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/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; diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c index bc750b7ca19..9ca7933c094 100644 --- a/lib/tpm-v2.c +++ b/lib/tpm-v2.c @@ -44,12 +44,134 @@ static int tpm2_update_active_banks(struct udevice *dev) return 0; } -u32 tpm2_startup(struct udevice *dev, enum tpm2_startup_types mode) +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; 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 +181,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 +206,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; @@ -222,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: @@ -399,6 +524,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) - 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; } diff --git a/lib/tpm_tcg2.c b/lib/tpm_tcg2.c index 4134d93a358..c314b401d0b 100644 --- a/lib/tpm_tcg2.c +++ b/lib/tpm_tcg2.c @@ -19,6 +19,7 @@ #include <linux/unaligned/generic.h> #include <linux/unaligned/le_byteshift.h> #include "tpm-utils.h" +#include <bloblist.h> int tcg2_get_pcr_info(struct udevice *dev, u32 *supported_bank, u32 *active_bank, u32 *bank_num) @@ -358,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; @@ -374,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; @@ -419,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); @@ -445,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); @@ -552,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; } @@ -615,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 @@ -672,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)); @@ -694,22 +698,19 @@ __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; + return 0; + } - a = ofnode_get_addr_size(args.node, "reg", &s); - if (a == FDT_ADDR_T_NONE) - return -ENOMEM; + if (dev_read_phandle_with_args(dev, "memory-region", NULL, 0, 0, &args)) + return -ENODEV; - *addr = map_physmem(a, s, MAP_NOCACHE); - *size = (u32)s; - } + 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; } 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)); |