UNIQLOCK

2010年9月21日 星期二

忙碌


最近在測試東西,目前無法整理學習資料.
這幾天學了一些新的知識和舊的複習,等有空一併整理.

2010年9月17日 星期五

PCI-Linux(觀念)

以下是Linux的PCI架構,大部分觀念是相同的,不過在匯流排編號與存取PCI和EFI BIOS是有些不同的!要釐清!避免回頭看EFI時造成混亂!

    外圍裝置互連(PCI)是一種將系統中外部裝置以結構化與可控制方式連接到起來的匯流排標準,包括系統部件連接的電氣特性及行為。
6.1 基於PCI匯流排的系統

6.1是一個基於PCI的系統示意圖。PCI匯流排和PCI-PCI橋接器在連接系統中裝置到上起關鍵作用,在這個系統中CPU和顯示裝置 被連到PCI bus 0上,它是系統中的主PCI匯流排。而PCI-PCI橋接器這個特殊PCI裝置將主匯流排PCI bus 0與下級匯流排PCI bus 1連接到一起。PCI標準術語中,PCI bus 1PCI-PCI橋接器的downstreamPCI bus 0是此橋接器的upstreamSCSI和乙太網裝置通過第二PCI匯流排連接到這個系統中。而在物理實現上,橋接器和第二PCI匯流排被集成到一塊 PCI卡上。而PCI-ISA橋接器用來支持古老的ISA裝置,圖中有一個高級I/O控制晶片來控制鍵盤、滑鼠。

6.1  PCI位址空間
CPUPCI裝置需要存取在它們之間共享的記憶體空間。這塊記憶體區域被裝置驅動用來控制PCI裝置並在CPUPCI裝置之間傳遞訊息。最典型的共享記憶體包括裝置的控制與狀態暫存器。這些暫存器用來控制裝置並讀取其訊息。例如PCI SCSI裝置驅動可以通過讀取其狀態暫存器,找出已準備好將一塊資料寫入SCSI磁盤的SCSI裝置。同時還可以在裝置上電後,通過對控制暫存器寫入訊息來啟動裝置。
CPU的系統記憶體可以被用作這種共享記憶體,但是如果採用這種方式,則每次PCI裝置存取此記憶體區塊時,CPU將被迫停止工作以等待PCI裝置完成此操作。這種方式將共享記憶體限制成每次只允許一個系統裝置存取。該策略會大大降低系統性能。但如果允許系統外圍裝置不受限制地存取主記憶體也不是好辦法。它的危險之處在於一個有惡意行為的裝置將使整個系統置於不穩定狀態。
外圍裝置有其自身的記憶體空間。CPU可以自由存取此空間,但裝置對系統主記憶體的存取將處於DMA(直接記憶體存取)通道的嚴格控制下。ISA裝置需要存取兩種位址空間:ISA I/O(輸入輸出)和ISA記憶體。而PCI裝置需要存取三種位址空間:PCI I/OPCI記憶體和PCI配置空間。CPU則可以存取所有這些位址空間。PCI I/O PCI記憶體由裝置驅動程式使用而PCI配置空間被Linux 核心中的PCI初始化程式碼使用。
Alpha AXP處理器並不能像存取系統位址空間那樣隨意存取這些位址空間,它只能通過輔助晶片組來存取這些位址空間,如PCI配置空間。Alpha AXP處理器使用稀疏位址映射策略來從系統巨大的虛擬記憶體中"竊取"一部分並將其映射到PCI位址空間。

6.2  PCI 表頭空間
6.2 PCI Configuration Header

系統中每一個PCI裝置,包括PCI-PCI橋接器在內,都有配置一個資料結構,它通常位於PCI配置位址空間中。PCI表頭空間允許系統來標識與控制裝置。表頭空間在PCI配置空間的位置取決於系統中PCI裝置的拓撲結構。例如將一個PCI顯示卡插入不同的PCI插槽,其表頭空間位置會變化。但對系統沒什麼影響,系統將找到每一個PCI裝置與橋接器並使用它們表頭空間中的訊息來配置其暫存器。
典型的辦法是用PCI插槽相對主機板的位置來決定其PCI表頭空間在配置空間中的偏移。比如主機板中的第一個PCI插槽的PCI表頭空間位於配置空間偏移0,而第二個則位於偏移256(所有PCI表頭空間長度都相等,為256Byte),其它插槽可以此類推。系統還將提供一種硬體相關機制以便PCI設置程式碼能正確的辨認出對應PCI匯流排上所有存在的裝置的PCI表頭空間。通過PCI表頭空間中的某些域來判斷哪些裝置存在及哪些裝置不存在(這個域叫製造商辨識碼: Vendor ID)。對空的PCI插槽中這個域的讀取操作將得到一個值為0xFFFFFFFF的錯誤訊息。

