日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

UEFI开发探索101 – PCD探究

發布時間:2023/12/16 编程问答 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 UEFI开发探索101 – PCD探究 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

(請保留-> 作者: 羅冰 https://blog.csdn.net/luobing4365)

PCD探究

  • 1 PCD簡介
  • 2 如何使用PCD
    • 2.1 PCD的類型
      • FixedAtBuild類型
      • FeatureFlag類型
      • PatchableInModule類型
      • Dynamic類型、DynamicHii類型和DynamicVpd類型
      • DynamicEx類型
    • 2.2 訪問PCD變量
    • 2.3 PCD的聲明和使用
  • 3 試著寫個例子


從《UEFI編程實踐》出版后,一系列的事情接踵而來,終于在今天,算是告一段落了。

這段時間,有不少機會和業內人員討論UEFI和BIOS。反思自己對這個領域的理解,深感自己理論的不足。

看過《UEFI編程實踐》的網友,應該能了解,書中對于概念的理論部分,闡述得相對較少。我一般都是遵循“提出問題-介紹UEFI相關知識-提供實例”的框架,圍繞某一課題進行研究。

其中的一個原因,是因為我本來就是奔著實踐為目的,將平時開發中所遇到的課題逐漸展開討論。另一個原因,是沒有深入到EDK2的具體實現去。

因此,從UEFI開發探索第101篇開始,我想逐漸轉向對EDK2的代碼研究了。如之前研究PCI Option ROM開發一樣,這次設立的目標包括:

  • 對OvmfPkg源代碼進行研究,搞清楚固件架構、編譯過程、各階段代碼實現等;
  • 了解Qemu怎么使用固件啟動的,以及如何啟動操作系統;
  • 了解Ovmf固件如何提供操作系統所需要的各種Table、Runtime Services,甚至SMI Handler等。搞清楚一個UEFI操作系統如何與BIOS結合的。
  • 我了解這是一個不小的目標,會遇到不少的困難,興趣所在,倒不是特別畏懼。本來的想法,是在樹莓派上進行實驗,在lab-z(博客:https://www.lab-z.com/)的建議下,覺得OvmfPkg做實驗更方便些。

    主題確定,后續想到哪里不熟就補足哪里的知識。嗯,先從EDK2全局配置的關鍵核心-PCD開始研究。

    1 PCD簡介

    Platform Configuration Database(PCD)是EDK2用來進行全局配置的機制,在代碼復用、模塊化方面發揮巨大作用。

    PCD是把代碼里面的可配置選項抽取出來,platform需要修改的時候,可以不用去修改源代碼。其參數的配置,可以在編譯過程中、運行時中都可以進行,甚至在二進制文件中也可以配置。

    這種設計方式就比較讓人著迷,這也使得定制化更為容易,代碼更容易維護。

    早期我一直以為PCD如同C/C++中的宏,用來提取公用代碼,這是錯誤的。它提供的功能更為廣泛,也更復雜。

    首先直觀地看下平常程序中用到的PCD,以前幾篇中的Diskdump工程為例,使用如下命令編譯,提取出其所用的PCD信息:

    C:\vUDK2018\edk2>build -Y PCD -y pcd.log -p RobinPkg\RobinPkg.dsc -m RobinPkg\Applications\Diskdump\Diskdump.inf -a IA32

    輸出的信息,存在了pcd.log中。查看下log信息:
    圖1 Diskdump中用到的PCD

    觀察一下,可以看到許多編程時壓根就沒注意到的PCD參數。Diskdump使用了MdePkg和ShellPkg中的Protocol,因此也用到了其相關的PCD。

    對于PCD的文檔,可以參考:
    EDK2代碼:
    MdeModulePkg\Universal\PCD\Dxe\Pcd.inf
    https://github.com/tianocore/tianocore.github.io/wiki/EDK-II-Documents:
    《EDK II Platform Configuration Database Infrastructure Description》
    《EDK II Platform Description(DSC) File Specification》
    《EDK II Package Declaration(DEC) File Format Specification》
    《EDK II Build Specification》
    https://uefi.org/specifications:
    《Platform Initialization(PI) Specification》

    2 如何使用PCD

    PCD可以使用于UEFI存在的大部分時間,除了在SEC階段、早期的PEI和DXE階段,基本都可以訪問。在使用前,我們需要搞清楚PCD的結構和類型。

    2.1 PCD的類型

    PCD變量的格式有點像結構體:

    TokenSpaceGuidCName.PcdCName

    其中,TokenSpaceGuidCName是GUID,而PcdCName是變量名,兩者組合構成了一個PCD變量。

    PCD有如下的類型。

    FixedAtBuild類型

    它在編譯階段確定,是靜態值,在運行階段或二進制形態下都不可改。可以認為它就是一個宏了。

    FeatureFlag類型

    它實際上和FixedAtBuild是同一類型,返回一個Bool類型(True或False),可用于判斷條件。

    PatchableInModule類型

    此類型的變量值在編譯的時候確定,它在編譯后的二進制文件上使用工具修改。與FixedAtBuild不同,它只能影響一個模塊(作用域在一個模塊)。

    Dynamic類型、DynamicHii類型和DynamicVpd類型

    Dynamic類型變量的作用域是整個系統,它是動態的PCD,可以在UEFI運行過程中修改。

    DynamicHii類型與Dynamic類型存儲的位置不同,Dynamic類型可以認為是存在于Memory中,再加載是會失去原始設置的;而DynamicHii類型是存在Efi variable中的(NVRAM中),其修改時非易失性的。

    而DynamicVpd類型變量是只讀的,不可寫的,一般出廠確定。

    DynamicEx類型

    與Dynamic類型類似,相當于加強版。其與Dynamic類型的區別,在于是否使用二進制文件中的PCD。比如FSP,如果要使用其中的PCD變量,則FSP中的PCD類型必須設置為### DynamicEx類型。

    2.2 訪問PCD變量

    為管理PCD變量,PEI提供了PCD_PPI和EFI_PEI_PCD_PPI;DXE提供了PCD_PROTOCOL和EFI_PCD_PROTOCOL。

    不過,為了方便使用,EDK2中引入了PCD Library,把這些訪問細節隱藏了起來。(MdePkg\Include\Library\PcdLib.h)

    庫中包含如下函數:

    PcdGetXX() PcdSetXX() PcdGetExXX() PcdSetExXX() PcdToken() PCDSetSku() PcdGetNextToken() PcdGetNextTokenSpace() CallBackOnSet() CancelCallBack()

    其中,XX可以為8、16、32、Size、Ptr或者Boolean。

    2.3 PCD的聲明和使用

    PCD的使用,基本可以按照如下流程進行。

  • DEC文件中聲明PCD變量的基本信息,比如:
  • [PcdsFixedAtBuild, PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx]gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintTimes|1|UINT32|0x40000005gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintString|L"UEFI Hello World!\n"|VOID*|0x40000004

    其格式為:

    TokenSpaceGuidCname.PcdCname|DefaultValue|DatumType|Token

    如前所述,PcdCname為變量名,DefaultValue為其默認值,DatumType是PCD的數據類型,Token是一個32位的整型,在DEC中每個PCD都有一個獨有的Token。

    DatumType可以是BOOLEAN、UINT8、UINT16、UINT32、UINT64或VOID *型。

  • DSC文件中設置PCD變量的值
    可以在DSC文件中設置相應PCD變量的值,比如:
  • [PcdsFixedAtBuild]gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x0f

    此設置過程不是必須的,如果沒有設置,則使用DEC文件中的默認值。

  • INF文件中聲明
    在模塊的INF文件中,需要聲明PCD變量,才可以在源碼中使用。比如:
  • [Pcd]gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintString gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintTimes

    只需要列出PCD變量名就可以了,其他信息不用列出。

    完成上述工作后,就可以在源代碼中,使用PCD庫函數訪問PCD變量了。示例如下:(摘自MdeModulePkg\Application\HelloWorld\HelloWorld.c)

    if (FeaturePcdGet (PcdHelloWorldPrintEnable)) {for (Index = 0; Index < PcdGet32 (PcdHelloWorldPrintTimes); Index ++) {//// Use UefiLib Print API to print string to UEFI console//Print ((CHAR16*)PcdGetPtr (PcdHelloWorldPrintString));}}

    3 試著寫個例子

    在UEFI應用開發或者Option ROM開發中,基本上不用PCD變量。但并不妨礙在UEFI應用上使用它們,我們試著在RobinPkg的某個Application上,來使用PCD變量。

    我選擇之前開發的Diskdump工程,改名為Pcdtouch,嘗試使用PCD變量。當然,隨便選一個其他的工程也可以,剛好這個工程就在眼前,就隨手在它上面改造了。

    修改步驟如下:

    1. 修改RobinPkg.dec
    添加如下語句:

    [Guids]gRobinPkgPcdSampleGuid = { 0xe7e1efa6, 0x7607, 0x3a78, { 0xc7, 0xdd, 0x43, 0xe4, 0xbd, 0x72, 0xc1, 0x19 }} # [PcdsFixedAtBuild, PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx] [PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx]gRobinPkgPcdSampleGuid.PcdtouchValue|12345|UINT32|0x90000005gRobinPkgPcdSampleGuid.PcdtouchStr|L"Hello,UEFI World, this is robin!\n"|VOID*|0x90000004

    2. 修改Pcdtouch.inf
    DSC文件中可以修改PCD變量的值,這里我們不需要修改,不用去改DSC文件。

    直接修改INF文件就可以了,添加將要在源程序中用到的PCD變量:

    [FeaturePcd]gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable ## CONSUMES[Pcd]gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintString ## SOMETIMES_CONSUMESgEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintTimes ## SOMETIMES_CONSUMESgRobinPkgPcdSampleGuid.PcdtouchStrgRobinPkgPcdSampleGuid.PcdtouchValue

    除了在DEC文件中添加的兩個PCD變量外,還把MdeModulePkg中的幾個PCD變量也聲明了,待會在程序中要用。

    3. 在源程序Pcdtouch.c中添加代碼
    主要是修改main程序:

    int main (IN int Argc,IN char **Argv) {UINT32 Index,myValue;Index = 0;myValue = 0;// 測試MdeModulePkg中的PCD變量,參考HelloWorldif (FeaturePcdGet (PcdHelloWorldPrintEnable)) {for (Index = 0; Index < PcdGet32 (PcdHelloWorldPrintTimes); Index ++) {Print ((CHAR16*)PcdGetPtr (PcdHelloWorldPrintString));}}Print ((CHAR16*)PcdGetPtr (PcdtouchStr)); //打印PCD變量Print(L"\n");myValue = PcdGet32(PcdtouchValue);Print(L"PcdtouchValue = %d\n", myValue);PcdSet32(PcdtouchValue,321);// LibPcdSet32(PcdtouchValue,321);myValue = PcdGet32(PcdtouchValue);Print(L"now,PcdtouchValue = %d\n", myValue); }

    至此修改完成。編譯后在模擬器中運行,結果如下:

    圖2 Pcdtouch運行結果

    在編寫過程中,得到的一些經驗:

  • VOID *型(字符串類型)的PCD變量不能只定義為PcdsDynamic型,會編譯不通過的。(是因為不能修改嗎,具體原因不清楚);
  • 使用PcdSet32的PCD變量,不能定義為PcdsFixedAtBuild型,編譯會提示找不到此PCD變量。(這個倒是很好理解,因為PcdsFixedAtBuild型是編譯時確定的)
  • TokenSpaceGuidCname.PcdCname|DefaultValue|DatumType|Token
    程序中,應該是通過TokenSpaceGuidCname和Token來唯一確定PCD變量的,PcdCname是方便程序員識別的(待確認);
  • PCD的類型是有優先級,FixedAtBuild>PatchableInModule>Dynamic>DynamicEx,在沒有顯式聲明時按照此順序認定(待確認);
  • 示例中沒有改DSC文件,需要注意,對于FixedAtBuild,在DEC文件和DSC文件中都是在[PcdsFixedAtBuild]下定義;
    而對于Dynamic類型,DEC中是在[PcdsDynamic]下定義,在DSC中則在[PcdsDynamicDefault](或者PcdsDynamicHii、PcdsDynamicVpd)。名字都是確定的,可查看DSC和DEC文件規范;
  • 后續再繼續加深理解。

    Gitee地址:https://gitee.com/luobing4365/uefi-exolorer
    項目代碼位于:/ FF RobinPkg/RobinPkg/Applications/Pcdtouch下


    總結

    以上是生活随笔為你收集整理的UEFI开发探索101 – PCD探究的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。