ACPI _OSI 和 _REV 方法

ACPI BIOS 可以使用“操作系统接口”方法 (_OSI) 来了解操作系统支持什么。 例如,如果 BIOS AML 代码包含 _OSI(“XYZ”),内核的 AML 解释器可以评估该方法,查看它是否支持 ‘XYZ’ 并向 BIOS 回答是或否。

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 应该创建自定义 ASL,由 Linux 内核执行,并更改 Linux 内核驱动程序来执行此自定义 ASL。 最简单的方法是引入一个特定于设备的方法 (_DSM),该方法从 Linux 内核调用。

过去,内核曾经支持类似这样的东西:_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 提供描述接口的字符串,并询问操作系统:“是/否,你是否与此接口兼容?”

例如,如果操作系统知道如何处理 ACPI 3.0 规范的热扩展,则 _OSI(“3.0 热模型”) 将返回 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 社区通过返回 TRUE 来响应 _OSI(“Linux”) 使情况变得更糟。这样做甚至比 Windows 滥用 _OSI 更糟糕,因为“Linux”甚至不包含任何版本信息。由于 BIOS 编写者在未经测试的 BIOS 流中使用它,_OSI(“Linux”) 导致某些 BIOS 出现故障。但是一些 OEM 在经过测试的流程中使用 _OSI(“Linux”) 来支持真正的 Linux 功能。2009 年,Linux 删除了 _OSI(“Linux”),并添加了一个 cmdline 参数来为仍然需要它的旧系统恢复它。此外,还会为调用它的所有 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 时,该支持中断了。

为了解决这个问题,Linux 从 2015 年年中开始始终返回 _REV = 2。ACPI 规范也将更新以反映 _REV 已被弃用,并且始终返回 2。

Apple Mac 和 _OSI(“Darwin”)

在 Apple 的 Mac 平台上,ACPI BIOS 调用 _OSI(“Darwin”) 来确定机器是否正在运行 Apple OSX。

就像 Linux 的 _OSI(”Windows”) 策略一样,Linux 默认回答 YES 来响应 _OSI(“Darwin”),以允许完全访问硬件和 OSX 所见的已验证的 BIOS 路径。 就像在 Windows 测试的平台上一样,这种策略存在风险。

从 Linux-3.18 开始,内核回答 YES 来响应 _OSI(“Darwin”),目的是启用 Mac Thunderbolt 支持。 此外,如果内核注意到正在调用 _OSI(“Darwin”),它还会禁用所有 _OSI(”Windows”),以防止编写不良的 Mac BIOS 进入未经测试的路径组合。

Linux-3.18 中默认值的更改导致 Mac 笔记本电脑的功耗回归,并且 3.18 实现不允许通过 cmdline “acpi_osi=!Darwin” 更改默认值。 Linux-4.7 修复了将 acpi_osi=!Darwin 用作解决方法的能力,我们希望在 Linux-4.11 中看到 Mac Thunderbolt 电源管理支持。