[PATCH] tpm: Add TPM2_GetTestResult command support
Ilias Apalodimas
ilias.apalodimas at linaro.org
Fri Jul 28 13:41:53 CEST 2023
Hi Julia,
Apologies for the late response, I was on vacation.
On Mon, 3 Jul 2023 at 16:03, Julia Daxenberger
<julia.daxenberger at infineon.com> wrote:
>
> Add TPM2_GetTestResult command support and change the command file and the
> help accordingly. Add Python tests and sandbox driver functionality.
>
> The TPM2_GetTestResult command is performed after the TPM2_SelfTest command
> and returns manufacturer-specific information regarding the results of the
> self-test and an indication of the test status.
What the code does is quite obvious, can you please elaborate on why
we need this applied?
[...]
> +#define TEST_RESULT_DATA_BUFFER_SIZE 256
> +
> static int do_tpm2_startup(struct cmd_tbl *cmdtp, int flag, int argc,
> char *const argv[])
> {
> @@ -356,6 +359,57 @@ static int do_tpm_pcr_setauthvalue(struct cmd_tbl *cmdtp, int flag,
> key, key_sz));
> }
>
> +static int do_tpm2_get_test_result(struct cmd_tbl *cmdtp, int flag, int argc,
> + char *const argv[])
> +{
> + struct udevice *dev;
> + int ret;
> + u8 data[TEST_RESULT_DATA_BUFFER_SIZE];
> + size_t data_len = TEST_RESULT_DATA_BUFFER_SIZE;
= sizeof(data)
> + u32 test_result;
> + u32 rc;
> +
> + ret = get_tpm(&dev);
> + if (ret)
> + return ret;
> +
> + if (argc != 1)
> + return CMD_RET_USAGE;
Can you move this one above the get_tpm?
> +
> + rc = tpm2_get_test_result(dev, data, &data_len, &test_result);
> + if (rc)
> + goto out;
> +
> + printf("Test Result:\n\t0x%08X", test_result);
> + switch (test_result) {
> + case 0x0000:
We have enum tpm2_return_codes. Please add the missing cases there
and use that instead of magic values
> + printf("\tTPM2_RC_SUCCESS\n");
> + break;
> + case 0x0101:
> + printf("\tTPM2_RC_FAILURE\n");
> + break;
> + case 0x0153:
> + printf("\tTPM2_RC_NEEDS_TEST\n");
> + break;
> + case 0x090A:
> + printf("\tTPM2_RC_TESTING\n");
> + break;
> + }
> +
> + if (!data_len) {
> + printf("No Test Result Data available\n");
> + goto out;
> + }
> +
> + printf("Test Result Data of Self Test:\n\t0x");
> + for (int i = 0; i < data_len; i++)
> + printf("%02X", data[i]);
> + printf("\n");
> +
> +out:
> + return report_return_code(rc);
> +}
> +
> static struct cmd_tbl tpm2_commands[] = {
> U_BOOT_CMD_MKENT(device, 0, 1, do_tpm_device, "", ""),
> U_BOOT_CMD_MKENT(info, 0, 1, do_tpm_info, "", ""),
> @@ -374,6 +428,8 @@ 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(get_test_result, 0, 1,
> + do_tpm2_get_test_result, "", ""),
> };
>
> struct cmd_tbl *get_tpm2_commands(unsigned int *size)
> @@ -447,4 +503,8 @@ 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"
> +"get_test_result\n"
> +" Show manufacturer-specific information regarding the results of a\n"
> +" self-test and an indication of the test status.\n"
> +
> );
> diff --git a/drivers/tpm/tpm2_tis_sandbox.c b/drivers/tpm/tpm2_tis_sandbox.c
> index e4004cfcca..17979cd33a 100644
> --- a/drivers/tpm/tpm2_tis_sandbox.c
> +++ b/drivers/tpm/tpm2_tis_sandbox.c
> @@ -2,6 +2,7 @@
> /*
> * Copyright (c) 2018, Bootlin
> * Author: Miquel Raynal <miquel.raynal at bootlin.com>
> + * Copyright (C) 2023 Infineon Technologies AG
> */
>
> #include <common.h>
> @@ -231,6 +232,7 @@ static int sandbox_tpm2_check_session(struct udevice *dev, u32 command, u16 tag,
> case TPM2_CC_SELF_TEST:
> case TPM2_CC_GET_CAPABILITY:
> case TPM2_CC_PCR_READ:
> + case TPM2_CC_GET_TEST_RESULT:
> if (tag != TPM2_ST_NO_SESSIONS) {
> printf("No session required for command 0x%x\n",
> command);
> @@ -364,6 +366,13 @@ static int sandbox_tpm2_check_readyness(struct udevice *dev, int command)
> if (!tpm->startup_done)
> return TPM2_RC_INITIALIZE;
>
> + break;
> + case TPM2_CC_GET_TEST_RESULT:
> + if (!tpm->init_done || !tpm->startup_done)
> + return TPM2_RC_INITIALIZE;
> + if (!tpm->tests_done)
> + return TPM2_RC_NEEDS_TEST;
> +
> break;
> default:
> /* Skip this, since the startup may have happened in SPL
> @@ -458,7 +467,7 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf,
> command = get_unaligned_be32(sent);
> sent += sizeof(command);
> rc = sandbox_tpm2_check_readyness(dev, command);
> - if (rc) {
> + if (rc && rc != TPM2_RC_NEEDS_TEST) {
> sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
> return 0;
> }
> @@ -778,6 +787,42 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf,
> *recv_len = 12;
> memset(recvbuf, '\0', *recv_len);
> break;
> +
> + case TPM2_CC_GET_TEST_RESULT: {
> + u32 testresult = 0;
> + u8 data[10] = { 0 };
> +
> + /* Check readiness */
> + testresult = sandbox_tpm2_check_readyness(dev, command);
> +
> + /* Write tag */
> + put_unaligned_be16(tag, recv);
> + recv += sizeof(tag);
> +
> + /* Ignore length for now */
> + recv += sizeof(u32);
> +
> + /* Write return code */
> + put_unaligned_be32(rc, recv);
> + recv += sizeof(rc);
> +
> + /* Write manufacturer-specific test result data */
> + memcpy(recv, data, sizeof(data));
> + recv += sizeof(data);
> +
> + /* Write test result */
> + put_unaligned_be32(testresult, recv);
> + recv += sizeof(testresult);
> +
> + /* Add trailing \0 */
> + *recv = '\0';
> +
> + /* Write response length */
> + *recv_len = recv - recvbuf;
> + put_unaligned_be32(*recv_len, recvbuf + sizeof(tag));
> +
> + break;
> + }
> default:
> printf("TPM2 command %02x unknown in Sandbox\n", command);
> rc = TPM2_RC_COMMAND_CODE;
> diff --git a/include/tpm-v2.h b/include/tpm-v2.h
> index 2b6980e441..f6ec430316 100644
> --- a/include/tpm-v2.h
> +++ b/include/tpm-v2.h
> @@ -3,6 +3,7 @@
> * Defines APIs and structures that allow software to interact with a
> * TPM2 device
> *
> + * Copyright (C) 2023 Infineon Technologies AG
> * Copyright (c) 2020 Linaro
> * Copyright (c) 2018 Bootlin
> *
> @@ -276,6 +277,7 @@ enum tpm2_handles {
> * @TPM2_CC_DAM_PARAMETERS: TPM2_DictionaryAttackParameters().
> * @TPM2_CC_GET_CAPABILITY: TPM2_GetCapibility().
> * @TPM2_CC_GET_RANDOM: TPM2_GetRandom().
> + * @TPM2_CC_GET_TEST_RESULT: TPM2_GetTestResult().
> * @TPM2_CC_PCR_READ: TPM2_PCR_Read().
> * @TPM2_CC_PCR_EXTEND: TPM2_PCR_Extend().
> * @TPM2_CC_PCR_SETAUTHVAL: TPM2_PCR_SetAuthValue().
> @@ -296,6 +298,7 @@ enum tpm2_command_codes {
> TPM2_CC_NV_READ = 0x014E,
> TPM2_CC_GET_CAPABILITY = 0x017A,
> TPM2_CC_GET_RANDOM = 0x017B,
> + TPM2_CC_GET_TEST_RESULT = 0x017C,
> TPM2_CC_PCR_READ = 0x017E,
> TPM2_CC_PCR_EXTEND = 0x0182,
> TPM2_CC_PCR_SETAUTHVAL = 0x0183,
> @@ -706,4 +709,24 @@ u32 tpm2_enable_nvcommits(struct udevice *dev, uint vendor_cmd,
> */
> u32 tpm2_auto_start(struct udevice *dev);
>
> +/**
> + * Issue a TPM2_GetTestResult command.
> + *
> + * @dev TPM device
> + * @data output buffer for manufacturer-specific test result data
> + * @data_len input/output parameter
> + * input: size of data buffer, output: size of test result data
> + * @test_result output parameter: test result response code:
> + * TPM2_RC_NEEDS_TEST, if TPM2 self-test has not been executed and
> + * a testable function has not been tested
> + * TPM2_RC_TESTING, if TPM2 self-test is in progress.
> + * TPM2_RC_SUCCESS, if testing of all functions is complete without
> + * functional failures.
> + * TPM2_RC_FAILURE, if any test failed.
> + *
> + * Return: code of the operation
> + */
> +u32 tpm2_get_test_result(struct udevice *dev, void *data, size_t *data_len,
> + u32 *test_result);
> +
> #endif /* __TPM_V2_H */
> diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c
> index 9ab5b46df1..a3a29048e3 100644
> --- a/lib/tpm-v2.c
> +++ b/lib/tpm-v2.c
> @@ -2,12 +2,14 @@
> /*
> * Copyright (c) 2018 Bootlin
> * Author: Miquel Raynal <miquel.raynal at bootlin.com>
> + * Copyright (C) 2023 Infineon Technologies AG
> */
>
> #include <common.h>
> #include <dm.h>
> #include <tpm-common.h>
> #include <tpm-v2.h>
> +#include <asm/unaligned.h>
> #include <linux/bitops.h>
> #include "tpm-utils.h"
>
> @@ -742,3 +744,83 @@ u32 tpm2_enable_nvcommits(struct udevice *dev, uint vendor_cmd,
>
> return 0;
> }
> +
> +u32 tpm2_get_test_result(struct udevice *dev, void *data,
> + size_t *data_len, u32 *test_result)
> +{
> + const u8 command_v2[COMMAND_BUFFER_SIZE] = {
> + /* header 10 bytes */
> + tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
> + tpm_u32(10), /* Length */
> + tpm_u32(TPM2_CC_GET_TEST_RESULT), /* Command code */
> + };
> +
> + int ret;
> + u8 response[COMMAND_BUFFER_SIZE];
> + size_t response_len = COMMAND_BUFFER_SIZE;
> + u32 response_size; /* Size of response bytestream */
reposnse_size and response_len is a bit confusing. Please find some
names that indicate the first one is the reserved size while the
second is the actual
> + unsigned int response_size_off;
> + unsigned int data_off;
> + unsigned int test_result_off;
> + size_t data_buff_len;
> +
> + ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
> +
> + if (ret) {
> + log_debug("Returned on: ret = tpm_sendrecv_command()\n");
> + return ret;
Isn't this an error? Can we use return log_msg_ret("test", ret); or
something along those lines?
> + }
> +
> + /*
> + * Get responseSize from return bytestream, switch endianness: big->little
> + * In the response buffer, the responseSize is located after:
> + * tag (u16)
> + */
> + response_size_off = sizeof(u16);
Keep the comment and just do response + sizeof(u16)
> + response_size = get_unaligned_be32(response + response_size_off);
> +
> + if (response_len < 10) {
> + log_debug
> + ("Returned on plausibility check: response_len < 10\n");
Why is this an error?
> + return -EINVAL;
> + }
> +
> + if (response_size > response_len) {
> + log_debug
> + ("Returned on plausibility check: response_size > response_len\n\t"
Can you remove this line and just report the size mismatch? It should
be obvious why it's wrong.
> + "Response Size: %d\n\tResponse Length: %ld\n",
> + response_size, response_len);
> + return -E2BIG;
> + }
> +
> + /*
> + * Copy the test result data to buffer, transfer data_len
> + * In the response buffer, the test result data is located after:
> + * tag (u16), response size (u32), response code (u32).
> + */
> + data_off = sizeof(u16) + sizeof(u32) + sizeof(u32);
> + data_buff_len = *data_len;
> + *data_len = response_size - data_off - sizeof(u32);
> +
> + /* Checks, if the reserved data buffer size is insufficient */
> + if (*data_len > data_buff_len) {
> + log_debug
> + ("Returned on plausibility check: data_len > data_buff_len\n\t"
> + "Data Length: %ld\n\tData Buffer Length: %ld\n\t",
> + *data_len, data_buff_len);
> + return -E2BIG;
> + }
> +
> + memcpy(data, &response[data_off], *data_len);
> +
> + /*
> + * Get testResult from return bytestream, switch endianness: big->little
> + * In the response buffer, the test result is located after:
> + * tag (u16), response size (u32), response code (u32),
> + * test result data (*data_len).
> + */
> + test_result_off = data_off + *data_len;
> + *test_result = get_unaligned_be32(response + test_result_off);
> +
> + return 0;
> +}
[...]
Thanks
/Ilias
More information about the U-Boot
mailing list