6.2給出了256 Bytes PCI表頭空間的結構,它包含以下域:

製造商辨識碼(Vendor ID)
用來唯一標識PCI裝置生產廠家的數值。DigitalPCI廠商標識為0x1011Intel的為0x8086

裝置辨識碼(Device ID)
用來唯一標識裝置的數值。Digital 21141快速乙太裝置的裝置標識為0x0009

指令暫存器(Command)
通過對此域的寫可以控制此裝置,如允許裝置存取PCI I/O記憶體。

狀態暫存器(Status)
此域提供PCI標準定義中此裝置的狀態訊息。

版本辨識碼(Revision ID)
用來指定此PCI裝置的版本;有時會有特殊版本,搭配此欄位,OS更能準確    的選擇專用的驅動程式。

類別型態碼(Class Code)
此域標識本裝置的類型。對於每種類型的顯示,SCSI等裝置都有標準的類別型態碼。如SCSI裝置類別型態碼為0x0100

基底位址暫存器(Base Address Registers)
這些暫存器用來決定和分配此裝置可以使用的PCI I/OPCI記憶體空間的類型,數量及位置。

中斷接腳(Interrupt Pin)
PCI卡上的四個物理接腳可以將中斷信號從介面卡上帶到PCI匯流排上。這四個接腳標準的標記分別為ABCD。中斷接腳域描述此 PCI裝置使用的接腳號。通常特定裝置都是採用硬體連接方式。這也是系統啟動時,裝置總使用相同中斷接腳的原因。中斷處理子系統用它來管理來自該裝置的中斷。

中斷線(Interrupt Line)
本裝置表頭空間中的中斷線域用來在PCI初始化程式碼、裝置驅動以及Linux中斷處理子系統間傳遞中斷處理過程。雖然本域中記錄的這個數值對於裝置驅動毫無意義。但是它可以將中斷處理過程從PCI卡上正確路由到Linux操作系統中相應的裝置驅動中斷處理程式碼中。

6.3  PCI I/OPCI記憶體位址
這兩個位址空間用來實現PCI裝置和Linux核心中裝置驅動程式之間的通訊。例如DEC21141快速乙太裝置的內部暫存器被映射到PCI I/O空間上時,其對應的Linux裝置驅動可以通過對這些暫存器的讀寫來控制此裝置。PCI顯示卡通常使用大量的PCI記憶體空間來存儲顯示訊息。
PCI系統建立並通過用PCI表頭空間中的指令暫存器來打開這些位址空間前,系統決不允許對它們進行存取。值得注意的是只有PCI配置程式碼讀取和寫入PCI配置空間,Linux裝置驅動只讀寫PCI I/OPCI記憶體位址。

6.4  PCI-ISA 橋接器
這種橋接器通過將PCI I/OPCI記憶體空間的存取轉換成對ISA I/OISA記憶體空間的存取來支持古老的ISA裝置。市場上許多主機板中同時包含幾個ISA匯流排插槽和PCI插槽。但今後對ISA裝置的向後兼容支持將逐漸減弱,最終主機板上只會有PCI插槽。早期的Intel 8086 PC就將ISA裝置的ISA位址空間固定了下來。即使在價值5000美圓的Alpha AXP 系統中其ISA軟盤控制器位址也和最早IBM PC上的相同。PCI標準將PCI I/OPCI記憶體的低端部分保留給系統中的ISA外圍裝置,另外還使用PCI-ISA橋接器實現從PCI記憶體存取到ISA記憶體存取的轉換。

6.5  PCI-PCI 橋接器
PCI-PCI橋接器是一種將系統中所有PCI匯流排連接起來的特殊PCI裝置。在簡單系統中只存在一條PCI匯流排,由於受電氣特性的限制,它所連接 的PCI裝置個數有限。引入PCI-PCI橋接器後系統可以使用更多的PCI裝置。對於高性能服務器這是非常重要的。Linux提供了對PCI-PCI橋接器的全面支持。


--------------------------------------------------------------------------------------------

