win-pvdrivers

view xenvbd_filter/xenvbd_filter.c @ 1027:0f40ce5cb467

Fix hibernate in scsiport
author James Harper <james.harper@bendigoit.com.au>
date Tue Feb 19 20:57:09 2013 +1100 (2013-02-19)
parents aa2e51f67f7c
children b448f01b31e8
line source
1 /*
2 PV Drivers for Windows Xen HVM Domains
3 Copyright (C) 2013 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 "xenvbd_filter.h"
21 #include "ntddscsi.h"
22 #include "srb.h"
24 DRIVER_INITIALIZE DriverEntry;
26 static EVT_WDF_DRIVER_UNLOAD XenVbd_EvtDriverUnload;
27 static EVT_WDF_DRIVER_DEVICE_ADD XenVbd_EvtDeviceAdd;
28 static EVT_WDF_REQUEST_COMPLETION_ROUTINE XenVbd_SendRequestComplete;
29 static EVT_WDF_DEVICE_D0_ENTRY XenVbd_EvtDeviceD0Entry;
30 static EVT_WDF_DEVICE_D0_EXIT XenVbd_EvtDeviceD0Exit;
31 static EVT_WDFDEVICE_WDM_IRP_PREPROCESS XenVbd_EvtDeviceWdmIrpPreprocess_START_DEVICE;
32 static EVT_WDFDEVICE_WDM_IRP_PREPROCESS XenVbd_EvtDeviceWdmIrpPreprocess_SET_POWER;
33 static EVT_WDF_DPC XenVbd_EvtDpcEvent;
34 static IO_COMPLETION_ROUTINE XenVbd_IoCompletion_START_DEVICE;
36 static VOID XenVbd_DeviceCallback(PVOID context, ULONG callback_type, PVOID value);
37 static VOID XenVbd_HandleEventDIRQL(PVOID context);
38 static VOID XenVbd_StopRing(PXENVBD_DEVICE_DATA xvdd, BOOLEAN suspend);
39 static VOID XenVbd_StartRing(PXENVBD_DEVICE_DATA xvdd, BOOLEAN suspend);
41 #include "../xenvbd_common/common_xen.h"
43 static VOID XenVbd_SendEvent(WDFDEVICE device);
45 static VOID
46 XenVbd_StopRing(PXENVBD_DEVICE_DATA xvdd, BOOLEAN suspend) {
47 PXENVBD_FILTER_DATA xvfd = (PXENVBD_FILTER_DATA)xvdd->xvfd;
48 NTSTATUS status;
49 WDFREQUEST request;
50 WDF_REQUEST_SEND_OPTIONS send_options;
51 IO_STACK_LOCATION stack;
52 SCSI_REQUEST_BLOCK srb;
53 SRB_IO_CONTROL sic;
55 FUNCTION_ENTER();
57 /* send a 'stop' down if we are suspending */
58 if (suspend) {
59 status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, xvfd->wdf_target, &request);
60 FUNCTION_MSG("WdfRequestCreate = %08x\n", status);
62 RtlZeroMemory(&stack, sizeof(IO_STACK_LOCATION));
63 stack.MajorFunction = IRP_MJ_SCSI;
64 stack.MinorFunction = IRP_MN_SCSI_CLASS;
65 stack.Parameters.Scsi.Srb = &srb;
67 RtlZeroMemory(&srb, SCSI_REQUEST_BLOCK_SIZE);
68 srb.SrbFlags = SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE;
69 srb.Length = SCSI_REQUEST_BLOCK_SIZE;
70 srb.PathId = 0;
71 srb.TargetId = 0;
72 srb.Lun = 0;
73 srb.OriginalRequest = WdfRequestWdmGetIrp(request);
74 srb.Function = SRB_FUNCTION_IO_CONTROL;
75 srb.DataBuffer = &sic;
77 RtlZeroMemory(&sic, sizeof(SRB_IO_CONTROL));
78 sic.HeaderLength = sizeof(SRB_IO_CONTROL);
79 memcpy(sic.Signature, XENVBD_CONTROL_SIG, 8);
80 sic.Timeout = 60;
81 sic.ControlCode = XENVBD_CONTROL_STOP;
83 WdfRequestWdmFormatUsingStackLocation(request, &stack);
85 WDF_REQUEST_SEND_OPTIONS_INIT(&send_options, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);
86 if (!WdfRequestSend(request, xvfd->wdf_target, &send_options)) {
87 FUNCTION_MSG("Request was _NOT_ sent\n");
88 }
89 status = WdfRequestGetStatus(request);
90 FUNCTION_MSG("Request Status = %08x\n", status);
91 FUNCTION_MSG("SRB Status = %08x\n", srb.SrbStatus);
93 WdfObjectDelete(request);
94 }
96 status = XnWriteInt32(xvdd->handle, XN_BASE_FRONTEND, "state", XenbusStateClosing);
98 FUNCTION_EXIT();
99 }
101 static VOID
102 XenVbd_StartRing(PXENVBD_DEVICE_DATA xvdd, BOOLEAN suspend) {
103 PXENVBD_FILTER_DATA xvfd = (PXENVBD_FILTER_DATA)xvdd->xvfd;
104 NTSTATUS status;
105 WDFREQUEST request;
106 WDF_REQUEST_SEND_OPTIONS send_options;
107 IO_STACK_LOCATION stack;
108 SCSI_REQUEST_BLOCK srb;
109 SRB_IO_CONTROL sic;
111 FUNCTION_ENTER();
113 /* send a 'start' down if we are resuming from a suspend */
114 if (suspend) {
115 status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, xvfd->wdf_target, &request);
116 FUNCTION_MSG("WdfRequestCreate = %08x\n", status);
118 RtlZeroMemory(&stack, sizeof(IO_STACK_LOCATION));
119 stack.MajorFunction = IRP_MJ_SCSI;
120 stack.Parameters.Scsi.Srb = &srb;
122 RtlZeroMemory(&srb, SCSI_REQUEST_BLOCK_SIZE);
123 srb.SrbFlags = SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE;
124 srb.Length = SCSI_REQUEST_BLOCK_SIZE;
125 srb.PathId = 0;
126 srb.TargetId = 0;
127 srb.Lun = 0;
128 srb.OriginalRequest = WdfRequestWdmGetIrp(request);
129 srb.Function = SRB_FUNCTION_IO_CONTROL;
130 srb.DataBuffer = &sic;
132 RtlZeroMemory(&sic, sizeof(SRB_IO_CONTROL));
133 sic.HeaderLength = sizeof(SRB_IO_CONTROL);
134 memcpy(sic.Signature, XENVBD_CONTROL_SIG, 8);
135 sic.Timeout = 60;
136 sic.ControlCode = XENVBD_CONTROL_START;
138 WdfRequestWdmFormatUsingStackLocation(request, &stack);
140 WDF_REQUEST_SEND_OPTIONS_INIT(&send_options, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);
141 if (!WdfRequestSend(request, xvfd->wdf_target, &send_options)) {
142 FUNCTION_MSG("Request was _NOT_ sent\n");
143 }
144 status = WdfRequestGetStatus(request);
145 FUNCTION_MSG("Request Status = %08x\n", status);
146 FUNCTION_MSG("SRB Status = %08x\n", srb.SrbStatus);
148 WdfObjectDelete(request);
149 }
151 FUNCTION_EXIT();
152 }
154 static VOID
155 XenVbd_SendRequestComplete(WDFREQUEST request, WDFIOTARGET target, PWDF_REQUEST_COMPLETION_PARAMS params, WDFCONTEXT context) {
156 NTSTATUS status;
157 PSCSI_REQUEST_BLOCK srb = context;
159 UNREFERENCED_PARAMETER(target);
160 UNREFERENCED_PARAMETER(params);
161 UNREFERENCED_PARAMETER(context);
163 status = WdfRequestGetStatus(request);
164 if (status != 0 || srb->SrbStatus != SRB_STATUS_SUCCESS) {
165 FUNCTION_MSG("Request Status = %08x, SRB Status = %08x\n", status, srb->SrbStatus);
166 }
167 ExFreePoolWithTag(context, XENVBD_POOL_TAG);
168 WdfObjectDelete(request);
169 }
171 static VOID
172 XenVbd_SendEvent(WDFDEVICE device) {
173 PXENVBD_FILTER_DATA xvfd = GetXvfd(device);
174 NTSTATUS status;
175 WDFREQUEST request;
176 WDF_REQUEST_SEND_OPTIONS send_options;
177 IO_STACK_LOCATION stack;
178 PUCHAR buf;
179 PSCSI_REQUEST_BLOCK srb;
180 PSRB_IO_CONTROL sic;
182 status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, xvfd->wdf_target, &request);
183 if (status != STATUS_SUCCESS) {
184 /* this is bad - event will be dropped */
185 return;
186 }
188 buf = ExAllocatePoolWithTag(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK) + sizeof(SRB_IO_CONTROL), XENVBD_POOL_TAG);
189 RtlZeroMemory(buf, sizeof(SCSI_REQUEST_BLOCK) + sizeof(SRB_IO_CONTROL));
190 srb = (PSCSI_REQUEST_BLOCK)(buf);
191 sic = (PSRB_IO_CONTROL)(buf + sizeof(SCSI_REQUEST_BLOCK));
193 srb->Length = sizeof(SCSI_REQUEST_BLOCK);
194 srb->SrbFlags = SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE;
195 srb->PathId = 0;
196 srb->TargetId = 0;
197 srb->Lun = 0;
198 srb->OriginalRequest = WdfRequestWdmGetIrp(request);
199 srb->Function = SRB_FUNCTION_IO_CONTROL;
200 srb->DataBuffer = sic;
201 srb->DataTransferLength = sizeof(SCSI_REQUEST_BLOCK) + sizeof(SRB_IO_CONTROL);
202 srb->TimeOutValue = (ULONG)-1;
204 sic->HeaderLength = sizeof(SRB_IO_CONTROL);
205 memcpy(sic->Signature, XENVBD_CONTROL_SIG, 8);
206 sic->Timeout = (ULONG)-1;
207 sic->ControlCode = XENVBD_CONTROL_EVENT;
209 RtlZeroMemory(&stack, sizeof(IO_STACK_LOCATION));
210 stack.MajorFunction = IRP_MJ_SCSI;
211 stack.MinorFunction = IRP_MN_SCSI_CLASS;
212 stack.Parameters.Scsi.Srb = srb;
214 WdfRequestWdmFormatUsingStackLocation(request, &stack);
215 WdfRequestSetCompletionRoutine(request, XenVbd_SendRequestComplete, buf);
217 WDF_REQUEST_SEND_OPTIONS_INIT(&send_options, 0); //WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE);
218 if (!WdfRequestSend(request, xvfd->wdf_target, &send_options)) {
219 FUNCTION_MSG("Error sending request\n");
220 }
221 }
223 static VOID
224 XenVbd_EvtDpcEvent(WDFDPC dpc) {
225 WDFDEVICE device = WdfDpcGetParentObject(dpc);
227 XenVbd_SendEvent(device);
228 }
230 static VOID
231 XenVbd_HandleEventDIRQL(PVOID context) {
232 PXENVBD_DEVICE_DATA xvdd = (PXENVBD_DEVICE_DATA)context;
233 PXENVBD_FILTER_DATA xvfd = (PXENVBD_FILTER_DATA)xvdd->xvfd;
234 WdfDpcEnqueue(xvfd->dpc);
235 }
237 static NTSTATUS
238 XenVbd_EvtDeviceD0Entry(WDFDEVICE device, WDF_POWER_DEVICE_STATE previous_state) {
239 PXENVBD_FILTER_DATA xvfd = GetXvfd(device);
240 NTSTATUS status;
242 UNREFERENCED_PARAMETER(previous_state);
243 // if waking from hibernate then same as suspend... maybe?
244 FUNCTION_ENTER();
245 status = XenVbd_Connect(&xvfd->xvdd, FALSE);
246 FUNCTION_EXIT();
247 return status;
248 }
250 static NTSTATUS
251 XenVbd_EvtDeviceD0Exit(WDFDEVICE device, WDF_POWER_DEVICE_STATE target_state) {
252 PXENVBD_FILTER_DATA xvfd = GetXvfd(device);
253 NTSTATUS status = STATUS_SUCCESS;
255 FUNCTION_ENTER();
256 switch (target_state) {
257 case WdfPowerDeviceD0:
258 FUNCTION_MSG("WdfPowerDeviceD1\n");
259 break;
260 case WdfPowerDeviceD1:
261 FUNCTION_MSG("WdfPowerDeviceD1\n");
262 break;
263 case WdfPowerDeviceD2:
264 FUNCTION_MSG("WdfPowerDeviceD2\n");
265 break;
266 case WdfPowerDeviceD3:
267 FUNCTION_MSG("WdfPowerDeviceD3\n");
268 if (xvfd->hibernate_flag) {
269 FUNCTION_MSG("(but really WdfPowerDevicePrepareForHibernation)\n");
270 target_state = WdfPowerDevicePrepareForHibernation;
271 }
272 break;
273 case WdfPowerDeviceD3Final:
274 FUNCTION_MSG("WdfPowerDeviceD3Final\n");
275 break;
276 case WdfPowerDevicePrepareForHibernation:
277 FUNCTION_MSG("WdfPowerDevicePrepareForHibernation\n");
278 break;
279 default:
280 FUNCTION_MSG("Unknown WdfPowerDevice state %d\n", target_state);
281 break;
282 }
283 if (target_state != WdfPowerDevicePrepareForHibernation) {
284 status = XenVbd_Disconnect(&xvfd->xvdd, FALSE);
285 }
286 FUNCTION_EXIT();
287 return status;
288 }
290 static NTSTATUS
291 XenVbd_IoCompletion_START_DEVICE(PDEVICE_OBJECT device, PIRP irp, PVOID context) {
292 UNREFERENCED_PARAMETER(device);
293 UNREFERENCED_PARAMETER(irp);
294 FUNCTION_ENTER();
295 ExFreePoolWithTag(context, XENVBD_POOL_TAG);
296 FUNCTION_EXIT();
297 return STATUS_SUCCESS;
298 }
300 static NTSTATUS
301 XenVbd_EvtDeviceWdmIrpPreprocess_START_DEVICE(WDFDEVICE device, PIRP irp) {
302 PXENVBD_FILTER_DATA xvfd = GetXvfd(device);
303 PIO_STACK_LOCATION stack;
304 PCM_RESOURCE_LIST crl;
305 PCM_FULL_RESOURCE_DESCRIPTOR cfrd;
306 PCM_PARTIAL_RESOURCE_LIST cprl;
307 PCM_PARTIAL_RESOURCE_DESCRIPTOR prd;
309 FUNCTION_ENTER();
311 /*
312 Pass down the xvdd area as a memory resource. This gives xenvbd the data in a known place
313 and also satisifies the scsiport requirement for a memory resource
314 */
315 IoCopyCurrentIrpStackLocationToNext(irp);
316 stack = IoGetNextIrpStackLocation(irp);
318 crl = ExAllocatePoolWithTag(NonPagedPool,
319 FIELD_OFFSET(CM_RESOURCE_LIST, List) +
320 FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList) +
321 FIELD_OFFSET(CM_PARTIAL_RESOURCE_LIST, PartialDescriptors) +
322 sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * 1, XENVBD_POOL_TAG);
323 if (!crl) {
324 // TODO: Fail this correctly
325 }
326 crl->Count = 1;
327 cfrd = &crl->List[0];
328 cfrd->InterfaceType = PNPBus;
329 cfrd->BusNumber = 0;
330 cprl = &cfrd->PartialResourceList;
331 cprl->Version = 1;
332 cprl->Revision = 1;
333 cprl->Count = 1;
334 prd = &cprl->PartialDescriptors[0];
335 prd->Type = CmResourceTypeMemory;
336 prd->ShareDisposition = CmResourceShareShared;
337 prd->Flags = CM_RESOURCE_MEMORY_READ_WRITE | CM_RESOURCE_MEMORY_CACHEABLE;
338 prd->u.Memory.Start.QuadPart = (ULONG_PTR)&xvfd->xvdd;
339 prd->u.Memory.Length = sizeof(XENVBD_DEVICE_DATA);
340 stack->Parameters.StartDevice.AllocatedResources = crl;
341 stack->Parameters.StartDevice.AllocatedResourcesTranslated = crl;
343 IoSetCompletionRoutine(irp, XenVbd_IoCompletion_START_DEVICE, crl, TRUE, TRUE, TRUE);
345 FUNCTION_EXIT();
347 return WdfDeviceWdmDispatchPreprocessedIrp(device, irp);
348 }
350 /* scsiport doesn't process SET_POWER correctly so we have to fudge detection of hibernate */
351 static NTSTATUS
352 XenVbd_EvtDeviceWdmIrpPreprocess_SET_POWER(WDFDEVICE device, PIRP irp) {
353 PXENVBD_FILTER_DATA xvfd = GetXvfd(device);
354 PIO_STACK_LOCATION stack;
356 FUNCTION_ENTER();
357 stack = IoGetCurrentIrpStackLocation(irp);
358 if (stack->Parameters.Power.Type == DevicePowerState && stack->Parameters.Power.State.DeviceState == PowerDeviceD3 && stack->Parameters.Power.ShutdownType == PowerActionHibernate) {
359 FUNCTION_MSG("Going to hibernate\n");
360 xvfd->hibernate_flag = TRUE;
361 } else {
362 xvfd->hibernate_flag = FALSE;
363 }
364 IoSkipCurrentIrpStackLocation(irp);
365 FUNCTION_EXIT();
366 return WdfDeviceWdmDispatchPreprocessedIrp(device, irp);
367 }
369 static NTSTATUS
370 XenVbd_EvtDeviceAdd(WDFDRIVER driver, PWDFDEVICE_INIT device_init) {
371 PXENVBD_FILTER_DATA xvfd;
372 NTSTATUS status;
373 WDFDEVICE device;
374 WDF_OBJECT_ATTRIBUTES device_attributes;
375 WDF_PNPPOWER_EVENT_CALLBACKS pnp_power_callbacks;
376 WDF_DPC_CONFIG dpc_config;
377 WDF_OBJECT_ATTRIBUTES oa;
378 UCHAR pnp_minor_functions[] = { IRP_MN_START_DEVICE };
379 UCHAR power_minor_functions[] = { IRP_MN_SET_POWER };
381 UNREFERENCED_PARAMETER(driver);
383 FUNCTION_ENTER();
385 WdfDeviceInitSetDeviceType(device_init, FILE_DEVICE_UNKNOWN);
387 WdfFdoInitSetFilter(device_init);
389 WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnp_power_callbacks);
390 pnp_power_callbacks.EvtDeviceD0Entry = XenVbd_EvtDeviceD0Entry;
391 pnp_power_callbacks.EvtDeviceD0Exit = XenVbd_EvtDeviceD0Exit;
392 WdfDeviceInitSetPnpPowerEventCallbacks(device_init, &pnp_power_callbacks);
394 status = WdfDeviceInitAssignWdmIrpPreprocessCallback(device_init, XenVbd_EvtDeviceWdmIrpPreprocess_START_DEVICE,
395 IRP_MJ_PNP, pnp_minor_functions, ARRAY_SIZE(pnp_minor_functions));
396 if (!NT_SUCCESS(status)) {
397 return status;
398 }
400 status = WdfDeviceInitAssignWdmIrpPreprocessCallback(device_init, XenVbd_EvtDeviceWdmIrpPreprocess_SET_POWER,
401 IRP_MJ_POWER, power_minor_functions, ARRAY_SIZE(power_minor_functions));
402 if (!NT_SUCCESS(status)) {
403 return status;
404 }
406 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&device_attributes, XENVBD_FILTER_DATA);
407 status = WdfDeviceCreate(&device_init, &device_attributes, &device);
408 if (!NT_SUCCESS(status)) {
409 FUNCTION_MSG("Error creating device 0x%x\n", status);
410 return status;
411 }
413 xvfd = GetXvfd(device);
414 xvfd->wdf_device = device;
415 xvfd->wdf_target = WdfDeviceGetIoTarget(device);
416 xvfd->xvdd.xvfd = xvfd;
417 xvfd->xvdd.pdo = WdfDeviceWdmGetPhysicalDevice(device);
418 xvfd->xvdd.grant_tag = XENVBD_POOL_TAG;
420 KeInitializeEvent(&xvfd->xvdd.backend_event, SynchronizationEvent, FALSE);
422 WDF_DPC_CONFIG_INIT(&dpc_config, XenVbd_EvtDpcEvent);
423 WDF_OBJECT_ATTRIBUTES_INIT(&oa);
424 oa.ParentObject = device;
425 status = WdfDpcCreate(&dpc_config, &oa, &xvfd->dpc);
427 WdfDeviceSetSpecialFileSupport(device, WdfSpecialFilePaging, TRUE);
428 WdfDeviceSetSpecialFileSupport(device, WdfSpecialFileHibernation, TRUE);
429 WdfDeviceSetSpecialFileSupport(device, WdfSpecialFileDump, TRUE);
431 FUNCTION_EXIT();
432 return status;
433 }
435 NTSTATUS
436 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
437 NTSTATUS status;
438 WDF_DRIVER_CONFIG config;
439 WDFDRIVER driver;
441 UNREFERENCED_PARAMETER(RegistryPath);
443 FUNCTION_ENTER();
445 WDF_DRIVER_CONFIG_INIT(&config, XenVbd_EvtDeviceAdd);
446 status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, &driver);
447 if (!NT_SUCCESS(status)) {
448 FUNCTION_MSG("WdfDriverCreate failed with status 0x%x\n", status);
449 FUNCTION_EXIT();
450 return status;
451 }
452 FUNCTION_EXIT();
453 return status;
454 }