顯示具有 EDK II 標籤的文章。 顯示所有文章
顯示具有 EDK II 標籤的文章。 顯示所有文章

2019年2月23日 星期六

UEFI BIOS Editor

This utility can parse the standard UEFI BIOS image to extract the firmware volumes, firmware files, sections, variables IFR, etc.

Update History

Jul 19, 2026
  • Moved to .NET 8. The .NET 8 Desktop Runtime is required now.
  • Added Setup browser: browse the Setup screens interactively from the HII data, with dynamic visibility and values joined to their variables.
  • Added Flash Map: whole-flash usage (per-FV used/free/%) reverse-engineered from the image.
  • FFS editing: Replace accepts a larger file now (the FV is rebuilt automatically), and Insert FFS / Remove FFS / Export Decompressed were added.
  • The saved image is re-parsed to verify itself after Save As.
  • AMD support improved: PSP / BIOS firmware directories, AMD microcode, and zlib PEI FVs are parsed now.
  • Intel OSSE region is parsed (layout table, BPDT boot partitions, $CPD modules), in the firmware tree and in Flash Map.
  • The toolbar search runs across all tabs now and reports the matches per tab.
  • Added Compare ROMs and Compare Setup to diff two opened images.
  • Added Data Inspector (Ctrl+I).
  • Setup settings and the variable list can be exported.
  • High-DPI support, unsaved-change warning, and other UI improvements.
  • Fixed some bugs.
Apr 16, 2026
  • Fixed issues.
  • Improved ME parser.
  • Support Boot Guard.
  • Support AMD (testing).
Oct 05, 2022
  • Fixed an exception for the old PCD version.
  • Improved the code to find FIT and Microcode.
Apr 16, 2022
  • Updated the EfiCompress code to fix the exception when when decompressing some BIOS.
Feb 26, 2020
  • Fixed some bugs.
Feb 23, 2020
  • Improved the parsing speed.
  • DEPEX can be parsed.
Feb 20, 2020
  • Update UI.
  • Fixed some bugs.
  • Search and Find are available in VFR.
Feb 10, 2020
  • Fixed an UNI exception for some BIOS.
Jan 30, 2020
  • Improved EFI Variable.
  • Fixed some bugs.
Jan 19, 2020
  • Fixed some bugs.
Oct 24, 2019
  • Fixed some bugs.
Aug 07, 2019
  • Added language support.
Aug 02, 2019
  • Added Variable compare function.
  • The selected image name will be shown on the title.
  • Fixed some bugs.
Feb 22, 2019
  • Fixed the problem that IFR couldn't be found.
  • Fixed some bugs.
Feb 21, 2019
  • Added BootOption editor.
  • Added DevicePath converter.
  • Variable list is sortable now.
  • Fixed some bugs.
May 18, 2018
  • Changed FFS2 ExtendedSize to UINT64.
  • Fixed some bugs.
Dec 19, 2017
  • Fixed some BIOS couldn't find IFR data.
Nov 20, 2017
  • Variable compatibility enhanced.
Nov 05, 2017
  • IFR interpreter improved.
  • Convert UNI to text.
  • Convert IFR from text.
  • Convert IFR from binary.
Oct 29, 2017
  • Fixed some bugs.
  • IFR interpreter improved.
  • Firmware Contents Signed supported.
May 11, 2017
  • Parse the default variable value. 
September 05, 2016
  • Implemented VFR browser. It now can view the contents of VFR and UNI in the VFR page. 
  • EFI Compress and Tiano Compress supported.
June 28, 2016
  • The first version released.







Download:
UefiBiosEditor.7z

2014年10月22日 星期三

PCD (Platform Configuration Database) - Part 3 (DXE)

將接續上一篇,繼續探討DXE部分。

AutoGen.h
typedef struct {
  UINT32             PcdSetupConOutColumn_d3705011_bc19_4af7_be16_f68030378c15_VariableDefault_0;
  UINT32             PcdSetupConOutRow_d3705011_bc19_4af7_be16_f68030378c15_VariableDefault_0;

  DYNAMICEX_MAPPING  ExMapTable[DXE_EXMAPPING_TABLE_SIZE];
  UINT32             LocalTokenNumberTable[DXE_LOCAL_TOKEN_NUMBER_TABLE_SIZE];
  GUID               GuidTable[DXE_GUID_TABLE_SIZE];
  STRING_HEAD        PcdWinNtMemorySize_0d79a645_1d91_40a6_a81f_61e6982b32b4[1];
  STRING_HEAD        PcdWinNtPhysicalDisk_0d79a645_1d91_40a6_a81f_61e6982b32b4[1];
  STRING_HEAD        PcdWinNtVirtualDisk_0d79a645_1d91_40a6_a81f_61e6982b32b4[1];
  STRING_HEAD        PcdWinNtFileSystem_0d79a645_1d91_40a6_a81f_61e6982b32b4[1];
  STRING_HEAD        PcdWinNtSerialPort_0d79a645_1d91_40a6_a81f_61e6982b32b4[1];
  STRING_HEAD        PcdWinNtGop_0d79a645_1d91_40a6_a81f_61e6982b32b4[1];
  STRING_HEAD        PcdWinNtUga_0d79a645_1d91_40a6_a81f_61e6982b32b4[1];
  STRING_HEAD        PcdWinNtConsole_0d79a645_1d91_40a6_a81f_61e6982b32b4[1];

  VARIABLE_HEAD      PcdHardwareErrorRecordLevel_d3705011_bc19_4af7_be16_f68030378c15_Variable_Header[1];
  VARIABLE_HEAD      PcdPlatformBootTimeOut_d3705011_bc19_4af7_be16_f68030378c15_Variable_Header[1];
  VARIABLE_HEAD      PcdSetupConOutColumn_d3705011_bc19_4af7_be16_f68030378c15_Variable_Header[1];
  VARIABLE_HEAD      PcdSetupConOutRow_d3705011_bc19_4af7_be16_f68030378c15_Variable_Header[1];

  UINT8              StringTable[12]; /* PcdWinNtMemorySize_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  UINT8              StringTable_1[100]; /* PcdWinNtPhysicalDisk_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  UINT8              StringTable_2[26]; /* PcdWinNtVirtualDisk_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  UINT8              StringTable_3[106]; /* PcdWinNtFileSystem_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  UINT8              StringTable_4[20]; /* PcdWinNtSerialPort_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  UINT8              StringTable_5[52]; /* PcdWinNtGop_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  UINT8              StringTable_6[52]; /* PcdWinNtUga_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  UINT8              StringTable_7[52]; /* PcdWinNtConsole_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  UINT8              StringTable_8[32]; /* PcdHardwareErrorRecordLevel_d3705011_bc19_4af7_be16_f68030378c15 */
  UINT8              StringTable_9[16]; /* PcdPlatformBootTimeOut_d3705011_bc19_4af7_be16_f68030378c15 */
  UINT8              StringTable_10[38]; /* PcdSetupConOutColumn_d3705011_bc19_4af7_be16_f68030378c15 */

  SIZE_INFO          SizeTable[DXE_SIZE_TABLE_SIZE];

  UINT16             PcdHardwareErrorRecordLevel_d3705011_bc19_4af7_be16_f68030378c15_VariableDefault_0;
  UINT16             PcdPlatformBootTimeOut_d3705011_bc19_4af7_be16_f68030378c15_VariableDefault_0;

  BOOLEAN            PcdBootState_d3705011_bc19_4af7_be16_f68030378c15[1];

  UINT8              SkuIdTable[DXE_SKUID_TABLE_SIZE];

} DXE_PCD_DATABASE_INIT;

