UEFI開發(fā)實(shí)戰(zhàn)SlimBootloader中調(diào)用FSP
綜述
FSP的全稱是Firmware Support Package。FSP有以下的特性:
- FSP提供了Intel重要組件(包括處理器、內(nèi)存控制器、芯片組等)的初始化;
- FSP被編譯成獨(dú)立的二進(jìn)制,并可以集成到Bootloader中,這里說的Bootloader可以是Slim Bootloader,coreboot,UEFI等等;
- FSP的優(yōu)點(diǎn)有免費(fèi)、方便集成、可減少開發(fā)時(shí)間,等等。
FSP中包含若干個(gè)部分,如下圖所示:

按作用來分它包含三個(gè)大的組件,分別是:
FSP-T:它主要用來初始化CACHE以及其它早期需要的初始化,對(duì)應(yīng)提供給外部的接口是TempRamInit();
FSP-M:它主要用來初始化內(nèi)存以及其它需要的初始化,對(duì)應(yīng)提供給外部的接口是FspMemoryInit()和TempRamExit(),前者用于內(nèi)存初始化,后者用于處理FSP-T中使用的CACHE內(nèi)容;
FPS-S:它主要是CPU和芯片組的初始化,對(duì)應(yīng)提供給外部的接口是FspSiliconInit()和NotifyPhase(),其中后者又會(huì)在不同的階段調(diào)用,包括PCIE掃描之后,ReadyToBoot時(shí)和EndOfBootServices的時(shí)候;
按功能來區(qū)分,每個(gè)組件都包含頭部、配置和API三個(gè)部分。頭部是固定的,配置用于一些可控的定制化,API就是功能代碼。Bootloader要操作FSP,就需要完成文件配置,接口調(diào)用等。
BootLoader調(diào)用FSP的整個(gè)流程如下圖所示:

Bootloader中需要有相應(yīng)的代碼做上述的操作,以Slim Bootloader為例,有一個(gè)IntelFsp2Pkg用來處理FSP相關(guān)的內(nèi)容。不過需要注意,這里的IntelFsp2Pkg并不提供FSP源代碼的,只是提供了EDK與FSP之間的中間層,真正的用來初始化Intel組件的FSP的代碼并沒有開源,所以這里也拿不到,不過可以拿到用于QEMU的FSP源代碼,后續(xù)使用的就是這個(gè)。
編譯
代碼主要是https://gitee.com/jiangwei0512/edk2-beni中的QemuFspPkg,它是用在QEMU上的FSP,它有源碼可以下載,而其它Intel的FSP基本是不開源的,沒有辦法下載到,所以這里只能用QEMU的FSP作為示例。
QEMU對(duì)應(yīng)的FSP通過BuildFsp.py進(jìn)行編譯得到,該腳本執(zhí)行三個(gè)步驟:
- Prebuild
- Build
- PostBuild
PostBuild
Prebuild的流程如下:

1.構(gòu)建FspHeader.inf實(shí)際上就是創(chuàng)建FSP Header的頭部,它是固定的格式,位于QemuFspPkg\FspHeader\FspHeader.aslc,內(nèi)容如下:
TABLES mTable =
{
{
FSP_INFO_HEADER_SIGNATURE, // UINT32 Signature (FSPH)
sizeof(FSP_INFO_HEADER), // UINT32 HeaderLength;
{0x00, 0x00}, // UINT8 Reserved1[2];
FixedPcdGet8(PcdFspHeaderSpecVersion), // UINT8 SpecVersion;
FixedPcdGet8(PcdFspHeaderRevision), // UINT8 HeaderRevision;
FixedPcdGet32(PcdFspImageRevision), // UINT32 ImageRevision;
UINT64_TO_BYTE_ARRAY(
FixedPcdGet64(PcdFspImageIdString)), // CHAR8 ImageId[8];
0x12345678, // UINT32 ImageSize;
0x12345678, // UINT32 ImageBase;
FixedPcdGet16(PcdFspImageAttributes), // UINT16 ImageAttribute;
FixedPcdGet16(PcdFspComponentAttributes), // UINT16 ComponentAttribute; Bits[15:12] - 0001b: FSP-T, 0010b: FSP-M, 0011b: FSP-S
0x12345678, // UINT32 CfgRegionOffset;
0x12345678, // UINT32 CfgRegionSize;
0x00000000, // UINT32 Reserved2;
0x00000000, // UINT32 TempRamInitEntry;
0x00000000, // UINT32 Reserved3;
0x00000000, // UINT32 NotifyPhaseEntry;
0x00000000, // UINT32 FspMemoryInitEntry;
0x00000000, // UINT32 TempRamExitEntry;
0x00000000, // UINT32 FspSiliconInitEntry;
},
{
FSP_INFO_EXTENDED_HEADER_SIGNATURE, // UINT32 Signature (FSPE)
sizeof(FSP_INFO_EXTENDED_HEADER), // UINT32 Length;
FSPE_HEADER_REVISION_1, // UINT8 Revision;
0x00, // UINT8 Reserved;
{FSP_PRODUCER_ID}, // CHAR8 FspProducerId[6];
0x00000001, // UINT32 FspProducerRevision;
0x00000000, // UINT32 FspProducerDataSize;
},
{
FSP_FSPP_SIGNATURE, // UINT32 Signature (FSPP)
sizeof(FSP_PATCH_TABLE), // UINT16 Length;
FSPP_HEADER_REVISION_1, // UINT8 Revision;
0x00, // UINT8 Reserved;
1 // UINT32 PatchEntryNum;
},
0xFFFFFFFC // UINT32 Patch FVBASE at end of FV
};
里面的某些數(shù)據(jù)在之后還會(huì)被修改,通過這個(gè)頭部就可以找到對(duì)應(yīng)API的位置,從而進(jìn)行調(diào)用。
2.UPD txt文件是Build\QemuFspPkg\DEBUG_VS2019\FV(根據(jù)編譯工具的不同,對(duì)應(yīng)的目錄可能存在差異)下的如下內(nèi)容:

