diff mbox series

[v9,4/6] bootm: Support boot measurement

Message ID 20230308212537.1725343-5-eajames@linux.ibm.com
State Changes Requested
Delegated to: Ilias Apalodimas
Headers show
Series tpm: Support boot measurements | expand

Commit Message

Eddie James March 8, 2023, 9:25 p.m. UTC
Add a configuration option to measure the boot through the bootm
function. Add the measurement state to the booti and bootz paths
as well.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
---
Changes since v8:
 - Added a configuration option to select to ignore any existing
   event log. This would only be selected for systems that know
   that U-Boot is the first stage bootloader. This is necessary
   because the reserved memory region may persist through resets
   and so U-Boot attempts to append to the previous boot's log.

Changes since v6:
 - Added comment for bootm_measure
 - Fixed line length in bootm_measure

 boot/Kconfig    | 32 +++++++++++++++++++++
 boot/bootm.c    | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
 cmd/booti.c     |  1 +
 cmd/bootm.c     |  2 ++
 cmd/bootz.c     |  1 +
 include/bootm.h | 11 ++++++++
 include/image.h |  1 +
 7 files changed, 122 insertions(+)

Comments

Sean Edmond Aug. 4, 2023, 6:10 p.m. UTC | #1
On 2023-03-08 1:25 p.m., Eddie James wrote:
> Add a configuration option to measure the boot through the bootm
> function. Add the measurement state to the booti and bootz paths
> as well.
>
> Signed-off-by: Eddie James <eajames@linux.ibm.com>
> Reviewed-by: Simon Glass <sjg@chromium.org>
> ---
> Changes since v8:
>   - Added a configuration option to select to ignore any existing
>     event log. This would only be selected for systems that know
>     that U-Boot is the first stage bootloader. This is necessary
>     because the reserved memory region may persist through resets
>     and so U-Boot attempts to append to the previous boot's log.
>
> Changes since v6:
>   - Added comment for bootm_measure
>   - Fixed line length in bootm_measure
>
>   boot/Kconfig    | 32 +++++++++++++++++++++
>   boot/bootm.c    | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
>   cmd/booti.c     |  1 +
>   cmd/bootm.c     |  2 ++
>   cmd/bootz.c     |  1 +
>   include/bootm.h | 11 ++++++++
>   include/image.h |  1 +
>   7 files changed, 122 insertions(+)
>
> diff --git a/boot/Kconfig b/boot/Kconfig
> index 5f491625c8..8119519c9f 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -629,6 +629,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 to TPM and event log"
> +	depends on HASH && TPM_V2
> +	help
> +	  This option enables measurement of the boot process. 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
> diff --git a/boot/bootm.c b/boot/bootm.c
> index 2eec60ec7b..2685bdbd74 100644
> --- a/boot/bootm.c
> +++ b/boot/bootm.c
> @@ -22,6 +22,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
> @@ -659,6 +660,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;
> +

it looks like your measured boot implementation is hardcoding the 
following PCR indexes:

PCR #8 - kernel image measurement
PCR #9 - initrd measurement
PCR #0 - kernel DTB measurement
PCR #1 - bootargs measurement

I wasn't able to find any specificaton on this measured boot "profile".  
Are you able to provide a reference?

We've implemented our own version of measured boot, which maps 
measurements to different PCR indexes.  In many cases, the data we're 
measuring is also different.

To make this feature more usable by others it would be nice to see a 
more generic interface that would allow the user to specify the PCR 
indexes, and the data to hash into these indexes.  This would allow 
everyone to create their own custom measured boot "profile".  This 
request is probably beyond the scope of your current efforts, but I 
except this implementation to evolve significantly if/when it's accepted.

