Merge tag 'tpm-next-28022023' of https://source.denx.de/u-boot/custodians/u-boot-tpm into next

TPM auto startup and testing:

Due to U-Boot's lazy binding we always relied on command line tools to
initialize the TPM subsystem and devices.  One exception is the EFI
subsystem.  When compiled with TCG2 measured boot support the TPM was
automatically initialized.

However that init was not complete.  The TCG specs [0] (and specifically
§12.3 Self-test modes) describe how self-tests on the device should be
performed.  This PR is adding an extra API function, that can be used to
initialize the TPM2.0 properly.

Simon added the equivalent for TPM1.2 and refactored the DM tests to
include the new funtion.

[0] https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-1-Architecture-01.07-2014-03-13.pdf
This commit is contained in:
Tom Rini
2023-02-28 09:50:16 -05:00
11 changed files with 191 additions and 8 deletions

View File

@@ -1367,6 +1367,10 @@
compatible = "sandbox,tpm2";
};
tpm {
compatible = "google,sandbox-tpm";
};
uart0: serial {
compatible = "sandbox,serial";
bootph-all;

View File

@@ -810,7 +810,7 @@ static int sandbox_tpm2_open(struct udevice *dev)
struct sandbox_tpm2 *tpm = dev_get_priv(dev);
if (tpm->init_done)
return -EIO;
return -EBUSY;
tpm->init_done = true;

View File

@@ -94,7 +94,7 @@ struct tpm_ops {
* close().
*
* @dev: Device to open
* @return 0 ok OK, -ve on error
* @return 0 ok OK, -EBUSY if already opened, other -ve on other error
*/
int (*open)(struct udevice *dev);

View File

@@ -591,4 +591,15 @@ u32 tpm_set_global_lock(struct udevice *dev);
*/
u32 tpm1_resume(struct udevice *dev);
/**
* tpm1_auto_start() - start up the TPM
*
* This does not do a self test.
*
* @dev TPM device
* Return: TPM2_RC_SUCCESS, on success, or when the TPM returns
* TPM_INVALID_POSTINIT; TPM_FAILEDSELFTEST, if the TPM is in failure state
*/
u32 tpm1_auto_start(struct udevice *dev);
#endif /* __TPM_V1_H */

View File

@@ -690,4 +690,20 @@ 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_auto_start() - start up the TPM and perform selftests.
* If a testable function has not been tested and is
* requested the TPM2 will return TPM_RC_NEEDS_TEST.
*
* @param dev TPM device
* Return: 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.
* TPM2_RC_INITIALIZE, if the TPM has not gone through the Startup
* sequence
*/
u32 tpm2_auto_start(struct udevice *dev);
#endif /* __TPM_V2_H */

View File

@@ -331,4 +331,12 @@ static inline bool tpm_is_v2(struct udevice *dev)
return IS_ENABLED(CONFIG_TPM_V2) && tpm_get_version(dev) == TPM_V2;
}
/**
* tpm_auto_start() - start up the TPM and perform selftests
*
* @param dev TPM device
* Return: return code of the operation (0 = success)
*/
u32 tpm_auto_start(struct udevice *dev);
#endif /* __TPM_API_H */

View File

@@ -2495,7 +2495,7 @@ efi_status_t efi_tcg2_register(void)
}
/* initialize the TPM as early as possible. */
err = tpm_startup(dev, TPM_ST_CLEAR);
err = tpm_auto_start(dev);
if (err) {
log_err("TPM startup failed\n");
goto fail;

View File

@@ -69,6 +69,20 @@ u32 tpm1_continue_self_test(struct udevice *dev)
return tpm_sendrecv_command(dev, command, NULL, NULL);
}
u32 tpm1_auto_start(struct udevice *dev)
{
u32 rc;
rc = tpm1_startup(dev, TPM_ST_CLEAR);
/* continue on if the TPM is already inited */
if (rc && rc != TPM_INVALID_POSTINIT)
return rc;
rc = tpm1_self_test_full(dev);
return rc;
}
u32 tpm1_clear_and_reenable(struct udevice *dev)
{
u32 ret;

View File

@@ -44,6 +44,23 @@ u32 tpm2_self_test(struct udevice *dev, enum tpm2_yes_no full_test)
return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
}
u32 tpm2_auto_start(struct udevice *dev)
{
u32 rc;
rc = tpm2_self_test(dev, TPMI_YES);
if (rc == TPM2_RC_INITIALIZE) {
rc = tpm2_startup(dev, TPM2_SU_CLEAR);
if (rc)
return rc;
rc = tpm2_self_test(dev, TPMI_YES);
}
return rc;
}
u32 tpm2_clear(struct udevice *dev, u32 handle, const char *pw,
const ssize_t pw_sz)
{

View File

@@ -35,6 +35,27 @@ u32 tpm_startup(struct udevice *dev, enum tpm_startup_type mode)
}
}
u32 tpm_auto_start(struct udevice *dev)
{
u32 rc;
/*
* the tpm_init() will return -EBUSY if the init has already happened
* The selftest and startup code can run multiple times with no side
* effects
*/
rc = tpm_init(dev);
if (rc && rc != -EBUSY)
return rc;
if (tpm_is_v1(dev))
return tpm1_auto_start(dev);
else if (tpm_is_v2(dev))
return tpm2_auto_start(dev);
else
return -ENOSYS;
}
u32 tpm_resume(struct udevice *dev)
{
if (tpm_is_v1(dev))

View File

@@ -11,24 +11,116 @@
#include <test/test.h>
#include <test/ut.h>
/* Basic test of the TPM uclass */
/*
* get_tpm_version() - Get a TPM of the given version
*
* @version: Version to get
* @devp: Returns the TPM device
* Returns: 0 if OK, -ENODEV if not found
*/
static int get_tpm_version(enum tpm_version version, struct udevice **devp)
{
struct udevice *dev;
/*
* For now we have to probe each TPM, since the version is set up in
* of_to_plat(). We could require TPMs to declare their version when
* probed, to avoid this
*/
uclass_foreach_dev_probe(UCLASS_TPM, dev) {
if (tpm_get_version(dev) == version) {
*devp = dev;
return 0;
}
}
return -ENODEV;
}
/* Basic test of initing a TPM */
static int test_tpm_init(struct unit_test_state *uts, enum tpm_version version)
{
struct udevice *dev;
/* check probe success */
ut_assertok(get_tpm_version(version, &dev));
ut_assertok(tpm_init(dev));
return 0;
}
static int dm_test_tpm(struct unit_test_state *uts)
{
ut_assertok(test_tpm_init(uts, TPM_V1));
ut_assertok(test_tpm_init(uts, TPM_V2));
return 0;
}
DM_TEST(dm_test_tpm, UT_TESTF_SCAN_FDT);
/* Test report_state */
static int dm_test_tpm_report_state(struct unit_test_state *uts)
{
struct udevice *dev;
char buf[50];
/* check probe success */
ut_assertok(uclass_first_device_err(UCLASS_TPM, &dev));
ut_assert(tpm_is_v2(dev));
ut_assertok(get_tpm_version(TPM_V2, &dev));
ut_assert(tpm_report_state(dev, buf, sizeof(buf)));
ut_asserteq_str("init_done=0", buf);
ut_assertok(tpm_init(dev));
ut_assertok(tpm_auto_start(dev));
ut_assert(tpm_report_state(dev, buf, sizeof(buf)));
ut_asserteq_str("init_done=1", buf);
return 0;
}
DM_TEST(dm_test_tpm, UT_TESTF_SCAN_FDT);
DM_TEST(dm_test_tpm_report_state, UT_TESTF_SCAN_FDT);
/**
* test_tpm_autostart() - check the tpm_auto_start() call
*
* @uts: Unit test state
* @version: TPM version to use
* @reinit: true to call tpm_init() first
* Returns 0 if OK, non-zero on failure
*/
static int test_tpm_autostart(struct unit_test_state *uts,
enum tpm_version version, bool reinit)
{
struct udevice *dev;
/* check probe success */
ut_assertok(get_tpm_version(version, &dev));
if (reinit)
ut_assertok(tpm_init(dev));
/*
* tpm_auto_start will rerun tpm_init() if reinit, but handles the
* -EBUSY return code internally.
*/
ut_assertok(tpm_auto_start(dev));
return 0;
}
static int dm_test_tpm_autostart(struct unit_test_state *uts)
{
ut_assertok(test_tpm_autostart(uts, TPM_V1, false));
ut_assertok(test_tpm_autostart(uts, TPM_V2, false));
return 0;
}
DM_TEST(dm_test_tpm_autostart, UT_TESTF_SCAN_FDT);
static int dm_test_tpm_autostart_reinit(struct unit_test_state *uts)
{
ut_assertok(test_tpm_autostart(uts, TPM_V1, true));
ut_assertok(test_tpm_autostart(uts, TPM_V2, true));
return 0;
}
DM_TEST(dm_test_tpm_autostart_reinit, UT_TESTF_SCAN_FDT);