KMDF 정리

Jooss·2023년 8월 16일

driver

목록 보기
2/6

2020-07-20 github.io에서 작성

Kernel Mode Driver Development

지난번 TIL에서 Windows Driver Kit 개발 환경 세팅을 진행했습니다.
Windows architecture를 공부함과 동시에 어떤식으로 Driver를 개발해야 하는지에 대해서도 지속적으로 공부중입니다.
공부하고 있는 것들을 간단하게나마 정리하고자 합니다.

WDM과 WDF

Windows에서 지원하는 Driver Development KitWDM(Windows Driver Model)으로 과거 DDK(Driver Development Kit)이라는 이름이 통합되어 관리되었습니다.
추가로 MS에서는 개발자들에게 조금 더 쉽게 Driver를 작성 할 수 있도록 WDF(Windows Driver Frameworks)를 지원 해 주어 간단한 개발이라도 많은 것을 설정해야 하는 WDM보다 접근성있는 개발이 가능해졌습니다.
WDMWDF의 관계를 많은 사람들이 WinAPIMFC의 관계로 설명하고는 합니다.
일단 초기 개발은 WDF을 이용하여 개발을 진행하고 첫 완성 이후에 어느정도 환경을 이해 한 뒤 WDM으로 세부적인 코딩을 들어가려고 합니다.

Windows Architecture

윈도우 응용프로그램에서도 Windows Architecture에 대한 이해가 필요했지만 Driver 는 그 중요성이 조금 더 큰 것 같습니다.
WDF에서는 두 가지 모드를 지원합니다 UMDF(User Mode Driver Framework)KMDF(Kernel Mode Driver Framework).
Framewrok가 두 가지로 나뉘는 것은 User Mode에서는 사용자가 접근 할 수 있는 영역을 제한 해 두고 프로그램의 자원에 함부로 접근 할 수 없게 하는 모드입니다.
Kernel Mode에서만 모든 자원(CPU, Driver, Memory 등)에 접근이 가능하며 중요도가 있는 만큼 MS에 인증을 받아야 합니다.
windows architecture
windowsNT architecture

KMDF Example 분석

Driver에 관한 사전 지식이 전혀 없어 인터넷만으로 지식을 습득하기엔 무리가 있었습니다.
앞으로 KMDF와 관련된 공부 내용은 에이콘의 실전 윈도우 디바이스 드라이버 2/e의 내용을 분석하여 공부 할 예정입니다.

WdfSimple

모든 Driver의 진입점은 DriverEntry 함수에서 시작합니다. C/C++의 Main함수처럼 진입점으로 미리 정의된 함수입니다.

  • DriverEntry: Driver설정 Initialize이후 Driver를 생성하는 작업을 합니다.
  • WDF_DRIVER_CONFIG_INIT: Config 구조체 초기화 및 Device Add 루틴 등록
  • WdfDriverCreate: config를 이용하여 WdfDriver를 생성
NTSTATUS DriverEntry( ... ) {
    WDF_DRIVER_CONFIG_INIT( &config, SIMPLEEvtDeviceAdd );
    status = WdfDriverCreate( ... );
    return status;
}

DriverEntry에서 config에 추가한 Device Add의 정의하는 함수에는 실제 Device의 설정들을 관리합니다.

  • WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE: WDF_OBJECT_ATTRIBUTES를 초기화 해주는 매크로

  • wdfDeviceCreate: Device를 추가 HANDLE은 hDevice에 담겨져서 나옵니다.

  • SIMPLEFdoGetData: 추가 메모리로 FDO_DATA구조체를 지정해 주고 있습니다.(헤더 파일에 매크로를 이용하여 정의되어 있습니다.)

  • WdfDeviceCreateDeviceInterface: GUID를 이용하여 응용 프로그램에서 접근 할 수 있도록 합니다.
    GUID 값을 알면 인터페이스 문자열을 구할 수 있고 CreateFile 함수에 넣어서 드라이버와 통신 할 수 있습니다.

  • WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE: IO_QUEUE_CONFIG 초기화 매크로

  • WdfIoQueueCreate: IO QUEUE 생성

    NTSTATUS SIMPLEEvtDeviceAdd( ... ) {
        UNREFERENCED_PARAMETER(Driver);
        PAGED_CODE();
        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, FDO_DATA);
    
        status = WdfDeviceCreate( ... );
        fdoData = SIMPLEFdoGetData(hDevice);
    
        status = WdfDeviceCreateDeviceInterface( ... );
    
        WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( ... );
    // ReadFile() API, WriteFile API, DeviceIoControl() API 함수와 연결될 이벤트함수를 등록합니다
    queueConfig.EvtIoRead = SIMPLEEvtIoRead;
    queueConfig.EvtIoWrite = SIMPLEEvtIoWrite;
    queueConfig.EvtIoDeviceControl = SIMPLEEvtIoDeviceControl;

    status = WdfIoQueueCreate( ... );   // Queue 오브젝트 생성

    return status;
}
```

AddDevice 함수의 마지막부분에 QueueConfigIORead, IOWrite, IODeviceControl함수를 연결시켰습니다.

  • WdfRequestRetrieveOutputMemory/WdfRequestRetrieveInputMemory: Request에 대한 정보를 WDFMemory pointer로 반환합니다.
  • WdfRequestCompleteWithInformation: 읽거나 쓴 바이트 수를 기록하고 Request완료 요청을 합니다.
VOID SIMPLEEvtIoRead ( ... ) {
    ...

    status = WdfRequestRetrieveOutputMemory(Request, &memory);
    WdfRequestCompleteWithInformation(Request, status, bytesCopied);
}
VOID
SIMPLEEvtIoWrite ( ... ) {
    ...

    status = WdfRequestRetrieveInputMemory(Request, &memory);
    WdfRequestCompleteWithInformation(Request, status, bytesWritten);
}
  • IoControlCode: 응용 프로그램
VOID
SIMPLEEvtIoDeviceControl( ... )
{
 ...

 switch (IoControlCode) {
  case IOCTL_SIMPLE_CONTROL: // 응용프로그램이 사용한 IOControlCode를 확인합니다
  break;

 default:
  status = STATUS_INVALID_DEVICE_REQUEST;
 }
 WdfRequestCompleteWithInformation(Request, status, (ULONG_PTR) 0);
}

Macro

  • WDF_DECLARE_CONTEXT_TYPE_WITH_NAME - 추가 메모리에 접근하는 함수 선언
  • UNREFERENCED_PARAMETER - 참조하지 않은 인자에 컴파일러 경고를 발생하지 않게 합니다.
  • PAGED_CODE - 페이징영역에 있는 해당 이벤트루틴의 IRQL이 비정상적으로 높을때 강제로 시스템을 종료시키는 매크로.

Reference

profile
개발자 세상을 여행중인 히치하이커입니다.

0개의 댓글