txt文件名對(duì)應(yīng)的是三個(gè)FSP組件的GUID(位于BuildFsp.py):
FspGuid = {
'FspTUpdGuid' : '34686CA3-34F9-4901-B82A-BA630F0714C6',
'FspMUpdGuid' : '39A250DB-E465-4DD1-A2AC-E2BD3C0E2385',
'FspSUpdGuid' : 'CAE3605B-5B34-4C85-B3D7-27D54273C40F'
}
里面的內(nèi)容主要是一些PCD,以39A250DB-E465-4DD1-A2AC-E2BD3C0E2385.txt為例:
## @file
#
# THIS IS AUTO-GENERATED FILE BY BUILD TOOLS AND PLEASE DO NOT MAKE MODIFICATION.
#
# This file lists all VPD informations for a platform collected by build.exe.
#
# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution. The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
gQemuFspPkgTokenSpaceGuid.Signature|DEFAULT|0x0000|8|0x4D5F4450554D4551
gQemuFspPkgTokenSpaceGuid.Revision|DEFAULT|0x0008|1|0x01
gQemuFspPkgTokenSpaceGuid.Reserved|DEFAULT|0x0009|23|{0x00}
gQemuFspPkgTokenSpaceGuid.Revision|DEFAULT|0x0020|1|0x01
gQemuFspPkgTokenSpaceGuid.Reserved|DEFAULT|0x0021|3|{0x00}
gQemuFspPkgTokenSpaceGuid.NvsBufferPtr|DEFAULT|0x0024|4|0x00000000
gQemuFspPkgTokenSpaceGuid.StackBase|DEFAULT|0x0028|4|0x00070000
gQemuFspPkgTokenSpaceGuid.StackSize|DEFAULT|0x002C|4|0x00010000
gQemuFspPkgTokenSpaceGuid.BootLoaderTolumSize|DEFAULT|0x0030|4|0x00000000
gPlatformFspPkgTokenSpaceGuid.Bootmode|DEFAULT|0x0034|4|0x00000000
gQemuFspPkgTokenSpaceGuid.Reserved1|DEFAULT|0x0038|8|{0x00}
gQemuFspPkgTokenSpaceGuid.SerialDebugPortAddress|DEFAULT|0x0040|4|0x00000000
gQemuFspPkgTokenSpaceGuid.SerialDebugPortType|DEFAULT|0x0044|1|0x02
gQemuFspPkgTokenSpaceGuid.SerialDebugPortDevice|DEFAULT|0x0045|1|0x02
gQemuFspPkgTokenSpaceGuid.SerialDebugPortStrideSize|DEFAULT|0x0046|1|0x02
gQemuFspPkgTokenSpaceGuid.UnusedUpdSpace0|DEFAULT|0x0047|0x0031|{0}
gQemuFspPkgTokenSpaceGuid.ReservedFspmUpd|DEFAULT|0x0078|4|{0x00}
gQemuFspPkgTokenSpaceGuid.UnusedUpdSpace1|DEFAULT|0x007C|0x0002|{0}
gQemuFspPkgTokenSpaceGuid.UpdTerminator|DEFAULT|0x007E|2|0x55AA
txt文件的來源是QemuFspPkg\QemuFspPkg.dsc,里面有這些PCD的初始化值,位于[PcdsDynamicVpd.Upd]這個(gè)Section,其中的UnusedUpdSpaceX也是在dsc文件中通過PCD指定的偏移來確定的。
3.UPD bin文件名也對(duì)應(yīng)到前面提到的GUID,以39A250DB-E465-4DD1-A2AC-E2BD3C0E2385.bin為例:

bin文件跟txt文件中的PCD值是一一對(duì)應(yīng)的。
4.UPD頭文件和bsf文件比較直觀,不做詳細(xì)說明,它們也是通過QemuFspPkg\QemuFspPkg.dsc創(chuàng)建的。UPD頭文件中還包含一個(gè)通用的頭部結(jié)構(gòu)體:
#pragma pack(1)
///
/// FSP_UPD_HEADER Configuration.
///
typedef struct {
///
/// UPD Region Signature. This signature will be
/// "XXXXXX_T" for FSP-T
/// "XXXXXX_M" for FSP-M
/// "XXXXXX_S" for FSP-S
/// Where XXXXXX is an unique signature
///
UINT64 Signature;
///
/// Revision of the Data structure.
/// For FSP spec 2.0/2.1 value is 1.
/// For FSP spec 2.2 value is 2.
///
UINT8 Revision;
UINT8 Reserved[23];
} FSP_UPD_HEADER;
#pragma pack()
每個(gè)平臺(tái)的UPD和bsf內(nèi)容都是不同的,甚至同一個(gè)平臺(tái)的不同版本也可能存在差異,不同的FSP-X對(duì)應(yīng)不同的UPD,分別是FSPT_UPD、FSPM_UPD和FSPS_UPD,它們分別存放在FsptUpd.h、FspmUpd.h和FspsUpd.h中。以FSPM_UPD結(jié)構(gòu)體為例:
/** Fsp M UPD Configuration **/
typedef struct {
/** Offset 0x0000 **/
FSP_UPD_HEADER FspUpdHeader;
/** Offset 0x0020 **/
FSPM_ARCH_UPD FspmArchUpd;
/** Offset 0x0040 **/
FSP_M_CONFIG FspmConfig;
/** Offset 0x007C **/
UINT8 UnusedUpdSpace1[2];
/** Offset 0x007E **/
UINT16 UpdTerminator;
} FSPM_UPD;
其中的內(nèi)容跟前面的UPD txt中的PCD一一對(duì)應(yīng)。
上述的文件都在Build\QemuFspPkg\DEBUG_VS2019\FV(根據(jù)編譯工具的不同,對(duì)應(yīng)的目錄可能存在差異)創(chuàng)建,總的來說就是為了創(chuàng)建FSP的UPD配置文件和對(duì)應(yīng)用在代碼中的頭文件,頭文件會(huì)被拷貝到其它位置也是為了代碼能夠調(diào)用到。而UPD配置文件會(huì)通過二進(jìn)制的方式包含到QemuFspPkg\QemuFspPkg.fdf,下面是一個(gè)示例:
#
# Project specific configuration data files
#
!ifndef $(CFG_PREBUILD)
FILE RAW = $(FSP_M_UPD_FFS_GUID) {
SECTION RAW = $(OUTPUT_DIRECTORY)/$(TARGET)_$(TOOL_CHAIN_TAG)/FV/$(FSP_M_UPD_TOOL_GUID).bin
}
!endif
Build
該過程僅僅是執(zhí)行build操作而已,對(duì)應(yīng)的代碼:
def Build (target, toolchain):
cmd = '%s -p QemuFspPkg/QemuFspPkg.dsc -a IA32 -b %s -t %s -y Report%s.log' % (
'build' if os.name == 'posix' else 'build.bat', target, toolchain, target)
ret = subprocess.call(cmd.split(' '))
if ret:
Fatal('Failed to do Build QEMU FSP!')
print('End of Build...')
可以看到執(zhí)行對(duì)象是QemuFspPkg.dsc。完成這一步之后會(huì)生成FSP-M.Fv、FSP-S.Fv、FSP-T.Fv和QEMUFSP.fd。
到這里FSP二進(jìn)制已經(jīng)生成,就是QEMUFSP.fd,但是它不能直接使用,還需要后續(xù)操作。
PostBuild
這一步主要是通過PatchFv.py來修改前文生成的QEMUFSP.fd,Patch前后:

