Skip to content

Commit

Permalink
ArmPkg: ArmMmuLib: Pre-Allocate Page Table Memory
Browse files Browse the repository at this point in the history
Allocating memory when memory protection is active can cause the below
infinite loop:

1. gCpu->SetMemoryAttributes(EFI_MEMORY_RO)
2. ArmSetMemoryAttributes ()
3. SetMemoryRegionAttribute()
4. UpdateRegionMapping()
5. UpdateRegionMappingRecursive()
6. AllocatePages() -> Need memory for a translation table entry
7. CoreAllocatePages()
8. ApplyMemoryProtectionPolicy() -> Policy says new page should be XN
9. gCpu->SetMemoryAttributes()
10. Back to 3

To fix this previously, CpuDxe would update conventional memory to be XN
prior to installing the CpuArch protocol. However, when we transition to
setting EFI_MEMORY_RP on free memory, this will no longer work.

This PR updates ArmMmuLib to reserve page table memory for allocation
during table spits to prevent the infinite loop.

It also updates the consumers of ArmMmuLib to consume the PEI version
of the lib in the same commit so as to not break them.

Continuous-integration-options: PatchCheck.ignore-multi-package

Signed-off-by: Oliver Smith-Denny <[email protected]>
  • Loading branch information
TaylorBeebe authored and os-d committed Oct 3, 2024
1 parent f962adc commit 97397c5
Show file tree
Hide file tree
Showing 18 changed files with 389 additions and 97 deletions.
9 changes: 4 additions & 5 deletions ArmPkg/ArmPkg.dec
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@
## ArmPkg/Include/Protocol/ArmScmiPerformanceProtocol.h
gArmScmiPerformanceProtocolGuid = { 0x9b8ba84, 0x3dd3, 0x49a6, { 0xa0, 0x5a, 0x31, 0x34, 0xa5, 0xf0, 0x7b, 0xad } }

## Arm Page Table Memory Allocation Protocol
## ArmPkg/Include/Protocol/ArmPageTableMemoryAllocation.h
gArmPageTableMemoryAllocationProtocolGuid = { 0x623be44e, 0x506d, 0x487f, {0xa3, 0xa6, 0x92, 0xda, 0xef, 0x30, 0x2c, 0x70 } }

[Ppis]
## Include/Ppi/ArmMpCoreInfo.h
gArmMpCoreInfoPpiGuid = { 0x6847cc74, 0xe9ec, 0x4f8f, {0xa2, 0x9d, 0xab, 0x44, 0xe7, 0x54, 0xa8, 0xfc} }
Expand All @@ -135,11 +139,6 @@
# Define if the GICv3 controller should use the GICv2 legacy
gArmTokenSpaceGuid.PcdArmGicV3WithV2Legacy|FALSE|BOOLEAN|0x00000042

# Whether to remap all unused memory NX before installing the CPU arch
# protocol driver. This is needed on platforms that map all DRAM with RWX
# attributes initially, and can be disabled otherwise.
gArmTokenSpaceGuid.PcdRemapUnusedMemoryNx|TRUE|BOOLEAN|0x00000048

[PcdsFeatureFlag.ARM]
# Whether to map normal memory as non-shareable. FALSE is the safe choice, but
# TRUE may be appropriate to fix performance problems if you don't care about
Expand Down
1 change: 1 addition & 0 deletions ArmPkg/ArmPkg.dsc
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
ArmMmuLib|ArmPkg/Library/ArmMmuLib/ArmMmuPeiLib.inf

[Components.common]
ArmPkg/Library/ArmCacheMaintenanceLib/ArmCacheMaintenanceLib.inf
Expand Down
88 changes: 4 additions & 84 deletions ArmPkg/Drivers/CpuDxe/CpuDxe.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,77 +229,6 @@ InitializeDma (
CpuArchProtocol->DmaBufferAlignment = ArmCacheWritebackGranule ();
}

/**
Map all EfiConventionalMemory regions in the memory map with NX
attributes so that allocating or freeing EfiBootServicesData regions
does not result in changes to memory permission attributes.
**/
STATIC
VOID
RemapUnusedMemoryNx (
VOID
)
{
UINT64 TestBit;
UINTN MemoryMapSize;
UINTN MapKey;
UINTN DescriptorSize;
UINT32 DescriptorVersion;
EFI_MEMORY_DESCRIPTOR *MemoryMap;
EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
EFI_STATUS Status;

TestBit = LShiftU64 (1, EfiBootServicesData);
if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & TestBit) == 0) {
return;
}

MemoryMapSize = 0;
MemoryMap = NULL;