typedef struct {
  UINT8  dummy; /* PCD_DATABASE_UNINIT is emptry */
} DXE_PCD_DATABASE_UNINIT;

typedef struct {
  DXE_PCD_DATABASE_INIT    Init;
  DXE_PCD_DATABASE_UNINIT  Uninit;
} DXE_PCD_DATABASE;

typedef struct {
  PEI_PCD_DATABASE PeiDb;
  DXE_PCD_DATABASE DxeDb;
} PCD_DATABASE;

AutoGen.c
DXE_PCD_DATABASE_INIT gDXEPcdDbInit = {
  80U, /* PcdSetupConOutColumn_d3705011_bc19_4af7_be16_f68030378c15_VariableDefault_0 */
  25U, /* PcdSetupConOutRow_d3705011_bc19_4af7_be16_f68030378c15_VariableDefault_0 */

  /* VPD */

  /* ExMapTable */
  {
    { 0U, 0U, 0U },
  },
  /* LocalTokenNumberTable */
  {
    offsetof(DXE_PCD_DATABASE, Init.PcdWinNtMemorySize_0d79a645_1d91_40a6_a81f_61e6982b32b4) | PCD_TYPE_STRING,
    offsetof(DXE_PCD_DATABASE, Init.PcdWinNtPhysicalDisk_0d79a645_1d91_40a6_a81f_61e6982b32b4) | PCD_TYPE_STRING,
    offsetof(DXE_PCD_DATABASE, Init.PcdWinNtVirtualDisk_0d79a645_1d91_40a6_a81f_61e6982b32b4) | PCD_TYPE_STRING,
    offsetof(DXE_PCD_DATABASE, Init.PcdWinNtFileSystem_0d79a645_1d91_40a6_a81f_61e6982b32b4) | PCD_TYPE_STRING,
    offsetof(DXE_PCD_DATABASE, Init.PcdWinNtSerialPort_0d79a645_1d91_40a6_a81f_61e6982b32b4) | PCD_TYPE_STRING,
    offsetof(DXE_PCD_DATABASE, Init.PcdWinNtGop_0d79a645_1d91_40a6_a81f_61e6982b32b4) | PCD_TYPE_STRING,
    offsetof(DXE_PCD_DATABASE, Init.PcdWinNtUga_0d79a645_1d91_40a6_a81f_61e6982b32b4) | PCD_TYPE_STRING,
    offsetof(DXE_PCD_DATABASE, Init.PcdWinNtConsole_0d79a645_1d91_40a6_a81f_61e6982b32b4) | PCD_TYPE_STRING,
    offsetof(DXE_PCD_DATABASE, Init.PcdHardwareErrorRecordLevel_d3705011_bc19_4af7_be16_f68030378c15_Variable_Header) | PCD_TYPE_HII | PCD_DATUM_TYPE_UINT16,
    offsetof(DXE_PCD_DATABASE, Init.PcdPlatformBootTimeOut_d3705011_bc19_4af7_be16_f68030378c15_Variable_Header) | PCD_TYPE_HII | PCD_DATUM_TYPE_UINT16,
    offsetof(DXE_PCD_DATABASE, Init.PcdSetupConOutColumn_d3705011_bc19_4af7_be16_f68030378c15_Variable_Header) | PCD_DATUM_TYPE_UINT32 | PCD_TYPE_HII,
    offsetof(DXE_PCD_DATABASE, Init.PcdSetupConOutRow_d3705011_bc19_4af7_be16_f68030378c15_Variable_Header) | PCD_DATUM_TYPE_UINT32 | PCD_TYPE_HII,
    offsetof(DXE_PCD_DATABASE, Init.PcdBootState_d3705011_bc19_4af7_be16_f68030378c15) | PCD_DATUM_TYPE_UINT8 | PCD_TYPE_DATA,
  },
  /* GuidTable */
  {
    { 0x8BE4DF61, 0x93CA, 0x11D2, { 0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C }},
  },
  { 0U }, /* PcdWinNtMemorySize_0d79a645_1d91_40a6_a81f_61e6982b32b4[1] */
  { 12U }, /* PcdWinNtPhysicalDisk_0d79a645_1d91_40a6_a81f_61e6982b32b4[1] */
  { 112U }, /* PcdWinNtVirtualDisk_0d79a645_1d91_40a6_a81f_61e6982b32b4[1] */
  { 138U }, /* PcdWinNtFileSystem_0d79a645_1d91_40a6_a81f_61e6982b32b4[1] */
  { 244U }, /* PcdWinNtSerialPort_0d79a645_1d91_40a6_a81f_61e6982b32b4[1] */
  { 264U }, /* PcdWinNtGop_0d79a645_1d91_40a6_a81f_61e6982b32b4[1] */
  { 316U }, /* PcdWinNtUga_0d79a645_1d91_40a6_a81f_61e6982b32b4[1] */
  { 368U }, /* PcdWinNtConsole_0d79a645_1d91_40a6_a81f_61e6982b32b4[1] */

  /* PcdHardwareErrorRecordLevel_d3705011_bc19_4af7_be16_f68030378c15_Variable_Header[1] */
  {
    { 420U, offsetof(DXE_PCD_DATABASE, Init.PcdHardwareErrorRecordLevel_d3705011_bc19_4af7_be16_f68030378c15_VariableDefault_0), 0U, 0x0U }
  },
  /* PcdPlatformBootTimeOut_d3705011_bc19_4af7_be16_f68030378c15_Variable_Header[1] */
  {
    { 452U, offsetof(DXE_PCD_DATABASE, Init.PcdPlatformBootTimeOut_d3705011_bc19_4af7_be16_f68030378c15_VariableDefault_0), 0U, 0x0U }
  },
  /* PcdSetupConOutColumn_d3705011_bc19_4af7_be16_f68030378c15_Variable_Header[1] */
  {
    { 468U, offsetof(DXE_PCD_DATABASE, Init.PcdSetupConOutColumn_d3705011_bc19_4af7_be16_f68030378c15_VariableDefault_0), 0U, 0x0U }
  },
  /* PcdSetupConOutRow_d3705011_bc19_4af7_be16_f68030378c15_Variable_Header[1] */
  {
    { 468U, offsetof(DXE_PCD_DATABASE, Init.PcdSetupConOutRow_d3705011_bc19_4af7_be16_f68030378c15_VariableDefault_0), 0U, 0x4U }
  },

 /* StringTable */
  {0x36, 0x00, 0x34, 0x00, 0x21, 0x00, 0x36, 0x00, 0x34, 0x00, 0x00, 0x00}, /* PcdWinNtMemorySize_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  {0x61, 0x00, 0x3a, 0x00, 0x52, 0x00, 0x57, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x38, 0x00, 0x38, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x35, 0x00, 0x31, 0x00, 0x32, 0x00, 0x21, 0x00, 0x64, 0x00, 0x3a, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x3b, 0x00, 0x33, 0x00, 0x30, 0x00, 0x37, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x30, 0x00, 0x34, 0x00, 0x38, 0x00, 0x21, 0x00, 0x6a, 0x00, 0x3a, 0x00, 0x52, 0x00, 0x57, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x36, 0x00, 0x32, 0x00, 0x31, 0x00, 0x34, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x35, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0x00}, /* PcdWinNtPhysicalDisk_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  {0x46, 0x00, 0x57, 0x00, 0x3b, 0x00, 0x34, 0x00, 0x30, 0x00, 0x39, 0x00, 0x36, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x35, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0x00}, /* PcdWinNtVirtualDisk_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  {0x2e, 0x00, 0x21, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x5c, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x5c, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x64, 0x00, 0x75, 0x00, 0x50, 0x00, 0x6b, 0x00, 0x67, 0x00, 0x5c, 0x00, 0x44, 0x00, 0x45, 0x00, 0x42, 0x00, 0x55, 0x00, 0x47, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x53, 0x00, 0x32, 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x78, 0x00, 0x38, 0x00, 0x36, 0x00, 0x5c, 0x00, 0x49, 0x00, 0x41, 0x00, 0x33, 0x00, 0x32, 0x00, 0x00, 0x00}, /* PcdWinNtFileSystem_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  {0x43, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x31, 0x00, 0x21, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x32, 0x00, 0x00, 0x00}, /* PcdWinNtSerialPort_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  {0x55, 0x00, 0x47, 0x00, 0x41, 0x00, 0x20, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x00}, /* PcdWinNtGop_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  {0x55, 0x00, 0x47, 0x00, 0x41, 0x00, 0x20, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x00}, /* PcdWinNtUga_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  {0x42, 0x00, 0x75, 0x00, 0x73, 0x00, 0x20, 0x00, 0x44, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x00, 0x00}, /* PcdWinNtConsole_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  {0x48, 0x00, 0x77, 0x00, 0x45, 0x00, 0x72, 0x00, 0x72, 0x00, 0x52, 0x00, 0x65, 0x00, 0x63, 0x00, 0x53, 0x00, 0x75, 0x00, 0x70, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x74, 0x00, 0x00, 0x00}, /* PcdHardwareErrorRecordLevel_d3705011_bc19_4af7_be16_f68030378c15 */
  {0x54, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00}, /* PcdPlatformBootTimeOut_d3705011_bc19_4af7_be16_f68030378c15 */
  {0x53, 0x00, 0x65, 0x00, 0x74, 0x00, 0x75, 0x00, 0x70, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x69, 0x00, 0x67, 0x00, 0x00, 0x00}, /* PcdSetupConOutColumn_d3705011_bc19_4af7_be16_f68030378c15 */

  /* SizeTable */
  {
    12U, 12U, /* PcdWinNtMemorySize_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
    100U, 94U, /* PcdWinNtPhysicalDisk_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
    26U, 26U, /* PcdWinNtVirtualDisk_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
    106U, 78U, /* PcdWinNtFileSystem_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
    20U, 20U, /* PcdWinNtSerialPort_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
    52U, 26U, /* PcdWinNtGop_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
    52U, 26U, /* PcdWinNtUga_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
    52U, 52U, /* PcdWinNtConsole_0d79a645_1d91_40a6_a81f_61e6982b32b4 */
  },

  1U, /* PcdHardwareErrorRecordLevel_d3705011_bc19_4af7_be16_f68030378c15_VariableDefault_0 */
  10U, /* PcdPlatformBootTimeOut_d3705011_bc19_4af7_be16_f68030378c15_VariableDefault_0 */

  { 1U }, /*  PcdBootState_d3705011_bc19_4af7_be16_f68030378c15[1] */

  /* SkuIdTable */
  { 0U,  }, 
};

