X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fxen%2Ftpmback%2Ftpmback.c;fp=drivers%2Fxen%2Ftpmback%2Ftpmback.c;h=466c3ee581b8d74c002aa7af5f03702a9970c5f2;hb=1db395853d4f30d6120458bd279ede1f882a8525;hp=0000000000000000000000000000000000000000;hpb=34a75f0025b9cf803b6a88db032e6ad6950c9313;p=linux-2.6.git diff --git a/drivers/xen/tpmback/tpmback.c b/drivers/xen/tpmback/tpmback.c new file mode 100644 index 000000000..466c3ee58 --- /dev/null +++ b/drivers/xen/tpmback/tpmback.c @@ -0,0 +1,944 @@ +/****************************************************************************** + * drivers/xen/tpmback/tpmback.c + * + * Copyright (c) 2005, IBM Corporation + * + * Author: Stefan Berger, stefanb@us.ibm.com + * Grant table support: Mahadevan Gomathisankaran + * + * This code has been derived from drivers/xen/netback/netback.c + * Copyright (c) 2002-2004, K A Fraser + * + */ + +#include "common.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* local data structures */ +struct data_exchange { + struct list_head pending_pak; + struct list_head current_pak; + unsigned int copied_so_far; + u8 has_opener:1; + u8 aborted:1; + rwlock_t pak_lock; // protects all of the previous fields + wait_queue_head_t wait_queue; +}; + +struct vtpm_resp_hdr { + uint32_t instance_no; + uint16_t tag_no; + uint32_t len_no; + uint32_t ordinal_no; +} __attribute__ ((packed)); + +struct packet { + struct list_head next; + unsigned int data_len; + u8 *data_buffer; + tpmif_t *tpmif; + u32 tpm_instance; + u8 req_tag; + u32 last_read; + u8 flags; + struct timer_list processing_timer; +}; + +enum { + PACKET_FLAG_DISCARD_RESPONSE = 1, +}; + +/* local variables */ +static struct data_exchange dataex; + +/* local function prototypes */ +static int _packet_write(struct packet *pak, + const char *data, size_t size, int userbuffer); +static void processing_timeout(unsigned long ptr); +static int packet_read_shmem(struct packet *pak, + tpmif_t * tpmif, + u32 offset, + char *buffer, int isuserbuffer, u32 left); +static int vtpm_queue_packet(struct packet *pak); + +/*************************************************************** + Buffer copying fo user and kernel space buffes. +***************************************************************/ +static inline int copy_from_buffer(void *to, + const void *from, unsigned long size, + int isuserbuffer) +{ + if (isuserbuffer) { + if (copy_from_user(to, (void __user *)from, size)) + return -EFAULT; + } else { + memcpy(to, from, size); + } + return 0; +} + +static inline int copy_to_buffer(void *to, + const void *from, unsigned long size, + int isuserbuffer) +{ + if (isuserbuffer) { + if (copy_to_user((void __user *)to, from, size)) + return -EFAULT; + } else { + memcpy(to, from, size); + } + return 0; +} + + +static void dataex_init(struct data_exchange *dataex) +{ + INIT_LIST_HEAD(&dataex->pending_pak); + INIT_LIST_HEAD(&dataex->current_pak); + dataex->has_opener = 0; + rwlock_init(&dataex->pak_lock); + init_waitqueue_head(&dataex->wait_queue); +} + +/*************************************************************** + Packet-related functions +***************************************************************/ + +static struct packet *packet_find_instance(struct list_head *head, + u32 tpm_instance) +{ + struct packet *pak; + struct list_head *p; + + /* + * traverse the list of packets and return the first + * one with the given instance number + */ + list_for_each(p, head) { + pak = list_entry(p, struct packet, next); + + if (pak->tpm_instance == tpm_instance) { + return pak; + } + } + return NULL; +} + +static struct packet *packet_find_packet(struct list_head *head, void *packet) +{ + struct packet *pak; + struct list_head *p; + + /* + * traverse the list of packets and return the first + * one with the given instance number + */ + list_for_each(p, head) { + pak = list_entry(p, struct packet, next); + + if (pak == packet) { + return pak; + } + } + return NULL; +} + +static struct packet *packet_alloc(tpmif_t * tpmif, + u32 size, u8 req_tag, u8 flags) +{ + struct packet *pak = NULL; + pak = kzalloc(sizeof (struct packet), GFP_ATOMIC); + if (NULL != pak) { + if (tpmif) { + pak->tpmif = tpmif; + pak->tpm_instance = tpmback_get_instance(tpmif->bi); + tpmif_get(tpmif); + } + pak->data_len = size; + pak->req_tag = req_tag; + pak->last_read = 0; + pak->flags = flags; + + /* + * cannot do tpmif_get(tpmif); bad things happen + * on the last tpmif_put() + */ + init_timer(&pak->processing_timer); + pak->processing_timer.function = processing_timeout; + pak->processing_timer.data = (unsigned long)pak; + } + return pak; +} + +static void inline packet_reset(struct packet *pak) +{ + pak->last_read = 0; +} + +static void packet_free(struct packet *pak) +{ + if (timer_pending(&pak->processing_timer)) { + BUG(); + } + + if (pak->tpmif) + tpmif_put(pak->tpmif); + kfree(pak->data_buffer); + /* + * cannot do tpmif_put(pak->tpmif); bad things happen + * on the last tpmif_put() + */ + kfree(pak); +} + + +/* + * Write data to the shared memory and send it to the FE. + */ +static int packet_write(struct packet *pak, + const char *data, size_t size, int isuserbuffer) +{ + int rc = 0; + + if (0 != (pak->flags & PACKET_FLAG_DISCARD_RESPONSE)) { + /* Don't send a respone to this packet. Just acknowledge it. */ + rc = size; + } else { + rc = _packet_write(pak, data, size, isuserbuffer); + } + + return rc; +} + +int _packet_write(struct packet *pak, + const char *data, size_t size, int isuserbuffer) +{ + /* + * Write into the shared memory pages directly + * and send it to the front end. + */ + tpmif_t *tpmif = pak->tpmif; + grant_handle_t handle; + int rc = 0; + unsigned int i = 0; + unsigned int offset = 0; + + if (tpmif == NULL) { + return -EFAULT; + } + + if (tpmif->status == DISCONNECTED) { + return size; + } + + while (offset < size && i < TPMIF_TX_RING_SIZE) { + unsigned int tocopy; + struct gnttab_map_grant_ref map_op; + struct gnttab_unmap_grant_ref unmap_op; + tpmif_tx_request_t *tx; + + tx = &tpmif->tx->ring[i].req; + + if (0 == tx->addr) { + DPRINTK("ERROR: Buffer for outgoing packet NULL?! i=%d\n", i); + return 0; + } + + gnttab_set_map_op(&map_op, MMAP_VADDR(tpmif, i), + GNTMAP_host_map, tx->ref, tpmif->domid); + + if (unlikely(HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, + &map_op, 1))) { + BUG(); + } + + handle = map_op.handle; + + if (map_op.status) { + DPRINTK(" Grant table operation failure !\n"); + return 0; + } + + tocopy = min_t(size_t, size - offset, PAGE_SIZE); + + if (copy_from_buffer((void *)(MMAP_VADDR(tpmif, i) | + (tx->addr & ~PAGE_MASK)), + &data[offset], tocopy, isuserbuffer)) { + tpmif_put(tpmif); + return -EFAULT; + } + tx->size = tocopy; + + gnttab_set_unmap_op(&unmap_op, MMAP_VADDR(tpmif, i), + GNTMAP_host_map, handle); + + if (unlikely + (HYPERVISOR_grant_table_op + (GNTTABOP_unmap_grant_ref, &unmap_op, 1))) { + BUG(); + } + + offset += tocopy; + i++; + } + + rc = offset; + DPRINTK("Notifying frontend via irq %d\n", tpmif->irq); + notify_remote_via_irq(tpmif->irq); + + return rc; +} + +/* + * Read data from the shared memory and copy it directly into the + * provided buffer. Advance the read_last indicator which tells + * how many bytes have already been read. + */ +static int packet_read(struct packet *pak, size_t numbytes, + char *buffer, size_t buffersize, int isuserbuffer) +{ + tpmif_t *tpmif = pak->tpmif; + + /* + * Read 'numbytes' of data from the buffer. The first 4 + * bytes are the instance number in network byte order, + * after that come the data from the shared memory buffer. + */ + u32 to_copy; + u32 offset = 0; + u32 room_left = buffersize; + + if (pak->last_read < 4) { + /* + * copy the instance number into the buffer + */ + u32 instance_no = htonl(pak->tpm_instance); + u32 last_read = pak->last_read; + + to_copy = min_t(size_t, 4 - last_read, numbytes); + + if (copy_to_buffer(&buffer[0], + &(((u8 *) & instance_no)[last_read]), + to_copy, isuserbuffer)) { + return -EFAULT; + } + + pak->last_read += to_copy; + offset += to_copy; + room_left -= to_copy; + } + + /* + * If the packet has a data buffer appended, read from it... + */ + + if (room_left > 0) { + if (pak->data_buffer) { + u32 to_copy = min_t(u32, pak->data_len - offset, room_left); + u32 last_read = pak->last_read - 4; + + if (copy_to_buffer(&buffer[offset], + &pak->data_buffer[last_read], + to_copy, isuserbuffer)) { + return -EFAULT; + } + pak->last_read += to_copy; + offset += to_copy; + } else { + offset = packet_read_shmem(pak, + tpmif, + offset, + buffer, + isuserbuffer, room_left); + } + } + return offset; +} + +static int packet_read_shmem(struct packet *pak, + tpmif_t * tpmif, + u32 offset, char *buffer, int isuserbuffer, + u32 room_left) +{ + u32 last_read = pak->last_read - 4; + u32 i = (last_read / PAGE_SIZE); + u32 pg_offset = last_read & (PAGE_SIZE - 1); + u32 to_copy; + grant_handle_t handle; + + tpmif_tx_request_t *tx; + + tx = &tpmif->tx->ring[0].req; + /* + * Start copying data at the page with index 'index' + * and within that page at offset 'offset'. + * Copy a maximum of 'room_left' bytes. + */ + to_copy = min_t(u32, PAGE_SIZE - pg_offset, room_left); + while (to_copy > 0) { + void *src; + struct gnttab_map_grant_ref map_op; + struct gnttab_unmap_grant_ref unmap_op; + + tx = &tpmif->tx->ring[i].req; + + gnttab_set_map_op(&map_op, MMAP_VADDR(tpmif, i), + GNTMAP_host_map, tx->ref, tpmif->domid); + + if (unlikely(HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, + &map_op, 1))) { + BUG(); + } + + if (map_op.status) { + DPRINTK(" Grant table operation failure !\n"); + return -EFAULT; + } + + handle = map_op.handle; + + if (to_copy > tx->size) { + /* + * User requests more than what's available + */ + to_copy = min_t(u32, tx->size, to_copy); + } + + DPRINTK("Copying from mapped memory at %08lx\n", + (unsigned long)(MMAP_VADDR(tpmif, i) | + (tx->addr & ~PAGE_MASK))); + + src = (void *)(MMAP_VADDR(tpmif, i) | + ((tx->addr & ~PAGE_MASK) + pg_offset)); + if (copy_to_buffer(&buffer[offset], + src, to_copy, isuserbuffer)) { + return -EFAULT; + } + + DPRINTK("Data from TPM-FE of domain %d are %d %d %d %d\n", + tpmif->domid, buffer[offset], buffer[offset + 1], + buffer[offset + 2], buffer[offset + 3]); + + gnttab_set_unmap_op(&unmap_op, MMAP_VADDR(tpmif, i), + GNTMAP_host_map, handle); + + if (unlikely + (HYPERVISOR_grant_table_op + (GNTTABOP_unmap_grant_ref, &unmap_op, 1))) { + BUG(); + } + + offset += to_copy; + pg_offset = 0; + last_read += to_copy; + room_left -= to_copy; + + to_copy = min_t(u32, PAGE_SIZE, room_left); + i++; + } /* while (to_copy > 0) */ + /* + * Adjust the last_read pointer + */ + pak->last_read = last_read + 4; + return offset; +} + +/* ============================================================ + * The file layer for reading data from this device + * ============================================================ + */ +static int vtpm_op_open(struct inode *inode, struct file *f) +{ + int rc = 0; + unsigned long flags; + + write_lock_irqsave(&dataex.pak_lock, flags); + if (dataex.has_opener == 0) { + dataex.has_opener = 1; + } else { + rc = -EPERM; + } + write_unlock_irqrestore(&dataex.pak_lock, flags); + return rc; +} + +static ssize_t vtpm_op_read(struct file *file, + char __user * data, size_t size, loff_t * offset) +{ + int ret_size = -ENODATA; + struct packet *pak = NULL; + unsigned long flags; + + write_lock_irqsave(&dataex.pak_lock, flags); + if (dataex.aborted) { + dataex.aborted = 0; + dataex.copied_so_far = 0; + write_unlock_irqrestore(&dataex.pak_lock, flags); + return -EIO; + } + + if (list_empty(&dataex.pending_pak)) { + write_unlock_irqrestore(&dataex.pak_lock, flags); + wait_event_interruptible(dataex.wait_queue, + !list_empty(&dataex.pending_pak)); + write_lock_irqsave(&dataex.pak_lock, flags); + dataex.copied_so_far = 0; + } + + if (!list_empty(&dataex.pending_pak)) { + unsigned int left; + + pak = list_entry(dataex.pending_pak.next, struct packet, next); + left = pak->data_len - dataex.copied_so_far; + list_del(&pak->next); + write_unlock_irqrestore(&dataex.pak_lock, flags); + + DPRINTK("size given by app: %d, available: %d\n", size, left); + + ret_size = min_t(size_t, size, left); + + ret_size = packet_read(pak, ret_size, data, size, 1); + + write_lock_irqsave(&dataex.pak_lock, flags); + + if (ret_size < 0) { + del_singleshot_timer_sync(&pak->processing_timer); + packet_free(pak); + dataex.copied_so_far = 0; + } else { + DPRINTK("Copied %d bytes to user buffer\n", ret_size); + + dataex.copied_so_far += ret_size; + if (dataex.copied_so_far >= pak->data_len + 4) { + DPRINTK("All data from this packet given to app.\n"); + /* All data given to app */ + + del_singleshot_timer_sync(&pak-> + processing_timer); + list_add_tail(&pak->next, &dataex.current_pak); + /* + * The more fontends that are handled at the same time, + * the more time we give the TPM to process the request. + */ + mod_timer(&pak->processing_timer, + jiffies + (num_frontends * 60 * HZ)); + dataex.copied_so_far = 0; + } else { + list_add(&pak->next, &dataex.pending_pak); + } + } + } + write_unlock_irqrestore(&dataex.pak_lock, flags); + + DPRINTK("Returning result from read to app: %d\n", ret_size); + + return ret_size; +} + +/* + * Write operation - only works after a previous read operation! + */ +static ssize_t vtpm_op_write(struct file *file, + const char __user * data, size_t size, + loff_t * offset) +{ + struct packet *pak; + int rc = 0; + unsigned int off = 4; + unsigned long flags; + struct vtpm_resp_hdr vrh; + + /* + * Minimum required packet size is: + * 4 bytes for instance number + * 2 bytes for tag + * 4 bytes for paramSize + * 4 bytes for the ordinal + * sum: 14 bytes + */ + if (size < sizeof (vrh)) + return -EFAULT; + + if (copy_from_user(&vrh, data, sizeof (vrh))) + return -EFAULT; + + /* malformed packet? */ + if ((off + ntohl(vrh.len_no)) != size) + return -EFAULT; + + write_lock_irqsave(&dataex.pak_lock, flags); + pak = packet_find_instance(&dataex.current_pak, + ntohl(vrh.instance_no)); + + if (pak == NULL) { + write_unlock_irqrestore(&dataex.pak_lock, flags); + DPRINTK(KERN_ALERT "No associated packet! (inst=%d)\n", + ntohl(vrh.instance_no)); + return -EFAULT; + } + + del_singleshot_timer_sync(&pak->processing_timer); + list_del(&pak->next); + + write_unlock_irqrestore(&dataex.pak_lock, flags); + + /* + * The first 'offset' bytes must be the instance number - skip them. + */ + size -= off; + + rc = packet_write(pak, &data[off], size, 1); + + if (rc > 0) { + /* I neglected the first 4 bytes */ + rc += off; + } + packet_free(pak); + return rc; +} + +static int vtpm_op_release(struct inode *inode, struct file *file) +{ + unsigned long flags; + + vtpm_release_packets(NULL, 1); + write_lock_irqsave(&dataex.pak_lock, flags); + dataex.has_opener = 0; + write_unlock_irqrestore(&dataex.pak_lock, flags); + return 0; +} + +static unsigned int vtpm_op_poll(struct file *file, + struct poll_table_struct *pts) +{ + unsigned int flags = POLLOUT | POLLWRNORM; + + poll_wait(file, &dataex.wait_queue, pts); + if (!list_empty(&dataex.pending_pak)) { + flags |= POLLIN | POLLRDNORM; + } + return flags; +} + +static struct file_operations vtpm_ops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = vtpm_op_open, + .read = vtpm_op_read, + .write = vtpm_op_write, + .release = vtpm_op_release, + .poll = vtpm_op_poll, +}; + +static struct miscdevice vtpms_miscdevice = { + .minor = 225, + .name = "vtpm", + .fops = &vtpm_ops, +}; + +/*************************************************************** + Utility functions +***************************************************************/ + +static int tpm_send_fail_message(struct packet *pak, u8 req_tag) +{ + int rc; + static const unsigned char tpm_error_message_fail[] = { + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x00, 0x09 /* TPM_FAIL */ + }; + unsigned char buffer[sizeof (tpm_error_message_fail)]; + + memcpy(buffer, tpm_error_message_fail, + sizeof (tpm_error_message_fail)); + /* + * Insert the right response tag depending on the given tag + * All response tags are '+3' to the request tag. + */ + buffer[1] = req_tag + 3; + + /* + * Write the data to shared memory and notify the front-end + */ + rc = packet_write(pak, buffer, sizeof (buffer), 0); + + return rc; +} + +static int _vtpm_release_packets(struct list_head *head, + tpmif_t * tpmif, int send_msgs) +{ + int aborted = 0; + int c = 0; + struct packet *pak; + struct list_head *pos, *tmp; + + list_for_each_safe(pos, tmp, head) { + pak = list_entry(pos, struct packet, next); + c += 1; + + if (tpmif == NULL || pak->tpmif == tpmif) { + int can_send = 0; + + del_singleshot_timer_sync(&pak->processing_timer); + list_del(&pak->next); + + if (pak->tpmif && pak->tpmif->status == CONNECTED) { + can_send = 1; + } + + if (send_msgs && can_send) { + tpm_send_fail_message(pak, pak->req_tag); + } + packet_free(pak); + if (c == 1) + aborted = 1; + } + } + return aborted; +} + +int vtpm_release_packets(tpmif_t * tpmif, int send_msgs) +{ + unsigned long flags; + + write_lock_irqsave(&dataex.pak_lock, flags); + + dataex.aborted = _vtpm_release_packets(&dataex.pending_pak, + tpmif, + send_msgs); + _vtpm_release_packets(&dataex.current_pak, tpmif, send_msgs); + + write_unlock_irqrestore(&dataex.pak_lock, flags); + return 0; +} + +static int vtpm_queue_packet(struct packet *pak) +{ + int rc = 0; + + if (dataex.has_opener) { + unsigned long flags; + + write_lock_irqsave(&dataex.pak_lock, flags); + list_add_tail(&pak->next, &dataex.pending_pak); + /* give the TPM some time to pick up the request */ + mod_timer(&pak->processing_timer, jiffies + (30 * HZ)); + write_unlock_irqrestore(&dataex.pak_lock, flags); + + wake_up_interruptible(&dataex.wait_queue); + } else { + rc = -EFAULT; + } + return rc; +} + +static int vtpm_receive(tpmif_t * tpmif, u32 size) +{ + int rc = 0; + unsigned char buffer[10]; + __be32 *native_size; + struct packet *pak = packet_alloc(tpmif, size, 0, 0); + + if (!pak) + return -ENOMEM; + /* + * Read 10 bytes from the received buffer to test its + * content for validity. + */ + if (sizeof (buffer) != packet_read(pak, + sizeof (buffer), buffer, + sizeof (buffer), 0)) { + goto failexit; + } + /* + * Reset the packet read pointer so we can read all its + * contents again. + */ + packet_reset(pak); + + native_size = (__force __be32 *) (&buffer[4 + 2]); + /* + * Verify that the size of the packet is correct + * as indicated and that there's actually someone reading packets. + * The minimum size of the packet is '10' for tag, size indicator + * and ordinal. + */ + if (size < 10 || + be32_to_cpu(*native_size) != size || + 0 == dataex.has_opener || tpmif->status != CONNECTED) { + rc = -EINVAL; + goto failexit; + } else { + rc = vtpm_queue_packet(pak); + if (rc < 0) + goto failexit; + } + return 0; + + failexit: + if (pak) { + tpm_send_fail_message(pak, buffer[4 + 1]); + packet_free(pak); + } + return rc; +} + +/* + * Timeout function that gets invoked when a packet has not been processed + * during the timeout period. + * The packet must be on a list when this function is invoked. This + * also means that once its taken off a list, the timer must be + * destroyed as well. + */ +static void processing_timeout(unsigned long ptr) +{ + struct packet *pak = (struct packet *)ptr; + unsigned long flags; + + write_lock_irqsave(&dataex.pak_lock, flags); + /* + * The packet needs to be searched whether it + * is still on the list. + */ + if (pak == packet_find_packet(&dataex.pending_pak, pak) || + pak == packet_find_packet(&dataex.current_pak, pak)) { + if ((pak->flags & PACKET_FLAG_DISCARD_RESPONSE) == 0) { + tpm_send_fail_message(pak, pak->req_tag); + } + /* discard future responses */ + pak->flags |= PACKET_FLAG_DISCARD_RESPONSE; + } + + write_unlock_irqrestore(&dataex.pak_lock, flags); +} + +static void tpm_tx_action(unsigned long unused); +static DECLARE_TASKLET(tpm_tx_tasklet, tpm_tx_action, 0); + +static struct list_head tpm_schedule_list; +static spinlock_t tpm_schedule_list_lock; + +static inline void maybe_schedule_tx_action(void) +{ + smp_mb(); + tasklet_schedule(&tpm_tx_tasklet); +} + +static inline int __on_tpm_schedule_list(tpmif_t * tpmif) +{ + return tpmif->list.next != NULL; +} + +static void remove_from_tpm_schedule_list(tpmif_t * tpmif) +{ + spin_lock_irq(&tpm_schedule_list_lock); + if (likely(__on_tpm_schedule_list(tpmif))) { + list_del(&tpmif->list); + tpmif->list.next = NULL; + tpmif_put(tpmif); + } + spin_unlock_irq(&tpm_schedule_list_lock); +} + +static void add_to_tpm_schedule_list_tail(tpmif_t * tpmif) +{ + if (__on_tpm_schedule_list(tpmif)) + return; + + spin_lock_irq(&tpm_schedule_list_lock); + if (!__on_tpm_schedule_list(tpmif) && tpmif->active) { + list_add_tail(&tpmif->list, &tpm_schedule_list); + tpmif_get(tpmif); + } + spin_unlock_irq(&tpm_schedule_list_lock); +} + +void tpmif_schedule_work(tpmif_t * tpmif) +{ + add_to_tpm_schedule_list_tail(tpmif); + maybe_schedule_tx_action(); +} + +void tpmif_deschedule_work(tpmif_t * tpmif) +{ + remove_from_tpm_schedule_list(tpmif); +} + +static void tpm_tx_action(unsigned long unused) +{ + struct list_head *ent; + tpmif_t *tpmif; + tpmif_tx_request_t *tx; + + DPRINTK("%s: Getting data from front-end(s)!\n", __FUNCTION__); + + while (!list_empty(&tpm_schedule_list)) { + /* Get a tpmif from the list with work to do. */ + ent = tpm_schedule_list.next; + tpmif = list_entry(ent, tpmif_t, list); + tpmif_get(tpmif); + remove_from_tpm_schedule_list(tpmif); + + tx = &tpmif->tx->ring[0].req; + + /* pass it up */ + vtpm_receive(tpmif, tx->size); + + tpmif_put(tpmif); + } +} + +irqreturn_t tpmif_be_int(int irq, void *dev_id, struct pt_regs *regs) +{ + tpmif_t *tpmif = (tpmif_t *) dev_id; + + add_to_tpm_schedule_list_tail(tpmif); + maybe_schedule_tx_action(); + return IRQ_HANDLED; +} + +static int __init tpmback_init(void) +{ + int rc; + + if ((rc = misc_register(&vtpms_miscdevice)) != 0) { + printk(KERN_ALERT + "Could not register misc device for TPM BE.\n"); + return rc; + } + + dataex_init(&dataex); + + spin_lock_init(&tpm_schedule_list_lock); + INIT_LIST_HEAD(&tpm_schedule_list); + + tpmif_interface_init(); + tpmif_xenbus_init(); + + printk(KERN_ALERT "Successfully initialized TPM backend driver.\n"); + + return 0; +} + +module_init(tpmback_init); + +void __exit tpmback_exit(void) +{ + vtpm_release_packets(NULL, 0); + tpmif_xenbus_exit(); + tpmif_interface_exit(); + misc_deregister(&vtpms_miscdevice); +} + +MODULE_LICENSE("Dual BSD/GPL");