Status = gBS->GetMemoryMap (
&MemoryMapSize,
MemoryMap,
&MapKey,
&DescriptorSize,
&DescriptorVersion
);
ASSERT (Status == EFI_BUFFER_TOO_SMALL);
do {
MemoryMap = (EFI_MEMORY_DESCRIPTOR *)AllocatePool (MemoryMapSize);
ASSERT (MemoryMap != NULL);
Status = gBS->GetMemoryMap (
&MemoryMapSize,
MemoryMap,
&MapKey,
&DescriptorSize,
&DescriptorVersion
);
if (EFI_ERROR (Status)) {
FreePool (MemoryMap);
}
} while (Status == EFI_BUFFER_TOO_SMALL);

ASSERT_EFI_ERROR (Status);

MemoryMapEntry = MemoryMap;
MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + MemoryMapSize);
while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
if (MemoryMapEntry->Type == EfiConventionalMemory) {
ArmSetMemoryAttributes (
MemoryMapEntry->PhysicalStart,
EFI_PAGES_TO_SIZE (MemoryMapEntry->NumberOfPages),
EFI_MEMORY_XP,
EFI_MEMORY_XP
);
}

MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
}
}

EFI_STATUS
CpuDxeInitialize (
IN EFI_HANDLE ImageHandle,
Expand All @@ -313,26 +242,17 @@ CpuDxeInitialize (

InitializeDma (&mCpu);

//
// Once we install the CPU arch protocol, the DXE core's memory
// protection routines will invoke them to manage the permissions of page
// allocations as they are created. Given that this includes pages
// allocated for page tables by this driver, we must ensure that unused
// memory is mapped with the same permissions as boot services data
// regions. Otherwise, we may end up with unbounded recursion, due to the
// fact that updating permissions on a newly allocated page table may trigger
// a block entry split, which triggers a page table allocation, etc etc
//
if (FeaturePcdGet (PcdRemapUnusedMemoryNx)) {
RemapUnusedMemoryNx ();
}
Status = InitializePageTableMemory (ImageHandle);
ASSERT_EFI_ERROR (Status);

Status = gBS->InstallMultipleProtocolInterfaces (
&mCpuHandle,
&gEfiCpuArchProtocolGuid,
&mCpu,
&gEfiMemoryAttributeProtocolGuid,
&mMemoryAttribute,
&gArmPageTableMemoryAllocationProtocolGuid,
&mPageTableMemAllocProtocol,
NULL
);

Expand Down
18 changes: 18 additions & 0 deletions ArmPkg/Drivers/CpuDxe/CpuDxe.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@
#include <Protocol/DebugSupport.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/MemoryAttribute.h>
#include <Protocol/ArmPageTableMemoryAllocation.h>

extern BOOLEAN mIsFlushingGCD;

extern EFI_MEMORY_ATTRIBUTE_PROTOCOL mMemoryAttribute;

extern PAGE_TABLE_MEM_ALLOC_PROTOCOL mPageTableMemAllocProtocol;

/**
This function registers and enables the handler specified by InterruptHandler for a processor
interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
Expand Down Expand Up @@ -143,4 +146,19 @@ RegionAttributeToGcdAttribute (
IN UINTN PageAttributes
);

/**
Initialize the page table memory pool and produce the page table memory allocation
protocol.
@param[in] ImageHandle Handle on which to install the protocol.
@retval EFI_SUCCESS The page table pool was initialized and protocol produced.
@retval Others The driver returned an error while initializing.
**/
EFI_STATUS
EFIAPI
InitializePageTableMemory (
IN EFI_HANDLE ImageHandle
);

#endif // CPU_DXE_H_
4 changes: 3 additions & 1 deletion ArmPkg/Drivers/CpuDxe/CpuDxe.inf
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
CpuMmuCommon.c
Exception.c
MemoryAttribute.c
PageTableMemoryAllocation.c

[Sources.ARM]
Arm/Mmu.c
Expand All @@ -40,6 +41,7 @@
[LibraryClasses]
ArmLib
ArmMmuLib
BaseLib
BaseMemoryLib
CacheMaintenanceLib
CpuLib
Expand All @@ -56,6 +58,7 @@
[Protocols]
gEfiCpuArchProtocolGuid
gEfiMemoryAttributeProtocolGuid
gArmPageTableMemoryAllocationProtocolGuid

[Guids]
gEfiDebugImageInfoTableGuid
Expand All @@ -69,7 +72,6 @@

[FeaturePcd.common]
gArmTokenSpaceGuid.PcdDebuggerExceptionSupport
gArmTokenSpaceGuid.PcdRemapUnusedMemoryNx

[Depex]
gHardwareInterruptProtocolGuid OR gHardwareInterrupt2ProtocolGuid
Loading

0 comments on commit 97397c5

Please sign in to comment.