[PATCH v2 1/2] tpm: Add TPM2_GetTestResult command support

Julia Daxenberger julia.daxenberger at infineon.com
Wed Aug 2 10:35:20 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>
---
Changes in v2:
- Apply style changes suggested by Ilias Apalodimas
<ilias.apalodimas at linaro.org>
- Move test cases reliant on restart_uboot() to separate patch in
response to Simon Glass <sjg at 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 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_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 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,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 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,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 at 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)
+
+ 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 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



More information about the U-Boot mailing list