> +	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.
>    *
> @@ -710,6 +780,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();
> diff --git a/cmd/booti.c b/cmd/booti.c
> index 6ac39193db..659bb10549 100644
> --- a/cmd/booti.c
> +++ b/cmd/booti.c
> @@ -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);
> diff --git a/cmd/bootm.c b/cmd/bootm.c
> index 37c2af96e0..0c4a713e02 100644
> --- a/cmd/bootm.c
> +++ b/cmd/bootm.c
> @@ -161,6 +161,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);
> diff --git a/cmd/bootz.c b/cmd/bootz.c
> index f1423573d2..87922bfc3c 100644
> --- a/cmd/bootz.c
> +++ b/cmd/bootz.c
> @@ -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);
> diff --git a/include/bootm.h b/include/bootm.h
> index 044a4797ed..76e8e38c82 100644
> --- a/include/bootm.h
> +++ b/include/bootm.h
> @@ -55,6 +55,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);
> diff --git a/include/image.h b/include/image.h
> index 7717a4c13d..f7414b5338 100644
> --- a/include/image.h
> +++ b/include/image.h
> @@ -407,6 +407,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)
Eddie James Aug. 7, 2023, 2:43 p.m. UTC | #2
On 8/4/23 13:10, Sean Edmond wrote:
> On 2023-03-08 1:25 p.m., Eddie James wrote:
>> Add a configuration option to measure the boot through the bootm
>> function. Add the measurement state to the booti and bootz paths
>> as well.
>>
>> Signed-off-by: Eddie James <eajames@linux.ibm.com>
>> Reviewed-by: Simon Glass <sjg@chromium.org>
>> ---
>> Changes since v8:
>>   - Added a configuration option to select to ignore any existing
>>     event log. This would only be selected for systems that know
>>     that U-Boot is the first stage bootloader. This is necessary
>>     because the reserved memory region may persist through resets
>>     and so U-Boot attempts to append to the previous boot's log.
>>
>> Changes since v6:
>>   - Added comment for bootm_measure
>>   - Fixed line length in bootm_measure
>>
>>   boot/Kconfig    | 32 +++++++++++++++++++++
>>   boot/bootm.c    | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
>>   cmd/booti.c     |  1 +
>>   cmd/bootm.c     |  2 ++
>>   cmd/bootz.c     |  1 +
>>   include/bootm.h | 11 ++++++++
>>   include/image.h |  1 +
>>   7 files changed, 122 insertions(+)
>>
>> diff --git a/boot/Kconfig b/boot/Kconfig
>> index 5f491625c8..8119519c9f 100644
>> --- a/boot/Kconfig
>> +++ b/boot/Kconfig
>> @@ -629,6 +629,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 to TPM and event log"
>> +    depends on HASH && TPM_V2
>> +    help
>> +      This option enables measurement of the boot process. 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
>> diff --git a/boot/bootm.c b/boot/bootm.c
>> index 2eec60ec7b..2685bdbd74 100644
>> --- a/boot/bootm.c
>> +++ b/boot/bootm.c
>> @@ -22,6 +22,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
>> @@ -659,6 +660,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;
>> +
>
> it looks like your measured boot implementation is hardcoding the 
> following PCR indexes:
>
> PCR #8 - kernel image measurement
> PCR #9 - initrd measurement
> PCR #0 - kernel DTB measurement
> PCR #1 - bootargs measurement


Hi,


Yes, I followed this document as closely as I could: 
https://trustedcomputinggroup.org/wp-content/uploads/TCG_ServerManagementDomainFirmwareProfile_v1p00_11aug2020.pdf

Which provides what should go in what PCR. I can understand users 
wanting a different setup, but as you say, that's probably out of the 
scope of this series.


Thanks,

Eddie


>
> I wasn't able to find any specificaton on this measured boot 
> "profile".  Are you able to provide a reference?
>
> We've implemented our own version of measured boot, which maps 
> measurements to different PCR indexes.  In many cases, the data we're 
> measuring is also different.
>
> To make this feature more usable by others it would be nice to see a 
> more generic interface that would allow the user to specify the PCR 
> indexes, and the data to hash into these indexes.  This would allow 
> everyone to create their own custom measured boot "profile".  This 
> request is probably beyond the scope of your current efforts, but I 
> except this implementation to evolve significantly if/when it's accepted.
>
>> +    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.
>>    *
>> @@ -710,6 +780,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();
>> diff --git a/cmd/booti.c b/cmd/booti.c
>> index 6ac39193db..659bb10549 100644
>> --- a/cmd/booti.c
>> +++ b/cmd/booti.c
>> @@ -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);
>> diff --git a/cmd/bootm.c b/cmd/bootm.c
>> index 37c2af96e0..0c4a713e02 100644
>> --- a/cmd/bootm.c
>> +++ b/cmd/bootm.c
>> @@ -161,6 +161,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);
>> diff --git a/cmd/bootz.c b/cmd/bootz.c
>> index f1423573d2..87922bfc3c 100644
>> --- a/cmd/bootz.c
>> +++ b/cmd/bootz.c
>> @@ -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);
>> diff --git a/include/bootm.h b/include/bootm.h
>> index 044a4797ed..76e8e38c82 100644
>> --- a/include/bootm.h
>> +++ b/include/bootm.h
>> @@ -55,6 +55,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);
>> diff --git a/include/image.h b/include/image.h
>> index 7717a4c13d..f7414b5338 100644
>> --- a/include/image.h
>> +++ b/include/image.h
>> @@ -407,6 +407,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)
>
>
Ilias Apalodimas Aug. 7, 2023, 2:52 p.m. UTC | #3
Hi,

