Loading drivers/char/tpm/Kconfig +1 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ menuconfig TCG_TPM tristate "TPM Hardware Support" depends on HAS_IOMEM select SECURITYFS imply SECURITYFS select CRYPTO select CRYPTO_HASH_INFO ---help--- Loading drivers/char/tpm/tpm-dev-common.c +109 −38 Original line number Diff line number Diff line Loading @@ -17,11 +17,36 @@ * License. * */ #include <linux/poll.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/workqueue.h> #include "tpm.h" #include "tpm-dev.h" static struct workqueue_struct *tpm_dev_wq; static DEFINE_MUTEX(tpm_dev_wq_lock); static void tpm_async_work(struct work_struct *work) { struct file_priv *priv = container_of(work, struct file_priv, async_work); ssize_t ret; mutex_lock(&priv->buffer_mutex); priv->command_enqueued = false; ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer, sizeof(priv->data_buffer), 0); tpm_put_ops(priv->chip); if (ret > 0) { priv->data_pending = ret; mod_timer(&priv->user_read_timer, jiffies + (120 * HZ)); } mutex_unlock(&priv->buffer_mutex); wake_up_interruptible(&priv->async_wait); } static void user_reader_timeout(struct timer_list *t) { struct file_priv *priv = from_timer(priv, t, user_read_timer); Loading @@ -29,27 +54,32 @@ static void user_reader_timeout(struct timer_list *t) pr_warn("TPM user space timeout is deprecated (pid=%d)\n", task_tgid_nr(current)); schedule_work(&priv->work); schedule_work(&priv->timeout_work); } static void timeout_work(struct work_struct *work) static void tpm_timeout_work(struct work_struct *work) { struct file_priv *priv = container_of(work, struct file_priv, work); struct file_priv *priv = container_of(work, struct file_priv, timeout_work); mutex_lock(&priv->buffer_mutex); priv->data_pending = 0; memset(priv->data_buffer, 0, sizeof(priv->data_buffer)); mutex_unlock(&priv->buffer_mutex); wake_up_interruptible(&priv->async_wait); } void tpm_common_open(struct file *file, struct tpm_chip *chip, struct file_priv *priv) struct file_priv *priv, struct tpm_space *space) { priv->chip = chip; priv->space = space; mutex_init(&priv->buffer_mutex); timer_setup(&priv->user_read_timer, user_reader_timeout, 0); INIT_WORK(&priv->work, timeout_work); INIT_WORK(&priv->timeout_work, tpm_timeout_work); INIT_WORK(&priv->async_work, tpm_async_work); init_waitqueue_head(&priv->async_wait); file->private_data = priv; } Loading @@ -61,15 +91,17 @@ ssize_t tpm_common_read(struct file *file, char __user *buf, int rc; del_singleshot_timer_sync(&priv->user_read_timer); flush_work(&priv->work); flush_work(&priv->timeout_work); mutex_lock(&priv->buffer_mutex); if (priv->data_pending) { ret_size = min_t(ssize_t, size, priv->data_pending); if (ret_size > 0) { rc = copy_to_user(buf, priv->data_buffer, ret_size); memset(priv->data_buffer, 0, priv->data_pending); if (rc) ret_size = -EFAULT; } priv->data_pending = 0; } Loading @@ -79,13 +111,12 @@ ssize_t tpm_common_read(struct file *file, char __user *buf, } ssize_t tpm_common_write(struct file *file, const char __user *buf, size_t size, loff_t *off, struct tpm_space *space) size_t size, loff_t *off) { struct file_priv *priv = file->private_data; size_t in_size = size; ssize_t out_size; int ret = 0; if (in_size > TPM_BUFSIZE) if (size > TPM_BUFSIZE) return -E2BIG; mutex_lock(&priv->buffer_mutex); Loading @@ -94,21 +125,20 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf, * tpm_read or a user_read_timer timeout. This also prevents split * buffered writes from blocking here. */ if (priv->data_pending != 0) { mutex_unlock(&priv->buffer_mutex); return -EBUSY; if (priv->data_pending != 0 || priv->command_enqueued) { ret = -EBUSY; goto out; } if (copy_from_user (priv->data_buffer, (void __user *) buf, in_size)) { mutex_unlock(&priv->buffer_mutex); return -EFAULT; if (copy_from_user(priv->data_buffer, buf, size)) { ret = -EFAULT; goto out; } if (in_size < 6 || in_size < be32_to_cpu(*((__be32 *) (priv->data_buffer + 2)))) { mutex_unlock(&priv->buffer_mutex); return -EINVAL; if (size < 6 || size < be32_to_cpu(*((__be32 *)(priv->data_buffer + 2)))) { ret = -EINVAL; goto out; } /* atomic tpm command send and result receive. We only hold the ops Loading @@ -116,25 +146,50 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf, * the char dev is held open. */ if (tpm_try_get_ops(priv->chip)) { ret = -EPIPE; goto out; } /* * If in nonblocking mode schedule an async job to send * the command return the size. * In case of error the err code will be returned in * the subsequent read call. */ if (file->f_flags & O_NONBLOCK) { priv->command_enqueued = true; queue_work(tpm_dev_wq, &priv->async_work); mutex_unlock(&priv->buffer_mutex); return -EPIPE; return size; } out_size = tpm_transmit(priv->chip, space, priv->data_buffer, sizeof(priv->data_buffer), 0); ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer, sizeof(priv->data_buffer), 0); tpm_put_ops(priv->chip); if (out_size < 0) { if (ret > 0) { priv->data_pending = ret; mod_timer(&priv->user_read_timer, jiffies + (120 * HZ)); ret = size; } out: mutex_unlock(&priv->buffer_mutex); return out_size; return ret; } priv->data_pending = out_size; mutex_unlock(&priv->buffer_mutex); __poll_t tpm_common_poll(struct file *file, poll_table *wait) { struct file_priv *priv = file->private_data; __poll_t mask = 0; /* Set a timeout by which the reader must come claim the result */ mod_timer(&priv->user_read_timer, jiffies + (120 * HZ)); poll_wait(file, &priv->async_wait, wait); if (priv->data_pending) mask = EPOLLIN | EPOLLRDNORM; else mask = EPOLLOUT | EPOLLWRNORM; return in_size; return mask; } /* Loading @@ -142,8 +197,24 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf, */ void tpm_common_release(struct file *file, struct file_priv *priv) { flush_work(&priv->async_work); del_singleshot_timer_sync(&priv->user_read_timer); flush_work(&priv->work); flush_work(&priv->timeout_work); file->private_data = NULL; priv->data_pending = 0; } int __init tpm_dev_common_init(void) { tpm_dev_wq = alloc_workqueue("tpm_dev_wq", WQ_MEM_RECLAIM, 0); return !tpm_dev_wq ? -ENOMEM : 0; } void __exit tpm_dev_common_exit(void) { if (tpm_dev_wq) { destroy_workqueue(tpm_dev_wq); tpm_dev_wq = NULL; } } drivers/char/tpm/tpm-dev.c +3 −8 Original line number Diff line number Diff line Loading @@ -39,7 +39,7 @@ static int tpm_open(struct inode *inode, struct file *file) if (priv == NULL) goto out; tpm_common_open(file, chip, priv); tpm_common_open(file, chip, priv, NULL); return 0; Loading @@ -48,12 +48,6 @@ static int tpm_open(struct inode *inode, struct file *file) return -ENOMEM; } static ssize_t tpm_write(struct file *file, const char __user *buf, size_t size, loff_t *off) { return tpm_common_write(file, buf, size, off, NULL); } /* * Called on file close */ Loading @@ -73,6 +67,7 @@ const struct file_operations tpm_fops = { .llseek = no_llseek, .open = tpm_open, .read = tpm_common_read, .write = tpm_write, .write = tpm_common_write, .poll = tpm_common_poll, .release = tpm_release, }; drivers/char/tpm/tpm-dev.h +12 −6 Original line number Diff line number Diff line Loading @@ -2,27 +2,33 @@ #ifndef _TPM_DEV_H #define _TPM_DEV_H #include <linux/poll.h> #include "tpm.h" struct file_priv { struct tpm_chip *chip; struct tpm_space *space; /* Data passed to and from the tpm via the read/write calls */ size_t data_pending; /* Holds the amount of data passed or an error code from async op */ ssize_t data_pending; struct mutex buffer_mutex; struct timer_list user_read_timer; /* user needs to claim result */ struct work_struct work; struct work_struct timeout_work; struct work_struct async_work; wait_queue_head_t async_wait; bool command_enqueued; u8 data_buffer[TPM_BUFSIZE]; }; void tpm_common_open(struct file *file, struct tpm_chip *chip, struct file_priv *priv); struct file_priv *priv, struct tpm_space *space); ssize_t tpm_common_read(struct file *file, char __user *buf, size_t size, loff_t *off); ssize_t tpm_common_write(struct file *file, const char __user *buf, size_t size, loff_t *off, struct tpm_space *space); void tpm_common_release(struct file *file, struct file_priv *priv); size_t size, loff_t *off); __poll_t tpm_common_poll(struct file *file, poll_table *wait); void tpm_common_release(struct file *file, struct file_priv *priv); #endif drivers/char/tpm/tpm-interface.c +23 −7 Original line number Diff line number Diff line Loading @@ -663,7 +663,8 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, return len; err = be32_to_cpu(header->return_code); if (err != 0 && desc) if (err != 0 && err != TPM_ERR_DISABLED && err != TPM_ERR_DEACTIVATED && desc) dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err, desc); if (err) Loading Loading @@ -1321,7 +1322,8 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max) } rlength = be32_to_cpu(tpm_cmd.header.out.length); if (rlength < offsetof(struct tpm_getrandom_out, rng_data) + if (rlength < TPM_HEADER_SIZE + offsetof(struct tpm_getrandom_out, rng_data) + recd) { total = -EFAULT; break; Loading Loading @@ -1407,19 +1409,32 @@ static int __init tpm_init(void) tpmrm_class = class_create(THIS_MODULE, "tpmrm"); if (IS_ERR(tpmrm_class)) { pr_err("couldn't create tpmrm class\n"); class_destroy(tpm_class); return PTR_ERR(tpmrm_class); rc = PTR_ERR(tpmrm_class); goto out_destroy_tpm_class; } rc = alloc_chrdev_region(&tpm_devt, 0, 2*TPM_NUM_DEVICES, "tpm"); if (rc < 0) { pr_err("tpm: failed to allocate char dev region\n"); class_destroy(tpmrm_class); class_destroy(tpm_class); return rc; goto out_destroy_tpmrm_class; } rc = tpm_dev_common_init(); if (rc) { pr_err("tpm: failed to allocate char dev region\n"); goto out_unreg_chrdev; } return 0; out_unreg_chrdev: unregister_chrdev_region(tpm_devt, 2 * TPM_NUM_DEVICES); out_destroy_tpmrm_class: class_destroy(tpmrm_class); out_destroy_tpm_class: class_destroy(tpm_class); return rc; } static void __exit tpm_exit(void) Loading @@ -1428,6 +1443,7 @@ static void __exit tpm_exit(void) class_destroy(tpm_class); class_destroy(tpmrm_class); unregister_chrdev_region(tpm_devt, 2*TPM_NUM_DEVICES); tpm_dev_common_exit(); } subsys_initcall(tpm_init); Loading Loading
drivers/char/tpm/Kconfig +1 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ menuconfig TCG_TPM tristate "TPM Hardware Support" depends on HAS_IOMEM select SECURITYFS imply SECURITYFS select CRYPTO select CRYPTO_HASH_INFO ---help--- Loading
drivers/char/tpm/tpm-dev-common.c +109 −38 Original line number Diff line number Diff line Loading @@ -17,11 +17,36 @@ * License. * */ #include <linux/poll.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/workqueue.h> #include "tpm.h" #include "tpm-dev.h" static struct workqueue_struct *tpm_dev_wq; static DEFINE_MUTEX(tpm_dev_wq_lock); static void tpm_async_work(struct work_struct *work) { struct file_priv *priv = container_of(work, struct file_priv, async_work); ssize_t ret; mutex_lock(&priv->buffer_mutex); priv->command_enqueued = false; ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer, sizeof(priv->data_buffer), 0); tpm_put_ops(priv->chip); if (ret > 0) { priv->data_pending = ret; mod_timer(&priv->user_read_timer, jiffies + (120 * HZ)); } mutex_unlock(&priv->buffer_mutex); wake_up_interruptible(&priv->async_wait); } static void user_reader_timeout(struct timer_list *t) { struct file_priv *priv = from_timer(priv, t, user_read_timer); Loading @@ -29,27 +54,32 @@ static void user_reader_timeout(struct timer_list *t) pr_warn("TPM user space timeout is deprecated (pid=%d)\n", task_tgid_nr(current)); schedule_work(&priv->work); schedule_work(&priv->timeout_work); } static void timeout_work(struct work_struct *work) static void tpm_timeout_work(struct work_struct *work) { struct file_priv *priv = container_of(work, struct file_priv, work); struct file_priv *priv = container_of(work, struct file_priv, timeout_work); mutex_lock(&priv->buffer_mutex); priv->data_pending = 0; memset(priv->data_buffer, 0, sizeof(priv->data_buffer)); mutex_unlock(&priv->buffer_mutex); wake_up_interruptible(&priv->async_wait); } void tpm_common_open(struct file *file, struct tpm_chip *chip, struct file_priv *priv) struct file_priv *priv, struct tpm_space *space) { priv->chip = chip; priv->space = space; mutex_init(&priv->buffer_mutex); timer_setup(&priv->user_read_timer, user_reader_timeout, 0); INIT_WORK(&priv->work, timeout_work); INIT_WORK(&priv->timeout_work, tpm_timeout_work); INIT_WORK(&priv->async_work, tpm_async_work); init_waitqueue_head(&priv->async_wait); file->private_data = priv; } Loading @@ -61,15 +91,17 @@ ssize_t tpm_common_read(struct file *file, char __user *buf, int rc; del_singleshot_timer_sync(&priv->user_read_timer); flush_work(&priv->work); flush_work(&priv->timeout_work); mutex_lock(&priv->buffer_mutex); if (priv->data_pending) { ret_size = min_t(ssize_t, size, priv->data_pending); if (ret_size > 0) { rc = copy_to_user(buf, priv->data_buffer, ret_size); memset(priv->data_buffer, 0, priv->data_pending); if (rc) ret_size = -EFAULT; } priv->data_pending = 0; } Loading @@ -79,13 +111,12 @@ ssize_t tpm_common_read(struct file *file, char __user *buf, } ssize_t tpm_common_write(struct file *file, const char __user *buf, size_t size, loff_t *off, struct tpm_space *space) size_t size, loff_t *off) { struct file_priv *priv = file->private_data; size_t in_size = size; ssize_t out_size; int ret = 0; if (in_size > TPM_BUFSIZE) if (size > TPM_BUFSIZE) return -E2BIG; mutex_lock(&priv->buffer_mutex); Loading @@ -94,21 +125,20 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf, * tpm_read or a user_read_timer timeout. This also prevents split * buffered writes from blocking here. */ if (priv->data_pending != 0) { mutex_unlock(&priv->buffer_mutex); return -EBUSY; if (priv->data_pending != 0 || priv->command_enqueued) { ret = -EBUSY; goto out; } if (copy_from_user (priv->data_buffer, (void __user *) buf, in_size)) { mutex_unlock(&priv->buffer_mutex); return -EFAULT; if (copy_from_user(priv->data_buffer, buf, size)) { ret = -EFAULT; goto out; } if (in_size < 6 || in_size < be32_to_cpu(*((__be32 *) (priv->data_buffer + 2)))) { mutex_unlock(&priv->buffer_mutex); return -EINVAL; if (size < 6 || size < be32_to_cpu(*((__be32 *)(priv->data_buffer + 2)))) { ret = -EINVAL; goto out; } /* atomic tpm command send and result receive. We only hold the ops Loading @@ -116,25 +146,50 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf, * the char dev is held open. */ if (tpm_try_get_ops(priv->chip)) { ret = -EPIPE; goto out; } /* * If in nonblocking mode schedule an async job to send * the command return the size. * In case of error the err code will be returned in * the subsequent read call. */ if (file->f_flags & O_NONBLOCK) { priv->command_enqueued = true; queue_work(tpm_dev_wq, &priv->async_work); mutex_unlock(&priv->buffer_mutex); return -EPIPE; return size; } out_size = tpm_transmit(priv->chip, space, priv->data_buffer, sizeof(priv->data_buffer), 0); ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer, sizeof(priv->data_buffer), 0); tpm_put_ops(priv->chip); if (out_size < 0) { if (ret > 0) { priv->data_pending = ret; mod_timer(&priv->user_read_timer, jiffies + (120 * HZ)); ret = size; } out: mutex_unlock(&priv->buffer_mutex); return out_size; return ret; } priv->data_pending = out_size; mutex_unlock(&priv->buffer_mutex); __poll_t tpm_common_poll(struct file *file, poll_table *wait) { struct file_priv *priv = file->private_data; __poll_t mask = 0; /* Set a timeout by which the reader must come claim the result */ mod_timer(&priv->user_read_timer, jiffies + (120 * HZ)); poll_wait(file, &priv->async_wait, wait); if (priv->data_pending) mask = EPOLLIN | EPOLLRDNORM; else mask = EPOLLOUT | EPOLLWRNORM; return in_size; return mask; } /* Loading @@ -142,8 +197,24 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf, */ void tpm_common_release(struct file *file, struct file_priv *priv) { flush_work(&priv->async_work); del_singleshot_timer_sync(&priv->user_read_timer); flush_work(&priv->work); flush_work(&priv->timeout_work); file->private_data = NULL; priv->data_pending = 0; } int __init tpm_dev_common_init(void) { tpm_dev_wq = alloc_workqueue("tpm_dev_wq", WQ_MEM_RECLAIM, 0); return !tpm_dev_wq ? -ENOMEM : 0; } void __exit tpm_dev_common_exit(void) { if (tpm_dev_wq) { destroy_workqueue(tpm_dev_wq); tpm_dev_wq = NULL; } }
drivers/char/tpm/tpm-dev.c +3 −8 Original line number Diff line number Diff line Loading @@ -39,7 +39,7 @@ static int tpm_open(struct inode *inode, struct file *file) if (priv == NULL) goto out; tpm_common_open(file, chip, priv); tpm_common_open(file, chip, priv, NULL); return 0; Loading @@ -48,12 +48,6 @@ static int tpm_open(struct inode *inode, struct file *file) return -ENOMEM; } static ssize_t tpm_write(struct file *file, const char __user *buf, size_t size, loff_t *off) { return tpm_common_write(file, buf, size, off, NULL); } /* * Called on file close */ Loading @@ -73,6 +67,7 @@ const struct file_operations tpm_fops = { .llseek = no_llseek, .open = tpm_open, .read = tpm_common_read, .write = tpm_write, .write = tpm_common_write, .poll = tpm_common_poll, .release = tpm_release, };
drivers/char/tpm/tpm-dev.h +12 −6 Original line number Diff line number Diff line Loading @@ -2,27 +2,33 @@ #ifndef _TPM_DEV_H #define _TPM_DEV_H #include <linux/poll.h> #include "tpm.h" struct file_priv { struct tpm_chip *chip; struct tpm_space *space; /* Data passed to and from the tpm via the read/write calls */ size_t data_pending; /* Holds the amount of data passed or an error code from async op */ ssize_t data_pending; struct mutex buffer_mutex; struct timer_list user_read_timer; /* user needs to claim result */ struct work_struct work; struct work_struct timeout_work; struct work_struct async_work; wait_queue_head_t async_wait; bool command_enqueued; u8 data_buffer[TPM_BUFSIZE]; }; void tpm_common_open(struct file *file, struct tpm_chip *chip, struct file_priv *priv); struct file_priv *priv, struct tpm_space *space); ssize_t tpm_common_read(struct file *file, char __user *buf, size_t size, loff_t *off); ssize_t tpm_common_write(struct file *file, const char __user *buf, size_t size, loff_t *off, struct tpm_space *space); void tpm_common_release(struct file *file, struct file_priv *priv); size_t size, loff_t *off); __poll_t tpm_common_poll(struct file *file, poll_table *wait); void tpm_common_release(struct file *file, struct file_priv *priv); #endif
drivers/char/tpm/tpm-interface.c +23 −7 Original line number Diff line number Diff line Loading @@ -663,7 +663,8 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, return len; err = be32_to_cpu(header->return_code); if (err != 0 && desc) if (err != 0 && err != TPM_ERR_DISABLED && err != TPM_ERR_DEACTIVATED && desc) dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err, desc); if (err) Loading Loading @@ -1321,7 +1322,8 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max) } rlength = be32_to_cpu(tpm_cmd.header.out.length); if (rlength < offsetof(struct tpm_getrandom_out, rng_data) + if (rlength < TPM_HEADER_SIZE + offsetof(struct tpm_getrandom_out, rng_data) + recd) { total = -EFAULT; break; Loading Loading @@ -1407,19 +1409,32 @@ static int __init tpm_init(void) tpmrm_class = class_create(THIS_MODULE, "tpmrm"); if (IS_ERR(tpmrm_class)) { pr_err("couldn't create tpmrm class\n"); class_destroy(tpm_class); return PTR_ERR(tpmrm_class); rc = PTR_ERR(tpmrm_class); goto out_destroy_tpm_class; } rc = alloc_chrdev_region(&tpm_devt, 0, 2*TPM_NUM_DEVICES, "tpm"); if (rc < 0) { pr_err("tpm: failed to allocate char dev region\n"); class_destroy(tpmrm_class); class_destroy(tpm_class); return rc; goto out_destroy_tpmrm_class; } rc = tpm_dev_common_init(); if (rc) { pr_err("tpm: failed to allocate char dev region\n"); goto out_unreg_chrdev; } return 0; out_unreg_chrdev: unregister_chrdev_region(tpm_devt, 2 * TPM_NUM_DEVICES); out_destroy_tpmrm_class: class_destroy(tpmrm_class); out_destroy_tpm_class: class_destroy(tpm_class); return rc; } static void __exit tpm_exit(void) Loading @@ -1428,6 +1443,7 @@ static void __exit tpm_exit(void) class_destroy(tpm_class); class_destroy(tpmrm_class); unregister_chrdev_region(tpm_devt, 2*TPM_NUM_DEVICES); tpm_dev_common_exit(); } subsys_initcall(tpm_init); Loading