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
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
|
Up until now we've only used protocols.
Let's write a driver that would actually produce a protocol.
As a protocol a similar to the class, we'll call our simple protocol `SimpleClass`.
Our protocol would have 2 functions - simple getter and setter for internal class variable `mNumber`. `m` in `mNumber` in this case denotes that this variable is local to a module.
UefiLessonsPkg/SimpleClassProtocol/SimpleClassProtocol.c:
```
UINTN mNumber = 0;
EFI_STATUS
EFIAPI
SimpleClassProtocolSetNumber (
UINTN Number
)
{
mNumber = Number;
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
SimpleClassProtocolGetNumber (
UINTN* Number
)
{
if (Number == NULL) {
return EFI_INVALID_PARAMETER;
}
*Number = mNumber;
return EFI_SUCCESS;
}
SIMPLE_CLASS_PROTOCOL mSimpleClass = {
SimpleClassProtocolGetNumber,
SimpleClassProtocolSetNumber
};
```
In C syntax there is no such keyword as a `class`, but still it is possible to create something similar. We use `struct` keyword and class methods would be just fields with pointers to functions.
Now we need to create a header file that would contain the `SIMPLE_CLASS_PROTOCOL` type definition.
Usually packages contain headers for protocols in a folder:
```
<pkg>/Include/Protocol/
```
Therefore create a header UefiLessonsPkg/Include/Protocol/SimpleClass.h:
```
#ifndef __SIMPLE_CLASS_PROTOCOL_H__
#define __SIMPLE_CLASS_PROTOCOL_H__
typedef struct _SIMPLE_CLASS_PROTOCOL SIMPLE_CLASS_PROTOCOL;
typedef
EFI_STATUS
(EFIAPI* SIMPLE_CLASS_GET_NUMBER)(
UINTN* Number
);
typedef
EFI_STATUS
(EFIAPI* SIMPLE_CLASS_SET_NUMBER)(
UINTN Number
);
struct _SIMPLE_CLASS_PROTOCOL {
SIMPLE_CLASS_GET_NUMBER GetNumber;
SIMPLE_CLASS_SET_NUMBER SetNumber;
};
#endif
```
Include this file in our *.c file, so it would itself also know what is the `SIMPLE_CLASS_PROTOCOL` type:
```
#include <Protocol/SimpleClass.h>
```
To make protocol actually usable by other modules we need to install it in the system (protocol database).
For this task we can use `InstallProtocolInterface` function:
```
EFI_BOOT_SERVICES.InstallProtocolInterface()
Summary
Installs a protocol interface on a device handle. If the handle does not exist, it is created and added to the
list of handles in the system.
Prototype
typedef
EFI_STATUS
(EFIAPI *EFI_INSTALL_PROTOCOL_INTERFACE) (
IN OUT EFI_HANDLE *Handle,
IN EFI_GUID *Protocol,
IN EFI_INTERFACE_TYPE InterfaceType,
IN VOID *Interface
);
Parameters
Handle A pointer to the EFI_HANDLE on which the interface is to be installed. If *Handle is NULL on input,
a new handle is created and returned on output. If *Handle is not NULL on input, the protocol is
added to the handle, and the handle is returned unmodified.
If *Handle is not a valid handle, then EFI_INVALID_PARAMETER is returned.
Protocol The numeric ID of the protocol interface.
InterfaceType Indicates whether Interface is supplied in native form.
Interface A pointer to the protocol interface. The Interface must adhere to the structure defined by Protocol.
NULL can be used if a structure is not associated with Protocol.
Description
The InstallProtocolInterface() function installs a protocol interface (a GUID/Protocol Interface structure pair) on a device handle.
The same GUID cannot be installed more than once onto the same handle.
```
You can find `InstallProtocolInterface` all over in the edk2 codebase, but actually according to the same UEFI spec this API is outdated.
The suggested API is:
```
EFI_BOOT_SERVICES.InstallMultipleProtocolInterfaces()
Summary:
Installs one or more protocol interfaces into the boot services environment.
Prototype:
typedef
EFI_STATUS
EFIAPI *EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES) (
IN OUT EFI_HANDLE *Handle,
...
);
Parameters:
Handle The pointer to a handle to install the new protocol interfaces on, or a pointer to NULL if a new handle is to be
allocated.
... A variable argument list containing pairs of protocol GUIDs and protocol interfaces.
Description:
This function installs a set of protocol interfaces into the boot services environment. It removes
arguments from the variable argument list in pairs. The first item is always a pointer to the protocol’s
GUID, and the second item is always a pointer to the protocol’s interface. These pairs are used to call the
boot service EFI_BOOT_SERVICES.InstallProtocolInterface() to add a protocol interface to
Handle. If Handle is NULL on entry, then a new handle will be allocated. The pairs of arguments are
removed in order from the variable argument list until a NULL protocol GUID value is found.
```
As we are writing driver in our example, let's use `InstallMultipleProtocolInterfaces` in a function `SimpleClassProtocolDriverEntryPoint` that we will declare as entry point in the driver INF file:
```
EFI_HANDLE mSimpleClassHandle = NULL;
EFI_STATUS
EFIAPI
SimpleClassProtocolDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
Print(L"Hello from SimpleClassProtocol driver");
EFI_STATUS Status = gBS->InstallMultipleProtocolInterfaces(
&mSimpleClassHandle,
&gSimpleClassProtocolGuid,
&mSimpleClass,
NULL
);
if (!EFI_ERROR(Status))
Print(L", handle=%p\n", mSimpleClassHandle);
else
Print(L"\n", mSimpleClassHandle);
return Status;
}
```
Here I've also added `Print` statements that shows `mSimpleClassHandle` address.
`mSimpleClassHandle` and `mSimpleClass` are declared in the same `*.c` file (therefore the `m` prefix), and `gSimpleClassProtocolGuid` (`g` stands for global) we would declare in a package DEC file so any others modules could include this file and use our protocol.
UefiLessonsPkg/UefiLessonsPkg.dec:
```
[Protocols]
gSimpleClassProtocolGuid = { 0xb5510eea, 0x6f11, 0x4e4b, { 0xad, 0x0f, 0x35, 0xce, 0x17, 0xbd, 0x7a, 0x67 }}
```
Finally it is time to write INF file:
```
[Defines]
INF_VERSION = 1.25
BASE_NAME = SimpleClassProtocol
FILE_GUID = 51d6a90a-c021-4472-b2c1-5fdd1b7f2196
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = SimpleClassProtocolDriverEntryPoint <---- entry point that actually calls 'InstallMultipleProtocolInterfaces'
[Sources]
SimpleClassProtocol.c
[Packages]
MdePkg/MdePkg.dec
UefiLessonsPkg/UefiLessonsPkg.dec <---- we need to add this to get access to the GUID value 'gSimpleClassProtocolGuid'
[Protocols]
gSimpleClassProtocolGuid <---- protocols that are used in module
[LibraryClasses]
UefiDriverEntryPoint
UefiLib
```
# SimpleClassUser
Now it time to write an app that would use our protocol. As this protocol is not installed to the app image handle, we first need to find all handles with such protocol with a help of `LocateHandleBuffer` API, and then call `OpenProtocol` on every one of these handles.
This is no different as it was for some other protocols from the other edk2 packages.
UefiLessonsPkg/SimpleClassUser/SimpleClassUser.c:
```
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Protocol/SimpleClass.h>
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
UINTN HandleCount;
EFI_HANDLE* HandleBuffer;
UINTN Index;
SIMPLE_CLASS_PROTOCOL* SimpleClass;
EFI_STATUS Status = gBS->LocateHandleBuffer (
ByProtocol,
&gSimpleClassProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
Print(L"Error! Can't find any handle with gSimpleClassProtocolGuid: %r\n", Status);
return Status;
}
for (Index = 0; Index < HandleCount; Index++) {
Print(L"Handle = %p\n", HandleBuffer[Index]);
Status = gBS->OpenProtocol(
HandleBuffer[Index],
&gSimpleClassProtocolGuid,
(VOID **)&SimpleClass,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR(Status)) {
<...>
} else {
Print(L"Error! Can't open SimpleClass protocol: %r\n", Status);
}
}
return Status;
}
```
We would use our protocol this way:
- get and print current number value
- add 5 to the number value and set it
- get and print current number value
```
UINTN Number;
Status = SimpleClass->GetNumber(&Number);
if (!EFI_ERROR(Status)) {
Print(L"Number before=%d\n", Number);
} else {
Print(L"Error! Can't get number: %r\n", Status);
}
Status = SimpleClass->SetNumber(Number+5);
if (EFI_ERROR(Status))
Print(L"Error! Can't set number: %r\n", Status);
Status = SimpleClass->GetNumber(&Number);
if (!EFI_ERROR(Status)) {
Print(L"Number after=%d\n", Number);
} else {
Print(L"Error! Can't get number: %r\n", Status);
}
```
INF file for the protocol user app would be pretty simple UefiLessonsPkg/SimpleClassUser/SimpleClassUser.inf:
```
[Defines]
INF_VERSION = 1.25
BASE_NAME = SimpleClassUser
FILE_GUID = 466eed70-8def-44ea-9fb4-9012b266ec8c
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain
[Sources]
SimpleClassUser.c
[Packages]
MdePkg/MdePkg.dec
UefiLessonsPkg/UefiLessonsPkg.dec <---- we need to add it to get access to the 'gSimpleClassProtocolGuid'
[Protocols]
gSimpleClassProtocolGuid <----- add used protocol guids
[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
```
Add both `SimpleClassProtocol` and `SimpleClassUser` to the `[Components]` section of our package:
```
[Components]
...
UefiLessonsPkg/SimpleClassProtocol/SimpleClassProtocol.inf
UefiLessonsPkg/SimpleClassUser/SimpleClassUser.inf
```
# Testing
Now build, copy results to QEMU shared folder and run OVMF.
If we execute our `SimpleClassUser` first, we would get an error:
```
FS0:\> SimpleClassUser.efi
Error! Can't find any handle with gSimpleClassProtocolGuid: Not Found
```
It is understandable, our protocol is not yet installed in the system. Let's install it and use our app.
```
FS0:\> load SimpleClassProtocol.efi
Hello from SimpleClassProtocol driver, handle=6695318
Image 'FS0:\SimpleClassProtocol.efi' loaded at 6645000 - Success
FS0:\> SimpleClassUser.efi
Handle = 6695318
Number before=0
Number after=5
```
We can see handles for our driver and its protocol with `dh`:
```
FS0:\> dh
...
C6: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
C7: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
```
If we execute `SimpleClassUser.efi` again, the number would increase from 5 to 10. It means that the `number` value is really stored outside `SimpleClassUser.efi`. As you can guess, it is stored in a protocol with a handle `C7`.
```
FS0:\> SimpleClassUser.efi
Handle = 6695318
Number before=5
Number after=10
```
We can load another copy of our `SimpleClassProtocol.efi` driver:
```
FS0:\> load SimpleClassProtocol.efi
Hello from SimpleClassProtocol driver, handle=6635498
Image 'FS0:\SimpleClassProtocol.efi' loaded at 6631000 - Success
```
Now there would be two protocol handles in the system C7 and C9:
```
FS0:\> dh
...
C6: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
C7: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
C8: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
C9: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
```
If we execute our `SimpleClassUser.efi` application again, we'll see, that 2 separate version of `number` exist in the system:
```
FS0:\> SimpleClassUser.efi
Handle = 6695318
Number before=10
Number after=15
Handle = 6635498
Number before=0
Number after=5
```
We didn't implement unload function in our driver yet, so it is not possible to unload them.
# IMAGE_UNLOAD
Let's implemet unload function in our protocol driver (UefiLessonsPkg/SimpleClassProtocol/SimpleClassProtocol.inf):
```
[Defines]
...
UNLOAD_IMAGE = SimpleClassProtocolDriverUnload
```
For now write it like this (UefiLessonsPkg/SimpleClassProtocol/SimpleClassProtocol.c):
```
EFI_STATUS
EFIAPI
SimpleClassProtocolDriverUnload (
IN EFI_HANDLE ImageHandle
)
{
Print(L"Bye-bye from SimpleClassProtocol driver, handle=%p\n", mSimpleClassHandle);
return EFI_SUCCESS;
}
```
Let's test again. Load driver and execute our app once:
```
FS0:\> load SimpleClassProtocol.efi
Hello from SimpleClassProtocol driver, handle=665B618
Image 'FS0:\SimpleClassProtocol.efi' loaded at 6646000 - Success
FS0:\> SimpleClassUser.efi
Handle = 665B618
Number before=0
Number after=5
FS0:\> dh
...
C6: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
C7: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
```
It is not possible to perform unload for the protocol handler (C7), but as we've implemented unload function for our driver let's to try unload it:
```
FS0:\> unload c7
Unload - Handle [665B618]. [y/n]?
y
Unload - Handle [665B618] Result Invalid Parameter.
FS0:\> unload c6
Unload - Handle [665FF18]. [y/n]?
y
Bye-bye from SimpleClassProtocol driver, handle=665B618
Unload - Handle [665FF18] Result Success.
```
But there is a problem - C7 is still in the system:
```
FS0:\> dh
...
C7: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
```
Now if we try to execute our app we would get an exception. `OpenProtocol` call would give the same address for the protocol, but now this memory is freed, and a call to protocol functions like `SimpleClass->GetNumber` would crash the system:
```
FS0:\> SimpleClassUser.efi
Handle = 665B618
!!!! X64 Exception Type - 0D(#GP - General Protection) CPU Apic ID - 00000000 !!!!
ExceptionData - 0000000000000000
RIP - AFAFAFAFAFAFAFAF, CS - 0000000000000038, RFLAGS - 0000000000000246
RAX - 0000000006647740, RCX - 0000000007EBC468, RDX - 0000000000000000
RBX - 00000000079EE018, RSP - 0000000007EBC418, RBP - 0000000007EBC468
RSI - 0000000000000000, RDI - 000000000665EA18
R8 - 00000000000000AF, R9 - 000000000665EA18, R10 - 000000008005C440
R11 - 0000000000000000, R12 - 0000000000000000, R13 - 00000000066353E4
R14 - 0000000007EBC460, R15 - 0000000006636040
DS - 0000000000000030, ES - 0000000000000030, FS - 0000000000000030
GS - 0000000000000030, SS - 0000000000000030
CR0 - 0000000080010033, CR2 - 0000000000000000, CR3 - 0000000007C01000
CR4 - 0000000000000668, CR8 - 0000000000000000
DR0 - 0000000000000000, DR1 - 0000000000000000, DR2 - 0000000000000000
DR3 - 0000000000000000, DR6 - 00000000FFFF0FF0, DR7 - 0000000000000400
GDTR - 00000000079DE000 0000000000000047, LDTR - 0000000000000000
IDTR - 00000000072AD018 0000000000000FFF, TR - 0000000000000000
FXSAVE_STATE - 0000000007EBC070
```
To fix this we need to uninstall our protocol from the system on the driver unload.
# Uninstall protocol interface
Like with the protocol interface install there are two functions in UEFI API for the protocol uninstallation. The obsolete one `UninstallProtocolInterface` and a new one `UninstallMultipleProtocolInterfaces`:
```
EFI_BOOT_SERVICES.UninstallProtocolInterface()
Summary:
Removes a protocol interface from a device handle. It is recommended that UninstallMultipleProtocolInterfaces() be used in place of
UninstallProtocolInterface().
Prototype:
typedef
EFI_STATUS
(EFIAPI *EFI_UNINSTALL_PROTOCOL_INTERFACE) (
IN EFI_HANDLE Handle,
IN EFI_GUID *Protocol,
IN VOID *Interface
);
Parameters:
Handle The handle on which the interface was installed. If Handle is not a
valid handle, then EFI_INVALID_PARAMETER is returned.
Protocol The numeric ID of the interface.
Interface A pointer to the interface. NULL can be used if a structure is not associated with Protocol.
Description:
The UninstallProtocolInterface() function removes a protocol interface from the handle on
which it was previously installed. The Protocol and Interface values define the protocol interface to
remove from the handle.
If the last protocol interface is removed from a handle, the handle is freed and is no longer valid.
```
```
EFI_BOOT_SERVICES.UninstallMultipleProtocolInterfaces()
Summary:
Removes one or more protocol interfaces into the boot services environment.
Prototype:
typedef
EFI_STATUS
EFIAPI *EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES) (
IN EFI_HANDLE Handle,
...
);
Parameters:
Handle The handle to remove the protocol interfaces from.
... A variable argument list containing pairs of protocol GUIDs and protocol interfaces.
Description:
This function removes a set of protocol interfaces from the boot services environment. It removes
arguments from the variable argument list in pairs. The first item is always a pointer to the protocol’s
GUID, and the second item is always a pointer to the protocol’s interface. These pairs are used to call the
boot service EFI_BOOT_SERVICES.UninstallProtocolInterface() to remove a protocol
interface from Handle. The pairs of arguments are removed in order from the variable argument list until
a NULL protocol GUID value is found
```
Let's call `UninstallMultipleProtocolInterfaces` on the driver unload:
```
EFI_STATUS
EFIAPI
SimpleClassProtocolDriverUnload (
IN EFI_HANDLE ImageHandle
)
{
Print(L"Bye-bye from SimpleClassProtocol driver, handle=%p\n", mSimpleClassHandle);
EFI_STATUS Status = gBS->UninstallMultipleProtocolInterfaces(
mSimpleClassHandle,
&gSimpleClassProtocolGuid,
&mSimpleClass,
NULL
);
return Status;
}
```
Now build and perform our experiments again.
Load driver and execute our app once:
```
FS0:\> load SimpleClassProtocol.efi
Hello from SimpleClassProtocol driver, handle=665F118
Image 'FS0:\SimpleClassProtocol.efi' loaded at 6645000 - Success
FS0:\> SimpleClassUser.efi
Handle = 665F118
Number before=0
Number after=5
FS0:\> dh
...
C6: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
C7: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
```
Load another copy of the driver and execute our app again:
```
FS0:\> load SimpleClassProtocol.efi
Hello from SimpleClassProtocol driver, handle=6636898
Image 'FS0:\SimpleClassProtocol.efi' loaded at 6630000 - Success
FS0:\> SimpleClassUser.efi
Handle = 665F118
Number before=5
Number after=10
Handle = 6636898
Number before=0
Number after=5
FS0:\> dh
...
C6: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
C7: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
C8: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
C9: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
```
Now there are two protocols/drivers in the system. Let's try to unload the first one.
```
FS0:\> unload c6
Unload - Handle [665F018]. [y/n]?
y
Bye-bye from SimpleClassProtocol driver, handle=665F118
Unload - Handle [665F018] Result Success.
FS0:\> dh
C8: ImageDevicePath(..C1)/\SimpleClassProtocol.efi) LoadedImage(\SimpleClassProtocol.efi)
C9: B5510EEA-6F11-4E4B-AD0F-35CE17BD7A67
```
If we execute our app again, we would see, that that now there is only one handle in the system with the `SimpleClass` protocol, it's valid, and it was completely undisturbed by another protocol uninstall process:
```
FS0:\> SimpleClassUser.efi
Handle = 6636898
Number before=5
Number after=10
```
|