// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2015 Google, Inc */ #include #include #include #include #include #include "tpm-user-utils.h" #include /* 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");