The following series of patches implements a multi-instance vTPM
proxy driver that can dynamically create TPM 'server' and client device
pairs.
Using an ioctl on the provided /dev/vtpmx, a client-side vTPM device
and a server side file descriptor is created. The file descriptor must
be passed to a TPM emulator. The device driver will initialize the
emulated TPM using TPM 1.2 or TPM 2 startup commands and it will read
the command durations from the device in case of a TPM 1.2. The choice
of emulated TPM device (1.2 or 2) must be provided with a flag in
the ioctl.
The driver is based on a recent checkout of James Morris's 'next'
branch and uses several recently posted patches from Jason and Jarkko.
Stefan
v7->v8:
- minor tweaks on the documentation
- Reordered function calls in the VTPM proxy driver's server side release
function so that a client holding the 'ops' lock releases it before the
driver tries to grab the lock when unregistering the device.
v6->v7:
- Adjusted name of driver to tpm_vtpm_proxy from tpm_vtpm. Adjust function
names, names of structures, and names of constants.
- Adjusted IOCTL to use magic 0xa1 rather than the completely used 0xa0.
- Extended driver documentation and added documentation of ioctl.
- Moved test program to own project (dropped patch 11).
v5->v6:
- Adapted errno's for unsupported flags and ioctls following Jason's
comments
v4->v5:
- Introduced different error codes for unsupported flags and ioctls
- Added documentation patch
Jason Gunthorpe (4):
tpm: Get rid of chip->pdev
tpm: Get rid of devname
tpm: Provide strong locking for device removal
tpm: Split out the devm stuff from tpmm_chip_alloc
Stefan Berger (6):
tpm: Get rid of module locking
tpm: Replace device number bitmap with IDR
tpm: Introduce TPM_CHIP_FLAG_VIRTUAL
tpm: Proxy driver for supporting multiple emulated TPMs
tpm: Initialize TPM and get durations and timeouts
tpm: Add documentation for the tpm_vtpm device driver
Documentation/ioctl/ioctl-number.txt | 1 +
Documentation/tpm/tpm_vtpm_proxy.txt | 71 ++++
drivers/char/tpm/Kconfig | 10 +
drivers/char/tpm/Makefile | 1 +
drivers/char/tpm/tpm-chip.c | 221 ++++++++----
drivers/char/tpm/tpm-dev.c | 15 +-
drivers/char/tpm/tpm-interface.c | 50 +--
drivers/char/tpm/tpm-sysfs.c | 22 +-
drivers/char/tpm/tpm.h | 30 +-
drivers/char/tpm/tpm2-cmd.c | 8 +-
drivers/char/tpm/tpm_atmel.c | 14 +-
drivers/char/tpm/tpm_eventlog.c | 2 +-
drivers/char/tpm/tpm_eventlog.h | 2 +-
drivers/char/tpm/tpm_i2c_atmel.c | 16 +-
drivers/char/tpm/tpm_i2c_infineon.c | 6 +-
drivers/char/tpm/tpm_i2c_nuvoton.c | 24 +-
drivers/char/tpm/tpm_infineon.c | 22 +-
drivers/char/tpm/tpm_nsc.c | 20 +-
drivers/char/tpm/tpm_tis.c | 18 +-
drivers/char/tpm/tpm_vtpm_proxy.c | 649 +++++++++++++++++++++++++++++++++++
include/uapi/linux/Kbuild | 1 +
include/uapi/linux/vtpm_proxy.h | 42 +++
22 files changed, 1066 insertions(+), 179 deletions(-)
create mode 100644 Documentation/tpm/tpm_vtpm_proxy.txt
create mode 100644 drivers/char/tpm/tpm_vtpm_proxy.c
create mode 100644 include/uapi/linux/vtpm_proxy.h
--
2.4.3
Replace the device number bitmap with IDR. Extend the number of devices we
can create to 64k.
Since an IDR allows us to associate a pointer with an ID, we use this now
to rewrite tpm_chip_find_get() to simply look up the chip pointer by the
given device ID.
Protect the IDR calls with a mutex.
Signed-off-by: Stefan Berger <[email protected]>
Reviewed-by: Jason Gunthorpe <[email protected]>
Reviewed-by: Jarkko Sakkinen <[email protected]>
Tested-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
drivers/char/tpm/tpm-chip.c | 84 +++++++++++++++++++++-------------------
drivers/char/tpm/tpm-interface.c | 1 +
drivers/char/tpm/tpm.h | 5 +--
3 files changed, 48 insertions(+), 42 deletions(-)
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 5880377..f62c851 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -29,9 +29,8 @@
#include "tpm.h"
#include "tpm_eventlog.h"
-static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
-static LIST_HEAD(tpm_chip_list);
-static DEFINE_SPINLOCK(driver_lock);
+DEFINE_IDR(dev_nums_idr);
+static DEFINE_MUTEX(idr_lock);
struct class *tpm_class;
dev_t tpm_devt;
@@ -88,20 +87,30 @@ EXPORT_SYMBOL_GPL(tpm_put_ops);
*/
struct tpm_chip *tpm_chip_find_get(int chip_num)
{
- struct tpm_chip *pos, *chip = NULL;
+ struct tpm_chip *chip, *res = NULL;
+ int chip_prev;
+
+ mutex_lock(&idr_lock);
+
+ if (chip_num == TPM_ANY_NUM) {
+ chip_num = 0;
+ do {
+ chip_prev = chip_num;
+ chip = idr_get_next(&dev_nums_idr, &chip_num);
+ if (chip && !tpm_try_get_ops(chip)) {
+ res = chip;
+ break;
+ }
+ } while (chip_prev != chip_num);
+ } else {
+ chip = idr_find_slowpath(&dev_nums_idr, chip_num);
+ if (chip && !tpm_try_get_ops(chip))
+ res = chip;
+ }
- rcu_read_lock();
- list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
- if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num)
- continue;
+ mutex_unlock(&idr_lock);
- /* rcu prevents chip from being free'd */
- if (!tpm_try_get_ops(pos))
- chip = pos;
- break;
- }
- rcu_read_unlock();
- return chip;
+ return res;
}
/**
@@ -114,9 +123,10 @@ static void tpm_dev_release(struct device *dev)
{
struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev);
- spin_lock(&driver_lock);
- clear_bit(chip->dev_num, dev_mask);
- spin_unlock(&driver_lock);
+ mutex_lock(&idr_lock);
+ idr_remove(&dev_nums_idr, chip->dev_num);
+ mutex_unlock(&idr_lock);
+
kfree(chip);
}
@@ -142,21 +152,18 @@ struct tpm_chip *tpm_chip_alloc(struct device *dev,
mutex_init(&chip->tpm_mutex);
init_rwsem(&chip->ops_sem);
- INIT_LIST_HEAD(&chip->list);
chip->ops = ops;
- spin_lock(&driver_lock);
- chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES);
- spin_unlock(&driver_lock);
-
- if (chip->dev_num >= TPM_NUM_DEVICES) {
+ mutex_lock(&idr_lock);
+ rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL);
+ mutex_unlock(&idr_lock);
+ if (rc < 0) {
dev_err(dev, "No available tpm device numbers\n");
kfree(chip);
- return ERR_PTR(-ENOMEM);
+ return ERR_PTR(rc);
}
-
- set_bit(chip->dev_num, dev_mask);
+ chip->dev_num = rc;
device_initialize(&chip->dev);
@@ -242,19 +249,28 @@ static int tpm_add_char_device(struct tpm_chip *chip)
return rc;
}
+ /* Make the chip available. */
+ mutex_lock(&idr_lock);
+ idr_replace(&dev_nums_idr, chip, chip->dev_num);
+ mutex_unlock(&idr_lock);
+
return rc;
}
static void tpm_del_char_device(struct tpm_chip *chip)
{
cdev_del(&chip->cdev);
+ device_del(&chip->dev);
+
+ /* Make the chip unavailable. */
+ mutex_lock(&idr_lock);
+ idr_replace(&dev_nums_idr, NULL, chip->dev_num);
+ mutex_unlock(&idr_lock);
/* Make the driver uncallable. */
down_write(&chip->ops_sem);
chip->ops = NULL;
up_write(&chip->ops_sem);
-
- device_del(&chip->dev);
}
static int tpm1_chip_register(struct tpm_chip *chip)
@@ -309,11 +325,6 @@ int tpm_chip_register(struct tpm_chip *chip)
if (rc)
goto out_err;
- /* Make the chip available. */
- spin_lock(&driver_lock);
- list_add_tail_rcu(&chip->list, &tpm_chip_list);
- spin_unlock(&driver_lock);
-
chip->flags |= TPM_CHIP_FLAG_REGISTERED;
if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
@@ -350,11 +361,6 @@ void tpm_chip_unregister(struct tpm_chip *chip)
if (!(chip->flags & TPM_CHIP_FLAG_REGISTERED))
return;
- spin_lock(&driver_lock);
- list_del_rcu(&chip->list);
- spin_unlock(&driver_lock);
- synchronize_rcu();
-
if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
sysfs_remove_link(&chip->dev.parent->kobj, "ppi");
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 5caf154..5397b64 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -1139,6 +1139,7 @@ static int __init tpm_init(void)
static void __exit tpm_exit(void)
{
+ idr_destroy(&dev_nums_idr);
class_destroy(tpm_class);
unregister_chrdev_region(tpm_devt, TPM_NUM_DEVICES);
}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 5fcf788..928b47f 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -34,7 +34,7 @@
enum tpm_const {
TPM_MINOR = 224, /* officially assigned */
TPM_BUFSIZE = 4096,
- TPM_NUM_DEVICES = 256,
+ TPM_NUM_DEVICES = 65536,
TPM_RETRY = 50, /* 5 seconds */
};
@@ -195,8 +195,6 @@ struct tpm_chip {
acpi_handle acpi_dev_handle;
char ppi_version[TPM_PPI_VERSION_LEN + 1];
#endif /* CONFIG_ACPI */
-
- struct list_head list;
};
#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
@@ -492,6 +490,7 @@ static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
extern struct class *tpm_class;
extern dev_t tpm_devt;
extern const struct file_operations tpm_fops;
+extern struct idr dev_nums_idr;
ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *);
ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
--
2.4.3
From: Jason Gunthorpe <[email protected]>
This is a hold over from before the struct device conversion.
- All prints should be using &chip->dev, which is the Linux
standard. This changes prints to use tpm0 as the device name,
not the PnP/etc ID.
- The few places involving sysfs/modules that really do need the
parent just use chip->dev.parent instead
- We no longer need to get_device(pdev) in any places since it is no
longer used by any of the code. The kref on the parent is held
by the device core during device_add and dropped in device_del
Signed-off-by: Jason Gunthorpe <[email protected]>
Signed-off-by: Stefan Berger <[email protected]>
Tested-by: Stefan Berger <[email protected]>
Reviewed-by: Jarkko Sakkinen <[email protected]>
Tested-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
drivers/char/tpm/tpm-chip.c | 15 ++++++---------
drivers/char/tpm/tpm-dev.c | 4 +---
drivers/char/tpm/tpm-interface.c | 30 ++++++++++++++++--------------
drivers/char/tpm/tpm-sysfs.c | 6 +++---
drivers/char/tpm/tpm.h | 3 +--
drivers/char/tpm/tpm2-cmd.c | 8 ++++----
drivers/char/tpm/tpm_atmel.c | 14 +++++++-------
drivers/char/tpm/tpm_i2c_atmel.c | 16 ++++++++--------
drivers/char/tpm/tpm_i2c_infineon.c | 6 +++---
drivers/char/tpm/tpm_i2c_nuvoton.c | 22 +++++++++++-----------
drivers/char/tpm/tpm_infineon.c | 22 +++++++++++-----------
drivers/char/tpm/tpm_nsc.c | 20 ++++++++++----------
drivers/char/tpm/tpm_tis.c | 16 ++++++++--------
13 files changed, 89 insertions(+), 93 deletions(-)
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 274dd01..12829dd 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -49,7 +49,7 @@ struct tpm_chip *tpm_chip_find_get(int chip_num)
if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num)
continue;
- if (try_module_get(pos->pdev->driver->owner)) {
+ if (try_module_get(pos->dev.parent->driver->owner)) {
chip = pos;
break;
}
@@ -113,13 +113,11 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
scnprintf(chip->devname, sizeof(chip->devname), "tpm%d", chip->dev_num);
- chip->pdev = dev;
-
dev_set_drvdata(dev, chip);
chip->dev.class = tpm_class;
chip->dev.release = tpm_dev_release;
- chip->dev.parent = chip->pdev;
+ chip->dev.parent = dev;
#ifdef CONFIG_ACPI
chip->dev.groups = chip->groups;
#endif
@@ -134,7 +132,7 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
device_initialize(&chip->dev);
cdev_init(&chip->cdev, &tpm_fops);
- chip->cdev.owner = chip->pdev->driver->owner;
+ chip->cdev.owner = dev->driver->owner;
chip->cdev.kobj.parent = &chip->dev.kobj;
rc = devm_add_action(dev, (void (*)(void *)) put_device, &chip->dev);
@@ -241,9 +239,8 @@ int tpm_chip_register(struct tpm_chip *chip)
chip->flags |= TPM_CHIP_FLAG_REGISTERED;
if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
- rc = __compat_only_sysfs_link_entry_to_kobj(&chip->pdev->kobj,
- &chip->dev.kobj,
- "ppi");
+ rc = __compat_only_sysfs_link_entry_to_kobj(
+ &chip->dev.parent->kobj, &chip->dev.kobj, "ppi");
if (rc && rc != -ENOENT) {
tpm_chip_unregister(chip);
return rc;
@@ -278,7 +275,7 @@ void tpm_chip_unregister(struct tpm_chip *chip)
synchronize_rcu();
if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
- sysfs_remove_link(&chip->pdev->kobj, "ppi");
+ sysfs_remove_link(&chip->dev.parent->kobj, "ppi");
tpm1_chip_unregister(chip);
tpm_del_char_device(chip);
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index de0337e..4009765 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -61,7 +61,7 @@ static int tpm_open(struct inode *inode, struct file *file)
* by the check of is_open variable, which is protected
* by driver_lock. */
if (test_and_set_bit(0, &chip->is_open)) {
- dev_dbg(chip->pdev, "Another process owns this TPM\n");
+ dev_dbg(&chip->dev, "Another process owns this TPM\n");
return -EBUSY;
}
@@ -79,7 +79,6 @@ static int tpm_open(struct inode *inode, struct file *file)
INIT_WORK(&priv->work, timeout_work);
file->private_data = priv;
- get_device(chip->pdev);
return 0;
}
@@ -166,7 +165,6 @@ static int tpm_release(struct inode *inode, struct file *file)
file->private_data = NULL;
atomic_set(&priv->data_pending, 0);
clear_bit(0, &priv->chip->is_open);
- put_device(priv->chip->pdev);
kfree(priv);
return 0;
}
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index e2fa89c..483f86f 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -345,7 +345,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
if (count == 0)
return -ENODATA;
if (count > bufsiz) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"invalid count value %x %zx\n", count, bufsiz);
return -E2BIG;
}
@@ -354,7 +354,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
rc = chip->ops->send(chip, (u8 *) buf, count);
if (rc < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"tpm_transmit: tpm_send: error %zd\n", rc);
goto out;
}
@@ -373,7 +373,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
goto out_recv;
if (chip->ops->req_canceled(chip, status)) {
- dev_err(chip->pdev, "Operation Canceled\n");
+ dev_err(&chip->dev, "Operation Canceled\n");
rc = -ECANCELED;
goto out;
}
@@ -383,14 +383,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
} while (time_before(jiffies, stop));
chip->ops->cancel(chip);
- dev_err(chip->pdev, "Operation Timed out\n");
+ dev_err(&chip->dev, "Operation Timed out\n");
rc = -ETIME;
goto out;
out_recv:
rc = chip->ops->recv(chip, (u8 *) buf, bufsiz);
if (rc < 0)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"tpm_transmit: tpm_recv: error %zd\n", rc);
out:
mutex_unlock(&chip->tpm_mutex);
@@ -416,7 +416,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd,
err = be32_to_cpu(header->return_code);
if (err != 0 && desc)
- dev_err(chip->pdev, "A TPM error (%d) occurred %s\n", err,
+ dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err,
desc);
return err;
@@ -527,7 +527,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
if (rc == TPM_ERR_INVALID_POSTINIT) {
/* The TPM is not started, we are the first to talk to it.
Execute a startup command. */
- dev_info(chip->pdev, "Issuing TPM_STARTUP");
+ dev_info(&chip->dev, "Issuing TPM_STARTUP");
if (tpm_startup(chip, TPM_ST_CLEAR))
return rc;
@@ -539,7 +539,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
NULL);
}
if (rc) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"A TPM error (%zd) occurred attempting to determine the timeouts\n",
rc);
goto duration;
@@ -578,7 +578,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
/* Report adjusted timeouts */
if (chip->vendor.timeout_adjusted) {
- dev_info(chip->pdev,
+ dev_info(&chip->dev,
HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
old_timeout[0], new_timeout[0],
old_timeout[1], new_timeout[1],
@@ -625,7 +625,7 @@ duration:
chip->vendor.duration[TPM_MEDIUM] *= 1000;
chip->vendor.duration[TPM_LONG] *= 1000;
chip->vendor.duration_adjusted = true;
- dev_info(chip->pdev, "Adjusting TPM timeout parameters.");
+ dev_info(&chip->dev, "Adjusting TPM timeout parameters.");
}
return 0;
}
@@ -815,7 +815,9 @@ int tpm_do_selftest(struct tpm_chip *chip)
* around 300ms while the self test is ongoing, keep trying
* until the self test duration expires. */
if (rc == -ETIME) {
- dev_info(chip->pdev, HW_ERR "TPM command timed out during continue self test");
+ dev_info(
+ &chip->dev, HW_ERR
+ "TPM command timed out during continue self test");
msleep(delay_msec);
continue;
}
@@ -825,7 +827,7 @@ int tpm_do_selftest(struct tpm_chip *chip)
rc = be32_to_cpu(cmd.header.out.return_code);
if (rc == TPM_ERR_DISABLED || rc == TPM_ERR_DEACTIVATED) {
- dev_info(chip->pdev,
+ dev_info(&chip->dev,
"TPM is disabled/deactivated (0x%X)\n", rc);
/* TPM is disabled and/or deactivated; driver can
* proceed and TPM does handle commands for
@@ -978,10 +980,10 @@ int tpm_pm_suspend(struct device *dev)
}
if (rc)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"Error (%d) sending savestate before suspend\n", rc);
else if (try > 0)
- dev_warn(chip->pdev, "TPM savestate took %dms\n",
+ dev_warn(&chip->dev, "TPM savestate took %dms\n",
try * TPM_TIMEOUT_RETRY);
return rc;
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index ee66fd4..d93736a 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -284,16 +284,16 @@ static const struct attribute_group tpm_dev_group = {
int tpm_sysfs_add_device(struct tpm_chip *chip)
{
int err;
- err = sysfs_create_group(&chip->pdev->kobj,
+ err = sysfs_create_group(&chip->dev.parent->kobj,
&tpm_dev_group);
if (err)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"failed to create sysfs attributes, %d\n", err);
return err;
}
void tpm_sysfs_del_device(struct tpm_chip *chip)
{
- sysfs_remove_group(&chip->pdev->kobj, &tpm_dev_group);
+ sysfs_remove_group(&chip->dev.parent->kobj, &tpm_dev_group);
}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 28b477e..9c9be6c 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -167,7 +167,6 @@ enum tpm_chip_flags {
};
struct tpm_chip {
- struct device *pdev; /* Device stuff */
struct device dev;
struct cdev cdev;
@@ -199,7 +198,7 @@ struct tpm_chip {
static inline void tpm_chip_put(struct tpm_chip *chip)
{
- module_put(chip->pdev->driver->owner);
+ module_put(chip->dev.parent->driver->owner);
}
static inline int tpm_read_index(int base, int index)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index b28e4da..5fc0e7c 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -597,7 +597,7 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
if (rc) {
- dev_warn(chip->pdev, "0x%08x was not flushed, out of memory\n",
+ dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
handle);
return;
}
@@ -606,7 +606,7 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "flushing context");
if (rc)
- dev_warn(chip->pdev, "0x%08x was not flushed, rc=%d\n", handle,
+ dev_warn(&chip->dev, "0x%08x was not flushed, rc=%d\n", handle,
rc);
tpm_buf_destroy(&buf);
@@ -770,7 +770,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
* except print the error code on a system failure.
*/
if (rc < 0)
- dev_warn(chip->pdev, "transmit returned %d while stopping the TPM",
+ dev_warn(&chip->dev, "transmit returned %d while stopping the TPM",
rc);
}
EXPORT_SYMBOL_GPL(tpm2_shutdown);
@@ -837,7 +837,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
* immediately. This is a workaround for that.
*/
if (rc == TPM2_RC_TESTING) {
- dev_warn(chip->pdev, "Got RC_TESTING, ignoring\n");
+ dev_warn(&chip->dev, "Got RC_TESTING, ignoring\n");
rc = 0;
}
diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c
index dfadad0..a48a878 100644
--- a/drivers/char/tpm/tpm_atmel.c
+++ b/drivers/char/tpm/tpm_atmel.c
@@ -49,7 +49,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
for (i = 0; i < 6; i++) {
status = ioread8(chip->vendor.iobase + 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
- dev_err(chip->pdev, "error reading header\n");
+ dev_err(&chip->dev, "error reading header\n");
return -EIO;
}
*buf++ = ioread8(chip->vendor.iobase);
@@ -60,12 +60,12 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
size = be32_to_cpu(*native_size);
if (count < size) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"Recv size(%d) less than available space\n", size);
for (; i < size; i++) { /* clear the waiting data anyway */
status = ioread8(chip->vendor.iobase + 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
- dev_err(chip->pdev, "error reading data\n");
+ dev_err(&chip->dev, "error reading data\n");
return -EIO;
}
}
@@ -76,7 +76,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
for (; i < size; i++) {
status = ioread8(chip->vendor.iobase + 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
- dev_err(chip->pdev, "error reading data\n");
+ dev_err(&chip->dev, "error reading data\n");
return -EIO;
}
*buf++ = ioread8(chip->vendor.iobase);
@@ -86,7 +86,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
status = ioread8(chip->vendor.iobase + 1);
if (status & ATML_STATUS_DATA_AVAIL) {
- dev_err(chip->pdev, "data available is stuck\n");
+ dev_err(&chip->dev, "data available is stuck\n");
return -EIO;
}
@@ -97,9 +97,9 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
{
int i;
- dev_dbg(chip->pdev, "tpm_atml_send:\n");
+ dev_dbg(&chip->dev, "tpm_atml_send:\n");
for (i = 0; i < count; i++) {
- dev_dbg(chip->pdev, "%d 0x%x(%d)\n", i, buf[i], buf[i]);
+ dev_dbg(&chip->dev, "%d 0x%x(%d)\n", i, buf[i], buf[i]);
iowrite8(buf[i], chip->vendor.iobase);
}
diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c
index 8dfb88b..dd8f0eb 100644
--- a/drivers/char/tpm/tpm_i2c_atmel.c
+++ b/drivers/char/tpm/tpm_i2c_atmel.c
@@ -52,7 +52,7 @@ struct priv_data {
static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
{
struct priv_data *priv = chip->vendor.priv;
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
s32 status;
priv->len = 0;
@@ -62,7 +62,7 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
status = i2c_master_send(client, buf, len);
- dev_dbg(chip->pdev,
+ dev_dbg(&chip->dev,
"%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__,
(int)min_t(size_t, 64, len), buf, len, status);
return status;
@@ -71,7 +71,7 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
struct priv_data *priv = chip->vendor.priv;
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
struct tpm_output_header *hdr =
(struct tpm_output_header *)priv->buffer;
u32 expected_len;
@@ -88,7 +88,7 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
return -ENOMEM;
if (priv->len >= expected_len) {
- dev_dbg(chip->pdev,
+ dev_dbg(&chip->dev,
"%s early(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
(int)min_t(size_t, 64, expected_len), buf, count,
expected_len);
@@ -97,7 +97,7 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
}
rc = i2c_master_recv(client, buf, expected_len);
- dev_dbg(chip->pdev,
+ dev_dbg(&chip->dev,
"%s reread(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
(int)min_t(size_t, 64, expected_len), buf, count,
expected_len);
@@ -106,13 +106,13 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
static void i2c_atmel_cancel(struct tpm_chip *chip)
{
- dev_err(chip->pdev, "TPM operation cancellation was requested, but is not supported");
+ dev_err(&chip->dev, "TPM operation cancellation was requested, but is not supported");
}
static u8 i2c_atmel_read_status(struct tpm_chip *chip)
{
struct priv_data *priv = chip->vendor.priv;
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
int rc;
/* The TPM fails the I2C read until it is ready, so we do the entire
@@ -125,7 +125,7 @@ static u8 i2c_atmel_read_status(struct tpm_chip *chip)
/* Once the TPM has completed the command the command remains readable
* until another command is issued. */
rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer));
- dev_dbg(chip->pdev,
+ dev_dbg(&chip->dev,
"%s: sts=%d", __func__, rc);
if (rc <= 0)
return 0;
diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c
index 63d5d22..f2aa99e 100644
--- a/drivers/char/tpm/tpm_i2c_infineon.c
+++ b/drivers/char/tpm/tpm_i2c_infineon.c
@@ -446,7 +446,7 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
/* read first 10 bytes, including tag, paramsize, and result */
size = recv_data(chip, buf, TPM_HEADER_SIZE);
if (size < TPM_HEADER_SIZE) {
- dev_err(chip->pdev, "Unable to read header\n");
+ dev_err(&chip->dev, "Unable to read header\n");
goto out;
}
@@ -459,14 +459,14 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
size += recv_data(chip, &buf[TPM_HEADER_SIZE],
expected - TPM_HEADER_SIZE);
if (size < expected) {
- dev_err(chip->pdev, "Unable to read remainder of result\n");
+ dev_err(&chip->dev, "Unable to read remainder of result\n");
size = -ETIME;
goto out;
}
wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
if (status & TPM_STS_DATA_AVAIL) { /* retry? */
- dev_err(chip->pdev, "Error left over data\n");
+ dev_err(&chip->dev, "Error left over data\n");
size = -EIO;
goto out;
}
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
index 847f159..a1e1474 100644
--- a/drivers/char/tpm/tpm_i2c_nuvoton.c
+++ b/drivers/char/tpm/tpm_i2c_nuvoton.c
@@ -96,13 +96,13 @@ static s32 i2c_nuvoton_write_buf(struct i2c_client *client, u8 offset, u8 size,
/* read TPM_STS register */
static u8 i2c_nuvoton_read_status(struct tpm_chip *chip)
{
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
s32 status;
u8 data;
status = i2c_nuvoton_read_buf(client, TPM_STS, 1, &data);
if (status <= 0) {
- dev_err(chip->pdev, "%s() error return %d\n", __func__,
+ dev_err(&chip->dev, "%s() error return %d\n", __func__,
status);
data = TPM_STS_ERR_VAL;
}
@@ -127,13 +127,13 @@ static s32 i2c_nuvoton_write_status(struct i2c_client *client, u8 data)
/* write commandReady to TPM_STS register */
static void i2c_nuvoton_ready(struct tpm_chip *chip)
{
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
s32 status;
/* this causes the current command to be aborted */
status = i2c_nuvoton_write_status(client, TPM_STS_COMMAND_READY);
if (status < 0)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"%s() fail to write TPM_STS.commandReady\n", __func__);
}
@@ -212,7 +212,7 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value,
return 0;
} while (time_before(jiffies, stop));
}
- dev_err(chip->pdev, "%s(%02x, %02x) -> timeout\n", __func__, mask,
+ dev_err(&chip->dev, "%s(%02x, %02x) -> timeout\n", __func__, mask,
value);
return -ETIMEDOUT;
}
@@ -240,7 +240,7 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client,
&chip->vendor.read_queue) == 0) {
burst_count = i2c_nuvoton_get_burstcount(client, chip);
if (burst_count < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"%s() fail to read burstCount=%d\n", __func__,
burst_count);
return -EIO;
@@ -249,12 +249,12 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client,
rc = i2c_nuvoton_read_buf(client, TPM_DATA_FIFO_R,
bytes2read, &buf[size]);
if (rc < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"%s() fail on i2c_nuvoton_read_buf()=%d\n",
__func__, rc);
return -EIO;
}
- dev_dbg(chip->pdev, "%s(%d):", __func__, bytes2read);
+ dev_dbg(&chip->dev, "%s(%d):", __func__, bytes2read);
size += bytes2read;
}
@@ -264,7 +264,7 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client,
/* Read TPM command results */
static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
- struct device *dev = chip->pdev;
+ struct device *dev = chip->dev.parent;
struct i2c_client *client = to_i2c_client(dev);
s32 rc;
int expected, status, burst_count, retries, size = 0;
@@ -334,7 +334,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
break;
}
i2c_nuvoton_ready(chip);
- dev_dbg(chip->pdev, "%s() -> %d\n", __func__, size);
+ dev_dbg(&chip->dev, "%s() -> %d\n", __func__, size);
return size;
}
@@ -347,7 +347,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
*/
static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
{
- struct device *dev = chip->pdev;
+ struct device *dev = chip->dev.parent;
struct i2c_client *client = to_i2c_client(dev);
u32 ordinal;
size_t count = 0;
diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c
index 6c488e6..e3cf9f3 100644
--- a/drivers/char/tpm/tpm_infineon.c
+++ b/drivers/char/tpm/tpm_infineon.c
@@ -195,9 +195,9 @@ static int wait(struct tpm_chip *chip, int wait_for_bit)
}
if (i == TPM_MAX_TRIES) { /* timeout occurs */
if (wait_for_bit == STAT_XFE)
- dev_err(chip->pdev, "Timeout in wait(STAT_XFE)\n");
+ dev_err(&chip->dev, "Timeout in wait(STAT_XFE)\n");
if (wait_for_bit == STAT_RDA)
- dev_err(chip->pdev, "Timeout in wait(STAT_RDA)\n");
+ dev_err(&chip->dev, "Timeout in wait(STAT_RDA)\n");
return -EIO;
}
return 0;
@@ -220,7 +220,7 @@ static void wait_and_send(struct tpm_chip *chip, u8 sendbyte)
static void tpm_wtx(struct tpm_chip *chip)
{
number_of_wtx++;
- dev_info(chip->pdev, "Granting WTX (%02d / %02d)\n",
+ dev_info(&chip->dev, "Granting WTX (%02d / %02d)\n",
number_of_wtx, TPM_MAX_WTX_PACKAGES);
wait_and_send(chip, TPM_VL_VER);
wait_and_send(chip, TPM_CTRL_WTX);
@@ -231,7 +231,7 @@ static void tpm_wtx(struct tpm_chip *chip)
static void tpm_wtx_abort(struct tpm_chip *chip)
{
- dev_info(chip->pdev, "Aborting WTX\n");
+ dev_info(&chip->dev, "Aborting WTX\n");
wait_and_send(chip, TPM_VL_VER);
wait_and_send(chip, TPM_CTRL_WTX_ABORT);
wait_and_send(chip, 0x00);
@@ -257,7 +257,7 @@ recv_begin:
}
if (buf[0] != TPM_VL_VER) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"Wrong transport protocol implementation!\n");
return -EIO;
}
@@ -272,7 +272,7 @@ recv_begin:
}
if ((size == 0x6D00) && (buf[1] == 0x80)) {
- dev_err(chip->pdev, "Error handling on vendor layer!\n");
+ dev_err(&chip->dev, "Error handling on vendor layer!\n");
return -EIO;
}
@@ -284,7 +284,7 @@ recv_begin:
}
if (buf[1] == TPM_CTRL_WTX) {
- dev_info(chip->pdev, "WTX-package received\n");
+ dev_info(&chip->dev, "WTX-package received\n");
if (number_of_wtx < TPM_MAX_WTX_PACKAGES) {
tpm_wtx(chip);
goto recv_begin;
@@ -295,14 +295,14 @@ recv_begin:
}
if (buf[1] == TPM_CTRL_WTX_ABORT_ACK) {
- dev_info(chip->pdev, "WTX-abort acknowledged\n");
+ dev_info(&chip->dev, "WTX-abort acknowledged\n");
return size;
}
if (buf[1] == TPM_CTRL_ERROR) {
- dev_err(chip->pdev, "ERROR-package received:\n");
+ dev_err(&chip->dev, "ERROR-package received:\n");
if (buf[4] == TPM_INF_NAK)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"-> Negative acknowledgement"
" - retransmit command!\n");
return -EIO;
@@ -321,7 +321,7 @@ static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count)
ret = empty_fifo(chip, 1);
if (ret) {
- dev_err(chip->pdev, "Timeout while clearing FIFO\n");
+ dev_err(&chip->dev, "Timeout while clearing FIFO\n");
return -EIO;
}
diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c
index 289389e..766370b 100644
--- a/drivers/char/tpm/tpm_nsc.c
+++ b/drivers/char/tpm/tpm_nsc.c
@@ -113,7 +113,7 @@ static int nsc_wait_for_ready(struct tpm_chip *chip)
}
while (time_before(jiffies, stop));
- dev_info(chip->pdev, "wait for ready failed\n");
+ dev_info(&chip->dev, "wait for ready failed\n");
return -EBUSY;
}
@@ -129,12 +129,12 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
return -EIO;
if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
- dev_err(chip->pdev, "F0 timeout\n");
+ dev_err(&chip->dev, "F0 timeout\n");
return -EIO;
}
if ((data =
inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
- dev_err(chip->pdev, "not in normal mode (0x%x)\n",
+ dev_err(&chip->dev, "not in normal mode (0x%x)\n",
data);
return -EIO;
}
@@ -143,7 +143,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
for (p = buffer; p < &buffer[count]; p++) {
if (wait_for_stat
(chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"OBF timeout (while reading data)\n");
return -EIO;
}
@@ -154,11 +154,11 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
if ((data & NSC_STATUS_F0) == 0 &&
(wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0)) {
- dev_err(chip->pdev, "F0 not set\n");
+ dev_err(&chip->dev, "F0 not set\n");
return -EIO;
}
if ((data = inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_EOC) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"expected end of command(0x%x)\n", data);
return -EIO;
}
@@ -189,19 +189,19 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
return -EIO;
if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
- dev_err(chip->pdev, "IBF timeout\n");
+ dev_err(&chip->dev, "IBF timeout\n");
return -EIO;
}
outb(NSC_COMMAND_NORMAL, chip->vendor.base + NSC_COMMAND);
if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
- dev_err(chip->pdev, "IBR timeout\n");
+ dev_err(&chip->dev, "IBR timeout\n");
return -EIO;
}
for (i = 0; i < count; i++) {
if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"IBF timeout (while writing data)\n");
return -EIO;
}
@@ -209,7 +209,7 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
}
if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
- dev_err(chip->pdev, "IBF timeout\n");
+ dev_err(&chip->dev, "IBF timeout\n");
return -EIO;
}
outb(NSC_COMMAND_EOC, chip->vendor.base + NSC_COMMAND);
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index a507006..0a9aee9 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -260,7 +260,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
/* read first 10 bytes, including tag, paramsize, and result */
if ((size =
recv_data(chip, buf, TPM_HEADER_SIZE)) < TPM_HEADER_SIZE) {
- dev_err(chip->pdev, "Unable to read header\n");
+ dev_err(&chip->dev, "Unable to read header\n");
goto out;
}
@@ -273,7 +273,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
if ((size +=
recv_data(chip, &buf[TPM_HEADER_SIZE],
expected - TPM_HEADER_SIZE)) < expected) {
- dev_err(chip->pdev, "Unable to read remainder of result\n");
+ dev_err(&chip->dev, "Unable to read remainder of result\n");
size = -ETIME;
goto out;
}
@@ -282,7 +282,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
&chip->vendor.int_queue, false);
status = tpm_tis_status(chip);
if (status & TPM_STS_DATA_AVAIL) { /* retry? */
- dev_err(chip->pdev, "Error left over data\n");
+ dev_err(&chip->dev, "Error left over data\n");
size = -EIO;
goto out;
}
@@ -368,7 +368,7 @@ static void disable_interrupts(struct tpm_chip *chip)
iowrite32(intmask,
chip->vendor.iobase +
TPM_INT_ENABLE(chip->vendor.locality));
- devm_free_irq(chip->pdev, chip->vendor.irq, chip);
+ devm_free_irq(&chip->dev, chip->vendor.irq, chip);
chip->vendor.irq = 0;
}
@@ -497,7 +497,7 @@ static int probe_itpm(struct tpm_chip *chip)
rc = tpm_tis_send_data(chip, cmd_getticks, len);
if (rc == 0) {
- dev_info(chip->pdev, "Detected an iTPM.\n");
+ dev_info(&chip->dev, "Detected an iTPM.\n");
rc = 1;
} else
rc = -EFAULT;
@@ -576,9 +576,9 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask,
struct priv_data *priv = chip->vendor.priv;
u8 original_int_vec;
- if (devm_request_irq(chip->pdev, irq, tis_int_handler, flags,
+ if (devm_request_irq(&chip->dev, irq, tis_int_handler, flags,
chip->devname, chip) != 0) {
- dev_info(chip->pdev, "Unable to request irq: %d for probe\n",
+ dev_info(&chip->dev, "Unable to request irq: %d for probe\n",
irq);
return -1;
}
@@ -779,7 +779,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED,
tpm_info->irq);
if (!chip->vendor.irq)
- dev_err(chip->pdev, FW_BUG
+ dev_err(&chip->dev, FW_BUG
"TPM interrupt not working, polling instead\n");
} else
tpm_tis_probe_irq(chip, intmask);
--
2.4.3
From: Jason Gunthorpe <[email protected]>
Add a read/write semaphore around the ops function pointers so
ops can be set to null when the driver un-registers.
Previously the tpm core expected module locking to be enough to
ensure that tpm_unregister could not be called during certain times,
however that hasn't been sufficient for a long time.
Introduce a read/write semaphore around 'ops' so the core can set
it to null when unregistering. This provides a strong fence around
the driver callbacks, guaranteeing to the driver that no callbacks
are running or will run again.
For now the ops_lock is placed very high in the call stack, it could
be pushed down and made more granular in future if necessary.
Signed-off-by: Jason Gunthorpe <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Reviewed-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
drivers/char/tpm/tpm-chip.c | 72 ++++++++++++++++++++++++++++++++++++----
drivers/char/tpm/tpm-dev.c | 11 +++++-
drivers/char/tpm/tpm-interface.c | 19 ++++++-----
drivers/char/tpm/tpm-sysfs.c | 5 +++
drivers/char/tpm/tpm.h | 14 +++++---
5 files changed, 100 insertions(+), 21 deletions(-)
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index c21d81c..5793ea1 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -36,10 +36,60 @@ static DEFINE_SPINLOCK(driver_lock);
struct class *tpm_class;
dev_t tpm_devt;
-/*
- * tpm_chip_find_get - return tpm_chip for a given chip number
- * @chip_num the device number for the chip
+/**
+ * tpm_try_get_ops() - Get a ref to the tpm_chip
+ * @chip: Chip to ref
+ *
+ * The caller must already have some kind of locking to ensure that chip is
+ * valid. This function will lock the chip so that the ops member can be
+ * accessed safely. The locking prevents tpm_chip_unregister from
+ * completing, so it should not be held for long periods.
+ *
+ * Returns -ERRNO if the chip could not be got.
*/
+int tpm_try_get_ops(struct tpm_chip *chip)
+{
+ int rc = -EIO;
+
+ get_device(&chip->dev);
+
+ down_read(&chip->ops_sem);
+ if (!chip->ops)
+ goto out_lock;
+
+ if (!try_module_get(chip->dev.parent->driver->owner))
+ goto out_lock;
+
+ return 0;
+out_lock:
+ up_read(&chip->ops_sem);
+ put_device(&chip->dev);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_try_get_ops);
+
+/**
+ * tpm_put_ops() - Release a ref to the tpm_chip
+ * @chip: Chip to put
+ *
+ * This is the opposite pair to tpm_try_get_ops(). After this returns chip may
+ * be kfree'd.
+ */
+void tpm_put_ops(struct tpm_chip *chip)
+{
+ module_put(chip->dev.parent->driver->owner);
+ up_read(&chip->ops_sem);
+ put_device(&chip->dev);
+}
+EXPORT_SYMBOL_GPL(tpm_put_ops);
+
+/**
+ * tpm_chip_find_get() - return tpm_chip for a given chip number
+ * @chip_num: id to find
+ *
+ * The return'd chip has been tpm_try_get_ops'd and must be released via
+ * tpm_put_ops
+ */
struct tpm_chip *tpm_chip_find_get(int chip_num)
{
struct tpm_chip *pos, *chip = NULL;
@@ -49,10 +99,10 @@ struct tpm_chip *tpm_chip_find_get(int chip_num)
if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num)
continue;
- if (try_module_get(pos->dev.parent->driver->owner)) {
+ /* rcu prevents chip from being free'd */
+ if (!tpm_try_get_ops(pos))
chip = pos;
- break;
- }
+ break;
}
rcu_read_unlock();
return chip;
@@ -95,6 +145,7 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
return ERR_PTR(-ENOMEM);
mutex_init(&chip->tpm_mutex);
+ init_rwsem(&chip->ops_sem);
INIT_LIST_HEAD(&chip->list);
chip->ops = ops;
@@ -180,6 +231,12 @@ static int tpm_add_char_device(struct tpm_chip *chip)
static void tpm_del_char_device(struct tpm_chip *chip)
{
cdev_del(&chip->cdev);
+
+ /* Make the driver uncallable. */
+ down_write(&chip->ops_sem);
+ chip->ops = NULL;
+ up_write(&chip->ops_sem);
+
device_del(&chip->dev);
}
@@ -265,6 +322,9 @@ EXPORT_SYMBOL_GPL(tpm_chip_register);
* Takes the chip first away from the list of available TPM chips and then
* cleans up all the resources reserved by tpm_chip_register().
*
+ * Once this function returns the driver call backs in 'op's will not be
+ * running and will no longer start.
+ *
* NOTE: This function should be only called before deinitializing chip
* resources.
*/
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index 4009765..f5d4521 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -136,9 +136,18 @@ static ssize_t tpm_write(struct file *file, const char __user *buf,
return -EFAULT;
}
- /* atomic tpm command send and result receive */
+ /* 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));
+
+ tpm_put_ops(priv->chip);
if (out_size < 0) {
mutex_unlock(&priv->buffer_mutex);
return out_size;
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 483f86f..5caf154 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -700,7 +700,7 @@ int tpm_is_tpm2(u32 chip_num)
rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0;
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
@@ -729,7 +729,7 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
rc = tpm2_pcr_read(chip, pcr_idx, res_buf);
else
rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_pcr_read);
@@ -764,7 +764,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
rc = tpm2_pcr_extend(chip, pcr_idx, hash);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
@@ -774,7 +774,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
"attempting extend a PCR value");
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_pcr_extend);
@@ -855,7 +855,7 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen)
rc = tpm_transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd");
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_send);
@@ -1037,7 +1037,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
err = tpm2_get_random(chip, out, max);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return err;
}
@@ -1059,7 +1059,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
num_bytes -= recd;
} while (retries-- && total < max);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return total ? total : -EIO;
}
EXPORT_SYMBOL_GPL(tpm_get_random);
@@ -1085,7 +1085,7 @@ int tpm_seal_trusted(u32 chip_num, struct trusted_key_payload *payload,
rc = tpm2_seal_trusted(chip, payload, options);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_seal_trusted);
@@ -1111,7 +1111,8 @@ int tpm_unseal_trusted(u32 chip_num, struct trusted_key_payload *payload,
rc = tpm2_unseal_trusted(chip, payload, options);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
+
return rc;
}
EXPORT_SYMBOL_GPL(tpm_unseal_trusted);
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index d93736a..34e7fc7 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -295,5 +295,10 @@ int tpm_sysfs_add_device(struct tpm_chip *chip)
void tpm_sysfs_del_device(struct tpm_chip *chip)
{
+ /* The sysfs routines rely on an implicit tpm_try_get_ops, this
+ * function is called before ops is null'd and the sysfs core
+ * synchronizes this removal so that no callbacks are running or can
+ * run again
+ */
sysfs_remove_group(&chip->dev.parent->kobj, &tpm_dev_group);
}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 5d33ba5..c6376b1 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -170,7 +170,13 @@ struct tpm_chip {
struct device dev;
struct cdev cdev;
+ /* A driver callback under ops cannot be run unless ops_sem is held
+ * (sometimes implicitly, eg for the sysfs code). ops becomes null
+ * when the driver is unregistered, see tpm_try_get_ops.
+ */
+ struct rw_semaphore ops_sem;
const struct tpm_class_ops *ops;
+
unsigned int flags;
int dev_num; /* /dev/tpm# */
@@ -195,11 +201,6 @@ struct tpm_chip {
#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
-static inline void tpm_chip_put(struct tpm_chip *chip)
-{
- module_put(chip->dev.parent->driver->owner);
-}
-
static inline int tpm_read_index(int base, int index)
{
outb(index, base);
@@ -507,6 +508,9 @@ extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
wait_queue_head_t *, bool);
struct tpm_chip *tpm_chip_find_get(int chip_num);
+__must_check int tpm_try_get_ops(struct tpm_chip *chip);
+void tpm_put_ops(struct tpm_chip *chip);
+
extern struct tpm_chip *tpmm_chip_alloc(struct device *dev,
const struct tpm_class_ops *ops);
extern int tpm_chip_register(struct tpm_chip *chip);
--
2.4.3
Introduce TPM_CHIP_FLAG_VIRTUAL to be used when the chip device has no
parent device. Also adapt tpm_chip_alloc so that it can be called with
parent device being NULL.
Signed-off-by: Stefan Berger <[email protected]>
Reviewed-by: Jason Gunthorpe <[email protected]>
Reviewed-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
drivers/char/tpm/tpm-chip.c | 9 +++++----
drivers/char/tpm/tpm-sysfs.c | 15 +++++++++++----
drivers/char/tpm/tpm.h | 5 +++--
3 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index f62c851..e4e1fad 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -170,9 +170,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *dev,
chip->dev.class = tpm_class;
chip->dev.release = tpm_dev_release;
chip->dev.parent = dev;
-#ifdef CONFIG_ACPI
chip->dev.groups = chip->groups;
-#endif
if (chip->dev_num == 0)
chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR);
@@ -183,6 +181,9 @@ struct tpm_chip *tpm_chip_alloc(struct device *dev,
if (rc)
goto out;
+ if (!dev)
+ chip->flags |= TPM_CHIP_FLAG_VIRTUAL;
+
cdev_init(&chip->cdev, &tpm_fops);
chip->cdev.owner = THIS_MODULE;
chip->cdev.kobj.parent = &chip->dev.kobj;
@@ -327,7 +328,7 @@ int tpm_chip_register(struct tpm_chip *chip)
chip->flags |= TPM_CHIP_FLAG_REGISTERED;
- if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
+ if (!(chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL))) {
rc = __compat_only_sysfs_link_entry_to_kobj(
&chip->dev.parent->kobj, &chip->dev.kobj, "ppi");
if (rc && rc != -ENOENT) {
@@ -361,7 +362,7 @@ void tpm_chip_unregister(struct tpm_chip *chip)
if (!(chip->flags & TPM_CHIP_FLAG_REGISTERED))
return;
- if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
+ if (!(chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL)))
sysfs_remove_link(&chip->dev.parent->kobj, "ppi");
tpm1_chip_unregister(chip);
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index 34e7fc7..9ee0f4d 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -283,9 +283,15 @@ static const struct attribute_group tpm_dev_group = {
int tpm_sysfs_add_device(struct tpm_chip *chip)
{
- int err;
- err = sysfs_create_group(&chip->dev.parent->kobj,
- &tpm_dev_group);
+ int err = 0;
+
+ if (!(chip->flags & TPM_CHIP_FLAG_VIRTUAL))
+ err = sysfs_create_group(&chip->dev.parent->kobj,
+ &tpm_dev_group);
+ else {
+ dev_set_drvdata(&chip->dev, chip);
+ chip->groups[chip->groups_cnt++] = &tpm_dev_group;
+ }
if (err)
dev_err(&chip->dev,
@@ -300,5 +306,6 @@ void tpm_sysfs_del_device(struct tpm_chip *chip)
* synchronizes this removal so that no callbacks are running or can
* run again
*/
- sysfs_remove_group(&chip->dev.parent->kobj, &tpm_dev_group);
+ if (!(chip->flags & TPM_CHIP_FLAG_VIRTUAL))
+ sysfs_remove_group(&chip->dev.parent->kobj, &tpm_dev_group);
}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 928b47f..f197eef 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -164,6 +164,7 @@ struct tpm_vendor_specific {
enum tpm_chip_flags {
TPM_CHIP_FLAG_REGISTERED = BIT(0),
TPM_CHIP_FLAG_TPM2 = BIT(1),
+ TPM_CHIP_FLAG_VIRTUAL = BIT(2),
};
struct tpm_chip {
@@ -189,9 +190,9 @@ struct tpm_chip {
struct dentry **bios_dir;
-#ifdef CONFIG_ACPI
- const struct attribute_group *groups[2];
+ const struct attribute_group *groups[3];
unsigned int groups_cnt;
+#ifdef CONFIG_ACPI
acpi_handle acpi_dev_handle;
char ppi_version[TPM_PPI_VERSION_LEN + 1];
#endif /* CONFIG_ACPI */
--
2.4.3
This patch implements a proxy driver for supporting multiple emulated TPMs
in a system.
The driver implements a device /dev/vtpmx that is used to created
a client device pair /dev/tpmX (e.g., /dev/tpm10) and a server side that
is accessed using a file descriptor returned by an ioctl.
The device /dev/tpmX is the usual TPM device created by the core TPM
driver. Applications or kernel subsystems can send TPM commands to it
and the corresponding server-side file descriptor receives these
commands and delivers them to an emulated TPM.
Signed-off-by: Stefan Berger <[email protected]>
CC: [email protected]
CC: [email protected]
CC: [email protected]
---
drivers/char/tpm/Kconfig | 10 +
drivers/char/tpm/Makefile | 1 +
drivers/char/tpm/tpm_vtpm_proxy.c | 572 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/Kbuild | 1 +
include/uapi/linux/vtpm_proxy.h | 42 +++
5 files changed, 626 insertions(+)
create mode 100644 drivers/char/tpm/tpm_vtpm_proxy.c
create mode 100644 include/uapi/linux/vtpm_proxy.h
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index 3b84a8b..0eac596 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -122,5 +122,15 @@ config TCG_CRB
from within Linux. To compile this driver as a module, choose
M here; the module will be called tpm_crb.
+config TCG_VTPM_PROXY
+ tristate "VTPM Proxy Interface"
+ depends on TCG_TPM
+ ---help---
+ This driver proxies for an emulated TPM (vTPM) running in userspace.
+ A device /dev/vtpmx is provided that creates a device pair
+ /dev/vtpmX and a server-side file descriptor on which the vTPM
+ can receive commands.
+
+
source "drivers/char/tpm/st33zp24/Kconfig"
endif # TCG_TPM
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 56e8f1f..98de5e6 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/
obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o
obj-$(CONFIG_TCG_CRB) += tpm_crb.o
+obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
new file mode 100644
index 0000000..2bb2c8c
--- /dev/null
+++ b/drivers/char/tpm/tpm_vtpm_proxy.c
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2015, 2016 IBM Corporation
+ *
+ * Author: Stefan Berger <[email protected]>
+ *
+ * Maintained by: <[email protected]>
+ *
+ * Device driver for vTPM (vTPM proxy driver)
+ *
+ * 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/types.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/vtpm_proxy.h>
+#include <linux/file.h>
+#include <linux/anon_inodes.h>
+#include <linux/poll.h>
+#include <linux/compat.h>
+
+#include "tpm.h"
+
+#define VTPM_PROXY_REQ_COMPLETE_FLAG BIT(0)
+
+struct proxy_dev {
+ struct tpm_chip *chip;
+
+ u32 flags; /* public API flags */
+
+ wait_queue_head_t wq;
+
+ struct mutex buf_lock; /* protect buffer and flags */
+
+ long state; /* internal state */
+#define STATE_OPENED_FLAG BIT(0)
+#define STATE_WAIT_RESPONSE_FLAG BIT(1) /* waiting for emulator response */
+
+ size_t req_len; /* length of queued TPM request */
+ size_t resp_len; /* length of queued TPM response */
+ u8 buffer[TPM_BUFSIZE]; /* request/response buffer */
+};
+
+
+static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev);
+
+/*
+ * Functions related to 'server side'
+ */
+
+/**
+ * vtpm_proxy_fops_read - Read TPM commands on 'server side'
+ *
+ * Return value:
+ * Number of bytes read or negative error code
+ */
+static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *off)
+{
+ struct proxy_dev *proxy_dev = filp->private_data;
+ size_t len;
+ int sig, rc;
+
+ sig = wait_event_interruptible(proxy_dev->wq, proxy_dev->req_len != 0);
+ if (sig)
+ return -EINTR;
+
+ mutex_lock(&proxy_dev->buf_lock);
+
+ len = proxy_dev->req_len;
+
+ if (count < len) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ pr_debug("Invalid size in recv: count=%zd, req_len=%zd\n",
+ count, len);
+ return -EIO;
+ }
+
+ rc = copy_to_user(buf, proxy_dev->buffer, len);
+ memset(proxy_dev->buffer, 0, len);
+ proxy_dev->req_len = 0;
+
+ if (!rc)
+ proxy_dev->state |= STATE_WAIT_RESPONSE_FLAG;
+
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ if (rc)
+ return -EFAULT;
+
+ return len;
+}
+
+/**
+ * vtpm_proxy_fops_write - Write TPM responses on 'server side'
+ *
+ * Return value:
+ * Number of bytes read or negative error value
+ */
+static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *off)
+{
+ struct proxy_dev *proxy_dev = filp->private_data;
+
+ mutex_lock(&proxy_dev->buf_lock);
+
+ if (count > sizeof(proxy_dev->buffer) ||
+ !(proxy_dev->state & STATE_WAIT_RESPONSE_FLAG)) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ return -EIO;
+ }
+
+ proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG;
+
+ proxy_dev->req_len = 0;
+
+ if (copy_from_user(proxy_dev->buffer, buf, count)) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ return -EFAULT;
+ }
+
+ proxy_dev->resp_len = count;
+
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ wake_up_interruptible(&proxy_dev->wq);
+
+ return count;
+}
+
+/*
+ * vtpm_proxy_fops_poll: Poll status on 'server side'
+ *
+ * Return value:
+ * Poll flags
+ */
+static unsigned int vtpm_proxy_fops_poll(struct file *filp, poll_table *wait)
+{
+ struct proxy_dev *proxy_dev = filp->private_data;
+ unsigned ret;
+
+ poll_wait(filp, &proxy_dev->wq, wait);
+
+ ret = POLLOUT;
+
+ mutex_lock(&proxy_dev->buf_lock);
+
+ if (proxy_dev->req_len)
+ ret |= POLLIN | POLLRDNORM;
+
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ return ret;
+}
+
+/*
+ * vtpm_proxy_fops_open - Open vTPM device on 'server side'
+ *
+ * Called when setting up the anonymous file descriptor
+ */
+static void vtpm_proxy_fops_open(struct file *filp)
+{
+ struct proxy_dev *proxy_dev = filp->private_data;
+
+ proxy_dev->state |= STATE_OPENED_FLAG;
+}
+
+/**
+ * vtpm_proxy_fops_undo_open - counter-part to vtpm_fops_open
+ *
+ * Call to undo vtpm_proxy_fops_open
+ */
+static void vtpm_proxy_fops_undo_open(struct proxy_dev *proxy_dev)
+{
+ mutex_lock(&proxy_dev->buf_lock);
+
+ proxy_dev->state &= ~STATE_OPENED_FLAG;
+
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ /* no more TPM responses -- wake up anyone waiting for them */
+ wake_up_interruptible(&proxy_dev->wq);
+}
+
+/*
+ * vtpm_proxy_fops_release: Close 'server side'
+ *
+ * Return value:
+ * Always returns 0.
+ */
+static int vtpm_proxy_fops_release(struct inode *inode, struct file *filp)
+{
+ struct proxy_dev *proxy_dev = filp->private_data;
+
+ filp->private_data = NULL;
+
+ vtpm_proxy_delete_device(proxy_dev);
+
+ return 0;
+}
+
+static const struct file_operations vtpm_proxy_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = vtpm_proxy_fops_read,
+ .write = vtpm_proxy_fops_write,
+ .poll = vtpm_proxy_fops_poll,
+ .release = vtpm_proxy_fops_release,
+};
+
+/*
+ * Functions invoked by the core TPM driver to send TPM commands to
+ * 'server side' and receive responses from there.
+ */
+
+/*
+ * Called when core TPM driver reads TPM responses from 'server side'
+ *
+ * Return value:
+ * Number of TPM response bytes read, negative error value otherwise
+ */
+static int vtpm_proxy_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct proxy_dev *proxy_dev = chip->vendor.priv;
+ size_t len;
+
+ if (!proxy_dev)
+ return -EIO;
+
+ /* process gone ? */
+ mutex_lock(&proxy_dev->buf_lock);
+
+ if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ return -EPIPE;
+ }
+
+ len = proxy_dev->resp_len;
+ if (count < len) {
+ dev_err(&chip->dev,
+ "Invalid size in recv: count=%zd, resp_len=%zd\n",
+ count, len);
+ len = -EIO;
+ goto out;
+ }
+
+ memcpy(buf, proxy_dev->buffer, len);
+ proxy_dev->resp_len = 0;
+
+out:
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ return len;
+}
+
+/*
+ * Called when core TPM driver forwards TPM requests to 'server side'.
+ *
+ * Return value:
+ * 0 in case of success, negative error value otherwise.
+ */
+static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct proxy_dev *proxy_dev = chip->vendor.priv;
+ int rc = 0;
+
+ if (!proxy_dev)
+ return -EIO;
+
+ if (count > sizeof(proxy_dev->buffer)) {
+ dev_err(&chip->dev,
+ "Invalid size in send: count=%zd, buffer size=%zd\n",
+ count, sizeof(proxy_dev->buffer));
+ return -EIO;
+ }
+
+ mutex_lock(&proxy_dev->buf_lock);
+
+ if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ return -EPIPE;
+ }
+
+ proxy_dev->resp_len = 0;
+
+ proxy_dev->req_len = count;
+ memcpy(proxy_dev->buffer, buf, count);
+
+ proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG;
+
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ wake_up_interruptible(&proxy_dev->wq);
+
+ return rc;
+}
+
+static void vtpm_proxy_tpm_op_cancel(struct tpm_chip *chip)
+{
+ /* not supported */
+}
+
+static u8 vtpm_proxy_tpm_op_status(struct tpm_chip *chip)
+{
+ struct proxy_dev *proxy_dev = chip->vendor.priv;
+
+ if (proxy_dev->resp_len)
+ return VTPM_PROXY_REQ_COMPLETE_FLAG;
+
+ return 0;
+}
+
+static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ struct proxy_dev *proxy_dev = chip->vendor.priv;
+ bool ret;
+
+ mutex_lock(&proxy_dev->buf_lock);
+
+ ret = !(proxy_dev->state & STATE_OPENED_FLAG);
+
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ return ret;
+}
+
+static const struct tpm_class_ops vtpm_proxy_tpm_ops = {
+ .recv = vtpm_proxy_tpm_op_recv,
+ .send = vtpm_proxy_tpm_op_send,
+ .cancel = vtpm_proxy_tpm_op_cancel,
+ .status = vtpm_proxy_tpm_op_status,
+ .req_complete_mask = VTPM_PROXY_REQ_COMPLETE_FLAG,
+ .req_complete_val = VTPM_PROXY_REQ_COMPLETE_FLAG,
+ .req_canceled = vtpm_proxy_tpm_req_canceled,
+};
+
+/*
+ * Code related to creation and deletion of device pairs
+ */
+static struct proxy_dev *vtpm_proxy_create_proxy_dev(void)
+{
+ struct proxy_dev *proxy_dev;
+ struct tpm_chip *chip;
+ int err;
+
+ proxy_dev = kzalloc(sizeof(*proxy_dev), GFP_KERNEL);
+ if (proxy_dev == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ init_waitqueue_head(&proxy_dev->wq);
+ mutex_init(&proxy_dev->buf_lock);
+
+ chip = tpm_chip_alloc(NULL, &vtpm_proxy_tpm_ops);
+ if (IS_ERR(chip)) {
+ err = PTR_ERR(chip);
+ goto err_proxy_dev_free;
+ }
+ chip->vendor.priv = proxy_dev;
+
+ proxy_dev->chip = chip;
+
+ return proxy_dev;
+
+err_proxy_dev_free:
+ kfree(proxy_dev);
+
+ return ERR_PTR(err);
+}
+
+/*
+ * Undo what has been done in vtpm_create_proxy_dev
+ */
+static inline void vtpm_proxy_delete_proxy_dev(struct proxy_dev *proxy_dev)
+{
+ put_device(&proxy_dev->chip->dev); /* frees chip */
+ kfree(proxy_dev);
+}
+
+/*
+ * Create a /dev/tpm%d and 'server side' file descriptor pair
+ *
+ * Return value:
+ * Returns file pointer on success, an error value otherwise
+ */
+static struct file *vtpm_proxy_create_device(
+ struct vtpm_proxy_new_dev *vtpm_new_dev)
+{
+ struct proxy_dev *proxy_dev;
+ int rc, fd;
+ struct file *file;
+
+ if (vtpm_new_dev->flags & ~VTPM_PROXY_FLAGS_ALL)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ proxy_dev = vtpm_proxy_create_proxy_dev();
+ if (IS_ERR(proxy_dev))
+ return ERR_CAST(proxy_dev);
+
+ proxy_dev->flags = vtpm_new_dev->flags;
+
+ /* setup an anonymous file for the server-side */
+ fd = get_unused_fd_flags(O_RDWR);
+ if (fd < 0) {
+ rc = fd;
+ goto err_delete_proxy_dev;
+ }
+
+ file = anon_inode_getfile("[vtpms]", &vtpm_proxy_fops, proxy_dev,
+ O_RDWR);
+ if (IS_ERR(file)) {
+ rc = PTR_ERR(file);
+ goto err_put_unused_fd;
+ }
+
+ /* from now on we can unwind with put_unused_fd() + fput() */
+ /* simulate an open() on the server side */
+ vtpm_proxy_fops_open(file);
+
+ if (proxy_dev->flags & VTPM_PROXY_FLAG_TPM2)
+ proxy_dev->chip->flags |= TPM_CHIP_FLAG_TPM2;
+
+ rc = tpm_chip_register(proxy_dev->chip);
+ if (rc)
+ goto err_vtpm_fput;
+
+ vtpm_new_dev->fd = fd;
+ vtpm_new_dev->major = MAJOR(proxy_dev->chip->dev.devt);
+ vtpm_new_dev->minor = MINOR(proxy_dev->chip->dev.devt);
+ vtpm_new_dev->tpm_num = proxy_dev->chip->dev_num;
+
+ return file;
+
+err_vtpm_fput:
+ put_unused_fd(fd);
+ fput(file);
+
+ return ERR_PTR(rc);
+
+err_put_unused_fd:
+ put_unused_fd(fd);
+
+err_delete_proxy_dev:
+ vtpm_proxy_delete_proxy_dev(proxy_dev);
+
+ return ERR_PTR(rc);
+}
+
+/*
+ * Counter part to vtpm_create_device.
+ */
+static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev)
+{
+ /*
+ * A client may hold the 'ops' lock, so let it know that the server
+ * side shuts down before we try to grab the 'ops' lock when
+ * unregistering the chip.
+ */
+ vtpm_proxy_fops_undo_open(proxy_dev);
+
+ tpm_chip_unregister(proxy_dev->chip);
+
+ vtpm_proxy_delete_proxy_dev(proxy_dev);
+}
+
+/*
+ * Code related to the control device /dev/vtpmx
+ */
+
+/*
+ * vtpmx_fops_ioctl: ioctl on /dev/vtpmx
+ *
+ * Return value:
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct vtpm_proxy_new_dev *vtpm_new_dev_p;
+ struct vtpm_proxy_new_dev vtpm_new_dev;
+ struct file *file;
+
+ switch (ioctl) {
+ case VTPM_PROXY_IOC_NEW_DEV:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ vtpm_new_dev_p = argp;
+ if (copy_from_user(&vtpm_new_dev, vtpm_new_dev_p,
+ sizeof(vtpm_new_dev)))
+ return -EFAULT;
+ file = vtpm_proxy_create_device(&vtpm_new_dev);
+ if (IS_ERR(file))
+ return PTR_ERR(file);
+ if (copy_to_user(vtpm_new_dev_p, &vtpm_new_dev,
+ sizeof(vtpm_new_dev))) {
+ put_unused_fd(vtpm_new_dev.fd);
+ fput(file);
+ return -EFAULT;
+ }
+
+ fd_install(vtpm_new_dev.fd, file);
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static long vtpmx_fops_compat_ioctl(struct file *f, unsigned int ioctl,
+ unsigned long arg)
+{
+ return vtpmx_fops_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations vtpmx_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = vtpmx_fops_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vtpmx_fops_compat_ioctl,
+#endif
+ .llseek = noop_llseek,
+};
+
+static struct miscdevice vtpmx_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "vtpmx",
+ .fops = &vtpmx_fops,
+};
+
+static int vtpmx_init(void)
+{
+ return misc_register(&vtpmx_miscdev);
+}
+
+static void vtpmx_cleanup(void)
+{
+ misc_deregister(&vtpmx_miscdev);
+}
+
+static int __init vtpm_module_init(void)
+{
+ int rc;
+
+ rc = vtpmx_init();
+ if (rc) {
+ pr_err("couldn't create vtpmx device\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static void __exit vtpm_module_exit(void)
+{
+ vtpmx_cleanup();
+}
+
+module_init(vtpm_module_init);
+module_exit(vtpm_module_exit);
+
+MODULE_AUTHOR("Stefan Berger ([email protected])");
+MODULE_DESCRIPTION("vTPM Driver");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index ebd10e6..ac767ce 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -449,6 +449,7 @@ header-y += virtio_scsi.h
header-y += virtio_types.h
header-y += vm_sockets.h
header-y += vt.h
+header-y += vtpm_proxy.h
header-y += wait.h
header-y += wanrouter.h
header-y += watchdog.h
diff --git a/include/uapi/linux/vtpm_proxy.h b/include/uapi/linux/vtpm_proxy.h
new file mode 100644
index 0000000..dc73f00
--- /dev/null
+++ b/include/uapi/linux/vtpm_proxy.h
@@ -0,0 +1,42 @@
+/*
+ * Definitions for the VTPM proxy driver
+ * Copyright (c) 2015, 2016, IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef _UAPI_LINUX_VTPM_PROXY_H
+#define _UAPI_LINUX_VTPM_PROXY_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* ioctls */
+
+struct vtpm_proxy_new_dev {
+ __u32 flags; /* input */
+ __u32 tpm_num; /* output */
+ __u32 fd; /* output */
+ __u32 major; /* output */
+ __u32 minor; /* output */
+};
+
+/* above flags */
+#define VTPM_PROXY_FLAG_TPM2 1 /* emulator is TPM 2 */
+
+/* all supported flags */
+#define VTPM_PROXY_FLAGS_ALL (VTPM_PROXY_FLAG_TPM2)
+
+#define VTPM_PROXY_MAGIC 0xa1
+
+#define VTPM_PROXY_IOC_NEW_DEV _IOW(VTPM_PROXY_MAGIC, 0x00, \
+ struct vtpm_proxy_new_dev)
+
+#endif /* _UAPI_LINUX_VTPM_PROXY_H */
--
2.4.3
Add documentation for the tpm_vtpm device driver that implements
support for providing TPM functionality to Linux containers.
Parts of this documentation were recycled from the Xen vTPM
device driver documentation.
Update the documentation for the ioctl numbers.
Signed-off-by: Stefan Berger <[email protected]>
CC: [email protected]
CC: [email protected]
CC: [email protected]
---
Documentation/ioctl/ioctl-number.txt | 1 +
Documentation/tpm/tpm_vtpm_proxy.txt | 71 ++++++++++++++++++++++++++++++++++++
2 files changed, 72 insertions(+)
create mode 100644 Documentation/tpm/tpm_vtpm_proxy.txt
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 91261a3..7dbec90 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -303,6 +303,7 @@ Code Seq#(hex) Include File Comments
<mailto:[email protected]>
0xA0 all linux/sdp/sdp.h Industrial Device Project
<mailto:[email protected]>
+0xA1 0 linux/vtpm_proxy.h TPM Emulator Proxy Driver
0xA2 00-0F arch/tile/include/asm/hardwall.h
0xA3 80-8F Port ACL in development:
<mailto:[email protected]>
diff --git a/Documentation/tpm/tpm_vtpm_proxy.txt b/Documentation/tpm/tpm_vtpm_proxy.txt
new file mode 100644
index 0000000..30d1902
--- /dev/null
+++ b/Documentation/tpm/tpm_vtpm_proxy.txt
@@ -0,0 +1,71 @@
+Virtual TPM Proxy Driver for Linux Containers
+
+Authors: Stefan Berger (IBM)
+
+This document describes the virtual Trusted Platform Module (vTPM)
+proxy device driver for Linux containers.
+
+INTRODUCTION
+------------
+
+The goal of this work is to provide TPM functionality to each Linux
+container. This allows programs to interact with a TPM in a container
+the same way they interact with a TPM on the physical system. Each
+container gets its own unique, emulated, software TPM.
+
+
+DESIGN
+------
+
+To make an emulated software TPM available to each container, the container
+management stack needs to create a device pair consisting of a client TPM
+character device /dev/tpmX (with X=0,1,2...) and a 'server side' file
+descriptor. The former is moved into the container by creating a character
+device with the appropriate major and minor numbers while the file descriptor
+is passed to the TPM emulator. Software inside the container can then send
+TPM commands using the character device and the emulator will receive the
+commands via the file descriptor and use it for sending back responses.
+
+To support this, the virtual TPM proxy driver provides a device /dev/vtpmx
+that is used to create device pairs using an ioctl. The ioctl takes as
+an input flags for configuring the device. The flags for example indicate
+whether TPM 1.2 or TPM 2 functionality is supported by the TPM emulator.
+The result of the ioctl are the file descriptor for the 'server side'
+as well as the major and minor numbers of the character device that was created.
+Besides that the number of the TPM character device is return. If for
+example /dev/tpm10 was created, the number (dev_num) 10 is returned.
+
+The following is the data structure of the TPM_PROXY_IOC_NEW_DEV ioctl:
+
+struct vtpm_proxy_new_dev {
+ __u32 flags; /* input */
+ __u32 tpm_num; /* output */
+ __u32 fd; /* output */
+ __u32 major; /* output */
+ __u32 minor; /* output */
+};
+
+Note that if unsupported flags are passed to the device driver, the ioctl will
+fail and errno will be set to EOPNOTSUPP. Similarly, if an unsupported ioctl is
+called on the device driver, the ioctl will fail and errno will be set to
+ENOTTY.
+
+See /usr/include/linux/vtpm_proxy.h for definitions related to the public interface
+of this vTPM device driver.
+
+Once the device has been created, the driver will immediately try to talk
+to the TPM. All commands from the driver can be read from the file descriptor
+returned by the ioctl. The commands should be responded to immediately.
+
+Depending on the version of TPM the following commands will be sent by the
+driver:
+
+- TPM 1.2:
+ - the driver will send a TPM_Startup command to the TPM emulator
+ - the driver will send commands to read the command durations and
+ interface timeouts from the TPM emulator
+- TPM 2:
+ - the driver will send a TPM2_Startup command to the TPM emulator
+
+The TPM device /dev/tpmX will only appear if all of the relevant commands
+were responded to properly.
--
2.4.3
From: Jason Gunthorpe <[email protected]>
Now that we have a proper struct device just use dev_name() to
access this value instead of keeping two copies.
Signed-off-by: Jason Gunthorpe <[email protected]>
Signed-off-by: Stefan Berger <[email protected]>
Reviewed-by: Jarkko Sakkinen <[email protected]>
Tested-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
drivers/char/tpm/tpm-chip.c | 18 +++++++++++-------
drivers/char/tpm/tpm.h | 1 -
drivers/char/tpm/tpm_eventlog.c | 2 +-
drivers/char/tpm/tpm_eventlog.h | 2 +-
drivers/char/tpm/tpm_i2c_nuvoton.c | 2 +-
drivers/char/tpm/tpm_tis.c | 2 +-
6 files changed, 15 insertions(+), 12 deletions(-)
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 12829dd..c21d81c 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -111,7 +111,7 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
set_bit(chip->dev_num, dev_mask);
- scnprintf(chip->devname, sizeof(chip->devname), "tpm%d", chip->dev_num);
+ device_initialize(&chip->dev);
dev_set_drvdata(dev, chip);
@@ -127,9 +127,9 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
else
chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num);
- dev_set_name(&chip->dev, "%s", chip->devname);
-
- device_initialize(&chip->dev);
+ rc = dev_set_name(&chip->dev, "tpm%d", chip->dev_num);
+ if (rc)
+ goto out;
cdev_init(&chip->cdev, &tpm_fops);
chip->cdev.owner = dev->driver->owner;
@@ -142,6 +142,10 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
}
return chip;
+
+out:
+ put_device(&chip->dev);
+ return ERR_PTR(rc);
}
EXPORT_SYMBOL_GPL(tpmm_chip_alloc);
@@ -153,7 +157,7 @@ static int tpm_add_char_device(struct tpm_chip *chip)
if (rc) {
dev_err(&chip->dev,
"unable to cdev_add() %s, major %d, minor %d, err=%d\n",
- chip->devname, MAJOR(chip->dev.devt),
+ dev_name(&chip->dev), MAJOR(chip->dev.devt),
MINOR(chip->dev.devt), rc);
return rc;
@@ -163,7 +167,7 @@ static int tpm_add_char_device(struct tpm_chip *chip)
if (rc) {
dev_err(&chip->dev,
"unable to device_register() %s, major %d, minor %d, err=%d\n",
- chip->devname, MAJOR(chip->dev.devt),
+ dev_name(&chip->dev), MAJOR(chip->dev.devt),
MINOR(chip->dev.devt), rc);
cdev_del(&chip->cdev);
@@ -190,7 +194,7 @@ static int tpm1_chip_register(struct tpm_chip *chip)
if (rc)
return rc;
- chip->bios_dir = tpm_bios_log_setup(chip->devname);
+ chip->bios_dir = tpm_bios_log_setup(dev_name(&chip->dev));
return 0;
}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 9c9be6c..5d33ba5 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -174,7 +174,6 @@ struct tpm_chip {
unsigned int flags;
int dev_num; /* /dev/tpm# */
- char devname[7];
unsigned long is_open; /* only one allowed */
int time_expired;
diff --git a/drivers/char/tpm/tpm_eventlog.c b/drivers/char/tpm/tpm_eventlog.c
index 4e6940a..e722886 100644
--- a/drivers/char/tpm/tpm_eventlog.c
+++ b/drivers/char/tpm/tpm_eventlog.c
@@ -403,7 +403,7 @@ static int is_bad(void *p)
return 0;
}
-struct dentry **tpm_bios_log_setup(char *name)
+struct dentry **tpm_bios_log_setup(const char *name)
{
struct dentry **ret = NULL, *tpm_dir, *bin_file, *ascii_file;
diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h
index 267bfbd..cc9672f 100644
--- a/drivers/char/tpm/tpm_eventlog.h
+++ b/drivers/char/tpm/tpm_eventlog.h
@@ -77,7 +77,7 @@ int read_log(struct tpm_bios_log *log);
#if defined(CONFIG_TCG_IBMVTPM) || defined(CONFIG_TCG_IBMVTPM_MODULE) || \
defined(CONFIG_ACPI)
-extern struct dentry **tpm_bios_log_setup(char *);
+extern struct dentry **tpm_bios_log_setup(const char *);
extern void tpm_bios_log_teardown(struct dentry **);
#else
static inline struct dentry **tpm_bios_log_setup(char *name)
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
index a1e1474..d61d43f 100644
--- a/drivers/char/tpm/tpm_i2c_nuvoton.c
+++ b/drivers/char/tpm/tpm_i2c_nuvoton.c
@@ -560,7 +560,7 @@ static int i2c_nuvoton_probe(struct i2c_client *client,
rc = devm_request_irq(dev, chip->vendor.irq,
i2c_nuvoton_int_handler,
IRQF_TRIGGER_LOW,
- chip->devname,
+ dev_name(&chip->dev),
chip);
if (rc) {
dev_err(dev, "%s() Unable to request irq: %d for use\n",
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 0a9aee9..eed3bf5 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -577,7 +577,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask,
u8 original_int_vec;
if (devm_request_irq(&chip->dev, irq, tis_int_handler, flags,
- chip->devname, chip) != 0) {
+ dev_name(&chip->dev), chip) != 0) {
dev_info(&chip->dev, "Unable to request irq: %d for probe\n",
irq);
return -1;
--
2.4.3
Add the retrieval of TPM 1.2 durations and timeouts. Since this requires
the startup of the TPM, do this for TPM 1.2 and TPM 2.
Signed-off-by: Stefan Berger <[email protected]>
CC: [email protected]
CC: [email protected]
CC: [email protected]
---
drivers/char/tpm/tpm_vtpm_proxy.c | 95 +++++++++++++++++++++++++++++++++++----
1 file changed, 86 insertions(+), 9 deletions(-)
diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
index 2bb2c8c..7fd686b 100644
--- a/drivers/char/tpm/tpm_vtpm_proxy.c
+++ b/drivers/char/tpm/tpm_vtpm_proxy.c
@@ -45,8 +45,11 @@ struct proxy_dev {
size_t req_len; /* length of queued TPM request */
size_t resp_len; /* length of queued TPM response */
u8 buffer[TPM_BUFSIZE]; /* request/response buffer */
+
+ struct work_struct work; /* task that retrieves TPM timeouts */
};
+static struct workqueue_struct *workqueue;
static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev);
@@ -67,6 +70,15 @@ static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
size_t len;
int sig, rc;
+ mutex_lock(&proxy_dev->buf_lock);
+
+ if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ return -EPIPE;
+ }
+
+ mutex_unlock(&proxy_dev->buf_lock);
+
sig = wait_event_interruptible(proxy_dev->wq, proxy_dev->req_len != 0);
if (sig)
return -EINTR;
@@ -110,6 +122,11 @@ static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf,
mutex_lock(&proxy_dev->buf_lock);
+ if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ return -EPIPE;
+ }
+
if (count > sizeof(proxy_dev->buffer) ||
!(proxy_dev->state & STATE_WAIT_RESPONSE_FLAG)) {
mutex_unlock(&proxy_dev->buf_lock);
@@ -154,6 +171,9 @@ static unsigned int vtpm_proxy_fops_poll(struct file *filp, poll_table *wait)
if (proxy_dev->req_len)
ret |= POLLIN | POLLRDNORM;
+ if (!(proxy_dev->state & STATE_OPENED_FLAG))
+ ret |= POLLHUP;
+
mutex_unlock(&proxy_dev->buf_lock);
return ret;
@@ -341,6 +361,55 @@ static const struct tpm_class_ops vtpm_proxy_tpm_ops = {
};
/*
+ * Code related to the startup of the TPM 2 and startup of TPM 1.2 +
+ * retrieval of timeouts and durations.
+ */
+
+static void vtpm_proxy_work(struct work_struct *work)
+{
+ struct proxy_dev *proxy_dev = container_of(work, struct proxy_dev,
+ work);
+ int rc;
+
+ if (proxy_dev->flags & VTPM_PROXY_FLAG_TPM2)
+ rc = tpm2_startup(proxy_dev->chip, TPM2_SU_CLEAR);
+ else
+ rc = tpm_get_timeouts(proxy_dev->chip);
+
+ if (rc)
+ goto err;
+
+ rc = tpm_chip_register(proxy_dev->chip);
+ if (rc)
+ goto err;
+
+ return;
+
+err:
+ vtpm_proxy_fops_undo_open(proxy_dev);
+}
+
+/*
+ * vtpm_proxy_work_stop: make sure the work has finished
+ *
+ * This function is useful when user space closed the fd
+ * while the driver still determines timeouts.
+ */
+static void vtpm_proxy_work_stop(struct proxy_dev *proxy_dev)
+{
+ vtpm_proxy_fops_undo_open(proxy_dev);
+ flush_work(&proxy_dev->work);
+}
+
+/*
+ * vtpm_proxy_work_start: Schedule the work for TPM 1.2 & 2 initialization
+ */
+static inline void vtpm_proxy_work_start(struct proxy_dev *proxy_dev)
+{
+ queue_work(workqueue, &proxy_dev->work);
+}
+
+/*
* Code related to creation and deletion of device pairs
*/
static struct proxy_dev *vtpm_proxy_create_proxy_dev(void)
@@ -355,6 +424,7 @@ static struct proxy_dev *vtpm_proxy_create_proxy_dev(void)
init_waitqueue_head(&proxy_dev->wq);
mutex_init(&proxy_dev->buf_lock);
+ INIT_WORK(&proxy_dev->work, vtpm_proxy_work);
chip = tpm_chip_alloc(NULL, &vtpm_proxy_tpm_ops);
if (IS_ERR(chip)) {
@@ -425,9 +495,7 @@ static struct file *vtpm_proxy_create_device(
if (proxy_dev->flags & VTPM_PROXY_FLAG_TPM2)
proxy_dev->chip->flags |= TPM_CHIP_FLAG_TPM2;
- rc = tpm_chip_register(proxy_dev->chip);
- if (rc)
- goto err_vtpm_fput;
+ vtpm_proxy_work_start(proxy_dev);
vtpm_new_dev->fd = fd;
vtpm_new_dev->major = MAJOR(proxy_dev->chip->dev.devt);
@@ -436,12 +504,6 @@ static struct file *vtpm_proxy_create_device(
return file;
-err_vtpm_fput:
- put_unused_fd(fd);
- fput(file);
-
- return ERR_PTR(rc);
-
err_put_unused_fd:
put_unused_fd(fd);
@@ -456,6 +518,8 @@ err_delete_proxy_dev:
*/
static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev)
{
+ vtpm_proxy_work_stop(proxy_dev);
+
/*
* A client may hold the 'ops' lock, so let it know that the server
* side shuts down before we try to grab the 'ops' lock when
@@ -555,11 +619,24 @@ static int __init vtpm_module_init(void)
return rc;
}
+ workqueue = create_workqueue("tpm-vtpm");
+ if (!workqueue) {
+ pr_err("couldn't create workqueue\n");
+ rc = -ENOMEM;
+ goto err_vtpmx_cleanup;
+ }
+
return 0;
+
+err_vtpmx_cleanup:
+ vtpmx_cleanup();
+
+ return rc;
}
static void __exit vtpm_module_exit(void)
{
+ destroy_workqueue(workqueue);
vtpmx_cleanup();
}
--
2.4.3
From: Jason Gunthorpe <[email protected]>
tpm_chip_alloc becomes a typical subsystem allocate call.
Signed-off-by: Jason Gunthorpe <[email protected]>
Reviewed-by: Stefan Berger <[email protected]>
Tested-by: Stefan Berger <[email protected]>
Reviewed-by: Jarkko Sakkinen <[email protected]>
Tested-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
drivers/char/tpm/tpm-chip.c | 49 ++++++++++++++++++++++++++++++++-------------
drivers/char/tpm/tpm.h | 4 +++-
2 files changed, 38 insertions(+), 15 deletions(-)
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 6636728..5880377 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -121,17 +121,17 @@ static void tpm_dev_release(struct device *dev)
}
/**
- * tpmm_chip_alloc() - allocate a new struct tpm_chip instance
- * @dev: device to which the chip is associated
+ * tpm_chip_alloc() - allocate a new struct tpm_chip instance
+ * @pdev: device to which the chip is associated
+ * At this point pdev mst be initialized, but does not have to
+ * be registered
* @ops: struct tpm_class_ops instance
*
* Allocates a new struct tpm_chip instance and assigns a free
- * device number for it. Caller does not have to worry about
- * freeing the allocated resources. When the devices is removed
- * devres calls tpmm_chip_remove() to do the job.
+ * device number for it. Must be paired with put_device(&chip->dev).
*/
-struct tpm_chip *tpmm_chip_alloc(struct device *dev,
- const struct tpm_class_ops *ops)
+struct tpm_chip *tpm_chip_alloc(struct device *dev,
+ const struct tpm_class_ops *ops)
{
struct tpm_chip *chip;
int rc;
@@ -160,8 +160,6 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
device_initialize(&chip->dev);
- dev_set_drvdata(dev, chip);
-
chip->dev.class = tpm_class;
chip->dev.release = tpm_dev_release;
chip->dev.parent = dev;
@@ -182,17 +180,40 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
chip->cdev.owner = THIS_MODULE;
chip->cdev.kobj.parent = &chip->dev.kobj;
- rc = devm_add_action(dev, (void (*)(void *)) put_device, &chip->dev);
+ return chip;
+
+out:
+ put_device(&chip->dev);
+ return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(tpm_chip_alloc);
+
+/**
+ * tpmm_chip_alloc() - allocate a new struct tpm_chip instance
+ * @pdev: parent device to which the chip is associated
+ * @ops: struct tpm_class_ops instance
+ *
+ * Same as tpm_chip_alloc except devm is used to do the put_device
+ */
+struct tpm_chip *tpmm_chip_alloc(struct device *pdev,
+ const struct tpm_class_ops *ops)
+{
+ struct tpm_chip *chip;
+ int rc;
+
+ chip = tpm_chip_alloc(pdev, ops);
+ if (IS_ERR(chip))
+ return chip;
+
+ rc = devm_add_action(pdev, (void (*)(void *)) put_device, &chip->dev);
if (rc) {
put_device(&chip->dev);
return ERR_PTR(rc);
}
- return chip;
+ dev_set_drvdata(pdev, chip);
-out:
- put_device(&chip->dev);
- return ERR_PTR(rc);
+ return chip;
}
EXPORT_SYMBOL_GPL(tpmm_chip_alloc);
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index c6376b1..5fcf788 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -511,7 +511,9 @@ struct tpm_chip *tpm_chip_find_get(int chip_num);
__must_check int tpm_try_get_ops(struct tpm_chip *chip);
void tpm_put_ops(struct tpm_chip *chip);
-extern struct tpm_chip *tpmm_chip_alloc(struct device *dev,
+extern struct tpm_chip *tpm_chip_alloc(struct device *dev,
+ const struct tpm_class_ops *ops);
+extern struct tpm_chip *tpmm_chip_alloc(struct device *pdev,
const struct tpm_class_ops *ops);
extern int tpm_chip_register(struct tpm_chip *chip);
extern void tpm_chip_unregister(struct tpm_chip *chip);
--
2.4.3
Now that the tpm core has strong locking around 'ops' it is possible
to remove a TPM driver, module and all, even while user space still
has things like /dev/tpmX open. For consistency and simplicity, drop
the module locking entirely.
The module lock can be dropped since /dev/tpmX holds the reader lock
on 'ops' while using 'ops' and this prevents the module from un-
registering, which needs the writer lock. Once the module unregistered
the 'ops' cannot be found anymore.
Signed-off-by: Stefan Berger <[email protected]>
Reviewed-by: Jason Gunthorpe <[email protected]>
Reviewed-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
drivers/char/tpm/tpm-chip.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 5793ea1..6636728 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -57,9 +57,6 @@ int tpm_try_get_ops(struct tpm_chip *chip)
if (!chip->ops)
goto out_lock;
- if (!try_module_get(chip->dev.parent->driver->owner))
- goto out_lock;
-
return 0;
out_lock:
up_read(&chip->ops_sem);
@@ -77,7 +74,6 @@ EXPORT_SYMBOL_GPL(tpm_try_get_ops);
*/
void tpm_put_ops(struct tpm_chip *chip)
{
- module_put(chip->dev.parent->driver->owner);
up_read(&chip->ops_sem);
put_device(&chip->dev);
}
@@ -183,7 +179,7 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
goto out;
cdev_init(&chip->cdev, &tpm_fops);
- chip->cdev.owner = dev->driver->owner;
+ chip->cdev.owner = THIS_MODULE;
chip->cdev.kobj.parent = &chip->dev.kobj;
rc = devm_add_action(dev, (void (*)(void *)) put_device, &chip->dev);
--
2.4.3
On Sun, Mar 13, 2016 at 06:54:38PM -0400, Stefan Berger wrote:
> This patch implements a proxy driver for supporting multiple emulated TPMs
> in a system.
>
> The driver implements a device /dev/vtpmx that is used to created
> a client device pair /dev/tpmX (e.g., /dev/tpm10) and a server side that
> is accessed using a file descriptor returned by an ioctl.
> The device /dev/tpmX is the usual TPM device created by the core TPM
> driver. Applications or kernel subsystems can send TPM commands to it
> and the corresponding server-side file descriptor receives these
> commands and delivers them to an emulated TPM.
Tested-by: Jarkko Sakkinen <[email protected]>
My testing procedure was:
* Wine running TPM 2.0 simulator on the host side. [1]
* QEMU running an OS image with this patch. [2]
* Wrote a script for proxying the simulator: tpm2-simulator-vtpm. [3]
* Run some smoke tests: python -m unittest -v tpm2_smoke. [3]
[1] http://research.microsoft.com/en-us/downloads/35116857-e544-4003-8e7b-584182dc6833/
[2] git://git.infradead.org/users/jjs/buildroot-tpmdd.git
[3] git://git.infradead.org/users/jjs/tpm2-scripts.git
/Jarkko
> Signed-off-by: Stefan Berger <[email protected]>
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> ---
> drivers/char/tpm/Kconfig | 10 +
> drivers/char/tpm/Makefile | 1 +
> drivers/char/tpm/tpm_vtpm_proxy.c | 572 ++++++++++++++++++++++++++++++++++++++
> include/uapi/linux/Kbuild | 1 +
> include/uapi/linux/vtpm_proxy.h | 42 +++
> 5 files changed, 626 insertions(+)
> create mode 100644 drivers/char/tpm/tpm_vtpm_proxy.c
> create mode 100644 include/uapi/linux/vtpm_proxy.h
/Jarkko
On Sun, Mar 13, 2016 at 06:54:36PM -0400, Stefan Berger wrote:
> Replace the device number bitmap with IDR. Extend the number of devices we
> can create to 64k.
> Since an IDR allows us to associate a pointer with an ID, we use this now
> to rewrite tpm_chip_find_get() to simply look up the chip pointer by the
> given device ID.
>
> Protect the IDR calls with a mutex.
I've merged patches up to this to my next branch i.e. you don't have
to carry them in the seres anymore.
/Jarkko
> Signed-off-by: Stefan Berger <[email protected]>
> Reviewed-by: Jason Gunthorpe <[email protected]>
> Reviewed-by: Jarkko Sakkinen <[email protected]>
> Tested-by: Jarkko Sakkinen <[email protected]>
> Signed-off-by: Jarkko Sakkinen <[email protected]>
> ---
> drivers/char/tpm/tpm-chip.c | 84 +++++++++++++++++++++-------------------
> drivers/char/tpm/tpm-interface.c | 1 +
> drivers/char/tpm/tpm.h | 5 +--
> 3 files changed, 48 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
> index 5880377..f62c851 100644
> --- a/drivers/char/tpm/tpm-chip.c
> +++ b/drivers/char/tpm/tpm-chip.c
> @@ -29,9 +29,8 @@
> #include "tpm.h"
> #include "tpm_eventlog.h"
>
> -static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
> -static LIST_HEAD(tpm_chip_list);
> -static DEFINE_SPINLOCK(driver_lock);
> +DEFINE_IDR(dev_nums_idr);
> +static DEFINE_MUTEX(idr_lock);
>
> struct class *tpm_class;
> dev_t tpm_devt;
> @@ -88,20 +87,30 @@ EXPORT_SYMBOL_GPL(tpm_put_ops);
> */
> struct tpm_chip *tpm_chip_find_get(int chip_num)
> {
> - struct tpm_chip *pos, *chip = NULL;
> + struct tpm_chip *chip, *res = NULL;
> + int chip_prev;
> +
> + mutex_lock(&idr_lock);
> +
> + if (chip_num == TPM_ANY_NUM) {
> + chip_num = 0;
> + do {
> + chip_prev = chip_num;
> + chip = idr_get_next(&dev_nums_idr, &chip_num);
> + if (chip && !tpm_try_get_ops(chip)) {
> + res = chip;
> + break;
> + }
> + } while (chip_prev != chip_num);
> + } else {
> + chip = idr_find_slowpath(&dev_nums_idr, chip_num);
> + if (chip && !tpm_try_get_ops(chip))
> + res = chip;
> + }
>
> - rcu_read_lock();
> - list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
> - if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num)
> - continue;
> + mutex_unlock(&idr_lock);
>
> - /* rcu prevents chip from being free'd */
> - if (!tpm_try_get_ops(pos))
> - chip = pos;
> - break;
> - }
> - rcu_read_unlock();
> - return chip;
> + return res;
> }
>
> /**
> @@ -114,9 +123,10 @@ static void tpm_dev_release(struct device *dev)
> {
> struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev);
>
> - spin_lock(&driver_lock);
> - clear_bit(chip->dev_num, dev_mask);
> - spin_unlock(&driver_lock);
> + mutex_lock(&idr_lock);
> + idr_remove(&dev_nums_idr, chip->dev_num);
> + mutex_unlock(&idr_lock);
> +
> kfree(chip);
> }
>
> @@ -142,21 +152,18 @@ struct tpm_chip *tpm_chip_alloc(struct device *dev,
>
> mutex_init(&chip->tpm_mutex);
> init_rwsem(&chip->ops_sem);
> - INIT_LIST_HEAD(&chip->list);
>
> chip->ops = ops;
>
> - spin_lock(&driver_lock);
> - chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES);
> - spin_unlock(&driver_lock);
> -
> - if (chip->dev_num >= TPM_NUM_DEVICES) {
> + mutex_lock(&idr_lock);
> + rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL);
> + mutex_unlock(&idr_lock);
> + if (rc < 0) {
> dev_err(dev, "No available tpm device numbers\n");
> kfree(chip);
> - return ERR_PTR(-ENOMEM);
> + return ERR_PTR(rc);
> }
> -
> - set_bit(chip->dev_num, dev_mask);
> + chip->dev_num = rc;
>
> device_initialize(&chip->dev);
>
> @@ -242,19 +249,28 @@ static int tpm_add_char_device(struct tpm_chip *chip)
> return rc;
> }
>
> + /* Make the chip available. */
> + mutex_lock(&idr_lock);
> + idr_replace(&dev_nums_idr, chip, chip->dev_num);
> + mutex_unlock(&idr_lock);
> +
> return rc;
> }
>
> static void tpm_del_char_device(struct tpm_chip *chip)
> {
> cdev_del(&chip->cdev);
> + device_del(&chip->dev);
> +
> + /* Make the chip unavailable. */
> + mutex_lock(&idr_lock);
> + idr_replace(&dev_nums_idr, NULL, chip->dev_num);
> + mutex_unlock(&idr_lock);
>
> /* Make the driver uncallable. */
> down_write(&chip->ops_sem);
> chip->ops = NULL;
> up_write(&chip->ops_sem);
> -
> - device_del(&chip->dev);
> }
>
> static int tpm1_chip_register(struct tpm_chip *chip)
> @@ -309,11 +325,6 @@ int tpm_chip_register(struct tpm_chip *chip)
> if (rc)
> goto out_err;
>
> - /* Make the chip available. */
> - spin_lock(&driver_lock);
> - list_add_tail_rcu(&chip->list, &tpm_chip_list);
> - spin_unlock(&driver_lock);
> -
> chip->flags |= TPM_CHIP_FLAG_REGISTERED;
>
> if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
> @@ -350,11 +361,6 @@ void tpm_chip_unregister(struct tpm_chip *chip)
> if (!(chip->flags & TPM_CHIP_FLAG_REGISTERED))
> return;
>
> - spin_lock(&driver_lock);
> - list_del_rcu(&chip->list);
> - spin_unlock(&driver_lock);
> - synchronize_rcu();
> -
> if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
> sysfs_remove_link(&chip->dev.parent->kobj, "ppi");
>
> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> index 5caf154..5397b64 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -1139,6 +1139,7 @@ static int __init tpm_init(void)
>
> static void __exit tpm_exit(void)
> {
> + idr_destroy(&dev_nums_idr);
> class_destroy(tpm_class);
> unregister_chrdev_region(tpm_devt, TPM_NUM_DEVICES);
> }
> diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
> index 5fcf788..928b47f 100644
> --- a/drivers/char/tpm/tpm.h
> +++ b/drivers/char/tpm/tpm.h
> @@ -34,7 +34,7 @@
> enum tpm_const {
> TPM_MINOR = 224, /* officially assigned */
> TPM_BUFSIZE = 4096,
> - TPM_NUM_DEVICES = 256,
> + TPM_NUM_DEVICES = 65536,
> TPM_RETRY = 50, /* 5 seconds */
> };
>
> @@ -195,8 +195,6 @@ struct tpm_chip {
> acpi_handle acpi_dev_handle;
> char ppi_version[TPM_PPI_VERSION_LEN + 1];
> #endif /* CONFIG_ACPI */
> -
> - struct list_head list;
> };
>
> #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
> @@ -492,6 +490,7 @@ static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
> extern struct class *tpm_class;
> extern dev_t tpm_devt;
> extern const struct file_operations tpm_fops;
> +extern struct idr dev_nums_idr;
>
> ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *);
> ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
> --
> 2.4.3
>
On Sun, Mar 13, 2016 at 06:54:38PM -0400, Stefan Berger wrote:
> This patch implements a proxy driver for supporting multiple emulated TPMs
> in a system.
>
> The driver implements a device /dev/vtpmx that is used to created
> a client device pair /dev/tpmX (e.g., /dev/tpm10) and a server side that
> is accessed using a file descriptor returned by an ioctl.
> The device /dev/tpmX is the usual TPM device created by the core TPM
> driver. Applications or kernel subsystems can send TPM commands to it
> and the corresponding server-side file descriptor receives these
> commands and delivers them to an emulated TPM.
>
> Signed-off-by: Stefan Berger <[email protected]>
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
Alternative to this would be to have /dev/vtpmx create:
* /dev/vtpm0 for the server
* /dev/tpm0 for the client
This is how David Howell's PoC worked and that's why I want
to make this alternative visible.
The server could even respawn without container noticing it.
This solution have better availability properties.
/Jarkko
On 03/16/2016 08:09 AM, Jarkko Sakkinen wrote:
> On Sun, Mar 13, 2016 at 06:54:38PM -0400, Stefan Berger wrote:
>> This patch implements a proxy driver for supporting multiple emulated TPMs
>> in a system.
>>
>> The driver implements a device /dev/vtpmx that is used to created
>> a client device pair /dev/tpmX (e.g., /dev/tpm10) and a server side that
>> is accessed using a file descriptor returned by an ioctl.
>> The device /dev/tpmX is the usual TPM device created by the core TPM
>> driver. Applications or kernel subsystems can send TPM commands to it
>> and the corresponding server-side file descriptor receives these
>> commands and delivers them to an emulated TPM.
>>
>> Signed-off-by: Stefan Berger <[email protected]>
>> CC: [email protected]
>> CC: [email protected]
>> CC: [email protected]
> Alternative to this would be to have /dev/vtpmx create:
>
> * /dev/vtpm0 for the server
> * /dev/tpm0 for the client
>
> This is how David Howell's PoC worked and that's why I want
> to make this alternative visible.
My initial implementation had this as well.
>
> The server could even respawn without container noticing it.
> This solution have better availability properties.
A TPM should be stable enough to not have to be respawned...
Stefan
On Wed, Mar 16, 2016 at 02:09:16PM +0200, Jarkko Sakkinen wrote:
> On Sun, Mar 13, 2016 at 06:54:38PM -0400, Stefan Berger wrote:
> Alternative to this would be to have /dev/vtpmx create:
>
> * /dev/vtpm0 for the server
> * /dev/tpm0 for the client
>
> This is how David Howell's PoC worked and that's why I want
> to make this alternative visible.
>
> The server could even respawn without container noticing it.
> This solution have better availability properties.
Seriously, no, that doesn't make any sense. TPM is stateful, you can't
respawn the server side.
If anyone is ever clever enough to make that workable then they just
go ahead and save the server fd with the other state. systemd for
instance already has everything needed to make that work.
We don't need to have a server dev node and we certainly don't need
the leaking problem that leaves us with.
Jason
On Wed, Mar 16, 2016 at 11:49:04AM -0600, Jason Gunthorpe wrote:
> On Wed, Mar 16, 2016 at 02:09:16PM +0200, Jarkko Sakkinen wrote:
> > On Sun, Mar 13, 2016 at 06:54:38PM -0400, Stefan Berger wrote:
>
> > Alternative to this would be to have /dev/vtpmx create:
> >
> > * /dev/vtpm0 for the server
> > * /dev/tpm0 for the client
> >
> > This is how David Howell's PoC worked and that's why I want
> > to make this alternative visible.
> >
> > The server could even respawn without container noticing it.
> > This solution have better availability properties.
>
> Seriously, no, that doesn't make any sense. TPM is stateful, you can't
> respawn the server side.
>
> If anyone is ever clever enough to make that workable then they just
> go ahead and save the server fd with the other state. systemd for
> instance already has everything needed to make that work.
>
> We don't need to have a server dev node and we certainly don't need
> the leaking problem that leaves us with.
Fair enough.
> Jason
/Jarkko
On Sun, Mar 13, 2016 at 06:54:38PM -0400, Stefan Berger wrote:
> This patch implements a proxy driver for supporting multiple emulated TPMs
> in a system.
>
> The driver implements a device /dev/vtpmx that is used to created
> a client device pair /dev/tpmX (e.g., /dev/tpm10) and a server side that
> is accessed using a file descriptor returned by an ioctl.
> The device /dev/tpmX is the usual TPM device created by the core TPM
> driver. Applications or kernel subsystems can send TPM commands to it
> and the corresponding server-side file descriptor receives these
> commands and delivers them to an emulated TPM.
>
> Signed-off-by: Stefan Berger <[email protected]>
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> ---
> drivers/char/tpm/Kconfig | 10 +
> drivers/char/tpm/Makefile | 1 +
> drivers/char/tpm/tpm_vtpm_proxy.c | 572 ++++++++++++++++++++++++++++++++++++++
> include/uapi/linux/Kbuild | 1 +
> include/uapi/linux/vtpm_proxy.h | 42 +++
> 5 files changed, 626 insertions(+)
> create mode 100644 drivers/char/tpm/tpm_vtpm_proxy.c
> create mode 100644 include/uapi/linux/vtpm_proxy.h
>
> diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
> index 3b84a8b..0eac596 100644
> --- a/drivers/char/tpm/Kconfig
> +++ b/drivers/char/tpm/Kconfig
> @@ -122,5 +122,15 @@ config TCG_CRB
> from within Linux. To compile this driver as a module, choose
> M here; the module will be called tpm_crb.
>
> +config TCG_VTPM_PROXY
> + tristate "VTPM Proxy Interface"
> + depends on TCG_TPM
> + ---help---
> + This driver proxies for an emulated TPM (vTPM) running in userspace.
> + A device /dev/vtpmx is provided that creates a device pair
> + /dev/vtpmX and a server-side file descriptor on which the vTPM
> + can receive commands.
> +
> +
> source "drivers/char/tpm/st33zp24/Kconfig"
> endif # TCG_TPM
> diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
> index 56e8f1f..98de5e6 100644
> --- a/drivers/char/tpm/Makefile
> +++ b/drivers/char/tpm/Makefile
> @@ -23,3 +23,4 @@ obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
> obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/
> obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o
> obj-$(CONFIG_TCG_CRB) += tpm_crb.o
> +obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
> diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
> new file mode 100644
> index 0000000..2bb2c8c
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_vtpm_proxy.c
> @@ -0,0 +1,572 @@
> +/*
> + * Copyright (C) 2015, 2016 IBM Corporation
> + *
> + * Author: Stefan Berger <[email protected]>
> + *
> + * Maintained by: <[email protected]>
> + *
> + * Device driver for vTPM (vTPM proxy driver)
> + *
> + * 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/types.h>
> +#include <linux/spinlock.h>
> +#include <linux/uaccess.h>
> +#include <linux/wait.h>
> +#include <linux/miscdevice.h>
> +#include <linux/vtpm_proxy.h>
> +#include <linux/file.h>
> +#include <linux/anon_inodes.h>
> +#include <linux/poll.h>
> +#include <linux/compat.h>
> +
> +#include "tpm.h"
> +
> +#define VTPM_PROXY_REQ_COMPLETE_FLAG BIT(0)
> +
> +struct proxy_dev {
> + struct tpm_chip *chip;
> +
> + u32 flags; /* public API flags */
> +
> + wait_queue_head_t wq;
> +
> + struct mutex buf_lock; /* protect buffer and flags */
> +
> + long state; /* internal state */
> +#define STATE_OPENED_FLAG BIT(0)
> +#define STATE_WAIT_RESPONSE_FLAG BIT(1) /* waiting for emulator response */
> +
> + size_t req_len; /* length of queued TPM request */
> + size_t resp_len; /* length of queued TPM response */
> + u8 buffer[TPM_BUFSIZE]; /* request/response buffer */
> +};
> +
> +
> +static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev);
> +
> +/*
> + * Functions related to 'server side'
> + */
> +
> +/**
> + * vtpm_proxy_fops_read - Read TPM commands on 'server side'
> + *
> + * Return value:
> + * Number of bytes read or negative error code
> + */
> +static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
> + size_t count, loff_t *off)
> +{
> + struct proxy_dev *proxy_dev = filp->private_data;
> + size_t len;
> + int sig, rc;
> +
> + sig = wait_event_interruptible(proxy_dev->wq, proxy_dev->req_len != 0);
> + if (sig)
> + return -EINTR;
> +
> + mutex_lock(&proxy_dev->buf_lock);
> +
> + len = proxy_dev->req_len;
> +
> + if (count < len) {
> + mutex_unlock(&proxy_dev->buf_lock);
> + pr_debug("Invalid size in recv: count=%zd, req_len=%zd\n",
> + count, len);
> + return -EIO;
> + }
> +
> + rc = copy_to_user(buf, proxy_dev->buffer, len);
> + memset(proxy_dev->buffer, 0, len);
> + proxy_dev->req_len = 0;
> +
> + if (!rc)
> + proxy_dev->state |= STATE_WAIT_RESPONSE_FLAG;
> +
> + mutex_unlock(&proxy_dev->buf_lock);
> +
> + if (rc)
> + return -EFAULT;
> +
> + return len;
> +}
> +
> +/**
> + * vtpm_proxy_fops_write - Write TPM responses on 'server side'
> + *
> + * Return value:
> + * Number of bytes read or negative error value
> + */
> +static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf,
> + size_t count, loff_t *off)
> +{
> + struct proxy_dev *proxy_dev = filp->private_data;
> +
> + mutex_lock(&proxy_dev->buf_lock);
> +
> + if (count > sizeof(proxy_dev->buffer) ||
> + !(proxy_dev->state & STATE_WAIT_RESPONSE_FLAG)) {
> + mutex_unlock(&proxy_dev->buf_lock);
> + return -EIO;
> + }
> +
> + proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG;
> +
> + proxy_dev->req_len = 0;
> +
> + if (copy_from_user(proxy_dev->buffer, buf, count)) {
> + mutex_unlock(&proxy_dev->buf_lock);
> + return -EFAULT;
> + }
> +
> + proxy_dev->resp_len = count;
> +
> + mutex_unlock(&proxy_dev->buf_lock);
> +
> + wake_up_interruptible(&proxy_dev->wq);
> +
> + return count;
> +}
> +
> +/*
> + * vtpm_proxy_fops_poll: Poll status on 'server side'
> + *
> + * Return value:
> + * Poll flags
> + */
> +static unsigned int vtpm_proxy_fops_poll(struct file *filp, poll_table *wait)
> +{
> + struct proxy_dev *proxy_dev = filp->private_data;
> + unsigned ret;
> +
> + poll_wait(filp, &proxy_dev->wq, wait);
> +
> + ret = POLLOUT;
> +
> + mutex_lock(&proxy_dev->buf_lock);
> +
> + if (proxy_dev->req_len)
> + ret |= POLLIN | POLLRDNORM;
> +
> + mutex_unlock(&proxy_dev->buf_lock);
> +
> + return ret;
> +}
> +
> +/*
> + * vtpm_proxy_fops_open - Open vTPM device on 'server side'
> + *
> + * Called when setting up the anonymous file descriptor
> + */
> +static void vtpm_proxy_fops_open(struct file *filp)
> +{
> + struct proxy_dev *proxy_dev = filp->private_data;
> +
> + proxy_dev->state |= STATE_OPENED_FLAG;
> +}
> +
> +/**
> + * vtpm_proxy_fops_undo_open - counter-part to vtpm_fops_open
> + *
> + * Call to undo vtpm_proxy_fops_open
> + */
> +static void vtpm_proxy_fops_undo_open(struct proxy_dev *proxy_dev)
> +{
> + mutex_lock(&proxy_dev->buf_lock);
> +
> + proxy_dev->state &= ~STATE_OPENED_FLAG;
> +
> + mutex_unlock(&proxy_dev->buf_lock);
> +
> + /* no more TPM responses -- wake up anyone waiting for them */
> + wake_up_interruptible(&proxy_dev->wq);
> +}
> +
> +/*
> + * vtpm_proxy_fops_release: Close 'server side'
> + *
> + * Return value:
> + * Always returns 0.
> + */
> +static int vtpm_proxy_fops_release(struct inode *inode, struct file *filp)
> +{
> + struct proxy_dev *proxy_dev = filp->private_data;
> +
> + filp->private_data = NULL;
> +
> + vtpm_proxy_delete_device(proxy_dev);
> +
> + return 0;
> +}
> +
> +static const struct file_operations vtpm_proxy_fops = {
> + .owner = THIS_MODULE,
> + .llseek = no_llseek,
> + .read = vtpm_proxy_fops_read,
> + .write = vtpm_proxy_fops_write,
> + .poll = vtpm_proxy_fops_poll,
> + .release = vtpm_proxy_fops_release,
> +};
> +
> +/*
> + * Functions invoked by the core TPM driver to send TPM commands to
> + * 'server side' and receive responses from there.
> + */
> +
> +/*
> + * Called when core TPM driver reads TPM responses from 'server side'
> + *
> + * Return value:
> + * Number of TPM response bytes read, negative error value otherwise
> + */
> +static int vtpm_proxy_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count)
> +{
> + struct proxy_dev *proxy_dev = chip->vendor.priv;
> + size_t len;
> +
> + if (!proxy_dev)
> + return -EIO;
> +
> + /* process gone ? */
> + mutex_lock(&proxy_dev->buf_lock);
> +
> + if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
> + mutex_unlock(&proxy_dev->buf_lock);
> + return -EPIPE;
> + }
> +
> + len = proxy_dev->resp_len;
> + if (count < len) {
> + dev_err(&chip->dev,
> + "Invalid size in recv: count=%zd, resp_len=%zd\n",
> + count, len);
> + len = -EIO;
> + goto out;
> + }
> +
> + memcpy(buf, proxy_dev->buffer, len);
> + proxy_dev->resp_len = 0;
> +
> +out:
> + mutex_unlock(&proxy_dev->buf_lock);
> +
> + return len;
> +}
> +
> +/*
> + * Called when core TPM driver forwards TPM requests to 'server side'.
> + *
> + * Return value:
> + * 0 in case of success, negative error value otherwise.
> + */
> +static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count)
> +{
> + struct proxy_dev *proxy_dev = chip->vendor.priv;
> + int rc = 0;
> +
> + if (!proxy_dev)
> + return -EIO;
> +
> + if (count > sizeof(proxy_dev->buffer)) {
> + dev_err(&chip->dev,
> + "Invalid size in send: count=%zd, buffer size=%zd\n",
> + count, sizeof(proxy_dev->buffer));
> + return -EIO;
> + }
> +
> + mutex_lock(&proxy_dev->buf_lock);
> +
> + if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
> + mutex_unlock(&proxy_dev->buf_lock);
> + return -EPIPE;
> + }
> +
> + proxy_dev->resp_len = 0;
> +
> + proxy_dev->req_len = count;
> + memcpy(proxy_dev->buffer, buf, count);
> +
> + proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG;
> +
> + mutex_unlock(&proxy_dev->buf_lock);
> +
> + wake_up_interruptible(&proxy_dev->wq);
> +
> + return rc;
> +}
> +
> +static void vtpm_proxy_tpm_op_cancel(struct tpm_chip *chip)
> +{
> + /* not supported */
> +}
> +
> +static u8 vtpm_proxy_tpm_op_status(struct tpm_chip *chip)
> +{
> + struct proxy_dev *proxy_dev = chip->vendor.priv;
> +
> + if (proxy_dev->resp_len)
> + return VTPM_PROXY_REQ_COMPLETE_FLAG;
> +
> + return 0;
> +}
> +
> +static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip *chip, u8 status)
> +{
> + struct proxy_dev *proxy_dev = chip->vendor.priv;
> + bool ret;
> +
> + mutex_lock(&proxy_dev->buf_lock);
> +
> + ret = !(proxy_dev->state & STATE_OPENED_FLAG);
> +
> + mutex_unlock(&proxy_dev->buf_lock);
> +
> + return ret;
> +}
> +
> +static const struct tpm_class_ops vtpm_proxy_tpm_ops = {
> + .recv = vtpm_proxy_tpm_op_recv,
> + .send = vtpm_proxy_tpm_op_send,
> + .cancel = vtpm_proxy_tpm_op_cancel,
> + .status = vtpm_proxy_tpm_op_status,
> + .req_complete_mask = VTPM_PROXY_REQ_COMPLETE_FLAG,
> + .req_complete_val = VTPM_PROXY_REQ_COMPLETE_FLAG,
> + .req_canceled = vtpm_proxy_tpm_req_canceled,
> +};
> +
> +/*
> + * Code related to creation and deletion of device pairs
> + */
> +static struct proxy_dev *vtpm_proxy_create_proxy_dev(void)
> +{
> + struct proxy_dev *proxy_dev;
> + struct tpm_chip *chip;
> + int err;
> +
> + proxy_dev = kzalloc(sizeof(*proxy_dev), GFP_KERNEL);
> + if (proxy_dev == NULL)
> + return ERR_PTR(-ENOMEM);
> +
> + init_waitqueue_head(&proxy_dev->wq);
> + mutex_init(&proxy_dev->buf_lock);
> +
> + chip = tpm_chip_alloc(NULL, &vtpm_proxy_tpm_ops);
> + if (IS_ERR(chip)) {
> + err = PTR_ERR(chip);
> + goto err_proxy_dev_free;
> + }
> + chip->vendor.priv = proxy_dev;
> +
> + proxy_dev->chip = chip;
> +
> + return proxy_dev;
> +
> +err_proxy_dev_free:
> + kfree(proxy_dev);
> +
> + return ERR_PTR(err);
> +}
> +
> +/*
> + * Undo what has been done in vtpm_create_proxy_dev
> + */
> +static inline void vtpm_proxy_delete_proxy_dev(struct proxy_dev *proxy_dev)
> +{
> + put_device(&proxy_dev->chip->dev); /* frees chip */
> + kfree(proxy_dev);
> +}
> +
> +/*
> + * Create a /dev/tpm%d and 'server side' file descriptor pair
> + *
> + * Return value:
> + * Returns file pointer on success, an error value otherwise
> + */
> +static struct file *vtpm_proxy_create_device(
> + struct vtpm_proxy_new_dev *vtpm_new_dev)
> +{
> + struct proxy_dev *proxy_dev;
> + int rc, fd;
> + struct file *file;
> +
> + if (vtpm_new_dev->flags & ~VTPM_PROXY_FLAGS_ALL)
> + return ERR_PTR(-EOPNOTSUPP);
> +
> + proxy_dev = vtpm_proxy_create_proxy_dev();
> + if (IS_ERR(proxy_dev))
> + return ERR_CAST(proxy_dev);
> +
> + proxy_dev->flags = vtpm_new_dev->flags;
> +
> + /* setup an anonymous file for the server-side */
> + fd = get_unused_fd_flags(O_RDWR);
> + if (fd < 0) {
> + rc = fd;
> + goto err_delete_proxy_dev;
> + }
> +
> + file = anon_inode_getfile("[vtpms]", &vtpm_proxy_fops, proxy_dev,
> + O_RDWR);
> + if (IS_ERR(file)) {
> + rc = PTR_ERR(file);
> + goto err_put_unused_fd;
> + }
> +
> + /* from now on we can unwind with put_unused_fd() + fput() */
> + /* simulate an open() on the server side */
> + vtpm_proxy_fops_open(file);
> +
> + if (proxy_dev->flags & VTPM_PROXY_FLAG_TPM2)
> + proxy_dev->chip->flags |= TPM_CHIP_FLAG_TPM2;
> +
> + rc = tpm_chip_register(proxy_dev->chip);
> + if (rc)
> + goto err_vtpm_fput;
> +
> + vtpm_new_dev->fd = fd;
> + vtpm_new_dev->major = MAJOR(proxy_dev->chip->dev.devt);
> + vtpm_new_dev->minor = MINOR(proxy_dev->chip->dev.devt);
> + vtpm_new_dev->tpm_num = proxy_dev->chip->dev_num;
> +
> + return file;
> +
> +err_vtpm_fput:
> + put_unused_fd(fd);
> + fput(file);
> +
> + return ERR_PTR(rc);
> +
> +err_put_unused_fd:
> + put_unused_fd(fd);
> +
> +err_delete_proxy_dev:
> + vtpm_proxy_delete_proxy_dev(proxy_dev);
> +
> + return ERR_PTR(rc);
> +}
> +
> +/*
> + * Counter part to vtpm_create_device.
> + */
> +static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev)
> +{
> + /*
> + * A client may hold the 'ops' lock, so let it know that the server
> + * side shuts down before we try to grab the 'ops' lock when
> + * unregistering the chip.
> + */
> + vtpm_proxy_fops_undo_open(proxy_dev);
> +
> + tpm_chip_unregister(proxy_dev->chip);
> +
> + vtpm_proxy_delete_proxy_dev(proxy_dev);
> +}
> +
> +/*
> + * Code related to the control device /dev/vtpmx
> + */
> +
> +/*
> + * vtpmx_fops_ioctl: ioctl on /dev/vtpmx
> + *
> + * Return value:
> + * Returns 0 on success, a negative error code otherwise.
> + */
> +static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl,
> + unsigned long arg)
> +{
> + void __user *argp = (void __user *)arg;
> + struct vtpm_proxy_new_dev *vtpm_new_dev_p;
> + struct vtpm_proxy_new_dev vtpm_new_dev;
> + struct file *file;
> +
> + switch (ioctl) {
> + case VTPM_PROXY_IOC_NEW_DEV:
> + if (!capable(CAP_SYS_ADMIN))
> + return -EPERM;
> + vtpm_new_dev_p = argp;
> + if (copy_from_user(&vtpm_new_dev, vtpm_new_dev_p,
> + sizeof(vtpm_new_dev)))
> + return -EFAULT;
> + file = vtpm_proxy_create_device(&vtpm_new_dev);
> + if (IS_ERR(file))
> + return PTR_ERR(file);
> + if (copy_to_user(vtpm_new_dev_p, &vtpm_new_dev,
> + sizeof(vtpm_new_dev))) {
> + put_unused_fd(vtpm_new_dev.fd);
> + fput(file);
> + return -EFAULT;
> + }
> +
> + fd_install(vtpm_new_dev.fd, file);
> + return 0;
> +
> + default:
> + return -ENOIOCTLCMD;
> + }
> +}
> +
> +#ifdef CONFIG_COMPAT
> +static long vtpmx_fops_compat_ioctl(struct file *f, unsigned int ioctl,
> + unsigned long arg)
> +{
> + return vtpmx_fops_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
> +}
> +#endif
> +
> +static const struct file_operations vtpmx_fops = {
> + .owner = THIS_MODULE,
> + .unlocked_ioctl = vtpmx_fops_ioctl,
> +#ifdef CONFIG_COMPAT
> + .compat_ioctl = vtpmx_fops_compat_ioctl,
> +#endif
> + .llseek = noop_llseek,
> +};
> +
> +static struct miscdevice vtpmx_miscdev = {
> + .minor = MISC_DYNAMIC_MINOR,
> + .name = "vtpmx",
> + .fops = &vtpmx_fops,
> +};
> +
> +static int vtpmx_init(void)
> +{
> + return misc_register(&vtpmx_miscdev);
> +}
> +
> +static void vtpmx_cleanup(void)
> +{
> + misc_deregister(&vtpmx_miscdev);
> +}
> +
> +static int __init vtpm_module_init(void)
> +{
> + int rc;
> +
> + rc = vtpmx_init();
> + if (rc) {
> + pr_err("couldn't create vtpmx device\n");
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +static void __exit vtpm_module_exit(void)
> +{
> + vtpmx_cleanup();
> +}
> +
> +module_init(vtpm_module_init);
> +module_exit(vtpm_module_exit);
> +
> +MODULE_AUTHOR("Stefan Berger ([email protected])");
> +MODULE_DESCRIPTION("vTPM Driver");
> +MODULE_VERSION("0.1");
> +MODULE_LICENSE("GPL");
> diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
> index ebd10e6..ac767ce 100644
> --- a/include/uapi/linux/Kbuild
> +++ b/include/uapi/linux/Kbuild
> @@ -449,6 +449,7 @@ header-y += virtio_scsi.h
> header-y += virtio_types.h
> header-y += vm_sockets.h
> header-y += vt.h
> +header-y += vtpm_proxy.h
> header-y += wait.h
> header-y += wanrouter.h
> header-y += watchdog.h
> diff --git a/include/uapi/linux/vtpm_proxy.h b/include/uapi/linux/vtpm_proxy.h
> new file mode 100644
> index 0000000..dc73f00
> --- /dev/null
> +++ b/include/uapi/linux/vtpm_proxy.h
> @@ -0,0 +1,42 @@
> +/*
> + * Definitions for the VTPM proxy driver
> + * Copyright (c) 2015, 2016, IBM Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + */
> +
> +#ifndef _UAPI_LINUX_VTPM_PROXY_H
> +#define _UAPI_LINUX_VTPM_PROXY_H
> +
> +#include <linux/types.h>
> +#include <linux/ioctl.h>
> +
> +/* ioctls */
> +
> +struct vtpm_proxy_new_dev {
> + __u32 flags; /* input */
> + __u32 tpm_num; /* output */
> + __u32 fd; /* output */
> + __u32 major; /* output */
> + __u32 minor; /* output */
> +};
> +
> +/* above flags */
> +#define VTPM_PROXY_FLAG_TPM2 1 /* emulator is TPM 2 */
> +
> +/* all supported flags */
> +#define VTPM_PROXY_FLAGS_ALL (VTPM_PROXY_FLAG_TPM2)
This can be moved inside the .c-file?
> +
> +#define VTPM_PROXY_MAGIC 0xa1
> +
> +#define VTPM_PROXY_IOC_NEW_DEV _IOW(VTPM_PROXY_MAGIC, 0x00, \
> + struct vtpm_proxy_new_dev)
Could we simply replace these four lines with one line:
#deifne VTPM_PROXY_IOC_NEW_DEV _IOW('t', 0x00, struct vtpm_proxy_new_dev);
I changed the magic but does it matter?
> +
> +#endif /* _UAPI_LINUX_VTPM_PROXY_H */
> --
> 2.4.3
/Jarkko
On 03/16/2016 04:42 PM, Jarkko Sakkinen wrote:
> On Sun, Mar 13, 2016 at 06:54:38PM -0400, Stefan Berger wrote:
>> +
>> +/* above flags */
>> +#define VTPM_PROXY_FLAG_TPM2 1 /* emulator is TPM 2 */
>> +
>> +/* all supported flags */
>> +#define VTPM_PROXY_FLAGS_ALL (VTPM_PROXY_FLAG_TPM2)
> This can be moved inside the .c-file?
I can move that.
>
>> +
>> +#define VTPM_PROXY_MAGIC 0xa1
>> +
>> +#define VTPM_PROXY_IOC_NEW_DEV _IOW(VTPM_PROXY_MAGIC, 0x00, \
>> + struct vtpm_proxy_new_dev)
> Could we simply replace these four lines with one line:
>
> #deifne VTPM_PROXY_IOC_NEW_DEV _IOW('t', 0x00, struct vtpm_proxy_new_dev);
Does this make it better?
>
> I changed the magic but does it matter?
I would keep the magic at '0xa1'. The documentation is written to '0xa1'
now and seems to be good just as any other.
Stefan
On Thu, Mar 17, 2016 at 01:45:20PM -0400, Stefan Berger wrote:
> On 03/16/2016 04:42 PM, Jarkko Sakkinen wrote:
> >On Sun, Mar 13, 2016 at 06:54:38PM -0400, Stefan Berger wrote:
> >>+
> >>+/* above flags */
> >>+#define VTPM_PROXY_FLAG_TPM2 1 /* emulator is TPM 2 */
> >>+
> >>+/* all supported flags */
> >>+#define VTPM_PROXY_FLAGS_ALL (VTPM_PROXY_FLAG_TPM2)
> >This can be moved inside the .c-file?
>
> I can move that.
>
> >
> >>+
> >>+#define VTPM_PROXY_MAGIC 0xa1
> >>+
> >>+#define VTPM_PROXY_IOC_NEW_DEV _IOW(VTPM_PROXY_MAGIC, 0x00, \
> >>+ struct vtpm_proxy_new_dev)
> >Could we simply replace these four lines with one line:
> >
> >#deifne VTPM_PROXY_IOC_NEW_DEV _IOW('t', 0x00, struct vtpm_proxy_new_dev);
>
> Does this make it better?
>
> >
> >I changed the magic but does it matter?
>
> I would keep the magic at '0xa1'. The documentation is written to '0xa1' now
> and seems to be good just as any other.
OK. Works for me. Keep the ioctl definition as it is.
Reviewed-by: Jarkko Sakkinen <[email protected]>
I can move the constant. You don't have to send a new patch version
anymore. I start keeping this patch in my master but will not merge it
to next before 4.6-rc5 so at the moment it would be scheduled for 4.7.
Does this sound good for you?
Further improvemnts should be sent as separate fixup patches.
> Stefan
/Jarkko
On Fri, Mar 18, 2016 at 10:52:00AM +0200, Jarkko Sakkinen wrote:
> On Thu, Mar 17, 2016 at 01:45:20PM -0400, Stefan Berger wrote:
> > On 03/16/2016 04:42 PM, Jarkko Sakkinen wrote:
> > >On Sun, Mar 13, 2016 at 06:54:38PM -0400, Stefan Berger wrote:
> > >>+
> > >>+/* above flags */
> > >>+#define VTPM_PROXY_FLAG_TPM2 1 /* emulator is TPM 2 */
> > >>+
> > >>+/* all supported flags */
> > >>+#define VTPM_PROXY_FLAGS_ALL (VTPM_PROXY_FLAG_TPM2)
> > >This can be moved inside the .c-file?
> >
> > I can move that.
> >
> > >
> > >>+
> > >>+#define VTPM_PROXY_MAGIC 0xa1
> > >>+
> > >>+#define VTPM_PROXY_IOC_NEW_DEV _IOW(VTPM_PROXY_MAGIC, 0x00, \
> > >>+ struct vtpm_proxy_new_dev)
> > >Could we simply replace these four lines with one line:
> > >
> > >#deifne VTPM_PROXY_IOC_NEW_DEV _IOW('t', 0x00, struct vtpm_proxy_new_dev);
> >
> > Does this make it better?
> >
> > >
> > >I changed the magic but does it matter?
> >
> > I would keep the magic at '0xa1'. The documentation is written to '0xa1' now
> > and seems to be good just as any other.
>
> OK. Works for me. Keep the ioctl definition as it is.
>
> Reviewed-by: Jarkko Sakkinen <[email protected]>
>
> I can move the constant. You don't have to send a new patch version
> anymore. I start keeping this patch in my master but will not merge it
> to next before 4.6-rc5 so at the moment it would be scheduled for 4.7.
> Does this sound good for you?
>
> Further improvemnts should be sent as separate fixup patches.
Applied to git://git.infradead.org/users/jjs/linux-tpmdd.git.
/Jarkko
On Sun, Mar 13, 2016 at 06:54:39PM -0400, Stefan Berger wrote:
> Add the retrieval of TPM 1.2 durations and timeouts. Since this requires
> the startup of the TPM, do this for TPM 1.2 and TPM 2.
>
> Signed-off-by: Stefan Berger <[email protected]>
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
>
> ---
> drivers/char/tpm/tpm_vtpm_proxy.c | 95 +++++++++++++++++++++++++++++++++++----
> 1 file changed, 86 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
> index 2bb2c8c..7fd686b 100644
> --- a/drivers/char/tpm/tpm_vtpm_proxy.c
> +++ b/drivers/char/tpm/tpm_vtpm_proxy.c
> @@ -45,8 +45,11 @@ struct proxy_dev {
> size_t req_len; /* length of queued TPM request */
> size_t resp_len; /* length of queued TPM response */
> u8 buffer[TPM_BUFSIZE]; /* request/response buffer */
> +
> + struct work_struct work; /* task that retrieves TPM timeouts */
> };
>
> +static struct workqueue_struct *workqueue;
>
> static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev);
>
> @@ -67,6 +70,15 @@ static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
> size_t len;
> int sig, rc;
>
> + mutex_lock(&proxy_dev->buf_lock);
> +
> + if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
> + mutex_unlock(&proxy_dev->buf_lock);
> + return -EPIPE;
> + }
> +
> + mutex_unlock(&proxy_dev->buf_lock);
> +
> sig = wait_event_interruptible(proxy_dev->wq, proxy_dev->req_len != 0);
> if (sig)
> return -EINTR;
What if STATE_OPENED_FLAG is set after mutex_unlock()?
Is there some scenario where STATE_OPENED_FLAG would evaluate false
at this point?
Actually I couldn't find a scenario where this check would be needed
because:
* In vtpm_proxy_work() vtpm_proxy_fops_undo_open() is called after
sending TPM commands.
* vtpm_proxy_delete_device() calls vtpm_proxy_work_stop() as its
first statement.
Am I ignoring something?
/Jarkko
> @@ -110,6 +122,11 @@ static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf,
>
> mutex_lock(&proxy_dev->buf_lock);
>
> + if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
> + mutex_unlock(&proxy_dev->buf_lock);
> + return -EPIPE;
> + }
> +
> if (count > sizeof(proxy_dev->buffer) ||
> !(proxy_dev->state & STATE_WAIT_RESPONSE_FLAG)) {
> mutex_unlock(&proxy_dev->buf_lock);
> @@ -154,6 +171,9 @@ static unsigned int vtpm_proxy_fops_poll(struct file *filp, poll_table *wait)
> if (proxy_dev->req_len)
> ret |= POLLIN | POLLRDNORM;
>
> + if (!(proxy_dev->state & STATE_OPENED_FLAG))
> + ret |= POLLHUP;
> +
> mutex_unlock(&proxy_dev->buf_lock);
>
> return ret;
> @@ -341,6 +361,55 @@ static const struct tpm_class_ops vtpm_proxy_tpm_ops = {
> };
>
> /*
> + * Code related to the startup of the TPM 2 and startup of TPM 1.2 +
> + * retrieval of timeouts and durations.
> + */
> +
> +static void vtpm_proxy_work(struct work_struct *work)
> +{
> + struct proxy_dev *proxy_dev = container_of(work, struct proxy_dev,
> + work);
> + int rc;
> +
> + if (proxy_dev->flags & VTPM_PROXY_FLAG_TPM2)
> + rc = tpm2_startup(proxy_dev->chip, TPM2_SU_CLEAR);
> + else
> + rc = tpm_get_timeouts(proxy_dev->chip);
> +
> + if (rc)
> + goto err;
> +
> + rc = tpm_chip_register(proxy_dev->chip);
> + if (rc)
> + goto err;
> +
> + return;
> +
> +err:
> + vtpm_proxy_fops_undo_open(proxy_dev);
> +}
> +
> +/*
> + * vtpm_proxy_work_stop: make sure the work has finished
> + *
> + * This function is useful when user space closed the fd
> + * while the driver still determines timeouts.
> + */
> +static void vtpm_proxy_work_stop(struct proxy_dev *proxy_dev)
> +{
> + vtpm_proxy_fops_undo_open(proxy_dev);
> + flush_work(&proxy_dev->work);
> +}
> +
> +/*
> + * vtpm_proxy_work_start: Schedule the work for TPM 1.2 & 2 initialization
> + */
> +static inline void vtpm_proxy_work_start(struct proxy_dev *proxy_dev)
> +{
> + queue_work(workqueue, &proxy_dev->work);
> +}
> +
> +/*
> * Code related to creation and deletion of device pairs
> */
> static struct proxy_dev *vtpm_proxy_create_proxy_dev(void)
> @@ -355,6 +424,7 @@ static struct proxy_dev *vtpm_proxy_create_proxy_dev(void)
>
> init_waitqueue_head(&proxy_dev->wq);
> mutex_init(&proxy_dev->buf_lock);
> + INIT_WORK(&proxy_dev->work, vtpm_proxy_work);
>
> chip = tpm_chip_alloc(NULL, &vtpm_proxy_tpm_ops);
> if (IS_ERR(chip)) {
> @@ -425,9 +495,7 @@ static struct file *vtpm_proxy_create_device(
> if (proxy_dev->flags & VTPM_PROXY_FLAG_TPM2)
> proxy_dev->chip->flags |= TPM_CHIP_FLAG_TPM2;
>
> - rc = tpm_chip_register(proxy_dev->chip);
> - if (rc)
> - goto err_vtpm_fput;
> + vtpm_proxy_work_start(proxy_dev);
>
> vtpm_new_dev->fd = fd;
> vtpm_new_dev->major = MAJOR(proxy_dev->chip->dev.devt);
> @@ -436,12 +504,6 @@ static struct file *vtpm_proxy_create_device(
>
> return file;
>
> -err_vtpm_fput:
> - put_unused_fd(fd);
> - fput(file);
> -
> - return ERR_PTR(rc);
> -
> err_put_unused_fd:
> put_unused_fd(fd);
>
> @@ -456,6 +518,8 @@ err_delete_proxy_dev:
> */
> static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev)
> {
> + vtpm_proxy_work_stop(proxy_dev);
> +
> /*
> * A client may hold the 'ops' lock, so let it know that the server
> * side shuts down before we try to grab the 'ops' lock when
> @@ -555,11 +619,24 @@ static int __init vtpm_module_init(void)
> return rc;
> }
>
> + workqueue = create_workqueue("tpm-vtpm");
> + if (!workqueue) {
> + pr_err("couldn't create workqueue\n");
> + rc = -ENOMEM;
> + goto err_vtpmx_cleanup;
> + }
> +
> return 0;
> +
> +err_vtpmx_cleanup:
> + vtpmx_cleanup();
> +
> + return rc;
> }
>
> static void __exit vtpm_module_exit(void)
> {
> + destroy_workqueue(workqueue);
> vtpmx_cleanup();
> }
>
On 03/22/2016 02:34 AM, Jarkko Sakkinen wrote:
> On Sun, Mar 13, 2016 at 06:54:39PM -0400, Stefan Berger wrote:
>> Add the retrieval of TPM 1.2 durations and timeouts. Since this requires
>> the startup of the TPM, do this for TPM 1.2 and TPM 2.
>>
>> Signed-off-by: Stefan Berger <[email protected]>
>> CC: [email protected]
>> CC: [email protected]
>> CC: [email protected]
>>
>> ---
>> drivers/char/tpm/tpm_vtpm_proxy.c | 95 +++++++++++++++++++++++++++++++++++----
>> 1 file changed, 86 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
>> index 2bb2c8c..7fd686b 100644
>> --- a/drivers/char/tpm/tpm_vtpm_proxy.c
>> +++ b/drivers/char/tpm/tpm_vtpm_proxy.c
>> @@ -45,8 +45,11 @@ struct proxy_dev {
>> size_t req_len; /* length of queued TPM request */
>> size_t resp_len; /* length of queued TPM response */
>> u8 buffer[TPM_BUFSIZE]; /* request/response buffer */
>> +
>> + struct work_struct work; /* task that retrieves TPM timeouts */
>> };
>>
>> +static struct workqueue_struct *workqueue;
>>
>> static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev);
>>
>> @@ -67,6 +70,15 @@ static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
>> size_t len;
>> int sig, rc;
>>
>> + mutex_lock(&proxy_dev->buf_lock);
>> +
>> + if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
>> + mutex_unlock(&proxy_dev->buf_lock);
>> + return -EPIPE;
>> + }
>> +
>> + mutex_unlock(&proxy_dev->buf_lock);
>> +
>> sig = wait_event_interruptible(proxy_dev->wq, proxy_dev->req_len != 0);
>> if (sig)
>> return -EINTR;
> What if STATE_OPENED_FLAG is set after mutex_unlock()?
This flag is only set when the file descriptor for the server side is
created (vtpm_proxy_fops_open()). After that it can only be cleared
(vtpm_fops_undo_open()) due to an error condition, which then indicates
to the server side that the file descriptor is now unusable. One error
condition can for example be the failure by the TPM emulator to respond
to the TPM_Startup with a success in the response.
>
> Is there some scenario where STATE_OPENED_FLAG would evaluate false
> at this point?
Yes. The flag is reset in vtpm_fops_undo_open(), which is for example
called in error conditions detected by the worker thread
(vtpm_proxy_work()) where the server side for example didn't deliver the
timeouts and durations or the TPM_Startup() wasn't successful.
>
> Actually I couldn't find a scenario where this check would be needed
> because:
>
> * In vtpm_proxy_work() vtpm_proxy_fops_undo_open() is called after
> sending TPM commands.
> * vtpm_proxy_delete_device() calls vtpm_proxy_work_stop() as its
> first statement.
>
> Am I ignoring something?
Does the above explain it? The file descriptor is not closed by any
failure condition, so it stays around but it becomes 'useless' if the
work thread detected an error by the TPM emulator.
Stefan
On Tue, Mar 22, 2016 at 06:54:30AM -0400, Stefan Berger wrote:
> On 03/22/2016 02:34 AM, Jarkko Sakkinen wrote:
> >On Sun, Mar 13, 2016 at 06:54:39PM -0400, Stefan Berger wrote:
> >>Add the retrieval of TPM 1.2 durations and timeouts. Since this requires
> >>the startup of the TPM, do this for TPM 1.2 and TPM 2.
> >>
> >>Signed-off-by: Stefan Berger <[email protected]>
> >>CC: [email protected]
> >>CC: [email protected]
> >>CC: [email protected]
> >>
> >>---
> >>drivers/char/tpm/tpm_vtpm_proxy.c | 95 +++++++++++++++++++++++++++++++++++----
> >> 1 file changed, 86 insertions(+), 9 deletions(-)
> >>
> >>diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
> >>index 2bb2c8c..7fd686b 100644
> >>--- a/drivers/char/tpm/tpm_vtpm_proxy.c
> >>+++ b/drivers/char/tpm/tpm_vtpm_proxy.c
> >>@@ -45,8 +45,11 @@ struct proxy_dev {
> >> size_t req_len; /* length of queued TPM request */
> >> size_t resp_len; /* length of queued TPM response */
> >> u8 buffer[TPM_BUFSIZE]; /* request/response buffer */
> >>+
> >>+ struct work_struct work; /* task that retrieves TPM timeouts */
> >> };
> >>+static struct workqueue_struct *workqueue;
> >> static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev);
> >>@@ -67,6 +70,15 @@ static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
> >> size_t len;
> >> int sig, rc;
> >>+ mutex_lock(&proxy_dev->buf_lock);
> >>+
> >>+ if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
> >>+ mutex_unlock(&proxy_dev->buf_lock);
> >>+ return -EPIPE;
> >>+ }
> >>+
> >>+ mutex_unlock(&proxy_dev->buf_lock);
> >>+
> >> sig = wait_event_interruptible(proxy_dev->wq, proxy_dev->req_len != 0);
> >> if (sig)
> >> return -EINTR;
> >What if STATE_OPENED_FLAG is set after mutex_unlock()?
>
> This flag is only set when the file descriptor for the server side is
> created (vtpm_proxy_fops_open()). After that it can only be cleared
> (vtpm_fops_undo_open()) due to an error condition, which then indicates to
> the server side that the file descriptor is now unusable. One error
> condition can for example be the failure by the TPM emulator to respond to
> the TPM_Startup with a success in the response.
You take the lock two times and OPENED flag could change in-between.
Why couldn't you put the call after wait_event_* after taking the lock?
/Jarkko
On 03/29/2016 11:31 AM, Jarkko Sakkinen wrote:
> On Tue, Mar 22, 2016 at 06:54:30AM -0400, Stefan Berger wrote:
>> On 03/22/2016 02:34 AM, Jarkko Sakkinen wrote:
>>> On Sun, Mar 13, 2016 at 06:54:39PM -0400, Stefan Berger wrote:
>>>> Add the retrieval of TPM 1.2 durations and timeouts. Since this requires
>>>> the startup of the TPM, do this for TPM 1.2 and TPM 2.
>>>>
>>>> Signed-off-by: Stefan Berger <[email protected]>
>>>> CC: [email protected]
>>>> CC: [email protected]
>>>> CC: [email protected]
>>>>
>>>> ---
>>>> drivers/char/tpm/tpm_vtpm_proxy.c | 95 +++++++++++++++++++++++++++++++++++----
>>>> 1 file changed, 86 insertions(+), 9 deletions(-)
>>>>
>>>> diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
>>>> index 2bb2c8c..7fd686b 100644
>>>> --- a/drivers/char/tpm/tpm_vtpm_proxy.c
>>>> +++ b/drivers/char/tpm/tpm_vtpm_proxy.c
>>>> @@ -45,8 +45,11 @@ struct proxy_dev {
>>>> size_t req_len; /* length of queued TPM request */
>>>> size_t resp_len; /* length of queued TPM response */
>>>> u8 buffer[TPM_BUFSIZE]; /* request/response buffer */
>>>> +
>>>> + struct work_struct work; /* task that retrieves TPM timeouts */
>>>> };
>>>> +static struct workqueue_struct *workqueue;
>>>> static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev);
>>>> @@ -67,6 +70,15 @@ static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
>>>> size_t len;
>>>> int sig, rc;
>>>> + mutex_lock(&proxy_dev->buf_lock);
>>>> +
>>>> + if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
>>>> + mutex_unlock(&proxy_dev->buf_lock);
>>>> + return -EPIPE;
>>>> + }
>>>> +
>>>> + mutex_unlock(&proxy_dev->buf_lock);
>>>> +
>>>> sig = wait_event_interruptible(proxy_dev->wq, proxy_dev->req_len != 0);
>>>> if (sig)
>>>> return -EINTR;
>>> What if STATE_OPENED_FLAG is set after mutex_unlock()?
>> This flag is only set when the file descriptor for the server side is
>> created (vtpm_proxy_fops_open()). After that it can only be cleared
>> (vtpm_fops_undo_open()) due to an error condition, which then indicates to
>> the server side that the file descriptor is now unusable. One error
>> condition can for example be the failure by the TPM emulator to respond to
>> the TPM_Startup with a success in the response.
> You take the lock two times and OPENED flag could change in-between.
>
> Why couldn't you put the call after wait_event_* after taking the lock?
Good point. Actually the test for the flag has to go into the
wait_event_interruptible test (without mutex?!) and we have to look at
that flag after the wait_event_interruptible again to check whether no
more requests are coming due to the OPEN flag not being set anymore as
result of an error condition.
I can post a v9 with that change. However, the dropping of the priv
field from the vendor struct in the other patch series, will again
require changes either here or in that other series.
Stefan
On Sun, Mar 13, 2016 at 06:54:30PM -0400, Stefan Berger wrote:
> The following series of patches implements a multi-instance vTPM
> proxy driver that can dynamically create TPM 'server' and client device
> pairs.
>
> Using an ioctl on the provided /dev/vtpmx, a client-side vTPM device
> and a server side file descriptor is created. The file descriptor must
> be passed to a TPM emulator. The device driver will initialize the
> emulated TPM using TPM 1.2 or TPM 2 startup commands and it will read
> the command durations from the device in case of a TPM 1.2. The choice
> of emulated TPM device (1.2 or 2) must be provided with a flag in
> the ioctl.
>
> The driver is based on a recent checkout of James Morris's 'next'
> branch and uses several recently posted patches from Jason and Jarkko.
I don't remember if I said this but up to the IDR patch I merged patches
to next (at the same day you sent this) so for v9 you can only include
top four changes.
/Jarkko
> Stefan
>
> v7->v8:
> - minor tweaks on the documentation
> - Reordered function calls in the VTPM proxy driver's server side release
> function so that a client holding the 'ops' lock releases it before the
> driver tries to grab the lock when unregistering the device.
>
> v6->v7:
> - Adjusted name of driver to tpm_vtpm_proxy from tpm_vtpm. Adjust function
> names, names of structures, and names of constants.
> - Adjusted IOCTL to use magic 0xa1 rather than the completely used 0xa0.
> - Extended driver documentation and added documentation of ioctl.
> - Moved test program to own project (dropped patch 11).
>
> v5->v6:
> - Adapted errno's for unsupported flags and ioctls following Jason's
> comments
>
> v4->v5:
> - Introduced different error codes for unsupported flags and ioctls
> - Added documentation patch
>
>
> Jason Gunthorpe (4):
> tpm: Get rid of chip->pdev
> tpm: Get rid of devname
> tpm: Provide strong locking for device removal
> tpm: Split out the devm stuff from tpmm_chip_alloc
>
> Stefan Berger (6):
> tpm: Get rid of module locking
> tpm: Replace device number bitmap with IDR
> tpm: Introduce TPM_CHIP_FLAG_VIRTUAL
> tpm: Proxy driver for supporting multiple emulated TPMs
> tpm: Initialize TPM and get durations and timeouts
> tpm: Add documentation for the tpm_vtpm device driver
>
> Documentation/ioctl/ioctl-number.txt | 1 +
> Documentation/tpm/tpm_vtpm_proxy.txt | 71 ++++
> drivers/char/tpm/Kconfig | 10 +
> drivers/char/tpm/Makefile | 1 +
> drivers/char/tpm/tpm-chip.c | 221 ++++++++----
> drivers/char/tpm/tpm-dev.c | 15 +-
> drivers/char/tpm/tpm-interface.c | 50 +--
> drivers/char/tpm/tpm-sysfs.c | 22 +-
> drivers/char/tpm/tpm.h | 30 +-
> drivers/char/tpm/tpm2-cmd.c | 8 +-
> drivers/char/tpm/tpm_atmel.c | 14 +-
> drivers/char/tpm/tpm_eventlog.c | 2 +-
> drivers/char/tpm/tpm_eventlog.h | 2 +-
> drivers/char/tpm/tpm_i2c_atmel.c | 16 +-
> drivers/char/tpm/tpm_i2c_infineon.c | 6 +-
> drivers/char/tpm/tpm_i2c_nuvoton.c | 24 +-
> drivers/char/tpm/tpm_infineon.c | 22 +-
> drivers/char/tpm/tpm_nsc.c | 20 +-
> drivers/char/tpm/tpm_tis.c | 18 +-
> drivers/char/tpm/tpm_vtpm_proxy.c | 649 +++++++++++++++++++++++++++++++++++
> include/uapi/linux/Kbuild | 1 +
> include/uapi/linux/vtpm_proxy.h | 42 +++
> 22 files changed, 1066 insertions(+), 179 deletions(-)
> create mode 100644 Documentation/tpm/tpm_vtpm_proxy.txt
> create mode 100644 drivers/char/tpm/tpm_vtpm_proxy.c
> create mode 100644 include/uapi/linux/vtpm_proxy.h
>
> --
> 2.4.3
>