/* * Copyright (c) 2013 Google, Inc * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include /* TPM NVRAM location indices. */ #define FIRMWARE_NV_INDEX 0x1007 #define KERNEL_NV_INDEX 0x1008 #define NV_DATA_PUBLIC_PERMISSIONS_OFFSET 60 /* Kernel TPM space - KERNEL_NV_INDEX, locked with physical presence */ #define ROLLBACK_SPACE_KERNEL_VERSION 2 #define ROLLBACK_SPACE_KERNEL_UID 0x4752574C /* 'GRWL' */ struct rollback_space_kernel { /* Struct version, for backwards compatibility */ uint8_t struct_version; /* Unique ID to detect space redefinition */ uint32_t uid; /* Kernel versions */ uint32_t kernel_versions; /* Reserved for future expansion */ uint8_t reserved[3]; /* Checksum (v2 and later only) */ uint8_t crc8; } __packed rollback_space_kernel; /* * These numbers derive from adding the sizes of command fields as shown in * the TPM commands manual. */ #define TPM_REQUEST_HEADER_LENGTH 10 #define TPM_RESPONSE_HEADER_LENGTH 10 /* These are the different non-volatile spaces that we emulate */ enum { NV_GLOBAL_LOCK, NV_SEQ_FIRMWARE, NV_SEQ_KERNEL, NV_SEQ_COUNT, }; /* Size of each non-volatile space */ #define NV_DATA_SIZE 0x20 /* * Information about our TPM emulation. This is preserved in the sandbox * state file if enabled. */ static struct tpm_state { uint8_t nvdata[NV_SEQ_COUNT][NV_DATA_SIZE]; } 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) { const char *prop; int len; int i; if (!blob) return 0; for (i = 0; i < NV_SEQ_COUNT; i++) { char prop_name[20]; sprintf(prop_name, "nvdata%d", i); prop = fdt_getprop(blob, node, prop_name, &len); if (prop && len == NV_DATA_SIZE) memcpy(g_state.nvdata[i], prop, NV_DATA_SIZE); } return 0; } /** * cros_ec_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) { int i; /* * 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++) { char prop_name[20]; sprintf(prop_name, "nvdata%d", i); fdt_setprop(blob, node, prop_name, g_state.nvdata[i], NV_DATA_SIZE); } return 0; } SANDBOX_STATE_IO(sandbox_tpm, "google,sandbox-tpm", sandbox_tpm_read_state, sandbox_tpm_write_state); static int index_to_seq(uint32_t index) { switch (index) { case FIRMWARE_NV_INDEX: return NV_SEQ_FIRMWARE; case KERNEL_NV_INDEX: return NV_SEQ_KERNEL; case 0: return NV_GLOBAL_LOCK; } printf("Invalid nv index %#x\n", index); return -1; } 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)); printf("tpm: %zd bytes, recv_len %zd, cmd = %x\n", send_size, *recv_len, code); print_buffer(0, sendbuf, 1, send_size, 0); switch (code) { case 0x65: /* get flags */ type = get_unaligned_be32(sendbuf + 14); switch (type) { case 4: index = get_unaligned_be32(sendbuf + 18); printf("Get flags index %#02x\n", index); *recv_len = 22; memset(recvbuf, '\0', *recv_len); put_unaligned_be32(22, recvbuf + TPM_RESPONSE_HEADER_LENGTH); data = recvbuf + TPM_RESPONSE_HEADER_LENGTH + sizeof(uint32_t); switch (index) { case FIRMWARE_NV_INDEX: break; case KERNEL_NV_INDEX: /* TPM_NV_PER_PPWRITE */ put_unaligned_be32(1, data + NV_DATA_PUBLIC_PERMISSIONS_OFFSET); break; } break; case 0x11: /* TPM_CAP_NV_INDEX */ index = get_unaligned_be32(sendbuf + 18); printf("Get cap nv index %#02x\n", index); put_unaligned_be32(22, recvbuf + TPM_RESPONSE_HEADER_LENGTH); break; default: printf(" ** Unknown 0x65 command type %#02x\n", type); return -1; } break; case 0xcd: /* nvwrite */ index = get_unaligned_be32(sendbuf + 10); length = get_unaligned_be32(sendbuf + 18); seq = index_to_seq(index); if (seq < 0) return -1; printf("tpm: nvwrite index=%#02x, len=%#02x\n", index, length); memcpy(&tpm->nvdata[seq], sendbuf + 22, length); *recv_len = 12; memset(recvbuf, '\0', *recv_len); break; case 0xcf: /* nvread */ index = get_unaligned_be32(sendbuf + 10); length = get_unaligned_be32(sendbuf + 18); seq = index_to_seq(index); if (seq < 0) return -1; printf("tpm: nvread index=%#02x, len=%#02x\n", index, length); *recv_len = TPM_RESPONSE_HEADER_LENGTH + sizeof(uint32_t) + length; memset(recvbuf, '\0', *recv_len); put_unaligned_be32(length, recvbuf + TPM_RESPONSE_HEADER_LENGTH); if (seq == NV_SEQ_KERNEL) { struct rollback_space_kernel rsk; data = recvbuf + TPM_RESPONSE_HEADER_LENGTH + sizeof(uint32_t); memset(&rsk, 0, sizeof(struct rollback_space_kernel)); rsk.struct_version = 2; rsk.uid = ROLLBACK_SPACE_KERNEL_UID; rsk.crc8 = crc8(0, (unsigned char *)&rsk, offsetof(struct rollback_space_kernel, crc8)); memcpy(data, &rsk, sizeof(rsk)); } else { memcpy(recvbuf + TPM_RESPONSE_HEADER_LENGTH + sizeof(uint32_t), &tpm->nvdata[seq], length); } break; case 0x14: /* tpm extend */ case 0x15: /* pcr read */ case 0x5d: /* force clear */ case 0x6f: /* physical enable */ case 0x72: /* physical set deactivated */ case 0x99: /* startup */ case 0x4000000a: /* assert physical presence */ *recv_len = 12; memset(recvbuf, '\0', *recv_len); break; default: printf("Unknown tpm command %02x\n", code); return -1; } 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); memcpy(tpm, &g_state, sizeof(*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(sandbox_tpm) = { .name = "sandbox_tpm", .id = UCLASS_TPM, .of_match = sandbox_tpm_ids, .ops = &sandbox_tpm_ops, .probe = sandbox_tpm_probe, .priv_auto_alloc_size = sizeof(struct tpm_state), };