2010年10月5日 星期二

別以為你不會碰到 (1)

很多時候,問題都是出在空格上面

result = *x / *y;  //no problem

result = *x/*y;  //compile error

查了好久都不知道為什麼,後來發現… y 被當成多行注解,抹掉了

result = (*x) / (*y); //for your safety

其實有時候不是在寫公司的 code 很懶得去注意 style,上述的方法基本上是沒在用,只能說永遠記得,C 是 maximal munch strategy,你不把運算符切開,它一次就咬住最大口來分析代碼。

經典問題:

a = x+++y;

答案為下列何者?
1.
a = x+       ++y;

2.
a = x++     +y;

思考一下 :)

2010年9月16日 星期四

前進

下單位一個多月了,感覺 leader 和前輩都很好相處,而且大家知識很深,相較之下自己對於相關的東西還嫌太淺,以下簡述。

對硬體的架構和整個 UEFI 的 Bootflow 了解程度仍需加強,一個平台到手上後要先了解全局構造,有什麼部件,基本上就是要把他們推起來,這自然是廢話,要不然焊在板子上做啥?

EFI 的 flow,怎麼走,pei 怎麼去 init,dxemain 怎麼去推 dispatcher,順序、滿足條件,思考了一下,都需要經驗的累積,這部份其實還算好,common knowledge 上網就能查到,畢竟 tiano 在這一塊仍然是公開的。

難是難在不公開的這一段,就需要多問,多學,多筆記。把學習的東西轉成養份,好好去培。

目前 bring up 不是很大的問題,卡是卡在 windows 下要正常 function,這些東西就很機密了,但不違反商機的學習流路未來可以和大家分享一下。

2010年9月6日 星期一

什麼是將才之風?

今天跟朋友聊到 cache as ram 和 ram only 的工程模式,在 google code 上做了一些試驗,發現差異不是一個數量級的。朋友就來了個很妙的問題:大將之風…是什麼?

老實說,我不知道,因為從來沒當過大將。一輩子庸庸碌碌的寫 code,看 code,看算法,寫算法,肚子裡的東西不少,搬得上檯面的不多 (肚子本身除外)。

今天在公司好挫折,一個 20 分鐘就解出來的問題,卻遲遲不敢發,到晚上 8 點才不情願的弄了個 solution。為什麼會這樣,工程思維及經驗的不足導致行動遲滯,這一點再不改進,會是非常恐怖的致命傷。

這輩子錯過不少好機會,這次不會再讓它從指間流走。

關於 cache as ram 的問題可能週末搬過來這邊跟大家討論一下,這期間就做一些 common 的討論,順便寫一些紀錄。

2010年8月18日 星期三

Assembly

下單位之後,整天都在忙 power on,上 feature,自己寫 code 的空間是零。所以最近 leader 放了兩個 assembly 作業,真的超高興,寫得很用心。

組語是本行,寫得真是順手…寫完三個月的 UEFI code,換成組語也是滿恐怖的,沒有語言的保護,直接跟硬體面對面硬幹,有錯誤就毫不留情的噴給你看,這種特性也是蠻值得懷念。

今天又領了經理裝備 (羅西塔小姐的 "來領鋼盔水壺" 的梗不錯笑),20" 的 LCD,不免俗的普通牌鍵鼠組,8G dongle。螢幕接上去後,coding 果然是爽歪歪,字好大。

這幾天不忙再把 asm code 貼上來分析分享一下。

2010年7月29日 星期四

下部隊

經過三個月的努力,終於完成了階段性的任務,從 NTC 畢業了。

雖然說期末考被我玩炸了,不過看了一下期中考和作業成績,要過關應該還算是容易。

大家紛紛整理裝備往各層移動,一邊和同學說再見,一邊深知這種團體日子應該在今天就會做個總結。

大家都撤了,和 Ian 兩個人在空蕩蕩的會議室等別人來接,心情是很忐忑不安,但潛意識更希望能趕快與工作接軌,畢竟收了錢不是來讀書的,NTC 只是個小門檻。

Vincent 過來把兩人叫出去,分配了位置,結果真的如 mail 所說:暫時座位:x 樓 xx 號。

真囧,仔細想想,我的工作之路好像一路充滿笨點:識別證名字打錯,丟給 HR 重做,到現在還沒來。結果小三梯的學弟都有照片了,我識別證還是白牌,現在連座位都是暫時配置,搞什麼呢。。。

安頓好了之後,跑去問兩位學長接下來該準備什麼。請購單、SVN 申請,去找指導學長,一步步來,穩穩地走。

拷貝 codebase,porting guide,相關 spec。東西都齊了,剩下的就是這三個月磨出來的工程思維,不懂的,搜;不會的,查,心情不能游移,我要變強,我要成為首屈一指的 BIOS 工程師。

不過在達到目標之前,說的話都是屁就是了。

開 Slickedit,導入 codebase 做 tag,開 referrence,比照 code,追函數,查電氣特性,一切都是那麼熟悉,NTC 紮下來的底子果然有用。

接下來就是等 MIS 來新筆電、19" 螢幕、鍵鼠組、Dongle,林林總總的雞絲。
希望接下來的日子大家都能順利,成功就在不遠的地方,加油吧,1004。

2010年7月7日 星期三

PEIM & PPI

入口函式原型 (PeiApi.h @ EDK):

typedef
EFI_STATUS
(EFIAPI *EFI_PEIM_ENTRY_POINT)(
  IN EFI_FFS_FILE_HEADER       * FfsHeader,
  IN EFI_PEI_SERVICES          **PeiServices
  );

比較常用到的幾個函數:

(**PeiServices).LocatePpi
這個函數要注意的就是 PEI Phase 下 PPI 的存放和 DXE 下存放 PROTOCOL 略有不同,因為 PEI FOUNDATION 會用來存放 PPI DB,所以很有可能一個 DB 裡 N 個 INSTANCE,第三個參數就是讓你選第幾個 INSTANCE 用的,第一個為 0,以此類推。

Status = (**PeiServices).CreateHob (
    PeiServices,
    EFI_HOB_TYPE_XXX,
    (UINT16) (sizeof (EFI_HOB_GUID_TYPE) + PrivateDataSize),
    (VOID**) &HobBuffer
    );

這個函數要注意的地方就是 CreateHOB 他只幫你弄個 HEADER 出來,要塞 HOB 資料,要自己拉空間出來,算好 OFFSET 是要件。
函數成功的話,HobBuffer 就會指一塊空間出來給你。

(**PeiServices).CopyMem (
    (VOID*)(((UINT8*) HobBuffer) + sizeof (EFI_HOB_GUID_TYPE)),
    (VOID*) PrivateData,
    (UINTN) PrivateDataSize
    );

再來也是算 OFFSET 把資料 COPY 進去,就行了。

DXE Phase 下:

EFI_HOB_HANDOFF_INFORMATION_TABLE  *HobList;
LibGetSystemConfigurationTable (&gEfiHobListGuid, (VOID**) &HobList);

藉此取得 HOBLIST,然後再用想對應的函數把你要的 HOB 拉出來就行了,如果我上面 HOB 的 TYPE 是 EFI_HOB_TYPE_GUID_EXTENSION,同時這個 HOB.Name 有加以指定一個 GUID,那我在 DXE Phase 下就能使用下列函數把 HOB 倒出來:

GetNextGuidHob ((VOID**) &HobList, &HobNameGuid, (VOID**) &HobContent, &BufferSize);

關於為什麼 PEI Service Table 要做成雙指標,在 BIOSREN 論壇 srcore 兄做了很漂亮的解釋,這邊借轉載並改成台灣用語:

『這個問題細究起來有點意思。這裡頭有歷史原因,其實依現在看來,定義成直接指標就可以了。』

『PEI 階段中,在 Main Memory 可用之前,PEI Core 和 PEIM 都必須在 flash 上 XIP。XIP 就意味著全域變數只能讀,不能寫。所以PEI Core在 memory 可用並且把自己 Shadow 到 memory 之前,是不可以用全域變數保存自己在 Stack 中分配的 PrivateData 的位址的。』

『這就是說我們必須有一種方法能讓 PEI CORE 在 PEIM 調用 PEI Service 的時候,能從傳進來的指向 PEI Serivces Table 的間接指標得到 PEI CORE 自己私有資料的位址。』

『當初,規範定義 EFI_PEI_SERVICES ** 是期望如下的 PEI CORE 的實現:舉例說明』

typedef struct {    ...
  EFI_PEI_SERVICES *PS,
  ...
  } PEI_CORE_DATA;

『PEI_CORE_DATA 在 Stack 上分配,PS 域被初始化為 PEI Core 資料段中 PEI Service Table 的指標。』

『&PEI_CORE_DATA.PS 不就是EFI_PEI_SERVICES ** 嗎?通過 CR 就可能得到 Stack 上 PEI_CORE_DATA 的地址。』

『注意,這裡有個前提,就是 PEI Service Table 中所有域的值都是固定的,作為一個 PEI Core 的全域變數存放在資料段中。』

『對,這就是定義 EFI_PEI_SERVICES ** 的由來。』

『有人會問,如果PEI CORE按照如下實現:』

typedef struct {
  ...
  EFI_PEI_SERVICES PSTable,
  ...
} PEI_CORE_DATA;


『就是說在 PEI Core 的 PrivateData 中定義一份 PEI Service Table 而不是指向它的指標,PEI Core 在初始化的時候將自己資料段中 PEI Service Table 作為範本拷到 PEI_CORE_DATA.PSTable 中。』

『這樣,就只用定義 EFI_PEI_SERVICES * (即 &PEI_CORE_DATA.PSTable)就可以了。確實如此,但規範仍然定義 EFI_PEI_SERVICES **,是考慮到 PEI Service Table 是固定的,可以直接放在 flash 上 PEI Core image 的資料段裡,PEI CORE 私有資料裡有一個指標指向它就可以了,這樣可以省掉一些對 temporary memory 的佔用,畢竟 temporary memory 的資源還是比較寶貴的。』

『瞭解了它的最初的設計意圖,那為什麼說回過頭來看,其實定義成直接指標就可以了?』

『因為 PEI CIS 規範的修訂使得 PEI Service Table 裡面的值不再是 build time 決定之後就不變的了。PEI CIS 0.91 在 PEI Service Table 加入了 CPU IO PPI 和 PCI CFG PPI 的指標。這兩個指標必須在執行時填入,所以 PEI Service Table 不能再放在 flash 上了,它必須被放到 memory 裡,這樣它才能被修改。』

『所以現在PEI CORE的實現一般是這樣:』

typedef struct {
  ...
  EFI_PEI_SERVICES *PS,
  ...
  ...
  EFI_PEI_SERVICES PSTable,
  ...
} PEI_CORE_DATA;

『PEI Core 在初始化的時候將自己資料段中 PEI Service Table 作為模組拷到 PEI_CORE_DATA.PSTable 中,然後 PEI_CORE_DATA.PS = &PEI_CORE_DATA.PSTable。』

2010年6月27日 星期日

EFI SMM Driver Programming

SMI 分兩種,Sw 和 Hw。

作業是以 Dxe  Driver 為主,故討論 Dxe 面。

SwSmi 的主要攻略技巧就是先鎖定幾項重點:

1. SMM Driver 機制
2. 如何將 Dxe Driver 送進 SMRAM
3. 如何 Register SwSmi Notification
4. 如何控制 SwSmi Notification
5. 如何產生 SwSmi

SMM Driver 和一般 Dxe Driver 不同的地方,就是 SMM Driver 會跑兩次

第一次在 Dxe Phase,經用 EFI_SMM_BASE_PROTOCOL.InSmm () 先做驗證是否在 SMM Mode 下。

如果為 FALSE,則使用 Register () 函數執行第二次,這個函數可以看成是 SMM 下的 Driver Entry Point。

接著,在 SMM 下進入 Entry Point,再做 InSmm () 驗證,如果為 TRUE,則執行相關處理,抱括 SwSmiInputValue 的指定,利用 EFI_SMM_SW_DISPATCH_PROTOCOL  的 Register () 做函數綁定。

這部份很重要,因為 EFI_SMM_SW_DISPATCH_PROTOCOL 是由南橋 Bus 產生,所以當這邊有指定的 Value 填進 SMI Command Port 時,南橋偵測到就會觸發 SMI# 或是傳遞訊息。CPU 收到後,會有小小的 Delay (並不像書上寫的 "馬上",因為負責偵測 SMI 的組件並不一定就是接受 SMI 的組件),就會做 Function Callback,工程師就是在這塊做處理。

SmiInputValue 倒是好填,Command Port 指定就很麻煩,每個晶片組都不同,以前在寫 Win32 API 的經驗是 0xB2 for Intel ICH mostly,具體看那張板子什麼晶片組的吧....估計老師是做 AMD Chipset 的才會報 0xB0,這部份如果照單全收很容易『挫街』。

每個 codebase 都有自己的 SMI Cmd Port 定義的檔,ACPI Spec 也有,程式功夫一大抄,對 codebase 搜索強的自然吃香一點。

上面知道了,Trigger 就比較簡單,只要對特定的 SMI Cmd Port 填特定的值就行了。

進入到 SMM 後可用的服務被限制到很低,可用 SMMBASE 提供的 GetSmstLocation,拿到 SMM 的 System Table ,還是有一些東西可以用的,I/O Mem 存取之類。

比較需要注意的地方就是,因為 SMM 跨度十分廣,所以有可能 driver 存活得比自己的 producer 還久,故在使用 protocol 時務必注意,不要使用到跨度後會失效的 (例如 Boot Services 下的東西)。

另外目前了解到的知識是,如果想把 SMM 下的 driver unload,進到 SMM 解掉掛在上面的 dispatcher 就行了,如下:

mSwSmmDispatcher->UnRegister (mSwSmmDispatcher, mSwmSmmHandle);
mPbSmmDispatcher->UnRegister (mPbSmmDispatcher, mPbmSmmHandle);

只是比較奇怪的是,在 DXE 下 register 的 SMM Base 竟然在 DXE 下解不掉,Handle 都對的。。。不緊張,未來搞懂再補充回來。

9/22 補充: 為何 SMM Base 在 DXE 下解不掉,原因是在 Unload 處我把他 Handle 下注冊的函數全 UnRegister 了,根據 EFI Spec,當一 Handle 下沒任何掛載時,它會被 Handle Database 回收,所以造成在 UnRegister SMM Base 時找不到 Handle