bootX measurements and measurement API moved to u-boot core

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEgWII69YpahbL5iK5gS8AYozs+qIFAmU7j50ACgkQgS8AYozs
 +qIh8w/+O4UjT0sG1NLwmyV7U1Ypk+EyYaE6wmSWzpsJLfH/YvtObBJOYRuXxRVh
 J9lkgCsw8Ct1ZNCrp8iVO+Dz1DtV8+QvTecrUHZqcOhTYDaqxXnlvEH2/EUhgo5T
 9a/ZaDtOP1mKz754C4G6G363+iRCvbcqDECeKg9VYxfWCo1cINOmqyQCqlGxFT+h
 PKiB5VzUpN/K/yiie+Hr42/+6XaykAUjUvEWeyKOsRmYY4lNiK22vG/puE42bFTh
 catXwTE2a7x+yzPKkdhR0UGvDUlIKET2kF6mi+pYN2h/cSUxWTzbP/OxcU9yJOnm
 qJiRZ+Woez1I7ul6ln4ci2kiWc3CTYFXfctwrBJPuJ/EO+2EEb3oHqG2S3Fc9VBZ
 N17flHW7XZHEQbNexlUhk9cRpCwRuSA5OJXwW+IZIuydgNeo3xF0iYvipbjkEGgW
 BBkt8PH+ivTLjEz6Gcmquvo1fHGJLHRIPg7DNb0phGHviuC0zlDJ7N5DZk0CpkiT
 36siV9xK4X6qvWkOTa6Ldw60e4tN9nv3VG30uXtPHi3XdOkKfNkyIuqO/5BkkQPt
 6yEc9IYXYoWNKDVUGme5+xszZp1sSvqltajG9VVNupt958dFyOSgS5aNa6B4UsWX
 3XfndP1/s2bezUHoQx5zjraapKVrqBFLkGeTlCDUD+mEgP440G8=
 =gvDs
 -----END PGP SIGNATURE-----

Merge tag 'tpm-next-27102023' of https://source.denx.de/u-boot/custodians/u-boot-tpm

bootX measurements and measurement API moved to u-boot core:

Up to now, U-Boot could perform measurements and EventLog creation as
described by the TCG spec when booting via EFI.

The EFI code was residing in lib/efi_loader/efi_tcg2.c and contained
both EFI specific code + the API needed to access the TPM, extend PCRs
and create an EventLog. The non-EFI part proved modular enough and
moving it around to the TPM subsystem was straightforward.

With that in place we can have a common API for measuring binaries
regardless of the boot command, EFI or boot(m|i|z), and contructing an
EventLog.

I've tested all of the EFI cases -- booting with an empty EventLog and
booting with a previous stage loader providing one and found no
regressions.  Eddie tested the bootX part.

Eddie also fixed the sandbox TPM which couldn't be used for the EFI code
and it now supports all the required capabilities. This had a slight
sideeffect in our testing since the EFI subsystem initializes the TPM
early and 'tpm2 init' failed during some python tests. That code only
opens the device though, so we can replace it with 'tpm2 autostart'
which doesn't error out and still allows you to perfom the rest of the
tests but doesn't report an error if the device is already opened.

There's a few minor issues with this PR as well but since testing and
verifying the changes takes a considerable amount of time, I prefer
merging it now.

Heinrich has already sent a PR for -master containing "efi_loader: fix
EFI_ENTRY point on get_active_pcr_banks" and I am not sure if that will
cause any conflicts, but in any case they should be trivial to resolve.

Both the EFI and non-EFI code have a Kconfig for measuring the loaded
Device Tree.  The reason this is optional is that we can't reason
when/if devices add random info like kaslr-seed, mac addresses etc in
the DT. In that case measurements are random, board specific and
eventually useless.  The reason it was difficult to fix it prior to this
patchset is because the EFI subsystem and thus measurements was brought
up late and DT fixups might have already been applied. With this
patchset we can measure the DT really early in the future.

Heinrich also pointed out that the two Kconfigs for the DTB measurements
can be squashed in a single one and that the documentation only explains
the non-EFI case.  I agree on both but as I said this is a sane working
version, so let's pull this first it's aleady big enough and painful to
test.
This commit is contained in:
Tom Rini 2023-10-27 19:27:29 -04:00
commit d5d9770f58
24 changed files with 1490 additions and 1062 deletions

View File