6.5.1  PCI-PCI橋接器:PCI I/O PCI 記憶體窗口
PCI-PCI橋接器將PCI I/OPCI記憶體讀寫請求中的一個子集向下傳送。例如在圖6.1中,如果來自PCI 匯流排0請求是對SCSI或乙太裝置所擁有的PCI I/OPCI記憶體的讀寫,則此PCI-PCI橋接器將只需把請求從匯流排0傳遞到PCI匯流排1上;所有其它PCI I/O和記憶體位址都將被它忽略。這個過濾使得這些位址訊息不會在整個系統中擴散。為了實現這點,PCI-PCI橋接器必須編程為有某個PCI I/OPCI記憶體基底址和上限,只有在這個位址範圍內的PCI位址存取才能從主匯流排傳遞到第二匯流排。一旦系統中的PCI-PCI橋接器被設置成這樣,則只要當Linux裝置驅動程式通過這個窗口存取PCI I/OPCI記憶體空間時,此PCI-PCI橋接器就將變得透明。這樣也給Linux PCI裝置驅動編寫者提供了方便。
6.5.2  PCI-PCI橋接器:PCI配置週期及PCI匯流排記數方法
6.3 PCI配置周期類型0
6.4 PCI配置周期類型1

#註:EFI中的bit 0~1是設定成0,與圖中設定為1是不同的!EFI的PCI存取只有類型1

為了讓CPU上運行的PCI初始化程式碼能存取位於分支PCI匯流排上的裝置,必須為橋接器提供某種機制以便它可以決定是否將配置週期從主介面傳遞到其次介面。所謂一個"週期"是指出現在PCI匯流排上的一個位址。PCI標準定義了兩種PCI配置尋址格式;類型0和類型1;它們分別如圖6.36.4所示。類型0 PCI配置週期不包含匯流排號碼,同時在此PCI匯流排上對應於這個PCI配置位址的所有PCI裝置都會來對它們進行解釋。類型0 配置週期的11 bit到31 bit用來進行PCI裝置選擇。這種設計方式是讓每bit代表系統中一個不同的裝置。這時11 bit對應PCI插槽0中的PCI裝置而12 bit對硬PCI插槽1中的裝置,以此類推。另外一種方式是直接將裝置的插槽號碼寫入到31 bit到11 bit中。系統使用哪種機制依賴於系統PCI記憶體控制器。
類型1 PCI配置週期包含一個PCI匯流排號碼,同時這種配置週期將被除了橋接器外所有PCI裝置所忽略。所有發現類型1 配置週期的PCI-PCI橋接器把它們看到的位址傳遞到各自的下級PCI匯流排。至於PCI-PCI橋接器是否忽略類型1 配置週期或將其傳遞到PCI匯流排則依賴於PCI-PCI橋接器的配置方式。每一個PCI-PCI橋接器都擁有一個主匯流排介面號碼以及一個第二匯流排介面號碼。主匯流排是那個離CPU最近的PCI匯流排而第二匯流排是離它稍遠的PCI匯流排。任何PCI-PCI橋接器還包含一個次級匯流排號碼,這是所有第二匯流排介面所橋接的PCI匯流排中號碼最大的那個。或者說這個次級匯流排號碼是PCI-PCI橋接器向下連接中PCI匯流排的最大號碼。當PCI-PCI橋接器看到類型1 PCI配置週期時它將進行如下操作:
  • 如果此匯流排號碼不在橋接器的第二匯流排號碼和次級匯流排號碼之間則忽略掉它。
  • 如果此匯流排號碼與橋接器的第二匯流排號碼相同則將其轉換成類型0 配置命令。
  • 如果此匯流排號碼位於橋接器的第二匯流排號碼與次級匯流排號碼之間則將它不作改變的傳遞到第二匯流排介面中。
所以如果想尋址PCI-PCI配置例4中匯流排3上的裝置1,我們繼續從CPU中產生一個類型1 配置命令。橋接器1將其傳遞給匯流排1。橋接器2雖然忽略它但會將其轉換成一個類型0 配置命令並送到匯流排3上,在那裡裝置1將作出相應反應。


PCI配置中匯流排號碼由操作系統來分配。但是號碼分配策略必須遵循對系統中所有PCI-PCI橋接器都正確的描述:
"位於PCI-PCI橋接器後所有的PCI匯流排必須位於第二匯流排號碼和次級匯流排號碼之間"。

如果這個規則被打破,則PCI-PCI橋接器將不能正確的傳遞與轉換類型1 PCI配置命令,同時系統將找不到或者不能正確地初始化系統中的PCI裝置。為了滿足這個號碼分配策略,Linux以特殊的順序配置這些特殊的裝置。

6.6  Linux PCI 初始化過程
Linux中的PCI初始化程式碼邏輯上可分成三個部分:

PCI 裝置驅動
這個偽裝置驅動程式將從匯流排0開始搜索PCI系統並定位系統中所有的PCI裝置與橋接器。它將建立起一個描述系統拓撲結構的資料結構鏈表。另外它還為所有的橋接器進行編號。
 
PCI BIOS
這個軟體層提供了在bib-pci-bios定義中描述的服務。即使Alpha AXP沒有BIOS服務,Linux核心也將為它提供具有相同功能的程式碼。
 
