aboutsummaryrefslogtreecommitdiff
path: root/src/lib/tpm2_tlcl.c
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2016-04-05 16:01:57 -0700
committerMartin Roth <martinroth@google.com>2016-07-11 23:43:01 +0200
commit245d4577d507c0b45067d2e520ae94b283a91567 (patch)
treeea1dc481be1e000833189766f9c4881f22b723fe /src/lib/tpm2_tlcl.c
parent05155c0013d76ef324edcedb40ab61e50807a6a8 (diff)
tpm2: implement tlcl layer
This is the first approximation of implementing TPM2 support in coreboot. It is very clearly incomplete, some of the larger missing pieces being: - PCR(s) modification - protection NVRAM spaces from unauthorized deletion/modification. - resume handling - cr50 specific factory initialization The existing TPM1.2 firmware API is being implemented for TPM2. Some functions are not required at all, some do not map fully, but the API is not yet being changed, many functions are just stubs. An addition to the API is the new tlcl_define_space() function. It abstracts TMP internals allowing the caller to specify the privilege level of the space to be defined. Two privilege levels are defined, higher for the RO firmware and lower for RW firmware, they determine who can write into the spaces. BRANCH=none BUG=chrome-os-partner:50645 TEST=with the rest of the patches applied Kevin/Gru devices can initialize and use firmware and kernel spaces Change-Id: Ife3301cf161ce38d61f11e4b60f1b43cab9a4eba Signed-off-by: Martin Roth <martinroth@chromium.org> Original-Commit-Id: bcc8e62604c705798ca106e7995a0960b92b3f35 Original-Change-Id: Ib340fa8e7db51c10e5080973c16a19b0ebbb61e6 Original-Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/353914 Original-Commit-Ready: Martin Roth <martinroth@chromium.org> Original-Reviewed-by: Duncan Laurie <dlaurie@chromium.org> Reviewed-on: https://review.coreboot.org/15569 Tested-by: build bot (Jenkins) Reviewed-by: Furquan Shaikh <furquan@google.com> Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@googlemail.com>
Diffstat (limited to 'src/lib/tpm2_tlcl.c')
-rw-r--r--src/lib/tpm2_tlcl.c284
1 files changed, 284 insertions, 0 deletions
diff --git a/src/lib/tpm2_tlcl.c b/src/lib/tpm2_tlcl.c
new file mode 100644
index 0000000000..c352a2c4a6
--- /dev/null
+++ b/src/lib/tpm2_tlcl.c
@@ -0,0 +1,284 @@
+/*
+ * 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 <console/console.h>
+#include <endian.h>
+#include <lib/tpm2_tlcl_structures.h>
+#include <string.h>
+#include <tpm.h>
+#include <vb2_api.h>
+
+#include "tpm2_marshaling.h"
+
+/*
+ * This file provides interface between firmware and TPM2 device. The TPM1.2
+ * API was copied as is and relevant functions modified to comply with the
+ * TPM2 specification.
+ */
+
+static void *tpm_process_command(TPM_CC command, void *command_body)
+{
+ size_t out_size, in_size;
+ /* Command/response buffer. */
+ static uint8_t cr_buffer[TPM_BUFFER_SIZE];
+
+ out_size = tpm_marshal_command(command, command_body,
+ cr_buffer, sizeof(cr_buffer));
+ if (out_size < 0) {
+ printk(BIOS_ERR, "command %#x, cr size %zd\n",
+ command, out_size);
+ return NULL;
+ }
+
+ in_size = sizeof(cr_buffer);
+ if (tis_sendrecv(cr_buffer, out_size,
+ cr_buffer, &in_size)) {
+ printk(BIOS_ERR, "tpm transaction failed\n");
+ return NULL;
+ }
+
+ return tpm_unmarshal_response(command, cr_buffer, in_size);
+}
+
+
+uint32_t tlcl_get_permanent_flags(TPM_PERMANENT_FLAGS *pflags)
+{
+ printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_resume(void)
+{
+ printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_assert_physical_presence(void)
+{
+ /*
+ * Nothing to do on TPM2 for this, use platform hierarchy availability
+ * instead.
+ */
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_extend(int pcr_num, const uint8_t *in_digest,
+ uint8_t *out_digest)
+{
+ printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_finalize_physical_presence(void)
+{
+ /* Nothing needs to be done with tpm2. */
+ printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_force_clear(void)
+{
+ printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_get_flags(uint8_t *disable, uint8_t *deactivated,
+ uint8_t *nvlocked)
+{
+ /*
+ * TPM2 does not map directly into these flags TPM1.2 based firmware
+ * expects to be able to retrieve.
+ *
+ * In any case, if any of these conditions are present, the following
+ * firmware flow would be interrupted and will have a chance to report
+ * an error. Let's just hardcode an "All OK" response for now.
+ */
+
+ if (disable)
+ *disable = 0;
+
+ if (nvlocked)
+ *nvlocked = 1;
+
+ if (deactivated)
+ *deactivated = 0;
+
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_lib_init(void)
+{
+ /*
+ * This function is called directly by vboot, uses vboot return
+ * types.
+ */
+ if (tis_init())
+ return VB2_ERROR_UNKNOWN;
+ if (tis_open())
+ return VB2_ERROR_UNKNOWN;
+ return VB2_SUCCESS;
+}
+
+uint32_t tlcl_physical_presence_cmd_enable(void)
+{
+ printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_read(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;
+
+ printk(BIOS_INFO, "%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 tlcl_self_test_full(void)
+{
+ struct tpm2_self_test st;
+ struct tpm2_response *response;
+
+ st.yes_no = 1;
+
+ response = tpm_process_command(TPM2_SelfTest, &st);
+ printk(BIOS_INFO, "%s: response is %x\n",
+ __func__, response ? response->hdr.tpm_code : -1);
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_set_deactivated(uint8_t flag)
+{
+ printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_set_enable(void)
+{
+ printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_set_global_lock(void)
+{
+ /*
+ * This is where the locking of the RO NVram index is supposed to
+ * happen. The most likely way to achieve it is to extend PCR used for
+ * policy when defining this space.
+ */
+ printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ return TPM_SUCCESS;
+}
+uint32_t tlcl_set_nv_locked(void)
+{
+ printk(BIOS_INFO, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_startup(void)
+{
+ struct tpm2_startup startup;
+ struct tpm2_response *response;
+
+ startup.startup_type = TPM_SU_CLEAR;
+ response = tpm_process_command(TPM2_Startup, &startup);
+ if (response && response->hdr.tpm_code &&
+ (response->hdr.tpm_code != TPM_RC_INITIALIZE)) {
+ printk(BIOS_INFO, "startup return code is %x\n",
+ response->hdr.tpm_code);
+ return TPM_E_IOERROR;
+ }
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_write(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;
+
+ printk(BIOS_INFO, "%s:%d return code %x\n", __func__, __LINE__,
+ response->hdr.tpm_code);
+
+ return TPM_SUCCESS;
+}
+
+uint32_t tlcl_define_space(uint32_t space_index,
+ enum privilege_level priv_level, size_t space_size)
+{
+ struct tpm2_nv_define_space_cmd nvds_cmd;
+ struct tpm2_response *response;
+
+ /* Prepare the define space command structure. */
+ memset(&nvds_cmd, 0, sizeof(nvds_cmd));
+
+ nvds_cmd.publicInfo.dataSize = space_size;
+ nvds_cmd.publicInfo.nvIndex = HR_NV_INDEX + space_index;
+ nvds_cmd.publicInfo.nameAlg = TPM_ALG_SHA256;
+
+ /* Attributes common for all privilege levels. */
+ nvds_cmd.publicInfo.attributes.TPMA_NV_PPWRITE = 1;
+ nvds_cmd.publicInfo.attributes.TPMA_NV_AUTHREAD = 1;
+ nvds_cmd.publicInfo.attributes.TPMA_NV_PPREAD = 1;
+ nvds_cmd.publicInfo.attributes.TPMA_NV_PLATFORMCREATE = 1;
+
+ if (priv_level == high_privilege) {
+ nvds_cmd.publicInfo.attributes.TPMA_NV_WRITE_STCLEAR = 1;
+ nvds_cmd.publicInfo.attributes.TPMA_NV_POLICY_DELETE = 1;
+ }
+
+ response = tpm_process_command(TPM2_NV_DefineSpace, &nvds_cmd);
+ printk(BIOS_INFO, "%s: response is %x\n",
+ __func__, response ? response->hdr.tpm_code : -1);
+
+ if (!response)
+ return TPM_E_NO_DEVICE;
+
+ return response->hdr.tpm_code ? TPM_E_INTERNAL_INCONSISTENCY :
+ TPM_SUCCESS;
+}