xref: /illumos-gate/usr/src/uts/common/xen/os/gnttab.c (revision 2d6eb4a5)
1843e1988Sjohnlev /*
2843e1988Sjohnlev  * CDDL HEADER START
3843e1988Sjohnlev  *
4843e1988Sjohnlev  * The contents of this file are subject to the terms of the
5843e1988Sjohnlev  * Common Development and Distribution License (the "License").
6843e1988Sjohnlev  * You may not use this file except in compliance with the License.
7843e1988Sjohnlev  *
8843e1988Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9843e1988Sjohnlev  * or http://www.opensolaris.org/os/licensing.
10843e1988Sjohnlev  * See the License for the specific language governing permissions
11843e1988Sjohnlev  * and limitations under the License.
12843e1988Sjohnlev  *
13843e1988Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
14843e1988Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15843e1988Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
16843e1988Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
17843e1988Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
18843e1988Sjohnlev  *
19843e1988Sjohnlev  * CDDL HEADER END
20843e1988Sjohnlev  */
21843e1988Sjohnlev 
22843e1988Sjohnlev /*
23a576ab5bSrab  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24843e1988Sjohnlev  * Use is subject to license terms.
25843e1988Sjohnlev  */
26843e1988Sjohnlev 
27843e1988Sjohnlev /*
28843e1988Sjohnlev  * gnttab.c
29843e1988Sjohnlev  *
30843e1988Sjohnlev  * Granting foreign access to our memory reservation.
31843e1988Sjohnlev  *
32a576ab5bSrab  * Copyright (c) 2005-2006, Christopher Clark
33843e1988Sjohnlev  * Copyright (c) 2004-2005, K A Fraser
34843e1988Sjohnlev  *
35a576ab5bSrab  * This program is free software; you can redistribute it and/or
36a576ab5bSrab  * modify it under the terms of the GNU General Public License version 2
37a576ab5bSrab  * as published by the Free Software Foundation; or, when distributed
38a576ab5bSrab  * separately from the Linux kernel or incorporated into other
39a576ab5bSrab  * software packages, subject to the following license:
40843e1988Sjohnlev  *
41843e1988Sjohnlev  * Permission is hereby granted, free of charge, to any person obtaining a copy
42843e1988Sjohnlev  * of this source file (the "Software"), to deal in the Software without
43843e1988Sjohnlev  * restriction, including without limitation the rights to use, copy, modify,
44843e1988Sjohnlev  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
45843e1988Sjohnlev  * and to permit persons to whom the Software is furnished to do so, subject to
46843e1988Sjohnlev  * the following conditions:
47843e1988Sjohnlev  *
48843e1988Sjohnlev  * The above copyright notice and this permission notice shall be included in
49843e1988Sjohnlev  * all copies or substantial portions of the Software.
50843e1988Sjohnlev  *
51843e1988Sjohnlev  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
52843e1988Sjohnlev  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
53843e1988Sjohnlev  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
54843e1988Sjohnlev  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
55843e1988Sjohnlev  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
56843e1988Sjohnlev  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
57843e1988Sjohnlev  * IN THE SOFTWARE.
58843e1988Sjohnlev  */
59843e1988Sjohnlev 
60843e1988Sjohnlev #include <sys/types.h>
61843e1988Sjohnlev #include <sys/archsystm.h>
62551bc2a6Smrj #ifdef XPV_HVM_DRIVER
63551bc2a6Smrj #include <sys/xpv_support.h>
64551bc2a6Smrj #include <sys/mman.h>
65551bc2a6Smrj #include <vm/hat.h>
66551bc2a6Smrj #endif
67843e1988Sjohnlev #include <sys/hypervisor.h>
68843e1988Sjohnlev #include <sys/gnttab.h>
69843e1988Sjohnlev #include <sys/sysmacros.h>
70843e1988Sjohnlev #include <sys/machsystm.h>
71843e1988Sjohnlev #include <sys/systm.h>
72843e1988Sjohnlev #include <sys/mutex.h>
73843e1988Sjohnlev #include <sys/atomic.h>
74843e1988Sjohnlev #include <sys/spl.h>
75843e1988Sjohnlev #include <sys/condvar.h>
76843e1988Sjohnlev #include <sys/cpuvar.h>
77843e1988Sjohnlev #include <sys/taskq.h>
78843e1988Sjohnlev #include <sys/panic.h>
79843e1988Sjohnlev #include <sys/cmn_err.h>
80843e1988Sjohnlev #include <sys/promif.h>
81843e1988Sjohnlev #include <sys/cpu.h>
82843e1988Sjohnlev #include <sys/vmem.h>
83843e1988Sjohnlev #include <vm/hat_i86.h>
84843e1988Sjohnlev #include <sys/bootconf.h>
85843e1988Sjohnlev #include <sys/bootsvcs.h>
86551bc2a6Smrj #ifndef XPV_HVM_DRIVER
87843e1988Sjohnlev #include <sys/bootinfo.h>
88843e1988Sjohnlev #include <sys/multiboot.h>
89551bc2a6Smrj #include <vm/kboot_mmu.h>
90551bc2a6Smrj #endif
91843e1988Sjohnlev #include <sys/bootvfs.h>
92843e1988Sjohnlev #include <sys/bootprops.h>
93843e1988Sjohnlev #include <vm/seg_kmem.h>
94a576ab5bSrab #include <sys/mman.h>
95843e1988Sjohnlev 
96a576ab5bSrab /* Globals */
97843e1988Sjohnlev 
98a576ab5bSrab static grant_ref_t **gnttab_list;
99a576ab5bSrab static uint_t nr_grant_frames;
100843e1988Sjohnlev static int gnttab_free_count;
101843e1988Sjohnlev static grant_ref_t gnttab_free_head;
102843e1988Sjohnlev static kmutex_t gnttab_list_lock;
103843e1988Sjohnlev static grant_entry_t *shared;
104a576ab5bSrab static struct gnttab_free_callback *gnttab_free_callback_list;
105843e1988Sjohnlev 
106a576ab5bSrab /* Macros */
107843e1988Sjohnlev 
108a576ab5bSrab #define	GT_PGADDR(i) ((uintptr_t)shared + ((i) << MMU_PAGESHIFT))
109a576ab5bSrab #define	VALID_GRANT_REF(r) ((r) < (nr_grant_frames * GREFS_PER_GRANT_FRAME))
110a576ab5bSrab #define	RPP (PAGESIZE / sizeof (grant_ref_t))
111a576ab5bSrab #define	GNTTAB_ENTRY(entry) (gnttab_list[(entry) / RPP][(entry) % RPP])
112a576ab5bSrab #define	CMPXCHG(t, c, n) atomic_cas_16((t), (c), (n))
113a576ab5bSrab /* External tools reserve first few grant table entries. */
114a576ab5bSrab #define	NR_RESERVED_ENTRIES 8
115a576ab5bSrab #define	GNTTAB_LIST_END 0xffffffff
116a576ab5bSrab #define	GREFS_PER_GRANT_FRAME (PAGESIZE / sizeof (grant_entry_t))
117a576ab5bSrab 
118a576ab5bSrab /* Implementation */
119a576ab5bSrab 
120a576ab5bSrab static uint_t
max_nr_grant_frames(void)121a576ab5bSrab max_nr_grant_frames(void)
122843e1988Sjohnlev {
123a576ab5bSrab 	struct gnttab_query_size query;
124a576ab5bSrab 	int rc;
125843e1988Sjohnlev 
126a576ab5bSrab 	query.dom = DOMID_SELF;
127a576ab5bSrab 
128a576ab5bSrab 	rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
129a576ab5bSrab 	if ((rc < 0) || (query.status != GNTST_okay))
130a576ab5bSrab 		return (4); /* Legacy max supported number of frames */
131843e1988Sjohnlev 
132a576ab5bSrab 	ASSERT(query.max_nr_frames);
133a576ab5bSrab 	return (query.max_nr_frames);
134a576ab5bSrab }
135843e1988Sjohnlev 
136843e1988Sjohnlev static void
do_free_callbacks(void)137843e1988Sjohnlev do_free_callbacks(void)
138843e1988Sjohnlev {
139843e1988Sjohnlev 	struct gnttab_free_callback *callback, *next;
140843e1988Sjohnlev 
141843e1988Sjohnlev 	callback = gnttab_free_callback_list;
142843e1988Sjohnlev 	gnttab_free_callback_list = NULL;
143843e1988Sjohnlev 
144843e1988Sjohnlev 	while (callback != NULL) {
145843e1988Sjohnlev 		next = callback->next;
146843e1988Sjohnlev 		if (gnttab_free_count >= callback->count) {
147843e1988Sjohnlev 			callback->next = NULL;
148843e1988Sjohnlev 			callback->fn(callback->arg);
149843e1988Sjohnlev 		} else {
150843e1988Sjohnlev 			callback->next = gnttab_free_callback_list;
151843e1988Sjohnlev 			gnttab_free_callback_list = callback;
152843e1988Sjohnlev 		}
153843e1988Sjohnlev 		callback = next;
154843e1988Sjohnlev 	}
155843e1988Sjohnlev }
156843e1988Sjohnlev 
157843e1988Sjohnlev static void
check_free_callbacks(void)158843e1988Sjohnlev check_free_callbacks(void)
159843e1988Sjohnlev {
160843e1988Sjohnlev 	if (gnttab_free_callback_list)
161843e1988Sjohnlev 		do_free_callbacks();
162843e1988Sjohnlev }
163843e1988Sjohnlev 
164a576ab5bSrab static int
grow_gnttab_list(uint_t more_frames)165a576ab5bSrab grow_gnttab_list(uint_t more_frames)
166a576ab5bSrab {
167a576ab5bSrab 	uint_t new_nr_grant_frames, extra_entries, i;
1681213c83cSgarypen 	uint_t nr_glist_frames, new_nr_glist_frames;
169a576ab5bSrab 
170a576ab5bSrab 	ASSERT(MUTEX_HELD(&gnttab_list_lock));
171a576ab5bSrab 
172a576ab5bSrab 	new_nr_grant_frames = nr_grant_frames + more_frames;
173a576ab5bSrab 	extra_entries = more_frames * GREFS_PER_GRANT_FRAME;
174a576ab5bSrab 
1751213c83cSgarypen 	nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1)
1761213c83cSgarypen 	    / RPP;
1771213c83cSgarypen 	new_nr_glist_frames = (new_nr_grant_frames * GREFS_PER_GRANT_FRAME
1781213c83cSgarypen 	    + RPP - 1) / RPP;
1791213c83cSgarypen 	for (i = nr_glist_frames; i < new_nr_glist_frames; i++)
180a576ab5bSrab 		gnttab_list[i] = kmem_alloc(PAGESIZE, KM_SLEEP);
181a576ab5bSrab 
182a576ab5bSrab 	for (i = GREFS_PER_GRANT_FRAME * nr_grant_frames;
183a576ab5bSrab 	    i < GREFS_PER_GRANT_FRAME * new_nr_grant_frames - 1; i++)
184a576ab5bSrab 		GNTTAB_ENTRY(i) = i + 1;
185a576ab5bSrab 
186a576ab5bSrab 	GNTTAB_ENTRY(i) = gnttab_free_head;
187a576ab5bSrab 	gnttab_free_head = GREFS_PER_GRANT_FRAME * nr_grant_frames;
188a576ab5bSrab 	gnttab_free_count += extra_entries;
189a576ab5bSrab 
190a576ab5bSrab 	nr_grant_frames = new_nr_grant_frames;
191a576ab5bSrab 
192a576ab5bSrab 	check_free_callbacks();
193a576ab5bSrab 
194a576ab5bSrab 	return (0);
195a576ab5bSrab }
196a576ab5bSrab 
197a576ab5bSrab static int
gnttab_expand(uint_t req_entries)198a576ab5bSrab gnttab_expand(uint_t req_entries)
199a576ab5bSrab {
200a576ab5bSrab 	uint_t cur, extra;
201a576ab5bSrab 
202a576ab5bSrab 	ASSERT(MUTEX_HELD(&gnttab_list_lock));
203a576ab5bSrab 
204a576ab5bSrab 	cur = nr_grant_frames;
205a576ab5bSrab 	extra = ((req_entries + (GREFS_PER_GRANT_FRAME - 1)) /
206a576ab5bSrab 	    GREFS_PER_GRANT_FRAME);
207a576ab5bSrab 	if (cur + extra > max_nr_grant_frames())
208a576ab5bSrab 		return (-1);
209a576ab5bSrab 
210a576ab5bSrab 	return (grow_gnttab_list(extra));
211a576ab5bSrab }
212a576ab5bSrab 
213a576ab5bSrab static int
get_free_entries(int count)214a576ab5bSrab get_free_entries(int count)
215a576ab5bSrab {
216a576ab5bSrab 	int ref, rc;
217a576ab5bSrab 	grant_ref_t head;
218a576ab5bSrab 
219a576ab5bSrab 	mutex_enter(&gnttab_list_lock);
220a576ab5bSrab 	if (gnttab_free_count < count &&
221a576ab5bSrab 	    ((rc = gnttab_expand(count - gnttab_free_count)) < 0)) {
222a576ab5bSrab 		mutex_exit(&gnttab_list_lock);
223a576ab5bSrab 		return (rc);
224a576ab5bSrab 	}
225a576ab5bSrab 	ref = head = gnttab_free_head;
226a576ab5bSrab 	gnttab_free_count -= count;
227a576ab5bSrab 	while (count-- > 1)
228a576ab5bSrab 		head = GNTTAB_ENTRY(head);
229a576ab5bSrab 	gnttab_free_head = GNTTAB_ENTRY(head);
230a576ab5bSrab 	GNTTAB_ENTRY(head) = GNTTAB_LIST_END;
231a576ab5bSrab 	mutex_exit(&gnttab_list_lock);
232a576ab5bSrab 	return (ref);
233a576ab5bSrab }
234a576ab5bSrab 
235843e1988Sjohnlev static void
put_free_entry(grant_ref_t ref)236843e1988Sjohnlev put_free_entry(grant_ref_t ref)
237843e1988Sjohnlev {
238843e1988Sjohnlev 	ASSERT(VALID_GRANT_REF(ref));
239843e1988Sjohnlev 
240843e1988Sjohnlev 	mutex_enter(&gnttab_list_lock);
241a576ab5bSrab 	GNTTAB_ENTRY(ref) = gnttab_free_head;
242843e1988Sjohnlev 	gnttab_free_head = ref;
243843e1988Sjohnlev 	gnttab_free_count++;
244843e1988Sjohnlev 	check_free_callbacks();
245843e1988Sjohnlev 	mutex_exit(&gnttab_list_lock);
246843e1988Sjohnlev }
247843e1988Sjohnlev 
248843e1988Sjohnlev /*
249843e1988Sjohnlev  * Public grant-issuing interface functions
250843e1988Sjohnlev  */
251843e1988Sjohnlev 
252843e1988Sjohnlev int
gnttab_grant_foreign_access(domid_t domid,gnttab_frame_t frame,int readonly)253843e1988Sjohnlev gnttab_grant_foreign_access(domid_t domid, gnttab_frame_t frame, int readonly)
254843e1988Sjohnlev {
255843e1988Sjohnlev 	int ref;
256843e1988Sjohnlev 
257a576ab5bSrab 	if ((ref = get_free_entries(1)) == -1)
258843e1988Sjohnlev 		return (-1);
259843e1988Sjohnlev 
260843e1988Sjohnlev 	ASSERT(VALID_GRANT_REF(ref));
261843e1988Sjohnlev 
262843e1988Sjohnlev 	shared[ref].frame = frame;
263843e1988Sjohnlev 	shared[ref].domid = domid;
264843e1988Sjohnlev 	membar_producer();
265843e1988Sjohnlev 	shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
266843e1988Sjohnlev 
267843e1988Sjohnlev 	return (ref);
268843e1988Sjohnlev }
269843e1988Sjohnlev 
270843e1988Sjohnlev void
gnttab_grant_foreign_access_ref(grant_ref_t ref,domid_t domid,gnttab_frame_t frame,int readonly)271843e1988Sjohnlev gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid,
272843e1988Sjohnlev 				gnttab_frame_t frame, int readonly)
273843e1988Sjohnlev {
274843e1988Sjohnlev 	ASSERT(VALID_GRANT_REF(ref));
275843e1988Sjohnlev 
276843e1988Sjohnlev 	shared[ref].frame = frame;
277843e1988Sjohnlev 	shared[ref].domid = domid;
278843e1988Sjohnlev 	membar_producer();
279843e1988Sjohnlev 	shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
280843e1988Sjohnlev }
281843e1988Sjohnlev 
282843e1988Sjohnlev 
283843e1988Sjohnlev int
gnttab_query_foreign_access(grant_ref_t ref)284843e1988Sjohnlev gnttab_query_foreign_access(grant_ref_t ref)
285843e1988Sjohnlev {
286843e1988Sjohnlev 	uint16_t nflags;
287843e1988Sjohnlev 
288843e1988Sjohnlev 	ASSERT(VALID_GRANT_REF(ref));
289843e1988Sjohnlev 
290843e1988Sjohnlev 	nflags = shared[ref].flags;
291843e1988Sjohnlev 
292843e1988Sjohnlev 	return (nflags & (GTF_reading|GTF_writing));
293843e1988Sjohnlev }
294843e1988Sjohnlev 
295843e1988Sjohnlev /* ARGSUSED */
296843e1988Sjohnlev int
gnttab_end_foreign_access_ref(grant_ref_t ref,int readonly)297843e1988Sjohnlev gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
298843e1988Sjohnlev {
299843e1988Sjohnlev 	uint16_t flags, nflags;
300843e1988Sjohnlev 
301843e1988Sjohnlev 	ASSERT(VALID_GRANT_REF(ref));
302843e1988Sjohnlev 
303843e1988Sjohnlev 	nflags = shared[ref].flags;
304843e1988Sjohnlev 	do {
305843e1988Sjohnlev 		if ((flags = nflags) & (GTF_reading|GTF_writing)) {
306843e1988Sjohnlev 			cmn_err(CE_WARN, "g.e. still in use!");
307843e1988Sjohnlev 			return (0);
308843e1988Sjohnlev 		}
309a576ab5bSrab 	} while ((nflags = CMPXCHG(&shared[ref].flags, flags, 0)) != flags);
310843e1988Sjohnlev 
311843e1988Sjohnlev 	return (1);
312843e1988Sjohnlev }
313843e1988Sjohnlev 
314843e1988Sjohnlev void
gnttab_end_foreign_access(grant_ref_t ref,int readonly,gnttab_frame_t page)315843e1988Sjohnlev gnttab_end_foreign_access(grant_ref_t ref, int readonly, gnttab_frame_t page)
316843e1988Sjohnlev {
317843e1988Sjohnlev 	ASSERT(VALID_GRANT_REF(ref));
318843e1988Sjohnlev 
319843e1988Sjohnlev 	if (gnttab_end_foreign_access_ref(ref, readonly)) {
320843e1988Sjohnlev 		put_free_entry(ref);
321843e1988Sjohnlev 		/*
322843e1988Sjohnlev 		 * XXPV - we don't support freeing a page here
323843e1988Sjohnlev 		 */
324843e1988Sjohnlev 		if (page != 0) {
325843e1988Sjohnlev 			cmn_err(CE_WARN,
326843e1988Sjohnlev 	"gnttab_end_foreign_access_ref: using unsupported free_page interface");
327843e1988Sjohnlev 			/* free_page(page); */
328843e1988Sjohnlev 		}
329843e1988Sjohnlev 	} else {
330843e1988Sjohnlev 		/*
331843e1988Sjohnlev 		 * XXX This needs to be fixed so that the ref and page are
332843e1988Sjohnlev 		 * placed on a list to be freed up later.
333843e1988Sjohnlev 		 */
334843e1988Sjohnlev 		cmn_err(CE_WARN, "leaking g.e. and page still in use!");
335843e1988Sjohnlev 	}
336843e1988Sjohnlev }
337843e1988Sjohnlev 
338843e1988Sjohnlev int
gnttab_grant_foreign_transfer(domid_t domid,pfn_t pfn)339a576ab5bSrab gnttab_grant_foreign_transfer(domid_t domid, pfn_t pfn)
340843e1988Sjohnlev {
341843e1988Sjohnlev 	int ref;
342843e1988Sjohnlev 
343a576ab5bSrab 	if ((ref = get_free_entries(1)) == -1)
344843e1988Sjohnlev 		return (-1);
345843e1988Sjohnlev 
346843e1988Sjohnlev 	ASSERT(VALID_GRANT_REF(ref));
347843e1988Sjohnlev 
348a576ab5bSrab 	gnttab_grant_foreign_transfer_ref(ref, domid, pfn);
349843e1988Sjohnlev 
350843e1988Sjohnlev 	return (ref);
351843e1988Sjohnlev }
352843e1988Sjohnlev 
353843e1988Sjohnlev void
gnttab_grant_foreign_transfer_ref(grant_ref_t ref,domid_t domid,pfn_t pfn)354a576ab5bSrab gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid, pfn_t pfn)
355843e1988Sjohnlev {
356843e1988Sjohnlev 	ASSERT(VALID_GRANT_REF(ref));
357843e1988Sjohnlev 
358a576ab5bSrab 	shared[ref].frame = pfn;
359843e1988Sjohnlev 	shared[ref].domid = domid;
360843e1988Sjohnlev 	membar_producer();
361843e1988Sjohnlev 	shared[ref].flags = GTF_accept_transfer;
362843e1988Sjohnlev }
363843e1988Sjohnlev 
364843e1988Sjohnlev gnttab_frame_t
gnttab_end_foreign_transfer_ref(grant_ref_t ref)365843e1988Sjohnlev gnttab_end_foreign_transfer_ref(grant_ref_t ref)
366843e1988Sjohnlev {
367843e1988Sjohnlev 	gnttab_frame_t frame;
368843e1988Sjohnlev 	uint16_t flags;
369843e1988Sjohnlev 
370843e1988Sjohnlev 	ASSERT(VALID_GRANT_REF(ref));
371843e1988Sjohnlev 
372843e1988Sjohnlev 	/*
373843e1988Sjohnlev 	 * If a transfer is not even yet started, try to reclaim the grant
374843e1988Sjohnlev 	 * reference and return failure (== 0).
375843e1988Sjohnlev 	 */
376843e1988Sjohnlev 	while (!((flags = shared[ref].flags) & GTF_transfer_committed)) {
377a576ab5bSrab 		if (CMPXCHG(&shared[ref].flags, flags, 0) == flags)
378843e1988Sjohnlev 			return (0);
379843e1988Sjohnlev 		(void) HYPERVISOR_yield();
380843e1988Sjohnlev 	}
381843e1988Sjohnlev 
382843e1988Sjohnlev 	/* If a transfer is in progress then wait until it is completed. */
383843e1988Sjohnlev 	while (!(flags & GTF_transfer_completed)) {
384843e1988Sjohnlev 		flags = shared[ref].flags;
385843e1988Sjohnlev 		(void) HYPERVISOR_yield();
386843e1988Sjohnlev 	}
387843e1988Sjohnlev 
388843e1988Sjohnlev 	/* Read the frame number /after/ reading completion status. */
389843e1988Sjohnlev 	membar_consumer();
390843e1988Sjohnlev 	frame = shared[ref].frame;
391843e1988Sjohnlev 	ASSERT(frame != 0);
392843e1988Sjohnlev 
393843e1988Sjohnlev 	return (frame);
394843e1988Sjohnlev }
395843e1988Sjohnlev 
396843e1988Sjohnlev gnttab_frame_t
gnttab_end_foreign_transfer(grant_ref_t ref)397843e1988Sjohnlev gnttab_end_foreign_transfer(grant_ref_t ref)
398843e1988Sjohnlev {
399843e1988Sjohnlev 	gnttab_frame_t frame;
400843e1988Sjohnlev 
401843e1988Sjohnlev 	ASSERT(VALID_GRANT_REF(ref));
402843e1988Sjohnlev 
403843e1988Sjohnlev 	frame = gnttab_end_foreign_transfer_ref(ref);
404843e1988Sjohnlev 	put_free_entry(ref);
405843e1988Sjohnlev 	return (frame);
406843e1988Sjohnlev }
407843e1988Sjohnlev 
408843e1988Sjohnlev void
gnttab_free_grant_reference(grant_ref_t ref)409843e1988Sjohnlev gnttab_free_grant_reference(grant_ref_t ref)
410843e1988Sjohnlev {
411843e1988Sjohnlev 	ASSERT(VALID_GRANT_REF(ref));
412843e1988Sjohnlev 
413843e1988Sjohnlev 	put_free_entry(ref);
414843e1988Sjohnlev }
415843e1988Sjohnlev 
416843e1988Sjohnlev void
gnttab_free_grant_references(grant_ref_t head)417843e1988Sjohnlev gnttab_free_grant_references(grant_ref_t head)
418843e1988Sjohnlev {
419843e1988Sjohnlev 	grant_ref_t ref;
420843e1988Sjohnlev 	int count = 1;
421843e1988Sjohnlev 
422843e1988Sjohnlev 	if (head == GNTTAB_LIST_END)
423843e1988Sjohnlev 		return;
424843e1988Sjohnlev 	mutex_enter(&gnttab_list_lock);
425843e1988Sjohnlev 	ref = head;
426a576ab5bSrab 	while (GNTTAB_ENTRY(ref) != GNTTAB_LIST_END) {
427a576ab5bSrab 		ref = GNTTAB_ENTRY(ref);
428843e1988Sjohnlev 		count++;
429843e1988Sjohnlev 	}
430a576ab5bSrab 	GNTTAB_ENTRY(ref) = gnttab_free_head;
431843e1988Sjohnlev 	gnttab_free_head = head;
432843e1988Sjohnlev 	gnttab_free_count += count;
433843e1988Sjohnlev 	check_free_callbacks();
434843e1988Sjohnlev 	mutex_exit(&gnttab_list_lock);
435843e1988Sjohnlev }
436843e1988Sjohnlev 
437843e1988Sjohnlev int
gnttab_alloc_grant_references(uint16_t count,grant_ref_t * head)438843e1988Sjohnlev gnttab_alloc_grant_references(uint16_t count, grant_ref_t *head)
439843e1988Sjohnlev {
440843e1988Sjohnlev 	int h = get_free_entries(count);
441843e1988Sjohnlev 
442843e1988Sjohnlev 	if (h == -1)
443843e1988Sjohnlev 		return (-1);
444843e1988Sjohnlev 
445843e1988Sjohnlev 	*head = h;
446843e1988Sjohnlev 
447843e1988Sjohnlev 	return (0);
448843e1988Sjohnlev }
449843e1988Sjohnlev 
450a576ab5bSrab int
gnttab_empty_grant_references(const grant_ref_t * private_head)451a576ab5bSrab gnttab_empty_grant_references(const grant_ref_t *private_head)
452a576ab5bSrab {
453a576ab5bSrab 	return (*private_head == GNTTAB_LIST_END);
454a576ab5bSrab }
455a576ab5bSrab 
456843e1988Sjohnlev int
gnttab_claim_grant_reference(grant_ref_t * private_head)457843e1988Sjohnlev gnttab_claim_grant_reference(grant_ref_t *private_head)
458843e1988Sjohnlev {
459843e1988Sjohnlev 	grant_ref_t g = *private_head;
460843e1988Sjohnlev 
461843e1988Sjohnlev 	if (g == GNTTAB_LIST_END)
462843e1988Sjohnlev 		return (-1);
463a576ab5bSrab 	*private_head = GNTTAB_ENTRY(g);
464843e1988Sjohnlev 	return (g);
465843e1988Sjohnlev }
466843e1988Sjohnlev 
467843e1988Sjohnlev void
gnttab_release_grant_reference(grant_ref_t * private_head,grant_ref_t release)468843e1988Sjohnlev gnttab_release_grant_reference(grant_ref_t *private_head, grant_ref_t release)
469843e1988Sjohnlev {
470843e1988Sjohnlev 	ASSERT(VALID_GRANT_REF(release));
471843e1988Sjohnlev 
472a576ab5bSrab 	GNTTAB_ENTRY(release) = *private_head;
473843e1988Sjohnlev 	*private_head = release;
474843e1988Sjohnlev }
475843e1988Sjohnlev 
476843e1988Sjohnlev void
gnttab_request_free_callback(struct gnttab_free_callback * callback,void (* fn)(void *),void * arg,uint16_t count)477843e1988Sjohnlev gnttab_request_free_callback(struct gnttab_free_callback *callback,
478843e1988Sjohnlev 	void (*fn)(void *), void *arg, uint16_t count)
479843e1988Sjohnlev {
480843e1988Sjohnlev 	mutex_enter(&gnttab_list_lock);
481843e1988Sjohnlev 	if (callback->next)
482843e1988Sjohnlev 		goto out;
483843e1988Sjohnlev 	callback->fn = fn;
484843e1988Sjohnlev 	callback->arg = arg;
485843e1988Sjohnlev 	callback->count = count;
486843e1988Sjohnlev 	callback->next = gnttab_free_callback_list;
487843e1988Sjohnlev 	gnttab_free_callback_list = callback;
488843e1988Sjohnlev 	check_free_callbacks();
489843e1988Sjohnlev out:
490843e1988Sjohnlev 	mutex_exit(&gnttab_list_lock);
491843e1988Sjohnlev }
492843e1988Sjohnlev 
493a576ab5bSrab void
gnttab_cancel_free_callback(struct gnttab_free_callback * callback)494a576ab5bSrab gnttab_cancel_free_callback(struct gnttab_free_callback *callback)
495a576ab5bSrab {
496a576ab5bSrab 	struct gnttab_free_callback **pcb;
497a576ab5bSrab 
498a576ab5bSrab 	mutex_enter(&gnttab_list_lock);
499a576ab5bSrab 	for (pcb = &gnttab_free_callback_list; *pcb; pcb = &(*pcb)->next) {
500a576ab5bSrab 		if (*pcb == callback) {
501a576ab5bSrab 			*pcb = callback->next;
502a576ab5bSrab 			break;
503a576ab5bSrab 		}
504a576ab5bSrab 	}
505a576ab5bSrab 	mutex_exit(&gnttab_list_lock);
506a576ab5bSrab }
507a576ab5bSrab 
508a576ab5bSrab static gnttab_frame_t *
gnttab_setup(gnttab_setup_table_t * pset)509a576ab5bSrab gnttab_setup(gnttab_setup_table_t *pset)
510a576ab5bSrab {
511a576ab5bSrab 	gnttab_frame_t *frames;
512a576ab5bSrab 
513a576ab5bSrab 	frames = kmem_alloc(pset->nr_frames * sizeof (gnttab_frame_t),
514a576ab5bSrab 	    KM_SLEEP);
515a576ab5bSrab 
516a576ab5bSrab 	/*LINTED: constant in conditional context*/
517a576ab5bSrab 	set_xen_guest_handle(pset->frame_list, frames);
518a576ab5bSrab 
519*6eb35ee7Srab #ifndef XPV_HVM_DRIVER
520a576ab5bSrab 	/*
521a576ab5bSrab 	 * Take pset->nr_frames pages of grant table space from
522a576ab5bSrab 	 * the hypervisor and map it
523a576ab5bSrab 	 */
524a576ab5bSrab 	if ((HYPERVISOR_grant_table_op(GNTTABOP_setup_table, pset, 1) != 0) ||
525a576ab5bSrab 	    (pset->status != 0)) {
526a576ab5bSrab 		cmn_err(CE_PANIC, "Grant Table setup failed");
527a576ab5bSrab 	}
528*6eb35ee7Srab #endif
529a576ab5bSrab 
530a576ab5bSrab 	return (frames);
531a576ab5bSrab }
532551bc2a6Smrj 
533a576ab5bSrab #ifdef XPV_HVM_DRIVER
534551bc2a6Smrj static void
gnttab_map(void)535551bc2a6Smrj gnttab_map(void)
536551bc2a6Smrj {
537551bc2a6Smrj 	struct xen_add_to_physmap xatp;
538551bc2a6Smrj 	caddr_t va;
539551bc2a6Smrj 	pfn_t pfn;
540551bc2a6Smrj 	int i;
541551bc2a6Smrj 
542551bc2a6Smrj 	va = (caddr_t)shared;
543a576ab5bSrab 	for (i = 0; i < max_nr_grant_frames(); i++) {
544a576ab5bSrab 		if ((pfn = hat_getpfnum(kas.a_hat, va)) == PFN_INVALID)
545a576ab5bSrab 			cmn_err(CE_PANIC, "gnttab_map: Invalid pfn");
546551bc2a6Smrj 
547551bc2a6Smrj 		xatp.domid = DOMID_SELF;
548551bc2a6Smrj 		xatp.idx = i;
549551bc2a6Smrj 		xatp.space = XENMAPSPACE_grant_table;
550551bc2a6Smrj 		xatp.gpfn = pfn;
551551bc2a6Smrj 		hat_unload(kas.a_hat, va, MMU_PAGESIZE, HAT_UNLOAD);
552a576ab5bSrab 		/*
553a576ab5bSrab 		 * This call replaces the existing machine page backing
554a576ab5bSrab 		 * the given gpfn with the page from the allocated grant
555a576ab5bSrab 		 * table at index idx. The existing machine page is
556a576ab5bSrab 		 * returned to the free list.
557a576ab5bSrab 		 */
558551bc2a6Smrj 		if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp) != 0)
559551bc2a6Smrj 			panic("Couldn't map grant table");
560551bc2a6Smrj 		hat_devload(kas.a_hat, va, MMU_PAGESIZE, pfn,
5615b1df0b5Sjohnlev 		    PROT_READ | PROT_WRITE | HAT_STORECACHING_OK,
562551bc2a6Smrj 		    HAT_LOAD | HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST);
563551bc2a6Smrj 		va += MMU_PAGESIZE;
564551bc2a6Smrj 	}
565551bc2a6Smrj }
566a576ab5bSrab #endif /* XPV_HVM_DRIVER */
567551bc2a6Smrj 
568551bc2a6Smrj void
gnttab_init(void)569551bc2a6Smrj gnttab_init(void)
570551bc2a6Smrj {
571a576ab5bSrab 	gnttab_setup_table_t set;
572551bc2a6Smrj 	int i;
5731213c83cSgarypen 	uint_t nr_init_grefs, max_nr_glist_frames, nr_glist_frames;
574a576ab5bSrab 	gnttab_frame_t *frames;
575551bc2a6Smrj 
576a576ab5bSrab 	/*
577a576ab5bSrab 	 * gnttab_init() should only be invoked once.
578a576ab5bSrab 	 */
579a576ab5bSrab 	mutex_enter(&gnttab_list_lock);
580a576ab5bSrab 	ASSERT(nr_grant_frames == 0);
581a576ab5bSrab 	nr_grant_frames = 1;
582a576ab5bSrab 	mutex_exit(&gnttab_list_lock);
583551bc2a6Smrj 
584a576ab5bSrab 	max_nr_glist_frames = (max_nr_grant_frames() *
5851213c83cSgarypen 	    GREFS_PER_GRANT_FRAME / RPP);
586551bc2a6Smrj 
587a576ab5bSrab 	set.dom = DOMID_SELF;
588a576ab5bSrab 	set.nr_frames = max_nr_grant_frames();
589a576ab5bSrab 	frames = gnttab_setup(&set);
590551bc2a6Smrj 
591a576ab5bSrab #ifdef XPV_HVM_DRIVER
592a576ab5bSrab 	shared = (grant_entry_t *)xen_alloc_pages(set.nr_frames);
593551bc2a6Smrj 
594551bc2a6Smrj 	gnttab_map();
595551bc2a6Smrj #else /* XPV_HVM_DRIVER */
596a576ab5bSrab 	shared = vmem_xalloc(heap_arena, set.nr_frames * MMU_PAGESIZE,
597a576ab5bSrab 	    MMU_PAGESIZE, 0, 0, 0, 0, VM_SLEEP);
598a576ab5bSrab 	for (i = 0; i < set.nr_frames; i++) {
599a576ab5bSrab 		hat_devload(kas.a_hat, (caddr_t)GT_PGADDR(i), PAGESIZE,
6005b1df0b5Sjohnlev 		    xen_assign_pfn(frames[i]),
6015b1df0b5Sjohnlev 		    PROT_READ | PROT_WRITE | HAT_STORECACHING_OK,
602a576ab5bSrab 		    HAT_LOAD_LOCK);
603a576ab5bSrab 	}
604a576ab5bSrab #endif
605551bc2a6Smrj 
606a576ab5bSrab 	gnttab_list = kmem_alloc(max_nr_glist_frames * sizeof (grant_ref_t *),
607a576ab5bSrab 	    KM_SLEEP);
608843e1988Sjohnlev 
6091213c83cSgarypen 	nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1)
6101213c83cSgarypen 	    / RPP;
6111213c83cSgarypen 	for (i = 0; i < nr_glist_frames; i++) {
612a576ab5bSrab 		gnttab_list[i] = kmem_alloc(PAGESIZE, KM_SLEEP);
613843e1988Sjohnlev 	}
614843e1988Sjohnlev 
615a576ab5bSrab 	kmem_free(frames, set.nr_frames * sizeof (gnttab_frame_t));
616843e1988Sjohnlev 
617a576ab5bSrab 	nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME;
618843e1988Sjohnlev 
619a576ab5bSrab 	for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++)
620a576ab5bSrab 		GNTTAB_ENTRY(i) = i + 1;
621843e1988Sjohnlev 
622a576ab5bSrab 	GNTTAB_ENTRY(nr_init_grefs - 1) = GNTTAB_LIST_END;
623a576ab5bSrab 	gnttab_free_count = nr_init_grefs - NR_RESERVED_ENTRIES;
624a576ab5bSrab 	gnttab_free_head  = NR_RESERVED_ENTRIES;
625843e1988Sjohnlev }
626843e1988Sjohnlev 
627843e1988Sjohnlev void
gnttab_resume(void)628843e1988Sjohnlev gnttab_resume(void)
629843e1988Sjohnlev {
630843e1988Sjohnlev 	gnttab_setup_table_t set;
631843e1988Sjohnlev 	int i;
632a576ab5bSrab 	gnttab_frame_t *frames;
633a576ab5bSrab 	uint_t available_frames = max_nr_grant_frames();
634843e1988Sjohnlev 
635a576ab5bSrab 	if (available_frames < nr_grant_frames) {
636a576ab5bSrab 		cmn_err(CE_PANIC, "Hypervisor does not have enough grant "
637a576ab5bSrab 		    "frames: required(%u), available(%u)", nr_grant_frames,
638a576ab5bSrab 		    available_frames);
639843e1988Sjohnlev 	}
640843e1988Sjohnlev 
641a576ab5bSrab #ifdef XPV_HVM_DRIVER
642a576ab5bSrab 	gnttab_map();
643a576ab5bSrab #endif /* XPV_HVM_DRIVER */
644a576ab5bSrab 
645a576ab5bSrab 	set.dom = DOMID_SELF;
646a576ab5bSrab 	set.nr_frames = available_frames;
647a576ab5bSrab 	frames = gnttab_setup(&set);
648a576ab5bSrab 
649a576ab5bSrab 	for (i = 0; i < available_frames; i++) {
650843e1988Sjohnlev 		(void) HYPERVISOR_update_va_mapping(GT_PGADDR(i),
651843e1988Sjohnlev 		    FRAME_TO_MA(frames[i]) | PT_VALID | PT_WRITABLE,
652843e1988Sjohnlev 		    UVMF_INVLPG | UVMF_ALL);
653843e1988Sjohnlev 	}
654a576ab5bSrab 	kmem_free(frames, set.nr_frames * sizeof (gnttab_frame_t));
655843e1988Sjohnlev }
656843e1988Sjohnlev 
657843e1988Sjohnlev void
gnttab_suspend(void)658843e1988Sjohnlev gnttab_suspend(void)
659843e1988Sjohnlev {
660843e1988Sjohnlev 	int i;
661843e1988Sjohnlev 
662843e1988Sjohnlev 	/*
663843e1988Sjohnlev 	 * clear grant table mappings before suspending
664843e1988Sjohnlev 	 */
665a576ab5bSrab 	for (i = 0; i < max_nr_grant_frames(); i++) {
666843e1988Sjohnlev 		(void) HYPERVISOR_update_va_mapping(GT_PGADDR(i),
667843e1988Sjohnlev 		    0, UVMF_INVLPG);
668843e1988Sjohnlev 	}
669843e1988Sjohnlev }
670843e1988Sjohnlev 
671843e1988Sjohnlev /*
672843e1988Sjohnlev  * Local variables:
673843e1988Sjohnlev  *  c-file-style: "solaris"
674843e1988Sjohnlev  *  indent-tabs-mode: t
675843e1988Sjohnlev  *  c-indent-level: 8
676843e1988Sjohnlev  *  c-basic-offset: 8
677843e1988Sjohnlev  *  tab-width: 8
678843e1988Sjohnlev  * End:
679843e1988Sjohnlev  */
680