// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2013 Google, Inc */ #include #include #include #include #include #include #include #include "sandbox_common.h" #define NV_DATA_PUBLIC_PERMISSIONS_OFFSET 60 /* * Information about our TPM emulation. This is preserved in the sandbox * state file if enabled. */ static struct tpm_state { bool valid; struct nvdata_state nvdata[NV_SEQ_COUNT]; } s_state, *g_state; /** * sandbox_tpm_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_tpm_read_state(const void *blob, int node) { struct tpm_state *state = &s_state; const char *prop; int len; int i; if (!blob) return 0; for (i = 0; i < NV_SEQ_COUNT; i++) { struct nvdata_state *nvd = &state->nvdata[i]; char prop_name[20]; 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_tpm_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_tpm_write_state(void *blob, int node) { const struct tpm_state *state = g_state; int i; if (!state) return 0; /* * We are guaranteed enough space to write basic properties. * We could use fdt_add_subnode() to put each set of data in its * own node - perhaps useful if we add access informaiton to each. */ for (i = 0; i < NV_SEQ_COUNT; i++) { const struct nvdata_state *nvd = &state->nvdata[i]; char prop_name[20]; 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_tpm, "google,sandbox-tpm", sandbox_tpm_read_state, sandbox_tpm_write_state); static void handle_cap_flag_space(u8 **datap, uint index) { struct tpm_nv_data_public pub; /* TPM_NV_PER_PPWRITE */ memset(&pub, '\0', sizeof(pub)); pub.nv_index = __cpu_to_be32(index); pub.pcr_info_read.pcr_selection.size_of_select = __cpu_to_be16( sizeof(pub.pcr_info_read.pcr_selection.pcr_select)); pub.permission.attributes = __cpu_to_be32(1); pub.pcr_info_write = pub.pcr_info_read; memcpy(*datap, &pub, sizeof(pub)); *datap += sizeof(pub); } static int sandbox_tpm_xfer(struct udevice *dev, const uint8_t *sendbuf, size_t send_size, uint8_t *recvbuf, size_t *recv_len) { struct tpm_state *tpm = dev_get_priv(dev); uint32_t code, index, length, type; uint8_t *data; int seq; code = get_unaligned_be32(sendbuf + sizeof(uint16_t) + sizeof(uint32_t)); #ifdef DEBUG printf("tpm: %zd bytes, recv_len %zd, cmd = %x\n", send_size, *recv_len, code); print_buffer(0, sendbuf, 1, send_size, 0); #endif switch (code) { case TPM_CMD_GET_CAPABILITY: type = get_unaligned_be32(sendbuf + 14); switch (type) { case TPM_CAP_FLAG: index = get_unaligned_be32(sendbuf + 18); printf("Get flags index %#02x\n", index); *recv_len = 22; memset(recvbuf, '\0', *recv_len); data = recvbuf + TPM_HDR_LEN + sizeof(uint32_t); switch (index) { case FIRMWARE_NV_INDEX: break; case KERNEL_NV_INDEX: handle_cap_flag_space(&data, index); *recv_len = data - recvbuf; break; case TPM_CAP_FLAG_PERMANENT: { struct tpm_permanent_flags *pflags; pflags = (struct tpm_permanent_flags *)data; memset(pflags, '\0', sizeof(*pflags)); put_unaligned_be32(TPM_TAG_PERMANENT_FLAGS, &pflags->tag); *recv_len = TPM_HEADER_SIZE + 4 + sizeof(*pflags); break; } default: printf(" ** Unknown flags index %x\n", index); return -ENOSYS; } put_unaligned_be32(*recv_len, recvbuf + TPM_HDR_LEN); break; case TPM_CAP_NV_INDEX: index = get_unaligned_be32(sendbuf + 18); printf("Get cap nv index %#02x\n", index); put_unaligned_be32(22, recvbuf + TPM_HDR_LEN); break; default: printf(" ** Unknown 0x65 command type %#02x\n", type); return -ENOSYS; } break; case TPM_CMD_NV_WRITE_VALUE: index = get_unaligned_be32(sendbuf + 10); length = get_unaligned_be32(sendbuf + 18); seq = sb_tpm_index_to_seq(index); if (seq < 0) return -EINVAL; printf("tpm: nvwrite index=%#02x, len=%#02x\n", index, length); sb_tpm_write_data(tpm->nvdata, seq, sendbuf, 22, length); break; case TPM_CMD_NV_READ_VALUE: /* nvread */ index = get_unaligned_be32(sendbuf + 10); length = get_unaligned_be32(sendbuf + 18); seq = sb_tpm_index_to_seq(index); if (seq < 0) return -EINVAL; printf("tpm: nvread index=%#02x, len=%#02x, seq=%#02x\n", index, length, seq); *recv_len = TPM_HDR_LEN + sizeof(uint32_t) + length; memset(recvbuf, '\0', *recv_len); put_unaligned_be32(length, recvbuf + TPM_HDR_LEN); sb_tpm_read_data(tpm->nvdata, seq, recvbuf, TPM_HDR_LEN + 4, length); break; case TPM_CMD_EXTEND: *recv_len = 30; memset(recvbuf, '\0', *recv_len); break; case TPM_CMD_NV_DEFINE_SPACE: index = get_unaligned_be32(sendbuf + 12); length = get_unaligned_be32(sendbuf + 77); seq = sb_tpm_index_to_seq(index); if (seq < 0) return -EINVAL; printf("tpm: define_space index=%#02x, len=%#02x, seq=%#02x\n", index, length, seq); sb_tpm_define_data(tpm->nvdata, seq, length); *recv_len = 12; memset(recvbuf, '\0', *recv_len); break; case 0x15: /* pcr read */ case 0x5d: /* force clear */ case 0x6f: /* physical enable */ case 0x72: /* physical set deactivated */ case 0x99: /* startup */ case 0x50: /* self test full */ case 0x4000000a: /* assert physical presence */ *recv_len = 12; memset(recvbuf, '\0', *recv_len); break; default: printf("Unknown tpm command %02x\n", code); return -ENOSYS; } #ifdef DEBUG printf("tpm: rx recv_len %zd\n", *recv_len); print_buffer(0, recvbuf, 1, *recv_len, 0); #endif return 0; } static int sandbox_tpm_get_desc(struct udevice *dev, char *buf, int size) { if (size < 15) return -ENOSPC; return snprintf(buf, size, "sandbox TPM"); } static int sandbox_tpm_probe(struct udevice *dev) { struct tpm_state *tpm = dev_get_priv(dev); if (s_state.valid) memcpy(tpm, &s_state, sizeof(*tpm)); g_state = tpm; return 0; } static int sandbox_tpm_open(struct udevice *dev) { return 0; } static int sandbox_tpm_close(struct udevice *dev) { return 0; } static const struct tpm_ops sandbox_tpm_ops = { .open = sandbox_tpm_open, .close = sandbox_tpm_close, .get_desc = sandbox_tpm_get_desc, .xfer = sandbox_tpm_xfer, }; static const struct udevice_id sandbox_tpm_ids[] = { { .compatible = "google,sandbox-tpm" }, { } }; U_BOOT_DRIVER(google_sandbox_tpm) = { .name = "google_sandbox_tpm", .id = UCLASS_TPM, .of_match = sandbox_tpm_ids, .ops = &sandbox_tpm_ops, .probe = sandbox_tpm_probe, .priv_auto = sizeof(struct tpm_state), };