aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lesson_34/README.md352
-rw-r--r--Lesson_34/UefiLessonsPkg/SimpleDriver/SimpleDriver.c26
-rw-r--r--Lesson_34/UefiLessonsPkg/SimpleDriver/SimpleDriver.inf19
-rw-r--r--README.md1
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
+
diff --git a/README.md b/README.md
index 488d1a1..fae461a 100644
--- a/README.md
+++ b/README.md
@@ -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
______