win-pvdrivers

view xenpci/xenpci_fdo.c @ 1054:471c94d04d8a

Refactoring to support xencache (tmem)
author James Harper <james.harper@bendigoit.com.au>
date Sun Jun 02 16:37:21 2013 +1000 (2013-06-02)
parents 37c0c84a42e8
children 5be1f70687ad
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 #define XEN_SIGNATURE_LOWER 0x40000000
37 #define XEN_SIGNATURE_UPPER 0x4000FFFF
39 USHORT xen_version_major = (USHORT)-1;
40 USHORT xen_version_minor = (USHORT)-1;
41 PVOID hypercall_stubs = NULL;
43 static VOID
44 hvm_get_hypercall_stubs() {
45 ULONG base;
46 DWORD32 cpuid_output[4];
47 char xensig[13];
48 ULONG i;
49 ULONG pages;
50 ULONG msr;
52 if (hypercall_stubs) {
53 FUNCTION_MSG("hypercall_stubs alread set\n");
54 return;
55 }
57 for (base = XEN_SIGNATURE_LOWER; base < XEN_SIGNATURE_UPPER; base += 0x100) {
58 __cpuid(cpuid_output, base);
59 *(ULONG*)(xensig + 0) = cpuid_output[1];
60 *(ULONG*)(xensig + 4) = cpuid_output[2];
61 *(ULONG*)(xensig + 8) = cpuid_output[3];
62 xensig[12] = '\0';
63 FUNCTION_MSG("base = 0x%08x, Xen Signature = %s, EAX = 0x%08x\n", base, xensig, cpuid_output[0]);
64 if (!strncmp("XenVMMXenVMM", xensig, 12) && ((cpuid_output[0] - base) >= 2))
65 break;
66 }
67 if (base == XEN_SIGNATURE_UPPER) {
68 FUNCTION_MSG("Cannot find Xen signature\n");
69 return;
70 }
72 __cpuid(cpuid_output, base + 1);
73 xen_version_major = (USHORT)(cpuid_output[0] >> 16);
74 xen_version_minor = (USHORT)(cpuid_output[0] & 0xFFFF);
75 FUNCTION_MSG("Xen Version %d.%d\n", xen_version_major, xen_version_minor);
77 __cpuid(cpuid_output, base + 2);
78 pages = cpuid_output[0];
79 msr = cpuid_output[1];
81 hypercall_stubs = ExAllocatePoolWithTag(NonPagedPool, pages * PAGE_SIZE, XENPCI_POOL_TAG);
82 FUNCTION_MSG("Hypercall area at %p\n", hypercall_stubs);
84 if (!hypercall_stubs)
85 return;
86 for (i = 0; i < pages; i++) {
87 ULONGLONG pfn;
88 pfn = (MmGetPhysicalAddress((PUCHAR)hypercall_stubs + i * PAGE_SIZE).QuadPart >> PAGE_SHIFT);
89 __writemsr(msr, (pfn << PAGE_SHIFT) + i);
90 }
91 }
93 static VOID
94 hvm_free_hypercall_stubs() {
95 ExFreePoolWithTag(hypercall_stubs, XENPCI_POOL_TAG);
96 hypercall_stubs = NULL;
97 }
99 static VOID
100 XenPci_MapHalThenPatchKernel(PXENPCI_DEVICE_DATA xpdd)
101 {
102 NTSTATUS status;
103 PAUX_MODULE_EXTENDED_INFO amei;
104 ULONG module_info_buffer_size;
105 ULONG i;
107 FUNCTION_ENTER();
109 amei = NULL;
110 /* buffer size could change between requesting and allocating - need to loop until we are successful */
111 while ((status = AuxKlibQueryModuleInformation(&module_info_buffer_size, sizeof(AUX_MODULE_EXTENDED_INFO), amei)) == STATUS_BUFFER_TOO_SMALL || amei == NULL)
112 {
113 if (amei != NULL)
114 ExFreePoolWithTag(amei, XENPCI_POOL_TAG);
115 amei = ExAllocatePoolWithTag(NonPagedPool, module_info_buffer_size, XENPCI_POOL_TAG);
116 }
118 FUNCTION_MSG("AuxKlibQueryModuleInformation = %d\n", status);
119 for (i = 0; i < module_info_buffer_size / sizeof(AUX_MODULE_EXTENDED_INFO); i++)
120 {
121 if (strcmp((PCHAR)amei[i].FullPathName + amei[i].FileNameOffset, "hal.dll") == 0)
122 {
123 FUNCTION_MSG("hal.dll found at %p - %p\n",
124 amei[i].BasicInfo.ImageBase,
125 ((PUCHAR)amei[i].BasicInfo.ImageBase) + amei[i].ImageSize);
126 XenPci_PatchKernel(xpdd, amei[i].BasicInfo.ImageBase, amei[i].ImageSize);
127 }
128 }
129 ExFreePoolWithTag(amei, XENPCI_POOL_TAG);
130 FUNCTION_EXIT();
131 }
133 /*
134 * Alloc MMIO from the device's MMIO region. There is no corresponding free() fn
135 */
136 PHYSICAL_ADDRESS
137 XenPci_AllocMMIO(PXENPCI_DEVICE_DATA xpdd, ULONG len)
138 {
139 PHYSICAL_ADDRESS addr;
141 len = (len + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
143 addr = xpdd->platform_mmio_addr;
144 addr.QuadPart += xpdd->platform_mmio_alloc;
145 xpdd->platform_mmio_alloc += len;
147 XN_ASSERT(xpdd->platform_mmio_alloc <= xpdd->platform_mmio_len);
149 return addr;
150 }
152 extern ULONG tpr_patch_requested;
154 NTSTATUS
155 XenPci_EvtDeviceQueryRemove(WDFDEVICE device)
156 {
157 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
158 NTSTATUS status;
160 FUNCTION_ENTER();
161 if (xpdd->removable)
162 status = STATUS_SUCCESS;
163 else
164 status = STATUS_UNSUCCESSFUL;
165 FUNCTION_EXIT();
166 return status;
167 }
169 static NTSTATUS
170 XenPci_Init(PXENPCI_DEVICE_DATA xpdd)
171 {
172 struct xen_add_to_physmap xatp;
173 int ret;
175 FUNCTION_ENTER();
177 if (!hypercall_stubs)
178 {
179 XN_ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
180 hvm_get_hypercall_stubs();
181 }
182 if (!hypercall_stubs)
183 return STATUS_UNSUCCESSFUL;
185 if (!xpdd->shared_info_area)
186 {
187 XN_ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
188 /* this should be safe as this part will never be called on resume where IRQL == HIGH_LEVEL */
189 xpdd->shared_info_area_unmapped = XenPci_AllocMMIO(xpdd, PAGE_SIZE);
190 xpdd->shared_info_area = MmMapIoSpace(xpdd->shared_info_area_unmapped,
191 PAGE_SIZE, MmNonCached);
192 }
193 FUNCTION_MSG("shared_info_area_unmapped.QuadPart = %lx\n", xpdd->shared_info_area_unmapped.QuadPart);
194 xatp.domid = DOMID_SELF;
195 xatp.idx = 0;
196 xatp.space = XENMAPSPACE_shared_info;
197 xatp.gpfn = (xen_pfn_t)(xpdd->shared_info_area_unmapped.QuadPart >> PAGE_SHIFT);
198 FUNCTION_MSG("gpfn = %x\n", xatp.gpfn);
199 ret = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp);
200 FUNCTION_MSG("hypervisor memory op (XENMAPSPACE_shared_info) ret = %d\n", ret);
202 FUNCTION_EXIT();
204 return STATUS_SUCCESS;
205 }
207 static NTSTATUS
208 XenPci_Resume(PXENPCI_DEVICE_DATA xpdd)
209 {
210 return XenPci_Init(xpdd);
211 }
213 static VOID
214 XenPci_SysrqHandler(char *path, PVOID context) {
215 PXENPCI_DEVICE_DATA xpdd = context;
216 char *value;
217 char letter;
218 char *res;
220 UNREFERENCED_PARAMETER(path);
222 FUNCTION_ENTER();
224 XenBus_Read(xpdd, XBT_NIL, SYSRQ_PATH, &value);
226 FUNCTION_MSG("SysRq Value = %s\n", value);
228 if (value != NULL && strlen(value) != 0) {
229 letter = *value;
230 res = XenBus_Write(xpdd, XBT_NIL, SYSRQ_PATH, "");
231 if (res) {
232 FUNCTION_MSG("Error writing sysrq path\n");
233 XenPci_FreeMem(res);
234 return;
235 }
236 } else {
237 letter = 0;
238 }
240 if (value != NULL) {
241 XenPci_FreeMem(value);
242 }
244 switch (letter) {
245 case 0:
246 break;
247 case 'B': /* cause a bug check */
248 #pragma warning(suppress:28159)
249 KeBugCheckEx(('X' << 16)|('E' << 8)|('N'), 0x00000001, 0x00000000, 0x00000000, 0x00000000);
250 break;
251 case 'A': /* cause an assert */
252 #pragma warning(suppress:28138)
253 XN_ASSERT(letter != 'A');
254 break;
255 default:
256 FUNCTION_MSG("Unhandled sysrq letter %c\n", letter);
257 break;
258 }
260 FUNCTION_EXIT();
261 }
263 static VOID
264 XenPci_BalloonThreadProc(PVOID StartContext)
265 {
266 PXENPCI_DEVICE_DATA xpdd = StartContext;
267 ULONG new_target_kb = xpdd->current_memory_kb;
268 LARGE_INTEGER timeout;
269 PLARGE_INTEGER ptimeout;
270 PMDL head;
271 PMDL mdl;
272 struct xen_memory_reservation reservation;
273 xen_pfn_t *pfns;
274 int i;
275 ULONG ret;
276 int pfn_count;
277 int timeout_ms = 1000;
278 DECLARE_CONST_UNICODE_STRING(low_mem_name, L"\\KernelObjects\\LowMemoryCondition");
279 PKEVENT low_mem_event;
280 HANDLE low_mem_handle;
281 BOOLEAN hit_initial_target = FALSE;
283 FUNCTION_ENTER();
285 head = NULL;
287 low_mem_event = IoCreateNotificationEvent((PUNICODE_STRING)&low_mem_name, &low_mem_handle);
288 //high_commit_event = IoCreateNotificationEvent((PUNICODE_STRING)&high_commit_name, &high_commit_handle);
289 //max_commit_event = IoCreateNotificationEvent((PUNICODE_STRING)&max_commit_name, &max_commit_handle);
291 for(;;) {
292 /* 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 */
293 if (xpdd->current_memory_kb != new_target_kb) {
294 if (!hit_initial_target) {
295 timeout_ms = 0;
296 }
297 timeout.QuadPart = WDF_REL_TIMEOUT_IN_MS(timeout_ms);
298 ptimeout = &timeout;
299 timeout_ms <<= 1;
300 if (timeout_ms > 60000)
301 timeout_ms = 60000;
302 } else {
303 hit_initial_target = TRUE;
304 ptimeout = NULL;
305 timeout_ms = 1000;
306 }
307 KeWaitForSingleObject(&xpdd->balloon_event, Executive, KernelMode, FALSE, ptimeout);
308 if (xpdd->balloon_shutdown)
309 PsTerminateSystemThread(0);
310 FUNCTION_MSG("Got balloon event, current = %d, target = %d\n", xpdd->current_memory_kb, xpdd->target_memory_kb);
311 /* not really worried about races here, but cache target so we only read it once */
312 new_target_kb = xpdd->target_memory_kb;
313 // perform some sanity checks on target_memory
314 // make sure target <= initial
315 // make sure target > some % of initial
317 if (xpdd->current_memory_kb == new_target_kb) {
318 FUNCTION_MSG("No change to memory\n");
319 continue;
320 } else if (xpdd->current_memory_kb < new_target_kb) {
321 FUNCTION_MSG("Trying to take %d KB from Xen\n", new_target_kb - xpdd->current_memory_kb);
322 while ((mdl = head) != NULL && xpdd->current_memory_kb < new_target_kb) {
323 pfn_count = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(mdl), MmGetMdlByteCount(mdl));
324 pfns = ExAllocatePoolWithTag(NonPagedPool, pfn_count * sizeof(xen_pfn_t), XENPCI_POOL_TAG);
325 /* sizeof(xen_pfn_t) may not be the same as PPFN_NUMBER */
326 for (i = 0; i < pfn_count; i++)
327 pfns[i] = (xen_pfn_t)(MmGetMdlPfnArray(mdl)[i]);
328 reservation.address_bits = 0;
329 reservation.extent_order = 0;
330 reservation.domid = DOMID_SELF;
331 reservation.nr_extents = pfn_count;
332 #pragma warning(disable: 4127) /* conditional expression is constant */
333 set_xen_guest_handle(reservation.extent_start, pfns);
335 //FUNCTION_MSG("Calling HYPERVISOR_memory_op(XENMEM_populate_physmap) - pfn_count = %d\n", pfn_count);
336 ret = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation);
337 //FUNCTION_MSG("populated %d pages\n", ret);
338 if (ret < (ULONG)pfn_count) {
339 if (ret > 0) {
340 /* We hit the Xen hard limit: reprobe. */
341 reservation.nr_extents = ret;
342 ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
343 FUNCTION_MSG("decreased %d pages (xen is out of pages)\n", ret);
344 }
345 ExFreePoolWithTag(pfns, XENPCI_POOL_TAG);
346 break;
347 }
348 ExFreePoolWithTag(pfns, XENPCI_POOL_TAG);
349 head = mdl->Next;
350 mdl->Next = NULL;
351 MmFreePagesFromMdl(mdl);
352 ExFreePool(mdl);
353 xpdd->current_memory_kb += BALLOON_UNITS_KB;
354 }
355 } else {
356 FUNCTION_MSG("Trying to give %d KB to Xen\n", xpdd->current_memory_kb - new_target_kb);
357 while (xpdd->current_memory_kb > new_target_kb) {
358 PHYSICAL_ADDRESS alloc_low;
359 PHYSICAL_ADDRESS alloc_high;
360 PHYSICAL_ADDRESS alloc_skip;
361 alloc_low.QuadPart = 0;
362 alloc_high.QuadPart = 0xFFFFFFFFFFFFFFFFULL;
363 alloc_skip.QuadPart = 0;
365 if (!hit_initial_target && low_mem_event && KeReadStateEvent(low_mem_event)) {
366 FUNCTION_MSG("Low memory condition exists. Waiting.\n");
367 break;
368 }
370 #if (NTDDI_VERSION >= NTDDI_WS03SP1)
371 /* our contract says that we must zero pages before returning to xen, so we can't use MM_DONT_ZERO_ALLOCATION */
372 mdl = MmAllocatePagesForMdlEx(alloc_low, alloc_high, alloc_skip, BALLOON_UNITS_KB * 1024, MmCached, 0);
373 #else
374 mdl = MmAllocatePagesForMdl(alloc_low, alloc_high, alloc_skip, BALLOON_UNITS_KB * 1024);
375 #endif
376 if (!mdl) {
377 FUNCTION_MSG("Allocation failed - try again soon\n");
378 break;
379 } else {
380 int i;
381 ULONG ret;
382 int pfn_count = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(mdl), MmGetMdlByteCount(mdl));
383 if (pfn_count != BALLOON_UNIT_PAGES) {
384 /* we could probably do this better but it will only happen in low memory conditions... */
385 FUNCTION_MSG("wanted %d pages got %d pages\n", BALLOON_UNIT_PAGES, pfn_count);
386 MmFreePagesFromMdl(mdl);
387 ExFreePool(mdl);
388 break;
389 }
390 pfns = ExAllocatePoolWithTag(NonPagedPool, pfn_count * sizeof(xen_pfn_t), XENPCI_POOL_TAG);
391 /* sizeof(xen_pfn_t) may not be the same as PPFN_NUMBER */
392 for (i = 0; i < pfn_count; i++)
393 pfns[i] = (xen_pfn_t)(MmGetMdlPfnArray(mdl)[i]);
394 reservation.address_bits = 0;
395 reservation.extent_order = 0;
396 reservation.domid = DOMID_SELF;
397 reservation.nr_extents = pfn_count;
398 #pragma warning(disable: 4127) /* conditional expression is constant */
399 set_xen_guest_handle(reservation.extent_start, pfns);
401 ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
402 ExFreePoolWithTag(pfns, XENPCI_POOL_TAG);
403 if (head) {
404 mdl->Next = head;
405 head = mdl;
406 } else {
407 head = mdl;
408 }
409 xpdd->current_memory_kb -= BALLOON_UNITS_KB;
410 }
411 }
412 }
413 FUNCTION_MSG("Memory = %d, Balloon Target = %d\n", xpdd->current_memory_kb, new_target_kb);
414 }
415 }
417 static VOID
418 XenPci_BalloonHandler(char *path, PVOID context) {
419 WDFDEVICE device = context;
420 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
421 char *value;
423 UNREFERENCED_PARAMETER(path);
425 FUNCTION_ENTER();
427 XenBus_Read(xpdd, XBT_NIL, BALLOON_PATH, &value);
429 if (value == NULL) {
430 FUNCTION_MSG("Failed to read balloon target value\n");
431 FUNCTION_EXIT();
432 return;
433 }
435 if (atoi(value) > 0)
436 xpdd->target_memory_kb = atoi(value);
438 FUNCTION_MSG("target memory value = %d (%s)\n", xpdd->target_memory_kb, value);
440 XenPci_FreeMem(value);
442 KeSetEvent(&xpdd->balloon_event, IO_NO_INCREMENT, FALSE);
444 FUNCTION_EXIT();
445 }
447 static VOID
448 XenPci_Suspend0(PVOID context)
449 {
450 PXENPCI_DEVICE_DATA xpdd = context;
451 ULONG cancelled;
452 ULONGLONG sysenter_cs, sysenter_esp, sysenter_eip;
454 FUNCTION_ENTER();
456 GntTbl_Suspend(xpdd);
458 sysenter_cs = __readmsr(0x174);
459 sysenter_esp = __readmsr(0x175);
460 sysenter_eip = __readmsr(0x176);
462 cancelled = hvm_shutdown(SHUTDOWN_suspend);
464 /* this code was to fix a bug that existed in Xen for a short time... it is harmless but can probably be removed */
465 if (__readmsr(0x174) != sysenter_cs) {
466 FUNCTION_MSG("sysenter_cs not restored. Fixing.\n");
467 __writemsr(0x174, sysenter_cs);
468 }
469 if (__readmsr(0x175) != sysenter_esp) {
470 FUNCTION_MSG("sysenter_esp not restored. Fixing.\n");
471 __writemsr(0x175, sysenter_esp);
472 }
473 if (__readmsr(0x176) != sysenter_eip) {
474 FUNCTION_MSG("sysenter_eip not restored. Fixing.\n");
475 __writemsr(0x176, sysenter_eip);
476 }
478 FUNCTION_MSG("back from suspend, cancelled = %d\n", cancelled);
480 if (qemu_hide_flags_value) {
481 XenPci_HideQemuDevices();
482 }
484 XenPci_Resume(xpdd);
485 GntTbl_Resume(xpdd);
486 EvtChn_Resume(xpdd); /* this enables interrupts again too */
488 FUNCTION_EXIT();
489 }
491 static VOID
492 XenPci_SuspendN(PVOID context)
493 {
494 UNREFERENCED_PARAMETER(context);
496 FUNCTION_ENTER();
497 FUNCTION_MSG("doing nothing on cpu N\n");
498 FUNCTION_EXIT();
499 }
501 static VOID
502 XenPci_SuspendEvtDpc(PVOID context);
503 static NTSTATUS
504 XenPci_ConnectSuspendEvt(PXENPCI_DEVICE_DATA xpdd);
506 /* called at PASSIVE_LEVEL */
507 static NTSTATUS
508 XenPci_ConnectSuspendEvt(PXENPCI_DEVICE_DATA xpdd) {
509 CHAR path[128];
511 xpdd->suspend_evtchn = EvtChn_AllocUnbound(xpdd, 0);
512 FUNCTION_MSG("suspend event channel = %d\n", xpdd->suspend_evtchn);
513 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "device/suspend/event-channel");
514 XenBus_Printf(xpdd, XBT_NIL, path, "%d", xpdd->suspend_evtchn);
515 EvtChn_BindDpc(xpdd, xpdd->suspend_evtchn, XenPci_SuspendEvtDpc, xpdd->wdf_device, EVT_ACTION_FLAGS_NO_SUSPEND);
517 return STATUS_SUCCESS;
518 }
520 /* Called at PASSIVE_LEVEL */
521 static VOID
522 XenPci_SuspendResume(WDFWORKITEM workitem) {
523 NTSTATUS status;
524 //KAFFINITY ActiveProcessorMask = 0; // this is for Vista+
525 WDFDEVICE device = WdfWorkItemGetParentObject(workitem);
526 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
527 WDFCHILDLIST child_list = WdfFdoGetDefaultChildList(device);
528 WDF_CHILD_LIST_ITERATOR child_iterator;
529 WDFDEVICE child_device;
531 FUNCTION_ENTER();
533 if (xpdd->suspend_state == SUSPEND_STATE_NONE) {
534 ExAcquireFastMutex(&xpdd->suspend_mutex);
535 xpdd->suspend_state = SUSPEND_STATE_SCHEDULED;
536 KeMemoryBarrier();
538 // how to prevent device addition etc here? is it implied because dom0 initiated the suspend?
539 WDF_CHILD_LIST_ITERATOR_INIT(&child_iterator, WdfRetrievePresentChildren);
541 WdfChildListBeginIteration(child_list, &child_iterator);
542 while ((status = WdfChildListRetrieveNextDevice(child_list, &child_iterator, &child_device, NULL)) == STATUS_SUCCESS) {
543 XenPci_SuspendPdo(child_device);
544 }
545 WdfChildListEndIteration(child_list, &child_iterator);
547 XenBus_Suspend(xpdd);
548 EvtChn_Suspend(xpdd);
549 XenPci_HighSync(XenPci_Suspend0, XenPci_SuspendN, xpdd);
551 xpdd->suspend_state = SUSPEND_STATE_RESUMING;
552 XenBus_Resume(xpdd);
554 XenPci_ConnectSuspendEvt(xpdd);
556 WdfChildListBeginIteration(child_list, &child_iterator);
557 while ((status = WdfChildListRetrieveNextDevice(child_list, &child_iterator, &child_device, NULL)) == STATUS_SUCCESS) {
558 XenPci_ResumePdo(child_device);
559 }
560 WdfChildListEndIteration(child_list, &child_iterator);
562 xpdd->suspend_state = SUSPEND_STATE_NONE;
563 ExReleaseFastMutex(&xpdd->suspend_mutex);
564 }
565 FUNCTION_EXIT();
566 }
568 /* called at DISPATCH_LEVEL */
569 static VOID
570 XenPci_SuspendEvtDpc(PVOID context)
571 {
572 NTSTATUS status;
573 WDFDEVICE device = context;
574 //KIRQL old_irql;
575 WDF_OBJECT_ATTRIBUTES attributes;
576 WDF_WORKITEM_CONFIG workitem_config;
577 WDFWORKITEM workitem;
579 FUNCTION_MSG("Suspend detected via Dpc\n");
580 WDF_WORKITEM_CONFIG_INIT(&workitem_config, XenPci_SuspendResume);
581 WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
582 attributes.ParentObject = device;
583 status = WdfWorkItemCreate(&workitem_config, &attributes, &workitem);
584 if (status != STATUS_SUCCESS) {
585 /* how should we fail here */
586 FUNCTION_MSG("WdfWorkItemCreate failed\n");
587 return;
588 }
589 WdfWorkItemEnqueue(workitem);
590 }
592 static void
593 XenPci_ShutdownHandler(char *path, PVOID context)
594 {
595 NTSTATUS status;
596 WDFDEVICE device = context;
597 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
598 char *res;
599 char *value;
600 //KIRQL old_irql;
601 WDF_OBJECT_ATTRIBUTES attributes;
602 WDF_WORKITEM_CONFIG workitem_config;
603 WDFWORKITEM workitem;
605 UNREFERENCED_PARAMETER(path);
607 FUNCTION_ENTER();
609 res = XenBus_Read(xpdd, XBT_NIL, SHUTDOWN_PATH, &value);
610 if (res)
611 {
612 FUNCTION_MSG("Error reading shutdown path - %s\n", res);
613 XenPci_FreeMem(res);
614 FUNCTION_EXIT();
615 return;
616 }
618 FUNCTION_MSG("Shutdown value = %s\n", value);
620 if (strlen(value) && strcmp(value, "suspend") == 0)
621 {
622 {
623 FUNCTION_MSG("Suspend detected\n");
624 /* we have to queue this as a work item as we stop the xenbus thread, which we are currently running in! */
625 WDF_WORKITEM_CONFIG_INIT(&workitem_config, XenPci_SuspendResume);
626 WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
627 attributes.ParentObject = device;
628 status = WdfWorkItemCreate(&workitem_config, &attributes, &workitem);
629 // TODO: check status here
630 WdfWorkItemEnqueue(workitem);
631 }
632 }
634 XenPci_FreeMem(value);
636 FUNCTION_EXIT();
637 }
639 static VOID
640 XenPci_DeviceWatchHandler(char *path, PVOID context)
641 {
642 char **bits;
643 int count;
644 char *err;
645 char *value;
646 PXENPCI_DEVICE_DATA xpdd = context;
648 FUNCTION_ENTER();
650 bits = SplitString(path, '/', 4, &count);
651 if (count == 3)
652 {
653 err = XenBus_Read(xpdd, XBT_NIL, path, &value);
654 if (err)
655 {
656 /* obviously path no longer exists, in which case the removal is being taken care of elsewhere and we shouldn't invalidate now */
657 XenPci_FreeMem(err);
658 }
659 else
660 {
661 XenPci_FreeMem(value);
662 /* we probably have to be a bit smarter here and do nothing if xenpci isn't running yet */
663 FUNCTION_MSG("Rescanning child list\n");
664 XenPci_EvtChildListScanForChildren(xpdd->child_list);
665 }
666 }
667 FreeSplitString(bits, count);
669 FUNCTION_EXIT();
670 }
672 NTSTATUS
673 XenPci_EvtDevicePrepareHardware (WDFDEVICE device, WDFCMRESLIST resources_raw, WDFCMRESLIST resources_translated)
674 {
675 NTSTATUS status = STATUS_SUCCESS;
676 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
677 PCM_PARTIAL_RESOURCE_DESCRIPTOR raw_descriptor, translated_descriptor;
678 ULONG i;
680 FUNCTION_ENTER();
682 XN_ASSERT(WdfCmResourceListGetCount(resources_raw) == WdfCmResourceListGetCount(resources_translated));
684 for (i = 0; i < WdfCmResourceListGetCount(resources_raw); i++)
685 {
686 raw_descriptor = WdfCmResourceListGetDescriptor(resources_raw, i);
687 translated_descriptor = WdfCmResourceListGetDescriptor(resources_translated, i);
688 switch (raw_descriptor->Type) {
689 case CmResourceTypePort:
690 FUNCTION_MSG("IoPort Address(%x) Length: %d\n", translated_descriptor->u.Port.Start.LowPart, translated_descriptor->u.Port.Length);
691 xpdd->platform_ioport_addr = translated_descriptor->u.Port.Start.LowPart;
692 xpdd->platform_ioport_len = translated_descriptor->u.Port.Length;
693 break;
694 case CmResourceTypeMemory:
695 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);
696 FUNCTION_MSG("Memory flags = %04X\n", translated_descriptor->Flags);
697 xpdd->platform_mmio_addr = translated_descriptor->u.Memory.Start;
698 xpdd->platform_mmio_len = translated_descriptor->u.Memory.Length;
699 xpdd->platform_mmio_flags = translated_descriptor->Flags;
700 break;
701 case CmResourceTypeInterrupt:
702 xpdd->irq_level = (KIRQL)translated_descriptor->u.Interrupt.Level;
703 xpdd->irq_vector = translated_descriptor->u.Interrupt.Vector;
704 xpdd->irq_affinity = translated_descriptor->u.Interrupt.Affinity;
705 xpdd->irq_mode = (translated_descriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED)?Latched:LevelSensitive;
706 xpdd->irq_number = raw_descriptor->u.Interrupt.Vector;
707 FUNCTION_MSG("irq_number = %03x\n", raw_descriptor->u.Interrupt.Vector);
708 FUNCTION_MSG("irq_vector = %03x\n", translated_descriptor->u.Interrupt.Vector);
709 FUNCTION_MSG("irq_level = %03x\n", translated_descriptor->u.Interrupt.Level);
710 FUNCTION_MSG("irq_mode = %s\n", (xpdd->irq_mode == Latched)?"Latched":"LevelSensitive");
711 switch(translated_descriptor->ShareDisposition)
712 {
713 case CmResourceShareDeviceExclusive:
714 FUNCTION_MSG("ShareDisposition = CmResourceShareDeviceExclusive\n");
715 break;
716 case CmResourceShareDriverExclusive:
717 FUNCTION_MSG("ShareDisposition = CmResourceShareDriverExclusive\n");
718 break;
719 case CmResourceShareShared:
720 FUNCTION_MSG("ShareDisposition = CmResourceShareShared\n");
721 break;
722 default:
723 FUNCTION_MSG("ShareDisposition = %d\n", translated_descriptor->ShareDisposition);
724 break;
725 }
726 break;
727 case CmResourceTypeDevicePrivate:
728 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]);
729 break;
730 default:
731 FUNCTION_MSG("Unhandled resource type (0x%x)\n", translated_descriptor->Type);
732 break;
733 }
734 }
736 FUNCTION_EXIT();
738 return status;
739 }
741 NTSTATUS
742 XenPci_EvtDeviceD0Entry(WDFDEVICE device, WDF_POWER_DEVICE_STATE previous_state)
743 {
744 NTSTATUS status = STATUS_SUCCESS;
745 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
747 FUNCTION_ENTER();
749 xpdd->hibernated = FALSE;
750 switch (previous_state)
751 {
752 case WdfPowerDeviceD0:
753 FUNCTION_MSG("WdfPowerDeviceD1\n");
754 break;
755 case WdfPowerDeviceD1:
756 FUNCTION_MSG("WdfPowerDeviceD1\n");
757 break;
758 case WdfPowerDeviceD2:
759 FUNCTION_MSG("WdfPowerDeviceD2\n");
760 break;
761 case WdfPowerDeviceD3:
762 FUNCTION_MSG("WdfPowerDeviceD3\n");
763 break;
764 case WdfPowerDeviceD3Final:
765 FUNCTION_MSG("WdfPowerDeviceD3Final\n");
766 break;
767 case WdfPowerDevicePrepareForHibernation:
768 FUNCTION_MSG("WdfPowerDevicePrepareForHibernation\n");
769 break;
770 default:
771 FUNCTION_MSG("Unknown WdfPowerDevice state %d\n", previous_state);
772 break;
773 }
775 if (previous_state == WdfPowerDevicePrepareForHibernation && qemu_hide_flags_value) {
776 XenPci_HideQemuDevices();
777 }
779 if (previous_state == WdfPowerDeviceD3Final) {
780 XenPci_Init(xpdd);
781 if (tpr_patch_requested && !xpdd->tpr_patched) {
782 XenPci_MapHalThenPatchKernel(xpdd);
783 xpdd->tpr_patched = TRUE;
784 xpdd->removable = FALSE;
785 }
786 GntTbl_Init(xpdd);
787 EvtChn_Init(xpdd);
788 } else {
789 XenPci_Resume(xpdd);
790 GntTbl_Resume(xpdd);
791 EvtChn_Resume(xpdd);
792 }
794 FUNCTION_EXIT();
796 return status;
797 }
799 NTSTATUS
800 XenPci_EvtDeviceD0EntryPostInterruptsEnabled(WDFDEVICE device, WDF_POWER_DEVICE_STATE previous_state)
801 {
802 NTSTATUS status = STATUS_SUCCESS;
803 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
804 PCHAR response;
805 HANDLE thread_handle;
807 UNREFERENCED_PARAMETER(previous_state);
809 FUNCTION_ENTER();
811 if (previous_state == WdfPowerDeviceD3Final)
812 {
813 XenBus_Init(xpdd);
815 XenPci_ConnectSuspendEvt(xpdd);
817 response = XenBus_AddWatch(xpdd, XBT_NIL, SYSRQ_PATH, XenPci_SysrqHandler, xpdd);
819 response = XenBus_AddWatch(xpdd, XBT_NIL, SHUTDOWN_PATH, XenPci_ShutdownHandler, device);
821 response = XenBus_AddWatch(xpdd, XBT_NIL, "device", XenPci_DeviceWatchHandler, xpdd);
823 /* prime target as current until the watch gets kicked off */
824 xpdd->target_memory_kb = xpdd->current_memory_kb;
825 xpdd->balloon_shutdown = FALSE;
826 status = PsCreateSystemThread(&thread_handle, THREAD_ALL_ACCESS, NULL, NULL, NULL, XenPci_BalloonThreadProc, xpdd);
827 if (!NT_SUCCESS(status)) {
828 FUNCTION_MSG("Could not start balloon thread\n");
829 return status;
830 }
831 response = XenBus_AddWatch(xpdd, XBT_NIL, BALLOON_PATH, XenPci_BalloonHandler, device);
832 status = ObReferenceObjectByHandle(thread_handle, THREAD_ALL_ACCESS, NULL, KernelMode, &xpdd->balloon_thread, NULL);
833 ZwClose(thread_handle);
834 } else {
835 XenBus_Resume(xpdd);
836 XenPci_ConnectSuspendEvt(xpdd);
837 }
838 FUNCTION_EXIT();
840 return status;
841 }
843 NTSTATUS
844 XenPci_EvtDeviceD0ExitPreInterruptsDisabled(WDFDEVICE device, WDF_POWER_DEVICE_STATE target_state)
845 {
846 NTSTATUS status = STATUS_SUCCESS;
847 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
848 LARGE_INTEGER timeout;
850 FUNCTION_ENTER();
852 switch (target_state)
853 {
854 case WdfPowerDeviceD0:
855 FUNCTION_MSG("WdfPowerDeviceD1\n");
856 break;
857 case WdfPowerDeviceD1:
858 FUNCTION_MSG("WdfPowerDeviceD1\n");
859 break;
860 case WdfPowerDeviceD2:
861 FUNCTION_MSG("WdfPowerDeviceD2\n");
862 break;
863 case WdfPowerDeviceD3:
864 FUNCTION_MSG("WdfPowerDeviceD3\n");
865 break;
866 case WdfPowerDeviceD3Final:
867 FUNCTION_MSG("WdfPowerDeviceD3Final\n");
868 break;
869 case WdfPowerDevicePrepareForHibernation:
870 FUNCTION_MSG("WdfPowerDevicePrepareForHibernation\n");
871 break;
872 default:
873 FUNCTION_MSG("Unknown WdfPowerDevice state %d\n", target_state);
874 break;
875 }
877 if (target_state == WdfPowerDeviceD3Final) {
878 FUNCTION_MSG("Shutting down threads\n");
880 xpdd->balloon_shutdown = TRUE;
881 KeSetEvent(&xpdd->balloon_event, IO_NO_INCREMENT, FALSE);
883 timeout.QuadPart = (LONGLONG)-1 * 1000 * 1000 * 10;
884 while ((status = KeWaitForSingleObject(xpdd->balloon_thread, Executive, KernelMode, FALSE, &timeout)) != STATUS_SUCCESS)
885 {
886 timeout.QuadPart = (LONGLONG)-1 * 1000 * 1000 * 10;
887 FUNCTION_MSG("Waiting for balloon thread to stop\n");
888 }
889 ObDereferenceObject(xpdd->balloon_thread);
891 XenBus_Halt(xpdd);
892 }
893 else
894 {
895 XenBus_Suspend(xpdd);
896 }
898 FUNCTION_EXIT();
900 return status;
901 }
903 NTSTATUS
904 XenPci_EvtDeviceD0Exit(WDFDEVICE device, WDF_POWER_DEVICE_STATE target_state) {
905 NTSTATUS status = STATUS_SUCCESS;
906 PXENPCI_DEVICE_DATA xpdd = GetXpdd(device);
908 FUNCTION_ENTER();
910 switch (target_state) {
911 case WdfPowerDeviceD0:
912 FUNCTION_MSG("WdfPowerDeviceD1\n");
913 break;
914 case WdfPowerDeviceD1:
915 FUNCTION_MSG("WdfPowerDeviceD1\n");
916 break;
917 case WdfPowerDeviceD2:
918 FUNCTION_MSG("WdfPowerDeviceD2\n");
919 break;
920 case WdfPowerDeviceD3:
921 FUNCTION_MSG("WdfPowerDeviceD3\n");
922 break;
923 case WdfPowerDeviceD3Final:
924 FUNCTION_MSG("WdfPowerDeviceD3Final\n");
925 break;
926 case WdfPowerDevicePrepareForHibernation:
927 FUNCTION_MSG("WdfPowerDevicePrepareForHibernation\n");
928 xpdd->hibernated = TRUE;
929 break;
930 default:
931 FUNCTION_MSG("Unknown WdfPowerDevice state %d\n", target_state);
932 break;
933 }
935 if (target_state == WdfPowerDeviceD3Final) {
936 /* we don't really support exit here */
937 } else {
938 EvtChn_Suspend(xpdd);
939 GntTbl_Suspend(xpdd);
940 }
942 FUNCTION_EXIT();
944 return status;
945 }
947 NTSTATUS
948 XenPci_EvtDeviceReleaseHardware(WDFDEVICE device, WDFCMRESLIST resources_translated)
949 {
950 NTSTATUS status = STATUS_SUCCESS;
952 UNREFERENCED_PARAMETER(device);
953 UNREFERENCED_PARAMETER(resources_translated);
955 FUNCTION_ENTER();
956 FUNCTION_EXIT();
958 return status;
959 }
961 /* Called at PASSIVE_LEVEL but with pagefile unavailable */
962 /* Can be called concurrently, but KMDF takes care of concurrent calls to WdfChildListXxx */
963 VOID
964 XenPci_EvtChildListScanForChildren(WDFCHILDLIST child_list)
965 {
966 NTSTATUS status;
967 PXENPCI_DEVICE_DATA xpdd = GetXpdd(WdfChildListGetDevice(child_list));
968 char *msg;
969 char **devices;
970 char **instances;
971 ULONG i, j;
972 CHAR path[128];
973 XENPCI_PDO_IDENTIFICATION_DESCRIPTION child_description;
974 PVOID entry;
975 WDFDEVICE child_device;
976 WDF_CHILD_RETRIEVE_INFO retrieve_info;
978 FUNCTION_ENTER();
980 WdfChildListBeginScan(child_list);
982 msg = XenBus_List(xpdd, XBT_NIL, "device", &devices);
983 if (!msg)
984 {
985 for (i = 0; devices[i]; i++)
986 {
987 /* make sure the key is not in the veto list */
988 for (entry = xpdd->veto_list.Flink; entry != &xpdd->veto_list; entry = ((PLIST_ENTRY)entry)->Flink)
989 {
990 if (!strcmp(devices[i], (PCHAR)entry + sizeof(LIST_ENTRY)))
991 break;
992 }
993 if (entry != &xpdd->veto_list)
994 {
995 XenPci_FreeMem(devices[i]);
996 continue;
997 }
999 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "device/%s", devices[i]);
1000 msg = XenBus_List(xpdd, XBT_NIL, path, &instances);
1001 if (!msg)
1003 for (j = 0; instances[j]; j++)
1005 /* the device comparison is done as a memory compare so zero-ing the structure is important */
1006 RtlZeroMemory(&child_description, sizeof(child_description));
1007 WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&child_description.header, sizeof(child_description));
1008 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "device/%s/%s", devices[i], instances[j]);
1009 FUNCTION_MSG("Found path = %s\n", path);
1010 RtlStringCbCopyA(child_description.path, ARRAY_SIZE(child_description.path), path);
1011 RtlStringCbCopyA(child_description.device, ARRAY_SIZE(child_description.device), devices[i]);
1012 child_description.index = atoi(instances[j]);
1013 WDF_CHILD_RETRIEVE_INFO_INIT(&retrieve_info, &child_description.header);
1014 child_device = WdfChildListRetrievePdo(child_list, &retrieve_info);
1015 if (child_device)
1017 PXENPCI_PDO_DEVICE_DATA xppdd = GetXppdd(child_device);
1018 char *err;
1019 char *value;
1020 char backend_state_path[128];
1022 if (xppdd->do_not_enumerate)
1024 RtlStringCbPrintfA(backend_state_path, ARRAY_SIZE(path), "%s/state", xppdd->backend_path);
1026 err = XenBus_Read(xpdd, XBT_NIL, backend_state_path, &value);
1027 if (err)
1029 XenPci_FreeMem(err);
1030 xppdd->backend_state = XenbusStateUnknown;
1032 else
1034 xppdd->backend_state = atoi(value);
1035 XenPci_FreeMem(value);
1037 if (xppdd->backend_state == XenbusStateClosing || xppdd->backend_state == XenbusStateClosed)
1039 FUNCTION_MSG("Surprise removing %s due to backend initiated remove\n", path);
1040 XenPci_FreeMem(instances[j]);
1041 continue;
1043 else
1045 /* I guess we are being added again ... */
1046 xppdd->backend_initiated_remove = FALSE;
1047 xppdd->do_not_enumerate = FALSE;
1051 status = WdfChildListAddOrUpdateChildDescriptionAsPresent(child_list, &child_description.header, NULL);
1052 if (!NT_SUCCESS(status))
1054 FUNCTION_MSG("WdfChildListAddOrUpdateChildDescriptionAsPresent failed with status 0x%08x\n", status);
1056 XenPci_FreeMem(instances[j]);
1058 XenPci_FreeMem(instances);
1060 else
1062 // wtf do we do here???
1063 FUNCTION_MSG("Failed to list %s tree\n", devices[i]);
1065 XenPci_FreeMem(devices[i]);
1067 XenPci_FreeMem(devices);
1069 else
1071 // wtf do we do here???
1072 FUNCTION_MSG("Failed to list device tree\n");
1075 WdfChildListEndScan(child_list);
1077 FUNCTION_EXIT();