xref: /illumos-gate/usr/src/uts/common/xen/os/gnttab.c (revision 2850d85b7b93f31e578520dc3b3feb24db609c62)
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 #ifdef XPV_HVM_DRIVER
62 #include <sys/xpv_support.h>
63 #include <sys/mman.h>
64 #include <vm/hat.h>
65 #endif
66 #include <sys/hypervisor.h>
67 #include <sys/gnttab.h>
68 #include <sys/sysmacros.h>
69 #include <sys/machsystm.h>
70 #include <sys/systm.h>
71 #include <sys/mutex.h>
72 #include <sys/atomic.h>
73 #include <sys/spl.h>
74 #include <sys/condvar.h>
75 #include <sys/cpuvar.h>
76 #include <sys/taskq.h>
77 #include <sys/panic.h>
78 #include <sys/cmn_err.h>
79 #include <sys/promif.h>
80 #include <sys/cpu.h>
81 #include <sys/vmem.h>
82 #include <vm/hat_i86.h>
83 #include <sys/bootconf.h>
84 #include <sys/bootsvcs.h>
85 #ifndef XPV_HVM_DRIVER
86 #include <sys/bootinfo.h>
87 #include <sys/multiboot.h>
88 #include <vm/kboot_mmu.h>
89 #endif
90 #include <sys/bootvfs.h>
91 #include <sys/bootprops.h>
92 #include <vm/seg_kmem.h>
93 
94 #define	cmpxchg(t, c, n) atomic_cas_16((t), (c), (n))
95 
96 /* External tools reserve first few grant table entries. */
97 #define	NR_RESERVED_ENTRIES 8
98 
99 #define	NR_GRANT_ENTRIES (NR_GRANT_FRAMES * \
100 	    MMU_PAGESIZE / sizeof (grant_entry_t))
101 #define	GNTTAB_LIST_END (NR_GRANT_ENTRIES + 1)
102 #define	VALID_GRANT_REF(r) ((r) < NR_GRANT_ENTRIES)
103 
104 static grant_ref_t gnttab_list[NR_GRANT_ENTRIES];
105 static int gnttab_free_count;
106 static grant_ref_t gnttab_free_head;
107 static kmutex_t gnttab_list_lock;
108 
109 static grant_entry_t *shared;
110 #define	GT_PGADDR(i) ((uintptr_t)shared + ((i) << PAGESHIFT))
111 
112 static struct gnttab_free_callback *gnttab_free_callback_list = NULL;
113 
114 static int
115 get_free_entries(int count)
116 {
117 	int ref;
118 	grant_ref_t head;
119 
120 	mutex_enter(&gnttab_list_lock);
121 	if (gnttab_free_count < count) {
122 		mutex_exit(&gnttab_list_lock);
123 		return (-1);
124 	}
125 	ref = head = gnttab_free_head;
126 	gnttab_free_count -= count;
127 	while (count-- > 1)
128 		head = gnttab_list[head];
129 	gnttab_free_head = gnttab_list[head];
130 	gnttab_list[head] = GNTTAB_LIST_END;
131 	mutex_exit(&gnttab_list_lock);
132 	return (ref);
133 }
134 
135 #define	get_free_entry() get_free_entries(1)
136 
137 static void
138 do_free_callbacks(void)
139 {
140 	struct gnttab_free_callback *callback, *next;
141 
142 	callback = gnttab_free_callback_list;
143 	gnttab_free_callback_list = NULL;
144 
145 	while (callback != NULL) {
146 		next = callback->next;
147 		if (gnttab_free_count >= callback->count) {
148 			callback->next = NULL;
149 			callback->fn(callback->arg);
150 		} else {
151 			callback->next = gnttab_free_callback_list;
152 			gnttab_free_callback_list = callback;
153 		}
154 		callback = next;
155 	}
156 }
157 
158 static void
159 check_free_callbacks(void)
160 {
161 	if (gnttab_free_callback_list)
162 		do_free_callbacks();
163 }
164 
165 static void
166 put_free_entry(grant_ref_t ref)
167 {
168 	ASSERT(VALID_GRANT_REF(ref));
169 
170 	mutex_enter(&gnttab_list_lock);
171 	gnttab_list[ref] = gnttab_free_head;
172 	gnttab_free_head = ref;
173 	gnttab_free_count++;
174 	check_free_callbacks();
175 	mutex_exit(&gnttab_list_lock);
176 }
177 
178 /*
179  * Public grant-issuing interface functions
180  */
181 
182 int
183 gnttab_grant_foreign_access(domid_t domid, gnttab_frame_t frame, int readonly)
184 {
185 	int ref;
186 
187 	if ((ref = get_free_entry()) == -1)
188 		return (-1);
189 
190 	ASSERT(VALID_GRANT_REF(ref));
191 
192 	shared[ref].frame = frame;
193 	shared[ref].domid = domid;
194 	membar_producer();
195 	shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
196 
197 	return (ref);
198 }
199 
200 void
201 gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid,
202 				gnttab_frame_t frame, int readonly)
203 {
204 	ASSERT(VALID_GRANT_REF(ref));
205 
206 	shared[ref].frame = frame;
207 	shared[ref].domid = domid;
208 	membar_producer();
209 	shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
210 }
211 
212 
213 int
214 gnttab_query_foreign_access(grant_ref_t ref)
215 {
216 	uint16_t nflags;
217 
218 	ASSERT(VALID_GRANT_REF(ref));
219 
220 	nflags = shared[ref].flags;
221 
222 	return (nflags & (GTF_reading|GTF_writing));
223 }
224 
225 /* ARGSUSED */
226 int
227 gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
228 {
229 	uint16_t flags, nflags;
230 
231 	ASSERT(VALID_GRANT_REF(ref));
232 
233 	nflags = shared[ref].flags;
234 	do {
235 		if ((flags = nflags) & (GTF_reading|GTF_writing)) {
236 			cmn_err(CE_WARN, "g.e. still in use!");
237 			return (0);
238 		}
239 	} while ((nflags = cmpxchg(&shared[ref].flags, flags, 0)) != flags);
240 
241 	return (1);
242 }
243 
244 void
245 gnttab_end_foreign_access(grant_ref_t ref, int readonly, gnttab_frame_t page)
246 {
247 	ASSERT(VALID_GRANT_REF(ref));
248 
249 	if (gnttab_end_foreign_access_ref(ref, readonly)) {
250 		put_free_entry(ref);
251 		/*
252 		 * XXPV - we don't support freeing a page here
253 		 */
254 		if (page != 0) {
255 			cmn_err(CE_WARN,
256 	"gnttab_end_foreign_access_ref: using unsupported free_page interface");
257 			/* free_page(page); */
258 		}
259 	} else {
260 		/*
261 		 * XXX This needs to be fixed so that the ref and page are
262 		 * placed on a list to be freed up later.
263 		 */
264 		cmn_err(CE_WARN, "leaking g.e. and page still in use!");
265 	}
266 }
267 
268 int
269 gnttab_grant_foreign_transfer(domid_t domid)
270 {
271 	int ref;
272 
273 	if ((ref = get_free_entry()) == -1)
274 		return (-1);
275 
276 	ASSERT(VALID_GRANT_REF(ref));
277 
278 	shared[ref].frame = 0;
279 	shared[ref].domid = domid;
280 	membar_producer();
281 	shared[ref].flags = GTF_accept_transfer;
282 
283 	return (ref);
284 }
285 
286 void
287 gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid)
288 {
289 	ASSERT(VALID_GRANT_REF(ref));
290 
291 	shared[ref].frame = 0;
292 	shared[ref].domid = domid;
293 	membar_producer();
294 	shared[ref].flags = GTF_accept_transfer;
295 }
296 
297 gnttab_frame_t
298 gnttab_end_foreign_transfer_ref(grant_ref_t ref)
299 {
300 	gnttab_frame_t frame;
301 	uint16_t flags;
302 
303 	ASSERT(VALID_GRANT_REF(ref));
304 
305 	/*
306 	 * If a transfer is not even yet started, try to reclaim the grant
307 	 * reference and return failure (== 0).
308 	 */
309 	while (!((flags = shared[ref].flags) & GTF_transfer_committed)) {
310 		if (cmpxchg(&shared[ref].flags, flags, 0) == flags)
311 			return (0);
312 		(void) HYPERVISOR_yield();
313 	}
314 
315 	/* If a transfer is in progress then wait until it is completed. */
316 	while (!(flags & GTF_transfer_completed)) {
317 		flags = shared[ref].flags;
318 		(void) HYPERVISOR_yield();
319 	}
320 
321 	/* Read the frame number /after/ reading completion status. */
322 	membar_consumer();
323 	frame = shared[ref].frame;
324 	ASSERT(frame != 0);
325 
326 	return (frame);
327 }
328 
329 gnttab_frame_t
330 gnttab_end_foreign_transfer(grant_ref_t ref)
331 {
332 	gnttab_frame_t frame;
333 
334 	ASSERT(VALID_GRANT_REF(ref));
335 
336 	frame = gnttab_end_foreign_transfer_ref(ref);
337 	put_free_entry(ref);
338 	return (frame);
339 }
340 
341 void
342 gnttab_free_grant_reference(grant_ref_t ref)
343 {
344 	ASSERT(VALID_GRANT_REF(ref));
345 
346 	put_free_entry(ref);
347 }
348 
349 void
350 gnttab_free_grant_references(grant_ref_t head)
351 {
352 	grant_ref_t ref;
353 	int count = 1;
354 
355 	if (head == GNTTAB_LIST_END)
356 		return;
357 	mutex_enter(&gnttab_list_lock);
358 	ref = head;
359 	while (gnttab_list[ref] != GNTTAB_LIST_END) {
360 		ref = gnttab_list[ref];
361 		count++;
362 	}
363 	gnttab_list[ref] = gnttab_free_head;
364 	gnttab_free_head = head;
365 	gnttab_free_count += count;
366 	check_free_callbacks();
367 	mutex_exit(&gnttab_list_lock);
368 }
369 
370 int
371 gnttab_alloc_grant_references(uint16_t count, grant_ref_t *head)
372 {
373 	int h = get_free_entries(count);
374 
375 	if (h == -1)
376 		return (-1);
377 
378 	*head = h;
379 
380 	return (0);
381 }
382 
383 int
384 gnttab_claim_grant_reference(grant_ref_t *private_head)
385 {
386 	grant_ref_t g = *private_head;
387 
388 	if (g == GNTTAB_LIST_END)
389 		return (-1);
390 	*private_head = gnttab_list[g];
391 	return (g);
392 }
393 
394 void
395 gnttab_release_grant_reference(grant_ref_t *private_head, grant_ref_t release)
396 {
397 	ASSERT(VALID_GRANT_REF(release));
398 
399 	gnttab_list[release] = *private_head;
400 	*private_head = release;
401 }
402 
403 void
404 gnttab_request_free_callback(struct gnttab_free_callback *callback,
405 	void (*fn)(void *), void *arg, uint16_t count)
406 {
407 	mutex_enter(&gnttab_list_lock);
408 	if (callback->next)
409 		goto out;
410 	callback->fn = fn;
411 	callback->arg = arg;
412 	callback->count = count;
413 	callback->next = gnttab_free_callback_list;
414 	gnttab_free_callback_list = callback;
415 	check_free_callbacks();
416 out:
417 	mutex_exit(&gnttab_list_lock);
418 }
419 
420 #ifdef XPV_HVM_DRIVER
421 
422 static void
423 gnttab_map(void)
424 {
425 	struct xen_add_to_physmap xatp;
426 	caddr_t va;
427 	pfn_t pfn;
428 	int i;
429 
430 	va = (caddr_t)shared;
431 	for (i = 0; i < NR_GRANT_FRAMES; i++) {
432 		pfn = hat_getpfnum(kas.a_hat, va);
433 
434 		xatp.domid = DOMID_SELF;
435 		xatp.idx = i;
436 		xatp.space = XENMAPSPACE_grant_table;
437 		xatp.gpfn = pfn;
438 		hat_unload(kas.a_hat, va, MMU_PAGESIZE, HAT_UNLOAD);
439 		if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp) != 0)
440 			panic("Couldn't map grant table");
441 
442 		hat_devload(kas.a_hat, va, MMU_PAGESIZE, pfn,
443 		    PROT_READ | PROT_WRITE,
444 		    HAT_LOAD | HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST);
445 
446 		va += MMU_PAGESIZE;
447 	}
448 }
449 
450 void
451 gnttab_init(void)
452 {
453 	int i;
454 
455 	shared = (grant_entry_t *)xen_alloc_pages(NR_GRANT_FRAMES);
456 
457 	gnttab_map();
458 
459 	for (i = NR_RESERVED_ENTRIES; i < NR_GRANT_ENTRIES; i++)
460 		gnttab_list[i] = i + 1;
461 	gnttab_free_count = NR_GRANT_ENTRIES - NR_RESERVED_ENTRIES;
462 	gnttab_free_head  = NR_RESERVED_ENTRIES;
463 
464 	mutex_init(&gnttab_list_lock, NULL, MUTEX_DEFAULT, NULL);
465 }
466 
467 void
468 gnttab_resume(void)
469 {
470 	gnttab_map();
471 }
472 
473 #else /* XPV_HVM_DRIVER */
474 
475 void
476 gnttab_init(void)
477 {
478 	gnttab_setup_table_t set;
479 	gnttab_frame_t frames[NR_GRANT_FRAMES];
480 	int i;
481 
482 	set.dom = DOMID_SELF;
483 	set.nr_frames = NR_GRANT_FRAMES;
484 	/*LINTED: constant in conditional context*/
485 	set_xen_guest_handle(set.frame_list, frames);
486 
487 	/*
488 	 * Take 4 pages of grant table space from the hypervisor and map it
489 	 */
490 	if ((HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &set, 1) != 0) ||
491 	    (set.status != 0)) {
492 		cmn_err(CE_PANIC, "Grant Table setup failed");
493 	}
494 
495 	shared = vmem_xalloc(heap_arena, NR_GRANT_FRAMES * MMU_PAGESIZE,
496 	    MMU_PAGESIZE, 0, 0, 0, 0, VM_SLEEP);
497 
498 	for (i = 0; i < NR_GRANT_FRAMES; i++)
499 		kbm_map_ma(FRAME_TO_MA(frames[i]), GT_PGADDR(i), 0);
500 
501 	for (i = NR_RESERVED_ENTRIES; i < NR_GRANT_ENTRIES; i++)
502 		gnttab_list[i] = i + 1;
503 	gnttab_free_count = NR_GRANT_ENTRIES - NR_RESERVED_ENTRIES;
504 	gnttab_free_head  = NR_RESERVED_ENTRIES;
505 
506 	mutex_init(&gnttab_list_lock, NULL, MUTEX_DEFAULT, NULL);
507 }
508 
509 void
510 gnttab_resume(void)
511 {
512 	gnttab_setup_table_t set;
513 	gnttab_frame_t frames[NR_GRANT_FRAMES];
514 	int i;
515 
516 	set.dom = DOMID_SELF;
517 	set.nr_frames = NR_GRANT_FRAMES;
518 	/*LINTED: constant in conditional context*/
519 	set_xen_guest_handle(set.frame_list, frames);
520 
521 	/*
522 	 * Take NR_GRANT_FRAMES pages of grant table space from the
523 	 * hypervisor and map it
524 	 */
525 	if ((HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &set, 1) != 0) ||
526 	    (set.status != 0)) {
527 		cmn_err(CE_PANIC, "Grant Table setup failed");
528 	}
529 
530 	for (i = 0; i < NR_GRANT_FRAMES; i++) {
531 		(void) HYPERVISOR_update_va_mapping(GT_PGADDR(i),
532 		    FRAME_TO_MA(frames[i]) | PT_VALID | PT_WRITABLE,
533 		    UVMF_INVLPG | UVMF_ALL);
534 	}
535 }
536 
537 #endif /* XPV_HVM_DRIVER */
538 
539 void
540 gnttab_suspend(void)
541 {
542 	int i;
543 
544 	/*
545 	 * clear grant table mappings before suspending
546 	 */
547 	for (i = 0; i < NR_GRANT_FRAMES; i++) {
548 		(void) HYPERVISOR_update_va_mapping(GT_PGADDR(i),
549 		    0, UVMF_INVLPG);
550 	}
551 }
552 
553 /*
554  * Local variables:
555  *  c-file-style: "solaris"
556  *  indent-tabs-mode: t
557  *  c-indent-level: 8
558  *  c-basic-offset: 8
559  *  tab-width: 8
560  * End:
561  */
562