UEFI实战SlimBootloader中调用FSP

Posted jiangwei0512

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UEFI实战SlimBootloader中调用FSP相关的知识,希望对你有一定的参考价值。

综述

FSP的全称是Firmware Support Package。FSP有以下的特性:

  1. FSP提供了Intel重要组件(包括处理器、内存控制器、芯片组等)的初始化;
  2. FSP被编译成独立的二进制,并可以集成到Bootloader中,这里说的Bootloader可以是Slim Bootloader,coreboot,UEFI等等;
  3. FSP的优点有免费、方便集成、可减少开发时间,等等。

FSP中包含若干个部分,如下图所示:

按作用来分它包含三个大的组件,分别是:

  1. FSP-T:它主要用来初始化CACHE以及其它早期需要的初始化,对应提供给外部的接口是TempRamInit()
  2. FSP-M:它主要用来初始化内存以及其它需要的初始化,对应提供给外部的接口是FspMemoryInit()TempRamExit(),前者用于内存初始化,后者用于处理FSP-T中使用的CACHE内容;
  3. FPS-S:它主要是CPU和芯片组的初始化,对应提供给外部的接口是FspSiliconInit()NotifyPhase(),其中后者又会在不同的阶段调用,包括PCIE扫描之后,ReadyToBoot时和EndOfBootServices的时候;

按功能来区分,每个组件都包含头部、配置和API三个部分。头部是固定的,配置用于一些可控的定制化,API就是功能代码。Bootloader要操作FSP,就需要完成文件配置,接口调用等。

BootLoader调用FSP的整个流程如下图所示:

Bootloader中需要有相应的代码做上述的操作,以Slim Bootloader为例,有一个IntelFsp2Pkg用来处理FSP相关的内容。不过需要注意,这里的IntelFsp2Pkg并不提供FSP源代码的,只是提供了EDK与FSP之间的中间层,真正的用来初始化Intel组件的FSP的代码并没有开源,所以这里也拿不到,不过可以拿到用于QEMU的FSP源代码,后续使用的就是这个。

编译

代码主要是https://gitee.com/jiangwei0512/edk2-beni.git中的QemuFspPkg,它是用在QEMU上的FSP,它有源码可以下载,而其它Intel的FSP基本是不开源的,没有办法下载到,所以这里只能用QEMU的FSP作为示例。

QEMU对应的FSP通过BuildFsp.py进行编译得到,该脚本执行三个步骤:

  1. Prebuild
  2. Build
  3. PostBuild

Prebuild

Prebuild的流程如下:

构建BaseTools 构建FspHeader.inf 通过GenCfgOpt.py创建UPD-txt文件 通过BPDG.bat创建UPD-bin文件 通过GenCfgOpt.py创建UPD头文件 通过GenCfgOpt.py创建bsf文件 将UPD头文件放到指定目录
  1. 构建FspHeader.inf实际上就是创建FSP Header的头部,它是固定的格式,位于QemuFspPkg\\FspHeader\\FspHeader.aslc,内容如下:
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
;

里面的某些数据在之后还会被修改,通过这个头部就可以找到对应API的位置,从而进行调用。

  1. UPD txt文件是Build\\QemuFspPkg\\DEBUG_VS2019\\FV(根据编译工具的不同,对应的目录可能存在差异)下的如下内容:

txt文件名对应的是三个FSP组件的GUID(位于BuildFsp.py):

    FspGuid = 
        'FspTUpdGuid'       : '34686CA3-34F9-4901-B82A-BA630F0714C6',
        'FspMUpdGuid'       : '39A250DB-E465-4DD1-A2AC-E2BD3C0E2385',
        'FspSUpdGuid'       : 'CAE3605B-5B34-4C85-B3D7-27D54273C40F'
    

里面的内容主要是一些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]这个Section,其中的UnusedUpdSpaceX也是在dsc文件中通过PCD指定的偏移来确定的。

  1. UPD bin文件名也对应到前面提到的GUID,以39A250DB-E465-4DD1-A2AC-E2BD3C0E2385.bin为例:

bin文件跟txt文件中的PCD值是一一对应的。

  1. UPD头文件和bsf文件比较直观,不做详细说明,它们也是通过QemuFspPkg\\QemuFspPkg.dsc创建的。UPD头文件中还包含一个通用的头部结构体:
#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()

每个平台的UPD和bsf内容都是不同的,甚至同一个平台的不同版本也可能存在差异,不同的FSP-X对应不同的UPD,分别是FSPT_UPDFSPM_UPDFSPS_UPD,它们分别存放在FsptUpd.h、FspmUpd.h和FspsUpd.h中。以FSPM_UPD结构体为例:

/** 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;

其中的内容跟前面的UPD txt中的PCD一一对应。

上述的文件都在Build\\QemuFspPkg\\DEBUG_VS2019\\FV(根据编译工具的不同,对应的目录可能存在差异)创建,总的来说就是为了创建FSP的UPD配置文件和对应用在代码中的头文件,头文件会被拷贝到其它位置也是为了代码能够调用到。而UPD配置文件会通过二进制的方式包含到QemuFspPkg\\QemuFspPkg.fdf,下面是一个示例:

#
# 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

该过程仅仅是执行build操作而已,对应的代码:

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...')

可以看到执行对象是QemuFspPkg.dsc。完成这一步之后会生成FSP-M.Fv、FSP-S.Fv、FSP-T.Fv和QEMUFSP.fd。

到这里FSP二进制已经生成,就是QEMUFSP.fd,但是它不能直接使用,还需要后续操作。

PostBuild

这一步主要是通过PatchFv.py来修改前文生成的QEMUFSP.fd,Patch前后:

这里具体Patch了哪部分内容,需要先了解FSP二进制的组成部分,可以参考FSP二进制组成分析。这里Patch的大部分都是FSP Header中的内容,对应的默认初始化内容就是前面提到的QemuFspPkg\\FspHeader\\FspHeader.aslc,其结构体如下:

///
/// 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;

其中的ImageSizeImageBaseImageAttributeComponentAttributeCfgRegionOffsetCfgRegionSizeTempRamInitEntryOffsetFspMemoryInitEntryOffsetTempRamExitEntryOffsetFspSiliconInitEntryOffsetNotifyPhaseEntryOffset等都需要修改。因为每个FSP-X都有一个FSP_INFO_HEADER结构体,所以前提提到的XXXOffset会针对不同的FSP-X组件做对应的修改,比如FSP-T只需要TempRamInitEntryOffset

除了FSP Header的Patch,这里还有一个点被Patch了:

它们对应的是模块的入口(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二进制组成分析

二进制的组成如下:

FSP每个组件都是一个FV,所以都有一个FV Header(EFI_FIRMWARE_VOLUME_HEADER,位于MdePkg\\Include\\Pi\\PiFirmwareVolume.h),大小是0x48个字节,之后是一个FV Extended Header(EFI_FIRMWARE_VOLUME_EXT_HEADER,位于MdePkg\\Include\\Pi\\PiFirmwareVolume.h),之后才是FSP的内容,如下图所示:

FSP组件的第一个模块是FSP Header,对应二进制(Header的第一个成员是"FSPH",最后一个成员是0xFFFFFFFC)中:

这里可以看到里面有一些数据比较奇怪,都是0x12345678和0x00000000,这些都是占位符,并不是真正的有效数据,是通过FspHeader.aslc生成的,在后期这些数据会被Patch成有效的值。

FSP组件的第二个模块是UPD数据,它在Prebuild中生成,对应的数据(UPD数据的第一个成员是Signature(本例中是QEMUPD_T),最后一个成员是0x55AA):

再之后是通用的模块。以QEMU中的FSP对应的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需要放到前者指定的目录,对于QEMU来说对应的是Silicon\\QemuSocPkg\\FspBin,同时FSP对应的UPD头文件也需要放到指定目录。之后执行Slim Bootloader的各个阶段都会调用FSP的API接口,这里一一说明。

Stage1A

Stage1A阶段会执行FSP中的FspTempRamInit()接口,由于是执行阶段的早期,这里只有汇编部分的代码,具体的位置在BootloaderCorePkg\\Stage1A\\Ia32\\SecEntry.nasm,对应代码:

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_OFFSETPcdFSPTBase的值是:

  gPlatformModuleTokenSpaceGuid.PcdFSPTBase               | $(FSP_T_BASE)

FSP_T_BASE表示的是FSP-T.bin的开始位置,094h在前面也已经介绍过,其前面的内容是FV Header,该地址开始是FSP Header,而FSP_HEADER_TEMPRAMINIT_OFFSET是FSP Header的偏移,该位置对应成员是TempRamInitEntryOffset,到这里就对应起来了,FspTempRamInit()即是该位置的值。

不过对于FSP_T_BASE的值,它是FSP-T放到系统内存中位置的地址,可以在BootloaderCorePkg\\Platform.dsc中找到:

  DEFINE FSP_T_BASE               = 0xFFFF0000

这个值也跟SBL二进制产生关系:

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  UEFI实战SlimBootloader使用

UEFI实战SlimBootloader集成UEFI Payload

UEFI实战SlimBootloader集成UEFI Payload

UEFI实战SlimBootloader中的构建脚本BuildLoader.py

UEFI实战SlimBootloader定制化

UEFI实战SlimBootloader定制化