blob: 9c8b1c74384abeee612d09f1535f72a8abb6aa32 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Google, Inc
*/
#include <command.h>
#include <cpu_func.h>
#include <log.h>
#include <time.h>
#include <tpm-v1.h>
#include <linux/printk.h>
#include "tpm-user-utils.h"
#include <tpm_api.h>
/* Prints error and returns on failure */
#define TPM_CHECK(tpm_command) do { \
uint32_t result; \
\
result = (tpm_command); \
if (result != TPM_SUCCESS) { \
printf("TEST FAILED: line %d: " #tpm_command ": 0x%x\n", \
__LINE__, result); \
return result; \
} \
} while (0)
#define INDEX0 0xda70
#define INDEX1 0xda71
#define INDEX2 0xda72
#define INDEX3 0xda73
#define INDEX_INITIALISED 0xda80
#define PHYS_PRESENCE 4
#define PRESENCE 8
static uint32_t TlclStartupIfNeeded(struct udevice *dev)
{
uint32_t result = tpm_startup(dev, TPM_ST_CLEAR);
return result == TPM_INVALID_POSTINIT ? TPM_SUCCESS : result;
}
static int test_timer(struct udevice *dev)
{
printf("get_timer(0) = %lu\n", get_timer(0));
return 0;
}
static uint32_t tpm_get_flags(struct udevice *dev, uint8_t *disable,
uint8_t *deactivated, uint8_t *nvlocked)
{
struct tpm_permanent_flags pflags;
uint32_t result;
result = tpm1_get_permanent_flags(dev, &pflags);
if (result)
return result;
if (disable)
*disable = pflags.disable;
if (deactivated)
*deactivated = pflags.deactivated;
if (nvlocked)
*nvlocked = pflags.nv_locked;
debug("TPM: Got flags disable=%d, deactivated=%d, nvlocked=%d\n",
pflags.disable, pflags.deactivated, pflags.nv_locked);
return 0;
}
static uint32_t tpm_nv_write_value_lock(struct udevice *dev, uint32_t index)
{
debug("TPM: Write lock 0x%x\n", index);
return tpm_nv_write_value(dev, index, NULL, 0);
}
static int tpm_is_owned(struct udevice *dev)
{
uint8_t response[TPM_PUBEK_SIZE];
uint32_t result;
result = tpm_read_pubek(dev, response, sizeof(response));
return result != TPM_SUCCESS;
}
static int test_early_extend(struct udevice *dev)
{
uint8_t value_in[20];
uint8_t value_out[20];
printf("Testing earlyextend ...");
tpm_init(dev);
TPM_CHECK(tpm_startup(dev, TPM_ST_CLEAR));
TPM_CHECK(tpm_continue_self_test(dev));
TPM_CHECK(tpm_pcr_extend(dev, 1, value_in, sizeof(value_in), value_out,
"test"));
printf("done\n");
return 0;
}
static int test_early_nvram(struct udevice *dev)
{
uint32_t x;
printf("Testing earlynvram ...");
tpm_init(dev);
TPM_CHECK(tpm_startup(dev, TPM_ST_CLEAR));
TPM_CHECK(tpm_continue_self_test(dev));
TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
TPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)));
printf("done\n");
return 0;
}
static int test_early_nvram2(struct udevice *dev)
{
uint32_t x;
printf("Testing earlynvram2 ...");
tpm_init(dev);
TPM_CHECK(tpm_startup(dev, TPM_ST_CLEAR));
TPM_CHECK(tpm_continue_self_test(dev));
TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
TPM_CHECK(tpm_nv_write_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)));
printf("done\n");
return 0;
}
static int test_enable(struct udevice *dev)
{
uint8_t disable = 0, deactivated = 0;
printf("Testing enable ...\n");
tpm_init(dev);
TPM_CHECK(TlclStartupIfNeeded(dev));
TPM_CHECK(tpm_self_test_full(dev));
TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL));
printf("\tdisable is %d, deactivated is %d\n", disable, deactivated);
TPM_CHECK(tpm_physical_enable(dev));
TPM_CHECK(tpm_physical_set_deactivated(dev, 0));
TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL));
printf("\tdisable is %d, deactivated is %d\n", disable, deactivated);
if (disable == 1 || deactivated == 1)
printf("\tfailed to enable or activate\n");
printf("\tdone\n");
return 0;
}
#define reboot() do { \
printf("\trebooting...\n"); \
reset_cpu(); \
} while (0)
static int test_fast_enable(struct udevice *dev)
{
uint8_t disable = 0, deactivated = 0;
int i;
printf("Testing fastenable ...\n");
tpm_init(dev);
TPM_CHECK(TlclStartupIfNeeded(dev));
TPM_CHECK(tpm_self_test_full(dev));
TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL));
printf("\tdisable is %d, deactivated is %d\n", disable, deactivated);
for (i = 0; i < 2; i++) {
TPM_CHECK(tpm_force_clear(dev));
TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL));
printf("\tdisable is %d, deactivated is %d\n", disable,
deactivated);
assert(disable == 1 && deactivated == 1);
TPM_CHECK(tpm_physical_enable(dev));
TPM_CHECK(tpm_physical_set_deactivated(dev, 0));
TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL));
printf("\tdisable is %d, deactivated is %d\n", disable,
deactivated);
assert(disable == 0 && deactivated == 0);
}
printf("\tdone\n");
return 0;
}
static int test_global_lock(struct udevice *dev)
{
uint32_t zero = 0;
uint32_t result;
uint32_t x;
printf("Testing globallock ...\n");
tpm_init(dev);
TPM_CHECK(TlclStartupIfNeeded(dev));
TPM_CHECK(tpm_self_test_full(dev));
TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
TPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)));
TPM_CHECK(tpm_nv_write_value(dev, INDEX0, (uint8_t *)&zero,
sizeof(uint32_t)));
TPM_CHECK(tpm_nv_read_value(dev, INDEX1, (uint8_t *)&x, sizeof(x)));
TPM_CHECK(tpm_nv_write_value(dev, INDEX1, (uint8_t *)&zero,
sizeof(uint32_t)));
TPM_CHECK(tpm_set_global_lock(dev));
/* Verifies that write to index0 fails */
x = 1;
result = tpm_nv_write_value(dev, INDEX0, (uint8_t *)&x, sizeof(x));
assert(result == TPM_AREA_LOCKED);
TPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)));
assert(x == 0);
/* Verifies that write to index1 is still possible */
x = 2;
TPM_CHECK(tpm_nv_write_value(dev, INDEX1, (uint8_t *)&x, sizeof(x)));
TPM_CHECK(tpm_nv_read_value(dev, INDEX1, (uint8_t *)&x, sizeof(x)));
assert(x == 2);
/* Turns off PP */
tpm_tsc_physical_presence(dev, PHYS_PRESENCE);
/* Verifies that write to index1 fails */
x = 3;
result = tpm_nv_write_value(dev, INDEX1, (uint8_t *)&x, sizeof(x));
assert(result == TPM_BAD_PRESENCE);
TPM_CHECK(tpm_nv_read_value(dev, INDEX1, (uint8_t *)&x, sizeof(x)));
assert(x == 2);
printf("\tdone\n");
return 0;
}
static int test_lock(struct udevice *dev)
{
printf("Testing lock ...\n");
tpm_init(dev);
tpm_startup(dev, TPM_ST_CLEAR);
tpm_self_test_full(dev);
tpm_tsc_physical_presence(dev, PRESENCE);
tpm_nv_write_value_lock(dev, INDEX0);
printf("\tLocked 0x%x\n", INDEX0);
printf("\tdone\n");
return 0;
}
static void initialise_spaces(struct udevice *dev)
{
uint32_t zero = 0;
uint32_t perm = TPM_NV_PER_WRITE_STCLEAR | TPM_NV_PER_PPWRITE;
printf("\tInitialising spaces\n");
tpm1_nv_set_locked(dev); /* useful only the first time */
tpm1_nv_define_space(dev, INDEX0, perm, 4);
tpm_nv_write_value(dev, INDEX0, (uint8_t *)&zero, 4);
tpm1_nv_define_space(dev, INDEX1, perm, 4);
tpm_nv_write_value(dev, INDEX1, (uint8_t *)&zero, 4);
tpm1_nv_define_space(dev, INDEX2, perm, 4);
tpm_nv_write_value(dev, INDEX2, (uint8_t *)&zero, 4);
tpm1_nv_define_space(dev, INDEX3, perm, 4);
tpm_nv_write_value(dev, INDEX3, (uint8_t *)&zero, 4);
perm = TPM_NV_PER_READ_STCLEAR | TPM_NV_PER_WRITE_STCLEAR |
TPM_NV_PER_PPWRITE;
tpm1_nv_define_space(dev, INDEX_INITIALISED, perm, 1);
}
static int test_readonly(struct udevice *dev)
{
uint8_t c;
uint32_t index_0, index_1, index_2, index_3;
int read0, read1, read2, read3;
printf("Testing readonly ...\n");
tpm_init(dev);
tpm_startup(dev, TPM_ST_CLEAR);
tpm_self_test_full(dev);
tpm_tsc_physical_presence(dev, PRESENCE);
/*
* Checks if initialisation has completed by trying to read-lock a
* space that's created at the end of initialisation
*/
if (tpm_nv_read_value(dev, INDEX_INITIALISED, &c, 0) == TPM_BADINDEX) {
/* The initialisation did not complete */
initialise_spaces(dev);
}
/* Checks if spaces are OK or messed up */
read0 = tpm_nv_read_value(dev, INDEX0, (uint8_t *)&index_0,
sizeof(index_0));
read1 = tpm_nv_read_value(dev, INDEX1, (uint8_t *)&index_1,
sizeof(index_1));
read2 = tpm_nv_read_value(dev, INDEX2, (uint8_t *)&index_2,
sizeof(index_2));
read3 = tpm_nv_read_value(dev, INDEX3, (uint8_t *)&index_3,
sizeof(index_3));
if (read0 || read1 || read2 || read3) {
printf("Invalid contents\n");
return 0;
}
/*
* Writes space, and locks it. Then attempts to write again.
* I really wish I could use the imperative.
*/
index_0 += 1;
if (tpm_nv_write_value(dev, INDEX0, (uint8_t *)&index_0,
sizeof(index_0)) !=
TPM_SUCCESS) {
pr_err("\tcould not write index 0\n");
}
tpm_nv_write_value_lock(dev, INDEX0);
if (tpm_nv_write_value(dev, INDEX0, (uint8_t *)&index_0,
sizeof(index_0)) ==
TPM_SUCCESS)
pr_err("\tindex 0 is not locked\n");
printf("\tdone\n");
return 0;
}
static int test_redefine_unowned(struct udevice *dev)
{
uint32_t perm;
uint32_t result;
uint32_t x;
printf("Testing redefine_unowned ...");
tpm_init(dev);
TPM_CHECK(TlclStartupIfNeeded(dev));
TPM_CHECK(tpm_self_test_full(dev));
TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
assert(!tpm_is_owned(dev));
/* Ensures spaces exist. */
TPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)));
TPM_CHECK(tpm_nv_read_value(dev, INDEX1, (uint8_t *)&x, sizeof(x)));
/* Redefines spaces a couple of times. */
perm = TPM_NV_PER_PPWRITE | TPM_NV_PER_GLOBALLOCK;
TPM_CHECK(tpm1_nv_define_space(dev, INDEX0, perm,
2 * sizeof(uint32_t)));
TPM_CHECK(tpm1_nv_define_space(dev, INDEX0, perm, sizeof(uint32_t)));
perm = TPM_NV_PER_PPWRITE;
TPM_CHECK(tpm1_nv_define_space(dev, INDEX1, perm,
2 * sizeof(uint32_t)));
TPM_CHECK(tpm1_nv_define_space(dev, INDEX1, perm, sizeof(uint32_t)));
/* Sets the global lock */
tpm_set_global_lock(dev);
/* Verifies that index0 cannot be redefined */
result = tpm1_nv_define_space(dev, INDEX0, perm, sizeof(uint32_t));
assert(result == TPM_AREA_LOCKED);
/* Checks that index1 can */
TPM_CHECK(tpm1_nv_define_space(dev, INDEX1, perm,
2 * sizeof(uint32_t)));
TPM_CHECK(tpm1_nv_define_space(dev, INDEX1, perm, sizeof(uint32_t)));
/* Turns off PP */
tpm_tsc_physical_presence(dev, PHYS_PRESENCE);
/* Verifies that neither index0 nor index1 can be redefined */
result = tpm1_nv_define_space(dev, INDEX0, perm, sizeof(uint32_t));
assert(result == TPM_BAD_PRESENCE);
result = tpm1_nv_define_space(dev, INDEX1, perm, sizeof(uint32_t));
assert(result == TPM_BAD_PRESENCE);
printf("done\n");
return 0;
}
#define PERMPPGL (TPM_NV_PER_PPWRITE | TPM_NV_PER_GLOBALLOCK)
#define PERMPP TPM_NV_PER_PPWRITE
static int test_space_perm(struct udevice *dev)
{
uint32_t perm;
printf("Testing spaceperm ...");
tpm_init(dev);
TPM_CHECK(TlclStartupIfNeeded(dev));
TPM_CHECK(tpm_continue_self_test(dev));
TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
TPM_CHECK(tpm_get_permissions(dev, INDEX0, &perm));
assert((perm & PERMPPGL) == PERMPPGL);
TPM_CHECK(tpm_get_permissions(dev, INDEX1, &perm));
assert((perm & PERMPP) == PERMPP);
printf("done\n");
return 0;
}
static int test_startup(struct udevice *dev)
{
uint32_t result;
printf("Testing startup ...\n");
tpm_init(dev);
result = tpm_startup(dev, TPM_ST_CLEAR);
if (result != 0 && result != TPM_INVALID_POSTINIT)
printf("\ttpm startup failed with 0x%x\n", result);
result = tpm_get_flags(dev, NULL, NULL, NULL);
if (result != 0)
printf("\ttpm getflags failed with 0x%x\n", result);
printf("\texecuting SelfTestFull\n");
tpm_self_test_full(dev);
result = tpm_get_flags(dev, NULL, NULL, NULL);
if (result != 0)
printf("\ttpm getflags failed with 0x%x\n", result);
printf("\tdone\n");
return 0;
}
/*
* Runs [op] and ensures it returns success and doesn't run longer than
* [time_limit] in milliseconds.
*/
#define TTPM_CHECK(op, time_limit) do { \
ulong start, time; \
uint32_t __result; \
\
start = get_timer(0); \
__result = op; \
if (__result != TPM_SUCCESS) { \
printf("\t" #op ": error 0x%x\n", __result); \
return -1; \
} \
time = get_timer(start); \
printf("\t" #op ": %lu ms\n", time); \
if (time > (ulong)time_limit) { \
printf("\t" #op " exceeded " #time_limit " ms\n"); \
} \
} while (0)
static int test_timing(struct udevice *dev)
{
uint8_t in[20], out[20];
uint32_t x;
printf("Testing timing ...");
tpm_init(dev);
TTPM_CHECK(TlclStartupIfNeeded(dev), 50);
TTPM_CHECK(tpm_continue_self_test(dev), 100);
TTPM_CHECK(tpm_self_test_full(dev), 1000);
TTPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE), 100);
TTPM_CHECK(tpm_nv_write_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)),
100);
TTPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)),
100);
TTPM_CHECK(tpm_pcr_extend(dev, 0, in, sizeof(in), out, "test"), 200);
TTPM_CHECK(tpm_set_global_lock(dev), 50);
TTPM_CHECK(tpm_tsc_physical_presence(dev, PHYS_PRESENCE), 100);
printf("done\n");
return 0;
}
#define TPM_MAX_NV_WRITES_NOOWNER 64
static int test_write_limit(struct udevice *dev)
{
uint32_t result;
int i;
printf("Testing writelimit ...\n");
tpm_init(dev);
TPM_CHECK(TlclStartupIfNeeded(dev));
TPM_CHECK(tpm_self_test_full(dev));
TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE));
TPM_CHECK(tpm_force_clear(dev));
TPM_CHECK(tpm_physical_enable(dev));
TPM_CHECK(tpm_physical_set_deactivated(dev, 0));
for (i = 0; i < TPM_MAX_NV_WRITES_NOOWNER + 2; i++) {
printf("\twriting %d\n", i);
result = tpm_nv_write_value(dev, INDEX0, (uint8_t *)&i,
sizeof(i));
switch (result) {
case TPM_SUCCESS:
break;
case TPM_MAXNVWRITES:
assert(i >= TPM_MAX_NV_WRITES_NOOWNER);
break;
default:
pr_err("\tunexpected error code %d (0x%x)\n",
result, result);
}
}
/* Reset write count */
TPM_CHECK(tpm_force_clear(dev));
TPM_CHECK(tpm_physical_enable(dev));
TPM_CHECK(tpm_physical_set_deactivated(dev, 0));
/* Try writing again. */
TPM_CHECK(tpm_nv_write_value(dev, INDEX0, (uint8_t *)&i, sizeof(i)));
printf("\tdone\n");
return 0;
}
#define VOIDTEST(XFUNC) \
int do_test_##XFUNC(struct cmd_tbl *cmd_tbl, int flag, int argc, \
char *const argv[]) \
{ \
struct udevice *dev; \
int ret; \
\
ret = get_tpm(&dev); \
if (ret) \
return ret; \
return test_##XFUNC(dev); \
}
#define VOIDENT(XNAME) \
U_BOOT_CMD_MKENT(XNAME, 0, 1, do_test_##XNAME, "", ""),
VOIDTEST(early_extend)
VOIDTEST(early_nvram)
VOIDTEST(early_nvram2)
VOIDTEST(enable)
VOIDTEST(fast_enable)
VOIDTEST(global_lock)
VOIDTEST(lock)
VOIDTEST(readonly)
VOIDTEST(redefine_unowned)
VOIDTEST(space_perm)
VOIDTEST(startup)
VOIDTEST(timing)
VOIDTEST(write_limit)
VOIDTEST(timer)
static struct cmd_tbl cmd_cros_tpm_sub[] = {
VOIDENT(early_extend)
VOIDENT(early_nvram)
VOIDENT(early_nvram2)
VOIDENT(enable)
VOIDENT(fast_enable)
VOIDENT(global_lock)
VOIDENT(lock)
VOIDENT(readonly)
VOIDENT(redefine_unowned)
VOIDENT(space_perm)
VOIDENT(startup)
VOIDENT(timing)
VOIDENT(write_limit)
VOIDENT(timer)
};
static int do_tpmtest(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
struct cmd_tbl *c;
int i;
printf("argc = %d, argv = ", argc);
for (i = 0; i < argc; i++)
printf(" %s", argv[i]);
printf("\n------\n");
argc--;
argv++;
c = find_cmd_tbl(argv[0], cmd_cros_tpm_sub,
ARRAY_SIZE(cmd_cros_tpm_sub));
return c ? c->cmd(cmdtp, flag, argc, argv) : cmd_usage(cmdtp);
}
U_BOOT_CMD(tpmtest, 2, 1, do_tpmtest, "TPM tests",
"\n\tearly_extend\n"
"\tearly_nvram\n"
"\tearly_nvram2\n"
"\tenable\n"
"\tfast_enable\n"
"\tglobal_lock\n"
"\tlock\n"
"\treadonly\n"
"\tredefine_unowned\n"
"\tspace_perm\n"
"\tstartup\n"
"\ttiming\n"
"\twrite_limit\n");