PEI Shadow Process Overview

Understanding the PEI Core migration from Flash to RAM

SEC Phase
PEI (Pre-Memory)
Shadow Transition
PEI (Post-Memory)
DXE Phase
F

What is PEI Shadow?

C

Why Shadow?

D

When Does Shadow Happen?

Memory Architecture

The three memory regions involved in the PEI phase

Flash ROM

Cache as RAM (CAR)

Permanent Memory (DRAM)

Shadow Flow

Step-by-step walkthrough of the PEI Shadow process

Step 1 / 9

Pointer Fixup Details

Understanding post-shadow address translation

NewPtr = OldPtr + (DRAM_Base - Flash_Base)

HOB List Pointer Fixup

PPI Database Pointer Fixup

Stack Pointer Fixup

Key Code Analysis

Core PEI Shadow implementation in EDK2 source

PeiInstallPeiMemory()

// MdeModulePkg/Core/Pei/Memory/MemoryServices.c
EFI_STATUS
EFIAPI
PeiInstallPeiMemory (
  IN CONST EFI_PEI_SERVICES     **PeiServices,
  IN EFI_PHYSICAL_ADDRESS       MemoryBegin,
  IN UINT64                     MemoryLength
  )
{
  // Record permanent memory range
  PrivateData->PhysicalMemoryBegin  = MemoryBegin;
  PrivateData->PhysicalMemoryLength = MemoryLength;
  PrivateData->FreePhysicalMemoryTop =
      MemoryBegin + MemoryLength;

  // Install the Memory Discovered PPI
  // -> triggers shadow process
  Status = PeiInstallPpi (
             PeiServices,
             &mMemoryDiscoveredPpi
             );
  return Status;
}

PeiCore Shadow Logic

// MdeModulePkg/Core/Pei/PeiMain/PeiMain.c
VOID
EFIAPI
PeiCore (
  IN CONST EFI_SEC_PEI_HAND_OFF  *SecCoreData,
  IN CONST EFI_PEI_PPI_DESCRIPTOR *PpiList,
  IN VOID                         *Data
  )
{
  // ... initialization ...

  if (PrivateData.PeiMemoryInstalled &&
      !PrivateData.PeimDispatcherReenter) {
    //
    // Shadow PEI Core into permanent memory
    // so it runs faster than XIP from flash
    //
    PrivateData.ShadowedPeiCore = ShadowPeiCore (
                                    &PrivateData
                                    );
    //
    // Switch stack from CAR to permanent memory
    // and re-enter PEI Core from DRAM
    //
    SwitchStack (
      (SWITCH_STACK_ENTRY_POINT)(UINTN)
          PrivateData.ShadowedPeiCore,
      (VOID *)SecCoreData,
      NULL,
      (VOID *)(UINTN)NewStackTop
      );
    // -- never returns here --
  }
}

Stack Migration

// Conceptual: migrate stack from CAR to DRAM
//
// 1. Allocate new stack in permanent memory
NewStack = AllocatePages (EFI_SIZE_TO_PAGES (StackSize));

// 2. Copy CAR stack contents to new location
CopyMem (
  (VOID *)NewStack,
  (VOID *)OldStackBase,
  StackSize
  );

// 3. Calculate the fixup delta
Delta = (INTN)NewStack - (INTN)OldStackBase;

// 4. Adjust saved frame pointers & return addresses
//    that point within the old stack range
for (Ptr = NewStack; Ptr < NewStack + StackSize; Ptr++) {
  if (*Ptr >= OldStackBase && *Ptr < OldStackTop) {
    *Ptr += Delta;
  }
}

// 5. Switch ESP / RSP to new stack
SwitchStack (EntryPoint, Arg, NULL, NewStackTop);

References

Resources for deeper learning about PEI Shadow

PI Specification

EDK2 Source

PEI Dispatcher

PEI Dispatcher source

MdeModulePkg