summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2016-06-28 10:27:16 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-07-01 05:16:08 -0700
commit25589e5a1e07398238b514eb60a543ea8108e278 (patch)
tree6a4d87b3fc7590d86457a8de50c6b0479b908bc8
parent3c9acf83d516d4de01ecd3e8a7eb9486bb2896f2 (diff)
downloadvboot-25589e5a1e07398238b514eb60a543ea8108e278.tar.gz
tpm2: add marshaling/unmarshaling and tlcl support
The marshaling code is a port of the coreboot patch https://chromium-review.googlesource.com/353915. The only supported commands at this time are NV_read and NV_write. The tlcl layer includes functions necessary to satisfy compilation requirements of rollback_index.c, functions to lock spaces and clear TPM are not yet implemented, they just report being invoked. The missing functions implementation is coming, but even without it it is possible to boot Chrome OS with firmware and kernel rollback counters maintained in the TPM NVRAM. BRANCH=none BUG=chrome-os-partner:50645 TEST=with depthcharge patches applied kevin/gru boards boot into chrome OS with rollback counters read from/written to TPM2 Change-Id: I29fe9069d7c37c33d354f36c93bda15d439bf74f Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/356753 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--firmware/include/tpm2_marshaling.h49
-rw-r--r--firmware/lib/tpm2_lite/marshaling.c380
-rw-r--r--firmware/lib/tpm2_lite/tlcl.c140
3 files changed, 569 insertions, 0 deletions
diff --git a/firmware/include/tpm2_marshaling.h b/firmware/include/tpm2_marshaling.h
new file mode 100644
index 00000000..c72b076b
--- /dev/null
+++ b/firmware/include/tpm2_marshaling.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 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.
+ */
+#ifndef __SRC_LIB_TPM2_MARSHALING_H
+#define __SRC_LIB_TPM2_MARSHALING_H
+
+#include "tss_constants.h"
+
+/* The below functions are used to serialize/deserialize TPM2 commands. */
+
+/**
+ * tpm_marshal_command
+ *
+ * Given a structure containing a TPM2 command, serialize the structure for
+ * sending it to the TPM.
+ *
+ * @command: code of the TPM2 command to marshal
+ * @tpm_command_body: a pointer to the command specific structure
+ * @buffer: buffer where command is marshaled to
+ * @buffer_size: size of the buffer
+ *
+ * Returns number of bytes placed in the buffer, or -1 on error.
+ *
+ */
+int tpm_marshal_command(TPM_CC command, void *tpm_command_body,
+ void *buffer, int buffer_size);
+
+/**
+ * tpm_unmarshal_response
+ *
+ * Given a buffer received from the TPM in response to a certain command,
+ * deserialize the buffer into the expeced response structure.
+ *
+ * struct tpm2_response is a union of all possible responses.
+ *
+ * @command: code of the TPM2 command for which a response is unmarshaled
+ * @response_body: buffer containing the serialized response.
+ * @response_size: number of bytes in the buffer containing response
+ *
+ * Returns a pointer to the deserialized response or NULL in case of
+ * unmarshaling problems.
+ */
+struct tpm2_response *tpm_unmarshal_response(TPM_CC command,
+ void *response_body,
+ int response_size);
+
+#endif // __SRC_LIB_TPM2_MARSHALING_H
diff --git a/firmware/lib/tpm2_lite/marshaling.c b/firmware/lib/tpm2_lite/marshaling.c
new file mode 100644
index 00000000..672ffad0
--- /dev/null
+++ b/firmware/lib/tpm2_lite/marshaling.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2016 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 "tpm2_marshaling.h"
+#include "utility.h"
+
+static uint16_t tpm_tag; /* Depends on the command type. */
+
+static void write_be16(void *dest, uint16_t val)
+{
+ uint8_t *byte_dest = dest;
+
+ byte_dest[0] = val >> 8;
+ byte_dest[1] = val;
+}
+
+static void write_be32(void *dest, uint32_t val)
+{
+ uint8_t *byte_dest = dest;
+
+ byte_dest[0] = val >> 24;
+ byte_dest[1] = val >> 16;
+ byte_dest[2] = val >> 8;
+ byte_dest[3] = val;
+}
+
+static uint16_t read_be16(const void *src)
+{
+ const uint8_t *s = src;
+ return (((uint16_t)s[0]) << 8) | (((uint16_t)s[1]) << 0);
+}
+
+static inline uint32_t read_be32(const void *src)
+{
+ const uint8_t *s = src;
+
+ return (((uint32_t)s[0]) << 24) | (((uint32_t)s[1]) << 16) |
+ (((uint32_t)s[2]) << 8) | (((uint32_t)s[3]) << 0);
+}
+
+/*
+ * Each unmarshaling function receives a pointer to the buffer pointer and a
+ * pointer to the size of data still in the buffer. The function extracts data
+ * from the buffer and adjusts both buffer pointer and remaining data size.
+ *
+ * Should there be not enough data in the buffer to unmarshal the required
+ * object, the remaining data size is set to -1 to indicate the error. The
+ * remaining data size is expected to be set to zero once the last data item
+ * has been extracted from the buffer.
+ */
+
+static uint16_t unmarshal_u16(void **buffer, int *buffer_space)
+{
+ uint16_t value;
+
+ if (*buffer_space < sizeof(value)) {
+ *buffer_space = -1; /* Indicate a failure. */
+ return 0;
+ }
+
+ value = read_be16(*buffer);
+ *buffer = (void *) ((uintptr_t) (*buffer) + sizeof(value));
+ *buffer_space -= sizeof(value);
+
+ return value;
+}
+
+static uint16_t unmarshal_u32(void **buffer, int *buffer_space)
+{
+ uint32_t value;
+
+ if (*buffer_space < sizeof(value)) {
+ *buffer_space = -1; /* Indicate a failure. */
+ return 0;
+ }
+
+ value = read_be32(*buffer);
+ *buffer = (void *) ((uintptr_t) (*buffer) + sizeof(value));
+ *buffer_space -= sizeof(value);
+
+ return value;
+}
+
+static void unmarshal_TPM2B_MAX_NV_BUFFER(void **buffer,
+ int *size,
+ TPM2B_MAX_NV_BUFFER *nv_buffer)
+{
+ nv_buffer->t.size = unmarshal_u16(buffer, size);
+ if (nv_buffer->t.size > *size) {
+ VBDEBUG(("%s:%d - "
+ "size mismatch: expected %d, remaining %d\n",
+ __func__, __LINE__, nv_buffer->t.size, *size));
+ return;
+ }
+
+ nv_buffer->t.buffer = *buffer;
+
+ *buffer = ((uint8_t *)(*buffer)) + nv_buffer->t.size;
+ *size -= nv_buffer->t.size;
+}
+
+static void unmarshal_nv_read(void **buffer, int *size,
+ struct nv_read_response *nvr)
+{
+ /* Total size of the parameter field. */
+ nvr->params_size = unmarshal_u32(buffer, size);
+ unmarshal_TPM2B_MAX_NV_BUFFER(buffer, size, &nvr->buffer);
+
+ if (nvr->params_size !=
+ (nvr->buffer.t.size + sizeof(nvr->buffer.t.size))) {
+ VBDEBUG(("%s:%d - parameter/buffer %d/%d size mismatch",
+ __func__, __LINE__, nvr->params_size,
+ nvr->buffer.t.size));
+ return;
+ }
+
+ if (*size < 0)
+ return;
+ /*
+ * Let's ignore the authorisation section. It should be 5 bytes total,
+ * just confirm that this is the case and report any discrepancy.
+ */
+ if (*size != 5)
+ VBDEBUG(("%s:%d - unexpected authorisation seciton size %d\n",
+ __func__, __LINE__, *size));
+
+ *buffer = ((uint8_t *)(*buffer)) + *size;
+ *size = 0;
+}
+
+
+/*
+ * Each marshaling function receives a pointer to the buffer to marshal into,
+ * a pointer to the data item to be marshaled, and a pointer to the remaining
+ * room in the buffer.
+ */
+
+ /*
+ * Marshaling an arbitrary blob requires its size in addition to common
+ * parameter set.
+ */
+static void marshal_blob(void **buffer, void *blob,
+ size_t blob_size, int *buffer_space)
+{
+ if (*buffer_space < blob_size) {
+ *buffer_space = -1;
+ return;
+ }
+
+ Memcpy(*buffer, blob, blob_size);
+ buffer_space -= blob_size;
+ *buffer = (void *)((uintptr_t)(*buffer) + blob_size);
+}
+
+static void marshal_u8(void **buffer, uint8_t value, int *buffer_space)
+{
+ uint8_t *bp = *buffer;
+
+ if (*buffer_space < sizeof(value)) {
+ *buffer_space = -1;
+ return;
+ }
+
+ *bp++ = value;
+ *buffer = bp;
+ *buffer_space -= sizeof(value);
+}
+
+static void marshal_u16(void **buffer, uint16_t value, int *buffer_space)
+{
+ if (*buffer_space < sizeof(value)) {
+ *buffer_space = -1;
+ return;
+ }
+ write_be16(*buffer, value);
+ *buffer = (void *)((uintptr_t)(*buffer) + sizeof(value));
+ *buffer_space -= sizeof(value);
+}
+
+static void marshal_u32(void **buffer, uint32_t value, int *buffer_space)
+{
+ if (*buffer_space < sizeof(value)) {
+ *buffer_space = -1;
+ return;
+ }
+
+ write_be32(*buffer, value);
+ *buffer = (void *)((uintptr_t)(*buffer) + sizeof(value));
+ *buffer_space -= sizeof(value);
+}
+
+#define unmarshal_TPM_CC(a, b) unmarshal_u32(a, b)
+#define marshal_TPM_HANDLE(a, b, c) marshal_u32(a, b, c)
+
+static void marshal_session_header(void **buffer,
+ struct tpm2_session_header *session_header,
+ int *buffer_space)
+{
+ int base_size;
+ void *size_location = *buffer;
+
+ /* Skip room for the session header size. */
+ *buffer_space -= sizeof(uint32_t);
+ *buffer = (void *)(((uintptr_t) *buffer) + sizeof(uint32_t));
+
+ base_size = *buffer_space;
+
+ marshal_u32(buffer, session_header->session_handle, buffer_space);
+ marshal_u16(buffer, session_header->nonce_size, buffer_space);
+ marshal_blob(buffer, session_header->nonce,
+ session_header->nonce_size, buffer_space);
+ marshal_u8(buffer, session_header->session_attrs, buffer_space);
+ marshal_u16(buffer, session_header->auth_size, buffer_space);
+ marshal_blob(buffer, session_header->auth,
+ session_header->auth_size, buffer_space);
+
+ if (*buffer_space < 0)
+ return; /* The structure did not fit. */
+
+ /* Paste in the session size. */
+ marshal_u32(&size_location, base_size - *buffer_space, &base_size);
+}
+
+static void marshal_TPM2B(void **buffer,
+ TPM2B *data,
+ int *buffer_space)
+{
+ size_t total_size = data->size + sizeof(data->size);
+
+ if (total_size > *buffer_space) {
+ *buffer_space = -1;
+ return;
+ }
+ marshal_u16(buffer, data->size, buffer_space);
+ Memcpy(*buffer, data->buffer, data->size);
+ *buffer = ((uint8_t *)(*buffer)) + data->size;
+ *buffer_space -= data->size;
+}
+
+static void marshal_nv_write(void **buffer,
+ struct tpm2_nv_write_cmd *command_body,
+ int *buffer_space)
+{
+ struct tpm2_session_header session_header;
+
+ marshal_TPM_HANDLE(buffer, TPM_RH_PLATFORM, buffer_space);
+ marshal_TPM_HANDLE(buffer, command_body->nvIndex, buffer_space);
+ Memset(&session_header, 0, sizeof(session_header));
+ session_header.session_handle = TPM_RS_PW;
+ marshal_session_header(buffer, &session_header, buffer_space);
+ tpm_tag = TPM_ST_SESSIONS;
+
+ marshal_TPM2B(buffer, &command_body->data.b, buffer_space);
+ marshal_u16(buffer, command_body->offset, buffer_space);
+}
+
+static void marshal_nv_read(void **buffer,
+ struct tpm2_nv_read_cmd *command_body,
+ int *buffer_space)
+{
+ struct tpm2_session_header session_header;
+
+ marshal_TPM_HANDLE(buffer, TPM_RH_PLATFORM, buffer_space);
+ marshal_TPM_HANDLE(buffer, command_body->nvIndex, buffer_space);
+ Memset(&session_header, 0, sizeof(session_header));
+ session_header.session_handle = TPM_RS_PW;
+ marshal_session_header(buffer, &session_header, buffer_space);
+ tpm_tag = TPM_ST_SESSIONS;
+ marshal_u16(buffer, command_body->size, buffer_space);
+ marshal_u16(buffer, command_body->offset, buffer_space);
+}
+
+
+int tpm_marshal_command(TPM_CC command, void *tpm_command_body,
+ void *buffer, int buffer_size)
+{
+ void *cmd_body = (uint8_t *)buffer + sizeof(struct tpm_header);
+ int max_body_size = buffer_size - sizeof(struct tpm_header);
+ int body_size = max_body_size;
+
+ /* Will be modified when marshaling some commands. */
+ tpm_tag = TPM_ST_NO_SESSIONS;
+
+ switch (command) {
+
+ case TPM2_NV_Read:
+ marshal_nv_read(&cmd_body, tpm_command_body, &body_size);
+ break;
+
+ case TPM2_NV_Write:
+ marshal_nv_write(&cmd_body, tpm_command_body, &body_size);
+ break;
+
+ default:
+ body_size = -1;
+ VBDEBUG(("%s:%d:Request to marshal unsupported command %#x\n",
+ __FILE__, __LINE__, command));
+ }
+
+ if (body_size > 0) {
+
+ /* See how much room was taken by marshaling. */
+ body_size = max_body_size - body_size;
+
+ body_size += sizeof(struct tpm_header);
+
+ marshal_u16(&buffer, tpm_tag, &max_body_size);
+ marshal_u32(&buffer, body_size, &max_body_size);
+ marshal_u32(&buffer, command, &max_body_size);
+ }
+
+ return body_size;
+}
+
+struct tpm2_response *tpm_unmarshal_response(TPM_CC command,
+ void *response_body,
+ int cr_size)
+{
+ static struct tpm2_response tpm2_resp;
+
+ if (cr_size < sizeof(struct tpm_header))
+ return NULL;
+
+ tpm2_resp.hdr.tpm_tag = unmarshal_u16(&response_body, &cr_size);
+ tpm2_resp.hdr.tpm_size = unmarshal_u32(&response_body, &cr_size);
+ tpm2_resp.hdr.tpm_code = unmarshal_TPM_CC(&response_body, &cr_size);
+
+ if (!cr_size) {
+ if (tpm2_resp.hdr.tpm_size != sizeof(tpm2_resp.hdr))
+ VBDEBUG(("%s: size mismatch in response to command %#x\n",
+ __func__, command));
+ return &tpm2_resp;
+ }
+
+ switch (command) {
+ case TPM2_NV_Read:
+ unmarshal_nv_read(&response_body, &cr_size,
+ &tpm2_resp.nvr);
+ break;
+
+ case TPM2_NV_Write:
+ /* Session data included in response can be safely ignored. */
+ cr_size = 0;
+ break;
+
+ default:
+ {
+ int i;
+
+ VBDEBUG(("%s:%d:"
+ "Request to unmarshal unexpected command %#x,"
+ " code %#x",
+ __func__, __LINE__, command,
+ tpm2_resp.hdr.tpm_code));
+
+ for (i = 0; i < cr_size; i++) {
+ if (!(i % 16))
+ VBDEBUG(("\n"));
+ VBDEBUG(("%2.2x ",
+ ((uint8_t *)response_body)[i]));
+ }
+ }
+ VBDEBUG(("\n"));
+ return NULL;
+ }
+
+ if (cr_size) {
+ VBDEBUG(("%s:%d got %d bytes back in response to %#x,"
+ " failed to parse (%d)\n",
+ __func__, __LINE__, tpm2_resp.hdr.tpm_size,
+ command, cr_size));
+ return NULL;
+ }
+
+ /* The entire message have been parsed. */
+ return &tpm2_resp;
+}
diff --git a/firmware/lib/tpm2_lite/tlcl.c b/firmware/lib/tpm2_lite/tlcl.c
new file mode 100644
index 00000000..ce65c6ce
--- /dev/null
+++ b/firmware/lib/tpm2_lite/tlcl.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2016 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.
+ *
+ * Some TPM constants and type definitions for standalone compilation for use
+ * in the firmware
+ */
+
+#include "tpm2_marshaling.h"
+#include "utility.h"
+
+static void *tpm_process_command(TPM_CC command, void *command_body)
+{
+ /* Command/response buffer. */
+ static uint8_t cr_buffer[TPM_BUFFER_SIZE];
+ uint32_t out_size, in_size;
+
+ out_size = tpm_marshal_command(command, command_body,
+ cr_buffer, sizeof(cr_buffer));
+ if (out_size < 0) {
+ VBDEBUG(("command %#x, cr size %d\n",
+ command, out_size));
+ return NULL;
+ }
+
+ in_size = sizeof(cr_buffer);
+ if (VbExTpmSendReceive(cr_buffer, out_size,
+ cr_buffer, &in_size) != TPM_SUCCESS) {
+ VBDEBUG(("tpm transaction failed\n"));
+ return NULL;
+ }
+
+ return tpm_unmarshal_response(command, cr_buffer, in_size);
+}
+
+/**
+ * Issue a ForceClear. The TPM error code is returned.
+ */
+uint32_t TlclForceClear(void)
+{
+ VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
+ return TPM_SUCCESS;
+}
+
+uint32_t TlclSetDeactivated(uint8_t flag)
+{
+ VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
+ return TPM_SUCCESS;
+}
+
+uint32_t TlclSetEnable(void)
+{
+ VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
+ return TPM_SUCCESS;
+}
+
+
+/**
+ * Get the permission bits for the NVRAM space with |index|.
+ */
+uint32_t TlclGetPermissions(uint32_t index, uint32_t *permissions)
+{
+ *permissions = 0;
+ VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
+ return TPM_SUCCESS;
+}
+
+/**
+ * Turn off physical presence and locks it off until next reboot. The TPM
+ * error code is returned.
+ */
+uint32_t TlclLockPhysicalPresence(void)
+{
+ VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
+ return TPM_SUCCESS;
+}
+
+uint32_t TlclRead(uint32_t index, void* data, uint32_t length)
+{
+ struct tpm2_nv_read_cmd nv_readc;
+ struct tpm2_response *response;
+
+ Memset(&nv_readc, 0, sizeof(nv_readc));
+
+ nv_readc.nvIndex = HR_NV_INDEX + index;
+ nv_readc.size = length;
+
+ response = tpm_process_command(TPM2_NV_Read, &nv_readc);
+
+ /* Need to map tpm error codes into internal values. */
+ if (!response)
+ return TPM_E_READ_FAILURE;
+
+ VBDEBUG(("%s:%d index %#x return code %x\n",
+ __FILE__, __LINE__, index, response->hdr.tpm_code));
+ switch (response->hdr.tpm_code) {
+ case 0:
+ break;
+
+ case 0x28b:
+ return TPM_E_BADINDEX;
+
+ default:
+ return TPM_E_READ_FAILURE;
+ }
+
+ if (length > response->nvr.buffer.t.size)
+ return TPM_E_RESPONSE_TOO_LARGE;
+
+ if (length < response->nvr.buffer.t.size)
+ return TPM_E_READ_EMPTY;
+
+ Memcpy(data, response->nvr.buffer.t.buffer, length);
+
+ return TPM_SUCCESS;
+}
+
+uint32_t TlclWrite(uint32_t index, const void *data, uint32_t length)
+{
+ struct tpm2_nv_write_cmd nv_writec;
+ struct tpm2_response *response;
+
+ Memset(&nv_writec, 0, sizeof(nv_writec));
+
+ nv_writec.nvIndex = HR_NV_INDEX + index;
+ nv_writec.data.t.size = length;
+ nv_writec.data.t.buffer = data;
+
+ response = tpm_process_command(TPM2_NV_Write, &nv_writec);
+
+ /* Need to map tpm error codes into internal values. */
+ if (!response)
+ return TPM_E_WRITE_FAILURE;
+
+ VBDEBUG(("%s:%d return code %x\n", __func__, __LINE__,
+ response->hdr.tpm_code));
+
+ return TPM_SUCCESS;
+}