这种quirk机制是Linux内核用于处理特定硬件问题的一种灵活方式,允许内核开发者为特定设备或供应商定制解决方案,以确保硬件在Linux系统上能够正常工作。
"quirks" 这个词在中文中通常翻译为“怪癖”或“特点”。它指的是某人或某物的独特或不寻常的特征。拼读如下:
- qu - /kw/(类似于 "quick" 中的 "qu" 音)
- ir - /ɜː/(类似于 "bird" 中的 "ir" 音)
- ks - /ks/(类似于 "box" 中的 "ks" 音)
所以,整个单词 "quirks" 的发音是 /kwɜːks/。
功能:
PCI QUIRKS机制开启针对多种PCI芯片组的错误规避功能,仅在PCI芯片组确实没有没有任何bug时才关闭此功能。至于究竟哪些芯片组有bug,可以直接打开"drivers/pci/quirks.c"文件查看,不确定的选"Y".
PCI quirks (CONFIG_PCI_QUIRKS)[Y/n/?] 常用于修补BIOS中对PCI有影响的BUG。
默认CONFIG_PCI_QUIRKS为Y。
工作机制:
PCI Quirks的工作流程通常包括以下几个步骤:
-
设备识别:在PCI总线枚举过程中,内核会检查每个设备的供应商ID和设备ID,以及可能的其他标识符,如子系统ID或类代码。
-
quirk匹配:内核会根据这些标识符与已知quirks列表进行匹配。这个列表通常在
drivers/pci/quirks.c
文件中定义。 -
应用quirk:一旦找到匹配的quirk,内核会调用相应的quirk处理函数。这个函数可以执行各种操作,比如修改设备的配置空间、设置特定的设备状态、或者调整内核的驱动程序行为。
-
设备驱动程序加载:在quirks应用之后,内核会继续正常的设备驱动程序加载过程,此时设备已经被配置为能够更好地与内核和驱动程序兼容。
相关调用函数:
在pci_fixup_device
函数中,会调用DECLARE_PCI_FIXUP_*
宏定义的quirk函数,这些宏会在初始化时注册quirk函数,以便在设备枚举过程中被调用。
DECLARE_PCI_FIXUP_CLASS_HEADER(vendor ID, device ID, Class_Type, Class_shift, func_quirk);
DECLARE_PCI_FIXUP_CLASS_EARLY(vendor ID, device ID, Class_Type, Class_shift, func_quirk);
DECLARE_PCI_FIXUP_CLASS_FINAL(vendor ID, device ID, Class_Type, Class_shift, func_quirk);
DECLARE_PCI_FIXUP_CLASS_RESUME_EARLY(vendor ID, device ID,Class_Type, Class_shift, func_quirk);
DECLARE_PCI_FIXUP_EARLY(vendor ID, device ID, func_quirk);
DECLARE_PCI_FIXUP_HEADER(vendor ID, device ID, func_quirk);
DECLARE_PCI_FIXUP_FINAL(vendor ID, device ID, func_quirk);
DECLARE_PCI_FIXUP_ENABLE(vendor ID, device ID, func_quirk);
DECLARE_PCI_FIXUP_RESUME(vendor ID, device ID, func_quirk);
DECLARE_PCI_FIXUP_RESUME_EARLY(vendor ID, device ID, func_quirk);
DECLARE_PCI_FIXUP_SUSPEND(vendor ID, device ID, func_quirk);
DECLARE_PCI_FIXUP_SUSPEND_LATE(vendor ID, device ID, func_quirk);
以上宏的主要区别在于它们在PCI设备初始化过程中被调用的时机不同。
带CLASS的宏,意味着只有当设备的类代码与指定的类代码匹配时,quirk回调函数才会被调用。这提供了更细粒度的控制,允许针对特定类型的设备应用quirk。
(1)DECLARE_PCI_FIXUP_HEADER
()
DECLARE_PCI_FIXUP_HEADER
宏用于在PCI设备资源被探测之前,即在pci_read_bases
函数调用之后,对设备的某些属性或配置进行修正。这个宏通常用于处理那些在设备资源探测阶段需要特殊处理的情况。
(2)DECLARE_PCI_FIXUP_EARLY()
这个宏用于在PCI设备初始化的早期阶段注册quirk回调函数,具体是在PCI设备的基础地址寄存器(BARs)被探测之前。这允许在资源分配之前对设备进行必要的修正,例如,修改设备的资源请求或调整设备的某些能力。
(3) DECLARE_PCI_FIXUP_FINAL
宏
则用于在PCI设备枚举的最后阶段对设备进行修正。这通常在设备的所有资源都已经确定之后,但在设备驱动程序的probe函数被调用之前。这个宏可以用来做最后的调整,比如确保设备处于正确的电源状态,或者对设备进行必要的配置,以确保它能够与驱动程序正确交互。
(4)DECLARE_PCI_FIXUP_ENABLE()
宏
在设备启用时调用,用于处理设备启用阶段可能需要的特殊配置。
(5)DECLARE_PCI_FIXUP_RESUME()
宏
是专门用于处理PCI设备恢复阶段的quirk。
在PCI设备从挂起状态恢复时,内核会调用pci_device_resume()
函数,这个过程中会触发DECLARE_PCI_FIXUP_RESUME()
宏注册的quirk回调函数。这允许开发者为特定设备定义在恢复时需要执行的代码,例如,重新初始化设备的状态或者重新配置设备的某些寄存器。
(6)DECLARE_PCI_FIXUP_SUSPEND()
宏
允许开发者为特定设备定义在挂起时需要执行的代码,例如,保存设备的配置状态、禁用设备的某些功能,或者确保设备能够正确地进入低功耗状态。
在Linux内核的电源管理框架中,DECLARE_PCI_FIXUP_SUSPEND()
宏注册的回调函数会在 suspend
回调之后和 suspend_noirq
回调之前被调用。这确保了在设备驱动程序的 suspend
回调执行了必要的清理工作之后,以及在设备的硬件被完全配置为低功耗状态之前,quirk逻辑得以应用。
总结:
PCI Quirks机制是Linux内核中用于处理特定PCI设备或芯片组的非常规行为的一种方法。这些"quirks"(怪癖)是指设备的一些被认为是不符合预期操作的特性。例如,某些设备可能报告错误的奇偶校验错误,或者在BIOS设置中不正确地设置ISA PCI区域头信息。
在Linux内核的drivers/pci/quirks.c
文件中,可以找到许多这样的quirk定义。这些quirk通过检查设备的供应商ID和设备ID来识别受影响的设备,并为它们设置特定的属性或执行特定的代码,以便内核能够以不同的方式处理这些设备,或者绕过已知的问题。
此外,PCI Quirks机制还可以用来禁用或启用特定设备上的MSI(Message Signaled Interrupts)功能。MSI是一种允许PCI设备通过向特殊地址写入数据来生成中断的技术,它比传统的INTx中断更高效。有些设备可能不支持MSI,或者在启用MSI时可能会遇到问题。在这些情况下,内核可以通过quirk来禁用MSI,或者在必要时启用它。
总的来说,PCI Quirks机制是Linux内核用来确保与各种硬件设备兼容的一种重要工具,它通过特殊处理来解决那些不符合标准或预期行为的设备问题。