(转载请注明出处,发现问题请联系raymond_rule@hotmail.com)
在PCI device需要使用MMIO时,通常会调用pci_request_selected_regions()来标记MMIO所需要的内存空间,实际上最终调用的是__pci_request_region():
在该函数中,会利用诸如pci_resource_len()、pci_resource_start()这一类宏来获得MMIO所需的内存空间信息(包括大小),通过查看这些宏的定义发现,它们返回的是struct pci_dev的resource数组元素的值,因此MMIO所需的资源早在本函数之前已经分配好,并作为一个元素记录在resource数组中了。
那么,这些信息是什么时候被确定下来的呢?换句话说,我们的PCI device driver所映射的MMIO空间大小是何时由谁决定的呢?
MMIO空间的大小是在ACPI子系统初始化时确定的。具体的函数调用过程是:acpi_init()---->acpi_scan_init()---->acpi_bus_scan()---->acpi_bus_attach()---->acpi_scan_attach_handler()---->acpi_pci_root_add()---->pci_acpi_scan_root()---->pci_scan_child_bus()---->pci_scan_slot()---->pci_scan_single_device()---->pci_scan_device()---->pci_setup_device()---->pci_read_bases()---->__pci_read_base()。在__pci_read_base()中计算MMIO空间的大小。其计算方法可以归结为:以2为底,以BAR寄存器中read only的bit位的个数为幂的对数值就是MMIO空间大小,单位是Byte。
1. 读取BAR寄存器的值保存在变量l中,这是BAR中的初始值,保留BAR初始值的目的是,后面会向BAR中写入其他值,因此保留原值以便恢复。
2. 向该BAR寄存器中写入l|mask(其中mask的值是~0),因此相当于向BAR寄存器全部写入1。
3. 再次读取该BAR中的值,保存在sz中,并且把BAR的值恢复为l。至此,BAR寄存器中的非read only比特位全部被设置成了1.
4. 调用decode_bar(),根据BAR的值,来解析出资源类型等(IO or MEM, 32 bit or 64 bit)。我参考的AHCI Controller暗示的资源类型是MEM(并且是32 bit的),因此没有IORESOURCE_IO标志。
5. 把l的bit 0~bit 3清零,mask赋为0xf0
6. 调用pci_size(),传入l(把BAR的初始值的bit 0~bit 3清零后的值)、sz(把BAR的初始值中所有非read onle比特位设为1之后的值)和mask(0xf0),计算并返回MMIO空间的大小。
pci_size()的计算过程:从低位算起,找到第一个不为0的bit位,然后将其值减1,并返回。(减1的原因是该返回值要作为resource的end成员)
浙公网安备 33010602011771号