diff options
Diffstat (limited to 'drivers/tpm/tpm2_tis_sandbox.c')
-rw-r--r-- | drivers/tpm/tpm2_tis_sandbox.c | 283 |
1 files changed, 252 insertions, 31 deletions
diff --git a/drivers/tpm/tpm2_tis_sandbox.c b/drivers/tpm/tpm2_tis_sandbox.c index 24c804a564..ac6eb14353 100644 --- a/drivers/tpm/tpm2_tis_sandbox.c +++ b/drivers/tpm/tpm2_tis_sandbox.c @@ -11,6 +11,8 @@ #include <asm/unaligned.h> #include <linux/bitops.h> #include <u-boot/crc.h> +#include <u-boot/sha256.h> +#include "sandbox_common.h" /* Hierarchies */ enum tpm2_hierarchy { @@ -38,29 +40,178 @@ enum tpm2_cap_tpm_property { #define SANDBOX_TPM_PCR_NB 1 -static const u8 sandbox_extended_once_pcr[] = { - 0xf5, 0xa5, 0xfd, 0x42, 0xd1, 0x6a, 0x20, 0x30, - 0x27, 0x98, 0xef, 0x6e, 0xd3, 0x09, 0x97, 0x9b, - 0x43, 0x00, 0x3d, 0x23, 0x20, 0xd9, 0xf0, 0xe8, - 0xea, 0x98, 0x31, 0xa9, 0x27, 0x59, 0xfb, 0x4b, -}; - +/* + * Information about our TPM emulation. This is preserved in the sandbox + * state file if enabled. + * + * @valid: true if this is valid (only used in s_state) + * @init_done: true if open() has been called + * @startup_done: true if TPM2_CC_STARTUP has been processed + * @tests_done: true if TPM2_CC_SELF_TEST has be processed + * @pw: TPM password per hierarchy + * @pw_sz: Size of each password in bytes + * @properties: TPM properties + * @pcr: TPM Platform Configuration Registers. Each of these holds a hash and + * can be 'extended' a number of times, meaning another hash is added into + * its value (initial value all zeroes) + * @pcr_extensions: Number of times each PCR has been extended (starts at 0) + * @nvdata: non-volatile data, used to store important things for the platform + */ struct sandbox_tpm2 { + bool valid; /* TPM internal states */ bool init_done; bool startup_done; bool tests_done; - /* TPM password per hierarchy */ char pw[TPM2_HIERARCHY_NB][TPM2_DIGEST_LEN + 1]; int pw_sz[TPM2_HIERARCHY_NB]; - /* TPM properties */ u32 properties[TPM2_PROPERTY_NB]; - /* TPM PCRs */ u8 pcr[SANDBOX_TPM_PCR_NB][TPM2_DIGEST_LEN]; - /* TPM PCR extensions */ u32 pcr_extensions[SANDBOX_TPM_PCR_NB]; + struct nvdata_state nvdata[NV_SEQ_COUNT]; }; +static struct sandbox_tpm2 s_state, *g_state; + +/** + * sandbox_tpm2_read_state() - read the sandbox EC state from the state file + * + * If data is available, then blob and node will provide access to it. If + * not this function sets up an empty TPM. + * + * @blob: Pointer to device tree blob, or NULL if no data to read + * @node: Node offset to read from + */ +static int sandbox_tpm2_read_state(const void *blob, int node) +{ + struct sandbox_tpm2 *state = &s_state; + char prop_name[20]; + const char *prop; + int len; + int i; + + if (!blob) + return 0; + state->tests_done = fdtdec_get_int(blob, node, "tests-done", 0); + + for (i = 0; i < TPM2_HIERARCHY_NB; i++) { + snprintf(prop_name, sizeof(prop_name), "pw%d", i); + + prop = fdt_getprop(blob, node, prop_name, &len); + if (len > TPM2_DIGEST_LEN) + return log_msg_ret("pw", -E2BIG); + if (prop) { + memcpy(state->pw[i], prop, len); + state->pw_sz[i] = len; + } + } + + for (i = 0; i < TPM2_PROPERTY_NB; i++) { + snprintf(prop_name, sizeof(prop_name), "properties%d", i); + state->properties[i] = fdtdec_get_uint(blob, node, prop_name, + 0); + } + + for (i = 0; i < SANDBOX_TPM_PCR_NB; i++) { + int subnode; + + snprintf(prop_name, sizeof(prop_name), "pcr%d", i); + subnode = fdt_subnode_offset(blob, node, prop_name); + if (subnode < 0) + continue; + prop = fdt_getprop(blob, subnode, "value", &len); + if (len != TPM2_DIGEST_LEN) + return log_msg_ret("pcr", -E2BIG); + memcpy(state->pcr[i], prop, TPM2_DIGEST_LEN); + state->pcr_extensions[i] = fdtdec_get_uint(blob, subnode, + "extensions", 0); + } + + for (i = 0; i < NV_SEQ_COUNT; i++) { + struct nvdata_state *nvd = &state->nvdata[i]; + + sprintf(prop_name, "nvdata%d", i); + prop = fdt_getprop(blob, node, prop_name, &len); + if (len > NV_DATA_SIZE) + return log_msg_ret("nvd", -E2BIG); + if (prop) { + memcpy(nvd->data, prop, len); + nvd->length = len; + nvd->present = true; + } + } + s_state.valid = true; + + return 0; +} + +/** + * sandbox_tpm2_write_state() - Write out our state to the state file + * + * The caller will ensure that there is a node ready for the state. The node + * may already contain the old state, in which case it is overridden. + * + * @blob: Device tree blob holding state + * @node: Node to write our state into + */ +static int sandbox_tpm2_write_state(void *blob, int node) +{ + const struct sandbox_tpm2 *state = g_state; + char prop_name[20]; + int i; + + if (!state) + return 0; + + /* + * We are guaranteed enough space to write basic properties. This is + * SANDBOX_STATE_MIN_SPACE. + * + * We could use fdt_add_subnode() to put each set of data in its + * own node - perhaps useful if we add access information to each. + */ + fdt_setprop_u32(blob, node, "tests-done", state->tests_done); + + for (i = 0; i < TPM2_HIERARCHY_NB; i++) { + if (state->pw_sz[i]) { + snprintf(prop_name, sizeof(prop_name), "pw%d", i); + fdt_setprop(blob, node, prop_name, state->pw[i], + state->pw_sz[i]); + } + } + + for (i = 0; i < TPM2_PROPERTY_NB; i++) { + snprintf(prop_name, sizeof(prop_name), "properties%d", i); + fdt_setprop_u32(blob, node, prop_name, state->properties[i]); + } + + for (i = 0; i < SANDBOX_TPM_PCR_NB; i++) { + int subnode; + + snprintf(prop_name, sizeof(prop_name), "pcr%d", i); + subnode = fdt_add_subnode(blob, node, prop_name); + fdt_setprop(blob, subnode, "value", state->pcr[i], + TPM2_DIGEST_LEN); + fdt_setprop_u32(blob, subnode, "extensions", + state->pcr_extensions[i]); + } + + for (i = 0; i < NV_SEQ_COUNT; i++) { + const struct nvdata_state *nvd = &state->nvdata[i]; + + if (nvd->present) { + snprintf(prop_name, sizeof(prop_name), "nvdata%d", i); + fdt_setprop(blob, node, prop_name, nvd->data, + nvd->length); + } + } + + return 0; +} + +SANDBOX_STATE_IO(sandbox_tpm2, "sandbox,tpm2", sandbox_tpm2_read_state, + sandbox_tpm2_write_state); + /* * Check the tag validity depending on the command (authentication required or * not). If authentication is required, check it is valid. Update the auth @@ -93,6 +244,10 @@ static int sandbox_tpm2_check_session(struct udevice *dev, u32 command, u16 tag, case TPM2_CC_DAM_RESET: case TPM2_CC_DAM_PARAMETERS: case TPM2_CC_PCR_EXTEND: + case TPM2_CC_NV_READ: + case TPM2_CC_NV_WRITE: + case TPM2_CC_NV_WRITELOCK: + case TPM2_CC_NV_DEFINE_SPACE: if (tag != TPM2_ST_SESSIONS) { printf("Session required for command 0x%x\n", command); return TPM2_RC_AUTH_CONTEXT; @@ -121,6 +276,10 @@ static int sandbox_tpm2_check_session(struct udevice *dev, u32 command, u16 tag, break; case TPM2_RH_PLATFORM: *hierarchy = TPM2_HIERARCHY_PLATFORM; + if (command == TPM2_CC_NV_READ || + command == TPM2_CC_NV_WRITE || + command == TPM2_CC_NV_WRITELOCK) + *auth += sizeof(u32); break; default: printf("Wrong handle 0x%x\n", handle); @@ -242,15 +401,17 @@ static int sandbox_tpm2_extend(struct udevice *dev, int pcr_index, const u8 *extension) { struct sandbox_tpm2 *tpm = dev_get_priv(dev); - int i; + sha256_context ctx; - /* Only simulate the first extensions from all '0' with only '0' */ - for (i = 0; i < TPM2_DIGEST_LEN; i++) - if (tpm->pcr[pcr_index][i] || extension[i]) - return TPM2_RC_FAILURE; + /* Zero the PCR if this is the first use */ + if (!tpm->pcr_extensions[pcr_index]) + memset(tpm->pcr[pcr_index], '\0', TPM2_DIGEST_LEN); + + sha256_starts(&ctx); + sha256_update(&ctx, tpm->pcr[pcr_index], TPM2_DIGEST_LEN); + sha256_update(&ctx, extension, TPM2_DIGEST_LEN); + sha256_finish(&ctx, tpm->pcr[pcr_index]); - memcpy(tpm->pcr[pcr_index], sandbox_extended_once_pcr, - TPM2_DIGEST_LEN); tpm->pcr_extensions[pcr_index]++; return 0; @@ -477,15 +638,8 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf, for (i = 0; i < pcr_array_sz; i++) pcr_map += (u64)sent[i] << (i * 8); - if (pcr_map >> SANDBOX_TPM_PCR_NB) { - printf("Sandbox TPM handles up to %d PCR(s)\n", - SANDBOX_TPM_PCR_NB); - rc = TPM2_RC_VALUE; - return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc); - } - if (!pcr_map) { - printf("Empty PCR map.\n"); + printf("Empty PCR map\n"); rc = TPM2_RC_VALUE; return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc); } @@ -494,6 +648,13 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf, if (pcr_map & BIT(i)) pcr_index = i; + if (pcr_index >= SANDBOX_TPM_PCR_NB) { + printf("Invalid index %d, sandbox TPM handles up to %d PCR(s)\n", + pcr_index, SANDBOX_TPM_PCR_NB); + rc = TPM2_RC_VALUE; + return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc); + } + /* Write tag */ put_unaligned_be16(tag, recv); recv += sizeof(tag); @@ -527,9 +688,9 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf, pcr_index = get_unaligned_be32(sendbuf + sizeof(tag) + sizeof(length) + sizeof(command)); - if (pcr_index > SANDBOX_TPM_PCR_NB) { - printf("Sandbox TPM handles up to %d PCR(s)\n", - SANDBOX_TPM_PCR_NB); + if (pcr_index >= SANDBOX_TPM_PCR_NB) { + printf("Invalid index %d, sandbox TPM handles up to %d PCR(s)\n", + pcr_index, SANDBOX_TPM_PCR_NB); rc = TPM2_RC_VALUE; } @@ -557,6 +718,64 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf, sandbox_tpm2_fill_buf(recv, recv_len, tag, rc); break; + case TPM2_CC_NV_READ: { + int index, seq; + + index = get_unaligned_be32(sendbuf + TPM2_HDR_LEN + 4); + length = get_unaligned_be16(sent); + /* ignore offset */ + seq = sb_tpm_index_to_seq(index); + if (seq < 0) + return log_msg_ret("index", -EINVAL); + printf("tpm: nvread index=%#02x, len=%#02x, seq=%#02x\n", index, + length, seq); + *recv_len = TPM2_HDR_LEN + 6 + length; + memset(recvbuf, '\0', *recv_len); + put_unaligned_be32(length, recvbuf + 2); + sb_tpm_read_data(tpm->nvdata, seq, recvbuf, + TPM2_HDR_LEN + 4 + 2, length); + break; + } + case TPM2_CC_NV_WRITE: { + int index, seq; + + index = get_unaligned_be32(sendbuf + TPM2_HDR_LEN + 4); + length = get_unaligned_be16(sent); + sent += sizeof(u16); + + /* ignore offset */ + seq = sb_tpm_index_to_seq(index); + if (seq < 0) + return log_msg_ret("index", -EINVAL); + printf("tpm: nvwrite index=%#02x, len=%#02x, seq=%#02x\n", index, + length, seq); + memcpy(&tpm->nvdata[seq].data, sent, length); + tpm->nvdata[seq].present = true; + *recv_len = TPM2_HDR_LEN + 2; + memset(recvbuf, '\0', *recv_len); + break; + } + case TPM2_CC_NV_DEFINE_SPACE: { + int policy_size, index, seq; + + policy_size = get_unaligned_be16(sent + 12); + index = get_unaligned_be32(sent + 2); + sent += 14 + policy_size; + length = get_unaligned_be16(sent); + seq = sb_tpm_index_to_seq(index); + if (seq < 0) + return -EINVAL; + printf("tpm: define_space index=%x, len=%x, seq=%x, policy_size=%x\n", + index, length, seq, policy_size); + sb_tpm_define_data(tpm->nvdata, seq, length); + *recv_len = 12; + memset(recvbuf, '\0', *recv_len); + break; + } + case TPM2_CC_NV_WRITELOCK: + *recv_len = 12; + memset(recvbuf, '\0', *recv_len); + break; default: printf("TPM2 command %02x unknown in Sandbox\n", command); rc = TPM2_RC_COMMAND_CODE; @@ -594,11 +813,13 @@ static int sandbox_tpm2_probe(struct udevice *dev) /* Use the TPM v2 stack */ priv->version = TPM_V2; - memset(tpm, 0, sizeof(*tpm)); - priv->pcr_count = 32; priv->pcr_select_min = 2; + if (s_state.valid) + memcpy(tpm, &s_state, sizeof(*tpm)); + g_state = tpm; + return 0; } |