把一段裝置資源接到 CPU 的存取路徑
選擇步驟後,圖中對應的元件與下方解說會加上重點標示。完整圖與所有解說會持續顯示;也可用方向鍵切換步驟。
先看最內層 BAR 的需求,再向外檢查沿途視窗。這是配置及檢查順序;圖中的框表示位址範圍關係。
- BAR
- Base Address Register,基底位址暫存器
作者的位址涵蓋示意。CPU 位址與 BAR 中的位址是否相同,需依平台的位址轉譯判定。
- BAR
- Base Address Register,基底位址暫存器
BAR(Base Address Register,基底位址暫存器)提供裝置資源的型別與配置入口。軟體先取得需求,再分配對齊的位址,讓沿途 Bridge Window 涵蓋它,最後開啟適用的解碼。
前情提要:Chapter 3 已找到這個 Function。現在處理它的資源,尚未開始驅動程式的工作流程。
規格路線:Figures 7-10–7-12/Table 7-9 → Figure 7-14 與 Bridge Window 條文 → Figure 7-5/Table 7-4 的 Command 控制。
先讀型別與需求
本章的大小探查是受配置者控制的初始化情境;先停止相關解碼並保存原值,不對使用中的裝置任意寫全 1。
讀 Memory/I/O 類型、32/64-bit 與 Prefetchable 屬性,再用適用方式取得大小。64-bit Memory BAR 會使用相鄰兩個欄位。
Base 6.0 §7.5.1.2.1/Figures 7-10–7-12/Table 7-9,pp.928–930。
分配可容納且對齊的範圍
保留 BAR 的屬性位元,確認位址寬度、對齊及平台轉譯;下方算例逐項驗算。
依大小與型別選擇平台可提供的位址,再把基底寫入 BAR。大小遮罩與實際配置值分別用來回答需求及位置。
Base 6.0 §7.5.1.2.1,pp.929–930;Firmware 3.3 §3.1.1,p.32。
讓沿途視窗涵蓋目標
分別依視窗型別、粒度與上下界編碼處理;圖沒有把 Prefetchable 屬性當成 CPU 快取設定。
沿 Endpoint 到根部檢查每層 Memory 或 Prefetchable Window;任一層沒有涵蓋目標範圍,都會影響轉送。
Base 6.0 §7.5.1.3.8–§7.5.1.3.10,pp.940–941。
開啟本次路徑需要的解碼
MSE 是 Memory Space Enable,控制 Memory Space 存取;BME 是 Bus Master Enable,關係到 Function 發起交易。兩者的用途分開判讀。
完成資源配置後,依路徑設定 Memory Space Enable。裝置發起交易另看 Bus Master Enable;後續 DMA 準備由 Chapter 6 接續。
- DMA
- Direct Memory Access,裝置直接存取記憶體
Base 6.0 §7.5.1.1.3/Figure 7-5/Table 7-4,pp.918–920。
圖中元件
Endpoint 的 BAR
本圖最內層是這個 Function 提供的裝置資源範圍。
基本責任
BAR 保存配置資訊,本身不是一塊可供 CPU 任意讀寫的資料緩衝區。
1. BAR 是一個資料緩衝區,還是一份資源描述?
BAR 是 Base Address Register,位於 Function 的 Configuration Header。它讓軟體辨認資源類型、查出需求大小,並寫入分配的基底位址。裝置的工作暫存器或資料區,則位於這個資源所映射的位置;讀 BAR 本身不等於讀到那些內容。
| 設定 | 它描述什麼? | 不要拿它代替什麼? |
|---|---|---|
| Secondary/Subordinate Bus Number | 設定拓樸中的下游 Bus 範圍。 | 不能代替 Memory Request 的位址路由範圍。 |
| Endpoint 的 BAR | 這個 Function 自己要解碼的資源範圍。 | 不能單獨保證沿途 Bridge 會轉送。 |
| Bridge 的 Memory/Prefetchable Window | Bridge 要轉送到下游的位址範圍。 | 不是 Bridge 自己的 BAR,也不表示窗口內每個 byte 都有裝置回應。 |
先用 Figure 7-10 找 Type 0 Header 的 BAR 位置;遇到 Bridge,則用 Figure 7-14 分開看 BAR 與 Window。Type 0 Header 提供六個 DWORD 位置,從 0x10 到 0x24;Type 1 Header 只有兩個。64-bit BAR 會佔用連續兩個位置,所以「六個位置」不等於「六個獨立資源」。
規格位置
Base 6.0 §7.5.1.2.1,Figure 7-10,pp.928–930;§7.5.1.3,Figure 7-14,p.936;Memory/Prefetchable Window:§7.5.1.3.8–§7.5.1.3.10,pp.940–941。
2. 先讀哪幾個 bits,才知道後面怎麼算?
先看 BAR bit 0。為 0 時用 Memory BAR 格式,為 1 時用 I/O BAR 格式。兩者的位址遮罩不同,不能一律清掉低 4 bits。
| 格式 | 類型與屬性 | 位址部分 |
|---|---|---|
| Memory BAR | bit 0=0;bits 2:1 的 00b 表示 32-bit,10b 表示 64-bit;bit 3 是 Prefetchable。 | 低 DWORD 的 bits 31:4;若為 64-bit,下一個 DWORD 接成 bits 63:32。 |
| I/O BAR | bit 0=1,bit 1 保留;這個格式固定是 32-bit。 | bits 31:2。特定 16-bit I/O 相容配置另有大小計算規則。 |
Prefetchable 表示這段資源容許規格列出的讀取與合併行為,例如讀取沒有副作用,且可接受寫入合併。它不是「CPU 現在一定有快取」的狀態位,也不能拿來推論應用程式實際帶寬。
對本章的一般 PCI Express Endpoint,設為 Prefetchable 的 BAR 必須支援 64-bit addressing;非 Prefetchable BAR 可以是 32-bit。此類 Endpoint 也不得依賴 OS 配置 BAR 宣告的 I/O 資源,且不得發出 I/O Requests。本例因此只用 Memory BAR;I/O 格式保留在這裡,是為了讓讀者能辨認相容機制。
欄位與類型限制
Base 6.0 §7.5.1.2.1,Figures 7-11/7-12、Table 7-9,pp.929–930;一般 Endpoint 的限制見 §1.3.2.2,p.105。Memory Type 01b、11b 在本版為 Reserved;不要把舊版低於 1 MB 的編碼無聲套進本例。
3. 寫全 1 再讀回,為什麼能知道大小?
BAR 的位址欄位中,低位元有多少固定為 0,反映需要的自然對齊大小。規格提供的探測方法,是先停止對應位址解碼,保存原 BAR,寫全 1 再讀回;遮掉類型位元後,對指定寬度取反加 1。探測完要還原原值,才能回到原配置;若接著重新分配資源,也必須在重新啟用前完成一致的配置。
| 位置 | 探測前保存值 | 寫入全 1 後的讀回值 | 本例角色 |
|---|---|---|---|
| BAR0,0x10 | 0x00000000 | 0xFFFFC000 | 32-bit、非 Prefetchable Memory BAR。 |
| BAR2,0x18 | 0x0000000C | 0xFFE0000C | 64-bit、Prefetchable BAR 的低 DWORD。 |
| BAR3,0x1C | 0x00000000 | 0xFFFFFFFF | 上一列的高 DWORD,不能當成另一個 BAR 解碼。 |
本例另外假設 BAR1、BAR4、BAR5 未實作;未實作 BAR 固定回 0。探測前的 BAR0 也是 0,卻是有效、尚未分配基底的資源,所以不能只看初始值為 0,就宣告 BAR 不存在。
BAR0:先限定 32 bits,再取反
address_mask = 0xFFFFC000 & 0xFFFFFFF0 = 0xFFFFC000
size = ((~address_mask) & 0xFFFFFFFF) + 1
= 0x00004000 = 16 KiB
這表示基底必須以 16 KiB 對齊。若用沒有固定位寬的運算工具,必須把取反限制在 32 bits;直接使用無限精度的負整數結果,會算錯大小。未實作 BAR 的全 0 探測結果也不能拿去套公式,然後宣布要分配 4 GiB。
BAR2/BAR3:兩個 DWORD 是一個 64-bit 問題
address_mask = (0xFFFFFFFF << 32) | (0xFFE0000C & 0xFFFFFFF0)
= 0xFFFFFFFFFFE00000
size = ((~address_mask) & 0xFFFFFFFFFFFFFFFF) + 1
= 0x00200000 = 2 MiB
低 DWORD 的 0xC 解成 Memory Type=10b、Prefetchable=1。軟體探測時必須處理兩個 DWORD,保存與還原也要包含兩者;走訪 BAR 清單時,下次應前進到 BAR4,不能把 BAR3 的全 1 再當成 I/O BAR。
探測流程與算式依據
Base 6.0 §7.5.1.2.1,pp.929–930,尤其 p.930 的「Sizing a 32-bit Base Address Register Example」與其後 64-bit 說明。自然對齊、2 的冪次大小、未實作 BAR 回 0 均見同頁。本例沒有 Resizable BAR;若有該能力,還要確認目前選用的大小與相關規則,不能直接沿用本例固定需求。
4. 知道大小後,可以挑任意空位填進去嗎?
還要同時滿足平台允許的資源範圍、BAR 位址寬度、大小對齊,以及與其他分配不重疊。以下基底是作者從假設的可用資源中選出的結果;它們不是規格推薦的固定位址。
| 資源 | 分配的 PCI 位址範圍(含首尾) | 配置後的 BAR 值 |
|---|---|---|
| BAR0:16 KiB | 0x80004000–0x80007FFF | 0x10:0x80004000 |
| BAR2/3:2 MiB | 0x0000000120000000–0x00000001201FFFFF | 0x18:0x2000000C;0x1C:0x00000001 |
案例推演:BAR0 的尾端是 0x80004000 + 0x4000 - 1 = 0x80007FFF;BAR2/3 的尾端是 0x120000000 + 0x200000 - 1 = 0x1201FFFFF。兩個基底各自是所需大小的整數倍。64-bit BAR 的高 DWORD 為 1,讓這段資源位於 4 GiB 以上;這不代表裝置真的有 4 GiB RAM,也不表示一次交易要搬 64 bits 的資料。
接著假設裝置介面規格定義 BAR0 內的 +0x120 是可讀 Register,則它在 PCI 位址空間的位置為 0x80004120。本例另假設 Host Bridge 對這段 MMIO 使用相同的 CPU/PCI 位址,因此 CPU 也使用 0x80004120。這個相同映射是教學前提;Firmware 3.3 §3.1.1 明確容許兩種視角不同,不能把所有平台都當成如此。
- MMIO
- Memory-Mapped I/O,記憶體映射輸入輸出
來源與作者假設
Base 6.0 §7.5.1.2.1,pp.928–930。PCI Firmware 3.3 §3.1.1,p.32 說明 non-identity MMIO mapping。兩個可用範圍、相同映射,以及裝置內 +0x120 Register 都是本例假設;PCIe Base 並未定義這個裝置專屬 Register 的功能。
5. Endpoint 的 BAR 已經填好,為什麼還可能收不到?
位址要求還得穿過沿途的 Bridge。沿 Chapter 1 的拓樸,到目標要經過 Root Port 00:01.0、Switch Upstream Port 01:00.0,以及 Downstream A 02:01.0。三者的相關 Window 都必須涵蓋目標資源;只改最靠近 Endpoint 的那一個,無法補上上游缺少的範圍。
| Type 1 Header 欄位 | offset/寬度 | 假設配置值 |
|---|---|---|
| Memory Base/Memory Limit | 0x20/0x22,各 16 bits | 0x8000/0x8000 |
| Prefetchable Memory Base/Limit | 0x24/0x26,各 16 bits | 0x2001/0x2011 |
| Prefetchable Base Upper/Limit Upper | 0x28/0x2C,各 32 bits | 0x00000001/0x00000001 |
先看 Figure 7-14 定位,再讀 §7.5.1.3.8–§7.5.1.3.10。Memory Base/Limit 的高 12 bits 對應 Address[31:20];Base 省略的低 20 bits 補 0,Limit 則補 1。因此 Limit 不是排除式邊界,不能再少算或多算一個 byte。
Memory Base = (0x8000 & 0xFFF0) << 16 = 0x80000000
Memory Limit = ((0x8000 & 0xFFF0) << 16) | 0xFFFFF = 0x800FFFFF
Prefetchable Base = (1 << 32) | ((0x2001 & 0xFFF0) << 16)
= 0x120000000
Prefetchable Limit = (1 << 32) | ((0x2011 & 0xFFF0) << 16) | 0xFFFFF
= 0x1201FFFFF
本例三個 Bridge 都實作 64-bit Prefetchable Window,所以 Base/Limit 的低 nibble 為 1,並使用 Upper 32 Bits。非 Prefetchable Memory Window 則是 1 MiB 粒度,足以涵蓋 BAR0 的 16 KiB;Prefetchable Window 剛好涵蓋另一個 2 MiB BAR。其餘分支不使用這兩段資源,平台也已保留這些範圍供本例使用。
窗口編碼的來源
Base 6.0 Figure 7-14,p.936;§7.5.1.3.8,p.940;§7.5.1.3.9–§7.5.1.3.10,pp.940–941。未使用的窗口要依該節規則處理,不能把未配置的零值直接當成「關閉」。本章的窗口大小、同值配置於三個 Bridge,以及資源分支配置皆為作者假設。
6. MSE 與 BME,哪一個才是「啟用裝置」?
沒有一個位元能代替所有準備工作。Command Register 在 offset 0x04、寬度 16 bits;讀 Figure 7-5/Table 7-4 時,應把以下三個控制分開。
| 位元 | 對本例 Endpoint 的作用 | 沿途 Port 還要注意什麼? |
|---|---|---|
| bit 0:I/O Space Enable | 控制是否解碼並處理收到的 I/O 存取;本例不用 I/O BAR。 | Type 1 的控制涉及 Primary Side 收到的 I/O 存取。 |
| bit 1:Memory Space Enable(MSE) | 控制是否解碼並處理收到的 Memory 存取。 | 各 Bridge 的相關解碼與窗口也須有效。 |
| bit 2:Bus Master Enable(BME) | 允許 Function 發起 Memory/I/O Requests;一般 PCIe Endpoint 另受不得發 I/O Requests 的限制。 | Type 1 Port 的 BME 也控制向上游發起/轉送 Memory、I/O Requests;不阻擋 Completion 轉送。 |
案例推演:所有 BAR、Window 與平台映射已正確配置,且裝置介面已可接受本例讀取時,Endpoint 與路徑上各 Bridge 的 Command 若為 0x0002,表示 MSE=1、BME=0。CPU 可依已建立的路徑讀取本例 MMIO Register;Endpoint 仍不能自行發起 DMA 的 Memory Request。它回覆這筆讀取所需的 Completion,不是被 BME 禁止的 Memory Request。
- MMIO
- Memory-Mapped I/O,記憶體映射輸入輸出
- DMA
- Direct Memory Access,裝置直接存取記憶體
之後若要讓 Endpoint 主動搬移資料,不能只把自己的 Command 改成 0x0006 就宣告成功。還要確認上游 Port 的 BME、裝置工作設定、有效緩衝區,以及平台位址與存取權限。BME 為 1 是允許相關要求的必要控制,不是 DMA 已準備好的證據。MSI/MSI-X 也屬於帶內 Memory Write,因此 BME=0 同樣會禁止這類通知。
- MSI
- Message Signaled Interrupt,訊息中斷
- MSI-X
- Message Signaled Interrupts Extension,表格式訊息中斷
修改 Command 時應使用對應欄位寬度並保存其他控制;相鄰的 Status 在 0x06,含有 RW1C 位元。不要把 Command 與 Status 一起讀成 32 bits,再原樣回寫整個 DWORD,以免意外清除狀態。RW1C 的語義見 Chapter 2。
啟用條件的依據
Base 6.0 §7.5.1.1.3,Figure 7-5/Table 7-4,pp.917–920,尤其 MSE p.918、BME 與 MSI/MSI-X/Completion p.919。Command 與 Status 的相鄰布局見 Figure 7-4,p.916;Status 的 RW1C 範例見 Table 7-5,pp.922–923。配置 BAR 前後的解碼處理見 p.930。
設定空間可讀,為什麼 Memory Read 還會失敗?
設定存取用 BDF 與 Register offset 選擇 Function 和欄位;Memory Read 則使用記憶體位址選擇目標。沿用本章案例,先把 0x80004120 放回 BAR0 與每一層 Window,再檢查接收方向上的 Memory Space Enable(MSE,記憶體空間解碼控制)。
- BDF
- Bus/Device/Function,匯流排、裝置與功能識別
查閱時先由 Figure 7-10/Figure 7-14 定位 BAR 與 Bridge 欄位,使用 §7.5.1.3.8 解碼 Memory Base/Limit,接著讀 Figure 7-5/Table 7-4 的 MSE,注意 Type 1 Header 的 Primary Side 條件。最後用 §2.3.1 的 Request Handling 規則確認需要 Completion 的 UR 如何回覆。(Base 6.0,文件頁/PDF 頁 208、916–918、928、936、940。)
只要位址落在 BAR 內,每個 byte 都可以讀嗎?
BAR 的大小定義資源位址範圍;實際有哪些可用的裝置 Register,要查裝置介面文件。本例只假設 +0x120 是可讀 Register,並未定義 +0x3000 的用途,雖然它也在 BAR0 範圍內。Base 6.0 §7.5.1.2.1(p.930)允許 Function 對程式模型從不存取的未使用位置回 UR。要判定某次存取的結果,還需要該裝置介面及實際交易證據。
7. OS 怎麼知道韌體交給它的 BAR 有效?
Firmware 3.3 §3.5 說明,OS 以 Command 的 I/O/Memory Enable,以及 Expansion ROM 的 Enable,判斷相應資源是否已由韌體配置;不使用 BME 判定 BAR 是否有效。所以交接時看到 MSE=1、BME=0,不代表 BAR 無效;資源既然已啟用,韌體就必須正確配置。
- ROM
- Read-Only Memory,唯讀記憶體
這項交接契約也不表示只要讀到 MSE=1,就能忽略平台映射或任意操作裝置。本章的配置表解決了位址與轉送問題;驅動程式仍要依裝置介面規格初始化工作流程。
交接規則來源
PCI Firmware Specification Revision 3.3 §3.5,pp.34–35;本段採用該版本的資源有效性判斷,沒有把 BME 當成 BAR 配置的替代證據。
8. 把型別、對齊與視窗接起來
把 16 KiB 的 BAR0 放在 0x80005000,其他條件都不變,可以嗎?
不行。0x80005000 & 0x3FFF = 0x1000,不是 16 KiB 對齊。大小正確但基底未自然對齊,仍不是合法的分配。(§7.5.1.2.1,p.930。)
BAR2 的低 DWORD 讀到 0xFFE0000C,能只用這 32 bits 判定 64-bit 資源大小嗎?
不能。先用 bits 2:1 確定它是 64-bit BAR,再把 BAR3 作為高 DWORD 一起探測與計算。低 DWORD 只是完整遮罩的一部分。(Figures 7-11、Table 7-9,p.929;64-bit 探測,p.930。)
三個 Bridge 的 Memory Window 都正確,Endpoint 的 MSE 卻為 0,CPU 讀 BAR0 還會成功嗎?
不能據此期待成功。Endpoint 的 MSE=0 時,收到的 Memory Space 存取依 UR 處理;沿途可轉送不等於終點已啟用。(Table 7-4,p.918。)
Command 從 0x0002 改成 0x0006,就證明 DMA 緩衝區位址正確了嗎?
沒有。兩者差在 BME;它不驗證裝置工作描述項、緩衝區生命週期或平台轉譯。還要建立並核對那些資料,才能讓裝置開始工作。
9. 接下來追蹤一筆真正的交易
到這裡,系統已能選定 Function、找到能力、建立 Bus 路徑並配置資源。Chapter 5 接著追蹤 CPU 的一次讀取如何形成 Request,以及 Completion 如何回到要求者。完整安排見 課程閱讀路線。
版本與來源
PCI Express Base Specification Revision 6.0 與 PCI Firmware Specification Revision 3.3。所有來源位置就近標示;大小、位址、裝置內 Register 與平台映射均為教學假設。Firmware 3.3 並非 Base 6.0 的同版配套,兩者分別用於欄位規則與交接背景。查核日期:2026-10-28。