From nobody Thu Dec 26 13:03:53 2024 Delivered-To: importer@patchew.org Received-SPF: none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) client-ip=198.145.21.10; envelope-from=edk2-devel-bounces@lists.01.org; helo=ml01.01.org; Authentication-Results: mx.zohomail.com; spf=none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) smtp.mailfrom=edk2-devel-bounces@lists.01.org Return-Path: Received: from ml01.01.org (ml01.01.org [198.145.21.10]) by mx.zohomail.com with SMTPS id 1505294724054314.10170276984775; Wed, 13 Sep 2017 02:25:24 -0700 (PDT) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 5F56521D492E9; Wed, 13 Sep 2017 02:22:25 -0700 (PDT) Received: from mga06.intel.com (mga06.intel.com [134.134.136.31]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 36E7021E8796F for ; Wed, 13 Sep 2017 02:22:24 -0700 (PDT) Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by orsmga104.jf.intel.com with ESMTP; 13 Sep 2017 02:25:21 -0700 Received: from jwang36-mobl2.ccr.corp.intel.com ([10.239.192.50]) by fmsmga005.fm.intel.com with ESMTP; 13 Sep 2017 02:25:19 -0700 X-Original-To: edk2-devel@lists.01.org X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.42,387,1500966000"; d="scan'208";a="150787223" From: "Wang, Jian J" To: edk2-devel@lists.01.org Date: Wed, 13 Sep 2017 17:25:04 +0800 Message-Id: <20170913092507.12504-2-jian.j.wang@intel.com> X-Mailer: git-send-email 2.14.1.windows.1 In-Reply-To: <20170913092507.12504-1-jian.j.wang@intel.com> References: <20170913092507.12504-1-jian.j.wang@intel.com> Subject: [edk2] [PATCH 1/4] MdeModulePkg/Core: Implement NULL pointer detection in EDK-II Core. X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Justen@ml01.01.org, Eric Dong , Kinney@ml01.01.org, Jordan L , Wolman@ml01.01.org, Jiewen Yao , Ayellet , Michael D , Laszlo Ersek , Star Zeng MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Errors-To: edk2-devel-bounces@lists.01.org Sender: "edk2-devel" X-ZohoMail: RSF_4 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" The mechanism behind is to trigger a page fault exception at address 0. Thi= s can be made by disabling page 0 (0-4095) during page table setup. So this= feature can only be available on platform with paging enabled. Once this f= eature is enabled, any code, like CSM, which has to access memory in page 0= needs to enable this page temporarily in advance and disable it afterwards= . PcdNullPointerDetectionPropertyMask is used to control and elaborate the = use cases. Cc: Jiewen Yao Cc: Eric Dong Cc: Star Zeng Cc: Laszlo Ersek Cc: Justen, Jordan L Cc: Kinney, Michael D Cc: Wolman, Ayellet Suggested-by: Wolman, Ayellet Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Wang, Jian J Reviewed-by: Brian J. Johnson --- MdeModulePkg/Core/Dxe/DxeMain.inf | 3 +- MdeModulePkg/Core/Dxe/Mem/Page.c | 21 ++++++---- MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 47 +++++++++++++++++++++ MdeModulePkg/Core/DxeIplPeim/DxeIpl.h | 15 +++++++ MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf | 3 +- MdeModulePkg/Core/DxeIplPeim/DxeLoad.c | 53 ++++++++++++++++++++= ++++ MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c | 8 +++- MdeModulePkg/Core/DxeIplPeim/X64/DxeLoadFunc.c | 2 + MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c | 23 ++++++---- MdeModulePkg/MdeModulePkg.dec | 12 ++++++ 10 files changed, 167 insertions(+), 20 deletions(-) diff --git a/MdeModulePkg/Core/Dxe/DxeMain.inf b/MdeModulePkg/Core/Dxe/DxeM= ain.inf index 30d5984f7c..273b8b7c0e 100644 --- a/MdeModulePkg/Core/Dxe/DxeMain.inf +++ b/MdeModulePkg/Core/Dxe/DxeMain.inf @@ -179,7 +179,7 @@ gEfiWatchdogTimerArchProtocolGuid ## CONSUMES =20 [FeaturePcd] - gEfiMdeModulePkgTokenSpaceGuid.PcdFrameworkCompatibilitySupport ## CO= NSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFrameworkCompatibilitySupport = ## CONSUMES =20 [Pcd] gEfiMdeModulePkgTokenSpaceGuid.PcdLoadFixAddressBootTimeCodePageNumber = ## SOMETIMES_CONSUMES @@ -192,6 +192,7 @@ gEfiMdeModulePkgTokenSpaceGuid.PcdPropertiesTableEnable = ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdImageProtectionPolicy = ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdDxeNxMemoryProtectionPolicy = ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask = ## CONSUMES =20 # [Hob] # RESOURCE_DESCRIPTOR ## CONSUMES diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/P= age.c index a142c79ee2..2e0b72f864 100644 --- a/MdeModulePkg/Core/Dxe/Mem/Page.c +++ b/MdeModulePkg/Core/Dxe/Mem/Page.c @@ -170,6 +170,7 @@ CoreAddRange ( { LIST_ENTRY *Link; MEMORY_MAP *Entry; + EFI_STATUS Status; =20 ASSERT ((Start & EFI_PAGE_MASK) =3D=3D 0); ASSERT (End > Start) ; @@ -188,7 +189,17 @@ CoreAddRange ( // used for other purposes. // =20 if (Type =3D=3D EfiConventionalMemory && Start =3D=3D 0 && (End >=3D EFI= _PAGE_SIZE - 1)) { - SetMem ((VOID *)(UINTN)Start, EFI_PAGE_SIZE, 0); + if ((PcdGet8(PcdNullPointerDetectionPropertyMask) & BIT0) =3D=3D 0) { + SetMem ((VOID *)(UINTN)Start, EFI_PAGE_SIZE, 0); + } else if (gCpu !=3D NULL) { + Status =3D gCpu->SetMemoryAttributes(gCpu, 0, EFI_PAGE_SIZE, 0); + ASSERT_EFI_ERROR(Status); + + SetMem ((VOID *)(UINTN)Start, EFI_PAGE_SIZE, 0); + + Status =3D gCpu->SetMemoryAttributes(gCpu, 0, EFI_PAGE_SIZE, EFI_MEM= ORY_RP); + ASSERT_EFI_ERROR(Status); + } } =20 // @@ -1972,11 +1983,3 @@ Done: return Status; } =20 - - - - - - - - diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/C= ore/Dxe/Misc/MemoryProtection.c index a73c4ccd64..2367d674e1 100644 --- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c +++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c @@ -995,6 +995,36 @@ MemoryProtectionExitBootServicesCallback ( } } =20 +/** + Disable NULL pointer detection after EndOfDxe. This is a workaround reso= rt in=20 + order to skip unfixable NULL pointer access issues detected in OptionROM= or=20 + boot loaders. + + @param[in] Event The Event this notify function registered to. + @param[in] Context Pointer to the context data registered to the Even= t. +**/ +VOID +EFIAPI +DisableNullDetectionAtTheEndOfDxe ( + EFI_EVENT Event, + VOID *Context + ) +{ + EFI_STATUS Status; + + DEBUG((DEBUG_INFO, "DisableNullDetectionAtTheEndOfDxe(): start\r\n")); + // + // Disable NULL pointer detection by enabling first 4K page + // + Status =3D gCpu->SetMemoryAttributes(gCpu, 0, EFI_PAGE_SIZE, 0); + ASSERT_EFI_ERROR(Status); + + CoreCloseEvent (Event); + DEBUG((DEBUG_INFO, "DisableNullDetectionAtTheEndOfDxe(): end\r\n")); + + return; +} + /** Initialize Memory Protection support. **/ @@ -1006,6 +1036,7 @@ CoreInitializeMemoryProtection ( { EFI_STATUS Status; EFI_EVENT Event; + EFI_EVENT EndOfDxeEvent; VOID *Registration; =20 mImageProtectionPolicy =3D PcdGet32(PcdImageProtectionPolicy); @@ -1044,6 +1075,22 @@ CoreInitializeMemoryProtection ( ); ASSERT_EFI_ERROR(Status); } + + // + // Register a callback to disable NULL pointer detection at EndOfDxe + // + if ((PcdGet8(PcdNullPointerDetectionPropertyMask) & (BIT0|BIT7)) =3D=3D = (BIT0|BIT7)) { + Status =3D CoreCreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + DisableNullDetectionAtTheEndOfDxe, + NULL, + &gEfiEndOfDxeEventGroupGuid, + &EndOfDxeEvent + ); + ASSERT_EFI_ERROR (Status); + } + return ; } =20 diff --git a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.h b/MdeModulePkg/Core/DxeI= plPeim/DxeIpl.h index 72d2532f50..104599156c 100644 --- a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.h +++ b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.h @@ -52,6 +52,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER= EXPRESS OR IMPLIED. #define STACK_SIZE 0x20000 #define BSP_STORE_SIZE 0x4000 =20 +#define NULL_DETECTION_ENABLED ((PcdGet8(PcdNullPointerDetectionPropertyM= ask) & BIT0) !=3D 0) =20 // // This PPI is installed to indicate the end of the PEI usage of memory @@ -240,4 +241,18 @@ Decompress ( OUT UINTN *OutputSize ); =20 +/** + Clear legacy memory located at the first 4K-page. + + This function traverses the whole HOB list to check if memory from 0 to= 4095=20 + exists and has not been allocated, and then clear it if so. + + @param HoStart The start of HobList passed to DxeCore. + +**/ +VOID +ClearLegacyMemory( + IN VOID *HobStart + ); + #endif diff --git a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf b/MdeModulePkg/Core/Dx= eIplPeim/DxeIpl.inf index c54afe4aa6..fde70f94bb 100644 --- a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf +++ b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf @@ -110,11 +110,12 @@ gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplBuildPageTables ## CONSUMES =20 [FeaturePcd] - gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSupportUefiDecompress ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSupportUefiDecompress ##= CONSUMES =20 [Pcd.IA32,Pcd.X64] gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable ##= SOMETIMES_CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ##= CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask ##= CONSUMES =20 [Pcd.IA32,Pcd.X64,Pcd.ARM,Pcd.AARCH64] gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack ## SOMETIM= ES_CONSUMES diff --git a/MdeModulePkg/Core/DxeIplPeim/DxeLoad.c b/MdeModulePkg/Core/Dxe= IplPeim/DxeLoad.c index 50b5440d15..b5f9d92f5b 100644 --- a/MdeModulePkg/Core/DxeIplPeim/DxeLoad.c +++ b/MdeModulePkg/Core/DxeIplPeim/DxeLoad.c @@ -825,3 +825,56 @@ UpdateStackHob ( Hob.Raw =3D GET_NEXT_HOB (Hob); } } + +/** + Clear legacy memory located at the first 4K-page, if available. + + This function traverses the whole HOB list to check if memory from 0 to= 4095=20 + exists and has not been allocated, and then clear it if so. + + @param HoStart The start of HobList passed to DxeCore. + +**/ +VOID +ClearLegacyMemory( + IN VOID *HobStart + ) +{ + EFI_PEI_HOB_POINTERS RscDescHob; + EFI_PEI_HOB_POINTERS MemAllocHob; + BOOLEAN DoClear; + + RscDescHob.Raw =3D HobStart; + MemAllocHob.Raw =3D HobStart; + DoClear =3D FALSE; + + // + // Check if page 0 exists and free + // + while ((RscDescHob.Raw =3D GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR,= RscDescHob.Raw)) !=3D NULL) { + if (RscDescHob.ResourceDescriptor->ResourceType =3D=3D EFI_RESOURCE_SY= STEM_MEMORY &&=20 + RscDescHob.ResourceDescriptor->PhysicalStart =3D=3D 0) { + DoClear =3D TRUE; + //=20 + // Make sure memory at 0-4095 has not been allocated. + // + while ((MemAllocHob.Raw =3D GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATI= ON, MemAllocHob.Raw)) !=3D NULL) { + if (MemAllocHob.MemoryAllocation->AllocDescriptor.MemoryBaseAddres= s < EFI_PAGE_SIZE) { + DoClear =3D FALSE; + break; + } + MemAllocHob.Raw =3D GET_NEXT_HOB (MemAllocHob); + } + break; + } + RscDescHob.Raw =3D GET_NEXT_HOB (RscDescHob); + } + + if (DoClear) { + DEBUG((DEBUG_INFO, "Clearing first 4K-page!\r\n")); + SetMem(NULL, EFI_PAGE_SIZE, 0); + } + + return; +} + diff --git a/MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c b/MdeModulePkg= /Core/DxeIplPeim/Ia32/DxeLoadFunc.c index 1957326caf..a8aa0d5d1b 100644 --- a/MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c +++ b/MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c @@ -123,7 +123,8 @@ Create4GPageTablesIa32Pae ( PageDirectoryPointerEntry->Bits.Present =3D 1; =20 for (IndexOfPageDirectoryEntries =3D 0; IndexOfPageDirectoryEntries < = 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress += =3D SIZE_2MB) { - if ((PhysicalAddress < StackBase + StackSize) && ((PhysicalAddress += SIZE_2MB) > StackBase)) { + if ((NULL_DETECTION_ENABLED && PhysicalAddress =3D=3D 0) + || ((PhysicalAddress < StackBase + StackSize) && ((PhysicalAddre= ss + SIZE_2MB) > StackBase))) { // // Need to split this 2M page that covers stack range. // @@ -240,6 +241,8 @@ HandOffToDxeCore ( EFI_PEI_VECTOR_HANDOFF_INFO_PPI *VectorHandoffInfoPpi; BOOLEAN BuildPageTablesIa32Pae; =20 + ClearLegacyMemory(HobList.Raw); + Status =3D PeiServicesAllocatePages (EfiBootServicesData, EFI_SIZE_TO_PA= GES (STACK_SIZE), &BaseOfStack); ASSERT_EFI_ERROR (Status); =20 @@ -379,7 +382,8 @@ HandOffToDxeCore ( TopOfStack =3D (EFI_PHYSICAL_ADDRESS) (UINTN) ALIGN_POINTER (TopOfStac= k, CPU_STACK_ALIGNMENT); =20 PageTables =3D 0; - BuildPageTablesIa32Pae =3D (BOOLEAN) (PcdGetBool (PcdSetNxForStack) &&= IsIa32PaeSupport () && IsExecuteDisableBitAvailable ()); + BuildPageTablesIa32Pae =3D (BOOLEAN) (IsIa32PaeSupport () && IsExecute= DisableBitAvailable () + && (PcdGetBool (PcdSetNxForStack) = || NULL_DETECTION_ENABLED)); if (BuildPageTablesIa32Pae) { PageTables =3D Create4GPageTablesIa32Pae (BaseOfStack, STACK_SIZE); EnableExecuteDisableBit (); diff --git a/MdeModulePkg/Core/DxeIplPeim/X64/DxeLoadFunc.c b/MdeModulePkg/= Core/DxeIplPeim/X64/DxeLoadFunc.c index 6488880eab..50a8d77a5b 100644 --- a/MdeModulePkg/Core/DxeIplPeim/X64/DxeLoadFunc.c +++ b/MdeModulePkg/Core/DxeIplPeim/X64/DxeLoadFunc.c @@ -42,6 +42,8 @@ HandOffToDxeCore ( EFI_VECTOR_HANDOFF_INFO *VectorInfo; EFI_PEI_VECTOR_HANDOFF_INFO_PPI *VectorHandoffInfoPpi; =20 + ClearLegacyMemory(HobList.Raw); + // // Get Vector Hand-off Info PPI and build Guided HOB // diff --git a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c b/MdeModulePk= g/Core/DxeIplPeim/X64/VirtualMemory.c index 48150be4e1..ccd6e10cb2 100644 --- a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c +++ b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c @@ -90,8 +90,14 @@ Split2MPageTo4K ( // PageTableEntry->Uint64 =3D (UINT64) PhysicalAddress4K | AddressEncMask; PageTableEntry->Bits.ReadWrite =3D 1; - PageTableEntry->Bits.Present =3D 1; - if ((PhysicalAddress4K >=3D StackBase) && (PhysicalAddress4K < StackBa= se + StackSize)) { + + if (NULL_DETECTION_ENABLED && PhysicalAddress4K =3D=3D 0) { + PageTableEntry->Bits.Present =3D 0; + } else { + PageTableEntry->Bits.Present =3D 1; + } + + if (PcdGetBool (PcdSetNxForStack) && (PhysicalAddress4K >=3D StackBase= ) && (PhysicalAddress4K < StackBase + StackSize)) { // // Set Nx bit for stack. // @@ -137,9 +143,10 @@ Split1GPageTo2M ( =20 PhysicalAddress2M =3D PhysicalAddress; for (IndexOfPageDirectoryEntries =3D 0; IndexOfPageDirectoryEntries < 51= 2; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress2M += =3D SIZE_2MB) { - if ((PhysicalAddress2M < StackBase + StackSize) && ((PhysicalAddress2M= + SIZE_2MB) > StackBase)) { + if ((NULL_DETECTION_ENABLED && PhysicalAddress2M =3D=3D 0) + || (PcdGetBool (PcdSetNxForStack) && (PhysicalAddress2M < StackBas= e + StackSize) && ((PhysicalAddress2M + SIZE_2MB) > StackBase))) { // - // Need to split this 2M page that covers stack range. + // Need to split this 2M page that covers NULL or stack range. // Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, S= tackBase, StackSize); } else { @@ -279,7 +286,8 @@ CreateIdentityMappingPageTables ( PageDirectory1GEntry =3D (VOID *) PageDirectoryPointerEntry; =20 for (IndexOfPageDirectoryEntries =3D 0; IndexOfPageDirectoryEntries = < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += =3D SIZE_1GB) { - if (PcdGetBool (PcdSetNxForStack) && (PageAddress < StackBase + St= ackSize) && ((PageAddress + SIZE_1GB) > StackBase)) { + if ((NULL_DETECTION_ENABLED && PageAddress =3D=3D 0) + || (PcdGetBool (PcdSetNxForStack) && (PageAddress < StackBase = + StackSize) && ((PageAddress + SIZE_1GB) > StackBase))) { Split1GPageTo2M (PageAddress, (UINT64 *) PageDirectory1GEntry, S= tackBase, StackSize); } else { // @@ -308,9 +316,10 @@ CreateIdentityMappingPageTables ( PageDirectoryPointerEntry->Bits.Present =3D 1; =20 for (IndexOfPageDirectoryEntries =3D 0; IndexOfPageDirectoryEntrie= s < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += =3D SIZE_2MB) { - if (PcdGetBool (PcdSetNxForStack) && (PageAddress < StackBase + = StackSize) && ((PageAddress + SIZE_2MB) > StackBase)) { + if ((NULL_DETECTION_ENABLED && PageAddress =3D=3D 0) + || (PcdGetBool (PcdSetNxForStack) && (PageAddress < StackBas= e + StackSize) && ((PageAddress + SIZE_2MB) > StackBase))) { // - // Need to split this 2M page that covers stack range. + // Need to split this 2M page that covers NULL or stack range. // Split2MPageTo4K (PageAddress, (UINT64 *) PageDirectoryEntry, S= tackBase, StackSize); } else { diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 593bff357a..1cc84894af 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -860,6 +860,18 @@ # @ValidList 0x80000006 | 0x03058002 gEfiMdeModulePkgTokenSpaceGuid.PcdErrorCodeSetVariable|0x03058002|UINT32= |0x30001040 =20 + ## Mask to control the NULL address detection in code for different phas= es. + # If enabled, accessing NULL address in UEFI or SMM code can be caught.=

+ # BIT0 - Enable NULL pointer detection for UEFI.
+ # BIT1 - Enable NULL pointer detection for SMM.
+ # BIT2..6 - Reserved for future uses.
+ # BIT7 - Disable NULL pointer detection just after EndOfDxe.
+ # This is a workaround for those unsolvable NULL access iss= ues in OptionROM, boot loader, etc. + # It can also help to avoid unnecessary exception caused by= legacy memory (0-4095) access after=20 + # EndOfDxe, such as Windows 7 boot on Qemu.
+ # @Prompt Enable NULL address detection. + gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask|0x0|U= INT8|0x30001050 + [PcdsFixedAtBuild, PcdsPatchableInModule] ## Dynamic type PCD can be registered callback function for Pcd setting = action. # PcdMaxPeiPcdCallBackNumberPerPcdEntry indicates the maximum number of= callback function --=20 2.14.1.windows.1 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel