Index: tpmd_dev/darwin/tpm_bridge.c |
diff --git a/tpmd_dev/darwin/tpm_bridge.c b/tpmd_dev/darwin/tpm_bridge.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5dc923965c14195e315e0b72e9971ba7d8e392bc |
--- /dev/null |
+++ b/tpmd_dev/darwin/tpm_bridge.c |
@@ -0,0 +1,449 @@ |
+/* |
+ * Copyright (c) 2009-2010 Amit Singh. All Rights Reserved. |
+ * http://osxbook.com |
+ * |
+ * TPM Emulator Device Bridge for Mac OS X |
+ * |
+ * Redistribution and use in source and binary forms, with or without |
+ * modification, are permitted provided that the following conditions are met: |
+ * |
+ * 1. Redistributions of source code must retain the above copyright notice, |
+ * this list of conditions and the following disclaimer. |
+ * 2. Redistributions in binary form must reproduce the above copyright |
+ * notice, this list of conditions and the following disclaimer in the |
+ * documentation and/or other materials provided with the distribution. |
+ * |
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR “AS IS” AND ANY EXPRESS OR IMPLIED |
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ */ |
+ |
+#include <sys/types.h> |
+#include <sys/fcntl.h> |
+#include <sys/systm.h> |
+#include <sys/conf.h> |
+#include <sys/ioctl.h> |
+#include <sys/param.h> |
+#include <sys/unistd.h> |
+#include <sys/malloc.h> |
+#include <sys/kpi_socket.h> |
+#include <sys/kpi_mbuf.h> |
+#include <sys/un.h> |
+#include <kern/locks.h> |
+#include <miscfs/devfs/devfs.h> |
+#include <mach/mach_types.h> |
+#include <libkern/OSAtomic.h> |
+#include "config.h" |
+ |
+/* configurable */ |
+ |
+#define TPM_BRIDGE_NAME "tpm" /* bridge device file name (/dev/tpm) */ |
+#define TPM_BRIDGE_MODE 0666 /* world readable/writable by default */ |
+#define TPM_BRIDGE_UID UID_ROOT /* bridge device file owner ID */ |
+#define TPM_BRIDGE_GID GID_WHEEL /* bridge device file group ID */ |
+ |
+/* buffer */ |
+ |
+static char tpm_buffer[TPM_CMD_BUF_SIZE] = { 0 }; |
+ |
+/* locking */ |
+ |
+static lck_grp_attr_t* tpm_mtx_grp_attr = NULL; |
+static lck_grp_t* tpm_mtx_grp = NULL; |
+static lck_attr_t* tpm_mtx_attr = NULL; |
+static lck_mtx_t* tpm_mtx = NULL; |
+ |
+/* user socket */ |
+ |
+errno_t sock_nointerrupt(socket_t sock, int on); |
+ |
+static SInt32 tpm_activity = 0; |
+static UInt32 tpm_in_io = 0; |
+static socket_t tpmd_socket = 0; |
+ |
+static struct sockaddr_un tpmd_socket_addr = { |
+ sizeof(struct sockaddr_un), |
+ AF_LOCAL, |
+ TPM_SOCKET_NAME, |
+}; |
+ |
+/* device */ |
+ |
+static int dev_tpm_index = -1; |
+static const int dev_tpm_minor = 0; |
+static void* dev_tpm_node = NULL; |
+ |
+d_open_t tpm_dev_open; |
+d_read_t tpm_dev_read; |
+d_write_t tpm_dev_write; |
+extern int seltrue(dev_t, int, struct proc*); |
+ |
+static struct cdevsw cdev_tpm = { |
+ tpm_dev_open, |
+ (d_close_t*)&nulldev, |
+ tpm_dev_read, |
+ tpm_dev_write, |
+ (d_ioctl_t*)&enodev, |
+ (d_stop_t*)&nulldev, |
+ (d_reset_t*)&nulldev, |
+ 0, |
+ (select_fcn_t*)seltrue, |
+ eno_mmap, |
+ eno_strat, |
+ eno_getc, |
+ eno_putc, |
+ D_TTY, |
+}; |
+ |
+static int tpmd_connect(void); |
+static void tpmd_disconnect(void); |
+kern_return_t tpm_bridge_start(kmod_info_t* ki, void* d); |
+kern_return_t tpm_bridge_stop(kmod_info_t* ki, void* d); |
+static int tpm_bridge_locking_start(void); |
+static int tpm_bridge_locking_stop(void); |
+static int tpm_bridge_devfs_start(void); |
+static int tpm_bridge_devfs_stop(void); |
+ |
+int |
+tpm_dev_open(dev_t dev, int flags, int devtype, struct proc* p) |
+{ |
+ (void)OSIncrementAtomic(&tpm_activity); |
+ |
+ int error = 0; |
+ |
+ lck_mtx_lock(tpm_mtx); |
+ |
+ if ((tpmd_socket == NULL) || !sock_isconnected(tpmd_socket)) { |
+ if (tpmd_connect() != 0) { |
+ tpmd_socket = NULL; |
+ lck_mtx_unlock(tpm_mtx); |
+ error = ECONNREFUSED; |
+ goto out; |
+ } |
+ } |
+ |
+ lck_mtx_unlock(tpm_mtx); |
+ |
+out: |
+ |
+ (void)OSDecrementAtomic(&tpm_activity); |
+ |
+ return error; |
+} |
+ |
+int |
+tpm_dev_read(dev_t dev, struct uio* uio, int ioflag) |
+{ |
+ (void)OSIncrementAtomic(&tpm_activity); |
+ |
+ errno_t error = 0; |
+ size_t recvlen; |
+ struct msghdr msg; |
+ struct iovec aiov[1]; |
+ |
+ lck_mtx_lock(tpm_mtx); |
+ |
+ if ((tpmd_socket == NULL) || !sock_isconnected(tpmd_socket)) { |
+ lck_mtx_unlock(tpm_mtx); |
+ error = ENOTCONN; |
+ goto out; |
+ } |
+ |
+ if (tpm_in_io) { |
+ error = msleep(&tpm_in_io, tpm_mtx, PCATCH, "tpm_in_io", NULL); |
+ if (error != 0) { |
+ lck_mtx_unlock(tpm_mtx); |
+ error = EAGAIN; |
+ goto out; |
+ } |
+ } |
+ |
+ tpm_in_io = 1; |
+ |
+ lck_mtx_unlock(tpm_mtx); |
+ |
+ (void)sock_nointerrupt(tpmd_socket, 1); |
+ |
+ recvlen = (uint32_t)uio_resid(uio); |
+ |
+ memset(&msg, 0, sizeof(msg)); |
+ aiov[0].iov_base = (caddr_t)tpm_buffer; |
+ aiov[0].iov_len = TPM_CMD_BUF_SIZE; |
+ if (recvlen < TPM_CMD_BUF_SIZE) { |
+ aiov[0].iov_len = recvlen; |
+ } |
+ msg.msg_iovlen = 1; |
+ msg.msg_iov = aiov; |
+ |
+ if ((error = sock_receive(tpmd_socket, &msg, 0, (size_t*)&recvlen)) == 0) { |
+ error = uiomove64((addr64_t)(uintptr_t)tpm_buffer, (int)recvlen, uio); |
+ } |
+ |
+ lck_mtx_lock(tpm_mtx); |
+ tpm_in_io = 0; |
+ wakeup_one((caddr_t)&tpm_in_io); |
+ lck_mtx_unlock(tpm_mtx); |
+ |
+out: |
+ |
+ (void)OSDecrementAtomic(&tpm_activity); |
+ |
+ return error; |
+} |
+ |
+int |
+tpm_dev_write(dev_t dev, struct uio* uio, int ioflag) |
+{ |
+ (void)OSIncrementAtomic(&tpm_activity); |
+ |
+ errno_t error = 0; |
+ size_t sentlen; |
+ struct msghdr msg; |
+ struct iovec aiov[1]; |
+ |
+ lck_mtx_lock(tpm_mtx); |
+ |
+ if ((tpmd_socket == NULL) || !sock_isconnected(tpmd_socket)) { |
+ lck_mtx_unlock(tpm_mtx); |
+ error = ENOTCONN; |
+ goto out; |
+ } |
+ |
+ if (tpm_in_io) { |
+ error = msleep(&tpm_in_io, tpm_mtx, PCATCH, "tpm_in_io", NULL); |
+ if (error != 0) { |
+ lck_mtx_unlock(tpm_mtx); |
+ error = EAGAIN; |
+ goto out; |
+ } |
+ } |
+ |
+ tpm_in_io = 1; |
+ |
+ lck_mtx_unlock(tpm_mtx); |
+ |
+ sentlen = min((uint32_t)uio_resid(uio), TPM_CMD_BUF_SIZE); |
+ |
+ if ((error = uiomove64((addr64_t)(uintptr_t)tpm_buffer, |
+ (int)sentlen, uio)) == 0) { |
+ memset(&msg, 0, sizeof(msg)); |
+ aiov[0].iov_base = (caddr_t)tpm_buffer; |
+ aiov[0].iov_len = sentlen; |
+ msg.msg_iovlen = 1; |
+ msg.msg_iov = aiov; |
+ error = sock_send(tpmd_socket, &msg, 0, &sentlen); |
+ } |
+ |
+ lck_mtx_lock(tpm_mtx); |
+ tpm_in_io = 0; |
+ wakeup_one((caddr_t)&tpm_in_io); |
+ lck_mtx_unlock(tpm_mtx); |
+ |
+out: |
+ |
+ (void)OSDecrementAtomic(&tpm_activity); |
+ |
+ return error; |
+} |
+ |
+static int |
+tpmd_connect(void) |
+{ |
+ errno_t error; |
+ struct timeval tv; |
+ |
+ error = sock_socket(PF_LOCAL, SOCK_STREAM, 0, NULL, NULL, &tpmd_socket); |
+ if (error != 0) { |
+ tpmd_socket = NULL; |
+ return error; |
+ } |
+ |
+ tv.tv_sec = 10; |
+ tv.tv_usec = 0; |
+ error = sock_setsockopt(tpmd_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, |
+ sizeof(struct timeval)); |
+ if (error != 0) { |
+ sock_close(tpmd_socket); |
+ tpmd_socket = NULL; |
+ return error; |
+ } |
+ |
+ error = sock_connect(tpmd_socket, |
+ (const struct sockaddr*)&tpmd_socket_addr, 0); |
+ if (error != 0) { |
+ sock_close(tpmd_socket); |
+ tpmd_socket = NULL; |
+ return error; |
+ } |
+ |
+ return 0; |
+} |
+ |
+static void |
+tpmd_disconnect(void) |
+{ |
+ if (tpmd_socket != NULL) { |
+ sock_shutdown(tpmd_socket, SHUT_RDWR); |
+ sock_close(tpmd_socket); |
+ tpmd_socket = NULL; |
+ } |
+} |
+ |
+static int |
+tpm_bridge_locking_start(void) |
+{ |
+ tpm_mtx_grp_attr = lck_grp_attr_alloc_init(); |
+ if (tpm_mtx_grp_attr == NULL) { |
+ goto failed; |
+ } |
+ |
+ tpm_mtx_grp = lck_grp_alloc_init("tpm_mtx", tpm_mtx_grp_attr); |
+ if (tpm_mtx_grp == NULL) { |
+ goto failed; |
+ } |
+ |
+ tpm_mtx_attr = lck_attr_alloc_init(); |
+ if (tpm_mtx_attr == NULL) { |
+ goto failed; |
+ } |
+ |
+ tpm_mtx = lck_mtx_alloc_init(tpm_mtx_grp, tpm_mtx_attr); |
+ if (tpm_mtx == NULL) { |
+ goto failed; |
+ } |
+ |
+ return KERN_SUCCESS; |
+ |
+failed: |
+ |
+ (void)tpm_bridge_locking_stop(); |
+ |
+ return KERN_FAILURE; |
+} |
+ |
+static int |
+tpm_bridge_locking_stop(void) |
+{ |
+ if (tpm_mtx != NULL) { |
+ lck_mtx_free(tpm_mtx, tpm_mtx_grp); |
+ tpm_mtx = NULL; |
+ } |
+ |
+ if (tpm_mtx_attr != NULL) { |
+ lck_attr_free(tpm_mtx_attr); |
+ tpm_mtx_attr = NULL; |
+ } |
+ |
+ if (tpm_mtx_grp != NULL) { |
+ lck_grp_free(tpm_mtx_grp); |
+ tpm_mtx_grp = NULL; |
+ } |
+ |
+ if (tpm_mtx_grp_attr != NULL) { |
+ lck_grp_attr_free(tpm_mtx_grp_attr); |
+ tpm_mtx_grp_attr = NULL; |
+ } |
+ |
+ return KERN_SUCCESS; |
+} |
+ |
+static int |
+tpm_bridge_devfs_start(void) |
+{ |
+ dev_tpm_index = cdevsw_add(-1, &cdev_tpm); |
+ if (dev_tpm_index == -1) { |
+ return KERN_FAILURE; |
+ } |
+ |
+ dev_tpm_node = devfs_make_node(makedev(dev_tpm_index, dev_tpm_minor), |
+ DEVFS_CHAR, TPM_BRIDGE_UID, TPM_BRIDGE_GID, |
+ TPM_BRIDGE_MODE, TPM_BRIDGE_NAME); |
+ if (dev_tpm_node == NULL) { |
+ (void)tpm_bridge_devfs_stop(); |
+ return KERN_FAILURE; |
+ } |
+ |
+ return KERN_SUCCESS; |
+} |
+ |
+static int |
+tpm_bridge_devfs_stop(void) |
+{ |
+ int ret = KERN_SUCCESS; |
+ |
+ if (dev_tpm_node != NULL) { |
+ devfs_remove(dev_tpm_node); |
+ dev_tpm_node = NULL; |
+ } |
+ |
+ if (dev_tpm_index != -1) { |
+ ret = cdevsw_remove(dev_tpm_index, &cdev_tpm); |
+ if (ret != dev_tpm_index) { |
+ ret = KERN_FAILURE; |
+ } else { |
+ dev_tpm_index = -1; |
+ ret = KERN_SUCCESS; |
+ } |
+ } |
+ |
+ return ret; |
+} |
+ |
+kern_return_t |
+tpm_bridge_start(kmod_info_t* ki, void* d) |
+{ |
+ if (tpm_bridge_locking_start() != KERN_SUCCESS) { |
+ return KERN_FAILURE; |
+ } |
+ |
+ if (tpm_bridge_devfs_start() != KERN_SUCCESS) { |
+ tpm_bridge_locking_stop(); |
+ return KERN_FAILURE; |
+ } |
+ |
+#ifndef SUN_LEN |
+#define SUN_LEN(su) \ |
+ (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) |
+#define SUN_LEN_PRIVATELY_DEFINED 1 |
+#endif |
+ |
+ tpmd_socket_addr.sun_len = SUN_LEN(&tpmd_socket_addr); |
+ |
+#if SUN_LEN_PRIVATELY_DEFINED |
+#undef SUN_LEN |
+#endif |
+ |
+ return KERN_SUCCESS; |
+} |
+ |
+kern_return_t |
+tpm_bridge_stop(kmod_info_t* ki, void* d) |
+{ |
+ lck_mtx_lock(tpm_mtx); |
+ |
+ (void)tpm_bridge_devfs_stop(); |
+ |
+ if ((tpmd_socket != NULL) && sock_isconnected(tpmd_socket)) { |
+ tpmd_disconnect(); |
+ tpmd_socket = NULL; |
+ } |
+ |
+ lck_mtx_unlock(tpm_mtx); |
+ |
+ do { |
+ struct timespec ts = { 1, 0 }; |
+ (void)msleep(&tpm_activity, NULL, PUSER, "tpm_activity", &ts); |
+ } while (tpm_activity > 0); |
+ |
+ |
+ (void)tpm_bridge_locking_stop(); |
+ |
+ return KERN_SUCCESS; |
+} |