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