UNIQLOCK

2010年10月29日 星期五

PCI Expansion ROM(3)


Execution of initialization code
在大多數情況下,系統的上電自測試(POST)程式碼對插接的PCI設備的處理與對那些焊接在母板上的設備的處理相同。但對Expansion ROM的處理是不同的。POST程式碼分兩步檢測一個選項ROM是否存在:其中第一步確定設備是否在配置空間中實現了一個Expansion ROM基底位址暫存器,如果該暫存器存在,POST就必須將ROM映射到位址空間中未用的部分並將使能位置1;第二步是檢查前兩個位元組是否為標籤值AA55H,如果是,就表示存在一個ROM,否則就說明該設備上沒有ROM
     如果檢測到設備上有一個ROMPOST就必須在該ROM中尋找一個具有合適的程式碼類型的映射,同時該映射中的供應商識別和設備識別欄位也要與設備配置空間的相應欄位吻合。當這個合適的映射找到之後,POST就從ROM中拷貝適當數量的資料到RAM中,然後執行該設備的初時化程式碼。至於拷貝多少資料量,以及怎樣執行設備初始化程式碼,要由程式碼類型來決定。
系統中的POST程式碼根據初始化長度欄位所指定的位元組數,將資料從ROM拷貝到RAM中,然後以03H處的值作為入口點調用INIT功能。在INIT返回之前,POST程式碼應將上述拷貝過來的RAM區保持為可寫狀態,以使INIT程式碼能在該區存放一些靜態資料,並調整運行存儲分配,使得系統在運行時耗費較少的空間。
當涉及到Expansion ROM時,在系統POST程式碼中有一套為PC相容而設的特別步驟,它們是:
1.       映射並使能Expansion ROM到記憶體位址空間的一個空區域。
2.       ROM中尋找一個適當的映射區,並根據初始化長度指定的位元組數從ROM中拷貝資料到RAM相容區。
3.       使Expansion ROM基底位址暫存器失效。
4.       保持RAM區為可寫狀態並遠端調用(FAR CALLINIT功能。
5.       INIT返回之後,利用偏移位址02H處的值決定運行時需要多少記憶體空間。
在系統引導之前,POST程式碼必須把含有Expansion ROM程式碼的RAM區變為唯讀區。
POST程式碼必須用特殊的方法去處理帶有Expansion ROMVGA設備,VGA設備的Expansion BIOS必須拷貝到0C0000H處。VGA設備的識別是通過檢查設備配置空間中的分類程式碼欄位來實現的。
6.        ROM的初始化(INIT)程式碼
PC相容的Expansion ROM中,含有一個INIT功能,用來負責I/O設備的初始化以及為運行操作做準備。由於在執行INIT功能時程式碼所駐留的RAM區被保持為可寫狀態,因此,允許PCI-Expansion ROM中的INIT功能具有某種Expansion 能力。
INIT功能執行期間,INIT功能可以在它的RAM區中存放靜態參數。運行BIOS或設備驅動程式便可以調用這些參數。但是該RAM區在運行期間是不能寫入的。
INIT功能還可以調整它在運行期間所消耗的RAM總量,具體方法是修改映像中位於偏移位址02H處的長度欄位,從而能夠保持Expansion ROM區(0C0000H~0DFFFFH)佔用有限的記憶體資源。例如,一個設備的Expansion ROM為其初始化和運行程式碼要求24KB的存儲空間,但運行程式碼只要求8KBROM中的映像所表現出的長度是24KB。所以,POST程式碼將全部資料拷貝到RAM中。然後,當運行到INIT功能時,它可以調整長度欄位使其減為8KB。當退出INIT功能時,POST程式碼看到的運行長度為8KB並且可以將下一個Expansion BIOS拷貝到最佳位置。
INIT功能負責保證整個映像長度的檢查的校驗和是正確的。如果它以任何方式修改了RAM區,就必須計算出新的校驗並存入映像中。INIT功能不能以任何方式修改系統記憶體(除RAM區域的INIT功能),除非它對配置記憶體採用適當的協定或BIOS服務。
如果要將INIT功能從Expansion ROM中移去,只要向初始化長度欄位(偏移位址02H)寫入0即可。在這種情況下,不產生檢查。
    INIT從入口處可以得到三個參數:設備的匯流排編號(Bus)、設備編號(Device)和功能編號(Function)。這些參數可以用來訪問正在被初始化的設備。它們被傳送到X86的寄存器AX中,其中AH包含匯流排編號,AL的高5位元為設備編號,AL的低3位元是功能編號。
    映射結構的三個長度:
一個PC相容的映射有三個長度與映射結構有關:映射長度、初始化長度和運行長度。
映射長度是映射的總長度,它必須大於或等於初始化長度。
初始化長度表示映射中所含的初始化程式碼和運行程式碼兩部分的總和,這也是在執行初始化例行程式之前,POST程式碼要拷貝到RAM區中的資料總量。初始化長度必須大於或等於運行長度。拷貝到RAM區中的初始化資料的檢查和必須為0
運行長度是指映射中所包含運行程式碼的總量。這也是系統運行時POST程式碼保留在RAM區中的資料量。這部分映射的檢查和必須為0.
PCI資料結構必須包含與映射的運行分配部分中,否則他必須包含於初始化部分


以上資料來自 老公公  百度空間
Peter Pan 修改整理.

PCI Expansion ROM(2)

這裡主要寫對ROM shadowing RequiredROM content
Expansion ROM2
2.1 ROM Shadowing Required
why ROM為什麼需要映射?它又被映射到什麼地方呢?
      PCI SPEC中要求device's expansion rom code 是不能夠在它自己的ROM裡執行的,它必須被copyRAM中去執行。這樣做的原因是:
& ROM的訪問效率是很慢的,無論何時從ROM中去指令執行性能都是很長的。
& 一旦在ROM中的driver 初始化部分已經被執行(在ram中),它可能被丟棄(比如init code copy),在ram中的code image 能夠變小,使RAM中只存在需要的code。這樣執行剩下的code 效率更高。
    一旦device rom被檢查存在,這個配置軟體就copy code image main memory並且disabled rom address decoder(把Expansion rom base address register bit 0 設為0)。在非pc環境中copycode 可能在記憶體中的任何位置。
         pc環境中,則ROM CODE image 必須被拷貝到規定的device ROMS 位址範圍:000C0000H---000DFFFFH,如果在class code(見配置空間暫存器對其描述)指明四個VGA’s device rom, 那麼它的code image必須拷貝進開始位址:000C0000h
  
哪一個image需要copy?這就要瞭解下ROM content的結構了
  
2.2 ROM Content
    2.2.1概述
   PCI Device ROM中可以包含超過一個code image(如圖 2.1),一個ROM包含適合各種廠家的device各種各樣的code image。所以被copy進主存的image 必須匹配這個function ID. 所以每個code image 包含:
-Vendor ID Device ID
-Class code。這個主要是對class device ,具體參考class code register

2.1 Multiple code images contain in one device ROM
          每一個image都是開始於一個512位元組邊界並且包含有PCI擴展ROM頭標,其開始位置決定於前一個image的長度。最後一個image在其頭標中需要標明(在位於擴展ROM data structure offset 14H的一個byte,其bit 0-bit 6 保留即全為0bit 7=1-->last imagebit 7=0-->not last image。)。
           
每一個ROM映射中所要求的資訊分為四個不同的區域:一個區域是ROM頭標,應該放在ROM映射的開始處;二個區域是PCI資料結構,必須放置在映射的第一個64KB處(為啥是64K呢?清注意頭標區 offsetPCI資料結構指標,是一個2bytes 16bits的指標-->即最大offset 64K)。圖2.2表示了擴展ROM中一個映射的典型佈局。三個是Runtime code,四個是initialization code

2.2 ROM IMAGE的典型佈局




2.     ROM頭標
ROM頭標格式如下:

ROM標識為55H 0AAHPOST程式在檢測ROM開頭兩個位元組,只要檢測到55 AA就知道知道有擴展ROM了。加電自檢程式(POST)當檢測到合適的ROM後,會直接調用偏移+3處的指令,故在此位置上(偏移+3處)總是放置一個跳轉指令。
3.     PCI資料結構
PCI資料結構格式如下:

供應商識別碼和設備識別碼必須和PCI配置空間中的相應欄位一樣,否則也不會執行映射中指令的。設備分類代碼雖然也要和PCI配置空間中資料一致,但是POST不會進行強行檢測,也就是說:這不是ROM映射是否執行的必須條件。POST主要根據該欄位(55AA)來判斷設備類型,以進行一些特別處理,例如,當為VGA設備時,POST就會將ROM 中的BIOS拷貝至RAM0C0000H處。

以上資料來自 老公公  百度空間
Peter Pan 修改整理.

PCI Expansion ROM(1)

1.Expansion ROM概述:
1.1 ROM存在的目的
           Expansion ROM的存在主要是為了Device可以在boot過程中使用,因為在Boot過程中,OS還沒有裝載進Memory,所以就沒有可能把任何 driverDISK拷貝進入Memory,所以devices在啟動過程中的enableinitialization都是通過 Expansion ROM裡的image來完成的。
1.2 Expansion ROM base address register
          這個暫存器位於配置空間的offset 30H位置處。它就是用來反應Expansion ROM的一些資訊和資源需求的。
      1.2.1   位址映射
         通過這個暫存器來判斷確定塊容量和分配位址範圍。首先判斷映射類型(即需要資源是Memory? I/O?),此暫存器的bit 0為唯讀位,用來標示映射類型:0-->記憶體空間,1-->I/O位址空間。

        * 記憶體基底位址暫存器:可以使32bits也可以是64bits的暫存器。其bit 1bit 2的對應類型如下表:
2和 位1
映射類型
00
基底位址暫存器為32位寬,可以在32位表示的記憶體位址範圍的任何地方進行映射
01
保留
10
基底位址暫存器為64位寬,可以映射到以64位表示的記憶體空間的任何地方
11
保留
至於bit 3,若資料是可預取的,就應將它設為1,否則清0。該暫存器的區域各位用來將一設備映射到記憶體空間。
基底位址暫存器中用於32位記憶體解碼器的bit31::4】和用於64位記憶體解碼器的bit63::4】稱為基底位址單元。它的作用是:確定與解碼器相關的記憶體的大小;給解碼器分配地址。如果記憶體設備需要小於4K的存儲空間,規範建議記憶體範圍強行設為4KB
*I/O基底位址暫存器
映射到I/O空間的基址暫存器寬度只能是32bits的暫存器(why?因為I/O port就那一點)。

其中bit 0值為1(用硬體實現的),bit 1為保留位並且其讀出值必須為0,其餘各位用來把設備映射到I/O空間。
當基底位址暫存器bit 0的返回值為1時,表示這是一個I/O解碼器,而不是記憶體解碼器,bit 1保留並總是返回0,【31::2】是基底位址單元,並用於確定需要的I/O塊容量,設置它的起始位址。
1.2.2塊容量和分配位址範圍
要確定記憶體的容量或I/O空間大小可以通過簡單地向基底位址暫存器寫入全“1”並回讀來確定。若返回一個是0值,則表示未實現基底位址暫存器;如果讀回地值為非0,則編程人員通過從基底位址單元的最低有效位元向上掃描返回值以找到第一個被成功置“1”bit來確定所需記憶體的容量或I/0空間的大小。假設暫存器的bit 0是一個加權二進位1,那麼bit 1的值就是2bit 2的值就是4,依此類推。這樣,在基底位址單元中第一個發現的1所對應的加權二進位值便是所需的空間數。這也是暫存器的第一個可R/W bit,在它之上的所有位均定義為可R/W bit。這個資訊發現後,程式將32/64位元記憶體起始位址或32I/O位址寫入基底位址暫存器中。
例如:寫入FFFFFFFFH,返回FFF00000H,這表明:
Bit 0=0,表示這是一個memory address decoder
Bits[2:1]=00,表示可以映射到32bits 記憶體任意位置。
Bit 3=0,表明這不是一個可預取的memory
Bit 20=1,表明bit 20是第一個值為1bit。那麼它的權為:220次方=1 048 576,這是一個記憶體大小1MB的解碼器。

以上資料來自 老公公  百度空間
Peter Pan 修改整理.

2010年10月28日 星期四

IE & FIREFOX

剛剛切換成IE時才發現,原來IE看到的字體那麼小,而且上面的時鐘版面也不完全.
因為個人是習慣使用FIREFOX,沒遇過那些問題.所以請多多見諒了!

PS:請有不同看法或是見解的可以留言給我唷!

Duplicate

當機器在測試的時候會碰到問題,也許不是你遇到的,而你需要知道這個問題是如何出現時,就需要duplicate.

因為不知道root cause,所以必須要有Steps to Reproduce.在成功 duplicate時又分為:
1. 100%
2. fail rate
這兩種.

如果是第一種當然是最好的了!可以直接開始分析找出root cause知道是誰該負責,或是可以開始debug.但如果是第二種時就非常麻煩了!就算已經可以duplicate,可能是幾百、幾千次以後才有一次fail,這時只能先推測發生的原因可能有哪些,再找個方向去做實驗,直到無法duplicate,上一次的實驗有可能就含有fail的原因.

所以在duplicate是要有方法的,不是一股腦的一直測試就行的,O.SDriverBIOSEC的版本不一樣就可能無法duplicate.第一步驟就是環境要相同.


當釐清問題後,是自己的部分造成時就要分析是否有更動過什麼,再去做實驗,雖然現在沒有任何部分是我更動的(),但也是可以學著找出發生問題的部分是跟哪些code有關,從他們updatecode去瞭解修改原因,這樣就不會只是duplicate卻不知道如何解決了!(雖然也不是我解決)

2010年10月26日 星期二

MACRO

常常看大陸的文章,他們都直接翻譯,所以看起來會滿頭問號.
MACRO他們直譯成"",宏定義;宏指令之類的.看得霧煞煞!
我們這就翻得很好了,"巨集"!

從維基百科所解釋的來看: 電腦科學裡的巨集是一種抽象(Abstraction),它根據一系列預定義的規則替換一定的文字範式。直譯器或編譯器在遇到巨集時會自動進行這一範式替換。

現在的EFI當中可以看到幾乎所有的名稱都被定義了,避免在讀寫code時無法理解感到混亂.可以了解到每個檔案內容和每一段code是在做什麼,知道他是引用誰或是被誰引用,可以快速找到相關檔案(現在的環境也很厲害!).

所以想看得懂EFIcode就要先把C的應用基礎加強!目前很缺這個!不然看不懂他的使用!

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裝置的存取。

以上經過重新修改整理