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: ``` .|||[|[|]] ``` 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 | ------------------------------------------------------------------------------------------------------------------------------- ```