diff options
author | Konstantin Aladyshev <aladyshev22@gmail.com> | 2021-07-09 17:10:54 +0300 |
---|---|---|
committer | Konstantin Aladyshev <aladyshev22@gmail.com> | 2021-07-09 17:12:54 +0300 |
commit | b1aaf9dc98c8f2e135d21c5f241860012d5f74a5 (patch) | |
tree | c546e947550443a1bca397dcfd56f3cfdb2a5895 | |
parent | 7803db085920f1b57f8f6186921674cd394db840 (diff) | |
download | UEFI-Lessons-b1aaf9dc98c8f2e135d21c5f241860012d5f74a5.tar.gz UEFI-Lessons-b1aaf9dc98c8f2e135d21c5f241860012d5f74a5.tar.bz2 UEFI-Lessons-b1aaf9dc98c8f2e135d21c5f241860012d5f74a5.zip |
Add lesson 34
Signed-off-by: Konstantin Aladyshev <aladyshev22@gmail.com>
-rw-r--r-- | Lesson_34/README.md | 352 | ||||
-rw-r--r-- | Lesson_34/UefiLessonsPkg/SimpleDriver/SimpleDriver.c | 26 | ||||
-rw-r--r-- | Lesson_34/UefiLessonsPkg/SimpleDriver/SimpleDriver.inf | 19 | ||||
-rw-r--r-- | README.md | 1 |
4 files changed, 398 insertions, 0 deletions
diff --git a/Lesson_34/README.md b/Lesson_34/README.md new file mode 100644 index 0000000..933237f --- /dev/null +++ b/Lesson_34/README.md @@ -0,0 +1,352 @@ +Let's create a simple UEFI driver. + +Up until now we've created only UEFI applications. The main difference between application and a driver is a fact that application is unloaded from the memory after its execution. But the driver is a thing that stays in memory. And while it stays there it can provide usefull protocols for other applications to use. + + +Let's create a simplest driver 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 +``` +Things that have changed from our usual INF file: +- The `MODULE_TYPE` is `UEFI_DRIVER` (earlier we've always used `UEFI_APPLICATION`), +- The `ENTRY_POINT` is `SimpleDriverEntryPoint` (earlier we've always used `UefiMain`, but the driver operates with Entry/Unload functions, so it is better to start to give them proper names), +- `UefiDriverEntryPoint` library class is used (earlier we've always used `UefiApplicationEntryPoint`). In case you wonder about `UefiDriverEntryPoint` library internals take a look into https://github.com/tianocore/edk2/tree/master/MdePkg/Library/UefiDriverEntryPoint + +Now let's write *.c source file UefiLessonsPkg/SimpleDriver/SimpleDriver.c + +The only function that we need to implement is our entry function `SimpleDriverEntryPoint` that we've declared: +``` +#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; +} +``` + +Include this driver in the components section of our `UefiLessonsPkg/UefiLessonsPkg.dsc`: +``` +[Components] + ... + UefiLessonsPkg/SimpleDriver/SimpleDriver.inf +``` +If you try to build, the process would fail: +``` +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] +``` +As usually we need to find proper library implementation: +``` +$ 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 +``` +And place it in our DSC: +``` +[LibraryClasses] + ... + UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf +``` + +Build, copy file to our QEMU shared folder and run OVMF. + +First let's try to execute it as an app: +``` +FS0:\> SimpleDriver.efi +The image is not an application. +``` +As you see this action is not possible. + +To load driver we need to use `load` shell command: +``` +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 +``` + +Use this command to load our driver: +``` +FS0:\> load SimpleDriver.efi +Hello from driver! +Image 'FS0:\SimpleDriver.efi' loaded at 6646000 - Success +``` + +Now let's try to use `dh` command to look at our driver handle: +``` +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 +``` + +If you'll execute it without any parameters, you'll get all handles in the system. And our driver would be the last one: +``` +FS0:\> dh +... +C6: ImageDevicePath(..F,0xFBFC1)/\SimpleDriver.efi) LoadedImage(\SimpleDriver.efi) +``` +You can print more verbose output for our handle: +``` +FS0:\> dh -d -v c6 +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 +``` + + +You can load more instanses of our driver, this is not a problem: +``` +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 +``` + +If you look at dh now, there would be multiple handles from our driver: +``` +FS0:\> dh +... +C6: ImageDevicePath(..F,0xFBFC1)/\SimpleDriver.efi) LoadedImage(\SimpleDriver.efi) +C7: ImageDevicePath(..F,0xFBFC1)/\SimpleDriver.efi) LoadedImage(\SimpleDriver.efi) +C8: ImageDevicePath(..F,0xFBFC1)/\SimpleDriver.efi) LoadedImage(\SimpleDriver.efi) +C9: ImageDevicePath(..F,0xFBFC1)/\SimpleDriver.efi) LoadedImage(\SimpleDriver.efi) +``` + +To unload driver from memory you can utilize `unload` command: +``` +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 +``` + +But it is now possible to use it now as our driver don't have an Unload function. If you'll look at the earlier output of `dh -d -v c6` command, you can see that `Unload........: 0`. + +Therefore if you'll try to unload our driver you'll get an error: +``` +FS0:\> unload c6 +Unload - Handle [664C998]. [y/n]? +y +Unload - Handle [664C998] Result Unsupported. +``` +If you'll execute `dh`, you would still see that our driver handle is still present in the system. + +# Add unload function + +Now let's try add unload function to our driver. Add it to the INF file UefiLessonsPkg/SimpleDriver/SimpleDriver.inf: +``` +[Defines] + ... + ENTRY_POINT = SimpleDriverEntryPoint ++ UNLOAD_IMAGE = SimpleDriverUnload +``` +And add some simple implementation to the *.c file UefiLessonsPkg/SimpleDriver/SimpleDriver.c: +``` +EFI_STATUS +EFIAPI +SimpleDriverUnload ( + EFI_HANDLE ImageHandle + ) +{ + Print(L"Bye-bye from driver!\n"); + + return EFI_SUCCESS; +} +``` + +If you try to execute `dh -d -v` on the handle from this driver you'll get something like: +``` +FS0:\> dh -d -v c6 +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 +``` +As you can see now `Unload` string is filled with a pointer to the driver unload function. + +Before preforming an unload take a look at the ImageBase address with `dmem`: +``` +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` signature signifies the header of a PE/COFF image (*.efi file). So our driver is actually there. + +Now perform unload: +``` +FS0:\> unload c6 +Unload - Handle [664CF18]. [y/n]? +y +Bye-bye from driver! +Unload - Handle [664CF18] Result Success. +``` + +Look at the memory again: +``` +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 *................* +``` +As you can see in was automatically freed. + + +One more notice. If you'll load your image again, it would have a handle with C7 number. Number C6 would be skipped: +``` +C5: ... +C7: ImageDevicePath(..F,0xFBFC1)/\SimpleDriver.efi) LoadedImage(\SimpleDriver.efi) +``` + diff --git a/Lesson_34/UefiLessonsPkg/SimpleDriver/SimpleDriver.c b/Lesson_34/UefiLessonsPkg/SimpleDriver/SimpleDriver.c new file mode 100644 index 0000000..fd96d17 --- /dev/null +++ b/Lesson_34/UefiLessonsPkg/SimpleDriver/SimpleDriver.c @@ -0,0 +1,26 @@ +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> + + +EFI_STATUS +EFIAPI +SimpleDriverUnload ( + EFI_HANDLE ImageHandle + ) +{ + Print(L"Bye-bye from driver!\n"); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +SimpleDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + Print(L"Hello from driver!\n"); + + return EFI_SUCCESS; +} diff --git a/Lesson_34/UefiLessonsPkg/SimpleDriver/SimpleDriver.inf b/Lesson_34/UefiLessonsPkg/SimpleDriver/SimpleDriver.inf new file mode 100644 index 0000000..a6008c2 --- /dev/null +++ b/Lesson_34/UefiLessonsPkg/SimpleDriver/SimpleDriver.inf @@ -0,0 +1,19 @@ +[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 + UNLOAD_IMAGE = SimpleDriverUnload + +[Sources] + SimpleDriver.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + UefiLib + @@ -37,6 +37,7 @@ Lessons description: - Lesson 31: Search `pci.ids` database to get PCI Vendor/Device information with a help of `ShellLib`/`PrintLib` functions - Lesson 32: Show PCI Option ROM images with the help of `EFI_PCI_IO_PROTOCOL` protocol - Lesson 33: Use `EfiRom` utility for parsing and creation of PCI Option ROM images +- Lesson 34: Create a simple UEFI driver. Use `load`/`unload` UEFI shell commands to work with a driver image ______ |