PEI阶段的“搬家“——UEFI启动流程中你的代码是怎么从临时内存搬到真内存的

发布时间:2026/11/10 14:23:05

PEI阶段的“搬家“——UEFI启动流程中你的代码是怎么从临时内存搬到真内存的 本文基于EDK2源码MdeModulePkg/Core/Pei逐行拆解不是翻译spec。先说结论UEFI启动流程中PEI阶段的核心矛盾只有一句话早期代码在临时RAM里跑但临时RAM只有几十KB迟早要搬到真正的DRAM里去。这个搬迁过程——从临时内存到永久内存的迁移——是PEI Core最精巧也最容易出bug的地方。❌ 搬错了 → 指针全乱 → 机器直接死✅ 搬对了 → 后续所有模块才能在真正的内存里舒展手脚一、为什么需要临时内存按下电源键之后CPU从SPI Flash里读出第一段代码SEC阶段然后跳进PEI Core。但这时候DRAM还没初始化——内存控制器还没配置DDR还没训练你连一根内存条都不能用。那代码在哪跑CPU内部有一小块SRAM叫Temporary RAM临时RAM。在Intel平台上这通常是CPU Cache-as-RAMCAR机制——把L2/L3 Cache当RAM用。属性数值临时RAM大小一般只有64KB~128KBStack区域给PEI Core和早期PEIM跑代码用Heap区域放HOB列表和早期内存分配SEC把这些信息打包成EFI_SEC_PEI_HAND_OFF结构体交给PEI Coretypedefstruct_EFI_SEC_PEI_HAND_OFF{UINT16 DataSize;// 结构体大小VOID*BootFirmwareVolumeBase;// BFV起始地址SPI Flash映射区UINTN BootFirmwareVolumeSize;// BFV大小VOID*TemporaryRamBase;// 临时RAM起始地址UINTN TemporaryRamSize;// 临时RAM总大小VOID*PeiTemporaryRamBase;// PEI Heap起始地址UINTN PeiTemporaryRamSize;// PEI Heap大小VOID*StackBase;// Stack起始地址UINTN StackSize;// Stack大小}EFI_SEC_PEI_HAND_OFF;类比临时RAM就像酒店临时寄存柜——你刚到酒店行李先塞进寄存柜房间还没准备好。柜子很小64KB但够你先洗个脸换身衣服。二、PEI Core第一次进场——只做最轻的事看PeiMain.c里的PeiCore()函数入口当OldCoreData NULL时第一次进PEI Core代码非常克制if(OldCoreDataNULL){// 第一次进PEI Core临时RAM还没搬家ZeroMem(PrivateData,sizeof(PEI_CORE_INSTANCE));PrivateData.SignaturePEI_CORE_HANDLE_SIGNATURE;CopyMem(PrivateData.ServiceTableShadow,gPs,sizeof(gPs));}它做了什么步骤操作含义①ZeroMem (PrivateData)把PEI Core的私有数据结构清零②CopyMem ServiceTableShadow把PEI Services Table拷贝到临时RAM③建HOB列表InitializeMemoryServices()把HobList指向临时RAM关键代码——HOB账本建在临时RAM上PrivateData.HobList.RawSecCoreData-PeiTemporaryRamBase;PeiCoreBuildHobHandoffInfoTable(BOOT_WITH_FULL_CONFIGURATION,(EFI_PHYSICAL_ADDRESS)(UINTN)SecCoreData-PeiTemporaryRamBase,(UINTN)SecCoreData-PeiTemporaryRamSize);HOB的整个账本——记录内存分布、资源分配、固件卷信息——全部写在临时RAM上。然后PEI Core开始dispatch PEIM逐个执行早期模块。这些模块也在临时RAM的Stack上跑HOB也在临时RAM的Heap上增长。⚠️一切都在那64KB的寄存柜里进行。三、谁触发搬家——PeiInstallPeiMemory()某个早期PEIM通常是Memory Init PEIM比如Intel FSP中的MrcInit完成DRAM训练后调用PeiInstallPeiMemory(PeiServices,MemoryBegin,MemoryLength);看MemoryServices.c里这个函数的实现EFI_STATUS EFIAPIPeiInstallPeiMemory(IN CONST EFI_PEI_SERVICES**PeiServices,IN EFI_PHYSICAL_ADDRESS MemoryBegin,IN UINT64 MemoryLength){PrivateDataPEI_CORE_INSTANCE_FROM_PS_THIS(PeiServices);// 只能调用一次调两次直接ASSERTif(PrivateData-PeiMemoryInstalled){ASSERT(FALSE);returnEFI_SUCCESS;}PrivateData-PhysicalMemoryBeginMemoryBegin;PrivateData-PhysicalMemoryLengthMemoryLength;PrivateData-FreePhysicalMemoryTopMemoryBeginMemoryLength;// 关键设这个flag告诉PEI Core该搬家了PrivateData-SwitchStackSignalTRUE;returnEFI_SUCCESS;}这个函数做的事情极其简单——只是记下永久内存的地址和大小然后设一个flag。它不搬任何东西不切换Stack不迁移HOB。类比你告诉前台房间准备好了前台在账本上记下房间号但不会立刻让你冲进去——因为你的行李还在寄存柜里没搬你现在手上还拿着寄存柜的钥匙。为什么不能立刻搬因为调用PeiInstallPeiMemory()的PEIM此刻还在临时RAM的Stack上跑。你不能在自己脚底下拆地板——立刻搬迁Stack当前函数的返回地址、局部变量全部失效直接crash。四、搬家的时机——Dispatcher回来之后PEI Dispatcher在每次dispatch完一个PEIM之后会检查SwitchStackSignal。如果flag被设了调用PeiCheckAndSwitchStack()——这才是真正搬家的地方。8个步骤按顺序执行Step 1 · 在永久内存里预留新StackNewStackSizeRShiftU64(Private-PhysicalMemoryLength,1);NewStackSizeALIGN_VALUE(NewStackSize,EFI_PAGE_SIZE);NewStackSizeMIN(PcdGet32(PcdPeiCoreMaxPeiStackSize),NewStackSize);新Stack大小 内存总大小的一半但不超过PcdPeiCoreMaxPeiStackSize限制从永久内存底部开始分配。Step 2 · 计算偏移量TopOfOldStack(UINTN)SecCoreData-StackBaseSecCoreData-StackSize;TopOfNewStackPrivate-PhysicalMemoryBeginNewStackSize;if(TopOfNewStackTopOfOldStack){StackOffsetPositiveTRUE;StackOffset(UINTN)(TopOfNewStack-TopOfOldStack);}else{StackOffsetPositiveFALSE;StackOffset(UINTN)(TopOfOldStack-TopOfNewStack);}新旧Stack顶的偏移量。偏移方向正/负很重要——临时RAM和永久RAM的地址谁高谁低取决于平台实现。Step 3 · Heap搬迁HeapTemporaryRamSize(UINTN)(Private-HobList.HandoffInformationTable-EfiFreeMemoryBottom-Private-HobList.HandoffInformationTable-EfiMemoryBottom);CopyMem((UINT8*)(UINTN)BaseOfNewHeap,PeiTemporaryRamBase,HeapTemporaryRamSize);直接把整个临时RAM的Heap内容包括HOB列表拷贝到永久内存。一个CopyMem把寄存柜里的账本和行李搬到新房间。Step 4 · Stack搬迁CopyMem((UINT8*)(UINTN)(TopOfNewStack-TemporaryStackSize),TemporaryStackBase,TemporaryStackSize);把旧Stack上的内容完整拷贝到新Stack。这包括了当前PEI Core函数的返回地址、局部变量、函数参数——全部。Step 5 · 迁移已分配的Memory PagesMigrateMemoryPages(Private,FALSE);如果早期PEIM在临时RAM阶段已通过AllocatePages分过内存页这些页也要搬Private-MemoryPages.Size(UINTN)(Private-HobList.HandoffInformationTable-EfiMemoryTop-Private-HobList.HandoffInformationTable-EfiFreeMemoryTop);NewMemPagesBasePrivate-FreePhysicalMemoryTop-Private-MemoryPages.Size;NewMemPagesBase~(UINT64)EFI_PAGE_MASK;CopyMem((VOID*)(UINTN)NewMemPagesBase,(VOID*)(UINTN)Private-MemoryPages.Base,Private-MemoryPages.Size);Step 6 · 修正所有指针——这是最危险的一步搬迁之后所有指向临时RAM的指针都失效了。PEI Core必须逐一修正修正HOB列表指针if(OldCoreData-HeapOffsetPositive){OldCoreData-HobList.RawOldCoreData-HeapOffset;}else{OldCoreData-HobList.Raw-OldCoreData-HeapOffset;}修正PPI数据库指针OldCoreData-PpiData.PpiList.PpiPtrs(PEI_PPI_LIST_POINTERS*)((UINT8*)OldCoreData-PpiData.PpiList.PpiPtrsHeapOffset);修正FV Handle指针、PeimState数组、FileHandles数组……一行一行修每个都不能漏。修正Memory Allocation HOB里的地址if((MemAllocHob-MemoryBaseAddressOldMemPagesBase)(MemAllocHob-MemoryBaseAddress(OldMemPagesBaseOldMemPagesSize))){if(PrivateData-MemoryPages.OffsetPositive){MemAllocHob-MemoryBaseAddressPrivateData-MemoryPages.Offset;}else{MemAllocHob-MemoryBaseAddress-PrivateData-MemoryPages.Offset;}}类比搬家之后纸笔记的所有东西在第3号寄存柜都得改成东西在301房第2个柜子。漏改一条你就找不到那件东西了。在固件里漏改一个指针机器就死了。Step 7 · Shadow PeiCore到永久内存OldCoreData-ShadowedPeiCoreShadowPeiCore(OldCoreData);把PEI Core自身从Flash拷到永久内存执行——XIP变成内存执行速度大幅提升。Step 8 · 切换Stack重入PeiCoreOldCoreData-ShadowedPeiCore(SecCoreData,PpiList,OldCoreData);// Should never reach here.ASSERT(FALSE);旧Stack上的PeiCore函数到此为止不再返回。新的PeiCore在永久内存的新Stack上重新开始执行。五、第二次进场——Permanent Memory上的新生活第二次进PeiCore()OldCoreData ! NULL且ShadowedPeiCore已经有值。走另一个分支CopyMem(NewSecCoreData,SecCoreDataPtr,sizeof(NewSecCoreData));CopyMem(PrivateData,OldCoreData,sizeof(PrivateData));然后安装最重要的PPI// 安装Memory Discovered PPI——告诉所有人真正的内存来了StatusPeiServicesInstallPpi(mMemoryDiscoveredPpi);ProcessDispatchNotifyList(PrivateData);gEfiPeiMemoryDiscoveredPpiGuid是PEI阶段最重要的PPI之一。所有注册了Notify的PEIM会被唤醒——比如需要大块内存的模块现在终于可以干活了。然后继续PeiDispatcherdispatch剩余的PEIM。这次它们在真正的DRAM上跑有充足的Stack和Heap不再受64KB限制。最终PEI Core找到gEfiDxeIplPpiGuid把HOB列表交给DXE IPLPEI阶段结束。六、实战踩坑点坑1临时RAM溢出临时RAM只有64KB左右。如果早期PEIM分配太多HOB或PoolPeiCreateHob会返回EFI_OUT_OF_RESOURCESFreeMemoryHandOffHob-EfiFreeMemoryTop-HandOffHob-EfiFreeMemoryBottom;if(FreeMemoryLength){DEBUG((DEBUG_ERROR,PeiCreateHob fail: Length - 0x%08x\n,(UINTN)Length));returnEFI_OUT_OF_RESOURCES;}经验临时RAM阶段的代码要极致精简。PeiAllocatePool有硬限制——最大不能超过0xFFF8 - sizeof(EFI_HOB_MEMORY_POOL)大约64KB不到。任何大数组、大数据结构都不要在这里分配。坑2Stack偏移方向搞错HeapOffsetPositive和StackOffsetPositive决定指针修正的方向。不同平台上临时RAM地址可能高于也可能低于永久RAM地址。如果平台代码把这两个值搞反了 → 所有指针修正都是反的 → 机器直接死 → 死得很深 → JTAG调试器都不一定能抓到。坑3PeiInstallPeiMemory只能调一次if(PrivateData-PeiMemoryInstalled){DEBUG((DEBUG_ERROR,ERROR: PeiInstallPeiMemory is called more than once!\n));ASSERT(FALSE);returnEFI_SUCCESS;}Debug版直接断死Release版忽略。如果有两个PEIM都试图注册永久内存——要么是平台配置错了要么是FSP和自定义Memory Init PEIM冲突了。坑4TemporaryRamSupportPPI vs 直接CopyMem搬迁Stack有两种路径条件搬迁方式注意事项平台提供gEfiTemporaryRamSupportPpiGuid调用TemporaryRamMigration()Intel平台一般提供因为CAR需要特殊Cache刷新平台不提供该PPIPEI Core自己CopyMem搬⚠️ 必须确保Cache一致性CAR模式下非常危险七、从源码学什么这段搬迁代码的价值不只是知道怎么搬。它展示了一个固件设计原则在资源受限的早期阶段所有操作都要可逆、可迁移。HOB列表就是一个可搬迁的账本——它不直接引用临时RAM的绝对地址而是通过偏移量修正来实现搬迁。PEI Core的私有数据结构也设计成了可以被整体拷贝和偏移修正的形态。这个原则在BIOS开发的很多地方都有体现场景类似机制PCI资源分配先临时分配再根据冲突调整ACPI表构建在临时缓冲区建好再拷到最终位置SMM通信缓冲区先在SMRAM外构建再迁入理解了PEI的搬迁机制后面遇到类似设计就能一眼看出本质。源码路径索引文件内容MdeModulePkg/Core/Pei/PeiMain/PeiMain.cPeiCore主入口搬迁总控MdeModulePkg/Core/Pei/Memory/MemoryServices.cInstallPeiMemory、AllocatePages、内存迁移MdeModulePkg/Core/Pei/Dispatcher/Dispatcher.cDispatcher和Stack切换逻辑MdeModulePkg/Core/Pei/Hob/Hob.cHOB操作创建、安装SEC HOBMdeModulePkg/Core/Pei/PeiMain.hPEI_CORE_INSTANCE结构体定义MdePkg/Include/Pi/PiPeiCis.hEFI_SEC_PEI_HAND_OFF定义
延伸阅读

更多相关文章

2026/11/10 17:33:10

51单片机动态数码管显示学号:从I/O口、定时器到驱动电路全解析

1. 项目概述与核心价值最近在整理以前的学习笔记,翻到了一个特别经典的51单片机入门项目——用动态数码管显示自己的学号。这个项目看似简单,却是一个绝佳的“麻雀虽小,五脏俱全”的练手案例。它几乎串联了51单片机开发中所有最基础、最核心的…

2026/11/10 17:33:06

Python装饰器底层原理与5个实战用法

在Python编程中,装饰器是一个让许多初学者感到困惑,却又让资深开发者爱不释手的特性。简单来说,装饰器就是一个用于修改或增强函数行为的函数。如果把函数比作一部手机,那么装饰器就是给手机贴的防窥膜或保护壳,它在不…

2026/11/10 20:36:23

重庆低压电工作业题库千万别踩坑,工地太忙怎么高效备考?

重庆低压电工作业题库千万别踩坑,工地太忙怎么高效备考? 工地太忙根本没时间复习考试,这不是个别现象,而是很多一线电工的共同困扰。尤其是像重庆这样的建筑大市,项目多、工期紧、人员流动性大,很多电工师傅连吃饭的时间都挤不出来,更别说抽空备考低压…

2026/11/10 20:36:23

电工证淘宝年审怎么考?2026河南最新政策告诉你

电工证淘宝年审怎么考?2026河南最新政策告诉你 怕考不过白交培训费?2026年河南最新政策出炉,电工证年审流程更规范,考试题库也更新了,不少学员反映淘宝上买题库容易踩坑。如果你正在备考电工证年审,或者打算通过淘宝找题库,这篇内容能帮你少走…

2026/11/10 20:36:23

无学历也能考高级电工证二级?上岗必备的全流程解析

无学历也能考高级电工证二级?上岗必备的全流程解析 你是不是也担心自己学历不高,考不了高级电工证二级?其实很多人和你一样,担心自己因为学历问题被拒之门外,但事实是, 高级电工证二级 的报考门槛并没有想象中那么高,只要符合基本条件, 上岗必备…

2026/11/10 20:36:23

固始县办理建筑电工证怎么报名 薪资前景及避坑指南

固始县办理建筑电工证怎么报名 薪资前景及避坑指南 证书过期了不知道怎么复审延期,很多人在找“固始县办理建筑电工证怎么报名”的时候,其实心里都藏着一个更大的问题: 这证到底有没有用?值不值得花时间去考?…

2026/11/10 8:12:38

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/10 0:01:28

学历不高怕报不上名?考电工证报名费+网上查询全攻略来了

学历不高怕报不上名?考电工证报名费+网上查询全攻略来了 你是不是也担心自己学历不高,考电工证报名费太贵,根本报不上名?别急,今天就来给你讲讲,怎么用最少的钱,把证拿到手,还能在工地上多挣点钱。 电工证报名费多少钱?网上查询是关键…

2026/11/10 0:01:29

绍兴考电工证培训机构怎么选?2026河南最新政策别错过

绍兴考电工证培训机构怎么选?2026河南最新政策别错过 怕考不过白交培训费?你不是一个人。2026河南最新政策出台,对电工证考试、报名、培训、复审都有明确规定。选择绍兴考电工证培训机构,别再踩坑,下面一步步教你避坑。…

2026/11/10 20:08:15

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/10 10:29:37

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