[PATCH] tpm: Add TPM2_GetTestResult command support
Julia Daxenberger
julia.daxenberger at infineon.com
Mon Jul 3 15:03:09 CEST 2023
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 at infineon.com>
---
cmd/tpm-v2.c | 60 +++++++++++++++++++++++++
drivers/tpm/tpm2_tis_sandbox.c | 47 ++++++++++++++++++-
include/tpm-v2.h | 23 ++++++++++
lib/tpm-v2.c | 82 ++++++++++++++++++++++++++++++++++
test/py/tests/test_tpm2.py | 50 +++++++++++++++++++++
5 files changed, 261 insertions(+), 1 deletion(-)
diff --git a/cmd/tpm-v2.c b/cmd/tpm-v2.c
index d93b83ada9..c6bec7387a 100644
--- a/cmd/tpm-v2.c
+++ b/cmd/tpm-v2.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>
@@ -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_len = TEST_RESULT_DATA_BUFFER_SIZE;
+ u32 test_result;
+ u32 rc;
+
+ ret = get_tpm(&dev);
+ if (ret)
+ return ret;
+
+ if (argc != 1)
+ return CMD_RET_USAGE;
+
+ 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:
+ 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 */
+ 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;
+ }
+
+ /*
+ * 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);
+ response_size = get_unaligned_be32(response + response_size_off);
+
+ if (response_len < 10) {
+ log_debug
+ ("Returned on plausibility check: response_len < 10\n");
+ return -EINVAL;
+ }
+
+ if (response_size > response_len) {
+ log_debug
+ ("Returned on plausibility check: response_size > response_len\n\t"
+ "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;
+}
diff --git a/test/py/tests/test_tpm2.py b/test/py/tests/test_tpm2.py
index d2ad6f9e73..aad1d7a55b 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 at bootlin.com>
+# Copyright (C) 2023 Infineon Technologies AG
import os.path
import pytest
@@ -316,3 +317,52 @@ def test_tpm2_cleanup(u_boot_console):
"""Ensure the TPM is cleared from password or test related configuration."""
force_init(u_boot_console, True)
+
+ at 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 return value of tpm2_get_test_result, if the TPM has not been initialized:
+ - TPM2_RC_INITIALIZE = TPM2_RC_VER1 + 0x0000 = 0x00000100.
+
+ Expected default value for test_result:
+ - TPM_RC_NEEDS_TEST = 0x00000153, if tpm2 self_test has not been executed.
+ - 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.
+ """
+ if is_sandbox(u_boot_console):
+ u_boot_console.restart_uboot()
+
+ skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
+ if skip_test:
+ pytest.skip('skip TPM device test')
+
+ read_res = u_boot_console.run_command('tpm2 get_test_result')
+ output = u_boot_console.run_command('echo $?')
+ assert 'Error: 256' in read_res
+
+ u_boot_console.run_command('tpm2 init')
+ read_res = u_boot_console.run_command('tpm2 get_test_result')
+ output = u_boot_console.run_command('echo $?')
+ assert 'Error: 256' in read_res
+
+ u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
+ 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\n\t0x00000153\tTPM2_RC_NEEDS_TEST' 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\n\t0x' in read_res
+
+ 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\n\t0x00000000\tTPM2_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\n\t0x' in read_res
--
2.34.1
More information about the U-Boot
mailing list