tpmdd update for Linux v5.5
-----BEGIN PGP SIGNATURE----- iJYEABYIAD4WIQRE6pSOnaBC00OEHEIaerohdGur0gUCXcsMpCAcamFya2tvLnNh a2tpbmVuQGxpbnV4LmludGVsLmNvbQAKCRAaerohdGur0pTtAP9+6nxJGXpr8eEA PJgcKiRenRpTeY3xqAsHKRtw6WqpywEAzFUlZvDKEogE15SEVA03vzk/KuP8jM0q pq8mHOdAnwQ= =xCn9 -----END PGP SIGNATURE----- Merge tag 'tpmdd-next-20191112' of git://git.infradead.org/users/jjs/linux-tpmdd Pull tpmd updates from Jarkko Sakkinen: - support for Cr50 fTPM - support for fTPM on AMD Zen+ CPUs - TPM 2.0 trusted keys code relocated from drivers/char/tpm to security/keys * tag 'tpmdd-next-20191112' of git://git.infradead.org/users/jjs/linux-tpmdd: KEYS: trusted: Remove set but not used variable 'keyhndl' tpm: Switch to platform_get_irq_optional() tpm_crb: fix fTPM on AMD Zen+ CPUs KEYS: trusted: Move TPM2 trusted keys code KEYS: trusted: Create trusted keys subsystem KEYS: Use common tpm_buf for trusted and asymmetric keys tpm: Move tpm_buf code to include/linux/ tpm: use GFP_KERNEL instead of GFP_HIGHMEM for tpm_buf tpm: add check after commands attribs tab allocation tpm: tpm_tis_spi: Drop THIS_MODULE usage from driver struct tpm: tpm_tis_spi: Cleanup includes tpm: tpm_tis_spi: Support cr50 devices tpm: tpm_tis_spi: Introduce a flow control callback tpm: Add a flag to indicate TPM power is managed by firmware dt-bindings: tpm: document properties for cr50 tpm_tis: override durations for STM tpm with firmware 1.2.8.28 tpm: provide a way to override the chip returned durations tpm: Remove duplicate code from caps_show() in tpm-sysfs.c
This commit is contained in:
commit
54f0e54011
@ -0,0 +1,19 @@
|
||||
* H1 Secure Microcontroller with Cr50 Firmware on SPI Bus.
|
||||
|
||||
H1 Secure Microcontroller running Cr50 firmware provides several
|
||||
functions, including TPM-like functionality. It communicates over
|
||||
SPI using the FIFO protocol described in the PTP Spec, section 6.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "google,cr50".
|
||||
- spi-max-frequency: Maximum SPI frequency.
|
||||
|
||||
Example:
|
||||
|
||||
&spi0 {
|
||||
tpm@0 {
|
||||
compatible = "google,cr50";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <800000>;
|
||||
};
|
||||
};
|
@ -13,7 +13,7 @@
|
||||
#include <crypto/sha.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <keys/asymmetric-subtype.h>
|
||||
#include <keys/trusted.h>
|
||||
#include <keys/trusted_tpm.h>
|
||||
#include <crypto/asym_tpm_subtype.h>
|
||||
#include <crypto/public_key.h>
|
||||
|
||||
@ -21,10 +21,6 @@
|
||||
#define TPM_ORD_LOADKEY2 65
|
||||
#define TPM_ORD_UNBIND 30
|
||||
#define TPM_ORD_SIGN 60
|
||||
#define TPM_LOADKEY2_SIZE 59
|
||||
#define TPM_FLUSHSPECIFIC_SIZE 18
|
||||
#define TPM_UNBIND_SIZE 63
|
||||
#define TPM_SIGN_SIZE 63
|
||||
|
||||
#define TPM_RT_KEY 0x00000001
|
||||
|
||||
@ -68,16 +64,13 @@ static int tpm_loadkey2(struct tpm_buf *tb,
|
||||
return ret;
|
||||
|
||||
/* build the request buffer */
|
||||
INIT_BUF(tb);
|
||||
store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
|
||||
store32(tb, TPM_LOADKEY2_SIZE + keybloblen);
|
||||
store32(tb, TPM_ORD_LOADKEY2);
|
||||
store32(tb, keyhandle);
|
||||
storebytes(tb, keyblob, keybloblen);
|
||||
store32(tb, authhandle);
|
||||
storebytes(tb, nonceodd, TPM_NONCE_SIZE);
|
||||
store8(tb, cont);
|
||||
storebytes(tb, authdata, SHA1_DIGEST_SIZE);
|
||||
tpm_buf_reset(tb, TPM_TAG_RQU_AUTH1_COMMAND, TPM_ORD_LOADKEY2);
|
||||
tpm_buf_append_u32(tb, keyhandle);
|
||||
tpm_buf_append(tb, keyblob, keybloblen);
|
||||
tpm_buf_append_u32(tb, authhandle);
|
||||
tpm_buf_append(tb, nonceodd, TPM_NONCE_SIZE);
|
||||
tpm_buf_append_u8(tb, cont);
|
||||
tpm_buf_append(tb, authdata, SHA1_DIGEST_SIZE);
|
||||
|
||||
ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
|
||||
if (ret < 0) {
|
||||
@ -101,12 +94,9 @@ static int tpm_loadkey2(struct tpm_buf *tb,
|
||||
*/
|
||||
static int tpm_flushspecific(struct tpm_buf *tb, uint32_t handle)
|
||||
{
|
||||
INIT_BUF(tb);
|
||||
store16(tb, TPM_TAG_RQU_COMMAND);
|
||||
store32(tb, TPM_FLUSHSPECIFIC_SIZE);
|
||||
store32(tb, TPM_ORD_FLUSHSPECIFIC);
|
||||
store32(tb, handle);
|
||||
store32(tb, TPM_RT_KEY);
|
||||
tpm_buf_reset(tb, TPM_TAG_RQU_COMMAND, TPM_ORD_FLUSHSPECIFIC);
|
||||
tpm_buf_append_u32(tb, handle);
|
||||
tpm_buf_append_u32(tb, TPM_RT_KEY);
|
||||
|
||||
return trusted_tpm_send(tb->data, MAX_BUF_SIZE);
|
||||
}
|
||||
@ -155,17 +145,14 @@ static int tpm_unbind(struct tpm_buf *tb,
|
||||
return ret;
|
||||
|
||||
/* build the request buffer */
|
||||
INIT_BUF(tb);
|
||||
store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
|
||||
store32(tb, TPM_UNBIND_SIZE + bloblen);
|
||||
store32(tb, TPM_ORD_UNBIND);
|
||||
store32(tb, keyhandle);
|
||||
store32(tb, bloblen);
|
||||
storebytes(tb, blob, bloblen);
|
||||
store32(tb, authhandle);
|
||||
storebytes(tb, nonceodd, TPM_NONCE_SIZE);
|
||||
store8(tb, cont);
|
||||
storebytes(tb, authdata, SHA1_DIGEST_SIZE);
|
||||
tpm_buf_reset(tb, TPM_TAG_RQU_AUTH1_COMMAND, TPM_ORD_UNBIND);
|
||||
tpm_buf_append_u32(tb, keyhandle);
|
||||
tpm_buf_append_u32(tb, bloblen);
|
||||
tpm_buf_append(tb, blob, bloblen);
|
||||
tpm_buf_append_u32(tb, authhandle);
|
||||
tpm_buf_append(tb, nonceodd, TPM_NONCE_SIZE);
|
||||
tpm_buf_append_u8(tb, cont);
|
||||
tpm_buf_append(tb, authdata, SHA1_DIGEST_SIZE);
|
||||
|
||||
ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
|
||||
if (ret < 0) {
|
||||
@ -241,17 +228,14 @@ static int tpm_sign(struct tpm_buf *tb,
|
||||
return ret;
|
||||
|
||||
/* build the request buffer */
|
||||
INIT_BUF(tb);
|
||||
store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
|
||||
store32(tb, TPM_SIGN_SIZE + bloblen);
|
||||
store32(tb, TPM_ORD_SIGN);
|
||||
store32(tb, keyhandle);
|
||||
store32(tb, bloblen);
|
||||
storebytes(tb, blob, bloblen);
|
||||
store32(tb, authhandle);
|
||||
storebytes(tb, nonceodd, TPM_NONCE_SIZE);
|
||||
store8(tb, cont);
|
||||
storebytes(tb, authdata, SHA1_DIGEST_SIZE);
|
||||
tpm_buf_reset(tb, TPM_TAG_RQU_AUTH1_COMMAND, TPM_ORD_SIGN);
|
||||
tpm_buf_append_u32(tb, keyhandle);
|
||||
tpm_buf_append_u32(tb, bloblen);
|
||||
tpm_buf_append(tb, blob, bloblen);
|
||||
tpm_buf_append_u32(tb, authhandle);
|
||||
tpm_buf_append(tb, nonceodd, TPM_NONCE_SIZE);
|
||||
tpm_buf_append_u8(tb, cont);
|
||||
tpm_buf_append(tb, authdata, SHA1_DIGEST_SIZE);
|
||||
|
||||
ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
|
||||
if (ret < 0) {
|
||||
@ -519,7 +503,7 @@ static int tpm_key_decrypt(struct tpm_key *tk,
|
||||
struct kernel_pkey_params *params,
|
||||
const void *in, void *out)
|
||||
{
|
||||
struct tpm_buf *tb;
|
||||
struct tpm_buf tb;
|
||||
uint32_t keyhandle;
|
||||
uint8_t srkauth[SHA1_DIGEST_SIZE];
|
||||
uint8_t keyauth[SHA1_DIGEST_SIZE];
|
||||
@ -533,14 +517,14 @@ static int tpm_key_decrypt(struct tpm_key *tk,
|
||||
if (strcmp(params->encoding, "pkcs1"))
|
||||
return -ENOPKG;
|
||||
|
||||
tb = kzalloc(sizeof(*tb), GFP_KERNEL);
|
||||
if (!tb)
|
||||
return -ENOMEM;
|
||||
r = tpm_buf_init(&tb, 0, 0);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* TODO: Handle a non-all zero SRK authorization */
|
||||
memset(srkauth, 0, sizeof(srkauth));
|
||||
|
||||
r = tpm_loadkey2(tb, SRKHANDLE, srkauth,
|
||||
r = tpm_loadkey2(&tb, SRKHANDLE, srkauth,
|
||||
tk->blob, tk->blob_len, &keyhandle);
|
||||
if (r < 0) {
|
||||
pr_devel("loadkey2 failed (%d)\n", r);
|
||||
@ -550,16 +534,16 @@ static int tpm_key_decrypt(struct tpm_key *tk,
|
||||
/* TODO: Handle a non-all zero key authorization */
|
||||
memset(keyauth, 0, sizeof(keyauth));
|
||||
|
||||
r = tpm_unbind(tb, keyhandle, keyauth,
|
||||
r = tpm_unbind(&tb, keyhandle, keyauth,
|
||||
in, params->in_len, out, params->out_len);
|
||||
if (r < 0)
|
||||
pr_devel("tpm_unbind failed (%d)\n", r);
|
||||
|
||||
if (tpm_flushspecific(tb, keyhandle) < 0)
|
||||
if (tpm_flushspecific(&tb, keyhandle) < 0)
|
||||
pr_devel("flushspecific failed (%d)\n", r);
|
||||
|
||||
error:
|
||||
kzfree(tb);
|
||||
tpm_buf_destroy(&tb);
|
||||
pr_devel("<==%s() = %d\n", __func__, r);
|
||||
return r;
|
||||
}
|
||||
@ -643,7 +627,7 @@ static int tpm_key_sign(struct tpm_key *tk,
|
||||
struct kernel_pkey_params *params,
|
||||
const void *in, void *out)
|
||||
{
|
||||
struct tpm_buf *tb;
|
||||
struct tpm_buf tb;
|
||||
uint32_t keyhandle;
|
||||
uint8_t srkauth[SHA1_DIGEST_SIZE];
|
||||
uint8_t keyauth[SHA1_DIGEST_SIZE];
|
||||
@ -681,15 +665,14 @@ static int tpm_key_sign(struct tpm_key *tk,
|
||||
goto error_free_asn1_wrapped;
|
||||
}
|
||||
|
||||
r = -ENOMEM;
|
||||
tb = kzalloc(sizeof(*tb), GFP_KERNEL);
|
||||
if (!tb)
|
||||
r = tpm_buf_init(&tb, 0, 0);
|
||||
if (r)
|
||||
goto error_free_asn1_wrapped;
|
||||
|
||||
/* TODO: Handle a non-all zero SRK authorization */
|
||||
memset(srkauth, 0, sizeof(srkauth));
|
||||
|
||||
r = tpm_loadkey2(tb, SRKHANDLE, srkauth,
|
||||
r = tpm_loadkey2(&tb, SRKHANDLE, srkauth,
|
||||
tk->blob, tk->blob_len, &keyhandle);
|
||||
if (r < 0) {
|
||||
pr_devel("loadkey2 failed (%d)\n", r);
|
||||
@ -699,15 +682,15 @@ static int tpm_key_sign(struct tpm_key *tk,
|
||||
/* TODO: Handle a non-all zero key authorization */
|
||||
memset(keyauth, 0, sizeof(keyauth));
|
||||
|
||||
r = tpm_sign(tb, keyhandle, keyauth, in, in_len, out, params->out_len);
|
||||
r = tpm_sign(&tb, keyhandle, keyauth, in, in_len, out, params->out_len);
|
||||
if (r < 0)
|
||||
pr_devel("tpm_sign failed (%d)\n", r);
|
||||
|
||||
if (tpm_flushspecific(tb, keyhandle) < 0)
|
||||
if (tpm_flushspecific(&tb, keyhandle) < 0)
|
||||
pr_devel("flushspecific failed (%d)\n", r);
|
||||
|
||||
error_free_tb:
|
||||
kzfree(tb);
|
||||
tpm_buf_destroy(&tb);
|
||||
error_free_asn1_wrapped:
|
||||
kfree(asn1_wrapped);
|
||||
pr_devel("<==%s() = %d\n", __func__, r);
|
||||
|
@ -67,6 +67,13 @@ config TCG_TIS_SPI
|
||||
within Linux. To compile this driver as a module, choose M here;
|
||||
the module will be called tpm_tis_spi.
|
||||
|
||||
config TCG_TIS_SPI_CR50
|
||||
bool "Cr50 SPI Interface"
|
||||
depends on TCG_TIS_SPI
|
||||
help
|
||||
If you have a H1 secure module running Cr50 firmware on SPI bus,
|
||||
say Yes and it will be accessible from within Linux.
|
||||
|
||||
config TCG_TIS_I2C_ATMEL
|
||||
tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)"
|
||||
depends on I2C
|
||||
|
@ -21,7 +21,9 @@ tpm-$(CONFIG_EFI) += eventlog/efi.o
|
||||
tpm-$(CONFIG_OF) += eventlog/of.o
|
||||
obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o
|
||||
obj-$(CONFIG_TCG_TIS) += tpm_tis.o
|
||||
obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi.o
|
||||
obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi_mod.o
|
||||
tpm_tis_spi_mod-y := tpm_tis_spi.o
|
||||
tpm_tis_spi_mod-$(CONFIG_TCG_TIS_SPI_CR50) += tpm_tis_spi_cr50.o
|
||||
obj-$(CONFIG_TCG_TIS_I2C_ATMEL) += tpm_i2c_atmel.o
|
||||
obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
|
||||
obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/tpm_eventlog.h>
|
||||
|
||||
@ -394,7 +395,11 @@ int tpm_pm_suspend(struct device *dev)
|
||||
return -ENODEV;
|
||||
|
||||
if (chip->flags & TPM_CHIP_FLAG_ALWAYS_POWERED)
|
||||
return 0;
|
||||
goto suspended;
|
||||
|
||||
if ((chip->flags & TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED) &&
|
||||
!pm_suspend_via_firmware())
|
||||
goto suspended;
|
||||
|
||||
if (!tpm_chip_start(chip)) {
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2)
|
||||
@ -405,6 +410,7 @@ int tpm_pm_suspend(struct device *dev)
|
||||
tpm_chip_stop(chip);
|
||||
}
|
||||
|
||||
suspended:
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_pm_suspend);
|
||||
@ -453,62 +459,6 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_get_random);
|
||||
|
||||
/**
|
||||
* tpm_seal_trusted() - seal a trusted key payload
|
||||
* @chip: a &struct tpm_chip instance, %NULL for the default chip
|
||||
* @options: authentication values and other options
|
||||
* @payload: the key data in clear and encrypted form
|
||||
*
|
||||
* Note: only TPM 2.0 chip are supported. TPM 1.x implementation is located in
|
||||
* the keyring subsystem.
|
||||
*
|
||||
* Return: same as with tpm_transmit_cmd()
|
||||
*/
|
||||
int tpm_seal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options)
|
||||
{
|
||||
int rc;
|
||||
|
||||
chip = tpm_find_get_ops(chip);
|
||||
if (!chip || !(chip->flags & TPM_CHIP_FLAG_TPM2))
|
||||
return -ENODEV;
|
||||
|
||||
rc = tpm2_seal_trusted(chip, payload, options);
|
||||
|
||||
tpm_put_ops(chip);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_seal_trusted);
|
||||
|
||||
/**
|
||||
* tpm_unseal_trusted() - unseal a trusted key
|
||||
* @chip: a &struct tpm_chip instance, %NULL for the default chip
|
||||
* @options: authentication values and other options
|
||||
* @payload: the key data in clear and encrypted form
|
||||
*
|
||||
* Note: only TPM 2.0 chip are supported. TPM 1.x implementation is located in
|
||||
* the keyring subsystem.
|
||||
*
|
||||
* Return: same as with tpm_transmit_cmd()
|
||||
*/
|
||||
int tpm_unseal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options)
|
||||
{
|
||||
int rc;
|
||||
|
||||
chip = tpm_find_get_ops(chip);
|
||||
if (!chip || !(chip->flags & TPM_CHIP_FLAG_TPM2))
|
||||
return -ENODEV;
|
||||
|
||||
rc = tpm2_unseal_trusted(chip, payload, options);
|
||||
|
||||
tpm_put_ops(chip);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_unseal_trusted);
|
||||
|
||||
static int __init tpm_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
@ -217,6 +217,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct tpm_chip *chip = to_tpm_chip(dev);
|
||||
struct tpm1_version *version;
|
||||
ssize_t rc = 0;
|
||||
char *str = buf;
|
||||
cap_t cap;
|
||||
@ -232,31 +233,31 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
|
||||
str += sprintf(str, "Manufacturer: 0x%x\n",
|
||||
be32_to_cpu(cap.manufacturer_id));
|
||||
|
||||
/* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */
|
||||
rc = tpm1_getcap(chip, TPM_CAP_VERSION_1_2, &cap,
|
||||
/* TPM 1.2 */
|
||||
if (!tpm1_getcap(chip, TPM_CAP_VERSION_1_2, &cap,
|
||||
"attempting to determine the 1.2 version",
|
||||
sizeof(cap.tpm_version_1_2));
|
||||
if (!rc) {
|
||||
str += sprintf(str,
|
||||
"TCG version: %d.%d\nFirmware version: %d.%d\n",
|
||||
cap.tpm_version_1_2.Major,
|
||||
cap.tpm_version_1_2.Minor,
|
||||
cap.tpm_version_1_2.revMajor,
|
||||
cap.tpm_version_1_2.revMinor);
|
||||
} else {
|
||||
/* Otherwise just use TPM_STRUCT_VER */
|
||||
if (tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap,
|
||||
"attempting to determine the 1.1 version",
|
||||
sizeof(cap.tpm_version)))
|
||||
goto out_ops;
|
||||
str += sprintf(str,
|
||||
"TCG version: %d.%d\nFirmware version: %d.%d\n",
|
||||
cap.tpm_version.Major,
|
||||
cap.tpm_version.Minor,
|
||||
cap.tpm_version.revMajor,
|
||||
cap.tpm_version.revMinor);
|
||||
sizeof(cap.version2))) {
|
||||
version = &cap.version2.version;
|
||||
goto out_print;
|
||||
}
|
||||
|
||||
/* TPM 1.1 */
|
||||
if (tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap,
|
||||
"attempting to determine the 1.1 version",
|
||||
sizeof(cap.version1))) {
|
||||
goto out_ops;
|
||||
}
|
||||
|
||||
version = &cap.version1;
|
||||
|
||||
out_print:
|
||||
str += sprintf(str,
|
||||
"TCG version: %d.%d\nFirmware version: %d.%d\n",
|
||||
version->major, version->minor,
|
||||
version->rev_major, version->rev_minor);
|
||||
|
||||
rc = str - buf;
|
||||
|
||||
out_ops:
|
||||
tpm_put_ops(chip);
|
||||
return rc;
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/tpm.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/tpm_eventlog.h>
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
@ -58,123 +57,6 @@ enum tpm_addr {
|
||||
#define TPM_ERR_DISABLED 0x7
|
||||
#define TPM_ERR_INVALID_POSTINIT 38
|
||||
|
||||
#define TPM_HEADER_SIZE 10
|
||||
|
||||
enum tpm2_const {
|
||||
TPM2_PLATFORM_PCR = 24,
|
||||
TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8),
|
||||
};
|
||||
|
||||
enum tpm2_timeouts {
|
||||
TPM2_TIMEOUT_A = 750,
|
||||
TPM2_TIMEOUT_B = 2000,
|
||||
TPM2_TIMEOUT_C = 200,
|
||||
TPM2_TIMEOUT_D = 30,
|
||||
TPM2_DURATION_SHORT = 20,
|
||||
TPM2_DURATION_MEDIUM = 750,
|
||||
TPM2_DURATION_LONG = 2000,
|
||||
TPM2_DURATION_LONG_LONG = 300000,
|
||||
TPM2_DURATION_DEFAULT = 120000,
|
||||
};
|
||||
|
||||
enum tpm2_structures {
|
||||
TPM2_ST_NO_SESSIONS = 0x8001,
|
||||
TPM2_ST_SESSIONS = 0x8002,
|
||||
};
|
||||
|
||||
/* Indicates from what layer of the software stack the error comes from */
|
||||
#define TSS2_RC_LAYER_SHIFT 16
|
||||
#define TSS2_RESMGR_TPM_RC_LAYER (11 << TSS2_RC_LAYER_SHIFT)
|
||||
|
||||
enum tpm2_return_codes {
|
||||
TPM2_RC_SUCCESS = 0x0000,
|
||||
TPM2_RC_HASH = 0x0083, /* RC_FMT1 */
|
||||
TPM2_RC_HANDLE = 0x008B,
|
||||
TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */
|
||||
TPM2_RC_FAILURE = 0x0101,
|
||||
TPM2_RC_DISABLED = 0x0120,
|
||||
TPM2_RC_COMMAND_CODE = 0x0143,
|
||||
TPM2_RC_TESTING = 0x090A, /* RC_WARN */
|
||||
TPM2_RC_REFERENCE_H0 = 0x0910,
|
||||
TPM2_RC_RETRY = 0x0922,
|
||||
};
|
||||
|
||||
enum tpm2_command_codes {
|
||||
TPM2_CC_FIRST = 0x011F,
|
||||
TPM2_CC_HIERARCHY_CONTROL = 0x0121,
|
||||
TPM2_CC_HIERARCHY_CHANGE_AUTH = 0x0129,
|
||||
TPM2_CC_CREATE_PRIMARY = 0x0131,
|
||||
TPM2_CC_SEQUENCE_COMPLETE = 0x013E,
|
||||
TPM2_CC_SELF_TEST = 0x0143,
|
||||
TPM2_CC_STARTUP = 0x0144,
|
||||
TPM2_CC_SHUTDOWN = 0x0145,
|
||||
TPM2_CC_NV_READ = 0x014E,
|
||||
TPM2_CC_CREATE = 0x0153,
|
||||
TPM2_CC_LOAD = 0x0157,
|
||||
TPM2_CC_SEQUENCE_UPDATE = 0x015C,
|
||||
TPM2_CC_UNSEAL = 0x015E,
|
||||
TPM2_CC_CONTEXT_LOAD = 0x0161,
|
||||
TPM2_CC_CONTEXT_SAVE = 0x0162,
|
||||
TPM2_CC_FLUSH_CONTEXT = 0x0165,
|
||||
TPM2_CC_VERIFY_SIGNATURE = 0x0177,
|
||||
TPM2_CC_GET_CAPABILITY = 0x017A,
|
||||
TPM2_CC_GET_RANDOM = 0x017B,
|
||||
TPM2_CC_PCR_READ = 0x017E,
|
||||
TPM2_CC_PCR_EXTEND = 0x0182,
|
||||
TPM2_CC_EVENT_SEQUENCE_COMPLETE = 0x0185,
|
||||
TPM2_CC_HASH_SEQUENCE_START = 0x0186,
|
||||
TPM2_CC_CREATE_LOADED = 0x0191,
|
||||
TPM2_CC_LAST = 0x0193, /* Spec 1.36 */
|
||||
};
|
||||
|
||||
enum tpm2_permanent_handles {
|
||||
TPM2_RS_PW = 0x40000009,
|
||||
};
|
||||
|
||||
enum tpm2_capabilities {
|
||||
TPM2_CAP_HANDLES = 1,
|
||||
TPM2_CAP_COMMANDS = 2,
|
||||
TPM2_CAP_PCRS = 5,
|
||||
TPM2_CAP_TPM_PROPERTIES = 6,
|
||||
};
|
||||
|
||||
enum tpm2_properties {
|
||||
TPM_PT_TOTAL_COMMANDS = 0x0129,
|
||||
};
|
||||
|
||||
enum tpm2_startup_types {
|
||||
TPM2_SU_CLEAR = 0x0000,
|
||||
TPM2_SU_STATE = 0x0001,
|
||||
};
|
||||
|
||||
enum tpm2_cc_attrs {
|
||||
TPM2_CC_ATTR_CHANDLES = 25,
|
||||
TPM2_CC_ATTR_RHANDLE = 28,
|
||||
};
|
||||
|
||||
#define TPM_VID_INTEL 0x8086
|
||||
#define TPM_VID_WINBOND 0x1050
|
||||
#define TPM_VID_STM 0x104A
|
||||
|
||||
enum tpm_chip_flags {
|
||||
TPM_CHIP_FLAG_TPM2 = BIT(1),
|
||||
TPM_CHIP_FLAG_IRQ = BIT(2),
|
||||
TPM_CHIP_FLAG_VIRTUAL = BIT(3),
|
||||
TPM_CHIP_FLAG_HAVE_TIMEOUTS = BIT(4),
|
||||
TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5),
|
||||
};
|
||||
|
||||
#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
|
||||
|
||||
struct tpm_header {
|
||||
__be16 tag;
|
||||
__be32 length;
|
||||
union {
|
||||
__be32 ordinal;
|
||||
__be32 return_code;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
#define TPM_TAG_RQU_COMMAND 193
|
||||
|
||||
struct stclear_flags_t {
|
||||
@ -186,19 +68,16 @@ struct stclear_flags_t {
|
||||
u8 bGlobalLock;
|
||||
} __packed;
|
||||
|
||||
struct tpm_version_t {
|
||||
u8 Major;
|
||||
u8 Minor;
|
||||
u8 revMajor;
|
||||
u8 revMinor;
|
||||
struct tpm1_version {
|
||||
u8 major;
|
||||
u8 minor;
|
||||
u8 rev_major;
|
||||
u8 rev_minor;
|
||||
} __packed;
|
||||
|
||||
struct tpm_version_1_2_t {
|
||||
__be16 tag;
|
||||
u8 Major;
|
||||
u8 Minor;
|
||||
u8 revMajor;
|
||||
u8 revMinor;
|
||||
struct tpm1_version2 {
|
||||
__be16 tag;
|
||||
struct tpm1_version version;
|
||||
} __packed;
|
||||
|
||||
struct timeout_t {
|
||||
@ -243,8 +122,8 @@ typedef union {
|
||||
struct stclear_flags_t stclear_flags;
|
||||
__u8 owned;
|
||||
__be32 num_pcrs;
|
||||
struct tpm_version_t tpm_version;
|
||||
struct tpm_version_1_2_t tpm_version_1_2;
|
||||
struct tpm1_version version1;
|
||||
struct tpm1_version2 version2;
|
||||
__be32 manufacturer_id;
|
||||
struct timeout_t timeout;
|
||||
struct duration_t duration;
|
||||
@ -274,102 +153,6 @@ enum tpm_sub_capabilities {
|
||||
* compiler warnings about stack frame size. */
|
||||
#define TPM_MAX_RNG_DATA 128
|
||||
|
||||
/* A string buffer type for constructing TPM commands. This is based on the
|
||||
* ideas of string buffer code in security/keys/trusted.h but is heap based
|
||||
* in order to keep the stack usage minimal.
|
||||
*/
|
||||
|
||||
enum tpm_buf_flags {
|
||||
TPM_BUF_OVERFLOW = BIT(0),
|
||||
};
|
||||
|
||||
struct tpm_buf {
|
||||
struct page *data_page;
|
||||
unsigned int flags;
|
||||
u8 *data;
|
||||
};
|
||||
|
||||
static inline void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
|
||||
{
|
||||
struct tpm_header *head = (struct tpm_header *)buf->data;
|
||||
|
||||
head->tag = cpu_to_be16(tag);
|
||||
head->length = cpu_to_be32(sizeof(*head));
|
||||
head->ordinal = cpu_to_be32(ordinal);
|
||||
}
|
||||
|
||||
static inline int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
|
||||
{
|
||||
buf->data_page = alloc_page(GFP_HIGHUSER);
|
||||
if (!buf->data_page)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->flags = 0;
|
||||
buf->data = kmap(buf->data_page);
|
||||
tpm_buf_reset(buf, tag, ordinal);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void tpm_buf_destroy(struct tpm_buf *buf)
|
||||
{
|
||||
kunmap(buf->data_page);
|
||||
__free_page(buf->data_page);
|
||||
}
|
||||
|
||||
static inline u32 tpm_buf_length(struct tpm_buf *buf)
|
||||
{
|
||||
struct tpm_header *head = (struct tpm_header *)buf->data;
|
||||
|
||||
return be32_to_cpu(head->length);
|
||||
}
|
||||
|
||||
static inline u16 tpm_buf_tag(struct tpm_buf *buf)
|
||||
{
|
||||
struct tpm_header *head = (struct tpm_header *)buf->data;
|
||||
|
||||
return be16_to_cpu(head->tag);
|
||||
}
|
||||
|
||||
static inline void tpm_buf_append(struct tpm_buf *buf,
|
||||
const unsigned char *new_data,
|
||||
unsigned int new_len)
|
||||
{
|
||||
struct tpm_header *head = (struct tpm_header *)buf->data;
|
||||
u32 len = tpm_buf_length(buf);
|
||||
|
||||
/* Return silently if overflow has already happened. */
|
||||
if (buf->flags & TPM_BUF_OVERFLOW)
|
||||
return;
|
||||
|
||||
if ((len + new_len) > PAGE_SIZE) {
|
||||
WARN(1, "tpm_buf: overflow\n");
|
||||
buf->flags |= TPM_BUF_OVERFLOW;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&buf->data[len], new_data, new_len);
|
||||
head->length = cpu_to_be32(len + new_len);
|
||||
}
|
||||
|
||||
static inline void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value)
|
||||
{
|
||||
tpm_buf_append(buf, &value, 1);
|
||||
}
|
||||
|
||||
static inline void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value)
|
||||
{
|
||||
__be16 value2 = cpu_to_be16(value);
|
||||
|
||||
tpm_buf_append(buf, (u8 *) &value2, 2);
|
||||
}
|
||||
|
||||
static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
|
||||
{
|
||||
__be32 value2 = cpu_to_be32(value);
|
||||
|
||||
tpm_buf_append(buf, (u8 *) &value2, 4);
|
||||
}
|
||||
|
||||
extern struct class *tpm_class;
|
||||
extern struct class *tpmrm_class;
|
||||
extern dev_t tpm_devt;
|
||||
@ -429,11 +212,6 @@ static inline void tpm_add_ppi(struct tpm_chip *chip)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline u32 tpm2_rc_value(u32 rc)
|
||||
{
|
||||
return (rc & BIT(7)) ? rc & 0xff : rc;
|
||||
}
|
||||
|
||||
int tpm2_get_timeouts(struct tpm_chip *chip);
|
||||
int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
|
||||
struct tpm_digest *digest, u16 *digest_size_ptr);
|
||||
@ -441,12 +219,6 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
|
||||
struct tpm_digest *digests);
|
||||
int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max);
|
||||
void tpm2_flush_context(struct tpm_chip *chip, u32 handle);
|
||||
int tpm2_seal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options);
|
||||
int tpm2_unseal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options);
|
||||
ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
|
||||
u32 *value, const char *desc);
|
||||
|
||||
|
@ -343,6 +343,7 @@ int tpm1_get_timeouts(struct tpm_chip *chip)
|
||||
{
|
||||
cap_t cap;
|
||||
unsigned long timeout_old[4], timeout_chip[4], timeout_eff[4];
|
||||
unsigned long durations[3];
|
||||
ssize_t rc;
|
||||
|
||||
rc = tpm1_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, NULL,
|
||||
@ -427,6 +428,20 @@ int tpm1_get_timeouts(struct tpm_chip *chip)
|
||||
usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
|
||||
chip->duration[TPM_LONG_LONG] = 0; /* not used under 1.2 */
|
||||
|
||||
/*
|
||||
* Provide the ability for vendor overrides of duration values in case
|
||||
* of misreporting.
|
||||
*/
|
||||
if (chip->ops->update_durations)
|
||||
chip->ops->update_durations(chip, durations);
|
||||
|
||||
if (chip->duration_adjusted) {
|
||||
dev_info(&chip->dev, HW_ERR "Adjusting reported durations.");
|
||||
chip->duration[TPM_SHORT] = durations[0];
|
||||
chip->duration[TPM_MEDIUM] = durations[1];
|
||||
chip->duration[TPM_LONG] = durations[2];
|
||||
}
|
||||
|
||||
/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
|
||||
* value wrong and apparently reports msecs rather than usecs. So we
|
||||
* fix up the resulting too-small TPM_SHORT value to make things work.
|
||||
|
@ -13,20 +13,6 @@
|
||||
|
||||
#include "tpm.h"
|
||||
#include <crypto/hash_info.h>
|
||||
#include <keys/trusted-type.h>
|
||||
|
||||
enum tpm2_object_attributes {
|
||||
TPM2_OA_USER_WITH_AUTH = BIT(6),
|
||||
};
|
||||
|
||||
enum tpm2_session_attributes {
|
||||
TPM2_SA_CONTINUE_SESSION = BIT(0),
|
||||
};
|
||||
|
||||
struct tpm2_hash {
|
||||
unsigned int crypto_id;
|
||||
unsigned int tpm_id;
|
||||
};
|
||||
|
||||
static struct tpm2_hash tpm2_hash_map[] = {
|
||||
{HASH_ALGO_SHA1, TPM_ALG_SHA1},
|
||||
@ -377,299 +363,6 @@ void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
|
||||
tpm_buf_destroy(&buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_seal_trusted() - seal the payload of a trusted key
|
||||
*
|
||||
* @chip: TPM chip to use
|
||||
* @payload: the key data in clear and encrypted form
|
||||
* @options: authentication values and other options
|
||||
*
|
||||
* Return: < 0 on error and 0 on success.
|
||||
*/
|
||||
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;
|
||||
u32 hash;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
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;
|
||||
|
||||
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 */
|
||||
tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len + 1);
|
||||
|
||||
tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE);
|
||||
tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE);
|
||||
tpm_buf_append_u16(&buf, payload->key_len + 1);
|
||||
tpm_buf_append(&buf, payload->key, payload->key_len);
|
||||
tpm_buf_append_u8(&buf, payload->migratable);
|
||||
|
||||
/* public */
|
||||
tpm_buf_append_u16(&buf, 14 + options->policydigest_len);
|
||||
tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH);
|
||||
tpm_buf_append_u16(&buf, hash);
|
||||
|
||||
/* policy */
|
||||
if (options->policydigest_len) {
|
||||
tpm_buf_append_u32(&buf, 0);
|
||||
tpm_buf_append_u16(&buf, options->policydigest_len);
|
||||
tpm_buf_append(&buf, options->policydigest,
|
||||
options->policydigest_len);
|
||||
} else {
|
||||
tpm_buf_append_u32(&buf, TPM2_OA_USER_WITH_AUTH);
|
||||
tpm_buf_append_u16(&buf, 0);
|
||||
}
|
||||
|
||||
/* public parameters */
|
||||
tpm_buf_append_u16(&buf, TPM_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;
|
||||
}
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
|
||||
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;
|
||||
}
|
||||
if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len);
|
||||
payload->blob_len = blob_len;
|
||||
|
||||
out:
|
||||
tpm_buf_destroy(&buf);
|
||||
|
||||
if (rc > 0) {
|
||||
if (tpm2_rc_value(rc) == TPM2_RC_HASH)
|
||||
rc = -EINVAL;
|
||||
else
|
||||
rc = -EPERM;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_load_cmd() - execute a TPM2_Load command
|
||||
*
|
||||
* @chip: TPM chip to use
|
||||
* @payload: the key data in clear and encrypted form
|
||||
* @options: authentication values and other options
|
||||
* @blob_handle: returned blob handle
|
||||
*
|
||||
* Return: 0 on success.
|
||||
* -E2BIG on wrong payload size.
|
||||
* -EPERM on tpm error status.
|
||||
* < 0 error from tpm_transmit_cmd.
|
||||
*/
|
||||
static int tpm2_load_cmd(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options,
|
||||
u32 *blob_handle)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob");
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_unseal_cmd() - execute a TPM2_Unload command
|
||||
*
|
||||
* @chip: TPM chip to use
|
||||
* @payload: the key data in clear and encrypted form
|
||||
* @options: authentication values and other options
|
||||
* @blob_handle: blob handle
|
||||
*
|
||||
* Return: 0 on success
|
||||
* -EPERM on tpm error status
|
||||
* < 0 error from tpm_transmit_cmd
|
||||
*/
|
||||
static int tpm2_unseal_cmd(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options,
|
||||
u32 blob_handle)
|
||||
{
|
||||
struct tpm_buf buf;
|
||||
u16 data_len;
|
||||
u8 *data;
|
||||
int rc;
|
||||
|
||||
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
tpm_buf_append_u32(&buf, blob_handle);
|
||||
tpm2_buf_append_auth(&buf,
|
||||
options->policyhandle ?
|
||||
options->policyhandle : TPM2_RS_PW,
|
||||
NULL /* nonce */, 0,
|
||||
TPM2_SA_CONTINUE_SESSION,
|
||||
options->blobauth /* hmac */,
|
||||
TPM_DIGEST_SIZE);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
|
||||
if (rc > 0)
|
||||
rc = -EPERM;
|
||||
|
||||
if (!rc) {
|
||||
data_len = be16_to_cpup(
|
||||
(__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
|
||||
if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE + 1) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 6 + data_len) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
data = &buf.data[TPM_HEADER_SIZE + 6];
|
||||
|
||||
memcpy(payload->key, data, data_len - 1);
|
||||
payload->key_len = data_len - 1;
|
||||
payload->migratable = data[data_len - 1];
|
||||
}
|
||||
|
||||
out:
|
||||
tpm_buf_destroy(&buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_unseal_trusted() - unseal the payload of a trusted key
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
int tpm2_unseal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options)
|
||||
{
|
||||
u32 blob_handle;
|
||||
int rc;
|
||||
|
||||
rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
|
||||
tpm2_flush_context(chip, blob_handle);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct tpm2_get_cap_out {
|
||||
u8 more_data;
|
||||
__be32 subcap_id;
|
||||
@ -939,6 +632,10 @@ static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
|
||||
|
||||
chip->cc_attrs_tbl = devm_kcalloc(&chip->dev, 4, nr_commands,
|
||||
GFP_KERNEL);
|
||||
if (!chip->cc_attrs_tbl) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
|
||||
if (rc)
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "tpm.h"
|
||||
|
||||
#define ACPI_SIG_TPM2 "TPM2"
|
||||
#define TPM_CRB_MAX_RESOURCES 3
|
||||
|
||||
static const guid_t crb_acpi_start_guid =
|
||||
GUID_INIT(0x6BBF6CAB, 0x5463, 0x4714,
|
||||
@ -91,7 +92,6 @@ enum crb_status {
|
||||
struct crb_priv {
|
||||
u32 sm;
|
||||
const char *hid;
|
||||
void __iomem *iobase;
|
||||
struct crb_regs_head __iomem *regs_h;
|
||||
struct crb_regs_tail __iomem *regs_t;
|
||||
u8 __iomem *cmd;
|
||||
@ -434,21 +434,27 @@ static const struct tpm_class_ops tpm_crb = {
|
||||
|
||||
static int crb_check_resource(struct acpi_resource *ares, void *data)
|
||||
{
|
||||
struct resource *io_res = data;
|
||||
struct resource *iores_array = data;
|
||||
struct resource_win win;
|
||||
struct resource *res = &(win.res);
|
||||
int i;
|
||||
|
||||
if (acpi_dev_resource_memory(ares, res) ||
|
||||
acpi_dev_resource_address_space(ares, &win)) {
|
||||
*io_res = *res;
|
||||
io_res->name = NULL;
|
||||
for (i = 0; i < TPM_CRB_MAX_RESOURCES + 1; ++i) {
|
||||
if (resource_type(iores_array + i) != IORESOURCE_MEM) {
|
||||
iores_array[i] = *res;
|
||||
iores_array[i].name = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv,
|
||||
struct resource *io_res, u64 start, u32 size)
|
||||
static void __iomem *crb_map_res(struct device *dev, struct resource *iores,
|
||||
void __iomem **iobase_ptr, u64 start, u32 size)
|
||||
{
|
||||
struct resource new_res = {
|
||||
.start = start,
|
||||
@ -460,10 +466,16 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv,
|
||||
if (start != new_res.start)
|
||||
return (void __iomem *) ERR_PTR(-EINVAL);
|
||||
|
||||
if (!resource_contains(io_res, &new_res))
|
||||
if (!iores)
|
||||
return devm_ioremap_resource(dev, &new_res);
|
||||
|
||||
return priv->iobase + (new_res.start - io_res->start);
|
||||
if (!*iobase_ptr) {
|
||||
*iobase_ptr = devm_ioremap_resource(dev, iores);
|
||||
if (IS_ERR(*iobase_ptr))
|
||||
return *iobase_ptr;
|
||||
}
|
||||
|
||||
return *iobase_ptr + (new_res.start - iores->start);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -490,9 +502,13 @@ static u64 crb_fixup_cmd_size(struct device *dev, struct resource *io_res,
|
||||
static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
struct acpi_table_tpm2 *buf)
|
||||
{
|
||||
struct list_head resources;
|
||||
struct resource io_res;
|
||||
struct list_head acpi_resource_list;
|
||||
struct resource iores_array[TPM_CRB_MAX_RESOURCES + 1] = { {0} };
|
||||
void __iomem *iobase_array[TPM_CRB_MAX_RESOURCES] = {NULL};
|
||||
struct device *dev = &device->dev;
|
||||
struct resource *iores;
|
||||
void __iomem **iobase_ptr;
|
||||
int i;
|
||||
u32 pa_high, pa_low;
|
||||
u64 cmd_pa;
|
||||
u32 cmd_size;
|
||||
@ -501,21 +517,41 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
u32 rsp_size;
|
||||
int ret;
|
||||
|
||||
INIT_LIST_HEAD(&resources);
|
||||
ret = acpi_dev_get_resources(device, &resources, crb_check_resource,
|
||||
&io_res);
|
||||
INIT_LIST_HEAD(&acpi_resource_list);
|
||||
ret = acpi_dev_get_resources(device, &acpi_resource_list,
|
||||
crb_check_resource, iores_array);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
acpi_dev_free_resource_list(&resources);
|
||||
acpi_dev_free_resource_list(&acpi_resource_list);
|
||||
|
||||
if (resource_type(&io_res) != IORESOURCE_MEM) {
|
||||
if (resource_type(iores_array) != IORESOURCE_MEM) {
|
||||
dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n");
|
||||
return -EINVAL;
|
||||
} else if (resource_type(iores_array + TPM_CRB_MAX_RESOURCES) ==
|
||||
IORESOURCE_MEM) {
|
||||
dev_warn(dev, "TPM2 ACPI table defines too many memory resources\n");
|
||||
memset(iores_array + TPM_CRB_MAX_RESOURCES,
|
||||
0, sizeof(*iores_array));
|
||||
iores_array[TPM_CRB_MAX_RESOURCES].flags = 0;
|
||||
}
|
||||
|
||||
priv->iobase = devm_ioremap_resource(dev, &io_res);
|
||||
if (IS_ERR(priv->iobase))
|
||||
return PTR_ERR(priv->iobase);
|
||||
iores = NULL;
|
||||
iobase_ptr = NULL;
|
||||
for (i = 0; resource_type(iores_array + i) == IORESOURCE_MEM; ++i) {
|
||||
if (buf->control_address >= iores_array[i].start &&
|
||||
buf->control_address + sizeof(struct crb_regs_tail) - 1 <=
|
||||
iores_array[i].end) {
|
||||
iores = iores_array + i;
|
||||
iobase_ptr = iobase_array + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
priv->regs_t = crb_map_res(dev, iores, iobase_ptr, buf->control_address,
|
||||
sizeof(struct crb_regs_tail));
|
||||
|
||||
if (IS_ERR(priv->regs_t))
|
||||
return PTR_ERR(priv->regs_t);
|
||||
|
||||
/* The ACPI IO region starts at the head area and continues to include
|
||||
* the control area, as one nice sane region except for some older
|
||||
@ -523,9 +559,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
*/
|
||||
if ((priv->sm == ACPI_TPM2_COMMAND_BUFFER) ||
|
||||
(priv->sm == ACPI_TPM2_MEMORY_MAPPED)) {
|
||||
if (buf->control_address == io_res.start +
|
||||
if (iores &&
|
||||
buf->control_address == iores->start +
|
||||
sizeof(*priv->regs_h))
|
||||
priv->regs_h = priv->iobase;
|
||||
priv->regs_h = *iobase_ptr;
|
||||
else
|
||||
dev_warn(dev, FW_BUG "Bad ACPI memory layout");
|
||||
}
|
||||
@ -534,13 +571,6 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->regs_t = crb_map_res(dev, priv, &io_res, buf->control_address,
|
||||
sizeof(struct crb_regs_tail));
|
||||
if (IS_ERR(priv->regs_t)) {
|
||||
ret = PTR_ERR(priv->regs_t);
|
||||
goto out_relinquish_locality;
|
||||
}
|
||||
|
||||
/*
|
||||
* PTT HW bug w/a: wake up the device to access
|
||||
* possibly not retained registers.
|
||||
@ -552,13 +582,26 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
pa_high = ioread32(&priv->regs_t->ctrl_cmd_pa_high);
|
||||
pa_low = ioread32(&priv->regs_t->ctrl_cmd_pa_low);
|
||||
cmd_pa = ((u64)pa_high << 32) | pa_low;
|
||||
cmd_size = crb_fixup_cmd_size(dev, &io_res, cmd_pa,
|
||||
ioread32(&priv->regs_t->ctrl_cmd_size));
|
||||
cmd_size = ioread32(&priv->regs_t->ctrl_cmd_size);
|
||||
|
||||
iores = NULL;
|
||||
iobase_ptr = NULL;
|
||||
for (i = 0; iores_array[i].end; ++i) {
|
||||
if (cmd_pa >= iores_array[i].start &&
|
||||
cmd_pa <= iores_array[i].end) {
|
||||
iores = iores_array + i;
|
||||
iobase_ptr = iobase_array + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (iores)
|
||||
cmd_size = crb_fixup_cmd_size(dev, iores, cmd_pa, cmd_size);
|
||||
|
||||
dev_dbg(dev, "cmd_hi = %X cmd_low = %X cmd_size %X\n",
|
||||
pa_high, pa_low, cmd_size);
|
||||
|
||||
priv->cmd = crb_map_res(dev, priv, &io_res, cmd_pa, cmd_size);
|
||||
priv->cmd = crb_map_res(dev, iores, iobase_ptr, cmd_pa, cmd_size);
|
||||
if (IS_ERR(priv->cmd)) {
|
||||
ret = PTR_ERR(priv->cmd);
|
||||
goto out;
|
||||
@ -566,11 +609,25 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
|
||||
memcpy_fromio(&__rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8);
|
||||
rsp_pa = le64_to_cpu(__rsp_pa);
|
||||
rsp_size = crb_fixup_cmd_size(dev, &io_res, rsp_pa,
|
||||
ioread32(&priv->regs_t->ctrl_rsp_size));
|
||||
rsp_size = ioread32(&priv->regs_t->ctrl_rsp_size);
|
||||
|
||||
iores = NULL;
|
||||
iobase_ptr = NULL;
|
||||
for (i = 0; resource_type(iores_array + i) == IORESOURCE_MEM; ++i) {
|
||||
if (rsp_pa >= iores_array[i].start &&
|
||||
rsp_pa <= iores_array[i].end) {
|
||||
iores = iores_array + i;
|
||||
iobase_ptr = iobase_array + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (iores)
|
||||
rsp_size = crb_fixup_cmd_size(dev, iores, rsp_pa, rsp_size);
|
||||
|
||||
if (cmd_pa != rsp_pa) {
|
||||
priv->rsp = crb_map_res(dev, priv, &io_res, rsp_pa, rsp_size);
|
||||
priv->rsp = crb_map_res(dev, iores, iobase_ptr,
|
||||
rsp_pa, rsp_size);
|
||||
ret = PTR_ERR_OR_ZERO(priv->rsp);
|
||||
goto out;
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ static int tpm_tis_plat_probe(struct platform_device *pdev)
|
||||
}
|
||||
tpm_info.res = *res;
|
||||
|
||||
tpm_info.irq = platform_get_irq(pdev, 0);
|
||||
tpm_info.irq = platform_get_irq_optional(pdev, 0);
|
||||
if (tpm_info.irq <= 0) {
|
||||
if (pdev != force_pdev)
|
||||
tpm_info.irq = -1;
|
||||
|
@ -506,6 +506,84 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct tis_vendor_durations_override {
|
||||
u32 did_vid;
|
||||
struct tpm1_version version;
|
||||
unsigned long durations[3];
|
||||
};
|
||||
|
||||
static const struct tis_vendor_durations_override vendor_dur_overrides[] = {
|
||||
/* STMicroelectronics 0x104a */
|
||||
{ 0x0000104a,
|
||||
{ 1, 2, 8, 28 },
|
||||
{ (2 * 60 * HZ), (2 * 60 * HZ), (2 * 60 * HZ) } },
|
||||
};
|
||||
|
||||
static void tpm_tis_update_durations(struct tpm_chip *chip,
|
||||
unsigned long *duration_cap)
|
||||
{
|
||||
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
|
||||
struct tpm1_version *version;
|
||||
u32 did_vid;
|
||||
int i, rc;
|
||||
cap_t cap;
|
||||
|
||||
chip->duration_adjusted = false;
|
||||
|
||||
if (chip->ops->clk_enable != NULL)
|
||||
chip->ops->clk_enable(chip, true);
|
||||
|
||||
rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid);
|
||||
if (rc < 0) {
|
||||
dev_warn(&chip->dev, "%s: failed to read did_vid. %d\n",
|
||||
__func__, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Try to get a TPM version 1.2 or 1.1 TPM_CAP_VERSION_INFO */
|
||||
rc = tpm1_getcap(chip, TPM_CAP_VERSION_1_2, &cap,
|
||||
"attempting to determine the 1.2 version",
|
||||
sizeof(cap.version2));
|
||||
if (!rc) {
|
||||
version = &cap.version2.version;
|
||||
} else {
|
||||
rc = tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap,
|
||||
"attempting to determine the 1.1 version",
|
||||
sizeof(cap.version1));
|
||||
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
version = &cap.version1;
|
||||
}
|
||||
|
||||
for (i = 0; i != ARRAY_SIZE(vendor_dur_overrides); i++) {
|
||||
if (vendor_dur_overrides[i].did_vid != did_vid)
|
||||
continue;
|
||||
|
||||
if ((version->major ==
|
||||
vendor_dur_overrides[i].version.major) &&
|
||||
(version->minor ==
|
||||
vendor_dur_overrides[i].version.minor) &&
|
||||
(version->rev_major ==
|
||||
vendor_dur_overrides[i].version.rev_major) &&
|
||||
(version->rev_minor ==
|
||||
vendor_dur_overrides[i].version.rev_minor)) {
|
||||
|
||||
memcpy(duration_cap,
|
||||
vendor_dur_overrides[i].durations,
|
||||
sizeof(vendor_dur_overrides[i].durations));
|
||||
|
||||
chip->duration_adjusted = true;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (chip->ops->clk_enable != NULL)
|
||||
chip->ops->clk_enable(chip, false);
|
||||
}
|
||||
|
||||
struct tis_vendor_timeout_override {
|
||||
u32 did_vid;
|
||||
unsigned long timeout_us[4];
|
||||
@ -842,6 +920,7 @@ static const struct tpm_class_ops tpm_tis = {
|
||||
.send = tpm_tis_send,
|
||||
.cancel = tpm_tis_ready,
|
||||
.update_timeouts = tpm_tis_update_timeouts,
|
||||
.update_durations = tpm_tis_update_durations,
|
||||
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_canceled = tpm_tis_req_canceled,
|
||||
|
@ -20,42 +20,64 @@
|
||||
* Dorn and Kyleen Hall and Jarko Sakkinnen.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/tpm.h>
|
||||
|
||||
#include "tpm.h"
|
||||
#include "tpm_tis_core.h"
|
||||
#include "tpm_tis_spi.h"
|
||||
|
||||
#define MAX_SPI_FRAMESIZE 64
|
||||
|
||||
struct tpm_tis_spi_phy {
|
||||
struct tpm_tis_data priv;
|
||||
struct spi_device *spi_device;
|
||||
u8 *iobuf;
|
||||
};
|
||||
|
||||
static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data)
|
||||
/*
|
||||
* TCG SPI flow control is documented in section 6.4 of the spec[1]. In short,
|
||||
* keep trying to read from the device until MISO goes high indicating the
|
||||
* wait state has ended.
|
||||
*
|
||||
* [1] https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/
|
||||
*/
|
||||
static int tpm_tis_spi_flow_control(struct tpm_tis_spi_phy *phy,
|
||||
struct spi_transfer *spi_xfer)
|
||||
{
|
||||
return container_of(data, struct tpm_tis_spi_phy, priv);
|
||||
struct spi_message m;
|
||||
int ret, i;
|
||||
|
||||
if ((phy->iobuf[3] & 0x01) == 0) {
|
||||
// handle SPI wait states
|
||||
phy->iobuf[0] = 0;
|
||||
|
||||
for (i = 0; i < TPM_RETRY; i++) {
|
||||
spi_xfer->len = 1;
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(spi_xfer, &m);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (phy->iobuf[0] & 0x01)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == TPM_RETRY)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
|
||||
u8 *in, const u8 *out)
|
||||
int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
|
||||
u8 *in, const u8 *out)
|
||||
{
|
||||
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
|
||||
int ret = 0;
|
||||
int i;
|
||||
struct spi_message m;
|
||||
struct spi_transfer spi_xfer;
|
||||
u8 transfer_len;
|
||||
@ -82,26 +104,9 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
if ((phy->iobuf[3] & 0x01) == 0) {
|
||||
// handle SPI wait states
|
||||
phy->iobuf[0] = 0;
|
||||
|
||||
for (i = 0; i < TPM_RETRY; i++) {
|
||||
spi_xfer.len = 1;
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
if (phy->iobuf[0] & 0x01)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == TPM_RETRY) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
ret = phy->flow_control(phy, &spi_xfer);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
spi_xfer.cs_change = 0;
|
||||
spi_xfer.len = transfer_len;
|
||||
@ -117,6 +122,7 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
reinit_completion(&phy->ready);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
@ -146,7 +152,7 @@ static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
return tpm_tis_spi_transfer(data, addr, len, NULL, value);
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
|
||||
int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
|
||||
{
|
||||
__le16 result_le;
|
||||
int rc;
|
||||
@ -159,7 +165,7 @@ static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
|
||||
int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
|
||||
{
|
||||
__le32 result_le;
|
||||
int rc;
|
||||
@ -172,7 +178,7 @@ static int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value)
|
||||
int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value)
|
||||
{
|
||||
__le32 value_le;
|
||||
int rc;
|
||||
@ -184,6 +190,18 @@ static int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value)
|
||||
return rc;
|
||||
}
|
||||
|
||||
int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy,
|
||||
int irq, const struct tpm_tis_phy_ops *phy_ops)
|
||||
{
|
||||
phy->iobuf = devm_kmalloc(&spi->dev, MAX_SPI_FRAMESIZE, GFP_KERNEL);
|
||||
if (!phy->iobuf)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->spi_device = spi;
|
||||
|
||||
return tpm_tis_core_init(&spi->dev, &phy->priv, irq, phy_ops, NULL);
|
||||
}
|
||||
|
||||
static const struct tpm_tis_phy_ops tpm_spi_phy_ops = {
|
||||
.read_bytes = tpm_tis_spi_read_bytes,
|
||||
.write_bytes = tpm_tis_spi_write_bytes,
|
||||
@ -202,11 +220,7 @@ static int tpm_tis_spi_probe(struct spi_device *dev)
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->spi_device = dev;
|
||||
|
||||
phy->iobuf = devm_kmalloc(&dev->dev, MAX_SPI_FRAMESIZE, GFP_KERNEL);
|
||||
if (!phy->iobuf)
|
||||
return -ENOMEM;
|
||||
phy->flow_control = tpm_tis_spi_flow_control;
|
||||
|
||||
/* If the SPI device has an IRQ then use that */
|
||||
if (dev->irq > 0)
|
||||
@ -214,11 +228,27 @@ static int tpm_tis_spi_probe(struct spi_device *dev)
|
||||
else
|
||||
irq = -1;
|
||||
|
||||
return tpm_tis_core_init(&dev->dev, &phy->priv, irq, &tpm_spi_phy_ops,
|
||||
NULL);
|
||||
init_completion(&phy->ready);
|
||||
return tpm_tis_spi_init(dev, phy, irq, &tpm_spi_phy_ops);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
|
||||
typedef int (*tpm_tis_spi_probe_func)(struct spi_device *);
|
||||
|
||||
static int tpm_tis_spi_driver_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *spi_dev_id = spi_get_device_id(spi);
|
||||
tpm_tis_spi_probe_func probe_func;
|
||||
|
||||
probe_func = of_device_get_match_data(&spi->dev);
|
||||
if (!probe_func && spi_dev_id)
|
||||
probe_func = (tpm_tis_spi_probe_func)spi_dev_id->driver_data;
|
||||
if (!probe_func)
|
||||
return -ENODEV;
|
||||
|
||||
return probe_func(spi);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_spi_resume);
|
||||
|
||||
static int tpm_tis_spi_remove(struct spi_device *dev)
|
||||
{
|
||||
@ -230,15 +260,17 @@ static int tpm_tis_spi_remove(struct spi_device *dev)
|
||||
}
|
||||
|
||||
static const struct spi_device_id tpm_tis_spi_id[] = {
|
||||
{"tpm_tis_spi", 0},
|
||||
{ "tpm_tis_spi", (unsigned long)tpm_tis_spi_probe },
|
||||
{ "cr50", (unsigned long)cr50_spi_probe },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, tpm_tis_spi_id);
|
||||
|
||||
static const struct of_device_id of_tis_spi_match[] = {
|
||||
{ .compatible = "st,st33htpm-spi", },
|
||||
{ .compatible = "infineon,slb9670", },
|
||||
{ .compatible = "tcg,tpm_tis-spi", },
|
||||
{ .compatible = "st,st33htpm-spi", .data = tpm_tis_spi_probe },
|
||||
{ .compatible = "infineon,slb9670", .data = tpm_tis_spi_probe },
|
||||
{ .compatible = "tcg,tpm_tis-spi", .data = tpm_tis_spi_probe },
|
||||
{ .compatible = "google,cr50", .data = cr50_spi_probe },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_tis_spi_match);
|
||||
@ -251,13 +283,12 @@ MODULE_DEVICE_TABLE(acpi, acpi_tis_spi_match);
|
||||
|
||||
static struct spi_driver tpm_tis_spi_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "tpm_tis_spi",
|
||||
.pm = &tpm_tis_pm,
|
||||
.of_match_table = of_match_ptr(of_tis_spi_match),
|
||||
.acpi_match_table = ACPI_PTR(acpi_tis_spi_match),
|
||||
},
|
||||
.probe = tpm_tis_spi_probe,
|
||||
.probe = tpm_tis_spi_driver_probe,
|
||||
.remove = tpm_tis_spi_remove,
|
||||
.id_table = tpm_tis_spi_id,
|
||||
};
|
||||
|
53
drivers/char/tpm/tpm_tis_spi.h
Normal file
53
drivers/char/tpm/tpm_tis_spi.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2015 Infineon Technologies AG
|
||||
* Copyright (C) 2016 STMicroelectronics SAS
|
||||
*/
|
||||
|
||||
#ifndef TPM_TIS_SPI_H
|
||||
#define TPM_TIS_SPI_H
|
||||
|
||||
#include "tpm_tis_core.h"
|
||||
|
||||
struct tpm_tis_spi_phy {
|
||||
struct tpm_tis_data priv;
|
||||
struct spi_device *spi_device;
|
||||
int (*flow_control)(struct tpm_tis_spi_phy *phy,
|
||||
struct spi_transfer *xfer);
|
||||
struct completion ready;
|
||||
unsigned long wake_after;
|
||||
|
||||
u8 *iobuf;
|
||||
};
|
||||
|
||||
static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data)
|
||||
{
|
||||
return container_of(data, struct tpm_tis_spi_phy, priv);
|
||||
}
|
||||
|
||||
extern int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy,
|
||||
int irq, const struct tpm_tis_phy_ops *phy_ops);
|
||||
|
||||
extern int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
|
||||
u8 *in, const u8 *out);
|
||||
|
||||
extern int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result);
|
||||
extern int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result);
|
||||
extern int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value);
|
||||
|
||||
#ifdef CONFIG_TCG_TIS_SPI_CR50
|
||||
extern int cr50_spi_probe(struct spi_device *spi);
|
||||
#else
|
||||
static inline int cr50_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_TCG_TIS_SPI_CR50)
|
||||
extern int tpm_tis_spi_resume(struct device *dev);
|
||||
#else
|
||||
#define tpm_tis_spi_resume NULL
|
||||
#endif
|
||||
|
||||
#endif
|
322
drivers/char/tpm/tpm_tis_spi_cr50.c
Normal file
322
drivers/char/tpm/tpm_tis_spi_cr50.c
Normal file
@ -0,0 +1,322 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2016 Google, Inc
|
||||
*
|
||||
* This device driver implements a TCG PTP FIFO interface over SPI for chips
|
||||
* with Cr50 firmware.
|
||||
* It is based on tpm_tis_spi driver by Peter Huewe and Christophe Ricard.
|
||||
*/
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include "tpm_tis_core.h"
|
||||
#include "tpm_tis_spi.h"
|
||||
|
||||
/*
|
||||
* Cr50 timing constants:
|
||||
* - can go to sleep not earlier than after CR50_SLEEP_DELAY_MSEC.
|
||||
* - needs up to CR50_WAKE_START_DELAY_USEC to wake after sleep.
|
||||
* - requires waiting for "ready" IRQ, if supported; or waiting for at least
|
||||
* CR50_NOIRQ_ACCESS_DELAY_MSEC between transactions, if IRQ is not supported.
|
||||
* - waits for up to CR50_FLOW_CONTROL for flow control 'ready' indication.
|
||||
*/
|
||||
#define CR50_SLEEP_DELAY_MSEC 1000
|
||||
#define CR50_WAKE_START_DELAY_USEC 1000
|
||||
#define CR50_NOIRQ_ACCESS_DELAY msecs_to_jiffies(2)
|
||||
#define CR50_READY_IRQ_TIMEOUT msecs_to_jiffies(TPM2_TIMEOUT_A)
|
||||
#define CR50_FLOW_CONTROL msecs_to_jiffies(TPM2_TIMEOUT_A)
|
||||
#define MAX_IRQ_CONFIRMATION_ATTEMPTS 3
|
||||
|
||||
#define TPM_CR50_FW_VER(l) (0x0f90 | ((l) << 12))
|
||||
#define TPM_CR50_MAX_FW_VER_LEN 64
|
||||
|
||||
struct cr50_spi_phy {
|
||||
struct tpm_tis_spi_phy spi_phy;
|
||||
|
||||
struct mutex time_track_mutex;
|
||||
unsigned long last_access;
|
||||
|
||||
unsigned long access_delay;
|
||||
|
||||
unsigned int irq_confirmation_attempt;
|
||||
bool irq_needs_confirmation;
|
||||
bool irq_confirmed;
|
||||
};
|
||||
|
||||
static inline struct cr50_spi_phy *to_cr50_spi_phy(struct tpm_tis_spi_phy *phy)
|
||||
{
|
||||
return container_of(phy, struct cr50_spi_phy, spi_phy);
|
||||
}
|
||||
|
||||
/*
|
||||
* The cr50 interrupt handler just signals waiting threads that the
|
||||
* interrupt was asserted. It does not do any processing triggered
|
||||
* by interrupts but is instead used to avoid fixed delays.
|
||||
*/
|
||||
static irqreturn_t cr50_spi_irq_handler(int dummy, void *dev_id)
|
||||
{
|
||||
struct cr50_spi_phy *cr50_phy = dev_id;
|
||||
|
||||
cr50_phy->irq_confirmed = true;
|
||||
complete(&cr50_phy->spi_phy.ready);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cr50 needs to have at least some delay between consecutive
|
||||
* transactions. Make sure we wait.
|
||||
*/
|
||||
static void cr50_ensure_access_delay(struct cr50_spi_phy *phy)
|
||||
{
|
||||
unsigned long allowed_access = phy->last_access + phy->access_delay;
|
||||
unsigned long time_now = jiffies;
|
||||
struct device *dev = &phy->spi_phy.spi_device->dev;
|
||||
|
||||
/*
|
||||
* Note: There is a small chance, if Cr50 is not accessed in a few days,
|
||||
* that time_in_range will not provide the correct result after the wrap
|
||||
* around for jiffies. In this case, we'll have an unneeded short delay,
|
||||
* which is fine.
|
||||
*/
|
||||
if (time_in_range_open(time_now, phy->last_access, allowed_access)) {
|
||||
unsigned long remaining, timeout = allowed_access - time_now;
|
||||
|
||||
remaining = wait_for_completion_timeout(&phy->spi_phy.ready,
|
||||
timeout);
|
||||
if (!remaining && phy->irq_confirmed)
|
||||
dev_warn(dev, "Timeout waiting for TPM ready IRQ\n");
|
||||
}
|
||||
|
||||
if (phy->irq_needs_confirmation) {
|
||||
unsigned int attempt = ++phy->irq_confirmation_attempt;
|
||||
|
||||
if (phy->irq_confirmed) {
|
||||
phy->irq_needs_confirmation = false;
|
||||
phy->access_delay = CR50_READY_IRQ_TIMEOUT;
|
||||
dev_info(dev, "TPM ready IRQ confirmed on attempt %u\n",
|
||||
attempt);
|
||||
} else if (attempt > MAX_IRQ_CONFIRMATION_ATTEMPTS) {
|
||||
phy->irq_needs_confirmation = false;
|
||||
dev_warn(dev, "IRQ not confirmed - will use delays\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Cr50 might go to sleep if there is no SPI activity for some time and
|
||||
* miss the first few bits/bytes on the bus. In such case, wake it up
|
||||
* by asserting CS and give it time to start up.
|
||||
*/
|
||||
static bool cr50_needs_waking(struct cr50_spi_phy *phy)
|
||||
{
|
||||
/*
|
||||
* Note: There is a small chance, if Cr50 is not accessed in a few days,
|
||||
* that time_in_range will not provide the correct result after the wrap
|
||||
* around for jiffies. In this case, we'll probably timeout or read
|
||||
* incorrect value from TPM_STS and just retry the operation.
|
||||
*/
|
||||
return !time_in_range_open(jiffies, phy->last_access,
|
||||
phy->spi_phy.wake_after);
|
||||
}
|
||||
|
||||
static void cr50_wake_if_needed(struct cr50_spi_phy *cr50_phy)
|
||||
{
|
||||
struct tpm_tis_spi_phy *phy = &cr50_phy->spi_phy;
|
||||
|
||||
if (cr50_needs_waking(cr50_phy)) {
|
||||
/* Assert CS, wait 1 msec, deassert CS */
|
||||
struct spi_transfer spi_cs_wake = { .delay_usecs = 1000 };
|
||||
|
||||
spi_sync_transfer(phy->spi_device, &spi_cs_wake, 1);
|
||||
/* Wait for it to fully wake */
|
||||
usleep_range(CR50_WAKE_START_DELAY_USEC,
|
||||
CR50_WAKE_START_DELAY_USEC * 2);
|
||||
}
|
||||
|
||||
/* Reset the time when we need to wake Cr50 again */
|
||||
phy->wake_after = jiffies + msecs_to_jiffies(CR50_SLEEP_DELAY_MSEC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Flow control: clock the bus and wait for cr50 to set LSB before
|
||||
* sending/receiving data. TCG PTP spec allows it to happen during
|
||||
* the last byte of header, but cr50 never does that in practice,
|
||||
* and earlier versions had a bug when it was set too early, so don't
|
||||
* check for it during header transfer.
|
||||
*/
|
||||
static int cr50_spi_flow_control(struct tpm_tis_spi_phy *phy,
|
||||
struct spi_transfer *spi_xfer)
|
||||
{
|
||||
struct device *dev = &phy->spi_device->dev;
|
||||
unsigned long timeout = jiffies + CR50_FLOW_CONTROL;
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
|
||||
spi_xfer->len = 1;
|
||||
|
||||
do {
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(spi_xfer, &m);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_warn(dev, "Timeout during flow control\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
} while (!(phy->iobuf[0] & 0x01));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_cr50_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
|
||||
u8 *in, const u8 *out)
|
||||
{
|
||||
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
|
||||
struct cr50_spi_phy *cr50_phy = to_cr50_spi_phy(phy);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&cr50_phy->time_track_mutex);
|
||||
/*
|
||||
* Do this outside of spi_bus_lock in case cr50 is not the
|
||||
* only device on that spi bus.
|
||||
*/
|
||||
cr50_ensure_access_delay(cr50_phy);
|
||||
cr50_wake_if_needed(cr50_phy);
|
||||
|
||||
ret = tpm_tis_spi_transfer(data, addr, len, in, out);
|
||||
|
||||
cr50_phy->last_access = jiffies;
|
||||
mutex_unlock(&cr50_phy->time_track_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_cr50_read_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, u8 *result)
|
||||
{
|
||||
return tpm_tis_spi_cr50_transfer(data, addr, len, result, NULL);
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_cr50_write_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, const u8 *value)
|
||||
{
|
||||
return tpm_tis_spi_cr50_transfer(data, addr, len, NULL, value);
|
||||
}
|
||||
|
||||
static const struct tpm_tis_phy_ops tpm_spi_cr50_phy_ops = {
|
||||
.read_bytes = tpm_tis_spi_cr50_read_bytes,
|
||||
.write_bytes = tpm_tis_spi_cr50_write_bytes,
|
||||
.read16 = tpm_tis_spi_read16,
|
||||
.read32 = tpm_tis_spi_read32,
|
||||
.write32 = tpm_tis_spi_write32,
|
||||
};
|
||||
|
||||
static void cr50_print_fw_version(struct tpm_tis_data *data)
|
||||
{
|
||||
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
|
||||
int i, len = 0;
|
||||
char fw_ver[TPM_CR50_MAX_FW_VER_LEN + 1];
|
||||
char fw_ver_block[4];
|
||||
|
||||
/*
|
||||
* Write anything to TPM_CR50_FW_VER to start from the beginning
|
||||
* of the version string
|
||||
*/
|
||||
tpm_tis_write8(data, TPM_CR50_FW_VER(data->locality), 0);
|
||||
|
||||
/* Read the string, 4 bytes at a time, until we get '\0' */
|
||||
do {
|
||||
tpm_tis_read_bytes(data, TPM_CR50_FW_VER(data->locality), 4,
|
||||
fw_ver_block);
|
||||
for (i = 0; i < 4 && fw_ver_block[i]; ++len, ++i)
|
||||
fw_ver[len] = fw_ver_block[i];
|
||||
} while (i == 4 && len < TPM_CR50_MAX_FW_VER_LEN);
|
||||
fw_ver[len] = '\0';
|
||||
|
||||
dev_info(&phy->spi_device->dev, "Cr50 firmware version: %s\n", fw_ver);
|
||||
}
|
||||
|
||||
int cr50_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct tpm_tis_spi_phy *phy;
|
||||
struct cr50_spi_phy *cr50_phy;
|
||||
int ret;
|
||||
struct tpm_chip *chip;
|
||||
|
||||
cr50_phy = devm_kzalloc(&spi->dev, sizeof(*cr50_phy), GFP_KERNEL);
|
||||
if (!cr50_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
phy = &cr50_phy->spi_phy;
|
||||
phy->flow_control = cr50_spi_flow_control;
|
||||
phy->wake_after = jiffies;
|
||||
init_completion(&phy->ready);
|
||||
|
||||
cr50_phy->access_delay = CR50_NOIRQ_ACCESS_DELAY;
|
||||
cr50_phy->last_access = jiffies;
|
||||
mutex_init(&cr50_phy->time_track_mutex);
|
||||
|
||||
if (spi->irq > 0) {
|
||||
ret = devm_request_irq(&spi->dev, spi->irq,
|
||||
cr50_spi_irq_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
"cr50_spi", cr50_phy);
|
||||
if (ret < 0) {
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
dev_warn(&spi->dev, "Requesting IRQ %d failed: %d\n",
|
||||
spi->irq, ret);
|
||||
/*
|
||||
* This is not fatal, the driver will fall back to
|
||||
* delays automatically, since ready will never
|
||||
* be completed without a registered irq handler.
|
||||
* So, just fall through.
|
||||
*/
|
||||
} else {
|
||||
/*
|
||||
* IRQ requested, let's verify that it is actually
|
||||
* triggered, before relying on it.
|
||||
*/
|
||||
cr50_phy->irq_needs_confirmation = true;
|
||||
}
|
||||
} else {
|
||||
dev_warn(&spi->dev,
|
||||
"No IRQ - will use delays between transactions.\n");
|
||||
}
|
||||
|
||||
ret = tpm_tis_spi_init(spi, phy, -1, &tpm_spi_cr50_phy_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cr50_print_fw_version(&phy->priv);
|
||||
|
||||
chip = dev_get_drvdata(&spi->dev);
|
||||
chip->flags |= TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
int tpm_tis_spi_resume(struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
struct tpm_tis_data *data = dev_get_drvdata(&chip->dev);
|
||||
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
|
||||
/*
|
||||
* Jiffies not increased during suspend, so we need to reset
|
||||
* the time to wake Cr50 after resume.
|
||||
*/
|
||||
phy->wake_after = jiffies;
|
||||
|
||||
return tpm_tis_resume(dev);
|
||||
}
|
||||
#endif
|
@ -65,7 +65,6 @@ header-test- += keys/asymmetric-subtype.h
|
||||
header-test- += keys/asymmetric-type.h
|
||||
header-test- += keys/big_key-type.h
|
||||
header-test- += keys/request_key_auth-type.h
|
||||
header-test- += keys/trusted.h
|
||||
header-test- += kvm/arm_arch_timer.h
|
||||
header-test- += kvm/arm_pmu.h
|
||||
header-test-$(CONFIG_ARM) += kvm/arm_psci.h
|
||||
|
@ -1,14 +1,13 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __TRUSTED_KEY_H
|
||||
#define __TRUSTED_KEY_H
|
||||
#ifndef __TRUSTED_TPM_H
|
||||
#define __TRUSTED_TPM_H
|
||||
|
||||
#include <keys/trusted-type.h>
|
||||
#include <linux/tpm_command.h>
|
||||
|
||||
/* implementation specific TPM constants */
|
||||
#define MAX_BUF_SIZE 1024
|
||||
#define TPM_GETRANDOM_SIZE 14
|
||||
#define TPM_OSAP_SIZE 36
|
||||
#define TPM_OIAP_SIZE 10
|
||||
#define TPM_SEAL_SIZE 87
|
||||
#define TPM_UNSEAL_SIZE 104
|
||||
#define TPM_SIZE_OFFSET 2
|
||||
#define TPM_RETURN_OFFSET 6
|
||||
#define TPM_DATA_OFFSET 10
|
||||
@ -17,13 +16,6 @@
|
||||
#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset])
|
||||
#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset]))
|
||||
|
||||
struct tpm_buf {
|
||||
int len;
|
||||
unsigned char data[MAX_BUF_SIZE];
|
||||
};
|
||||
|
||||
#define INIT_BUF(tb) (tb->len = 0)
|
||||
|
||||
struct osapsess {
|
||||
uint32_t handle;
|
||||
unsigned char secret[SHA1_DIGEST_SIZE];
|
||||
@ -48,6 +40,13 @@ int TSS_checkhmac1(unsigned char *buffer,
|
||||
int trusted_tpm_send(unsigned char *cmd, size_t buflen);
|
||||
int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce);
|
||||
|
||||
int tpm2_seal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options);
|
||||
int tpm2_unseal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options);
|
||||
|
||||
#define TPM_DEBUG 0
|
||||
|
||||
#if TPM_DEBUG
|
||||
@ -109,28 +108,4 @@ static inline void dump_tpm_buf(unsigned char *buf)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void store8(struct tpm_buf *buf, const unsigned char value)
|
||||
{
|
||||
buf->data[buf->len++] = value;
|
||||
}
|
||||
|
||||
static inline void store16(struct tpm_buf *buf, const uint16_t value)
|
||||
{
|
||||
*(uint16_t *) & buf->data[buf->len] = htons(value);
|
||||
buf->len += sizeof value;
|
||||
}
|
||||
|
||||
static inline void store32(struct tpm_buf *buf, const uint32_t value)
|
||||
{
|
||||
*(uint32_t *) & buf->data[buf->len] = htonl(value);
|
||||
buf->len += sizeof value;
|
||||
}
|
||||
|
||||
static inline void storebytes(struct tpm_buf *buf, const unsigned char *in,
|
||||
const int len)
|
||||
{
|
||||
memcpy(buf->data + buf->len, in, len);
|
||||
buf->len += len;
|
||||
}
|
||||
#endif
|
@ -21,6 +21,7 @@
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <crypto/hash_info.h>
|
||||
|
||||
#define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */
|
||||
@ -67,6 +68,8 @@ struct tpm_class_ops {
|
||||
u8 (*status) (struct tpm_chip *chip);
|
||||
void (*update_timeouts)(struct tpm_chip *chip,
|
||||
unsigned long *timeout_cap);
|
||||
void (*update_durations)(struct tpm_chip *chip,
|
||||
unsigned long *duration_cap);
|
||||
int (*go_idle)(struct tpm_chip *chip);
|
||||
int (*cmd_ready)(struct tpm_chip *chip);
|
||||
int (*request_locality)(struct tpm_chip *chip, int loc);
|
||||
@ -161,6 +164,235 @@ struct tpm_chip {
|
||||
int locality;
|
||||
};
|
||||
|
||||
#define TPM_HEADER_SIZE 10
|
||||
|
||||
enum tpm2_const {
|
||||
TPM2_PLATFORM_PCR = 24,
|
||||
TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8),
|
||||
};
|
||||
|
||||
enum tpm2_timeouts {
|
||||
TPM2_TIMEOUT_A = 750,
|
||||
TPM2_TIMEOUT_B = 2000,
|
||||
TPM2_TIMEOUT_C = 200,
|
||||
TPM2_TIMEOUT_D = 30,
|
||||
TPM2_DURATION_SHORT = 20,
|
||||
TPM2_DURATION_MEDIUM = 750,
|
||||
TPM2_DURATION_LONG = 2000,
|
||||
TPM2_DURATION_LONG_LONG = 300000,
|
||||
TPM2_DURATION_DEFAULT = 120000,
|
||||
};
|
||||
|
||||
enum tpm2_structures {
|
||||
TPM2_ST_NO_SESSIONS = 0x8001,
|
||||
TPM2_ST_SESSIONS = 0x8002,
|
||||
};
|
||||
|
||||
/* Indicates from what layer of the software stack the error comes from */
|
||||
#define TSS2_RC_LAYER_SHIFT 16
|
||||
#define TSS2_RESMGR_TPM_RC_LAYER (11 << TSS2_RC_LAYER_SHIFT)
|
||||
|
||||
enum tpm2_return_codes {
|
||||
TPM2_RC_SUCCESS = 0x0000,
|
||||
TPM2_RC_HASH = 0x0083, /* RC_FMT1 */
|
||||
TPM2_RC_HANDLE = 0x008B,
|
||||
TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */
|
||||
TPM2_RC_FAILURE = 0x0101,
|
||||
TPM2_RC_DISABLED = 0x0120,
|
||||
TPM2_RC_COMMAND_CODE = 0x0143,
|
||||
TPM2_RC_TESTING = 0x090A, /* RC_WARN */
|
||||
TPM2_RC_REFERENCE_H0 = 0x0910,
|
||||
TPM2_RC_RETRY = 0x0922,
|
||||
};
|
||||
|
||||
enum tpm2_command_codes {
|
||||
TPM2_CC_FIRST = 0x011F,
|
||||
TPM2_CC_HIERARCHY_CONTROL = 0x0121,
|
||||
TPM2_CC_HIERARCHY_CHANGE_AUTH = 0x0129,
|
||||
TPM2_CC_CREATE_PRIMARY = 0x0131,
|
||||
TPM2_CC_SEQUENCE_COMPLETE = 0x013E,
|
||||
TPM2_CC_SELF_TEST = 0x0143,
|
||||
TPM2_CC_STARTUP = 0x0144,
|
||||
TPM2_CC_SHUTDOWN = 0x0145,
|
||||
TPM2_CC_NV_READ = 0x014E,
|
||||
TPM2_CC_CREATE = 0x0153,
|
||||
TPM2_CC_LOAD = 0x0157,
|
||||
TPM2_CC_SEQUENCE_UPDATE = 0x015C,
|
||||
TPM2_CC_UNSEAL = 0x015E,
|
||||
TPM2_CC_CONTEXT_LOAD = 0x0161,
|
||||
TPM2_CC_CONTEXT_SAVE = 0x0162,
|
||||
TPM2_CC_FLUSH_CONTEXT = 0x0165,
|
||||
TPM2_CC_VERIFY_SIGNATURE = 0x0177,
|
||||
TPM2_CC_GET_CAPABILITY = 0x017A,
|
||||
TPM2_CC_GET_RANDOM = 0x017B,
|
||||
TPM2_CC_PCR_READ = 0x017E,
|
||||
TPM2_CC_PCR_EXTEND = 0x0182,
|
||||
TPM2_CC_EVENT_SEQUENCE_COMPLETE = 0x0185,
|
||||
TPM2_CC_HASH_SEQUENCE_START = 0x0186,
|
||||
TPM2_CC_CREATE_LOADED = 0x0191,
|
||||
TPM2_CC_LAST = 0x0193, /* Spec 1.36 */
|
||||
};
|
||||
|
||||
enum tpm2_permanent_handles {
|
||||
TPM2_RS_PW = 0x40000009,
|
||||
};
|
||||
|
||||
enum tpm2_capabilities {
|
||||
TPM2_CAP_HANDLES = 1,
|
||||
TPM2_CAP_COMMANDS = 2,
|
||||
TPM2_CAP_PCRS = 5,
|
||||
TPM2_CAP_TPM_PROPERTIES = 6,
|
||||
};
|
||||
|
||||
enum tpm2_properties {
|
||||
TPM_PT_TOTAL_COMMANDS = 0x0129,
|
||||
};
|
||||
|
||||
enum tpm2_startup_types {
|
||||
TPM2_SU_CLEAR = 0x0000,
|
||||
TPM2_SU_STATE = 0x0001,
|
||||
};
|
||||
|
||||
enum tpm2_cc_attrs {
|
||||
TPM2_CC_ATTR_CHANDLES = 25,
|
||||
TPM2_CC_ATTR_RHANDLE = 28,
|
||||
};
|
||||
|
||||
#define TPM_VID_INTEL 0x8086
|
||||
#define TPM_VID_WINBOND 0x1050
|
||||
#define TPM_VID_STM 0x104A
|
||||
|
||||
enum tpm_chip_flags {
|
||||
TPM_CHIP_FLAG_TPM2 = BIT(1),
|
||||
TPM_CHIP_FLAG_IRQ = BIT(2),
|
||||
TPM_CHIP_FLAG_VIRTUAL = BIT(3),
|
||||
TPM_CHIP_FLAG_HAVE_TIMEOUTS = BIT(4),
|
||||
TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5),
|
||||
TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED = BIT(6),
|
||||
};
|
||||
|
||||
#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
|
||||
|
||||
struct tpm_header {
|
||||
__be16 tag;
|
||||
__be32 length;
|
||||
union {
|
||||
__be32 ordinal;
|
||||
__be32 return_code;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* A string buffer type for constructing TPM commands. This is based on the
|
||||
* ideas of string buffer code in security/keys/trusted.h but is heap based
|
||||
* in order to keep the stack usage minimal.
|
||||
*/
|
||||
|
||||
enum tpm_buf_flags {
|
||||
TPM_BUF_OVERFLOW = BIT(0),
|
||||
};
|
||||
|
||||
struct tpm_buf {
|
||||
unsigned int flags;
|
||||
u8 *data;
|
||||
};
|
||||
|
||||
enum tpm2_object_attributes {
|
||||
TPM2_OA_USER_WITH_AUTH = BIT(6),
|
||||
};
|
||||
|
||||
enum tpm2_session_attributes {
|
||||
TPM2_SA_CONTINUE_SESSION = BIT(0),
|
||||
};
|
||||
|
||||
struct tpm2_hash {
|
||||
unsigned int crypto_id;
|
||||
unsigned int tpm_id;
|
||||
};
|
||||
|
||||
static inline void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
|
||||
{
|
||||
struct tpm_header *head = (struct tpm_header *)buf->data;
|
||||
|
||||
head->tag = cpu_to_be16(tag);
|
||||
head->length = cpu_to_be32(sizeof(*head));
|
||||
head->ordinal = cpu_to_be32(ordinal);
|
||||
}
|
||||
|
||||
static inline int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
|
||||
{
|
||||
buf->data = (u8 *)__get_free_page(GFP_KERNEL);
|
||||
if (!buf->data)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->flags = 0;
|
||||
tpm_buf_reset(buf, tag, ordinal);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void tpm_buf_destroy(struct tpm_buf *buf)
|
||||
{
|
||||
free_page((unsigned long)buf->data);
|
||||
}
|
||||
|
||||
static inline u32 tpm_buf_length(struct tpm_buf *buf)
|
||||
{
|
||||
struct tpm_header *head = (struct tpm_header *)buf->data;
|
||||
|
||||
return be32_to_cpu(head->length);
|
||||
}
|
||||
|
||||
static inline u16 tpm_buf_tag(struct tpm_buf *buf)
|
||||
{
|
||||
struct tpm_header *head = (struct tpm_header *)buf->data;
|
||||
|
||||
return be16_to_cpu(head->tag);
|
||||
}
|
||||
|
||||
static inline void tpm_buf_append(struct tpm_buf *buf,
|
||||
const unsigned char *new_data,
|
||||
unsigned int new_len)
|
||||
{
|
||||
struct tpm_header *head = (struct tpm_header *)buf->data;
|
||||
u32 len = tpm_buf_length(buf);
|
||||
|
||||
/* Return silently if overflow has already happened. */
|
||||
if (buf->flags & TPM_BUF_OVERFLOW)
|
||||
return;
|
||||
|
||||
if ((len + new_len) > PAGE_SIZE) {
|
||||
WARN(1, "tpm_buf: overflow\n");
|
||||
buf->flags |= TPM_BUF_OVERFLOW;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&buf->data[len], new_data, new_len);
|
||||
head->length = cpu_to_be32(len + new_len);
|
||||
}
|
||||
|
||||
static inline void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value)
|
||||
{
|
||||
tpm_buf_append(buf, &value, 1);
|
||||
}
|
||||
|
||||
static inline void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value)
|
||||
{
|
||||
__be16 value2 = cpu_to_be16(value);
|
||||
|
||||
tpm_buf_append(buf, (u8 *) &value2, 2);
|
||||
}
|
||||
|
||||
static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
|
||||
{
|
||||
__be32 value2 = cpu_to_be32(value);
|
||||
|
||||
tpm_buf_append(buf, (u8 *) &value2, 4);
|
||||
}
|
||||
|
||||
static inline u32 tpm2_rc_value(u32 rc)
|
||||
{
|
||||
return (rc & BIT(7)) ? rc & 0xff : rc;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)
|
||||
|
||||
extern int tpm_is_tpm2(struct tpm_chip *chip);
|
||||
@ -170,12 +402,6 @@ extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
|
||||
struct tpm_digest *digests);
|
||||
extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen);
|
||||
extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max);
|
||||
extern int tpm_seal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options);
|
||||
extern int tpm_unseal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options);
|
||||
extern struct tpm_chip *tpm_default_chip(void);
|
||||
#else
|
||||
static inline int tpm_is_tpm2(struct tpm_chip *chip)
|
||||
@ -204,18 +430,6 @@ static inline int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int tpm_seal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int tpm_unseal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline struct tpm_chip *tpm_default_chip(void)
|
||||
{
|
||||
return NULL;
|
||||
|
@ -28,5 +28,5 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_pkey.o
|
||||
# Key types
|
||||
#
|
||||
obj-$(CONFIG_BIG_KEYS) += big_key.o
|
||||
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
|
||||
obj-$(CONFIG_TRUSTED_KEYS) += trusted-keys/
|
||||
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
|
||||
|
8
security/keys/trusted-keys/Makefile
Normal file
8
security/keys/trusted-keys/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for trusted keys
|
||||
#
|
||||
|
||||
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
|
||||
trusted-y += trusted_tpm1.o
|
||||
trusted-y += trusted_tpm2.o
|
@ -27,7 +27,7 @@
|
||||
#include <linux/tpm.h>
|
||||
#include <linux/tpm_command.h>
|
||||
|
||||
#include <keys/trusted.h>
|
||||
#include <keys/trusted_tpm.h>
|
||||
|
||||
static const char hmac_alg[] = "hmac(sha1)";
|
||||
static const char hash_alg[] = "sha1";
|
||||
@ -406,13 +406,10 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
|
||||
if (ret != TPM_NONCE_SIZE)
|
||||
return ret;
|
||||
|
||||
INIT_BUF(tb);
|
||||
store16(tb, TPM_TAG_RQU_COMMAND);
|
||||
store32(tb, TPM_OSAP_SIZE);
|
||||
store32(tb, TPM_ORD_OSAP);
|
||||
store16(tb, type);
|
||||
store32(tb, handle);
|
||||
storebytes(tb, ononce, TPM_NONCE_SIZE);
|
||||
tpm_buf_reset(tb, TPM_TAG_RQU_COMMAND, TPM_ORD_OSAP);
|
||||
tpm_buf_append_u16(tb, type);
|
||||
tpm_buf_append_u32(tb, handle);
|
||||
tpm_buf_append(tb, ononce, TPM_NONCE_SIZE);
|
||||
|
||||
ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
|
||||
if (ret < 0)
|
||||
@ -437,10 +434,7 @@ int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
|
||||
if (!chip)
|
||||
return -ENODEV;
|
||||
|
||||
INIT_BUF(tb);
|
||||
store16(tb, TPM_TAG_RQU_COMMAND);
|
||||
store32(tb, TPM_OIAP_SIZE);
|
||||
store32(tb, TPM_ORD_OIAP);
|
||||
tpm_buf_reset(tb, TPM_TAG_RQU_COMMAND, TPM_ORD_OIAP);
|
||||
ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -535,20 +529,17 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
|
||||
goto out;
|
||||
|
||||
/* build and send the TPM request packet */
|
||||
INIT_BUF(tb);
|
||||
store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
|
||||
store32(tb, TPM_SEAL_SIZE + pcrinfosize + datalen);
|
||||
store32(tb, TPM_ORD_SEAL);
|
||||
store32(tb, keyhandle);
|
||||
storebytes(tb, td->encauth, SHA1_DIGEST_SIZE);
|
||||
store32(tb, pcrinfosize);
|
||||
storebytes(tb, pcrinfo, pcrinfosize);
|
||||
store32(tb, datalen);
|
||||
storebytes(tb, data, datalen);
|
||||
store32(tb, sess.handle);
|
||||
storebytes(tb, td->nonceodd, TPM_NONCE_SIZE);
|
||||
store8(tb, cont);
|
||||
storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE);
|
||||
tpm_buf_reset(tb, TPM_TAG_RQU_AUTH1_COMMAND, TPM_ORD_SEAL);
|
||||
tpm_buf_append_u32(tb, keyhandle);
|
||||
tpm_buf_append(tb, td->encauth, SHA1_DIGEST_SIZE);
|
||||
tpm_buf_append_u32(tb, pcrinfosize);
|
||||
tpm_buf_append(tb, pcrinfo, pcrinfosize);
|
||||
tpm_buf_append_u32(tb, datalen);
|
||||
tpm_buf_append(tb, data, datalen);
|
||||
tpm_buf_append_u32(tb, sess.handle);
|
||||
tpm_buf_append(tb, td->nonceodd, TPM_NONCE_SIZE);
|
||||
tpm_buf_append_u8(tb, cont);
|
||||
tpm_buf_append(tb, td->pubauth, SHA1_DIGEST_SIZE);
|
||||
|
||||
ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
|
||||
if (ret < 0)
|
||||
@ -594,7 +585,6 @@ static int tpm_unseal(struct tpm_buf *tb,
|
||||
uint32_t authhandle2 = 0;
|
||||
unsigned char cont = 0;
|
||||
uint32_t ordinal;
|
||||
uint32_t keyhndl;
|
||||
int ret;
|
||||
|
||||
/* sessions for unsealing key and data */
|
||||
@ -610,7 +600,6 @@ static int tpm_unseal(struct tpm_buf *tb,
|
||||
}
|
||||
|
||||
ordinal = htonl(TPM_ORD_UNSEAL);
|
||||
keyhndl = htonl(SRKHANDLE);
|
||||
ret = tpm_get_random(chip, nonceodd, TPM_NONCE_SIZE);
|
||||
if (ret != TPM_NONCE_SIZE) {
|
||||
pr_info("trusted_key: tpm_get_random failed (%d)\n", ret);
|
||||
@ -628,20 +617,17 @@ static int tpm_unseal(struct tpm_buf *tb,
|
||||
return ret;
|
||||
|
||||
/* build and send TPM request packet */
|
||||
INIT_BUF(tb);
|
||||
store16(tb, TPM_TAG_RQU_AUTH2_COMMAND);
|
||||
store32(tb, TPM_UNSEAL_SIZE + bloblen);
|
||||
store32(tb, TPM_ORD_UNSEAL);
|
||||
store32(tb, keyhandle);
|
||||
storebytes(tb, blob, bloblen);
|
||||
store32(tb, authhandle1);
|
||||
storebytes(tb, nonceodd, TPM_NONCE_SIZE);
|
||||
store8(tb, cont);
|
||||
storebytes(tb, authdata1, SHA1_DIGEST_SIZE);
|
||||
store32(tb, authhandle2);
|
||||
storebytes(tb, nonceodd, TPM_NONCE_SIZE);
|
||||
store8(tb, cont);
|
||||
storebytes(tb, authdata2, SHA1_DIGEST_SIZE);
|
||||
tpm_buf_reset(tb, TPM_TAG_RQU_AUTH2_COMMAND, TPM_ORD_UNSEAL);
|
||||
tpm_buf_append_u32(tb, keyhandle);
|
||||
tpm_buf_append(tb, blob, bloblen);
|
||||
tpm_buf_append_u32(tb, authhandle1);
|
||||
tpm_buf_append(tb, nonceodd, TPM_NONCE_SIZE);
|
||||
tpm_buf_append_u8(tb, cont);
|
||||
tpm_buf_append(tb, authdata1, SHA1_DIGEST_SIZE);
|
||||
tpm_buf_append_u32(tb, authhandle2);
|
||||
tpm_buf_append(tb, nonceodd, TPM_NONCE_SIZE);
|
||||
tpm_buf_append_u8(tb, cont);
|
||||
tpm_buf_append(tb, authdata2, SHA1_DIGEST_SIZE);
|
||||
|
||||
ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
|
||||
if (ret < 0) {
|
||||
@ -670,23 +656,23 @@ static int tpm_unseal(struct tpm_buf *tb,
|
||||
static int key_seal(struct trusted_key_payload *p,
|
||||
struct trusted_key_options *o)
|
||||
{
|
||||
struct tpm_buf *tb;
|
||||
struct tpm_buf tb;
|
||||
int ret;
|
||||
|
||||
tb = kzalloc(sizeof *tb, GFP_KERNEL);
|
||||
if (!tb)
|
||||
return -ENOMEM;
|
||||
ret = tpm_buf_init(&tb, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* include migratable flag at end of sealed key */
|
||||
p->key[p->key_len] = p->migratable;
|
||||
|
||||
ret = tpm_seal(tb, o->keytype, o->keyhandle, o->keyauth,
|
||||
ret = tpm_seal(&tb, o->keytype, o->keyhandle, o->keyauth,
|
||||
p->key, p->key_len + 1, p->blob, &p->blob_len,
|
||||
o->blobauth, o->pcrinfo, o->pcrinfo_len);
|
||||
if (ret < 0)
|
||||
pr_info("trusted_key: srkseal failed (%d)\n", ret);
|
||||
|
||||
kzfree(tb);
|
||||
tpm_buf_destroy(&tb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -696,14 +682,14 @@ static int key_seal(struct trusted_key_payload *p,
|
||||
static int key_unseal(struct trusted_key_payload *p,
|
||||
struct trusted_key_options *o)
|
||||
{
|
||||
struct tpm_buf *tb;
|
||||
struct tpm_buf tb;
|
||||
int ret;
|
||||
|
||||
tb = kzalloc(sizeof *tb, GFP_KERNEL);
|
||||
if (!tb)
|
||||
return -ENOMEM;
|
||||
ret = tpm_buf_init(&tb, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tpm_unseal(tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
|
||||
ret = tpm_unseal(&tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
|
||||
o->blobauth, p->key, &p->key_len);
|
||||
if (ret < 0)
|
||||
pr_info("trusted_key: srkunseal failed (%d)\n", ret);
|
||||
@ -711,7 +697,7 @@ static int key_unseal(struct trusted_key_payload *p,
|
||||
/* pull migratable flag out of sealed key */
|
||||
p->migratable = p->key[--p->key_len];
|
||||
|
||||
kzfree(tb);
|
||||
tpm_buf_destroy(&tb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1016,7 +1002,7 @@ static int trusted_instantiate(struct key *key,
|
||||
switch (key_cmd) {
|
||||
case Opt_load:
|
||||
if (tpm2)
|
||||
ret = tpm_unseal_trusted(chip, payload, options);
|
||||
ret = tpm2_unseal_trusted(chip, payload, options);
|
||||
else
|
||||
ret = key_unseal(payload, options);
|
||||
dump_payload(payload);
|
||||
@ -1032,7 +1018,7 @@ static int trusted_instantiate(struct key *key,
|
||||
goto out;
|
||||
}
|
||||
if (tpm2)
|
||||
ret = tpm_seal_trusted(chip, payload, options);
|
||||
ret = tpm2_seal_trusted(chip, payload, options);
|
||||
else
|
||||
ret = key_seal(payload, options);
|
||||
if (ret < 0)
|
314
security/keys/trusted-keys/trusted_tpm2.c
Normal file
314
security/keys/trusted-keys/trusted_tpm2.c
Normal file
@ -0,0 +1,314 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2004 IBM Corporation
|
||||
* Copyright (C) 2014 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/tpm.h>
|
||||
#include <linux/tpm_command.h>
|
||||
|
||||
#include <keys/trusted-type.h>
|
||||
#include <keys/trusted_tpm.h>
|
||||
|
||||
static struct tpm2_hash tpm2_hash_map[] = {
|
||||
{HASH_ALGO_SHA1, TPM_ALG_SHA1},
|
||||
{HASH_ALGO_SHA256, TPM_ALG_SHA256},
|
||||
{HASH_ALGO_SHA384, TPM_ALG_SHA384},
|
||||
{HASH_ALGO_SHA512, TPM_ALG_SHA512},
|
||||
{HASH_ALGO_SM3_256, TPM_ALG_SM3_256},
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_seal_trusted() - seal the payload of a trusted key
|
||||
*
|
||||
* @chip: TPM chip to use
|
||||
* @payload: the key data in clear and encrypted form
|
||||
* @options: authentication values and other options
|
||||
*
|
||||
* Return: < 0 on error and 0 on success.
|
||||
*/
|
||||
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;
|
||||
u32 hash;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
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;
|
||||
|
||||
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 */
|
||||
tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len + 1);
|
||||
|
||||
tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE);
|
||||
tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE);
|
||||
tpm_buf_append_u16(&buf, payload->key_len + 1);
|
||||
tpm_buf_append(&buf, payload->key, payload->key_len);
|
||||
tpm_buf_append_u8(&buf, payload->migratable);
|
||||
|
||||
/* public */
|
||||
tpm_buf_append_u16(&buf, 14 + options->policydigest_len);
|
||||
tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH);
|
||||
tpm_buf_append_u16(&buf, hash);
|
||||
|
||||
/* policy */
|
||||
if (options->policydigest_len) {
|
||||
tpm_buf_append_u32(&buf, 0);
|
||||
tpm_buf_append_u16(&buf, options->policydigest_len);
|
||||
tpm_buf_append(&buf, options->policydigest,
|
||||
options->policydigest_len);
|
||||
} else {
|
||||
tpm_buf_append_u32(&buf, TPM2_OA_USER_WITH_AUTH);
|
||||
tpm_buf_append_u16(&buf, 0);
|
||||
}
|
||||
|
||||
/* public parameters */
|
||||
tpm_buf_append_u16(&buf, TPM_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;
|
||||
}
|
||||
|
||||
rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
|
||||
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;
|
||||
}
|
||||
if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len);
|
||||
payload->blob_len = blob_len;
|
||||
|
||||
out:
|
||||
tpm_buf_destroy(&buf);
|
||||
|
||||
if (rc > 0) {
|
||||
if (tpm2_rc_value(rc) == TPM2_RC_HASH)
|
||||
rc = -EINVAL;
|
||||
else
|
||||
rc = -EPERM;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_load_cmd() - execute a TPM2_Load command
|
||||
*
|
||||
* @chip: TPM chip to use
|
||||
* @payload: the key data in clear and encrypted form
|
||||
* @options: authentication values and other options
|
||||
* @blob_handle: returned blob handle
|
||||
*
|
||||
* Return: 0 on success.
|
||||
* -E2BIG on wrong payload size.
|
||||
* -EPERM on tpm error status.
|
||||
* < 0 error from tpm_send.
|
||||
*/
|
||||
static int tpm2_load_cmd(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options,
|
||||
u32 *blob_handle)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_unseal_cmd() - execute a TPM2_Unload command
|
||||
*
|
||||
* @chip: TPM chip to use
|
||||
* @payload: the key data in clear and encrypted form
|
||||
* @options: authentication values and other options
|
||||
* @blob_handle: blob handle
|
||||
*
|
||||
* Return: 0 on success
|
||||
* -EPERM on tpm error status
|
||||
* < 0 error from tpm_send
|
||||
*/
|
||||
static int tpm2_unseal_cmd(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options,
|
||||
u32 blob_handle)
|
||||
{
|
||||
struct tpm_buf buf;
|
||||
u16 data_len;
|
||||
u8 *data;
|
||||
int rc;
|
||||
|
||||
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
tpm_buf_append_u32(&buf, blob_handle);
|
||||
tpm2_buf_append_auth(&buf,
|
||||
options->policyhandle ?
|
||||
options->policyhandle : TPM2_RS_PW,
|
||||
NULL /* nonce */, 0,
|
||||
TPM2_SA_CONTINUE_SESSION,
|
||||
options->blobauth /* hmac */,
|
||||
TPM_DIGEST_SIZE);
|
||||
|
||||
rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
|
||||
if (rc > 0)
|
||||
rc = -EPERM;
|
||||
|
||||
if (!rc) {
|
||||
data_len = be16_to_cpup(
|
||||
(__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
|
||||
if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE + 1) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 6 + data_len) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
data = &buf.data[TPM_HEADER_SIZE + 6];
|
||||
|
||||
memcpy(payload->key, data, data_len - 1);
|
||||
payload->key_len = data_len - 1;
|
||||
payload->migratable = data[data_len - 1];
|
||||
}
|
||||
|
||||
out:
|
||||
tpm_buf_destroy(&buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_unseal_trusted() - unseal the payload of a trusted key
|
||||
*
|
||||
* @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_send.
|
||||
*/
|
||||
int tpm2_unseal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options)
|
||||
{
|
||||
u32 blob_handle;
|
||||
int rc;
|
||||
|
||||
rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
|
||||
|
||||
return rc;
|
||||
}
|
Loading…
Reference in New Issue
Block a user