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 演示如何解码风扇速度读数。