diff options
Diffstat (limited to 'drivers/tpm/tpm_tis_sandbox.c')
| -rw-r--r-- | drivers/tpm/tpm_tis_sandbox.c | 260 | 
1 files changed, 260 insertions, 0 deletions
| diff --git a/drivers/tpm/tpm_tis_sandbox.c b/drivers/tpm/tpm_tis_sandbox.c new file mode 100644 index 000000000..ed4b03912 --- /dev/null +++ b/drivers/tpm/tpm_tis_sandbox.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/state.h> +#include <asm/unaligned.h> +#include <linux/crc8.h> + +/* 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]; +} 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(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, 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; +} + +int tis_sendrecv(const u8 *sendbuf, size_t send_size, +		 u8 *recvbuf, size_t *recv_len) +{ +	struct tpm_state *tpm = &state; +	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); +			rsk.struct_version = 2; +			rsk.uid = ROLLBACK_SPACE_KERNEL_UID; +			rsk.kernel_versions = 0; +			rsk.crc8 = crc8((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; +} + +int tis_open(void) +{ +	printf("%s\n", __func__); +	return 0; +} + +int tis_close(void) +{ +	printf("%s\n", __func__); +	return 0; +} + +int tis_init(void) +{ +	printf("%s\n", __func__); +	return 0; +} |