From e1a53273fc1e06ef5bed32348c80639d4661c994 Mon Sep 17 00:00:00 2001 From: Matthew Fioravante Date: Tue, 13 Nov 2012 10:46:58 +0000 Subject: minios: add tpmfront, tpm_tis, and tpmback drivers This patch adds 3 new drivers to mini-os. tpmfront - paravirtualized tpm frontend driver tpmback - paravirtualized tpm backend driver tpm_tis - hardware tpm driver Unfortunately these drivers were derived from GPL licensed linux kernel drivers so they must carry the GPL license. However, since mini-os now supports conditional compilation, hopefully these drivers can be included into the xen tree and conditionally removed from non-gpl projects. By default they are disabled in the makefile. Signed-off-by: Matthew Fioravante Acked-by: Ian Campbell Committed-by: Ian Campbell --- extras/mini-os/tpmfront.c | 608 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 608 insertions(+) create mode 100644 extras/mini-os/tpmfront.c (limited to 'extras/mini-os/tpmfront.c') diff --git a/extras/mini-os/tpmfront.c b/extras/mini-os/tpmfront.c new file mode 100644 index 0000000000..0218d7fb84 --- /dev/null +++ b/extras/mini-os/tpmfront.c @@ -0,0 +1,608 @@ +/* + * Copyright (c) 2010-2012 United States Government, as represented by + * the Secretary of Defense. All rights reserved. + * + * This code has been derived from drivers/char/tpm_vtpm.c + * from the xen 2.6.18 linux kernel + * + * Copyright (C) 2006 IBM Corporation + * + * This code has also been derived from drivers/char/tpm_xen.c + * from the xen 2.6.18 linux kernel + * + * Copyright (c) 2005, IBM Corporation + * + * which was itself derived from drivers/xen/netfront/netfront.c + * from the linux kernel + * + * Copyright (c) 2002-2004, K A Fraser + * + * This program 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, version 2 of the + * License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define TPMFRONT_PRINT_DEBUG +#ifdef TPMFRONT_PRINT_DEBUG +#define TPMFRONT_DEBUG(fmt,...) printk("Tpmfront:Debug("__FILE__":%d) " fmt, __LINE__, ##__VA_ARGS__) +#define TPMFRONT_DEBUG_MORE(fmt,...) printk(fmt, ##__VA_ARGS__) +#else +#define TPMFRONT_DEBUG(fmt,...) +#endif +#define TPMFRONT_ERR(fmt,...) printk("Tpmfront:Error " fmt, ##__VA_ARGS__) +#define TPMFRONT_LOG(fmt,...) printk("Tpmfront:Info " fmt, ##__VA_ARGS__) + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +void tpmfront_handler(evtchn_port_t port, struct pt_regs *regs, void *data) { + struct tpmfront_dev* dev = (struct tpmfront_dev*) data; + /*If we get a response when we didnt make a request, just ignore it */ + if(!dev->waiting) { + return; + } + + dev->waiting = 0; +#ifdef HAVE_LIBC + if(dev->fd >= 0) { + files[dev->fd].read = 1; + } +#endif + wake_up(&dev->waitq); +} + +static int publish_xenbus(struct tpmfront_dev* dev) { + xenbus_transaction_t xbt; + int retry; + char* err; + /* Write the grant reference and event channel to xenstore */ +again: + if((err = xenbus_transaction_start(&xbt))) { + TPMFRONT_ERR("Unable to start xenbus transaction, error was %s\n", err); + free(err); + return -1; + } + + if((err = xenbus_printf(xbt, dev->nodename, "ring-ref", "%u", (unsigned int) dev->ring_ref))) { + TPMFRONT_ERR("Unable to write %s/ring-ref, error was %s\n", dev->nodename, err); + free(err); + goto abort_transaction; + } + + if((err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", (unsigned int) dev->evtchn))) { + TPMFRONT_ERR("Unable to write %s/event-channel, error was %s\n", dev->nodename, err); + free(err); + goto abort_transaction; + } + + if((err = xenbus_transaction_end(xbt, 0, &retry))) { + TPMFRONT_ERR("Unable to complete xenbus transaction, error was %s\n", err); + free(err); + return -1; + } + if(retry) { + goto again; + } + + return 0; +abort_transaction: + if((err = xenbus_transaction_end(xbt, 1, &retry))) { + free(err); + } + return -1; +} + +static int wait_for_backend_connect(xenbus_event_queue* events, char* path) +{ + int state; + + TPMFRONT_LOG("Waiting for backend connection..\n"); + /* Wait for the backend to connect */ + while(1) { + state = xenbus_read_integer(path); + if ( state < 0) + state = XenbusStateUnknown; + switch(state) { + /* Bad states, we quit with error */ + case XenbusStateUnknown: + case XenbusStateClosing: + case XenbusStateClosed: + TPMFRONT_ERR("Unable to connect to backend\n"); + return -1; + /* If backend is connected then break out of loop */ + case XenbusStateConnected: + TPMFRONT_LOG("Backend Connected\n"); + return 0; + default: + xenbus_wait_for_watch(events); + } + } + +} + +static int wait_for_backend_closed(xenbus_event_queue* events, char* path) +{ + int state; + + TPMFRONT_LOG("Waiting for backend to close..\n"); + while(1) { + state = xenbus_read_integer(path); + if ( state < 0) + state = XenbusStateUnknown; + switch(state) { + case XenbusStateUnknown: + TPMFRONT_ERR("Backend Unknown state, forcing shutdown\n"); + return -1; + case XenbusStateClosed: + TPMFRONT_LOG("Backend Closed\n"); + return 0; + default: + xenbus_wait_for_watch(events); + } + } + +} + +static int wait_for_backend_state_changed(struct tpmfront_dev* dev, XenbusState state) { + char* err; + int ret = 0; + xenbus_event_queue events = NULL; + char path[512]; + + snprintf(path, 512, "%s/state", dev->bepath); + /*Setup the watch to wait for the backend */ + if((err = xenbus_watch_path_token(XBT_NIL, path, path, &events))) { + TPMFRONT_ERR("Could not set a watch on %s, error was %s\n", path, err); + free(err); + return -1; + } + + /* Do the actual wait loop now */ + switch(state) { + case XenbusStateConnected: + ret = wait_for_backend_connect(&events, path); + break; + case XenbusStateClosed: + ret = wait_for_backend_closed(&events, path); + break; + default: + break; + } + + if((err = xenbus_unwatch_path_token(XBT_NIL, path, path))) { + TPMFRONT_ERR("Unable to unwatch %s, error was %s, ignoring..\n", path, err); + free(err); + } + return ret; +} + +static int tpmfront_connect(struct tpmfront_dev* dev) +{ + char* err; + /* Create shared page */ + dev->tx = (tpmif_tx_interface_t*) alloc_page(); + if(dev->tx == NULL) { + TPMFRONT_ERR("Unable to allocate page for shared memory\n"); + goto error; + } + memset(dev->tx, 0, PAGE_SIZE); + dev->ring_ref = gnttab_grant_access(dev->bedomid, virt_to_mfn(dev->tx), 0); + TPMFRONT_DEBUG("grant ref is %lu\n", (unsigned long) dev->ring_ref); + + /*Create event channel */ + if(evtchn_alloc_unbound(dev->bedomid, tpmfront_handler, dev, &dev->evtchn)) { + TPMFRONT_ERR("Unable to allocate event channel\n"); + goto error_postmap; + } + unmask_evtchn(dev->evtchn); + TPMFRONT_DEBUG("event channel is %lu\n", (unsigned long) dev->evtchn); + + /* Write the entries to xenstore */ + if(publish_xenbus(dev)) { + goto error_postevtchn; + } + + /* Change state to connected */ + dev->state = XenbusStateConnected; + + /* Tell the backend that we are ready */ + if((err = xenbus_printf(XBT_NIL, dev->nodename, "state", "%u", dev->state))) { + TPMFRONT_ERR("Unable to write to xenstore %s/state, value=%u", dev->nodename, XenbusStateConnected); + free(err); + goto error; + } + + return 0; +error_postevtchn: + mask_evtchn(dev->evtchn); + unbind_evtchn(dev->evtchn); +error_postmap: + gnttab_end_access(dev->ring_ref); + free_page(dev->tx); +error: + return -1; +} + +struct tpmfront_dev* init_tpmfront(const char* _nodename) +{ + struct tpmfront_dev* dev; + const char* nodename; + char path[512]; + char* value, *err; + unsigned long long ival; + int i; + + printk("============= Init TPM Front ================\n"); + + dev = malloc(sizeof(struct tpmfront_dev)); + memset(dev, 0, sizeof(struct tpmfront_dev)); + +#ifdef HAVE_LIBC + dev->fd = -1; +#endif + + nodename = _nodename ? _nodename : "device/vtpm/0"; + dev->nodename = strdup(nodename); + + init_waitqueue_head(&dev->waitq); + + /* Get backend domid */ + snprintf(path, 512, "%s/backend-id", dev->nodename); + if((err = xenbus_read(XBT_NIL, path, &value))) { + TPMFRONT_ERR("Unable to read %s during tpmfront initialization! error = %s\n", path, err); + free(err); + goto error; + } + if(sscanf(value, "%llu", &ival) != 1) { + TPMFRONT_ERR("%s has non-integer value (%s)\n", path, value); + free(value); + goto error; + } + free(value); + dev->bedomid = ival; + + /* Get backend xenstore path */ + snprintf(path, 512, "%s/backend", dev->nodename); + if((err = xenbus_read(XBT_NIL, path, &dev->bepath))) { + TPMFRONT_ERR("Unable to read %s during tpmfront initialization! error = %s\n", path, err); + free(err); + goto error; + } + + /* Create and publish grant reference and event channel */ + if (tpmfront_connect(dev)) { + goto error; + } + + /* Wait for backend to connect */ + if( wait_for_backend_state_changed(dev, XenbusStateConnected)) { + goto error; + } + + /* Allocate pages that will contain the messages */ + dev->pages = malloc(sizeof(void*) * TPMIF_TX_RING_SIZE); + if(dev->pages == NULL) { + goto error; + } + memset(dev->pages, 0, sizeof(void*) * TPMIF_TX_RING_SIZE); + for(i = 0; i < TPMIF_TX_RING_SIZE; ++i) { + dev->pages[i] = (void*)alloc_page(); + if(dev->pages[i] == NULL) { + goto error; + } + } + + TPMFRONT_LOG("Initialization Completed successfully\n"); + + return dev; + +error: + shutdown_tpmfront(dev); + return NULL; +} +void shutdown_tpmfront(struct tpmfront_dev* dev) +{ + char* err; + char path[512]; + int i; + tpmif_tx_request_t* tx; + if(dev == NULL) { + return; + } + TPMFRONT_LOG("Shutting down tpmfront\n"); + /* disconnect */ + if(dev->state == XenbusStateConnected) { + dev->state = XenbusStateClosing; + //FIXME: Transaction for this? + /* Tell backend we are closing */ + if((err = xenbus_printf(XBT_NIL, dev->nodename, "state", "%u", (unsigned int) dev->state))) { + free(err); + } + + /* Clean up xenstore entries */ + snprintf(path, 512, "%s/event-channel", dev->nodename); + if((err = xenbus_rm(XBT_NIL, path))) { + free(err); + } + snprintf(path, 512, "%s/ring-ref", dev->nodename); + if((err = xenbus_rm(XBT_NIL, path))) { + free(err); + } + + /* Tell backend we are closed */ + dev->state = XenbusStateClosed; + if((err = xenbus_printf(XBT_NIL, dev->nodename, "state", "%u", (unsigned int) dev->state))) { + TPMFRONT_ERR("Unable to write to %s, error was %s", dev->nodename, err); + free(err); + } + + /* Wait for the backend to close and unmap shared pages, ignore any errors */ + wait_for_backend_state_changed(dev, XenbusStateClosed); + + /* Cleanup any shared pages */ + if(dev->pages) { + for(i = 0; i < TPMIF_TX_RING_SIZE; ++i) { + if(dev->pages[i]) { + tx = &dev->tx->ring[i].req; + if(tx->ref != 0) { + gnttab_end_access(tx->ref); + } + free_page(dev->pages[i]); + } + } + free(dev->pages); + } + + /* Close event channel and unmap shared page */ + mask_evtchn(dev->evtchn); + unbind_evtchn(dev->evtchn); + gnttab_end_access(dev->ring_ref); + + free_page(dev->tx); + + } + + /* Cleanup memory usage */ + if(dev->respbuf) { + free(dev->respbuf); + } + if(dev->bepath) { + free(dev->bepath); + } + if(dev->nodename) { + free(dev->nodename); + } + free(dev); +} + +int tpmfront_send(struct tpmfront_dev* dev, const uint8_t* msg, size_t length) +{ + int i; + tpmif_tx_request_t* tx = NULL; + /* Error Checking */ + if(dev == NULL || dev->state != XenbusStateConnected) { + TPMFRONT_ERR("Tried to send message through disconnected frontend\n"); + return -1; + } + +#ifdef TPMFRONT_PRINT_DEBUG + TPMFRONT_DEBUG("Sending Msg to backend size=%u", (unsigned int) length); + for(i = 0; i < length; ++i) { + if(!(i % 30)) { + TPMFRONT_DEBUG_MORE("\n"); + } + TPMFRONT_DEBUG_MORE("%02X ", msg[i]); + } + TPMFRONT_DEBUG_MORE("\n"); +#endif + + /* Copy to shared pages now */ + for(i = 0; length > 0 && i < TPMIF_TX_RING_SIZE; ++i) { + /* Share the page */ + tx = &dev->tx->ring[i].req; + tx->unused = 0; + tx->addr = virt_to_mach(dev->pages[i]); + tx->ref = gnttab_grant_access(dev->bedomid, virt_to_mfn(dev->pages[i]), 0); + /* Copy the bits to the page */ + tx->size = length > PAGE_SIZE ? PAGE_SIZE : length; + memcpy(dev->pages[i], &msg[i * PAGE_SIZE], tx->size); + + /* Update counters */ + length -= tx->size; + } + dev->waiting = 1; + dev->resplen = 0; +#ifdef HAVE_LIBC + if(dev->fd >= 0) { + files[dev->fd].read = 0; + files[dev->fd].tpmfront.respgot = 0; + files[dev->fd].tpmfront.offset = 0; + } +#endif + notify_remote_via_evtchn(dev->evtchn); + return 0; +} +int tpmfront_recv(struct tpmfront_dev* dev, uint8_t** msg, size_t *length) +{ + tpmif_tx_request_t* tx; + int i; + if(dev == NULL || dev->state != XenbusStateConnected) { + TPMFRONT_ERR("Tried to receive message from disconnected frontend\n"); + return -1; + } + /*Wait for the response */ + wait_event(dev->waitq, (!dev->waiting)); + + /* Initialize */ + *msg = NULL; + *length = 0; + + /* special case, just quit */ + tx = &dev->tx->ring[0].req; + if(tx->size == 0 ) { + goto quit; + } + /* Get the total size */ + tx = &dev->tx->ring[0].req; + for(i = 0; i < TPMIF_TX_RING_SIZE && tx->size > 0; ++i) { + tx = &dev->tx->ring[i].req; + *length += tx->size; + } + /* Alloc the buffer */ + if(dev->respbuf) { + free(dev->respbuf); + } + *msg = dev->respbuf = malloc(*length); + dev->resplen = *length; + /* Copy the bits */ + tx = &dev->tx->ring[0].req; + for(i = 0; i < TPMIF_TX_RING_SIZE && tx->size > 0; ++i) { + tx = &dev->tx->ring[i].req; + memcpy(&(*msg)[i * PAGE_SIZE], dev->pages[i], tx->size); + gnttab_end_access(tx->ref); + tx->ref = 0; + } +#ifdef TPMFRONT_PRINT_DEBUG + TPMFRONT_DEBUG("Received response from backend size=%u", (unsigned int) *length); + for(i = 0; i < *length; ++i) { + if(!(i % 30)) { + TPMFRONT_DEBUG_MORE("\n"); + } + TPMFRONT_DEBUG_MORE("%02X ", (*msg)[i]); + } + TPMFRONT_DEBUG_MORE("\n"); +#endif +#ifdef HAVE_LIBC + if(dev->fd >= 0) { + files[dev->fd].tpmfront.respgot = 1; + } +#endif +quit: + return 0; +} + +int tpmfront_cmd(struct tpmfront_dev* dev, uint8_t* req, size_t reqlen, uint8_t** resp, size_t* resplen) +{ + int rc; + if((rc = tpmfront_send(dev, req, reqlen))) { + return rc; + } + if((rc = tpmfront_recv(dev, resp, resplen))) { + return rc; + } + + return 0; +} + +#ifdef HAVE_LIBC +#include +int tpmfront_open(struct tpmfront_dev* dev) +{ + /* Silently prevent multiple opens */ + if(dev->fd != -1) { + return dev->fd; + } + + dev->fd = alloc_fd(FTYPE_TPMFRONT); + printk("tpmfront_open(%s) -> %d\n", dev->nodename, dev->fd); + files[dev->fd].tpmfront.dev = dev; + files[dev->fd].tpmfront.offset = 0; + files[dev->fd].tpmfront.respgot = 0; + return dev->fd; +} + +int tpmfront_posix_write(int fd, const uint8_t* buf, size_t count) +{ + int rc; + struct tpmfront_dev* dev; + dev = files[fd].tpmfront.dev; + + if(count == 0) { + return 0; + } + + /* Return an error if we are already processing a command */ + if(dev->waiting) { + errno = EINPROGRESS; + return -1; + } + /* Send the command now */ + if((rc = tpmfront_send(dev, buf, count)) != 0) { + errno = EIO; + return -1; + } + return count; +} + +int tpmfront_posix_read(int fd, uint8_t* buf, size_t count) +{ + int rc; + uint8_t* dummybuf; + size_t dummysz; + struct tpmfront_dev* dev; + + dev = files[fd].tpmfront.dev; + + if(count == 0) { + return 0; + } + + /* get the response if we haven't already */ + if(files[dev->fd].tpmfront.respgot == 0) { + if ((rc = tpmfront_recv(dev, &dummybuf, &dummysz)) != 0) { + errno = EIO; + return -1; + } + } + + /* handle EOF case */ + if(files[dev->fd].tpmfront.offset >= dev->resplen) { + return 0; + } + + /* Compute the number of bytes and do the copy operation */ + if((rc = min(count, dev->resplen - files[dev->fd].tpmfront.offset)) != 0) { + memcpy(buf, dev->respbuf + files[dev->fd].tpmfront.offset, rc); + files[dev->fd].tpmfront.offset += rc; + } + + return rc; +} + +int tpmfront_posix_fstat(int fd, struct stat* buf) +{ + uint8_t* dummybuf; + size_t dummysz; + int rc; + struct tpmfront_dev* dev = files[fd].tpmfront.dev; + + /* If we have a response waiting, then read it now from the backend + * so we can get its length*/ + if(dev->waiting || (files[dev->fd].read == 1 && !files[dev->fd].tpmfront.respgot)) { + if ((rc = tpmfront_recv(dev, &dummybuf, &dummysz)) != 0) { + errno = EIO; + return -1; + } + } + + buf->st_mode = O_RDWR; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = dev->resplen; + buf->st_atime = buf->st_mtime = buf->st_ctime = time(NULL); + + return 0; +} + + +#endif -- cgit v1.2.3