
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.
Signed-off-by: Julia Daxenberger julia.daxenberger@infineon.com --- Changes in v2: - Apply style changes suggested by Ilias Apalodimas ilias.apalodimas@linaro.org - Move test cases reliant on restart_uboot() to separate patch in response to Simon Glass sjg@chromium.org - Change test result data to be read directly from the response - Fix minor style issues
cmd/tpm-v2.c | 60 +++++++++++++++++++++ drivers/tpm/tpm2_tis_sandbox.c | 51 +++++++++++++++++- include/tpm-v2.h | 23 ++++++++ lib/tpm-v2.c | 97 ++++++++++++++++++++++++++++++++++ test/py/tests/test_tpm2.py | 22 ++++++++ 5 files changed, 252 insertions(+), 1 deletion(-)
diff --git a/cmd/tpm-v2.c b/cmd/tpm-v2.c index 7e479b9dfe..acbd7171f5 100644 --- a/cmd/tpm-v2.c +++ b/cmd/tpm-v2.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2018 Bootlin * Author: Miquel Raynal miquel.raynal@bootlin.com + * Copyright (C) 2023 Infineon Technologies AG */
#include <common.h> @@ -13,6 +14,8 @@ #include <tpm-v2.h> #include "tpm-user-utils.h"
+#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_size = sizeof(data); + u32 test_result; + u32 rc; + + if (argc != 1) + return CMD_RET_USAGE; + + ret = get_tpm(&dev); + if (ret) + return ret; + + rc = tpm2_get_test_result(dev, data, &data_size, &test_result); + if (rc) + goto out; + + printf("Test Result:\n0x%08X ", test_result); + switch (test_result) { + case TPM2_RC_SUCCESS: + printf("TPM2_RC_SUCCESS\n"); + break; + case TPM2_RC_FAILURE: + printf("TPM2_RC_FAILURE\n"); + break; + case TPM2_RC_NEEDS_TEST: + printf("TPM2_RC_NEEDS_TEST\n"); + break; + case TPM2_RC_TESTING: + printf("TPM2_RC_TESTING\n"); + break; + } + + if (!data_size) { + printf("No Test Result Data available\n"); + goto out; + } + + printf("Test Result Data of Self Test:\n0x"); + for (int i = 0; i < data_size; 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, "", ""), @@ -375,6 +429,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) @@ -453,4 +509,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..420961944d 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@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,46 @@ 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[13] = { 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. + * The first two bytes contain the length of the data + */ + data[1] = 13 - 2; + 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..0df08e6aaa 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_size 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_size, + u32 *test_result); + #endif /* __TPM_V2_H */ diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c index 9ab5b46df1..7689832213 100644 --- a/lib/tpm-v2.c +++ b/lib/tpm-v2.c @@ -2,12 +2,14 @@ /* * Copyright (c) 2018 Bootlin * Author: Miquel Raynal miquel.raynal@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,98 @@ 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_size, 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_reserved_size = sizeof(response); + u32 response_size; /* Size of response bytestream */ + unsigned int data_size_offset; + unsigned int data_offset; + unsigned int test_result_offset; + size_t data_response_size; + u32 min_response_size; + + ret = tpm_sendrecv_command(dev, command_v2, response, + &response_reserved_size); + log_debug("ret=%s, %x\n", dev->name, ret); + if (ret) + return ret; + + /* + * Get responseSize from return bytestream, switch endianness: big->host + * endianness + * In the response buffer, the responseSize is located after: + * tag (u16) + * The minimal response contains: + * tag (u16), response size (u32), response code (u32) + */ + response_size = get_unaligned_be32(response + sizeof(u16)); + min_response_size = sizeof(u16) + sizeof(u32) + sizeof(u32); + if (response_size < min_response_size) { + log_debug + ("Response Size: %d\nMinimal Response Size: %d\n", + response_size, min_response_size); + return TPM_LIB_ERROR; + } + if (response_size > response_reserved_size) { + log_debug + ("Response Size: %d\nReserved Response Size: %ld\n", + response_size, response_reserved_size); + return TPM_LIB_ERROR; + } + + /* + * Copy the test result data to buffer + * In the response buffer, the test result data is located after: + * tag (u16), response size (u32), response code (u32) and the data + * buffer size (u16). + */ + data_size_offset = sizeof(u16) + sizeof(u32) + sizeof(u32); + data_response_size = get_unaligned_be16(response + data_size_offset); + data_offset = data_size_offset + sizeof(u16); + + /* + * Checks, if the data buffer size data_response_size corresponds with + * the actual data size + */ + if (data_response_size != response_size - data_offset - sizeof(u32)) { + log_debug + ("Data Size from response: %ld\nActual Response Size: %ld\n", + data_response_size, + (response_size - data_offset - sizeof(u32))); + return TPM_LIB_ERROR; + } + + /* Checks, if the reserved data buffer size is insufficient */ + if (data_response_size > *data_size) { + log_debug + ("Data Size from response: %ld\nReserved Buffer Length: %ld\n", + data_response_size, *data_size); + return TPM_LIB_ERROR; + } + + memcpy(data, &response[data_offset], data_response_size); + + /* + * Get testResult from return bytestream, switch endianness: big->host + * endianness + * In the response buffer, the test result is located after: + * test result data (*data_size). + */ + test_result_offset = data_offset + data_response_size; + *test_result = get_unaligned_be32(response + test_result_offset); + + /* Return data length */ + *data_size = data_response_size; + + return 0; +} diff --git a/test/py/tests/test_tpm2.py b/test/py/tests/test_tpm2.py index fce689cd99..96bed36c5f 100644 --- a/test/py/tests/test_tpm2.py +++ b/test/py/tests/test_tpm2.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # Copyright (c) 2018, Bootlin # Author: Miquel Raynal miquel.raynal@bootlin.com +# Copyright (C) 2023 Infineon Technologies AG
import os.path import pytest @@ -313,3 +314,24 @@ def test_tpm2_cleanup(u_boot_console): """Ensure the TPM is cleared from password or test related configuration."""
force_init(u_boot_console, True) + +@pytest.mark.buildconfigspec('cmd_tpm_v2') +def test_tpm2_get_test_result(u_boot_console): + """Execute a TPM_GetTestResult command. + + Ask the TPM to get the test result of the self test. + Display the Test Result and Test Result Data. + + Expected default value for test_result: + - TPM_RC_SUCCESS = 0x00000000, if testing is complete without functional failures. + + There is no expected default value for the test result data because it would depend on the chip + used. The test result data is therefore not tested. + """ + u_boot_console.run_command('tpm2 self_test full') + read_res = u_boot_console.run_command('tpm2 get_test_result') + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + assert 'Test Result:\r\r\n0x00000000 TPM2_RC_SUCCESS' in read_res + """ Assert console output. Test Result Data can not be tested, as it is vendor specific""" + assert 'Test Result Data of Self Test:\r\r\n0x' in read_res -- 2.34.1