天天看點

PCI總線及LSPCI指令詳解分析

lspci指令詳解參考  https://blog.csdn.net/huangxuesi/article/details/69959679

一、PCI簡介

     PCI是一種外設總線規範。我們先來看一下什麼是總線:總線是一種傳輸信号的路徑或信道。典型情況是,總線是連接配接于一個或多個導體的電氣連線,總 線上連接配接的所有裝置可在同一時間收到所有的傳輸内容。總線由電氣接口和程式設計接口組成。本文讨論Linux 下的裝置驅動,是以,重點關注程式設計接口。

     PCI是Peripheral Component Interconnect(外圍裝置互聯)的簡稱,是普遍使用在桌面及更大型的計算機上的外 設總線。PCI架構被設計為ISA标準的替代品,它有三個主要目标:獲得在計算機和外設之間傳輸資料時更好的性能;盡可能的平台無關;簡化往系統中添加和 删除外設的工作。

二、PCI尋址

     從現在開始,我想盡可能通過一些實際的例子來說明問題,而減少理論方面的問題的描述,因為,相關的理論的東西,可以在其它地方找到。

     我們先來看一個例子,我的電腦裝有1G的RAM,1G以後的實體記憶體位址空間都是外部裝置IO在系統記憶體位址空間上的映射。 /proc/iomem描述了系統中所有的裝置I/O在記憶體位址空間上的映射。我們來看位址從1G開始的第一個裝置在/proc/iomem中是如何描述 的:

             40000000-400003ff : 0000:00:1f.1

     這是一個PCI裝置,40000000-400003ff是它所映射的記憶體位址空間,占據了記憶體位址空間的1024 bytes的位置,而 0000:00:1f.1則是一個PCI外設的位址,它以冒号和逗号分隔為4個部分,第一個16位表示域,第二個8位表示一個總線編号,第三個5位表示一 個裝置号,最後是3位,表示功能号。

     因為PCI規範允許單個系統擁有高達256個總線,是以總線編号是8位。但對于大型系統而言,這是不夠的,是以,引入了域的概念,每個 PCI域可以擁有最多256個總線,每個總線上可支援32個裝置,是以裝置号是5位,而每個裝置上最多可有8種功能,是以功能号是3位。由此,我們可以得 出上述的PCI裝置的位址是0号域0号總線上的31号裝置上的1号功能。

PCI總線及LSPCI指令詳解分析

     那上述的這個PCI裝置到底是什麼呢?下面是我的電腦上的lspci指令的輸出:

     00:00.0 Host bridge: Intel Corporation 82845 845 (Brookdale) Chipset Host Bridge (rev 04)

     00:01.0 PCI bridge: Intel Corporation 82845 845 (Brookdale) Chipset AGP Bridge(rev 04)

     00:1d.0 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #1) (rev 02)

     00:1d.1 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #2) (rev 02)

     00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev 42)

     00:1f.0 ISA bridge: Intel Corporation 82801CAM ISA Bridge (LPC) (rev 02)

     00:1f.1 IDE interface: Intel Corporation 82801CAM IDE U100 (rev 02)

     00:1f.3 SMBus: Intel Corporation 82801CA/CAM SMBus Controller (rev 02)

     00:1f.5 Multimedia audio controller:Intel Corporation 82801CA/CAM AC'97 Audio Controller (rev 02)

     00:1f.6 Modem: Intel Corporation 82801CA/CAM AC'97 Modem Controller (rev 02)

     01:00.0 VGA compatible controller: nVidia Corporation NV17 [GeForce4 420 Go](rev a3)

     02:00.0 FireWire (IEEE 1394): VIA Technologies, Inc. IEEE 1394 Host Controller(rev 46)

     02:01.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+(rev 10)

     02:04.0 CardBus bridge: O2 Micro, Inc. OZ6933 Cardbus Controller (rev 01)

     02:04.1 CardBus bridge: O2 Micro, Inc. OZ6933 Cardbus Controller (rev 01)

     lspci沒有标明域,但對于一台PC而言,一般隻有一個域,即0号域。通過這個輸出我們可以看到它是一個IDE interface。由上述的 輸出可以看到,我的電腦上共有3個PCI總線(0号,1号,2号)。在單個系統上,插入多個總線是通過橋(bridge)來完成的,橋是一種用來連接配接總線 的特殊PCI外設。是以,PCI系統的整體布局組織為樹型,我們可以通過上面的lspci輸出,來畫出我的電腦上的PCI系統的樹型結構:

