xref: /netbsd-src/sys/arch/xen/xen/xengnt.c (revision 1b9578b8c2c1f848eeb16dabbfd7d1f0d9fdefbd)
1 /*      $NetBSD: xengnt.c,v 1.19 2011/06/15 19:51:50 rmind 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.19 2011/06/15 19:51:50 rmind 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 void xengnt_resume(void);
71 static int xengnt_more_entries(void);
72 
73 void
74 xengnt_init(void)
75 {
76 	struct gnttab_query_size query;
77 	int rc;
78 	int nr_grant_entries;
79 	int i;
80 
81 	query.dom = DOMID_SELF;
82 	rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
83 	if ((rc < 0) || (query.status != GNTST_okay))
84 		gnt_max_grant_frames = 4; /* Legacy max number of frames */
85 	else
86 		gnt_max_grant_frames = query.max_nr_frames;
87 	gnt_nr_grant_frames = 0;
88 
89 	nr_grant_entries =
90 	    gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE;
91 
92 	grant_table = (void *)uvm_km_alloc(kernel_map,
93 	    gnt_max_grant_frames * PAGE_SIZE, 0, UVM_KMF_VAONLY);
94 	if (grant_table == NULL)
95 		panic("xengnt_init() no VM space");
96 	gnt_entries = malloc((nr_grant_entries + 1) * sizeof(grant_ref_t),
97 	    M_DEVBUF, M_NOWAIT);
98 	if (gnt_entries == NULL)
99 		panic("xengnt_init() no space for bitmask");
100 	for (i = 0; i <= nr_grant_entries; i++)
101 		gnt_entries[i] = XENGNT_NO_ENTRY;
102 
103 	last_gnt_entry = 0;
104 	xengnt_resume();
105 
106 }
107 
108 /*
109  * Resume grant table state
110  */
111 static void
112 xengnt_resume(void)
113 {
114 	int previous_nr_grant_frames = gnt_nr_grant_frames;
115 	gnt_nr_grant_frames = 0;
116 	while (gnt_nr_grant_frames < previous_nr_grant_frames) {
117 		if (xengnt_more_entries() != 0)
118 			panic("xengnt_resume: can't restore grant frames");
119 	}
120 }
121 
122 /*
123  * Add another page to the grant table
124  * Returns 0 on success, ENOMEM on failure
125  */
126 static int
127 xengnt_more_entries(void)
128 {
129 	gnttab_setup_table_t setup;
130 	u_long *pages;
131 	int nframes_new = gnt_nr_grant_frames + 1;
132 	int i;
133 
134 	if (gnt_nr_grant_frames == gnt_max_grant_frames)
135 		return ENOMEM;
136 
137 	pages = malloc(nframes_new * sizeof(u_long), M_DEVBUF, M_NOWAIT);
138 	if (pages == NULL)
139 		return ENOMEM;
140 
141 	setup.dom = DOMID_SELF;
142 	setup.nr_frames = nframes_new;
143 	xenguest_handle(setup.frame_list) = pages;
144 
145 	/*
146 	 * setup the grant table, made of nframes_new frames
147 	 * and return the list of their virtual addresses
148 	 * in 'pages'
149 	 */
150 	if (HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1) != 0)
151 		panic("%s: setup table failed", __func__);
152 	if (setup.status != GNTST_okay) {
153 		aprint_error("%s: setup table returned %d\n",
154 		    __func__, setup.status);
155 		free(pages, M_DEVBUF);
156 		return ENOMEM;
157 	}
158 
159 	DPRINTF(("xengnt_more_entries: map 0x%lx -> %p\n",
160 	    pages[gnt_nr_grant_frames],
161 	    (char *)grant_table + gnt_nr_grant_frames * PAGE_SIZE));
162 
163 	/*
164 	 * map between grant_table addresses and the machine addresses of
165 	 * the grant table frames
166 	 */
167 	pmap_kenter_ma(((vaddr_t)grant_table) + gnt_nr_grant_frames * PAGE_SIZE,
168 	    ((paddr_t)pages[gnt_nr_grant_frames]) << PAGE_SHIFT,
169 	    VM_PROT_WRITE, 0);
170 	pmap_update(pmap_kernel());
171 
172 	/*
173 	 * add the grant entries associated to the last grant table frame
174 	 * and mark them as free
175 	 */
176 	for (i = gnt_nr_grant_frames * NR_GRANT_ENTRIES_PER_PAGE;
177 	    i < nframes_new * NR_GRANT_ENTRIES_PER_PAGE;
178 	    i++) {
179 		KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
180 		gnt_entries[last_gnt_entry] = i;
181 		last_gnt_entry++;
182 	}
183 	gnt_nr_grant_frames = nframes_new;
184 	free(pages, M_DEVBUF);
185 	return 0;
186 }
187 
188 /*
189  * Returns a reference to the first free entry in grant table
190  */
191 static grant_ref_t
192 xengnt_get_entry(void)
193 {
194 	grant_ref_t entry;
195 	int s = splvm();
196 	static struct timeval xengnt_nonmemtime;
197 	static const struct timeval xengnt_nonmemintvl = {5,0};
198 
199 	if (last_gnt_entry == 0) {
200 		if (xengnt_more_entries()) {
201 			splx(s);
202 			if (ratecheck(&xengnt_nonmemtime, &xengnt_nonmemintvl))
203 				printf("xengnt_get_entry: out of grant "
204 				    "table entries\n");
205 			return XENGNT_NO_ENTRY;
206 		}
207 	}
208 	KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
209 	last_gnt_entry--;
210 	entry = gnt_entries[last_gnt_entry];
211 	gnt_entries[last_gnt_entry] = XENGNT_NO_ENTRY;
212 	splx(s);
213 	KASSERT(entry != XENGNT_NO_ENTRY);
214 	KASSERT(last_gnt_entry >= 0);
215 	KASSERT(last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE);
216 	return entry;
217 }
218 
219 /*
220  * Mark the grant table entry as free
221  */
222 static void
223 xengnt_free_entry(grant_ref_t entry)
224 {
225 	int s = splvm();
226 	KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
227 	KASSERT(last_gnt_entry >= 0);
228 	KASSERT(last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE);
229 	gnt_entries[last_gnt_entry] = entry;
230 	last_gnt_entry++;
231 	splx(s);
232 }
233 
234 int
235 xengnt_grant_access(domid_t dom, paddr_t ma, int ro, grant_ref_t *entryp)
236 {
237 	*entryp = xengnt_get_entry();
238 	if (__predict_false(*entryp == XENGNT_NO_ENTRY))
239 		return ENOMEM;
240 
241 	grant_table[*entryp].frame = ma >> PAGE_SHIFT;
242 	grant_table[*entryp].domid = dom;
243 	/*
244 	 * ensure that the above values reach global visibility
245 	 * before permitting frame's access (done when we set flags)
246 	 */
247 	xen_rmb();
248 	grant_table[*entryp].flags =
249 	    GTF_permit_access | (ro ? GTF_readonly : 0);
250 	return 0;
251 }
252 
253 void
254 xengnt_revoke_access(grant_ref_t entry)
255 {
256 	uint16_t flags, nflags;
257 
258 	nflags = grant_table[entry].flags;
259 
260 	do {
261 		if ((flags = nflags) & (GTF_reading|GTF_writing))
262 			panic("xengnt_revoke_access: still in use");
263 		nflags = xen_atomic_cmpxchg16(&grant_table[entry].flags,
264 		    flags, 0);
265 	} while (nflags != flags);
266 	xengnt_free_entry(entry);
267 }
268 
269 int
270 xengnt_grant_transfer(domid_t dom, grant_ref_t *entryp)
271 {
272 	*entryp = xengnt_get_entry();
273 	if (__predict_false(*entryp == XENGNT_NO_ENTRY))
274 		return ENOMEM;
275 
276 	grant_table[*entryp].frame = 0;
277 	grant_table[*entryp].domid = dom;
278 	/*
279 	 * ensure that the above values reach global visibility
280 	 * before permitting frame's transfer (done when we set flags)
281 	 */
282 	xen_rmb();
283 	grant_table[*entryp].flags = GTF_accept_transfer;
284 	return 0;
285 }
286 
287 paddr_t
288 xengnt_revoke_transfer(grant_ref_t entry)
289 {
290 	paddr_t page;
291 	uint16_t flags;
292 
293 	/* if the transfer has not started, free the entry and return 0 */
294 	while (!((flags = grant_table[entry].flags) & GTF_transfer_committed)) {
295 		if (xen_atomic_cmpxchg16(&grant_table[entry].flags,
296 		    flags, 0) == flags ) {
297 			xengnt_free_entry(entry);
298 			return 0;
299 		}
300 		HYPERVISOR_yield();
301 	}
302 
303 	/* If transfer in progress, wait for completion */
304 	while (!((flags = grant_table[entry].flags) & GTF_transfer_completed))
305 		HYPERVISOR_yield();
306 
307 	/* Read the frame number /after/ reading completion status. */
308 	__insn_barrier();
309 	page = grant_table[entry].frame;
310 	if (page == 0)
311 		printf("xengnt_revoke_transfer: guest sent pa 0\n");
312 
313 	xengnt_free_entry(entry);
314 	return page;
315 }
316 
317 int
318 xengnt_status(grant_ref_t entry)
319 {
320 	return (grant_table[entry].flags & (GTF_reading|GTF_writing));
321 }
322