This commit was manufactured by cvs2svn to create branch 'vserver'.
[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)
29                 return ERR_PTR(-ENOMEM);
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->pagerange = balloon_alloc_empty_page_range(TPMIF_TX_RING_SIZE);
39         BUG_ON(tpmif->pagerange == NULL);
40         tpmif->mmap_vstart = (unsigned long)pfn_to_kaddr(
41                                             page_to_pfn(tpmif->pagerange));
42
43         list_add(&tpmif->tpmif_list, &tpmif_list);
44         num_frontends++;
45
46         return tpmif;
47 }
48
49 static void free_tpmif(tpmif_t * tpmif)
50 {
51         num_frontends--;
52         list_del(&tpmif->tpmif_list);
53         balloon_dealloc_empty_page_range(tpmif->pagerange, TPMIF_TX_RING_SIZE);
54         kmem_cache_free(tpmif_cachep, tpmif);
55 }
56
57 tpmif_t *tpmif_find(domid_t domid, struct backend_info *bi)
58 {
59         tpmif_t *tpmif;
60
61         list_for_each_entry(tpmif, &tpmif_list, tpmif_list) {
62                 if (tpmif->bi == bi) {
63                         if (tpmif->domid == domid) {
64                                 tpmif_get(tpmif);
65                                 return tpmif;
66                         } else {
67                                 return ERR_PTR(-EEXIST);
68                         }
69                 }
70         }
71
72         return alloc_tpmif(domid, bi);
73 }
74
75 static int map_frontend_page(tpmif_t *tpmif, unsigned long shared_page)
76 {
77         int ret;
78         struct gnttab_map_grant_ref op;
79
80         gnttab_set_map_op(&op, (unsigned long)tpmif->tx_area->addr,
81                           GNTMAP_host_map, shared_page, tpmif->domid);
82
83         lock_vm_area(tpmif->tx_area);
84         ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1);
85         unlock_vm_area(tpmif->tx_area);
86         BUG_ON(ret);
87
88         if (op.status) {
89                 DPRINTK(" Grant table operation failure !\n");
90                 return op.status;
91         }
92
93         tpmif->shmem_ref = shared_page;
94         tpmif->shmem_handle = op.handle;
95
96         return 0;
97 }
98
99 static void unmap_frontend_page(tpmif_t *tpmif)
100 {
101         struct gnttab_unmap_grant_ref op;
102         int ret;
103
104         gnttab_set_unmap_op(&op, (unsigned long)tpmif->tx_area->addr,
105                             GNTMAP_host_map, tpmif->shmem_handle);
106
107         lock_vm_area(tpmif->tx_area);
108         ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1);
109         unlock_vm_area(tpmif->tx_area);
110         BUG_ON(ret);
111 }
112
113 int tpmif_map(tpmif_t *tpmif, unsigned long shared_page, unsigned int evtchn)
114 {
115         int err;
116         struct evtchn_bind_interdomain bind_interdomain;
117
118         if (tpmif->irq) {
119                 return 0;
120         }
121
122         if ((tpmif->tx_area = alloc_vm_area(PAGE_SIZE)) == NULL)
123                 return -ENOMEM;
124
125         err = map_frontend_page(tpmif, shared_page);
126         if (err) {
127                 free_vm_area(tpmif->tx_area);
128                 return err;
129         }
130
131
132         bind_interdomain.remote_dom  = tpmif->domid;
133         bind_interdomain.remote_port = evtchn;
134
135         err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain,
136                                           &bind_interdomain);
137         if (err) {
138                 unmap_frontend_page(tpmif);
139                 free_vm_area(tpmif->tx_area);
140                 return err;
141         }
142
143         tpmif->evtchn = bind_interdomain.local_port;
144
145         tpmif->tx = (tpmif_tx_interface_t *)tpmif->tx_area->addr;
146
147         tpmif->irq = bind_evtchn_to_irqhandler(
148                 tpmif->evtchn, tpmif_be_int, 0, tpmif->devname, tpmif);
149         tpmif->shmem_ref = shared_page;
150         tpmif->active = 1;
151
152         return 0;
153 }
154
155 void tpmif_disconnect_complete(tpmif_t *tpmif)
156 {
157         if (tpmif->irq)
158                 unbind_from_irqhandler(tpmif->irq, tpmif);
159
160         if (tpmif->tx) {
161                 unmap_frontend_page(tpmif);
162                 free_vm_area(tpmif->tx_area);
163         }
164
165         free_tpmif(tpmif);
166 }
167
168 void __init tpmif_interface_init(void)
169 {
170         tpmif_cachep = kmem_cache_create("tpmif_cache", sizeof (tpmif_t),
171                                          0, 0, NULL, NULL);
172 }
173
174 void __exit tpmif_interface_exit(void)
175 {
176         kmem_cache_destroy(tpmif_cachep);
177 }