On Mon, 7 Aug 2023 at 17:43, Eddie James <eajames@linux.ibm.com> wrote:
>
>
> On 8/4/23 13:10, Sean Edmond wrote:
> > On 2023-03-08 1:25 p.m., Eddie James wrote:
> >> Add a configuration option to measure the boot through the bootm
> >> function. Add the measurement state to the booti and bootz paths
> >> as well.
> >>
> >> Signed-off-by: Eddie James <eajames@linux.ibm.com>
> >> Reviewed-by: Simon Glass <sjg@chromium.org>
> >> ---
> >> Changes since v8:
> >>   - Added a configuration option to select to ignore any existing
> >>     event log. This would only be selected for systems that know
> >>     that U-Boot is the first stage bootloader. This is necessary
> >>     because the reserved memory region may persist through resets
> >>     and so U-Boot attempts to append to the previous boot's log.
> >>
> >> Changes since v6:
> >>   - Added comment for bootm_measure
> >>   - Fixed line length in bootm_measure
> >>
> >>   boot/Kconfig    | 32 +++++++++++++++++++++
> >>   boot/bootm.c    | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
> >>   cmd/booti.c     |  1 +
> >>   cmd/bootm.c     |  2 ++
> >>   cmd/bootz.c     |  1 +
> >>   include/bootm.h | 11 ++++++++
> >>   include/image.h |  1 +
> >>   7 files changed, 122 insertions(+)
> >>
> >> diff --git a/boot/Kconfig b/boot/Kconfig
> >> index 5f491625c8..8119519c9f 100644
> >> --- a/boot/Kconfig
> >> +++ b/boot/Kconfig
> >> @@ -629,6 +629,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 to TPM and event log"
> >> +    depends on HASH && TPM_V2
> >> +    help
> >> +      This option enables measurement of the boot process. 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
> >> diff --git a/boot/bootm.c b/boot/bootm.c
> >> index 2eec60ec7b..2685bdbd74 100644
> >> --- a/boot/bootm.c
> >> +++ b/boot/bootm.c
> >> @@ -22,6 +22,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
> >> @@ -659,6 +660,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;
> >> +
> >
> > it looks like your measured boot implementation is hardcoding the
> > following PCR indexes:
> >
> > PCR #8 - kernel image measurement
> > PCR #9 - initrd measurement
> > PCR #0 - kernel DTB measurement
> > PCR #1 - bootargs measurement
>
>
> Hi,
>
>
> Yes, I followed this document as closely as I could:
> https://trustedcomputinggroup.org/wp-content/uploads/TCG_ServerManagementDomainFirmwareProfile_v1p00_11aug2020.pdf
>
> Which provides what should go in what PCR. I can understand users
> wanting a different setup, but as you say, that's probably out of the
> scope of this series.
>

