3. MSI WMI 平臺特性驅動 (msi-wmi-platform)

3.1. 簡介

許多 MSI 筆記型電腦支援各種功能,例如讀取風扇感測器。這些功能由嵌入式控制器控制,ACPI 韌體在嵌入式控制器介面之上公開了一個標準的 ACPI WMI 介面。

3.2. WMI 介面描述

可以使用 bmfdec 實用程式從嵌入式二進位制 MOF (bmof) 資料中解碼 WMI 介面描述。

[WMI, Locale("MS\0x409"),
 Description("This class contains the definition of the package used in other classes"),
 guid("{ABBC0F60-8EA1-11d1-00A0-C90629100000}")]
class Package {
  [WmiDataId(1), read, write, Description("16 bytes of data")] uint8 Bytes[16];
};

[WMI, Locale("MS\0x409"),
 Description("This class contains the definition of the package used in other classes"),
 guid("{ABBC0F63-8EA1-11d1-00A0-C90629100000}")]
class Package_32 {
  [WmiDataId(1), read, write, Description("32 bytes of data")] uint8 Bytes[32];
};

[WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"),
 Description("Class used to operate methods on a package"),
 guid("{ABBC0F6E-8EA1-11d1-00A0-C90629100000}")]
class MSI_ACPI {
  [key, read] string InstanceName;
  [read] boolean Active;

  [WmiMethodId(1), Implemented, read, write, Description("Return the contents of a package")]
  void GetPackage([out, id(0)] Package Data);

  [WmiMethodId(2), Implemented, read, write, Description("Set the contents of a package")]
  void SetPackage([in, id(0)] Package Data);

  [WmiMethodId(3), Implemented, read, write, Description("Return the contents of a package")]
  void Get_EC([out, id(0)] Package_32 Data);

  [WmiMethodId(4), Implemented, read, write, Description("Set the contents of a package")]
  void Set_EC([in, id(0)] Package_32 Data);

  [WmiMethodId(5), Implemented, read, write, Description("Return the contents of a package")]
  void Get_BIOS([in, out, id(0)] Package_32 Data);

  [WmiMethodId(6), Implemented, read, write, Description("Set the contents of a package")]
  void Set_BIOS([in, out, id(0)] Package_32 Data);

  [WmiMethodId(7), Implemented, read, write, Description("Return the contents of a package")]
  void Get_SMBUS([in, out, id(0)] Package_32 Data);

  [WmiMethodId(8), Implemented, read, write, Description("Set the contents of a package")]
  void Set_SMBUS([in, out, id(0)] Package_32 Data);

  [WmiMethodId(9), Implemented, read, write, Description("Return the contents of a package")]
  void Get_MasterBattery([in, out, id(0)] Package_32 Data);

  [WmiMethodId(10), Implemented, read, write, Description("Set the contents of a package")]
  void Set_MasterBattery([in, out, id(0)] Package_32 Data);

  [WmiMethodId(11), Implemented, read, write, Description("Return the contents of a package")]
  void Get_SlaveBattery([in, out, id(0)] Package_32 Data);

  [WmiMethodId(12), Implemented, read, write, Description("Set the contents of a package")]
  void Set_SlaveBattery([in, out, id(0)] Package_32 Data);

  [WmiMethodId(13), Implemented, read, write, Description("Return the contents of a package")]
  void Get_Temperature([in, out, id(0)] Package_32 Data);

  [WmiMethodId(14), Implemented, read, write, Description("Set the contents of a package")]
  void Set_Temperature([in, out, id(0)] Package_32 Data);

  [WmiMethodId(15), Implemented, read, write, Description("Return the contents of a package")]
  void Get_Thermal([in, out, id(0)] Package_32 Data);

  [WmiMethodId(16), Implemented, read, write, Description("Set the contents of a package")]
  void Set_Thermal([in, out, id(0)] Package_32 Data);

  [WmiMethodId(17), Implemented, read, write, Description("Return the contents of a package")]
  void Get_Fan([in, out, id(0)] Package_32 Data);

  [WmiMethodId(18), Implemented, read, write, Description("Set the contents of a package")]
  void Set_Fan([in, out, id(0)] Package_32 Data);

  [WmiMethodId(19), Implemented, read, write, Description("Return the contents of a package")]
  void Get_Device([in, out, id(0)] Package_32 Data);

  [WmiMethodId(20), Implemented, read, write, Description("Set the contents of a package")]
  void Set_Device([in, out, id(0)] Package_32 Data);

