Files
xen/extras/mini-os/tpmfront.c
T
Jan Beulich af501e3af4 tpmif: fix identifier prefixes
The definitions here shouldn't use vtpm_ or VPTM_ as their prefixes,
the interface should instead make use of tpmif_ and TPMIF_. This
fixes a build failure after syncing the public headers to
linux-2.6.18-xen.hg (where a struct vtpm_state already exists).

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Daniel De Graaf <dgdegra@tycho.nsa.gov>
2013-06-21 08:39:43 +02:00

631 lines
16 KiB
C

/*
* 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 <mini-os/os.h>
#include <mini-os/xenbus.h>
#include <mini-os/xmalloc.h>
#include <mini-os/events.h>
#include <mini-os/wait.h>
#include <mini-os/gnttab.h>
#include <xen/io/xenbus.h>
#include <xen/io/tpmif.h>
#include <mini-os/tpmfront.h>
#include <fcntl.h>
//#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;
tpmif_shared_page_t *shr = dev->page;
/*If we get a response when we didnt make a request, just ignore it */
if(!dev->waiting) {
return;
}
switch (shr->state) {
case TPMIF_STATE_FINISH: /* request was completed */
case TPMIF_STATE_IDLE: /* request was cancelled */
break;
default:
/* Spurious wakeup; do nothing, request is still pending */
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;
case XenbusStateInitWait:
TPMFRONT_LOG("Backend Closed (waiting for reconnect)\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:
TPMFRONT_ERR("Bad wait state %d, ignoring\n", state);
}
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->page = (tpmif_shared_page_t *)alloc_page();
if(dev->page == NULL) {
TPMFRONT_ERR("Unable to allocate page for shared memory\n");
goto error;
}
memset(dev->page, 0, PAGE_SIZE);
dev->ring_ref = gnttab_grant_access(dev->bedomid, virt_to_mfn(dev->page), 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->page);
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;
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;
}
/* Publish protocol v2 feature */
snprintf(path, 512, "%s/feature-protocol-v2", dev->nodename);
if ((err = xenbus_write(XBT_NIL, path, "1")))
{
TPMFRONT_ERR("Unable to write feature-protocol-v2 node: %s\n", 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;
}
/* Ensure backend is also using protocol v2 */
snprintf(path, 512, "%s/feature-protocol-v2", dev->bepath);
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(strcmp(value, "1")) {
TPMFRONT_ERR("%s has an invalid value (%s)\n", path, value);
free(value);
goto error;
}
free(value);
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];
if(dev == NULL) {
return;
}
TPMFRONT_LOG("Shutting down tpmfront\n");
/* disconnect */
if(dev->state == XenbusStateConnected) {
/* Tell backend we are closing */
dev->state = XenbusStateClosing;
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);
}
/* 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);
/* Prepare for a later reopen (possibly by a kexec'd kernel) */
dev->state = XenbusStateInitialising;
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);
}
/* Close event channel and unmap shared page */
mask_evtchn(dev->evtchn);
unbind_evtchn(dev->evtchn);
gnttab_end_access(dev->ring_ref);
free_page(dev->page);
}
/* 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)
{
unsigned int offset;
tpmif_shared_page_t *shr = NULL;
#ifdef TPMFRONT_PRINT_DEBUG
int i;
#endif
/* Error Checking */
if(dev == NULL || dev->state != XenbusStateConnected) {
TPMFRONT_ERR("Tried to send message through disconnected frontend\n");
return -1;
}
shr = dev->page;
#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 */
offset = sizeof(*shr);
if (length + offset > PAGE_SIZE) {
TPMFRONT_ERR("Message too long for shared page\n");
return -1;
}
memcpy(offset + (uint8_t*)shr, msg, length);
shr->length = length;
barrier();
shr->state = TPMIF_STATE_SUBMIT;
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
wmb();
notify_remote_via_evtchn(dev->evtchn);
return 0;
}
int tpmfront_recv(struct tpmfront_dev* dev, uint8_t** msg, size_t *length)
{
unsigned int offset;
tpmif_shared_page_t *shr = NULL;
#ifdef TPMFRONT_PRINT_DEBUG
int i;
#endif
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));
shr = dev->page;
/* Initialize */
*msg = NULL;
*length = 0;
offset = sizeof(*shr);
if (shr->state != TPMIF_STATE_FINISH)
goto quit;
*length = shr->length;
if (*length + offset > PAGE_SIZE) {
TPMFRONT_ERR("Reply too long for shared page\n");
return -1;
}
/* Alloc the buffer */
if(dev->respbuf) {
free(dev->respbuf);
}
*msg = dev->respbuf = malloc(*length);
dev->resplen = *length;
/* Copy the bits */
memcpy(*msg, offset + (uint8_t*)shr, *length);
#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;
}
int tpmfront_set_locality(struct tpmfront_dev* dev, int locality)
{
if (!dev || !dev->page)
return -1;
dev->page->locality = locality;
return 0;
}
#ifdef HAVE_LIBC
#include <errno.h>
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