ACPI BIOS 可以使用“操作系统接口(Operating System Interfaces)”方法 (_OSI) 来查找操作系统支持的内容。例如,如果 BIOS AML(ACPI Machine Language) 代码包含 _OSI(“XYZ”),则内核的 AML 解释器可以评估该方法,查看它是否支持“XYZ”,并向 BIOS 回答 YES 或 NO。
ACPI _REV 方法返回“OSPM 支持的 ACPI 规范的修订版本”
本文解释了 BIOS 和 Linux 应如何以及为何使用这些方法。它还解释了它们被广泛滥用的原因和方式。
如何使用_OSI
Linux 运行在两组机器上 - 一组是由 OEM 测试过的与 Linux 兼容的机器,另一组从未经过 Linux 测试,但安装了 Linux 来替换原始操作系统(Windows 或 OSX)。
较大的一组是仅针对运行 Windows 进行测试的系统。不仅如此,许多系统还针对仅运行一个特定版本的 Windows 进行测试。因此,即使 BIOS 可以使用 _OSI 来查询正在运行哪个版本的 Windows,但实际上只测试了通过 BIOS 的一条路径。经验表明,通过未经测试的 BIOS 路径会使 Linux 暴露于整个类别的 BIOS 错误。因此,Linux _OSI 默认值必须继续声明与所有版本的 Windows 兼容。
但 Linux 实际上并不兼容 Windows,当 Linux 将最新版本的 Windows 添加到其 _OSI 字符串列表时,Linux 社区也受到了回归的影响。因此,将来在向上游发布之前,可能会对其他字符串进行更彻底的审查。但最终它们很可能都会被添加。
如果 OEM 想要使用相同的 BIOS 映像支持 Linux 和 Windows,他们应该怎么做?通常他们需要针对 Linux 做一些不同的事情,以处理 Linux 与 Windows 的不同之处。
在这种情况下,OEM 应该创建由 Linux 内核执行的自定义 ASL,并更改 Linux 内核驱动程序以执行此自定义 ASL。实现此目的的最简单方法是引入从 Linux 内核调用的设备特定方法 (_DSM)。
过去,内核支持类似这样的代码:_OSI(“Linux-OEM-my_interface_name”),其中如果这是 OEM 特定的钩子,则需要使用“OEM”,而“my_interface_name”则描述该钩子,这可能是一个怪癖、一个错误或一个错误修复。
然而,人们发现其他 BIOS 供应商会滥用此功能来更改完全不相关的系统上的完全不相关的代码。这促使对其所有用途进行评估。结果发现,出于任何原始原因,它们都不是必需的。因此,内核默认不会响应任何自定义的 Linux-* 字符串。
这很简单。请继续阅读,找出错误的原因。
在 _OSI 之前,有 _OS
ACPI 1.0 将“_OS”指定为“可计算为标识操作系统的字符串的对象”。
ACPI BIOS 流程将包括对 _OS 的计算,并且内核中的 AML 解释器将向其返回一个标识操作系统的字符串:
Windows 98,SE:“ Microsoft Windows”
Windows ME:“ Microsoft WindowsME:Millennium Edition”
Windows NT:“ Microsoft Windows NT”
这个想法是在一个运行多个操作系统的平台上,BIOS 可以使用 _OS 来启用操作系统可能支持的设备,或者启用必要的怪癖或错误解决方法,以使平台与预先存在的操作系统兼容。
但 _OS 存在根本问题。首先,BIOS 需要知道在其上运行的所有可能操作系统版本的名称,并且需要知道这些操作系统的所有怪癖。当然,BIOS 询问操作系统的具体信息(例如“您是否支持特定接口”)会更有意义,因此在 ACPI 3.0 中,_OSI 诞生以取代 _OS。
_OS 已被废弃,但即使在今天,许多 BIOS 仍会寻找 _OS“Microsoft Windows NT”,尽管有人会安装那些旧操作系统来取代机器自带的操作系统,这似乎有些牵强。
Linux 回答“Microsoft Windows NT”以迎合 BIOS 习语。这是唯一可行的策略,因为这就是现代 Windows 所做的,否则可能会让 BIOS 走上一条未经测试的道路。
_OSI 诞生后立即被滥用
使用 _OSI,BIOS 提供描述接口的字符串,并询问操作系统:“yes/no,您是否兼容此接口?”
例如,如果操作系统知道如何处理对 ACPI 3.0 规范所做的热扩展,则 _OSI(“3.0 Thermal Model”) 将返回 TRUE。不了解这些扩展的旧操作系统将回答 FALSE,而新操作系统可能会返回 TRUE。
对于特定于操作系统的接口,ACPI 规范规定 BIOS 和操作系统应就“Windows-interface_name”等形式的字符串达成一致。
但发生了两件坏事。首先,Windows 生态系统使用 _OSI 并非按照设计,而是将其作为 _OS 的直接替代品——用于标识操作系统版本,而不是操作系统支持的接口。事实上,从一开始,ACPI 3.0 规范本身就使用 _OSI(“Windows 2001”)在示例代码中编纂了这种滥用。
这种滥用被采纳并持续至今。
Linux 别无选择,只能将 TRUE 返回给 _OSI(“Windows 2001”) 及其后续版本。否则,几乎可以保证破坏仅使用 _OSI 返回 TRUE 进行测试的 BIOS。
这种策略是有问题的,因为 Linux 永远不会与最新版本的 Windows 完全兼容,有时需要一年多的时间才能解决不兼容问题。
Linux 社区不甘示弱,将 _OSI(“Linux”) 返回 TRUE,这让事情变得更糟。这样做甚至比 Windows 滥用 _OSI 更糟糕,因为“Linux”甚至不包含任何版本信息。_OSI(“Linux”) 导致某些 BIOS 出现故障,因为 BIOS 编写者在未经测试的 BIOS 流程中使用它。但一些 OEM 在经过测试的流程中使用 _OSI(“Linux”) 来支持真正的 Linux 功能。2009 年,Linux 删除了 _OSI(“Linux”),并添加了一个命令行参数来为仍需要它的旧系统恢复它。此外,所有调用它的 BIOS 都会打印 BIOS_BUG 警告。
任何 BIOS 都不应使用 _OSI(“Linux”)。
结果是 Linux 的策略是最大限度地提高与在 Windows 机器上测试过的 ACPI BIOS 的兼容性。夸大兼容性确实存在风险;但另一种选择往往是灾难性的故障,因为 BIOS 采取了从未在任何操作系统下验证过的路径。
不要使用 _REV
由于 _OSI(“Linux”)消失,一些 BIOS 编写者使用 _REV 在同一 BIOS 中支持 Linux 和 Windows 的差异。
_REV 在 ACPI 1.0 中定义,用于返回操作系统和操作系统 AML 解释器支持的 ACPI 版本。
现代 Windows 返回 _REV = 2。Linux 使用 ACPI_CA_SUPPORT_LEVEL,它会根据支持的规范版本递增。
不幸的是,_REV 也被误用了。例如,某些 BIOS 会检查 _REV = 3,并对 Linux 执行某些操作,但当 Linux 返回 _REV = 4 时,该支持就中断了。
为了解决这个问题,从 2015 年中期开始,Linux 始终返回 _REV = 2。ACPI 规范也将更新,以反映 _REV 已被弃用,并且始终返回 2。
Apple Mac 和 _OSI(“Darwin”)
在 Apple 的 Mac 平台上,ACPI BIOS 调用 _OSI(“Darwin”) 来确定机器是否正在运行 Apple OSX。
与 Linux 的 _OSI(“Windows”) 策略一样,Linux 默认对 _OSI(“Darwin”) 回答 YES,以启用对 OSX 看到的硬件和经过验证的 BIOS 路径的完全访问。就像在经过 Windows 测试的平台上一样,这种策略存在风险。
从 Linux-3.18 开始,内核对 _OSI(“Darwin”) 的回答为 YES,目的是启用 Mac Thunderbolt 支持。此外,如果内核注意到 _OSI(“Darwin”) 被调用,它还会禁用所有 _OSI(”Windows”),以防止编写不当的 Mac BIOS 进入未经测试的路径组合。
Linux-3.18 的默认更改导致 Mac 笔记本电脑的功率下降,并且 3.18 实现不允许通过命令行“acpi_osi=!Darwin”更改默认值。Linux-4.7 修复了使用 acpi_osi=!Darwin 作为解决方法的能力,我们希望在 Linux-4.11 中看到 Mac Thunderbolt 电源管理支持。