diff options
-rw-r--r-- | src/include/tpm_lite/tlcl.h | 23 | ||||
-rw-r--r-- | src/lib/Makefile.inc | 3 | ||||
-rw-r--r-- | src/lib/tpm2_tlcl.c | 284 | ||||
-rw-r--r-- | src/lib/tpm2_tlcl_structures.h | 325 |
4 files changed, 634 insertions, 1 deletions
diff --git a/src/include/tpm_lite/tlcl.h b/src/include/tpm_lite/tlcl.h index 77245922c9..c777ff997c 100644 --- a/src/include/tpm_lite/tlcl.h +++ b/src/include/tpm_lite/tlcl.h @@ -12,6 +12,7 @@ #ifndef TPM_LITE_TLCL_H_ #define TPM_LITE_TLCL_H_ #include <stdint.h> +#include <types.h> #include "tss_constants.h" @@ -56,12 +57,34 @@ uint32_t tlcl_self_test_full(void); */ uint32_t tlcl_continue_self_test(void); +#if IS_ENABLED(CONFIG_TPM) /** * Define a space with permission [perm]. [index] is the index for the space, * [size] the usable data size. The TPM error code is returned. */ uint32_t tlcl_define_space(uint32_t index, uint32_t perm, uint32_t size); +#elif IS_ENABLED(CONFIG_TPM2) + +/* + * This enum allows to communicate firmware privilege levels to the TPM layer, + * which can map them into its own attributes. + */ +enum privilege_level { + high_privilege = 1, + low_privilege +}; + +/* + * Define a TPM space. Privilege level describes who can modify the space + * (high_privilege - the RO code only, low_privilege - ether RO or RW. The + * privilege level needs to be dropped below low_privilege before starting the + * kernel. + */ +uint32_t tlcl_define_space(uint32_t space_index, + enum privilege_level priv_level, size_t space_size); +#endif + /** * Write [length] bytes of [data] to space at [index]. The TPM error code is * returned. diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index 0c34b7535d..fe8ec0ac8e 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -54,7 +54,8 @@ verstage-$(CONFIG_COMMON_CBFS_SPI_WRAPPER) += cbfs_spi.c ifeq ($(MOCK_TPM),1) libverstage-y += mocked_tlcl.c else -libverstage-y += tlcl.c +libverstage-$(CONFIG_TPM) += tlcl.c +libverstage-$(CONFIG_TPM2) += tpm2_tlcl.c endif verstage-$(CONFIG_GENERIC_UDELAY) += timer.c 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; +} diff --git a/src/lib/tpm2_tlcl_structures.h b/src/lib/tpm2_tlcl_structures.h new file mode 100644 index 0000000000..fad9c0d7bd --- /dev/null +++ b/src/lib/tpm2_tlcl_structures.h @@ -0,0 +1,325 @@ +/* + * 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_TLCL_STRUCTURES_H +#define __SRC_LIB_TPM2_TLCL_STRUCTURES_H + +/* + * This file includes a subset of definitions of TPM protocol version 2.x + * constants and structures needed for functions used in coreboot. + */ +#include <stdint.h> +#include <tpm_lite/tlcl.h> +#include <types.h> + +/* This should be plenty for what firmware needs. */ +#define TPM_BUFFER_SIZE 256 + +/* Basic TPM2 types. */ +typedef uint16_t TPM_SU; +typedef uint16_t TPM_ALG_ID; +typedef uint32_t TPM_CC; +typedef uint32_t TPM_HANDLE; +typedef uint32_t TPM_RC; +typedef uint8_t TPMI_YES_NO; +typedef TPM_HANDLE TPMI_RH_NV_INDEX; +typedef TPM_HANDLE TPMI_RH_PROVISION; +typedef TPM_HANDLE TPMI_SH_AUTH_SESSION; +typedef TPM_HANDLE TPM_RH; +typedef TPM_ALG_ID TPMI_ALG_HASH; + +/* Some hardcoded algorithm values. */ +#define TPM_ALG_HMAC ((TPM_ALG_ID)0x0005) +#define TPM_ALG_NULL ((TPM_ALG_ID)0x0010) +#define TPM_ALG_SHA1 ((TPM_ALG_ID)0x0004) +#define TPM_ALG_SHA256 ((TPM_ALG_ID)0x000b) + +/* Some hardcoded hierarchies. */ +#define TPM_RH_NULL 0x40000007 +#define TPM_RS_PW 0x40000009 +#define TPM_RH_PLATFORM 0x4000000C + +typedef struct { + uint16_t size; + uint8_t *buffer; +} TPM2B; + +typedef union { + uint8_t body[512]; +} TPMU_HA; + +typedef struct { + TPMI_ALG_HASH hashAlg; + TPMU_HA digest; +} TPMT_HA; + +typedef union { + TPMT_HA digest; + TPM_HANDLE handle; +} TPMU_NAME; + +typedef union { + struct { + uint16_t size; + uint8_t name[sizeof(TPMU_NAME)]; + } t; + TPM2B b; +} TPM2B_NAME; + +/* Relevant TPM Command's structures. */ +/* Common command/response header. */ +struct tpm_header { + uint16_t tpm_tag; + uint32_t tpm_size; + TPM_CC tpm_code; +} __attribute__((packed)); + +/* TPM command codes. */ +#define TPM2_NV_DefineSpace ((TPM_CC)0x0000012A) +#define TPM2_NV_Write ((TPM_CC)0x00000137) +#define TPM2_SelfTest ((TPM_CC)0x00000143) +#define TPM2_Startup ((TPM_CC)0x00000144) +#define TPM2_NV_Read ((TPM_CC)0x0000014E) +#define TPM2_GetCapability ((TPM_CC)0x0000017A) + +/* Startup values. */ +#define TPM_SU_CLEAR 0 +#define TPM_SU_STATE 1 + +#define TPM_HT_NV_INDEX 0x01 +#define TPM_HT_HMAC_SESSION 0x02 +#define TPM_HT_POLICY_SESSION 0x03 + +#define HR_SHIFT 24 +#define HR_PCR (TPM_HT_PCR << HR_SHIFT) +#define HR_HMAC_SESSION (TPM_HT_HMAC_SESSION << HR_SHIFT) +#define HR_POLICY_SESSION (TPM_HT_POLICY_SESSION << HR_SHIFT) +#define HR_TRANSIENT (TPM_HT_TRANSIENT << HR_SHIFT) +#define HR_PERSISTENT (TPM_HT_PERSISTENT << HR_SHIFT) +#define HR_NV_INDEX (TPM_HT_NV_INDEX << HR_SHIFT) +#define HR_PERMANENT (TPM_HT_PERMANENT << HR_SHIFT) +#define PCR_FIRST (HR_PCR + 0) +#define PCR_LAST (PCR_FIRST + IMPLEMENTATION_PCR-1) +#define HMAC_SESSION_FIRST (HR_HMAC_SESSION + 0) +#define HMAC_SESSION_LAST (HMAC_SESSION_FIRST+MAX_ACTIVE_SESSIONS-1) +#define LOADED_SESSION_FIRST HMAC_SESSION_FIRST +#define LOADED_SESSION_LAST HMAC_SESSION_LAST +#define POLICY_SESSION_FIRST (HR_POLICY_SESSION + 0) +#define POLICY_SESSION_LAST (POLICY_SESSION_FIRST + MAX_ACTIVE_SESSIONS-1) +#define TRANSIENT_FIRST (HR_TRANSIENT + 0) +#define ACTIVE_SESSION_FIRST POLICY_SESSION_FIRST +#define ACTIVE_SESSION_LAST POLICY_SESSION_LAST +#define TRANSIENT_LAST (TRANSIENT_FIRST+MAX_LOADED_OBJECTS-1) +#define PERSISTENT_FIRST (HR_PERSISTENT + 0) +#define PERSISTENT_LAST (PERSISTENT_FIRST + 0x00FFFFFF) +#define PLATFORM_PERSISTENT (PERSISTENT_FIRST + 0x00800000) +#define NV_INDEX_FIRST (HR_NV_INDEX + 0) +#define NV_INDEX_LAST (NV_INDEX_FIRST + 0x00FFFFFF) +#define PERMANENT_FIRST TPM_RH_FIRST +#define PERMANENT_LAST TPM_RH_LAST + +/* Tpm2 command tags. */ +#define TPM_ST_NO_SESSIONS 0x8001 +#define TPM_ST_SESSIONS 0x8002 + +#define RC_VER1 0x100 +#define TPM_RC_INITIALIZE ((TPM_RC)(RC_VER1 + 0x000)) + +/* TPM command structures. */ + +struct tpm2_startup { + TPM_SU startup_type; +}; + +/* Various TPM capability types to use when querying the device. */ +typedef uint32_t TPM_CAP; +#define TPM_CAP_TPM_PROPERTIES ((TPM_CAP)0x00000006) + +typedef TPM_HANDLE TPMI_RH_NV_AUTH; +typedef TPM_HANDLE TPMI_RH_NV_INDEX; + +/* TPM Property capability constants. */ +typedef uint32_t TPM_PT; +#define PT_GROUP 0x00000100 +#define PT_VAR (PT_GROUP * 2) +#define TPM_PT_PERMANENT ((TPM_PT)(PT_VAR + 0)) + +/* Structures of payloads of various TPM2 commands. */ +struct tpm2_get_capability { + TPM_CAP capability; + uint32_t property; + uint32_t propertyCount; +}; + +/* get_capability response when PT_PERMANENT is requested. */ +typedef struct { + uint32_t ownerAuthSet : 1; + uint32_t endorsementAuthSet : 1; + uint32_t lockoutAuthSet : 1; + uint32_t reserved3_7 : 5; + uint32_t disableClear : 1; + uint32_t inLockout : 1; + uint32_t tpmGeneratedEPS : 1; + uint32_t reserved11_31 : 21; +} TPMA_PERMANENT; + +typedef struct { + uint32_t TPMA_NV_PPWRITE : 1; + uint32_t TPMA_NV_OWNERWRITE : 1; + uint32_t TPMA_NV_AUTHWRITE : 1; + uint32_t TPMA_NV_POLICYWRITE : 1; + uint32_t TPMA_NV_COUNTER : 1; + uint32_t TPMA_NV_BITS : 1; + uint32_t TPMA_NV_EXTEND : 1; + uint32_t reserved7_9 : 3; + uint32_t TPMA_NV_POLICY_DELETE : 1; + uint32_t TPMA_NV_WRITELOCKED : 1; + uint32_t TPMA_NV_WRITEALL : 1; + uint32_t TPMA_NV_WRITEDEFINE : 1; + uint32_t TPMA_NV_WRITE_STCLEAR : 1; + uint32_t TPMA_NV_GLOBALLOCK : 1; + uint32_t TPMA_NV_PPREAD : 1; + uint32_t TPMA_NV_OWNERREAD : 1; + uint32_t TPMA_NV_AUTHREAD : 1; + uint32_t TPMA_NV_POLICYREAD : 1; + uint32_t reserved20_24 : 5; + uint32_t TPMA_NV_NO_DA : 1; + uint32_t TPMA_NV_ORDERLY : 1; + uint32_t TPMA_NV_CLEAR_STCLEAR : 1; + uint32_t TPMA_NV_READLOCKED : 1; + uint32_t TPMA_NV_WRITTEN : 1; + uint32_t TPMA_NV_PLATFORMCREATE : 1; + uint32_t TPMA_NV_READ_STCLEAR : 1; +} TPMA_NV; + +typedef union { + struct { + uint16_t size; + uint8_t buffer[sizeof(TPMU_HA)]; + } t; + TPM2B b; +} TPM2B_DIGEST; + +typedef TPM2B_DIGEST TPM2B_AUTH; +typedef TPM2B_DIGEST TPM2B_NONCE; + +typedef struct { + TPM_PT property; + uint32_t value; +} TPMS_TAGGED_PROPERTY; + +#define MAX_CAP_DATA (TPM_BUFFER_SIZE - sizeof(struct tpm_header) - \ + sizeof(TPMI_YES_NO) - sizeof(TPM_CAP) - sizeof(uint32_t)) +#define MAX_TPM_PROPERTIES (MAX_CAP_DATA/sizeof(TPMS_TAGGED_PROPERTY)) + +/* Somewhat arbitrary, leave enough room for command wrappers. */ +#define MAX_NV_BUFFER_SIZE (TPM_BUFFER_SIZE - sizeof(struct tpm_header) - 50) + +typedef struct { + uint32_t count; + TPMS_TAGGED_PROPERTY tpmProperty[MAX_TPM_PROPERTIES]; +} TPML_TAGGED_TPM_PROPERTY; + +typedef union { + TPML_TAGGED_TPM_PROPERTY tpmProperties; +} TPMU_CAPABILITIES; + +typedef struct { + TPM_CAP capability; + TPMU_CAPABILITIES data; +} TPMS_CAPABILITY_DATA; + +struct get_cap_response { + TPMI_YES_NO more_data; + TPMS_CAPABILITY_DATA cd; +}; + +typedef struct { + TPMI_RH_NV_INDEX nvIndex; + TPMI_ALG_HASH nameAlg; + TPMA_NV attributes; + TPM2B_DIGEST authPolicy; + uint16_t dataSize; +} TPMS_NV_PUBLIC; + +typedef union { + struct { + uint16_t size; + TPMS_NV_PUBLIC nvPublic; + } t; + TPM2B b; +} TPM2B_NV_PUBLIC; + +typedef union { + struct { + uint16_t size; + const uint8_t *buffer; + } t; + TPM2B b; +} TPM2B_MAX_NV_BUFFER; + +struct nv_read_response { + uint32_t params_size; + TPM2B_MAX_NV_BUFFER buffer; +}; + +struct tpm2_session_attrs { + uint8_t continueSession : 1; + uint8_t auditExclusive : 1; + uint8_t auditReset : 1; + uint8_t reserved3_4 : 2; + uint8_t decrypt : 1; + uint8_t encrypt : 1; + uint8_t audit : 1; +}; + +/* + * TPM session header for commands requiring session information. Also + * included in the responses to those commands. + */ +struct tpm2_session_header { + uint32_t session_handle; + uint16_t nonce_size; + uint8_t *nonce; + union { + struct tpm2_session_attrs session_attr_bits; + uint8_t session_attrs; + } __attribute__((packed)); + uint16_t auth_size; + uint8_t *auth; +}; + +struct tpm2_response { + struct tpm_header hdr; + union { + struct get_cap_response gc; + struct nv_read_response nvr; + struct tpm2_session_header def_space; + }; +}; + +struct tpm2_nv_define_space_cmd { + TPM2B_AUTH auth; + TPMS_NV_PUBLIC publicInfo; +}; + +struct tpm2_nv_write_cmd { + TPMI_RH_NV_INDEX nvIndex; + TPM2B_MAX_NV_BUFFER data; + uint16_t offset; +}; + +struct tpm2_self_test { + TPMI_YES_NO yes_no; +}; + +struct tpm2_nv_read_cmd { + TPMI_RH_NV_INDEX nvIndex; + uint16_t size; + uint16_t offset; +}; + +#endif // __SRC_LIB_TPM2_TLCL_STRUCTURES_H |