PCI Fixup
系統相關補丁程式碼將整理PCI初始化最後階段的一些系統相關事物。

6.6.1  Linux 核心PCI資料結構
6.5 Linux核心PCI資料結構

Linux核心初始化PCI系統時同時也建立了反應系統中真實PCI拓撲的資料結構。圖6.5顯示了圖6.1所標識的PCI示例系統中資料結構間關係。每一個PCI裝置(包括PCI-PCI橋接器)用一個pci_dev資料結構來描述。每一個 PCI匯流排用一個pci_bus資料結構來描述。這樣系統中形成了一個PCI匯流排樹狀結構,每個樹狀結構由一些子PCI裝置組成。由於PCI匯流排僅能通過PCI- PCI橋接器(除了主PCI匯流排0)存取,所以pci_bus結構中包含一個指向PCI-PCI橋接器的指標。這個PCI裝置是PCI匯流排的父PCI匯流排的子裝置。
在圖6.5中沒有顯示出來的是一個指向系統中所有PCI裝置的指標,pci_devices。系統中所有的PCI裝置將其各自的pci_dev資料結構加入此佇列中。這個佇列被Linux核心用來迅速查找系統中所有的PCI裝置。

6.6.2  PCI裝置驅動
PCI裝置驅動根本不是真正的裝置驅動,它僅是在系統初始化時由操作系統調用的一些函數。PCI初始化程式碼將掃描系統中所有的PCI匯流排以找到系統中所有的PCI裝置(包括PCI-PCI橋接器)。
它通過PCI BIOS程式碼來檢查當前PCI匯流排的每一個插槽是否已被佔用。如果被佔用則它建立一個pci_dev資料結構來描述此裝置並將其連接到已知PCI裝置鏈表中(由pci_devices指向)。
首先PCI初始化程式碼掃描PCI匯流排0。它將試圖讀取對每一個PCI插槽中可能的PCI裝置製造商辨識碼與裝置辨識碼。當發現插槽被佔用後將建立一個 pci_dev結構來描述此裝置。所有這些PCI初始化程式碼建立的pci_dev結構(包括PCI-PCI橋接器)將被連接到一個單向鏈表 pci_devices中。
如果這個PCI裝置是一個PCI-PCI橋接器則建立一個pci_bus結構並將其連接到由pci_root指向的pci_dev結構和pci_bus樹中。PCI初始化程式碼通過類別程式碼0x060400來判斷此PCI裝置是否是一個PCI-PCI橋接器。然後Linux 核心程式碼將配置此PCI-PCI橋接器下方的PCI裝置。如果有更多的橋接器被找到則進行同樣的配置。顯然這個過程使用了深度優先演算法;系統中PCI 拓撲將在進行廣度映射前先進行深度優先映射。圖6.1Linux將在配置PCI匯流排0上的顯示裝置前先配置PCI裝置1上的乙太與SCSI裝置。
由於Linux優先搜索次級的PCI匯流排,它必須處理PCI-PCI橋接器第二匯流排與次級匯流排號碼。在下面的pci-pci匯流排號碼分配中將進行詳細討論。
配置PCI-PCI橋接器 - 指定PCI匯流排號碼
6.6 配置PCI系統:第一部分

為了讓PCI-PCI橋接器可以傳遞PCI I/OPCI記憶體或PCI配置位址空間,它們需要如下內容:


Primary Bus Number:主匯流排號碼
位於PCI-PCI橋接器上方的匯流排號碼


Secondary Bus Number:第二匯流排號碼
位於PCI-PCI橋接器下方的匯流排號碼


Subordinate Bus Number:次級匯流排號碼
在橋接器下方可達的最大匯流排號碼


PCI I/O and PCI Memory Windows : PCI I/OPCI記憶體窗口
對於PCI-PCI橋接器下方所有PCI I/O位址空間與PCI記憶體位址空間的窗口基底址和大小。


配置任一PCI-PCI橋接器時我們對此橋接器的次級匯流排號碼一無所知。不知道是否還有下一級橋接器存在,同時也不知道指派給它們的號碼是什麼。但可以使用深度遞迴演算法來對掃描出指定PCI-PCI橋接器連接的每條匯流排,同時將它們編號。當找到一個PCI-PCI橋接器時,其第二匯流排被編號並且暫時將次級號碼0xff指派給它以便對其所有下屬PCI-PCI橋接器進行掃描與指定號碼。以上過程看起來十分複雜,下面將提供一個實例以幫助理解。

