Kernel-Mode Driver Framework (KMDF)

Documentation for developing Windows kernel-mode drivers.

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:

  1. Implement a DriverEntry routine, which is the driver's entry point.
  2. Create a WdfDriver object.
  3. Create a WdfDevice object for each hardware device the driver manages.
  4. 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.