2014-12-13 03:46:38 +08:00
|
|
|
/*
|
2015-05-30 13:09:04 +08:00
|
|
|
* Copyright (C) 2014, 2015 Intel Corporation
|
2014-12-13 03:46:38 +08:00
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
|
|
|
|
*
|
|
|
|
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
|
|
|
*
|
|
|
|
* This file contains TPM2 protocol implementations of the commands
|
|
|
|
* used by the kernel internally.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; version 2
|
|
|
|
* of the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "tpm.h"
|
2015-11-06 03:43:06 +08:00
|
|
|
#include <crypto/hash_info.h>
|
2015-05-30 13:09:04 +08:00
|
|
|
#include <keys/trusted-type.h>
|
|
|
|
|
|
|
|
enum tpm2_object_attributes {
|
2016-02-13 17:51:23 +08:00
|
|
|
TPM2_OA_USER_WITH_AUTH = BIT(6),
|
|
|
|
};
|
|
|
|
|
|
|
|
enum tpm2_session_attributes {
|
|
|
|
TPM2_SA_CONTINUE_SESSION = BIT(0),
|
2015-05-30 13:09:04 +08:00
|
|
|
};
|
2014-12-13 03:46:38 +08:00
|
|
|
|
2015-11-06 03:43:06 +08:00
|
|
|
struct tpm2_hash {
|
|
|
|
unsigned int crypto_id;
|
|
|
|
unsigned int tpm_id;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct tpm2_hash tpm2_hash_map[] = {
|
|
|
|
{HASH_ALGO_SHA1, TPM2_ALG_SHA1},
|
|
|
|
{HASH_ALGO_SHA256, TPM2_ALG_SHA256},
|
|
|
|
{HASH_ALGO_SHA384, TPM2_ALG_SHA384},
|
|
|
|
{HASH_ALGO_SHA512, TPM2_ALG_SHA512},
|
|
|
|
{HASH_ALGO_SM3_256, TPM2_ALG_SM3_256},
|
|
|
|
};
|
|
|
|
|
2018-10-20 02:22:52 +08:00
|
|
|
int tpm2_get_timeouts(struct tpm_chip *chip)
|
|
|
|
{
|
|
|
|
/* Fixed timeouts for TPM2 */
|
|
|
|
chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
|
|
|
|
chip->timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B);
|
|
|
|
chip->timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C);
|
|
|
|
chip->timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D);
|
|
|
|
|
|
|
|
/* PTP spec timeouts */
|
|
|
|
chip->duration[TPM_SHORT] = msecs_to_jiffies(TPM2_DURATION_SHORT);
|
|
|
|
chip->duration[TPM_MEDIUM] = msecs_to_jiffies(TPM2_DURATION_MEDIUM);
|
|
|
|
chip->duration[TPM_LONG] = msecs_to_jiffies(TPM2_DURATION_LONG);
|
|
|
|
|
|
|
|
/* Key creation commands long timeouts */
|
|
|
|
chip->duration[TPM_LONG_LONG] =
|
|
|
|
msecs_to_jiffies(TPM2_DURATION_LONG_LONG);
|
|
|
|
|
|
|
|
chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-20 02:22:48 +08:00
|
|
|
/**
|
|
|
|
* tpm2_ordinal_duration_index() - returns an index to the chip duration table
|
|
|
|
* @ordinal: TPM command ordinal.
|
|
|
|
*
|
|
|
|
* The function returns an index to the chip duration table
|
|
|
|
* (enum tpm_duration), that describes the maximum amount of
|
|
|
|
* time the chip could take to return the result for a particular ordinal.
|
|
|
|
*
|
|
|
|
* The values of the MEDIUM, and LONG durations are taken
|
|
|
|
* from the PC Client Profile (PTP) specification (750, 2000 msec)
|
|
|
|
*
|
|
|
|
* LONG_LONG is for commands that generates keys which empirically takes
|
|
|
|
* a longer time on some systems.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* * TPM_MEDIUM
|
|
|
|
* * TPM_LONG
|
|
|
|
* * TPM_LONG_LONG
|
|
|
|
* * TPM_UNDEFINED
|
2014-12-13 03:46:38 +08:00
|
|
|
*/
|
2018-10-20 02:22:48 +08:00
|
|
|
static u8 tpm2_ordinal_duration_index(u32 ordinal)
|
|
|
|
{
|
|
|
|
switch (ordinal) {
|
|
|
|
/* Startup */
|
|
|
|
case TPM2_CC_STARTUP: /* 144 */
|
|
|
|
return TPM_MEDIUM;
|
|
|
|
|
|
|
|
case TPM2_CC_SELF_TEST: /* 143 */
|
|
|
|
return TPM_LONG;
|
|
|
|
|
|
|
|
case TPM2_CC_GET_RANDOM: /* 17B */
|
|
|
|
return TPM_LONG;
|
|
|
|
|
|
|
|
case TPM2_CC_SEQUENCE_UPDATE: /* 15C */
|
|
|
|
return TPM_MEDIUM;
|
|
|
|
case TPM2_CC_SEQUENCE_COMPLETE: /* 13E */
|
|
|
|
return TPM_MEDIUM;
|
|
|
|
case TPM2_CC_EVENT_SEQUENCE_COMPLETE: /* 185 */
|
|
|
|
return TPM_MEDIUM;
|
|
|
|
case TPM2_CC_HASH_SEQUENCE_START: /* 186 */
|
|
|
|
return TPM_MEDIUM;
|
|
|
|
|
|
|
|
case TPM2_CC_VERIFY_SIGNATURE: /* 177 */
|
|
|
|
return TPM_LONG;
|
|
|
|
|
|
|
|
case TPM2_CC_PCR_EXTEND: /* 182 */
|
|
|
|
return TPM_MEDIUM;
|
|
|
|
|
|
|
|
case TPM2_CC_HIERARCHY_CONTROL: /* 121 */
|
|
|
|
return TPM_LONG;
|
|
|
|
case TPM2_CC_HIERARCHY_CHANGE_AUTH: /* 129 */
|
|
|
|
return TPM_LONG;
|
|
|
|
|
|
|
|
case TPM2_CC_GET_CAPABILITY: /* 17A */
|
|
|
|
return TPM_MEDIUM;
|
|
|
|
|
|
|
|
case TPM2_CC_NV_READ: /* 14E */
|
|
|
|
return TPM_LONG;
|
|
|
|
|
|
|
|
case TPM2_CC_CREATE_PRIMARY: /* 131 */
|
|
|
|
return TPM_LONG_LONG;
|
|
|
|
case TPM2_CC_CREATE: /* 153 */
|
|
|
|
return TPM_LONG_LONG;
|
|
|
|
case TPM2_CC_CREATE_LOADED: /* 191 */
|
|
|
|
return TPM_LONG_LONG;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return TPM_UNDEFINED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tpm2_calc_ordinal_duration() - calculate the maximum command duration
|
|
|
|
* @chip: TPM chip to use.
|
|
|
|
* @ordinal: TPM command ordinal.
|
|
|
|
*
|
|
|
|
* The function returns the maximum amount of time the chip could take
|
|
|
|
* to return the result for a particular ordinal in jiffies.
|
|
|
|
*
|
|
|
|
* Return: A maximal duration time for an ordinal in jiffies.
|
|
|
|
*/
|
|
|
|
unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
|
|
|
|
{
|
|
|
|
unsigned int index;
|
|
|
|
|
|
|
|
index = tpm2_ordinal_duration_index(ordinal);
|
|
|
|
|
|
|
|
if (index != TPM_UNDEFINED)
|
|
|
|
return chip->duration[index];
|
|
|
|
else
|
|
|
|
return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
|
|
|
|
}
|
|
|
|
|
2014-12-13 03:46:38 +08:00
|
|
|
|
2017-06-23 21:41:56 +08:00
|
|
|
struct tpm2_pcr_read_out {
|
|
|
|
__be32 update_cnt;
|
|
|
|
__be32 pcr_selects_cnt;
|
|
|
|
__be16 hash_alg;
|
|
|
|
u8 pcr_select_size;
|
|
|
|
u8 pcr_select[TPM2_PCR_SELECT_MIN];
|
|
|
|
__be32 digests_cnt;
|
|
|
|
__be16 digest_size;
|
|
|
|
u8 digest[];
|
|
|
|
} __packed;
|
|
|
|
|
2014-12-13 03:46:38 +08:00
|
|
|
/**
|
|
|
|
* tpm2_pcr_read() - read a PCR value
|
|
|
|
* @chip: TPM chip to use.
|
|
|
|
* @pcr_idx: index of the PCR to read.
|
2016-11-23 18:04:12 +08:00
|
|
|
* @res_buf: buffer to store the resulting hash.
|
2014-12-13 03:46:38 +08:00
|
|
|
*
|
2016-11-23 18:04:12 +08:00
|
|
|
* Return: Same as with tpm_transmit_cmd.
|
2014-12-13 03:46:38 +08:00
|
|
|
*/
|
|
|
|
int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
|
|
|
|
{
|
|
|
|
int rc;
|
2017-06-23 21:41:56 +08:00
|
|
|
struct tpm_buf buf;
|
|
|
|
struct tpm2_pcr_read_out *out;
|
|
|
|
u8 pcr_select[TPM2_PCR_SELECT_MIN] = {0};
|
2014-12-13 03:46:38 +08:00
|
|
|
|
|
|
|
if (pcr_idx >= TPM2_PLATFORM_PCR)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2017-06-23 21:41:56 +08:00
|
|
|
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
|
|
|
|
|
|
|
|
tpm_buf_append_u32(&buf, 1);
|
|
|
|
tpm_buf_append_u16(&buf, TPM2_ALG_SHA1);
|
|
|
|
tpm_buf_append_u8(&buf, TPM2_PCR_SELECT_MIN);
|
|
|
|
tpm_buf_append(&buf, (const unsigned char *)pcr_select,
|
|
|
|
sizeof(pcr_select));
|
|
|
|
|
|
|
|
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
|
|
|
|
res_buf ? "attempting to read a pcr value" : NULL);
|
|
|
|
if (rc == 0 && res_buf) {
|
|
|
|
out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE];
|
|
|
|
memcpy(res_buf, out->digest, SHA1_DIGEST_SIZE);
|
2014-12-13 03:46:38 +08:00
|
|
|
}
|
|
|
|
|
2017-06-23 21:41:56 +08:00
|
|
|
tpm_buf_destroy(&buf);
|
2014-12-13 03:46:38 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2017-01-30 17:59:41 +08:00
|
|
|
struct tpm2_null_auth_area {
|
|
|
|
__be32 handle;
|
|
|
|
__be16 nonce_size;
|
|
|
|
u8 attributes;
|
|
|
|
__be16 auth_size;
|
|
|
|
} __packed;
|
2014-12-13 03:46:38 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* tpm2_pcr_extend() - extend a PCR value
|
2016-11-23 18:04:12 +08:00
|
|
|
*
|
2014-12-13 03:46:38 +08:00
|
|
|
* @chip: TPM chip to use.
|
|
|
|
* @pcr_idx: index of the PCR.
|
2017-01-30 17:59:41 +08:00
|
|
|
* @count: number of digests passed.
|
|
|
|
* @digests: list of pcr banks and corresponding digest values to extend.
|
2014-12-13 03:46:38 +08:00
|
|
|
*
|
2016-11-23 18:04:12 +08:00
|
|
|
* Return: Same as with tpm_transmit_cmd.
|
2014-12-13 03:46:38 +08:00
|
|
|
*/
|
2017-01-30 17:59:41 +08:00
|
|
|
int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, u32 count,
|
|
|
|
struct tpm2_digest *digests)
|
2014-12-13 03:46:38 +08:00
|
|
|
{
|
2017-01-30 17:59:41 +08:00
|
|
|
struct tpm_buf buf;
|
|
|
|
struct tpm2_null_auth_area auth_area;
|
2014-12-13 03:46:38 +08:00
|
|
|
int rc;
|
2017-01-30 17:59:41 +08:00
|
|
|
int i;
|
|
|
|
int j;
|
2014-12-13 03:46:38 +08:00
|
|
|
|
2017-01-30 17:59:41 +08:00
|
|
|
if (count > ARRAY_SIZE(chip->active_banks))
|
|
|
|
return -EINVAL;
|
2014-12-13 03:46:38 +08:00
|
|
|
|
2017-01-30 17:59:41 +08:00
|
|
|
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
tpm_buf_append_u32(&buf, pcr_idx);
|
|
|
|
|
|
|
|
auth_area.handle = cpu_to_be32(TPM2_RS_PW);
|
|
|
|
auth_area.nonce_size = 0;
|
|
|
|
auth_area.attributes = 0;
|
|
|
|
auth_area.auth_size = 0;
|
|
|
|
|
|
|
|
tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area));
|
|
|
|
tpm_buf_append(&buf, (const unsigned char *)&auth_area,
|
|
|
|
sizeof(auth_area));
|
|
|
|
tpm_buf_append_u32(&buf, count);
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
for (j = 0; j < ARRAY_SIZE(tpm2_hash_map); j++) {
|
|
|
|
if (digests[i].alg_id != tpm2_hash_map[j].tpm_id)
|
|
|
|
continue;
|
|
|
|
tpm_buf_append_u16(&buf, digests[i].alg_id);
|
|
|
|
tpm_buf_append(&buf, (const unsigned char
|
|
|
|
*)&digests[i].digest,
|
|
|
|
hash_digest_size[tpm2_hash_map[j].crypto_id]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-06 20:03:45 +08:00
|
|
|
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
|
2014-12-13 03:46:38 +08:00
|
|
|
"attempting extend a PCR value");
|
|
|
|
|
2017-01-30 17:59:41 +08:00
|
|
|
tpm_buf_destroy(&buf);
|
|
|
|
|
2014-12-13 03:46:38 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2017-01-30 17:59:41 +08:00
|
|
|
|
2018-03-26 20:14:06 +08:00
|
|
|
struct tpm2_get_random_out {
|
|
|
|
__be16 size;
|
|
|
|
u8 buffer[TPM_MAX_RNG_DATA];
|
|
|
|
} __packed;
|
2014-12-13 03:46:38 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* tpm2_get_random() - get random bytes from the TPM RNG
|
2016-11-23 18:04:12 +08:00
|
|
|
*
|
2018-03-26 20:14:06 +08:00
|
|
|
* @chip: a &tpm_chip instance
|
|
|
|
* @dest: destination buffer
|
|
|
|
* @max: the max number of random bytes to pull
|
2014-12-13 03:46:38 +08:00
|
|
|
*
|
2016-11-23 18:04:12 +08:00
|
|
|
* Return:
|
2018-03-26 20:14:06 +08:00
|
|
|
* size of the buffer on success,
|
|
|
|
* -errno otherwise
|
2014-12-13 03:46:38 +08:00
|
|
|
*/
|
2018-03-26 20:14:06 +08:00
|
|
|
int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
|
2014-12-13 03:46:38 +08:00
|
|
|
{
|
2018-03-26 20:14:06 +08:00
|
|
|
struct tpm2_get_random_out *out;
|
|
|
|
struct tpm_buf buf;
|
|
|
|
u32 recd;
|
|
|
|
u32 num_bytes = max;
|
2014-12-13 03:46:38 +08:00
|
|
|
int err;
|
|
|
|
int total = 0;
|
|
|
|
int retries = 5;
|
2018-03-26 20:14:06 +08:00
|
|
|
u8 *dest_ptr = dest;
|
2014-12-13 03:46:38 +08:00
|
|
|
|
2018-03-26 20:14:06 +08:00
|
|
|
if (!num_bytes || max > TPM_MAX_RNG_DATA)
|
2014-12-13 03:46:38 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2018-03-26 20:14:06 +08:00
|
|
|
err = tpm_buf_init(&buf, 0, 0);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2014-12-13 03:46:38 +08:00
|
|
|
|
2018-03-26 20:14:06 +08:00
|
|
|
do {
|
|
|
|
tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_RANDOM);
|
|
|
|
tpm_buf_append_u16(&buf, num_bytes);
|
|
|
|
err = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE,
|
2017-01-19 20:19:12 +08:00
|
|
|
offsetof(struct tpm2_get_random_out,
|
|
|
|
buffer),
|
|
|
|
0, "attempting get random");
|
2014-12-13 03:46:38 +08:00
|
|
|
if (err)
|
2018-03-26 20:14:06 +08:00
|
|
|
goto out;
|
2014-12-13 03:46:38 +08:00
|
|
|
|
2018-03-26 20:14:06 +08:00
|
|
|
out = (struct tpm2_get_random_out *)
|
|
|
|
&buf.data[TPM_HEADER_SIZE];
|
|
|
|
recd = min_t(u32, be16_to_cpu(out->size), num_bytes);
|
|
|
|
if (tpm_buf_length(&buf) <
|
2018-09-03 09:01:26 +08:00
|
|
|
TPM_HEADER_SIZE +
|
|
|
|
offsetof(struct tpm2_get_random_out, buffer) +
|
|
|
|
recd) {
|
2018-03-26 20:14:06 +08:00
|
|
|
err = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
memcpy(dest_ptr, out->buffer, recd);
|
2014-12-13 03:46:38 +08:00
|
|
|
|
2018-03-26 20:14:06 +08:00
|
|
|
dest_ptr += recd;
|
2014-12-13 03:46:38 +08:00
|
|
|
total += recd;
|
|
|
|
num_bytes -= recd;
|
|
|
|
} while (retries-- && total < max);
|
|
|
|
|
2018-03-26 20:14:06 +08:00
|
|
|
tpm_buf_destroy(&buf);
|
2014-12-13 03:46:38 +08:00
|
|
|
return total ? total : -EIO;
|
2018-03-26 20:14:06 +08:00
|
|
|
out:
|
|
|
|
tpm_buf_destroy(&buf);
|
|
|
|
return err;
|
2014-12-13 03:46:38 +08:00
|
|
|
}
|
|
|
|
|
2016-11-26 19:39:35 +08:00
|
|
|
/**
|
|
|
|
* tpm2_flush_context_cmd() - execute a TPM2_FlushContext command
|
|
|
|
* @chip: TPM chip to use
|
|
|
|
* @payload: the key data in clear and encrypted form
|
|
|
|
* @options: authentication values and other options
|
|
|
|
*
|
|
|
|
* Return: same as with tpm_transmit_cmd
|
|
|
|
*/
|
|
|
|
void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
struct tpm_buf buf;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
|
|
|
|
if (rc) {
|
|
|
|
dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
|
|
|
|
handle);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tpm_buf_append_u32(&buf, handle);
|
|
|
|
|
2017-01-06 20:03:45 +08:00
|
|
|
(void) tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, flags,
|
2016-11-26 19:39:35 +08:00
|
|
|
"flushing context");
|
|
|
|
|
|
|
|
tpm_buf_destroy(&buf);
|
|
|
|
}
|
|
|
|
|
2015-05-30 13:09:04 +08:00
|
|
|
/**
|
2016-11-23 18:04:12 +08:00
|
|
|
* tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
|
|
|
|
*
|
|
|
|
* @buf: an allocated tpm_buf instance
|
|
|
|
* @session_handle: session handle
|
|
|
|
* @nonce: the session nonce, may be NULL if not used
|
|
|
|
* @nonce_len: the session nonce length, may be 0 if not used
|
|
|
|
* @attributes: the session attributes
|
|
|
|
* @hmac: the session HMAC or password, may be NULL if not used
|
|
|
|
* @hmac_len: the session HMAC or password length, maybe 0 if not used
|
2015-05-30 13:09:04 +08:00
|
|
|
*/
|
|
|
|
static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle,
|
|
|
|
const u8 *nonce, u16 nonce_len,
|
|
|
|
u8 attributes,
|
|
|
|
const u8 *hmac, u16 hmac_len)
|
|
|
|
{
|
|
|
|
tpm_buf_append_u32(buf, 9 + nonce_len + hmac_len);
|
|
|
|
tpm_buf_append_u32(buf, session_handle);
|
|
|
|
tpm_buf_append_u16(buf, nonce_len);
|
|
|
|
|
|
|
|
if (nonce && nonce_len)
|
|
|
|
tpm_buf_append(buf, nonce, nonce_len);
|
|
|
|
|
|
|
|
tpm_buf_append_u8(buf, attributes);
|
|
|
|
tpm_buf_append_u16(buf, hmac_len);
|
|
|
|
|
|
|
|
if (hmac && hmac_len)
|
|
|
|
tpm_buf_append(buf, hmac, hmac_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-08-17 03:00:38 +08:00
|
|
|
* tpm2_seal_trusted() - seal the payload of a trusted key
|
2016-11-23 18:04:12 +08:00
|
|
|
*
|
|
|
|
* @chip: TPM chip to use
|
2015-05-30 13:09:04 +08:00
|
|
|
* @payload: the key data in clear and encrypted form
|
2016-08-17 03:00:38 +08:00
|
|
|
* @options: authentication values and other options
|
2015-05-30 13:09:04 +08:00
|
|
|
*
|
2016-08-17 03:00:38 +08:00
|
|
|
* Return: < 0 on error and 0 on success.
|
2015-05-30 13:09:04 +08:00
|
|
|
*/
|
|
|
|
int tpm2_seal_trusted(struct tpm_chip *chip,
|
|
|
|
struct trusted_key_payload *payload,
|
|
|
|
struct trusted_key_options *options)
|
|
|
|
{
|
|
|
|
unsigned int blob_len;
|
|
|
|
struct tpm_buf buf;
|
2018-03-26 20:14:06 +08:00
|
|
|
u32 hash;
|
2015-11-06 03:43:06 +08:00
|
|
|
int i;
|
2015-05-30 13:09:04 +08:00
|
|
|
int rc;
|
|
|
|
|
2015-11-06 03:43:06 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) {
|
|
|
|
if (options->hash == tpm2_hash_map[i].crypto_id) {
|
|
|
|
hash = tpm2_hash_map[i].tpm_id;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == ARRAY_SIZE(tpm2_hash_map))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2015-05-30 13:09:04 +08:00
|
|
|
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
tpm_buf_append_u32(&buf, options->keyhandle);
|
|
|
|
tpm2_buf_append_auth(&buf, TPM2_RS_PW,
|
|
|
|
NULL /* nonce */, 0,
|
|
|
|
0 /* session_attributes */,
|
|
|
|
options->keyauth /* hmac */,
|
|
|
|
TPM_DIGEST_SIZE);
|
|
|
|
|
|
|
|
/* sensitive */
|
2015-10-30 20:57:02 +08:00
|
|
|
tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len + 1);
|
2015-05-30 13:09:04 +08:00
|
|
|
|
|
|
|
tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE);
|
|
|
|
tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE);
|
2015-10-30 20:57:02 +08:00
|
|
|
tpm_buf_append_u16(&buf, payload->key_len + 1);
|
2015-05-30 13:09:04 +08:00
|
|
|
tpm_buf_append(&buf, payload->key, payload->key_len);
|
2015-10-30 20:57:02 +08:00
|
|
|
tpm_buf_append_u8(&buf, payload->migratable);
|
2015-05-30 13:09:04 +08:00
|
|
|
|
|
|
|
/* public */
|
2016-01-06 22:43:30 +08:00
|
|
|
tpm_buf_append_u16(&buf, 14 + options->policydigest_len);
|
2015-05-30 13:09:04 +08:00
|
|
|
tpm_buf_append_u16(&buf, TPM2_ALG_KEYEDHASH);
|
2015-11-06 03:43:06 +08:00
|
|
|
tpm_buf_append_u16(&buf, hash);
|
2015-10-31 23:53:44 +08:00
|
|
|
|
|
|
|
/* policy */
|
2016-01-06 22:43:30 +08:00
|
|
|
if (options->policydigest_len) {
|
2015-10-31 23:53:44 +08:00
|
|
|
tpm_buf_append_u32(&buf, 0);
|
2016-01-06 22:43:30 +08:00
|
|
|
tpm_buf_append_u16(&buf, options->policydigest_len);
|
2015-10-31 23:53:44 +08:00
|
|
|
tpm_buf_append(&buf, options->policydigest,
|
2016-01-06 22:43:30 +08:00
|
|
|
options->policydigest_len);
|
2015-10-31 23:53:44 +08:00
|
|
|
} else {
|
2016-02-13 17:51:23 +08:00
|
|
|
tpm_buf_append_u32(&buf, TPM2_OA_USER_WITH_AUTH);
|
2015-10-31 23:53:44 +08:00
|
|
|
tpm_buf_append_u16(&buf, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* public parameters */
|
2015-05-30 13:09:04 +08:00
|
|
|
tpm_buf_append_u16(&buf, TPM2_ALG_NULL);
|
|
|
|
tpm_buf_append_u16(&buf, 0);
|
|
|
|
|
|
|
|
/* outside info */
|
|
|
|
tpm_buf_append_u16(&buf, 0);
|
|
|
|
|
|
|
|
/* creation PCR */
|
|
|
|
tpm_buf_append_u32(&buf, 0);
|
|
|
|
|
|
|
|
if (buf.flags & TPM_BUF_OVERFLOW) {
|
|
|
|
rc = -E2BIG;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-01-06 20:03:45 +08:00
|
|
|
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, 0,
|
2017-01-19 20:19:12 +08:00
|
|
|
"sealing data");
|
2015-05-30 13:09:04 +08:00
|
|
|
if (rc)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]);
|
|
|
|
if (blob_len > MAX_BLOB_SIZE) {
|
|
|
|
rc = -E2BIG;
|
|
|
|
goto out;
|
|
|
|
}
|
2018-03-26 20:14:06 +08:00
|
|
|
if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) {
|
2017-01-19 20:19:12 +08:00
|
|
|
rc = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
2015-05-30 13:09:04 +08:00
|
|
|
|
|
|
|
memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len);
|
|
|
|
payload->blob_len = blob_len;
|
|
|
|
|
|
|
|
out:
|
|
|
|
tpm_buf_destroy(&buf);
|
|
|
|
|
2015-11-06 03:43:06 +08:00
|
|
|
if (rc > 0) {
|
2017-01-26 05:00:22 +08:00
|
|
|
if (tpm2_rc_value(rc) == TPM2_RC_HASH)
|
2015-11-06 03:43:06 +08:00
|
|
|
rc = -EINVAL;
|
|
|
|
else
|
|
|
|
rc = -EPERM;
|
|
|
|
}
|
2015-05-30 13:09:04 +08:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2016-08-17 03:00:38 +08:00
|
|
|
/**
|
|
|
|
* tpm2_load_cmd() - execute a TPM2_Load command
|
2016-11-23 18:04:12 +08:00
|
|
|
*
|
|
|
|
* @chip: TPM chip to use
|
2016-08-17 03:00:38 +08:00
|
|
|
* @payload: the key data in clear and encrypted form
|
|
|
|
* @options: authentication values and other options
|
2016-11-23 18:04:12 +08:00
|
|
|
* @blob_handle: returned blob handle
|
|
|
|
* @flags: tpm transmit flags
|
2016-08-17 03:00:38 +08:00
|
|
|
*
|
2016-11-23 18:04:12 +08:00
|
|
|
* Return: 0 on success.
|
|
|
|
* -E2BIG on wrong payload size.
|
|
|
|
* -EPERM on tpm error status.
|
|
|
|
* < 0 error from tpm_transmit_cmd.
|
2016-08-17 03:00:38 +08:00
|
|
|
*/
|
|
|
|
static int tpm2_load_cmd(struct tpm_chip *chip,
|
|
|
|
struct trusted_key_payload *payload,
|
|
|
|
struct trusted_key_options *options,
|
|
|
|
u32 *blob_handle, unsigned int flags)
|
2015-05-30 13:09:04 +08:00
|
|
|
{
|
|
|
|
struct tpm_buf buf;
|
|
|
|
unsigned int private_len;
|
|
|
|
unsigned int public_len;
|
|
|
|
unsigned int blob_len;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
private_len = be16_to_cpup((__be16 *) &payload->blob[0]);
|
|
|
|
if (private_len > (payload->blob_len - 2))
|
|
|
|
return -E2BIG;
|
|
|
|
|
|
|
|
public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]);
|
|
|
|
blob_len = private_len + public_len + 4;
|
|
|
|
if (blob_len > payload->blob_len)
|
|
|
|
return -E2BIG;
|
|
|
|
|
|
|
|
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
tpm_buf_append_u32(&buf, options->keyhandle);
|
|
|
|
tpm2_buf_append_auth(&buf, TPM2_RS_PW,
|
|
|
|
NULL /* nonce */, 0,
|
|
|
|
0 /* session_attributes */,
|
|
|
|
options->keyauth /* hmac */,
|
|
|
|
TPM_DIGEST_SIZE);
|
|
|
|
|
|
|
|
tpm_buf_append(&buf, payload->blob, blob_len);
|
|
|
|
|
|
|
|
if (buf.flags & TPM_BUF_OVERFLOW) {
|
|
|
|
rc = -E2BIG;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-01-06 20:03:45 +08:00
|
|
|
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, flags,
|
2017-01-19 20:19:12 +08:00
|
|
|
"loading blob");
|
2015-05-30 13:09:04 +08:00
|
|
|
if (!rc)
|
|
|
|
*blob_handle = be32_to_cpup(
|
|
|
|
(__be32 *) &buf.data[TPM_HEADER_SIZE]);
|
|
|
|
|
|
|
|
out:
|
|
|
|
tpm_buf_destroy(&buf);
|
|
|
|
|
|
|
|
if (rc > 0)
|
|
|
|
rc = -EPERM;
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2016-08-17 03:00:38 +08:00
|
|
|
/**
|
|
|
|
* tpm2_unseal_cmd() - execute a TPM2_Unload command
|
2016-11-23 18:04:12 +08:00
|
|
|
*
|
|
|
|
* @chip: TPM chip to use
|
2016-08-17 03:00:38 +08:00
|
|
|
* @payload: the key data in clear and encrypted form
|
|
|
|
* @options: authentication values and other options
|
2016-11-23 18:04:12 +08:00
|
|
|
* @blob_handle: blob handle
|
|
|
|
* @flags: tpm_transmit_cmd flags
|
2016-08-17 03:00:38 +08:00
|
|
|
*
|
2016-11-23 18:04:12 +08:00
|
|
|
* Return: 0 on success
|
|
|
|
* -EPERM on tpm error status
|
|
|
|
* < 0 error from tpm_transmit_cmd
|
2016-08-17 03:00:38 +08:00
|
|
|
*/
|
|
|
|
static int tpm2_unseal_cmd(struct tpm_chip *chip,
|
|
|
|
struct trusted_key_payload *payload,
|
|
|
|
struct trusted_key_options *options,
|
|
|
|
u32 blob_handle, unsigned int flags)
|
2015-05-30 13:09:04 +08:00
|
|
|
{
|
|
|
|
struct tpm_buf buf;
|
2015-10-30 20:57:02 +08:00
|
|
|
u16 data_len;
|
|
|
|
u8 *data;
|
2015-05-30 13:09:04 +08:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
tpm_buf_append_u32(&buf, blob_handle);
|
2015-10-31 23:53:44 +08:00
|
|
|
tpm2_buf_append_auth(&buf,
|
|
|
|
options->policyhandle ?
|
|
|
|
options->policyhandle : TPM2_RS_PW,
|
2015-05-30 13:09:04 +08:00
|
|
|
NULL /* nonce */, 0,
|
2016-02-13 17:51:23 +08:00
|
|
|
TPM2_SA_CONTINUE_SESSION,
|
2015-05-30 13:09:04 +08:00
|
|
|
options->blobauth /* hmac */,
|
|
|
|
TPM_DIGEST_SIZE);
|
|
|
|
|
2017-01-06 20:03:45 +08:00
|
|
|
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 6, flags,
|
2017-01-19 20:19:12 +08:00
|
|
|
"unsealing");
|
2015-05-30 13:09:04 +08:00
|
|
|
if (rc > 0)
|
|
|
|
rc = -EPERM;
|
|
|
|
|
|
|
|
if (!rc) {
|
2015-10-30 20:57:02 +08:00
|
|
|
data_len = be16_to_cpup(
|
2015-05-30 13:09:04 +08:00
|
|
|
(__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
|
2018-02-09 04:28:08 +08:00
|
|
|
if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE + 1) {
|
|
|
|
rc = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
2017-01-19 20:19:12 +08:00
|
|
|
|
2018-03-26 20:14:06 +08:00
|
|
|
if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 6 + data_len) {
|
2017-01-19 20:19:12 +08:00
|
|
|
rc = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
2015-10-30 20:57:02 +08:00
|
|
|
data = &buf.data[TPM_HEADER_SIZE + 6];
|
2015-05-30 13:09:04 +08:00
|
|
|
|
2015-10-30 20:57:02 +08:00
|
|
|
memcpy(payload->key, data, data_len - 1);
|
|
|
|
payload->key_len = data_len - 1;
|
|
|
|
payload->migratable = data[data_len - 1];
|
2015-05-30 13:09:04 +08:00
|
|
|
}
|
|
|
|
|
2017-01-19 20:19:12 +08:00
|
|
|
out:
|
2015-05-30 13:09:04 +08:00
|
|
|
tpm_buf_destroy(&buf);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-11-06 17:02:45 +08:00
|
|
|
* tpm2_unseal_trusted() - unseal the payload of a trusted key
|
2016-11-23 18:04:12 +08:00
|
|
|
*
|
|
|
|
* @chip: TPM chip to use
|
2015-05-30 13:09:04 +08:00
|
|
|
* @payload: the key data in clear and encrypted form
|
2016-08-17 03:00:38 +08:00
|
|
|
* @options: authentication values and other options
|
2015-05-30 13:09:04 +08:00
|
|
|
*
|
2016-11-23 18:04:12 +08:00
|
|
|
* Return: Same as with tpm_transmit_cmd.
|
2015-05-30 13:09:04 +08:00
|
|
|
*/
|
|
|
|
int tpm2_unseal_trusted(struct tpm_chip *chip,
|
|
|
|
struct trusted_key_payload *payload,
|
|
|
|
struct trusted_key_options *options)
|
|
|
|
{
|
|
|
|
u32 blob_handle;
|
|
|
|
int rc;
|
|
|
|
|
2016-08-17 03:00:38 +08:00
|
|
|
mutex_lock(&chip->tpm_mutex);
|
|
|
|
rc = tpm2_load_cmd(chip, payload, options, &blob_handle,
|
|
|
|
TPM_TRANSMIT_UNLOCKED);
|
2015-05-30 13:09:04 +08:00
|
|
|
if (rc)
|
2016-08-17 03:00:38 +08:00
|
|
|
goto out;
|
2015-05-30 13:09:04 +08:00
|
|
|
|
2016-08-17 03:00:38 +08:00
|
|
|
rc = tpm2_unseal_cmd(chip, payload, options, blob_handle,
|
|
|
|
TPM_TRANSMIT_UNLOCKED);
|
|
|
|
tpm2_flush_context_cmd(chip, blob_handle, TPM_TRANSMIT_UNLOCKED);
|
|
|
|
out:
|
|
|
|
mutex_unlock(&chip->tpm_mutex);
|
2015-05-30 13:09:04 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2018-03-26 20:14:05 +08:00
|
|
|
struct tpm2_get_cap_out {
|
|
|
|
u8 more_data;
|
|
|
|
__be32 subcap_id;
|
|
|
|
__be32 property_cnt;
|
|
|
|
__be32 property_id;
|
|
|
|
__be32 value;
|
|
|
|
} __packed;
|
|
|
|
|
2014-12-13 03:46:38 +08:00
|
|
|
/**
|
|
|
|
* tpm2_get_tpm_pt() - get value of a TPM_CAP_TPM_PROPERTIES type property
|
2018-03-26 20:14:05 +08:00
|
|
|
* @chip: a &tpm_chip instance
|
2014-12-13 03:46:38 +08:00
|
|
|
* @property_id: property ID.
|
|
|
|
* @value: output variable.
|
|
|
|
* @desc: passed to tpm_transmit_cmd()
|
|
|
|
*
|
2018-03-26 20:14:05 +08:00
|
|
|
* Return:
|
|
|
|
* 0 on success,
|
|
|
|
* -errno or a TPM return code otherwise
|
2014-12-13 03:46:38 +08:00
|
|
|
*/
|
|
|
|
ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
|
|
|
|
const char *desc)
|
|
|
|
{
|
2018-03-26 20:14:05 +08:00
|
|
|
struct tpm2_get_cap_out *out;
|
|
|
|
struct tpm_buf buf;
|
2014-12-13 03:46:38 +08:00
|
|
|
int rc;
|
|
|
|
|
2018-03-26 20:14:05 +08:00
|
|
|
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
|
|
|
|
tpm_buf_append_u32(&buf, property_id);
|
|
|
|
tpm_buf_append_u32(&buf, 1);
|
|
|
|
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, NULL);
|
|
|
|
if (!rc) {
|
|
|
|
out = (struct tpm2_get_cap_out *)
|
|
|
|
&buf.data[TPM_HEADER_SIZE];
|
|
|
|
*value = be32_to_cpu(out->value);
|
|
|
|
}
|
|
|
|
tpm_buf_destroy(&buf);
|
2014-12-13 03:46:38 +08:00
|
|
|
return rc;
|
|
|
|
}
|
2016-06-12 21:42:09 +08:00
|
|
|
EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
|
2014-12-13 03:46:38 +08:00
|
|
|
|
|
|
|
/**
|
2018-06-06 03:22:58 +08:00
|
|
|
* tpm2_shutdown() - send a TPM shutdown command
|
2016-11-23 18:04:12 +08:00
|
|
|
*
|
2018-06-06 03:22:58 +08:00
|
|
|
* Sends a TPM shutdown command. The shutdown command is used in call
|
|
|
|
* sites where the system is going down. If it fails, there is not much
|
|
|
|
* that can be done except print an error message.
|
|
|
|
*
|
|
|
|
* @chip: a &tpm_chip instance
|
|
|
|
* @shutdown_type: TPM_SU_CLEAR or TPM_SU_STATE.
|
2014-12-13 03:46:38 +08:00
|
|
|
*/
|
2015-01-29 13:43:47 +08:00
|
|
|
void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
|
2014-12-13 03:46:38 +08:00
|
|
|
{
|
2018-06-06 03:22:58 +08:00
|
|
|
struct tpm_buf buf;
|
2015-01-29 13:43:47 +08:00
|
|
|
int rc;
|
2014-12-13 03:46:38 +08:00
|
|
|
|
2018-06-06 03:22:58 +08:00
|
|
|
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN);
|
|
|
|
if (rc)
|
|
|
|
return;
|
|
|
|
tpm_buf_append_u16(&buf, shutdown_type);
|
|
|
|
tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
|
|
|
|
"stopping the TPM");
|
|
|
|
tpm_buf_destroy(&buf);
|
2014-12-13 03:46:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-09-01 01:18:56 +08:00
|
|
|
* tpm2_do_selftest() - ensure that all self tests have passed
|
2016-11-23 18:04:12 +08:00
|
|
|
*
|
2014-12-13 03:46:38 +08:00
|
|
|
* @chip: TPM chip to use
|
|
|
|
*
|
2016-11-23 18:04:12 +08:00
|
|
|
* Return: Same as with tpm_transmit_cmd.
|
|
|
|
*
|
tpm: React correctly to RC_TESTING from TPM 2.0 self tests
The TPM can choose one of two ways to react to the TPM2_SelfTest command.
It can either run all self tests synchronously and then return RC_SUCCESS
once all tests were successful. Or it can choose to run the tests
asynchronously and return RC_TESTING immediately while the self tests still
execute in the background.
The previous implementation apparently was not aware of those possibilities
and attributed RC_TESTING to some prototype chips instead. With this change
the return code of TPM2_SelfTest is interpreted correctly, i.e. the self
test result is polled if and only if RC_TESTING is received.
Unfortunately, the polling cannot be done in the most straightforward way.
If RC_TESTING is received, ideally the code should now poll the
selfTestDone bit in the STS register, as this avoids sending more commands,
that might interrupt self tests executing in the background and thus
prevent them from ever completing. But it cannot be guaranteed that this
bit is correctly implemented for all devices, so the next best thing would
be to use TPM2_GetTestResult to query the test result. But the response to
that command can be very long, and the code currently lacks the
capabilities for efficient unmarshalling, so it is difficult to execute
this command.
Therefore, we simply run the TPM2_SelfTest command in a loop, which should
complete eventually, since we only request the execution of self tests that
have not yet been done.
Signed-off-by: Alexander Steffen <Alexander.Steffen@infineon.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Tested-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
2017-09-01 01:18:58 +08:00
|
|
|
* The TPM can either run all self tests synchronously and then return
|
|
|
|
* RC_SUCCESS once all tests were successful. Or it can choose to run the tests
|
|
|
|
* asynchronously and return RC_TESTING immediately while the self tests still
|
|
|
|
* execute in the background. This function handles both cases and waits until
|
|
|
|
* all tests have completed.
|
2014-12-13 03:46:38 +08:00
|
|
|
*/
|
2016-07-13 01:41:49 +08:00
|
|
|
static int tpm2_do_selftest(struct tpm_chip *chip)
|
2014-12-13 03:46:38 +08:00
|
|
|
{
|
2018-03-22 23:32:20 +08:00
|
|
|
struct tpm_buf buf;
|
|
|
|
int full;
|
2014-12-13 03:46:38 +08:00
|
|
|
int rc;
|
|
|
|
|
2018-03-22 23:32:20 +08:00
|
|
|
for (full = 0; full < 2; full++) {
|
|
|
|
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
2014-12-13 03:46:38 +08:00
|
|
|
|
2018-03-22 23:32:20 +08:00
|
|
|
tpm_buf_append_u8(&buf, full);
|
|
|
|
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
|
|
|
|
"attempting the self test");
|
|
|
|
tpm_buf_destroy(&buf);
|
2014-12-13 03:46:38 +08:00
|
|
|
|
2018-03-22 23:32:20 +08:00
|
|
|
if (rc == TPM2_RC_TESTING)
|
|
|
|
rc = TPM2_RC_SUCCESS;
|
|
|
|
if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS)
|
|
|
|
return rc;
|
2014-12-13 03:46:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2015-02-04 22:21:09 +08:00
|
|
|
/**
|
2018-03-26 20:14:04 +08:00
|
|
|
* tpm2_probe() - probe for the TPM 2.0 protocol
|
|
|
|
* @chip: a &tpm_chip instance
|
2015-02-04 22:21:09 +08:00
|
|
|
*
|
2018-03-26 20:14:04 +08:00
|
|
|
* Send an idempotent TPM 2.0 command and see whether there is TPM2 chip in the
|
|
|
|
* other end based on the response tag. The flag TPM_CHIP_FLAG_TPM2 is set by
|
|
|
|
* this function if this is the case.
|
2016-11-23 18:04:12 +08:00
|
|
|
*
|
2018-03-26 20:14:04 +08:00
|
|
|
* Return:
|
|
|
|
* 0 on success,
|
|
|
|
* -errno otherwise
|
2015-02-04 22:21:09 +08:00
|
|
|
*/
|
|
|
|
int tpm2_probe(struct tpm_chip *chip)
|
|
|
|
{
|
2018-03-26 20:14:04 +08:00
|
|
|
struct tpm_output_header *out;
|
|
|
|
struct tpm_buf buf;
|
2015-02-04 22:21:09 +08:00
|
|
|
int rc;
|
|
|
|
|
2018-03-26 20:14:04 +08:00
|
|
|
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
|
|
|
|
if (rc)
|
2015-02-04 22:21:09 +08:00
|
|
|
return rc;
|
2018-03-26 20:14:04 +08:00
|
|
|
tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
|
|
|
|
tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS);
|
|
|
|
tpm_buf_append_u32(&buf, 1);
|
|
|
|
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, NULL);
|
|
|
|
/* We ignore TPM return codes on purpose. */
|
|
|
|
if (rc >= 0) {
|
|
|
|
out = (struct tpm_output_header *)buf.data;
|
|
|
|
if (be16_to_cpu(out->tag) == TPM2_ST_NO_SESSIONS)
|
|
|
|
chip->flags |= TPM_CHIP_FLAG_TPM2;
|
|
|
|
}
|
|
|
|
tpm_buf_destroy(&buf);
|
2015-02-04 22:21:09 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tpm2_probe);
|
2016-07-13 01:41:49 +08:00
|
|
|
|
2017-01-30 17:59:40 +08:00
|
|
|
struct tpm2_pcr_selection {
|
|
|
|
__be16 hash_alg;
|
|
|
|
u8 size_of_select;
|
|
|
|
u8 pcr_select[3];
|
|
|
|
} __packed;
|
|
|
|
|
2017-02-16 02:02:28 +08:00
|
|
|
static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
|
2017-01-30 17:59:40 +08:00
|
|
|
{
|
|
|
|
struct tpm2_pcr_selection pcr_selection;
|
|
|
|
struct tpm_buf buf;
|
|
|
|
void *marker;
|
|
|
|
void *end;
|
|
|
|
void *pcr_select_offset;
|
|
|
|
unsigned int count;
|
|
|
|
u32 sizeof_pcr_selection;
|
|
|
|
u32 rsp_len;
|
|
|
|
int rc;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
tpm_buf_append_u32(&buf, TPM2_CAP_PCRS);
|
|
|
|
tpm_buf_append_u32(&buf, 0);
|
|
|
|
tpm_buf_append_u32(&buf, 1);
|
|
|
|
|
2017-01-06 20:03:45 +08:00
|
|
|
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 9, 0,
|
2017-01-30 17:59:40 +08:00
|
|
|
"get tpm pcr allocation");
|
|
|
|
if (rc)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
count = be32_to_cpup(
|
|
|
|
(__be32 *)&buf.data[TPM_HEADER_SIZE + 5]);
|
|
|
|
|
|
|
|
if (count > ARRAY_SIZE(chip->active_banks)) {
|
|
|
|
rc = -ENODEV;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
marker = &buf.data[TPM_HEADER_SIZE + 9];
|
|
|
|
|
|
|
|
rsp_len = be32_to_cpup((__be32 *)&buf.data[2]);
|
|
|
|
end = &buf.data[rsp_len];
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
pcr_select_offset = marker +
|
|
|
|
offsetof(struct tpm2_pcr_selection, size_of_select);
|
|
|
|
if (pcr_select_offset >= end) {
|
|
|
|
rc = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&pcr_selection, marker, sizeof(pcr_selection));
|
|
|
|
chip->active_banks[i] = be16_to_cpu(pcr_selection.hash_alg);
|
|
|
|
sizeof_pcr_selection = sizeof(pcr_selection.hash_alg) +
|
|
|
|
sizeof(pcr_selection.size_of_select) +
|
|
|
|
pcr_selection.size_of_select;
|
|
|
|
marker = marker + sizeof_pcr_selection;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (i < ARRAY_SIZE(chip->active_banks))
|
|
|
|
chip->active_banks[i] = TPM2_ALG_ERROR;
|
|
|
|
|
|
|
|
tpm_buf_destroy(&buf);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
2017-02-16 02:02:28 +08:00
|
|
|
|
2016-11-11 12:42:07 +08:00
|
|
|
static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
|
|
|
|
{
|
|
|
|
struct tpm_buf buf;
|
|
|
|
u32 nr_commands;
|
2017-09-14 00:58:49 +08:00
|
|
|
__be32 *attrs;
|
2016-11-11 12:42:07 +08:00
|
|
|
u32 cc;
|
|
|
|
int i;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL);
|
|
|
|
if (rc)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (nr_commands > 0xFFFFF) {
|
|
|
|
rc = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 05:07:58 +08:00
|
|
|
chip->cc_attrs_tbl = devm_kcalloc(&chip->dev, 4, nr_commands,
|
2016-11-11 12:42:07 +08:00
|
|
|
GFP_KERNEL);
|
|
|
|
|
|
|
|
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
|
|
|
|
if (rc)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
tpm_buf_append_u32(&buf, TPM2_CAP_COMMANDS);
|
|
|
|
tpm_buf_append_u32(&buf, TPM2_CC_FIRST);
|
|
|
|
tpm_buf_append_u32(&buf, nr_commands);
|
|
|
|
|
2017-01-06 20:03:45 +08:00
|
|
|
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE,
|
|
|
|
9 + 4 * nr_commands, 0, NULL);
|
2016-11-11 12:42:07 +08:00
|
|
|
if (rc) {
|
|
|
|
tpm_buf_destroy(&buf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nr_commands !=
|
|
|
|
be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5])) {
|
|
|
|
tpm_buf_destroy(&buf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
chip->nr_commands = nr_commands;
|
|
|
|
|
2017-09-14 00:58:49 +08:00
|
|
|
attrs = (__be32 *)&buf.data[TPM_HEADER_SIZE + 9];
|
2016-11-11 12:42:07 +08:00
|
|
|
for (i = 0; i < nr_commands; i++, attrs++) {
|
|
|
|
chip->cc_attrs_tbl[i] = be32_to_cpup(attrs);
|
|
|
|
cc = chip->cc_attrs_tbl[i] & 0xFFFF;
|
|
|
|
|
|
|
|
if (cc == TPM2_CC_CONTEXT_SAVE || cc == TPM2_CC_FLUSH_CONTEXT) {
|
|
|
|
chip->cc_attrs_tbl[i] &=
|
|
|
|
~(GENMASK(2, 0) << TPM2_CC_ATTR_CHANDLES);
|
|
|
|
chip->cc_attrs_tbl[i] |= 1 << TPM2_CC_ATTR_CHANDLES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tpm_buf_destroy(&buf);
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (rc > 0)
|
|
|
|
rc = -ENODEV;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2017-02-16 02:02:28 +08:00
|
|
|
/**
|
|
|
|
* tpm2_auto_startup - Perform the standard automatic TPM initialization
|
|
|
|
* sequence
|
|
|
|
* @chip: TPM chip to use
|
|
|
|
*
|
2016-11-11 12:42:07 +08:00
|
|
|
* Returns 0 on success, < 0 in case of fatal error.
|
2017-02-16 02:02:28 +08:00
|
|
|
*/
|
|
|
|
int tpm2_auto_startup(struct tpm_chip *chip)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = tpm_get_timeouts(chip);
|
|
|
|
if (rc)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
rc = tpm2_do_selftest(chip);
|
2018-03-22 23:32:20 +08:00
|
|
|
if (rc && rc != TPM2_RC_INITIALIZE)
|
2017-02-16 02:02:28 +08:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (rc == TPM2_RC_INITIALIZE) {
|
2017-06-21 15:31:34 +08:00
|
|
|
rc = tpm_startup(chip);
|
2017-02-16 02:02:28 +08:00
|
|
|
if (rc)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
rc = tpm2_do_selftest(chip);
|
2018-03-22 23:32:20 +08:00
|
|
|
if (rc)
|
2017-02-16 02:02:28 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = tpm2_get_pcr_allocation(chip);
|
2016-11-11 12:42:07 +08:00
|
|
|
if (rc)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
rc = tpm2_get_cc_attrs_tbl(chip);
|
2017-02-16 02:02:28 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
if (rc > 0)
|
|
|
|
rc = -ENODEV;
|
|
|
|
return rc;
|
|
|
|
}
|
2016-11-11 12:42:07 +08:00
|
|
|
|
|
|
|
int tpm2_find_cc(struct tpm_chip *chip, u32 cc)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < chip->nr_commands; i++)
|
|
|
|
if (cc == (chip->cc_attrs_tbl[i] & GENMASK(15, 0)))
|
|
|
|
return i;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|