做为一个BIOS工程师,在去找工作的时候,必然会被问到 pei是什么, dxe 是什么, 大家必然会回答 做各种初始化,那具体到一支PEIM, 或者一个DXE 驱动,它到底做了啥,又是怎么做的呢?
今天就以PcdPeim 为例,详细说说一支PEIM的工作原理。
PCD 翻译成中文叫平台配置数据信息, Platform Configuration Database, 这个在ami 代码里面叫token位于skl 文件里面,对于oem里面的工程师大部分时间
都是改这些token的值,所以某种程度上,BIOS 工程师也叫token 工程师。
书归正转,具体干了什么,还得看代码:
/**
Main entry for PCD PEIM driver.
This routine initialize the PCD database for PEI phase and install PCD_PPI/EFI_PEI_PCD_PPI.
@param FileHandle Handle of the file being invoked.
@param PeiServices Describes the list of possible PEI Services.
@return Status of install PCD_PPI
**/
EFI_STATUS
EFIAPI
PcdPeimInit (
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
EFI_STATUS Status;
BuildPcdDatabase (FileHandle);
//
// Install PCD_PPI and EFI_PEI_PCD_PPI.
//
Status = PeiServicesInstallPpi (&mPpiList[0]);
ASSERT_EFI_ERROR (Status);
//
// Install GET_PCD_INFO_PPI and EFI_GET_PCD_INFO_PPI.
//
Status = PeiServicesInstallPpi (&mPpiList2[0]);
ASSERT_EFI_ERROR (Status);
return Status;
}
其次它安装了两个PPI. 用于支持动态PCD在运行时被设置和访问
这两个Ppi 具体是什么,我们只需要在同名文件里去搜索mPpiList 就行了,
然后它的第三个参数就是PPI实例
///
/// Instance of PCD_PPI protocol is EDKII native implementation.
/// This protocol instance support dynamic and dynamicEx type PCDs.
///
PCD_PPI mPcdPpiInstance = {
PeiPcdSetSku,
PeiPcdGet8,
PeiPcdGet16,
PeiPcdGet32,
PeiPcdGet64,
PeiPcdGetPtr,
PeiPcdGetBool,
PeiPcdGetSize,
PeiPcdGet8Ex,
PeiPcdGet16Ex,
PeiPcdGet32Ex,
PeiPcdGet64Ex,
PeiPcdGetPtrEx,
PeiPcdGetBoolEx,
PeiPcdGetSizeEx,
PeiPcdSet8,
PeiPcdSet16,
PeiPcdSet32,
PeiPcdSet64,
PeiPcdSetPtr,
PeiPcdSetBool,
PeiPcdSet8Ex,
PeiPcdSet16Ex,
PeiPcdSet32Ex,
PeiPcdSet64Ex,
PeiPcdSetPtrEx,
PeiPcdSetBoolEx,
PeiRegisterCallBackOnSet,
PcdUnRegisterCallBackOnSet,
PeiPcdGetNextToken,
PeiPcdGetNextTokenSpace
};
可以很清楚的看到,就是对各种类型(长度)数据的get 和set.
重要数据结构的说明:
第一个当然是对DATABASE的说明:
//
// PEI and DXE Pcd driver use the same PCD database
//
typedef PCD_DATABASE_INIT PEI_PCD_DATABASE;
typedef PCD_DATABASE_INIT DXE_PCD_DATABASE;
typedef struct {
GUID Signature; // PcdDataBaseGuid.
UINT32 BuildVersion;
UINT32 Length;
UINT32 UninitDataBaseSize; // Total size for PCD those default value with 0.
TABLE_OFFSET LocalTokenNumberTableOffset;
TABLE_OFFSET ExMapTableOffset;
TABLE_OFFSET GuidTableOffset;
TABLE_OFFSET StringTableOffset;
TABLE_OFFSET SizeTableOffset;
TABLE_OFFSET SkuIdTableOffset;
TABLE_OFFSET PcdNameTableOffset;
UINT16 LocalTokenCount; // LOCAL_TOKEN_NUMBER for all.
UINT16 ExTokenCount; // EX_TOKEN_NUMBER for DynamicEx.
UINT16 GuidTableCount; // The Number of Guid in GuidTable.
SKU_ID SystemSkuId; // Current SkuId value.
UINT8 Pad; // Pad bytes to satisfy the alignment.
//
// Default initialized external PCD database binary structure
//
// Padding is needed to keep necessary alignment
//
//UINT64 ValueUint64[];
//UINT32 ValueUint32[];
//VPD_HEAD VpdHead[]; // VPD Offset
//DYNAMICEX_MAPPING ExMapTable[]; // DynamicEx PCD mapped to LocalIndex in LocalTokenNumberTable. It can be accessed by the ExMapTableOffset.
//UINT32 LocalTokenNumberTable[]; // Offset | DataType | PCD Type. It can be accessed by LocalTokenNumberTableOffset.
//GUID GuidTable[]; // GUID for DynamicEx and HII PCD variable Guid. It can be accessed by the GuidTableOffset.
//STRING_HEAD StringHead[]; // String PCD
//PCD_NAME_INDEX PcdNameTable[]; // PCD name index info. It can be accessed by the PcdNameTableOffset.
//VARIABLE_HEAD VariableHead[]; // HII PCD
//SKU_HEAD SkuHead[]; // Store SKU info for each PCD with SKU enable.
//UINT8 StringTable[]; // String for String PCD value and HII PCD Variable Name. It can be accessed by StringTableOffset.
//SIZE_INFO SizeTable[]; // MaxSize and CurSize for String PCD. It can be accessed by SizeTableOffset.
//UINT16 ValueUint16[];
//UINT8 ValueUint8[];
//BOOLEAN ValueBoolean[];
//UINT8 SkuIdTable[]; // SkuIds system supports.
//UINT8 SkuIndexTable[]; // SkuIds for each PCD with SKU enable.
} PCD_DATABASE_INIT;
这个结构体用于描述动态PCD的各种信息,Init 成员记录扩展PCD 的成员,GUID 数组,String 类型的数据及skuId
关键内部函数
在实现PcdPpi服务时,许多相关的内部函数被使用。他们是:GetPcdDatabase(), GetWorker(), GetPtrTypeSize(), GetExPcdTokenNumber(),
ExGetWorker(). 其中Get相关的函数是负责读取动态PCD的信息,Set相关的函数是负责更新动态PCD的值,注册回调函数是负责注册或者注销
回调函数。现在对这些主要的内部函数做进一步的介绍。
GetPcdDatabase() 函数,实现Pcd\Service.c
/**
Get PCD database from GUID HOB in PEI phase.
@return Pointer to PCD database.
**/
PEI_PCD_DATABASE *
GetPcdDatabase (
VOID
)
{
EFI_HOB_GUID_TYPE *GuidHob;
GuidHob = GetFirstGuidHob (&gPcdDataBaseHobGuid);
ASSERT (GuidHob != NULL);
return (PEI_PCD_DATABASE *) GET_GUID_HOB_DATA (GuidHob);
}
通过GUID 查找GUID类型的HOB数据块,并返回找到动态PCD的数据库。
PcdPei 入口函数
PcdPeimInit() 函数,实现在Pcd\Pcd.c 。 创建数据库用于维护PEI 阶段使用的动态PCD, 同时安装两个
PcdPpi支持动态PCD 在运行时被设置和访问。注意只有动态(dynamic) 类型的PCD ,才有数据库这个概念,
说明数据库,大家也不要想复杂了,就是一个结构体,然后搞一个数组,里面挨个放着Pcd。
安装的Ppi, 既然是处于pei 阶段,就要安装ppi, 要不然,别人(别的模块)没法调用,在pei 阶段的描述符典型的就两个:
EFI_PEI_PPI_DESCRIPTOR mPpiList[] = {
{
EFI_PEI_PPI_DESCRIPTOR_PPI,
&gPcdPpiGuid,
&mPcdPpiInstance
},
{
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEfiPeiPcdPpiGuid,
&mEfiPcdPpiInstance
}
};
EFI_PEI_PPI_DESCRIPTOR mPpiList2[] = {
{
EFI_PEI_PPI_DESCRIPTOR_PPI,
&gGetPcdInfoPpiGuid,
&mGetPcdInfoInstance
},
{
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEfiGetPcdInfoPpiGuid,
&mEfiGetPcdInfoInstance
}
};