Completely out of scope* of the series.  The purpose is follow the TCG
spec.  The minor deviations is our choice of the DTB in PCR1 (but
that's what the spec does for ACPI tables) and the choice for initrd
(which is what we do in the linux kernel).

We can reuse the functions ofc to measure random blobs, but that would
require some kind of config (maybe in a dts??) of what to measure.


Eddie,  I've pinged you in the past.  I rebased and fixed a few issues
of your tree here [0]. Do you plan to resend it at some point?

[0]https://source.denx.de/u-boot/custodians/u-boot-tpm/-/commits/eddie2/

Cheers
/Ilias
>
> Thanks,
>
> Eddie
>
>
> >
> > I wasn't able to find any specificaton on this measured boot
> > "profile".  Are you able to provide a reference?
> >
> > We've implemented our own version of measured boot, which maps
> > measurements to different PCR indexes.  In many cases, the data we're
> > measuring is also different.
> >
> > To make this feature more usable by others it would be nice to see a
> > more generic interface that would allow the user to specify the PCR
> > indexes, and the data to hash into these indexes.  This would allow
> > everyone to create their own custom measured boot "profile".  This
> > request is probably beyond the scope of your current efforts, but I
> > except this implementation to evolve significantly if/when it's accepted.
> >
> >> +    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.
> >>    *
> >> @@ -710,6 +780,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();
> >> diff --git a/cmd/booti.c b/cmd/booti.c
> >> index 6ac39193db..659bb10549 100644
> >> --- a/cmd/booti.c
> >> +++ b/cmd/booti.c
> >> @@ -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);
> >> diff --git a/cmd/bootm.c b/cmd/bootm.c
> >> index 37c2af96e0..0c4a713e02 100644
> >> --- a/cmd/bootm.c
> >> +++ b/cmd/bootm.c
> >> @@ -161,6 +161,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);
> >> diff --git a/cmd/bootz.c b/cmd/bootz.c
> >> index f1423573d2..87922bfc3c 100644
> >> --- a/cmd/bootz.c
> >> +++ b/cmd/bootz.c
> >> @@ -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);
> >> diff --git a/include/bootm.h b/include/bootm.h
> >> index 044a4797ed..76e8e38c82 100644
> >> --- a/include/bootm.h
> >> +++ b/include/bootm.h
> >> @@ -55,6 +55,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);
> >> diff --git a/include/image.h b/include/image.h
> >> index 7717a4c13d..f7414b5338 100644
> >> --- a/include/image.h
> >> +++ b/include/image.h
> >> @@ -407,6 +407,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)
> >
> >
Eddie James Aug. 7, 2023, 2:57 p.m. UTC | #4
On 8/7/23 09:52, Ilias Apalodimas wrote:
> Hi,
>
> On Mon, 7 Aug 2023 at 17:43, Eddie James <eajames@linux.ibm.com> wrote:
>>
>> On 8/4/23 13:10, Sean Edmond wrote:
>>> On 2023-03-08 1:25 p.m., Eddie James wrote:
>>>> Add a configuration option to measure the boot through the bootm
>>>> function. Add the measurement state to the booti and bootz paths
>>>> as well.
>>>>
>>>> Signed-off-by: Eddie James <eajames@linux.ibm.com>
>>>> Reviewed-by: Simon Glass <sjg@chromium.org>
>>>> ---
>>>> Changes since v8:
>>>>    - Added a configuration option to select to ignore any existing
>>>>      event log. This would only be selected for systems that know
>>>>      that U-Boot is the first stage bootloader. This is necessary
>>>>      because the reserved memory region may persist through resets
>>>>      and so U-Boot attempts to append to the previous boot's log.
>>>>
>>>> Changes since v6:
>>>>    - Added comment for bootm_measure
>>>>    - Fixed line length in bootm_measure
>>>>
>>>>    boot/Kconfig    | 32 +++++++++++++++++++++
>>>>    boot/bootm.c    | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>>    cmd/booti.c     |  1 +
>>>>    cmd/bootm.c     |  2 ++
>>>>    cmd/bootz.c     |  1 +
>>>>    include/bootm.h | 11 ++++++++
>>>>    include/image.h |  1 +
>>>>    7 files changed, 122 insertions(+)
>>>>
>>>> diff --git a/boot/Kconfig b/boot/Kconfig
>>>> index 5f491625c8..8119519c9f 100644
>>>> --- a/boot/Kconfig
>>>> +++ b/boot/Kconfig
>>>> @@ -629,6 +629,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 to TPM and event log"
>>>> +    depends on HASH && TPM_V2
>>>> +    help
>>>> +      This option enables measurement of the boot process. 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
>>>> diff --git a/boot/bootm.c b/boot/bootm.c
>>>> index 2eec60ec7b..2685bdbd74 100644
>>>> --- a/boot/bootm.c
>>>> +++ b/boot/bootm.c
>>>> @@ -22,6 +22,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
>>>> @@ -659,6 +660,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;
>>>> +
>>> it looks like your measured boot implementation is hardcoding the
>>> following PCR indexes:
>>>
>>> PCR #8 - kernel image measurement
>>> PCR #9 - initrd measurement
>>> PCR #0 - kernel DTB measurement
>>> PCR #1 - bootargs measurement
>>
>> Hi,
>>
>>
>> Yes, I followed this document as closely as I could:
>> https://trustedcomputinggroup.org/wp-content/uploads/TCG_ServerManagementDomainFirmwareProfile_v1p00_11aug2020.pdf
>>
>> Which provides what should go in what PCR. I can understand users
>> wanting a different setup, but as you say, that's probably out of the
>> scope of this series.
>>
> Completely out of scope* of the series.  The purpose is follow the TCG
> spec.  The minor deviations is our choice of the DTB in PCR1 (but
> that's what the spec does for ACPI tables) and the choice for initrd
> (which is what we do in the linux kernel).
>
> We can reuse the functions ofc to measure random blobs, but that would
> require some kind of config (maybe in a dts??) of what to measure.
>
>
> Eddie,  I've pinged you in the past.  I rebased and fixed a few issues
> of your tree here [0]. Do you plan to resend it at some point?
>
> [0]https://source.denx.de/u-boot/custodians/u-boot-tpm/-/commits/eddie2/


Yes, sorry for the delay. I'll resend it now. Thank you for rebasing!

Thanks,

Eddie


>
> Cheers
> /Ilias
>> Thanks,
>>
>> Eddie
>>
>>
>>> I wasn't able to find any specificaton on this measured boot
>>> "profile".  Are you able to provide a reference?
>>>
>>> We've implemented our own version of measured boot, which maps
>>> measurements to different PCR indexes.  In many cases, the data we're
>>> measuring is also different.
>>>
>>> To make this feature more usable by others it would be nice to see a
>>> more generic interface that would allow the user to specify the PCR
>>> indexes, and the data to hash into these indexes.  This would allow
>>> everyone to create their own custom measured boot "profile".  This
>>> request is probably beyond the scope of your current efforts, but I
>>> except this implementation to evolve significantly if/when it's accepted.
>>>
>>>> +    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.
>>>>     *
>>>> @@ -710,6 +780,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();
>>>> diff --git a/cmd/booti.c b/cmd/booti.c
>>>> index 6ac39193db..659bb10549 100644
>>>> --- a/cmd/booti.c
>>>> +++ b/cmd/booti.c
>>>> @@ -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);
>>>> diff --git a/cmd/bootm.c b/cmd/bootm.c
>>>> index 37c2af96e0..0c4a713e02 100644
>>>> --- a/cmd/bootm.c
>>>> +++ b/cmd/bootm.c
>>>> @@ -161,6 +161,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);
>>>> diff --git a/cmd/bootz.c b/cmd/bootz.c
>>>> index f1423573d2..87922bfc3c 100644
>>>> --- a/cmd/bootz.c
>>>> +++ b/cmd/bootz.c
>>>> @@ -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);
>>>> diff --git a/include/bootm.h b/include/bootm.h
>>>> index 044a4797ed..76e8e38c82 100644
>>>> --- a/include/bootm.h
>>>> +++ b/include/bootm.h
>>>> @@ -55,6 +55,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);
>>>> diff --git a/include/image.h b/include/image.h
>>>> index 7717a4c13d..f7414b5338 100644
>>>> --- a/include/image.h
>>>> +++ b/include/image.h
>>>> @@ -407,6 +407,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)
>>>
diff mbox series