PCI-PCI 橋接器號碼分配:步驟一
考慮圖6.6所顯示的拓撲結構,第一個被掃描到的橋接器將是橋1。所以橋1下方的匯流排將被編號成匯流排1,同時橋1被設置為第二匯流排1且擁有臨時匯流排號碼0xff。這意味著所有PCI匯流排號碼為1或以上的類型1 PCI配置位址將被通過橋1傳遞到PCI匯流排1上。如果其匯流排號碼為1則此配置週期將被轉換成類型0 配置週期,對於其它號碼不作轉換。這正是Linux PCI初始化程式碼所需要的按序存取及掃描 PCI匯流排1
6.7 配置PCI系統:第二部分

PCI-PCI 橋接器號碼分配:步驟二
由於Linux使用深度優先演算法,初始化程式碼將繼續掃描PCI匯流排1。在此處它將發現一個PCI-PCI橋接器2。除此橋接器2外再沒有其它橋接器存在,因此它被分配給次級匯流排號碼2,這正好和其次介面號碼相同。圖6.7畫出了此處的PCI-PCI橋接器與匯流排的編號情況。
6.8 配置PCI系統:第三部分

PCI-PCI 橋接器號碼分配:步驟三
PCI初始化程式碼將繼續掃描匯流排1並發現另外一個PCI-PCI橋接器,橋3。橋3的主匯流排介面號碼被設置成1,第二匯流排介面號碼為 3,同時次級匯流排號碼為0xff。圖6.8給出了系統現在的配置情況。含有匯流排號碼12或者3的類型1 PCI配置週期將被發送到正確的PCI匯流排。
6.9 配置PCI系統:第四部分

PCI-PCI 橋接器號碼分配:步驟四
Linux開始沿PCI匯流排3向下掃描PCI-PCI橋接器。PCI匯流排3上有另外一個PCI-PCI橋接器(橋4),橋4的主匯流排號碼被設置成3,第二匯流排號碼為4。由於它是此分支上最後一個橋接器所以它的次級匯流排介面號碼為4。初始化程式碼將重新從PCI-PCI橋接器3開始並將其次級匯流排號碼設為4。最後PCI初始化程式碼將PCI-PCI橋接器1的次級匯流排號碼設置為4。圖6.9給出了最後的匯流排號碼分配情況。

6.6.3  PCI BIOS 函數
PCI BIOS函數是一組適用於所有平台的標準過程。在IntelAlpha AXP系統上沒有區別。雖然在CPU控制下可以用它們對所有PCI位址空間進行存取。但只有Linux核心程式碼和裝置驅動才能使用它們。
 
6.6.4  PCI 補丁程式碼
Alpha AXP平台上的PCI補丁程式碼所作工作量要大於Intel平臺。
基於Intel的系統在系統啟動時就已經由系統BIOS完成了PCI系統的配置。Linux只需要完成簡單的映射配置。非Intel系統將需要更多的配置:
·         為每一個裝置分配PCI I/OPCI記憶體空間。
·         為系統中每一個PCI-PCI橋接器配置PCI I/OPCI記憶體位址窗口。
·         為這些裝置產生中斷線值;用來控制裝置的中斷處理。
下一節將描述這些程式碼的工作過程。

確定裝置所需PCI I/OPCI記憶體空間的大小
系統要查詢每一個PCI裝置需要多少PCI I/OPCI記憶體位址空間。為了完成這項工作,每一個基底位址暫存器先全部寫入1再讀取出來。裝置將不必要的bit設為0從而有效的定義所需位址空間大小。

6.10 PCI表頭空間:基底位址暫存器

有兩類基本的基底位址暫存器,一類標識裝置暫存器必須駐留的位址空間;另一類是PCI I/OPCI記憶體空間。用暫存器的bit 0來進行類型的區分。圖6.10給出了對應於PCI記憶體和PCI I/O兩種不同類型的基底位址暫存器。確定某個基底位址暫存器所需位址空間大小時,先向此暫存器寫入全1再讀取此暫存器,裝置將在某些bit填上0來形成一個二進制數表示所需有效位址空間。
以初始化DEC 21142 PCI快速乙太裝置為例,它將告訴系統需要0x100BytePCI I/O空間或者PCI記憶體空間。於是初始化程式碼為其分配空間。空間分配完畢後,就可以在那些位址上看到21142的控制與狀態暫存器。


