mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
synced 2026-05-02 18:17:50 -04:00
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris:
"Highlights:
IMA:
- provide ">" and "<" operators for fowner/uid/euid rules
KEYS:
- add a system blacklist keyring
- add KEYCTL_RESTRICT_KEYRING, exposes keyring link restriction
functionality to userland via keyctl()
LSM:
- harden LSM API with __ro_after_init
- add prlmit security hook, implement for SELinux
- revive security_task_alloc hook
TPM:
- implement contextual TPM command 'spaces'"
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (98 commits)
tpm: Fix reference count to main device
tpm_tis: convert to using locality callbacks
tpm: fix handling of the TPM 2.0 event logs
tpm_crb: remove a cruft constant
keys: select CONFIG_CRYPTO when selecting DH / KDF
apparmor: Make path_max parameter readonly
apparmor: fix parameters so that the permission test is bypassed at boot
apparmor: fix invalid reference to index variable of iterator line 836
apparmor: use SHASH_DESC_ON_STACK
security/apparmor/lsm.c: set debug messages
apparmor: fix boolreturn.cocci warnings
Smack: Use GFP_KERNEL for smk_netlbl_mls().
smack: fix double free in smack_parse_opts_str()
KEYS: add SP800-56A KDF support for DH
KEYS: Keyring asymmetric key restrict method with chaining
KEYS: Restrict asymmetric key linkage using a specific keychain
KEYS: Add a lookup_restriction function for the asymmetric key type
KEYS: Add KEYCTL_RESTRICT_KEYRING
KEYS: Consistent ordering for __key_link_begin and restrict check
KEYS: Add an optional lookup_restriction hook to key_type
...
This commit is contained in:
@@ -6,6 +6,7 @@ menuconfig TCG_TPM
|
||||
tristate "TPM Hardware Support"
|
||||
depends on HAS_IOMEM
|
||||
select SECURITYFS
|
||||
select CRYPTO
|
||||
select CRYPTO_HASH_INFO
|
||||
---help---
|
||||
If you have a TPM security chip in your system, which
|
||||
@@ -135,7 +136,7 @@ config TCG_XEN
|
||||
|
||||
config TCG_CRB
|
||||
tristate "TPM 2.0 CRB Interface"
|
||||
depends on X86 && ACPI
|
||||
depends on ACPI
|
||||
---help---
|
||||
If you have a TPM security chip that is compliant with the
|
||||
TCG CRB 2.0 TPM specification say Yes and it will be accessible
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
#
|
||||
obj-$(CONFIG_TCG_TPM) += tpm.o
|
||||
tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o \
|
||||
tpm1_eventlog.o tpm2_eventlog.o
|
||||
tpm-dev-common.o tpmrm-dev.o tpm1_eventlog.o tpm2_eventlog.o \
|
||||
tpm2-space.o
|
||||
tpm-$(CONFIG_ACPI) += tpm_ppi.o tpm_acpi.o
|
||||
tpm-$(CONFIG_OF) += tpm_of.o
|
||||
obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o
|
||||
|
||||
@@ -111,6 +111,13 @@ static const struct st33zp24_phy_ops i2c_phy_ops = {
|
||||
.recv = st33zp24_i2c_recv,
|
||||
};
|
||||
|
||||
static const struct acpi_gpio_params lpcpd_gpios = { 1, 0, false };
|
||||
|
||||
static const struct acpi_gpio_mapping acpi_st33zp24_gpios[] = {
|
||||
{ "lpcpd-gpios", &lpcpd_gpios, 1 },
|
||||
{},
|
||||
};
|
||||
|
||||
static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client)
|
||||
{
|
||||
struct tpm_chip *chip = i2c_get_clientdata(client);
|
||||
@@ -118,10 +125,14 @@ static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client)
|
||||
struct st33zp24_i2c_phy *phy = tpm_dev->phy_id;
|
||||
struct gpio_desc *gpiod_lpcpd;
|
||||
struct device *dev = &client->dev;
|
||||
int ret;
|
||||
|
||||
ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), acpi_st33zp24_gpios);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Get LPCPD GPIO from ACPI */
|
||||
gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1,
|
||||
GPIOD_OUT_HIGH);
|
||||
gpiod_lpcpd = devm_gpiod_get(dev, "lpcpd", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpiod_lpcpd)) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to retrieve lpcpd-gpios from acpi.\n");
|
||||
@@ -268,8 +279,14 @@ static int st33zp24_i2c_probe(struct i2c_client *client,
|
||||
static int st33zp24_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tpm_chip *chip = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
return st33zp24_remove(chip);
|
||||
ret = st33zp24_remove(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&client->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id st33zp24_i2c_id[] = {
|
||||
|
||||
@@ -230,6 +230,13 @@ static const struct st33zp24_phy_ops spi_phy_ops = {
|
||||
.recv = st33zp24_spi_recv,
|
||||
};
|
||||
|
||||
static const struct acpi_gpio_params lpcpd_gpios = { 1, 0, false };
|
||||
|
||||
static const struct acpi_gpio_mapping acpi_st33zp24_gpios[] = {
|
||||
{ "lpcpd-gpios", &lpcpd_gpios, 1 },
|
||||
{},
|
||||
};
|
||||
|
||||
static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev)
|
||||
{
|
||||
struct tpm_chip *chip = spi_get_drvdata(spi_dev);
|
||||
@@ -237,10 +244,14 @@ static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev)
|
||||
struct st33zp24_spi_phy *phy = tpm_dev->phy_id;
|
||||
struct gpio_desc *gpiod_lpcpd;
|
||||
struct device *dev = &spi_dev->dev;
|
||||
int ret;
|
||||
|
||||
ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), acpi_st33zp24_gpios);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Get LPCPD GPIO from ACPI */
|
||||
gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1,
|
||||
GPIOD_OUT_HIGH);
|
||||
gpiod_lpcpd = devm_gpiod_get(dev, "lpcpd", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpiod_lpcpd)) {
|
||||
dev_err(dev, "Failed to retrieve lpcpd-gpios from acpi.\n");
|
||||
phy->io_lpcpd = -1;
|
||||
@@ -385,8 +396,14 @@ static int st33zp24_spi_probe(struct spi_device *dev)
|
||||
static int st33zp24_spi_remove(struct spi_device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = spi_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
return st33zp24_remove(chip);
|
||||
ret = st33zp24_remove(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&dev->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id st33zp24_spi_id[] = {
|
||||
|
||||
@@ -117,9 +117,9 @@ static u8 st33zp24_status(struct tpm_chip *chip)
|
||||
/*
|
||||
* check_locality if the locality is active
|
||||
* @param: chip, the tpm chip description
|
||||
* @return: the active locality or -EACCESS.
|
||||
* @return: true if LOCALITY0 is active, otherwise false
|
||||
*/
|
||||
static int check_locality(struct tpm_chip *chip)
|
||||
static bool check_locality(struct tpm_chip *chip)
|
||||
{
|
||||
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
|
||||
u8 data;
|
||||
@@ -129,9 +129,9 @@ static int check_locality(struct tpm_chip *chip)
|
||||
if (status && (data &
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
|
||||
return tpm_dev->locality;
|
||||
return true;
|
||||
|
||||
return -EACCES;
|
||||
return false;
|
||||
} /* check_locality() */
|
||||
|
||||
/*
|
||||
@@ -146,7 +146,7 @@ static int request_locality(struct tpm_chip *chip)
|
||||
long ret;
|
||||
u8 data;
|
||||
|
||||
if (check_locality(chip) == tpm_dev->locality)
|
||||
if (check_locality(chip))
|
||||
return tpm_dev->locality;
|
||||
|
||||
data = TPM_ACCESS_REQUEST_USE;
|
||||
@@ -158,7 +158,7 @@ static int request_locality(struct tpm_chip *chip)
|
||||
|
||||
/* Request locality is usually effective after the request */
|
||||
do {
|
||||
if (check_locality(chip) >= 0)
|
||||
if (check_locality(chip))
|
||||
return tpm_dev->locality;
|
||||
msleep(TPM_TIMEOUT);
|
||||
} while (time_before(jiffies, stop));
|
||||
|
||||
@@ -33,6 +33,7 @@ DEFINE_IDR(dev_nums_idr);
|
||||
static DEFINE_MUTEX(idr_lock);
|
||||
|
||||
struct class *tpm_class;
|
||||
struct class *tpmrm_class;
|
||||
dev_t tpm_devt;
|
||||
|
||||
/**
|
||||
@@ -128,9 +129,19 @@ static void tpm_dev_release(struct device *dev)
|
||||
mutex_unlock(&idr_lock);
|
||||
|
||||
kfree(chip->log.bios_event_log);
|
||||
kfree(chip->work_space.context_buf);
|
||||
kfree(chip->work_space.session_buf);
|
||||
kfree(chip);
|
||||
}
|
||||
|
||||
static void tpm_devs_release(struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = container_of(dev, struct tpm_chip, devs);
|
||||
|
||||
/* release the master device reference */
|
||||
put_device(&chip->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_chip_alloc() - allocate a new struct tpm_chip instance
|
||||
* @pdev: device to which the chip is associated
|
||||
@@ -167,18 +178,36 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
|
||||
chip->dev_num = rc;
|
||||
|
||||
device_initialize(&chip->dev);
|
||||
device_initialize(&chip->devs);
|
||||
|
||||
chip->dev.class = tpm_class;
|
||||
chip->dev.release = tpm_dev_release;
|
||||
chip->dev.parent = pdev;
|
||||
chip->dev.groups = chip->groups;
|
||||
|
||||
chip->devs.parent = pdev;
|
||||
chip->devs.class = tpmrm_class;
|
||||
chip->devs.release = tpm_devs_release;
|
||||
/* get extra reference on main device to hold on
|
||||
* behalf of devs. This holds the chip structure
|
||||
* while cdevs is in use. The corresponding put
|
||||
* is in the tpm_devs_release (TPM2 only)
|
||||
*/
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2)
|
||||
get_device(&chip->dev);
|
||||
|
||||
if (chip->dev_num == 0)
|
||||
chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR);
|
||||
else
|
||||
chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num);
|
||||
|
||||
chip->devs.devt =
|
||||
MKDEV(MAJOR(tpm_devt), chip->dev_num + TPM_NUM_DEVICES);
|
||||
|
||||
rc = dev_set_name(&chip->dev, "tpm%d", chip->dev_num);
|
||||
if (rc)
|
||||
goto out;
|
||||
rc = dev_set_name(&chip->devs, "tpmrm%d", chip->dev_num);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
@@ -186,12 +215,28 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
|
||||
chip->flags |= TPM_CHIP_FLAG_VIRTUAL;
|
||||
|
||||
cdev_init(&chip->cdev, &tpm_fops);
|
||||
cdev_init(&chip->cdevs, &tpmrm_fops);
|
||||
chip->cdev.owner = THIS_MODULE;
|
||||
chip->cdevs.owner = THIS_MODULE;
|
||||
chip->cdev.kobj.parent = &chip->dev.kobj;
|
||||
chip->cdevs.kobj.parent = &chip->devs.kobj;
|
||||
|
||||
chip->work_space.context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!chip->work_space.context_buf) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
chip->work_space.session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!chip->work_space.session_buf) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
chip->locality = -1;
|
||||
return chip;
|
||||
|
||||
out:
|
||||
put_device(&chip->devs);
|
||||
put_device(&chip->dev);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
@@ -236,7 +281,6 @@ static int tpm_add_char_device(struct tpm_chip *chip)
|
||||
"unable to cdev_add() %s, major %d, minor %d, err=%d\n",
|
||||
dev_name(&chip->dev), MAJOR(chip->dev.devt),
|
||||
MINOR(chip->dev.devt), rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -251,6 +295,27 @@ static int tpm_add_char_device(struct tpm_chip *chip)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2)
|
||||
rc = cdev_add(&chip->cdevs, chip->devs.devt, 1);
|
||||
if (rc) {
|
||||
dev_err(&chip->dev,
|
||||
"unable to cdev_add() %s, major %d, minor %d, err=%d\n",
|
||||
dev_name(&chip->devs), MAJOR(chip->devs.devt),
|
||||
MINOR(chip->devs.devt), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2)
|
||||
rc = device_add(&chip->devs);
|
||||
if (rc) {
|
||||
dev_err(&chip->dev,
|
||||
"unable to device_register() %s, major %d, minor %d, err=%d\n",
|
||||
dev_name(&chip->devs), MAJOR(chip->devs.devt),
|
||||
MINOR(chip->devs.devt), rc);
|
||||
cdev_del(&chip->cdevs);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Make the chip available. */
|
||||
mutex_lock(&idr_lock);
|
||||
idr_replace(&dev_nums_idr, chip, chip->dev_num);
|
||||
@@ -384,6 +449,10 @@ void tpm_chip_unregister(struct tpm_chip *chip)
|
||||
{
|
||||
tpm_del_legacy_sysfs(chip);
|
||||
tpm_bios_log_teardown(chip);
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
|
||||
cdev_del(&chip->cdevs);
|
||||
device_del(&chip->devs);
|
||||
}
|
||||
tpm_del_char_device(chip);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_chip_unregister);
|
||||
|
||||
148
drivers/char/tpm/tpm-dev-common.c
Normal file
148
drivers/char/tpm/tpm-dev-common.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2004 IBM Corporation
|
||||
* Authors:
|
||||
* Leendert van Doorn <leendert@watson.ibm.com>
|
||||
* Dave Safford <safford@watson.ibm.com>
|
||||
* Reiner Sailer <sailer@watson.ibm.com>
|
||||
* Kylene Hall <kjhall@us.ibm.com>
|
||||
*
|
||||
* Copyright (C) 2013 Obsidian Research Corp
|
||||
* Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
|
||||
*
|
||||
* Device file system interface to the TPM
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2 of the
|
||||
* License.
|
||||
*
|
||||
*/
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include "tpm.h"
|
||||
#include "tpm-dev.h"
|
||||
|
||||
static void user_reader_timeout(unsigned long ptr)
|
||||
{
|
||||
struct file_priv *priv = (struct file_priv *)ptr;
|
||||
|
||||
pr_warn("TPM user space timeout is deprecated (pid=%d)\n",
|
||||
task_tgid_nr(current));
|
||||
|
||||
schedule_work(&priv->work);
|
||||
}
|
||||
|
||||
static void timeout_work(struct work_struct *work)
|
||||
{
|
||||
struct file_priv *priv = container_of(work, struct file_priv, work);
|
||||
|
||||
mutex_lock(&priv->buffer_mutex);
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
memset(priv->data_buffer, 0, sizeof(priv->data_buffer));
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
}
|
||||
|
||||
void tpm_common_open(struct file *file, struct tpm_chip *chip,
|
||||
struct file_priv *priv)
|
||||
{
|
||||
priv->chip = chip;
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
mutex_init(&priv->buffer_mutex);
|
||||
setup_timer(&priv->user_read_timer, user_reader_timeout,
|
||||
(unsigned long)priv);
|
||||
INIT_WORK(&priv->work, timeout_work);
|
||||
|
||||
file->private_data = priv;
|
||||
}
|
||||
|
||||
ssize_t tpm_common_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
struct file_priv *priv = file->private_data;
|
||||
ssize_t ret_size;
|
||||
ssize_t orig_ret_size;
|
||||
int rc;
|
||||
|
||||
del_singleshot_timer_sync(&priv->user_read_timer);
|
||||
flush_work(&priv->work);
|
||||
ret_size = atomic_read(&priv->data_pending);
|
||||
if (ret_size > 0) { /* relay data */
|
||||
orig_ret_size = ret_size;
|
||||
if (size < ret_size)
|
||||
ret_size = size;
|
||||
|
||||
mutex_lock(&priv->buffer_mutex);
|
||||
rc = copy_to_user(buf, priv->data_buffer, ret_size);
|
||||
memset(priv->data_buffer, 0, orig_ret_size);
|
||||
if (rc)
|
||||
ret_size = -EFAULT;
|
||||
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
}
|
||||
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
|
||||
return ret_size;
|
||||
}
|
||||
|
||||
ssize_t tpm_common_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *off, struct tpm_space *space)
|
||||
{
|
||||
struct file_priv *priv = file->private_data;
|
||||
size_t in_size = size;
|
||||
ssize_t out_size;
|
||||
|
||||
/* Cannot perform a write until the read has cleared either via
|
||||
* tpm_read or a user_read_timer timeout. This also prevents split
|
||||
* buffered writes from blocking here.
|
||||
*/
|
||||
if (atomic_read(&priv->data_pending) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
if (in_size > TPM_BUFSIZE)
|
||||
return -E2BIG;
|
||||
|
||||
mutex_lock(&priv->buffer_mutex);
|
||||
|
||||
if (copy_from_user
|
||||
(priv->data_buffer, (void __user *) buf, in_size)) {
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* atomic tpm command send and result receive. We only hold the ops
|
||||
* lock during this period so that the tpm can be unregistered even if
|
||||
* the char dev is held open.
|
||||
*/
|
||||
if (tpm_try_get_ops(priv->chip)) {
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
return -EPIPE;
|
||||
}
|
||||
out_size = tpm_transmit(priv->chip, space, priv->data_buffer,
|
||||
sizeof(priv->data_buffer), 0);
|
||||
|
||||
tpm_put_ops(priv->chip);
|
||||
if (out_size < 0) {
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
return out_size;
|
||||
}
|
||||
|
||||
atomic_set(&priv->data_pending, out_size);
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
|
||||
/* Set a timeout by which the reader must come claim the result */
|
||||
mod_timer(&priv->user_read_timer, jiffies + (120 * HZ));
|
||||
|
||||
return in_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called on file close
|
||||
*/
|
||||
void tpm_common_release(struct file *file, struct file_priv *priv)
|
||||
{
|
||||
del_singleshot_timer_sync(&priv->user_read_timer);
|
||||
flush_work(&priv->work);
|
||||
file->private_data = NULL;
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
}
|
||||
@@ -18,48 +18,15 @@
|
||||
*
|
||||
*/
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include "tpm.h"
|
||||
|
||||
struct file_priv {
|
||||
struct tpm_chip *chip;
|
||||
|
||||
/* Data passed to and from the tpm via the read/write calls */
|
||||
atomic_t data_pending;
|
||||
struct mutex buffer_mutex;
|
||||
|
||||
struct timer_list user_read_timer; /* user needs to claim result */
|
||||
struct work_struct work;
|
||||
|
||||
u8 data_buffer[TPM_BUFSIZE];
|
||||
};
|
||||
|
||||
static void user_reader_timeout(unsigned long ptr)
|
||||
{
|
||||
struct file_priv *priv = (struct file_priv *)ptr;
|
||||
|
||||
pr_warn("TPM user space timeout is deprecated (pid=%d)\n",
|
||||
task_tgid_nr(current));
|
||||
|
||||
schedule_work(&priv->work);
|
||||
}
|
||||
|
||||
static void timeout_work(struct work_struct *work)
|
||||
{
|
||||
struct file_priv *priv = container_of(work, struct file_priv, work);
|
||||
|
||||
mutex_lock(&priv->buffer_mutex);
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
memset(priv->data_buffer, 0, sizeof(priv->data_buffer));
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
}
|
||||
#include "tpm-dev.h"
|
||||
|
||||
static int tpm_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct tpm_chip *chip =
|
||||
container_of(inode->i_cdev, struct tpm_chip, cdev);
|
||||
struct tpm_chip *chip;
|
||||
struct file_priv *priv;
|
||||
|
||||
chip = container_of(inode->i_cdev, struct tpm_chip, cdev);
|
||||
|
||||
/* It's assured that the chip will be opened just once,
|
||||
* by the check of is_open variable, which is protected
|
||||
* by driver_lock. */
|
||||
@@ -69,100 +36,22 @@ static int tpm_open(struct inode *inode, struct file *file)
|
||||
}
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (priv == NULL) {
|
||||
clear_bit(0, &chip->is_open);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (priv == NULL)
|
||||
goto out;
|
||||
|
||||
priv->chip = chip;
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
mutex_init(&priv->buffer_mutex);
|
||||
setup_timer(&priv->user_read_timer, user_reader_timeout,
|
||||
(unsigned long)priv);
|
||||
INIT_WORK(&priv->work, timeout_work);
|
||||
tpm_common_open(file, chip, priv);
|
||||
|
||||
file->private_data = priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t tpm_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
struct file_priv *priv = file->private_data;
|
||||
ssize_t ret_size;
|
||||
int rc;
|
||||
|
||||
del_singleshot_timer_sync(&priv->user_read_timer);
|
||||
flush_work(&priv->work);
|
||||
ret_size = atomic_read(&priv->data_pending);
|
||||
if (ret_size > 0) { /* relay data */
|
||||
ssize_t orig_ret_size = ret_size;
|
||||
if (size < ret_size)
|
||||
ret_size = size;
|
||||
|
||||
mutex_lock(&priv->buffer_mutex);
|
||||
rc = copy_to_user(buf, priv->data_buffer, ret_size);
|
||||
memset(priv->data_buffer, 0, orig_ret_size);
|
||||
if (rc)
|
||||
ret_size = -EFAULT;
|
||||
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
}
|
||||
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
|
||||
return ret_size;
|
||||
out:
|
||||
clear_bit(0, &chip->is_open);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static ssize_t tpm_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
struct file_priv *priv = file->private_data;
|
||||
size_t in_size = size;
|
||||
ssize_t out_size;
|
||||
|
||||
/* cannot perform a write until the read has cleared
|
||||
either via tpm_read or a user_read_timer timeout.
|
||||
This also prevents splitted buffered writes from blocking here.
|
||||
*/
|
||||
if (atomic_read(&priv->data_pending) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
if (in_size > TPM_BUFSIZE)
|
||||
return -E2BIG;
|
||||
|
||||
mutex_lock(&priv->buffer_mutex);
|
||||
|
||||
if (copy_from_user
|
||||
(priv->data_buffer, (void __user *) buf, in_size)) {
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* atomic tpm command send and result receive. We only hold the ops
|
||||
* lock during this period so that the tpm can be unregistered even if
|
||||
* the char dev is held open.
|
||||
*/
|
||||
if (tpm_try_get_ops(priv->chip)) {
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
return -EPIPE;
|
||||
}
|
||||
out_size = tpm_transmit(priv->chip, priv->data_buffer,
|
||||
sizeof(priv->data_buffer), 0);
|
||||
|
||||
tpm_put_ops(priv->chip);
|
||||
if (out_size < 0) {
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
return out_size;
|
||||
}
|
||||
|
||||
atomic_set(&priv->data_pending, out_size);
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
|
||||
/* Set a timeout by which the reader must come claim the result */
|
||||
mod_timer(&priv->user_read_timer, jiffies + (120 * HZ));
|
||||
|
||||
return in_size;
|
||||
return tpm_common_write(file, buf, size, off, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -172,12 +61,10 @@ static int tpm_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct file_priv *priv = file->private_data;
|
||||
|
||||
del_singleshot_timer_sync(&priv->user_read_timer);
|
||||
flush_work(&priv->work);
|
||||
file->private_data = NULL;
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
tpm_common_release(file, priv);
|
||||
clear_bit(0, &priv->chip->is_open);
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -185,9 +72,7 @@ const struct file_operations tpm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = tpm_open,
|
||||
.read = tpm_read,
|
||||
.read = tpm_common_read,
|
||||
.write = tpm_write,
|
||||
.release = tpm_release,
|
||||
};
|
||||
|
||||
|
||||
|
||||
27
drivers/char/tpm/tpm-dev.h
Normal file
27
drivers/char/tpm/tpm-dev.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef _TPM_DEV_H
|
||||
#define _TPM_DEV_H
|
||||
|
||||
#include "tpm.h"
|
||||
|
||||
struct file_priv {
|
||||
struct tpm_chip *chip;
|
||||
|
||||
/* Data passed to and from the tpm via the read/write calls */
|
||||
atomic_t data_pending;
|
||||
struct mutex buffer_mutex;
|
||||
|
||||
struct timer_list user_read_timer; /* user needs to claim result */
|
||||
struct work_struct work;
|
||||
|
||||
u8 data_buffer[TPM_BUFSIZE];
|
||||
};
|
||||
|
||||
void tpm_common_open(struct file *file, struct tpm_chip *chip,
|
||||
struct file_priv *priv);
|
||||
ssize_t tpm_common_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *off);
|
||||
ssize_t tpm_common_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *off, struct tpm_space *space);
|
||||
void tpm_common_release(struct file *file, struct file_priv *priv);
|
||||
|
||||
#endif
|
||||
@@ -328,6 +328,47 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
|
||||
|
||||
static bool tpm_validate_command(struct tpm_chip *chip,
|
||||
struct tpm_space *space,
|
||||
const u8 *cmd,
|
||||
size_t len)
|
||||
{
|
||||
const struct tpm_input_header *header = (const void *)cmd;
|
||||
int i;
|
||||
u32 cc;
|
||||
u32 attrs;
|
||||
unsigned int nr_handles;
|
||||
|
||||
if (len < TPM_HEADER_SIZE)
|
||||
return false;
|
||||
|
||||
if (!space)
|
||||
return true;
|
||||
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2 && chip->nr_commands) {
|
||||
cc = be32_to_cpu(header->ordinal);
|
||||
|
||||
i = tpm2_find_cc(chip, cc);
|
||||
if (i < 0) {
|
||||
dev_dbg(&chip->dev, "0x%04X is an invalid command\n",
|
||||
cc);
|
||||
return false;
|
||||
}
|
||||
|
||||
attrs = chip->cc_attrs_tbl[i];
|
||||
nr_handles =
|
||||
4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0));
|
||||
if (len < TPM_HEADER_SIZE + 4 * nr_handles)
|
||||
goto err_len;
|
||||
}
|
||||
|
||||
return true;
|
||||
err_len:
|
||||
dev_dbg(&chip->dev,
|
||||
"%s: insufficient command length %zu", __func__, len);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* tmp_transmit - Internal kernel interface to transmit TPM commands.
|
||||
*
|
||||
@@ -340,14 +381,17 @@ EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
|
||||
* 0 when the operation is successful.
|
||||
* A negative number for system errors (errno).
|
||||
*/
|
||||
ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
|
||||
unsigned int flags)
|
||||
ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
||||
u8 *buf, size_t bufsiz, unsigned int flags)
|
||||
{
|
||||
ssize_t rc;
|
||||
struct tpm_output_header *header = (void *)buf;
|
||||
int rc;
|
||||
ssize_t len = 0;
|
||||
u32 count, ordinal;
|
||||
unsigned long stop;
|
||||
bool need_locality;
|
||||
|
||||
if (bufsiz < TPM_HEADER_SIZE)
|
||||
if (!tpm_validate_command(chip, space, buf, bufsiz))
|
||||
return -EINVAL;
|
||||
|
||||
if (bufsiz > TPM_BUFSIZE)
|
||||
@@ -369,10 +413,24 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
|
||||
if (chip->dev.parent)
|
||||
pm_runtime_get_sync(chip->dev.parent);
|
||||
|
||||
/* Store the decision as chip->locality will be changed. */
|
||||
need_locality = chip->locality == -1;
|
||||
|
||||
if (need_locality && chip->ops->request_locality) {
|
||||
rc = chip->ops->request_locality(chip, 0);
|
||||
if (rc < 0)
|
||||
goto out_no_locality;
|
||||
chip->locality = rc;
|
||||
}
|
||||
|
||||
rc = tpm2_prepare_space(chip, space, ordinal, buf);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = chip->ops->send(chip, (u8 *) buf, count);
|
||||
if (rc < 0) {
|
||||
dev_err(&chip->dev,
|
||||
"tpm_transmit: tpm_send: error %zd\n", rc);
|
||||
"tpm_transmit: tpm_send: error %d\n", rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -405,17 +463,36 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
|
||||
goto out;
|
||||
|
||||
out_recv:
|
||||
rc = chip->ops->recv(chip, (u8 *) buf, bufsiz);
|
||||
if (rc < 0)
|
||||
len = chip->ops->recv(chip, (u8 *) buf, bufsiz);
|
||||
if (len < 0) {
|
||||
rc = len;
|
||||
dev_err(&chip->dev,
|
||||
"tpm_transmit: tpm_recv: error %zd\n", rc);
|
||||
"tpm_transmit: tpm_recv: error %d\n", rc);
|
||||
goto out;
|
||||
} else if (len < TPM_HEADER_SIZE) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len != be32_to_cpu(header->length)) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = tpm2_commit_space(chip, space, ordinal, buf, &len);
|
||||
|
||||
out:
|
||||
if (need_locality && chip->ops->relinquish_locality) {
|
||||
chip->ops->relinquish_locality(chip, chip->locality);
|
||||
chip->locality = -1;
|
||||
}
|
||||
out_no_locality:
|
||||
if (chip->dev.parent)
|
||||
pm_runtime_put_sync(chip->dev.parent);
|
||||
|
||||
if (!(flags & TPM_TRANSMIT_UNLOCKED))
|
||||
mutex_unlock(&chip->tpm_mutex);
|
||||
return rc;
|
||||
return rc ? rc : len;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -434,23 +511,18 @@ out:
|
||||
* A negative number for system errors (errno).
|
||||
* A positive number for a TPM error.
|
||||
*/
|
||||
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *buf,
|
||||
size_t bufsiz, size_t min_rsp_body_length,
|
||||
unsigned int flags, const char *desc)
|
||||
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
|
||||
const void *buf, size_t bufsiz,
|
||||
size_t min_rsp_body_length, unsigned int flags,
|
||||
const char *desc)
|
||||
{
|
||||
const struct tpm_output_header *header;
|
||||
const struct tpm_output_header *header = buf;
|
||||
int err;
|
||||
ssize_t len;
|
||||
|
||||
len = tpm_transmit(chip, (const u8 *)buf, bufsiz, flags);
|
||||
len = tpm_transmit(chip, space, (u8 *)buf, bufsiz, flags);
|
||||
if (len < 0)
|
||||
return len;
|
||||
else if (len < TPM_HEADER_SIZE)
|
||||
return -EFAULT;
|
||||
|
||||
header = buf;
|
||||
if (len != be32_to_cpu(header->length))
|
||||
return -EFAULT;
|
||||
|
||||
err = be32_to_cpu(header->return_code);
|
||||
if (err != 0 && desc)
|
||||
@@ -501,7 +573,7 @@ ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
|
||||
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
|
||||
tpm_cmd.params.getcap_in.subcap = cpu_to_be32(subcap_id);
|
||||
}
|
||||
rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
|
||||
rc = tpm_transmit_cmd(chip, NULL, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
|
||||
min_cap_length, 0, desc);
|
||||
if (!rc)
|
||||
*cap = tpm_cmd.params.getcap_out.cap;
|
||||
@@ -525,7 +597,8 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
|
||||
start_cmd.header.in = tpm_startup_header;
|
||||
|
||||
start_cmd.params.startup_in.startup_type = startup_type;
|
||||
return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
|
||||
return tpm_transmit_cmd(chip, NULL, &start_cmd,
|
||||
TPM_INTERNAL_RESULT_SIZE, 0,
|
||||
0, "attempting to start the TPM");
|
||||
}
|
||||
|
||||
@@ -682,8 +755,8 @@ static int tpm_continue_selftest(struct tpm_chip *chip)
|
||||
struct tpm_cmd_t cmd;
|
||||
|
||||
cmd.header.in = continue_selftest_header;
|
||||
rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, 0, 0,
|
||||
"continue selftest");
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, CONTINUE_SELFTEST_RESULT_SIZE,
|
||||
0, 0, "continue selftest");
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -703,7 +776,7 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
|
||||
|
||||
cmd.header.in = pcrread_header;
|
||||
cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx);
|
||||
rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE,
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, READ_PCR_RESULT_SIZE,
|
||||
READ_PCR_RESULT_BODY_SIZE, 0,
|
||||
"attempting to read a pcr value");
|
||||
|
||||
@@ -815,7 +888,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
|
||||
cmd.header.in = pcrextend_header;
|
||||
cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx);
|
||||
memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE);
|
||||
rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, EXTEND_PCR_RESULT_SIZE,
|
||||
EXTEND_PCR_RESULT_BODY_SIZE, 0,
|
||||
"attempting extend a PCR value");
|
||||
|
||||
@@ -920,8 +993,8 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen)
|
||||
if (chip == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
rc = tpm_transmit_cmd(chip, cmd, buflen, 0, 0, "attempting tpm_cmd");
|
||||
|
||||
rc = tpm_transmit_cmd(chip, NULL, cmd, buflen, 0, 0,
|
||||
"attempting tpm_cmd");
|
||||
tpm_put_ops(chip);
|
||||
return rc;
|
||||
}
|
||||
@@ -1022,16 +1095,16 @@ int tpm_pm_suspend(struct device *dev)
|
||||
cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(tpm_suspend_pcr);
|
||||
memcpy(cmd.params.pcrextend_in.hash, dummy_hash,
|
||||
TPM_DIGEST_SIZE);
|
||||
rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
|
||||
EXTEND_PCR_RESULT_BODY_SIZE, 0,
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, EXTEND_PCR_RESULT_SIZE,
|
||||
EXTEND_PCR_RESULT_BODY_SIZE, 0,
|
||||
"extending dummy pcr before suspend");
|
||||
}
|
||||
|
||||
/* now do the actual savestate */
|
||||
for (try = 0; try < TPM_RETRY; try++) {
|
||||
cmd.header.in = savestate_header;
|
||||
rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, 0,
|
||||
0, NULL);
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, SAVESTATE_RESULT_SIZE,
|
||||
0, 0, NULL);
|
||||
|
||||
/*
|
||||
* If the TPM indicates that it is too busy to respond to
|
||||
@@ -1114,7 +1187,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
|
||||
tpm_cmd.header.in = tpm_getrandom_header;
|
||||
tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes);
|
||||
|
||||
err = tpm_transmit_cmd(chip, &tpm_cmd,
|
||||
err = tpm_transmit_cmd(chip, NULL, &tpm_cmd,
|
||||
TPM_GETRANDOM_RESULT_SIZE + num_bytes,
|
||||
offsetof(struct tpm_getrandom_out,
|
||||
rng_data),
|
||||
@@ -1205,9 +1278,17 @@ static int __init tpm_init(void)
|
||||
return PTR_ERR(tpm_class);
|
||||
}
|
||||
|
||||
rc = alloc_chrdev_region(&tpm_devt, 0, TPM_NUM_DEVICES, "tpm");
|
||||
tpmrm_class = class_create(THIS_MODULE, "tpmrm");
|
||||
if (IS_ERR(tpmrm_class)) {
|
||||
pr_err("couldn't create tpmrm class\n");
|
||||
class_destroy(tpm_class);
|
||||
return PTR_ERR(tpmrm_class);
|
||||
}
|
||||
|
||||
rc = alloc_chrdev_region(&tpm_devt, 0, 2*TPM_NUM_DEVICES, "tpm");
|
||||
if (rc < 0) {
|
||||
pr_err("tpm: failed to allocate char dev region\n");
|
||||
class_destroy(tpmrm_class);
|
||||
class_destroy(tpm_class);
|
||||
return rc;
|
||||
}
|
||||
@@ -1219,7 +1300,8 @@ static void __exit tpm_exit(void)
|
||||
{
|
||||
idr_destroy(&dev_nums_idr);
|
||||
class_destroy(tpm_class);
|
||||
unregister_chrdev_region(tpm_devt, TPM_NUM_DEVICES);
|
||||
class_destroy(tpmrm_class);
|
||||
unregister_chrdev_region(tpm_devt, 2*TPM_NUM_DEVICES);
|
||||
}
|
||||
|
||||
subsys_initcall(tpm_init);
|
||||
|
||||
@@ -40,7 +40,7 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
|
||||
struct tpm_chip *chip = to_tpm_chip(dev);
|
||||
|
||||
tpm_cmd.header.in = tpm_readpubek_header;
|
||||
err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
|
||||
err = tpm_transmit_cmd(chip, NULL, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
|
||||
READ_PUBEK_RESULT_MIN_BODY_SIZE, 0,
|
||||
"attempting to read the PUBEK");
|
||||
if (err)
|
||||
|
||||
@@ -89,10 +89,13 @@ enum tpm2_structures {
|
||||
};
|
||||
|
||||
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_DISABLED = 0x0120,
|
||||
TPM2_RC_TESTING = 0x090A, /* RC_WARN */
|
||||
TPM2_RC_REFERENCE_H0 = 0x0910,
|
||||
};
|
||||
|
||||
enum tpm2_algorithms {
|
||||
@@ -114,6 +117,8 @@ enum tpm2_command_codes {
|
||||
TPM2_CC_CREATE = 0x0153,
|
||||
TPM2_CC_LOAD = 0x0157,
|
||||
TPM2_CC_UNSEAL = 0x015E,
|
||||
TPM2_CC_CONTEXT_LOAD = 0x0161,
|
||||
TPM2_CC_CONTEXT_SAVE = 0x0162,
|
||||
TPM2_CC_FLUSH_CONTEXT = 0x0165,
|
||||
TPM2_CC_GET_CAPABILITY = 0x017A,
|
||||
TPM2_CC_GET_RANDOM = 0x017B,
|
||||
@@ -127,21 +132,39 @@ enum tpm2_permanent_handles {
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
#define TPM_PPI_VERSION_LEN 3
|
||||
|
||||
struct tpm_space {
|
||||
u32 context_tbl[3];
|
||||
u8 *context_buf;
|
||||
u32 session_tbl[3];
|
||||
u8 *session_buf;
|
||||
};
|
||||
|
||||
enum tpm_chip_flags {
|
||||
TPM_CHIP_FLAG_TPM2 = BIT(1),
|
||||
TPM_CHIP_FLAG_IRQ = BIT(2),
|
||||
@@ -161,7 +184,9 @@ struct tpm_chip_seqops {
|
||||
|
||||
struct tpm_chip {
|
||||
struct device dev;
|
||||
struct device devs;
|
||||
struct cdev cdev;
|
||||
struct cdev cdevs;
|
||||
|
||||
/* A driver callback under ops cannot be run unless ops_sem is held
|
||||
* (sometimes implicitly, eg for the sysfs code). ops becomes null
|
||||
@@ -199,6 +224,13 @@ struct tpm_chip {
|
||||
acpi_handle acpi_dev_handle;
|
||||
char ppi_version[TPM_PPI_VERSION_LEN + 1];
|
||||
#endif /* CONFIG_ACPI */
|
||||
|
||||
struct tpm_space work_space;
|
||||
u32 nr_commands;
|
||||
u32 *cc_attrs_tbl;
|
||||
|
||||
/* active locality */
|
||||
int locality;
|
||||
};
|
||||
|
||||
#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
|
||||
@@ -485,18 +517,21 @@ static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
|
||||
}
|
||||
|
||||
extern struct class *tpm_class;
|
||||
extern struct class *tpmrm_class;
|
||||
extern dev_t tpm_devt;
|
||||
extern const struct file_operations tpm_fops;
|
||||
extern const struct file_operations tpmrm_fops;
|
||||
extern struct idr dev_nums_idr;
|
||||
|
||||
enum tpm_transmit_flags {
|
||||
TPM_TRANSMIT_UNLOCKED = BIT(0),
|
||||
};
|
||||
|
||||
ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
|
||||
unsigned int flags);
|
||||
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *buf, size_t bufsiz,
|
||||
size_t min_rsp_body_len, unsigned int flags,
|
||||
ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
||||
u8 *buf, size_t bufsiz, unsigned int flags);
|
||||
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
|
||||
const void *buf, size_t bufsiz,
|
||||
size_t min_rsp_body_length, unsigned int flags,
|
||||
const char *desc);
|
||||
ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
|
||||
const char *desc, size_t min_cap_length);
|
||||
@@ -541,6 +576,8 @@ int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf);
|
||||
int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, u32 count,
|
||||
struct tpm2_digest *digests);
|
||||
int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max);
|
||||
void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
|
||||
unsigned int flags);
|
||||
int tpm2_seal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options);
|
||||
@@ -554,4 +591,11 @@ int tpm2_auto_startup(struct tpm_chip *chip);
|
||||
void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type);
|
||||
unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
|
||||
int tpm2_probe(struct tpm_chip *chip);
|
||||
int tpm2_find_cc(struct tpm_chip *chip, u32 cc);
|
||||
int tpm2_init_space(struct tpm_space *space);
|
||||
void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space);
|
||||
int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
|
||||
u8 *cmd);
|
||||
int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
|
||||
u32 cc, u8 *buf, size_t *bufsiz);
|
||||
#endif
|
||||
|
||||
@@ -266,7 +266,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
|
||||
sizeof(cmd.params.pcrread_in.pcr_select));
|
||||
cmd.params.pcrread_in.pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd),
|
||||
TPM2_PCR_READ_RESP_BODY_SIZE,
|
||||
0, "attempting to read a pcr value");
|
||||
if (rc == 0) {
|
||||
@@ -333,7 +333,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, u32 count,
|
||||
}
|
||||
}
|
||||
|
||||
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 0, 0,
|
||||
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
|
||||
"attempting extend a PCR value");
|
||||
|
||||
tpm_buf_destroy(&buf);
|
||||
@@ -382,7 +382,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max)
|
||||
cmd.header.in = tpm2_getrandom_header;
|
||||
cmd.params.getrandom_in.size = cpu_to_be16(num_bytes);
|
||||
|
||||
err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
|
||||
err = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd),
|
||||
offsetof(struct tpm2_get_random_out,
|
||||
buffer),
|
||||
0, "attempting get random");
|
||||
@@ -418,6 +418,35 @@ static const struct tpm_input_header tpm2_get_tpm_pt_header = {
|
||||
.ordinal = cpu_to_be32(TPM2_CC_GET_CAPABILITY)
|
||||
};
|
||||
|
||||
/**
|
||||
* tpm2_flush_context_cmd() - execute a TPM2_FlushContext command
|
||||
* @chip: TPM chip to use
|
||||
* @payload: the key data in clear and encrypted form
|
||||
* @options: authentication values and other options
|
||||
*
|
||||
* Return: same as with tpm_transmit_cmd
|
||||
*/
|
||||
void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct tpm_buf buf;
|
||||
int rc;
|
||||
|
||||
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
|
||||
if (rc) {
|
||||
dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
|
||||
handle);
|
||||
return;
|
||||
}
|
||||
|
||||
tpm_buf_append_u32(&buf, handle);
|
||||
|
||||
(void) tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, flags,
|
||||
"flushing context");
|
||||
|
||||
tpm_buf_destroy(&buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
|
||||
*
|
||||
@@ -528,7 +557,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 4, 0,
|
||||
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, 0,
|
||||
"sealing data");
|
||||
if (rc)
|
||||
goto out;
|
||||
@@ -612,7 +641,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 4, flags,
|
||||
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, flags,
|
||||
"loading blob");
|
||||
if (!rc)
|
||||
*blob_handle = be32_to_cpup(
|
||||
@@ -627,39 +656,6 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_flush_context_cmd() - execute a TPM2_FlushContext command
|
||||
*
|
||||
* @chip: TPM chip to use
|
||||
* @handle: the key data in clear and encrypted form
|
||||
* @flags: tpm transmit flags
|
||||
*
|
||||
* Return: Same as with tpm_transmit_cmd.
|
||||
*/
|
||||
static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct tpm_buf buf;
|
||||
int rc;
|
||||
|
||||
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
|
||||
if (rc) {
|
||||
dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
|
||||
handle);
|
||||
return;
|
||||
}
|
||||
|
||||
tpm_buf_append_u32(&buf, handle);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 0, flags,
|
||||
"flushing context");
|
||||
if (rc)
|
||||
dev_warn(&chip->dev, "0x%08x was not flushed, rc=%d\n", handle,
|
||||
rc);
|
||||
|
||||
tpm_buf_destroy(&buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_unseal_cmd() - execute a TPM2_Unload command
|
||||
*
|
||||
@@ -697,7 +693,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
|
||||
options->blobauth /* hmac */,
|
||||
TPM_DIGEST_SIZE);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 6, flags,
|
||||
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 6, flags,
|
||||
"unsealing");
|
||||
if (rc > 0)
|
||||
rc = -EPERM;
|
||||
@@ -774,7 +770,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
|
||||
cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(property_id);
|
||||
cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd),
|
||||
TPM2_GET_TPM_PT_OUT_BODY_SIZE, 0, desc);
|
||||
if (!rc)
|
||||
*value = be32_to_cpu(cmd.params.get_tpm_pt_out.value);
|
||||
@@ -809,7 +805,7 @@ static int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
|
||||
cmd.header.in = tpm2_startup_header;
|
||||
|
||||
cmd.params.startup_in.startup_type = cpu_to_be16(startup_type);
|
||||
return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0,
|
||||
return tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0,
|
||||
"attempting to start the TPM");
|
||||
}
|
||||
|
||||
@@ -838,7 +834,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
|
||||
cmd.header.in = tpm2_shutdown_header;
|
||||
cmd.params.startup_in.startup_type = cpu_to_be16(shutdown_type);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0,
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0,
|
||||
"stopping the TPM");
|
||||
|
||||
/* In places where shutdown command is sent there's no much we can do
|
||||
@@ -902,7 +898,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
|
||||
cmd.header.in = tpm2_selftest_header;
|
||||
cmd.params.selftest_in.full_test = full;
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE, 0, 0,
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, TPM2_SELF_TEST_IN_SIZE, 0, 0,
|
||||
"continue selftest");
|
||||
|
||||
/* At least some prototype chips seem to give RC_TESTING error
|
||||
@@ -953,7 +949,8 @@ static int tpm2_do_selftest(struct tpm_chip *chip)
|
||||
cmd.params.pcrread_in.pcr_select[1] = 0x00;
|
||||
cmd.params.pcrread_in.pcr_select[2] = 0x00;
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0, NULL);
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0,
|
||||
NULL);
|
||||
if (rc < 0)
|
||||
break;
|
||||
|
||||
@@ -986,7 +983,7 @@ int tpm2_probe(struct tpm_chip *chip)
|
||||
cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(0x100);
|
||||
cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0, NULL);
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0, NULL);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
@@ -1024,7 +1021,7 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
|
||||
tpm_buf_append_u32(&buf, 0);
|
||||
tpm_buf_append_u32(&buf, 1);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 9, 0,
|
||||
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 9, 0,
|
||||
"get tpm pcr allocation");
|
||||
if (rc)
|
||||
goto out;
|
||||
@@ -1067,15 +1064,76 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_buf buf;
|
||||
u32 nr_commands;
|
||||
u32 *attrs;
|
||||
u32 cc;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
if (nr_commands > 0xFFFFF) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
chip->cc_attrs_tbl = devm_kzalloc(&chip->dev, 4 * nr_commands,
|
||||
GFP_KERNEL);
|
||||
|
||||
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
tpm_buf_append_u32(&buf, TPM2_CAP_COMMANDS);
|
||||
tpm_buf_append_u32(&buf, TPM2_CC_FIRST);
|
||||
tpm_buf_append_u32(&buf, nr_commands);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE,
|
||||
9 + 4 * nr_commands, 0, NULL);
|
||||
if (rc) {
|
||||
tpm_buf_destroy(&buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (nr_commands !=
|
||||
be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5])) {
|
||||
tpm_buf_destroy(&buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
chip->nr_commands = nr_commands;
|
||||
|
||||
attrs = (u32 *)&buf.data[TPM_HEADER_SIZE + 9];
|
||||
for (i = 0; i < nr_commands; i++, attrs++) {
|
||||
chip->cc_attrs_tbl[i] = be32_to_cpup(attrs);
|
||||
cc = chip->cc_attrs_tbl[i] & 0xFFFF;
|
||||
|
||||
if (cc == TPM2_CC_CONTEXT_SAVE || cc == TPM2_CC_FLUSH_CONTEXT) {
|
||||
chip->cc_attrs_tbl[i] &=
|
||||
~(GENMASK(2, 0) << TPM2_CC_ATTR_CHANDLES);
|
||||
chip->cc_attrs_tbl[i] |= 1 << TPM2_CC_ATTR_CHANDLES;
|
||||
}
|
||||
}
|
||||
|
||||
tpm_buf_destroy(&buf);
|
||||
|
||||
out:
|
||||
if (rc > 0)
|
||||
rc = -ENODEV;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_auto_startup - Perform the standard automatic TPM initialization
|
||||
* sequence
|
||||
* @chip: TPM chip to use
|
||||
*
|
||||
* Initializes timeout values for operation and command durations, conducts
|
||||
* a self-test and reads the list of active PCR banks.
|
||||
*
|
||||
* Return: 0 on success. Otherwise, a system error code is returned.
|
||||
* Returns 0 on success, < 0 in case of fatal error.
|
||||
*/
|
||||
int tpm2_auto_startup(struct tpm_chip *chip)
|
||||
{
|
||||
@@ -1104,9 +1162,24 @@ int tpm2_auto_startup(struct tpm_chip *chip)
|
||||
}
|
||||
|
||||
rc = tpm2_get_pcr_allocation(chip);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = tpm2_get_cc_attrs_tbl(chip);
|
||||
|
||||
out:
|
||||
if (rc > 0)
|
||||
rc = -ENODEV;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int tpm2_find_cc(struct tpm_chip *chip, u32 cc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < chip->nr_commands; i++)
|
||||
if (cc == (chip->cc_attrs_tbl[i] & GENMASK(15, 0)))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
528
drivers/char/tpm/tpm2-space.c
Normal file
528
drivers/char/tpm/tpm2-space.c
Normal file
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Intel Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
|
||||
*
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
* This file contains TPM2 protocol implementations of the commands
|
||||
* used by the kernel internally.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*/
|
||||
|
||||
#include <linux/gfp.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "tpm.h"
|
||||
|
||||
enum tpm2_handle_types {
|
||||
TPM2_HT_HMAC_SESSION = 0x02000000,
|
||||
TPM2_HT_POLICY_SESSION = 0x03000000,
|
||||
TPM2_HT_TRANSIENT = 0x80000000,
|
||||
};
|
||||
|
||||
struct tpm2_context {
|
||||
__be64 sequence;
|
||||
__be32 saved_handle;
|
||||
__be32 hierarchy;
|
||||
__be16 blob_size;
|
||||
} __packed;
|
||||
|
||||
static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
|
||||
if (space->session_tbl[i])
|
||||
tpm2_flush_context_cmd(chip, space->session_tbl[i],
|
||||
TPM_TRANSMIT_UNLOCKED);
|
||||
}
|
||||
}
|
||||
|
||||
int tpm2_init_space(struct tpm_space *space)
|
||||
{
|
||||
space->context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!space->context_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
space->session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (space->session_buf == NULL) {
|
||||
kfree(space->context_buf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
|
||||
{
|
||||
mutex_lock(&chip->tpm_mutex);
|
||||
tpm2_flush_sessions(chip, space);
|
||||
mutex_unlock(&chip->tpm_mutex);
|
||||
kfree(space->context_buf);
|
||||
kfree(space->session_buf);
|
||||
}
|
||||
|
||||
static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
|
||||
unsigned int *offset, u32 *handle)
|
||||
{
|
||||
struct tpm_buf tbuf;
|
||||
struct tpm2_context *ctx;
|
||||
unsigned int body_size;
|
||||
int rc;
|
||||
|
||||
rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
ctx = (struct tpm2_context *)&buf[*offset];
|
||||
body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
|
||||
tpm_buf_append(&tbuf, &buf[*offset], body_size);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 4,
|
||||
TPM_TRANSMIT_UNLOCKED, NULL);
|
||||
if (rc < 0) {
|
||||
dev_warn(&chip->dev, "%s: failed with a system error %d\n",
|
||||
__func__, rc);
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return -EFAULT;
|
||||
} else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||
|
||||
rc == TPM2_RC_REFERENCE_H0) {
|
||||
/*
|
||||
* TPM_RC_HANDLE means that the session context can't
|
||||
* be loaded because of an internal counter mismatch
|
||||
* that makes the TPM think there might have been a
|
||||
* replay. This might happen if the context was saved
|
||||
* and loaded outside the space.
|
||||
*
|
||||
* TPM_RC_REFERENCE_H0 means the session has been
|
||||
* flushed outside the space
|
||||
*/
|
||||
rc = -ENOENT;
|
||||
tpm_buf_destroy(&tbuf);
|
||||
} else if (rc > 0) {
|
||||
dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
|
||||
__func__, rc);
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
|
||||
*offset += body_size;
|
||||
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
|
||||
unsigned int buf_size, unsigned int *offset)
|
||||
{
|
||||
struct tpm_buf tbuf;
|
||||
unsigned int body_size;
|
||||
int rc;
|
||||
|
||||
rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
tpm_buf_append_u32(&tbuf, handle);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 0,
|
||||
TPM_TRANSMIT_UNLOCKED, NULL);
|
||||
if (rc < 0) {
|
||||
dev_warn(&chip->dev, "%s: failed with a system error %d\n",
|
||||
__func__, rc);
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return -EFAULT;
|
||||
} else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return -ENOENT;
|
||||
} else if (rc) {
|
||||
dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
|
||||
__func__, rc);
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
|
||||
if ((*offset + body_size) > buf_size) {
|
||||
dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
|
||||
*offset += body_size;
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpm2_flush_space(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++)
|
||||
if (space->context_tbl[i] && ~space->context_tbl[i])
|
||||
tpm2_flush_context_cmd(chip, space->context_tbl[i],
|
||||
TPM_TRANSMIT_UNLOCKED);
|
||||
|
||||
tpm2_flush_sessions(chip, space);
|
||||
}
|
||||
|
||||
static int tpm2_load_space(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
unsigned int offset;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
|
||||
if (!space->context_tbl[i])
|
||||
continue;
|
||||
|
||||
/* sanity check, should never happen */
|
||||
if (~space->context_tbl[i]) {
|
||||
dev_err(&chip->dev, "context table is inconsistent");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
rc = tpm2_load_context(chip, space->context_buf, &offset,
|
||||
&space->context_tbl[i]);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
|
||||
u32 handle;
|
||||
|
||||
if (!space->session_tbl[i])
|
||||
continue;
|
||||
|
||||
rc = tpm2_load_context(chip, space->session_buf,
|
||||
&offset, &handle);
|
||||
if (rc == -ENOENT) {
|
||||
/* load failed, just forget session */
|
||||
space->session_tbl[i] = 0;
|
||||
} else if (rc) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
if (handle != space->session_tbl[i]) {
|
||||
dev_warn(&chip->dev, "session restored to wrong handle\n");
|
||||
tpm2_flush_space(chip);
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool tpm2_map_to_phandle(struct tpm_space *space, void *handle)
|
||||
{
|
||||
u32 vhandle = be32_to_cpup((__be32 *)handle);
|
||||
u32 phandle;
|
||||
int i;
|
||||
|
||||
i = 0xFFFFFF - (vhandle & 0xFFFFFF);
|
||||
if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i])
|
||||
return false;
|
||||
|
||||
phandle = space->context_tbl[i];
|
||||
*((__be32 *)handle) = cpu_to_be32(phandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
unsigned int nr_handles;
|
||||
u32 attrs;
|
||||
u32 *handle;
|
||||
int i;
|
||||
|
||||
i = tpm2_find_cc(chip, cc);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
|
||||
attrs = chip->cc_attrs_tbl[i];
|
||||
nr_handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
|
||||
|
||||
handle = (u32 *)&cmd[TPM_HEADER_SIZE];
|
||||
for (i = 0; i < nr_handles; i++, handle++) {
|
||||
if ((be32_to_cpu(*handle) & 0xFF000000) == TPM2_HT_TRANSIENT) {
|
||||
if (!tpm2_map_to_phandle(space, handle))
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
|
||||
u8 *cmd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!space)
|
||||
return 0;
|
||||
|
||||
memcpy(&chip->work_space.context_tbl, &space->context_tbl,
|
||||
sizeof(space->context_tbl));
|
||||
memcpy(&chip->work_space.session_tbl, &space->session_tbl,
|
||||
sizeof(space->session_tbl));
|
||||
memcpy(chip->work_space.context_buf, space->context_buf, PAGE_SIZE);
|
||||
memcpy(chip->work_space.session_buf, space->session_buf, PAGE_SIZE);
|
||||
|
||||
rc = tpm2_load_space(chip);
|
||||
if (rc) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = tpm2_map_command(chip, cc, cmd);
|
||||
if (rc) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool tpm2_add_session(struct tpm_chip *chip, u32 handle)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++)
|
||||
if (space->session_tbl[i] == 0)
|
||||
break;
|
||||
|
||||
if (i == ARRAY_SIZE(space->session_tbl))
|
||||
return false;
|
||||
|
||||
space->session_tbl[i] = handle;
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
|
||||
if (alloc) {
|
||||
if (!space->context_tbl[i]) {
|
||||
space->context_tbl[i] = phandle;
|
||||
break;
|
||||
}
|
||||
} else if (space->context_tbl[i] == phandle)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(space->context_tbl))
|
||||
return 0;
|
||||
|
||||
return TPM2_HT_TRANSIENT | (0xFFFFFF - i);
|
||||
}
|
||||
|
||||
static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,
|
||||
size_t len)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
struct tpm_output_header *header = (void *)rsp;
|
||||
u32 phandle;
|
||||
u32 phandle_type;
|
||||
u32 vhandle;
|
||||
u32 attrs;
|
||||
int i;
|
||||
|
||||
if (be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS)
|
||||
return 0;
|
||||
|
||||
i = tpm2_find_cc(chip, cc);
|
||||
/* sanity check, should never happen */
|
||||
if (i < 0)
|
||||
return -EFAULT;
|
||||
|
||||
attrs = chip->cc_attrs_tbl[i];
|
||||
if (!((attrs >> TPM2_CC_ATTR_RHANDLE) & 1))
|
||||
return 0;
|
||||
|
||||
phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]);
|
||||
phandle_type = phandle & 0xFF000000;
|
||||
|
||||
switch (phandle_type) {
|
||||
case TPM2_HT_TRANSIENT:
|
||||
vhandle = tpm2_map_to_vhandle(space, phandle, true);
|
||||
if (!vhandle)
|
||||
goto out_no_slots;
|
||||
|
||||
*(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle);
|
||||
break;
|
||||
case TPM2_HT_HMAC_SESSION:
|
||||
case TPM2_HT_POLICY_SESSION:
|
||||
if (!tpm2_add_session(chip, phandle))
|
||||
goto out_no_slots;
|
||||
break;
|
||||
default:
|
||||
dev_err(&chip->dev, "%s: unknown handle 0x%08X\n",
|
||||
__func__, phandle);
|
||||
break;
|
||||
};
|
||||
|
||||
return 0;
|
||||
out_no_slots:
|
||||
tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_UNLOCKED);
|
||||
dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__,
|
||||
phandle);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
struct tpm2_cap_handles {
|
||||
u8 more_data;
|
||||
__be32 capability;
|
||||
__be32 count;
|
||||
__be32 handles[];
|
||||
} __packed;
|
||||
|
||||
static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp,
|
||||
size_t len)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
struct tpm_output_header *header = (void *)rsp;
|
||||
struct tpm2_cap_handles *data;
|
||||
u32 phandle;
|
||||
u32 phandle_type;
|
||||
u32 vhandle;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
if (cc != TPM2_CC_GET_CAPABILITY ||
|
||||
be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len < TPM_HEADER_SIZE + 9)
|
||||
return -EFAULT;
|
||||
|
||||
data = (void *)&rsp[TPM_HEADER_SIZE];
|
||||
if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES)
|
||||
return 0;
|
||||
|
||||
if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count))
|
||||
return -EFAULT;
|
||||
|
||||
for (i = 0, j = 0; i < be32_to_cpu(data->count); i++) {
|
||||
phandle = be32_to_cpup((__be32 *)&data->handles[i]);
|
||||
phandle_type = phandle & 0xFF000000;
|
||||
|
||||
switch (phandle_type) {
|
||||
case TPM2_HT_TRANSIENT:
|
||||
vhandle = tpm2_map_to_vhandle(space, phandle, false);
|
||||
if (!vhandle)
|
||||
break;
|
||||
|
||||
data->handles[j] = cpu_to_be32(vhandle);
|
||||
j++;
|
||||
break;
|
||||
|
||||
default:
|
||||
data->handles[j] = cpu_to_be32(phandle);
|
||||
j++;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
header->length = cpu_to_be32(TPM_HEADER_SIZE + 9 + 4 * j);
|
||||
data->count = cpu_to_be32(j);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm2_save_space(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
unsigned int offset;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
|
||||
if (!(space->context_tbl[i] && ~space->context_tbl[i]))
|
||||
continue;
|
||||
|
||||
rc = tpm2_save_context(chip, space->context_tbl[i],
|
||||
space->context_buf, PAGE_SIZE,
|
||||
&offset);
|
||||
if (rc == -ENOENT) {
|
||||
space->context_tbl[i] = 0;
|
||||
continue;
|
||||
} else if (rc)
|
||||
return rc;
|
||||
|
||||
tpm2_flush_context_cmd(chip, space->context_tbl[i],
|
||||
TPM_TRANSMIT_UNLOCKED);
|
||||
space->context_tbl[i] = ~0;
|
||||
}
|
||||
|
||||
for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
|
||||
if (!space->session_tbl[i])
|
||||
continue;
|
||||
|
||||
rc = tpm2_save_context(chip, space->session_tbl[i],
|
||||
space->session_buf, PAGE_SIZE,
|
||||
&offset);
|
||||
|
||||
if (rc == -ENOENT) {
|
||||
/* handle error saving session, just forget it */
|
||||
space->session_tbl[i] = 0;
|
||||
} else if (rc < 0) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
|
||||
u32 cc, u8 *buf, size_t *bufsiz)
|
||||
{
|
||||
struct tpm_output_header *header = (void *)buf;
|
||||
int rc;
|
||||
|
||||
if (!space)
|
||||
return 0;
|
||||
|
||||
rc = tpm2_map_response_header(chip, cc, buf, *bufsiz);
|
||||
if (rc) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = tpm2_map_response_body(chip, cc, buf, *bufsiz);
|
||||
if (rc) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = tpm2_save_space(chip);
|
||||
if (rc) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
*bufsiz = be32_to_cpu(header->length);
|
||||
|
||||
memcpy(&space->context_tbl, &chip->work_space.context_tbl,
|
||||
sizeof(space->context_tbl));
|
||||
memcpy(&space->session_tbl, &chip->work_space.session_tbl,
|
||||
sizeof(space->session_tbl));
|
||||
memcpy(space->context_buf, chip->work_space.context_buf, PAGE_SIZE);
|
||||
memcpy(space->session_buf, chip->work_space.session_buf, PAGE_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -56,18 +56,24 @@ static int calc_tpm2_event_size(struct tcg_pcr_event2 *event,
|
||||
|
||||
efispecid = (struct tcg_efi_specid_event *)event_header->event;
|
||||
|
||||
for (i = 0; (i < event->count) && (i < TPM2_ACTIVE_PCR_BANKS);
|
||||
i++) {
|
||||
/* Check if event is malformed. */
|
||||
if (event->count > efispecid->num_algs)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < event->count; i++) {
|
||||
halg_size = sizeof(event->digests[i].alg_id);
|
||||
memcpy(&halg, marker, halg_size);
|
||||
marker = marker + halg_size;
|
||||
for (j = 0; (j < efispecid->num_algs); j++) {
|
||||
for (j = 0; j < efispecid->num_algs; j++) {
|
||||
if (halg == efispecid->digest_sizes[j].alg_id) {
|
||||
marker = marker +
|
||||
marker +=
|
||||
efispecid->digest_sizes[j].digest_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Algorithm without known length. Such event is unparseable. */
|
||||
if (j == efispecid->num_algs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
event_field = (struct tcg_event_field *)marker;
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#ifdef CONFIG_ARM64
|
||||
#include <linux/arm-smccc.h>
|
||||
#endif
|
||||
#include "tpm.h"
|
||||
|
||||
#define ACPI_SIG_TPM2 "TPM2"
|
||||
@@ -34,6 +37,16 @@ enum crb_defaults {
|
||||
CRB_ACPI_START_INDEX = 1,
|
||||
};
|
||||
|
||||
enum crb_loc_ctrl {
|
||||
CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0),
|
||||
CRB_LOC_CTRL_RELINQUISH = BIT(1),
|
||||
};
|
||||
|
||||
enum crb_loc_state {
|
||||
CRB_LOC_STATE_LOC_ASSIGNED = BIT(1),
|
||||
CRB_LOC_STATE_TPM_REG_VALID_STS = BIT(7),
|
||||
};
|
||||
|
||||
enum crb_ctrl_req {
|
||||
CRB_CTRL_REQ_CMD_READY = BIT(0),
|
||||
CRB_CTRL_REQ_GO_IDLE = BIT(1),
|
||||
@@ -52,18 +65,28 @@ enum crb_cancel {
|
||||
CRB_CANCEL_INVOKE = BIT(0),
|
||||
};
|
||||
|
||||
struct crb_control_area {
|
||||
u32 req;
|
||||
u32 sts;
|
||||
u32 cancel;
|
||||
u32 start;
|
||||
u32 int_enable;
|
||||
u32 int_sts;
|
||||
u32 cmd_size;
|
||||
u32 cmd_pa_low;
|
||||
u32 cmd_pa_high;
|
||||
u32 rsp_size;
|
||||
u64 rsp_pa;
|
||||
struct crb_regs_head {
|
||||
u32 loc_state;
|
||||
u32 reserved1;
|
||||
u32 loc_ctrl;
|
||||
u32 loc_sts;
|
||||
u8 reserved2[32];
|
||||
u64 intf_id;
|
||||
u64 ctrl_ext;
|
||||
} __packed;
|
||||
|
||||
struct crb_regs_tail {
|
||||
u32 ctrl_req;
|
||||
u32 ctrl_sts;
|
||||
u32 ctrl_cancel;
|
||||
u32 ctrl_start;
|
||||
u32 ctrl_int_enable;
|
||||
u32 ctrl_int_sts;
|
||||
u32 ctrl_cmd_size;
|
||||
u32 ctrl_cmd_pa_low;
|
||||
u32 ctrl_cmd_pa_high;
|
||||
u32 ctrl_rsp_size;
|
||||
u64 ctrl_rsp_pa;
|
||||
} __packed;
|
||||
|
||||
enum crb_status {
|
||||
@@ -73,15 +96,26 @@ enum crb_status {
|
||||
enum crb_flags {
|
||||
CRB_FL_ACPI_START = BIT(0),
|
||||
CRB_FL_CRB_START = BIT(1),
|
||||
CRB_FL_CRB_SMC_START = BIT(2),
|
||||
};
|
||||
|
||||
struct crb_priv {
|
||||
unsigned int flags;
|
||||
void __iomem *iobase;
|
||||
struct crb_control_area __iomem *cca;
|
||||
struct crb_regs_head __iomem *regs_h;
|
||||
struct crb_regs_tail __iomem *regs_t;
|
||||
u8 __iomem *cmd;
|
||||
u8 __iomem *rsp;
|
||||
u32 cmd_size;
|
||||
u32 smc_func_id;
|
||||
};
|
||||
|
||||
struct tpm2_crb_smc {
|
||||
u32 interrupt;
|
||||
u8 interrupt_flags;
|
||||
u8 op_flags;
|
||||
u16 reserved2;
|
||||
u32 smc_func_id;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -101,15 +135,35 @@ struct crb_priv {
|
||||
*/
|
||||
static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv)
|
||||
{
|
||||
if (priv->flags & CRB_FL_ACPI_START)
|
||||
if ((priv->flags & CRB_FL_ACPI_START) ||
|
||||
(priv->flags & CRB_FL_CRB_SMC_START))
|
||||
return 0;
|
||||
|
||||
iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->cca->req);
|
||||
iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req);
|
||||
/* we don't really care when this settles */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
|
||||
unsigned long timeout)
|
||||
{
|
||||
ktime_t start;
|
||||
ktime_t stop;
|
||||
|
||||
start = ktime_get();
|
||||
stop = ktime_add(start, ms_to_ktime(timeout));
|
||||
|
||||
do {
|
||||
if ((ioread32(reg) & mask) == value)
|
||||
return true;
|
||||
|
||||
usleep_range(50, 100);
|
||||
} while (ktime_before(ktime_get(), stop));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* crb_cmd_ready - request tpm crb device to enter ready state
|
||||
*
|
||||
@@ -127,22 +181,15 @@ static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv)
|
||||
static int __maybe_unused crb_cmd_ready(struct device *dev,
|
||||
struct crb_priv *priv)
|
||||
{
|
||||
ktime_t stop, start;
|
||||
|
||||
if (priv->flags & CRB_FL_ACPI_START)
|
||||
if ((priv->flags & CRB_FL_ACPI_START) ||
|
||||
(priv->flags & CRB_FL_CRB_SMC_START))
|
||||
return 0;
|
||||
|
||||
iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->cca->req);
|
||||
|
||||
start = ktime_get();
|
||||
stop = ktime_add(start, ms_to_ktime(TPM2_TIMEOUT_C));
|
||||
do {
|
||||
if (!(ioread32(&priv->cca->req) & CRB_CTRL_REQ_CMD_READY))
|
||||
return 0;
|
||||
usleep_range(50, 100);
|
||||
} while (ktime_before(ktime_get(), stop));
|
||||
|
||||
if (ioread32(&priv->cca->req) & CRB_CTRL_REQ_CMD_READY) {
|
||||
iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->regs_t->ctrl_req);
|
||||
if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req,
|
||||
CRB_CTRL_REQ_CMD_READY /* mask */,
|
||||
0, /* value */
|
||||
TPM2_TIMEOUT_C)) {
|
||||
dev_warn(dev, "cmdReady timed out\n");
|
||||
return -ETIME;
|
||||
}
|
||||
@@ -150,12 +197,41 @@ static int __maybe_unused crb_cmd_ready(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int crb_request_locality(struct tpm_chip *chip, int loc)
|
||||
{
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
u32 value = CRB_LOC_STATE_LOC_ASSIGNED |
|
||||
CRB_LOC_STATE_TPM_REG_VALID_STS;
|
||||
|
||||
if (!priv->regs_h)
|
||||
return 0;
|
||||
|
||||
iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl);
|
||||
if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value,
|
||||
TPM2_TIMEOUT_C)) {
|
||||
dev_warn(&chip->dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void crb_relinquish_locality(struct tpm_chip *chip, int loc)
|
||||
{
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
|
||||
if (!priv->regs_h)
|
||||
return;
|
||||
|
||||
iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl);
|
||||
}
|
||||
|
||||
static u8 crb_status(struct tpm_chip *chip)
|
||||
{
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
u8 sts = 0;
|
||||
|
||||
if ((ioread32(&priv->cca->start) & CRB_START_INVOKE) !=
|
||||
if ((ioread32(&priv->regs_t->ctrl_start) & CRB_START_INVOKE) !=
|
||||
CRB_START_INVOKE)
|
||||
sts |= CRB_DRV_STS_COMPLETE;
|
||||
|
||||
@@ -171,13 +247,12 @@ static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
if (count < 6)
|
||||
return -EIO;
|
||||
|
||||
if (ioread32(&priv->cca->sts) & CRB_CTRL_STS_ERROR)
|
||||
if (ioread32(&priv->regs_t->ctrl_sts) & CRB_CTRL_STS_ERROR)
|
||||
return -EIO;
|
||||
|
||||
memcpy_fromio(buf, priv->rsp, 6);
|
||||
expected = be32_to_cpup((__be32 *) &buf[2]);
|
||||
|
||||
if (expected > count)
|
||||
if (expected > count || expected < 6)
|
||||
return -EIO;
|
||||
|
||||
memcpy_fromio(&buf[6], &priv->rsp[6], expected - 6);
|
||||
@@ -202,6 +277,34 @@ static int crb_do_acpi_start(struct tpm_chip *chip)
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARM64
|
||||
/*
|
||||
* This is a TPM Command Response Buffer start method that invokes a
|
||||
* Secure Monitor Call to requrest the firmware to execute or cancel
|
||||
* a TPM 2.0 command.
|
||||
*/
|
||||
static int tpm_crb_smc_start(struct device *dev, unsigned long func_id)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
arm_smccc_smc(func_id, 0, 0, 0, 0, 0, 0, 0, &res);
|
||||
if (res.a0 != 0) {
|
||||
dev_err(dev,
|
||||
FW_BUG "tpm_crb_smc_start() returns res.a0 = 0x%lx\n",
|
||||
res.a0);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int tpm_crb_smc_start(struct device *dev, unsigned long func_id)
|
||||
{
|
||||
dev_err(dev, FW_BUG "tpm_crb: incorrect start method\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
{
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
@@ -210,7 +313,7 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
/* Zero the cancel register so that the next command will not get
|
||||
* canceled.
|
||||
*/
|
||||
iowrite32(0, &priv->cca->cancel);
|
||||
iowrite32(0, &priv->regs_t->ctrl_cancel);
|
||||
|
||||
if (len > priv->cmd_size) {
|
||||
dev_err(&chip->dev, "invalid command count value %zd %d\n",
|
||||
@@ -224,11 +327,16 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
wmb();
|
||||
|
||||
if (priv->flags & CRB_FL_CRB_START)
|
||||
iowrite32(CRB_START_INVOKE, &priv->cca->start);
|
||||
iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start);
|
||||
|
||||
if (priv->flags & CRB_FL_ACPI_START)
|
||||
rc = crb_do_acpi_start(chip);
|
||||
|
||||
if (priv->flags & CRB_FL_CRB_SMC_START) {
|
||||
iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start);
|
||||
rc = tpm_crb_smc_start(&chip->dev, priv->smc_func_id);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -236,7 +344,7 @@ static void crb_cancel(struct tpm_chip *chip)
|
||||
{
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
|
||||
iowrite32(CRB_CANCEL_INVOKE, &priv->cca->cancel);
|
||||
iowrite32(CRB_CANCEL_INVOKE, &priv->regs_t->ctrl_cancel);
|
||||
|
||||
if ((priv->flags & CRB_FL_ACPI_START) && crb_do_acpi_start(chip))
|
||||
dev_err(&chip->dev, "ACPI Start failed\n");
|
||||
@@ -245,7 +353,7 @@ static void crb_cancel(struct tpm_chip *chip)
|
||||
static bool crb_req_canceled(struct tpm_chip *chip, u8 status)
|
||||
{
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
u32 cancel = ioread32(&priv->cca->cancel);
|
||||
u32 cancel = ioread32(&priv->regs_t->ctrl_cancel);
|
||||
|
||||
return (cancel & CRB_CANCEL_INVOKE) == CRB_CANCEL_INVOKE;
|
||||
}
|
||||
@@ -257,6 +365,8 @@ static const struct tpm_class_ops tpm_crb = {
|
||||
.send = crb_send,
|
||||
.cancel = crb_cancel,
|
||||
.req_canceled = crb_req_canceled,
|
||||
.request_locality = crb_request_locality,
|
||||
.relinquish_locality = crb_relinquish_locality,
|
||||
.req_complete_mask = CRB_DRV_STS_COMPLETE,
|
||||
.req_complete_val = CRB_DRV_STS_COMPLETE,
|
||||
};
|
||||
@@ -295,6 +405,27 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv,
|
||||
return priv->iobase + (new_res.start - io_res->start);
|
||||
}
|
||||
|
||||
/*
|
||||
* Work around broken BIOSs that return inconsistent values from the ACPI
|
||||
* region vs the registers. Trust the ACPI region. Such broken systems
|
||||
* probably cannot send large TPM commands since the buffer will be truncated.
|
||||
*/
|
||||
static u64 crb_fixup_cmd_size(struct device *dev, struct resource *io_res,
|
||||
u64 start, u64 size)
|
||||
{
|
||||
if (io_res->start > start || io_res->end < start)
|
||||
return size;
|
||||
|
||||
if (start + size - 1 <= io_res->end)
|
||||
return size;
|
||||
|
||||
dev_err(dev,
|
||||
FW_BUG "ACPI region does not cover the entire command/response buffer. %pr vs %llx %llx\n",
|
||||
io_res, start, size);
|
||||
|
||||
return io_res->end - start + 1;
|
||||
}
|
||||
|
||||
static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
struct acpi_table_tpm2 *buf)
|
||||
{
|
||||
@@ -324,10 +455,22 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
if (IS_ERR(priv->iobase))
|
||||
return PTR_ERR(priv->iobase);
|
||||
|
||||
priv->cca = crb_map_res(dev, priv, &io_res, buf->control_address,
|
||||
sizeof(struct crb_control_area));
|
||||
if (IS_ERR(priv->cca))
|
||||
return PTR_ERR(priv->cca);
|
||||
/* 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
|
||||
* stuff that puts the control area outside the ACPI IO region.
|
||||
*/
|
||||
if (!(priv->flags & CRB_FL_ACPI_START)) {
|
||||
if (buf->control_address == io_res.start +
|
||||
sizeof(*priv->regs_h))
|
||||
priv->regs_h = priv->iobase;
|
||||
else
|
||||
dev_warn(dev, FW_BUG "Bad ACPI memory layout");
|
||||
}
|
||||
|
||||
priv->regs_t = crb_map_res(dev, priv, &io_res, buf->control_address,
|
||||
sizeof(struct crb_regs_tail));
|
||||
if (IS_ERR(priv->regs_t))
|
||||
return PTR_ERR(priv->regs_t);
|
||||
|
||||
/*
|
||||
* PTT HW bug w/a: wake up the device to access
|
||||
@@ -337,10 +480,11 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pa_high = ioread32(&priv->cca->cmd_pa_high);
|
||||
pa_low = ioread32(&priv->cca->cmd_pa_low);
|
||||
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 = ioread32(&priv->cca->cmd_size);
|
||||
cmd_size = crb_fixup_cmd_size(dev, &io_res, cmd_pa,
|
||||
ioread32(&priv->regs_t->ctrl_cmd_size));
|
||||
|
||||
dev_dbg(dev, "cmd_hi = %X cmd_low = %X cmd_size %X\n",
|
||||
pa_high, pa_low, cmd_size);
|
||||
@@ -351,9 +495,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy_fromio(&rsp_pa, &priv->cca->rsp_pa, 8);
|
||||
memcpy_fromio(&rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8);
|
||||
rsp_pa = le64_to_cpu(rsp_pa);
|
||||
rsp_size = ioread32(&priv->cca->rsp_size);
|
||||
rsp_size = crb_fixup_cmd_size(dev, &io_res, rsp_pa,
|
||||
ioread32(&priv->regs_t->ctrl_rsp_size));
|
||||
|
||||
if (cmd_pa != rsp_pa) {
|
||||
priv->rsp = crb_map_res(dev, priv, &io_res, rsp_pa, rsp_size);
|
||||
@@ -386,6 +531,7 @@ static int crb_acpi_add(struct acpi_device *device)
|
||||
struct crb_priv *priv;
|
||||
struct tpm_chip *chip;
|
||||
struct device *dev = &device->dev;
|
||||
struct tpm2_crb_smc *crb_smc;
|
||||
acpi_status status;
|
||||
u32 sm;
|
||||
int rc;
|
||||
@@ -418,6 +564,19 @@ static int crb_acpi_add(struct acpi_device *device)
|
||||
sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD)
|
||||
priv->flags |= CRB_FL_ACPI_START;
|
||||
|
||||
if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_SMC) {
|
||||
if (buf->header.length < (sizeof(*buf) + sizeof(*crb_smc))) {
|
||||
dev_err(dev,
|
||||
FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n",
|
||||
buf->header.length,
|
||||
ACPI_TPM2_COMMAND_BUFFER_WITH_SMC);
|
||||
return -EINVAL;
|
||||
}
|
||||
crb_smc = ACPI_ADD_PTR(struct tpm2_crb_smc, buf, sizeof(*buf));
|
||||
priv->smc_func_id = crb_smc->smc_func_id;
|
||||
priv->flags |= CRB_FL_CRB_SMC_START;
|
||||
}
|
||||
|
||||
rc = crb_map_io(device, priv, buf);
|
||||
if (rc)
|
||||
return rc;
|
||||
@@ -463,8 +622,7 @@ static int crb_acpi_remove(struct acpi_device *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int crb_pm_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused crb_pm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
@@ -472,17 +630,38 @@ static int crb_pm_runtime_suspend(struct device *dev)
|
||||
return crb_go_idle(dev, priv);
|
||||
}
|
||||
|
||||
static int crb_pm_runtime_resume(struct device *dev)
|
||||
static int __maybe_unused crb_pm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
|
||||
return crb_cmd_ready(dev, priv);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static int __maybe_unused crb_pm_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = tpm_pm_suspend(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return crb_pm_runtime_suspend(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused crb_pm_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = crb_pm_runtime_resume(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return tpm_pm_resume(dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops crb_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tpm_pm_suspend, tpm_pm_resume)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(crb_pm_suspend, crb_pm_resume)
|
||||
SET_RUNTIME_PM_OPS(crb_pm_runtime_suspend, crb_pm_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
|
||||
@@ -278,22 +278,22 @@ enum tis_defaults {
|
||||
#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
|
||||
#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
|
||||
|
||||
static int check_locality(struct tpm_chip *chip, int loc)
|
||||
static bool check_locality(struct tpm_chip *chip, int loc)
|
||||
{
|
||||
u8 buf;
|
||||
int rc;
|
||||
|
||||
rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
return false;
|
||||
|
||||
if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
|
||||
tpm_dev.locality = loc;
|
||||
return loc;
|
||||
return true;
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* implementation similar to tpm_tis */
|
||||
@@ -315,7 +315,7 @@ static int request_locality(struct tpm_chip *chip, int loc)
|
||||
unsigned long stop;
|
||||
u8 buf = TPM_ACCESS_REQUEST_USE;
|
||||
|
||||
if (check_locality(chip, loc) >= 0)
|
||||
if (check_locality(chip, loc))
|
||||
return loc;
|
||||
|
||||
iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
|
||||
@@ -323,7 +323,7 @@ static int request_locality(struct tpm_chip *chip, int loc)
|
||||
/* wait for burstcount */
|
||||
stop = jiffies + chip->timeout_a;
|
||||
do {
|
||||
if (check_locality(chip, loc) >= 0)
|
||||
if (check_locality(chip, loc))
|
||||
return loc;
|
||||
usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
|
||||
} while (time_before(jiffies, stop));
|
||||
|
||||
@@ -49,9 +49,10 @@
|
||||
*/
|
||||
#define TPM_I2C_MAX_BUF_SIZE 32
|
||||
#define TPM_I2C_RETRY_COUNT 32
|
||||
#define TPM_I2C_BUS_DELAY 1 /* msec */
|
||||
#define TPM_I2C_RETRY_DELAY_SHORT 2 /* msec */
|
||||
#define TPM_I2C_RETRY_DELAY_LONG 10 /* msec */
|
||||
#define TPM_I2C_BUS_DELAY 1000 /* usec */
|
||||
#define TPM_I2C_RETRY_DELAY_SHORT (2 * 1000) /* usec */
|
||||
#define TPM_I2C_RETRY_DELAY_LONG (10 * 1000) /* usec */
|
||||
#define TPM_I2C_DELAY_RANGE 300 /* usec */
|
||||
|
||||
#define OF_IS_TPM2 ((void *)1)
|
||||
#define I2C_IS_TPM2 1
|
||||
@@ -123,7 +124,9 @@ static s32 i2c_nuvoton_write_status(struct i2c_client *client, u8 data)
|
||||
/* this causes the current command to be aborted */
|
||||
for (i = 0, status = -1; i < TPM_I2C_RETRY_COUNT && status < 0; i++) {
|
||||
status = i2c_nuvoton_write_buf(client, TPM_STS, 1, &data);
|
||||
msleep(TPM_I2C_BUS_DELAY);
|
||||
if (status < 0)
|
||||
usleep_range(TPM_I2C_BUS_DELAY, TPM_I2C_BUS_DELAY
|
||||
+ TPM_I2C_DELAY_RANGE);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -160,7 +163,8 @@ static int i2c_nuvoton_get_burstcount(struct i2c_client *client,
|
||||
burst_count = min_t(u8, TPM_I2C_MAX_BUF_SIZE, data);
|
||||
break;
|
||||
}
|
||||
msleep(TPM_I2C_BUS_DELAY);
|
||||
usleep_range(TPM_I2C_BUS_DELAY, TPM_I2C_BUS_DELAY
|
||||
+ TPM_I2C_DELAY_RANGE);
|
||||
} while (time_before(jiffies, stop));
|
||||
|
||||
return burst_count;
|
||||
@@ -203,13 +207,17 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value,
|
||||
return 0;
|
||||
|
||||
/* use polling to wait for the event */
|
||||
ten_msec = jiffies + msecs_to_jiffies(TPM_I2C_RETRY_DELAY_LONG);
|
||||
ten_msec = jiffies + usecs_to_jiffies(TPM_I2C_RETRY_DELAY_LONG);
|
||||
stop = jiffies + timeout;
|
||||
do {
|
||||
if (time_before(jiffies, ten_msec))
|
||||
msleep(TPM_I2C_RETRY_DELAY_SHORT);
|
||||
usleep_range(TPM_I2C_RETRY_DELAY_SHORT,
|
||||
TPM_I2C_RETRY_DELAY_SHORT
|
||||
+ TPM_I2C_DELAY_RANGE);
|
||||
else
|
||||
msleep(TPM_I2C_RETRY_DELAY_LONG);
|
||||
usleep_range(TPM_I2C_RETRY_DELAY_LONG,
|
||||
TPM_I2C_RETRY_DELAY_LONG
|
||||
+ TPM_I2C_DELAY_RANGE);
|
||||
status_valid = i2c_nuvoton_check_status(chip, mask,
|
||||
value);
|
||||
if (status_valid)
|
||||
|
||||
@@ -299,6 +299,8 @@ static int tpm_ibmvtpm_remove(struct vio_dev *vdev)
|
||||
}
|
||||
|
||||
kfree(ibmvtpm);
|
||||
/* For tpm_ibmvtpm_get_desired_dma */
|
||||
dev_set_drvdata(&vdev->dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -313,14 +315,16 @@ static int tpm_ibmvtpm_remove(struct vio_dev *vdev)
|
||||
static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(&vdev->dev);
|
||||
struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
|
||||
struct ibmvtpm_dev *ibmvtpm;
|
||||
|
||||
/*
|
||||
* ibmvtpm initializes at probe time, so the data we are
|
||||
* asking for may not be set yet. Estimate that 4K required
|
||||
* for TCE-mapped buffer in addition to CRQ.
|
||||
*/
|
||||
if (!ibmvtpm)
|
||||
if (chip)
|
||||
ibmvtpm = dev_get_drvdata(&chip->dev);
|
||||
else
|
||||
return CRQ_RES_BUF_SIZE + PAGE_SIZE;
|
||||
|
||||
return CRQ_RES_BUF_SIZE + ibmvtpm->rtce_size;
|
||||
|
||||
@@ -56,7 +56,7 @@ static int wait_startup(struct tpm_chip *chip, int l)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int check_locality(struct tpm_chip *chip, int l)
|
||||
static bool check_locality(struct tpm_chip *chip, int l)
|
||||
{
|
||||
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
|
||||
int rc;
|
||||
@@ -64,30 +64,22 @@ static int check_locality(struct tpm_chip *chip, int l)
|
||||
|
||||
rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
return false;
|
||||
|
||||
if ((access & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
|
||||
return priv->locality = l;
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
|
||||
priv->locality = l;
|
||||
return true;
|
||||
}
|
||||
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void release_locality(struct tpm_chip *chip, int l, int force)
|
||||
static void release_locality(struct tpm_chip *chip, int l)
|
||||
{
|
||||
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
|
||||
int rc;
|
||||
u8 access;
|
||||
|
||||
rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access);
|
||||
if (rc < 0)
|
||||
return;
|
||||
|
||||
if (force || (access &
|
||||
(TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID))
|
||||
tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
|
||||
|
||||
tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
|
||||
}
|
||||
|
||||
static int request_locality(struct tpm_chip *chip, int l)
|
||||
@@ -96,7 +88,7 @@ static int request_locality(struct tpm_chip *chip, int l)
|
||||
unsigned long stop, timeout;
|
||||
long rc;
|
||||
|
||||
if (check_locality(chip, l) >= 0)
|
||||
if (check_locality(chip, l))
|
||||
return l;
|
||||
|
||||
rc = tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_REQUEST_USE);
|
||||
@@ -112,7 +104,7 @@ again:
|
||||
return -1;
|
||||
rc = wait_event_interruptible_timeout(priv->int_queue,
|
||||
(check_locality
|
||||
(chip, l) >= 0),
|
||||
(chip, l)),
|
||||
timeout);
|
||||
if (rc > 0)
|
||||
return l;
|
||||
@@ -123,7 +115,7 @@ again:
|
||||
} else {
|
||||
/* wait for burstcount */
|
||||
do {
|
||||
if (check_locality(chip, l) >= 0)
|
||||
if (check_locality(chip, l))
|
||||
return l;
|
||||
msleep(TPM_TIMEOUT);
|
||||
} while (time_before(jiffies, stop));
|
||||
@@ -160,8 +152,10 @@ static int get_burstcount(struct tpm_chip *chip)
|
||||
u32 value;
|
||||
|
||||
/* wait for burstcount */
|
||||
/* which timeout value, spec has 2 answers (c & d) */
|
||||
stop = jiffies + chip->timeout_d;
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2)
|
||||
stop = jiffies + chip->timeout_a;
|
||||
else
|
||||
stop = jiffies + chip->timeout_d;
|
||||
do {
|
||||
rc = tpm_tis_read32(priv, TPM_STS(priv->locality), &value);
|
||||
if (rc < 0)
|
||||
@@ -250,7 +244,6 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
|
||||
out:
|
||||
tpm_tis_ready(chip);
|
||||
release_locality(chip, priv->locality, 0);
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -266,9 +259,6 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
size_t count = 0;
|
||||
bool itpm = priv->flags & TPM_TIS_ITPM_WORKAROUND;
|
||||
|
||||
if (request_locality(chip, 0) < 0)
|
||||
return -EBUSY;
|
||||
|
||||
status = tpm_tis_status(chip);
|
||||
if ((status & TPM_STS_COMMAND_READY) == 0) {
|
||||
tpm_tis_ready(chip);
|
||||
@@ -327,7 +317,6 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
|
||||
out_err:
|
||||
tpm_tis_ready(chip);
|
||||
release_locality(chip, priv->locality, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -388,7 +377,6 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
return len;
|
||||
out_err:
|
||||
tpm_tis_ready(chip);
|
||||
release_locality(chip, priv->locality, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -475,12 +463,14 @@ static int probe_itpm(struct tpm_chip *chip)
|
||||
if (vendor != TPM_VID_INTEL)
|
||||
return 0;
|
||||
|
||||
if (request_locality(chip, 0) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
rc = tpm_tis_send_data(chip, cmd_getticks, len);
|
||||
if (rc == 0)
|
||||
goto out;
|
||||
|
||||
tpm_tis_ready(chip);
|
||||
release_locality(chip, priv->locality, 0);
|
||||
|
||||
priv->flags |= TPM_TIS_ITPM_WORKAROUND;
|
||||
|
||||
@@ -494,7 +484,7 @@ static int probe_itpm(struct tpm_chip *chip)
|
||||
|
||||
out:
|
||||
tpm_tis_ready(chip);
|
||||
release_locality(chip, priv->locality, 0);
|
||||
release_locality(chip, priv->locality);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -533,7 +523,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)
|
||||
wake_up_interruptible(&priv->read_queue);
|
||||
if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT)
|
||||
for (i = 0; i < 5; i++)
|
||||
if (check_locality(chip, i) >= 0)
|
||||
if (check_locality(chip, i))
|
||||
break;
|
||||
if (interrupt &
|
||||
(TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT |
|
||||
@@ -668,7 +658,6 @@ void tpm_tis_remove(struct tpm_chip *chip)
|
||||
interrupt = 0;
|
||||
|
||||
tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt);
|
||||
release_locality(chip, priv->locality, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_tis_remove);
|
||||
|
||||
@@ -682,6 +671,8 @@ static const struct tpm_class_ops tpm_tis = {
|
||||
.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,
|
||||
.request_locality = request_locality,
|
||||
.relinquish_locality = release_locality,
|
||||
};
|
||||
|
||||
int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
|
||||
@@ -724,11 +715,6 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
|
||||
intmask &= ~TPM_GLOBAL_INT_ENABLE;
|
||||
tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
|
||||
|
||||
if (request_locality(chip, 0) != 0) {
|
||||
rc = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
rc = tpm2_probe(chip);
|
||||
if (rc)
|
||||
goto out_err;
|
||||
|
||||
@@ -47,8 +47,8 @@ struct tpm_tis_spi_phy {
|
||||
struct tpm_tis_data priv;
|
||||
struct spi_device *spi_device;
|
||||
|
||||
u8 tx_buf[MAX_SPI_FRAMESIZE + 4];
|
||||
u8 rx_buf[MAX_SPI_FRAMESIZE + 4];
|
||||
u8 tx_buf[4];
|
||||
u8 rx_buf[4];
|
||||
};
|
||||
|
||||
static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data)
|
||||
@@ -56,120 +56,96 @@ static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *da
|
||||
return container_of(data, struct tpm_tis_spi_phy, priv);
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, u8 *result)
|
||||
static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
|
||||
u8 *buffer, u8 direction)
|
||||
{
|
||||
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
|
||||
int ret, i;
|
||||
int ret = 0;
|
||||
int i;
|
||||
struct spi_message m;
|
||||
struct spi_transfer spi_xfer = {
|
||||
.tx_buf = phy->tx_buf,
|
||||
.rx_buf = phy->rx_buf,
|
||||
.len = 4,
|
||||
};
|
||||
|
||||
if (len > MAX_SPI_FRAMESIZE)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->tx_buf[0] = 0x80 | (len - 1);
|
||||
phy->tx_buf[1] = 0xd4;
|
||||
phy->tx_buf[2] = (addr >> 8) & 0xFF;
|
||||
phy->tx_buf[3] = addr & 0xFF;
|
||||
|
||||
spi_xfer.cs_change = 1;
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
struct spi_transfer spi_xfer;
|
||||
u8 transfer_len;
|
||||
|
||||
spi_bus_lock(phy->spi_device->master);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
memset(phy->tx_buf, 0, len);
|
||||
while (len) {
|
||||
transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);
|
||||
|
||||
phy->tx_buf[0] = direction | (transfer_len - 1);
|
||||
phy->tx_buf[1] = 0xd4;
|
||||
phy->tx_buf[2] = addr >> 8;
|
||||
phy->tx_buf[3] = addr;
|
||||
|
||||
memset(&spi_xfer, 0, sizeof(spi_xfer));
|
||||
spi_xfer.tx_buf = phy->tx_buf;
|
||||
spi_xfer.rx_buf = phy->rx_buf;
|
||||
spi_xfer.len = 4;
|
||||
spi_xfer.cs_change = 1;
|
||||
|
||||
/* According to TCG PTP specification, if there is no TPM present at
|
||||
* all, then the design has a weak pull-up on MISO. If a TPM is not
|
||||
* present, a pull-up on MISO means that the SB controller sees a 1,
|
||||
* and will latch in 0xFF on the read.
|
||||
*/
|
||||
for (i = 0; (phy->rx_buf[0] & 0x01) == 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->rx_buf[3] & 0x01) == 0) {
|
||||
// handle SPI wait states
|
||||
phy->tx_buf[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->rx_buf[0] & 0x01)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == TPM_RETRY) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
spi_xfer.cs_change = 0;
|
||||
spi_xfer.len = transfer_len;
|
||||
spi_xfer.delay_usecs = 5;
|
||||
|
||||
if (direction) {
|
||||
spi_xfer.tx_buf = NULL;
|
||||
spi_xfer.rx_buf = buffer;
|
||||
} else {
|
||||
spi_xfer.tx_buf = buffer;
|
||||
spi_xfer.rx_buf = NULL;
|
||||
}
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
len -= transfer_len;
|
||||
buffer += transfer_len;
|
||||
}
|
||||
|
||||
spi_xfer.cs_change = 0;
|
||||
spi_xfer.len = len;
|
||||
spi_xfer.rx_buf = result;
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
|
||||
exit:
|
||||
spi_bus_unlock(phy->spi_device->master);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, u8 *result)
|
||||
{
|
||||
return tpm_tis_spi_transfer(data, addr, len, result, 0x80);
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, u8 *value)
|
||||
{
|
||||
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
|
||||
int ret, i;
|
||||
struct spi_message m;
|
||||
struct spi_transfer spi_xfer = {
|
||||
.tx_buf = phy->tx_buf,
|
||||
.rx_buf = phy->rx_buf,
|
||||
.len = 4,
|
||||
};
|
||||
|
||||
if (len > MAX_SPI_FRAMESIZE)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->tx_buf[0] = len - 1;
|
||||
phy->tx_buf[1] = 0xd4;
|
||||
phy->tx_buf[2] = (addr >> 8) & 0xFF;
|
||||
phy->tx_buf[3] = addr & 0xFF;
|
||||
|
||||
spi_xfer.cs_change = 1;
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
|
||||
spi_bus_lock(phy->spi_device->master);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
memset(phy->tx_buf, 0, len);
|
||||
|
||||
/* According to TCG PTP specification, if there is no TPM present at
|
||||
* all, then the design has a weak pull-up on MISO. If a TPM is not
|
||||
* present, a pull-up on MISO means that the SB controller sees a 1,
|
||||
* and will latch in 0xFF on the read.
|
||||
*/
|
||||
for (i = 0; (phy->rx_buf[0] & 0x01) == 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;
|
||||
}
|
||||
|
||||
spi_xfer.len = len;
|
||||
spi_xfer.tx_buf = value;
|
||||
spi_xfer.cs_change = 0;
|
||||
spi_xfer.tx_buf = value;
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
|
||||
exit:
|
||||
spi_bus_unlock(phy->spi_device->master);
|
||||
return ret;
|
||||
return tpm_tis_spi_transfer(data, addr, len, value, 0);
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
|
||||
|
||||
65
drivers/char/tpm/tpmrm-dev.c
Normal file
65
drivers/char/tpm/tpmrm-dev.c
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2017 James.Bottomley@HansenPartnership.com
|
||||
*
|
||||
* GPLv2
|
||||
*/
|
||||
#include <linux/slab.h>
|
||||
#include "tpm-dev.h"
|
||||
|
||||
struct tpmrm_priv {
|
||||
struct file_priv priv;
|
||||
struct tpm_space space;
|
||||
};
|
||||
|
||||
static int tpmrm_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct tpm_chip *chip;
|
||||
struct tpmrm_priv *priv;
|
||||
int rc;
|
||||
|
||||
chip = container_of(inode->i_cdev, struct tpm_chip, cdevs);
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = tpm2_init_space(&priv->space);
|
||||
if (rc) {
|
||||
kfree(priv);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tpm_common_open(file, chip, &priv->priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmrm_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct file_priv *fpriv = file->private_data;
|
||||
struct tpmrm_priv *priv = container_of(fpriv, struct tpmrm_priv, priv);
|
||||
|
||||
tpm_common_release(file, fpriv);
|
||||
tpm2_del_space(fpriv->chip, &priv->space);
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t tpmrm_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
struct file_priv *fpriv = file->private_data;
|
||||
struct tpmrm_priv *priv = container_of(fpriv, struct tpmrm_priv, priv);
|
||||
|
||||
return tpm_common_write(file, buf, size, off, &priv->space);
|
||||
}
|
||||
|
||||
const struct file_operations tpmrm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = tpmrm_open,
|
||||
.read = tpm_common_read,
|
||||
.write = tpmrm_write,
|
||||
.release = tpmrm_release,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user