Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.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 <xen/balloon.h>
16 #include <xen/gnttab.h>
17
18 static kmem_cache_t *tpmif_cachep;
19 int num_frontends = 0;
20
21 LIST_HEAD(tpmif_list);
22
23 static tpmif_t *alloc_tpmif(domid_t domid, struct backend_info *bi)
24 {
25         tpmif_t *tpmif;
26
27         tpmif = kmem_cache_alloc(tpmif_cachep, GFP_KERNEL);
28         if (tpmif == NULL)
29                 goto out_of_memory;
30
31         memset(tpmif, 0, sizeof (*tpmif));
32         tpmif->domid = domid;
33         tpmif->status = DISCONNECTED;
34         tpmif->bi = bi;
35         snprintf(tpmif->devname, sizeof(tpmif->devname), "tpmif%d", domid);
36         atomic_set(&tpmif->refcnt, 1);
37
38         tpmif->mmap_pages = alloc_empty_pages_and_pagevec(TPMIF_TX_RING_SIZE);
39         if (tpmif->mmap_pages == NULL)
40                 goto out_of_memory;
41
42         list_add(&tpmif->tpmif_list, &tpmif_list);
43         num_frontends++;
44
45         return tpmif;
46
47  out_of_memory:
48         if (tpmif != NULL)
49                 kmem_cache_free(tpmif_cachep, tpmif);
50         printk("%s: out of memory\n", __FUNCTION__);
51         return ERR_PTR(-ENOMEM);
52 }
53
54 static void free_tpmif(tpmif_t * tpmif)
55 {
56         num_frontends--;
57         list_del(&tpmif->tpmif_list);
58         free_empty_pages_and_pagevec(tpmif->mmap_pages, TPMIF_TX_RING_SIZE);
59         kmem_cache_free(tpmif_cachep, tpmif);
60 }
61
62 tpmif_t *tpmif_find(domid_t domid, struct backend_info *bi)
63 {
64         tpmif_t *tpmif;
65
66         list_for_each_entry(tpmif, &tpmif_list, tpmif_list) {
67                 if (tpmif->bi == bi) {
68                         if (tpmif->domid == domid) {
69                                 tpmif_get(tpmif);
70                                 return tpmif;
71                         } else {
72                                 return ERR_PTR(-EEXIST);
73                         }
74                 }
75         }
76
77         return alloc_tpmif(domid, bi);
78 }
79
80 static int map_frontend_page(tpmif_t *tpmif, unsigned long shared_page)
81 {
82         int ret;
83         struct gnttab_map_grant_ref op;
84
85         gnttab_set_map_op(&op, (unsigned long)tpmif->tx_area->addr,
86                           GNTMAP_host_map, shared_page, tpmif->domid);
87
88         lock_vm_area(tpmif->tx_area);
89         ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1);
90         unlock_vm_area(tpmif->tx_area);
91         BUG_ON(ret);
92
93         if (op.status) {
94                 DPRINTK(" Grant table operation failure !\n");
95                 return op.status;
96         }
97
98         tpmif->shmem_ref = shared_page;
99         tpmif->shmem_handle = op.handle;
100
101         return 0;
102 }
103
104 static void unmap_frontend_page(tpmif_t *tpmif)
105 {
106         struct gnttab_unmap_grant_ref op;
107         int ret;
108
109         gnttab_set_unmap_op(&op, (unsigned long)tpmif->tx_area->addr,
110                             GNTMAP_host_map, tpmif->shmem_handle);
111
112         lock_vm_area(tpmif->tx_area);
113         ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1);
114         unlock_vm_area(tpmif->tx_area);
115         BUG_ON(ret);
116 }
117
118 int tpmif_map(tpmif_t *tpmif, unsigned long shared_page, unsigned int evtchn)
119 {
120         int err;
121         struct evtchn_bind_interdomain bind_interdomain;
122
123         if (tpmif->irq) {
124                 return 0;
125         }
126
127         if ((tpmif->tx_area = alloc_vm_area(PAGE_SIZE)) == NULL)
128                 return -ENOMEM;
129
130         err = map_frontend_page(tpmif, shared_page);
131         if (err) {
132                 free_vm_area(tpmif->tx_area);
133                 return err;
134         }
135
136
137         bind_interdomain.remote_dom  = tpmif->domid;
138         bind_interdomain.remote_port = evtchn;
139
140         err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain,
141                                           &bind_interdomain);
142         if (err) {
143                 unmap_frontend_page(tpmif);
144                 free_vm_area(tpmif->tx_area);
145                 return err;
146         }
147
148         tpmif->evtchn = bind_interdomain.local_port;
149
150         tpmif->tx = (tpmif_tx_interface_t *)tpmif->tx_area->addr;
151
152         tpmif->irq = bind_evtchn_to_irqhandler(
153                 tpmif->evtchn, tpmif_be_int, 0, tpmif->devname, tpmif);
154         tpmif->shmem_ref = shared_page;
155         tpmif->active = 1;
156
157         return 0;
158 }
159
160 void tpmif_disconnect_complete(tpmif_t *tpmif)
161 {
162         if (tpmif->irq)
163                 unbind_from_irqhandler(tpmif->irq, tpmif);
164
165         if (tpmif->tx) {
166                 unmap_frontend_page(tpmif);
167                 free_vm_area(tpmif->tx_area);
168         }
169
170         free_tpmif(tpmif);
171 }
172
173 void __init tpmif_interface_init(void)
174 {
175         tpmif_cachep = kmem_cache_create("tpmif_cache", sizeof (tpmif_t),
176                                          0, 0, NULL, NULL);
177 }
178
179 void __exit tpmif_interface_exit(void)
180 {
181         kmem_cache_destroy(tpmif_cachep);
182 }