第六章 PCI
圖6.1是一個基于PCI的系統(tǒng)示意圖。PCI總線和PCI-PCI橋接器在連接系統(tǒng)中設(shè)備到上起關(guān)鍵作用,在這個系統(tǒng)中CPU和視頻設(shè)備 被連到PCI bus 0上,它是系統(tǒng)中的主干PCI總線。而PCI-PCI橋接器這個特殊PCI設(shè)備將主干總線PCI bus 0與下級總線PCI bus 1連接到一起。PCI標準術(shù)語中,PCI bus 1是PCI-PCI橋接器的downstream而PCI bus 0是此橋接器的up-stream。SCSI和以太網(wǎng)設(shè)備通過二級PCI總線連接到這個系統(tǒng)中。而在物理實現(xiàn)上,橋接器和二級PCI總線被集成到一塊 PCI卡上。而PCI-ISA橋接器用來支持古老的ISA設(shè)備,圖中有一個高級I/O控制芯片來控制鍵盤、鼠標及軟盤設(shè)備。
6.1 PCI地址空間CPU和PCI設(shè)備需要存取在它們之間共享的內(nèi)存空間。這塊內(nèi)存區(qū)域被設(shè)備驅(qū)動用來控制PCI設(shè)備并在CPU與PCI設(shè)備之間傳遞信息。最典型的共享內(nèi)存包括設(shè)備的控制與狀態(tài)寄存器。這些寄存器用來控制設(shè)備并讀取其 信息。例如PCI SCSI設(shè)備驅(qū)動可以通過讀取其狀態(tài)寄存器,找出已準備好將一塊數(shù)據(jù)寫入SCSI磁盤的SCSI設(shè)備。同時還可以在設(shè)備加電后,通過對控制寄存器寫入信息來啟動設(shè)備。 CPU的系統(tǒng)內(nèi)存可以被用作這種共享內(nèi)存,但是如果采用這種方式,則每次PCI設(shè)備訪問此內(nèi)存塊時,CPU將被迫停止工作以等待PCI設(shè)備完成此操作。這 種方式將共享內(nèi)存限制成每次只允許一個系統(tǒng)設(shè)備訪問。該策略會大大降低系統(tǒng)性能。但如果允許系統(tǒng)外設(shè)不受限制地訪問主存也不是好辦法。它的危險之處在于一 個有惡意行為的設(shè)備將使整個系統(tǒng)置于不穩(wěn)定狀態(tài)。 外設(shè)有其自身的內(nèi)存空間。CPU可以自由存取此空間,但設(shè)備對系統(tǒng)主存的訪問將處于DMA(直接內(nèi)存訪問)通道的嚴格控制下。ISA設(shè)備需要存取兩個地址空間:ISA I/O(輸入輸出)和ISA內(nèi)存。而PCI設(shè)備需要訪問三種地址空間:PCI I/O、PCI內(nèi)存和PCI配置空間。CPU則可以訪問所有這些地址空間。PCI I/O和 PCI內(nèi)存由設(shè)備驅(qū)動程序使用而PCI配置空間被Linux 核心中的PCI初始化代碼使用。 Alpha AXP處理器并不能象訪問系統(tǒng)地址空間那樣隨意訪問這些地址空間,它只能通過輔助芯片組來存取這些 地址空間,如PCI配置空間。Alpha AXP處理器使用稀疏地址映射策略來從系統(tǒng)巨大的虛擬內(nèi)存中"竊取"一部分并將其映射到PCI地址空間。
6.2 PCI 配置頭
圖6.2 PCI配置頭
系統(tǒng)中每個PCI設(shè)備,包括PCI-PCI橋接器在內(nèi),都有一個配置數(shù)據(jù)結(jié)構(gòu),它通常位于PCI配置地址空間中。PCI配置頭允許系統(tǒng)來標 識與控制設(shè)備。配置頭在PCI配置空間的位置取決于系統(tǒng)中PCI設(shè)備的拓撲結(jié)構(gòu)。例如將一個PCI視頻卡插入不同的PCI槽,其配置頭位置會變化。但對系 統(tǒng)沒什么影響,系統(tǒng)將找到每個PCI設(shè)備與橋接器并使用它們配置頭中的信息來配置其寄存器。 典型的辦法是用PCI槽相對主板的位置來決定其PCI配置頭在配置空間中的偏移。比如主板中的第一個PCI槽的PCI配置頭位于配置空間偏移0處,而第二 個則位于偏移256處(所有PCI配置頭長度都相等,為256字節(jié)),其它槽可以由此類推。系統(tǒng)還將提供一種硬件相關(guān)機制以便PCI設(shè)置代碼能正確的辨認 出對應PCI總線上所有存在的設(shè)備的PCI配置頭。通過PCI配置頭中的某些域來判斷哪些設(shè)備存在及哪些設(shè)備不存在(這個域叫廠商標志域: Vendor Identification field)。對空PCI槽中這個域的讀操作將得到一個值為0xFFFFFFFF的錯誤信息。 圖6.2給出了256字節(jié)PCI配置頭的結(jié)構(gòu),它包含以下域:
6.3 PCI I/O和PCI內(nèi)存地址這兩個地址空間用來實現(xiàn)PCI設(shè)備和Linux核心中設(shè)備驅(qū)動程序之間的通訊。例如DEC21141快速以太網(wǎng)設(shè)備的內(nèi)部寄存器被映射到PIC I/O空間上時,其對應的Linux設(shè)備驅(qū)動可以通過對這些寄存器的讀寫來控制此設(shè)備。PCI視頻卡通常使用大量的PCI內(nèi)存空間來存儲視頻信息。 在PCI系統(tǒng)建立并通過用PCI配置頭中的命令域來打開這些地址空間前,系統(tǒng)決不允許對它們進行存取。值得注意的是只有PCI配置代碼讀取和寫入PCI配置空間,Linux設(shè)備驅(qū)動只讀寫PCI I/O和PCI內(nèi)存地址。
6.4 PCI-ISA 橋接器這種橋接器通過將PCI I/O和PCI內(nèi)存空間的存取轉(zhuǎn)換成對ISA I/O和ISA內(nèi)存的存取來支持古老的ISA設(shè)備。市場上許多主板中同時包含幾個ISA總線槽和PCI槽。但今后對ISA設(shè)備的向后兼容支持將逐漸減弱,最終主板上只會有PCI槽。早期的Intel 8080 PC就將ISA設(shè)備的ISA地址空間固定了下來。即使在價值5000美圓的Alpha AXP 系統(tǒng)中其ISA軟盤控制器地址也和最早IBM PC上的相同。PCI標準將PCI I/O和PCI內(nèi)存的低端部分保留給系統(tǒng)中的ISA外設(shè),另外還使用PCI-ISA橋接器實現(xiàn)從PCI內(nèi)存訪問到ISA內(nèi)存訪問的轉(zhuǎn)換。
6.5 PCI-PCI 橋接器PCI-PCI橋接器是一種將系統(tǒng)中所有PCI總線連接起來的特殊PCI設(shè)備。在簡單系統(tǒng)中只存在一條PCI總線,由于受電氣特性的限制,它所連接的 PCI設(shè)備個數(shù)有限。引入PCI-PCI橋接器后系統(tǒng)可以使用更多的PCI設(shè)備。對于高性能服務(wù)器這是非常重要的。Linux提供了對PCI-PCI橋接 器的全面支持。
6.5.1 PCI-PCI橋接器:PCI I/O和PCI 內(nèi)存窗口PCI-PCI橋接器將PCI I/O和PCI內(nèi)存讀寫請求中的一個子集向下傳送。例如在圖6.1中,如果來自PCI 總線0請求是對SCSI或以太設(shè)備所擁有的PCI I/O或PCI內(nèi)存的讀寫,則此PCI-PCI橋接器將只需把請求從總線0傳遞到PCI總線1上;所有其它PCI I/O和內(nèi)存地址都將被它忽略。這個過濾使得這些地址信息不會在整個系統(tǒng)中擴散。為了實現(xiàn)這點,PCI-PCI橋接器必須編程為有某個PCI I/O及PCI內(nèi)存基址和上限,只有在這個地址范圍內(nèi)的PCI地址訪問才能從主干總線傳遞到二級總線。一旦系統(tǒng)中的PCI-PCI橋接器被設(shè)置成這樣,則只要當Linux設(shè)備驅(qū)動程序通過這個窗口訪問PCI I/O和PCI內(nèi)存空間時,此PCI-PCI橋接器就將變得透明。這樣也給Linux PCI設(shè)備驅(qū)動編寫者提供了方便。我們在稍后的討論中將看到Linux對PCI-PCI橋接器非常巧妙的配置。
6.5.2 PCI-PCI橋接器:PCI配置循環(huán)及PCI總線編號方式
為了讓CPU上運行的PCI初始化代碼能訪問位于分支PCI總線上的設(shè)備,必須為橋接器提供某種機制以便它可以決定是否將配置循環(huán)從主干接 口傳遞到其二級接口。循環(huán)是出現(xiàn)在PCI總線上的一個地址。PCI 標準定義了兩種PCI配置尋址格式;類型0和類型1;它們分別如圖6.3及6.4所示。類型0 PCI配置循環(huán)不包含總線序號,同時在此PCI總線上對應于這個PCI配置地址的所有PCI設(shè)備都會來對它們進行解釋。類型0 配置循環(huán)的11 位到31位用來進行PCI設(shè)備選擇。有種設(shè)計方式是讓每位代表系統(tǒng)中一個不同的設(shè)備。這時11位對應PCI槽0中的PCI設(shè)備而12位標識槽1中的設(shè)備等 等,如此類推。另外一種方式是直接將設(shè)備的槽號寫入到位31到11中。系統(tǒng)使用哪種機制依賴于系統(tǒng)PCI內(nèi)存控制器。 類型1 PCI配置循環(huán)包含一個PCI總線序號,同時這種配置循環(huán)將被除橋接器外的所有PCI設(shè)備所忽略。所有發(fā)現(xiàn)類型1 配置循環(huán)的PCI-PCI橋接器把它們看到的地址傳遞到各自的下級PCI總線。至于PCI-PCI橋接器是否忽略類型1 配置循環(huán)或?qū)⑵鋫鬟f到PCI總線則依賴于PCI-PCI橋接器的配置方式。每個PCI-PCI橋接器都擁有一個主干總線接口序號以及一個二級總線接口序 號。主干總線是那個離CPU最近的PCI總線而二級總線是離它稍遠的PCI總線。任何PCI-PCI橋接器還包含一個從屬總線序號,這是所有二級總線接口 所橋接的PCI總線中序號最大的那個?;蛘哒f這個從屬總線序號是PCI-PCI橋接器向下連接中PCI總線的最大序號。當PCI-PCI橋接器看到類型1 PCI配置循環(huán)時它將進行如下操作:
所以如果想尋址PCI-PCI配置例4中總線3上的設(shè)備1,我們繼續(xù)從CPU中產(chǎn)生一個類型1 配置命令。橋接器1將其傳遞給總線1。橋接器2雖然忽略它但會將其轉(zhuǎn)換成一個類型0 配置命令并送到總線3上,在那里設(shè)備1將作出相應反應。 PCI配置中總線序號由操作系統(tǒng)來分配。但是序號分配策略必須遵循對系統(tǒng)中所有PCI-PCI橋接器都正確的描敘: “位于PCI-PCI橋接器后所有的PCI總線必須位于二級總線序號和從屬總線序號之間”。 如果這個規(guī)則被打破,則PCI-PCI橋接器將不能正確的傳遞與轉(zhuǎn)換類型1 PCI配置循環(huán),同時系統(tǒng)將找不到或者不能正確地初始化系統(tǒng)中的PCI設(shè)備。為了滿足這個序號分配策略,Linux以特殊的順序配置這些特殊的設(shè)備。PCI-PCI總線序號分配一節(jié)詳細描敘了Linux的PCI橋接器與總線序號分配策略。
6.6 Linux PCI 初始化過程Linux中的PCI初始化代碼邏輯上可分成三個部分:
6.6.1 Linux 核心PCI數(shù)據(jù)結(jié)構(gòu)
圖6.5 Linux核心PCI數(shù)據(jù)結(jié)構(gòu)
Linux核心初始化PCI系統(tǒng)時同時也建立了反應系統(tǒng)中真實PCI拓撲的數(shù)據(jù)結(jié)構(gòu)。 圖6.5顯示了圖6.1所標識的PCI示例系統(tǒng)中數(shù)據(jù)結(jié)構(gòu)間關(guān)系。每個PCI設(shè)備(包括PCI-PCI橋接器)用一個pci_dev數(shù)據(jù)結(jié)構(gòu)來描敘。每個 PCI總線用一個pci_bus數(shù)據(jù)結(jié)構(gòu)來描敘。這樣系統(tǒng)中形成了一個PCI總線樹,每棵樹上由一些子PCI設(shè)備組成。由于PCI總線僅能通過PCI- PCI橋接器(除了主干PCI總線0)存取,所以pci_bus結(jié)構(gòu)中包含一個指向PCI-PCI橋接器的指針。這個PCI設(shè)備是PCI總線的父PCI總 線的子設(shè)備。 在圖6.5中沒有顯示出來的是一個指向系統(tǒng)中所有PCI設(shè)備的指針,pci_devices。系統(tǒng)中所有的PCI設(shè)備將其各自的pci_dev數(shù)據(jù)結(jié)構(gòu)加入此隊列中。這個隊列被Linux核心用來迅速查找系統(tǒng)中所有的PCI設(shè)備。
6.6.2 PCI設(shè)備驅(qū)動PCI設(shè)備驅(qū)動根本不是真正的設(shè)備驅(qū)動,它僅是在系統(tǒng)初始化時由操作系統(tǒng)調(diào)用的一些函數(shù)。PCI初始化代碼將掃描系統(tǒng)中所有的PCI總線以找到系統(tǒng)中所有的PCI設(shè)備(包括PCI-PCI橋接器)。 它通過PCI BIOS代碼來檢查當前PCI總線的每個插槽是否已被占用。如果被占用則它建立一個pci_dev數(shù)據(jù)結(jié)構(gòu)來描敘此設(shè)備并將其連接到已知PCI設(shè)備鏈表中(由pci_devices指向)。 首先PCI初始化代碼掃描PCI總線0。它將試圖讀取對每個PCI槽中可能的PCI設(shè)備廠商標志與設(shè)備標志域。當發(fā)現(xiàn)槽被占用后將建立一個pci_dev 結(jié)構(gòu)來描敘此設(shè)備。所有這些PCI初始化代碼建立的pci_dev結(jié)構(gòu)(包括PCI-PCI橋接器)將被連接到一個單向鏈表pci_devices中。 如果這個PCI設(shè)備是一個PCI-PCI橋接器則建立一個pci_bus結(jié)構(gòu)并將其連接到由pci_root指向的pci_dev結(jié)構(gòu)和pci_bus樹 中。PCI初始化代碼通過類別代碼0x060400來判斷此PCI設(shè)備是否是一個PCI-PCI橋接器。然后Linux 核心代碼將配置此PCI-PCI橋接器下方的PCI設(shè)備。如果有更多的橋接器被找到則進行同樣的配置。顯然這個過程使用了深度優(yōu)先搜索算法;系統(tǒng)中PCI 拓撲將在進行廣度映射前先進行深度優(yōu)先映射。圖6.1中Linux將在配置PCI總線0上的視頻設(shè)備前先配置PCI設(shè)備1上的以太與SCSI設(shè)備。 由于Linux優(yōu)先搜索從屬的PCI總線,它必須處理PCI-PCI橋接器二級總線與從屬總線序號。在下面的pci-pci總線序號分配中將進行詳細討論。
配置PCI-PCI橋接器 - 指定PCI總線序號
為了讓PCI-PCI橋接器可以傳遞PCI I/O、PCI內(nèi)存或PCI配置地址空間,它們需要如下內(nèi)容:
配置任一PCI-PCI橋接器時我們對此橋接器的從屬總線序號一無所知。不知道是否還有下一級橋接器存在,同時也不知道指派給它們的序號是什么。但可以使 用深度優(yōu)先遍歷算法來對掃描出指定PCI-PCI橋接器連接的每條總線,同時將它們編號。當找到一個PCI-PCI橋接器時,其二級總線被編號并且將臨時 從屬序號0xff指派給它以便對其所有下屬PCI-PCI橋接器進行掃描與指定序號。以上過程看起來十分復雜,下面將提供一個實例以幫助理解。
6.6.3 PCI BIOS 函數(shù)PCI BIOS函數(shù)是一組適用于所有平臺的標準過程。在Intel和Alpha AXP系統(tǒng)上沒有區(qū)別。雖然在CPU控制下可以用它們對所有PCI地址空間進行訪問。但只有Linux核心代碼和設(shè)備驅(qū)動才能使用它們。
6.6.4 PCI 補丁代碼在Alpha AXP平臺上的PCI補丁代碼所作工作量要大于Intel平臺。 基于Intel的系統(tǒng)在系統(tǒng)啟動時就已經(jīng)由系統(tǒng)BIOS完成了PCI系統(tǒng)的配置。Linux只需要完成簡單的映射配置. 非Intel系統(tǒng)將需要更多的配置:
下一節(jié)將描敘這些代碼的工作過程。
確定設(shè)備所需PCI I/O和PCI內(nèi)存空間的大小系統(tǒng)要查詢每個PCI設(shè)備需要多少PCI I/O于PCI內(nèi)存地址空間。為了完成這項工作,每個基地址寄存器將被寫上全1并讀取出來。設(shè)備將把不必要的地址位設(shè)為0從而有效的定義所需地址空間。
有兩類基本的基地址寄存器,一類標識設(shè)備寄存器必須駐留的地址空間;另一類是PCI I/O或PCI內(nèi)存空間。此寄存器的0位來進行類型的區(qū)分。圖6.10給出了對應于PCI內(nèi)存和PCI I/O兩種不同類型的基地址寄存器。 確定某個基地址寄存器所需地址空間大小時,先向此寄存器寫入全1再讀取此寄存器,設(shè)備將在某些位填上0來形成一個二進制數(shù)表示所需有效地址空間。 以初始化DEC 21142 PCI快速以太設(shè)備為例,它將告訴系統(tǒng)需要0x100字節(jié)的PCI I/O空間或者PCI內(nèi)存空間。于是初始化代碼為其分配空間。空間分配完畢后,就可以在那些地址上看到21142的控制與狀態(tài)寄存器。
為PCI-PCI橋接器與設(shè)備分配PCI I/O與PCI內(nèi)存象所有內(nèi)存一樣,PCI I/O和PCI內(nèi)存空間是非常有限甚至匱乏。非Intel系統(tǒng)的PCI補丁代碼(或者Intel 系統(tǒng)的BIOS代碼)必須為每個設(shè)備分配其所要求的內(nèi)存。PCI I/O和PCI內(nèi)存必須以自然對齊方式分配給每個設(shè)備。比如如果一個設(shè)備要求0xB0大小的PCI I/O空間則它必須和一個0xB0倍數(shù)的地址對齊。除此以外,對于任何指定橋接器,其PCI I/O和PCI內(nèi)存基址必須以在1M字節(jié)邊界上以4K字節(jié)方式對齊。所以在橋接器下方的設(shè)備的地址空間必須位于任意指定設(shè)備上方的PCI-PCI橋接器的內(nèi)存范圍內(nèi)。進行有效的空間分配是一件比較困難的工作。Linux使用的算法依賴于由PCI設(shè)備驅(qū)動程序建立的描敘PCI設(shè)備的總線/設(shè)備樹,每個設(shè)備的地址空間按照PCI I/O內(nèi)存順序的升序來分配。同時再次使用遍歷算法來遍歷由PCI初始化代碼建立的pci_bus和 pci_dev結(jié)構(gòu)。從根PCI總線開始(由pci_boot指向)PCI補丁代碼將完成下列工作:
以圖6.1中的PCI系統(tǒng)為例,PCI補丁代碼將以如下方式設(shè)置系統(tǒng):
File translated from TEX by TTH, version 1.0. 1996-1999 David A Rusling copyright notice. |
|
來自: WUCANADA > 《hardware》