【UEFI实战】SlimBootloader定制化

综述

SBL的一个特性是Customizable,它分为构建前和构建后的定制化。

SBL有两种配置数据,分别是内部配置数据和外部配置数据。内部配置数据是代码写死的默认数据,当获取不到外部数据的时候就使用这个默认数据;外部配置数据通过预定义文件和配置工具生成。

SBL中的预定义文件和配置工具包括:

  • YAML文件:它定义配置;
  • DLT(delta,表示的其实是变量数据)文件:Override数据,用来覆盖YAML文件中的配置;
  • 配置操作工具:通常是Python脚本。

YAML文件

YAML文件中包含了内存、Silicon、GPIO、启动策略、安全配置等等。

YAML文件通常包含在特定单板目录下,比如ApolloLake平台:

【UEFI实战】SlimBootloader定制化

还有一些通用的YAML文件在Platform\CommonBoardPkg\CfgData目录中:

【UEFI实战】SlimBootloader定制化

其中平台需要的配置文件入口是CfgDataDef.yaml,其它的配置文件都作为它的子文件包含在CfgDataDef.yaml中。比如Platform\ApollolakeBoardPkg\CfgData\CfgDataDef.yaml:

## @file
#
#  Slim Bootloader CFGDATA Default File.
#
#  Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
#  SPDX-License-Identifier: BSD-2-Clause-Patent
#
##

variable:
  COND_GPIO_SKIP                 : ($GPIO_CFG_DATA.$(1)_Half0.GpioSkip == 0)
  COND_GPIO_PID_ENABLE           : ($PID_GPIO_CFG_DATA.$(1).Enable==1) and ($PLATFORMID_CFG_DATA.PlatformId==0)
  COND_PCIE_RP_PWR_PIN_SKIP      : ($PCIE_RP_CFG_DATA.PcieRpPower$(1).Skip == 0)
  COND_PCIE_RP_RST_PIN_SKIP      : ($PCIE_RP_CFG_DATA.PcieRpReset$(1).Skip == 0)
  COND_PCIE_RP_EN                : ($PCIE_RP_CFG_DATA.PcieRpFeatures$(1).En == 1)
  COND_PCIE_RP_CLK_REQ_SUP       : (($PCIE_RP_CFG_DATA.PcieRpFeatures$(1).ClkReqSup == 1) and ($PCIE_RP_CFG_DATA.PcieRpFeatures$(1).En == 1))
  COND_HDA_EN                    : ($HDA_CFG_DATA.HdaEnable == 1)
  COND_HDA_DSP_EN                : (($HDA_CFG_DATA.HdaEnable == 1) and ($HDA_CFG_DATA.DspEnable == 1))

template:

  - !include Template_CfgData.yaml