00:00.0(主橋)--00:01.0(PCI橋)-----01:00:0(nVidia顯示卡)

                   |

                   |---00:1d(USB控制器)--00:1d:0(USB1号控制器)

                   |                    |

                   |                    |--00:1d:1(USB2号控制器)                    |

                   |-00:1e:0(PCI橋)--02:00.0(IEEE1394)

                   |                |

                   |                |-02:01.0(8139網卡)

                   |                |

                   |                |-02:04(CardBus橋)-02:04.0(橋1)

                   |                                   |

                   |                                   |--02:04.1(橋2)

                   |

                   |-00:1f(多功能闆卡)-00:1f:0(ISA橋)

                                        |

                                        |--00:1f:1(IDE接口)

                                        |

                                        |--00:1f:3(SMBus)

                                        |

                                        |--00:1f:5(多媒體聲音控制器)

                                        |

                                        |--00:1f:6(數據機)

     由上圖可以得出,我的電腦上共有8個PCI裝置,其中0号總線上(主橋)上連有4個,1号總線上連有1個,2号總線上連有3個。00:1f是一個連有5個功能的多功能闆卡。

     每一個PCI裝置都有它映射的記憶體位址空間和它的I/O區域,這點是比較容易了解的。除此之外,PCI裝置還有它的配置寄存器。有了配置寄存器, PCI的驅動程式就不需要探測就能通路裝置。配置寄存器的布局是标準化的,配置空間的4個位元組含有一個獨一無二的功能ID,是以,驅動程式可通過查詢外設 的特定 ID來識别其裝置。是以,PCI接口标準在ISA之上的主要創新在于配置位址空間。

前文已講過,PCI驅動程式不需要探測就能通路裝置,而這得益于配置位址空間。在系統引導階段,PCI硬體裝置保持未激活狀态,但每個PCI主機闆均配備有能夠處理PCI的固件,固件通過讀寫PCI控制器中的寄存器,提供了對裝置配置位址空間的通路。

     配置位址空間的前64位元組是标準化的,它提供了廠商号,裝置号,版本号等資訊,唯一辨別一個PCI裝置。同時,它也提供了最多可多達6個的I/O 位址區域,每個區域可以是記憶體也可以是I/O位址。這幾個I/O位址區域是驅動程式找到裝置映射到記憶體和I/O空間的具體位置的唯一途徑。有了這兩點, PCI驅動程式就完成了相當于探測的功能。關于這64個位元組的配置空間的詳細情況,可參閱《Linux裝置驅動程式第三版》P306,不再詳述。

PCI裝置擁有256B的空間,用于存放配置寄存器。改空間是辨識PCI卡型号和性能的關鍵。我們可以檢視配置去的詳細情況

PCI寄存器是小端位元組序格式,以通過sysfs來檢視PCI的配置,如下指令,檢視指令和結果如下

PCI總線及LSPCI指令詳解分析

對上圖進行解釋,前面兩位元組是廠家ID,表示生産廠家。PCI廠家ID在全球都是統一配置設定和管理的,在www.pcidatabase.com可以檢視。PCI配置空間含義如下

PCI總線及LSPCI指令詳解分析

對應上上圖的輸出資訊,翻譯起來就是

廠家ID是0x1002,裝置ID是6778,裝置類型代碼是0300,基址寄存器是0x000c-0000

子廠家ID是174b,子裝置ID是d145,

配置空間中包含10位元組用于表示裝置類型的編碼。表示PCI橋裝置類編碼的第一個位元組是0x06,網絡裝置類編碼的第一個位元組是0x02,類編碼類型的定義見include/linux/pci_ids.h

PCI驅動程式向PCI子系統注冊其支援的廠家ID、裝置ID和裝置類編碼。使用插入的卡通過配置空間被識别後,PCI子系統把插入的卡和對應的驅動綁定

通路PCI

PCI裝置包括3個尋址空間:配置空間、IO端口和裝置記憶體。

配置區

核心為驅動程式提供6個可以調用的函數來通路PCI配置區:

pci_read_config_[byte|word|dword](struct pci_dev *pdev,int offset,int *value);

pci_write_config_[byte|word|dword](struct pci_dev *pdev,int offset,int *value);

