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 */ 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 946f99612eSStefan Berger if (count < len) { 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 3666f99612eSStefan Berger static void vtpm_proxy_tpm_op_cancel(struct tpm_chip *chip) 3676f99612eSStefan Berger { 3686f99612eSStefan Berger /* not supported */ 3696f99612eSStefan Berger } 3706f99612eSStefan Berger 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 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 6866f99612eSStefan Berger static int vtpmx_init(void) 6876f99612eSStefan Berger { 6886f99612eSStefan Berger return misc_register(&vtpmx_miscdev); 6896f99612eSStefan Berger } 6906f99612eSStefan Berger 6916f99612eSStefan Berger static void vtpmx_cleanup(void) 6926f99612eSStefan Berger { 6936f99612eSStefan Berger misc_deregister(&vtpmx_miscdev); 6946f99612eSStefan Berger } 6956f99612eSStefan Berger 6966f99612eSStefan Berger static int __init vtpm_module_init(void) 6976f99612eSStefan Berger { 6986f99612eSStefan Berger int rc; 6996f99612eSStefan Berger 7006f99612eSStefan Berger rc = vtpmx_init(); 7016f99612eSStefan Berger if (rc) { 7026f99612eSStefan Berger pr_err("couldn't create vtpmx device\n"); 7036f99612eSStefan Berger return rc; 7046f99612eSStefan Berger } 7056f99612eSStefan Berger 7066f99612eSStefan Berger workqueue = create_workqueue("tpm-vtpm"); 7076f99612eSStefan Berger if (!workqueue) { 7086f99612eSStefan Berger pr_err("couldn't create workqueue\n"); 7096f99612eSStefan Berger rc = -ENOMEM; 7106f99612eSStefan Berger goto err_vtpmx_cleanup; 7116f99612eSStefan Berger } 7126f99612eSStefan Berger 7136f99612eSStefan Berger return 0; 7146f99612eSStefan Berger 7156f99612eSStefan Berger err_vtpmx_cleanup: 7166f99612eSStefan Berger vtpmx_cleanup(); 7176f99612eSStefan Berger 7186f99612eSStefan Berger return rc; 7196f99612eSStefan Berger } 7206f99612eSStefan Berger 7216f99612eSStefan Berger static void __exit vtpm_module_exit(void) 7226f99612eSStefan Berger { 7236f99612eSStefan Berger destroy_workqueue(workqueue); 7246f99612eSStefan Berger vtpmx_cleanup(); 7256f99612eSStefan Berger } 7266f99612eSStefan Berger 7276f99612eSStefan Berger module_init(vtpm_module_init); 7286f99612eSStefan Berger module_exit(vtpm_module_exit); 7296f99612eSStefan Berger 7306f99612eSStefan Berger MODULE_AUTHOR("Stefan Berger (stefanb@us.ibm.com)"); 7316f99612eSStefan Berger MODULE_DESCRIPTION("vTPM Driver"); 7326f99612eSStefan Berger MODULE_VERSION("0.1"); 7336f99612eSStefan Berger MODULE_LICENSE("GPL"); 734