以上是在Compile過程中,Build Tool為PCD DXE所生成的檔案,跟PEIM的很相似,但多了一些沒看過的PCD。。
上面有個PCD_DATABASE,裡面分了PeiDb與DxeDb,在一開始的EntryPoint中會呼叫BuildPcdDxeDataBase,並把PEI_PCD_DATABASE從HOB複製過來。

PCD DXE的GetWorkerSetWorker

GetWorker:


VOID *
GetWorker (
  IN UINTN               TokenNumber,
  IN UINTN               GetSize
  )

在一開始會做一些前置處理,判斷傳進來的TokenNumber判斷是PEI的PCD還是DXE的PCD。
然後把對應的LocalTokenNumber、LocalTokenNumber、StringTable、PcdDb的Pointer另取出來。
IsPeiDb = (BOOLEAN) ((TokenNumber + 1 < PEI_LOCAL_TOKEN_NUMBER + 1) ? TRUE : FALSE);

透過Mask單獨取得Offset。
Offset = LocalTokenNumber & PCD_DATABASE_OFFSET_MASK;

接下來會依照各種不同PCD Type做不同的處理:
PCD_TYPE_VPD:
傳回VPD (Vital Product Data)中特定的Offset的資料。
VPD_HEAD *VpdHead;
VpdHead = (VPD_HEAD *) ((UINT8 *)PeiPcdDb + Offset);
return (VOID *) (UINTN) (PcdGet32 (PcdVpdBaseAddress) + VpdHead->Offset);


PCD_TYPE_HII|PCD_TYPE_STRING:
PCD_TYPE_HII:
透過Offset取得Variable Head,裡面有著GUID與Name的Index。
VariableHead = (VARIABLE_HEAD *) ((UINT8 *)PeiPcdDb + Offset);

Guid = &(PeiPcdDb->Init.GuidTable[VariableHead->GuidTableIndex]);
Name = (UINT16*)&StringTable[VariableHead->StringIndex];


會先判斷是否為PCD_TYPE_HII|PCD_TYPE_STRING或PCD_TYPE_HII,再去做不同的處理,差別只是在於在GetVariable失敗時,
PCD_TYPE_HII|PCD_TYPE_STRING會於StringTable取得預設的資料(字串),而PCD_TYPE_HII會直接以DefaultValueOffset去取得對應的預設值。
if ((LocalTokenNumber & PCD_TYPE_ALL_SET) == (PCD_TYPE_HII|PCD_TYPE_STRING)) {
  StringTableIdx = *(STRING_HEAD*)((UINT8 *) PcdDb + VariableHead->DefaultValueOffset);  
  VaraiableDefaultBuffer = (VOID *) (StringTable + StringTableIdx);
  ...
} else {
  VaraiableDefaultBuffer = (UINT8 *) PcdDb + VariableHead->DefaultValueOffset;
  ...
}


PCD_TYPE_STRING:
先取得該PCD的STRING_HEAD (StringTableIdx (UINT32)),裡面存放著對應StringTable的Offset。
StringTableIdx = * (STRING_HEAD*) ((UINT8 *) PeiPcdDb + Offset);
return (VOID *) (&StringTable[StringTableIdx]);


PCD_TYPE_DATA:
直接透過Offset取得Data。
return (VOID *) ((UINT8 *)PeiPcdDb + Offset);

SetWorker:


EFI_STATUS
SetWorker (
  IN          UINTN               TokenNumber,
  IN          VOID                *Data,
  IN OUT      UINTN               *Size,
  IN          BOOLEAN             PtrType
  )