這里具體Patch了哪部分內(nèi)容,需要先了解FSP二進(jìn)制的組成部分,可以參考FSP二進(jìn)制組成分析。這里Patch的大部分都是FSP Header中的內(nèi)容,對(duì)應(yīng)的默認(rèn)初始化內(nèi)容就是前面提到的QemuFspPkg\FspHeader\FspHeader.aslc,其結(jié)構(gòu)體如下:
///
/// FSP Information Header as described in FSP v2.0 Spec section 5.1.1.
///
typedef struct {
///
/// Byte 0x00: Signature ('FSPH') for the FSP Information Header.
///
UINT32 Signature;
///
/// Byte 0x04: Length of the FSP Information Header.
///
UINT32 HeaderLength;
///
/// Byte 0x08: Reserved.
///
UINT8 Reserved1[2];
///
/// Byte 0x0A: Indicates compliance with a revision of this specification in the BCD format.
///
UINT8 SpecVersion;
///
/// Byte 0x0B: Revision of the FSP Information Header.
///
UINT8 HeaderRevision;
///
/// Byte 0x0C: Revision of the FSP binary.
///
UINT32 ImageRevision;
///
/// Byte 0x10: Signature string that will help match the FSP Binary to a supported HW configuration.
///
CHAR8 ImageId[8];
///
/// Byte 0x18: Size of the entire FSP binary.
///
UINT32 ImageSize;
///
/// Byte 0x1C: FSP binary preferred base address.
///
UINT32 ImageBase;
///
/// Byte 0x20: Attribute for the FSP binary.
///
UINT16 ImageAttribute;
///
/// Byte 0x22: Attributes of the FSP Component.
///
UINT16 ComponentAttribute;
///
/// Byte 0x24: Offset of the FSP configuration region.
///
UINT32 CfgRegionOffset;
///
/// Byte 0x28: Size of the FSP configuration region.
///
UINT32 CfgRegionSize;
///
/// Byte 0x2C: Reserved2.
///
UINT32 Reserved2;
///
/// Byte 0x30: The offset for the API to setup a temporary stack till the memory is initialized.
///
UINT32 TempRamInitEntryOffset;
///
/// Byte 0x34: Reserved3.
///
UINT32 Reserved3;
///
/// Byte 0x38: The offset for the API to inform the FSP about the different stages in the boot process.
///
UINT32 NotifyPhaseEntryOffset;
///
/// Byte 0x3C: The offset for the API to initialize the memory.
///
UINT32 FspMemoryInitEntryOffset;
///
/// Byte 0x40: The offset for the API to tear down temporary RAM.
///
UINT32 TempRamExitEntryOffset;
///
/// Byte 0x44: The offset for the API to initialize the CPU and chipset.
///
UINT32 FspSiliconInitEntryOffset;
///
/// Byte 0x48: Offset for the API for the optional Multi-Phase processor and chipset initialization.
/// This value is only valid if FSP HeaderRevision is >= 5.
/// If the value is set to 0x00000000, then this API is not available in this component.
///
UINT32 FspMultiPhaseSiInitEntryOffset;
} FSP_INFO_HEADER;
其中的ImageSize、ImageBase、ImageAttribute、ComponentAttribute、CfgRegionOffset、CfgRegionSize、TempRamInitEntryOffset、FspMemoryInitEntryOffset、TempRamExitEntryOffset、FspSiliconInitEntryOffset、NotifyPhaseEntryOffset等都需要修改。
因?yàn)槊總€(gè)FSP-X都有一個(gè)FSP_INFO_HEADER結(jié)構(gòu)體,所以前提提到的XXXOffset會(huì)針對(duì)不同的FSP-X組件做對(duì)應(yīng)的修改,比如FSP-T只需要TempRamInitEntryOffset。
除了FSP Header的Patch,這里還有一個(gè)點(diǎn)被Patch了:

它們對(duì)應(yīng)的是模塊的入口(IntelFsp2Pkg\FspSecCore\Ia32\FspHelper.nasm):
global ASM_PFX(FspInfoHeaderRelativeOff) ASM_PFX(FspInfoHeaderRelativeOff): DD 0x12345678 ; This value must be patched by the build script
從上面的代碼也可以看到這部分是需要Patch的。
FSP二進(jìn)制組成分析
二進(jìn)制的組成如下:

FSP每個(gè)組件都是一個(gè)FV,所以都有一個(gè)FV Header(EFI_FIRMWARE_VOLUME_HEADER,位于MdePkg\Include\Pi\PiFirmwareVolume.h),大小是0x48個(gè)字節(jié),之后是一個(gè)FV Extended Header(EFI_FIRMWARE_VOLUME_EXT_HEADER,位于MdePkg\Include\Pi\PiFirmwareVolume.h),之后才是FSP的內(nèi)容,如下圖所示:

