EfiTypeToSblEntry() maps the outer firmware's memory descriptors into
the SBL memory-map HOB. Its default case sends EfiRuntimeServicesCode
and EfiRuntimeServicesData to Reserved, so the OS never sees that
memory even though nothing on the payload side can use it: ChainloadApp
calls ExitBootServices() on the outer firmware, discards its System
Table, installs its own translation tables and jumps to a payload whose
DxeCore publishes a fresh gRT and its own SetVirtualAddressMap(). No
runtime service in either range is ever called after the branch.
Report the outer runtime ranges as usable RAM. On the AArch64 -m 2G
reference platform that returns 4,224 KiB (256 + 1600 + 2368) to the
OS, and it is a gain over the direct-boot baseline as well since the
outer runtime was non-usable there too.
Two data pointers do survive into the payload's HOB list:
1) The ACPI RSDP. edk2's AcpiTableDxe puts the RSDP, XSDT and every
table in EfiACPIReclaimMemory or EfiACPIMemoryNVS
(AcpiTableProtocol.c allocates only those two types), and the
payload's InstallAcpiTableFromHob() copies each table into fresh
reclaim before publishing, so RSDP requires no additional
protection.
2) The SMBIOS entry point. edk2's SmbiosDxe allocates the entry-point
structure and the structure table it points to as
EfiRuntimeServicesData (SmbiosCreateTable() and
SmbiosCreate64BitTable()), so on any edk2-based outer firmware,
OVMF and ArmVirtQemu among them, the SMBIOS handoff points into a
range this change reclassifies to RAM.
Pin those pages in the same change that reclassifies them, so that no
tree state exists in which the SMBIOS handoff sits in reclaimed RAM
unprotected. RecordSmbiosBootTimeReservation() adds the entry point and
the structure table to the launcher's boot-time reservation list, so
the payload publishes each as SYSTEM_MEMORY with an EfiBootServicesData
memory-allocation HOB and its DXE cannot allocate over them. The
payload's own SmbiosDxe copies every record into fresh
EfiRuntimeServicesData during dispatch, after which the outer copy is
dead and the pin is released at the OS's ExitBootServices().
Both anchor variants are handled: SMBIOS 3.0 (_SM3_) carries a 64-bit
TableAddress and 32-bit TableMaximumSize, SMBIOS 2.x (_SM_) a 32-bit
TableAddress and 16-bit TableLength. The entry point and the structure
table may be separate allocations, so both are pinned, and the extents
are read from the entry point and not guessed. Whole pages are pinned
because the payload's memory-allocation HOB and its HOB-memory
search work at page granularity; the two allocations are page-sized and
sit inside a much larger EfiRuntimeServicesData descriptor, which is
why the reservation list is a per-range list and not a flag byte on a
memory-map descriptor. An anchor beginning with neither signature is
left alone: some non-edk2 outer firmware places the SMBIOS anchor
outside RT_Data (F-segment shadow, ACPI NVS), and pinning irrelevant
memory would only cost parity.
A non-edk2 outer firmware whose RSDP or XSDT lands in RT_Data would
need the same treatment; that has not been observed and is not handled
here.
The reclassification is unconditional because ChainloadApp is by
construction a full-replacement chainloader; it has no mode in which
the outer runtime services survive the branch, so an opt-in knob would
gate a code path that cannot be exercised.
Combined with "UefiPayloadPkg/ChainloadApp: Publish launcher
allocations as boot-time", the AArch64 -m 2G reference platform
reports:
Usable-to-OS 2,068,068 -> 2,081,820 KiB (+13,752 KiB)
which is 100 KiB below the direct-boot reference of 2,081,920 KiB.
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 | 128 +++++++++++++++++++++
1 file changed, 128 insertions(+)
diff --git a/UefiPayloadPkg/ChainloadApp/ChainloadApp.c
b/UefiPayloadPkg/ChainloadApp/ChainloadApp.c
index 029d56e4aa..6373ca531e 100644
--- a/UefiPayloadPkg/ChainloadApp/ChainloadApp.c
+++ b/UefiPayloadPkg/ChainloadApp/ChainloadApp.c
@@ -33,6 +33,7 @@
#include <UniversalPayload/UniversalPayload.h>
#include <UniversalPayload/SerialPortInfo.h>
#include <IndustryStandard/Acpi.h>
+#include <IndustryStandard/SmBios.h>
#include <IndustryStandard/SerialPortConsoleRedirectionTable.h>
#include <IndustryStandard/MemoryMappedConfigurationSpaceAccessTable.h>
#include <IndustryStandard/Pci22.h>
@@ -285,6 +286,106 @@ RecordBootTimeReservation (
mBootTimeReservationCount++;
}
+/**
+ Pin the outer firmware's SMBIOS entry point and structure table as
+ boot-time reservations.
+
+ On an edk2-based outer firmware, SmbiosDxe allocates both the
+ entry-point structure and the structure table it points to as
+ EfiRuntimeServicesData (SmbiosCreateTable() /
+ SmbiosCreate64BitTable() in
+ MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c), and this change
+ reclassifies EfiRuntimeServicesData to usable RAM. The
+ payload's SmbiosDxe copies every record out during DXE dispatch,
+ but until it does the outer entry point and structure table must
+ not be allocated over. Pinning them here makes the payload publish
+ each as SYSTEM_MEMORY with an EfiBootServicesData memory-allocation
+ HOB, so DxeCore keeps off them and the OS reclaims them at
+ ExitBootServices().
+
+ Both anchor variants are handled: SMBIOS 3.0 (_SM3_) carries a
+ 64-bit TableAddress and 32-bit TableMaximumSize; SMBIOS 2.x (_SM_)
+ carries a 32-bit TableAddress and 16-bit TableLength. The entry
+ point and the structure table may be separate allocations, so both
+ are pinned; the extents are read from the entry point rather than
+ guessed. An anchor that does not begin with either signature is
+ left alone: some non-edk2 outer firmwares place the SMBIOS anchor
+ outside RT_Data (F-segment shadow, ACPI NVS), and pinning
+ irrelevant memory would only cost parity.
+
+ @param[in] SmbiosTable Physical address of the outer firmware's
+ SMBIOS entry-point structure, or 0.
+**/
+STATIC
+VOID
+RecordSmbiosBootTimeReservation (
+ IN EFI_PHYSICAL_ADDRESS SmbiosTable
+ )
+{
+ SMBIOS_TABLE_ENTRY_POINT *Ep2;
+ SMBIOS_TABLE_3_0_ENTRY_POINT *Ep3;
+ EFI_PHYSICAL_ADDRESS EpBase;
+ EFI_PHYSICAL_ADDRESS TableBase;
+ UINTN EpLen;
+ UINT64 TableLen;
+
+ if (SmbiosTable == 0) {
+ return;
+ }
+
+ Ep3 = (SMBIOS_TABLE_3_0_ENTRY_POINT *)(UINTN)SmbiosTable;
+ Ep2 = (SMBIOS_TABLE_ENTRY_POINT *)(UINTN)SmbiosTable;
+
+ if (CompareMem (
+ Ep3->AnchorString,
+ SMBIOS_3_0_ANCHOR_STRING,
+ SMBIOS_3_0_ANCHOR_STRING_LENGTH
+ ) == 0)
+ {
+ EpLen = Ep3->EntryPointLength;
+ TableBase = Ep3->TableAddress;
+ TableLen = Ep3->TableMaximumSize;
+ } else if (CompareMem (
+ Ep2->AnchorString,
+ SMBIOS_ANCHOR_STRING,
+ SMBIOS_ANCHOR_STRING_LENGTH
+ ) == 0)
+ {
+ EpLen = Ep2->EntryPointLength;
+ TableBase = Ep2->TableAddress;
+ TableLen = Ep2->TableLength;
+ } else {
+ return;
+ }
+
+ //
+ // Pin whole pages: the payload's memory-allocation HOB and
+ // FindFreeMemForHobCallback() operate at page granularity.
+ //
+ EpBase = SmbiosTable & ~(EFI_PHYSICAL_ADDRESS)EFI_PAGE_MASK;
+ RecordBootTimeReservation (
+ EpBase,
+ EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES ((SmbiosTable - EpBase) + EpLen))
+ );
+
+ if ((TableBase != 0) && (TableLen != 0)) {
+ EpBase = TableBase & ~(EFI_PHYSICAL_ADDRESS)EFI_PAGE_MASK;
+ RecordBootTimeReservation (
+ EpBase,
+ EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES ((TableBase - EpBase) + TableLen))
+ );
+ }
+
+ Print (
+ L"ChainloadApp: SMBIOS entry point 0x%lx (%lu bytes), "
+ L"structure table 0x%lx (%lu bytes) pinned as boot-time\n",
+ SmbiosTable,
+ (UINT64)EpLen,
+ TableBase,
+ TableLen
+ );
+}
+
/**
Return whether a Reserved outer memory-map descriptor is entirely
covered by the recorded boot-time reservations.
@@ -369,6 +470,23 @@ EfiTypeToSblEntry (
case EfiBootServicesData:
case EfiLoaderCode:
case EfiLoaderData:
+ //
+ // The outer firmware's runtime services do not survive the
+ // chainload: ExitBootServices() has already returned, its System
+ // Table is discarded, and the payload's DxeCore installs a fresh
+ // gRT with its own SetVirtualAddressMap(). Nothing on the far
+ // side of JumpToPayload() can call into these ranges. The RSDP
+ // handoff points into EfiACPIReclaimMemory / EfiACPIMemoryNVS on
+ // an edk2 outer firmware, which are preserved. The SMBIOS
+ // handoff, however, points into EfiRuntimeServicesData on an
+ // edk2 outer firmware; those pages are pinned via
+ // gLoaderBootTimeReservationGuid so the payload's DXE cannot
+ // allocate over them until its own SmbiosDxe has copied out.
+ // Reserving the whole range would make the OS pay for two
+ // firmwares' runtimes when only the payload's is live.
+ //
+ case EfiRuntimeServicesCode:
+ case EfiRuntimeServicesData:
*Type = 1;
break;
case EfiACPIReclaimMemory:
@@ -1383,6 +1501,16 @@ ChainloadEntry (
Print (L"ChainloadApp: ACPI RSDP 0x%lx SMBIOS 0x%lx\n", AcpiRsdp,
SmbiosTable);
+ //
+ // On an edk2 outer firmware the SMBIOS entry point and structure
+ // table live in EfiRuntimeServicesData, which this application
+ // reports as usable RAM. Pin both so the payload's DXE
+ // cannot allocate over them until its own SmbiosDxe has copied out.
+ // The RSDP is not pinned: edk2's AcpiTableDxe places it in
+ // EfiACPIReclaimMemory / EfiACPIMemoryNVS, which are preserved.
+ //
+ RecordSmbiosBootTimeReservation (SmbiosTable);
+
//
// Program endpoint BARs the outer firmware left at zero, through
// ECAM located from the ACPI MCFG, while boot services are still
--
2.47.3
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#122102): https://edk2.groups.io/g/devel/message/122102
Mute This Topic: https://groups.io/mt/120797305/21656
Group Owner: [email protected]
Unsubscribe: https://edk2.groups.io/g/devel/unsub [[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-