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
|
In our DEC file we have Dynamic and DynamicEx PCDs:
```
[PcdsDynamic]
gUefiLessonsPkgTokenSpaceGuid.PcdDynamicInt32|0xCAFECAFE|UINT32|0x4F9259A3
[PcdsDynamicEx]
gUefiLessonsPkgTokenSpaceGuid.PcdDynamicExInt32|0xBABEBABE|UINT32|0xAF35F3B2
```
When we were investigating PCD override via DSC file we've used `PcdsDynamicDefault` and `PcdsDynamicExDefault` section names:
```
[PcdsDynamicDefault]
gUefiLessonsPkgTokenSpaceGuid.PcdDynamicInt32|0x11111111
[PcdsDynamicExDefault]
gUefiLessonsPkgTokenSpaceGuid.PcdDynamicExInt32|0x22222222
```
The section name in this case plays important role. It sets the PCD storage method. The EDKII supports 3 storage methods for Dynamic/DynamicEx PCDs:
- `Default` - PCDs of this kind are stored in temporary memory (`[PcdsDynamicDefault]/[PcdsDynamicExDefault]`)
- `Hii` - PCDs of this kind are stored in EFI variables (`[PcdsDynamicHii]/[PcdsDynamicExHii]`)
- `Vpd` - PCDs of this kind are stored in read-only VPD data (`[PcdsDynamicVpd]/[PcdsDynamicExVpd]`)
So far we've worked only with `Default` PCDs. Initial values for these PCDs are always the same on each boot, it is the values encoded in the PCD database. All the PCD value changes happen in the temporary memory, so on the next boot you start from defaults.
Now let's try to investigate `Hii` PCDs.
For this storage method PCDs are declared this way:
```
<TokenGuid>.<TokenName>|<EFI Var Name>|<EFI Var GUID>|<Offset in EFI Var>[|<Override value>[|<EFI Var attributes>]]
```
For example let's use this code in the DSC file:
```
[PcdsDynamicHii]
gUefiLessonsPkgTokenSpaceGuid.PcdDynamicInt32|L"MyHiiVar"|gUefiLessonsPkgTokenSpaceGuid|0x5|0x11111111|BS,RT
[PcdsDynamicExHii]
gUefiLessonsPkgTokenSpaceGuid.PcdDynamicExInt32|L"MyExHiiVar"|gUefiLessonsPkgTokenSpaceGuid|0x7|0x22222222|BS
```
Here we've used the same GUID for the EFI var that we use for the PCD token space, but you should know that it is not mandatory.
Now additionaly correct `PCDLesson` application, so it would only get these PCDs, without any modifications:
```cpp
if (PcdToken(PcdDynamicInt32)) {
Print(L"PcdDynamicInt32=0x%x\n", PcdGet32(PcdDynamicInt32));
} else {
Print(L"PcdDynamicInt32 token is unassigned\n");
}
Print(L"PcdDynamicExInt32=0x%x\n", PcdGetEx32(&gUefiLessonsPkgTokenSpaceGuid, PcdDynamicExInt32));
```
Rebuild OVMF and copy updated `PCDLesson.efi` application to the shared folder:
```
build --platform=OvmfPkg/OvmfPkgX64.dsc --arch=X64 --buildtarget=RELEASE --tagname=GCC5
$ cp Build/OvmfX64/RELEASE_GCC5/X64/PCDLesson.efi ~/UEFI_disk/
```
Check the `parse_pcd_db` output:
```
$ parse_pcd_db \
--peidb "Build/OvmfX64/RELEASE_GCC5/X64/MdeModulePkg/Universal/PCD/Pei/Pcd/OUTPUT/PEIPcdDataBase.raw" \
--dxedb "Build/OvmfX64/RELEASE_GCC5/X64/MdeModulePkg/Universal/PCD/Dxe/Pcd/OUTPUT/DXEPcdDataBase.raw"
...
38:
Token type = HII
Datum type = UINT32
HII VARIABLE
Guid:
150cab53-ad47-4385-b5ddbcfc76bacaf0 [gUefiLessonsPkgTokenSpaceGuid]
Name:
4d 00 79 00 48 00 69 00 69 00 56 00 61 00 72 00 | M.y.H.i.i.V.a.r.
00 00 | ..
Attributes:
RT+BS
Offset:
0x0005
Value:
0x11111111 (=286331153)
...
42:
Token type = HII
Datum type = UINT32
DynamicEx Token = 0xaf35f3b2
DynamicEx GUID = 150cab53-ad47-4385-b5ddbcfc76bacaf0 [gUefiLessonsPkgTokenSpaceGuid]
HII VARIABLE
Guid:
150cab53-ad47-4385-b5ddbcfc76bacaf0 [gUefiLessonsPkgTokenSpaceGuid]
Name:
4d 00 79 00 45 00 78 00 48 00 69 00 69 00 56 00 | M.y.E.x.H.i.i.V.
61 00 72 00 00 00 | a.r...
Attributes:
BS
Offset:
0x0007
Value:
0x22222222 (=572662306)
```
You can see that now our PCDs are encoded completely differently in the PCD database.
Now launch OVMF. You can check `DumpDynPcd.efi` output, but it doesn't show anything specific about the PCD storage methood:
```
FS0:\> DumpDynPcd.efi
...
Default Token Space
Token = 0x00000026 - Type = UINT32:DYNAMIC - Size = 0x4 - Value = 0x11111111
...
150CAB53-AD47-4385-B5DD-BCFC76BACAF0
Token = 0xAF35F3B2 - Type = UINT32:DYNAMICEX - Size = 0x4 - Value = 0x22222222
```
Check the EFI Variables under the `gUefiLessonsPkgTokenSpaceGuid`:
```
FS0:\> dmpstore -guid 150cab53-ad47-4385-b5dd-bcfc76bacaf0
dmpstore: No matching variables found. Guid 150CAB53-AD47-4385-B5DD-BCFC76BACAF0
```
So our PCDs didn't create any EFI variables.
Let's execute our `PCDLesson.efi` application that right now only use `PcdGet*` functions:
```
FS0:\> PCDLesson.efi
...
PcdDynamicInt32=0x11111111
PcdDynamicExInt32=0x22222222
```
And check the EFI variables again:
```
FS0:\> dmpstore -guid 150cab53-ad47-4385-b5dd-bcfc76bacaf0
dmpstore: No matching variables found. Guid 150CAB53-AD47-4385-B5DD-BCFC76BACAF0
```
The point of this is that `PcdGet*` doesn't work with the EFI variable interface if there is no EFI variable beforehand. In this case `PcdGet*` simply returns default value from the PCD Database.
Now let's return the `PcdSet*` code to our `PCDLesson.efi` application:
```cpp
if (PcdToken(PcdDynamicInt32)) {
Print(L"PcdDynamicInt32=0x%x\n", PcdGet32(PcdDynamicInt32));
Status = PcdSet32S(PcdDynamicInt32, 0xBEEFBEEF);
Print(L"Status=%r\n", Status);
Print(L"PcdDynamicInt32=0x%x\n", PcdGet32(PcdDynamicInt32));
} else {
Print(L"PcdDynamicInt32 token is unassigned\n");
}
Print(L"PcdDynamicExInt32=0x%x\n", PcdGetEx32(&gUefiLessonsPkgTokenSpaceGuid, PcdDynamicExInt32));
PcdSetEx32S(&gUefiLessonsPkgTokenSpaceGuid, PcdDynamicExInt32, 0x77777777);
Print(L"Status=%r\n", Status);
Print(L"PcdDynamicExInt32=0x%x\n", PcdGetEx32(&gUefiLessonsPkgTokenSpaceGuid, PcdDynamicExInt32));
```
Again rebuild OVMF and copy updated `PCDLesson.efi` application to the shared folder:
```
build --platform=OvmfPkg/OvmfPkgX64.dsc --arch=X64 --buildtarget=RELEASE --tagname=GCC5
$ cp Build/OvmfX64/RELEASE_GCC5/X64/PCDLesson.efi ~/UEFI_disk/
```
Repeat our experiment:
```
FS0:\> dmpstore -guid 150cab53-ad47-4385-b5dd-bcfc76bacaf0
dmpstore: No matching variables found. Guid 150CAB53-AD47-4385-B5DD-BCFC76BACAF0
FS0:\> PCDLesson.efi
...
PcdDynamicInt32=0x11111111
Status=Success
PcdDynamicInt32=0xBEEFBEEF
PcdDynamicExInt32=0x22222222
Status=Success
PcdDynamicExInt32=0x77777777
FS0:\> dmpstore -guid 150cab53-ad47-4385-b5dd-bcfc76bacaf0
Variable BS '150CAB53-AD47-4385-B5DD-BCFC76BACAF0:MyExHiiVar' DataSize = 0x0B
00000000: 00 00 00 00 00 00 00 77-77 77 77 *.......wwww*
Variable RT+BS '150CAB53-AD47-4385-B5DD-BCFC76BACAF0:MyHiiVar' DataSize = 0x09
00000000: 00 00 00 00 00 EF BE EF-BE *.........*
```
As you can see the `PcdSet*` statements have created EFI variables that were not present before. The variables were created with a minimal possible size = `Offset in EFI Var` + `sizeof(value)`.
In our case it is `5 + 4 = 9 = 0x09` and `7 + 4 = 11 = 0x0B`.
You can see how all the fields from the PCD statement in the DSC are transformed to the EFI variable settings.
# `Non-volatile` variables
As we didn't give our variables the `NV` attributes they wouldn't be present on the next OVMF reboot from the start.
```
FS0:\> dmpstore -guid 150cab53-ad47-4385-b5dd-bcfc76bacaf0
dmpstore: No matching variables found. Guid 150CAB53-AD47-4385-B5DD-BCFC76BACAF0
```
So let's add `NV` attribute to our PCDs:
```
[PcdsDynamicHii]
gUefiLessonsPkgTokenSpaceGuid.PcdDynamicInt32|L"MyHiiVar"|gUefiLessonsPkgTokenSpaceGuid|0x5|0x11111111|BS,RT,NV
[PcdsDynamicExHii]
gUefiLessonsPkgTokenSpaceGuid.PcdDynamicExInt32|L"MyExHiiVar"|gUefiLessonsPkgTokenSpaceGuid|0x7|0x22222222|BS,NV
```
Rebuild OVMF and repeat our test:
```
FS0:\> dmpstore -guid 150cab53-ad47-4385-b5dd-bcfc76bacaf0
dmpstore: No matching variables found. Guid 150CAB53-AD47-4385-B5DD-BCFC76BACAF0
FS0:\> PCDLesson.efi
PcdDynamicInt32=0x11111111
Status=Success
PcdDynamicInt32=0xBEEFBEEF
PcdDynamicExInt32=0x22222222
Status=Success
PcdDynamicExInt32=0x77777777
FS0:\> dmpstore -guid 150cab53-ad47-4385-b5dd-bcfc76bacaf0
Variable NV+BS '150CAB53-AD47-4385-B5DD-BCFC76BACAF0:MyExHiiVar' DataSize = 0x0B
00000000: 00 00 00 00 00 00 00 77-77 77 77 *.......wwww*
Variable NV+RT+BS '150CAB53-AD47-4385-B5DD-BCFC76BACAF0:MyHiiVar' DataSize = 0x09
00000000: 00 00 00 00 00 EF BE EF-BE *.........*
```
Now restart OVMF and check EFI variables content:
```
FS0:\> dmpstore -guid 150cab53-ad47-4385-b5dd-bcfc76bacaf0
Variable NV+BS '150CAB53-AD47-4385-B5DD-BCFC76BACAF0:MyExHiiVar' DataSize = 0x0B
00000000: 00 00 00 00 00 00 00 77-77 77 77 *.......wwww*
Variable NV+RT+BS '150CAB53-AD47-4385-B5DD-BCFC76BACAF0:MyHiiVar' DataSize = 0x09
00000000: 00 00 00 00 00 EF BE EF-BE *.........*
```
As you can see now the EFI variables are present from the start. And `PcdGet*`/`PcdSet*` functions successfully use their content:
```
FS0:\> PCDLesson.efi
...
PcdDynamicInt32=0xBEEFBEEF
Status=Success
PcdDynamicInt32=0xBEEFBEEF
PcdDynamicExInt32=0x77777777
Status=Success
PcdDynamicExInt32=0x77777777
```
So the PCD default values are used only if there is no EFI variable. If the according EFI variable is present in the system the `PcdGet*` functions just read its content.
# Simplified override
It is possible to omit EFI variable attributes or value override in the PCD declaration. For example:
```
[PcdsDynamicHii]
gUefiLessonsPkgTokenSpaceGuid.PcdDynamicInt32|L"MyHiiVar"|gUefiLessonsPkgTokenSpaceGuid|0x5
[PcdsDynamicExHii]
gUefiLessonsPkgTokenSpaceGuid.PcdDynamicExInt32|L"MyExHiiVar"|gUefiLessonsPkgTokenSpaceGuid|0x7|0x22222222
```
Rebuild OVMF and check the `parse_pcd_db` output:
```
$ parse_pcd_db \
--peidb "Build/OvmfX64/RELEASE_GCC5/X64/MdeModulePkg/Universal/PCD/Pei/Pcd/OUTPUT/PEIPcdDataBase.raw" \
--dxedb "Build/OvmfX64/RELEASE_GCC5/X64/MdeModulePkg/Universal/PCD/Dxe/Pcd/OUTPUT/DXEPcdDataBase.raw"
...
38:
Token type = HII
Datum type = UINT32
HII VARIABLE
Guid:
150cab53-ad47-4385-b5ddbcfc76bacaf0 [gUefiLessonsPkgTokenSpaceGuid]
Name:
4d 00 79 00 48 00 69 00 69 00 56 00 61 00 72 00 | M.y.H.i.i.V.a.r.
00 00 | ..
Attributes:
NV+RT+BS
Offset:
0x0005
Value:
0xcafecafe (=3405695742)
...
42:
Token type = HII
Datum type = UINT32
DynamicEx Token = 0xaf35f3b2
DynamicEx GUID = 150cab53-ad47-4385-b5ddbcfc76bacaf0 [gUefiLessonsPkgTokenSpaceGuid]
HII VARIABLE
Guid:
150cab53-ad47-4385-b5ddbcfc76bacaf0 [gUefiLessonsPkgTokenSpaceGuid]
Name:
4d 00 79 00 45 00 78 00 48 00 69 00 69 00 56 00 | M.y.E.x.H.i.i.V.
61 00 72 00 00 00 | a.r...
Attributes:
NV+RT+BS
Offset:
0x0007
Value:
0x22222222 (=572662306)
```
As you can see from this example:
- by default EFI Variable gets attributes `NV+RT+BS`
- if the override value is not provided, the EDK2 just uses the value from the INF/DEC
# `Hii` PCDs in PEI and DXE stages
It is important to note that it is forbidden to use `PcdSet*` for `Hii` PCDs in PEI stage. It would cause assert and `EFI_INVALID_PARAMETER` error [https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/PCD/Pei/Service.c](https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/PCD/Pei/Service.c)
Generally the `Hii` PCD rules can be simplified to this table:
```
-------------------------------------------------------------------------------------------------------------------------------
| | PEI | DXE |
-------------------------------------------------------------------------------------------------------------------------------
| PcdGet* | If there is a EFI var, return the content from it | If there is a EFI var, return the content from it |
| | If there is no EFI var, return PCD default | If there is no EFI var, return PCD default |
|----------|-------------------------------------------------------|----------------------------------------------------------|
| PcdSet* | X | If there is a EFI var, modify PCD part |
| | | If there is no EFI var, create EFI var of minimal size |
-------------------------------------------------------------------------------------------------------------------------------
```
|