win-pvdrivers

view xenpci/xenpci_fdo.c @ 1093:5be1f70687ad

Refactor xenpci init to fix a bug when drivers are installed on a non-xen machine
author James Harper <james.harper@bendigoit.com.au>
date Mon Jan 13 20:24:56 2014 +1100 (2014-01-13)
parents 471c94d04d8a
children 05efa89a4ef7
line source
1 /*
2 PV Drivers for Windows Xen HVM Domains
3 Copyright (C) 2007 James Harper
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
20 #include "xenpci.h"
21 #include <stdlib.h>
22 #include <aux_klib.h>
24 #define SYSRQ_PATH "control/sysrq"
25 #define SHUTDOWN_PATH "control/shutdown"
26 #define BALLOON_PATH "memory/target"
28 //extern PMDL balloon_mdl_head;
30 /* Not really necessary but keeps PREfast happy */
31 static EVT_WDF_WORKITEM XenPci_SuspendResume;
32 #if (VER_PRODUCTBUILD >= 7600)
33 static KSTART_ROUTINE XenPci_BalloonThreadProc;
34 #endif
36 static VOID
37 XenPci_MapHalThenPatchKernel(PXENPCI_DEVICE_DATA xpdd)
38 {
39 NTSTATUS status;
40 PAUX_MODULE_EXTENDED_INFO amei;
41 ULONG module_info_buffer_size;
42 ULONG i;
44 FUNCTION_ENTER();
46 amei = NULL;
47 /* buffer size could change between requesting and allocating - need to loop until we are successful */
48 while ((status = AuxKlibQueryModuleInformation(&module_info_buffer_size, sizeof(AUX_MODULE_EXTENDED_INFO), amei)) == STATUS_BUFFER_TOO_SMALL || amei == NULL)
49 {
50 if (amei != NULL)
51 ExFreePoolWithTag(amei, XENPCI_POOL_TAG);
52 amei = ExAllocatePoolWithTag(NonPagedPool, module_info_buffer_size, XENPCI_POOL_TAG);
53 }
55 FUNCTION_MSG("AuxKlibQueryModuleInformation = %d\n", status);
56 for (i = 0; i < module_info_buffer_size / sizeof(AUX_MODULE_EXTENDED_INFO); i++)
57 {
58 if (strcmp((PCHAR)amei[i].FullPathName + amei[i].FileNameOffset, "hal.dll") == 0)
59 {
60 FUNCTION_MSG("hal.dll found at %p - %p\n",
61 amei[i].BasicInfo.ImageBase,
62 ((PUCHAR)amei[i].BasicInfo.ImageBase) + amei[i].ImageSize);
63 XenPci_PatchKernel(xpdd, amei[i].BasicInfo.ImageBase, amei[i].ImageSize);
64 }
65 }
66 ExFreePoolWithTag(amei, XENPCI_POOL_TAG);
67 FUNCTION_EXIT();
68 }
70 /*
71 * Alloc MMIO from the device's MMIO region. There is no corresponding free() fn
72 */
73 PHYSICAL_ADDRESS
74 XenPci_AllocMMIO(PXENPCI_DEVICE_DATA xpdd, ULONG len)
75 {
76 PHYSICAL_ADDRESS addr;
78 len = (len + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
80 addr = xpdd->platform_mmio_addr;
81 addr.QuadPart += xpdd->platform_mmio_alloc;
82 xpdd->platform_mmio_alloc += len;
84 XN_ASSERT(xpdd->platform_mmio_alloc <= xpdd->platform_mmio_len);
86 return addr;
87 }
89 extern ULONG tpr_patch_requested;
91 NTSTATUS
92 XenPci_EvtDeviceQueryRemove(WDFDEVICE device)
93 {
94 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
95 NTSTATUS status;
97 FUNCTION_ENTER();
98 if (xpdd->removable)
99 status = STATUS_SUCCESS;
100 else
101 status = STATUS_UNSUCCESSFUL;
102 FUNCTION_EXIT();
103 return status;
104 }
106 static NTSTATUS
107 XenPci_Init(PXENPCI_DEVICE_DATA xpdd)
108 {
109 struct xen_add_to_physmap xatp;
110 int ret;
112 FUNCTION_ENTER();
114 if (!hypercall_stubs)
115 return STATUS_UNSUCCESSFUL;
117 if (!xpdd->shared_info_area)
118 {
119 XN_ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
120 /* this should be safe as this part will never be called on resume where IRQL == HIGH_LEVEL */
121 xpdd->shared_info_area_unmapped = XenPci_AllocMMIO(xpdd, PAGE_SIZE);
122 xpdd->shared_info_area = MmMapIoSpace(xpdd->shared_info_area_unmapped,
123 PAGE_SIZE, MmNonCached);
124 }
125 FUNCTION_MSG("shared_info_area_unmapped.QuadPart = %lx\n", xpdd->shared_info_area_unmapped.QuadPart);
126 xatp.domid = DOMID_SELF;
127 xatp.idx = 0;
128 xatp.space = XENMAPSPACE_shared_info;
129 xatp.gpfn = (xen_pfn_t)(xpdd->shared_info_area_unmapped.QuadPart >> PAGE_SHIFT);
130 FUNCTION_MSG("gpfn = %x\n", xatp.gpfn);
131 ret = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp);
132 FUNCTION_MSG("hypervisor memory op (XENMAPSPACE_shared_info) ret = %d\n", ret);
134 FUNCTION_EXIT();
136 return STATUS_SUCCESS;
137 }
139 static NTSTATUS
140 XenPci_Resume(PXENPCI_DEVICE_DATA xpdd)
141 {
142 return XenPci_Init(xpdd);
143 }
145 static VOID
146 XenPci_SysrqHandler(char *path, PVOID context) {
147 PXENPCI_DEVICE_DATA xpdd = context;
148 char *value;
149 char letter;
150 char *res;
152 UNREFERENCED_PARAMETER(path);
154 FUNCTION_ENTER();
156 XenBus_Read(xpdd, XBT_NIL, SYSRQ_PATH, &value);
158 FUNCTION_MSG("SysRq Value = %s\n", value);
160 if (value != NULL && strlen(value) != 0) {
161 letter = *value;
162 res = XenBus_Write(xpdd, XBT_NIL, SYSRQ_PATH, "");
163 if (res) {
164 FUNCTION_MSG("Error writing sysrq path\n");
165 XenPci_FreeMem(res);
166 return;
167 }
168 } else {
169 letter = 0;
170 }
172 if (value != NULL) {
173 XenPci_FreeMem(value);
174 }
176 switch (letter) {
177 case 0:
178 break;
179 case 'B': /* cause a bug check */
180 #pragma warning(suppress:28159)
181 KeBugCheckEx(('X' << 16)|('E' << 8)|('N'), 0x00000001, 0x00000000, 0x00000000, 0x00000000);
182 break;
183 case 'A': /* cause an assert */
184 #pragma warning(suppress:28138)
185 XN_ASSERT(letter != 'A');
186 break;
187 default:
188 FUNCTION_MSG("Unhandled sysrq letter %c\n", letter);
189 break;
190 }
192 FUNCTION_EXIT();
193 }
195 static VOID
196 XenPci_BalloonThreadProc(PVOID StartContext)
197 {
198 PXENPCI_DEVICE_DATA xpdd = StartContext;
199 ULONG new_target_kb = xpdd->current_memory_kb;
200 LARGE_INTEGER timeout;
201 PLARGE_INTEGER ptimeout;
202 PMDL head;
203 PMDL mdl;
204 struct xen_memory_reservation reservation;
205 xen_pfn_t *pfns;
206 int i;
207 ULONG ret;
208 int pfn_count;
209 int timeout_ms = 1000;
210 DECLARE_CONST_UNICODE_STRING(low_mem_name, L"\\KernelObjects\\LowMemoryCondition");
211 PKEVENT low_mem_event;
212 HANDLE low_mem_handle;
213 BOOLEAN hit_initial_target = FALSE;
215 FUNCTION_ENTER();
217 head = NULL;
219 low_mem_event = IoCreateNotificationEvent((PUNICODE_STRING)&low_mem_name, &low_mem_handle);
220 //high_commit_event = IoCreateNotificationEvent((PUNICODE_STRING)&high_commit_name, &high_commit_handle);
221 //max_commit_event = IoCreateNotificationEvent((PUNICODE_STRING)&max_commit_name, &max_commit_handle);
223 for(;;) {
224 /* 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 */
225 if (xpdd->current_memory_kb != new_target_kb) {
226 if (!hit_initial_target) {
227 timeout_ms = 0;
228 }
229 timeout.QuadPart = WDF_REL_TIMEOUT_IN_MS(timeout_ms);
230 ptimeout = &timeout;
231 timeout_ms <<= 1;
232 if (timeout_ms > 60000)
233 timeout_ms = 60000;
234 } else {
235 hit_initial_target = TRUE;
236 ptimeout = NULL;
237 timeout_ms = 1000;
238 }
239 KeWaitForSingleObject(&xpdd->balloon_event, Executive, KernelMode, FALSE, ptimeout);
240 if (xpdd->balloon_shutdown)
241 PsTerminateSystemThread(0);
242 FUNCTION_MSG("Got balloon event, current = %d, target = %d\n", xpdd->current_memory_kb, xpdd->target_memory_kb);
243 /* not really worried about races here, but cache target so we only read it once */
244 new_target_kb = xpdd->target_memory_kb;
245 // perform some sanity checks on target_memory
246 // make sure target <= initial
247 // make sure target > some % of initial
249 if (xpdd->current_memory_kb == new_target_kb) {
250 FUNCTION_MSG("No change to memory\n");
251 continue;
252 } else if (xpdd->current_memory_kb < new_target_kb) {
253 FUNCTION_MSG("Trying to take %d KB from Xen\n", new_target_kb - xpdd->current_memory_kb);
254 while ((mdl = head) != NULL && xpdd->current_memory_kb < new_target_kb) {
255 pfn_count = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(mdl), MmGetMdlByteCount(mdl));
256 pfns = ExAllocatePoolWithTag(NonPagedPool, pfn_count * sizeof(xen_pfn_t), XENPCI_POOL_TAG);
257 /* sizeof(xen_pfn_t) may not be the same as PPFN_NUMBER */
258 for (i = 0; i < pfn_count; i++)
259 pfns[i] = (xen_pfn_t)(MmGetMdlPfnArray(mdl)[i]);
260 reservation.address_bits = 0;
261 reservation.extent_order = 0;
262 reservation.domid = DOMID_SELF;
263 reservation.nr_extents = pfn_count;
264 #pragma warning(disable: 4127) /* conditional expression is constant */
265 set_xen_guest_handle(reservation.extent_start, pfns);
267 //FUNCTION_MSG("Calling HYPERVISOR_memory_op(XENMEM_populate_physmap) - pfn_count = %d\n", pfn_count);
268 ret = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation);
269 //FUNCTION_MSG("populated %d pages\n", ret);
270 if (ret < (ULONG)pfn_count) {
271 if (ret > 0) {
272 /* We hit the Xen hard limit: reprobe. */
273 reservation.nr_extents = ret;
274 ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
275 FUNCTION_MSG("decreased %d pages (xen is out of pages)\n", ret);
276 }
277 ExFreePoolWithTag(pfns, XENPCI_POOL_TAG);
278 break;
279 }
280 ExFreePoolWithTag(pfns, XENPCI_POOL_TAG);
281 head = mdl->Next;
282 mdl->Next = NULL;
283 MmFreePagesFromMdl(mdl);
284 ExFreePool(mdl);
285 xpdd->current_memory_kb += BALLOON_UNITS_KB;
286 }
287 } else {
288 FUNCTION_MSG("Trying to give %d KB to Xen\n", xpdd->current_memory_kb - new_target_kb);
289 while (xpdd->current_memory_kb > new_target_kb) {
290 PHYSICAL_ADDRESS alloc_low;
291 PHYSICAL_ADDRESS alloc_high;
292 PHYSICAL_ADDRESS alloc_skip;
293 alloc_low.QuadPart = 0;
294 alloc_high.QuadPart = 0xFFFFFFFFFFFFFFFFULL;
295 alloc_skip.QuadPart = 0;
297 if (!hit_initial_target && low_mem_event && KeReadStateEvent(low_mem_event)) {
298 FUNCTION_MSG("Low memory condition exists. Waiting.\n");
299 break;
300 }
302 #if (NTDDI_VERSION >= NTDDI_WS03SP1)
303 /* our contract says that we must zero pages before returning to xen, so we can't use MM_DONT_ZERO_ALLOCATION */
304 mdl = MmAllocatePagesForMdlEx(alloc_low, alloc_high, alloc_skip, BALLOON_UNITS_KB * 1024, MmCached, 0);
305 #else
306 mdl = MmAllocatePagesForMdl(alloc_low, alloc_high, alloc_skip, BALLOON_UNITS_KB * 1024);
307 #endif
308 if (!mdl) {
309 FUNCTION_MSG("Allocation failed - try again soon\n");
310 break;
311 } else {
312 int i;
313 ULONG ret;
314 int pfn_count = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(mdl), MmGetMdlByteCount(mdl));
315 if (pfn_count != BALLOON_UNIT_PAGES) {
316 /* we could probably do this better but it will only happen in low memory conditions... */
317 FUNCTION_MSG("wanted %d pages got %d pages\n", BALLOON_UNIT_PAGES, pfn_count);
318 MmFreePagesFromMdl(mdl);
319 ExFreePool(mdl);
320 break;
321 }
322 pfns = ExAllocatePoolWithTag(NonPagedPool, pfn_count * sizeof(xen_pfn_t), XENPCI_POOL_TAG);
323 /* sizeof(xen_pfn_t) may not be the same as PPFN_NUMBER */
324 for (i = 0; i < pfn_count; i++)
325 pfns[i] = (xen_pfn_t)(MmGetMdlPfnArray(mdl)[i]);
326 reservation.address_bits = 0;
327 reservation.extent_order = 0;
328 reservation.domid = DOMID_SELF;
329 reservation.nr_extents = pfn_count;
330 #pragma warning(disable: 4127) /* conditional expression is constant */
331 set_xen_guest_handle(reservation.extent_start, pfns);
333 ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
334 ExFreePoolWithTag(pfns, XENPCI_POOL_TAG);
335 if (head) {
336 mdl->Next = head;
337 head = mdl;
338 } else {
339 head = mdl;
340 }
341 xpdd->current_memory_kb -= BALLOON_UNITS_KB;
342 }
343 }
344 }
345 FUNCTION_MSG("Memory = %d, Balloon Target = %d\n", xpdd->current_memory_kb, new_target_kb);
346 }
347 }
349 static VOID
350 XenPci_BalloonHandler(char *path, PVOID context) {
351 WDFDEVICE device = context;
352 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
353 char *value;
355 UNREFERENCED_PARAMETER(path);
357 FUNCTION_ENTER();
359 XenBus_Read(xpdd, XBT_NIL, BALLOON_PATH, &value);
361 if (value == NULL) {
362 FUNCTION_MSG("Failed to read balloon target value\n");
363 FUNCTION_EXIT();
364 return;
365 }
367 if (atoi(value) > 0)
368 xpdd->target_memory_kb = atoi(value);
370 FUNCTION_MSG("target memory value = %d (%s)\n", xpdd->target_memory_kb, value);
372 XenPci_FreeMem(value);
374 KeSetEvent(&xpdd->balloon_event, IO_NO_INCREMENT, FALSE);
376 FUNCTION_EXIT();
377 }
379 static VOID
380 XenPci_Suspend0(PVOID context)
381 {
382 PXENPCI_DEVICE_DATA xpdd = context;
383 ULONG cancelled;
384 ULONGLONG sysenter_cs, sysenter_esp, sysenter_eip;
386 FUNCTION_ENTER();
388 GntTbl_Suspend(xpdd);
390 sysenter_cs = __readmsr(0x174);
391 sysenter_esp = __readmsr(0x175);
392 sysenter_eip = __readmsr(0x176);
394 cancelled = hvm_shutdown(SHUTDOWN_suspend);
396 /* this code was to fix a bug that existed in Xen for a short time... it is harmless but can probably be removed */
397 if (__readmsr(0x174) != sysenter_cs) {
398 FUNCTION_MSG("sysenter_cs not restored. Fixing.\n");
399 __writemsr(0x174, sysenter_cs);
400 }
401 if (__readmsr(0x175) != sysenter_esp) {
402 FUNCTION_MSG("sysenter_esp not restored. Fixing.\n");
403 __writemsr(0x175, sysenter_esp);
404 }
405 if (__readmsr(0x176) != sysenter_eip) {
406 FUNCTION_MSG("sysenter_eip not restored. Fixing.\n");
407 __writemsr(0x176, sysenter_eip);
408 }
410 FUNCTION_MSG("back from suspend, cancelled = %d\n", cancelled);
412 if (qemu_hide_flags_value) {
413 XenPci_HideQemuDevices();
414 }
416 XenPci_Resume(xpdd);
417 GntTbl_Resume(xpdd);
418 EvtChn_Resume(xpdd); /* this enables interrupts again too */
420 FUNCTION_EXIT();
421 }
423 static VOID
424 XenPci_SuspendN(PVOID context)
425 {
426 UNREFERENCED_PARAMETER(context);
428 FUNCTION_ENTER();
429 FUNCTION_MSG("doing nothing on cpu N\n");
430 FUNCTION_EXIT();
431 }
433 static VOID
434 XenPci_SuspendEvtDpc(PVOID context);
435 static NTSTATUS
436 XenPci_ConnectSuspendEvt(PXENPCI_DEVICE_DATA xpdd);
438 /* called at PASSIVE_LEVEL */
439 static NTSTATUS
440 XenPci_ConnectSuspendEvt(PXENPCI_DEVICE_DATA xpdd) {
441 CHAR path[128];
443 xpdd->suspend_evtchn = EvtChn_AllocUnbound(xpdd, 0);
444 FUNCTION_MSG("suspend event channel = %d\n", xpdd->suspend_evtchn);
445 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "device/suspend/event-channel");
446 XenBus_Printf(xpdd, XBT_NIL, path, "%d", xpdd->suspend_evtchn);
447 EvtChn_BindDpc(xpdd, xpdd->suspend_evtchn, XenPci_SuspendEvtDpc, xpdd->wdf_device, EVT_ACTION_FLAGS_NO_SUSPEND);
449 return STATUS_SUCCESS;
450 }
452 /* Called at PASSIVE_LEVEL */
453 static VOID
454 XenPci_SuspendResume(WDFWORKITEM workitem) {
455 NTSTATUS status;
456 //KAFFINITY ActiveProcessorMask = 0; // this is for Vista+
457 WDFDEVICE device = WdfWorkItemGetParentObject(workitem);
458 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
459 WDFCHILDLIST child_list = WdfFdoGetDefaultChildList(device);
460 WDF_CHILD_LIST_ITERATOR child_iterator;
461 WDFDEVICE child_device;
463 FUNCTION_ENTER();
465 if (xpdd->suspend_state == SUSPEND_STATE_NONE) {
466 ExAcquireFastMutex(&xpdd->suspend_mutex);
467 xpdd->suspend_state = SUSPEND_STATE_SCHEDULED;
468 KeMemoryBarrier();
470 // how to prevent device addition etc here? is it implied because dom0 initiated the suspend?
471 WDF_CHILD_LIST_ITERATOR_INIT(&child_iterator, WdfRetrievePresentChildren);
473 WdfChildListBeginIteration(child_list, &child_iterator);
474 while ((status = WdfChildListRetrieveNextDevice(child_list, &child_iterator, &child_device, NULL)) == STATUS_SUCCESS) {
475 XenPci_SuspendPdo(child_device);
476 }
477 WdfChildListEndIteration(child_list, &child_iterator);
479 XenBus_Suspend(xpdd);
480 EvtChn_Suspend(xpdd);
481 XenPci_HighSync(XenPci_Suspend0, XenPci_SuspendN, xpdd);
483 xpdd->suspend_state = SUSPEND_STATE_RESUMING;
484 XenBus_Resume(xpdd);
486 XenPci_ConnectSuspendEvt(xpdd);
488 WdfChildListBeginIteration(child_list, &child_iterator);
489 while ((status = WdfChildListRetrieveNextDevice(child_list, &child_iterator, &child_device, NULL)) == STATUS_SUCCESS) {
490 XenPci_ResumePdo(child_device);
491 }
492 WdfChildListEndIteration(child_list, &child_iterator);
494 xpdd->suspend_state = SUSPEND_STATE_NONE;
495 ExReleaseFastMutex(&xpdd->suspend_mutex);
496 }
497 FUNCTION_EXIT();
498 }
500 /* called at DISPATCH_LEVEL */
501 static VOID
502 XenPci_SuspendEvtDpc(PVOID context)
503 {
504 NTSTATUS status;
505 WDFDEVICE device = context;
506 //KIRQL old_irql;
507 WDF_OBJECT_ATTRIBUTES attributes;
508 WDF_WORKITEM_CONFIG workitem_config;
509 WDFWORKITEM workitem;
511 FUNCTION_MSG("Suspend detected via Dpc\n");
512 WDF_WORKITEM_CONFIG_INIT(&workitem_config, XenPci_SuspendResume);
513 WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
514 attributes.ParentObject = device;
515 status = WdfWorkItemCreate(&workitem_config, &attributes, &workitem);
516 if (status != STATUS_SUCCESS) {
517 /* how should we fail here */
518 FUNCTION_MSG("WdfWorkItemCreate failed\n");
519 return;
520 }
521 WdfWorkItemEnqueue(workitem);
522 }
524 static void
525 XenPci_ShutdownHandler(char *path, PVOID context)
526 {
527 NTSTATUS status;
528 WDFDEVICE device = context;
529 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
530 char *res;
531 char *value;
532 //KIRQL old_irql;
533 WDF_OBJECT_ATTRIBUTES attributes;
534 WDF_WORKITEM_CONFIG workitem_config;
535 WDFWORKITEM workitem;
537 UNREFERENCED_PARAMETER(path);
539 FUNCTION_ENTER();
541 res = XenBus_Read(xpdd, XBT_NIL, SHUTDOWN_PATH, &value);
542 if (res)
543 {
544 FUNCTION_MSG("Error reading shutdown path - %s\n", res);
545 XenPci_FreeMem(res);
546 FUNCTION_EXIT();
547 return;
548 }
550 FUNCTION_MSG("Shutdown value = %s\n", value);
552 if (strlen(value) && strcmp(value, "suspend") == 0)
553 {
554 {
555 FUNCTION_MSG("Suspend detected\n");
556 /* we have to queue this as a work item as we stop the xenbus thread, which we are currently running in! */
557 WDF_WORKITEM_CONFIG_INIT(&workitem_config, XenPci_SuspendResume);
558 WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
559 attributes.ParentObject = device;
560 status = WdfWorkItemCreate(&workitem_config, &attributes, &workitem);
561 // TODO: check status here
562 WdfWorkItemEnqueue(workitem);
563 }
564 }
566 XenPci_FreeMem(value);
568 FUNCTION_EXIT();
569 }
571 static VOID
572 XenPci_DeviceWatchHandler(char *path, PVOID context)
573 {
574 char **bits;
575 int count;
576 char *err;
577 char *value;
578 PXENPCI_DEVICE_DATA xpdd = context;
580 FUNCTION_ENTER();
582 bits = SplitString(path, '/', 4, &count);
583 if (count == 3)
584 {
585 err = XenBus_Read(xpdd, XBT_NIL, path, &value);
586 if (err)
587 {
588 /* obviously path no longer exists, in which case the removal is being taken care of elsewhere and we shouldn't invalidate now */
589 XenPci_FreeMem(err);
590 }
591 else
592 {
593 XenPci_FreeMem(value);
594 /* we probably have to be a bit smarter here and do nothing if xenpci isn't running yet */
595 FUNCTION_MSG("Rescanning child list\n");
596 XenPci_EvtChildListScanForChildren(xpdd->child_list);
597 }
598 }
599 FreeSplitString(bits, count);
601 FUNCTION_EXIT();
602 }
604 NTSTATUS
605 XenPci_EvtDevicePrepareHardware (WDFDEVICE device, WDFCMRESLIST resources_raw, WDFCMRESLIST resources_translated)
606 {
607 NTSTATUS status = STATUS_SUCCESS;
608 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
609 PCM_PARTIAL_RESOURCE_DESCRIPTOR raw_descriptor, translated_descriptor;
610 ULONG i;
612 FUNCTION_ENTER();
614 XN_ASSERT(WdfCmResourceListGetCount(resources_raw) == WdfCmResourceListGetCount(resources_translated));
616 for (i = 0; i < WdfCmResourceListGetCount(resources_raw); i++)
617 {
618 raw_descriptor = WdfCmResourceListGetDescriptor(resources_raw, i);
619 translated_descriptor = WdfCmResourceListGetDescriptor(resources_translated, i);
620 switch (raw_descriptor->Type) {
621 case CmResourceTypePort:
622 FUNCTION_MSG("IoPort Address(%x) Length: %d\n", translated_descriptor->u.Port.Start.LowPart, translated_descriptor->u.Port.Length);
623 xpdd->platform_ioport_addr = translated_descriptor->u.Port.Start.LowPart;
624 xpdd->platform_ioport_len = translated_descriptor->u.Port.Length;
625 break;
626 case CmResourceTypeMemory:
627 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);
628 FUNCTION_MSG("Memory flags = %04X\n", translated_descriptor->Flags);
629 xpdd->platform_mmio_addr = translated_descriptor->u.Memory.Start;
630 xpdd->platform_mmio_len = translated_descriptor->u.Memory.Length;
631 xpdd->platform_mmio_flags = translated_descriptor->Flags;
632 break;
633 case CmResourceTypeInterrupt:
634 xpdd->irq_level = (KIRQL)translated_descriptor->u.Interrupt.Level;
635 xpdd->irq_vector = translated_descriptor->u.Interrupt.Vector;
636 xpdd->irq_affinity = translated_descriptor->u.Interrupt.Affinity;
637 xpdd->irq_mode = (translated_descriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED)?Latched:LevelSensitive;
638 xpdd->irq_number = raw_descriptor->u.Interrupt.Vector;
639 FUNCTION_MSG("irq_number = %03x\n", raw_descriptor->u.Interrupt.Vector);
640 FUNCTION_MSG("irq_vector = %03x\n", translated_descriptor->u.Interrupt.Vector);
641 FUNCTION_MSG("irq_level = %03x\n", translated_descriptor->u.Interrupt.Level);
642 FUNCTION_MSG("irq_mode = %s\n", (xpdd->irq_mode == Latched)?"Latched":"LevelSensitive");
643 switch(translated_descriptor->ShareDisposition)
644 {
645 case CmResourceShareDeviceExclusive:
646 FUNCTION_MSG("ShareDisposition = CmResourceShareDeviceExclusive\n");
647 break;
648 case CmResourceShareDriverExclusive:
649 FUNCTION_MSG("ShareDisposition = CmResourceShareDriverExclusive\n");
650 break;
651 case CmResourceShareShared:
652 FUNCTION_MSG("ShareDisposition = CmResourceShareShared\n");
653 break;
654 default:
655 FUNCTION_MSG("ShareDisposition = %d\n", translated_descriptor->ShareDisposition);
656 break;
657 }
658 break;
659 case CmResourceTypeDevicePrivate:
660 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]);
661 break;
662 default:
663 FUNCTION_MSG("Unhandled resource type (0x%x)\n", translated_descriptor->Type);
664 break;
665 }
666 }
668 FUNCTION_EXIT();
670 return status;
671 }
673 NTSTATUS
674 XenPci_EvtDeviceD0Entry(WDFDEVICE device, WDF_POWER_DEVICE_STATE previous_state)
675 {
676 NTSTATUS status = STATUS_SUCCESS;
677 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
679 FUNCTION_ENTER();
681 xpdd->hibernated = FALSE;
682 switch (previous_state)
683 {
684 case WdfPowerDeviceD0:
685 FUNCTION_MSG("WdfPowerDeviceD1\n");
686 break;
687 case WdfPowerDeviceD1:
688 FUNCTION_MSG("WdfPowerDeviceD1\n");
689 break;
690 case WdfPowerDeviceD2:
691 FUNCTION_MSG("WdfPowerDeviceD2\n");
692 break;
693 case WdfPowerDeviceD3:
694 FUNCTION_MSG("WdfPowerDeviceD3\n");
695 break;
696 case WdfPowerDeviceD3Final:
697 FUNCTION_MSG("WdfPowerDeviceD3Final\n");
698 break;
699 case WdfPowerDevicePrepareForHibernation:
700 FUNCTION_MSG("WdfPowerDevicePrepareForHibernation\n");
701 break;
702 default:
703 FUNCTION_MSG("Unknown WdfPowerDevice state %d\n", previous_state);
704 break;
705 }
707 if (previous_state == WdfPowerDevicePrepareForHibernation && qemu_hide_flags_value) {
708 XenPci_HideQemuDevices();
709 }
711 if (previous_state == WdfPowerDeviceD3Final) {
712 XenPci_Init(xpdd);
713 if (tpr_patch_requested && !xpdd->tpr_patched) {
714 XenPci_MapHalThenPatchKernel(xpdd);
715 xpdd->tpr_patched = TRUE;
716 xpdd->removable = FALSE;
717 }
718 GntTbl_Init(xpdd);
719 EvtChn_Init(xpdd);
720 } else {
721 XenPci_Resume(xpdd);
722 GntTbl_Resume(xpdd);
723 EvtChn_Resume(xpdd);
724 }
726 FUNCTION_EXIT();
728 return status;
729 }
731 NTSTATUS
732 XenPci_EvtDeviceD0EntryPostInterruptsEnabled(WDFDEVICE device, WDF_POWER_DEVICE_STATE previous_state)
733 {
734 NTSTATUS status = STATUS_SUCCESS;
735 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
736 PCHAR response;
737 HANDLE thread_handle;
739 UNREFERENCED_PARAMETER(previous_state);
741 FUNCTION_ENTER();
743 if (previous_state == WdfPowerDeviceD3Final)
744 {
745 XenBus_Init(xpdd);
747 XenPci_ConnectSuspendEvt(xpdd);
749 response = XenBus_AddWatch(xpdd, XBT_NIL, SYSRQ_PATH, XenPci_SysrqHandler, xpdd);
751 response = XenBus_AddWatch(xpdd, XBT_NIL, SHUTDOWN_PATH, XenPci_ShutdownHandler, device);
753 response = XenBus_AddWatch(xpdd, XBT_NIL, "device", XenPci_DeviceWatchHandler, xpdd);
755 /* prime target as current until the watch gets kicked off */
756 xpdd->target_memory_kb = xpdd->current_memory_kb;
757 xpdd->balloon_shutdown = FALSE;
758 status = PsCreateSystemThread(&thread_handle, THREAD_ALL_ACCESS, NULL, NULL, NULL, XenPci_BalloonThreadProc, xpdd);
759 if (!NT_SUCCESS(status)) {
760 FUNCTION_MSG("Could not start balloon thread\n");
761 return status;
762 }
763 response = XenBus_AddWatch(xpdd, XBT_NIL, BALLOON_PATH, XenPci_BalloonHandler, device);
764 status = ObReferenceObjectByHandle(thread_handle, THREAD_ALL_ACCESS, NULL, KernelMode, &xpdd->balloon_thread, NULL);
765 ZwClose(thread_handle);
766 } else {
767 XenBus_Resume(xpdd);
768 XenPci_ConnectSuspendEvt(xpdd);
769 }
770 FUNCTION_EXIT();
772 return status;
773 }
775 NTSTATUS
776 XenPci_EvtDeviceD0ExitPreInterruptsDisabled(WDFDEVICE device, WDF_POWER_DEVICE_STATE target_state)
777 {
778 NTSTATUS status = STATUS_SUCCESS;
779 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
780 LARGE_INTEGER timeout;
782 FUNCTION_ENTER();
784 switch (target_state)
785 {
786 case WdfPowerDeviceD0:
787 FUNCTION_MSG("WdfPowerDeviceD1\n");
788 break;
789 case WdfPowerDeviceD1:
790 FUNCTION_MSG("WdfPowerDeviceD1\n");
791 break;
792 case WdfPowerDeviceD2:
793 FUNCTION_MSG("WdfPowerDeviceD2\n");
794 break;
795 case WdfPowerDeviceD3:
796 FUNCTION_MSG("WdfPowerDeviceD3\n");
797 break;
798 case WdfPowerDeviceD3Final:
799 FUNCTION_MSG("WdfPowerDeviceD3Final\n");
800 break;
801 case WdfPowerDevicePrepareForHibernation:
802 FUNCTION_MSG("WdfPowerDevicePrepareForHibernation\n");
803 break;
804 default:
805 FUNCTION_MSG("Unknown WdfPowerDevice state %d\n", target_state);
806 break;
807 }
809 if (target_state == WdfPowerDeviceD3Final) {
810 FUNCTION_MSG("Shutting down threads\n");
812 xpdd->balloon_shutdown = TRUE;
813 KeSetEvent(&xpdd->balloon_event, IO_NO_INCREMENT, FALSE);
815 timeout.QuadPart = (LONGLONG)-1 * 1000 * 1000 * 10;
816 while ((status = KeWaitForSingleObject(xpdd->balloon_thread, Executive, KernelMode, FALSE, &timeout)) != STATUS_SUCCESS)
817 {
818 timeout.QuadPart = (LONGLONG)-1 * 1000 * 1000 * 10;
819 FUNCTION_MSG("Waiting for balloon thread to stop\n");
820 }
821 ObDereferenceObject(xpdd->balloon_thread);
823 XenBus_Halt(xpdd);
824 }
825 else
826 {
827 XenBus_Suspend(xpdd);
828 }
830 FUNCTION_EXIT();
832 return status;
833 }
835 NTSTATUS
836 XenPci_EvtDeviceD0Exit(WDFDEVICE device, WDF_POWER_DEVICE_STATE target_state) {
837 NTSTATUS status = STATUS_SUCCESS;
838 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
840 FUNCTION_ENTER();
842 switch (target_state) {
843 case WdfPowerDeviceD0:
844 FUNCTION_MSG("WdfPowerDeviceD1\n");
845 break;
846 case WdfPowerDeviceD1:
847 FUNCTION_MSG("WdfPowerDeviceD1\n");
848 break;
849 case WdfPowerDeviceD2:
850 FUNCTION_MSG("WdfPowerDeviceD2\n");
851 break;
852 case WdfPowerDeviceD3:
853 FUNCTION_MSG("WdfPowerDeviceD3\n");
854 break;
855 case WdfPowerDeviceD3Final:
856 FUNCTION_MSG("WdfPowerDeviceD3Final\n");
857 break;
858 case WdfPowerDevicePrepareForHibernation:
859 FUNCTION_MSG("WdfPowerDevicePrepareForHibernation\n");
860 xpdd->hibernated = TRUE;
861 break;
862 default:
863 FUNCTION_MSG("Unknown WdfPowerDevice state %d\n", target_state);
864 break;
865 }
867 if (target_state == WdfPowerDeviceD3Final) {
868 /* we don't really support exit here */
869 } else {
870 EvtChn_Suspend(xpdd);
871 GntTbl_Suspend(xpdd);
872 }
874 FUNCTION_EXIT();
876 return status;
877 }
879 NTSTATUS
880 XenPci_EvtDeviceReleaseHardware(WDFDEVICE device, WDFCMRESLIST resources_translated)
881 {
882 NTSTATUS status = STATUS_SUCCESS;
884 UNREFERENCED_PARAMETER(device);
885 UNREFERENCED_PARAMETER(resources_translated);
887 FUNCTION_ENTER();
888 FUNCTION_EXIT();
890 return status;
891 }
893 /* Called at PASSIVE_LEVEL but with pagefile unavailable */
894 /* Can be called concurrently, but KMDF takes care of concurrent calls to WdfChildListXxx */
895 VOID
896 XenPci_EvtChildListScanForChildren(WDFCHILDLIST child_list)
897 {
898 NTSTATUS status;
899 PXENPCI_DEVICE_DATA xpdd = GetXpdd(WdfChildListGetDevice(child_list));
900 char *msg;
901 char **devices;
902 char **instances;
903 ULONG i, j;
904 CHAR path[128];
905 XENPCI_PDO_IDENTIFICATION_DESCRIPTION child_description;
906 PVOID entry;
907 WDFDEVICE child_device;
908 WDF_CHILD_RETRIEVE_INFO retrieve_info;
910 FUNCTION_ENTER();
912 WdfChildListBeginScan(child_list);
914 msg = XenBus_List(xpdd, XBT_NIL, "device", &devices);
915 if (!msg)
916 {
917 for (i = 0; devices[i]; i++)
918 {
919 /* make sure the key is not in the veto list */
920 for (entry = xpdd->veto_list.Flink; entry != &xpdd->veto_list; entry = ((PLIST_ENTRY)entry)->Flink)
921 {
922 if (!strcmp(devices[i], (PCHAR)entry + sizeof(LIST_ENTRY)))
923 break;
924 }
925 if (entry != &xpdd->veto_list)
926 {
927 XenPci_FreeMem(devices[i]);
928 continue;
929 }
931 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "device/%s", devices[i]);
932 msg = XenBus_List(xpdd, XBT_NIL, path, &instances);
933 if (!msg)
934 {
935 for (j = 0; instances[j]; j++)
936 {
937 /* the device comparison is done as a memory compare so zero-ing the structure is important */
938 RtlZeroMemory(&child_description, sizeof(child_description));
939 WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&child_description.header, sizeof(child_description));
940 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "device/%s/%s", devices[i], instances[j]);
941 FUNCTION_MSG("Found path = %s\n", path);
942 RtlStringCbCopyA(child_description.path, ARRAY_SIZE(child_description.path), path);
943 RtlStringCbCopyA(child_description.device, ARRAY_SIZE(child_description.device), devices[i]);
944 child_description.index = atoi(instances[j]);
945 WDF_CHILD_RETRIEVE_INFO_INIT(&retrieve_info, &child_description.header);
946 child_device = WdfChildListRetrievePdo(child_list, &retrieve_info);
947 if (child_device)
948 {
949 PXENPCI_PDO_DEVICE_DATA xppdd = GetXppdd(child_device);
950 char *err;
951 char *value;
952 char backend_state_path[128];
954 if (xppdd->do_not_enumerate)
955 {
956 RtlStringCbPrintfA(backend_state_path, ARRAY_SIZE(path), "%s/state", xppdd->backend_path);
958 err = XenBus_Read(xpdd, XBT_NIL, backend_state_path, &value);
959 if (err)
960 {
961 XenPci_FreeMem(err);
962 xppdd->backend_state = XenbusStateUnknown;
963 }
964 else
965 {
966 xppdd->backend_state = atoi(value);
967 XenPci_FreeMem(value);
968 }
969 if (xppdd->backend_state == XenbusStateClosing || xppdd->backend_state == XenbusStateClosed)
970 {
971 FUNCTION_MSG("Surprise removing %s due to backend initiated remove\n", path);
972 XenPci_FreeMem(instances[j]);
973 continue;
974 }
975 else
976 {
977 /* I guess we are being added again ... */
978 xppdd->backend_initiated_remove = FALSE;
979 xppdd->do_not_enumerate = FALSE;
980 }
981 }
982 }
983 status = WdfChildListAddOrUpdateChildDescriptionAsPresent(child_list, &child_description.header, NULL);
984 if (!NT_SUCCESS(status))
985 {
986 FUNCTION_MSG("WdfChildListAddOrUpdateChildDescriptionAsPresent failed with status 0x%08x\n", status);
987 }
988 XenPci_FreeMem(instances[j]);
989 }
990 XenPci_FreeMem(instances);
991 }
992 else
993 {
994 // wtf do we do here???
995 FUNCTION_MSG("Failed to list %s tree\n", devices[i]);
996 }
997 XenPci_FreeMem(devices[i]);
998 }
999 XenPci_FreeMem(devices);
1001 else
1003 // wtf do we do here???
1004 FUNCTION_MSG("Failed to list device tree\n");
1007 WdfChildListEndScan(child_list);
1009 FUNCTION_EXIT();