summaryrefslogtreecommitdiff
path: root/prm/PrmFunc/prmfuncsample.c
blob: c7eed1ebe0202beebeca57dc8ddd25f8bc261987 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
/*--

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.


Module Name:

    PrmFuncSample.c

Abstract:

    This module implements a sample that utilizes the 
    Windows PRM direct call interface.

Environment:

    Kernel mode only.

--*/

#include "prmfuncsample.h"

//
// General client/class interfaces
//

DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD PrmFuncTestEvtDeviceAdd;

#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, PrmFuncTestEvtDeviceAdd)
#pragma alloc_text (PAGE, PrmFuncTestEvtIoDeviceControl)
#endif

HANDLE FileHandle = NULL;

NTSTATUS
DriverEntry (
    _In_ PDRIVER_OBJECT  DriverObject,
    _In_ PUNICODE_STRING RegistryPath
    )

/*++

Routine Description:
    DriverEntry initializes the driver and is the first routine called by the
    system after the driver is loaded. DriverEntry configures and creates a WDF driver
    object.
    .
Parameters Description:

    DriverObject - represents the instance of the function driver that is loaded
        into memory. DriverObject is allocated by the system before the
        driver is loaded, and it is released by the system after the system unloads
        the function driver from memory.

    RegistryPath - represents the driver specific path in the Registry.
        The function driver can use the path to store driver related data between
        reboots. The path does not store hardware instance specific data.

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise.

--*/

{
    WDF_DRIVER_CONFIG Config;
    WDFDRIVER Driver;
    NTSTATUS Status;

    KdPrint(("PRM Function Test Driver - Driver Framework Edition.\n"));

    WDF_DRIVER_CONFIG_INIT(
        &Config,
        PrmFuncTestEvtDeviceAdd);

    //
    // Create a framework driver object to represent our driver.
    //

    Status = WdfDriverCreate(
        DriverObject,
        RegistryPath,
        WDF_NO_OBJECT_ATTRIBUTES, // Driver Attributes
        &Config,                  // Driver Config Info
        &Driver);

    if (!NT_SUCCESS(Status)) {
        KdPrint(("WdfDriverCreate failed with status 0x%x\n", Status));
        return Status;
    }

    return Status;
}

NTSTATUS
PrmFuncTestEvtDeviceAdd (
    _In_ WDFDRIVER       Driver,
    _In_ PWDFDEVICE_INIT DeviceInit
    )

/*++

Routine Description:

    ToasterEvtDeviceAdd is called by the framework in response to AddDevice
    call from the PnP manager. We create and initialize a WDF device object to
    represent a new instance of toaster device.

Arguments:

    Driver - Handle to a framework driver object created in DriverEntry

    DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.

Return Value:

    NTSTATUS

--*/

{
    NTSTATUS                Status;
    WDF_IO_QUEUE_CONFIG     QueueConfig;
    WDFDEVICE               Device;
    WDFQUEUE                Queue;
    WDF_FILEOBJECT_CONFIG   FileConfig;

    UNREFERENCED_PARAMETER(Driver);

    PAGED_CODE();

    KdPrint(("PrmFuncTestEvtDeviceAdd called\n"));

    //
    // Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the
    // framework whether you are interested in handling Create, Close and
    // Cleanup requests that gets genereate when an application or another
    // kernel component opens an handle to the device. If you don't register,
    // the framework default behaviour would be complete these requests
    // with STATUS_SUCCESS. A driver might be interested in registering these
    // events if it wants to do security validation and also wants to maintain
    // per handle (fileobject) context.
    //

    WDF_FILEOBJECT_CONFIG_INIT(
        &FileConfig,
        PrmFuncTestEvtDeviceFileCreate,
        NULL,
        NULL);

    FileConfig.FileObjectClass = WdfFileObjectNotRequired;
    WdfDeviceInitSetFileObjectConfig(
        DeviceInit,
        &FileConfig,
        WDF_NO_OBJECT_ATTRIBUTES);

    //
    // Create a framework device object.This call will in turn create
    // a WDM device object, attach to the lower stack, and set the
    // appropriate flags and attributes.
    //

    Status = WdfDeviceCreate(&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &Device);
    if (!NT_SUCCESS(Status)) {
        KdPrint( ("WdfDeviceCreate failed with status code 0x%x\n", Status));
        return Status;
    }

    //
    // Tell the Framework that this device will need an interface
    //

    Status = WdfDeviceCreateDeviceInterface(
                 Device,
                 (LPGUID) &GUID_DEVINTERFACE_PRMFUNCTEST,
                 NULL);

    if (!NT_SUCCESS (Status)) {
        KdPrint( ("WdfDeviceCreateDeviceInterface failed 0x%x\n", Status));
        return Status;
    }

    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig,  WdfIoQueueDispatchParallel);
    QueueConfig.EvtIoDeviceControl = PrmFuncTestEvtIoDeviceControl;
    Status = WdfIoQueueCreate(
        Device,
        &QueueConfig,
        WDF_NO_OBJECT_ATTRIBUTES,
        &Queue
        );

    if (!NT_SUCCESS (Status)) {
        KdPrint( ("WdfIoQueueCreate failed 0x%x\n", Status));
        return Status;
    }

    return Status;
}