PCI-PCI橋接器與裝置分配PCI I/OPCI記憶體
像所有記憶體一樣,PCI I/OPCI記憶體空間是非常有限甚至匱乏。非Intel系統的PCI補丁程式碼(或者Intel 系統的BIOS程式碼)必須為每一個裝置分配其所要求的記憶體。PCI I/OPCI記憶體必須以自然對齊方式分配給每一個裝置。假設一個裝置要求0xB0大小的PCI I/O空間則它必須和一個0xB0倍數的位址對齊。除此以外,對於任何指定橋接器,其PCI I/OPCI記憶體基底位址必須以在1M Byte邊界上以4K Byte方式對齊。所以在橋接器下方的裝置的位址空間必須位於任意指定裝置上方的PCI-PCI橋接器的記憶體範圍內。進行有效的空間分配是一件比較困難的工作。
Linux使用的演算法依賴於由PCI裝置驅動程式建立的描述PCI裝置的匯流排/裝置樹狀資料結構,每一個裝置的位址空間按照PCI I/O記憶體順序的升序來分配。同時再次使用遞迴演算法來掃描由PCI初始化程式碼建立的pci_bus pci_dev結構。從根PCI匯流排開始(由pci_boot指向)PCI補丁程式碼將完成下列工作:
·         使當前全局PCI I/O和記憶體的基底位址在4K,邊界在1M上對齊。
·         對於當前匯流排上的每一個裝置(按照PCI I/O記憶體需要的升序排列)
o        PCI I/OPCI記憶體中為其分配空間
o        為全局PCI I/O和記憶體基底位址同時加上一個適當值
o        授予裝置對PCI I/OPCI記憶體的使用權

·         為對於當前匯流排下方的所有匯流排週期分配空間。注意這將改變全局PCI I/O和記憶體基底位址。
·         使當前全局PCI I/O和記憶體的基底位址和邊界分別在4K1M對齊,以便確定當前PCI-PCI橋接器所需的PCI I/OPCI記憶體基底位址及大小。
·         對此PCI-PCI橋接器編程,將其PCI I/OPCI記憶體基底位址及界限連接到匯流排上。
·         打開PCI-PCI橋接器上的PCI I/OPCI記憶體存取橋接功能。這時在此橋接器主PCI匯流排上位於此橋接器PCI I/OPCI記憶體位址窗口中的任何PCI I/O或者PCI記憶體位址將被橋接到第二PCI匯流排上。

以圖6.1中的PCI系統為例,PCI補丁程式碼將以如下方式設置系統:

對齊PCI基底位址
PCI I/O基底位址為0x4000PCI記憶體基底位址為0x100000。這樣允許PCI-ISA橋接器將此位址以下的位址轉換成ISA位址週期。

顯示裝置
我們按照它的請求從當前PCI記憶體基底位址開始分配0x200000 Byte給它,這樣可以在邊界上對齊。PCI記憶體基底位址被移到0x400000同時PCI I/O基底位址保持在0x4000

PCI-PCI 橋接器
現在我們將穿過PCI-PCI橋接器來分配PCI記憶體,注意此時我們無需對齊這些基底位址,因為它們已經自然對齊。

乙太裝置
它需要0xB0 BytePCI I/OPCI記憶體空間。這些空間從PCI I/O位址0x4000PCI記憶體位址 0x400000處開始。PCI記憶體基底位址被移動到0x4000B0同時PCI I/O基底位址移動到0x40B0

SCSI 裝置
它需要0x1000BytePCI記憶體,所以它將在自然對齊後從0x401000處開始分配空間。PCI I/O基底位址仍然在0x40B0PCI記憶體基底位址被移動到0x402000

PCI-PCI 橋接器的PCI I/O和記憶體窗口
現在我們重新回到橋接器並將其PCI I/O窗口設置成0x40000x40B0之間,同時其PCI記憶體窗口被設置到0x4000000x402000之間。這樣此PCI-PCI橋接器將忽略對顯示裝置的PCI記憶體存取但傳遞對乙太裝置或者SCSI裝置的存取。

以上經過重新修改整理

2010年9月16日 星期四

PEI Phase


PEI phase

PEI的階段主要負責下列的事情。
1. 在記憶體初始化的前後做一些routine
2. HOB的資訊交代好,如:memory, FV location
3. 把控制權交代到DXE階段。
4. crisis recovery and S3, S5, normal boot
因為這些重要的因素,所以PEI要求被放在fault-tolerant FV

PEI Service

PEI foundation建立一個system table叫做PEI service table,是可以被所有的PEIMs所存取的。因為記憶體的空間只有在PEI最後的階段才可以使用,所以PEI所可以使用的資源比起後面的DXE phase變的非常的有限。因為PEI foundation和暫時可以使用的RAM,在build time並不知道,所以PEI service table的指標必須要passPEIMs entry point,同時也是PPI的一部分。