@ -4,11 +4,23 @@
* and sandbox64 builds.
*/
#include <config.h>
#include <dt-bindings/input/input.h>
#define USB_CLASS_HUB 9
/ {
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
event_log: tcg_event_log {
no-map;
reg = <(CFG_SYS_SDRAM_SIZE - 0x2000) 0x2000>;
};
};
binman {
};
@ -342,6 +354,7 @@
tpm2 {
compatible = "sandbox,tpm2";
memory-region = <&event_log>;
};
triangle {

View File

@ -9,6 +9,7 @@
/dts-v1/;
#include <config.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/gpio/sandbox-gpio.h>
#include <dt-bindings/input/input.h>
@ -69,6 +70,17 @@
osd0 = "/osd";
};
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
event_log: tcg_event_log {
no-map;
reg = <(CFG_SYS_SDRAM_SIZE - 0x2000) 0x2000>;
};
};
binman: binman {
};
@ -1436,6 +1448,7 @@
tpm2 {
compatible = "sandbox,tpm2";
memory-region = <&event_log>;
};
tpm {

View File

@ -685,6 +685,38 @@ config LEGACY_IMAGE_FORMAT
loaded. If a board needs the legacy image format support in this
case, enable it here.
config MEASURED_BOOT
bool "Measure boot images and configuration when booting without EFI"
depends on HASH && TPM_V2
help
This option enables measurement of the boot process when booting
without UEFI . Measurement involves creating cryptographic hashes
of the binary images that are booting and storing them in the TPM.
In addition, a log of these hashes is stored in memory for the OS
to verify the booted images and configuration. Enable this if the
OS has configured some memory area for the event log and you intend
to use some attestation tools on your system.
if MEASURED_BOOT
config MEASURE_DEVICETREE
bool "Measure the devicetree image"
default y if MEASURED_BOOT
help
On some platforms, the devicetree is not static as it may contain
random MAC addresses or other such data that changes each boot.
Therefore, it should not be measured into the TPM. In that case,
disable the measurement here.
config MEASURE_IGNORE_LOG
bool "Ignore the existing event log"
default n
help
On platforms that use an event log memory region that persists
through system resets and are the first stage bootloader, then
this option should be enabled to ignore any existing data in the
event log memory region.
endif # MEASURED_BOOT
config SUPPORT_RAW_INITRD
bool "Enable raw initrd images"
help

View File

@ -23,6 +23,7 @@
#include <asm/global_data.h>
#include <asm/io.h>
#include <linux/sizes.h>
#include <tpm-v2.h>
#if defined(CONFIG_CMD_USB)
#include <usb.h>
#endif
@ -673,6 +674,75 @@ int bootm_process_cmdline_env(int flags)
return 0;
}
int bootm_measure(struct bootm_headers *images)
{
int ret = 0;
/* Skip measurement if EFI is going to do it */
if (images->os.os == IH_OS_EFI &&
IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL) &&
IS_ENABLED(CONFIG_BOOTM_EFI))
return ret;
if (IS_ENABLED(CONFIG_MEASURED_BOOT)) {
struct tcg2_event_log elog;
struct udevice *dev;
void *initrd_buf;
void *image_buf;
const char *s;
u32 rd_len;
bool ign;
elog.log_size = 0;
ign = IS_ENABLED(CONFIG_MEASURE_IGNORE_LOG);
ret = tcg2_measurement_init(&dev, &elog, ign);
if (ret)
return ret;
image_buf = map_sysmem(images->os.image_start,
images->os.image_len);
ret = tcg2_measure_data(dev, &elog, 8, images->os.image_len,
image_buf, EV_COMPACT_HASH,
strlen("linux") + 1, (u8 *)"linux");
if (ret)
goto unmap_image;
rd_len = images->rd_end - images->rd_start;
initrd_buf = map_sysmem(images->rd_start, rd_len);
ret = tcg2_measure_data(dev, &elog, 9, rd_len, initrd_buf,
EV_COMPACT_HASH, strlen("initrd") + 1,
(u8 *)"initrd");
if (ret)
goto unmap_initrd;
if (IS_ENABLED(CONFIG_MEASURE_DEVICETREE)) {
ret = tcg2_measure_data(dev, &elog, 0, images->ft_len,
(u8 *)images->ft_addr,
EV_TABLE_OF_DEVICES,
strlen("dts") + 1,
(u8 *)"dts");
if (ret)
goto unmap_initrd;
}
s = env_get("bootargs");
if (!s)
s = "";
ret = tcg2_measure_data(dev, &elog, 1, strlen(s) + 1, (u8 *)s,
EV_PLATFORM_CONFIG_FLAGS,
strlen(s) + 1, (u8 *)s);
unmap_initrd:
unmap_sysmem(initrd_buf);
unmap_image:
unmap_sysmem(image_buf);
tcg2_measurement_term(dev, &elog, ret != 0);
}
return ret;
}
/**
* Execute selected states of the bootm command.
*
@ -724,6 +794,10 @@ int do_bootm_states(struct cmd_tbl *cmdtp, int flag, int argc,
if (!ret && (states & BOOTM_STATE_FINDOTHER))
ret = bootm_find_other(cmdtp, flag, argc, argv);
if (IS_ENABLED(CONFIG_MEASURED_BOOT) && !ret &&
(states & BOOTM_STATE_MEASURE))
bootm_measure(images);
/* Load the OS */
if (!ret && (states & BOOTM_STATE_LOADOS)) {
iflag = bootm_disable_interrupts();

View File

@ -127,6 +127,7 @@ int do_booti(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
BOOTM_STATE_RAMDISK |
#endif
BOOTM_STATE_MEASURE |
BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
BOOTM_STATE_OS_GO,
&images, 1);

View File

@ -147,6 +147,8 @@ int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
BOOTM_STATE_OS_GO;
if (IS_ENABLED(CONFIG_SYS_BOOT_RAMDISK_HIGH))
states |= BOOTM_STATE_RAMDISK;
if (IS_ENABLED(CONFIG_MEASURED_BOOT))
states |= BOOTM_STATE_MEASURE;
if (IS_ENABLED(CONFIG_PPC) || IS_ENABLED(CONFIG_MIPS))
states |= BOOTM_STATE_OS_CMDLINE;
ret = do_bootm_states(cmdtp, flag, argc, argv, states, &images, 1);

View File

@ -81,6 +81,7 @@ int do_bootz(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
BOOTM_STATE_RAMDISK |
#endif
BOOTM_STATE_MEASURE |
BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
BOOTM_STATE_OS_GO,
&images, 1);

View File

@ -14,6 +14,7 @@ CONFIG_FIT_RSASSA_PSS=y
CONFIG_FIT_CIPHER=y
CONFIG_FIT_VERBOSE=y
CONFIG_LEGACY_IMAGE_FORMAT=y
CONFIG_MEASURED_BOOT=y
CONFIG_DISTRO_DEFAULTS=y
CONFIG_BOOTSTAGE=y
CONFIG_BOOTSTAGE_REPORT=y

View File

@ -14,6 +14,7 @@ Use U-Boot
partitions
cmdline
semihosting
measured_boot
Shell commands
--------------

View File

@ -0,0 +1,31 @@
.. SPDX-License-Identifier: GPL-2.0+
Measured Boot
=====================
U-Boot can perform a measured boot, the process of hashing various components
of the boot process, extending the results in the TPM and logging the
component's measurement in memory for the operating system to consume.
By default, U-Boot will measure the operating system (linux) image, the
initrd image, and the "bootargs" environment variable. By enabling
CONFIG_MEASURE_DEVICETREE, U-Boot will also measure the devicetree image.
The operating system typically would verify that the hashes found in the
TPM PCRs match the contents of the event log. This can further be checked
against the hash results of previous boots.
Requirements
---------------------
* A hardware TPM 2.0 supported by the U-Boot drivers
* CONFIG_TPM=y
* CONFIG_MEASURED_BOOT=y
* Device-tree configuration of the TPM device to specify the memory area
for event logging. The TPM device node must either contain a phandle to
a reserved memory region or "linux,sml-base" and "linux,sml-size"
indicating the address and size of the memory region. An example can be
found in arch/sandbox/dts/test.dts
* The operating system must also be configured to use the memory regions
specified in the U-Boot device-tree in order to make use of the event
log.

View File

@ -22,11 +22,6 @@ enum tpm2_hierarchy {
TPM2_HIERARCHY_NB,
};
/* Subset of supported capabilities */
enum tpm2_capability {
TPM_CAP_TPM_PROPERTIES = 0x6,
};
/* Subset of supported properties */
#define TPM2_PROPERTIES_OFFSET 0x0000020E
@ -38,7 +33,8 @@ enum tpm2_cap_tpm_property {
TPM2_PROPERTY_NB,
};
#define SANDBOX_TPM_PCR_NB 1
#define SANDBOX_TPM_PCR_NB TPM2_MAX_PCRS
#define SANDBOX_TPM_PCR_SELECT_MAX ((SANDBOX_TPM_PCR_NB + 7) / 8)
/*
* Information about our TPM emulation. This is preserved in the sandbox
@ -433,7 +429,7 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf,
int i, j;
/* TPM2_GetProperty */
u32 capability, property, property_count;
u32 capability, property, property_count, val;
/* TPM2_PCR_Read/Extend variables */
int pcr_index = 0;
@ -542,19 +538,32 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf,
case TPM2_CC_GET_CAPABILITY:
capability = get_unaligned_be32(sent);
sent += sizeof(capability);
if (capability != TPM_CAP_TPM_PROPERTIES) {
printf("Sandbox TPM only support TPM_CAPABILITIES\n");
return TPM2_RC_HANDLE;
}
property = get_unaligned_be32(sent);
sent += sizeof(property);
property -= TPM2_PROPERTIES_OFFSET;
property_count = get_unaligned_be32(sent);
sent += sizeof(property_count);
if (!property_count ||
property + property_count > TPM2_PROPERTY_NB) {
switch (capability) {
case TPM2_CAP_PCRS:
break;
case TPM2_CAP_TPM_PROPERTIES:
if (!property_count) {
rc = TPM2_RC_HANDLE;
return sandbox_tpm2_fill_buf(recv, recv_len,
tag, rc);
}
if (property >= TPM2_PROPERTIES_OFFSET &&
((property - TPM2_PROPERTIES_OFFSET) +
property_count > TPM2_PROPERTY_NB)) {
rc = TPM2_RC_HANDLE;
return sandbox_tpm2_fill_buf(recv, recv_len,
tag, rc);
}
break;
default:
printf("Sandbox TPM2 only supports TPM2_CAP_PCRS or "
"TPM2_CAP_TPM_PROPERTIES\n");
rc = TPM2_RC_HANDLE;
return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
}
@ -578,18 +587,53 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf,
put_unaligned_be32(capability, recv);
recv += sizeof(capability);
/* Give the number of properties that follow */
put_unaligned_be32(property_count, recv);
recv += sizeof(property_count);
switch (capability) {
case TPM2_CAP_PCRS:
/* Give the number of algorithms supported - just SHA256 */
put_unaligned_be32(1, recv);
recv += sizeof(u32);
/* Fill with the properties */
for (i = 0; i < property_count; i++) {
put_unaligned_be32(TPM2_PROPERTIES_OFFSET + property +
i, recv);
recv += sizeof(property);
put_unaligned_be32(tpm->properties[property + i],
recv);
recv += sizeof(property);
/* Give SHA256 algorithm */
put_unaligned_be16(TPM2_ALG_SHA256, recv);
recv += sizeof(u16);
/* Select the PCRs supported */
*recv = SANDBOX_TPM_PCR_SELECT_MAX;
recv++;
/* Activate all the PCR bits */
for (i = 0; i < SANDBOX_TPM_PCR_SELECT_MAX; ++i) {
*recv = 0xff;
recv++;
}
break;
case TPM2_CAP_TPM_PROPERTIES:
/* Give the number of properties that follow */
put_unaligned_be32(property_count, recv);
recv += sizeof(property_count);
/* Fill with the properties */
for (i = 0; i < property_count; i++) {
put_unaligned_be32(property + i, recv);
recv += sizeof(property);
if (property >= TPM2_PROPERTIES_OFFSET) {
val = tpm->properties[(property -
TPM2_PROPERTIES_OFFSET) + i];
} else {
switch (property) {
case TPM2_PT_PCR_COUNT:
val = SANDBOX_TPM_PCR_NB;
break;
default:
val = 0xffffffff;
break;
}
}
put_unaligned_be32(val, recv);
recv += sizeof(property);
}
break;
}
/* Add trailing \0 */

View File

@ -56,6 +56,17 @@ ulong bootm_disable_interrupts(void);
int bootm_find_images(int flag, int argc, char *const argv[], ulong start,
ulong size);
/*
* Measure the boot images. Measurement is the process of hashing some binary
* data and storing it into secure memory, i.e. TPM PCRs. In addition, each
* measurement is logged into the platform event log such that the operating
* system can access it and perform attestation of the boot.
*
* @images: The structure containing the various images to boot (linux,
* initrd, dts, etc.)
*/
int bootm_measure(struct bootm_headers *images);
int do_bootm_states(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[], int states, struct bootm_headers *images,
int boot_progress);

View File

@ -129,50 +129,6 @@ struct efi_tcg2_boot_service_capability {
#define BOOT_SERVICE_CAPABILITY_MIN \
offsetof(struct efi_tcg2_boot_service_capability, number_of_pcr_banks)
#define TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03 "Spec ID Event03"
#define TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MAJOR_TPM2 2
#define TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MINOR_TPM2 0
#define TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_ERRATA_TPM2 2
/**
* struct TCG_EfiSpecIdEventAlgorithmSize - hashing algorithm information
*
* @algorithm_id: algorithm defined in enum tpm2_algorithms
* @digest_size: size of the algorithm
*/
struct tcg_efi_spec_id_event_algorithm_size {
u16 algorithm_id;
u16 digest_size;
} __packed;
/**
* struct TCG_EfiSpecIDEventStruct - content of the event log header
*
* @signature: signature, set to Spec ID Event03
* @platform_class: class defined in TCG ACPI Specification
* Client Common Header.
* @spec_version_minor: minor version
* @spec_version_major: major version
* @spec_version_errata: major version
* @uintn_size: size of the efi_uintn_t fields used in various
* data structures used in this specification.
* 0x01 indicates u32 and 0x02 indicates u64
* @number_of_algorithms: hashing algorithms used in this event log
* @digest_sizes: array of number_of_algorithms pairs
* 1st member defines the algorithm id
* 2nd member defines the algorithm size
*/
struct tcg_efi_spec_id_event {
u8 signature[16];
u32 platform_class;
u8 spec_version_minor;
u8 spec_version_major;
u8 spec_errata;
u8 uintn_size;
u32 number_of_algorithms;
struct tcg_efi_spec_id_event_algorithm_size digest_sizes[];
} __packed;
/**
* struct tdEFI_TCG2_FINAL_EVENTS_TABLE - log entries after Get Event Log
* @version: version number for this structure

View File

@ -409,6 +409,7 @@ struct bootm_headers {
#define BOOTM_STATE_OS_FAKE_GO 0x00000200 /* 'Almost' run the OS */
#define BOOTM_STATE_OS_GO 0x00000400
#define BOOTM_STATE_PRE_LOAD 0x00000800
#define BOOTM_STATE_MEASURE 0x00001000
int state;
#if defined(CONFIG_LMB) && !defined(USE_HOSTCC)

View File

@ -46,6 +46,7 @@ int do_ut_lib(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
int do_ut_loadm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
int do_ut_log(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]);
int do_ut_mbr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
int do_ut_measurement(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]);
int do_ut_mem(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
int do_ut_optee(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
int do_ut_overlay(struct cmd_tbl *cmdtp, int flag, int argc,

View File

@ -169,7 +169,7 @@ struct tcg_pcr_event {
/**
* Definition of TPMU_HA Union
*/
union tmpu_ha {
union tpmu_ha {
u8 sha1[TPM2_SHA1_DIGEST_SIZE];
u8 sha256[TPM2_SHA256_DIGEST_SIZE];
u8 sm3_256[TPM2_SM3_256_DIGEST_SIZE];
@ -185,7 +185,7 @@ union tmpu_ha {
*/
struct tpmt_ha {
u16 hash_alg;
union tmpu_ha digest;
union tpmu_ha digest;
} __packed;
/**
@ -216,6 +216,50 @@ struct tcg_pcr_event2 {
u8 event[];
} __packed;
/**
* struct TCG_EfiSpecIdEventAlgorithmSize - hashing algorithm information
*
* @algorithm_id: algorithm defined in enum tpm2_algorithms
* @digest_size: size of the algorithm
*/
struct tcg_efi_spec_id_event_algorithm_size {
u16 algorithm_id;
u16 digest_size;
} __packed;
#define TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03 "Spec ID Event03"
#define TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MAJOR_TPM2 2
#define TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MINOR_TPM2 0
#define TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_ERRATA_TPM2 2
/**
* struct TCG_EfiSpecIDEventStruct - content of the event log header
*
* @signature: signature, set to Spec ID Event03
* @platform_class: class defined in TCG ACPI Specification
* Client Common Header.
* @spec_version_minor: minor version
* @spec_version_major: major version
* @spec_version_errata: major version
* @uintn_size: size of the efi_uintn_t fields used in various
* data structures used in this specification.
* 0x01 indicates u32 and 0x02 indicates u64
* @number_of_algorithms: hashing algorithms used in this event log
* @digest_sizes: array of number_of_algorithms pairs
* 1st member defines the algorithm id
* 2nd member defines the algorithm size
*/
struct tcg_efi_spec_id_event {
u8 signature[16];
u32 platform_class;
u8 spec_version_minor;
u8 spec_version_major;
u8 spec_errata;
u8 uintn_size;
u32 number_of_algorithms;
struct tcg_efi_spec_id_event_algorithm_size digest_sizes[];
} __packed;
/**
* TPM2 Structure Tags for command/response buffers.
*
@ -342,6 +386,26 @@ enum tpm2_algorithms {
TPM2_ALG_SM3_256 = 0x12,
};
extern const enum tpm2_algorithms tpm2_supported_algorithms[4];
static inline u16 tpm2_algorithm_to_len(enum tpm2_algorithms a)
{
switch (a) {
case TPM2_ALG_SHA1:
return TPM2_SHA1_DIGEST_SIZE;
case TPM2_ALG_SHA256:
return TPM2_SHA256_DIGEST_SIZE;
case TPM2_ALG_SHA384:
return TPM2_SHA384_DIGEST_SIZE;
case TPM2_ALG_SHA512:
return TPM2_SHA512_DIGEST_SIZE;
default:
return 0;
}
}
#define tpm2_algorithm_to_mask(a) (1 << (a))
/* NV index attributes */
enum tpm_index_attrs {
TPMA_NV_PPWRITE = 1UL << 0,
@ -421,6 +485,188 @@ enum {
HR_NV_INDEX = TPM_HT_NV_INDEX << HR_SHIFT,
};
/**
* struct tcg2_event_log - Container for managing the platform event log
*
* @log: Address of the log
* @log_position: Current entry position
* @log_size: Log space available
* @found: Boolean indicating if an existing log was discovered
*/
struct tcg2_event_log {
u8 *log;
u32 log_position;
u32 log_size;
bool found;
};
/**
* Create a list of digests of the supported PCR banks for a given input data
*
* @dev TPM device
* @input Data
* @length Length of the data to calculate the digest
* @digest_list List of digests to fill in
*
* Return: zero on success, negative errno otherwise
*/
int tcg2_create_digest(struct udevice *dev, const u8 *input, u32 length,
struct tpml_digest_values *digest_list);
/**
* Get the event size of the specified digests
*
* @digest_list List of digests for the event
*
* Return: Size in bytes of the event
*/
u32 tcg2_event_get_size(struct tpml_digest_values *digest_list);
/**
* tcg2_get_active_pcr_banks
*
* @dev TPM device
* @active_pcr_banks Bitmask of PCR algorithms supported
*
* Return: zero on success, negative errno otherwise
*/
int tcg2_get_active_pcr_banks(struct udevice *dev, u32 *active_pcr_banks);
/**
* tcg2_log_append - Append an event to an event log
*
* @pcr_index Index of the PCR
* @event_type Type of event
* @digest_list List of digests to add
* @size Size of event
* @event Event data
* @log Log buffer to append the event to
*/
void tcg2_log_append(u32 pcr_index, u32 event_type,
struct tpml_digest_values *digest_list, u32 size,
const u8 *event, u8 *log);
/**
* Extend the PCR with specified digests
*
* @dev TPM device
* @pcr_index Index of the PCR
* @digest_list List of digests to extend
*
* Return: zero on success, negative errno otherwise
*/
int tcg2_pcr_extend(struct udevice *dev, u32 pcr_index,
struct tpml_digest_values *digest_list);
/**
* Read the PCR into a list of digests
*
* @dev TPM device
* @pcr_index Index of the PCR
* @digest_list List of digests to extend
*
* Return: zero on success, negative errno otherwise
*/
int tcg2_pcr_read(struct udevice *dev, u32 pcr_index,
struct tpml_digest_values *digest_list);
/**
* Measure data into the TPM PCRs and the platform event log.
*
* @dev TPM device
* @log Platform event log
* @pcr_index Index of the PCR
* @size Size of the data or 0 for event only
* @data Pointer to the data or NULL for event only
* @event_type Event log type
* @event_size Size of the event
* @event Pointer to the event
*
* Return: zero on success, negative errno otherwise
*/
int tcg2_measure_data(struct udevice *dev, struct tcg2_event_log *elog,
u32 pcr_index, u32 size, const u8 *data, u32 event_type,
u32 event_size, const u8 *event);
#define tcg2_measure_event(dev, elog, pcr_index, event_type, size, event) \
tcg2_measure_data(dev, elog, pcr_index, 0, NULL, event_type, size, \
event)
/**
* Prepare the event log buffer. This function tries to discover an existing
* event log in memory from a previous bootloader stage. If such a log exists
* and the PCRs are not extended, the log is "replayed" to extend the PCRs.
* If no log is discovered, create the log header.
*
* @dev TPM device
* @elog Platform event log. The log pointer and log_size
* members must be initialized to either 0 or to a valid
* memory region, in which case any existing log
* discovered will be copied to the specified memory
* region.
* @ignore_existing_log Boolean to indicate whether or not to ignore an
* existing platform log in memory
*
* Return: zero on success, negative errno otherwise
*/
int tcg2_log_prepare_buffer(struct udevice *dev, struct tcg2_event_log *elog,
bool ignore_existing_log);
/**
* Begin measurements.
*
* @dev TPM device
* @elog Platform event log. The log pointer and log_size
* members must be initialized to either 0 or to a valid
* memory region, in which case any existing log
* discovered will be copied to the specified memory
* region.
* @ignore_existing_log Boolean to indicate whether or not to ignore an
* existing platform log in memory
*
* Return: zero on success, negative errno otherwise
*/
int tcg2_measurement_init(struct udevice **dev, struct tcg2_event_log *elog,
bool ignore_existing_log);
/**
* Stop measurements and record separator events.
*
* @dev TPM device
* @elog Platform event log
* @error Boolean to indicate whether an error ocurred or not
*/
void tcg2_measurement_term(struct udevice *dev, struct tcg2_event_log *elog,
bool error);
/**
* Get the platform event log address and size.
*
* @dev TPM device
* @addr Address of the log
* @size Size of the log
*
* Return: zero on success, negative errno otherwise
*/
int tcg2_platform_get_log(struct udevice *dev, void **addr, u32 *size);
/**
* Get the first TPM2 device found.
*
* @dev TPM device
*
* Return: zero on success, negative errno otherwise
*/
int tcg2_platform_get_tpm2(struct udevice **dev);
/**
* Platform-specific function for handling TPM startup errors
*
* @dev TPM device
* @rc The TPM response code
*/
void tcg2_platform_startup_error(struct udevice *dev, int rc);
/**
* Issue a TPM2_Startup command.
*
@ -540,6 +786,19 @@ u32 tpm2_pcr_read(struct udevice *dev, u32 idx, unsigned int idx_min_sz,
u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
void *buf, size_t prop_count);
/**
* tpm2_get_pcr_info() - get the supported, active PCRs and number of banks
*
* @dev: TPM device
* @supported_pcr: bitmask with the algorithms supported
* @active_pcr: bitmask with the active algorithms
* @pcr_banks: number of PCR banks
*
* @return 0 on success, code of operation or negative errno on failure
*/
int tpm2_get_pcr_info(struct udevice *dev, u32 *supported_pcr, u32 *active_pcr,
u32 *pcr_banks);
/**
* Issue a TPM2_DictionaryAttackLockReset command.
*

View File

@ -431,6 +431,10 @@ config TPM
bool "Trusted Platform Module (TPM) Support"
depends on DM
imply DM_RNG
select SHA1
select SHA256
select SHA384
select SHA512
help
This enables support for TPMs which can be used to provide security
features for your board. The TPM can be connected via LPC or I2C

View File

@ -379,8 +379,6 @@ config EFI_TCG2_PROTOCOL
bool "EFI_TCG2_PROTOCOL support"
default y
depends on TPM_V2
# Sandbox TPM currently fails on GetCapabilities needed for TCG2
depends on !SANDBOX
select SHA1
select SHA256
select SHA384

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,705 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2023 Linaro Limited
* Copyright (c) 2018 Bootlin
* Author: Miquel Raynal <miquel.raynal@bootlin.com>
*/
#include <common.h>
#include <dm.h>
#include <dm/of_access.h>
#include <tpm_api.h>
#include <tpm-common.h>
#include <tpm-v2.h>
#include <u-boot/sha1.h>
#include <u-boot/sha256.h>
#include <u-boot/sha512.h>
#include <version_string.h>
#include <asm/io.h>
#include <linux/bitops.h>
#include <linux/unaligned/be_byteshift.h>
#include <linux/unaligned/generic.h>
#include <linux/unaligned/le_byteshift.h>
#include "tpm-utils.h"
const enum tpm2_algorithms tpm2_supported_algorithms[4] = {
TPM2_ALG_SHA1,
TPM2_ALG_SHA256,
TPM2_ALG_SHA384,
TPM2_ALG_SHA512,
};
int tcg2_get_active_pcr_banks(struct udevice *dev, u32 *active_pcr_banks)
{
u32 supported = 0;
u32 pcr_banks = 0;
u32 active = 0;
int rc;
rc = tpm2_get_pcr_info(dev, &supported, &active, &pcr_banks);
if (rc)
return rc;
*active_pcr_banks = active;
return 0;
}
u32 tcg2_event_get_size(struct tpml_digest_values *digest_list)
{
u32 len;
size_t i;
len = offsetof(struct tcg_pcr_event2, digests);
len += offsetof(struct tpml_digest_values, digests);
for (i = 0; i < digest_list->count; ++i) {
u16 l = tpm2_algorithm_to_len(digest_list->digests[i].hash_alg);
if (!l)
continue;
len += l + offsetof(struct tpmt_ha, digest);
}
len += sizeof(u32);
return len;
}
int tcg2_create_digest(struct udevice *dev, const u8 *input, u32 length,
struct tpml_digest_values *digest_list)
{
u8 final[sizeof(union tpmu_ha)];
sha256_context ctx_256;
sha512_context ctx_512;
sha1_context ctx;
u32 active;
size_t i;
u32 len;
int rc;
rc = tcg2_get_active_pcr_banks(dev, &active);
if (rc)
return rc;
digest_list->count = 0;
for (i = 0; i < ARRAY_SIZE(tpm2_supported_algorithms); ++i) {
u32 mask =
tpm2_algorithm_to_mask(tpm2_supported_algorithms[i]);
if (!(active & mask))
continue;
switch (tpm2_supported_algorithms[i]) {
case TPM2_ALG_SHA1:
sha1_starts(&ctx);
sha1_update(&ctx, input, length);
sha1_finish(&ctx, final);
len = TPM2_SHA1_DIGEST_SIZE;
break;
case TPM2_ALG_SHA256:
sha256_starts(&ctx_256);
sha256_update(&ctx_256, input, length);
sha256_finish(&ctx_256, final);
len = TPM2_SHA256_DIGEST_SIZE;
break;
case TPM2_ALG_SHA384:
sha384_starts(&ctx_512);
sha384_update(&ctx_512, input, length);
sha384_finish(&ctx_512, final);
len = TPM2_SHA384_DIGEST_SIZE;
break;
case TPM2_ALG_SHA512:
sha512_starts(&ctx_512);
sha512_update(&ctx_512, input, length);
sha512_finish(&ctx_512, final);
len = TPM2_SHA512_DIGEST_SIZE;
break;
default:
printf("%s: unsupported algorithm %x\n", __func__,
tpm2_supported_algorithms[i]);
continue;
}
digest_list->digests[digest_list->count].hash_alg =
tpm2_supported_algorithms[i];
memcpy(&digest_list->digests[digest_list->count].digest, final,
len);
digest_list->count++;
}
return 0;
}
void tcg2_log_append(u32 pcr_index, u32 event_type,
struct tpml_digest_values *digest_list, u32 size,
const u8 *event, u8 *log)
{
size_t len;
size_t pos;
u32 i;
pos = offsetof(struct tcg_pcr_event2, pcr_index);
put_unaligned_le32(pcr_index, log);
pos = offsetof(struct tcg_pcr_event2, event_type);
put_unaligned_le32(event_type, log + pos);
pos = offsetof(struct tcg_pcr_event2, digests) +
offsetof(struct tpml_digest_values, count);
put_unaligned_le32(digest_list->count, log + pos);
pos = offsetof(struct tcg_pcr_event2, digests) +
offsetof(struct tpml_digest_values, digests);
for (i = 0; i < digest_list->count; ++i) {
u16 hash_alg = digest_list->digests[i].hash_alg;
len = tpm2_algorithm_to_len(hash_alg);
if (!len)
continue;
pos += offsetof(struct tpmt_ha, hash_alg);
put_unaligned_le16(hash_alg, log + pos);
pos += offsetof(struct tpmt_ha, digest);
memcpy(log + pos, (u8 *)&digest_list->digests[i].digest, len);
pos += len;
}
put_unaligned_le32(size, log + pos);
pos += sizeof(u32);
memcpy(log + pos, event, size);
}
static int tcg2_log_append_check(struct tcg2_event_log *elog, u32 pcr_index,
u32 event_type,
struct tpml_digest_values *digest_list,
u32 size, const u8 *event)
{
u32 event_size;
u8 *log;
event_size = size + tcg2_event_get_size(digest_list);
if (elog->log_position + event_size > elog->log_size) {
printf("%s: log too large: %u + %u > %u\n", __func__,
elog->log_position, event_size, elog->log_size);
return -ENOBUFS;
}
log = elog->log + elog->log_position;
elog->log_position += event_size;
tcg2_log_append(pcr_index, event_type, digest_list, size, event, log);
return 0;
}
static int tcg2_log_init(struct udevice *dev, struct tcg2_event_log *elog)
{
struct tcg_efi_spec_id_event *ev;
struct tcg_pcr_event *log;
u32 event_size;
u32 count = 0;
u32 log_size;
u32 active;
u32 mask;
size_t i;
u16 len;
int rc;
rc = tcg2_get_active_pcr_banks(dev, &active);
if (rc)
return rc;
event_size = offsetof(struct tcg_efi_spec_id_event, digest_sizes);
for (i = 0; i < ARRAY_SIZE(tpm2_supported_algorithms); ++i) {
mask = tpm2_algorithm_to_mask(tpm2_supported_algorithms[i]);
if (!(active & mask))
continue;
switch (tpm2_supported_algorithms[i]) {
case TPM2_ALG_SHA1:
case TPM2_ALG_SHA256:
case TPM2_ALG_SHA384:
case TPM2_ALG_SHA512:
count++;
break;
default:
continue;
}
}
event_size += 1 +
(sizeof(struct tcg_efi_spec_id_event_algorithm_size) * count);
log_size = offsetof(struct tcg_pcr_event, event) + event_size;
if (log_size > elog->log_size) {
printf("%s: log too large: %u > %u\n", __func__, log_size,
elog->log_size);
return -ENOBUFS;
}
log = (struct tcg_pcr_event *)elog->log;
put_unaligned_le32(0, &log->pcr_index);
put_unaligned_le32(EV_NO_ACTION, &log->event_type);
memset(&log->digest, 0, sizeof(log->digest));
put_unaligned_le32(event_size, &log->event_size);
ev = (struct tcg_efi_spec_id_event *)log->event;
strlcpy((char *)ev->signature, TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03,
sizeof(ev->signature));
put_unaligned_le32(0, &ev->platform_class);
ev->spec_version_minor = TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MINOR_TPM2;
ev->spec_version_major = TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MAJOR_TPM2;
ev->spec_errata = TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_ERRATA_TPM2;
ev->uintn_size = sizeof(size_t) / sizeof(u32);
put_unaligned_le32(count, &ev->number_of_algorithms);
count = 0;
for (i = 0; i < ARRAY_SIZE(tpm2_supported_algorithms); ++i) {
mask = tpm2_algorithm_to_mask(tpm2_supported_algorithms[i]);
if (!(active & mask))
continue;
len = tpm2_algorithm_to_len(tpm2_supported_algorithms[i]);
if (!len)
continue;
put_unaligned_le16(tpm2_supported_algorithms[i],
&ev->digest_sizes[count].algorithm_id);
put_unaligned_le16(len, &ev->digest_sizes[count].digest_size);
count++;
}
*((u8 *)ev + (event_size - 1)) = 0;
elog->log_position = log_size;
return 0;
}
static int tcg2_replay_eventlog(struct tcg2_event_log *elog,
struct udevice *dev,
struct tpml_digest_values *digest_list,
u32 log_position)
{
const u32 offset = offsetof(struct tcg_pcr_event2, digests) +
offsetof(struct tpml_digest_values, digests);
u32 event_size;
u32 count;
u16 algo;
u32 pcr;
u32 pos;
u16 len;
u8 *log;
int rc;
u32 i;
while (log_position + offset < elog->log_size) {
log = elog->log + log_position;
pos = offsetof(struct tcg_pcr_event2, pcr_index);
pcr = get_unaligned_le32(log + pos);
pos = offsetof(struct tcg_pcr_event2, event_type);
if (!get_unaligned_le32(log + pos))
return 0;
pos = offsetof(struct tcg_pcr_event2, digests) +
offsetof(struct tpml_digest_values, count);
count = get_unaligned_le32(log + pos);
if (count > ARRAY_SIZE(tpm2_supported_algorithms) ||
(digest_list->count && digest_list->count != count))
return 0;
pos = offsetof(struct tcg_pcr_event2, digests) +
offsetof(struct tpml_digest_values, digests);
for (i = 0; i < count; ++i) {
pos += offsetof(struct tpmt_ha, hash_alg);
if (log_position + pos + sizeof(u16) >= elog->log_size)
return 0;
algo = get_unaligned_le16(log + pos);
pos += offsetof(struct tpmt_ha, digest);
switch (algo) {
case TPM2_ALG_SHA1:
case TPM2_ALG_SHA256:
case TPM2_ALG_SHA384:
case TPM2_ALG_SHA512:
len = tpm2_algorithm_to_len(algo);
break;
default:
return 0;
}
if (digest_list->count) {
if (algo != digest_list->digests[i].hash_alg ||
log_position + pos + len >= elog->log_size)
return 0;
memcpy(digest_list->digests[i].digest.sha512,
log + pos, len);
}
pos += len;
}
if (log_position + pos + sizeof(u32) >= elog->log_size)
return 0;
event_size = get_unaligned_le32(log + pos);
pos += event_size + sizeof(u32);
if (log_position + pos > elog->log_size)
return 0;
if (digest_list->count) {
rc = tcg2_pcr_extend(dev, pcr, digest_list);
if (rc)
return rc;
}
log_position += pos;
}
elog->log_position = log_position;
elog->found = true;
return 0;
}
static int tcg2_log_parse(struct udevice *dev, struct tcg2_event_log *elog)
{
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;
u32 evsz;
u32 mask;
u16 algo;
u16 len;
int rc;
u32 i;
u16 j;
if (elog->log_size <= offsetof(struct tcg_pcr_event, event))
return 0;
log = (struct tcg_pcr_event *)elog->log;
if (get_unaligned_le32(&log->pcr_index) != 0 ||
get_unaligned_le32(&log->event_type) != EV_NO_ACTION)
return 0;
for (i = 0; i < sizeof(log->digest); i++) {
if (log->digest[i])
return 0;
}
evsz = get_unaligned_le32(&log->event_size);
if (evsz < offsetof(struct tcg_efi_spec_id_event, digest_sizes) ||
evsz + offsetof(struct tcg_pcr_event, event) > elog->log_size)
return 0;
event = (struct tcg_efi_spec_id_event *)log->event;
if (memcmp(event->signature, TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03,
sizeof(TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03)))
return 0;
if (event->spec_version_minor != TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MINOR_TPM2 ||
event->spec_version_major != TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MAJOR_TPM2)
return 0;
count = get_unaligned_le32(&event->number_of_algorithms);
if (count > ARRAY_SIZE(tpm2_supported_algorithms))
return 0;
calc_size = offsetof(struct tcg_efi_spec_id_event, digest_sizes) +
(sizeof(struct tcg_efi_spec_id_event_algorithm_size) * count) +
1;
if (evsz != calc_size)
return 0;
rc = tcg2_get_active_pcr_banks(dev, &active);
if (rc)
return rc;
digest_list.count = 0;
log_active = 0;
for (i = 0; i < count; ++i) {
algo = get_unaligned_le16(&event->digest_sizes[i].algorithm_id);
mask = tpm2_algorithm_to_mask(algo);
if (!(active & mask))
return 0;
switch (algo) {
case TPM2_ALG_SHA1:
case TPM2_ALG_SHA256:
case TPM2_ALG_SHA384:
case TPM2_ALG_SHA512:
len = get_unaligned_le16(&event->digest_sizes[i].digest_size);
if (tpm2_algorithm_to_len(algo) != len)
return 0;
digest_list.digests[digest_list.count++].hash_alg = algo;
break;
default:
return 0;
}
log_active |= mask;
}
/* Ensure the previous firmware extended all the PCRs. */
if (log_active != active)
return 0;
/* Read PCR0 to check if previous firmware extended the PCRs or not. */
rc = tcg2_pcr_read(dev, 0, &digest_list);
if (rc)
return rc;
for (i = 0; i < digest_list.count; ++i) {
len = tpm2_algorithm_to_len(digest_list.digests[i].hash_alg);
for (j = 0; j < len; ++j) {
if (digest_list.digests[i].digest.sha512[j])
break;
}
/* PCR is non-zero; it has been extended, so skip extending. */
if (j != len) {
digest_list.count = 0;
break;
}
}
return tcg2_replay_eventlog(elog, dev, &digest_list,
offsetof(struct tcg_pcr_event, event) +
evsz);
}
int tcg2_pcr_extend(struct udevice *dev, u32 pcr_index,
struct tpml_digest_values *digest_list)
{
u32 rc;
u32 i;
for (i = 0; i < digest_list->count; i++) {
u32 alg = digest_list->digests[i].hash_alg;
rc = tpm2_pcr_extend(dev, pcr_index, alg,
(u8 *)&digest_list->digests[i].digest,
tpm2_algorithm_to_len(alg));
if (rc) {
printf("%s: error pcr:%u alg:%08x\n", __func__,
pcr_index, alg);
return rc;
}
}
return 0;
}
int tcg2_pcr_read(struct udevice *dev, u32 pcr_index,
struct tpml_digest_values *digest_list)
{
struct tpm_chip_priv *priv;
u32 rc;
u32 i;
priv = dev_get_uclass_priv(dev);
if (!priv)
return -ENODEV;
for (i = 0; i < digest_list->count; i++) {
u32 alg = digest_list->digests[i].hash_alg;
u8 *digest = (u8 *)&digest_list->digests[i].digest;
rc = tpm2_pcr_read(dev, pcr_index, priv->pcr_select_min, alg,
digest, tpm2_algorithm_to_len(alg), NULL);
if (rc) {
printf("%s: error pcr:%u alg:%08x\n", __func__,
pcr_index, alg);
return rc;
}
}
return 0;
}
int tcg2_measure_data(struct udevice *dev, struct tcg2_event_log *elog,
u32 pcr_index, u32 size, const u8 *data, u32 event_type,
u32 event_size, const u8 *event)
{
struct tpml_digest_values digest_list;
int rc;
if (data)
rc = tcg2_create_digest(dev, data, size, &digest_list);
else
rc = tcg2_create_digest(dev, event, event_size, &digest_list);
if (rc)
return rc;
rc = tcg2_pcr_extend(dev, pcr_index, &digest_list);
if (rc)
return rc;
return tcg2_log_append_check(elog, pcr_index, event_type, &digest_list,
event_size, event);
}
int tcg2_log_prepare_buffer(struct udevice *dev, struct tcg2_event_log *elog,
bool ignore_existing_log)
{
struct tcg2_event_log log;
int rc;
elog->log_position = 0;
elog->found = false;
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);
if (rc)
return rc;
}
if (elog->log_size) {
if (log.found) {
if (elog->log_size < log.log_position)
return -ENOSPC;
/*
* Copy the discovered log into the user buffer
* if there's enough space.
*/
memcpy(elog->log, log.log, log.log_position);
}
unmap_physmem(log.log, MAP_NOCACHE);
} else {
elog->log = log.log;
elog->log_size = log.log_size;
}
elog->log_position = log.log_position;
elog->found = log.found;
}
/*
* 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
* memory region is found.
*/
if (!elog->found && elog->log_size)
rc = tcg2_log_init(dev, elog);
return rc;
}
int tcg2_measurement_init(struct udevice **dev, struct tcg2_event_log *elog,
bool ignore_existing_log)
{
int rc;
rc = tcg2_platform_get_tpm2(dev);
if (rc)
return rc;
rc = tpm_auto_start(*dev);
if (rc)
return rc;
rc = tcg2_log_prepare_buffer(*dev, elog, ignore_existing_log);
if (rc) {
tcg2_measurement_term(*dev, elog, true);
return rc;
}
rc = tcg2_measure_event(*dev, elog, 0, EV_S_CRTM_VERSION,
strlen(version_string) + 1,
(u8 *)version_string);
if (rc) {
tcg2_measurement_term(*dev, elog, true);
return rc;
}
return 0;
}
void tcg2_measurement_term(struct udevice *dev, struct tcg2_event_log *elog,
bool error)
{
u32 event = error ? 0x1 : 0xffffffff;
int i;
for (i = 0; i < 8; ++i)
tcg2_measure_event(dev, elog, i, EV_SEPARATOR, sizeof(event),
(const u8 *)&event);
if (elog->log)
unmap_physmem(elog->log, MAP_NOCACHE);
}
__weak int tcg2_platform_get_log(struct udevice *dev, void **addr, u32 *size)
{
const __be32 *addr_prop;
const __be32 *size_prop;
int asize;
int ssize;
*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);
size_prop = dev_read_prop(dev, "tpm_event_log_size", &ssize);
if (!size_prop)
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));
u64 s = of_read_number(size_prop, ssize / sizeof(__be32));
*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;
}
__weak int tcg2_platform_get_tpm2(struct udevice **dev)
{
for_each_tpm_device(*dev) {
if (tpm_get_version(*dev) == TPM_V2)
return 0;
}
return -ENODEV;
}
__weak void tcg2_platform_startup_error(struct udevice *dev, int rc) {}
u32 tpm2_startup(struct udevice *dev, enum tpm2_startup_types mode)
{
const u8 command_v2[12] = {
@ -359,6 +1048,131 @@ u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
return 0;
}
static int tpm2_get_num_pcr(struct udevice *dev, u32 *num_pcr)
{
u8 response[(sizeof(struct tpms_capability_data) -
offsetof(struct tpms_capability_data, data))];
u32 properties_offset =
offsetof(struct tpml_tagged_tpm_property, tpm_property) +
offsetof(struct tpms_tagged_property, value);
u32 ret;
memset(response, 0, sizeof(response));
ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
TPM2_PT_PCR_COUNT, response, 1);
if (ret)
return ret;
*num_pcr = get_unaligned_be32(response + properties_offset);
if (*num_pcr > TPM2_MAX_PCRS) {
printf("%s: too many pcrs: %u\n", __func__, *num_pcr);
return -E2BIG;
}
return 0;
}
static bool tpm2_is_active_pcr(struct tpms_pcr_selection *selection)
{
int i;
/*
* check the pcr_select. If at least one of the PCRs supports the
* algorithm add it on the active ones
*/
for (i = 0; i < selection->size_of_select; i++) {
if (selection->pcr_select[i])
return true;
}
return false;
}
int tpm2_get_pcr_info(struct udevice *dev, u32 *supported_pcr, u32 *active_pcr,
u32 *pcr_banks)
{
u8 response[(sizeof(struct tpms_capability_data) -
offsetof(struct tpms_capability_data, data))];
struct tpml_pcr_selection pcrs;
u32 num_pcr;
size_t i;
u32 ret;
*supported_pcr = 0;
*active_pcr = 0;
*pcr_banks = 0;
memset(response, 0, sizeof(response));
ret = tpm2_get_capability(dev, TPM2_CAP_PCRS, 0, response, 1);
if (ret)
return ret;
pcrs.count = get_unaligned_be32(response);
/*
* We only support 5 algorithms for now so check against that
* instead of TPM2_NUM_PCR_BANKS
*/
if (pcrs.count > ARRAY_SIZE(tpm2_supported_algorithms) ||
pcrs.count < 1) {
printf("%s: too many pcrs: %u\n", __func__, pcrs.count);
return -EMSGSIZE;
}
ret = tpm2_get_num_pcr(dev, &num_pcr);
if (ret)
return ret;
for (i = 0; i < pcrs.count; i++) {
/*
* Definition of TPMS_PCR_SELECTION Structure
* hash: u16
* size_of_select: u8
* pcr_select: u8 array
*
* The offsets depend on the number of the device PCRs
* so we have to calculate them based on that
*/
u32 hash_offset = offsetof(struct tpml_pcr_selection, selection) +
i * offsetof(struct tpms_pcr_selection, pcr_select) +
i * ((num_pcr + 7) / 8);
u32 size_select_offset =
hash_offset + offsetof(struct tpms_pcr_selection,
size_of_select);
u32 pcr_select_offset =
hash_offset + offsetof(struct tpms_pcr_selection,
pcr_select);
pcrs.selection[i].hash =
get_unaligned_be16(response + hash_offset);
pcrs.selection[i].size_of_select =
__get_unaligned_be(response + size_select_offset);
if (pcrs.selection[i].size_of_select > TPM2_PCR_SELECT_MAX) {
printf("%s: pcrs selection too large: %u\n", __func__,
pcrs.selection[i].size_of_select);
return -ENOBUFS;
}
/* copy the array of pcr_select */
memcpy(pcrs.selection[i].pcr_select, response + pcr_select_offset,
pcrs.selection[i].size_of_select);
}
for (i = 0; i < pcrs.count; i++) {
u32 hash_mask = tpm2_algorithm_to_mask(pcrs.selection[i].hash);
if (hash_mask) {
*supported_pcr |= hash_mask;
if (tpm2_is_active_pcr(&pcrs.selection[i]))
*active_pcr |= hash_mask;
} else {
printf("%s: unknown algorithm %x\n", __func__,
pcrs.selection[i].hash);
}
}
*pcr_banks = pcrs.count;
return 0;
}
u32 tpm2_dam_reset(struct udevice *dev, const char *pw, const ssize_t pw_sz)
{
u8 command_v2[COMMAND_BUFFER_SIZE] = {

View File

@ -4,6 +4,7 @@
obj-$(CONFIG_BOOTSTD) += bootdev.o bootstd_common.o bootflow.o bootmeth.o
obj-$(CONFIG_FIT) += image.o
obj-$(CONFIG_MEASURED_BOOT) += measurement.o
obj-$(CONFIG_EXPO) += expo.o
obj-$(CONFIG_CEDIT) += cedit.o

66
test/boot/measurement.c Normal file
View File

@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Test for measured boot functions
*
* Copyright 2023 IBM Corp.
* Written by Eddie James <eajames@linux.ibm.com>
*/
#include <common.h>
#include <bootm.h>
#include <malloc.h>
#include <test/suites.h>
#include <test/test.h>
#include <test/ut.h>
#include <asm/io.h>
#define MEASUREMENT_TEST(_name, _flags) \
UNIT_TEST(_name, _flags, measurement_test)
static int measure(struct unit_test_state *uts)
{
struct bootm_headers images;
const size_t size = 1024;
u8 *kernel;
u8 *initrd;
size_t i;
kernel = malloc(size);
initrd = malloc(size);
images.os.image_start = map_to_sysmem(kernel);
images.os.image_len = size;
images.rd_start = map_to_sysmem(initrd);
images.rd_end = images.rd_start + size;
images.ft_addr = malloc(size);
images.ft_len = size;
env_set("bootargs", "measurement testing");
for (i = 0; i < size; ++i) {
kernel[i] = 0xf0 | (i & 0xf);
initrd[i] = (i & 0xf0) | 0xf;
images.ft_addr[i] = i & 0xff;
}
ut_assertok(bootm_measure(&images));
free(images.ft_addr);
free(initrd);
free(kernel);
return 0;
}
MEASUREMENT_TEST(measure, 0);
int do_ut_measurement(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
struct unit_test *tests = UNIT_TEST_SUITE_START(measurement_test);
const int n_ents = UNIT_TEST_SUITE_COUNT(measurement_test);
return cmd_ut_category("measurement", "measurement_test_", tests,
n_ents, argc, argv);
}

View File

@ -103,6 +103,10 @@ static struct cmd_tbl cmd_ut_sub[] = {
#if CONFIG_IS_ENABLED(UT_UNICODE) && !defined(API_BUILD)
U_BOOT_CMD_MKENT(unicode, CONFIG_SYS_MAXARGS, 1, do_ut_unicode, "", ""),
#endif
#ifdef CONFIG_MEASURED_BOOT
U_BOOT_CMD_MKENT(measurement, CONFIG_SYS_MAXARGS, 1, do_ut_measurement,
"", ""),
#endif
#ifdef CONFIG_SANDBOX
U_BOOT_CMD_MKENT(compression, CONFIG_SYS_MAXARGS, 1, do_ut_compression,
"", ""),

View File

@ -61,7 +61,7 @@ def test_tpm2_init(u_boot_console):
skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
if skip_test:
pytest.skip('skip TPM device test')
u_boot_console.run_command('tpm2 init')
u_boot_console.run_command('tpm2 autostart')
output = u_boot_console.run_command('echo $?')
assert output.endswith('0')
@ -100,7 +100,7 @@ def test_tpm2_sandbox_self_test_full(u_boot_console):
"""
if is_sandbox(u_boot_console):
u_boot_console.restart_uboot()
u_boot_console.run_command('tpm2 init')
u_boot_console.run_command('tpm2 autostart')
output = u_boot_console.run_command('echo $?')
assert output.endswith('0')
@ -239,7 +239,7 @@ def test_tpm2_dam_parameters(u_boot_console):
def test_tpm2_pcr_read(u_boot_console):
"""Execute a TPM2_PCR_Read command.
Perform a PCR read of the 0th PCR. Must be zero.
Perform a PCR read of the 10th PCR. Must be zero.
"""
if is_sandbox(u_boot_console):
tpm2_sandbox_init(u_boot_console)
@ -247,7 +247,7 @@ def test_tpm2_pcr_read(u_boot_console):
force_init(u_boot_console)
ram = u_boot_utils.find_ram_base(u_boot_console)
read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % ram)
read_pcr = u_boot_console.run_command('tpm2 pcr_read 10 0x%x' % ram)
output = u_boot_console.run_command('echo $?')
assert output.endswith('0')
@ -257,7 +257,7 @@ def test_tpm2_pcr_read(u_boot_console):
updates = int(re.findall(r'\d+', str)[0])
# Check the output value
assert 'PCR #0 content' in read_pcr
assert 'PCR #10 content' in read_pcr
assert '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' in read_pcr
@pytest.mark.buildconfigspec('cmd_tpm_v2')
@ -275,19 +275,19 @@ def test_tpm2_pcr_extend(u_boot_console):
force_init(u_boot_console)
ram = u_boot_utils.find_ram_base(u_boot_console)
read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % (ram + 0x20))
read_pcr = u_boot_console.run_command('tpm2 pcr_read 10 0x%x' % (ram + 0x20))
output = u_boot_console.run_command('echo $?')
assert output.endswith('0')
str = re.findall(r'\d+ known updates', read_pcr)[0]
updates = int(re.findall(r'\d+', str)[0])
u_boot_console.run_command('tpm2 pcr_extend 0 0x%x' % ram)
u_boot_console.run_command('tpm2 pcr_extend 10 0x%x' % ram)
output = u_boot_console.run_command('echo $?')
assert output.endswith('0')
# Read the value back into a different place so we can still use 'ram' as
# our zero bytes
read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % (ram + 0x20))
read_pcr = u_boot_console.run_command('tpm2 pcr_read 10 0x%x' % (ram + 0x20))
output = u_boot_console.run_command('echo $?')
assert output.endswith('0')
assert 'f5 a5 fd 42 d1 6a 20 30 27 98 ef 6e d3 09 97 9b' in read_pcr
@ -297,11 +297,11 @@ def test_tpm2_pcr_extend(u_boot_console):
new_updates = int(re.findall(r'\d+', str)[0])
assert (updates + 1) == new_updates
u_boot_console.run_command('tpm2 pcr_extend 0 0x%x' % ram)
u_boot_console.run_command('tpm2 pcr_extend 10 0x%x' % ram)
output = u_boot_console.run_command('echo $?')
assert output.endswith('0')
read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % (ram + 0x20))
read_pcr = u_boot_console.run_command('tpm2 pcr_read 10 0x%x' % (ram + 0x20))
output = u_boot_console.run_command('echo $?')
assert output.endswith('0')
assert '7a 05 01 f5 95 7b df 9c b3 a8 ff 49 66 f0 22 65' in read_pcr