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