34. 간단한 UEFI 드라이버 생성
이번 장에서는 UEFI 드라이버를 생성한다.
지금까지는 UEFI 애플리케이션만 만들었다. 애플리케이션과 드라이버의 주요 차이점은 애플리케이션이 실행된 후 메모리에 언로드된다는 점이다. 하지만 드라이버는 메모리에 남아있다. 또한 다른 애플리케이션이 사용할 유용한 프로토콜을 제공하기도 한다.
가장 간단한 드라이버로 UefiLessonsPkg/SimpleDriver/SimpleDriver.inf
를 생성한다.
[Defines]
INF_VERSION = 1.25
BASE_NAME = SimpleDriver
FILE_GUID = 384aeb18-105d-4af1-bf17-5e349e8f4d4c
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = SimpleDriverEntryPoint
[Sources]
SimpleDriver.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiDriverEntryPoint
UefiLib
기존에 애플리케이션을 생성할 때 사용했던 application.inf
파일과 드라이버를 생성할 때 사용하는 driver.inf
파일의 차이는 아래와 같다.
MODULE_TYPE
은UEFI_DRIVER
이다. (이전에는 항상UEFI_APPLICATION
을 사용했다.ENTRY_POINT
는SimpleDriverEntryPoint
이다. (이전에는 항상UefiMain
을 사용했지만, 드라이버는Entry/Unload
함수로 작동하므로 적절한 이름을 지정한다.)UefiDriverEntryPoint
라이브러리 클래스가 사용된다. (이전에는 항상UefiApplicationEntryPoint
를 사용했다.)UefiApplicationEntryPoint
라이브러리에 대해 궁금하다면 아래 링크를 통해 확인할 수 있다. https://github.com/tianocore/edk2/tree/master/MdePkg/Library/UefiDriverEntryPoint
이제 UefiLessonsPkg/SimpleDriver/SimpleDriver.c
소스 파일을 생성한다.
구현해야 할 유일한 함수는 entry 함수인 SimpleDriverEntryPoint
이다.
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
EFI_STATUS
EFIAPI
SimpleDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
Print(L"Hello from driver!\n");
return EFI_SUCCESS;
}
드라이버를 생성하기 위해 UefiLessonsPkg/UefiLessonsPkg.dsc
의 Components
에 추가한다.
[Components]
...
UefiLessonsPkg/SimpleDriver/SimpleDriver.inf
아직은빌드를 시도하면 아래와 같은 이유로 실패한다.
build.py...
/home/aladyshev/tiano/edk2/UefiLessonsPkg/UefiLessonsPkg.dsc(...): error 4000: Instance of library class [UefiDriverEntryPoint] is not found
in [/home/aladyshev/tiano/edk2/UefiLessonsPkg/SimpleDriver/SimpleDriver.inf] [X64]
consumed by module [/home/aladyshev/tiano/edk2/UefiLessonsPkg/SimpleDriver/SimpleDriver.inf]
애플리케이션을 만들 때와 마찬가지로 필요한 라이브러리를 찾아야 한다. 2개의 결과가 나오지만 필요한 라이브러리는 MdePkg
의 것이므로 이것을 사용한다.
$ grep UefiDriverEntryPoint -r ./ --exclude-dir=Build | grep LIBRARY_CLASS
./MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf: LIBRARY_CLASS = UefiDriverEntryPoint|DXE_DRIVER DXE_RUNTIME_DRIVER UEFI_DRIVER SMM_CORE DXE_SMM_DRIVER
확인한 UefiDriverEntryPoint.int
파일의 경로를 UefiLessonsPkg/UefiLessonsPkg.dsc
에 추가한다.
[LibraryClasses]
...
UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
빌드 후 SimpleDriver.efi
를 실행하면 아래와 같이 애플리케이션이 아니라는 메시지를 확인할 수 있다.
FS0:\> SimpleDriver.efi
The image is not an application.
위 메시지를 통해서 알 수 있듯이 드라이버는 실행이 불가능하다. 그래서 드라이버를 로드하기 위해서 Shell에서 load
명령어를 사용해야 한다.
FS0:\> load -? -b
Loads a UEFI driver into memory.
LOAD [-nc] file [file...]
-nc - Loads the driver, but does not connect the driver.
File - Specifies a file that contains the image of the UEFI driver (wildcards are
permitted).
NOTES:
1. This command loads a driver into memory. It can load multiple files at
one time. The file name supports wildcards.
2. If the -nc flag is not specified, this command attempts to connect the
driver to a proper device. It might also cause previously loaded drivers
to be connected to their corresponding devices.
3. Use the 'UNLOAD' command to unload a driver.
EXAMPLES:
* To load a driver:
fs0:\> load Isabus.efi
* To load multiple drivers:
fs0:\> load Isabus.efi IsaSerial.efi
* To load multiple drivers using file name wildcards:
fs0:\> load Isa*.efi
* To load a driver without connecting it to a device:
fs0:\> load -nc IsaBus.efi
load
명령어를 사용해 드라이버를 로드한다.
FS0:\> load SimpleDriver.efi
Hello from driver!
Image 'FS0:\SimpleDriver.efi' loaded at 6646000 - Success
이제 dh
명령어로 드라이버 핸들을 살펴본다.
FS0:\> dh -? -b
Displays the device handles in the UEFI environment.
DH [-l <lang>] [handle | -p <prot_id>] [-d] [-v]
-p - Dumps all handles of a protocol specified by the GUID.
-d - Dumps UEFI Driver Model-related information.
-l - Dumps information using the language codes (e.g. ISO 639-2).
-sfo - Displays information as described in Standard-Format Output.
-v - Dumps verbose information about a specific handle.
handle - Specifies a handle to dump information about (a hexadecimal number).
If not present, then all information will be dumped.
NOTES:
1. When neither 'handle' nor 'prot_id' is specified, a list of all the
device handles in the UEFI environment is displayed.
2. The '-d' option displays UEFI Driver Model related information including
parent handles, child handles, all drivers installed on the handle, etc.
3. The '-v' option displays verbose information for the specified handle
including all the protocols on the handle and their details.
4. If the '-p' option is specified, all handles containing the specified
protocol will be displayed. Otherwise, the 'handle' parameter has to be
specified for display. In this case, the '-d' option will be enabled
automatically if the '-v' option is not specified.
EXAMPLES:
* To display all handles and display one screen at a time:
Shell> dh -b
* To display the detailed information on handle 0x30:
Shell> dh 30
* To display all handles with 'diskio' protocol:
Shell> dh -p diskio
* To display all handles with 'LoadedImage' protocol and break when the screen is
full:
Shell> dh -p LoadedImage -b
매개변수 없이 dh
명령어를 실행하면 시스템의 모든 핸들을 얻을 수 있다. 우리가 만든 드라이버는 맨 마지막에 확인할 수 있다.
FS0:\> dh
...
C0: ImageDevicePath(..F,0xFBFC1)/\SimpleDriver.efi) LoadedImage(\SimpleDriver.efi)
핸들을 더 상세하게 확인할 수도 있다.
FS0:\> dh -d -v c0
C6: 664C998
ImageDevicePath(664A018)
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)/\SimpleDriver.efi
LoadedImage(664A440)
Revision......: 0x00001000
ParentHandle..: 6EE5D18
SystemTable...: 79EE018
DeviceHandle..: 6E36798
FilePath......: \SimpleDriver.efi
PdbFileName...: /home/aladyshev/tiano/edk2/Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/SimpleDriver/SimpleDriver/DEBUG/SimpleDriver.dll
OptionsSize...: 0
LoadOptions...: 0
ImageBase.....: 6646000
ImageSize.....: 16C0
CodeType......: EfiBootServicesCode
DataType......: EfiBootServicesData
Unload........: 0
생성한 드라이버로 더 많은 인스턴스를 생성할 수 있으며, 많이 생성한다고 해서 문제되지 않는다.
FS0:\> load SimpleDriver.efi
Hello from driver!
Image 'FS0:\SimpleDriver.efi' loaded at 6619000 - Success
FS0:\> load SimpleDriver.efi
Hello from driver!
Image 'FS0:\SimpleDriver.efi' loaded at 6617000 - Success
FS0:\> load SimpleDriver.efi
Hello from driver!
Image 'FS0:\SimpleDriver.efi' loaded at 6613000 - Success
dh 명령어를 통해 확인하면 드라이버 핸들이 여러 개인 것을 확인할 수 있다.
FS0:\> dh
...
C0: ImageDevicePath(..F,0xFBFC1)/\SimpleDriver.efi) LoadedImage(\SimpleDriver.efi)
C1: ImageDevicePath(..F,0xFBFC1)/\SimpleDriver.efi) LoadedImage(\SimpleDriver.efi)
C2: ImageDevicePath(..F,0xFBFC1)/\SimpleDriver.efi) LoadedImage(\SimpleDriver.efi)
C3: ImageDevicePath(..F,0xFBFC1)/\SimpleDriver.efi) LoadedImage(\SimpleDriver.efi)
메모리에서 드라이버를 언로드하려면 unload
명령을 사용하면 된다.
FS0:\> unload -?
Unloads a driver image that was already loaded.
UNLOAD [-n] [-v|-verbose] Handle
-n - Skips all prompts during unloading, so that it can be used
in a script file.
-v, -verbose - Dumps verbose status information before the image is unloaded.
Handle - Specifies the handle of driver to unload, always taken as hexadecimal number.
NOTES:
1. The '-n' option can be used to skip all prompts during unloading.
2. If the '-v' option is specified, verbose image information will be
displayed before the image is unloaded.
3. Only drivers that support unloading can be successfully unloaded.
4. Use the 'LOAD' command to load a driver.
EXAMPLES:
* To find the handle for the UEFI driver image to unload:
Shell> dh -b
* To unload the UEFI driver image with handle 27:
Shell> unload 27
unload
명령은 드라이버에 Unload 함수가 없기
때문에 지금은 사용할 수 없다.
dh -d -v c0
을 사용하면 Unload........: 0
부분을 확인할 수 있다.
따라서 드라이버를 언로드하려고 하면 오류가 발생한다.
FS0:\> unload c0
Unload - Handle [664C998]. [y/n]?
y
Unload - Handle [664C998] Result Unsupported.
dh
명령어를 실행하면 시스템에 드라이버 핸들이 여전히 존재하는 것을 볼 수 있다.
Unload 함수 추가
이제 언로드 함수를 드라이버에 추가한다. 아래 설정을 UefiLessonsPkg/SimpleDriver/SimpleDriver.inf
에 추가한다.
[Defines]
...
ENTRY_POINT = SimpleDriverEntryPoint
+ UNLOAD_IMAGE = SimpleDriverUnload
UefiLessonsPkg/SimpleDriver/SimpleDriver.c
에 함수를 추가한다.
EFI_STATUS
EFIAPI
SimpleDriverUnload (
EFI_HANDLE ImageHandle
)
{
Print(L"Bye-bye from driver!\n");
return EFI_SUCCESS;
}
새로 빌드하고 드라이버를 로드한 후 dh -d -v
명령어를 사용하면 위에서 확인한 결과와는 달리 Unload
에 값이 들어간 것을 볼 수 있다.
FS0:\> dh -d -v c0
C6: 664CA98
ImageDevicePath(664C618)
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)/\SimpleDriver.efi
LoadedImage(664A240)
Revision......: 0x00001000
ParentHandle..: 6EE5D18
SystemTable...: 79EE018
DeviceHandle..: 6E36798
FilePath......: \SimpleDriver.efi
PdbFileName...: /home/aladyshev/tiano/edk2/Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/SimpleDriver/SimpleDriver/DEBUG/SimpleDriver.dll
OptionsSize...: 0
LoadOptions...: 0
ImageBase.....: 6646000
ImageSize.....: 1780
CodeType......: EfiBootServicesCode
DataType......: EfiBootServicesData
Unload........: 6647047
출력된 결과를 통해 알 수 있듯이 언로드 문자열이 드라이버 언로드 함수에 대한 포인터로 채워진다.
드라이버를 언로드하기 전에 dmem
을 통해 ImageBase
주소를 확인한다.
FS0:\> dmem 6646000 A0
Memory Address 0000000006646000 A0 Bytes
06646000: 4D 5A 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *MZ..............*
06646010: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................*
06646020: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................*
06646030: 00 00 00 00 00 00 00 00-00 00 00 00 80 00 00 00 *................*
06646040: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................*
06646050: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................*
06646060: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................*
06646070: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................*
06646080: 50 45 00 00 64 86 02 00-00 00 00 00 00 00 00 00 *PE..d...........*
06646090: 00 00 00 00 F0 00 2E 00-0B 02 00 00 40 14 00 00 *............@...*
MZ
시그니처는 PE/COFF 이미지(*.efi
파일)의 헤더를 나타낸다. 그래서 실제 드라이버는 ImageBase
에 존재한다.
이제 드라이버를 언로드 한다.
FS0:\> unload c6
Unload - Handle [664CF18]. [y/n]?
y
Bye-bye from driver!
Unload - Handle [664CF18] Result Success.
메모리를 다시 확인하면 아래와 같이 비어진 것을 확인할 수 있다.
FS0:\> dmem 6646000 A0
Memory Address 0000000006646000 A0 Bytes
06646000: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF *................*
06646010: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF *................*
06646020: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF *................*
06646030: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF *................*
06646040: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF *................*
06646050: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF *................*
06646060: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF *................*
06646070: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF *................*
06646080: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF *................*
06646090: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF *................*
드라이버를 새로 로드하면 기존에 사용했던 C0
은 넘어가고 C1
으로 핸들이 존재하는 것을 볼 수 있다.
FS0:\> dh
...
C7: ImageDevicePath(..F,0xFBFC1)/\SimpleDriver.efi) LoadedImage(\SimpleDriver.efi)
Last updated