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

UEFI Detect Utility - 2014




已做部分更新,先前版本在某些機台會無法使用。

解壓密碼:leon
UefiDetectUtility_131211.7z

2011年11月18日 星期五

MTRR


MTRR (Memory type range registers) 是CPU內的一組MSR (Model-specific registers) ,它作用為告訴CPU要如何最佳的存取各個記憶體區段,簡單來說是CPU要以哪種快取模式來進行存取。

通常MTRR的初始化是BIOS的任務。

在UEFI中可以使用EfiReadMsr, EfiWriteMsr, MsrRead64, MsrWrite64...等Function來存取,只要拉相對應的Header與Library就好。


IA32_MTRRCAP, Index: 0xFE, Read-Only.
  • VCNT (variable range registers count) bits 0 ~ 7 - 指出這個CPU支援幾組動態的MTRR,MTRR也是有數量的,也有機會被塞爆。
  • FIX (fixed range registers supported) flag, bit 8 - 指出是否支援Fixed range MTRRs (IA32_MTRR_FIX64K_00000 through IA32_MTRR_FIX4K_0F8000)。
  • WC (write combining) flag, bit 10 - 是否支援write-combining (WC) memory。
  • SMRR (System-Management Range Register) flag, bit 11 - 是否支援SMRR。


IA32_MTRR_DEF_TYPE, Index: 0x2FF.
  • Type field, bits 0 ~ 7 - 指出未涵蓋在MTRR所定義的記憶體該以何種Cache為預設值。
  • FE (fixed MTRRs enabled) flag, bit 10 - Enable或Disable Fixed-range MTRR。
  • E (MTRRs enabled) flag, bit 11 - Enable或Disable動態的MTRR。


Variable - Range Register Pair
一組MTRR有兩個Registers,Base與Mask,起始索引可參考EDK的定義:
#define EFI_CACHE_VARIABLE_MTRR_BASE 0x200

  • Type field, bits 0 ~ 7 - 此MTRR所定義的範圍該以何種Cache方式存取。
  • Valid field, bits 11 - 指出此組MTRR是否有效。
  • PhysBase field, bits 12 ~ (MAXPHYADDR-1) - 取12 ~ (MAXPHYADDR-1) bits,補上3個0,就是組MTRR的Base Address。
  • PhysMask field, bits 12 ~ (MAXPHYADDR-1) - 一樣只取12 ~ (MAXPHYADDR-1) bits,補上3個0,再做1的補數運算,即可算出此MTRR的長度,值得注意的是長度需為2的次方。
MAXPHYADD代表是CPU的定址能力,長度依CPU而定。

下表為各種存取方式對應到MTRR的Type:
Memory Type and MnemonicEncoding in MTRR
Uncacheable (UC)00h
Write Combining (WC)01h
Reserved*02h
Reserved*03h
Write-through (WT)04h
Write-protected (WP)05h
Writeback (WB)06h
Reserved*07h ~ FFh


範例:
MTRRIndexValueType/SizeRange
Base20000000006WriteBack
Mask201F800008002048MB0 ~ 7FFFFFFF
Base20280000006WriteBack
Mask203FC00008001024MB80000000 ~ BFFFFFFF
略...
Base206D8000000Uncacheable
Mask207FF8000800128MBD8000000 ~ DFFFFFFF
Base208D7000000Uncacheable
Mask209FFF00080016MBD7000000 ~ D7FFFFFF
略...

對於MTRR所需要注意的Type有沒有設定為正確的Type,還有MTRR有沒有被塞爆,可能導致某些要使用Cache的Memory區域會因沒有宣告Cacheable,造成效能低落...等。