xref: /netbsd-src/sys/arch/xen/xen/xengnt.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*      $NetBSD: xengnt.c,v 1.5 2007/11/22 16:17:10 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  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by Manuel Bouyer.
17  * 4. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32 
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/malloc.h>
37 #include <sys/queue.h>
38 #include <sys/extent.h>
39 #include <sys/kernel.h>
40 #include <uvm/uvm.h>
41 
42 #include <xen/hypervisor.h>
43 #include <xen/xen.h>
44 #include <xen/granttables.h>
45 
46 /* #define XENDEBUG */
47 #ifdef XENDEBUG
48 #define DPRINTF(x) printf x
49 #else
50 #define DPRINTF(x)
51 #endif
52 
53 #define NR_GRANT_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(grant_entry_t))
54 
55 int gnt_nr_grant_frames;
56 int gnt_max_grant_frames;
57 
58 /* table of free grant entries */
59 grant_ref_t *gnt_entries;
60 int last_gnt_entry;
61 
62 /* VM address of the grant table */
63 grant_entry_t *grant_table;
64 
65 static grant_ref_t xengnt_get_entry(void);
66 #define XENGNT_NO_ENTRY 0xffffffff
67 static void xengnt_free_entry(grant_ref_t);
68 static void xengnt_resume(void);
69 static int xengnt_more_entries(void);
70 
71 void
72 xengnt_init()
73 {
74 	struct gnttab_query_size query;
75 	int rc;
76 	int nr_grant_entries;
77 	int i;
78 
79 	query.dom = DOMID_SELF;
80 	rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
81 	if ((rc < 0) || (query.status != GNTST_okay))
82 		gnt_max_grant_frames = 4; /* Legacy max number of frames */
83 	else
84 		gnt_max_grant_frames = query.max_nr_frames;
85 	gnt_nr_grant_frames = 0;
86 
87 	nr_grant_entries =
88 	    gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE;
89 
90 	grant_table = (void *)uvm_km_alloc(kernel_map,
91 	    gnt_max_grant_frames * PAGE_SIZE, 0, UVM_KMF_VAONLY);
92 	if (grant_table == NULL)
93 		panic("xengnt_init() no VM space");
94 	gnt_entries = malloc((nr_grant_entries + 1) * sizeof(grant_ref_t),
95 	    M_DEVBUF, M_NOWAIT);
96 	if (gnt_entries == NULL)
97 		panic("xengnt_init() no space for bitmask");
98 	for (i = 0; i <= nr_grant_entries; i++)
99 		gnt_entries[i] = XENGNT_NO_ENTRY;
100 
101 	last_gnt_entry = 0;
102 	xengnt_resume();
103 
104 }
105 
106 static void
107 xengnt_resume()
108 {
109 	int previous_nr_grant_frames = gnt_nr_grant_frames;
110 	gnt_nr_grant_frames = 0;
111 	while (gnt_nr_grant_frames < previous_nr_grant_frames) {
112 		if (xengnt_more_entries() != 0)
113 			panic("xengnt_resume: can't restore grant frames");
114 	}
115 }
116 
117 static int
118 xengnt_more_entries()
119 {
120 	gnttab_setup_table_t setup;
121 	unsigned long *pages;
122 	int nframes_new = gnt_nr_grant_frames + 1;
123 	int i;
124 
125 	if (gnt_nr_grant_frames == gnt_max_grant_frames)
126 		return ENOMEM;
127 
128 	pages = malloc(nframes_new * sizeof(long), M_DEVBUF, M_NOWAIT);
129 	if (pages == NULL)
130 		return ENOMEM;
131 
132 	setup.dom = DOMID_SELF;
133 	setup.nr_frames = nframes_new;
134 	setup.frame_list = pages;
135 
136 	if (HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1) != 0)
137 		panic("xengnt_more_entries: setup table failed");
138 	if (setup.status != 0) {
139 		printf("xengnt_more_entries: setup table returned %d\n",
140 		    setup.status);
141 		return ENOMEM;
142 	}
143 
144 	DPRINTF(("xengnt_more_entries: map 0x%lx -> %p\n",
145 	    pages[gnt_nr_grant_frames],
146 	    (char *)grant_table + gnt_nr_grant_frames * PAGE_SIZE));
147 
148 	pmap_kenter_ma(((vaddr_t)grant_table) + gnt_nr_grant_frames * PAGE_SIZE,
149 	    pages[gnt_nr_grant_frames] << PAGE_SHIFT, VM_PROT_WRITE);
150 
151 
152 	for (i = gnt_nr_grant_frames * NR_GRANT_ENTRIES_PER_PAGE;
153 	    i < nframes_new * NR_GRANT_ENTRIES_PER_PAGE;
154 	    i++) {
155 		KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
156 		gnt_entries[last_gnt_entry] = i;
157 		last_gnt_entry++;
158 	}
159 	gnt_nr_grant_frames = nframes_new;
160 	return 0;
161 }
162 
163 static grant_ref_t
164 xengnt_get_entry()
165 {
166 	grant_ref_t entry;
167 	int s = splvm();
168 	static struct timeval xengnt_nonmemtime;
169 	const static struct timeval xengnt_nonmemintvl = {5,0};
170 
171 	if (last_gnt_entry == 0) {
172 		if (xengnt_more_entries()) {
173 			splx(s);
174 			if (ratecheck(&xengnt_nonmemtime, &xengnt_nonmemintvl))
175 				printf("xengnt_get_entry: out of grant "
176 				    "table entries\n");
177 			return XENGNT_NO_ENTRY;
178 		}
179 	}
180 	KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
181 	last_gnt_entry--;
182 	entry = gnt_entries[last_gnt_entry];
183 	gnt_entries[last_gnt_entry] = XENGNT_NO_ENTRY;
184 	splx(s);
185 	KASSERT(entry != XENGNT_NO_ENTRY);
186 	KASSERT(last_gnt_entry >= 0 && last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE);
187 	return entry;
188 }
189 
190 static void
191 xengnt_free_entry(grant_ref_t entry)
192 {
193 	int s = splvm();
194 	KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
195 	KASSERT(last_gnt_entry >= 0 && last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE);
196 	gnt_entries[last_gnt_entry] = entry;
197 	last_gnt_entry++;
198 	splx(s);
199 }
200 
201 int
202 xengnt_grant_access(domid_t dom, paddr_t ma, int ro, grant_ref_t *entryp)
203 {
204 	*entryp = xengnt_get_entry();
205 	if (__predict_false(*entryp == XENGNT_NO_ENTRY))
206 		return ENOMEM;
207 
208 	grant_table[*entryp].frame = ma >> PAGE_SHIFT;
209 	grant_table[*entryp].domid  = dom;
210 	x86_lfence();
211 	grant_table[*entryp].flags =
212 	    GTF_permit_access | (ro ? GTF_readonly : 0);
213 	return 0;
214 }
215 
216 void
217 xengnt_revoke_access(grant_ref_t entry)
218 {
219 	uint16_t flags, nflags;
220 
221 	nflags = grant_table[entry].flags;
222 
223 	do {
224 		if ((flags = nflags) & (GTF_reading|GTF_writing))
225 			panic("xengnt_revoke_access: still in use");
226 		nflags = xen_atomic_cmpxchg16(&grant_table[entry].flags,
227 		    flags, 0);
228 	} while (nflags != flags);
229 	xengnt_free_entry(entry);
230 }
231 
232 int
233 xengnt_grant_transfer(domid_t dom, grant_ref_t *entryp)
234 {
235 	*entryp = xengnt_get_entry();
236 	if (__predict_false(*entryp == XENGNT_NO_ENTRY))
237 		return ENOMEM;
238 
239 	grant_table[*entryp].frame = 0;
240 	grant_table[*entryp].domid  =dom;
241 	x86_lfence();
242 	grant_table[*entryp].flags = GTF_accept_transfer;
243 	return 0;
244 }
245 
246 paddr_t
247 xengnt_revoke_transfer(grant_ref_t entry)
248 {
249 	paddr_t page;
250 	uint16_t flags;
251 
252 	/* if the transfer has not started, free the entry and return 0 */
253 	while (!((flags = grant_table[entry].flags) & GTF_transfer_committed)) {
254 		if (xen_atomic_cmpxchg16(&grant_table[entry].flags,
255 		    flags, 0) == flags ) {
256 			xengnt_free_entry(entry);
257 			return 0;
258 		}
259 		HYPERVISOR_yield();
260 	}
261 
262 	/* If transfer in progress, wait for completion */
263 	while (!((flags = grant_table[entry].flags) & GTF_transfer_completed))
264 		HYPERVISOR_yield();
265 
266 	/* Read the frame number /after/ reading completion status. */
267 	__insn_barrier();
268 	page = grant_table[entry].frame;
269 	if (page == 0)
270 		printf("xengnt_revoke_transfer: guest sent pa 0\n");
271 
272 	xengnt_free_entry(entry);
273 	return page;
274 }
275 
276 int
277 xengnt_status(grant_ref_t entry)
278 {
279 	return (grant_table[entry].flags & (GTF_reading|GTF_writing));
280 }
281