summaryrefslogtreecommitdiff
path: root/board/cr50/tpm2
diff options
context:
space:
mode:
authorLouis Collard <louiscollard@chromium.org>2018-06-26 19:57:13 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-08-14 16:05:22 -0700
commita1e04f74c47d003bddd1802ab50b3ac74c2ea875 (patch)
tree3d6d6b7f8c513ab6bb5dbfbebbcd1475b11a392f /board/cr50/tpm2
parent5419afaaf8986380a1e293330589b7850a074939 (diff)
downloadchrome-ec-a1e04f74c47d003bddd1802ab50b3ac74c2ea875.tar.gz
cr50: Add support for virtual NV indexes.
This is to allow reading of board ID and serial number through NV indexes. CQ-DEPEND=CL:1114675 BRANCH=none BUG=b:110971075, chromium:846114 TEST=NV_Read of virtual indices using trunks_send --raw, b:110971075 #3 Change-Id: Iaf7256ea50e36021b61c92f8606d1a62af37df2d Signed-off-by: Louis Collard <louiscollard@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1114674 Reviewed-by: Andrey Pronin <apronin@chromium.org>
Diffstat (limited to 'board/cr50/tpm2')
-rw-r--r--board/cr50/tpm2/NVMem.c6
-rw-r--r--board/cr50/tpm2/mfg_nvmem_allocations.txt24
-rw-r--r--board/cr50/tpm2/virtual_nvmem.c314
3 files changed, 344 insertions, 0 deletions
diff --git a/board/cr50/tpm2/NVMem.c b/board/cr50/tpm2/NVMem.c
index 33ce13a336..1a2515236e 100644
--- a/board/cr50/tpm2/NVMem.c
+++ b/board/cr50/tpm2/NVMem.c
@@ -12,6 +12,7 @@
#include <string.h>
+#include "Platform.h"
#include "PlatformData.h"
#include "TpmError.h"
#include "assert.h"
@@ -114,6 +115,11 @@ void _plat__NvMemoryRead(unsigned int startOffset,
unsigned int size,
void *data)
{
+ if (_plat__NvOffsetIsVirtual(startOffset)) {
+ _plat__NvVirtualMemoryRead(startOffset, size, data);
+ return;
+ }
+
assert(startOffset + size <= NV_MEMORY_SIZE);
/* Copy the data from the NV image */
#ifdef CONFIG_FLASH_NVMEM
diff --git a/board/cr50/tpm2/mfg_nvmem_allocations.txt b/board/cr50/tpm2/mfg_nvmem_allocations.txt
new file mode 100644
index 0000000000..a17a71b7f1
--- /dev/null
+++ b/board/cr50/tpm2/mfg_nvmem_allocations.txt
@@ -0,0 +1,24 @@
+A range of NV indices are reserved for use by the TPM manufacturer,
+and can be allocated arbitrarily, without the need to consult the TCG,
+or any expectation that these be consistent across different models
+of TPM. See 'Registery of reserved TPM 2.0 handles and localities' for
+more details.
+
+The range allocated to TPM manufacturers is 0x01000000 - 0x013fffff
+
+This file documents indices that have been allocated as part of the TPM
+implementation in cr50.
+
+Index Description Definition
+
+0x01001007 FIRMWARE_NV_INDEX src/platform/vboot_reference/firmware/lib/include/rollback_index.h
+0x01001008 KERNEL_NV_INDEX src/platform/vboot_reference/firmware/lib/include/rollback_index.h
+0x01001009 BACKUP_NV_INDEX src/platform/vboot_reference/firmware/lib/include/rollback_index.h
+0x0100100a FWMP_NV_INDEX src/platform/vboot_reference/firmware/lib/include/rollback_index.h
+0x0100100b REC_HASH_NV_INDEX src/platform/vboot_reference/firmware/lib/include/rollback_index.h
+
+ Virtual NV indices src/platform/ec/board/cr50/tpm2/virtual_nvmem.c
+0x013fff00 BOARD_ID
+0x013fff01 SN_BITS
+- to - Reserved
+0x013fffff
diff --git a/board/cr50/tpm2/virtual_nvmem.c b/board/cr50/tpm2/virtual_nvmem.c
new file mode 100644
index 0000000000..9bb6caec0d
--- /dev/null
+++ b/board/cr50/tpm2/virtual_nvmem.c
@@ -0,0 +1,314 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <string.h>
+
+#include "Global.h"
+
+#include "board_id.h"
+#include "console.h"
+#include "link_defs.h"
+
+/*
+ * Functions to allow access to non-NVRam data through NVRam Indexes.
+ *
+ * These functions map virtual NV indexes to virtual offsets, and allow
+ * reads from those virtual offsets. The functions are contrained based on the
+ * implementation of the calling TPM functions; these constraints and other
+ * assumptions are described below.
+ *
+ * The TPM NVRam functions make use of the available NVRam space to store NVRam
+ * Indexes in a linked list with the following structure:
+ *
+ * struct nvram_list_node {
+ * UINT32 next_node_offset;
+ * TPM_HANDLE this_node_handle;
+ * NV_INDEX index;
+ * BYTE data[];
+ * };
+ *
+ * The TPM functions for operating on NVRam begin by iterating through the list
+ * to find the offset for the relevant Index.
+ *
+ * See NvFindHandle() in //third_party/tpm2/NV.c for more details.
+ *
+ * Once the offset has been found, read operations on the NV Index will
+ * call _plat__NvMemoryRead() twice, first to read the NV_INDEX data, and
+ * second to read the actual NV data.
+ *
+ * The offset x returned by NvFindHandle() is to the this_node_handle element of
+ * the linked list node; the subsequent reads are therefore to
+ * x+sizeof(TPM_HANDLE) and x+sizeof(TPM_HANDLE)+sizeof(NV_INDEX).
+ *
+ * The first read, to retrieve NV_INDEX data, is always a fixed size
+ * (sizeof(NV_INDEX)). The size of the second read is user defined, but will
+ * not exceed the size of the data.
+ */
+
+/* Size constraints for virtual NV indexes. */
+#define VIRTUAL_NV_INDEX_HEADER_SIZE sizeof(NV_INDEX)
+#define MAX_VIRTUAL_NV_INDEX_DATA_SIZE 0x200
+#define MAX_VIRTUAL_NV_INDEX_SLOT_SIZE (sizeof(TPM_HANDLE) + \
+ VIRTUAL_NV_INDEX_HEADER_SIZE + \
+ MAX_VIRTUAL_NV_INDEX_DATA_SIZE)
+
+/*
+ * Prefix for virtual NV offsets. Chosen such that all virtual NV offsets are
+ * not valid memory addresses, to ensure it is impossible to accidentally read
+ * (incorrect) virtual NV data from anywhere other than these functions.
+ */
+#define VIRTUAL_NV_OFFSET_START 0xfff00000
+#define VIRTUAL_NV_OFFSET_END 0xffffffff
+/* Used to check if offsets are virtual. */
+#define VIRTUAL_NV_OFFSET_MASK (~(VIRTUAL_NV_OFFSET_END - \
+ VIRTUAL_NV_OFFSET_START))
+
+/*
+ * These offsets are the two offsets queried by the TPM code, as a result of the
+ * design of that code, and the linked list structure described above.
+ */
+#define NV_INDEX_READ_OFFSET 0x00000004 /* sizeof(uint32_t) */
+#define NV_DATA_READ_OFFSET 0x00000098 /* NV_INDEX_READ_OFFSET
+ * + sizeof(NV_INDEX)
+ */
+
+/* Template for the NV_INDEX data. */
+#define NV_INDEX_TEMPLATE { \
+ .publicArea = { \
+ .nameAlg = TPM_ALG_SHA256, \
+ .attributes = { \
+ /* Allow index to be read using its authValue. */ \
+ .TPMA_NV_AUTHREAD = 1, \
+ /* \
+ * The spec requires at least one write \
+ * authentication method to be specified. We \
+ * intentionally don't include one, so that \
+ * this index cannot be spoofed by an \
+ * attacker running a version of cr50 that \
+ * pre-dates the implementation of virtual \
+ * NV indices. \
+ * .TPMA_NV_AUTHWRITE = 1, \
+ * Only allow deletion if the authPolicy is \
+ * satisied. The authPolicy is empty, and so \
+ * cannot be satisfied, so this effectively \
+ * disables deletion. \
+ */ \
+ .TPMA_NV_POLICY_DELETE = 1, \
+ /* Prevent writes. */ \
+ .TPMA_NV_WRITELOCKED = 1, \
+ /* Write-lock will not be cleared on startup. */ \
+ .TPMA_NV_WRITEDEFINE = 1, \
+ /* Index has been written, can be read. */ \
+ .TPMA_NV_WRITTEN = 1, \
+ }, \
+ .authPolicy = { }, \
+ }, \
+ .authValue = { }, \
+};
+
+/* Configuration data for virtual NV indexes. */
+struct virtual_nv_index_cfg {
+ uint16_t size;
+
+ void (*get_data_fn)(BYTE *to, size_t offset, size_t size);
+} __packed;
+
+#define REGISTER_CONFIG(r_index, r_size, r_get_data_fn) \
+ [r_index - VIRTUAL_NV_INDEX_START] = { \
+ .size = r_size, \
+ .get_data_fn = r_get_data_fn \
+ },
+
+#define REGISTER_DEPRECATED_CONFIG(r_index) \
+ REGISTER_CONFIG(r_index, 0, 0)
+
+/*
+ * Currently supported virtual NV indexes.
+ *
+ * The range for virtual NV indexes is chosen such that all indexes
+ * fall within a range designated by the TCG for use by TPM manufacturers,
+ * without expectation of consultation with the TCG, or consistent behavior
+ * across TPM models. See Table 3 in the 'Registry of reserved TPM 2.0
+ * handles and localities' for more details.
+ *
+ * Active entries in this enum must have a size and data function registered
+ * with a REGISTER_CONFIG statement below.
+ *
+ * Deprecated indices should use the REGISTER_DEPRECATED_CONFIG variant.
+ *
+ * Values in this enum must be consecutive.
+ */
+enum virtual_nv_index {
+ VIRTUAL_NV_INDEX_START = 0x013fff00,
+ VIRTUAL_NV_INDEX_BOARD_ID = VIRTUAL_NV_INDEX_START,
+ VIRTUAL_NV_INDEX_END,
+};
+/* Reserved space for future virtual indexes; this is the last valid index. */
+#define VIRTUAL_NV_INDEX_MAX 0x013fffff
+
+static void GetBoardId(BYTE *to, size_t offset, size_t size);
+
+static const struct virtual_nv_index_cfg index_config[] = {
+ REGISTER_CONFIG(VIRTUAL_NV_INDEX_BOARD_ID,
+ 12 /* data_size */, GetBoardId)
+};
+
+/* Check sanity of above config. */
+BUILD_ASSERT(VIRTUAL_NV_INDEX_END <= (VIRTUAL_NV_INDEX_MAX + 1));
+BUILD_ASSERT((VIRTUAL_NV_INDEX_END - VIRTUAL_NV_INDEX_START) ==
+ ARRAY_SIZE(index_config));
+/* Check we will never overrun the virtual address space. */
+BUILD_ASSERT((VIRTUAL_NV_INDEX_MAX - VIRTUAL_NV_INDEX_START + 1) *
+ MAX_VIRTUAL_NV_INDEX_SLOT_SIZE <
+ (VIRTUAL_NV_OFFSET_END - VIRTUAL_NV_OFFSET_START));
+
+
+/*
+ * Helpers for dealing with NV indexes, associated configs and offsets.
+ */
+
+/*
+ * Looks up the config for the specified virtual NV index, and sets a default
+ * 'empty' config if the index is not defined.
+ */
+static inline void GetNvIndexConfig(
+ enum virtual_nv_index index, struct virtual_nv_index_cfg *cfg)
+{
+ if (index >= VIRTUAL_NV_INDEX_START && index < VIRTUAL_NV_INDEX_END) {
+ *cfg = index_config[index - VIRTUAL_NV_INDEX_START];
+ } else {
+ cfg->size = 0;
+ cfg->get_data_fn = 0;
+ }
+}
+
+/* Converts a virtual NV index to the corresponding virtual offset. */
+static inline BOOL NvIndexToNvOffset(uint32_t index)
+{
+ return VIRTUAL_NV_OFFSET_START +
+ ((index - VIRTUAL_NV_INDEX_START) *
+ MAX_VIRTUAL_NV_INDEX_SLOT_SIZE);
+}
+
+/* Converts an virtual offset to the corresponding NV Index. */
+static inline BOOL NvOffsetToNvIndex(uint32_t offset)
+{
+ return VIRTUAL_NV_INDEX_START +
+ ((offset - VIRTUAL_NV_OFFSET_START) /
+ MAX_VIRTUAL_NV_INDEX_SLOT_SIZE);
+}
+
+/*
+ * Copies the template NV_INDEX data to the specified destination, and updates
+ * it with the specified NV index and size values.
+ */
+static inline void CopyNvIndex(void *dest, size_t start, size_t count,
+ uint32_t nvIndex, uint32_t size)
+{
+ NV_INDEX nv_index_template = NV_INDEX_TEMPLATE;
+
+ nv_index_template.publicArea.nvIndex = nvIndex;
+ nv_index_template.publicArea.dataSize = size;
+ memcpy(dest, ((BYTE *) &nv_index_template) + start, count);
+}
+
+/*
+ * Functions exposed to the TPM2 code.
+ */
+
+uint32_t _plat__NvGetHandleVirtualOffset(uint32_t handle)
+{
+ if (handle >= VIRTUAL_NV_INDEX_START && handle <= VIRTUAL_NV_INDEX_MAX)
+ return NvIndexToNvOffset(handle);
+ else
+ return 0;
+}
+
+BOOL _plat__NvOffsetIsVirtual(unsigned int startOffset)
+{
+ return (startOffset & VIRTUAL_NV_OFFSET_MASK) ==
+ VIRTUAL_NV_OFFSET_START;
+}
+
+void _plat__NvVirtualMemoryRead(unsigned int startOffset, unsigned int size,
+ void *data)
+{
+ uint32_t nvIndex;
+ struct virtual_nv_index_cfg nvIndexConfig;
+ unsigned int offset;
+
+ nvIndex = NvOffsetToNvIndex(startOffset);
+ GetNvIndexConfig(nvIndex, &nvIndexConfig);
+
+ /* Calculate offset within this section. */
+ startOffset = startOffset - NvIndexToNvOffset(nvIndex);
+
+ offset = startOffset;
+ while (size > 0) {
+ int section_offset;
+ int copied;
+
+ if (offset < NV_INDEX_READ_OFFSET) {
+ /*
+ * The first 4 bytes are supposed to represent a pointer
+ * to the next element in the NV index list; we don't
+ * have a next item, so return 0.
+ */
+ copied = MIN(sizeof(TPM_HANDLE) - offset, size);
+
+ memset((BYTE *) data, 0, copied);
+ } else if (offset < NV_DATA_READ_OFFSET) {
+ /*
+ * The NV_INDEX section is the second section, which
+ * immediately folows the 'next' pointer above.
+ */
+ section_offset = offset - NV_INDEX_READ_OFFSET;
+ copied = MIN(VIRTUAL_NV_INDEX_HEADER_SIZE -
+ section_offset, size);
+
+ CopyNvIndex((BYTE *)data + offset - startOffset,
+ section_offset,
+ copied,
+ nvIndex, nvIndexConfig.size);
+ } else if (offset < NV_DATA_READ_OFFSET + nvIndexConfig.size) {
+ /*
+ * The actual NV data is the final section, which
+ * immediately follos the NV_INDEX.
+ */
+ section_offset = offset - NV_DATA_READ_OFFSET;
+ copied = MIN(nvIndexConfig.size - section_offset, size);
+
+ nvIndexConfig.get_data_fn((BYTE *)data + offset -
+ startOffset,
+ section_offset,
+ copied);
+ } else {
+ /* More data was requested than is available. */
+#ifdef CR50_DEV
+ cprints(CC_TPM,
+ "Invalid vNVRAM read, offset: %x, size: %x",
+ offset, size);
+#endif
+ memset((BYTE *)data + offset - startOffset, 0,
+ size);
+ break;
+ }
+
+ offset += copied;
+ size -= copied;
+ }
+}
+
+/*
+ * Helpers to fetch actual virtual NV data.
+ */
+
+static void GetBoardId(BYTE *to, size_t offset, size_t size)
+{
+ struct board_id board_id_tmp;
+
+ read_board_id(&board_id_tmp);
+ memcpy(to, ((BYTE *) &board_id_tmp) + offset, size);
+}