win-pvdrivers

view xenpci/gnttbl.c @ 1099:27bd2a5a4704

License change from GPL to BSD
author James Harper <james.harper@bendigoit.com.au>
date Thu Mar 13 13:38:31 2014 +1100 (2014-03-13)
parents 471c94d04d8a
children
line source
1 /*
2 PV Drivers for Windows Xen HVM Domains
4 Copyright (c) 2014, James Harper
5 All rights reserved.
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * 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 * Neither the name of James Harper nor the
15 names of its contributors may be used to endorse or promote products
16 derived from this software without specific prior written permission.
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL JAMES HARPER BE LIABLE FOR ANY
22 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
30 #include "xenpci.h"
32 VOID
33 GntTbl_PutRef(PVOID Context, grant_ref_t ref, ULONG tag)
34 {
35 PXENPCI_DEVICE_DATA xpdd = Context;
37 UNREFERENCED_PARAMETER(tag);
39 #if DBG
40 if (xpdd->gnttbl_tag[ref].tag != tag)
41 FUNCTION_MSG("Grant Entry %d for %.4s doesn't match %.4s\n", ref, (PUCHAR)&tag, (PUCHAR)&xpdd->gnttbl_tag[ref].tag);
42 XN_ASSERT(xpdd->gnttbl_tag[ref].tag == tag);
43 xpdd->gnttbl_tag[ref].tag = 0;
44 xpdd->gnttbl_tag[ref].generation = (ULONG)-1;
45 #endif
46 stack_push(xpdd->gnttbl_ss, (PVOID)ref);
47 }
49 grant_ref_t
50 GntTbl_GetRef(PVOID Context, ULONG tag)
51 {
52 PXENPCI_DEVICE_DATA xpdd = Context;
53 unsigned int ref;
54 PVOID ptr_ref;
56 UNREFERENCED_PARAMETER(tag);
58 if (!stack_pop(xpdd->gnttbl_ss, &ptr_ref))
59 {
60 FUNCTION_MSG("No free grant refs\n");
61 return INVALID_GRANT_REF;
62 }
63 ref = (grant_ref_t)(ULONG_PTR)ptr_ref;
64 #if DBG
65 if (xpdd->gnttbl_tag[ref].tag)
66 FUNCTION_MSG("Grant Entry %d for %.4s in use by %.4s\n", ref, (PUCHAR)&tag, (PUCHAR)&xpdd->gnttbl_tag[ref].tag);
67 XN_ASSERT(!xpdd->gnttbl_tag[ref].tag);
68 xpdd->gnttbl_tag[ref].generation = xpdd->gnttbl_generation;
69 xpdd->gnttbl_tag[ref].tag = tag;
70 #endif
72 return ref;
73 }
75 int
76 GntTbl_Map(PVOID Context, unsigned int start_idx, unsigned int end_idx)
77 {
78 PXENPCI_DEVICE_DATA xpdd = Context;
79 struct xen_add_to_physmap xatp;
80 unsigned int i = end_idx;
82 FUNCTION_ENTER();
83 /* Loop backwards, so that the first hypercall has the largest index, ensuring that the table will grow only once. */
84 do {
85 xatp.domid = DOMID_SELF;
86 xatp.idx = i;
87 xatp.space = XENMAPSPACE_grant_table;
88 xatp.gpfn = (xen_pfn_t)MmGetMdlPfnArray(xpdd->gnttbl_mdl)[i];
89 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
90 {
91 FUNCTION_MSG("*** ERROR MAPPING FRAME %d ***\n", i);
92 }
93 } while (i-- > start_idx);
94 FUNCTION_EXIT();
96 return 0;
97 }
99 grant_ref_t
100 GntTbl_GrantAccess(
101 PVOID Context,
102 domid_t domid,
103 uint32_t frame, // xen api limits pfn to 32bit, so no guests over 8TB
104 int readonly,
105 grant_ref_t ref,
106 ULONG tag)
107 {
108 PXENPCI_DEVICE_DATA xpdd = Context;
110 if (ref == INVALID_GRANT_REF)
111 ref = GntTbl_GetRef(Context, tag);
112 if (ref == INVALID_GRANT_REF)
113 return ref;
115 XN_ASSERT(xpdd->gnttbl_tag[ref].tag == tag);
117 xpdd->gnttbl_table[ref].frame = frame;
118 xpdd->gnttbl_table[ref].domid = domid;
120 if (xpdd->gnttbl_table[ref].flags)
121 {
122 #if DBG
123 FUNCTION_MSG("Grant Entry %d for %.4s still in use by %.4s\n", ref, (PUCHAR)&tag, (PUCHAR)&xpdd->gnttbl_tag[ref].tag);
124 #else
125 FUNCTION_MSG("Grant Entry %d for %.4s still in use\n", ref, (PUCHAR)&tag);
126 #endif
127 }
128 XN_ASSERT(!xpdd->gnttbl_table[ref].flags);
130 KeMemoryBarrier();
131 readonly *= GTF_readonly;
132 xpdd->gnttbl_table[ref].flags = GTF_permit_access | (uint16_t)readonly;
134 return ref;
135 }
137 BOOLEAN
138 GntTbl_EndAccess(
139 PVOID Context,
140 grant_ref_t ref,
141 BOOLEAN keepref,
142 ULONG tag)
143 {
144 PXENPCI_DEVICE_DATA xpdd = Context;
145 unsigned short flags, nflags;
147 XN_ASSERT(ref != INVALID_GRANT_REF);
148 XN_ASSERT(xpdd->gnttbl_tag[ref].tag == tag);
150 nflags = xpdd->gnttbl_table[ref].flags;
151 do {
152 if ((flags = nflags) & (GTF_reading|GTF_writing))
153 {
154 FUNCTION_MSG("Grant Entry %d for %.4s still use\n", ref, (PUCHAR)&tag);
155 return FALSE;
156 }
157 } while ((nflags = InterlockedCompareExchange16(
158 (volatile SHORT *)&xpdd->gnttbl_table[ref].flags, 0, flags)) != flags);
160 if (!keepref)
161 GntTbl_PutRef(Context, ref, tag);
163 return TRUE;
164 }
166 static unsigned int
167 GntTbl_QueryMaxFrames(PXENPCI_DEVICE_DATA xpdd) {
168 struct gnttab_query_size query;
169 int rc;
171 UNREFERENCED_PARAMETER(xpdd);
172 query.dom = DOMID_SELF;
174 rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
175 if ((rc < 0) || (query.status != GNTST_okay))
176 {
177 FUNCTION_MSG("***CANNOT QUERY MAX GRANT FRAME***\n");
178 return 4; /* Legacy max supported number of frames */
179 }
180 return query.max_nr_frames;
181 }
183 VOID
184 GntTbl_Init(PXENPCI_DEVICE_DATA xpdd) {
185 int i;
186 int grant_entries;
188 XN_ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
190 FUNCTION_ENTER();
192 xpdd->grant_frames = GntTbl_QueryMaxFrames(xpdd);
193 FUNCTION_MSG("grant_frames = %d\n", xpdd->grant_frames);
194 grant_entries = min(NR_GRANT_ENTRIES, (xpdd->grant_frames * PAGE_SIZE / sizeof(grant_entry_t)));
195 FUNCTION_MSG("grant_entries = %d\n", grant_entries);
196 #if DBG
197 xpdd->gnttbl_tag = ExAllocatePoolWithTag(NonPagedPool, grant_entries * sizeof(grant_tag_t), XENPCI_POOL_TAG);
198 RtlZeroMemory(xpdd->gnttbl_tag, grant_entries * sizeof(grant_tag_t));
199 xpdd->gnttbl_tag_copy = ExAllocatePoolWithTag(NonPagedPool, grant_entries * sizeof(grant_tag_t), XENPCI_POOL_TAG);
200 xpdd->gnttbl_generation = 0;
201 #endif
202 xpdd->gnttbl_table_copy = ExAllocatePoolWithTag(NonPagedPool, xpdd->grant_frames * PAGE_SIZE, XENPCI_POOL_TAG);
203 XN_ASSERT(xpdd->gnttbl_table_copy); // lazy
204 xpdd->gnttbl_table = ExAllocatePoolWithTag(NonPagedPool, xpdd->grant_frames * PAGE_SIZE, XENPCI_POOL_TAG);
205 XN_ASSERT(xpdd->gnttbl_table); // lazy
206 /* dom0 crashes if we allocate the wrong amount of memory here! */
207 xpdd->gnttbl_mdl = IoAllocateMdl(xpdd->gnttbl_table, xpdd->grant_frames * PAGE_SIZE, FALSE, FALSE, NULL);
208 XN_ASSERT(xpdd->gnttbl_mdl); // lazy
209 MmBuildMdlForNonPagedPool(xpdd->gnttbl_mdl);
211 /* make some holes for the grant pages to fill in */
212 for (i = 0; i < (int)xpdd->grant_frames; i++)
213 {
214 struct xen_memory_reservation reservation;
215 xen_pfn_t pfn;
216 ULONG ret;
218 reservation.address_bits = 0;
219 reservation.extent_order = 0;
220 reservation.domid = DOMID_SELF;
221 reservation.nr_extents = 1;
222 #pragma warning(disable: 4127) /* conditional expression is constant */
223 pfn = (xen_pfn_t)MmGetMdlPfnArray(xpdd->gnttbl_mdl)[i];
224 FUNCTION_MSG("pfn = %x\n", (ULONG)pfn);
225 set_xen_guest_handle(reservation.extent_start, &pfn);
227 FUNCTION_MSG("Calling HYPERVISOR_memory_op - pfn = %x\n", (ULONG)pfn);
228 ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
229 FUNCTION_MSG("decreased %d pages for grant table frame %d\n", ret, i);
230 }
232 stack_new(&xpdd->gnttbl_ss, grant_entries);
234 for (i = NR_RESERVED_ENTRIES; i < grant_entries; i++)
235 stack_push(xpdd->gnttbl_ss, (PVOID)i);
237 GntTbl_Map(xpdd, 0, xpdd->grant_frames - 1);
239 RtlZeroMemory(xpdd->gnttbl_table, PAGE_SIZE * xpdd->grant_frames);
241 FUNCTION_EXIT();
242 }
244 VOID
245 GntTbl_Suspend(PXENPCI_DEVICE_DATA xpdd)
246 {
247 #if DBG
248 int grant_entries;
249 #endif
250 int i;
252 FUNCTION_ENTER();
254 #if DBG
255 for (i = 0; i < (int)min(NR_GRANT_ENTRIES, (xpdd->grant_frames * PAGE_SIZE / sizeof(grant_entry_t))); i++)
256 {
257 if (xpdd->gnttbl_tag[i].tag != 0) // && xpdd->gnttbl_tag[i].generation < xpdd->gnttbl_generation)
258 {
259 FUNCTION_MSG("grant entry for %.4s from generation %d\n", (PUCHAR)&xpdd->gnttbl_tag[i].tag, xpdd->gnttbl_tag[i].generation);
260 }
261 }
262 xpdd->gnttbl_generation++;
263 #endif
265 /* copy some grant refs and switch to an alternate freelist, but only on hiber */
266 if (KeGetCurrentIrql() <= DISPATCH_LEVEL)
267 {
268 FUNCTION_MSG("backing up grant ref stack\n");
269 for (i = 0; i < HIBER_GREF_COUNT; i++)
270 {
271 xpdd->hiber_grefs[i] = INVALID_GRANT_REF;
272 }
273 for (i = 0; i < HIBER_GREF_COUNT; i++)
274 {
275 if ((xpdd->hiber_grefs[i] = GntTbl_GetRef(xpdd, (ULONG)'HIBR')) == INVALID_GRANT_REF)
276 break;
277 }
278 FUNCTION_MSG("%d grant refs reserved\n", i);
279 xpdd->gnttbl_ss_copy = xpdd->gnttbl_ss;
280 stack_new(&xpdd->gnttbl_ss, HIBER_GREF_COUNT);
281 }
282 else
283 {
284 xpdd->gnttbl_ss_copy = NULL;
285 }
287 memcpy(xpdd->gnttbl_table_copy, xpdd->gnttbl_table, xpdd->grant_frames * PAGE_SIZE);
288 #if DBG
289 /* even though gnttbl_tag is actually preserved, it is used by the dump driver so must be restored to exactly the same state as it was on suspend */
290 grant_entries = min(NR_GRANT_ENTRIES, (xpdd->grant_frames * PAGE_SIZE / sizeof(grant_entry_t)));
291 memcpy(xpdd->gnttbl_tag_copy, xpdd->gnttbl_tag, grant_entries * sizeof(grant_tag_t));
292 #endif
294 /* put the grant entries on the new freelist, after copying the tables above */
295 if (KeGetCurrentIrql() <= DISPATCH_LEVEL)
296 {
297 for (i = 0; i < HIBER_GREF_COUNT; i++)
298 {
299 if (xpdd->hiber_grefs[i] == INVALID_GRANT_REF)
300 break;
301 GntTbl_PutRef(xpdd, xpdd->hiber_grefs[i], (ULONG)'HIBR');
302 }
303 }
305 FUNCTION_EXIT();
306 }
308 VOID
309 GntTbl_Resume(PXENPCI_DEVICE_DATA xpdd)
310 {
311 ULONG new_grant_frames;
312 ULONG result;
313 int i;
314 #if DBG
315 int grant_entries;
316 #endif
318 FUNCTION_ENTER();
320 for (i = 0; i < (int)xpdd->grant_frames; i++)
321 {
322 struct xen_memory_reservation reservation;
323 xen_pfn_t pfn;
324 ULONG ret;
326 reservation.address_bits = 0;
327 reservation.extent_order = 0;
328 reservation.domid = DOMID_SELF;
329 reservation.nr_extents = 1;
330 #pragma warning(disable: 4127) /* conditional expression is constant */
331 pfn = (xen_pfn_t)MmGetMdlPfnArray(xpdd->gnttbl_mdl)[i];
332 FUNCTION_MSG("pfn = %x\n", (ULONG)pfn);
333 set_xen_guest_handle(reservation.extent_start, &pfn);
335 FUNCTION_MSG("Calling HYPERVISOR_memory_op - pfn = %x\n", (ULONG)pfn);
336 ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
337 FUNCTION_MSG("decreased %d pages for grant table frame %d\n", ret, i);
338 }
340 new_grant_frames = GntTbl_QueryMaxFrames(xpdd);
341 FUNCTION_MSG("new_grant_frames = %d\n", new_grant_frames);
342 XN_ASSERT(new_grant_frames >= xpdd->grant_frames); // lazy
343 result = GntTbl_Map(xpdd, 0, xpdd->grant_frames - 1);
344 FUNCTION_MSG("GntTbl_Map result = %d\n", result);
345 memcpy(xpdd->gnttbl_table, xpdd->gnttbl_table_copy, xpdd->grant_frames * PAGE_SIZE);
346 #if DBG
347 grant_entries = min(NR_GRANT_ENTRIES, (xpdd->grant_frames * PAGE_SIZE / sizeof(grant_entry_t)));
348 memcpy(xpdd->gnttbl_tag, xpdd->gnttbl_tag_copy, grant_entries * sizeof(grant_tag_t));
349 #endif
351 /* switch back and put the hiber grants back again */
352 if (xpdd->gnttbl_ss_copy)
353 {
354 FUNCTION_MSG("restoring grant ref stack\n");
355 stack_delete(xpdd->gnttbl_ss, NULL, NULL);
356 xpdd->gnttbl_ss = xpdd->gnttbl_ss_copy;
357 for (i = 0; i < HIBER_GREF_COUNT; i++)
358 {
359 if (xpdd->hiber_grefs[i] == INVALID_GRANT_REF)
360 break;
361 GntTbl_PutRef(xpdd, xpdd->hiber_grefs[i], (ULONG)'HIBR');
362 }
363 xpdd->gnttbl_ss_copy = NULL;
364 }
366 FUNCTION_EXIT();
367 }