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