win-pvdrivers

view xenpci/xenpci_fdo.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 05efa89a4ef7
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"
31 #include <stdlib.h>
32 #include <aux_klib.h>
34 #define SYSRQ_PATH "control/sysrq"
35 #define SHUTDOWN_PATH "control/shutdown"
36 #define BALLOON_PATH "memory/target"
38 //extern PMDL balloon_mdl_head;
40 /* Not really necessary but keeps PREfast happy */
41 static EVT_WDF_WORKITEM XenPci_SuspendResume;
42 #if (VER_PRODUCTBUILD >= 7600)
43 static KSTART_ROUTINE XenPci_BalloonThreadProc;
44 #endif
46 static VOID
47 XenPci_MapHalThenPatchKernel(PXENPCI_DEVICE_DATA xpdd)
48 {
49 NTSTATUS status;
50 PAUX_MODULE_EXTENDED_INFO amei;
51 ULONG module_info_buffer_size;
52 ULONG i;
54 FUNCTION_ENTER();
56 amei = NULL;
57 /* buffer size could change between requesting and allocating - need to loop until we are successful */
58 while ((status = AuxKlibQueryModuleInformation(&module_info_buffer_size, sizeof(AUX_MODULE_EXTENDED_INFO), amei)) == STATUS_BUFFER_TOO_SMALL || amei == NULL)
59 {
60 if (amei != NULL)
61 ExFreePoolWithTag(amei, XENPCI_POOL_TAG);
62 amei = ExAllocatePoolWithTag(NonPagedPool, module_info_buffer_size, XENPCI_POOL_TAG);
63 }
65 FUNCTION_MSG("AuxKlibQueryModuleInformation = %d\n", status);
66 for (i = 0; i < module_info_buffer_size / sizeof(AUX_MODULE_EXTENDED_INFO); i++)
67 {
68 if (strcmp((PCHAR)amei[i].FullPathName + amei[i].FileNameOffset, "hal.dll") == 0)
69 {
70 FUNCTION_MSG("hal.dll found at %p - %p\n",
71 amei[i].BasicInfo.ImageBase,
72 ((PUCHAR)amei[i].BasicInfo.ImageBase) + amei[i].ImageSize);
73 XenPci_PatchKernel(xpdd, amei[i].BasicInfo.ImageBase, amei[i].ImageSize);
74 }
75 }
76 ExFreePoolWithTag(amei, XENPCI_POOL_TAG);
77 FUNCTION_EXIT();
78 }
80 /*
81 * Alloc MMIO from the device's MMIO region. There is no corresponding free() fn
82 */
83 PHYSICAL_ADDRESS
84 XenPci_AllocMMIO(PXENPCI_DEVICE_DATA xpdd, ULONG len)
85 {
86 PHYSICAL_ADDRESS addr;
88 len = (len + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
90 addr = xpdd->platform_mmio_addr;
91 addr.QuadPart += xpdd->platform_mmio_alloc;
92 xpdd->platform_mmio_alloc += len;
94 XN_ASSERT(xpdd->platform_mmio_alloc <= xpdd->platform_mmio_len);
96 return addr;
97 }
99 extern ULONG tpr_patch_requested;
101 NTSTATUS
102 XenPci_EvtDeviceQueryRemove(WDFDEVICE device)
103 {
104 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
105 NTSTATUS status;
107 FUNCTION_ENTER();
108 if (xpdd->removable)
109 status = STATUS_SUCCESS;
110 else
111 status = STATUS_UNSUCCESSFUL;
112 FUNCTION_EXIT();
113 return status;
114 }
116 static NTSTATUS
117 XenPci_Init(PXENPCI_DEVICE_DATA xpdd)
118 {
119 struct xen_add_to_physmap xatp;
120 int ret;
122 FUNCTION_ENTER();
124 if (!hypercall_stubs)
125 return STATUS_UNSUCCESSFUL;
127 if (!xpdd->shared_info_area)
128 {
129 XN_ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
130 /* this should be safe as this part will never be called on resume where IRQL == HIGH_LEVEL */
131 xpdd->shared_info_area_unmapped = XenPci_AllocMMIO(xpdd, PAGE_SIZE);
132 xpdd->shared_info_area = MmMapIoSpace(xpdd->shared_info_area_unmapped,
133 PAGE_SIZE, MmNonCached);
134 }
135 FUNCTION_MSG("shared_info_area_unmapped.QuadPart = %lx\n", xpdd->shared_info_area_unmapped.QuadPart);
136 xatp.domid = DOMID_SELF;
137 xatp.idx = 0;
138 xatp.space = XENMAPSPACE_shared_info;
139 xatp.gpfn = (xen_pfn_t)(xpdd->shared_info_area_unmapped.QuadPart >> PAGE_SHIFT);
140 FUNCTION_MSG("gpfn = %x\n", xatp.gpfn);
141 ret = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp);
142 FUNCTION_MSG("hypervisor memory op (XENMAPSPACE_shared_info) ret = %d\n", ret);
144 FUNCTION_EXIT();
146 return STATUS_SUCCESS;
147 }
149 static NTSTATUS
150 XenPci_Resume(PXENPCI_DEVICE_DATA xpdd)
151 {
152 return XenPci_Init(xpdd);
153 }
155 static VOID
156 XenPci_SysrqHandler(char *path, PVOID context) {
157 PXENPCI_DEVICE_DATA xpdd = context;
158 char *value;
159 char letter;
161 UNREFERENCED_PARAMETER(path);
163 FUNCTION_ENTER();
165 XenBus_Read(xpdd, XBT_NIL, SYSRQ_PATH, &value);
167 FUNCTION_MSG("SysRq Value = %s\n", value);
169 if (!value || !strlen(value)) {
170 letter = 0;
171 } else {
172 letter = *value;
173 }
175 if (value != NULL) {
176 XenPci_FreeMem(value);
177 }
179 switch (letter) {
180 case 0:
181 break;
182 case 'B': /* cause a bug check */
183 #pragma warning(suppress:28159)
184 KeBugCheckEx(('X' << 16)|('E' << 8)|('N'), 0x00000001, 0x00000000, 0x00000000, 0x00000000);
185 break;
186 case 'A': /* cause an assert */
187 #pragma warning(suppress:28138)
188 XN_ASSERT(letter != 'A');
189 break;
190 default:
191 FUNCTION_MSG("Unhandled sysrq letter %c\n", letter);
192 break;
193 }
195 FUNCTION_EXIT();
196 }
198 static VOID
199 XenPci_BalloonThreadProc(PVOID StartContext)
200 {
201 PXENPCI_DEVICE_DATA xpdd = StartContext;
202 ULONG new_target_kb = xpdd->current_memory_kb;
203 LARGE_INTEGER timeout;
204 PLARGE_INTEGER ptimeout;
205 PMDL head;
206 PMDL mdl;
207 struct xen_memory_reservation reservation;
208 xen_pfn_t *pfns;
209 int i;
210 ULONG ret;
211 int pfn_count;
212 int timeout_ms = 1000;
213 DECLARE_CONST_UNICODE_STRING(low_mem_name, L"\\KernelObjects\\LowMemoryCondition");
214 PKEVENT low_mem_event;
215 HANDLE low_mem_handle;
216 BOOLEAN hit_initial_target = FALSE;
218 FUNCTION_ENTER();
220 head = NULL;
222 low_mem_event = IoCreateNotificationEvent((PUNICODE_STRING)&low_mem_name, &low_mem_handle);
223 //high_commit_event = IoCreateNotificationEvent((PUNICODE_STRING)&high_commit_name, &high_commit_handle);
224 //max_commit_event = IoCreateNotificationEvent((PUNICODE_STRING)&max_commit_name, &max_commit_handle);
226 for(;;) {
227 /* back off exponentially if we have adjustments to make and we have already hit our initial target, or wait for event if we don't */
228 if (xpdd->current_memory_kb != new_target_kb) {
229 if (!hit_initial_target) {
230 timeout_ms = 0;
231 }
232 timeout.QuadPart = WDF_REL_TIMEOUT_IN_MS(timeout_ms);
233 ptimeout = &timeout;
234 timeout_ms <<= 1;
235 if (timeout_ms > 60000)
236 timeout_ms = 60000;
237 } else {
238 hit_initial_target = TRUE;
239 ptimeout = NULL;
240 timeout_ms = 1000;
241 }
242 KeWaitForSingleObject(&xpdd->balloon_event, Executive, KernelMode, FALSE, ptimeout);
243 if (xpdd->balloon_shutdown)
244 PsTerminateSystemThread(0);
245 FUNCTION_MSG("Got balloon event, current = %d, target = %d\n", xpdd->current_memory_kb, xpdd->target_memory_kb);
246 /* not really worried about races here, but cache target so we only read it once */
247 new_target_kb = xpdd->target_memory_kb;
248 // perform some sanity checks on target_memory
249 // make sure target <= initial
250 // make sure target > some % of initial
252 if (xpdd->current_memory_kb == new_target_kb) {
253 FUNCTION_MSG("No change to memory\n");
254 continue;
255 } else if (xpdd->current_memory_kb < new_target_kb) {
256 FUNCTION_MSG("Trying to take %d KB from Xen\n", new_target_kb - xpdd->current_memory_kb);
257 while ((mdl = head) != NULL && xpdd->current_memory_kb < new_target_kb) {
258 pfn_count = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(mdl), MmGetMdlByteCount(mdl));
259 pfns = ExAllocatePoolWithTag(NonPagedPool, pfn_count * sizeof(xen_pfn_t), XENPCI_POOL_TAG);
260 /* sizeof(xen_pfn_t) may not be the same as PPFN_NUMBER */
261 for (i = 0; i < pfn_count; i++)
262 pfns[i] = (xen_pfn_t)(MmGetMdlPfnArray(mdl)[i]);
263 reservation.address_bits = 0;
264 reservation.extent_order = 0;
265 reservation.domid = DOMID_SELF;
266 reservation.nr_extents = pfn_count;
267 #pragma warning(disable: 4127) /* conditional expression is constant */
268 set_xen_guest_handle(reservation.extent_start, pfns);
270 //FUNCTION_MSG("Calling HYPERVISOR_memory_op(XENMEM_populate_physmap) - pfn_count = %d\n", pfn_count);
271 ret = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation);
272 //FUNCTION_MSG("populated %d pages\n", ret);
273 if (ret < (ULONG)pfn_count) {
274 if (ret > 0) {
275 /* We hit the Xen hard limit: reprobe. */
276 reservation.nr_extents = ret;
277 ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
278 FUNCTION_MSG("decreased %d pages (xen is out of pages)\n", ret);
279 }
280 ExFreePoolWithTag(pfns, XENPCI_POOL_TAG);
281 break;
282 }
283 ExFreePoolWithTag(pfns, XENPCI_POOL_TAG);
284 head = mdl->Next;
285 mdl->Next = NULL;
286 MmFreePagesFromMdl(mdl);
287 ExFreePool(mdl);
288 xpdd->current_memory_kb += BALLOON_UNITS_KB;
289 }
290 } else {
291 FUNCTION_MSG("Trying to give %d KB to Xen\n", xpdd->current_memory_kb - new_target_kb);
292 while (xpdd->current_memory_kb > new_target_kb) {
293 PHYSICAL_ADDRESS alloc_low;
294 PHYSICAL_ADDRESS alloc_high;
295 PHYSICAL_ADDRESS alloc_skip;
296 alloc_low.QuadPart = 0;
297 alloc_high.QuadPart = 0xFFFFFFFFFFFFFFFFULL;
298 alloc_skip.QuadPart = 0;
300 if (!hit_initial_target && low_mem_event && KeReadStateEvent(low_mem_event)) {
301 FUNCTION_MSG("Low memory condition exists. Waiting.\n");
302 break;
303 }
305 #if (NTDDI_VERSION >= NTDDI_WS03SP1)
306 /* our contract says that we must zero pages before returning to xen, so we can't use MM_DONT_ZERO_ALLOCATION */
307 mdl = MmAllocatePagesForMdlEx(alloc_low, alloc_high, alloc_skip, BALLOON_UNITS_KB * 1024, MmCached, 0);
308 #else
309 mdl = MmAllocatePagesForMdl(alloc_low, alloc_high, alloc_skip, BALLOON_UNITS_KB * 1024);
310 #endif
311 if (!mdl) {
312 FUNCTION_MSG("Allocation failed - try again soon\n");
313 break;
314 } else {
315 int i;
316 ULONG ret;
317 int pfn_count = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(mdl), MmGetMdlByteCount(mdl));
318 if (pfn_count != BALLOON_UNIT_PAGES) {
319 /* we could probably do this better but it will only happen in low memory conditions... */
320 FUNCTION_MSG("wanted %d pages got %d pages\n", BALLOON_UNIT_PAGES, pfn_count);
321 MmFreePagesFromMdl(mdl);
322 ExFreePool(mdl);
323 break;
324 }
325 pfns = ExAllocatePoolWithTag(NonPagedPool, pfn_count * sizeof(xen_pfn_t), XENPCI_POOL_TAG);
326 /* sizeof(xen_pfn_t) may not be the same as PPFN_NUMBER */
327 for (i = 0; i < pfn_count; i++)
328 pfns[i] = (xen_pfn_t)(MmGetMdlPfnArray(mdl)[i]);
329 reservation.address_bits = 0;
330 reservation.extent_order = 0;
331 reservation.domid = DOMID_SELF;
332 reservation.nr_extents = pfn_count;
333 #pragma warning(disable: 4127) /* conditional expression is constant */
334 set_xen_guest_handle(reservation.extent_start, pfns);
336 ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
337 ExFreePoolWithTag(pfns, XENPCI_POOL_TAG);
338 if (head) {
339 mdl->Next = head;
340 head = mdl;
341 } else {
342 head = mdl;
343 }
344 xpdd->current_memory_kb -= BALLOON_UNITS_KB;
345 }
346 }
347 }
348 FUNCTION_MSG("Memory = %d, Balloon Target = %d\n", xpdd->current_memory_kb, new_target_kb);
349 }
350 }
352 static VOID
353 XenPci_BalloonHandler(char *path, PVOID context) {
354 WDFDEVICE device = context;
355 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
356 char *value;
358 UNREFERENCED_PARAMETER(path);
360 FUNCTION_ENTER();
362 XenBus_Read(xpdd, XBT_NIL, BALLOON_PATH, &value);
364 if (value == NULL) {
365 FUNCTION_MSG("Failed to read balloon target value\n");
366 FUNCTION_EXIT();
367 return;
368 }
370 if (atoi(value) > 0)
371 xpdd->target_memory_kb = atoi(value);
373 FUNCTION_MSG("target memory value = %d (%s)\n", xpdd->target_memory_kb, value);
375 XenPci_FreeMem(value);
377 KeSetEvent(&xpdd->balloon_event, IO_NO_INCREMENT, FALSE);
379 FUNCTION_EXIT();
380 }
382 static VOID
383 XenPci_Suspend0(PVOID context)
384 {
385 PXENPCI_DEVICE_DATA xpdd = context;
386 ULONG cancelled;
387 ULONGLONG sysenter_cs, sysenter_esp, sysenter_eip;
389 FUNCTION_ENTER();
391 GntTbl_Suspend(xpdd);
393 sysenter_cs = __readmsr(0x174);
394 sysenter_esp = __readmsr(0x175);
395 sysenter_eip = __readmsr(0x176);
397 cancelled = hvm_shutdown(SHUTDOWN_suspend);
399 /* this code was to fix a bug that existed in Xen for a short time... it is harmless but can probably be removed */
400 if (__readmsr(0x174) != sysenter_cs) {
401 FUNCTION_MSG("sysenter_cs not restored. Fixing.\n");
402 __writemsr(0x174, sysenter_cs);
403 }
404 if (__readmsr(0x175) != sysenter_esp) {
405 FUNCTION_MSG("sysenter_esp not restored. Fixing.\n");
406 __writemsr(0x175, sysenter_esp);
407 }
408 if (__readmsr(0x176) != sysenter_eip) {
409 FUNCTION_MSG("sysenter_eip not restored. Fixing.\n");
410 __writemsr(0x176, sysenter_eip);
411 }
413 FUNCTION_MSG("back from suspend, cancelled = %d\n", cancelled);
415 if (qemu_hide_flags_value) {
416 XenPci_HideQemuDevices();
417 }
419 XenPci_Resume(xpdd);
420 GntTbl_Resume(xpdd);
421 EvtChn_Resume(xpdd); /* this enables interrupts again too */
423 FUNCTION_EXIT();
424 }
426 static VOID
427 XenPci_SuspendN(PVOID context)
428 {
429 UNREFERENCED_PARAMETER(context);
431 FUNCTION_ENTER();
432 FUNCTION_MSG("doing nothing on cpu N\n");
433 FUNCTION_EXIT();
434 }
436 static VOID
437 XenPci_SuspendEvtDpc(PVOID context);
438 static NTSTATUS
439 XenPci_ConnectSuspendEvt(PXENPCI_DEVICE_DATA xpdd);
441 /* called at PASSIVE_LEVEL */
442 static NTSTATUS
443 XenPci_ConnectSuspendEvt(PXENPCI_DEVICE_DATA xpdd) {
444 CHAR path[128];
446 xpdd->suspend_evtchn = EvtChn_AllocUnbound(xpdd, 0);
447 FUNCTION_MSG("suspend event channel = %d\n", xpdd->suspend_evtchn);
448 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "device/suspend/event-channel");
449 XenBus_Printf(xpdd, XBT_NIL, path, "%d", xpdd->suspend_evtchn);
450 EvtChn_BindDpc(xpdd, xpdd->suspend_evtchn, XenPci_SuspendEvtDpc, xpdd->wdf_device, EVT_ACTION_FLAGS_NO_SUSPEND);
452 return STATUS_SUCCESS;
453 }
455 /* Called at PASSIVE_LEVEL */
456 static VOID
457 XenPci_SuspendResume(WDFWORKITEM workitem) {
458 NTSTATUS status;
459 //KAFFINITY ActiveProcessorMask = 0; // this is for Vista+
460 WDFDEVICE device = WdfWorkItemGetParentObject(workitem);
461 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
462 WDFCHILDLIST child_list = WdfFdoGetDefaultChildList(device);
463 WDF_CHILD_LIST_ITERATOR child_iterator;
464 WDFDEVICE child_device;
466 FUNCTION_ENTER();
468 if (xpdd->suspend_state == SUSPEND_STATE_NONE) {
469 ExAcquireFastMutex(&xpdd->suspend_mutex);
470 xpdd->suspend_state = SUSPEND_STATE_SCHEDULED;
471 KeMemoryBarrier();
473 // how to prevent device addition etc here? is it implied because dom0 initiated the suspend?
474 WDF_CHILD_LIST_ITERATOR_INIT(&child_iterator, WdfRetrievePresentChildren);
476 WdfChildListBeginIteration(child_list, &child_iterator);
477 while ((status = WdfChildListRetrieveNextDevice(child_list, &child_iterator, &child_device, NULL)) == STATUS_SUCCESS) {
478 XenPci_SuspendPdo(child_device);
479 }
480 WdfChildListEndIteration(child_list, &child_iterator);
482 XenBus_Suspend(xpdd);
483 EvtChn_Suspend(xpdd);
484 XenPci_HighSync(XenPci_Suspend0, XenPci_SuspendN, xpdd);
486 xpdd->suspend_state = SUSPEND_STATE_RESUMING;
487 XenBus_Resume(xpdd);
489 XenPci_ConnectSuspendEvt(xpdd);
491 WdfChildListBeginIteration(child_list, &child_iterator);
492 while ((status = WdfChildListRetrieveNextDevice(child_list, &child_iterator, &child_device, NULL)) == STATUS_SUCCESS) {
493 XenPci_ResumePdo(child_device);
494 }
495 WdfChildListEndIteration(child_list, &child_iterator);
497 xpdd->suspend_state = SUSPEND_STATE_NONE;
498 ExReleaseFastMutex(&xpdd->suspend_mutex);
499 }
500 FUNCTION_EXIT();
501 }
503 /* called at DISPATCH_LEVEL */
504 static VOID
505 XenPci_SuspendEvtDpc(PVOID context)
506 {
507 NTSTATUS status;
508 WDFDEVICE device = context;
509 //KIRQL old_irql;
510 WDF_OBJECT_ATTRIBUTES attributes;
511 WDF_WORKITEM_CONFIG workitem_config;
512 WDFWORKITEM workitem;
514 FUNCTION_MSG("Suspend detected via Dpc\n");
515 WDF_WORKITEM_CONFIG_INIT(&workitem_config, XenPci_SuspendResume);
516 WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
517 attributes.ParentObject = device;
518 status = WdfWorkItemCreate(&workitem_config, &attributes, &workitem);
519 if (status != STATUS_SUCCESS) {
520 /* how should we fail here */
521 FUNCTION_MSG("WdfWorkItemCreate failed\n");
522 return;
523 }
524 WdfWorkItemEnqueue(workitem);
525 }
527 static void
528 XenPci_ShutdownHandler(char *path, PVOID context)
529 {
530 NTSTATUS status;
531 WDFDEVICE device = context;
532 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
533 char *res;
534 char *value;
535 //KIRQL old_irql;
536 WDF_OBJECT_ATTRIBUTES attributes;
537 WDF_WORKITEM_CONFIG workitem_config;
538 WDFWORKITEM workitem;
540 UNREFERENCED_PARAMETER(path);
542 FUNCTION_ENTER();
544 res = XenBus_Read(xpdd, XBT_NIL, SHUTDOWN_PATH, &value);
545 if (res)
546 {
547 FUNCTION_MSG("Error reading shutdown path - %s\n", res);
548 XenPci_FreeMem(res);
549 FUNCTION_EXIT();
550 return;
551 }
553 FUNCTION_MSG("Shutdown value = %s\n", value);
555 if (strlen(value) && strcmp(value, "suspend") == 0)
556 {
557 {
558 FUNCTION_MSG("Suspend detected\n");
559 /* we have to queue this as a work item as we stop the xenbus thread, which we are currently running in! */
560 WDF_WORKITEM_CONFIG_INIT(&workitem_config, XenPci_SuspendResume);
561 WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
562 attributes.ParentObject = device;
563 status = WdfWorkItemCreate(&workitem_config, &attributes, &workitem);
564 // TODO: check status here
565 WdfWorkItemEnqueue(workitem);
566 }
567 }
569 XenPci_FreeMem(value);
571 FUNCTION_EXIT();
572 }
574 static VOID
575 XenPci_DeviceWatchHandler(char *path, PVOID context)
576 {
577 char **bits;
578 int count;
579 char *err;
580 char *value;
581 PXENPCI_DEVICE_DATA xpdd = context;
583 FUNCTION_ENTER();
585 bits = SplitString(path, '/', 4, &count);
586 if (count == 3)
587 {
588 err = XenBus_Read(xpdd, XBT_NIL, path, &value);
589 if (err)
590 {
591 /* obviously path no longer exists, in which case the removal is being taken care of elsewhere and we shouldn't invalidate now */
592 XenPci_FreeMem(err);
593 }
594 else
595 {
596 XenPci_FreeMem(value);
597 /* we probably have to be a bit smarter here and do nothing if xenpci isn't running yet */
598 FUNCTION_MSG("Rescanning child list\n");
599 XenPci_EvtChildListScanForChildren(xpdd->child_list);
600 }
601 }
602 FreeSplitString(bits, count);
604 FUNCTION_EXIT();
605 }
607 NTSTATUS
608 XenPci_EvtDevicePrepareHardware (WDFDEVICE device, WDFCMRESLIST resources_raw, WDFCMRESLIST resources_translated)
609 {
610 NTSTATUS status = STATUS_SUCCESS;
611 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
612 PCM_PARTIAL_RESOURCE_DESCRIPTOR raw_descriptor, translated_descriptor;
613 ULONG i;
615 FUNCTION_ENTER();
617 XN_ASSERT(WdfCmResourceListGetCount(resources_raw) == WdfCmResourceListGetCount(resources_translated));
619 for (i = 0; i < WdfCmResourceListGetCount(resources_raw); i++)
620 {
621 raw_descriptor = WdfCmResourceListGetDescriptor(resources_raw, i);
622 translated_descriptor = WdfCmResourceListGetDescriptor(resources_translated, i);
623 switch (raw_descriptor->Type) {
624 case CmResourceTypePort:
625 FUNCTION_MSG("IoPort Address(%x) Length: %d\n", translated_descriptor->u.Port.Start.LowPart, translated_descriptor->u.Port.Length);
626 xpdd->platform_ioport_addr = translated_descriptor->u.Port.Start.LowPart;
627 xpdd->platform_ioport_len = translated_descriptor->u.Port.Length;
628 break;
629 case CmResourceTypeMemory:
630 FUNCTION_MSG("Memory mapped CSR:(%x:%x) Length:(%d)\n", translated_descriptor->u.Memory.Start.LowPart, translated_descriptor->u.Memory.Start.HighPart, translated_descriptor->u.Memory.Length);
631 FUNCTION_MSG("Memory flags = %04X\n", translated_descriptor->Flags);
632 xpdd->platform_mmio_addr = translated_descriptor->u.Memory.Start;
633 xpdd->platform_mmio_len = translated_descriptor->u.Memory.Length;
634 xpdd->platform_mmio_flags = translated_descriptor->Flags;
635 break;
636 case CmResourceTypeInterrupt:
637 xpdd->irq_level = (KIRQL)translated_descriptor->u.Interrupt.Level;
638 xpdd->irq_vector = translated_descriptor->u.Interrupt.Vector;
639 xpdd->irq_affinity = translated_descriptor->u.Interrupt.Affinity;
640 xpdd->irq_mode = (translated_descriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED)?Latched:LevelSensitive;
641 xpdd->irq_number = raw_descriptor->u.Interrupt.Vector;
642 FUNCTION_MSG("irq_number = %03x\n", raw_descriptor->u.Interrupt.Vector);
643 FUNCTION_MSG("irq_vector = %03x\n", translated_descriptor->u.Interrupt.Vector);
644 FUNCTION_MSG("irq_level = %03x\n", translated_descriptor->u.Interrupt.Level);
645 FUNCTION_MSG("irq_mode = %s\n", (xpdd->irq_mode == Latched)?"Latched":"LevelSensitive");
646 switch(translated_descriptor->ShareDisposition)
647 {
648 case CmResourceShareDeviceExclusive:
649 FUNCTION_MSG("ShareDisposition = CmResourceShareDeviceExclusive\n");
650 break;
651 case CmResourceShareDriverExclusive:
652 FUNCTION_MSG("ShareDisposition = CmResourceShareDriverExclusive\n");
653 break;
654 case CmResourceShareShared:
655 FUNCTION_MSG("ShareDisposition = CmResourceShareShared\n");
656 break;
657 default:
658 FUNCTION_MSG("ShareDisposition = %d\n", translated_descriptor->ShareDisposition);
659 break;
660 }
661 break;
662 case CmResourceTypeDevicePrivate:
663 FUNCTION_MSG("Private Data: 0x%02x 0x%02x 0x%02x\n", translated_descriptor->u.DevicePrivate.Data[0], translated_descriptor->u.DevicePrivate.Data[1], translated_descriptor->u.DevicePrivate.Data[2]);
664 break;
665 default:
666 FUNCTION_MSG("Unhandled resource type (0x%x)\n", translated_descriptor->Type);
667 break;
668 }
669 }
671 FUNCTION_EXIT();
673 return status;
674 }
676 NTSTATUS
677 XenPci_EvtDeviceD0Entry(WDFDEVICE device, WDF_POWER_DEVICE_STATE previous_state)
678 {
679 NTSTATUS status = STATUS_SUCCESS;
680 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
682 FUNCTION_ENTER();
684 xpdd->hibernated = FALSE;
685 switch (previous_state)
686 {
687 case WdfPowerDeviceD0:
688 FUNCTION_MSG("WdfPowerDeviceD1\n");
689 break;
690 case WdfPowerDeviceD1:
691 FUNCTION_MSG("WdfPowerDeviceD1\n");
692 break;
693 case WdfPowerDeviceD2:
694 FUNCTION_MSG("WdfPowerDeviceD2\n");
695 break;
696 case WdfPowerDeviceD3:
697 FUNCTION_MSG("WdfPowerDeviceD3\n");
698 break;
699 case WdfPowerDeviceD3Final:
700 FUNCTION_MSG("WdfPowerDeviceD3Final\n");
701 break;
702 case WdfPowerDevicePrepareForHibernation:
703 FUNCTION_MSG("WdfPowerDevicePrepareForHibernation\n");
704 break;
705 default:
706 FUNCTION_MSG("Unknown WdfPowerDevice state %d\n", previous_state);
707 break;
708 }
710 if (previous_state == WdfPowerDevicePrepareForHibernation && qemu_hide_flags_value) {
711 XenPci_HideQemuDevices();
712 }
714 if (previous_state == WdfPowerDeviceD3Final) {
715 XenPci_Init(xpdd);
716 if (tpr_patch_requested && !xpdd->tpr_patched) {
717 XenPci_MapHalThenPatchKernel(xpdd);
718 xpdd->tpr_patched = TRUE;
719 xpdd->removable = FALSE;
720 }
721 GntTbl_Init(xpdd);
722 EvtChn_Init(xpdd);
723 } else {
724 XenPci_Resume(xpdd);
725 GntTbl_Resume(xpdd);
726 EvtChn_Resume(xpdd);
727 }
729 FUNCTION_EXIT();
731 return status;
732 }
734 NTSTATUS
735 XenPci_EvtDeviceD0EntryPostInterruptsEnabled(WDFDEVICE device, WDF_POWER_DEVICE_STATE previous_state)
736 {
737 NTSTATUS status = STATUS_SUCCESS;
738 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
739 PCHAR response;
740 HANDLE thread_handle;
742 UNREFERENCED_PARAMETER(previous_state);
744 FUNCTION_ENTER();
746 if (previous_state == WdfPowerDeviceD3Final)
747 {
748 XenBus_Init(xpdd);
750 XenPci_ConnectSuspendEvt(xpdd);
752 response = XenBus_AddWatch(xpdd, XBT_NIL, SYSRQ_PATH, XenPci_SysrqHandler, xpdd);
754 response = XenBus_AddWatch(xpdd, XBT_NIL, SHUTDOWN_PATH, XenPci_ShutdownHandler, device);
756 response = XenBus_AddWatch(xpdd, XBT_NIL, "device", XenPci_DeviceWatchHandler, xpdd);
758 /* prime target as current until the watch gets kicked off */
759 xpdd->target_memory_kb = xpdd->current_memory_kb;
760 xpdd->balloon_shutdown = FALSE;
761 status = PsCreateSystemThread(&thread_handle, THREAD_ALL_ACCESS, NULL, NULL, NULL, XenPci_BalloonThreadProc, xpdd);
762 if (!NT_SUCCESS(status)) {
763 FUNCTION_MSG("Could not start balloon thread\n");
764 return status;
765 }
766 response = XenBus_AddWatch(xpdd, XBT_NIL, BALLOON_PATH, XenPci_BalloonHandler, device);
767 status = ObReferenceObjectByHandle(thread_handle, THREAD_ALL_ACCESS, NULL, KernelMode, &xpdd->balloon_thread, NULL);
768 ZwClose(thread_handle);
769 } else {
770 XenBus_Resume(xpdd);
771 XenPci_ConnectSuspendEvt(xpdd);
772 }
773 FUNCTION_EXIT();
775 return status;
776 }
778 NTSTATUS
779 XenPci_EvtDeviceD0ExitPreInterruptsDisabled(WDFDEVICE device, WDF_POWER_DEVICE_STATE target_state)
780 {
781 NTSTATUS status = STATUS_SUCCESS;
782 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
783 LARGE_INTEGER timeout;
785 FUNCTION_ENTER();
787 switch (target_state)
788 {
789 case WdfPowerDeviceD0:
790 FUNCTION_MSG("WdfPowerDeviceD1\n");
791 break;
792 case WdfPowerDeviceD1:
793 FUNCTION_MSG("WdfPowerDeviceD1\n");
794 break;
795 case WdfPowerDeviceD2:
796 FUNCTION_MSG("WdfPowerDeviceD2\n");
797 break;
798 case WdfPowerDeviceD3:
799 FUNCTION_MSG("WdfPowerDeviceD3\n");
800 break;
801 case WdfPowerDeviceD3Final:
802 FUNCTION_MSG("WdfPowerDeviceD3Final\n");
803 break;
804 case WdfPowerDevicePrepareForHibernation:
805 FUNCTION_MSG("WdfPowerDevicePrepareForHibernation\n");
806 break;
807 default:
808 FUNCTION_MSG("Unknown WdfPowerDevice state %d\n", target_state);
809 break;
810 }
812 if (target_state == WdfPowerDeviceD3Final) {
813 FUNCTION_MSG("Shutting down threads\n");
815 xpdd->balloon_shutdown = TRUE;
816 KeSetEvent(&xpdd->balloon_event, IO_NO_INCREMENT, FALSE);
818 timeout.QuadPart = (LONGLONG)-1 * 1000 * 1000 * 10;
819 while ((status = KeWaitForSingleObject(xpdd->balloon_thread, Executive, KernelMode, FALSE, &timeout)) != STATUS_SUCCESS)
820 {
821 timeout.QuadPart = (LONGLONG)-1 * 1000 * 1000 * 10;
822 FUNCTION_MSG("Waiting for balloon thread to stop\n");
823 }
824 ObDereferenceObject(xpdd->balloon_thread);
826 XenBus_Halt(xpdd);
827 }
828 else
829 {
830 XenBus_Suspend(xpdd);
831 }
833 FUNCTION_EXIT();
835 return status;
836 }
838 NTSTATUS
839 XenPci_EvtDeviceD0Exit(WDFDEVICE device, WDF_POWER_DEVICE_STATE target_state) {
840 NTSTATUS status = STATUS_SUCCESS;
841 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
843 FUNCTION_ENTER();
845 switch (target_state) {
846 case WdfPowerDeviceD0:
847 FUNCTION_MSG("WdfPowerDeviceD1\n");
848 break;
849 case WdfPowerDeviceD1:
850 FUNCTION_MSG("WdfPowerDeviceD1\n");
851 break;
852 case WdfPowerDeviceD2:
853 FUNCTION_MSG("WdfPowerDeviceD2\n");
854 break;
855 case WdfPowerDeviceD3:
856 FUNCTION_MSG("WdfPowerDeviceD3\n");
857 break;
858 case WdfPowerDeviceD3Final:
859 FUNCTION_MSG("WdfPowerDeviceD3Final\n");
860 break;
861 case WdfPowerDevicePrepareForHibernation:
862 FUNCTION_MSG("WdfPowerDevicePrepareForHibernation\n");
863 xpdd->hibernated = TRUE;
864 break;
865 default:
866 FUNCTION_MSG("Unknown WdfPowerDevice state %d\n", target_state);
867 break;
868 }
870 if (target_state == WdfPowerDeviceD3Final) {
871 /* we don't really support exit here */
872 } else {
873 EvtChn_Suspend(xpdd);
874 GntTbl_Suspend(xpdd);
875 }
877 FUNCTION_EXIT();
879 return status;
880 }
882 NTSTATUS
883 XenPci_EvtDeviceReleaseHardware(WDFDEVICE device, WDFCMRESLIST resources_translated)
884 {
885 NTSTATUS status = STATUS_SUCCESS;
887 UNREFERENCED_PARAMETER(device);
888 UNREFERENCED_PARAMETER(resources_translated);
890 FUNCTION_ENTER();
891 FUNCTION_EXIT();
893 return status;
894 }
896 /* Called at PASSIVE_LEVEL but with pagefile unavailable */
897 /* Can be called concurrently, but KMDF takes care of concurrent calls to WdfChildListXxx */
898 VOID
899 XenPci_EvtChildListScanForChildren(WDFCHILDLIST child_list)
900 {
901 NTSTATUS status;
902 PXENPCI_DEVICE_DATA xpdd = GetXpdd(WdfChildListGetDevice(child_list));
903 char *msg;
904 char **devices;
905 char **instances;
906 ULONG i, j;
907 CHAR path[128];
908 XENPCI_PDO_IDENTIFICATION_DESCRIPTION child_description;
909 PVOID entry;
910 WDFDEVICE child_device;
911 WDF_CHILD_RETRIEVE_INFO retrieve_info;
913 FUNCTION_ENTER();
915 WdfChildListBeginScan(child_list);
917 msg = XenBus_List(xpdd, XBT_NIL, "device", &devices);
918 if (!msg)
919 {
920 for (i = 0; devices[i]; i++)
921 {
922 /* make sure the key is not in the veto list */
923 for (entry = xpdd->veto_list.Flink; entry != &xpdd->veto_list; entry = ((PLIST_ENTRY)entry)->Flink)
924 {
925 if (!strcmp(devices[i], (PCHAR)entry + sizeof(LIST_ENTRY)))
926 break;
927 }
928 if (entry != &xpdd->veto_list)
929 {
930 XenPci_FreeMem(devices[i]);
931 continue;
932 }
934 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "device/%s", devices[i]);
935 msg = XenBus_List(xpdd, XBT_NIL, path, &instances);
936 if (!msg)
937 {
938 for (j = 0; instances[j]; j++)
939 {
940 /* the device comparison is done as a memory compare so zero-ing the structure is important */
941 RtlZeroMemory(&child_description, sizeof(child_description));
942 WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&child_description.header, sizeof(child_description));
943 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "device/%s/%s", devices[i], instances[j]);
944 FUNCTION_MSG("Found path = %s\n", path);
945 RtlStringCbCopyA(child_description.path, ARRAY_SIZE(child_description.path), path);
946 RtlStringCbCopyA(child_description.device, ARRAY_SIZE(child_description.device), devices[i]);
947 child_description.index = atoi(instances[j]);
948 WDF_CHILD_RETRIEVE_INFO_INIT(&retrieve_info, &child_description.header);
949 child_device = WdfChildListRetrievePdo(child_list, &retrieve_info);
950 if (child_device)
951 {
952 PXENPCI_PDO_DEVICE_DATA xppdd = GetXppdd(child_device);
953 char *err;
954 char *value;
955 char backend_state_path[128];
957 if (xppdd->do_not_enumerate)
958 {
959 RtlStringCbPrintfA(backend_state_path, ARRAY_SIZE(path), "%s/state", xppdd->backend_path);
961 err = XenBus_Read(xpdd, XBT_NIL, backend_state_path, &value);
962 if (err)
963 {
964 XenPci_FreeMem(err);
965 xppdd->backend_state = XenbusStateUnknown;
966 }
967 else
968 {
969 xppdd->backend_state = atoi(value);
970 XenPci_FreeMem(value);
971 }
972 if (xppdd->backend_state == XenbusStateClosing || xppdd->backend_state == XenbusStateClosed)
973 {
974 FUNCTION_MSG("Surprise removing %s due to backend initiated remove\n", path);
975 XenPci_FreeMem(instances[j]);
976 continue;
977 }
978 else
979 {
980 /* I guess we are being added again ... */
981 xppdd->backend_initiated_remove = FALSE;
982 xppdd->do_not_enumerate = FALSE;
983 }
984 }
985 }
986 status = WdfChildListAddOrUpdateChildDescriptionAsPresent(child_list, &child_description.header, NULL);
987 if (!NT_SUCCESS(status))
988 {
989 FUNCTION_MSG("WdfChildListAddOrUpdateChildDescriptionAsPresent failed with status 0x%08x\n", status);
990 }
991 XenPci_FreeMem(instances[j]);
992 }
993 XenPci_FreeMem(instances);
994 }
995 else
996 {
997 // wtf do we do here???
998 FUNCTION_MSG("Failed to list %s tree\n", devices[i]);
999 }
1000 XenPci_FreeMem(devices[i]);
1002 XenPci_FreeMem(devices);
1004 else
1006 // wtf do we do here???
1007 FUNCTION_MSG("Failed to list device tree\n");
1010 WdfChildListEndScan(child_list);
1012 FUNCTION_EXIT();