sandbox: tpm: Split out common nvdata code

We want to support nvdata in TPM2 as well. To avoid code duplicating the
associated code, move it into a common file.

Drop the special-case logic for the kernel space. This can be handled by
the higher-level code now, i.e. in vboot itself.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile
index f64d200..c65be52 100644
--- a/drivers/tpm/Makefile
+++ b/drivers/tpm/Makefile
@@ -6,11 +6,11 @@
 obj-$(CONFIG_TPM_ATMEL_TWI) += tpm_atmel_twi.o
 obj-$(CONFIG_TPM_TIS_INFINEON) += tpm_tis_infineon.o
 obj-$(CONFIG_TPM_TIS_LPC) += tpm_tis_lpc.o
-obj-$(CONFIG_TPM_TIS_SANDBOX) += tpm_tis_sandbox.o
+obj-$(CONFIG_TPM_TIS_SANDBOX) += tpm_tis_sandbox.o sandbox_common.o
 obj-$(CONFIG_TPM_ST33ZP24_I2C) += tpm_tis_st33zp24_i2c.o
 obj-$(CONFIG_TPM_ST33ZP24_SPI) += tpm_tis_st33zp24_spi.o
 
 obj-$(CONFIG_$(SPL_TPL_)TPM2_CR50_I2C) += cr50_i2c.o
-obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o
+obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o sandbox_common.o
 obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_spi.o
 obj-$(CONFIG_TPM2_FTPM_TEE) += tpm2_ftpm_tee.o
diff --git a/drivers/tpm/sandbox_common.c b/drivers/tpm/sandbox_common.c
new file mode 100644
index 0000000..13f5e03
--- /dev/null
+++ b/drivers/tpm/sandbox_common.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Common features for sandbox TPM1 and TPM2 implementations
+ *
+ * Copyright 2021 Google LLC
+ */
+
+#define LOG_CATEGORY	UCLASS_TPM
+
+#include <common.h>
+#include <tpm-v1.h>
+#include <tpm-v2.h>
+#include <asm/unaligned.h>
+#include "sandbox_common.h"
+
+#define TPM_ERR_CODE_OFS	(2 + 4)		/* after tag and size */
+
+int sb_tpm_index_to_seq(u32 index)
+{
+	index &= ~HR_NV_INDEX;
+	switch (index) {
+	case FIRMWARE_NV_INDEX:
+		return NV_SEQ_FIRMWARE;
+	case KERNEL_NV_INDEX:
+		return NV_SEQ_KERNEL;
+	case BACKUP_NV_INDEX:
+		return NV_SEQ_BACKUP;
+	case FWMP_NV_INDEX:
+		return NV_SEQ_FWMP;
+	case MRC_REC_HASH_NV_INDEX:
+		return NV_SEQ_REC_HASH;
+	case 0:
+		return NV_SEQ_GLOBAL_LOCK;
+	case TPM_NV_INDEX_LOCK:
+		return NV_SEQ_ENABLE_LOCKING;
+	}
+
+	printf("Invalid nv index %#x\n", index);
+	return -1;
+}
+
+void sb_tpm_read_data(const struct nvdata_state nvdata[NV_SEQ_COUNT],
+		      enum sandbox_nv_space seq, u8 *buf, int data_ofs,
+		      int length)
+{
+	const struct nvdata_state *nvd = &nvdata[seq];
+
+	if (!nvd->present)
+		put_unaligned_be32(TPM_BADINDEX, buf + TPM_ERR_CODE_OFS);
+	else if (length > nvd->length)
+		put_unaligned_be32(TPM_BAD_DATASIZE, buf + TPM_ERR_CODE_OFS);
+	else
+		memcpy(buf + data_ofs, &nvd->data, length);
+}
+
+void sb_tpm_write_data(struct nvdata_state nvdata[NV_SEQ_COUNT],
+		       enum sandbox_nv_space seq, const u8 *buf, int data_ofs,
+		       int length)
+{
+	struct nvdata_state *nvd = &nvdata[seq];
+
+	if (length > nvd->length)
+		log_err("Invalid length %x (max %x)\n", length, nvd->length);
+	else
+		memcpy(&nvdata[seq].data, buf + data_ofs, length);
+}
diff --git a/drivers/tpm/sandbox_common.h b/drivers/tpm/sandbox_common.h
new file mode 100644
index 0000000..aa5292d
--- /dev/null
+++ b/drivers/tpm/sandbox_common.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Common features for sandbox TPM1 and TPM2 implementations
+ *
+ * Copyright 2021 Google LLC
+ */
+
+#ifndef __TPM_SANDBOX_COMMON_H
+#define __TPM_SANDBOX_COMMON_H
+
+/*
+ * These numbers derive from adding the sizes of command fields as shown in
+ * the TPM commands manual.
+ */
+#define TPM_HDR_LEN	10
+
+/* These are the different non-volatile spaces that we emulate */
+enum sandbox_nv_space {
+	NV_SEQ_ENABLE_LOCKING,
+	NV_SEQ_GLOBAL_LOCK,
+	NV_SEQ_FIRMWARE,
+	NV_SEQ_KERNEL,
+	NV_SEQ_BACKUP,
+	NV_SEQ_FWMP,
+	NV_SEQ_REC_HASH,
+
+	NV_SEQ_COUNT,
+};
+
+/* TPM NVRAM location indices */
+#define FIRMWARE_NV_INDEX		0x1007
+#define KERNEL_NV_INDEX			0x1008
+#define BACKUP_NV_INDEX			0x1009
+#define FWMP_NV_INDEX			0x100a
+#define MRC_REC_HASH_NV_INDEX		0x100b
+
+/* Size of each non-volatile space */
+#define NV_DATA_SIZE		0x28
+
+/**
+ * struct nvdata_state - state of a single non-volatile-data 'space'
+ *
+ * @present: true if present
+ * @length: length in bytes (max NV_DATA_SIZE)
+ * @data: contents of non-volatile space
+ */
+struct nvdata_state {
+	bool present;
+	int length;
+	u8 data[NV_DATA_SIZE];
+};
+
+/**
+ * sb_tpm_index_to_seq() - convert an index into a space sequence number
+ *
+ * This converts the index as used by the vboot code into an internal sequence
+ * number used by the sandbox emulation.
+ *
+ * @index: Index to use (FIRMWARE_NV_INDEX, etc.)
+ * @return associated space (enum sandbox_nv_space)
+ */
+int sb_tpm_index_to_seq(uint index);
+
+/**
+ * sb_tpm_read_data() - Read non-volatile data
+ *
+ * This handles a TPM read of nvdata. If the nvdata is not present, a
+ * TPM_BADINDEX error is put in the buffer. If @length is too large,
+ * TPM_BAD_DATASIZE is put in the buffer.
+ *
+ * @nvdata: Current nvdata state
+ * @seq: Sequence number to read
+ * @recvbuf: Buffer to update with the TPM response, assumed to contain zeroes
+ * @data_ofs: Offset of the 'data' portion of @recvbuf
+ * @length: Number of bytes to read
+ */
+void sb_tpm_read_data(const struct nvdata_state nvdata[NV_SEQ_COUNT],
+		      enum sandbox_nv_space seq, u8 *recvbuf, int data_ofs,
+		      int length);
+
+/**
+ * sb_tpm_write_data() - Write non-volatile data
+ *
+ * If @length is too large, an error is logged and nothing is written.
+ *
+ * @nvdata: Current nvdata state
+ * @seq: Sequence number to read
+ * @buf: Buffer containing the data to write
+ * @data_ofs: Offset of the 'data' portion of @buf
+ * @length: Number of bytes to write
+ */
+void sb_tpm_write_data(struct nvdata_state nvdata[NV_SEQ_COUNT],
+		       enum sandbox_nv_space seq, const u8 *buf, int data_ofs,
+		       int length);
+
+#endif
diff --git a/drivers/tpm/tpm_tis_sandbox.c b/drivers/tpm/tpm_tis_sandbox.c
index 67139ce..294d98d 100644
--- a/drivers/tpm/tpm_tis_sandbox.c
+++ b/drivers/tpm/tpm_tis_sandbox.c
@@ -9,61 +9,10 @@
 #include <asm/state.h>
 #include <asm/unaligned.h>
 #include <u-boot/crc.h>