FSP組件的第一個(gè)模塊是FSP Header,對(duì)應(yīng)二進(jìn)制(Header的第一個(gè)成員是"FSPH",最后一個(gè)成員是0xFFFFFFFC)中:

這里可以看到里面有一些數(shù)據(jù)比較奇怪,都是0x12345678和0x00000000,這些都是占位符,并不是真正的有效數(shù)據(jù),是通過FspHeader.aslc生成的,在后期這些數(shù)據(jù)會(huì)被Patch成有效的值。
FSP組件的第二個(gè)模塊是UPD數(shù)據(jù),它在Prebuild中生成,對(duì)應(yīng)的數(shù)據(jù)(UPD數(shù)據(jù)的第一個(gè)成員是Signature(本例中是QEMUPD_T),最后一個(gè)成員是0x55AA):

再之后是通用的模塊。以QEMU中的FSP對(duì)應(yīng)的fdf文件為例:
#
# FSP header
#
INF RuleOverride = FSPHEADER $(FSP_PACKAGE)/FspHeader/FspHeader.inf
#
# Project specific configuration data files
#
!ifndef $(CFG_PREBUILD)
FILE RAW = $(FSP_T_UPD_FFS_GUID) {
SECTION RAW = $(OUTPUT_DIRECTORY)/$(TARGET)_$(TOOL_CHAIN_TAG)/FV/$(FSP_T_UPD_TOOL_GUID).bin
}
!endif
INF RuleOverride = RELOC IntelFsp2Pkg/FspSecCore/FspSecCoreT.inf
使用
用于Slim Bootloader的FSP需要放到前者指定的目錄,對(duì)于QEMU來說對(duì)應(yīng)的是Silicon\QemuSocPkg\FspBin,同時(shí)FSP對(duì)應(yīng)的UPD頭文件也需要放到指定目錄。之后執(zhí)行Slim Bootloader的各個(gè)階段都會(huì)調(diào)用FSP的API接口,這里一一說明。
Stage1A
Stage1A階段會(huì)執(zhí)行FSP中的FspTempRamInit()接口,由于是執(zhí)行階段的早期,這里只有匯編部分的代碼,具體的位置在BootloaderCorePkg\Stage1A\Ia32\SecEntry.nasm,對(duì)應(yīng)代碼:
global ASM_PFX(_ModuleEntryPoint)
ASM_PFX(_ModuleEntryPoint):
movd mm0, eax
;
; Read time stamp
;
rdtsc
mov esi, eax
mov edi, edx
;
; Early board hooks
;
mov esp, EarlyBoardInitRet
jmp ASM_PFX(EarlyBoardInit)
EarlyBoardInitRet:
mov esp, FspTempRamInitRet
jmp ASM_PFX(FspTempRamInit)
這里jmp到BootloaderCorePkg\Library\FspApiLib\Ia32\FspTempRamInit.nasm:
global ASM_PFX(FspTempRamInit)
ASM_PFX(FspTempRamInit):
;
; This hook is called to initialize temporay RAM
; ESI, EDI need to be preserved
; ESP contains return address
; ECX, EDX return the temprary RAM start and end
;
;
; Get FSP-T base in EAX
;
mov ebp, esp
mov eax, dword [ASM_PFX(PcdGet32(PcdFSPTBase))]
;
; Find the fsp info header
; Jump to TempRamInit API
;
add eax, dword [eax + 094h + FSP_HEADER_TEMPRAMINIT_OFFSET]
mov esp, TempRamInitStack
jmp eax
TempRamInitDone:
mov esp, ebp
jmp esp
FSP中的FspTempRamInit()真正的入口是PcdGet32(PcdFSPTBase)+ 094h + FSP_HEADER_TEMPRAMINIT_OFFSET,PcdFSPTBase的值是:
gPlatformModuleTokenSpaceGuid.PcdFSPTBase | $(FSP_T_BASE)
FSP_T_BASE表示的是FSP-T.bin的開始位置,094h在前面也已經(jīng)介紹過,其前面的內(nèi)容是FV Header,該地址開始是FSP Header,而FSP_HEADER_TEMPRAMINIT_OFFSET是FSP Header的偏移,該位置對(duì)應(yīng)成員是TempRamInitEntryOffset,到這里就對(duì)應(yīng)起來了,FspTempRamInit()即是該位置的值。
不過對(duì)于FSP_T_BASE的值,它是FSP-T放到系統(tǒng)內(nèi)存中位置的地址,可以在BootloaderCorePkg\Platform.dsc中找到:
DEFINE FSP_T_BASE = 0xFFFF0000
這個(gè)值也跟SBL二進(jìn)制產(chǎn)生關(guān)系:
Flash Map Information: +------------------------------------------------------------------------+ | FLASH MAP | | (RomSize = 0x00721000) | +------------------------------------------------------------------------+ | NAME | OFFSET (BASE) | SIZE | FLAGS | +----------+------------------------+------------+-----------------------+ +------------------------------------------------------------------------+ | TOP SWAP A | +------------------------------------------------------------------------+ | SG1A | 0x711000(0xFFFF0000) | 0x010000 | Uncompressed, TS_A | +------------------------------------------------------------------------+ | TOP SWAP B | +------------------------------------------------------------------------+ | SG1A | 0x701000(0xFFFE0000) | 0x010000 | Uncompressed, TS_B | +------------------------------------------------------------------------+ | REDUNDANT A | +------------------------------------------------------------------------+ | KEYH | 0x700000(0xFFFDF000) | 0x001000 | Uncompressed, R_A | | CNFG | 0x6ff000(0xFFFDE000) | 0x001000 | Uncompressed, R_A | | FWUP | 0x6e7000(0xFFFC6000) | 0x018000 | Compressed , R_A | | SG1B | 0x6b7000(0xFFF96000) | 0x030000 | Compressed , R_A | | SG02 | 0x69f000(0xFFF7E000) | 0x018000 | Compressed , R_A | | EMTY | 0x681000(0xFFF60000) | 0x01e000 | Uncompressed, R_A | +------------------------------------------------------------------------+ | REDUNDANT B | +------------------------------------------------------------------------+ | KEYH | 0x680000(0xFFF5F000) | 0x001000 | Uncompressed, R_B | | CNFG | 0x67f000(0xFFF5E000) | 0x001000 | Uncompressed, R_B | | FWUP | 0x667000(0xFFF46000) | 0x018000 | Compressed , R_B | | SG1B | 0x637000(0xFFF16000) | 0x030000 | Compressed , R_B | | SG02 | 0x61f000(0xFFEFE000) | 0x018000 | Compressed , R_B | | EMTY | 0x601000(0xFFEE0000) | 0x01e000 | Uncompressed, R_B | +------------------------------------------------------------------------+ | NON REDUNDANT | +------------------------------------------------------------------------+ | PTES | 0x600000(0xFFEDF000) | 0x001000 | Uncompressed, NR | | IPFW | 0x5f0000(0xFFECF000) | 0x010000 | Uncompressed, NR | | EPLD | 0x3e3000(0xFFCC2000) | 0x20d000 | Uncompressed, NR | | PYLD | 0x2e3000(0xFFBC2000) | 0x100000 | Compressed , NR | | VARS | 0x2e1000(0xFFBC0000) | 0x002000 | Uncompressed, NR | | EMTY | 0x001000(0xFF8E0000) | 0x2e0000 | Uncompressed, NR | +------------------------------------------------------------------------+ | NON VOLATILE | +------------------------------------------------------------------------+ | RSVD | 0x000000(0xFF8DF000) | 0x001000 | Uncompressed, NV | +----------+------------------------+------------+-----------------------+Flash Map Information:
+------------------------------------------------------------------------+
| FLASH MAP |
| (RomSize = 0x00721000) |
+------------------------------------------------------------------------+
| NAME | OFFSET (BASE) | SIZE | FLAGS |
+----------+------------------------+------------+-----------------------+
+------------------------------------------------------------------------+
| TOP SWAP A |
+------------------------------------------------------------------------+
| SG1A | 0x711000(0xFFFF0000) | 0x010000 | Uncompressed, TS_A |
+------------------------------------------------------------------------+
| TOP SWAP B |
+------------------------------------------------------------------------+
| SG1A | 0x701000(0xFFFE0000) | 0x010000 | Uncompressed, TS_B |
+------------------------------------------------------------------------+
| REDUNDANT A |
+------------------------------------------------------------------------+
| KEYH | 0x700000(0xFFFDF000) | 0x001000 | Uncompressed, R_A |
| CNFG | 0x6ff000(0xFFFDE000) | 0x001000 | Uncompressed, R_A |
| FWUP | 0x6e7000(0xFFFC6000) | 0x018000 | Compressed , R_A |
| SG1B | 0x6b7000(0xFFF96000) | 0x030000 | Compressed , R_A |
| SG02 | 0x69f000(0xFFF7E000) | 0x018000 | Compressed , R_A |
| EMTY | 0x681000(0xFFF60000) | 0x01e000 | Uncompressed, R_A |
+------------------------------------------------------------------------+
| REDUNDANT B |
+------------------------------------------------------------------------+
| KEYH | 0x680000(0xFFF5F000) | 0x001000 | Uncompressed, R_B |
| CNFG | 0x67f000(0xFFF5E000) | 0x001000 | Uncompressed, R_B |
| FWUP | 0x667000(0xFFF46000) | 0x018000 | Compressed , R_B |
| SG1B | 0x637000(0xFFF16000) | 0x030000 | Compressed , R_B |
| SG02 | 0x61f000(0xFFEFE000) | 0x018000 | Compressed , R_B |
| EMTY | 0x601000(0xFFEE0000) | 0x01e000 | Uncompressed, R_B |
+------------------------------------------------------------------------+
| NON REDUNDANT |
+------------------------------------------------------------------------+
| PTES | 0x600000(0xFFEDF000) | 0x001000 | Uncompressed, NR |
| IPFW | 0x5f0000(0xFFECF000) | 0x010000 | Uncompressed, NR |
| EPLD | 0x3e3000(0xFFCC2000) | 0x20d000 | Uncompressed, NR |
| PYLD | 0x2e3000(0xFFBC2000) | 0x100000 | Compressed , NR |
| VARS | 0x2e1000(0xFFBC0000) | 0x002000 | Uncompressed, NR |
| EMTY | 0x001000(0xFF8E0000) | 0x2e0000 | Uncompressed, NR |
+------------------------------------------------------------------------+
| NON VOLATILE |
+------------------------------------------------------------------------+
| RSVD | 0x000000(0xFF8DF000) | 0x001000 | Uncompressed, NV |
+----------+------------------------+------------+-----------------------+
由于SBL會(huì)放到4G以下的空間,而FSP-T.Fv放在了SG1A中,大小是0x10000,所以位置就是0xFFFF0000。通過下述命令能夠更清楚的看出來:
F:\Gitee\sbl>BootloaderCorePkg\Tools\IfwiUtility.py view -i Outputs\qemu\SlimBootloader.bin
IFWI [O:0x00000000 L:0x00721000]
BIOS [O:0x00000000 L:0x00721000]
NVS [O:0x00000000 L:0x00001000]
RSVD [O:0x00000000 L:0x00001000]
NRD [O:0x00001000 L:0x00600000]
EMTY [O:0x00001000 L:0x002E0000]
VARS [O:0x002E1000 L:0x00002000]
PYLD [O:0x002E3000 L:0x00100000]
EPLD [O:0x003E3000 L:0x0020D000]
IPFW [O:0x005F0000 L:0x00010000]
PTES [O:0x00600000 L:0x00001000]
RD1 [O:0x00601000 L:0x00080000]
EMTY [O:0x00601000 L:0x0001E000]
SG02 [O:0x0061F000 L:0x00018000]
SG1B [O:0x00637000 L:0x00030000]
FWUP [O:0x00667000 L:0x00018000]
CNFG [O:0x0067F000 L:0x00001000]
KEYH [O:0x00680000 L:0x00001000]
RD0 [O:0x00681000 L:0x00080000]
EMTY [O:0x00681000 L:0x0001E000]
SG02 [O:0x0069F000 L:0x00018000]
SG1B [O:0x006B7000 L:0x00030000]
FWUP [O:0x006E7000 L:0x00018000]
CNFG [O:0x006FF000 L:0x00001000]
KEYH [O:0x00700000 L:0x00001000]
TS1 [O:0x00701000 L:0x00010000]
SG1A [O:0x00701000 L:0x00010000]
TS0 [O:0x00711000 L:0x00010000]
SG1A [O:0x00711000 L:0x00010000] ---- 這里的最后就是0x100000000的位置
SBL二進(jìn)制和FSP-T.bin的對(duì)應(yīng)關(guān)系:

最終可以查看到PcdGet32(PcdFSPTBase)+ 094h + FSP_HEADER_TEMPRAMINIT_OFFSET處的值是0x473(位于0x7110C4),這也跟編譯FSP時(shí)的Patch對(duì)應(yīng):
Patched offset 0x000370C4:[00000000] with value 0x00000473 # TempRamInit API
從上圖可以看到該位置的值是EB 0B 90 90 90等等,可以確定這些就是代碼了,但是它對(duì)應(yīng)到的是哪個(gè)模塊呢?其實(shí)可以從QemuFspPkg\QemuFspPkg.fdf中找到答案:
[FV.FSP-T]
BlockSize = $(FLASH_BLOCK_SIZE)
FvAlignment = 16
ERASE_POLARITY = 1
MEMORY_MAPPED = TRUE
STICKY_WRITE = TRUE
LOCK_CAP = TRUE
LOCK_STATUS = TRUE
WRITE_DISABLED_CAP = TRUE
WRITE_ENABLED_CAP = TRUE
WRITE_STATUS = TRUE
WRITE_LOCK_CAP = TRUE
WRITE_LOCK_STATUS = TRUE
READ_DISABLED_CAP = TRUE
READ_ENABLED_CAP = TRUE
READ_STATUS = TRUE
READ_LOCK_CAP = TRUE
READ_LOCK_STATUS = TRUE
FvNameGuid = 52F1AFB6-78A6-448f-8274-F370549AC5D0
#
# FSP header
#
INF RuleOverride = FSPHEADER $(FSP_PACKAGE)/FspHeader/FspHeader.inf
#
# Project specific configuration data files
#
!ifndef $(CFG_PREBUILD)
FILE RAW = $(FSP_T_UPD_FFS_GUID) {
SECTION RAW = $(OUTPUT_DIRECTORY)/$(TARGET)_$(TOOL_CHAIN_TAG)/FV/$(FSP_T_UPD_TOOL_GUID).bin
}
!endif
INF RuleOverride = RELOC IntelFsp2Pkg/FspSecCore/FspSecCoreT.inf
根據(jù)前面的介紹,F(xiàn)spHeader.inf是FSP Header,$(FSP_T_UPD_TOOL_GUID).bin是UPD配置文件,那么FspSecCoreT.inf就應(yīng)該是包含FspTempRamInit()函數(shù)代碼的模塊了。
這里直接找對(duì)應(yīng)的模塊,它被編譯成一個(gè)二進(jìn)制FspSecCoreT.efi,efi文件符合的是《Microsoft Portable Executable and Common Object File Format Specification》(后稱Spec)規(guī)范。文檔可以點(diǎn)擊下載。在這個(gè)文檔中描述了efi二進(jìn)制頭部的格式如下:

