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
|
In the last lesson we've discovered that internally HII Database stores Package lists and its packages not in a continious data array, but in a complex data structure with many double linked lists.
But when we've used `ExportPackageLists` from the `EFI_HII_DATABASE_PROTOCOL`, we received continious data array of Package lists and its packages. It is a handy interface to hide/abstract inernals of the HII Database and provide data to the user in a form that is easy to parse.
The same goes when we want to add Package list to the database via `NewPackageList` from the `EFI_HII_DATABASE_PROTOCOL`. This functions expects incoming Package list in a continious data array in the same form.
```
EFI_HII_DATABASE_PROTOCOL.NewPackageList()
Summary:
Adds the packages in the package list to the HII database.
Prototype:
typedef
EFI_STATUS
(EFIAPI *EFI_HII_DATABASE_NEW_PACK) (
IN CONST EFI_HII_DATABASE_PROTOCOL *This,
IN CONST EFI_HII_PACKAGE_LIST_HEADER *PackageList,
IN CONST EFI_HANDLE DriverHandle, OPTIONAL
OUT EFI_HII_HANDLE *Handle
);
Parameters:
This A pointer to the EFI_HII_DATABASE_PROTOCOL instance
PackageList A pointer to an EFI_HII_PACKAGE_LIST_HEADER structure
DriverHandle Associate the package list with this EFI handle
Handle A pointer to the EFI_HII_HANDLE instance
Description This function adds the packages in the package list to the database and returns a handle. If there is a
EFI_DEVICE_PATH_PROTOCOL associated with the DriverHandle, then this function will create a
package of type EFI_PACKAGE_TYPE_DEVICE_PATH and add it to the package list.
```
Let's inspect one more time the output of our `ShowHII` application:
```
FS0:\> ShowHII.efi
PackageList[0]: GUID=A487A478-51EF-48AA-8794-7BEE2A0562F1; size=0x1ADC
Package[0]: type=STRINGS; size=0x1AC4
Package[1]: type=END; size=0x4
PackageList[1]: GUID=19618BCE-55AE-09C6-37E9-4CE04084C7A1; size=0x21E4
Package[0]: type=STRINGS; size=0x21CC
Package[1]: type=END; size=0x4
PackageList[2]: GUID=2F30DA26-F51B-4B6F-85C4-31873C281BCA; size=0xA93
Package[0]: type=STRINGS; size=0xA7B
Package[1]: type=END; size=0x4
PackageList[3]: GUID=F74D20EE-37E7-48FC-97F7-9B1047749C69; size=0x2EE9
Package[0]: type=IMAGES; size=0x2ED1
Package[1]: type=END; size=0x4
PackageList[4]: GUID=EBF8ED7C-0DD1-4787-84F1-F48D537DCACF; size=0x46C
Package[0]: type=FORMS; size=0x82
Package[1]: type=FORMS; size=0x82
Package[2]: type=STRINGS; size=0x199
Package[3]: type=STRINGS; size=0x19B
Package[4]: type=DEVICE_PATH; size=0x1C
Package[5]: type=END; size=0x4
PackageList[5]: GUID=FE561596-E6BF-41A6-8376-C72B719874D0; size=0x93F
Package[0]: type=FORMS; size=0xF5
Package[1]: type=STRINGS; size=0x40A
Package[2]: type=STRINGS; size=0x40C
Package[3]: type=DEVICE_PATH; size=0x1C
Package[4]: type=END; size=0x4
PackageList[6]: GUID=2A46715F-3581-4A55-8E73-2B769AAA30C5; size=0x6B0
Package[0]: type=FORMS; size=0x143
Package[1]: type=STRINGS; size=0x539
Package[2]: type=DEVICE_PATH; size=0x1C
Package[3]: type=END; size=0x4
PackageList[7]: GUID=99FDC8FD-849B-4EBA-AD13-FB9699C90A4D; size=0x6FE
Package[0]: type=STRINGS; size=0x340
Package[1]: type=STRINGS; size=0x3A6
Package[2]: type=END; size=0x4
PackageList[8]: GUID=E38C1029-E38F-45B9-8F0D-E2E60BC9B262; size=0x15DA
Package[0]: type=STRINGS; size=0xA88
Package[1]: type=STRINGS; size=0xB3A
Package[2]: type=END; size=0x4
PackageList[9]: GUID=D9DCC5DF-4007-435E-9098-8970935504B2; size=0x855
Package[0]: type=FORMS; size=0x1F6
Package[1]: type=STRINGS; size=0x62B
Package[2]: type=DEVICE_PATH; size=0x1C
Package[3]: type=END; size=0x4
PackageList[10]: GUID=F5F219D3-7006-4648-AC8D-D61DFB7BC6AD; size=0x14EC
Package[0]: type=SIMPLE_FONTS; size=0x14D4
Package[1]: type=END; size=0x4
PackageList[11]: GUID=4B47D616-A8D6-4552-9D44-CCAD2E0F4CF9; size=0x6AC8
Package[0]: type=FORMS; size=0x1030
Package[1]: type=STRINGS; size=0x3C99
Package[2]: type=STRINGS; size=0x1DCB
Package[3]: type=DEVICE_PATH; size=0x1C
Package[4]: type=END; size=0x4
PackageList[12]: GUID=F95A7CCC-4C55-4426-A7B4-DC8961950BAE; size=0x13909
Package[0]: type=STRINGS; size=0x138F1
Package[1]: type=END; size=0x4
PackageList[13]: GUID=DEC5DAA4-6781-4820-9C63-A7B0E4F1DB31; size=0x8677
Package[0]: type=STRINGS; size=0x865F
Package[1]: type=END; size=0x4
PackageList[14]: GUID=4344558D-4EF9-4725-B1E4-3376E8D6974F; size=0x83BD
Package[0]: type=STRINGS; size=0x83A5
Package[1]: type=END; size=0x4
PackageList[15]: GUID=0AF0B742-63EC-45BD-8DB6-71AD7F2FE8E8; size=0xCB04
Package[0]: type=STRINGS; size=0xCAEC
Package[1]: type=END; size=0x4
PackageList[16]: GUID=25F200AA-D3CB-470A-BF51-E7D162D22E6F; size=0x1D3D7
Package[0]: type=STRINGS; size=0x1D3BF
Package[1]: type=END; size=0x4
PackageList[17]: GUID=5F5F605D-1583-4A2D-A6B2-EB12DAB4A2B6; size=0x3048
Package[0]: type=STRINGS; size=0x3030
Package[1]: type=END; size=0x4
PackageList[18]: GUID=F3D301BB-F4A5-45A8-B0B7-FA999C6237AE; size=0x26B5
Package[0]: type=STRINGS; size=0x269D
Package[1]: type=END; size=0x4
PackageList[19]: GUID=7C04A583-9E3E-4F1C-AD65-E05268D0B4D1; size=0x5CB
Package[0]: type=STRINGS; size=0x5B3
Package[1]: type=END; size=0x4
```
From this output you can see that each Package list contains one or more data packages and ends with a special `END` package.
Ordinary package contains `EFI_HII_PACKAGE_HEADER` and data content. But the `END` package is simply a `EFI_HII_PACKAGE_HEADER` with a `Type` field set to `EFI_HII_PACKAGE_END`.
Just in case here are prototypes for the header structures once again:
```
typedef struct {
EFI_GUID PackageListGuid;
UINT32 PackagLength;
} EFI_HII_PACKAGE_LIST_HEADER;
typedef struct {
UINT32 Length:24;
UINT32 Type:8;
UINT8 Data[ … ];
} EFI_HII_PACKAGE_HEADER;
```
So basically package list data here looks something like this:
![Package_list](Package_list.png?raw=true "Package list")
Ordinary packages can be of different types. For the examples take a look at the possible defines for the `EFI_HII_PACKAGE_HEADER.type` field:
```
//
// Value of HII package type
//
#define EFI_HII_PACKAGE_TYPE_ALL 0x00
#define EFI_HII_PACKAGE_TYPE_GUID 0x01
#define EFI_HII_PACKAGE_FORMS 0x02
#define EFI_HII_PACKAGE_STRINGS 0x04
#define EFI_HII_PACKAGE_FONTS 0x05
#define EFI_HII_PACKAGE_IMAGES 0x06
#define EFI_HII_PACKAGE_SIMPLE_FONTS 0x07
#define EFI_HII_PACKAGE_DEVICE_PATH 0x08
#define EFI_HII_PACKAGE_KEYBOARD_LAYOUT 0x09
#define EFI_HII_PACKAGE_ANIMATIONS 0x0A
#define EFI_HII_PACKAGE_END 0xDF
#define EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN 0xE0
#define EFI_HII_PACKAGE_TYPE_SYSTEM_END 0xFF
```
In the next lessons we would try to add a Package list with Strings packages.
```
EFI_HII_PACKAGE_HEADER.type = EFI_HII_PACKAGE_STRINGS
```
# Create app from template
Initialize new app from our template script:
```
./createNewApp.sh HIIStringsC
```
And add new app to the `UefiLessonsPkg/UefiLessonsPkg.dsc`:
```
[Components]
...
UefiLessonsPkg/HIIStringsC/HIIStringsC.inf
```
# Package list GUID
As every Package list has its own GUID we need to create GUID and add it to our DEC file (`UefiLessonsPkg/UefiLessonsPkg.dec`):
```
[Guids]
...
gHIIStringsCGuid = { 0x8e0b8ed3, 0x14f7, 0x499d, { 0xa2, 0x24, 0xae, 0xe8, 0x9d, 0xc9, 0x7f, 0xa3 }}
```
To reference it in our code we should declare it in the application INF file as well. Add this to our `UefiLessonsPkg/HIIStringsC/HIIStringsC.inf` file:
```
[Guids]
gHIIStringsCGuid
```
For this GUID to be included we also need to add `UefiLessonsPkg/UefiLessonsPkg.dec` in the `Packages` section:
```
[Packages]
...
UefiLessonsPkg/UefiLessonsPkg.dec
```
# UefiHiiServicesLib
As each of the HII protocols can have only one instance in the system, there is a library that abstracts all the `LocateProtocol` logic in its constructor and fills global variables for protocols (https://github.com/tianocore/edk2/tree/master/MdeModulePkg/Library/UefiHiiServicesLib):
```
EFI_HII_STRING_PROTOCOL *gHiiString // UEFI HII String Protocol
EFI_HII_DATABASE_PROTOCOL *gHiiDatabase // UEFI HII Database Protocol
EFI_HII_CONFIG_ROUTING_PROTOCOL *gHiiConfigRouting // UEFI HII Config Routing Protocol
EFI_HII_FONT_PROTOCOL *gHiiFont // UEFI HII Font Protocol
EFI_HII_IMAGE_PROTOCOL *gHiiImage // UEFI HII Image Protocol
```
So instead of using this in our last application:
```
EFI_STATUS Status;
EFI_HII_DATABASE_PROTOCOL* HiiDbProtocol;
Status = gBS->LocateProtocol(&gEfiHiiDatabaseProtocolGuid,
NULL,
(VOID**)&HiiDbProtocol);
if (EFI_ERROR(Status)) {
Print(L"ERROR: Could not find HII Database protocol: %r\n", Status);
return Status;
}
```
We could simply include `UefiHiiServicesLib` to the app INF file and use `gHiiDatabase` instead as a `EFI_HII_DATABASE_PROTOCOL*`.
Let's use `UefiHiiServicesLib` in our current app. For this add `UefiHiiServicesLib` to the Library classes in the `UefiLessonsPkg/HIIStringsC/HIIStringsC.inf`:
```
[LibraryClasses]
...
UefiHiiServicesLib
```
Also we need to include this library package DEC file in the application INF:
```
[Packages]
...
MdeModulePkg/MdeModulePkg.dec
```
Finally add necessary include to our *.c file `UefiLessonsPkg/HIIStringsC/HIIStringsC.c`:
```
#include <Library/UefiHiiServicesLib.h>
```
# Application code
Here is a starting template for our application. We cheat a little bit here as we don't calculate size for our Package list, but use some number bigger that we would actually need in this example. This lesson is splitted in many parts and is hard enough as it is, I don't want to complicate things even more, so take my word on it that this size would be enough for the thing we are about to do:
```
CHAR8* Data = (CHAR8*) AllocateZeroPool(200); // CHEAT! NEEDS CORRECTION FOR YOUR OWN PACKAGES!
UINT32 offset = 0;
EFI_HII_PACKAGE_LIST_HEADER* PackageListHdr = (EFI_HII_PACKAGE_LIST_HEADER*)&Data[offset];
PackageListHdr->PackageListGuid = gHIIStringsCGuid;
offset += sizeof(EFI_HII_PACKAGE_LIST_HEADER);
<...> // Fill String Packages in the memory starting from &Data[offset]
offset += <...> // Add packages size to the 'offset' variable
EFI_HII_PACKAGE_HEADER* HIIEndPackageHdr = (EFI_HII_PACKAGE_HEADER*)&Data[offset];
HIIEndPackageHdr->Type = EFI_HII_PACKAGE_END;
HIIEndPackageHdr->Length = sizeof(EFI_HII_PACKAGE_HEADER);
offset += sizeof(EFI_HII_PACKAGE_HEADER);
PackageListHdr->PackageLength = offset;
<...> // Add new package to the HII Database
FreePool(Data);
```
Off course don't forget to add include for using memory allocation functions:
```
#include <Library/MemoryAllocationLib.h>
```
|