透過Token Number取得該PCD的LocalTokenNumber (Offset、PCD Type、Data Type)。
LocalTokenNumber = PeiPcdDb->Init.LocalTokenNumberTable[TokenNumber];

會先判斷Data Type是不是Pointer,通常像String之類的,會被歸類為Pointer。
要注意到在Build Tool會在PCD Database中,先宣告好固定長度的陣列來存放String等其它Data。
如果PtrType為True,而傳入的Size又大於原先預留好的最大值的話,就會造成問題,這邊就先擋掉並回傳MaxSize。
if (PtrType) {
  //
  // Get MaxSize first, then check new size with max buffer size.
  //
  GetPtrTypeSize (TokenNumber, &MaxSize, PeiPcdDb);
  if (*Size > MaxSize) {
    *Size = MaxSize;
    return EFI_INVALID_PARAMETER;
  }
} else {
  if (*Size != PeiPcdGetSize (TokenNumber + 1)) {
    return EFI_INVALID_PARAMETER;
  }
}


當某個PCD被修改時會去呼叫,為該PCD註冊Notify的Function。
可以透過PCD Library的LibPcdCallbackOnSet來進行註冊會比較方便。
if ((TokenNumber + 1 < PEI_NEX_TOKEN_NUMBER + 1) || (TokenNumber + 1 >= PEI_LOCAL_TOKEN_NUMBER + 1 || TokenNumber + 1 < (PEI_LOCAL_TOKEN_NUMBER + DXE_NEX_TOKEN_NUMBER + 1))) {
  InvokeCallbackOnSet (0, NULL, TokenNumber + 1, Data, *Size);
}


跟GetWorker一樣會判斷傳進來的TokenNumber判斷是PEI的PCD還是DXE的PCD。
然後把對應的LocalTokenNumber、LocalTokenNumber、StringTable、PcdDb的Pointer另取出來。

透過Mask單獨取得Offset,並取得Data。
Offset = LocalTokenNumber & PCD_DATABASE_OFFSET_MASK;
InternalData    = (VOID *) ((UINT8 *) PeiPcdDb + Offset);


接下來會依照各種不同PCD Type做不同的處理:
PCD_TYPE_VPD:
可能是上述幾個Type都不支援在PEI修改NVS,所以這邊就直接return回EFI_INVALID_PARAMETER。

PCD_TYPE_STRING:
傳入的資料為一串Array,資料長度可能會變動,所以先更改Current Size。
SizeTable中有兩個欄位,一個是Max Size,一個是Current Size。
之後再透過CopyMem,將傳入的Data Copy到StringTable中。
if (SetPtrTypeSize (TokenNumber, Size, PeiPcdDb)) {
  CopyMem (StringTable + *((STRING_HEAD *)InternalData), Data, *Size);
  return EFI_SUCCESS;
} else {
  return EFI_INVALID_PARAMETER;
}


PCD_TYPE_HII:
PCD_TYPE_HII|PCD_TYPE_STRING:
透過SetVariable將資料存於NVS並更新PCD Database內的資料。
VariableHead = (VARIABLE_HEAD *) (PcdDb + Offset);
    
Guid = GuidTable + VariableHead->GuidTableIndex;
Name = (UINT16*) (StringTable + VariableHead->StringIndex);
VariableOffset = VariableHead->Offset;
Status = SetHiiVariable (Guid, Name, Data, *Size, VariableOffset);

if (EFI_NOT_FOUND == Status) {
  if ((LocalTokenNumber & PCD_TYPE_ALL_SET) == (PCD_TYPE_HII|PCD_TYPE_STRING))  {
    CopyMem (StringTable + *(STRING_HEAD *)(PcdDb + VariableHead->DefaultValueOffset), Data, *Size);
  } else {
    CopyMem (PcdDb + VariableHead->DefaultValueOffset, Data, *Size);
  }
  Status = EFI_SUCCESS;
}


PCD_TYPE_DATA:
PtrType的處理方式跟PCD_TYPE_STRING很像。
if (PtrType) {
  if (SetPtrTypeSize (TokenNumber, Size, PeiPcdDb)) {
    CopyMem (InternalData, Data, *Size);
    return EFI_SUCCESS;
  } else {
    return EFI_INVALID_PARAMETER;
  }
}


其餘基本型態只是透過轉型來處理。
switch (*Size) {
  case sizeof(UINT8):
    *((UINT8 *) InternalData) = *((UINT8 *) Data);
    return EFI_SUCCESS;

  case sizeof(UINT16):
    *((UINT16 *) InternalData) = *((UINT16 *) Data);
    return EFI_SUCCESS;

  case sizeof(UINT32):
    *((UINT32 *) InternalData) = *((UINT32 *) Data);
    return EFI_SUCCESS;

  case sizeof(UINT64):
    *((UINT64 *) InternalData) = *((UINT64 *) Data);
    return EFI_SUCCESS;

  default:
    ASSERT (FALSE);
    return EFI_NOT_FOUND;
}


2014年10月20日 星期一

PCD (Platform Configuration Database) - Part 2 (PEI)

這篇將探討PCD背後的原理,PEI與DXE都分別提供PPI與Protocol給予其它Driver使用,雖然一般都是透過PcdLib來存取Dynamic類型的PCD,但實際上PcdLib是使用PPI或Protocol來達成存取PCD的目的。

AutoGen.h
typedef struct {
  DYNAMICEX_MAPPING  ExMapTable[PEI_EXMAPPING_TABLE_SIZE];
  UINT32             LocalTokenNumberTable[PEI_LOCAL_TOKEN_NUMBER_TABLE_SIZE];
  GUID               GuidTable[PEI_GUID_TABLE_SIZE];

  UINT8              StringTable[1]; /* _ */

  SIZE_INFO          SizeTable[PEI_SIZE_TABLE_SIZE];

  UINT8              SkuIdTable[PEI_SKUID_TABLE_SIZE];
  SKU_ID             SystemSkuId;
} PEI_PCD_DATABASE_INIT;

typedef struct {
  UINT64   PcdS3BootScriptTablePrivateDataPtr_a1aff049_fdeb_442a_b320_13ab4cb72bbc[1];

  UINT32   PcdFlashNvStorageFtwWorkingBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc[1];
  UINT32   PcdFlashNvStorageFtwSpareBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc[1];
  UINT32   PcdFlashNvStorageVariableBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc[1];
} PEI_PCD_DATABASE_UNINIT;

typedef struct {
  PEI_PCD_DATABASE_INIT    Init;
  PEI_PCD_DATABASE_UNINIT  Uninit;
} PEI_PCD_DATABASE;

