18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Intel Corporation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Maintained by: <tpmdd-devel@lists.sourceforge.net> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This file contains TPM2 protocol implementations of the commands 118c2ecf20Sopenharmony_ci * used by the kernel internally. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/gfp.h> 158c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 168c2ecf20Sopenharmony_ci#include "tpm.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cienum tpm2_handle_types { 198c2ecf20Sopenharmony_ci TPM2_HT_HMAC_SESSION = 0x02000000, 208c2ecf20Sopenharmony_ci TPM2_HT_POLICY_SESSION = 0x03000000, 218c2ecf20Sopenharmony_ci TPM2_HT_TRANSIENT = 0x80000000, 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct tpm2_context { 258c2ecf20Sopenharmony_ci __be64 sequence; 268c2ecf20Sopenharmony_ci __be32 saved_handle; 278c2ecf20Sopenharmony_ci __be32 hierarchy; 288c2ecf20Sopenharmony_ci __be16 blob_size; 298c2ecf20Sopenharmony_ci} __packed; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci int i; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) { 368c2ecf20Sopenharmony_ci if (space->session_tbl[i]) 378c2ecf20Sopenharmony_ci tpm2_flush_context(chip, space->session_tbl[i]); 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ciint tpm2_init_space(struct tpm_space *space, unsigned int buf_size) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci space->context_buf = kzalloc(buf_size, GFP_KERNEL); 448c2ecf20Sopenharmony_ci if (!space->context_buf) 458c2ecf20Sopenharmony_ci return -ENOMEM; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci space->session_buf = kzalloc(buf_size, GFP_KERNEL); 488c2ecf20Sopenharmony_ci if (space->session_buf == NULL) { 498c2ecf20Sopenharmony_ci kfree(space->context_buf); 508c2ecf20Sopenharmony_ci /* Prevent caller getting a dangling pointer. */ 518c2ecf20Sopenharmony_ci space->context_buf = NULL; 528c2ecf20Sopenharmony_ci return -ENOMEM; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci space->buf_size = buf_size; 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_civoid tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (tpm_try_get_ops(chip) == 0) { 638c2ecf20Sopenharmony_ci tpm2_flush_sessions(chip, space); 648c2ecf20Sopenharmony_ci tpm_put_ops(chip); 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci kfree(space->context_buf); 688c2ecf20Sopenharmony_ci kfree(space->session_buf); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int tpm2_load_context(struct tpm_chip *chip, u8 *buf, 728c2ecf20Sopenharmony_ci unsigned int *offset, u32 *handle) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct tpm_buf tbuf; 758c2ecf20Sopenharmony_ci struct tpm2_context *ctx; 768c2ecf20Sopenharmony_ci unsigned int body_size; 778c2ecf20Sopenharmony_ci int rc; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD); 808c2ecf20Sopenharmony_ci if (rc) 818c2ecf20Sopenharmony_ci return rc; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci ctx = (struct tpm2_context *)&buf[*offset]; 848c2ecf20Sopenharmony_ci body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size); 858c2ecf20Sopenharmony_ci tpm_buf_append(&tbuf, &buf[*offset], body_size); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL); 888c2ecf20Sopenharmony_ci if (rc < 0) { 898c2ecf20Sopenharmony_ci dev_warn(&chip->dev, "%s: failed with a system error %d\n", 908c2ecf20Sopenharmony_ci __func__, rc); 918c2ecf20Sopenharmony_ci tpm_buf_destroy(&tbuf); 928c2ecf20Sopenharmony_ci return -EFAULT; 938c2ecf20Sopenharmony_ci } else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE || 948c2ecf20Sopenharmony_ci rc == TPM2_RC_REFERENCE_H0) { 958c2ecf20Sopenharmony_ci /* 968c2ecf20Sopenharmony_ci * TPM_RC_HANDLE means that the session context can't 978c2ecf20Sopenharmony_ci * be loaded because of an internal counter mismatch 988c2ecf20Sopenharmony_ci * that makes the TPM think there might have been a 998c2ecf20Sopenharmony_ci * replay. This might happen if the context was saved 1008c2ecf20Sopenharmony_ci * and loaded outside the space. 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * TPM_RC_REFERENCE_H0 means the session has been 1038c2ecf20Sopenharmony_ci * flushed outside the space 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci *handle = 0; 1068c2ecf20Sopenharmony_ci tpm_buf_destroy(&tbuf); 1078c2ecf20Sopenharmony_ci return -ENOENT; 1088c2ecf20Sopenharmony_ci } else if (rc > 0) { 1098c2ecf20Sopenharmony_ci dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n", 1108c2ecf20Sopenharmony_ci __func__, rc); 1118c2ecf20Sopenharmony_ci tpm_buf_destroy(&tbuf); 1128c2ecf20Sopenharmony_ci return -EFAULT; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci *handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]); 1168c2ecf20Sopenharmony_ci *offset += body_size; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci tpm_buf_destroy(&tbuf); 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf, 1238c2ecf20Sopenharmony_ci unsigned int buf_size, unsigned int *offset) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct tpm_buf tbuf; 1268c2ecf20Sopenharmony_ci unsigned int body_size; 1278c2ecf20Sopenharmony_ci int rc; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE); 1308c2ecf20Sopenharmony_ci if (rc) 1318c2ecf20Sopenharmony_ci return rc; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci tpm_buf_append_u32(&tbuf, handle); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL); 1368c2ecf20Sopenharmony_ci if (rc < 0) { 1378c2ecf20Sopenharmony_ci dev_warn(&chip->dev, "%s: failed with a system error %d\n", 1388c2ecf20Sopenharmony_ci __func__, rc); 1398c2ecf20Sopenharmony_ci tpm_buf_destroy(&tbuf); 1408c2ecf20Sopenharmony_ci return -EFAULT; 1418c2ecf20Sopenharmony_ci } else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) { 1428c2ecf20Sopenharmony_ci tpm_buf_destroy(&tbuf); 1438c2ecf20Sopenharmony_ci return -ENOENT; 1448c2ecf20Sopenharmony_ci } else if (rc) { 1458c2ecf20Sopenharmony_ci dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n", 1468c2ecf20Sopenharmony_ci __func__, rc); 1478c2ecf20Sopenharmony_ci tpm_buf_destroy(&tbuf); 1488c2ecf20Sopenharmony_ci return -EFAULT; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE; 1528c2ecf20Sopenharmony_ci if ((*offset + body_size) > buf_size) { 1538c2ecf20Sopenharmony_ci dev_warn(&chip->dev, "%s: out of backing storage\n", __func__); 1548c2ecf20Sopenharmony_ci tpm_buf_destroy(&tbuf); 1558c2ecf20Sopenharmony_ci return -ENOMEM; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size); 1598c2ecf20Sopenharmony_ci *offset += body_size; 1608c2ecf20Sopenharmony_ci tpm_buf_destroy(&tbuf); 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_civoid tpm2_flush_space(struct tpm_chip *chip) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct tpm_space *space = &chip->work_space; 1678c2ecf20Sopenharmony_ci int i; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) 1708c2ecf20Sopenharmony_ci if (space->context_tbl[i] && ~space->context_tbl[i]) 1718c2ecf20Sopenharmony_ci tpm2_flush_context(chip, space->context_tbl[i]); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci tpm2_flush_sessions(chip, space); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int tpm2_load_space(struct tpm_chip *chip) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct tpm_space *space = &chip->work_space; 1798c2ecf20Sopenharmony_ci unsigned int offset; 1808c2ecf20Sopenharmony_ci int i; 1818c2ecf20Sopenharmony_ci int rc; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) { 1848c2ecf20Sopenharmony_ci if (!space->context_tbl[i]) 1858c2ecf20Sopenharmony_ci continue; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* sanity check, should never happen */ 1888c2ecf20Sopenharmony_ci if (~space->context_tbl[i]) { 1898c2ecf20Sopenharmony_ci dev_err(&chip->dev, "context table is inconsistent"); 1908c2ecf20Sopenharmony_ci return -EFAULT; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci rc = tpm2_load_context(chip, space->context_buf, &offset, 1948c2ecf20Sopenharmony_ci &space->context_tbl[i]); 1958c2ecf20Sopenharmony_ci if (rc) 1968c2ecf20Sopenharmony_ci return rc; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) { 2008c2ecf20Sopenharmony_ci u32 handle; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!space->session_tbl[i]) 2038c2ecf20Sopenharmony_ci continue; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci rc = tpm2_load_context(chip, space->session_buf, 2068c2ecf20Sopenharmony_ci &offset, &handle); 2078c2ecf20Sopenharmony_ci if (rc == -ENOENT) { 2088c2ecf20Sopenharmony_ci /* load failed, just forget session */ 2098c2ecf20Sopenharmony_ci space->session_tbl[i] = 0; 2108c2ecf20Sopenharmony_ci } else if (rc) { 2118c2ecf20Sopenharmony_ci tpm2_flush_space(chip); 2128c2ecf20Sopenharmony_ci return rc; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci if (handle != space->session_tbl[i]) { 2158c2ecf20Sopenharmony_ci dev_warn(&chip->dev, "session restored to wrong handle\n"); 2168c2ecf20Sopenharmony_ci tpm2_flush_space(chip); 2178c2ecf20Sopenharmony_ci return -EFAULT; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic bool tpm2_map_to_phandle(struct tpm_space *space, void *handle) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci u32 vhandle = be32_to_cpup((__be32 *)handle); 2278c2ecf20Sopenharmony_ci u32 phandle; 2288c2ecf20Sopenharmony_ci int i; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci i = 0xFFFFFF - (vhandle & 0xFFFFFF); 2318c2ecf20Sopenharmony_ci if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i]) 2328c2ecf20Sopenharmony_ci return false; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci phandle = space->context_tbl[i]; 2358c2ecf20Sopenharmony_ci *((__be32 *)handle) = cpu_to_be32(phandle); 2368c2ecf20Sopenharmony_ci return true; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct tpm_space *space = &chip->work_space; 2428c2ecf20Sopenharmony_ci unsigned int nr_handles; 2438c2ecf20Sopenharmony_ci u32 attrs; 2448c2ecf20Sopenharmony_ci __be32 *handle; 2458c2ecf20Sopenharmony_ci int i; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci i = tpm2_find_cc(chip, cc); 2488c2ecf20Sopenharmony_ci if (i < 0) 2498c2ecf20Sopenharmony_ci return -EINVAL; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci attrs = chip->cc_attrs_tbl[i]; 2528c2ecf20Sopenharmony_ci nr_handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci handle = (__be32 *)&cmd[TPM_HEADER_SIZE]; 2558c2ecf20Sopenharmony_ci for (i = 0; i < nr_handles; i++, handle++) { 2568c2ecf20Sopenharmony_ci if ((be32_to_cpu(*handle) & 0xFF000000) == TPM2_HT_TRANSIENT) { 2578c2ecf20Sopenharmony_ci if (!tpm2_map_to_phandle(space, handle)) 2588c2ecf20Sopenharmony_ci return -EINVAL; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int tpm_find_and_validate_cc(struct tpm_chip *chip, 2668c2ecf20Sopenharmony_ci struct tpm_space *space, 2678c2ecf20Sopenharmony_ci const void *cmd, size_t len) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci const struct tpm_header *header = (const void *)cmd; 2708c2ecf20Sopenharmony_ci int i; 2718c2ecf20Sopenharmony_ci u32 cc; 2728c2ecf20Sopenharmony_ci u32 attrs; 2738c2ecf20Sopenharmony_ci unsigned int nr_handles; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (len < TPM_HEADER_SIZE || !chip->nr_commands) 2768c2ecf20Sopenharmony_ci return -EINVAL; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci cc = be32_to_cpu(header->ordinal); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci i = tpm2_find_cc(chip, cc); 2818c2ecf20Sopenharmony_ci if (i < 0) { 2828c2ecf20Sopenharmony_ci dev_dbg(&chip->dev, "0x%04X is an invalid command\n", 2838c2ecf20Sopenharmony_ci cc); 2848c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci attrs = chip->cc_attrs_tbl[i]; 2888c2ecf20Sopenharmony_ci nr_handles = 2898c2ecf20Sopenharmony_ci 4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0)); 2908c2ecf20Sopenharmony_ci if (len < TPM_HEADER_SIZE + 4 * nr_handles) 2918c2ecf20Sopenharmony_ci goto err_len; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return cc; 2948c2ecf20Sopenharmony_cierr_len: 2958c2ecf20Sopenharmony_ci dev_dbg(&chip->dev, "%s: insufficient command length %zu", __func__, 2968c2ecf20Sopenharmony_ci len); 2978c2ecf20Sopenharmony_ci return -EINVAL; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ciint tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd, 3018c2ecf20Sopenharmony_ci size_t cmdsiz) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci int rc; 3048c2ecf20Sopenharmony_ci int cc; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (!space) 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci cc = tpm_find_and_validate_cc(chip, space, cmd, cmdsiz); 3108c2ecf20Sopenharmony_ci if (cc < 0) 3118c2ecf20Sopenharmony_ci return cc; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci memcpy(&chip->work_space.context_tbl, &space->context_tbl, 3148c2ecf20Sopenharmony_ci sizeof(space->context_tbl)); 3158c2ecf20Sopenharmony_ci memcpy(&chip->work_space.session_tbl, &space->session_tbl, 3168c2ecf20Sopenharmony_ci sizeof(space->session_tbl)); 3178c2ecf20Sopenharmony_ci memcpy(chip->work_space.context_buf, space->context_buf, 3188c2ecf20Sopenharmony_ci space->buf_size); 3198c2ecf20Sopenharmony_ci memcpy(chip->work_space.session_buf, space->session_buf, 3208c2ecf20Sopenharmony_ci space->buf_size); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci rc = tpm2_load_space(chip); 3238c2ecf20Sopenharmony_ci if (rc) { 3248c2ecf20Sopenharmony_ci tpm2_flush_space(chip); 3258c2ecf20Sopenharmony_ci return rc; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci rc = tpm2_map_command(chip, cc, cmd); 3298c2ecf20Sopenharmony_ci if (rc) { 3308c2ecf20Sopenharmony_ci tpm2_flush_space(chip); 3318c2ecf20Sopenharmony_ci return rc; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci chip->last_cc = cc; 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic bool tpm2_add_session(struct tpm_chip *chip, u32 handle) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct tpm_space *space = &chip->work_space; 3418c2ecf20Sopenharmony_ci int i; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) 3448c2ecf20Sopenharmony_ci if (space->session_tbl[i] == 0) 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(space->session_tbl)) 3488c2ecf20Sopenharmony_ci return false; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci space->session_tbl[i] = handle; 3518c2ecf20Sopenharmony_ci return true; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci int i; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) { 3598c2ecf20Sopenharmony_ci if (alloc) { 3608c2ecf20Sopenharmony_ci if (!space->context_tbl[i]) { 3618c2ecf20Sopenharmony_ci space->context_tbl[i] = phandle; 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci } else if (space->context_tbl[i] == phandle) 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(space->context_tbl)) 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return TPM2_HT_TRANSIENT | (0xFFFFFF - i); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp, 3758c2ecf20Sopenharmony_ci size_t len) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct tpm_space *space = &chip->work_space; 3788c2ecf20Sopenharmony_ci struct tpm_header *header = (struct tpm_header *)rsp; 3798c2ecf20Sopenharmony_ci u32 phandle; 3808c2ecf20Sopenharmony_ci u32 phandle_type; 3818c2ecf20Sopenharmony_ci u32 vhandle; 3828c2ecf20Sopenharmony_ci u32 attrs; 3838c2ecf20Sopenharmony_ci int i; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci i = tpm2_find_cc(chip, cc); 3898c2ecf20Sopenharmony_ci /* sanity check, should never happen */ 3908c2ecf20Sopenharmony_ci if (i < 0) 3918c2ecf20Sopenharmony_ci return -EFAULT; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci attrs = chip->cc_attrs_tbl[i]; 3948c2ecf20Sopenharmony_ci if (!((attrs >> TPM2_CC_ATTR_RHANDLE) & 1)) 3958c2ecf20Sopenharmony_ci return 0; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]); 3988c2ecf20Sopenharmony_ci phandle_type = phandle & 0xFF000000; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci switch (phandle_type) { 4018c2ecf20Sopenharmony_ci case TPM2_HT_TRANSIENT: 4028c2ecf20Sopenharmony_ci vhandle = tpm2_map_to_vhandle(space, phandle, true); 4038c2ecf20Sopenharmony_ci if (!vhandle) 4048c2ecf20Sopenharmony_ci goto out_no_slots; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci *(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle); 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci case TPM2_HT_HMAC_SESSION: 4098c2ecf20Sopenharmony_ci case TPM2_HT_POLICY_SESSION: 4108c2ecf20Sopenharmony_ci if (!tpm2_add_session(chip, phandle)) 4118c2ecf20Sopenharmony_ci goto out_no_slots; 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci default: 4148c2ecf20Sopenharmony_ci dev_err(&chip->dev, "%s: unknown handle 0x%08X\n", 4158c2ecf20Sopenharmony_ci __func__, phandle); 4168c2ecf20Sopenharmony_ci break; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ciout_no_slots: 4218c2ecf20Sopenharmony_ci tpm2_flush_context(chip, phandle); 4228c2ecf20Sopenharmony_ci dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__, 4238c2ecf20Sopenharmony_ci phandle); 4248c2ecf20Sopenharmony_ci return -ENOMEM; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistruct tpm2_cap_handles { 4288c2ecf20Sopenharmony_ci u8 more_data; 4298c2ecf20Sopenharmony_ci __be32 capability; 4308c2ecf20Sopenharmony_ci __be32 count; 4318c2ecf20Sopenharmony_ci __be32 handles[]; 4328c2ecf20Sopenharmony_ci} __packed; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp, 4358c2ecf20Sopenharmony_ci size_t len) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct tpm_space *space = &chip->work_space; 4388c2ecf20Sopenharmony_ci struct tpm_header *header = (struct tpm_header *)rsp; 4398c2ecf20Sopenharmony_ci struct tpm2_cap_handles *data; 4408c2ecf20Sopenharmony_ci u32 phandle; 4418c2ecf20Sopenharmony_ci u32 phandle_type; 4428c2ecf20Sopenharmony_ci u32 vhandle; 4438c2ecf20Sopenharmony_ci int i; 4448c2ecf20Sopenharmony_ci int j; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (cc != TPM2_CC_GET_CAPABILITY || 4478c2ecf20Sopenharmony_ci be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) { 4488c2ecf20Sopenharmony_ci return 0; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (len < TPM_HEADER_SIZE + 9) 4528c2ecf20Sopenharmony_ci return -EFAULT; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci data = (void *)&rsp[TPM_HEADER_SIZE]; 4558c2ecf20Sopenharmony_ci if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES) 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (be32_to_cpu(data->count) > (UINT_MAX - TPM_HEADER_SIZE - 9) / 4) 4598c2ecf20Sopenharmony_ci return -EFAULT; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count)) 4628c2ecf20Sopenharmony_ci return -EFAULT; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci for (i = 0, j = 0; i < be32_to_cpu(data->count); i++) { 4658c2ecf20Sopenharmony_ci phandle = be32_to_cpup((__be32 *)&data->handles[i]); 4668c2ecf20Sopenharmony_ci phandle_type = phandle & 0xFF000000; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci switch (phandle_type) { 4698c2ecf20Sopenharmony_ci case TPM2_HT_TRANSIENT: 4708c2ecf20Sopenharmony_ci vhandle = tpm2_map_to_vhandle(space, phandle, false); 4718c2ecf20Sopenharmony_ci if (!vhandle) 4728c2ecf20Sopenharmony_ci break; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci data->handles[j] = cpu_to_be32(vhandle); 4758c2ecf20Sopenharmony_ci j++; 4768c2ecf20Sopenharmony_ci break; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci default: 4798c2ecf20Sopenharmony_ci data->handles[j] = cpu_to_be32(phandle); 4808c2ecf20Sopenharmony_ci j++; 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci header->length = cpu_to_be32(TPM_HEADER_SIZE + 9 + 4 * j); 4878c2ecf20Sopenharmony_ci data->count = cpu_to_be32(j); 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int tpm2_save_space(struct tpm_chip *chip) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct tpm_space *space = &chip->work_space; 4948c2ecf20Sopenharmony_ci unsigned int offset; 4958c2ecf20Sopenharmony_ci int i; 4968c2ecf20Sopenharmony_ci int rc; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) { 4998c2ecf20Sopenharmony_ci if (!(space->context_tbl[i] && ~space->context_tbl[i])) 5008c2ecf20Sopenharmony_ci continue; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci rc = tpm2_save_context(chip, space->context_tbl[i], 5038c2ecf20Sopenharmony_ci space->context_buf, space->buf_size, 5048c2ecf20Sopenharmony_ci &offset); 5058c2ecf20Sopenharmony_ci if (rc == -ENOENT) { 5068c2ecf20Sopenharmony_ci space->context_tbl[i] = 0; 5078c2ecf20Sopenharmony_ci continue; 5088c2ecf20Sopenharmony_ci } else if (rc) 5098c2ecf20Sopenharmony_ci return rc; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci tpm2_flush_context(chip, space->context_tbl[i]); 5128c2ecf20Sopenharmony_ci space->context_tbl[i] = ~0; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) { 5168c2ecf20Sopenharmony_ci if (!space->session_tbl[i]) 5178c2ecf20Sopenharmony_ci continue; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci rc = tpm2_save_context(chip, space->session_tbl[i], 5208c2ecf20Sopenharmony_ci space->session_buf, space->buf_size, 5218c2ecf20Sopenharmony_ci &offset); 5228c2ecf20Sopenharmony_ci if (rc == -ENOENT) { 5238c2ecf20Sopenharmony_ci /* handle error saving session, just forget it */ 5248c2ecf20Sopenharmony_ci space->session_tbl[i] = 0; 5258c2ecf20Sopenharmony_ci } else if (rc < 0) { 5268c2ecf20Sopenharmony_ci tpm2_flush_space(chip); 5278c2ecf20Sopenharmony_ci return rc; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return 0; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ciint tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, 5358c2ecf20Sopenharmony_ci void *buf, size_t *bufsiz) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct tpm_header *header = buf; 5388c2ecf20Sopenharmony_ci int rc; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (!space) 5418c2ecf20Sopenharmony_ci return 0; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci rc = tpm2_map_response_header(chip, chip->last_cc, buf, *bufsiz); 5448c2ecf20Sopenharmony_ci if (rc) { 5458c2ecf20Sopenharmony_ci tpm2_flush_space(chip); 5468c2ecf20Sopenharmony_ci goto out; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci rc = tpm2_map_response_body(chip, chip->last_cc, buf, *bufsiz); 5508c2ecf20Sopenharmony_ci if (rc) { 5518c2ecf20Sopenharmony_ci tpm2_flush_space(chip); 5528c2ecf20Sopenharmony_ci goto out; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci rc = tpm2_save_space(chip); 5568c2ecf20Sopenharmony_ci if (rc) { 5578c2ecf20Sopenharmony_ci tpm2_flush_space(chip); 5588c2ecf20Sopenharmony_ci goto out; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci *bufsiz = be32_to_cpu(header->length); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci memcpy(&space->context_tbl, &chip->work_space.context_tbl, 5648c2ecf20Sopenharmony_ci sizeof(space->context_tbl)); 5658c2ecf20Sopenharmony_ci memcpy(&space->session_tbl, &chip->work_space.session_tbl, 5668c2ecf20Sopenharmony_ci sizeof(space->session_tbl)); 5678c2ecf20Sopenharmony_ci memcpy(space->context_buf, chip->work_space.context_buf, 5688c2ecf20Sopenharmony_ci space->buf_size); 5698c2ecf20Sopenharmony_ci memcpy(space->session_buf, chip->work_space.session_buf, 5708c2ecf20Sopenharmony_ci space->buf_size); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci return 0; 5738c2ecf20Sopenharmony_ciout: 5748c2ecf20Sopenharmony_ci dev_err(&chip->dev, "%s: error %d\n", __func__, rc); 5758c2ecf20Sopenharmony_ci return rc; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci/* 5798c2ecf20Sopenharmony_ci * Put the reference to the main device. 5808c2ecf20Sopenharmony_ci */ 5818c2ecf20Sopenharmony_cistatic void tpm_devs_release(struct device *dev) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci struct tpm_chip *chip = container_of(dev, struct tpm_chip, devs); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* release the master device reference */ 5868c2ecf20Sopenharmony_ci put_device(&chip->dev); 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci/* 5908c2ecf20Sopenharmony_ci * Remove the device file for exposed TPM spaces and release the device 5918c2ecf20Sopenharmony_ci * reference. This may also release the reference to the master device. 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_civoid tpm_devs_remove(struct tpm_chip *chip) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci cdev_device_del(&chip->cdevs, &chip->devs); 5968c2ecf20Sopenharmony_ci put_device(&chip->devs); 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci/* 6008c2ecf20Sopenharmony_ci * Add a device file to expose TPM spaces. Also take a reference to the 6018c2ecf20Sopenharmony_ci * main device. 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_ciint tpm_devs_add(struct tpm_chip *chip) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci int rc; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci device_initialize(&chip->devs); 6088c2ecf20Sopenharmony_ci chip->devs.parent = chip->dev.parent; 6098c2ecf20Sopenharmony_ci chip->devs.class = tpmrm_class; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* 6128c2ecf20Sopenharmony_ci * Get extra reference on main device to hold on behalf of devs. 6138c2ecf20Sopenharmony_ci * This holds the chip structure while cdevs is in use. The 6148c2ecf20Sopenharmony_ci * corresponding put is in the tpm_devs_release. 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_ci get_device(&chip->dev); 6178c2ecf20Sopenharmony_ci chip->devs.release = tpm_devs_release; 6188c2ecf20Sopenharmony_ci chip->devs.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num + TPM_NUM_DEVICES); 6198c2ecf20Sopenharmony_ci cdev_init(&chip->cdevs, &tpmrm_fops); 6208c2ecf20Sopenharmony_ci chip->cdevs.owner = THIS_MODULE; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci rc = dev_set_name(&chip->devs, "tpmrm%d", chip->dev_num); 6238c2ecf20Sopenharmony_ci if (rc) 6248c2ecf20Sopenharmony_ci goto err_put_devs; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci rc = cdev_device_add(&chip->cdevs, &chip->devs); 6278c2ecf20Sopenharmony_ci if (rc) { 6288c2ecf20Sopenharmony_ci dev_err(&chip->devs, 6298c2ecf20Sopenharmony_ci "unable to cdev_device_add() %s, major %d, minor %d, err=%d\n", 6308c2ecf20Sopenharmony_ci dev_name(&chip->devs), MAJOR(chip->devs.devt), 6318c2ecf20Sopenharmony_ci MINOR(chip->devs.devt), rc); 6328c2ecf20Sopenharmony_ci goto err_put_devs; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci return 0; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cierr_put_devs: 6388c2ecf20Sopenharmony_ci put_device(&chip->devs); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return rc; 6418c2ecf20Sopenharmony_ci} 642