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

Eddie James eajames at linux.ibm.com
Mon Aug 7 16:57:05 CEST 2023


On 8/7/23 09:52, Ilias Apalodimas wrote:
> Hi,
>
> On Mon, 7 Aug 2023 at 17:43, Eddie James <eajames at 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 at linux.ibm.com>
>>>> Reviewed-by: Simon Glass <sjg at 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)
>>>


More information about the U-Boot mailing list