configs:
  - $ACTION      :
      page         : PLT::"Platform", MEM::"Memory Settings", SIL::"Silicon Settings", GEN::"General Settings", GIO::"Gpio Settings", OS::"OS Boot Options"
  - Signature    :
      length       : 0x04
      value        : {'CFGD'}
  - HeaderLength :
      length       : 0x01
      value        : 0x10
  - Reserved     :
      length       : 0x03
      value        : {0,0,0}
  - UsedLength   :
      length       : 0x04
      value        : _LENGTH_
  - TotalLength  :
      length       : 0x04
      value        : 0x2000

  - !include Platform/CommonBoardPkg/CfgData/CfgData_Platform.yaml

  - $ACTION      :
      page         : IOCUART:PLT:"IOC Uart Settings"
  - $ACTION      :
      page         : IOCUART
  - IOC_UART_CFG_DATA :
    - !expand { CFGHDR_TMPL : [ IOC_UART_CFG_DATA, 0x120, 0, 0 ] }
    - DeviceIndex  :
        name         : Device Index
        type         : Combo
        option       : 0:UART0, 1:UART1, 2:UART2, 3:UART3, 0xF:Disable
        help         : >
                       UART device index for IOC interface (0..3 or Disable)
        length       : 0x01
        value        : 0xF
    - BaudRate     :
        name         : Baud Rate
        type         : Combo
        option       : 0:9600, 1:19200, 2:38400, 3:57600, 4:115200, 5:921600, 6:1.5M
        help         : >
                       UART Baud Rate
        length       : 0x01
        value        : 0
    - Retries      :
        name         : Retries
        type         : EditNum, HEX, (0x00,0xFF)
        help         : >
                       specify retry count
        length       : 0x01
        value        : 0
    - TimeoutInitial :
        name         : TimeoutInitial
        type         : EditNum, HEX, (0x00,0xFF)
        help         : >
                       initial/setup time-out (in milliseconds)
        length       : 0x01
        value        : 0
    - TimeoutXmit  :
        name         : TimeoutXmit
        type         : EditNum, HEX, (0x00,0xFF)
        help         : >
                       transmission time-out
        length       : 0x01
        value        : 0
    - Rsvd         :
        length       : 0x03
        value        : 0

  - $ACTION      :
      page         : PSEL:PLT:"Payload Selection GPIO"
  - $ACTION      :
      page         : PSEL
  - PLATFORM_CFG_DATA :
    - !expand { CFGHDR_TMPL : [ PLATFORM_CFG_DATA, 0x280, 0, 0 ] }
    - PayloadSelGpio :
      - $STRUCT      :
          name         : GPIO pin for switching payload
          struct       : PAYLOAD_SEL_GPIO_PIN
          length       : 0x04
          value        : 0x000000c5
      - PadInfo      :
          name         : Pin Number
          type         : Combo
          option       : !include CfgData_GpioPinOption.yaml
          condition    : ($PLATFORM_CFG_DATA.PayloadSelGpio.Enable > 0)
          help         : >
                         Specify GPIO Pin Number
          length       : 24b
      - Rsvd1        :
          name         : Reserved
          type         : Reserved
          length       : 7b
      - Enable       :
          name         : Payload Selection Pin Enable
          type         : Combo
          option       : $EN_DIS
          help         : >
                         Enable/Disable this pin for payload selection.
          order        : 0000.0000
          length       : 1b

  - !include CfgData_Memory.yaml
  - !include CfgData_Silicon.yaml
  - !include CfgData_Usb.yaml
  - !include CfgData_Gpio.yaml
  - !include Platform/CommonBoardPkg/CfgData/CfgData_Common.yaml
  - !include CfgData_BootOption.yaml
  - !include CfgData_PidGpioPins.yaml
  - !include CfgData_PcieRp.yaml
  - !include CfgData_GpuConfig.yaml
  - !include CfgData_Features.yaml
  - !include CfgData_DeviceEnable.yaml
  - !include CfgData_Hda.yaml
  - !include CfgData_CapsuleInformation.yaml

可以看到包含很多的include命令,指定了内存、Silicon、USB、GPIO等等YAML文件。

DLT文件

DLT文件中的数据是用来覆盖YAML文件中的配置的。

DLT文件包含一个PlatformId,比如Platform\ApollolakeBoardPkg\CfgData\CfgData_Ext_Up2.dlt中:

#
# Delta configuration values for platform ID 0x000E
#
PLATFORMID_CFG_DATA.PlatformId           | 0x000E

一个平台中的DLT文件可以有多个,每个都对应一个PlatformId,并最终和一个特定的单板匹配。如果PlatformId等于0,表示对于所有的单板都适用,相当于改了YAML文件本身。这些DLT文件对应到BoardConfig.py中:

self._CFGDATA_EXT_FILE = ['CfgData_Ext_Gpmrb.dlt', 'CfgData_Ext_Up2.dlt','CfgData_Ext_OxbHill.dlt','CfgData_Ext_MB3.dlt','CfgData_Ext_JuniperHill.dlt']

