[PATCH v3 6/7] tpm: expose spaces via a device link /dev/tpmrm
From: James BottomleyCurrently the tpm spaces are not exposed to userspace. Make this exposure via a separate device, which can now be opened multiple times because each read/write transaction goes separately via the space. Concurrency is protected by the chip->tpm_mutex for each read/write transaction separately. The TPM is cleared of all transient objects by the time the mutex is dropped, so there should be no interference between the kernel and userspace. Signed-off-by: James Bottomley Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/Makefile| 3 +- drivers/char/tpm/tpm-chip.c | 58 ++- drivers/char/tpm/tpm-interface.c | 13 ++-- drivers/char/tpm/tpm.h | 4 +++ drivers/char/tpm/tpmrm-dev.c | 65 5 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 drivers/char/tpm/tpmrm-dev.c diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 10e5827..23681f0 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -3,7 +3,8 @@ # obj-$(CONFIG_TCG_TPM) += tpm.o tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o \ -tpm-dev-common.o tpm1_eventlog.o tpm2_eventlog.o tpm2-space.o +tpm-dev-common.o tpmrm-dev.o tpm1_eventlog.o tpm2_eventlog.o \ + tpm2-space.o tpm-$(CONFIG_ACPI) += tpm_ppi.o tpm_acpi.o tpm-$(CONFIG_OF) += tpm_of.o obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 364d92e..3db38f9 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -33,6 +33,7 @@ DEFINE_IDR(dev_nums_idr); static DEFINE_MUTEX(idr_lock); struct class *tpm_class; +struct class *tpmrm_class; dev_t tpm_devt; /** @@ -132,6 +133,14 @@ static void tpm_dev_release(struct device *dev) kfree(chip); } +static void tpm_devs_release(struct device *dev) +{ + struct tpm_chip *chip = container_of(dev, struct tpm_chip, devs); + + /* release the master device reference */ + put_device(>dev); +} + /** * tpm_chip_alloc() - allocate a new struct tpm_chip instance * @pdev: device to which the chip is associated @@ -168,27 +177,47 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev, chip->dev_num = rc; device_initialize(>dev); + device_initialize(>devs); chip->dev.class = tpm_class; chip->dev.release = tpm_dev_release; chip->dev.parent = pdev; chip->dev.groups = chip->groups; + chip->devs.parent = pdev; + chip->devs.class = tpmrm_class; + chip->devs.release = tpm_devs_release; + /* get extra reference on main device to hold on +* behalf of devs. This holds the chip structure +* while cdevs is in use. The corresponding put +* is in the tpm_devs_release +*/ + get_device(>dev); + if (chip->dev_num == 0) chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR); else chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num); + chip->devs.devt = + MKDEV(MAJOR(tpm_devt), chip->dev_num + TPM_NUM_DEVICES); + rc = dev_set_name(>dev, "tpm%d", chip->dev_num); if (rc) goto out; + rc = dev_set_name(>devs, "tpmrm%d", chip->dev_num); + if (rc) + goto out; if (!pdev) chip->flags |= TPM_CHIP_FLAG_VIRTUAL; cdev_init(>cdev, _fops); + cdev_init(>cdevs, _fops); chip->cdev.owner = THIS_MODULE; + chip->cdevs.owner = THIS_MODULE; chip->cdev.kobj.parent = >dev.kobj; + chip->cdevs.kobj.parent = >devs.kobj; chip->work_space.context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!chip->work_space.context_buf) { @@ -199,6 +228,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev, return chip; out: + put_device(>devs); put_device(>dev); return ERR_PTR(rc); } @@ -263,7 +293,6 @@ static int tpm_add_char_device(struct tpm_chip *chip) "unable to cdev_add() %s, major %d, minor %d, err=%d\n", dev_name(>dev), MAJOR(chip->dev.devt), MINOR(chip->dev.devt), rc); - return rc; } @@ -277,6 +306,29 @@ static int tpm_add_char_device(struct tpm_chip *chip) return rc; } + if (chip->flags & TPM_CHIP_FLAG_TPM2) + rc = cdev_add(>cdevs, chip->devs.devt, 1); + if (rc) { + dev_err(>dev, + "unable to cdev_add() %s, major %d, minor %d, err=%d\n", + dev_name(>devs),
[PATCH v3 6/7] tpm: expose spaces via a device link /dev/tpmrm
From: James Bottomley Currently the tpm spaces are not exposed to userspace. Make this exposure via a separate device, which can now be opened multiple times because each read/write transaction goes separately via the space. Concurrency is protected by the chip->tpm_mutex for each read/write transaction separately. The TPM is cleared of all transient objects by the time the mutex is dropped, so there should be no interference between the kernel and userspace. Signed-off-by: James Bottomley Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/Makefile| 3 +- drivers/char/tpm/tpm-chip.c | 58 ++- drivers/char/tpm/tpm-interface.c | 13 ++-- drivers/char/tpm/tpm.h | 4 +++ drivers/char/tpm/tpmrm-dev.c | 65 5 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 drivers/char/tpm/tpmrm-dev.c diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 10e5827..23681f0 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -3,7 +3,8 @@ # obj-$(CONFIG_TCG_TPM) += tpm.o tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o \ -tpm-dev-common.o tpm1_eventlog.o tpm2_eventlog.o tpm2-space.o +tpm-dev-common.o tpmrm-dev.o tpm1_eventlog.o tpm2_eventlog.o \ + tpm2-space.o tpm-$(CONFIG_ACPI) += tpm_ppi.o tpm_acpi.o tpm-$(CONFIG_OF) += tpm_of.o obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 364d92e..3db38f9 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -33,6 +33,7 @@ DEFINE_IDR(dev_nums_idr); static DEFINE_MUTEX(idr_lock); struct class *tpm_class; +struct class *tpmrm_class; dev_t tpm_devt; /** @@ -132,6 +133,14 @@ static void tpm_dev_release(struct device *dev) kfree(chip); } +static void tpm_devs_release(struct device *dev) +{ + struct tpm_chip *chip = container_of(dev, struct tpm_chip, devs); + + /* release the master device reference */ + put_device(>dev); +} + /** * tpm_chip_alloc() - allocate a new struct tpm_chip instance * @pdev: device to which the chip is associated @@ -168,27 +177,47 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev, chip->dev_num = rc; device_initialize(>dev); + device_initialize(>devs); chip->dev.class = tpm_class; chip->dev.release = tpm_dev_release; chip->dev.parent = pdev; chip->dev.groups = chip->groups; + chip->devs.parent = pdev; + chip->devs.class = tpmrm_class; + chip->devs.release = tpm_devs_release; + /* get extra reference on main device to hold on +* behalf of devs. This holds the chip structure +* while cdevs is in use. The corresponding put +* is in the tpm_devs_release +*/ + get_device(>dev); + if (chip->dev_num == 0) chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR); else chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num); + chip->devs.devt = + MKDEV(MAJOR(tpm_devt), chip->dev_num + TPM_NUM_DEVICES); + rc = dev_set_name(>dev, "tpm%d", chip->dev_num); if (rc) goto out; + rc = dev_set_name(>devs, "tpmrm%d", chip->dev_num); + if (rc) + goto out; if (!pdev) chip->flags |= TPM_CHIP_FLAG_VIRTUAL; cdev_init(>cdev, _fops); + cdev_init(>cdevs, _fops); chip->cdev.owner = THIS_MODULE; + chip->cdevs.owner = THIS_MODULE; chip->cdev.kobj.parent = >dev.kobj; + chip->cdevs.kobj.parent = >devs.kobj; chip->work_space.context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!chip->work_space.context_buf) { @@ -199,6 +228,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev, return chip; out: + put_device(>devs); put_device(>dev); return ERR_PTR(rc); } @@ -263,7 +293,6 @@ static int tpm_add_char_device(struct tpm_chip *chip) "unable to cdev_add() %s, major %d, minor %d, err=%d\n", dev_name(>dev), MAJOR(chip->dev.devt), MINOR(chip->dev.devt), rc); - return rc; } @@ -277,6 +306,29 @@ static int tpm_add_char_device(struct tpm_chip *chip) return rc; } + if (chip->flags & TPM_CHIP_FLAG_TPM2) + rc = cdev_add(>cdevs, chip->devs.devt, 1); + if (rc) { + dev_err(>dev, + "unable to cdev_add() %s, major %d, minor %d, err=%d\n", + dev_name(>devs), MAJOR(chip->devs.devt), + MINOR(chip->devs.devt), rc); + tpm_del_char_device(chip, true); + return rc; + } + + if (chip->flags