AutoGen.c
PEI_PCD_DATABASE_INIT gPEIPcdDbInit = {
  /* VPD */

  /* ExMapTable */
  {
    { 0U, 0U, 0U },
  },
  /* LocalTokenNumberTable */
  {
    offsetof(PEI_PCD_DATABASE, Uninit.PcdFlashNvStorageFtwWorkingBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_TYPE_UINT32,
    offsetof(PEI_PCD_DATABASE, Uninit.PcdFlashNvStorageFtwSpareBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_TYPE_UINT32,
    offsetof(PEI_PCD_DATABASE, Uninit.PcdFlashNvStorageVariableBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_TYPE_UINT32,
    offsetof(PEI_PCD_DATABASE, Uninit.PcdS3BootScriptTablePrivateDataPtr_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_DATUM_TYPE_UINT64 | PCD_TYPE_DATA,
  },
  /* GuidTable */
  {
    {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
  },
 /* StringTable */
  { 0 }, /* _ */

  /* SizeTable */
  {
    0U, 0U, /* _ */
  },
  /* SkuIdTable */
  { 0U,  },
  0U
};

以上是在Compile過程中,Build Tool為PCD PEIM所生成的檔案。
可以注意到這邊只有少數幾組PCD,原因是PCD PEIM中不會包含只在DXE被使用的PCD。
而這個PEI_PCD_DATABASE,最後會透過HOB傳給PCD DXE。
建立HOB的過程可參考BuildPcdDatabase

整個Structure分為InitUninit的部分,兩個差別是在於,如果有定義好預設值的Dynamic PCD,其預設值會被放在Init裡面,但如果在DSC裡定義的值為0或NULL的話,會被視為無預設值,且會放置於Uninit中。

  /* LocalTokenNumberTable */
  {
    offsetof(PEI_PCD_DATABASE, Uninit.PcdFlashNvStorageFtwWorkingBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_TYPE_UINT32,
    offsetof(PEI_PCD_DATABASE, Uninit.PcdFlashNvStorageFtwSpareBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_TYPE_UINT32,
    offsetof(PEI_PCD_DATABASE, Uninit.PcdFlashNvStorageVariableBase_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_TYPE_UINT32,
    offsetof(PEI_PCD_DATABASE, Uninit.PcdS3BootScriptTablePrivateDataPtr_a1aff049_fdeb_442a_b320_13ab4cb72bbc) | PCD_DATUM_TYPE_UINT64 | PCD_TYPE_DATA,
  },

PEI_PCD_DATABASE_INIT中的LocalTokenNumberTable會在Compile過程中,會決定好每個PCD中的Data在PEI_PCD_DATABASE中的Offset以及Data Type與PCD Type。

#define offsetof(s,m)  (UINT32) (UINTN) &(((s *)0)->m)
代入一個Structure的原型與其Member,會算出其Member的Offset為多少。

PCD Type Mask:
PCD_TYPE_DATA
PCD_TYPE_HII
PCD_TYPE_VPD
PCD_TYPE_SKU_ENABLED
PCD_TYPE_STRING

Datum Type:
PCD_DATUM_TYPE_POINTER
PCD_DATUM_TYPE_UINT8
PCD_DATUM_TYPE_UINT16
PCD_DATUM_TYPE_UINT32
PCD_DATUM_TYPE_UINT64

在上面的LocalTokenNumberTable中,共有四個PCD,如果去搜尋整套編譯過的Source Code,可以在某些AutoGen.h看到這些Dynamic PCD的Token Number,並且可以注意到第一個PCD的Token為1,那為何不是從0開始?0是給PCD_INVALID_TOKEN_NUMBER使用(Source Code中有註解),所以第一個PCD是從1開始,只是後面會將Token Number減1,再代入Table取得其相關資料。
#define _PCD_TOKEN_PcdFlashNvStorageFtwWorkingBase  1U
#define _PCD_GET_MODE_32_...  LibPcdGet32(_PCD_TOKEN_PcdFlashNvStorageFtwWorkingBase)
#define _PCD_SET_MODE_32_...(Value)  LibPcdSet32(_PCD_TOKEN_PcdFlashNvStorageFtwWorkingBase, (Value))

#define _PCD_TOKEN_PcdFlashNvStorageFtwSpareBase  2U
#define _PCD_GET_MODE_32_...  LibPcdGet32(_PCD_TOKEN_PcdFlashNvStorageFtwSpareBase)
#define _PCD_SET_MODE_32_...(Value)  LibPcdSet32(_PCD_TOKEN_PcdFlashNvStorageFtwSpareBase, (Value))

#define _PCD_TOKEN_PcdFlashNvStorageVariableBase  3U
#define _PCD_GET_MODE_32_...  LibPcdGet32(_PCD_TOKEN_PcdFlashNvStorageVariableBase)
#define _PCD_SET_MODE_32_...(Value)  LibPcdSet32(_PCD_TOKEN_PcdFlashNvStorageVariableBase, (Value))

#define _PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr  4U
#define _PCD_GET_MODE_64_...  LibPcdGet64(_PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr)
#define _PCD_SET_MODE_64_...(Value)  LibPcdSet64(_PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr, (Value))

在PPI或Protocol的底層會透過呼叫GetWorkerSetWorker來存取PCD。

GetWorker:


VOID *
GetWorker (
  IN UINTN               TokenNumber,
  IN UINTN               GetSize
  )

透過Token Number取得該PCD的LocalTokenNumber (Offset、PCD Type、Data Type)。
LocalTokenNumber = PeiPcdDb->Init.LocalTokenNumberTable[TokenNumber];

透過Mask單獨取得Offset。
Offset = LocalTokenNumber & PCD_DATABASE_OFFSET_MASK;

接下來會依照各種不同PCD Type做不同的處理:
PCD_TYPE_VPD:
傳回VPD (Vital Product Data)中特定的Offset的資料。
VPD_HEAD *VpdHead;
VpdHead = (VPD_HEAD *) ((UINT8 *)PeiPcdDb + Offset);
return (VOID *) (UINTN) (PcdGet32 (PcdVpdBaseAddress) + VpdHead->Offset);


PCD_TYPE_HII|PCD_TYPE_STRING:
PCD_TYPE_HII:
透過Offset取得Variable Head,裡面有著GUID與Name的Index。
VariableHead = (VARIABLE_HEAD *) ((UINT8 *)PeiPcdDb + Offset);

Guid = &(PeiPcdDb->Init.GuidTable[VariableHead->GuidTableIndex]);
Name = (UINT16*)&StringTable[VariableHead->StringIndex];


透過GetVariable取得Variable Data。
Status = GetHiiVariable (Guid, Name, &Data, &DataSize);

如有錯誤則去取得PCD設定的預設值(如:NOT_FOUND)。
if (Status == EFI_SUCCESS) {
  return (VOID *) ((UINT8 *) Data + VariableHead->Offset);
} else {
  //
  // Return the default value specified by Platform Integrator
  //
  if ((LocalTokenNumber & PCD_TYPE_ALL_SET) == (PCD_TYPE_HII|PCD_TYPE_STRING)) {
    return (VOID*)&StringTable[*(STRING_HEAD*)((UINT8*)PeiPcdDb + VariableHead->DefaultValueOffset)];
  } else {
    return (VOID *) ((UINT8 *) PeiPcdDb + VariableHead->DefaultValueOffset);
  }
}


PCD_TYPE_DATA:
直接透過Offset取得Data。
return (VOID *) ((UINT8 *)PeiPcdDb + Offset);

PCD_TYPE_STRING:
先取得該PCD的STRING_HEAD (StringTableIdx),裡面存放著對應StringTable的Offset。
StringTableIdx = * (STRING_HEAD*) ((UINT8 *) PeiPcdDb + Offset);
return (VOID *) (&StringTable[StringTableIdx]);


SetWorker:


EFI_STATUS
SetWorker (
  IN          UINTN               TokenNumber,
  IN          VOID                *Data,
  IN OUT      UINTN               *Size,
  IN          BOOLEAN             PtrType
  )

透過Token Number取得該PCD的LocalTokenNumber (Offset、PCD Type、Data Type)。
LocalTokenNumber = PeiPcdDb->Init.LocalTokenNumberTable[TokenNumber];

會先判斷Data Type是不是Pointer,通常像String之類的,會被歸類為Pointer。
要注意到在Build Tool會在PCD Database中,先宣告好固定長度的陣列來存放String等其它Data。
如果PtrType為True,而傳入的Size又大於原先預留好的最大值的話,就會造成問題,這邊就先擋掉並回傳MaxSize。
if (PtrType) {
  //
  // Get MaxSize first, then check new size with max buffer size.
  //
  GetPtrTypeSize (TokenNumber, &MaxSize, PeiPcdDb);
  if (*Size > MaxSize) {
    *Size = MaxSize;
    return EFI_INVALID_PARAMETER;
  }
} else {
  if (*Size != PeiPcdGetSize (TokenNumber + 1)) {
    return EFI_INVALID_PARAMETER;
  }
}


當某個PCD被修改時會去呼叫,為該PCD註冊Notify的Function。
可以透過PCD Library的LibPcdCallbackOnSet來進行註冊會比較方便。
if (TokenNumber + 1 < PEI_NEX_TOKEN_NUMBER + 1) {
  InvokeCallbackOnSet (0, NULL, TokenNumber + 1, Data, *Size);
}


透過Mask單獨取得Offset,並取得Data。
Offset = LocalTokenNumber & PCD_DATABASE_OFFSET_MASK;
InternalData    = (VOID *) ((UINT8 *) PeiPcdDb + Offset);


接下來會依照各種不同PCD Type做不同的處理:
PCD_TYPE_VPD:
PCD_TYPE_HII:
PCD_TYPE_HII|PCD_TYPE_STRING:
可能是上述幾個Type都不支援在PEI修改NVS,所以這邊就直接return回EFI_INVALID_PARAMETER。
尤其PCD_TYPE_HII實際上就是個Variable。
return EFI_INVALID_PARAMETER;

PCD_TYPE_STRING:
傳入的資料為一串Array,資料長度可能會變動,所以先更改Current Size。
SizeTable中有兩個欄位,一個是Max Size,一個是Current Size。
之後再透過CopyMem,將傳入的Data Copy到StringTable中。
if (SetPtrTypeSize (TokenNumber, Size, PeiPcdDb)) {
  StringTableIdx = *((STRING_HEAD *)InternalData);
  CopyMem (&PeiPcdDb->Init.StringTable[StringTableIdx], Data, *Size);
  return EFI_SUCCESS;
} else {
  return EFI_INVALID_PARAMETER;
}


PCD_TYPE_DATA:
PtrType的處理方式跟PCD_TYPE_STRING很像。
if (PtrType) {
  if (SetPtrTypeSize (TokenNumber, Size, PeiPcdDb)) {
    CopyMem (InternalData, Data, *Size);
    return EFI_SUCCESS;
  } else {
    return EFI_INVALID_PARAMETER;
  }
}


其餘基本型態只是透過轉型來處理。
switch (*Size) {
  case sizeof(UINT8):
    *((UINT8 *) InternalData) = *((UINT8 *) Data);
    return EFI_SUCCESS;

  case sizeof(UINT16):
    *((UINT16 *) InternalData) = *((UINT16 *) Data);
    return EFI_SUCCESS;

  case sizeof(UINT32):
    *((UINT32 *) InternalData) = *((UINT32 *) Data);
    return EFI_SUCCESS;

  case sizeof(UINT64):
    *((UINT64 *) InternalData) = *((UINT64 *) Data);
    return EFI_SUCCESS;

  default:
    ASSERT (FALSE);
    return EFI_NOT_FOUND;
}


2013年12月26日 星期四

PCD (Platform Configuration Database) - Part 1

EDK II相較於EDK來說,除了更模組化的設計(少了許多沒必要且重複的Library...),最大的不同就是多了PCD這個新功能!

依照PCD類型的不同,我們的Module可以讀取在Compiler前就已設定好的Macro,或者是使用Dynamic PCD來讓各個Module相互存取數值。

在EDK時BIOS內的不同Module要互相傳遞數值,最常使用的可能是CMOS、Memory或Variable。
但CMOS可使用的位置並不算多(雖然還算滿多的...),也要顧慮到該位址是否已被占用。
而Memory也要先配置好空間,然後別支Module也要知道該位置,說真的也有點麻煩...
再來就是Variable,Variable說方便是方便,但使用時都要做一些繁瑣動作,例如訂好Name、GUID還有Data Type(雖然PCD也要訂定好GUID與各資料型別等部分,但Compiler會做掉很多地方),對於一般不需保存於NVS的資料,個人比較偏好使用PCD,但其生命周期只有Boot-time而已。

PCD分為五種型態:
  • PcdsFeatureFlag
  • PcdsFixedAtBuild
  • PcdsPatchableInModule
  • PcdsDynamic
  • PcdsDynamicEx
某些PCD在編譯時,就會被宣告為使用它的Module裡的常數,所以只能去讀取它原先就定好的值。
其它PCD則是可以在Boot-time時期做存取的動作。

可在Package中的DEC檔中宣告每個不同的PCD。
MdePkg.dec為例:
[PcdsFixedAtBuild, PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx]  gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress|0xE0000000|UINT64|0x0000000a  gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLangCodes|"engfraengfra"|VOID*|0x0000001c  gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang|"eng"|VOID*|0x0000001d

在DEC中的PCD宣告,可以有多種型態,以上面的例子來說,這些PCDs就宣告了四種型態,並且分別給予它們初始值資料型別Token值

然後可在DSC中定義它實際的PCD型態初始值
MdePkg.dsc為例:
[PcdsFixedAtBuild]
  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x0f
  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000
  gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress|0xE0000000

========================================
那如果在DSC定義一個與DEC中所宣告的型態不同會如何?
MdePkg.dec
[PcdsFixedAtBuild,PcdsPatchableInModule]
  gEfiMdePkg...Guid.PcdDebugPrintErrorLevel|0x80000000|UINT32|0x00000006

Nt32Pkg.dsc
[PcdsDynamicDefault]
  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000040

將會Compile error...
Type [Dynamic] of PCD [gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel] in DSC file doesn't match the type [FixedAtBuild] defined in DEC file.

底下這句話是Spec中所提到的:
Some DEC files define multiple PCD types, but only one type can be used and will be chosen by the platform DSC file.
========================================

在編譯的時候Compiler會為每一支Module自動產生AutoGen.h與AutoGen.c。
關於PCD的部分,它會將使用者在inf中所加入的PCD轉換到上述的兩個檔案,對於不同型態的PCD,則會有不同的處理方式。



以下將會介紹各種不同的PCD:


PcdsFeatureFlag

FeatureFlag顧名思義可以用它來表示一個Feature是On或Off,所以它是一個Boolean值TRUE或FALSE。
可從下面的例子看出,PcdsFeatureFlag型態的PCD,將會被轉換成const的常數,也意味的它只能被唯讀。

Example: 
DSC
[PcdsFeatureFlag]
  gEfiMdePkgTokenSpaceGuid.PcdUgaConsumeSupport|TRUE

AutoGen.h
#define _PCD_TOKEN_PcdUgaConsumeSupport  11U
#define _PCD_VALUE_PcdUgaConsumeSupport  ((BOOLEAN)1U)
extern const  BOOLEAN  _gPcd_FixedAtBuild_PcdUgaConsumeSupport;
#define _PCD_GET_MODE_BOOL_PcdUgaConsumeSupport  _gPcd_FixedAtBuild_PcdUgaConsumeSupport
//#define _PCD_SET_MODE_BOOL_PcdUgaConsumeSupport  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD

AutoGen.c
GLOBAL_REMOVE_IF_UNREFERENCED const BOOLEAN _gPcd_FixedAtBuild_PcdUgaConsumeSupport = _PCD_VALUE_PcdUgaConsumeSupport;
========================================

PcdsFixedAtBuild

FixedAtBuild與FeatureFlag挺相似的,最大的不同則是在於它可以宣告多種不同的變數型別,而不是只有局限於Boolean而已。

Example:
DSC
[PcdsFixedAtBuild]
  gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress|0xE0000000

AutoGen.h
#define _PCD_TOKEN_PcdPciExpressBaseAddress  16U
#define _PCD_VALUE_PcdPciExpressBaseAddress  0xE0000000ULL
extern const  UINT64  _gPcd_FixedAtBuild_PcdPciExpressBaseAddress;
#define _PCD_GET_MODE_64_PcdPciExpressBaseAddress  _gPcd_FixedAtBuild_PcdPciExpressBaseAddress
//#define _PCD_SET_MODE_64_PcdPciExpressBaseAddress  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD

AutoGen.c
GLOBAL_REMOVE_IF_UNREFERENCED const UINT64 _gPcd_FixedAtBuild_PcdPciExpressBaseAddress = _PCD_VALUE_PcdPciExpressBaseAddress;
========================================

PcdsPatchableInModule

宣告成PatchableInModule與上面兩種型態的PCD的最大不同是,它可以在Runtime時去變動它的數值。
然後它還有一個特質,通過下面的範例可以注意到,它被宣告成一個全域變數(Global Variable),在PE/COFF的檔案格式中,而全域變數將會被初始化並放置於Data Section之中。並有機會透過外部工具直接修改。
記得Compile完所產生的.efi檔案都是PE(Portable Executable)格式,有興趣的人可以用7z將.efi打開看看。

由於它只被宣告一個全域變數,所以只有同一個Module能夠取存,不能透過它與各Module相互傳遞數值。

Example:
DSC
[PcdsPatchableInModule]
  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000

AutoGen.h
#define _PCD_TOKEN_PcdDebugPrintErrorLevel  17U
#define _PCD_PATCHABLE_VALUE_PcdDebugPrintErrorLevel  ((UINT32)0x80000040U)
extern volatile   UINT32  _gPcd_BinaryPatch_PcdDebugPrintErrorLevel;
#define _PCD_GET_MODE_32_PcdDebugPrintErrorLevel  _gPcd_BinaryPatch_PcdDebugPrintErrorLevel
#define _PCD_SET_MODE_32_PcdDebugPrintErrorLevel(Value)  (_gPcd_BinaryPatch_PcdDebugPrintErrorLevel = (Value))

AutoGen.c
volatile  UINT32 _gPcd_BinaryPatch_PcdDebugPrintErrorLevel = _PCD_PATCHABLE_VALUE_PcdDebugPrintErrorLevel;
========================================

PcdsDynamic v.s. PcdsDynamicEx

這兩種型態的PCD都是集中於PCD Database作集中管理,有PEI與DXE兩個部分。
型態又可細分為以下幾種:
  • PcdsDynamicDefault and PcdsDynamicExDefault
  • PcdsDynamicHii and PcdsDynamicExHii
  • PcdsDynamicVpd and PcdsDynamicExVpd

在DEC所宣告的Section需要為PcdsDynamic或PcdsDynamicEx,而在DSC中則是它們的子分類。

PcdsDynamicDefault

DynamicDefault型態的PCD的最大優勢是在於,各個Module之間可以藉由這個PCD來互相傳遞數值。
所以它有著多樣化的應用方式,例如預先把某個不會變動的值從HW讀出來,並存到某個PCD裡面,這樣接下來其它的Module就可以不用再經過繁複的動作去存取HW上的值,直接透過PCD即可。

可以看到下面的例子,它會去透過一個Library去存取數值,而不是宣告一個常數或全域變數。

還可注意到一點,在DEC中所定的Token與AutoGen.h的數值並不相同!
其實Spec有提到,不管DEC中如何訂,Compiler都會自己產生一組,這應該是為了避免人為因素而有重複的狀況。
Although the token is auto-generated. A value is required for the Token field; if it is not defined, then the the build will fail. 

Example:
DEC
[PcdsDynamic, PcdsDynamicEx]
  gEfiMdeModulePkgTokenSpaceGuid.PcdS3BootScriptTablePrivateDataPtr|0x0|UINT64|0x00030000

DSC
[PcdsDynamicDefault] 
  gEfiMdeModulePkgTokenSpaceGuid.PcdS3BootScriptTablePrivateDataPtr|0x0

AutoGen.h
#define _PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr  1U
#define _PCD_GET_MODE_64_PcdS3BootScriptTablePrivateDataPtr  LibPcdGet64(_PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr)
#define _PCD_SET_MODE_64_PcdS3BootScriptTablePrivateDataPtr(Value)  LibPcdSet64(_PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr, (Value))
========================================

PcdsDynamicExDefault

多了Ex的DynamicExDefault與DynamicDefault很相似,但如果一個Module不是同時間與整個Platform一起Build的,而是一個外部的Binary,Platform與Binary又需要互相存取某個PCD的數值的話,這時候一開始該PCD就需要宣告成DynamicEx型態的PCD。
但外部的Binary在使用DynamicEx PCD時,必須先知道該PCD的GUID與Token值,才能透過LibPcdGetExXX或LibPcdSetExXX來存取。

Example:
DEC
[PcdsDynamic, PcdsDynamicEx]
  gEfiMdeModulePkgTokenSpaceGuid.PcdS3BootScriptTablePrivateDataPtr|0x0|UINT64|0x00030000

DSC
[PcdsDynamicExDefault] 
  gEfiMdeModulePkgTokenSpaceGuid.PcdS3BootScriptTablePrivateDataPtr|0x0

AutoGen.h
#define _PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr  196608U
#define _PCD_GET_MODE_64_PcdS3BootScriptTablePrivateDataPtr  LibPcdGetEx64(&gEfiMdeModulePkgTokenSpaceGuid, _PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr)
#define _PCD_SET_MODE_64_PcdS3BootScriptTablePrivateDataPtr(Value)  LibPcdSetEx64(&gEfiMdeModulePkgTokenSpaceGuid, _PCD_TOKEN_PcdS3BootScriptTablePrivateDataPtr, (Value))
========================================

PcdsDynamicHii

HII型態的PCD在DSC需定好它所對應的Variable Name、GUID、Offset(位於Variable Data的哪個Offset)還有Default Value。
如果系統中不存在著該Variable,那麼就會從PCD取得DSC所定好的預設值,否則就會回傳該Variable的Data加上Offset。

Example:
DEC
[PcdsDynamic, PcdsDynamicEx]
  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdPlatformBootTimeOut|0xffff|UINT16|0x40000001

DSC
[PcdsDynamicHii]
  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdPlatformBootTimeOut|L"Timeout"|gEfiGlobalVariableGuid|0x0|10

AutoGen.h
#define _PCD_TOKEN_PcdPlatformBootTimeOut  15U
#define _PCD_GET_MODE_16_PcdPlatformBootTimeOut  LibPcdGet16(_PCD_TOKEN_PcdPlatformBootTimeOut)
#define _PCD_SET_MODE_16_PcdPlatformBootTimeOut(Value)  LibPcdSet16(_PCD_TOKEN_PcdPlatformBootTimeOut, (Value))
========================================

PcdsDynamicExHii

多了Ex的DynamicExHii則意思跟DynamicEx很相似。

Example:
DEC
[PcdsDynamic, PcdsDynamicEx]
  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdPlatformBootTimeOut|0xffff|UINT16|0x40000001

DSC
[PcdsDynamicExHii]
  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdPlatformBootTimeOut|L"Timeout"|gEfiGlobalVariableGuid|0x0|10

AutoGen.h
#define _PCD_TOKEN_PcdPlatformBootTimeOut  1073741825U
#define _PCD_GET_MODE_16_PcdPlatformBootTimeOut  LibPcdGetEx16(&gEfiIntelFrameworkModulePkgTokenSpaceGuid, _PCD_TOKEN_PcdPlatformBootTimeOut)
#define _PCD_SET_MODE_16_PcdPlatformBootTimeOut(Value)  LibPcdSetEx16(&gEfiIntelFrameworkModulePkgTokenSpaceGuid, _PCD_TOKEN_PcdPlatformBootTimeOut, (Value))
========================================

PcdsDynamicVpd and PcdsDynamicExVpd

VPD (Vital Product Data),在NVS會有一塊區域被用來儲存VPD的資料,與其它Dynamic PCD不同,VPD只能唯讀,無法在執行期間去修改它的數值。
關於VPD的部分,目前我還沒有機會用到。

以上就先介紹的這個部分,下一篇再來介紹應用與原理。

2013年12月17日 星期二

Package, Component, DEC and DSC

Package

EDK II與EDK最大的差異就是在於多了Package這個模組化的設計,把具有相同類型的Component、Library與各種Declaration放在這同一個目錄。
而每一個Package都有.dec (EDK II Package Declaration File)還有.dsc (EDK II Package Description File)這兩個檔案。

Component

每一個Component可以當作是EFI的執行檔,編譯完會產生出.efi的檔案。
而Component會在它的inf檔中宣告它的MODULE_TYPE,總共有以下這幾種類型:
BASE, SEC, PEI_CORE, PEIM,
DXE_CORE, DXE_DRIVER, DXE_RUNTIME_DRIVER, DXE_SMM_DRIVER, DXE_SAL_DRIVER, UEFI_DRIVER, UEFI_APPLICATION

但要注意的是BASE這種類型只有Library可以使用。
對一般的開發人員來說,常用的應該只有PEIM、 DXE_DRIVER, DXE_RUNTIME_DRIVER, DXE_SMM_DRIVER, UEFI_DRIVER與UEFI_APPLICATION。

DEC (EDK II Package Declaration File)

Package中有.dec這個檔案來宣告提供給內外部的Component所使用的interface,如:header files所在的位置、Library、GUID、protocol、PCD的宣告...等。
等於它是一個提供者的角色,對於使用者(Component、Library)來說,有用到該Package的資源的話,就需要在其inf加入這個DEC。

DEC中包含了以下這些區塊:
Defines、Includes、LibraryClasses、Guids、Ppis、Protocols與Pcds。

========================================
Includes:描述這個Package的header files都集中放在哪一個目錄底下,所以常可在實際的目錄下看到Library、Protocol、Ppi等資料夾存在。
[Includes]
  Include


[Includes.IA32]
  Include/Ia32

[Includes.X64]
  Include/X64


========================================
Guids、Ppis、Protocols:定義了這個Package所提供的Guids、Ppis與Protocols,在以往EDK使用某個GUID的變數時,都必須在.inf加入有定義這個變數的Library,不然就是要自己宣告一個。
而EDK II中我們只要加入包含它所被宣告的DEC即可使用,這部分Compiler都已經幫我們處理好了。
[Guids]
  #GuidCName = {xxxxxxxx,xxxx,xxxx,{xx,xx,xx,xx,xx,xx,xx,xx}}

[Ppis]
  #PpiGuidCName = {xxxxxxxx,xxxx,xxxx,{xx,xx,xx,xx,xx,xx,xx,xx}}

[Protocols]
  #ProtocolGuidCName = {xxxxxxxx,xxxx,xxxx,{xx,xx,xx,xx,xx,xx,xx,xx}}


========================================
以下描述的各種PCD型態的宣告與初始值。
[PcdsFeatureFlag]
  #FeatureFlag PCD is BOOLEAN type, the value is TRUR or FALSE.
  #PcdTokenSpaceCGuidName.PcdName|TRUE|BOOLEAN|TokenNumber
  #PcdTokenSpaceCGuidName.PcdName|FALSE|BOOLEAN|TokenNumber

[PcdsFixedAtBuild]
  #PcdTokenSpaceCGuidName.PcdName|DefaultValue|DataType|TokenNumber

[PcdsPatchableInModule]
  #PcdTokenSpaceCGuidName.PcdName|DefaultValue|DataType|TokenNumber

[PcdsDynamic]
  #PcdTokenSpaceCGuidName.PcdName|DefaultValue|DataType|TokenNumber

[PcdsDynamicEx]
  #PcdTokenSpaceCGuidName.PcdName|DefaultValue|DataType|TokenNumber


可以注意的是,在DEC中的PCD宣告可以是複合式的宣告。
[PcdsFixedAtBuild, PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx]  gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress|0xE0000000|UINT64|0x0000000a


DSC (EDK II Package Description File)

每一個Package內通常會有一個.dsc的檔案,用來指定要編譯那些Component與Library,並且定義每一個PCD的類型與實際的初始值,因為DEC檔內只是宣告PCD型態與初始值,而實際上的定義,則是在DSC之中。

DSC中包含了以下這些區塊:
Defines、LibraryClasses、與Pcds。

2013年12月11日 星期三

UDK vs. EDK II


在網路上會看到有UDK (UEFI Development Kits)跟EDK II這兩個相似的東西,但這兩者有甚麼樣的不同?
  1. EDK II是基於開放原始碼,並由一群自願者所開發與維護。
  2. 而UDK是由Intel基於某一個EDK II的特定版本所維護並發佈出來的,相對來講會來的比較穩定。
可經由此去取得EDK II與UDK。
http://sourceforge.net/apps/mediawiki/tianocore/index.php?title=EDK2