blob: ac067ef856728da76068e8b274371681db3c7b4e [file] [log] [blame]
/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <common.h>
#include <command.h>
#include <environment.h>
#include <tlcl.h>
/* Prints error and returns on failure */
#define TPM_CHECK(tpm_command) do { \
uint32_t result; \
if ((result = (tpm_command)) != 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_INITIALIZED 0xda80
static uint32_t TlclStartupIfNeeded(void) {
uint32_t result = TlclStartup();
return result == TPM_E_INVALID_POSTINIT ? TPM_SUCCESS : result;
}
/* u-boot internal timer test
*/
static int test_timer(void)
{
printf("get_timer(0) = %lu\n", get_timer(0));
return 0;
}
/* vboot_reference/tests/tpm_lite tests
*/
static int test_early_extend(void)
{
uint8_t value_in[20];
uint8_t value_out[20];
printf("Testing earlyextend ...");
TlclLibInit();
TPM_CHECK(TlclStartup());
TPM_CHECK(TlclContinueSelfTest());
TPM_CHECK(TlclExtend(1, value_in, value_out));
printf("done\n");
return 0;
}
static int test_early_nvram(void)
{
uint32_t x;
printf("Testing earlynvram ...");
TlclLibInit();
TPM_CHECK(TlclStartup());
TPM_CHECK(TlclContinueSelfTest());
TPM_CHECK(TlclAssertPhysicalPresence());
TPM_CHECK(TlclRead(INDEX0, (uint8_t*) &x, sizeof(x)));
printf("done\n");
return 0;
}
static int test_early_nvram2(void)
{
uint32_t x;
printf("Testing earlynvram2 ...");
TlclLibInit();
TPM_CHECK(TlclStartup());
TPM_CHECK(TlclContinueSelfTest());
TPM_CHECK(TlclAssertPhysicalPresence());
TPM_CHECK(TlclWrite(INDEX0, (uint8_t*) &x, sizeof(x)));
printf("done\n");
return 0;
}
static int test_enable(void)
{
uint8_t disable, deactivated;
printf("Testing enable ...\n");
TlclLibInit();
TPM_CHECK(TlclStartupIfNeeded());
TPM_CHECK(TlclSelfTestFull());
TPM_CHECK(TlclAssertPhysicalPresence());
TPM_CHECK(TlclGetFlags(&disable, &deactivated, NULL));
printf("\tdisable is %d, deactivated is %d\n", disable, deactivated);
TPM_CHECK(TlclSetEnable());
TPM_CHECK(TlclSetDeactivated(0));
TPM_CHECK(TlclGetFlags(&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 assert(XCOND) do { \
if (!(XCOND)) { \
printf("\tassert : " #XCOND "\n"); \
} \
} while (0)
#define reboot() do { \
printf("\trebooting...\n"); \
reset_cpu(0); \
} while (0)
static int test_fast_enable(void)
{
uint8_t disable, deactivated;
int i;
printf("Testing fastenable ...\n");
TlclLibInit();
TPM_CHECK(TlclStartupIfNeeded());
TPM_CHECK(TlclSelfTestFull());
TPM_CHECK(TlclAssertPhysicalPresence());
TPM_CHECK(TlclGetFlags(&disable, &deactivated, NULL));
printf("\tdisable is %d, deactivated is %d\n", disable, deactivated);
for (i = 0; i < 2; i++) {
TPM_CHECK(TlclForceClear());
TPM_CHECK(TlclGetFlags(&disable, &deactivated, NULL));
printf("\tdisable is %d, deactivated is %d\n", disable,
deactivated);
assert(disable == 1 && deactivated == 1);
TPM_CHECK(TlclSetEnable());
TPM_CHECK(TlclSetDeactivated(0));
TPM_CHECK(TlclGetFlags(&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(void)
{
uint32_t zero = 0;
uint32_t result;
uint32_t x;
printf("Testing globallock ...\n");
TlclLibInit();
TPM_CHECK(TlclStartupIfNeeded());
TPM_CHECK(TlclSelfTestFull());
TPM_CHECK(TlclAssertPhysicalPresence());
TPM_CHECK(TlclRead(INDEX0, (uint8_t*) &x, sizeof(x)));
TPM_CHECK(TlclWrite(INDEX0, (uint8_t*) &zero, sizeof(uint32_t)));
TPM_CHECK(TlclRead(INDEX1, (uint8_t*) &x, sizeof(x)));
TPM_CHECK(TlclWrite(INDEX1, (uint8_t*) &zero, sizeof(uint32_t)));
TPM_CHECK(TlclSetGlobalLock());
// Verifies that write to index0 fails.
x = 1;
result = TlclWrite(INDEX0, (uint8_t*) &x, sizeof(x));
assert(result == TPM_E_AREA_LOCKED);
TPM_CHECK(TlclRead(INDEX0, (uint8_t*) &x, sizeof(x)));
assert(x == 0);
// Verifies that write to index1 is still possible.
x = 2;
TPM_CHECK(TlclWrite(INDEX1, (uint8_t*) &x, sizeof(x)));
TPM_CHECK(TlclRead(INDEX1, (uint8_t*) &x, sizeof(x)));
assert(x == 2);
// Turns off PP.
TlclLockPhysicalPresence();
// Verifies that write to index1 fails.
x = 3;
result = TlclWrite(INDEX1, (uint8_t*) &x, sizeof(x));
assert(result == TPM_E_BAD_PRESENCE);
TPM_CHECK(TlclRead(INDEX1, (uint8_t*) &x, sizeof(x)));
assert(x == 2);
printf("\tdone\n");
return 0;
}
static int test_lock(void)
{
printf("Testing lock ...\n");
TlclLibInit();
TlclStartup();
TlclSelfTestFull();
TlclAssertPhysicalPresence();
TlclWriteLock(INDEX0);
printf("\tLocked 0x%x\n", INDEX0);
printf("\tdone\n");
return 0;
}
static void initialize_spaces(void) {
uint32_t zero = 0;
uint32_t perm = TPM_NV_PER_WRITE_STCLEAR | TPM_NV_PER_PPWRITE;
printf("\tInitializing spaces\n");
TlclSetNvLocked(); /* useful only the first time */
TlclDefineSpace(INDEX0, perm, 4);
TlclWrite(INDEX0, (uint8_t *) &zero, 4);
TlclDefineSpace(INDEX1, perm, 4);
TlclWrite(INDEX1, (uint8_t *) &zero, 4);
TlclDefineSpace(INDEX2, perm, 4);
TlclWrite(INDEX2, (uint8_t *) &zero, 4);
TlclDefineSpace(INDEX3, perm, 4);
TlclWrite(INDEX3, (uint8_t *) &zero, 4);
perm = TPM_NV_PER_READ_STCLEAR | TPM_NV_PER_WRITE_STCLEAR |
TPM_NV_PER_PPWRITE;
TlclDefineSpace(INDEX_INITIALIZED, perm, 1);
}
static void enter_recovery_mode(void) {
printf("entering recovery mode");
reboot();
}
static int test_readonly(void)
{
uint8_t c;
uint32_t index_0, index_1, index_2, index_3;
int read_0, read_1, read_2, read_3;
printf("Testing readonly ...\n");
TlclLibInit();
TlclStartup();
TlclSelfTestFull();
TlclAssertPhysicalPresence();
/* Checks if initialization has completed by trying to read-lock a space
* that's created at the end of initialization.
*/
if (TlclRead(INDEX_INITIALIZED, &c, 0) == TPM_E_BADINDEX) {
/* The initialization did not complete.
*/
initialize_spaces();
}
/* Checks if spaces are OK or messed up.
*/
read_0 = TlclRead(INDEX0, (uint8_t*) &index_0, sizeof(index_0));
read_1 = TlclRead(INDEX1, (uint8_t*) &index_1, sizeof(index_1));
read_2 = TlclRead(INDEX2, (uint8_t*) &index_2, sizeof(index_2));
read_3 = TlclRead(INDEX3, (uint8_t*) &index_3, sizeof(index_3));
if (read_0 != TPM_SUCCESS || read_1 != TPM_SUCCESS || read_2 !=
TPM_SUCCESS || read_3 != TPM_SUCCESS) {
enter_recovery_mode();
}
/* Writes space, and locks it. Then attempts to write again.
* I really wish I could use the imperative.
*/
index_0 += 1;
if (TlclWrite(INDEX0, (uint8_t*) &index_0, sizeof(index_0) !=
TPM_SUCCESS)) {
error("\tcould not write index 0\n");
}
TlclWriteLock(INDEX0);
if (TlclWrite(INDEX0, (uint8_t*) &index_0, sizeof(index_0)) ==
TPM_SUCCESS) {
error("\tindex 0 is not locked\n");
}
printf("\tdone\n");
return 0;
}
static int test_redefine_unowned(void)
{
uint32_t perm;
uint32_t result;
uint32_t x;
printf("Testing redefine_unowned ...");
TlclLibInit();
TPM_CHECK(TlclStartupIfNeeded());
TPM_CHECK(TlclSelfTestFull());
TPM_CHECK(TlclAssertPhysicalPresence());
assert(!TlclIsOwned());
/* Ensures spaces exist. */
TPM_CHECK(TlclRead(INDEX0, (uint8_t*) &x, sizeof(x)));
TPM_CHECK(TlclRead(INDEX1, (uint8_t*) &x, sizeof(x)));
/* Redefines spaces a couple of times. */
perm = TPM_NV_PER_PPWRITE | TPM_NV_PER_GLOBALLOCK;
TPM_CHECK(TlclDefineSpace(INDEX0, perm, 2 * sizeof(uint32_t)));
TPM_CHECK(TlclDefineSpace(INDEX0, perm, sizeof(uint32_t)));
perm = TPM_NV_PER_PPWRITE;
TPM_CHECK(TlclDefineSpace(INDEX1, perm, 2 * sizeof(uint32_t)));
TPM_CHECK(TlclDefineSpace(INDEX1, perm, sizeof(uint32_t)));
// Sets the global lock.
TlclSetGlobalLock();
// Verifies that index0 cannot be redefined.
result = TlclDefineSpace(INDEX0, perm, sizeof(uint32_t));
assert(result == TPM_E_AREA_LOCKED);
// Checks that index1 can.
TPM_CHECK(TlclDefineSpace(INDEX1, perm, 2 * sizeof(uint32_t)));
TPM_CHECK(TlclDefineSpace(INDEX1, perm, sizeof(uint32_t)));
// Turns off PP.
TlclLockPhysicalPresence();
// Verifies that neither index0 nor index1 can be redefined.
result = TlclDefineSpace(INDEX0, perm, sizeof(uint32_t));
assert(result == TPM_E_BAD_PRESENCE);
result = TlclDefineSpace(INDEX1, perm, sizeof(uint32_t));
assert(result == TPM_E_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(void)
{
uint32_t perm;
printf("Testing spaceperm ...");
TlclLibInit();
TPM_CHECK(TlclStartupIfNeeded());
TPM_CHECK(TlclContinueSelfTest());
TPM_CHECK(TlclAssertPhysicalPresence());
TPM_CHECK(TlclGetPermissions(INDEX0, &perm));
assert((perm & PERMPPGL) == PERMPPGL);
TPM_CHECK(TlclGetPermissions(INDEX1, &perm));
assert((perm & PERMPP) == PERMPP);
printf("done\n");
return 0;
}
static int test_startup(void)
{
uint32_t result;
printf("Testing startup ...\n");
TlclLibInit();
result = TlclStartup();
if (result != 0) {
printf("\ttpm startup failed with 0x%x\n", result);
}
result = TlclGetFlags(NULL, NULL, NULL);
if (result != 0) {
printf("\ttpm getflags failed with 0x%x\n", result);
}
printf("\texecuting SelfTestFull\n");
TlclSelfTestFull();
result = TlclGetFlags(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(void)
{
uint32_t x;
uint8_t in[20], out[20];
printf("Testing timing ...");
TlclLibInit();
TTPM_CHECK(TlclStartupIfNeeded(), 50);
TTPM_CHECK(TlclContinueSelfTest(), 100);
TTPM_CHECK(TlclSelfTestFull(), 1000);
TTPM_CHECK(TlclAssertPhysicalPresence(), 100);
TTPM_CHECK(TlclWrite(INDEX0, (uint8_t*) &x, sizeof(x)), 100);
TTPM_CHECK(TlclRead(INDEX0, (uint8_t*) &x, sizeof(x)), 100);
TTPM_CHECK(TlclExtend(0, in, out), 200);
TTPM_CHECK(TlclSetGlobalLock(), 50);
TTPM_CHECK(TlclLockPhysicalPresence(), 100);
printf("done\n");
return 0;
}
#define TPM_MAX_NV_WRITES_NOOWNER 64
static int test_write_limit(void)
{
printf("Testing writelimit ...\n");
int i;
uint32_t result;
TlclLibInit();
TPM_CHECK(TlclStartupIfNeeded());
TPM_CHECK(TlclSelfTestFull());
TPM_CHECK(TlclAssertPhysicalPresence());
TPM_CHECK(TlclForceClear());
TPM_CHECK(TlclSetEnable());
TPM_CHECK(TlclSetDeactivated(0));
for (i = 0; i < TPM_MAX_NV_WRITES_NOOWNER + 2; i++) {
printf("\twriting %d\n", i);
if ((result = TlclWrite(INDEX0, (uint8_t*)&i, sizeof(i))) !=
TPM_SUCCESS) {
switch (result) {
case TPM_E_MAXNVWRITES:
assert(i >= TPM_MAX_NV_WRITES_NOOWNER);
default:
error("\tunexpected error code %d (0x%x)\n",
result, result);
}
}
}
/* Reset write count */
TPM_CHECK(TlclForceClear());
TPM_CHECK(TlclSetEnable());
TPM_CHECK(TlclSetDeactivated(0));
/* Try writing again. */
TPM_CHECK(TlclWrite(INDEX0, (uint8_t*)&i, sizeof(i)));
printf("\tdone\n");
return 0;
}
/* u-boot command table (include/command.h)
*/
#define VOIDTEST(XFUNC) \
int do_test_##XFUNC(cmd_tbl_t *cmd_tbl, int flag, int argc, \
char * const argv[]) \
{ \
return test_##XFUNC(); \
} \
/* above blank line is a part of the macro */
#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 cmd_tbl_t 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)
};
/* u-boot shell commands
*/
static int do_cros_tpm_test(cmd_tbl_t * cmdtp, int flag, int argc,
char * const argv[])
{
cmd_tbl_t *c;
printf("argc = %d, argv = ", argc);
do {
int i = 0;
for (i = 0; i < argc; i++)
printf(" %s", argv[i]);
printf("\n------\n");
} while(0);
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(cros_tpm_test, 2, 1, do_cros_tpm_test, "TPM_Lite 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");