VOID
PrmFuncTestEvtDeviceFileCreate (
    _In_ WDFDEVICE Device,
    _In_ WDFREQUEST Request,
    _In_ WDFFILEOBJECT FileObject
    )

/*++

Routine Description:

    The framework calls a driver's EvtDeviceFileCreate callback
    when the framework receives an IRP_MJ_CREATE request.
    The system sends this request when a user application opens the
    device to perform an I/O operation, such as reading or writing to a device.
    This callback is called in the context of the thread
    that created the IRP_MJ_CREATE request.

Arguments:

    Device - Handle to a framework device object.
    FileObject - Pointer to fileobject that represents the open handle.
    CreateParams - Parameters for create

Return Value:

   NT status code

--*/

{

    PIRP Irp;
    WDFIOTARGET IoTarget;
    WDF_REQUEST_SEND_OPTIONS  RequestSendOptions;
    UNICODE_STRING FilePath;
    OBJECT_ATTRIBUTES ObjA;
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    PWSTR DeviceNames;

    PAGED_CODE();
    
    UNREFERENCED_PARAMETER(FileObject);
    UNREFERENCED_PARAMETER(Device);

    KdPrint( ("PrmFuncTestEvtDeviceFileCreate %p\n", Device));

    DeviceNames = NULL;
    Irp = WdfRequestWdmGetIrp(Request);
    if (Irp->RequestorMode == KernelMode) {

        //
        // Forward the IRP if coming from kernel-mode
        //

        IoTarget = WdfDeviceGetIoTarget(Device);
        WdfRequestFormatRequestUsingCurrentType(Request);
        WDF_REQUEST_SEND_OPTIONS_INIT(
              &RequestSendOptions, 
              WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET
              );

        WdfRequestSend(Request, IoTarget, &RequestSendOptions);
            
    } else {
        Status = IoGetDeviceInterfaces(
            &GUID_DEVINTERFACE_PRMFUNCTEST,
            WdfDeviceWdmGetPhysicalDevice(Device),
            0,
            &DeviceNames
            );

        if (NT_SUCCESS(Status)) {
            if (DeviceNames == NULL || DeviceNames[0] == UNICODE_NULL) {
                Status = STATUS_DEVICE_DOES_NOT_EXIST;

            } else {
                RtlInitUnicodeString(&FilePath, DeviceNames);
                InitializeObjectAttributes(&ObjA,
                    &FilePath,
                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                    NULL,
                    NULL);

                Status = ZwCreateFile(&FileHandle,
                    GENERIC_WRITE,
                    &ObjA,
                    &IoStatusBlock,
                    NULL,
                    FILE_ATTRIBUTE_NORMAL,
                    FILE_SHARE_WRITE,
                    FILE_OPEN_IF,
                    0,
                    NULL,
                    0);
            }
        }

        WdfRequestComplete(Request, Status);    
    }

    if (DeviceNames != NULL) {
        ExFreePool(DeviceNames);
    }

    return;
}

VOID
PrmFuncTestEvtIoDeviceControl(
    _In_ WDFQUEUE     Queue,
    _In_ WDFREQUEST   Request,
    _In_ size_t       OutputBufferLength,
    _In_ size_t       InputBufferLength,
    _In_ ULONG        IoControlCode
    )

/*++

Routine Description:

    This event is called when the framework receives IRP_MJ_DEVICE_CONTROL
    requests from the system.

Arguments:

    Queue - Handle to the framework queue object that is associated
            with the I/O request.
    Request - Handle to a framework request object.

    OutputBufferLength - length of the request's output buffer,
                        if an output buffer is available.
    InputBufferLength - length of the request's input buffer,
                        if an input buffer is available.

    IoControlCode - the driver-defined or system-defined I/O control code
                    (IOCTL) that is associated with the request.

Return Value:

   VOID

--*/