Patch

diff --git a/boot/Kconfig b/boot/Kconfig
index 5f491625c8..8119519c9f 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -629,6 +629,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 to TPM and event log"
+	depends on HASH && TPM_V2
+	help
+	  This option enables measurement of the boot process. 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
diff --git a/boot/bootm.c b/boot/bootm.c
index 2eec60ec7b..2685bdbd74 100644
--- a/boot/bootm.c
+++ b/boot/bootm.c
@@ -22,6 +22,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
@@ -659,6 +660,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.
  *
@@ -710,6 +780,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();
diff --git a/cmd/booti.c b/cmd/booti.c
index 6ac39193db..659bb10549 100644
--- a/cmd/booti.c
+++ b/cmd/booti.c
@@ -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);
diff --git a/cmd/bootm.c b/cmd/bootm.c
index 37c2af96e0..0c4a713e02 100644
--- a/cmd/bootm.c
+++ b/cmd/bootm.c
@@ -161,6 +161,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);
diff --git a/cmd/bootz.c b/cmd/bootz.c
index f1423573d2..87922bfc3c 100644
--- a/cmd/bootz.c
+++ b/cmd/bootz.c
@@ -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);
diff --git a/include/bootm.h b/include/bootm.h
index 044a4797ed..76e8e38c82 100644
--- a/include/bootm.h
+++ b/include/bootm.h
@@ -55,6 +55,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);
diff --git a/include/image.h b/include/image.h
index 7717a4c13d..f7414b5338 100644
--- a/include/image.h
+++ b/include/image.h
@@ -407,6 +407,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)