PEI foundation提供以下的服務。
1. PPI Service:管理PPI並在必要時,從temporary ROM裡頭的database呼叫他。
2. Boot Mode Services: 管理系統的boot mode( s3, s5, normal boot, diagnostic, etc.)
3. HOB Service: 創造一個HOB的資料結構,並傳到下一個DXE階段。
4. Firmware Volume Service:走訪FVFFS(Firmware File System)去找出PEIMs,並找出位在Flash deviceFirmware file
5. PEI memory Service:提供記憶體管理的服務在初始化記憶體以前,或是以後。
6. Status Code Service:提供錯誤回報的服務,例如:port 80h
7. Reset Service:提供cold or warm系統的重啟動。

PEI foundation (reside in Boot Firmware Volume, BFV):
是一個image。主要有四種功能。 
1. 成功的dispatch PEIMs .
2.維護boot mode  
3. 初始化memory   
4.呼叫DXE loader
PEI foundation 是與底層硬體有關的一層。他專門處理和指令集相依的平台結構,並且提供統一的介面給PEIMS c語言跨平台使用。
例如: IA-32平台有相對的PEI Foundation binaryItanium processor有相對應的 PEI foundation binary

PEIMS (Pre-EFI Initialization Modules)
PEIMs他類似DXE driversDXE層扮演的角色。是由PEI Foundationdispatch。在memory pool的環境裡,PEIMs是很難進行溝通的。因此在PEI裡頭提供
了一個機制讓PEIMs可以呼叫其他PEIMsinterface( PPIs )。在PEI的環境裡,我們盡可能的使用最少的資源來完成必要的工作。雖然PEIcodeSECcode
沒有編譯在一起,但是有定義說PEI foundation要放在FV0也就是BFV的地方,這樣SEC最後才能喚起PEI foundation

PPIS ( PEIMs to PEIMs Interfaces )
PPIsEFI_PEI_PPI_DESCRIPTOR的資料結構裡頭。他的指標包含了指向這個資料結構和service set的用途。 
有分做兩種:
1. Architectural PPI:提供介面給PEI foundation做使用。  
2. Additional PPI:PEIMs之間溝通所使用。
PPIs是放在相對應的PEIMs裡頭。當我們要做一個PPIs給其他程式使用時使用InstallPpi() or ReinstallPpi()
若是要使用別的PEIMsPPIs時使用LocatePpi()找出相對應的PPI

PEI Diapatcher
這是在PEI foundation裡頭的一個state machine,他會衡量每一個FV裡頭的PEIMS之間的相依性。決定哪些PEIMS應該要先Dispatch。而他們之間的相依性是由PPIs所決定,PPIs裡頭敘述了PEIMs之間的相依性。在一開始的時候PEI dispatcher會去PEI foundation裡頭的PPI Database檢查哪些PPI已經被install如果已經被install,他PEIMdependency expression會被評估成true,代表所屬的PEIMs已經可以被dispatch。在PEI dispatcher評估過全部FV裡頭所有PEIMdependency expression發現全部都是FALSE以後,代表已經沒有PEIM可以被dispatchPEI dispatcher結束,再來PEI foundationcontrol forwardDXE IPL PPI,進入DXE phase

轉自  bboy的筆記本

整理有關PEI

EFI架構中分SECPEIDXEBDS等幾個階段。
PPI
PEIM to PEIM interface)是PEI階段的概念,就是一個PEIMPEI moduleA 如果想用另外一個PEIM
  B中定義的函數,就可以在B中將這個函數組織成一個介面,然後安裝一下(就是存到 PPI database中),之後A中要用的時候,再從PPI databaselocate出來,就可以用了。

Protocol
就是DXE階段的類似PPI的東西,只不過要比PPI複雜一些,不過本質差不多。

driver
的概念比較廣,PEI階段的PEIMDXE階段的每一個模組都可以叫做driver。具體還分很多類。

首先解決為什麼需要PPI或者Protocol
這裡拿PEI Phase作例子。比如有兩個PEIM,分別為A PEIMB PEIMA PEIM實現了一個Function,比如R/W SMBus, B PEIM要用這個Function,怎麼辦?B PEIM也自己實現一個?

1:
不太符合軟體思想
2:
假如B PEIMSMBus不沾邊,這樣的話也與模組化不相符。倘若C PEIM以及N PEIM都要用,是不是都要實現?

這個時候PPI就登場了,A PEIM實現某個Function,並以PPI的形式install出來,這樣B PEIM也想使用這個Function,只需通過GUID(身份證)找到這個Function就可以使用了,不用自己再次實現。

DXE phase driver protocol 有點雷同,一樣是有個產生,有個消費。用這種觀念下去追幾組 PEI phase code 很容易明白的。


