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