Index: chromeos/drivers/tpmd_dev/tpmd_dev.c |
diff --git a/chromeos/drivers/tpmd_dev/tpmd_dev.c b/chromeos/drivers/tpmd_dev/tpmd_dev.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4b9e08c21bf9f327a6db6411df2dcc737d2d1c96 |
--- /dev/null |
+++ b/chromeos/drivers/tpmd_dev/tpmd_dev.c |
@@ -0,0 +1,271 @@ |
+/* Software-based Trusted Platform Module (TPM) Emulator |
+ * Copyright (C) 2004-2010 Mario Strasser <mast@gmx.net> |
+ * |
+ * This module is free software; you can redistribute it and/or modify |
+ * it under the terms of the GNU General Public License as published |
+ * by the Free Software Foundation; either version 2 of the License, |
+ * or (at your option) any later version. |
+ * |
+ * This module is distributed in the hope that it will be useful, |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ * GNU General Public License for more details. |
+ * |
+ * $Id: tpmd_dev.c 426 2010-02-22 17:11:58Z mast $ |
+ */ |
+ |
+#include <linux/module.h> |
+#include <linux/kernel.h> |
+#include <linux/init.h> |
+#include <linux/miscdevice.h> |
+#include <linux/poll.h> |
+ |
+#include <linux/socket.h> |
+#include <linux/net.h> |
+#include <linux/un.h> |
+ |
+#include "config.h" |
+ |
+#define TPM_DEVICE_MINOR 224 |
+#define TPM_DEVICE_ID "tpm" |
+#define TPM_MODULE_NAME "tpmd_dev" |
+ |
+#define TPM_STATE_IS_OPEN 0 |
+ |
+#ifdef DEBUG |
+#define debug(fmt, ...) printk(KERN_DEBUG "%s %s:%d: Debug: " fmt "\n", \ |
+ TPM_MODULE_NAME, __FILE__, __LINE__, ## __VA_ARGS__) |
+#else |
+#define debug(fmt, ...) |
+#endif |
+#define info(fmt, ...) printk(KERN_INFO "%s %s:%d: Info: " fmt "\n", \ |
+ TPM_MODULE_NAME, __FILE__, __LINE__, ## __VA_ARGS__) |
+#define error(fmt, ...) printk(KERN_ERR "%s %s:%d: Error: " fmt "\n", \ |
+ TPM_MODULE_NAME, __FILE__, __LINE__, ## __VA_ARGS__) |
+#define alert(fmt, ...) printk(KERN_ALERT "%s %s:%d: Alert: " fmt "\n", \ |
+ TPM_MODULE_NAME, __FILE__, __LINE__, ## __VA_ARGS__) |
+ |
+MODULE_LICENSE("GPL"); |
+MODULE_AUTHOR("Mario Strasser <mast@gmx.net>"); |
+MODULE_DESCRIPTION("Trusted Platform Module (TPM) Emulator"); |
+MODULE_SUPPORTED_DEVICE(TPM_DEVICE_ID); |
+ |
+/* module parameters */ |
+char *tpmd_socket_name = TPM_SOCKET_NAME; |
+module_param(tpmd_socket_name, charp, 0444); |
+MODULE_PARM_DESC(tpmd_socket_name, " Sets the name of the TPM daemon socket."); |
+ |
+/* TPM lock */ |
+static struct semaphore tpm_mutex; |
+ |
+/* TPM command response */ |
+static struct { |
+ uint8_t *data; |
+ uint32_t size; |
+} tpm_response; |
+ |
+/* module state */ |
+static uint32_t module_state; |
+static struct socket *tpmd_sock; |
+static struct sockaddr_un addr; |
+ |
+static int tpmd_connect(char *socket_name) |
+{ |
+ int res; |
+ res = sock_create(PF_UNIX, SOCK_STREAM, 0, &tpmd_sock); |
+ if (res != 0) { |
+ error("sock_create() failed: %d\n", res); |
+ tpmd_sock = NULL; |
+ return res; |
+ } |
+ addr.sun_family = AF_UNIX; |
+ strncpy(addr.sun_path, socket_name, sizeof(addr.sun_path)); |
+ res = tpmd_sock->ops->connect(tpmd_sock, |
+ (struct sockaddr*)&addr, sizeof(struct sockaddr_un), 0); |
+ if (res != 0) { |
+ error("sock_connect() failed: %d\n", res); |
+ tpmd_sock->ops->release(tpmd_sock); |
+ tpmd_sock = NULL; |
+ return res; |
+ } |
+ return 0; |
+} |
+ |
+static void tpmd_disconnect(void) |
+{ |
+ if (tpmd_sock != NULL) tpmd_sock->ops->release(tpmd_sock); |
+ tpmd_sock = NULL; |
+} |
+ |
+static int tpmd_handle_command(const uint8_t *in, uint32_t in_size) |
+{ |
+ int res; |
+ mm_segment_t oldmm; |
+ struct msghdr msg; |
+ struct iovec iov; |
+ /* send command to tpmd */ |
+ memset(&msg, 0, sizeof(msg)); |
+ iov.iov_base = (void*)in; |
+ iov.iov_len = in_size; |
+ msg.msg_iov = &iov; |
+ msg.msg_iovlen = 1; |
+ res = sock_sendmsg(tpmd_sock, &msg, in_size); |
+ if (res < 0) { |
+ error("sock_sendmsg() failed: %d\n", res); |
+ return res; |
+ } |
+ /* receive response from tpmd */ |
+ tpm_response.size = TPM_CMD_BUF_SIZE; |
+ tpm_response.data = kmalloc(tpm_response.size, GFP_KERNEL); |
+ if (tpm_response.data == NULL) return -1; |
+ memset(&msg, 0, sizeof(msg)); |
+ iov.iov_base = (void*)tpm_response.data; |
+ iov.iov_len = tpm_response.size; |
+ msg.msg_iov = &iov; |
+ msg.msg_iovlen = 1; |
+ oldmm = get_fs(); |
+ set_fs(KERNEL_DS); |
+ res = sock_recvmsg(tpmd_sock, &msg, tpm_response.size, 0); |
+ set_fs(oldmm); |
+ if (res < 0) { |
+ error("sock_recvmsg() failed: %d\n", res); |
+ tpm_response.data = NULL; |
+ return res; |
+ } |
+ tpm_response.size = res; |
+ return 0; |
+} |
+ |
+static int tpm_open(struct inode *inode, struct file *file) |
+{ |
+ int res; |
+ debug("%s()", __FUNCTION__); |
+ if (test_and_set_bit(TPM_STATE_IS_OPEN, (void*)&module_state)) return -EBUSY; |
+ down(&tpm_mutex); |
+ res = tpmd_connect(tpmd_socket_name); |
+ up(&tpm_mutex); |
+ if (res != 0) { |
+ clear_bit(TPM_STATE_IS_OPEN, (void*)&module_state); |
+ return -EIO; |
+ } |
+ return 0; |
+} |
+ |
+static int tpm_release(struct inode *inode, struct file *file) |
+{ |
+ debug("%s()", __FUNCTION__); |
+ down(&tpm_mutex); |
+ if (tpm_response.data != NULL) { |
+ kfree(tpm_response.data); |
+ tpm_response.data = NULL; |
+ } |
+ tpmd_disconnect(); |
+ up(&tpm_mutex); |
+ clear_bit(TPM_STATE_IS_OPEN, (void*)&module_state); |
+ return 0; |
+} |
+ |
+static ssize_t tpm_read(struct file *file, char *buf, size_t count, loff_t *ppos) |
+{ |
+ debug("%s(%zd)", __FUNCTION__, count); |
+ down(&tpm_mutex); |
+ if (tpm_response.data != NULL) { |
+ count = min(count, (size_t)tpm_response.size - (size_t)*ppos); |
+ count -= copy_to_user(buf, &tpm_response.data[*ppos], count); |
+ *ppos += count; |
+ if ((size_t)tpm_response.size == (size_t)*ppos) { |
+ kfree(tpm_response.data); |
+ tpm_response.data = NULL; |
+ } |
+ } else { |
+ count = 0; |
+ } |
+ up(&tpm_mutex); |
+ return count; |
+} |
+ |
+static ssize_t tpm_write(struct file *file, const char *buf, size_t count, loff_t *ppos) |
+{ |
+ debug("%s(%zd)", __FUNCTION__, count); |
+ down(&tpm_mutex); |
+ *ppos = 0; |
+ if (tpm_response.data != NULL) { |
+ kfree(tpm_response.data); |
+ tpm_response.data = NULL; |
+ } |
+ if (tpmd_handle_command(buf, count) != 0) { |
+ count = -EILSEQ; |
+ tpm_response.data = NULL; |
+ } |
+ up(&tpm_mutex); |
+ return count; |
+} |
+ |
+#define TPMIOC_CANCEL _IO('T', 0x00) |
+#define TPMIOC_TRANSMIT _IO('T', 0x01) |
+ |
+static int tpm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) |
+{ |
+ debug("%s(%d, %p)", __FUNCTION__, cmd, (char*)arg); |
+ if (cmd == TPMIOC_TRANSMIT) { |
+ uint32_t count = ntohl(*(uint32_t*)(arg + 2)); |
+ down(&tpm_mutex); |
+ if (tpm_response.data != NULL) { |
+ kfree(tpm_response.data); |
+ tpm_response.data = NULL; |
+ } |
+ if (tpmd_handle_command((char*)arg, count) == 0) { |
+ tpm_response.size -= copy_to_user((char*)arg, tpm_response.data, tpm_response.size); |
+ kfree(tpm_response.data); |
+ tpm_response.data = NULL; |
+ } else { |
+ tpm_response.size = 0; |
+ tpm_response.data = NULL; |
+ } |
+ up(&tpm_mutex); |
+ return tpm_response.size; |
+ } |
+ return -1; |
+} |
+ |
+struct file_operations fops = { |
+ .owner = THIS_MODULE, |
+ .open = tpm_open, |
+ .release = tpm_release, |
+ .read = tpm_read, |
+ .write = tpm_write, |
+ .ioctl = tpm_ioctl, |
+}; |
+ |
+static struct miscdevice tpm_dev = { |
+ .minor = TPM_DEVICE_MINOR, |
+ .name = TPM_DEVICE_ID, |
+ .fops = &fops, |
+}; |
+ |
+int __init init_tpm_module(void) |
+{ |
+ int res = misc_register(&tpm_dev); |
+ if (res != 0) { |
+ error("misc_register() failed for minor %d\n", TPM_DEVICE_MINOR); |
+ return res; |
+ } |
+ /* initialize variables */ |
+ sema_init(&tpm_mutex, 1); |
+ module_state = 0; |
+ tpm_response.data = NULL; |
+ tpm_response.size = 0; |
+ tpmd_sock = NULL; |
+ return 0; |
+} |
+ |
+void __exit cleanup_tpm_module(void) |
+{ |
+ misc_deregister(&tpm_dev); |
+ tpmd_disconnect(); |
+ if (tpm_response.data != NULL) kfree(tpm_response.data); |
+} |
+ |
+module_init(init_tpm_module); |
+module_exit(cleanup_tpm_module); |
+ |