xref: /illumos-gate/usr/src/uts/common/xen/os/gnttab.c (revision 843e1988)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * gnttab.c
31  *
32  * Granting foreign access to our memory reservation.
33  *
34  * Copyright (c) 2005, Christopher Clark
35  * Copyright (c) 2004-2005, K A Fraser
36  *
37  * This file may be distributed separately from the Linux kernel, or
38  * incorporated into other software packages, subject to the following license:
39  *
40  * Permission is hereby granted, free of charge, to any person obtaining a copy
41  * of this source file (the "Software"), to deal in the Software without
42  * restriction, including without limitation the rights to use, copy, modify,
43  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
44  * and to permit persons to whom the Software is furnished to do so, subject to
45  * the following conditions:
46  *
47  * The above copyright notice and this permission notice shall be included in
48  * all copies or substantial portions of the Software.
49  *
50  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
51  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
52  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
53  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
54  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
55  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
56  * IN THE SOFTWARE.
57  */
58 
59 #include <sys/types.h>
60 #include <sys/archsystm.h>
61 #include <sys/hypervisor.h>
62 #include <sys/gnttab.h>
63 #include <sys/sysmacros.h>
64 #include <sys/machsystm.h>
65 #include <sys/systm.h>
66 #include <sys/mutex.h>
67 #include <sys/atomic.h>
68 #include <sys/spl.h>
69 #include <sys/condvar.h>
70 #include <sys/cpuvar.h>
71 #include <sys/taskq.h>
72 #include <sys/panic.h>
73 #include <sys/cmn_err.h>
74 #include <sys/promif.h>
75 #include <sys/cpu.h>
76 #include <sys/vmem.h>
77 #include <vm/hat_i86.h>
78 #include <sys/bootconf.h>
79 #include <sys/bootsvcs.h>
80 #include <sys/bootinfo.h>
81 #include <sys/multiboot.h>
82 #include <sys/bootvfs.h>
83 #include <sys/bootprops.h>
84 #include <vm/kboot_mmu.h>
85 #include <vm/seg_kmem.h>
86 
87 #define	cmpxchg(t, c, n) atomic_cas_16((t), (c), (n))
88 
89 /* External tools reserve first few grant table entries. */
90 #define	NR_RESERVED_ENTRIES 8
91 
92 #define	NR_GRANT_ENTRIES (NR_GRANT_FRAMES * \
93 	    MMU_PAGESIZE / sizeof (grant_entry_t))
94 #define	GNTTAB_LIST_END (NR_GRANT_ENTRIES + 1)
95 #define	VALID_GRANT_REF(r) ((r) < NR_GRANT_ENTRIES)
96 
97 static grant_ref_t gnttab_list[NR_GRANT_ENTRIES];
98 static int gnttab_free_count;
99 static grant_ref_t gnttab_free_head;
100 static kmutex_t gnttab_list_lock;
101 
102 static grant_entry_t *shared;
103 #define	GT_PGADDR(i) ((uintptr_t)shared + ((i) << PAGESHIFT))
104 
105 static struct gnttab_free_callback *gnttab_free_callback_list = NULL;
106 
107 static int
108 get_free_entries(int count)
109 {
110 	int ref;
111 	grant_ref_t head;
112 
113 	mutex_enter(&gnttab_list_lock);
114 	if (gnttab_free_count < count) {
115 		mutex_exit(&gnttab_list_lock);
116 		return (-1);
117 	}
118 	ref = head = gnttab_free_head;
119 	gnttab_free_count -= count;
120 	while (count-- > 1)
121 		head = gnttab_list[head];
122 	gnttab_free_head = gnttab_list[head];
123 	gnttab_list[head] = GNTTAB_LIST_END;
124 	mutex_exit(&gnttab_list_lock);
125 	return (ref);
126 }
127 
128 #define	get_free_entry() get_free_entries(1)
129 
130 static void
131 do_free_callbacks(void)
132 {
133 	struct gnttab_free_callback *callback, *next;
134 
135 	callback = gnttab_free_callback_list;
136 	gnttab_free_callback_list = NULL;
137 
138 	while (callback != NULL) {
139 		next = callback->next;
140 		if (gnttab_free_count >= callback->count) {
141 			callback->next = NULL;
142 			callback->fn(callback->arg);
143 		} else {
144 			callback->next = gnttab_free_callback_list;
145 			gnttab_free_callback_list = callback;
146 		}
147 		callback = next;
148 	}
149 }
150 
151 static void
152 check_free_callbacks(void)
153 {
154 	if (gnttab_free_callback_list)
155 		do_free_callbacks();
156 }
157 
158 static void
159 put_free_entry(grant_ref_t ref)
160 {
161 	ASSERT(VALID_GRANT_REF(ref));
162 
163 	mutex_enter(&gnttab_list_lock);
164 	gnttab_list[ref] = gnttab_free_head;
165 	gnttab_free_head = ref;
166 	gnttab_free_count++;
167 	check_free_callbacks();
168 	mutex_exit(&gnttab_list_lock);
169 }
170 
171 /*
172  * Public grant-issuing interface functions
173  */
174 
175 int
176 gnttab_grant_foreign_access(domid_t domid, gnttab_frame_t frame, int readonly)
177 {
178 	int ref;
179 
180 	if ((ref = get_free_entry()) == -1)
181 		return (-1);
182 
183 	ASSERT(VALID_GRANT_REF(ref));
184 
185 	shared[ref].frame = frame;
186 	shared[ref].domid = domid;
187 	membar_producer();
188 	shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
189 
190 	return (ref);
191 }
192 
193 void
194 gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid,
195 				gnttab_frame_t frame, int readonly)
196 {
197 	ASSERT(VALID_GRANT_REF(ref));
198 
199 	shared[ref].frame = frame;
200 	shared[ref].domid = domid;
201 	membar_producer();
202 	shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
203 }
204 
205 
206 int
207 gnttab_query_foreign_access(grant_ref_t ref)
208 {
209 	uint16_t nflags;
210 
211 	ASSERT(VALID_GRANT_REF(ref));
212 
213 	nflags = shared[ref].flags;
214 
215 	return (nflags & (GTF_reading|GTF_writing));
216 }
217 
218 /* ARGSUSED */
219 int
220 gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
221 {
222 	uint16_t flags, nflags;
223 
224 	ASSERT(VALID_GRANT_REF(ref));
225 
226 	nflags = shared[ref].flags;
227 	do {
228 		if ((flags = nflags) & (GTF_reading|GTF_writing)) {
229 			cmn_err(CE_WARN, "g.e. still in use!");
230 			return (0);
231 		}
232 	} while ((nflags = cmpxchg(&shared[ref].flags, flags, 0)) != flags);
233 
234 	return (1);
235 }
236 
237 void
238 gnttab_end_foreign_access(grant_ref_t ref, int readonly, gnttab_frame_t page)
239 {
240 	ASSERT(VALID_GRANT_REF(ref));
241 
242 	if (gnttab_end_foreign_access_ref(ref, readonly)) {
243 		put_free_entry(ref);
244 		/*
245 		 * XXPV - we don't support freeing a page here
246 		 */
247 		if (page != 0) {
248 			cmn_err(CE_WARN,
249 	"gnttab_end_foreign_access_ref: using unsupported free_page interface");
250 			/* free_page(page); */
251 		}
252 	} else {
253 		/*
254 		 * XXX This needs to be fixed so that the ref and page are
255 		 * placed on a list to be freed up later.
256 		 */
257 		cmn_err(CE_WARN, "leaking g.e. and page still in use!");
258 	}
259 }
260 
261 int
262 gnttab_grant_foreign_transfer(domid_t domid)
263 {
264 	int ref;
265 
266 	if ((ref = get_free_entry()) == -1)
267 		return (-1);
268 
269 	ASSERT(VALID_GRANT_REF(ref));
270 
271 	shared[ref].frame = 0;
272 	shared[ref].domid = domid;
273 	membar_producer();
274 	shared[ref].flags = GTF_accept_transfer;
275 
276 	return (ref);
277 }
278 
279 void
280 gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid)
281 {
282 	ASSERT(VALID_GRANT_REF(ref));
283 
284 	shared[ref].frame = 0;
285 	shared[ref].domid = domid;
286 	membar_producer();
287 	shared[ref].flags = GTF_accept_transfer;
288 }
289 
290 gnttab_frame_t
291 gnttab_end_foreign_transfer_ref(grant_ref_t ref)
292 {
293 	gnttab_frame_t frame;
294 	uint16_t flags;
295 
296 	ASSERT(VALID_GRANT_REF(ref));
297 
298 	/*
299 	 * If a transfer is not even yet started, try to reclaim the grant
300 	 * reference and return failure (== 0).
301 	 */
302 	while (!((flags = shared[ref].flags) & GTF_transfer_committed)) {
303 		if (cmpxchg(&shared[ref].flags, flags, 0) == flags)
304 			return (0);
305 		(void) HYPERVISOR_yield();
306 	}
307 
308 	/* If a transfer is in progress then wait until it is completed. */
309 	while (!(flags & GTF_transfer_completed)) {
310 		flags = shared[ref].flags;
311 		(void) HYPERVISOR_yield();
312 	}
313 
314 	/* Read the frame number /after/ reading completion status. */
315 	membar_consumer();
316 	frame = shared[ref].frame;
317 	ASSERT(frame != 0);
318 
319 	return (frame);
320 }
321 
322 gnttab_frame_t
323 gnttab_end_foreign_transfer(grant_ref_t ref)
324 {
325 	gnttab_frame_t frame;
326 
327 	ASSERT(VALID_GRANT_REF(ref));
328 
329 	frame = gnttab_end_foreign_transfer_ref(ref);
330 	put_free_entry(ref);
331 	return (frame);
332 }
333 
334 void
335 gnttab_free_grant_reference(grant_ref_t ref)
336 {
337 	ASSERT(VALID_GRANT_REF(ref));
338 
339 	put_free_entry(ref);
340 }
341 
342 void
343 gnttab_free_grant_references(grant_ref_t head)
344 {
345 	grant_ref_t ref;
346 	int count = 1;
347 
348 	if (head == GNTTAB_LIST_END)
349 		return;
350 	mutex_enter(&gnttab_list_lock);
351 	ref = head;
352 	while (gnttab_list[ref] != GNTTAB_LIST_END) {
353 		ref = gnttab_list[ref];
354 		count++;
355 	}
356 	gnttab_list[ref] = gnttab_free_head;
357 	gnttab_free_head = head;
358 	gnttab_free_count += count;
359 	check_free_callbacks();
360 	mutex_exit(&gnttab_list_lock);
361 }
362 
363 int
364 gnttab_alloc_grant_references(uint16_t count, grant_ref_t *head)
365 {
366 	int h = get_free_entries(count);
367 
368 	if (h == -1)
369 		return (-1);
370 
371 	*head = h;
372 
373 	return (0);
374 }
375 
376 int
377 gnttab_claim_grant_reference(grant_ref_t *private_head)
378 {
379 	grant_ref_t g = *private_head;
380 
381 	if (g == GNTTAB_LIST_END)
382 		return (-1);
383 	*private_head = gnttab_list[g];
384 	return (g);
385 }
386 
387 void
388 gnttab_release_grant_reference(grant_ref_t *private_head, grant_ref_t release)
389 {
390 	ASSERT(VALID_GRANT_REF(release));
391 
392 	gnttab_list[release] = *private_head;
393 	*private_head = release;
394 }
395 
396 void
397 gnttab_request_free_callback(struct gnttab_free_callback *callback,
398 	void (*fn)(void *), void *arg, uint16_t count)
399 {
400 	mutex_enter(&gnttab_list_lock);
401 	if (callback->next)
402 		goto out;
403 	callback->fn = fn;
404 	callback->arg = arg;
405 	callback->count = count;
406 	callback->next = gnttab_free_callback_list;
407 	gnttab_free_callback_list = callback;
408 	check_free_callbacks();
409 out:
410 	mutex_exit(&gnttab_list_lock);
411 }
412 
413 void
414 gnttab_init(void)
415 {
416 	gnttab_setup_table_t set;
417 	gnttab_frame_t frames[NR_GRANT_FRAMES];
418 	int i;
419 
420 	set.dom = DOMID_SELF;
421 	set.nr_frames = NR_GRANT_FRAMES;
422 	/*LINTED: constant in conditional context*/
423 	set_xen_guest_handle(set.frame_list, frames);
424 
425 	/*
426 	 * Take 4 pages of grant table space from the hypervisor and map it
427 	 */
428 	if ((HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &set, 1) != 0) ||
429 	    (set.status != 0)) {
430 		cmn_err(CE_PANIC, "Grant Table setup failed");
431 	}
432 
433 	shared = vmem_xalloc(heap_arena, NR_GRANT_FRAMES * MMU_PAGESIZE,
434 	    MMU_PAGESIZE, 0, 0, 0, 0, VM_SLEEP);
435 
436 	for (i = 0; i < NR_GRANT_FRAMES; i++)
437 		kbm_map_ma(FRAME_TO_MA(frames[i]), GT_PGADDR(i), 0);
438 
439 	for (i = NR_RESERVED_ENTRIES; i < NR_GRANT_ENTRIES; i++)
440 		gnttab_list[i] = i + 1;
441 	gnttab_free_count = NR_GRANT_ENTRIES - NR_RESERVED_ENTRIES;
442 	gnttab_free_head  = NR_RESERVED_ENTRIES;
443 
444 	mutex_init(&gnttab_list_lock, NULL, MUTEX_DEFAULT, NULL);
445 }
446 
447 void
448 gnttab_resume(void)
449 {
450 	gnttab_setup_table_t set;
451 	gnttab_frame_t frames[NR_GRANT_FRAMES];
452 	int i;
453 
454 	set.dom = DOMID_SELF;
455 	set.nr_frames = NR_GRANT_FRAMES;
456 	/*LINTED: constant in conditional context*/
457 	set_xen_guest_handle(set.frame_list, frames);
458 
459 	/*
460 	 * Take NR_GRANT_FRAMES pages of grant table space from the
461 	 * hypervisor and map it
462 	 */
463 	if ((HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &set, 1) != 0) ||
464 	    (set.status != 0)) {
465 		cmn_err(CE_PANIC, "Grant Table setup failed");
466 	}
467 
468 	for (i = 0; i < NR_GRANT_FRAMES; i++) {
469 		(void) HYPERVISOR_update_va_mapping(GT_PGADDR(i),
470 		    FRAME_TO_MA(frames[i]) | PT_VALID | PT_WRITABLE,
471 		    UVMF_INVLPG | UVMF_ALL);
472 	}
473 }
474 
475 void
476 gnttab_suspend(void)
477 {
478 	int i;
479 
480 	/*
481 	 * clear grant table mappings before suspending
482 	 */
483 	for (i = 0; i < NR_GRANT_FRAMES; i++) {
484 		(void) HYPERVISOR_update_va_mapping(GT_PGADDR(i),
485 		    0, UVMF_INVLPG);
486 	}
487 }
488 
489 /*
490  * Local variables:
491  *  c-file-style: "solaris"
492  *  indent-tabs-mode: t
493  *  c-indent-level: 8
494  *  c-basic-offset: 8
495  *  tab-width: 8
496  * End:
497  */
498