内核版本:2.6.22
1. PCI access mode: BIOS, direct, mmconfig, any。
BIOS mode:有的BIOS程序提供了针对PCI总线的操作,这些操作包括总线枚举,此种BIOS称为PCI BIOS;此种方式直接使用BIOS程序枚举的结果。(64位平台没有该选项) 。
Direct mode: 内核进行PCI总线枚举过程。
MMConfig: PCIE才用的上,PCI用不上。
ANY: 首先尝试MMConfig、然后Direct、都失败后再尝试BIOS。
2. PCI设备配置寄存器的内容是谁写入的?
总线枚举。
芯片组:
北桥+南桥。
北桥中包含host bridge, 即RC(root complex)。
部分信息(如vendor id、device id)固化在设备中;其他信息在总线枚举阶段,有内核酌情设置。
3. 中断
过程:设备产生中断信号(电信号)->中断控制器->CPU->内核->中断处理程序(事先注册,与指定中断关联)。
PIC: 只能用于一个处理器的系统;每个PIC提供8个中断线。
APIC:
能够处理CPU之间的中断。
中断线数量增多至255。
Local APIC + IO APIC。
可通过/proc/interrupts查看系统有没有使用IO APIC。
MSI:
往预定义的内存地址写入预定义的消息来提出中断请求。
PCI设备->TLP消息-> Host Bridge -> CPU-> 内核->中断处理程序。
依赖于CONFIG_X86_LOCAL_APIC。
4. kconfig语法:Documentation/kbuild/kconfig-language.txt。
5. 源码:
drivers/pci/Makefile; arch/x86/pci/Makefile。
include/linux/init.h (page 902): 各个入口宏的定义。将不同的函数指针存放在.initcall.init的指定子节中。
6. 参数类型:
内核参数和模块参数:
内核参数说明:Documentation/kernel-parameters.txt。
查看模块参数:modinfo -p 模块名。
内核函数parse_args(): 参数解析。
内核启动时调用一次,解析内核参数。
模块加载时调用,解析模块参数。
early_param宏声明特权参数;module_param宏声明普通参数。
参数解析完之后,会调用xxx_initcall()入口函数。
7. 内核参数解析
drivers/pci/pci.c: pci_setup()函数解析内核参数中PCI相关的参数。
arch/x86/pci/common.c: pcibios_setup()。
off: 不进行PCI总线枚举。
bfsort和nobfsort:广度优先和深度优先。
bios: 使用PCI BIOS的枚举结果;建议在make menuconfig时PCI access mode选项使用any,由内核判断使用哪种方式。
nobios: 不使用PCI BIOS的枚举结果。
biosirq: 告诉内核通过PCI BIOS获取中断路由表。仅用于PIC,APIC不适用。
pirqaddr: 默认情况下,内核可以在0xf0000~0xfffff这段地址查找中断路由表。而pirqaddr告诉内核中断路由表的确切地址。
nommconfig: 告诉内核不要使用MMConfig方式访问设备;
noacpi: 禁止使用ACPI处理PCI总线枚举和PCI设备的中断路由。
noearly: 在内核启动过程的开始阶段,禁用对PCI设备的早期扫描(使用type1方式尝试访问每个可能存在的PCI设备的配置空间)。
assign-busses: 内核将无视PCI BIOS分配的总线号,自己重新分配;枚举包括总线号分配和其他地址空间的分配。此处仅指总线号重新分配。
routeirq: 内核为PCI设备分配中断路由。PCI驱动可能漏调用pci_enable_device()。
pci_enable_device(): 驱动程序访问PCI设备的任何资源前都要先调用pci_enable_device(),为设备完成中断路由,也就是分配中断请求线等工作。
realloc: 作用?看看代码。
8. 初始化(一)
1. 初始化函数执行顺序的判断依据
入口函数:init/main.c: do_initcalls()。
依次执行.initcall.init节中的函数指针。
.initcall.init节中有7个子节,优先执行.initcall1.init子节中的函数指针。
相同子节中的函数指针的执行顺序,根据Makefile中各个c文件的链接顺序确定。
2. 初始化流程
在driver/pci/和arch/x86/pci/目录下找出所有用xxx_initcall()声明的初始化函数,然后按照顺序执行。
.initcall2.init:
pcibus_class_init():
源文件:driver/pci/probe.c。
设备模型;注册一个名称pci_bus的struct class。
体现在sysfs上,是在/sys/class/目录下创建了一个pci_bus目录;用来存储各个pci总线。
pci_driver_init():
源文件:driver/pci/pci-driver.c。
注册pci总线(struct bus_type类型的结构);只有总线存在了,才会有设备链表和驱动链表,才会有设备和驱动的match。
9. 初始化(二)
.initcall3.init:
不同目录下的两个文件,哪个文件先链接:
位于同一子目录下,被同一Makefile编译的两个文件,可以很方便的分辨出哪个文件是先链接的。
而位于两个目录下被两个Makefile编译的两个文件,需要分辨哪个Makefile首先被上级Makefile调用。
内核构建顺序:根Makefile -> arch/*/Makefile -> driver/等其他子目录Makefile。
pci_access_init():
源文件:arch/i386/pci/init.c。
根据make menucofig时"PCI access mode"的配置,进行初始化。
Linux内核大量使用GNU C扩展,以至于GNU C成为能够编译内核的唯一编译器。
GNU C扩展:代码优化、目标代码布局、安全检查等。
pci_direct_probe():
源文件:arch/i386/pci/direct.c。
direct方式分为conf1和conf2,conf2主要为兼容老主板。
struct resource:
源文件:include/linux/ioport.h。
作用: 描述IO资源(IO端口或IO内存)。
flags字段:描述资源的种类和属性。
IORESOURCE_IO 对应/proc/ioports
IORESOURCE_MEM 对应/proc/iomem
IORESOURCE_IRQ 对应/proc/interrupt
IORESOURCE_DMA 对应/proc/dma
申请IO资源:
申请IO端口:request_region()。
申请IO内存request_mem_region()。
地址种类:
用户虚拟地址:用户空间进程使用的地址。
物理地址:cpu和内存简的地址。
总线地址:主桥和设备简的地址。
内核逻辑地址:在多多数体系结构中,与物理地址仅差一个固定的偏移量。
内核虚拟地址。
为设备申请的IO资源,得到的是总线地址,需要通过ioremap映射后,通过专用API访问。
include/linux/pci_regs.h:定义了PCI配置空间寄存器偏移量的宏。
pci_check_type1()
首先写0x0cf8地址,然后读;如果读写内容一致,则0x0cf8可能是主桥的地址端口。
pci_sanify_check(struct pci_raw_ops *o)
struct pci_raw_ops
源文件:include/linux/pci.h。
作用:包含指定BDF指定寄存器的读写API。
PCI设备的访问方式有BIOS、Direct的conf1和conf2、MMConfig,均对应一个struct pci_raw_ops结构的全局变量。
for循环中根据CLASS_ID和DENDOR_ID的值判断主桥是否存在。
DMI: Desktop Management Interface
SMBIOS: System Management BIOS,是主板厂商显示序列号、电池型号、网卡型号等系统管理信息时所必须遵守的规范。
DMI是用来访问和收集这些信息的接口。
DMI信息存储在BIOS ROM的一段数据区里,启动时,由BIOS拷贝至内存
dmidecode:显示dmi信息。
acpi_pci_init():
源文件:driver/pci/pci_acpi.c。