1.PEI Service Table 是如何實現對所有PEIM都可見的?
2.
我看的幾個PPIinstall的時候都是通過函數體裡面定義的
EFI_PEI_SERVICE 定義的變數調用InstallPpi函數指標來install這個PPI,這樣如何讓這個PPI對所有的PEIM都可見呢,進而locate到他?

EFI的框架有關,他的框架想儘量的減小代碼的size,但是又要有可擴展和容易維護的特性等等,這問題基本上就是一個PEI階段全局的包含大量service data database,這樣就可以增強代碼的可重用性。
example
: 如果某個完成特定功能的 service已經被 installPPI database,而你正好想用這樣一個功能,這種情況要做的就是locatePpi,在全局database找到這個函數的指標,下面就可以調用它了,就不必自己動手寫這樣一個service,減少了代碼的重複。

1. 每個PEIMEntryPoint入口參數中都有這個:
          IN EFI_PEI_SERVICES  **PeiServices;

2. PEI core
維護了一個私有資料結構中有一項為:
          PEI_PPI_DATABASE      PpiData;   所有安裝的PPI都儲存在那裡。安裝新的PPI時就往那添加,要locate一個PPI時就去那裡翻就是了。


整理自BIOSREN

Handle

作者: Gilvin


個人理解 handle 像竹竿,竹竿本身沒屬性的,下面掛衣服,他就是個曬衣架;下面掛箏幹,它就是烘食物的,什麼都不掛,竹竿橫躺在那就沒作用,就得收回箱子裡。

UEFI
  的角度來談,handle 本身並無屬性,他就是個指針,有個識別符 (Handle Key)。它下面掛著 image ,他就是個 image handle ,他掛條 device path ,他就是個 device handle,什麼都沒有,他就會被 handle database 回收。
由此可知,handle 下至少要有一個以上的 protocol 存在。所以一般建 protocol ,如果 install Protocol Interface 了,就會要求程式員指派給 handle 掛載,如果沒指派,就會創個新 handle。同理,要讓 handle 不存在,就得把他下面所有的 protocol uninstall
這是比較初步的概念,要再深入,請參照 Intel UEFI Driver Writers Guide(DWG 0.9 Draft) 關於 handle 的章節。

2010年9月7日 星期二

指標

什麼是指標?

    int *ptr;

上面的"ptr"他是一個指標型態變數,代表一個位址(address),"*ptr"代表此位址內的資料,他的資料型態是int.

做個比喻:

      有一列按照順序排列編號的櫃子從0~10由管理員管理.假設你想知道哪個 櫃子是空的,你問管理員,他便給了你一個號碼,你就按照號碼去找櫃子,或者你想直接拿你自己的東西便告訴管理員要從哪個櫃子拿給你.


上面的"管理員=ptr",他給的號碼就是"已經有地址的ptr",而拿直接拿給你的東西就是"*ptr".

這樣大概就很清楚了.說到這還要介紹"&".


     int num = 10 ;
     int *ptr ;
     ptr = &num ;
     
上面的"&"就是"把ptr的位址變成num的位址",所以*ptr的資料就是10.

還有很多指標的運用有空再補充.

2010年9月2日 星期四

EFI模組化


以下簡單整理目前對EFI模組化的了解:
  1. FD (Firmware Device):這是整個 EFI 儲存資料的地方,可以看成你電腦上那顆 HDD 也就是一個 Device.
  2. FV (Firmware Volume):在 FD 當中可以有很多個 FV , 這裡可以把 FV 看成 HDD 上你分割以後的磁碟 C:\ ; D:\ 等,這樣比較好理解.分別儲存了相關的code資料,避免不相關的code混亂.每一個FV必須遵循 FFS(Firmware File System) 的規定.

  3. FFS (Firmware File System):儲存於 FV 當中,每個 FFS 命名都有唯一性絕不重複並且以 GUID 做為名稱固定格式.每個 FFS 都有定義好的格式(Header)以便提供定義好的 Firmware Service 使用.
  4. Section:在 FFS 中分為許多段(Section),而且分為許多種不同類型,甚至可以包含一個壓縮過的FV.
這裡可以畫成一張簡圖:




了解模組化的結構後再來就是看懂 Protocol!

C 模組化


看到EFI code時發現以前學校所學得實在是太少又不夠扎實了!無法理解他的架構為何是這樣寫,有什麼含意. 
所以查了許多資料,了解到這就是對物件導向不熟悉,再來就是所謂的"模組化"! EFI就是模組化以後的產物,所以先了解什麼是模組化再去研讀EFI模組化架構,發現再去看code的時候更有感覺了!
現在要加強的就是物件導向和模組化的概念,把基礎打穩再去看code會更好更快!

以下是找到不錯的資料網址,可以連結去看看!