Skip to content

Latest commit

 

History

History

Lesson_PCD_HII

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

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:

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:

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

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  |
-------------------------------------------------------------------------------------------------------------------------------