/** Set head Guard and tail Guard for the given memory range. @param[in] Memory Base address of memory to set guard for. @param[in] NumberOfPages Memory size in pages. @return VOID. **/ VOID SetGuardForMemory ( IN EFI_PHYSICAL_ADDRESS Memory, IN UINTN NumberOfPages ) { EFI_PHYSICAL_ADDRESS GuardPage; // // Set tail Guard // GuardPage = Memory + EFI_PAGES_TO_SIZE (NumberOfPages); if (!IsGuardPage (GuardPage)) { SetGuardPage (GuardPage); } // Set head Guard GuardPage = Memory - EFI_PAGES_TO_SIZE (1); if (!IsGuardPage (GuardPage)) { SetGuardPage (GuardPage); } // // Mark the memory range as Guarded // SetGuardedMemoryBits (Memory, NumberOfPages); }
/** Adjust address of free memory according to existing and/or required Guard. This function will check if there're existing Guard pages of adjacent memory blocks, and try to use it as the Guard page of the memory to be allocated. @param[in] Start Start address of free memory block. @param[in] Size Size of free memory block. @param[in] SizeRequested Size of memory to allocate. @return The end address of memory block found. @return 0 if no enough space for the required size of memory and its Guard. **/ UINT64 AdjustMemoryS ( IN UINT64 Start, IN UINT64 Size, IN UINT64 SizeRequested ) { UINT64 Target; // // UEFI spec requires that allocated pool must be 8-byte aligned. If it's // indicated to put the pool near the Tail Guard, we need extra bytes to // make sure alignment of the returned pool address. // if ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0) { SizeRequested = ALIGN_VALUE(SizeRequested, 8); } Target = Start + Size - SizeRequested; ASSERT (Target >= Start); if (Target == 0) { return 0; } if (!IsGuardPage (Start + Size)) { // No Guard at tail to share. One more page is needed. Target -= EFI_PAGES_TO_SIZE (1); } // Out of range? if (Target < Start) { return 0; } // At the edge? if (Target == Start) { if (!IsGuardPage (Target - EFI_PAGES_TO_SIZE (1))) { // No enough space for a new head Guard if no Guard at head to share. return 0; } } // OK, we have enough pages for memory and its Guards. Return the End of the // free space. return Target + SizeRequested - 1; }
/** Adjust the base and number of pages to really allocate according to Guard. @param[in,out] Memory Base address of free memory. @param[in,out] NumberOfPages Size of memory to allocate. @return VOID. **/ VOID AdjustMemoryA ( IN OUT EFI_PHYSICAL_ADDRESS *Memory, IN OUT UINTN *NumberOfPages ) { // // FindFreePages() has already taken the Guard into account. It's safe to // adjust the start address and/or number of pages here, to make sure that // the Guards are also "allocated". // if (!IsGuardPage (*Memory + EFI_PAGES_TO_SIZE (*NumberOfPages))) { // No tail Guard, add one. *NumberOfPages += 1; } if (!IsGuardPage (*Memory - EFI_PAGE_SIZE)) { // No head Guard, add one. *Memory -= EFI_PAGE_SIZE; *NumberOfPages += 1; } }
/** Manage memory permission attributes on a memory range, according to the configured DXE memory protection policy. @param OldType The old memory type of the range @param NewType The new memory type of the range @param Memory The base address of the range @param Length The size of the range (in bytes) @return EFI_SUCCESS If we are executing in SMM mode. No permission attributes are updated in this case @return EFI_SUCCESS If the the CPU arch protocol is not installed yet @return EFI_SUCCESS If no DXE memory protection policy has been configured @return EFI_SUCCESS If OldType and NewType use the same permission attributes @return other Return value of gCpu->SetMemoryAttributes() **/ EFI_STATUS EFIAPI ApplyMemoryProtectionPolicy ( IN EFI_MEMORY_TYPE OldType, IN EFI_MEMORY_TYPE NewType, IN EFI_PHYSICAL_ADDRESS Memory, IN UINT64 Length ) { UINT64 OldAttributes; UINT64 NewAttributes; // // The policy configured in PcdDxeNxMemoryProtectionPolicy // does not apply to allocations performed in SMM mode. // if (IsInSmm ()) { return EFI_SUCCESS; } // // If the CPU arch protocol is not installed yet, we cannot manage memory // permission attributes, and it is the job of the driver that installs this // protocol to set the permissions on existing allocations. // if (gCpu == NULL) { return EFI_SUCCESS; } // // Check if a DXE memory protection policy has been configured // if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) == 0) { return EFI_SUCCESS; } // // Don't overwrite Guard pages, which should be the first and/or last page, // if any. // if (IsHeapGuardEnabled ()) { if (IsGuardPage (Memory)) { Memory += EFI_PAGE_SIZE; Length -= EFI_PAGE_SIZE; if (Length == 0) { return EFI_SUCCESS; } } if (IsGuardPage (Memory + Length - EFI_PAGE_SIZE)) { Length -= EFI_PAGE_SIZE; if (Length == 0) { return EFI_SUCCESS; } } } // // Update the executable permissions according to the DXE memory // protection policy, but only if // - the policy is different between the old and the new type, or // - this is a newly added region (OldType == EfiMaxMemoryType) // NewAttributes = GetPermissionAttributeForMemoryType (NewType); if (OldType != EfiMaxMemoryType) { OldAttributes = GetPermissionAttributeForMemoryType (OldType); if (OldAttributes == NewAttributes) { // policy is the same between OldType and NewType return EFI_SUCCESS; } } else if (NewAttributes == 0) { // newly added region of a type that does not require protection return EFI_SUCCESS; } return gCpu->SetMemoryAttributes (gCpu, Memory, Length, NewAttributes); }
/** Helper function of memory allocation with Guard pages. @param FreePageList The free page node. @param NumberOfPages Number of pages to be allocated. @param MaxAddress Request to allocate memory below this address. @param MemoryType Type of memory requested. @return Memory address of allocated pages. **/ UINTN InternalAllocMaxAddressWithGuard ( IN OUT LIST_ENTRY *FreePageList, IN UINTN NumberOfPages, IN UINTN MaxAddress, IN EFI_MEMORY_TYPE MemoryType ) { LIST_ENTRY *Node; FREE_PAGE_LIST *Pages; UINTN PagesToAlloc; UINTN HeadGuard; UINTN TailGuard; UINTN Address; for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) { Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); if (Pages->NumberOfPages >= NumberOfPages && (UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress) { // // We may need 1 or 2 more pages for Guard. Check it out. // PagesToAlloc = NumberOfPages; TailGuard = (UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages); if (!IsGuardPage (TailGuard)) { // // Add one if no Guard at the end of current free memory block. // PagesToAlloc += 1; TailGuard = 0; } HeadGuard = (UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages - PagesToAlloc) - EFI_PAGE_SIZE; if (!IsGuardPage (HeadGuard)) { // // Add one if no Guard at the page before the address to allocate // PagesToAlloc += 1; HeadGuard = 0; } if (Pages->NumberOfPages < PagesToAlloc) { // Not enough space to allocate memory with Guards? Try next block. continue; } Address = InternalAllocPagesOnOneNode (Pages, PagesToAlloc, MaxAddress); ConvertSmmMemoryMapEntry(MemoryType, Address, PagesToAlloc, FALSE); CoreFreeMemoryMapStack(); if (HeadGuard == 0) { // Don't pass the Guard page to user. Address += EFI_PAGE_SIZE; } SetGuardForMemory (Address, NumberOfPages); return Address; } } return (UINTN)(-1); }