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。最簡單的方法是引入一個由 Linux 核心呼叫的裝置特定方法 (_DSM)。

過去,核心曾支援類似 _OSI("Linux-OEM-my_interface_name") 的字串,其中“OEM”表示這是一個 OEM 特定的鉤子,“my_interface_name”描述了該鉤子,它可以是一個 quirk、一個 bug 或一個 bug 修復。

然而,後來發現這被其他 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 來啟用作業系統可能支援的裝置,或者啟用必要的 quirks 或 bug 解決方法,以使平臺與預先存在的作業系統相容。

但 _OS 存在根本性問題。首先,BIOS 需要知道將在其上執行的每個可能版本的作業系統的名稱,並且需要知道這些作業系統的所有 quirks。當然,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 Thermal Model") 將返回 TRUE。一個不知道這些擴充套件的舊作業系統將返回 FALSE,而一個新作業系統可能能夠返回 TRUE。

對於作業系統特定的介面,ACPI 規範指出 BIOS 和作業系統應就諸如“Windows-interface_name”之類的字串達成一致。

但發生了兩件不好的事情。首先,Windows 生態系統使用 _OSI 的方式並非按設計,而是直接替代了 _OS——用於標識作業系統版本,而不是作業系統支援的介面。事實上,從一開始,ACPI 3.0 規範本身就在示例程式碼中透過 _OSI("Windows 2001") 將這種濫用編入了法典。

這種濫用被採納並延續至今。

Linux 別無選擇,只能對 _OSI("Windows 2001") 及其後續版本返回 TRUE。否則,幾乎可以肯定會破壞僅在該 _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") 回答“是”,以完全訪問硬體和 OSX 驗證過的 BIOS 路徑。就像在 Windows 測試平臺上一樣,此策略也存在風險。

從 Linux-3.18 開始,核心對 _OSI("Darwin") 回答“是”,目的是啟用 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 電源管理支援。