一、概述
?virtio設備可以基于不同總線來實現,本文介紹基于pci實現的virtio-pci設備。以virtio-blk為例,首先介紹PCI配置空間內容,virtio-pci實現的硬件基礎——capability,最后分析PIC設備的初始化以及virtio-pci設備的初始化。
再后面不詳細介紹PCI太多的知識, 已virtio 相關為主。
?二、PCI 配置空間
virtio設備作為pci設備,必須實現pci local bus spec規定的配置空間(最大256字節,現在有的最大是1K,看芯片自己設置, 只要前64字節是PCI標準)。前64字節是spec中定義好的,稱預定義空間。其中前16字節對所有類型的PCI設備都相同,之后的空間格式因類型而不同,對前16字節空間,我稱其為通用配置空間。
2.1 通用配置空間
通用配置空間如下圖所示:
(1)vendor id
廠商ID,用來標識PCI設備出自哪個廠商。這里是0x1af4,來自Red Hat。
(2)device id
廠商下的產品ID。傳統virtio-blk設備,這里是0x1001。
(3)revision id
設備版本ID。廠商決定是否使用,這里未使用。
(4)header type
pci設備類型。0x00表示普通設備、0x01表示pci bridge、0x02表示CardBus bridge。virtio是普通設備,這里是0x00。
通用配置空間即PCI配置空間前16字節中的以上4個地方用來識別virtio設備。
(5)command
command字段用來控制pci設備,打開某些功能的開關。對于virtio-blk設備來說,是(0x0507 = 0b01010111)。command的各字段含義如下圖所示:
其中,低三位的含義如下:
- I/O Space位
如果PCI設備實現了IO空間,該字段用來控制是否接收總線上對IO空間的訪問;如果PCI設備沒有IO空間,該字段不可寫。
- Memory Space位
如果PCI設備實現了內存空間,該字段用來控制是否接收總線上對內存空間的訪問;如果PCI設備沒有內存空間,該字段不可寫。
- Bus Master位
控制PCI設備是否具有作為Master角色的權限。
(6)status
status字段用來記錄pci設備的狀態信息。對于virtio-blk設備,是(0x10 = 0b00010000)。status各字段含義如下圖所示:
其中,Capabilities List位的含義如下:
- Capabilities List位
Capabilities List是pci規范定義的附加空間標志位,其意義是允許在pci設備配置空間之后加上額外的寄存器。這些寄存器由Capability List組織起來,用來實現特定的功能,附加空間在64字節配置空間之后,最大不能超過256字節。以virtio-blk設備為例,它標記了這個位,因此在virtio-blk設備的配置空間之后,還有一段空間用來實現virtio-blk的一些特有功能。此處,Capabilities List位為1表示Capabilities Pointer
字段(0x34)存放了附加寄存器組的起始地址。這里的地址表示附加空間在PCI設備空間內的偏移。
virtio-blk設備配置空間的內容可以通過lspci命令查看到,如下圖所示:
2.2 virtio 配置空間
virtio-pci設備實現pci spec規定的通用配置空間后,設計了自己的配置空間,用來實現virtio-pci的功能。
上面已提到,PCI規范通過status
字段的capabilities list
位標記自己在64字節預定義配置空間之后有附加的寄存器組,capabilities pointer
字段會存放寄存器組鏈表的頭部指針,這里的指針代表寄存器在配置空間內的偏移。如下圖所示(仍然是上圖中的信息):
pci spec中描述的capabilities list
格式如下:第1個字節存放capability ID,標識后面配置空間實現的是哪種capability,第2個字節存放下一個capability的地址。capability ID查閱參見pci spec3.0
virtio-blk實現的capability有兩種:一種是MSI-X( Message Signaled Interrupts - Extension),ID為0x11;另一種是Vendor Specific,ID為0x9。后面一種capability設計目的就是讓廠商實現自己的功能。virtio-blk的實現以此為基礎。
virtio-pci根據自己的功能需求,設計了如下的capabilities布局:
上圖中右側是6個capability,其中5個用做描述virtio-pci的capability,1個用作描述MSI-X的capability。這里我們只介紹用作virtio-pci的capability。右側描述了virtio-blk的capability
布局,左邊是每個capability
指向的物理地址空間布局。
根據virtio spec的規范,要實現virtio-pci的capabilty,其布局應該如下:
- cap_vndr
表示capability類型。
- cap_next
表示下一個capability在pci配置空間的位置。
- cap_len
表示capability這個數據結構的長度。
- cfg_type
表示配置類型。cfg_type通過如下取值,將virtio-pci的capability又細分成幾類:
2.3?virtio通用配置空間
capability中最核心的內容是virtio_pci_common_cfg,它是virtio前后端溝通的主要橋梁。common config分兩部分:第一部分用于設備配置;第二部分用于virtqueue使用。virtio驅動初始化利用第一部分來和后端進行溝通協商,比如支持的特性(guest_feature),初始化時設備的狀態(device_status),設備的virtqueue個數(num_queues)。第二部分用來實現前后端數據傳輸。后面會詳細提到兩部分在virtio初始化和數據傳輸中的作用。virtio_pci_common_cfg
數據結構如下:
三、pci 的枚舉和配置
這部分百度上很多, 在之前的文章也寫到了, 在這不贅述了。
注意三點就行:
- 枚舉過程中訪問pci配置空間寄存器,軟件接口是往特定寄存器寫東西,真正發起讀寫請求的,是host bridge,它在pci總線上傳輸的是command type為configuration wirte/read的transaction。配置完成后,軟件對pci bar空間關聯的內存進行讀寫,可以直接使用mov內存訪問指令,這時候host bridge在pci總線上傳輸的是command type為IO Read/Write或者Memory Read/Write的transaction。
- pci設備被配置之后,bar寄存器中存放了關聯的內存起始地址,這個地址是pci總線域的物理地址,不是cpu域的物理地址,雖然這兩個值一樣,但這只是x86這種架構的特殊情況,因為host bridge轉換的時候是一一映射的,才造成這種假象。在別的架構上,比如power pc,cpu域的地址想要訪問pci總線域的地址,需要通過host bridge進行地址轉換才能進行,兩種地址并不相同。
- pci設備被配置之后,雖然看上去軟件可以像訪問內存一樣訪問pci設備的bar的空間,但cpu真正訪問的時候,是要把地址交給host bridge,讓它去訪問才可以。可以說,訪問pci設備bar空間的不是cpu,而是host bridge。至始至終,cpu都不能直接管理pci設備的bar空間,它只能通過host bridge來間接管理。