Introduction to Kernel-Mode Driver Framework (KMDF)
The Kernel-Mode Driver Framework (KMDF) is a set of libraries and conventions that simplify the development of Windows kernel-mode drivers. KMDF provides an object-oriented programming model and handles many low-level driver tasks, allowing developers to focus on the core functionality of their drivers.
Key benefits of using KMDF include:
- Reduced complexity compared to writing drivers from scratch using WDM (Windows Driver Model).
- Built-in handling for Plug and Play (PnP), power management, and I/O operations.
- Improved driver stability and reliability.
- Simplified memory management and synchronization.
Getting Started with KMDF
Setting Up Development Environment
To start developing KMDF drivers, you will need:
- Windows SDK
- Windows Driver Kit (WDK)
- Microsoft Visual Studio with C++ development tools
- A target machine for testing (can be a virtual machine)
Ensure that the WDK is correctly integrated with Visual Studio.
Your First KMDF Driver
Visual Studio provides templates for creating KMDF drivers. These templates set up the basic driver structure, including entry points and necessary WDF object initialization.
A typical KMDF driver will:
- Implement a
DriverEntryroutine, which is the driver's entry point. - Create a
WdfDriverobject. - Create a
WdfDeviceobject for each hardware device the driver manages. - Register I/O request handlers.
NTSTATUS DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
NTSTATUS status;
WDF_DRIVER_CONFIG config;
WDF_OBJECT_ATTRIBUTES attributes;
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
WDF_DRIVER_CONFIG_INIT(&config, MyEvtDeviceAdd);
status = WdfDriverCreate(DriverObject, RegistryPath, &attributes, &config, WDF_NO_HANDLE);
return status;
}
Core Concepts
Drivers and Devices
In KMDF, a driver is represented by a WdfDriver object, and each hardware device managed by the driver is represented by a WdfDevice object. The framework handles the interaction between the driver and the operating system's Plug and Play manager.
I/O Requests
Drivers receive I/O requests from the operating system and applications. In KMDF, these requests are represented by WdfRequest objects. You can register callback functions (EVTs) to handle various types of I/O requests, such as Read, Write, and Device Control.
PnP and Power Management
KMDF automates much of the PnP and power management handling. Drivers can implement event callback functions to respond to PnP events (e.g., device arrival, surprise removal) and power events (e.g., system sleep, device wake).
WDF Objects
KMDF is built around a framework object model. Key objects include:
WDFDRIVER: Represents the driver itself.WDFDEVICE: Represents a hardware device.WDFREQUEST: Represents an I/O request.WDFMEMORY: Represents a memory buffer.WDFWAITLOCK: For synchronization.
These objects have associated attributes and methods for management and manipulation.
KMDF API Reference
The KMDF API provides functions and structures for interacting with the framework.
WdfDriver
Represents the driver object. Created using WdfDriverCreate.
WdfDevice
Represents a functional device. Created using WdfDeviceCreate or implicitly during PnP processing.
WdfIoTarget
Represents a target for I/O operations, such as another driver or a device. Can be obtained for the current device, parent device, or other reported devices.
WdfRequest
Represents an I/O request passed to the driver. Handles include methods for retrieving parameters, completing the request, and forwarding it.
WdfMemory
Represents a block of memory managed by the framework. Useful for efficient buffer management.
Common I/O Handling Example
Example of handling a read request:
VOID MyEvtIoRead(
_In_ WDFQUEUE Queue,
_In_ WDFREQUEST Request,
_In_ size_t Length
)
{
UNREFERENCED_PARAMETER(Queue);
UNREFERENCED_PARAMETER(Length);
NTSTATUS status;
WdfMemory memory;
PVOID buffer;
// Get the buffer from the request
status = WdfRequestRetrieveOutputMemory(Request, &memory);
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, status);
return;
}
buffer = WdfMemoryGetBuffer(memory, NULL);
// ... Populate buffer with data ...
// Complete the request with the data
WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, Length);
}
Best Practices
- Always initialize WDF objects correctly.
- Use framework mechanisms for synchronization (e.g.,
WDFWAITLOCK) to avoid race conditions. - Handle all error conditions gracefully and complete I/O requests appropriately.
- Leverage framework features for PnP and power management to simplify your driver.
- Read and understand the WDK samples for common driver patterns.
Troubleshooting
- Driver Verifier: Use Driver Verifier to detect common driver errors.
- Debugging: Set up kernel debugging on your target machine.
- Event Tracing for Windows (ETW): Implement ETW logging for detailed diagnostics.
- WDK Samples: The WDK includes numerous KMDF samples that can be invaluable for debugging and understanding implementation details.