[PATCH v2 2/6] tpm: add TPM2_PCR_Allocate command

Ilias Apalodimas ilias.apalodimas at linaro.org
Thu Jan 23 07:20:12 CET 2025


On Fri, 17 Jan 2025 at 18:40, Raymond Mao <raymond.mao at linaro.org> wrote:
>
> TPM2_PCR_Allocate command is required to reconfigure a TPM device
> to enable or disable algorithms in run-time, thus this patch introduces
> the implementation of PCR allocate APIs and adds related cmd functions
> for testing.
>
> To test the feature, ensure that TPM is started up.
> Run pcr_allocate command to turn on/off an algorithm, multiple calls
> are supported and all changes will be cached:
> `tpm2 pcr_allocate <algorithm_name> <on|off>`
> Run startup command with argument 'off' to shutdown the TPM.
> `tpm2 startup TPM2_SU_CLEAR off`
> Reboot the board via `reset` to activate the changes.
>
> Signed-off-by: Raymond Mao <raymond.mao at linaro.org>
> ---
> changes in v2
> - Fixed typos.
> - Print the algorithm name directly.
> - Check if the algorithm supported by the TPM device before allocate.
>
>  cmd/tpm-v2.c     | 114 +++++++++++++++++++++++++++++++++++++++++++
>  include/tpm-v2.h |  29 +++++++++++
>  lib/tpm-v2.c     | 124 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 267 insertions(+)
>
> diff --git a/cmd/tpm-v2.c b/cmd/tpm-v2.c
> index a6d57ee7c4..a62862e94f 100644
> --- a/cmd/tpm-v2.c
> +++ b/cmd/tpm-v2.c
> @@ -232,6 +232,106 @@ unmap_data:
>         return report_return_code(rc);
>  }
>
> +static u32 select_mask(u32 mask, enum tpm2_algorithms algo, bool select)
> +{
> +       size_t i;
> +
> +       for (i = 0; i < ARRAY_SIZE(hash_algo_list); i++) {
> +               if (hash_algo_list[i].hash_alg != algo)
> +                       continue;
> +
> +               if (select)
> +                       mask |= hash_algo_list[i].hash_mask;
> +               else
> +                       mask &= ~hash_algo_list[i].hash_mask;
> +
> +               break;
> +       }
> +
> +       return mask;
> +}
> +
> +static bool
> +is_algo_in_pcrs(enum tpm2_algorithms algo, struct tpml_pcr_selection *pcrs)
> +{
> +       size_t i;
> +
> +       for (i = 0; i < pcrs->count; i++) {
> +               if (algo == pcrs->selection[i].hash)
> +                       return true;
> +       }
> +
> +       return false;
> +}
> +
> +static int do_tpm2_pcrallocate(struct cmd_tbl *cmdtp, int flag, int argc,
> +                              char *const argv[])
> +{
> +       struct udevice *dev;
> +       int ret;
> +       enum tpm2_algorithms algo;
> +       const char *pw = (argc < 4) ? NULL : argv[3];
> +       const ssize_t pw_sz = pw ? strlen(pw) : 0;
> +       static struct tpml_pcr_selection pcr = { 0 };
> +       u32 pcr_len = 0;
> +       bool bon = false;
> +       static u32 mask;
> +       int i;
> +
> +       /* argv[1]: algorithm (bank), argv[2]: on/off */
> +       if (argc < 3 || argc > 4)
> +               return CMD_RET_USAGE;
> +
> +       if (!strcasecmp("on", argv[2]))
> +               bon = true;
> +       else if (strcasecmp("off", argv[2]))
> +               return CMD_RET_USAGE;
> +
> +       algo = tpm2_name_to_algorithm(argv[1]);
> +       if (algo == -EINVAL)
> +               return CMD_RET_USAGE;
> +
> +       ret = get_tpm(&dev);
> +       if (ret)
> +               return ret;
> +
> +       if (!pcr.count) {
> +               /*
> +                * Get current active algorithms (banks), PCRs and mask via the
> +                * first call
> +                */
> +               ret = tpm2_get_pcr_info(dev, &pcr);
> +               if (ret)
> +                       return ret;
> +
> +               for (i = 0; i < pcr.count; i++) {
> +                       struct tpms_pcr_selection *sel = &pcr.selection[i];
> +                       const char *name;
> +
> +                       if (!tpm2_is_active_bank(sel))
> +                               continue;
> +
> +                       mask = select_mask(mask, sel->hash, true);
> +                       name = tpm2_algorithm_name(sel->hash);
> +                       if (name)
> +                               printf("Active bank[%d]: %s\n", i, name);
> +               }
> +       }
> +
> +       if (!is_algo_in_pcrs(algo, &pcr)) {
> +               printf("%s is not supported by the tpm device\n", argv[1]);
> +               return CMD_RET_USAGE;
> +       }
> +
> +       mask = select_mask(mask, algo, bon);
> +       ret = tpm2_pcr_config_algo(dev, mask, &pcr, &pcr_len);
> +       if (ret)
> +               return ret;
> +
> +       return report_return_code(tpm2_send_pcr_allocate(dev, pw, pw_sz, &pcr,
> +                                                        pcr_len));
> +}
> +
>  static int do_tpm_dam_reset(struct cmd_tbl *cmdtp, int flag, int argc,
>                             char *const argv[])
>  {
> @@ -401,6 +501,7 @@ static struct cmd_tbl tpm2_commands[] = {
>                          do_tpm_pcr_setauthpolicy, "", ""),
>         U_BOOT_CMD_MKENT(pcr_setauthvalue, 0, 1,
>                          do_tpm_pcr_setauthvalue, "", ""),
> +       U_BOOT_CMD_MKENT(pcr_allocate, 0, 1, do_tpm2_pcrallocate, "", ""),
>  };
>
>  struct cmd_tbl *get_tpm2_commands(unsigned int *size)
> @@ -481,4 +582,17 @@ U_BOOT_CMD(tpm2, CONFIG_SYS_MAXARGS, 1, do_tpm, "Issue a TPMv2.x command",
>  "    <pcr>: index of the PCR\n"
>  "    <key>: secret to protect the access of PCR #<pcr>\n"
>  "    <password>: optional password of the PLATFORM hierarchy\n"
> +"pcr_allocate <algorithm> <on/off> [<password>]\n"
> +"    Issue a TPM2_PCR_Allocate Command to reconfig PCR bank algorithm.\n"
> +"    <algorithm> is one of:\n"
> +"        * sha1\n"
> +"        * sha256\n"
> +"        * sha384\n"
> +"        * sha512\n"
> +"    <on|off> is one of:\n"
> +"        * on  - Select all available PCRs associated with the specified\n"
> +"                algorithm (bank)\n"
> +"        * off - Clear all available PCRs associated with the specified\n"
> +"                algorithm (bank)\n"
> +"    <password>: optional password\n"
>  );
> diff --git a/include/tpm-v2.h b/include/tpm-v2.h
> index f66a8e1bf4..af3158f6e4 100644
> --- a/include/tpm-v2.h
> +++ b/include/tpm-v2.h
> @@ -230,6 +230,7 @@ enum tpm2_command_codes {
>         TPM2_CC_PCR_READ        = 0x017E,
>         TPM2_CC_PCR_EXTEND      = 0x0182,
>         TPM2_CC_PCR_SETAUTHVAL  = 0x0183,
> +       TPM2_CC_PCR_ALLOCATE    = 0x012B,
>         TPM2_CC_SHUTDOWN        = 0x0145,
>  };
>
> @@ -702,6 +703,34 @@ u32 tpm2_report_state(struct udevice *dev, uint vendor_cmd, uint vendor_subcmd,
>  u32 tpm2_enable_nvcommits(struct udevice *dev, uint vendor_cmd,
>                           uint vendor_subcmd);
>
> +/**
> + * tpm2_pcr_config_algo() - Allocate the active PCRs. Requires reboot
> + *
> + * @dev                TPM device
> + * @algo_mask  Mask of the algorithms
> + * @pcr                PCR structure for allocation
> + * @pcr_len    Actual PCR data length
> + *
> + * Return: code of the operation
> + */
> +u32 tpm2_pcr_config_algo(struct udevice *dev, u32 algo_mask,
> +                        struct tpml_pcr_selection *pcr, u32 *pcr_len);
> +
> +/**
> + * tpm2_send_pcr_allocate() - Send PCR allocate command. Requires reboot
> + *
> + * @dev                TPM device
> + * @pw         Platform password
> + * @pw_sz      Length of the password
> + * @pcr                PCR structure for allocation
> + * @pcr_len    Actual PCR data length
> + *
> + * Return: code of the operation
> + */
> +u32 tpm2_send_pcr_allocate(struct udevice *dev, const char *pw,
> +                          const ssize_t pw_sz, struct tpml_pcr_selection *pcr,
> +                          u32 pcr_len);
> +
>  /**
>   * tpm2_auto_start() - start up the TPM and perform selftests.
>   *                     If a testable function has not been tested and is
> diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c
> index 3946ac2dbb..045b5dd9eb 100644
> --- a/lib/tpm-v2.c
> +++ b/lib/tpm-v2.c
> @@ -400,6 +400,130 @@ u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
>         return 0;
>  }
>
> +u32 tpm2_pcr_config_algo(struct udevice *dev, u32 algo_mask,
> +                        struct tpml_pcr_selection *pcr, u32 *pcr_len)
> +{
> +       int i;
> +
> +       if (pcr->count > TPM2_NUM_PCR_BANKS)
> +               return TPM_LIB_ERROR;
> +
> +       *pcr_len = sizeof(pcr->count);
> +
> +       for (i = 0; i < pcr->count; i++) {
> +               struct tpms_pcr_selection *sel = &pcr->selection[i];
> +               u8 pad = 0;
> +               int j;
> +
> +               if (sel->size_of_select > TPM2_PCR_SELECT_MAX)
> +                       return TPM_LIB_ERROR;
> +
> +               /*
> +                * Found the algorithm (bank) that matches, and enable all PCR
> +                * bits.
> +                * TODO: only select the bits needed
> +                */
> +               for (j = 0; j < ARRAY_SIZE(hash_algo_list); j++) {
> +                       if (hash_algo_list[j].hash_alg != sel->hash)
> +                               continue;
> +
> +                       if (algo_mask & hash_algo_list[j].hash_mask)
> +                               pad = 0xff;
> +               }
> +
> +               for (j = 0; j < sel->size_of_select; j++)
> +                       sel->pcr_select[j] = pad;
> +
> +               log_info("set bank[%d] %s %s\n", i,
> +                        tpm2_algorithm_name(sel->hash),
> +                        tpm2_is_active_bank(sel) ? "on" : "off");
> +
> +               *pcr_len += sizeof(sel->hash) + sizeof(sel->size_of_select) +
> +                           sel->size_of_select;
> +       }
> +
> +       return 0;
> +}
> +
> +u32 tpm2_send_pcr_allocate(struct udevice *dev, const char *pw,
> +                          const ssize_t pw_sz, struct tpml_pcr_selection *pcr,
> +                          u32 pcr_len)
> +{
> +       /* Length of the message header, up to start of password */
> +       uint offset = 27;
> +       u8 command_v2[COMMAND_BUFFER_SIZE] = {
> +               tpm_u16(TPM2_ST_SESSIONS),   /* TAG */
> +               tpm_u32(offset + pw_sz + pcr_len), /* Length */
> +               tpm_u32(TPM2_CC_PCR_ALLOCATE),  /* Command code */
> +
> +               /* handles 4 bytes */
> +               tpm_u32(TPM2_RH_PLATFORM),      /* Primary platform seed */
> +
> +               /* AUTH_SESSION */
> +               tpm_u32(9 + pw_sz),             /* Authorization size */
> +               tpm_u32(TPM2_RS_PW),            /* Session handle */
> +               tpm_u16(0),                     /* Size of <nonce> */
> +                                               /* <nonce> (if any) */
> +               0,                              /* Attributes: Cont/Excl/Rst */
> +               tpm_u16(pw_sz),                 /* Size of <hmac/password> */
> +               /* STRING(pw)                      <hmac/password> (if any) */
> +
> +               /* TPML_PCR_SELECTION */
> +       };
> +       u8 response[COMMAND_BUFFER_SIZE];
> +       size_t response_len = COMMAND_BUFFER_SIZE;
> +       u32 i;
> +       int ret;
> +
> +       /*
> +        * Fill the command structure starting from the first buffer:
> +        * the password (if any)
> +        */
> +       if (pack_byte_string(command_v2, sizeof(command_v2), "s", offset, pw,
> +                            pw_sz))
> +               return TPM_LIB_ERROR;
> +
> +       offset += pw_sz;
> +
> +       /* Pack the count field */
> +       if (pack_byte_string(command_v2, sizeof(command_v2), "d", offset, pcr->count))
> +               return TPM_LIB_ERROR;
> +
> +       offset += sizeof(pcr->count);
> +
> +       /* Pack each tpms_pcr_selection */
> +       for (i = 0; i < pcr->count; i++) {
> +               struct tpms_pcr_selection *sel = &pcr->selection[i];
> +
> +               /* Pack hash (16-bit) */
> +               if (pack_byte_string(command_v2, sizeof(command_v2), "w", offset,
> +                                    sel->hash))
> +                       return TPM_LIB_ERROR;
> +
> +               offset += sizeof(sel->hash);
> +
> +               /* Pack size_of_select (8-bit) */
> +               if (pack_byte_string(command_v2, sizeof(command_v2), "b", offset,
> +                                    sel->size_of_select))
> +                       return TPM_LIB_ERROR;
> +
> +               offset += sizeof(sel->size_of_select);
> +
> +               /* Pack pcr_select array */
> +               if (pack_byte_string(command_v2, sizeof(command_v2), "s", offset,
> +                                    sel->pcr_select, sel->size_of_select))
> +                       return TPM_LIB_ERROR;
> +
> +               offset += sel->size_of_select;
> +       }
> +
> +       ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
> +       if (!ret)
> +               tpm_init(dev);
> +
> +       return ret;
> +}
> +
>  static int tpm2_get_num_pcr(struct udevice *dev, u32 *num_pcr)
>  {
>         u8 response[(sizeof(struct tpms_capability_data) -
> --
> 2.25.1
>

Acked-by: Ilias Apalodimas <ilias.apalodimas at linaro.org>


More information about the U-Boot mailing list