{

    ULONG BytesReturned;
    PVOID InputBuffer;
    PVOID OutputBuffer;
    WDFMEMORY InputMemory;
    WDFMEMORY OutputMemory;
    size_t InputSize;
    size_t OutputSize;
    NTSTATUS Status;
    PPRM_DIRECT_CALL_PARAMETERS TestParameters;
    PPRM_TEST_RESULT PrmResult;
    PRM_INTERFACE PrmInterface;
    ULONG64 EfiStatus;
    BOOLEAN Found;

    PAGED_CODE();

    UNREFERENCED_PARAMETER(Queue);

    Status = STATUS_SUCCESS;
    InputBuffer = NULL;
    OutputBuffer = NULL;
    BytesReturned = 0;
    InputSize = 0;
    OutputSize = 0;

    //
    // VAlidate the input and output buffers
    //

    if (InputBufferLength != 0) {
        Status = WdfRequestRetrieveInputMemory(Request, &InputMemory);
        if (!NT_SUCCESS(Status)) {
            goto EvtIoDeviceControlEnd;
        }

        InputBuffer = WdfMemoryGetBuffer(InputMemory, &InputSize);
    }

    if (OutputBufferLength != 0) {
        Status = WdfRequestRetrieveOutputMemory(Request, &OutputMemory);
        if (!NT_SUCCESS(Status)) {
            goto EvtIoDeviceControlEnd;
        }

        OutputBuffer = WdfMemoryGetBuffer(OutputMemory, &OutputSize);
    }

    //
    // Use WdfRequestRetrieveInputBuffer and WdfRequestRetrieveOutputBuffer
    // to get the request buffers.
    //

    switch (IoControlCode) {
    case IOCTL_PRMFUNCTEST_DIRECT_CALL_TEST:
        if (InputSize < sizeof(PRM_DIRECT_CALL_PARAMETERS)) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

         if (OutputSize < sizeof(PRM_TEST_RESULT)) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        TestParameters = (PPRM_DIRECT_CALL_PARAMETERS)InputBuffer;
        PrmResult = (PPRM_TEST_RESULT)OutputBuffer;
        
        //
        // Acquire the direct-call PRM interface, which is defined in
        // prminterface.h.
        //
        //     typedef struct _PRM_INTERFACE {
        //        ULONG Version;
        //        PPRM_UNLOCK_MODULE UnlockModule;
        //        PPRM_LOCK_MODULE LockModule;
        //        PPRM_INVOKE_HANDLER InvokeHandler;
        //        PPRM_QUERY_HANDLER  QueryHandler;
        //     } PRM_INTERFACE, *PPRM_INTERFACE;
        //

        Status = ExGetPrmInterface(1, &PrmInterface);
        if (!NT_SUCCESS(Status)) {
            break;
        }
        
        //
        // Lock the handler's PRM module to synchronize against any potential
        // runtime update to the PRM module.
        //
        // N.B. Note that technically this is only needed if a series of PRM
        // handlers need to be called transactionally (thus preventing
        // interleaving of PRM module updates). However, we will do it here
        // as an example of how it could be done.
        //

        Status = PrmInterface.LockModule(
                (LPGUID)&TestParameters->Guid);

        if (!NT_SUCCESS(Status)) {
            break;
        }

        //
        // Query for the presence of the PRM handler.
        //

        Status = PrmInterface.QueryHandler(
                (LPGUID)&TestParameters->Guid,
                &Found);

        if ((!NT_SUCCESS(Status)) || (Found == FALSE)) {
            break;
        }

        //
        // Invoke the PRM handler
        //

        Status = PrmInterface.InvokeHandler(
                (LPGUID)&TestParameters->Guid,
                TestParameters->ParameterBuffer,
                0,
                &EfiStatus);

        if (!NT_SUCCESS(Status)) {
            break;
        }

        //
        // Unlock the PRM module
        //
        // N.B. Note that technically this is only needed if a series of PRM
        // handlers need to be called as part of transaction. However, we will
        // do it here as an example of how it could be done.
        //

        Status = PrmInterface.UnlockModule(
                (LPGUID)&TestParameters->Guid);

        if (!NT_SUCCESS(Status)) {
            break;
        }

        PrmResult->Status = Status;
        PrmResult->EfiStatus = EfiStatus;
        Status = STATUS_SUCCESS;
        BytesReturned = sizeof(PRM_TEST_RESULT);
        break;

    default:
        Status = STATUS_INVALID_DEVICE_REQUEST;
    }

EvtIoDeviceControlEnd:
    WdfRequestCompleteWithInformation(Request, Status, BytesReturned);
}