通過它可以找到真正的代碼入口位置,不過也不需要一一計(jì)算,可以通過對(duì)應(yīng)的map文件(這里就是Build\QemuFspPkg\DEBUG_VS2019\IA32\IntelFsp2Pkg\FspSecCore\FspSecCoreT\DEBUG\FspSecCoreT.map)找到需要的入口:
Address Publics by Value Rva+Base Lib:Object 0001:000001fb _TempRamInitApi 0000041b FspSecCoreT:FspApiEntryT.obj
可以看到地址是0000041b,查看FspSecCoreT.efi二進(jìn)制:

可以看到數(shù)據(jù)已經(jīng)對(duì)應(yīng)上了。再進(jìn)一步分析這些數(shù)據(jù)的話,會(huì)發(fā)現(xiàn)EB實(shí)際上是一個(gè)跳轉(zhuǎn)指令(注意之類是16位的代碼,所以是近跳轉(zhuǎn)),可以參考《64-ia-32-architectures-software-developer-instruction-set-reference-manual.pdf》中的“JMP—Jump”章節(jié):

cb表示的是跳轉(zhuǎn)偏移,這里的值是0xB,對(duì)應(yīng)的代碼在IntelFsp2Pkg\FspSecCore\Ia32\SaveRestoreSseNasm.inc:
%macro ENABLE_SSE 0
;
; Initialize floating point units
;
jmp NextAddress
align 4 ; 需要4字節(jié)對(duì)齊,所以后面補(bǔ)充了90,表示的是nop指令,總共3個(gè)字節(jié)
;
; Float control word initial value:
; all exceptions masked, double-precision, round-to-nearest
;
FpuControlWord DW 027Fh ; 占據(jù)2個(gè)字節(jié)
;
; Multimedia-extensions control word:
; all exceptions masked, round-to-nearest, flush to zero for masked underflow
;
MmxControlWord DD 01F80h ; 占據(jù)2個(gè)字節(jié)
SseError:
;
; Processor has to support SSE
;
jmp SseError ; 對(duì)應(yīng)的機(jī)器碼是EB FE,因?yàn)槭茄h(huán)執(zhí)行,相當(dāng)于跳轉(zhuǎn)回去執(zhí)行同一條命令,而該命令是2個(gè)字節(jié),所以就是-2,等于0xFE
NextAddress: ; 理論上到這里只有9個(gè)字節(jié),但是代碼中跳轉(zhuǎn)了0xB,也就是有11個(gè)字節(jié),多出來的2個(gè)字節(jié)用0補(bǔ)上了,應(yīng)該也是為了4字節(jié)對(duì)齊
到這里整個(gè)調(diào)用流程就完整了。
Stage1B
本階段SBL會(huì)調(diào)用FSP中的FspMemoryInit(),對(duì)應(yīng)的代碼在BootloaderCorePkg\Stage1B\Stage1B.c:
// Initialize memory HobList = NULL; DEBUG ((DEBUG_INIT, "Memory Init\n")); AddMeasurePoint (0x2020); Status = CallFspMemoryInit (PCD_GET32_WITH_ADJUST (PcdFSPMBase), &HobList); AddMeasurePoint (0x2030);
CallFspMemoryInit()執(zhí)行的最重要的代碼如下:
FspMemoryInit = (FSP_MEMORY_INIT)(UINTN)(FspHeader->ImageBase + \
FspHeader->FspMemoryInitEntryOffset);
Status = FspMemoryInit (&FspmUpd, HobList);
從這里可以看到這就是一個(gè)跳轉(zhuǎn)的動(dòng)作,而跳轉(zhuǎn)的位置就是FSP_INFO_HEADER中的成員FspMemoryInitEntryOffset,這個(gè)在前面已經(jīng)說明過。
FSP中對(duì)應(yīng)的模塊主要有:
# # It is important to keep the proper order for these PEIMs # for this implementation # INF RuleOverride = RELOC IntelFsp2Pkg/FspSecCore/FspSecCoreM.inf INF MdeModulePkg/Core/Pei/PeiMain.inf INF MdeModulePkg/Universal/PCD/Pei/Pcd.inf # # Project specific PEIMs # INF $(FSP_PACKAGE)/FspmInit/FspmInit.inf
FspSecCoreM.inf可以認(rèn)為是一個(gè)偽SEC代碼,主要的目的就是為了進(jìn)入之后的PEI階段,即PeiMain.inf,它跟UEFI中的PEI沒有本質(zhì)的區(qū)別,不過能夠Dispatch的模塊僅有后面的兩個(gè),Pcd.inf只是功能模塊在這里并不重要,而FspmInit.inf就是內(nèi)存初始化的主體。下面會(huì)簡單介紹其中的主要模塊。
FspSecCoreM.inf對(duì)應(yīng)的入口:
;---------------------------------------------------------------------------- ; FspMemoryInit API ; ; This FSP API is called after TempRamInit and initializes the memory. ; ;---------------------------------------------------------------------------- global ASM_PFX(FspMemoryInitApi) ASM_PFX(FspMemoryInitApi): mov eax, 3 ; FSP_API_INDEX.FspMemoryInitApiIndex jmp ASM_PFX(FspApiCommon)
對(duì)應(yīng)的調(diào)用路徑:

