1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
26f99612eSStefan Berger /*
36f99612eSStefan Berger * Copyright (C) 2015, 2016 IBM Corporation
47ea7861cSJarkko Sakkinen * Copyright (C) 2016 Intel Corporation
56f99612eSStefan Berger *
66f99612eSStefan Berger * Author: Stefan Berger <stefanb@us.ibm.com>
76f99612eSStefan Berger *
86f99612eSStefan Berger * Maintained by: <tpmdd-devel@lists.sourceforge.net>
96f99612eSStefan Berger *
106f99612eSStefan Berger * Device driver for vTPM (vTPM proxy driver)
116f99612eSStefan Berger */
126f99612eSStefan Berger
136f99612eSStefan Berger #include <linux/types.h>
146f99612eSStefan Berger #include <linux/spinlock.h>
156f99612eSStefan Berger #include <linux/uaccess.h>
166f99612eSStefan Berger #include <linux/wait.h>
176f99612eSStefan Berger #include <linux/miscdevice.h>
186f99612eSStefan Berger #include <linux/vtpm_proxy.h>
196f99612eSStefan Berger #include <linux/file.h>
206f99612eSStefan Berger #include <linux/anon_inodes.h>
216f99612eSStefan Berger #include <linux/poll.h>
226f99612eSStefan Berger #include <linux/compat.h>
236f99612eSStefan Berger
246f99612eSStefan Berger #include "tpm.h"
256f99612eSStefan Berger
266f99612eSStefan Berger #define VTPM_PROXY_REQ_COMPLETE_FLAG BIT(0)
276f99612eSStefan Berger
286f99612eSStefan Berger struct proxy_dev {
296f99612eSStefan Berger struct tpm_chip *chip;
306f99612eSStefan Berger
316f99612eSStefan Berger u32 flags; /* public API flags */
326f99612eSStefan Berger
336f99612eSStefan Berger wait_queue_head_t wq;
346f99612eSStefan Berger
356f99612eSStefan Berger struct mutex buf_lock; /* protect buffer and flags */
366f99612eSStefan Berger
376f99612eSStefan Berger long state; /* internal state */
386f99612eSStefan Berger #define STATE_OPENED_FLAG BIT(0)
396f99612eSStefan Berger #define STATE_WAIT_RESPONSE_FLAG BIT(1) /* waiting for emulator response */
40c4484f79SJason Gunthorpe #define STATE_REGISTERED_FLAG BIT(2)
41d8b5d945SStefan Berger #define STATE_DRIVER_COMMAND BIT(3) /* sending a driver specific command */
426f99612eSStefan Berger
436f99612eSStefan Berger size_t req_len; /* length of queued TPM request */
446f99612eSStefan Berger size_t resp_len; /* length of queued TPM response */
456f99612eSStefan Berger u8 buffer[TPM_BUFSIZE]; /* request/response buffer */
466f99612eSStefan Berger
476f99612eSStefan Berger struct work_struct work; /* task that retrieves TPM timeouts */
486f99612eSStefan Berger };
496f99612eSStefan Berger
506f99612eSStefan Berger /* all supported flags */
516f99612eSStefan Berger #define VTPM_PROXY_FLAGS_ALL (VTPM_PROXY_FLAG_TPM2)
526f99612eSStefan Berger
536f99612eSStefan Berger static struct workqueue_struct *workqueue;
546f99612eSStefan Berger
556f99612eSStefan Berger static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev);
566f99612eSStefan Berger
576f99612eSStefan Berger /*
586f99612eSStefan Berger * Functions related to 'server side'
596f99612eSStefan Berger */
606f99612eSStefan Berger
616f99612eSStefan Berger /**
626f99612eSStefan Berger * vtpm_proxy_fops_read - Read TPM commands on 'server side'
636f99612eSStefan Berger *
6493c12f29SWinkler, Tomas * @filp: file pointer
6593c12f29SWinkler, Tomas * @buf: read buffer
6693c12f29SWinkler, Tomas * @count: number of bytes to read
6793c12f29SWinkler, Tomas * @off: offset
6893c12f29SWinkler, Tomas *
6993c12f29SWinkler, Tomas * Return:
706f99612eSStefan Berger * Number of bytes read or negative error code
716f99612eSStefan Berger */
vtpm_proxy_fops_read(struct file * filp,char __user * buf,size_t count,loff_t * off)726f99612eSStefan Berger static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
736f99612eSStefan Berger size_t count, loff_t *off)
746f99612eSStefan Berger {
756f99612eSStefan Berger struct proxy_dev *proxy_dev = filp->private_data;
766f99612eSStefan Berger size_t len;
776f99612eSStefan Berger int sig, rc;
786f99612eSStefan Berger
796f99612eSStefan Berger sig = wait_event_interruptible(proxy_dev->wq,
806f99612eSStefan Berger proxy_dev->req_len != 0 ||
816f99612eSStefan Berger !(proxy_dev->state & STATE_OPENED_FLAG));
826f99612eSStefan Berger if (sig)
836f99612eSStefan Berger return -EINTR;
846f99612eSStefan Berger
856f99612eSStefan Berger mutex_lock(&proxy_dev->buf_lock);
866f99612eSStefan Berger
876f99612eSStefan Berger if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
886f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
896f99612eSStefan Berger return -EPIPE;
906f99612eSStefan Berger }
916f99612eSStefan Berger
926f99612eSStefan Berger len = proxy_dev->req_len;
936f99612eSStefan Berger
94e52432e1SKees Cook if (count < len || len > sizeof(proxy_dev->buffer)) {
956f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
966f99612eSStefan Berger pr_debug("Invalid size in recv: count=%zd, req_len=%zd\n",
976f99612eSStefan Berger count, len);
986f99612eSStefan Berger return -EIO;
996f99612eSStefan Berger }
1006f99612eSStefan Berger
1016f99612eSStefan Berger rc = copy_to_user(buf, proxy_dev->buffer, len);
1026f99612eSStefan Berger memset(proxy_dev->buffer, 0, len);
1036f99612eSStefan Berger proxy_dev->req_len = 0;
1046f99612eSStefan Berger
1056f99612eSStefan Berger if (!rc)
1066f99612eSStefan Berger proxy_dev->state |= STATE_WAIT_RESPONSE_FLAG;
1076f99612eSStefan Berger
1086f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
1096f99612eSStefan Berger
1106f99612eSStefan Berger if (rc)
1116f99612eSStefan Berger return -EFAULT;
1126f99612eSStefan Berger
1136f99612eSStefan Berger return len;
1146f99612eSStefan Berger }
1156f99612eSStefan Berger
1166f99612eSStefan Berger /**
1176f99612eSStefan Berger * vtpm_proxy_fops_write - Write TPM responses on 'server side'
1186f99612eSStefan Berger *
11993c12f29SWinkler, Tomas * @filp: file pointer
12093c12f29SWinkler, Tomas * @buf: write buffer
12193c12f29SWinkler, Tomas * @count: number of bytes to write
12293c12f29SWinkler, Tomas * @off: offset
12393c12f29SWinkler, Tomas *
12493c12f29SWinkler, Tomas * Return:
1256f99612eSStefan Berger * Number of bytes read or negative error value
1266f99612eSStefan Berger */
vtpm_proxy_fops_write(struct file * filp,const char __user * buf,size_t count,loff_t * off)1276f99612eSStefan Berger static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf,
1286f99612eSStefan Berger size_t count, loff_t *off)
1296f99612eSStefan Berger {
1306f99612eSStefan Berger struct proxy_dev *proxy_dev = filp->private_data;
1316f99612eSStefan Berger
1326f99612eSStefan Berger mutex_lock(&proxy_dev->buf_lock);
1336f99612eSStefan Berger
1346f99612eSStefan Berger if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
1356f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
1366f99612eSStefan Berger return -EPIPE;
1376f99612eSStefan Berger }
1386f99612eSStefan Berger
1396f99612eSStefan Berger if (count > sizeof(proxy_dev->buffer) ||
1406f99612eSStefan Berger !(proxy_dev->state & STATE_WAIT_RESPONSE_FLAG)) {
1416f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
1426f99612eSStefan Berger return -EIO;
1436f99612eSStefan Berger }
1446f99612eSStefan Berger
1456f99612eSStefan Berger proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG;
1466f99612eSStefan Berger
1476f99612eSStefan Berger proxy_dev->req_len = 0;
1486f99612eSStefan Berger
1496f99612eSStefan Berger if (copy_from_user(proxy_dev->buffer, buf, count)) {
1506f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
1516f99612eSStefan Berger return -EFAULT;
1526f99612eSStefan Berger }
1536f99612eSStefan Berger
1546f99612eSStefan Berger proxy_dev->resp_len = count;
1556f99612eSStefan Berger
1566f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
1576f99612eSStefan Berger
1586f99612eSStefan Berger wake_up_interruptible(&proxy_dev->wq);
1596f99612eSStefan Berger
1606f99612eSStefan Berger return count;
1616f99612eSStefan Berger }
1626f99612eSStefan Berger
1636f99612eSStefan Berger /*
16493c12f29SWinkler, Tomas * vtpm_proxy_fops_poll - Poll status on 'server side'
1656f99612eSStefan Berger *
16693c12f29SWinkler, Tomas * @filp: file pointer
16793c12f29SWinkler, Tomas * @wait: poll table
16893c12f29SWinkler, Tomas *
16993c12f29SWinkler, Tomas * Return: Poll flags
1706f99612eSStefan Berger */
vtpm_proxy_fops_poll(struct file * filp,poll_table * wait)171afc9a42bSAl Viro static __poll_t vtpm_proxy_fops_poll(struct file *filp, poll_table *wait)
1726f99612eSStefan Berger {
1736f99612eSStefan Berger struct proxy_dev *proxy_dev = filp->private_data;
174afc9a42bSAl Viro __poll_t ret;
1756f99612eSStefan Berger
1766f99612eSStefan Berger poll_wait(filp, &proxy_dev->wq, wait);
1776f99612eSStefan Berger
178a9a08845SLinus Torvalds ret = EPOLLOUT;
1796f99612eSStefan Berger
1806f99612eSStefan Berger mutex_lock(&proxy_dev->buf_lock);
1816f99612eSStefan Berger
1826f99612eSStefan Berger if (proxy_dev->req_len)
183a9a08845SLinus Torvalds ret |= EPOLLIN | EPOLLRDNORM;
1846f99612eSStefan Berger
1856f99612eSStefan Berger if (!(proxy_dev->state & STATE_OPENED_FLAG))
186a9a08845SLinus Torvalds ret |= EPOLLHUP;
1876f99612eSStefan Berger
1886f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
1896f99612eSStefan Berger
1906f99612eSStefan Berger return ret;
1916f99612eSStefan Berger }
1926f99612eSStefan Berger
1936f99612eSStefan Berger /*
1946f99612eSStefan Berger * vtpm_proxy_fops_open - Open vTPM device on 'server side'
1956f99612eSStefan Berger *
19693c12f29SWinkler, Tomas * @filp: file pointer
19793c12f29SWinkler, Tomas *
1986f99612eSStefan Berger * Called when setting up the anonymous file descriptor
1996f99612eSStefan Berger */
vtpm_proxy_fops_open(struct file * filp)2006f99612eSStefan Berger static void vtpm_proxy_fops_open(struct file *filp)
2016f99612eSStefan Berger {
2026f99612eSStefan Berger struct proxy_dev *proxy_dev = filp->private_data;
2036f99612eSStefan Berger
2046f99612eSStefan Berger proxy_dev->state |= STATE_OPENED_FLAG;
2056f99612eSStefan Berger }
2066f99612eSStefan Berger
2076f99612eSStefan Berger /**
2086f99612eSStefan Berger * vtpm_proxy_fops_undo_open - counter-part to vtpm_fops_open
2096f99612eSStefan Berger * Call to undo vtpm_proxy_fops_open
21093c12f29SWinkler, Tomas *
21193c12f29SWinkler, Tomas *@proxy_dev: tpm proxy device
2126f99612eSStefan Berger */
vtpm_proxy_fops_undo_open(struct proxy_dev * proxy_dev)2136f99612eSStefan Berger static void vtpm_proxy_fops_undo_open(struct proxy_dev *proxy_dev)
2146f99612eSStefan Berger {
2156f99612eSStefan Berger mutex_lock(&proxy_dev->buf_lock);
2166f99612eSStefan Berger
2176f99612eSStefan Berger proxy_dev->state &= ~STATE_OPENED_FLAG;
2186f99612eSStefan Berger
2196f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
2206f99612eSStefan Berger
2216f99612eSStefan Berger /* no more TPM responses -- wake up anyone waiting for them */
2226f99612eSStefan Berger wake_up_interruptible(&proxy_dev->wq);
2236f99612eSStefan Berger }
2246f99612eSStefan Berger
2256f99612eSStefan Berger /*
22693c12f29SWinkler, Tomas * vtpm_proxy_fops_release - Close 'server side'
2276f99612eSStefan Berger *
22893c12f29SWinkler, Tomas * @inode: inode
22993c12f29SWinkler, Tomas * @filp: file pointer
23093c12f29SWinkler, Tomas * Return:
2316f99612eSStefan Berger * Always returns 0.
2326f99612eSStefan Berger */
vtpm_proxy_fops_release(struct inode * inode,struct file * filp)2336f99612eSStefan Berger static int vtpm_proxy_fops_release(struct inode *inode, struct file *filp)
2346f99612eSStefan Berger {
2356f99612eSStefan Berger struct proxy_dev *proxy_dev = filp->private_data;
2366f99612eSStefan Berger
2376f99612eSStefan Berger filp->private_data = NULL;
2386f99612eSStefan Berger
2396f99612eSStefan Berger vtpm_proxy_delete_device(proxy_dev);
2406f99612eSStefan Berger
2416f99612eSStefan Berger return 0;
2426f99612eSStefan Berger }
2436f99612eSStefan Berger
2446f99612eSStefan Berger static const struct file_operations vtpm_proxy_fops = {
2456f99612eSStefan Berger .owner = THIS_MODULE,
2466f99612eSStefan Berger .llseek = no_llseek,
2476f99612eSStefan Berger .read = vtpm_proxy_fops_read,
2486f99612eSStefan Berger .write = vtpm_proxy_fops_write,
2496f99612eSStefan Berger .poll = vtpm_proxy_fops_poll,
2506f99612eSStefan Berger .release = vtpm_proxy_fops_release,
2516f99612eSStefan Berger };
2526f99612eSStefan Berger
2536f99612eSStefan Berger /*
2546f99612eSStefan Berger * Functions invoked by the core TPM driver to send TPM commands to
2556f99612eSStefan Berger * 'server side' and receive responses from there.
2566f99612eSStefan Berger */
2576f99612eSStefan Berger
2586f99612eSStefan Berger /*
2596f99612eSStefan Berger * Called when core TPM driver reads TPM responses from 'server side'
2606f99612eSStefan Berger *
26193c12f29SWinkler, Tomas * @chip: tpm chip to use
26293c12f29SWinkler, Tomas * @buf: receive buffer
26393c12f29SWinkler, Tomas * @count: bytes to read
26493c12f29SWinkler, Tomas * Return:
2656f99612eSStefan Berger * Number of TPM response bytes read, negative error value otherwise
2666f99612eSStefan Berger */
vtpm_proxy_tpm_op_recv(struct tpm_chip * chip,u8 * buf,size_t count)2676f99612eSStefan Berger static int vtpm_proxy_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count)
2686f99612eSStefan Berger {
2696f99612eSStefan Berger struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
2706f99612eSStefan Berger size_t len;
2716f99612eSStefan Berger
2726f99612eSStefan Berger /* process gone ? */
2736f99612eSStefan Berger mutex_lock(&proxy_dev->buf_lock);
2746f99612eSStefan Berger
2756f99612eSStefan Berger if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
2766f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
2776f99612eSStefan Berger return -EPIPE;
2786f99612eSStefan Berger }
2796f99612eSStefan Berger
2806f99612eSStefan Berger len = proxy_dev->resp_len;
2816f99612eSStefan Berger if (count < len) {
2826f99612eSStefan Berger dev_err(&chip->dev,
2836f99612eSStefan Berger "Invalid size in recv: count=%zd, resp_len=%zd\n",
2846f99612eSStefan Berger count, len);
2856f99612eSStefan Berger len = -EIO;
2866f99612eSStefan Berger goto out;
2876f99612eSStefan Berger }
2886f99612eSStefan Berger
2896f99612eSStefan Berger memcpy(buf, proxy_dev->buffer, len);
2906f99612eSStefan Berger proxy_dev->resp_len = 0;
2916f99612eSStefan Berger
2926f99612eSStefan Berger out:
2936f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
2946f99612eSStefan Berger
2956f99612eSStefan Berger return len;
2966f99612eSStefan Berger }
2976f99612eSStefan Berger
vtpm_proxy_is_driver_command(struct tpm_chip * chip,u8 * buf,size_t count)298d8b5d945SStefan Berger static int vtpm_proxy_is_driver_command(struct tpm_chip *chip,
299d8b5d945SStefan Berger u8 *buf, size_t count)
300d8b5d945SStefan Berger {
301b34b77a9SJarkko Sakkinen struct tpm_header *hdr = (struct tpm_header *)buf;
302d8b5d945SStefan Berger
303b34b77a9SJarkko Sakkinen if (count < sizeof(struct tpm_header))
304d8b5d945SStefan Berger return 0;
305d8b5d945SStefan Berger
306d8b5d945SStefan Berger if (chip->flags & TPM_CHIP_FLAG_TPM2) {
307d8b5d945SStefan Berger switch (be32_to_cpu(hdr->ordinal)) {
308d8b5d945SStefan Berger case TPM2_CC_SET_LOCALITY:
309d8b5d945SStefan Berger return 1;
310d8b5d945SStefan Berger }
311d8b5d945SStefan Berger } else {
312d8b5d945SStefan Berger switch (be32_to_cpu(hdr->ordinal)) {
313d8b5d945SStefan Berger case TPM_ORD_SET_LOCALITY:
314d8b5d945SStefan Berger return 1;
315d8b5d945SStefan Berger }
316d8b5d945SStefan Berger }
317d8b5d945SStefan Berger return 0;
318d8b5d945SStefan Berger }
319d8b5d945SStefan Berger
3206f99612eSStefan Berger /*
3216f99612eSStefan Berger * Called when core TPM driver forwards TPM requests to 'server side'.
3226f99612eSStefan Berger *
32393c12f29SWinkler, Tomas * @chip: tpm chip to use
32493c12f29SWinkler, Tomas * @buf: send buffer
32593c12f29SWinkler, Tomas * @count: bytes to send
32693c12f29SWinkler, Tomas *
32793c12f29SWinkler, Tomas * Return:
3286f99612eSStefan Berger * 0 in case of success, negative error value otherwise.
3296f99612eSStefan Berger */
vtpm_proxy_tpm_op_send(struct tpm_chip * chip,u8 * buf,size_t count)3306f99612eSStefan Berger static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count)
3316f99612eSStefan Berger {
3326f99612eSStefan Berger struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
3336f99612eSStefan Berger
3346f99612eSStefan Berger if (count > sizeof(proxy_dev->buffer)) {
3356f99612eSStefan Berger dev_err(&chip->dev,
3366f99612eSStefan Berger "Invalid size in send: count=%zd, buffer size=%zd\n",
3376f99612eSStefan Berger count, sizeof(proxy_dev->buffer));
3386f99612eSStefan Berger return -EIO;
3396f99612eSStefan Berger }
3406f99612eSStefan Berger
341d8b5d945SStefan Berger if (!(proxy_dev->state & STATE_DRIVER_COMMAND) &&
342d8b5d945SStefan Berger vtpm_proxy_is_driver_command(chip, buf, count))
343d8b5d945SStefan Berger return -EFAULT;
344d8b5d945SStefan Berger
3456f99612eSStefan Berger mutex_lock(&proxy_dev->buf_lock);
3466f99612eSStefan Berger
3476f99612eSStefan Berger if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
3486f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
3496f99612eSStefan Berger return -EPIPE;
3506f99612eSStefan Berger }
3516f99612eSStefan Berger
3526f99612eSStefan Berger proxy_dev->resp_len = 0;
3536f99612eSStefan Berger
3546f99612eSStefan Berger proxy_dev->req_len = count;
3556f99612eSStefan Berger memcpy(proxy_dev->buffer, buf, count);
3566f99612eSStefan Berger
3576f99612eSStefan Berger proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG;
3586f99612eSStefan Berger
3596f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
3606f99612eSStefan Berger
3616f99612eSStefan Berger wake_up_interruptible(&proxy_dev->wq);
3626f99612eSStefan Berger
363f5595f5bSJarkko Sakkinen return 0;
3646f99612eSStefan Berger }
3656f99612eSStefan Berger
vtpm_proxy_tpm_op_cancel(struct tpm_chip * chip)3666f99612eSStefan Berger static void vtpm_proxy_tpm_op_cancel(struct tpm_chip *chip)
3676f99612eSStefan Berger {
3686f99612eSStefan Berger /* not supported */
3696f99612eSStefan Berger }
3706f99612eSStefan Berger
vtpm_proxy_tpm_op_status(struct tpm_chip * chip)3716f99612eSStefan Berger static u8 vtpm_proxy_tpm_op_status(struct tpm_chip *chip)
3726f99612eSStefan Berger {
3736f99612eSStefan Berger struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
3746f99612eSStefan Berger
3756f99612eSStefan Berger if (proxy_dev->resp_len)
3766f99612eSStefan Berger return VTPM_PROXY_REQ_COMPLETE_FLAG;
3776f99612eSStefan Berger
3786f99612eSStefan Berger return 0;
3796f99612eSStefan Berger }
3806f99612eSStefan Berger
vtpm_proxy_tpm_req_canceled(struct tpm_chip * chip,u8 status)3816f99612eSStefan Berger static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip *chip, u8 status)
3826f99612eSStefan Berger {
3836f99612eSStefan Berger struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
3846f99612eSStefan Berger bool ret;
3856f99612eSStefan Berger
3866f99612eSStefan Berger mutex_lock(&proxy_dev->buf_lock);
3876f99612eSStefan Berger
3886f99612eSStefan Berger ret = !(proxy_dev->state & STATE_OPENED_FLAG);
3896f99612eSStefan Berger
3906f99612eSStefan Berger mutex_unlock(&proxy_dev->buf_lock);
3916f99612eSStefan Berger
3926f99612eSStefan Berger return ret;
3936f99612eSStefan Berger }
3946f99612eSStefan Berger
vtpm_proxy_request_locality(struct tpm_chip * chip,int locality)395be4c9acfSStefan Berger static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
396be4c9acfSStefan Berger {
397be4c9acfSStefan Berger struct tpm_buf buf;
398be4c9acfSStefan Berger int rc;
399b34b77a9SJarkko Sakkinen const struct tpm_header *header;
400d8b5d945SStefan Berger struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
401be4c9acfSStefan Berger
402be4c9acfSStefan Berger if (chip->flags & TPM_CHIP_FLAG_TPM2)
403be4c9acfSStefan Berger rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS,
404be4c9acfSStefan Berger TPM2_CC_SET_LOCALITY);
405be4c9acfSStefan Berger else
406be4c9acfSStefan Berger rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND,
407be4c9acfSStefan Berger TPM_ORD_SET_LOCALITY);
408be4c9acfSStefan Berger if (rc)
409be4c9acfSStefan Berger return rc;
410be4c9acfSStefan Berger tpm_buf_append_u8(&buf, locality);
411be4c9acfSStefan Berger
412d8b5d945SStefan Berger proxy_dev->state |= STATE_DRIVER_COMMAND;
413d8b5d945SStefan Berger
41447a6c28bSJarkko Sakkinen rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to set locality");
415d8b5d945SStefan Berger
416d8b5d945SStefan Berger proxy_dev->state &= ~STATE_DRIVER_COMMAND;
417d8b5d945SStefan Berger
418be4c9acfSStefan Berger if (rc < 0) {
419be4c9acfSStefan Berger locality = rc;
420be4c9acfSStefan Berger goto out;
421be4c9acfSStefan Berger }
422be4c9acfSStefan Berger
423b34b77a9SJarkko Sakkinen header = (const struct tpm_header *)buf.data;
424be4c9acfSStefan Berger rc = be32_to_cpu(header->return_code);
425be4c9acfSStefan Berger if (rc)
426be4c9acfSStefan Berger locality = -1;
427be4c9acfSStefan Berger
428be4c9acfSStefan Berger out:
429be4c9acfSStefan Berger tpm_buf_destroy(&buf);
430be4c9acfSStefan Berger
431be4c9acfSStefan Berger return locality;
432be4c9acfSStefan Berger }
433be4c9acfSStefan Berger
4346f99612eSStefan Berger static const struct tpm_class_ops vtpm_proxy_tpm_ops = {
435cae8b441SJason Gunthorpe .flags = TPM_OPS_AUTO_STARTUP,
4366f99612eSStefan Berger .recv = vtpm_proxy_tpm_op_recv,
4376f99612eSStefan Berger .send = vtpm_proxy_tpm_op_send,
4386f99612eSStefan Berger .cancel = vtpm_proxy_tpm_op_cancel,
4396f99612eSStefan Berger .status = vtpm_proxy_tpm_op_status,
4406f99612eSStefan Berger .req_complete_mask = VTPM_PROXY_REQ_COMPLETE_FLAG,
4416f99612eSStefan Berger .req_complete_val = VTPM_PROXY_REQ_COMPLETE_FLAG,
4426f99612eSStefan Berger .req_canceled = vtpm_proxy_tpm_req_canceled,
443be4c9acfSStefan Berger .request_locality = vtpm_proxy_request_locality,
4446f99612eSStefan Berger };
4456f99612eSStefan Berger
4466f99612eSStefan Berger /*
4476f99612eSStefan Berger * Code related to the startup of the TPM 2 and startup of TPM 1.2 +
4486f99612eSStefan Berger * retrieval of timeouts and durations.
4496f99612eSStefan Berger */
4506f99612eSStefan Berger
vtpm_proxy_work(struct work_struct * work)4516f99612eSStefan Berger static void vtpm_proxy_work(struct work_struct *work)
4526f99612eSStefan Berger {
4536f99612eSStefan Berger struct proxy_dev *proxy_dev = container_of(work, struct proxy_dev,
4546f99612eSStefan Berger work);
4556f99612eSStefan Berger int rc;
4566f99612eSStefan Berger
4576f99612eSStefan Berger rc = tpm_chip_register(proxy_dev->chip);
4586f99612eSStefan Berger if (rc)
4596f99612eSStefan Berger vtpm_proxy_fops_undo_open(proxy_dev);
460c4484f79SJason Gunthorpe else
461c4484f79SJason Gunthorpe proxy_dev->state |= STATE_REGISTERED_FLAG;
4626f99612eSStefan Berger }
4636f99612eSStefan Berger
4646f99612eSStefan Berger /*
4656f99612eSStefan Berger * vtpm_proxy_work_stop: make sure the work has finished
4666f99612eSStefan Berger *
4676f99612eSStefan Berger * This function is useful when user space closed the fd
4686f99612eSStefan Berger * while the driver still determines timeouts.
4696f99612eSStefan Berger */
vtpm_proxy_work_stop(struct proxy_dev * proxy_dev)4706f99612eSStefan Berger static void vtpm_proxy_work_stop(struct proxy_dev *proxy_dev)
4716f99612eSStefan Berger {
4726f99612eSStefan Berger vtpm_proxy_fops_undo_open(proxy_dev);
4736f99612eSStefan Berger flush_work(&proxy_dev->work);
4746f99612eSStefan Berger }
4756f99612eSStefan Berger
4766f99612eSStefan Berger /*
4776f99612eSStefan Berger * vtpm_proxy_work_start: Schedule the work for TPM 1.2 & 2 initialization
4786f99612eSStefan Berger */
vtpm_proxy_work_start(struct proxy_dev * proxy_dev)4796f99612eSStefan Berger static inline void vtpm_proxy_work_start(struct proxy_dev *proxy_dev)
4806f99612eSStefan Berger {
4816f99612eSStefan Berger queue_work(workqueue, &proxy_dev->work);
4826f99612eSStefan Berger }
4836f99612eSStefan Berger
4846f99612eSStefan Berger /*
4856f99612eSStefan Berger * Code related to creation and deletion of device pairs
4866f99612eSStefan Berger */
vtpm_proxy_create_proxy_dev(void)4876f99612eSStefan Berger static struct proxy_dev *vtpm_proxy_create_proxy_dev(void)
4886f99612eSStefan Berger {
4896f99612eSStefan Berger struct proxy_dev *proxy_dev;
4906f99612eSStefan Berger struct tpm_chip *chip;
4916f99612eSStefan Berger int err;
4926f99612eSStefan Berger
4936f99612eSStefan Berger proxy_dev = kzalloc(sizeof(*proxy_dev), GFP_KERNEL);
4946f99612eSStefan Berger if (proxy_dev == NULL)
4956f99612eSStefan Berger return ERR_PTR(-ENOMEM);
4966f99612eSStefan Berger
4976f99612eSStefan Berger init_waitqueue_head(&proxy_dev->wq);
4986f99612eSStefan Berger mutex_init(&proxy_dev->buf_lock);
4996f99612eSStefan Berger INIT_WORK(&proxy_dev->work, vtpm_proxy_work);
5006f99612eSStefan Berger
5016f99612eSStefan Berger chip = tpm_chip_alloc(NULL, &vtpm_proxy_tpm_ops);
5026f99612eSStefan Berger if (IS_ERR(chip)) {
5036f99612eSStefan Berger err = PTR_ERR(chip);
5046f99612eSStefan Berger goto err_proxy_dev_free;
5056f99612eSStefan Berger }
5066f99612eSStefan Berger dev_set_drvdata(&chip->dev, proxy_dev);
5076f99612eSStefan Berger
5086f99612eSStefan Berger proxy_dev->chip = chip;
5096f99612eSStefan Berger
5106f99612eSStefan Berger return proxy_dev;
5116f99612eSStefan Berger
5126f99612eSStefan Berger err_proxy_dev_free:
5136f99612eSStefan Berger kfree(proxy_dev);
5146f99612eSStefan Berger
5156f99612eSStefan Berger return ERR_PTR(err);
5166f99612eSStefan Berger }
5176f99612eSStefan Berger
5186f99612eSStefan Berger /*
5196f99612eSStefan Berger * Undo what has been done in vtpm_create_proxy_dev
5206f99612eSStefan Berger */
vtpm_proxy_delete_proxy_dev(struct proxy_dev * proxy_dev)5216f99612eSStefan Berger static inline void vtpm_proxy_delete_proxy_dev(struct proxy_dev *proxy_dev)
5226f99612eSStefan Berger {
5236f99612eSStefan Berger put_device(&proxy_dev->chip->dev); /* frees chip */
5246f99612eSStefan Berger kfree(proxy_dev);
5256f99612eSStefan Berger }
5266f99612eSStefan Berger
5276f99612eSStefan Berger /*
5286f99612eSStefan Berger * Create a /dev/tpm%d and 'server side' file descriptor pair
5296f99612eSStefan Berger *
53093c12f29SWinkler, Tomas * Return:
5316f99612eSStefan Berger * Returns file pointer on success, an error value otherwise
5326f99612eSStefan Berger */
vtpm_proxy_create_device(struct vtpm_proxy_new_dev * vtpm_new_dev)5336f99612eSStefan Berger static struct file *vtpm_proxy_create_device(
5346f99612eSStefan Berger struct vtpm_proxy_new_dev *vtpm_new_dev)
5356f99612eSStefan Berger {
5366f99612eSStefan Berger struct proxy_dev *proxy_dev;
5376f99612eSStefan Berger int rc, fd;
5386f99612eSStefan Berger struct file *file;
5396f99612eSStefan Berger
5406f99612eSStefan Berger if (vtpm_new_dev->flags & ~VTPM_PROXY_FLAGS_ALL)
5416f99612eSStefan Berger return ERR_PTR(-EOPNOTSUPP);
5426f99612eSStefan Berger
5436f99612eSStefan Berger proxy_dev = vtpm_proxy_create_proxy_dev();
5446f99612eSStefan Berger if (IS_ERR(proxy_dev))
5456f99612eSStefan Berger return ERR_CAST(proxy_dev);
5466f99612eSStefan Berger
5476f99612eSStefan Berger proxy_dev->flags = vtpm_new_dev->flags;
5486f99612eSStefan Berger
5496f99612eSStefan Berger /* setup an anonymous file for the server-side */
5506f99612eSStefan Berger fd = get_unused_fd_flags(O_RDWR);
5516f99612eSStefan Berger if (fd < 0) {
5526f99612eSStefan Berger rc = fd;
5536f99612eSStefan Berger goto err_delete_proxy_dev;
5546f99612eSStefan Berger }
5556f99612eSStefan Berger
5566f99612eSStefan Berger file = anon_inode_getfile("[vtpms]", &vtpm_proxy_fops, proxy_dev,
5576f99612eSStefan Berger O_RDWR);
5586f99612eSStefan Berger if (IS_ERR(file)) {
5596f99612eSStefan Berger rc = PTR_ERR(file);
5606f99612eSStefan Berger goto err_put_unused_fd;
5616f99612eSStefan Berger }
5626f99612eSStefan Berger
5636f99612eSStefan Berger /* from now on we can unwind with put_unused_fd() + fput() */
5646f99612eSStefan Berger /* simulate an open() on the server side */
5656f99612eSStefan Berger vtpm_proxy_fops_open(file);
5666f99612eSStefan Berger
5676f99612eSStefan Berger if (proxy_dev->flags & VTPM_PROXY_FLAG_TPM2)
5686f99612eSStefan Berger proxy_dev->chip->flags |= TPM_CHIP_FLAG_TPM2;
5696f99612eSStefan Berger
5706f99612eSStefan Berger vtpm_proxy_work_start(proxy_dev);
5716f99612eSStefan Berger
5726f99612eSStefan Berger vtpm_new_dev->fd = fd;
5736f99612eSStefan Berger vtpm_new_dev->major = MAJOR(proxy_dev->chip->dev.devt);
5746f99612eSStefan Berger vtpm_new_dev->minor = MINOR(proxy_dev->chip->dev.devt);
5756f99612eSStefan Berger vtpm_new_dev->tpm_num = proxy_dev->chip->dev_num;
5766f99612eSStefan Berger
5776f99612eSStefan Berger return file;
5786f99612eSStefan Berger
5796f99612eSStefan Berger err_put_unused_fd:
5806f99612eSStefan Berger put_unused_fd(fd);
5816f99612eSStefan Berger
5826f99612eSStefan Berger err_delete_proxy_dev:
5836f99612eSStefan Berger vtpm_proxy_delete_proxy_dev(proxy_dev);
5846f99612eSStefan Berger
5856f99612eSStefan Berger return ERR_PTR(rc);
5866f99612eSStefan Berger }
5876f99612eSStefan Berger
5886f99612eSStefan Berger /*
5896f99612eSStefan Berger * Counter part to vtpm_create_device.
5906f99612eSStefan Berger */
vtpm_proxy_delete_device(struct proxy_dev * proxy_dev)5916f99612eSStefan Berger static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev)
5926f99612eSStefan Berger {
5936f99612eSStefan Berger vtpm_proxy_work_stop(proxy_dev);
5946f99612eSStefan Berger
5956f99612eSStefan Berger /*
5966f99612eSStefan Berger * A client may hold the 'ops' lock, so let it know that the server
5976f99612eSStefan Berger * side shuts down before we try to grab the 'ops' lock when
5986f99612eSStefan Berger * unregistering the chip.
5996f99612eSStefan Berger */
6006f99612eSStefan Berger vtpm_proxy_fops_undo_open(proxy_dev);
6016f99612eSStefan Berger
602c4484f79SJason Gunthorpe if (proxy_dev->state & STATE_REGISTERED_FLAG)
6036f99612eSStefan Berger tpm_chip_unregister(proxy_dev->chip);
6046f99612eSStefan Berger
6056f99612eSStefan Berger vtpm_proxy_delete_proxy_dev(proxy_dev);
6066f99612eSStefan Berger }
6076f99612eSStefan Berger
6086f99612eSStefan Berger /*
6096f99612eSStefan Berger * Code related to the control device /dev/vtpmx
6106f99612eSStefan Berger */
6116f99612eSStefan Berger
6127ea7861cSJarkko Sakkinen /**
6137ea7861cSJarkko Sakkinen * vtpmx_ioc_new_dev - handler for the %VTPM_PROXY_IOC_NEW_DEV ioctl
6147ea7861cSJarkko Sakkinen * @file: /dev/vtpmx
6157ea7861cSJarkko Sakkinen * @ioctl: the ioctl number
6167ea7861cSJarkko Sakkinen * @arg: pointer to the struct vtpmx_proxy_new_dev
6177ea7861cSJarkko Sakkinen *
6187ea7861cSJarkko Sakkinen * Creates an anonymous file that is used by the process acting as a TPM to
6197ea7861cSJarkko Sakkinen * communicate with the client processes. The function will also add a new TPM
6207ea7861cSJarkko Sakkinen * device through which data is proxied to this TPM acting process. The caller
6217ea7861cSJarkko Sakkinen * will be provided with a file descriptor to communicate with the clients and
6227ea7861cSJarkko Sakkinen * major and minor numbers for the TPM device.
6237ea7861cSJarkko Sakkinen */
vtpmx_ioc_new_dev(struct file * file,unsigned int ioctl,unsigned long arg)6247ea7861cSJarkko Sakkinen static long vtpmx_ioc_new_dev(struct file *file, unsigned int ioctl,
6257ea7861cSJarkko Sakkinen unsigned long arg)
6267ea7861cSJarkko Sakkinen {
6277ea7861cSJarkko Sakkinen void __user *argp = (void __user *)arg;
6287ea7861cSJarkko Sakkinen struct vtpm_proxy_new_dev __user *vtpm_new_dev_p;
6297ea7861cSJarkko Sakkinen struct vtpm_proxy_new_dev vtpm_new_dev;
6307ea7861cSJarkko Sakkinen struct file *vtpm_file;
6317ea7861cSJarkko Sakkinen
6327ea7861cSJarkko Sakkinen if (!capable(CAP_SYS_ADMIN))
6337ea7861cSJarkko Sakkinen return -EPERM;
6347ea7861cSJarkko Sakkinen
6357ea7861cSJarkko Sakkinen vtpm_new_dev_p = argp;
6367ea7861cSJarkko Sakkinen
6377ea7861cSJarkko Sakkinen if (copy_from_user(&vtpm_new_dev, vtpm_new_dev_p,
6387ea7861cSJarkko Sakkinen sizeof(vtpm_new_dev)))
6397ea7861cSJarkko Sakkinen return -EFAULT;
6407ea7861cSJarkko Sakkinen
6417ea7861cSJarkko Sakkinen vtpm_file = vtpm_proxy_create_device(&vtpm_new_dev);
6427ea7861cSJarkko Sakkinen if (IS_ERR(vtpm_file))
6437ea7861cSJarkko Sakkinen return PTR_ERR(vtpm_file);
6447ea7861cSJarkko Sakkinen
6457ea7861cSJarkko Sakkinen if (copy_to_user(vtpm_new_dev_p, &vtpm_new_dev,
6467ea7861cSJarkko Sakkinen sizeof(vtpm_new_dev))) {
6477ea7861cSJarkko Sakkinen put_unused_fd(vtpm_new_dev.fd);
6487ea7861cSJarkko Sakkinen fput(vtpm_file);
6497ea7861cSJarkko Sakkinen return -EFAULT;
6507ea7861cSJarkko Sakkinen }
6517ea7861cSJarkko Sakkinen
6527ea7861cSJarkko Sakkinen fd_install(vtpm_new_dev.fd, vtpm_file);
6537ea7861cSJarkko Sakkinen return 0;
6547ea7861cSJarkko Sakkinen }
6557ea7861cSJarkko Sakkinen
6566f99612eSStefan Berger /*
6576f99612eSStefan Berger * vtpmx_fops_ioctl: ioctl on /dev/vtpmx
6586f99612eSStefan Berger *
65993c12f29SWinkler, Tomas * Return:
6606f99612eSStefan Berger * Returns 0 on success, a negative error code otherwise.
6616f99612eSStefan Berger */
vtpmx_fops_ioctl(struct file * f,unsigned int ioctl,unsigned long arg)6626f99612eSStefan Berger static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl,
6636f99612eSStefan Berger unsigned long arg)
6646f99612eSStefan Berger {
6656f99612eSStefan Berger switch (ioctl) {
6666f99612eSStefan Berger case VTPM_PROXY_IOC_NEW_DEV:
6677ea7861cSJarkko Sakkinen return vtpmx_ioc_new_dev(f, ioctl, arg);
6686f99612eSStefan Berger default:
6696f99612eSStefan Berger return -ENOIOCTLCMD;
6706f99612eSStefan Berger }
6716f99612eSStefan Berger }
6726f99612eSStefan Berger
6736f99612eSStefan Berger static const struct file_operations vtpmx_fops = {
6746f99612eSStefan Berger .owner = THIS_MODULE,
6756f99612eSStefan Berger .unlocked_ioctl = vtpmx_fops_ioctl,
676407e9ef7SArnd Bergmann .compat_ioctl = compat_ptr_ioctl,
6776f99612eSStefan Berger .llseek = noop_llseek,
6786f99612eSStefan Berger };
6796f99612eSStefan Berger
6806f99612eSStefan Berger static struct miscdevice vtpmx_miscdev = {
6816f99612eSStefan Berger .minor = MISC_DYNAMIC_MINOR,
6826f99612eSStefan Berger .name = "vtpmx",
6836f99612eSStefan Berger .fops = &vtpmx_fops,
6846f99612eSStefan Berger };
6856f99612eSStefan Berger
vtpm_module_init(void)6866f99612eSStefan Berger static int __init vtpm_module_init(void)
6876f99612eSStefan Berger {
6886f99612eSStefan Berger int rc;
6896f99612eSStefan Berger
6906f99612eSStefan Berger workqueue = create_workqueue("tpm-vtpm");
6916f99612eSStefan Berger if (!workqueue) {
6926f99612eSStefan Berger pr_err("couldn't create workqueue\n");
693*f4032d61SJarkko Sakkinen return -ENOMEM;
6946f99612eSStefan Berger }
6956f99612eSStefan Berger
696*f4032d61SJarkko Sakkinen rc = misc_register(&vtpmx_miscdev);
697*f4032d61SJarkko Sakkinen if (rc) {
698*f4032d61SJarkko Sakkinen pr_err("couldn't create vtpmx device\n");
699*f4032d61SJarkko Sakkinen destroy_workqueue(workqueue);
700*f4032d61SJarkko Sakkinen }
7016f99612eSStefan Berger
7026f99612eSStefan Berger return rc;
7036f99612eSStefan Berger }
7046f99612eSStefan Berger
vtpm_module_exit(void)7056f99612eSStefan Berger static void __exit vtpm_module_exit(void)
7066f99612eSStefan Berger {
7076f99612eSStefan Berger destroy_workqueue(workqueue);
708*f4032d61SJarkko Sakkinen misc_deregister(&vtpmx_miscdev);
7096f99612eSStefan Berger }
7106f99612eSStefan Berger
7116f99612eSStefan Berger module_init(vtpm_module_init);
7126f99612eSStefan Berger module_exit(vtpm_module_exit);
7136f99612eSStefan Berger
7146f99612eSStefan Berger MODULE_AUTHOR("Stefan Berger (stefanb@us.ibm.com)");
7156f99612eSStefan Berger MODULE_DESCRIPTION("vTPM Driver");
7166f99612eSStefan Berger MODULE_VERSION("0.1");
7176f99612eSStefan Berger MODULE_LICENSE("GPL");
718