xref: /netbsd-src/sys/arch/xen/xen/xengnt.c (revision 274254cdae52594c1aa480a736aef78313d15c9c)
1 /*      $NetBSD: xengnt.c,v 1.14 2009/03/16 06:18:32 cegger Exp $      */
2 
3 /*
4  * Copyright (c) 2006 Manuel Bouyer.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by Manuel Bouyer.
17  * 4. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: xengnt.c,v 1.14 2009/03/16 06:18:32 cegger Exp $");
35 
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/malloc.h>
40 #include <sys/queue.h>
41 #include <sys/extent.h>
42 #include <sys/kernel.h>
43 #include <uvm/uvm.h>
44 
45 #include <xen/hypervisor.h>
46 #include <xen/xen.h>
47 #include <xen/granttables.h>
48 
49 /* #define XENDEBUG */
50 #ifdef XENDEBUG
51 #define DPRINTF(x) printf x
52 #else
53 #define DPRINTF(x)
54 #endif
55 
56 #define NR_GRANT_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(grant_entry_t))
57 
58 /* Current number of frames making up the grant table */
59 int gnt_nr_grant_frames;
60 /* Maximum number of frames that can make up the grant table */
61 int gnt_max_grant_frames;
62 
63 /* table of free grant entries */
64 grant_ref_t *gnt_entries;
65 /* last free entry */
66 int last_gnt_entry;
67 /* empty entry in the list */
68 #define XENGNT_NO_ENTRY 0xffffffff
69 
70 /* VM address of the grant table */
71 grant_entry_t *grant_table;
72 
73 static grant_ref_t xengnt_get_entry(void);
74 static void xengnt_free_entry(grant_ref_t);
75 static void xengnt_resume(void);
76 static int xengnt_more_entries(void);
77 
78 void
79 xengnt_init(void)
80 {
81 	struct gnttab_query_size query;
82 	int rc;
83 	int nr_grant_entries;
84 	int i;
85 
86 	query.dom = DOMID_SELF;
87 	rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
88 	if ((rc < 0) || (query.status != GNTST_okay))
89 		gnt_max_grant_frames = 4; /* Legacy max number of frames */
90 	else
91 		gnt_max_grant_frames = query.max_nr_frames;
92 	gnt_nr_grant_frames = 0;
93 
94 	nr_grant_entries =
95 	    gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE;
96 
97 	grant_table = (void *)uvm_km_alloc(kernel_map,
98 	    gnt_max_grant_frames * PAGE_SIZE, 0, UVM_KMF_VAONLY);
99 	if (grant_table == NULL)
100 		panic("xengnt_init() no VM space");
101 	gnt_entries = malloc((nr_grant_entries + 1) * sizeof(grant_ref_t),
102 	    M_DEVBUF, M_NOWAIT);
103 	if (gnt_entries == NULL)
104 		panic("xengnt_init() no space for bitmask");
105 	for (i = 0; i <= nr_grant_entries; i++)
106 		gnt_entries[i] = XENGNT_NO_ENTRY;
107 
108 	last_gnt_entry = 0;
109 	xengnt_resume();
110 
111 }
112 
113 /*
114  * Resume grant table state
115  */
116 static void
117 xengnt_resume(void)
118 {
119 	int previous_nr_grant_frames = gnt_nr_grant_frames;
120 	gnt_nr_grant_frames = 0;
121 	while (gnt_nr_grant_frames < previous_nr_grant_frames) {
122 		if (xengnt_more_entries() != 0)
123 			panic("xengnt_resume: can't restore grant frames");
124 	}
125 }
126 
127 /*
128  * Add another page to the grant table
129  * Returns 0 on success, ENOMEM on failure
130  */
131 static int
132 xengnt_more_entries(void)
133 {
134 	gnttab_setup_table_t setup;
135 	unsigned long *pages;
136 	int nframes_new = gnt_nr_grant_frames + 1;
137 	int i;
138 
139 	if (gnt_nr_grant_frames == gnt_max_grant_frames)
140 		return ENOMEM;
141 
142 	pages = malloc(nframes_new * sizeof(long), M_DEVBUF, M_NOWAIT);
143 	if (pages == NULL)
144 		return ENOMEM;
145 
146 	setup.dom = DOMID_SELF;
147 	setup.nr_frames = nframes_new;
148 	xenguest_handle(setup.frame_list) = pages;
149 
150 	/*
151 	 * setup the grant table, made of nframes_new frames
152 	 * and return the list of their virtual addresses
153 	 * in 'pages'
154 	 */
155 	if (HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1) != 0)
156 		panic("%s: setup table failed", __func__);
157 	if (setup.status != GNTST_okay) {
158 		aprint_error("%s: setup table returned %d\n",
159 		    __func__, setup.status);
160 		free(pages, M_DEVBUF);
161 		return ENOMEM;
162 	}
163 
164 	DPRINTF(("xengnt_more_entries: map 0x%lx -> %p\n",
165 	    pages[gnt_nr_grant_frames],
166 	    (char *)grant_table + gnt_nr_grant_frames * PAGE_SIZE));
167 
168 	/*
169 	 * map between grant_table addresses and the machine addresses of
170 	 * the grant table frames
171 	 */
172 	pmap_kenter_ma(((vaddr_t)grant_table) + gnt_nr_grant_frames * PAGE_SIZE,
173 	    pages[gnt_nr_grant_frames] << PAGE_SHIFT, VM_PROT_WRITE);
174 
175 	/*
176 	 * add the grant entries associated to the last grant table frame
177 	 * and mark them as free
178 	 */
179 	for (i = gnt_nr_grant_frames * NR_GRANT_ENTRIES_PER_PAGE;
180 	    i < nframes_new * NR_GRANT_ENTRIES_PER_PAGE;
181 	    i++) {
182 		KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
183 		gnt_entries[last_gnt_entry] = i;
184 		last_gnt_entry++;
185 	}
186 	gnt_nr_grant_frames = nframes_new;
187 	free(pages, M_DEVBUF);
188 	return 0;
189 }
190 
191 /*
192  * Returns a reference to the first free entry in grant table
193  */
194 static grant_ref_t
195 xengnt_get_entry(void)
196 {
197 	grant_ref_t entry;
198 	int s = splvm();
199 	static struct timeval xengnt_nonmemtime;
200 	static const struct timeval xengnt_nonmemintvl = {5,0};
201 
202 	if (last_gnt_entry == 0) {
203 		if (xengnt_more_entries()) {
204 			splx(s);
205 			if (ratecheck(&xengnt_nonmemtime, &xengnt_nonmemintvl))
206 				printf("xengnt_get_entry: out of grant "
207 				    "table entries\n");
208 			return XENGNT_NO_ENTRY;
209 		}
210 	}
211 	KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
212 	last_gnt_entry--;
213 	entry = gnt_entries[last_gnt_entry];
214 	gnt_entries[last_gnt_entry] = XENGNT_NO_ENTRY;
215 	splx(s);
216 	KASSERT(entry != XENGNT_NO_ENTRY);
217 	KASSERT(last_gnt_entry >= 0 && last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE);
218 	return entry;
219 }
220 
221 /*
222  * Mark the grant table entry as free
223  */
224 static void
225 xengnt_free_entry(grant_ref_t entry)
226 {
227 	int s = splvm();
228 	KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
229 	KASSERT(last_gnt_entry >= 0 && last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE);
230 	gnt_entries[last_gnt_entry] = entry;
231 	last_gnt_entry++;
232 	splx(s);
233 }
234 
235 int
236 xengnt_grant_access(domid_t dom, paddr_t ma, int ro, grant_ref_t *entryp)
237 {
238 	*entryp = xengnt_get_entry();
239 	if (__predict_false(*entryp == XENGNT_NO_ENTRY))
240 		return ENOMEM;
241 
242 	grant_table[*entryp].frame = ma >> PAGE_SHIFT;
243 	grant_table[*entryp].domid = dom;
244 	/*
245 	 * ensure that the above values reach global visibility
246 	 * before permitting frame's access (done when we set flags)
247 	 */
248 	xen_rmb();
249 	grant_table[*entryp].flags =
250 	    GTF_permit_access | (ro ? GTF_readonly : 0);
251 	return 0;
252 }
253 
254 void
255 xengnt_revoke_access(grant_ref_t entry)
256 {
257 	uint16_t flags, nflags;
258 
259 	nflags = grant_table[entry].flags;
260 
261 	do {
262 		if ((flags = nflags) & (GTF_reading|GTF_writing))
263 			panic("xengnt_revoke_access: still in use");
264 		nflags = xen_atomic_cmpxchg16(&grant_table[entry].flags,
265 		    flags, 0);
266 	} while (nflags != flags);
267 	xengnt_free_entry(entry);
268 }
269 
270 int
271 xengnt_grant_transfer(domid_t dom, grant_ref_t *entryp)
272 {
273 	*entryp = xengnt_get_entry();
274 	if (__predict_false(*entryp == XENGNT_NO_ENTRY))
275 		return ENOMEM;
276 
277 	grant_table[*entryp].frame = 0;
278 	grant_table[*entryp].domid = dom;
279 	/*
280 	 * ensure that the above values reach global visibility
281 	 * before permitting frame's transfer (done when we set flags)
282 	 */
283 	xen_rmb();
284 	grant_table[*entryp].flags = GTF_accept_transfer;
285 	return 0;
286 }
287 
288 paddr_t
289 xengnt_revoke_transfer(grant_ref_t entry)
290 {
291 	paddr_t page;
292 	uint16_t flags;
293 
294 	/* if the transfer has not started, free the entry and return 0 */
295 	while (!((flags = grant_table[entry].flags) & GTF_transfer_committed)) {
296 		if (xen_atomic_cmpxchg16(&grant_table[entry].flags,
297 		    flags, 0) == flags ) {
298 			xengnt_free_entry(entry);
299 			return 0;
300 		}
301 		HYPERVISOR_yield();
302 	}
303 
304 	/* If transfer in progress, wait for completion */
305 	while (!((flags = grant_table[entry].flags) & GTF_transfer_completed))
306 		HYPERVISOR_yield();
307 
308 	/* Read the frame number /after/ reading completion status. */
309 	__insn_barrier();
310 	page = grant_table[entry].frame;
311 	if (page == 0)
312 		printf("xengnt_revoke_transfer: guest sent pa 0\n");
313 
314 	xengnt_free_entry(entry);
315 	return page;
316 }
317 
318 int
319 xengnt_status(grant_ref_t entry)
320 {
321 	return (grant_table[entry].flags & (GTF_reading|GTF_writing));
322 }
323