在參數清單中,strutc pci_dev是PCI裝置結構體,offset是想通路的配置空間中的位元組位置。對讀函數來講,value是資料緩沖區的指針;對寫函數來講,value放的是準備寫的資料。

看看下面的例子。

要得到配置設定給某卡功能的中斷号,進行如下操作

unsigend char_irq;

pci_read_config_byte(pdev,PCI_INTERRUPT_LINE,&irq);

按照PCI規定說明,PCI配置空間偏移量位址60處存放卡的IRQ号,配置空間的偏移位址都在檔案include/linux/pci_regs.h中有詳細定義,是以一般用到PCI_INTERRUPT_LINE 表示偏移位址。于此類似,要想讀PCI狀态寄存器(配置空間偏移6處),進行如下操作:

unsigned short status;  pci_read_config_word(pdev,PCI_STATUS,&status);

配置去開頭的64B是規範了的。其他地方由裝置生産廠家按照自己的意思定義。

比如說我們使用的Xircom卡,在64B偏移處的4個位元組用于電源管理的目的,為了禁止電灌管理功能,Xircom CardBus驅動程式在實作檔案drivers/net/tulip/xircom_cb.c裡進行如下操作:

#define PCI_POWERMGMT 0x40

pci_write_config_dword(pdev,PCI_POWERMGMT,0x0000);

IO和記憶體

PCI卡有6個IO或記憶體區域,I/O區域包括寄存器,記憶體區域存放資料。例如:視訊卡的I/0區域包含控制寄存器,記憶體區域映射緩沖。但并不是所有的卡都有可尋址的記憶體區域。

I/O和記憶體區域的功能和硬體有關系,在裝置手冊中可以查到。

像配置區一樣,核心提供一系列的輔助函數,可以用來操作PCI裝置的/O和記憶體區域

Unsigned long pci_resource_[start|len|end|flag](struct pci_dev *pdev,int bar);為操作I/O區域。如PCI視訊卡裝置控制寄存器,驅動程式要完成以下事件

(1)從配置區相應基址寄存器裡得到I/O區域的基址:

unsigned long io_base = pci_resource_start(pdev,bar);

這裡假定裝置控制寄存器被映射到由變量bar關聯的I/O區域,bar值的變化範圍是0-5。

(2)用核心的request_region()正常機制獲得這個I/O區域,并标明它對應的裝置:

request_region(io_base,length,”my_driver”);

通過調用request_region()申請I/O位址後,裝置驅動程式在申請的I/O位址中操作運作。此機制確定其他程式不能申請此區域,直至用過調用release_region()釋放占用的區域。

lenth用于控制寄存器空間的大小,my_driver表示這個區域的使用者。在檔案/proc/ioports中的my_driver對應的文本行裡可以看到這片I/O區域。也可以使用drivers/pci/pci.c檔案中定義的封裝函數pci_request_region()申請I/O端口

(3)用寄存器手冊上的偏移位址加上(1)得到的基址,然後用inb()和outb()函數來通路這些寄存器

Read函數

Register_data = inl(io_base+REGISTER_OFFSET);

write函數

Outl(register_data,io_base+REGISTER_OFFSET)

為了能夠操作PCI裝置的記憶體區域,按照下面步驟進行

(1)獲得基址、記憶體區域長度以及記憶體相關标志

Unsigned long mmio_base=pci_resource_start(pdev,bar)

Unsigned long mmio_length=pci_resource_length(pdev,bar)

Unsigned long mmio_flags=pci_resource_flags(pdev,bar)

這裡假定裝置記憶體區域被映射到基址寄存器bar

(2)用核心的request_men_region()正常機制标記這片記憶體區的擁有者

Request_men_region(mmio_base,mmio_length,”my_driver”);

用前面提到的封裝函數pci_request_region()也可以

(3)讓CPU通路第(1)步獲得的裝置記憶體。為防止發生意外,某些記憶體空間(如包含寄存器的地方)設定成不讓CPU可預取或不可啟用高速緩存。對于其他裝置(如本例中提到的區域),CPU 可以啟用高速緩存。根據記憶體區域的通路标志,使用合适的函數可以得到與映射區域相對應的核心虛拟位址

Viod __iomen *buffer;

If(flages & IORESOURCE_CACHEABLE)

{

Buffer=ioremap(mmio_base,mmio_length);

}

Else

