| 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;
|
| +}
|
|
|