- supported.conf: Added sparse_keymap (eeepc_laptop depends on it)
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / tpmback / interface.c
1  /*****************************************************************************
2  * drivers/xen/tpmback/interface.c
3  *
4  * Vritual TPM interface management.
5  *
6  * Copyright (c) 2005, IBM Corporation
7  *
8  * Author: Stefan Berger, stefanb@us.ibm.com
9  *
10  * This code has been derived from drivers/xen/netback/interface.c
11  * Copyright (c) 2004, Keir Fraser
12  */
13
14 #include "common.h"
15 #include <linux/delay.h>
16 #include <linux/err.h>
17 #include <xen/balloon.h>
18 #include <xen/gnttab.h>
19
20 static struct kmem_cache *tpmif_cachep;
21 int num_frontends = 0;
22
23 LIST_HEAD(tpmif_list);
24
25 static tpmif_t *alloc_tpmif(domid_t domid, struct backend_info *bi)
26 {
27         tpmif_t *tpmif;
28
29         tpmif = kmem_cache_alloc(tpmif_cachep, GFP_KERNEL);
30         if (tpmif == NULL)
31                 goto out_of_memory;
32
33         memset(tpmif, 0, sizeof (*tpmif));
34         tpmif->domid = domid;
35         tpmif->status = DISCONNECTED;
36         tpmif->bi = bi;
37         snprintf(tpmif->devname, sizeof(tpmif->devname), "tpmif%d", domid);
38         atomic_set(&tpmif->refcnt, 1);
39
40         tpmif->mmap_pages = alloc_empty_pages_and_pagevec(TPMIF_TX_RING_SIZE);
41         if (tpmif->mmap_pages == NULL)
42                 goto out_of_memory;
43
44         list_add(&tpmif->tpmif_list, &tpmif_list);
45         num_frontends++;
46
47         return tpmif;
48
49  out_of_memory:
50         if (tpmif != NULL)
51                 kmem_cache_free(tpmif_cachep, tpmif);
52         printk("%s: out of memory\n", __FUNCTION__);
53         return ERR_PTR(-ENOMEM);
54 }
55
56 static void free_tpmif(tpmif_t * tpmif)
57 {
58         num_frontends--;
59         list_del(&tpmif->tpmif_list);
60         free_empty_pages_and_pagevec(tpmif->mmap_pages, TPMIF_TX_RING_SIZE);
61         kmem_cache_free(tpmif_cachep, tpmif);
62 }
63
64 tpmif_t *tpmif_find(domid_t domid, struct backend_info *bi)
65 {
66         tpmif_t *tpmif;
67
68         list_for_each_entry(tpmif, &tpmif_list, tpmif_list) {
69                 if (tpmif->bi == bi) {
70                         if (tpmif->domid == domid) {
71                                 tpmif_get(tpmif);
72                                 return tpmif;
73                         } else {
74                                 return ERR_PTR(-EEXIST);
75                         }
76                 }
77         }
78
79         return alloc_tpmif(domid, bi);
80 }
81
82 static int map_frontend_page(tpmif_t *tpmif, unsigned long shared_page)
83 {
84         struct gnttab_map_grant_ref op;
85
86         gnttab_set_map_op(&op, (unsigned long)tpmif->tx_area->addr,
87                           GNTMAP_host_map, shared_page, tpmif->domid);
88
89     do {
90             if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1))
91                     BUG();
92         msleep(10);
93     } while(op.status == GNTST_eagain);
94
95         if (op.status) {
96                 DPRINTK(" Grant table operation failure !\n");
97                 return op.status;
98         }
99
100         tpmif->shmem_ref = shared_page;
101         tpmif->shmem_handle = op.handle;
102
103         return 0;
104 }
105
106 static void unmap_frontend_page(tpmif_t *tpmif)
107 {
108         struct gnttab_unmap_grant_ref op;
109
110         gnttab_set_unmap_op(&op, (unsigned long)tpmif->tx_area->addr,
111                             GNTMAP_host_map, tpmif->shmem_handle);
112
113         if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))
114                 BUG();
115 }
116
117 int tpmif_map(tpmif_t *tpmif, unsigned long shared_page, unsigned int evtchn)
118 {
119         int err;
120
121         if (tpmif->irq)
122                 return 0;
123
124         if ((tpmif->tx_area = alloc_vm_area(PAGE_SIZE)) == NULL)
125                 return -ENOMEM;
126
127         err = map_frontend_page(tpmif, shared_page);
128         if (err) {
129                 free_vm_area(tpmif->tx_area);
130                 return err;
131         }
132
133         tpmif->tx = (tpmif_tx_interface_t *)tpmif->tx_area->addr;
134         memset(tpmif->tx, 0, PAGE_SIZE);
135
136         err = bind_interdomain_evtchn_to_irqhandler(
137                 tpmif->domid, evtchn, tpmif_be_int, 0, tpmif->devname, tpmif);
138         if (err < 0) {
139                 unmap_frontend_page(tpmif);
140                 free_vm_area(tpmif->tx_area);
141                 return err;
142         }
143         tpmif->irq = err;
144
145         tpmif->shmem_ref = shared_page;
146         tpmif->active = 1;
147
148         return 0;
149 }
150
151 void tpmif_disconnect_complete(tpmif_t *tpmif)
152 {
153         if (tpmif->irq)
154                 unbind_from_irqhandler(tpmif->irq, tpmif);
155
156         if (tpmif->tx) {
157                 unmap_frontend_page(tpmif);
158                 free_vm_area(tpmif->tx_area);
159         }
160
161         free_tpmif(tpmif);
162 }
163
164 int __init tpmif_interface_init(void)
165 {
166         tpmif_cachep = kmem_cache_create("tpmif_cache", sizeof (tpmif_t),
167                                          0, 0, NULL);
168         return tpmif_cachep ? 0 : -ENOMEM;
169 }
170
171 void tpmif_interface_exit(void)
172 {
173         kmem_cache_destroy(tpmif_cachep);
174 }