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