{

Buffer = ioremap_nocache(mmio_base,mmio_length);

}

為了安全起見并避免執行前述的檢查,使用lib/iomap.c定義的函數pci_iomap()代替

  Buffer=pci_iomap(pdev,bar,mmio_length)

DMA緩沖區是DMA傳送時用做源端位址或者目的位址的記憶體區。如果總線接口能通路的位址受限,将會影響DMA緩沖區的大小。是以,用于24位總線(如ISA)的DMA緩沖區隻占系統記憶體底部的16MB。稱為ZONE_DMA。PCI總線預設為32位,是以在32的平台下一般不會碰到這種限制。可以用下面的函數告訴核心系統中可用作DMA緩沖區的位址的特殊要求:

dma_set_mask(struct device *dev,u64 mask);

如果這個函數傳回成功,就可以在mask指定的位址範圍内進行DMA操作。如e1000PCI-X吉位以太網驅動程式(drivers/net/e1000/e1000-main.c)裡:

If(!(err = pci_set_dma_mask(pdev,DMA_64)))

{

Pci_using_dac =1

}

Else

{

If((err =pci_set_dma_mask(pdev,DMA_32BIT_MASK)))

{

E1000_ERR(“No usable DMA configuration,aborting\n”);

Return err;

}

Pci_using_dac=0;

}

I/O裝置從總線控制器或IOMMU(I/O Memory Unit,I/O記憶體管理單元)的角度看待DMA緩沖區。是以,I/O裝置需要的是DMA緩沖區的總線位址,而不是實體位址或者核心虛拟位址。如果你想告訴PCI卡DMA緩沖區的位址資訊,必須讓PCI卡知道DMA緩沖區的總線位址。總線位址的類型是dma_addr_t,在檔案include/asm-your-arch/types.h裡有定義

DMA還有幾個概念要了解,一個是回彈緩沖區。回彈緩沖區駐留在可作為DMA緩沖區的記憶體區域裡,在DMA請求的源或目的位址沒有DMA功能的記憶體區域時,它可以作為臨時記憶體區存放資料。例如,用DMA方式從32位PCI外圍裝置向位址高于4GB的目标傳送資料時,如果沒有IOMMU單元,就需要用到回彈緩沖區了。資料先暫時存放在回彈緩沖區,再複制到目标位址。第二個概念頗具DMA的特色:分散、聚集。當要傳輸的資料分布在不連續的記憶體上時,分散/聚集能對這些不連續緩沖區的資料進行一次性發送,反過來,DMA也能把資料從卡正确地傳送到位址不連續的緩沖區中。分散/聚集通過減少重複的DMA傳送請求來提高效率。

核心提供有用的API函數來屏蔽配置DMA的細節。如果你是在給支援總線管理的PCI卡(大多數PCI卡都支援)寫驅動程式,這些API會更簡單。PCI DMA函數基本上就是封裝DMA的通用服務函數,在include/asm/-genetic/pci-dma-compat.h檔案中有定義。本章中我們隻用PCI的DMA API。

核心為PCI驅動程式提供了兩類DMA服務

(1)一緻性DMA通路方法。這些程式保證DMA傳送資料的一緻性。如果PCI裝置和CPU都有可能幹預DMA緩沖區,保證資料的一緻性是很重要的,這時要用一緻性API函數。它是性能上的一個折中。要得到能保證資料一緻性的DMA緩沖區,用下面的API函數

 Void *pci_alloc_consistent(strcuct pci_dev *pdev,size_t  size,dma_addr_t *dma_handle);

 這個函數配置設定一個DMA緩沖區,生成它的總線位址,并傳回相關的核心虛拟位址。前面兩個參數是PCI裝置結構體和DMA緩沖區的位元組大小,第三個參數(dma-handle)是指向總線位址的指針,由函數調用産生。下面的代碼片段配置設定和釋放一個一緻性DMA緩沖區

Void *vaddr = pci_alloc_consistent(pdev,size,&dma_handle)

Pci_free_consistent(pdev,size,vaddr,dma_handle);

(2)流式DMA通路函數。這些API不保證資料的一緻性,是以速度更快。它們在不太需要CPU和I/O裝置共享DMA緩沖區的時候比較有用。當裝置存儲的流式緩沖區被映射以便被裝置通路後,驅動程式要明确得去映射(或同步)流式緩沖區,之後CPU才可以可靠地操作該緩沖區。流式通路有兩類函數:pci_[map|unmap|dma_sync]_single()和pci_[map |unmap|dma_sync]_sg().