調(diào)用路徑中大部分是匯編,不過有幾個(gè)是C函數(shù),因?yàn)樵赟tage1A中已經(jīng)可以使用C函數(shù)了。SecStartup()位于UefiCpuPkg\SecCore\SecMain.c,是SEC的C函數(shù)入口,之后轉(zhuǎn)入執(zhí)行PeiMain,這個(gè)PEI階段Dispatch的模塊主要是FspmInit.inf,它完成真正的內(nèi)存初始化操作。
FspmInit.inf模塊中完成內(nèi)存初始化的代碼這里不多做介紹,因?yàn)檎嬲腎ntel平臺(tái)中的代碼要復(fù)雜的多,這里只是虛擬機(jī)的內(nèi)存初始化,本身的意義不大,不過需要注意的是其中的某些代碼:
EFI_PEI_NOTIFY_DESCRIPTOR mMemoryDiscoveredNotifyList = {
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_DISPATCH | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEfiPeiMemoryDiscoveredPpiGuid,
MemoryDiscoveredPpiNotifyCallback
};
//
// Now that all of the pre-permanent memory activities have
// been taken care of, post a call-back for the permanent-memory
// resident services, such as HOB construction.
// PEI Core will switch stack after this PEIM exit. After that the MTRR
// can be set.
//
Status = PeiServicesNotifyPpi (&mMemoryDiscoveredNotifyList);
這個(gè)操作在gEfiPeiMemoryDiscoveredPpiGuid被安裝后被調(diào)用,而安裝動(dòng)作在PeiCore()中完成:
//
// Alert any listeners that there is permanent memory available
//
PERF_INMODULE_BEGIN ("DisMem");
Status = PeiServicesInstallPpi (&mMemoryDiscoveredPpi);
gEfiPeiMemoryDiscoveredPpiGuid對(duì)應(yīng)的回調(diào)函數(shù)有很多個(gè),這里關(guān)注的是FspmInit.inf模塊中的。原因是這里有兩層的跳轉(zhuǎn),其中有如下的代碼:
EFI_STATUS
EFIAPI
MemoryDiscoveredPpiNotifyCallback (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
IN VOID *Ppi
)
{
//
// Migrate FSP-M UPD data before destroying CAR
//
MigrateFspmUpdData ();
//
// Give control back after MemoryInitApi
//
FspMemoryInitDone (HobListPtr);
if (GetFspApiCallingIndex() == TempRamExitApiIndex) {
DEBUG ((DEBUG_INFO | DEBUG_INIT, "Memory Discovered Notify completed ...\n"));
//
// Give control back after TempRamExitApi
//
FspTempRamExitDone ();
}
}
這里的FspMemoryInitDone()執(zhí)行之后,CPU又會(huì)跳轉(zhuǎn)到SBL代碼中去執(zhí)行,直到SBL中再次調(diào)用FSP中的TempRamExit()這個(gè)API,對(duì)應(yīng)SBL中的代碼:
Status = CallFspTempRamExit (PCD_GET32_WITH_ADJUST (PcdFSPMBase), NULL);
然后會(huì)再次開始執(zhí)行FspMemoryInitDone()之后的代碼,直到FspTempRamExitDone()退出。
Stage1B中調(diào)用的兩個(gè)API到這里就都介紹完畢了。
Stage2
本階段SBL會(huì)調(diào)用FSP中FspSiliconInit(),對(duì)應(yīng)的代碼在BootloaderCorePkg\Stage2\Stage2.c:
DEBUG ((DEBUG_INIT, "Silicon Init\n")); AddMeasurePoint (0x3020); Status = CallFspSiliconInit (); AddMeasurePoint (0x3030); FspResetHandler (Status); ASSERT_EFI_ERROR (Status);
跟Stage1B中調(diào)用FSP中的API一樣,這里也是一個(gè)跳轉(zhuǎn):
FspSiliconInit = (FSP_SILICON_INIT)(UINTN)(FspHeader->ImageBase + \
FspHeader->FspSiliconInitEntryOffset)
Status = FspSiliconInit (FspsUpdptr);
對(duì)應(yīng)的FSP-S的執(zhí)行過程跟FSP-M差不多,也有一個(gè)偽SEC模塊,對(duì)應(yīng)的模塊如下所示:
# # It is important to keep the proper order for these PEIMs # for this implementation # INF RuleOverride = RELOC IntelFsp2Pkg/FspSecCore/FspSecCoreS.inf INF MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf INF RuleOverride = PE32 $(FSP_PACKAGE)/FspsInit/FspsInit.inf INF RuleOverride = PE32 $(FSP_PACKAGE)/QemuVideo/QemuVideo.inf INF RuleOverride = PE32 IntelFsp2Pkg/FspNotifyPhase/FspNotifyPhasePeim.inf
不過實(shí)際上,當(dāng)調(diào)用FspSiliconInit()之后,代碼還是從Stage1B中的FSP退出的位置開始執(zhí)行的,即QemuFspPkg\FspmInit\FspmInit.c中的FspTempRamExitDone ()之后開始執(zhí)行代碼,其對(duì)應(yīng)的函數(shù)是ReportAndInstallNewFv (),也就是說,Stage2調(diào)用FSP-S之后,還是從PeiMain開始執(zhí)行,當(dāng)前述的函數(shù)安裝了FV之后,就又開始Dispatch,完成上述模塊的執(zhí)行。
Stage2還是調(diào)用FSP中的NotifyPhase(),對(duì)應(yīng)SBL中的代碼:
EFI_STATUS
EFIAPI
CallFspNotifyPhase (
FSP_INIT_PHASE Phase
)
{
FSP_INFO_HEADER *FspHeader;
FSP_NOTIFY_PHASE NotifyPhase;
NOTIFY_PHASE_PARAMS NotifyPhaseParams;
EFI_STATUS Status;
FspHeader = (FSP_INFO_HEADER *)(UINTN)(PcdGet32 (PcdFSPSBase) + FSP_INFO_HEADER_OFF);
ASSERT (FspHeader->Signature == FSP_INFO_HEADER_SIGNATURE);
ASSERT (FspHeader->ImageBase == PcdGet32 (PcdFSPSBase));
if (FspHeader->NotifyPhaseEntryOffset == 0) {
return EFI_UNSUPPORTED;
}
NotifyPhase = (FSP_NOTIFY_PHASE)(UINTN)(FspHeader->ImageBase +
FspHeader->NotifyPhaseEntryOffset);
NotifyPhaseParams.Phase = Phase;
DEBUG ((DEBUG_INFO, "Call FspNotifyPhase(%02X) ... ", Phase));
if (IS_X64) {
Status = Execute32BitCode ((UINTN)NotifyPhase, (UINTN)&NotifyPhaseParams, (UINTN)0, FALSE);
Status = (UINTN)LShiftU64 (Status & ((UINTN)MAX_INT32 + 1), 32) | (Status & MAX_INT32);
} else {
Status = NotifyPhase (&NotifyPhaseParams);
}
DEBUG ((DEBUG_INFO, "%r\n", Status));
return Status;
}
可以看到也只是一個(gè)簡單的跳轉(zhuǎn)。這里的參數(shù)FSP_INIT_PHASE對(duì)應(yīng)的值:
///
/// Enumeration of FSP_INIT_PHASE for NOTIFY_PHASE.
///
typedef enum {
///
/// This stage is notified when the bootloader completes the
/// PCI enumeration and the resource allocation for the
/// PCI devices is complete.
///
EnumInitPhaseAfterPciEnumeration = 0x20,
///
/// This stage is notified just before the bootloader hand-off
/// to the OS loader.
///
EnumInitPhaseReadyToBoot = 0x40,
///
/// This stage is notified just before the firmware/Preboot
/// environment transfers management of all system resources
/// to the OS or next level execution environment.
///
EnumInitPhaseEndOfFirmware = 0xF0
} FSP_INIT_PHASE;
標(biāo)明了調(diào)用NotifyPhase()的具體位置。
以上就是UEFI開發(fā)實(shí)戰(zhàn)SlimBootloader中調(diào)用FSP的詳細(xì)內(nèi)容,更多關(guān)于UEFI開發(fā)SlimBootloader調(diào)用FSP的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
UEFI開發(fā)實(shí)戰(zhàn)用戶交互界面使用說明UNI文件
這篇文章主要為大家介紹了UEFI開發(fā)實(shí)戰(zhàn)用戶交互界面使用說明UNI文件,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
匯編語言系列之匯編實(shí)現(xiàn)簡單數(shù)學(xué)運(yùn)算
這篇文章主要介紹了匯編語言系列之匯編實(shí)現(xiàn)簡單數(shù)學(xué)運(yùn)算的思路詳解,本文給大家列出了兩種算術(shù)運(yùn)算的代碼,設(shè)計(jì)思路給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-11-11
用匯編語言實(shí)現(xiàn)從1加到100的方法(1+2+...+100)
這篇文章主要介紹了用匯編語言實(shí)現(xiàn)從1加到100的方法(1+2+...+100),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01