-
-/* TPM NVRAM location indices. */
-#define FIRMWARE_NV_INDEX		0x1007
-#define KERNEL_NV_INDEX			0x1008
-#define BACKUP_NV_INDEX                 0x1009
-#define FWMP_NV_INDEX                   0x100a
-#define REC_HASH_NV_INDEX               0x100b
-#define REC_HASH_NV_SIZE                VB2_SHA256_DIGEST_SIZE
+#include "sandbox_common.h"
 
 #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_BACKUP,
-	NV_SEQ_FWMP,
-	NV_SEQ_REC_HASH,
-
-	NV_SEQ_COUNT,
-};
-
-/* Size of each non-volatile space */
-#define NV_DATA_SIZE		0x20
-
-struct nvdata_state {
-	bool present;
-	u8 data[NV_DATA_SIZE];
-};
-
 /*
  * Information about our TPM emulation. This is preserved in the sandbox
  * state file if enabled.
@@ -140,27 +89,6 @@
 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 BACKUP_NV_INDEX:
-		return NV_SEQ_BACKUP;
-	case FWMP_NV_INDEX:
-		return NV_SEQ_FWMP;
-	case REC_HASH_NV_INDEX:
-		return NV_SEQ_REC_HASH;
-	case 0:
-		return NV_GLOBAL_LOCK;
-	}
-
-	printf("Invalid nv index %#x\n", index);
-	return -1;
-}
-
 static void handle_cap_flag_space(u8 **datap, uint index)
 {
 	struct tpm_nv_data_public pub;
@@ -246,48 +174,25 @@
 	case TPM_CMD_NV_WRITE_VALUE:
 		index = get_unaligned_be32(sendbuf + 10);
 		length = get_unaligned_be32(sendbuf + 18);
-		seq = index_to_seq(index);
+		seq = sb_tpm_index_to_seq(index);
 		if (seq < 0)
 			return -EINVAL;
 		printf("tpm: nvwrite index=%#02x, len=%#02x\n", index, length);
-		memcpy(&tpm->nvdata[seq].data, sendbuf + 22, length);
-		tpm->nvdata[seq].present = true;
-		*recv_len = 12;
-		memset(recvbuf, '\0', *recv_len);
+		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 = index_to_seq(index);
+		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_RESPONSE_HEADER_LENGTH + sizeof(uint32_t) +
-					length;
+		*recv_len = TPM_HDR_LEN + 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 if (!tpm->nvdata[seq].present) {
-			put_unaligned_be32(TPM_BADINDEX, recvbuf +
-					   sizeof(uint16_t) + sizeof(uint32_t));
-		} else {
-			memcpy(recvbuf + TPM_RESPONSE_HEADER_LENGTH +
-			       sizeof(uint32_t), &tpm->nvdata[seq].data,
-			       length);
-		}
+		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;