第一組函數可以映射、去映射以及同步一個預先配置設定的單一DMA緩沖區。Pci_map_single()的原型如下

Dma_addr_t pci_map_single(struct pci_dev *pdev,void *ptr,size_t size,int direction);

前三個參數分别表示PCI裝置結構體,預先配置設定的DMA緩沖區虛拟核心位址和緩沖區的位元組數。第四個參數取下面幾個值:PCI_DMA_BIDIRECTION、PCI_DMA_TODEVICE、PCI_DMA_FROMDEVICE和PCI_DMA_NONE,從名字上就很容易看出這些變量的含義。但是使用第一個選項(PCI_DMA_BIDIRECTION)的代價比較大,最後一個選項是在調試的時候使用。

第二組函數映射、去映射并且同步一個分散/聚集的DMA緩沖區鍊。Pci_map_sg()的原型如下:

Int pci_map_sg(struct pci_dev *pdev,struct scatterlist *sgl,int num_entries,int direction);

第二個參數是sruct scatterlist表示分散記憶體的連結清單,在include/asm-your-arch/scatterlist.h

裡面定義。Num_entries變量表示分散記憶體的連結清單入口函數。第一和最後一個參數的意思跟pci_map_single()函數裡的參數是一樣的。

     下面,我們來看一下8139too網卡裝置的配置空間的詳細情況。在2.6核心的系統中,可以在目錄/sys/bus/pci/drivers/ 下看到很多以PCI裝置名命名的目錄,但不是說這些裝置都存在于你的系統中。我們進入8139too目錄,其中有一個以它的裝置位址0000:02: 01.0命名的目錄。在這個目錄下可以找到該網卡裝置相關的很多資訊。其中resource記錄了它的6個I/O位址區域。内容如下:

         0x0000000000003400 0x00000000000034ff 0x0000000000000101

         0x00000000e0000800 0x00000000e00008ff 0x0000000000000200

         0x0000000000000000 0x0000000000000000 0x0000000000000000

         0x0000000000000000 0x0000000000000000 0x0000000000000000

         0x0000000000000000 0x0000000000000000 0x0000000000000000

         0x0000000000000000 0x0000000000000000 0x0000000000000000

         0x0000000000000000 0x0000000000000000 0x0000000000000000

     由該檔案可以看出,8139too裝置使用了兩個I/O位址區域,第一個是它映射的I/O端口範圍,第二個是它映射的記憶體位址空間。關于這兩個值可以在/proc/iomem和/proc/ioport中得到驗證。

-[0000:00]-+-00.0

           +-02.0

           +-1d.0

           +-1d.1

           +-1d.2

           +-1d.7

           +-1e.0-[0000:01]--+-02.0

           |                 /-05.0

           +-1f.0

           +-1f.1

           +-1f.3

           /-1f.5

00:00.0 Host bridge: Intel Corporation 82845G/GL[Brookdale-G]/GE/PE DRAM Controller/Host-Hub Interface (rev 03)

00:02.0 VGA compatible controller: Intel Corporation 82845G/GL[Brookdale-G]/GE Chipset Integrated Graphics Device (rev 03)

00:1d.0 USB Controller: Intel Corporation 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (rev 02)

00:1d.1 USB Controller: Intel Corporation 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (rev 02)

00:1d.2 USB Controller: Intel Corporation 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (rev 02)

00:1d.7 USB Controller: Intel Corporation 82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (rev 02)

00:1e.0 PCI bridge: Intel Corporation 82801 PCI Bridge (rev 82)

00:1f.0 ISA bridge: Intel Corporation 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge (rev 02)

(LPC Hub 控制器 1 )

00:1f.1 IDE interface: Intel Corporation 82801DB (ICH4) IDE Controller (rev 02)

00:1f.3 SMBus: Intel Corporation 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (rev 02)

00:1f.5 Multimedia audio controller: Intel Corporation 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (rev 02)

01:02.0 Communication controller: Conexant HSF 56k HSFi Modem (rev 01)

01:05.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+ (rev 10)

轉載自:http://blog.chinaunix.net/uid-7374279-id-5765122.html 及https://blog.csdn.net/fight_onlyfor_you/article/details/76718741

繼續閱讀