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
|
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 package 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 that 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 this handles.
This is no different as it was some other protocol 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
```
FS0:\> SimpleClassUser.efi
Error! Can't find any handle with gSimpleClassProtocolGuid: Not Found
```
|