如果需要增加单板,通常不会直接修改YAML文件,而是增加DLT文件并修改配置来覆盖原有配置,并将DLT文件增加到上述的列表中。

顺便再说明下PlatformId,Intel的CRB单板中指定了若干个GPIO管脚,通过硬件配置死的方式来指定特定单板,这样代码中就可以通过读取GPIO的值来确定PlatformId。对应ApolloLake平台,相关的代码可以在Platform\ApollolakeBoardPkg\Library\Stage1BBoardInitLib\Stage1BBoardInitLib.c中看到:

/**
  Detect board and configure PlatformID.

  @retval  EFI_SUCCESS     Configuration data was loaded successfully.
  @retval  Others          Failed to get configuration data blob.
**/
EFI_STATUS
EFIAPI
PlatformIdInitialize (
  IN  VOID
  )
{
  UINT16     PlatformId;

  PlatformId = (UINT16)GetBoardIdFromGpioPins ();

  if (PlatformId != 0xFF) {
    PlatformId += 0x10; // Customer board identified, assign Platform Ids from 16 to 31
  } else {
    PlatformId = (UINT16)GetEmbeddedBoardId ();
    //Platform ID from GPIOs are read as 0 for Juniper hills due to GPIO pins
    //on the board reduced from 4 to 3 (hardware change) hence translating here
    //in the code.
    if (PlatformId == 0){
        DEBUG ((DEBUG_INFO, "GPIO returned platformID 0 translating to 8(JNH)\n"));
        PlatformId = 0x8;
    }
    if ((PlatformId != PLATFORM_ID_OXH) && (PlatformId != PLATFORM_ID_LFH) && (PlatformId != PLATFORM_ID_JNH)) {
      PlatformId = (UINT16)GetIVIBoardId ();
      if (PlatformId != PLATFORM_ID_GPMRB) {
        DEBUG ((DEBUG_ERROR, "BOARD NOT SUPPORTED: 0x%04X\n", PlatformId));
        CpuDeadLoop ();
      }
    }
  }

  SetPlatformId (PlatformId);
  return EFI_SUCCESS;
}

配置工具

配置工具处理YAML和DLT文件的方式大致就是下面这个样子(注意二进制本身同时可以作为输入和输出):

【UEFI实战】SlimBootloader定制化

所以本节就介绍配置工具,它位于BootloaderCorePkg/Tools目录下。最重要的是ConfigEditor.py,它是一个图形工具,打开之后如下所示:

【UEFI实战】SlimBootloader定制化

点击“File”,第一次只能选择“Open Config YAML file…“:

【UEFI实战】SlimBootloader定制化

这里可以选择前面提到的CfgDataDef.yaml,即基础配置文件。在这之后就可以选择“Load Config Changes from Delta File”,这样新加入的DLT文件会覆盖原本的显示,之后可以继续修改,并最终保存修改之后的数据,这样的数据可以是只包含修改部分的DLT文件,也可以是覆盖所有配置的DLT文件,或者直接生成二进制文件。

除了ConfigEditor.py,还有配置工具是用来将YAML文件转换成头文件Platform\XXXPkg\Include\ConfigDataStruct.h的,它会被包含到SBL代码中,通过代码来最终获取配置并使用。

最终配置工具的使用流程就像如下图所示的一样:

【UEFI实战】SlimBootloader定制化

总的来说,SBL的配置就是YAML文件定义基础配置,DLT文件根据实际单板修改配置,最终生成二进制配置文件,这个二进制配置文件会被放到SBL二进制*后续获取。在SBL代码中,会判断当前的PlatformId,并加载需要的二进制配置文件,来完成最终的配置。

最后,即使生成了SBL二进制,也还是可以通过工具修改其参数,这就是构建后自定义。

上一篇:Kubernetes 云平台部署 WordPress流程


下一篇:k8s-YAML详解