Commit 54f0e540 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 0be0ee71 0b40dbcb
* 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/acpi.h>
#include <linux/completion.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.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/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);
}
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_resume);
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,
};
......
/* 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
// 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
......
/* 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/
# 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)
......
// 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment