1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
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)
```
|