  [WmiMethodId(21), Implemented, read, write, Description("Return the contents of a package")]
  void Get_Power([in, out, id(0)] Package_32 Data);

  [WmiMethodId(22), Implemented, read, write, Description("Set the contents of a package")]
  void Set_Power([in, out, id(0)] Package_32 Data);

  [WmiMethodId(23), Implemented, read, write, Description("Return the contents of a package")]
  void Get_Debug([in, out, id(0)] Package_32 Data);

  [WmiMethodId(24), Implemented, read, write, Description("Set the contents of a package")]
  void Set_Debug([in, out, id(0)] Package_32 Data);

  [WmiMethodId(25), Implemented, read, write, Description("Return the contents of a package")]
  void Get_AP([in, out, id(0)] Package_32 Data);

  [WmiMethodId(26), Implemented, read, write, Description("Set the contents of a package")]
  void Set_AP([in, out, id(0)] Package_32 Data);

  [WmiMethodId(27), Implemented, read, write, Description("Return the contents of a package")]
  void Get_Data([in, out, id(0)] Package_32 Data);

  [WmiMethodId(28), Implemented, read, write, Description("Set the contents of a package")]
  void Set_Data([in, out, id(0)] Package_32 Data);

  [WmiMethodId(29), Implemented, read, write, Description("Return the contents of a package")]
  void Get_WMI([out, id(0)] Package_32 Data);
};

由於 Windows 處理 CreateByteField() ACPI 運算子的特殊性(只有在最終訪問無效位元組欄位時才會發生錯誤),所有方法都需要一個 32 位元組的輸入緩衝區,即使二進位制 MOF 另有說明。

輸入緩衝區包含一個位元組,用於選擇要訪問的子功能,以及 31 個位元組的輸入資料,其含義取決於要訪問的子功能。

輸出緩衝區包含一個位元組,用於指示成功或失敗(失敗時為 0x00),以及 31 個位元組的輸出資料,其含義取決於要訪問的子功能。

注意

負責處理 WMI 方法呼叫的 ACPI 控制方法不是執行緒安全的。 這是一個韌體錯誤,需要在驅動程式內部處理。

3.2.1. WMI 方法 Get_EC()

返回嵌入式控制器資訊,所選的子功能無關緊要。 輸出資料包含一個標誌位元組和一個 28 位元組的控制器韌體版本字串。

標誌位元組的前 4 位包含嵌入式控制器介面的次要版本,接下來的 2 位包含嵌入式控制器介面的主要版本。

第 7 位指示嵌入式控制器頁面是否已更改(確切含義未知),最後一位指示平臺是否為 Tigerlake 平臺。

MSI 軟體似乎僅在設定最後一位時才使用此介面。

3.2.2. WMI 方法 Get_Fan()

可以透過選擇子功能 0x00 來訪問風扇速度感測器。 輸出資料包含最多四個大端格式的 16 位風扇速度讀數。 大多數機器不支援所有四個風扇速度感測器,因此剩餘的讀數被硬編碼為 0x0000

可以使用以下公式計算風扇 RPM 讀數

RPM = 480000 / <風扇速度讀數>

如果風扇速度讀數為零,則風扇 RPM 也為零。

3.2.3. WMI 方法 Get_WMI()

返回 ACPI WMI 介面的版本,所選的子功能無關緊要。 輸出資料包含兩個位元組,第一個包含主要版本,最後一個包含 ACPI WMI 介面的次要修訂版。

MSI 軟體似乎僅在主要版本大於 2 時才使用此介面。

3.3. 逆向工程 MSI WMI 平臺介面

警告

隨機探測嵌入式控制器介面可能會對機器和其他不需要的副作用造成損壞,請小心。

底層嵌入式控制器介面由 msi-ec 驅動程式使用,並且似乎許多方法只是將嵌入式控制器記憶體的一部分複製到輸出緩衝區中。

這意味著可以透過檢視 ACPI AML 程式碼訪問嵌入式控制器記憶體的哪個部分來對剩餘的 WMI 方法進行逆向工程。 該驅動程式還支援 debugfs 介面,用於直接執行 WMI 方法。 此外,可以透過使用 force=true 載入模組來停用有關不支援硬體的任何安全檢查。

有關 MSI 嵌入式控制器介面的更多資訊,請訪問 msi-ec 專案

特別感謝 github 使用者 glpnk 演示如何解碼風扇速度讀數。