When a UEFI payload is chainloaded from an outer UEFI firmware, the memory the OS sees as available after ExitBootServices() should be essentially the same as when the OS boots directly on the outer firmware. Today it is not: on AArch64, -m 2G, the same memory-map probe under the payload sees 9,528 KiB less usable memory than a direct boot, and that whole difference is one EfiReservedMemoryType descriptor covering the launcher's own payload FV, HOB list, initial stack and translation-table pages.
None of those need to survive past the OS's ExitBootServices(): DxeCore loads every driver out of the FV into its own pages, the launcher's HOB list is dead once UefiPayloadEntry has rebuilt the HOB list, the launcher's stack is dead once HandOffToDxeCore() has switched to its own, and firmware translation tables are the same EfiBootServicesData in a normal ArmMmuLib-based boot. They are Reserved only so that they stay isolated in the outer memory-map snapshot and are never selected as free RAM by the payload's HOB-memory search. Introduce a gLoaderBootTimeReservationGuid HOB carrying every such range. ChainloadApp records the FV, HOB list and stack directly, and the translation-table pages via ReservedUefiMemoryAllocationLib, which backs every ArmMmuLib page allocation and records each Reserved page it hands out. Any Reserved outer-map descriptor whose whole span is covered by recorded reservations is emitted as SBL type 1 (RAM) so the payload publishes it as EFI_RESOURCE_SYSTEM_MEMORY, and the GUID HOB is emitted alongside. The reservation list is a separate GUID HOB and not a bit in the existing MEMORY_MAP_ENTRY.Flag byte because the pinned ranges do not, in general, coincide with memory-map descriptor boundaries: a reservation can be a page-sized allocation inside a much larger descriptor of a different type, and a Flag bit on that descriptor cannot express "pin these two pages of this range". A per-range list is required; the consumer rejects any Revision it does not understand. UefiPayloadEntry consumes the HOB before choosing HobMemBase and excludes any candidate that overlaps a listed range; after the HOB list is rebuilt, BuildGenericHob() pins every listed range with an EfiBootServicesData memory-allocation HOB. CoreInitializeMemoryServices()' FindLargestFreeRegion() and CoreInitializeGcdServices()' memory-allocation-HOB walk both honour those pins, so DXE never allocates over the launcher's live FV, stack or page tables and the OS reclaims them after ExitBootServices(). A launcher that emits no such HOB (Slim Bootloader, coreboot) sees no change: the count is 0 and the existing paths run as before. Measured on the AArch64 -m 2G reference platform, with the translation-table handover in place: Reserved 50,680 -> 41,152 KiB (-9,528 KiB) Usable-to-OS 2,068,068 -> 2,077,596 KiB (+9,528 KiB) The IsReservedInMemoryMap() assertion is unchanged: the allocations are still EfiReservedMemoryType in the outer memory map, and that is what keeps them from coalescing with adjacent conventional memory into a range large enough to be selected as HOB memory before any allocation HOB is honoured. Cc: Benjamin Doron <[email protected]> Cc: Gua Guo <[email protected]> Cc: Guo Dong <[email protected]> Cc: James Lu <[email protected]> Cc: Sean Rhodes <[email protected]> Cc: Shuo Liu <[email protected]> Assisted-by: claude-opus-5 Signed-off-by: Alexander Graf <[email protected]> --- UefiPayloadPkg/ChainloadApp/ChainloadApp.c | 204 ++++++++++++++++-- UefiPayloadPkg/ChainloadApp/ChainloadApp.inf | 1 + .../Include/Guid/BootTimeReservationGuid.h | 53 +++++ .../UefiPayloadEntry/UefiPayloadEntry.c | 151 +++++++++++++ .../UefiPayloadEntry/UefiPayloadEntry.inf | 1 + UefiPayloadPkg/UefiPayloadPkg.dec | 1 + UefiPayloadPkg/UefiPayloadPkg.dsc | 7 +- 7 files changed, 399 insertions(+), 19 deletions(-) create mode 100644 UefiPayloadPkg/Include/Guid/BootTimeReservationGuid.h diff --git a/UefiPayloadPkg/ChainloadApp/ChainloadApp.c b/UefiPayloadPkg/ChainloadApp/ChainloadApp.c index 77c19d12e2..029d56e4aa 100644 --- a/UefiPayloadPkg/ChainloadApp/ChainloadApp.c +++ b/UefiPayloadPkg/ChainloadApp/ChainloadApp.c @@ -40,6 +40,7 @@ #include <UniversalPayload/ExtraData.h> #include <UniversalPayload/SmbiosTable.h> #include <UniversalPayload/AcpiTable.h> +#include <Guid/BootTimeReservationGuid.h> // // PciBarFixup.c: program endpoint BARs the outer firmware left at @@ -82,6 +83,26 @@ FixupUnassignedBars ( #define PAYLOAD_STACK_PAGES 4 #define PAYLOAD_STACK_SIZE (EFI_PAGES_TO_SIZE (PAYLOAD_STACK_PAGES)) +// +// Boot-time-only reservations: the FV copy, the HOB list buffer, the +// payload's initial stack and, on AArch64, every translation-table +// page. All are allocated as EfiReservedMemoryType so they appear as +// isolated Reserved records in the outer memory-map snapshot and are +// therefore never selected as free RAM by the payload's HOB-memory +// search; but none of them needs to survive past the OS's +// ExitBootServices(), so they are handed to the payload in a +// gLoaderBootTimeReservationGuid HOB and the payload publishes each as +// SYSTEM_MEMORY pinned by an EfiBootServicesData memory-allocation +// HOB. The bound comfortably covers the FV/HOB/stack plus every +// translation-table page ArmMmuLib may allocate for the outer GCD +// map; on X64 no page tables are recorded and only three entries are +// used. +// +#define MAX_BOOT_TIME_RESERVATIONS 128 + +STATIC LOADER_BOOT_TIME_RESERVATION_ENTRY mBootTimeReservation[MAX_BOOT_TIME_RESERVATIONS]; +STATIC UINTN mBootTimeReservationCount; + // // A single HOB's length is UINT16 and must be 8-byte aligned (PI 5.2). // The largest data payload a GUID HOB can therefore carry: @@ -232,6 +253,91 @@ EmitGuidHob ( return GuidHob + 1; } +/** + Record one boot-time-only reservation for the + gLoaderBootTimeReservationGuid HOB. + + @param[in] Base Physical base of the allocation. + @param[in] Size Size in bytes (page-aligned). +**/ +STATIC +VOID +RecordBootTimeReservation ( + IN EFI_PHYSICAL_ADDRESS Base, + IN UINT64 Size + ) +{ + if (mBootTimeReservationCount >= MAX_BOOT_TIME_RESERVATIONS) { + // + // Overflow means some reservations stay Reserved forever. That + // is only a memory-parity loss, not a correctness problem, so a + // diagnostic is enough. + // + Print ( + L"ChainloadApp: boot-time reservation table full at 0x%lx\n", + Base + ); + return; + } + + mBootTimeReservation[mBootTimeReservationCount].Base = Base; + mBootTimeReservation[mBootTimeReservationCount].Size = Size; + mBootTimeReservationCount++; +} + +/** + Return whether a Reserved outer memory-map descriptor is entirely + covered by the recorded boot-time reservations. + + The four allocations are made back to back with AllocateMaxAddress + and, on AArch64, AllocateAnyPages, so the outer memory map typically + reports one coalesced EfiReservedMemoryType descriptor covering all + of them; but no assumption is made about that: any Reserved + descriptor whose whole span lies inside recorded reservations is + reclassified. A partial overlap is left as Reserved. + + @param[in] Base Descriptor start. + @param[in] End Descriptor end (exclusive). + + @retval TRUE Every page in [Base,End) is a recorded reservation. + @retval FALSE At least one page is not. +**/ +STATIC +BOOLEAN +IsBootTimeReservation ( + IN EFI_PHYSICAL_ADDRESS Base, + IN EFI_PHYSICAL_ADDRESS End + ) +{ + EFI_PHYSICAL_ADDRESS Cursor; + UINTN Index; + BOOLEAN Advanced; + + if (mBootTimeReservationCount == 0) { + return FALSE; + } + + Cursor = Base; + while (Cursor < End) { + Advanced = FALSE; + for (Index = 0; Index < mBootTimeReservationCount; Index++) { + if ((Cursor >= mBootTimeReservation[Index].Base) && + (Cursor < (mBootTimeReservation[Index].Base + mBootTimeReservation[Index].Size))) + { + Cursor = mBootTimeReservation[Index].Base + mBootTimeReservation[Index].Size; + Advanced = TRUE; + break; + } + } + + if (!Advanced) { + return FALSE; + } + } + + return TRUE; +} + /** Translate an EFI_MEMORY_TYPE from the outer firmware's memory map into the SBL memory-map HOB Type and Flag fields. @@ -593,11 +699,13 @@ IsReservedInMemoryMap ( The FV image, HOB list buffer, payload stack and (on AArch64) the translation-table pages are allocated as EfiReservedMemoryType before the caller takes its memory-map snapshot, so the snapshot - already describes all of them and EfiTypeToSblEntry() - maps them to SBL type 2 (Reserved); the payload's free-memory search - therefore cannot select them. No separate records are injected for - them, and this function asserts that the snapshot really does report - them as Reserved. + already describes all of them. This function asserts that the + snapshot really does report them as Reserved, then reclassifies + those Reserved records to SBL type 1 (RAM) and emits a + gLoaderBootTimeReservationGuid HOB naming them, so the payload + publishes them as EFI_RESOURCE_SYSTEM_MEMORY pinned by an + EfiBootServicesData allocation HOB and the OS reclaims them after + ExitBootServices(). @param[in] HobList Pre-allocated HOB list buffer (Reserved memory). @param[in] HobBufSize Size of HobList in bytes. @@ -642,6 +750,7 @@ BuildPayloadHobList ( UNIVERSAL_PAYLOAD_EXTRA_DATA *ExtraData; UNIVERSAL_PAYLOAD_SMBIOS_TABLE *SmbiosHob; UNIVERSAL_PAYLOAD_ACPI_TABLE *AcpiHob; + LOADER_BOOT_TIME_RESERVATION *BootTimeRes; SERIAL_PORT_INFO *SblSerial; UNIVERSAL_PAYLOAD_SERIAL_PORT_INFO *UplSerial; UNIVERSAL_PAYLOAD_SERIAL_PORT_INFO Serial; @@ -743,15 +852,18 @@ BuildPayloadHobList ( // SBL memory-map GUID HOB. The FV image, HOB list buffer, payload // stack and (on AArch64) the translation-table pages were allocated // as EfiReservedMemoryType before the caller took its snapshot, so - // the snapshot already reports all of them and - // EfiTypeToSblEntry() maps them to SBL type 2 (Reserved). Emitting - // separate records for them would produce exact duplicates: - // MemInfoCallbackMmio() in the payload calls - // BuildResourceDescriptorHob() once per record with no dedup and no - // overlap check, so on X64 the second copy is silently rejected by - // CoreInternalAddMemorySpace() and on AArch64 ConfigureMmuFromHobs() - // builds overlapping region descriptors from it. Assert the - // expectation rather than adding a second copy. + // the snapshot reports them as one or more Reserved descriptors. + // Allocating them Reserved keeps them from coalescing with adjacent + // conventional memory in the outer memory map, so each Reserved + // descriptor covering only launcher allocations stays smaller than + // PcdSystemMemoryUefiRegionSize and FindFreeMemForHobCallback() + // never selects it; the payload additionally excludes ranges the + // gLoaderBootTimeReservationGuid HOB names. Below, any Reserved + // descriptor entirely covered by recorded boot-time reservations is + // reclassified to SBL type 1 (RAM), so the payload publishes it as + // EFI_RESOURCE_SYSTEM_MEMORY, pins it with an EfiBootServicesData + // memory-allocation HOB, and the OS reclaims it after + // ExitBootServices(). // // The GCD MMIO regions are recorded as MMIO, including the ECAM // window: the payload maps the resulting MEMORY_RESERVED resource as @@ -847,6 +959,24 @@ BuildPayloadHobList ( &MemMapInfo->Entry[EntryIndex].Type, &MemMapInfo->Entry[EntryIndex].Flag ); + + // + // A Reserved descriptor entirely covered by our own boot-time + // reservations is DRAM the OS may reclaim after ExitBootServices(): + // report it as SBL type 1 so the payload publishes it as + // SYSTEM_MEMORY. The gLoaderBootTimeReservationGuid HOB below + // tells the payload to keep it out of its HOB-memory search and to + // pin it with an EfiBootServicesData memory-allocation HOB. + // + if ((Entry->Type == EfiReservedMemoryType) && + IsBootTimeReservation ( + Entry->PhysicalStart, + Entry->PhysicalStart + EFI_PAGES_TO_SIZE (Entry->NumberOfPages) + )) + { + MemMapInfo->Entry[EntryIndex].Type = 1; + } + EntryIndex++; } @@ -880,6 +1010,36 @@ BuildPayloadHobList ( MemMapInfo->Count = (UINT32)EntryIndex; + // + // Boot-time reservation GUID HOB: the payload excludes each range + // from FindFreeMemForHobCallback() and pins each with an + // EfiBootServicesData memory-allocation HOB. A payload that does + // not know the GUID sees the ranges as ordinary SBL type 1 RAM + // records, each far smaller than PcdSystemMemoryUefiRegionSize and + // therefore already unpickable by the HOB-memory search. + // + if (mBootTimeReservationCount > 0) { + BootTimeRes = EmitGuidHob ( + HobList, + &HobOffset, + HobLimit, + &gLoaderBootTimeReservationGuid, + sizeof (LOADER_BOOT_TIME_RESERVATION) + + mBootTimeReservationCount * sizeof (LOADER_BOOT_TIME_RESERVATION_ENTRY) + ); + if (BootTimeRes == NULL) { + return EFI_BUFFER_TOO_SMALL; + } + + BootTimeRes->Revision = 1; + BootTimeRes->Count = (UINT32)mBootTimeReservationCount; + CopyMem ( + BootTimeRes->Entry, + mBootTimeReservation, + mBootTimeReservationCount * sizeof (LOADER_BOOT_TIME_RESERVATION_ENTRY) + ); + } + if (Serial.RegisterBase != 0) { // // SERIAL_PORT_INFO.BaseAddr is UINT32, but SPCR can legally place an @@ -1242,8 +1402,8 @@ ChainloadEntry ( // used. The ExtraData HOB records the actual FV base, so nothing // downstream still assumes the PCD value. The HOB list and payload // stack are also placed below 4 GiB. All three are typed as - // EfiReservedMemoryType so that they survive as SBL type 2 (Reserved) - // in the memory-map HOB. + // EfiReservedMemoryType; IsBootTimeReservation() later reports them + // as SBL type 1 (RAM) with a matching allocation HOB to pin them. // FvAddress = PcdGet32 (PcdPayloadFdMemBase); Status = gBS->AllocatePages (AllocateAddress, EfiReservedMemoryType, FvPages, &FvAddress); @@ -1266,6 +1426,7 @@ ChainloadEntry ( CopyMem ((VOID *)(UINTN)FvAddress, EmbeddedFv, FvSize); Print (L"ChainloadApp: FV 0x%lx bytes copied to 0x%lx\n", (UINT64)FvSize, FvAddress); + RecordBootTimeReservation (FvAddress, EFI_PAGES_TO_SIZE (FvPages)); HobAddress = MAX_UINT32; Status = gBS->AllocatePages (AllocateMaxAddress, EfiReservedMemoryType, HOB_LIST_PAGES, &HobAddress); @@ -1277,6 +1438,7 @@ ChainloadEntry ( HobList = (VOID *)(UINTN)HobAddress; ZeroMem (HobList, HOB_LIST_SIZE); + RecordBootTimeReservation (HobAddress, HOB_LIST_SIZE); StackAddress = MAX_UINT32; Status = gBS->AllocatePages (AllocateMaxAddress, EfiReservedMemoryType, PAYLOAD_STACK_PAGES, &StackAddress); @@ -1286,6 +1448,8 @@ ChainloadEntry ( goto FreeReserved; } + RecordBootTimeReservation (StackAddress, PAYLOAD_STACK_SIZE); + // // Fetch the GCD memory space map. On AArch64 it drives the // ARM_MEMORY_REGION_DESCRIPTOR list handed to ArmConfigureMmu(); on @@ -1359,7 +1523,13 @@ ChainloadEntry ( goto FreeReserved; } - Print (L"ChainloadApp: HOB list at 0x%lx, stack at 0x%lx\n", HobAddress, StackAddress); + Print ( + L"ChainloadApp: HOB list at 0x%lx, stack at 0x%lx, %lu boot-time reservation%s\n", + HobAddress, + StackAddress, + (UINT64)mBootTimeReservationCount, + (mBootTimeReservationCount == 1) ? L"" : L"s" + ); // // Locate the SEC-core PE/COFF image inside the copied FV, then use diff --git a/UefiPayloadPkg/ChainloadApp/ChainloadApp.inf b/UefiPayloadPkg/ChainloadApp/ChainloadApp.inf index 1d75b7cbda..7eb5f31a06 100644 --- a/UefiPayloadPkg/ChainloadApp/ChainloadApp.inf +++ b/UefiPayloadPkg/ChainloadApp/ChainloadApp.inf @@ -63,6 +63,7 @@ gEfiSmbios3TableGuid gUniversalPayloadExtraDataGuid gLoaderMemoryMapInfoGuid + gLoaderBootTimeReservationGuid gUefiSerialPortInfoGuid gUniversalPayloadSerialPortInfoGuid gUniversalPayloadSmbiosTableGuid diff --git a/UefiPayloadPkg/Include/Guid/BootTimeReservationGuid.h b/UefiPayloadPkg/Include/Guid/BootTimeReservationGuid.h new file mode 100644 index 0000000000..d20e177b04 --- /dev/null +++ b/UefiPayloadPkg/Include/Guid/BootTimeReservationGuid.h @@ -0,0 +1,53 @@ +/** @file + Boot-time reservation GUID HOB. + + A UEFI-hosted launcher (ChainloadApp) allocates the payload FV, its + own HOB list, the payload's initial stack and, on AArch64, a full + translation-table hierarchy from the outer firmware as + EfiReservedMemoryType so that they appear as isolated Reserved + descriptors in the memory-map snapshot and cannot be selected as + free RAM by the payload's HOB-memory search. None of them, however, + needs to survive past the OS's ExitBootServices() call: DxeCore + loads every driver out of the FV into its own pages, the launcher's + HOB list is dead once UefiPayloadEntry has rebuilt the HOB list, the + launcher's stack is dead once HandOffToDxeCore() has switched to its + own, and firmware translation tables are the same EfiBootServicesData + in a normal ArmMmuLib-based boot. + + This HOB tells the payload which of the Reserved records in the SBL + memory-map HOB are the launcher's own boot-time-only allocations. + UefiPayloadEntry excludes them from FindFreeMemForHobCallback(), + publishes each as EFI_RESOURCE_SYSTEM_MEMORY, and pins each with an + EfiBootServicesData memory-allocation HOB so that the payload's DXE + never allocates over them and the OS reclaims them after + ExitBootServices(). + + A launcher that does not emit this HOB (Slim Bootloader, coreboot) + gets the pre-existing behaviour unchanged: the payload publishes the + Reserved records as EFI_RESOURCE_MEMORY_RESERVED and the OS never + touches them. + + Copyright (c) 2026, Amazon.com, Inc. or its affiliates. All Rights Reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef LOADER_BOOT_TIME_RESERVATION_GUID_H_ +#define LOADER_BOOT_TIME_RESERVATION_GUID_H_ + +extern EFI_GUID gLoaderBootTimeReservationGuid; + +#pragma pack(1) +typedef struct { + EFI_PHYSICAL_ADDRESS Base; + UINT64 Size; +} LOADER_BOOT_TIME_RESERVATION_ENTRY; + +typedef struct { + UINT8 Revision; + UINT8 Reserved0[3]; + UINT32 Count; + LOADER_BOOT_TIME_RESERVATION_ENTRY Entry[0]; +} LOADER_BOOT_TIME_RESERVATION; +#pragma pack() + +#endif diff --git a/UefiPayloadPkg/UefiPayloadEntry/UefiPayloadEntry.c b/UefiPayloadPkg/UefiPayloadEntry/UefiPayloadEntry.c index 00b016003b..fe66243280 100644 --- a/UefiPayloadPkg/UefiPayloadEntry/UefiPayloadEntry.c +++ b/UefiPayloadPkg/UefiPayloadEntry/UefiPayloadEntry.c @@ -8,12 +8,24 @@ **/ #include <Guid/MemoryTypeInformation.h> +#include <Guid/BootTimeReservationGuid.h> #include <Library/BaseArchLibSupport.h> #include "UefiPayloadEntry.h" STATIC UINT32 mTopOfLowerUsableDram = 0; STATIC BOOLEAN mMcfgResourceHobBuilt = FALSE; +// +// Boot-time reservations from the launcher's +// gLoaderBootTimeReservationGuid HOB, if it emitted one. +// FindFreeMemForHobCallback() excludes ranges overlapping any of +// these; BuildGenericHob() pins each with an EfiBootServicesData +// memory-allocation HOB. A launcher that does not emit the HOB +// (Slim Bootloader, coreboot) leaves both NULL/0. +// +STATIC LOADER_BOOT_TIME_RESERVATION_ENTRY *mBootTimeReservation; +STATIC UINTN mBootTimeReservationCount; + EFI_MEMORY_TYPE_INFORMATION mDefaultMemoryTypeInformation[] = { { EfiACPIReclaimMemory, FixedPcdGet32 (PcdMemoryTypeEfiACPIReclaimMemory) }, { EfiACPIMemoryNVS, FixedPcdGet32 (PcdMemoryTypeEfiACPIMemoryNVS) }, @@ -221,6 +233,36 @@ FindToludCallback ( return EFI_SUCCESS; } +/** + Return whether a range overlaps any of the launcher's boot-time + reservations. + + @param[in] Base Range start. + @param[in] End Range end (exclusive). + + @retval TRUE The range overlaps a boot-time reservation. + @retval FALSE It does not, or no launcher published any. +**/ +STATIC +BOOLEAN +OverlapsBootTimeReservation ( + IN UINT64 Base, + IN UINT64 End + ) +{ + UINTN Index; + + for (Index = 0; Index < mBootTimeReservationCount; Index++) { + if ((Base < (mBootTimeReservation[Index].Base + mBootTimeReservation[Index].Size)) && + (End > mBootTimeReservation[Index].Base)) + { + return TRUE; + } + } + + return FALSE; +} + /** Callback function to find free and usable DRAM for HOB The memory region returned will have at least PcdSystemMemoryUefiRegionSize bytes @@ -300,6 +342,24 @@ FindFreeMemForHobCallback ( return EFI_SUCCESS; } + // + // A ChainloadApp launcher reports its own FV/HOB/stack/page-table + // allocations as SBL type 1 records so they become SYSTEM_MEMORY + // rather than MEMORY_RESERVED. They are still live: HOB memory + // must not land on any of them. What actually protects them is + // the BuildMemoryAllocationHob() pin in BuildGenericHob() below; + // this check is belt-and-braces so an SBL type-1 entry that + // overlapped one is skipped outright rather than split. The + // launcher emits one SBL entry per outer memory-map descriptor + // and never merges Reserved with Conventional, so on today's + // launcher this check does not fire. A launcher that emits no + // gLoaderBootTimeReservationGuid HOB (Slim Bootloader, coreboot) + // never enters this branch. + // + if (OverlapsBootTimeReservation (Entry.Base, Entry.Base + Entry.Size)) { + return EFI_SUCCESS; + } + // // Overlaps UefiPayload, split into smaller chunks // @@ -633,6 +693,59 @@ FindBootloaderExtraDataHob ( return ExtraData; } +/** + Locate the launcher's boot-time reservation HOB, if it published one. + + @param[in] BootloaderParameter Bootloader-provided argument. + @param[out] Count Entry count, clamped to what the + HOB's own data size can hold. + + @return First reservation entry, or NULL if the launcher did not + hand over a PEI HOB list carrying the GUID HOB. +**/ +STATIC +LOADER_BOOT_TIME_RESERVATION_ENTRY * +FindBootloaderBootTimeReservationHob ( + IN UINTN BootloaderParameter, + OUT UINTN *Count + ) +{ + EFI_PEI_HOB_POINTERS BlHob; + LOADER_BOOT_TIME_RESERVATION *BootTimeRes; + UINTN DataSize; + + *Count = 0; + + BlHob.Raw = (UINT8 *)BootloaderParameter; + if ((BlHob.Raw == NULL) || + (BlHob.Header->HobType != EFI_HOB_TYPE_HANDOFF) || + (BlHob.Header->HobLength != sizeof (EFI_HOB_HANDOFF_INFO_TABLE))) + { + return NULL; + } + + BlHob.Raw = GetNextGuidHob (&gLoaderBootTimeReservationGuid, BlHob.Raw); + if (BlHob.Raw == NULL) { + return NULL; + } + + BootTimeRes = (LOADER_BOOT_TIME_RESERVATION *)GET_GUID_HOB_DATA (BlHob.Raw); + DataSize = GET_GUID_HOB_DATA_SIZE (BlHob.Raw); + if ((DataSize < sizeof (LOADER_BOOT_TIME_RESERVATION)) || + (BootTimeRes->Revision != 1)) + { + return NULL; + } + + *Count = MIN ( + (UINTN)BootTimeRes->Count, + (DataSize - sizeof (LOADER_BOOT_TIME_RESERVATION)) / + sizeof (LOADER_BOOT_TIME_RESERVATION_ENTRY) + ); + + return BootTimeRes->Entry; +} + /** Locate the payload FV base and size from the bootloader's ExtraData HOB, if it published one. @@ -699,11 +812,38 @@ BuildGenericHob ( ) { UINT8 PhysicalAddressBits; + UINTN Index; EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute; // The UEFI payload FV BuildMemoryAllocationHob (PayloadFvBase, PayloadFvSize, EfiBootServicesData); + // + // Pin every launcher boot-time reservation with an + // EfiBootServicesData memory-allocation HOB so that DXE never + // allocates over the launcher's HOB list, initial stack or (on + // AArch64) live translation tables. The FV was pinned just above, + // so the entry naming it is skipped. A launcher that emitted no + // gLoaderBootTimeReservationGuid HOB leaves the count at 0. + // + for (Index = 0; Index < mBootTimeReservationCount; Index++) { + if (mBootTimeReservation[Index].Base == PayloadFvBase) { + continue; + } + + BuildMemoryAllocationHob ( + mBootTimeReservation[Index].Base, + mBootTimeReservation[Index].Size, + EfiBootServicesData + ); + DEBUG (( + DEBUG_INFO, + "boot-time reservation: base = 0x%lx, size = 0x%lx\n", + mBootTimeReservation[Index].Base, + mBootTimeReservation[Index].Size + )); + } + PhysicalAddressBits = ArchGetPhysicalAddressBits (); BuildCpuHob (PhysicalAddressBits, 16); @@ -765,6 +905,17 @@ _ModuleEntryPoint ( PayloadFvSize = PcdGet32 (PcdPayloadFdMemSize); } + // + // If the launcher published boot-time reservations, cache them so + // FindFreeMemForHobCallback() below excludes those ranges from the + // HOB-memory search. A launcher that did not (Slim Bootloader, + // coreboot) leaves the count at 0 and nothing changes. + // + mBootTimeReservation = FindBootloaderBootTimeReservationHob ( + BootloaderParameter, + &mBootTimeReservationCount + ); + // HOB region is used for HOB and memory allocation for this module MemBase = PayloadFvBase; HobMemBase = 0; diff --git a/UefiPayloadPkg/UefiPayloadEntry/UefiPayloadEntry.inf b/UefiPayloadPkg/UefiPayloadEntry/UefiPayloadEntry.inf index 91aa8e3f5d..7e345a9349 100644 --- a/UefiPayloadPkg/UefiPayloadEntry/UefiPayloadEntry.inf +++ b/UefiPayloadPkg/UefiPayloadEntry/UefiPayloadEntry.inf @@ -72,6 +72,7 @@ gUniversalPayloadAcpiTableGuid gUniversalPayloadSerialPortInfoGuid gUniversalPayloadExtraDataGuid + gLoaderBootTimeReservationGuid gEfiFirmwareInfoHobGuid gEfiSmmStoreInfoHobGuid diff --git a/UefiPayloadPkg/UefiPayloadPkg.dec b/UefiPayloadPkg/UefiPayloadPkg.dec index 3c6352e0c6..3a445b0f87 100644 --- a/UefiPayloadPkg/UefiPayloadPkg.dec +++ b/UefiPayloadPkg/UefiPayloadPkg.dec @@ -40,6 +40,7 @@ gUefiAcpiBoardInfoGuid = {0xad3d31b, 0xb3d8, 0x4506, {0xae, 0x71, 0x2e, 0xf1, 0x10, 0x6, 0xd9, 0xf}} gUefiSerialPortInfoGuid = { 0x6c6872fe, 0x56a9, 0x4403, { 0xbb, 0x98, 0x95, 0x8d, 0x62, 0xde, 0x87, 0xf1 } } gLoaderMemoryMapInfoGuid = { 0xa1ff7424, 0x7a1a, 0x478e, { 0xa9, 0xe4, 0x92, 0xf3, 0x57, 0xd1, 0x28, 0x32 } } + gLoaderBootTimeReservationGuid = { 0x058e371c, 0x3486, 0x46b1, { 0xac, 0xf6, 0x2a, 0x3f, 0x26, 0x9c, 0xe2, 0x69 } } gEdkiiPayloadCommandLineGuid = {0xb5aeb34f, 0x3047, 0x4955, {0xb8, 0x80, 0xad, 0xd3, 0x6d, 0x86, 0xdc, 0x0f}} # SMM variable support diff --git a/UefiPayloadPkg/UefiPayloadPkg.dsc b/UefiPayloadPkg/UefiPayloadPkg.dsc index b6306ac7c3..ec1bee7ad2 100644 --- a/UefiPayloadPkg/UefiPayloadPkg.dsc +++ b/UefiPayloadPkg/UefiPayloadPkg.dsc @@ -1406,8 +1406,11 @@ # # ArmMmuLib allocates translation-table pages via # MemoryAllocationLib::AllocatePages(). Redirect those to - # EfiReservedMemoryType so the payload's memory-map HOB reports - # them as Reserved and DXE cannot allocate over the live tables. + # EfiReservedMemoryType so they stay isolated in the outer + # memory-map snapshot and cannot be selected as HOB memory, + # and record each one so ChainloadApp can hand the payload an + # explicit boot-time reservation list to publish as + # EfiBootServicesData. # MemoryAllocationLib|UefiPayloadPkg/ChainloadApp/ReservedUefiMemoryAllocationLib.inf } -- 2.47.3 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#122101): https://edk2.groups.io/g/devel/message/122101 Mute This Topic: https://groups.io/mt/120797296/21656 Group Owner: [email protected] Unsubscribe: https://edk2.groups.io/g/devel/unsub [[email protected]] -=-=-=-=-=-=-=-=-=-=-=-
