Skip to content

Commit 9c0beb6

Browse files
Wer-Wolfjwrdegoede
authored andcommitted
platform/x86: wmi: Add MSI WMI Platform driver
Add a new driver for the MSI WMI Platform interface. The underlying ACPI WMI interface supports many features, but so far only reading of fan speed sensors is implemented. The driver was reverse-engineered based on a user request to the lm-sensors project, see the github issue for details. The ACPI WMI interface used by this driver seems to use the same embedded controller interface as the msi-ec driver, but supports automatic discovery of supported machines without relying on a DMI whitelist. The driver was tested by the user who created the github issue. Closes: lm-sensors/lm-sensors#475 Signed-off-by: Armin Wolf <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Hans de Goede <[email protected]> Signed-off-by: Hans de Goede <[email protected]>
1 parent 44bbcc2 commit 9c0beb6

File tree

6 files changed

+656
-0
lines changed

6 files changed

+656
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
What: /sys/kernel/debug/msi-wmi-platform-<wmi_device_name>/*
2+
Date: April 2024
3+
KernelVersion: 6.10
4+
Contact: Armin Wolf <[email protected]>
5+
Description:
6+
This file allows to execute the associated WMI method with the same name.
7+
8+
To start the execution, write a buffer containing the method arguments
9+
at file offset 0. Partial writes or writes at a different offset are not
10+
supported.
11+
12+
The buffer returned by the WMI method can then be read from the file.
13+
14+
See Documentation/wmi/devices/msi-wmi-platform.rst for details.
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
.. SPDX-License-Identifier: GPL-2.0-or-later
2+
3+
===================================================
4+
MSI WMI Platform Features driver (msi-wmi-platform)
5+
===================================================
6+
7+
Introduction
8+
============
9+
10+
Many MSI notebooks support various features like reading fan sensors. This features are controlled
11+
by the embedded controller, with the ACPI firmware exposing a standard ACPI WMI interface on top
12+
of the embedded controller interface.
13+
14+
WMI interface description
15+
=========================
16+
17+
The WMI interface description can be decoded from the embedded binary MOF (bmof)
18+
data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
19+
20+
::
21+
22+
[WMI, Locale("MS\0x409"),
23+
Description("This class contains the definition of the package used in other classes"),
24+
guid("{ABBC0F60-8EA1-11d1-00A0-C90629100000}")]
25+
class Package {
26+
[WmiDataId(1), read, write, Description("16 bytes of data")] uint8 Bytes[16];
27+
};
28+
29+
[WMI, Locale("MS\0x409"),
30+
Description("This class contains the definition of the package used in other classes"),
31+
guid("{ABBC0F63-8EA1-11d1-00A0-C90629100000}")]
32+
class Package_32 {
33+
[WmiDataId(1), read, write, Description("32 bytes of data")] uint8 Bytes[32];
34+
};
35+
36+
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"),
37+
Description("Class used to operate methods on a package"),
38+
guid("{ABBC0F6E-8EA1-11d1-00A0-C90629100000}")]
39+
class MSI_ACPI {
40+
[key, read] string InstanceName;
41+
[read] boolean Active;
42+
43+
[WmiMethodId(1), Implemented, read, write, Description("Return the contents of a package")]
44+
void GetPackage([out, id(0)] Package Data);
45+
46+
[WmiMethodId(2), Implemented, read, write, Description("Set the contents of a package")]
47+
void SetPackage([in, id(0)] Package Data);
48+
49+
[WmiMethodId(3), Implemented, read, write, Description("Return the contents of a package")]
50+
void Get_EC([out, id(0)] Package_32 Data);
51+
52+
[WmiMethodId(4), Implemented, read, write, Description("Set the contents of a package")]
53+
void Set_EC([in, id(0)] Package_32 Data);
54+
55+
[WmiMethodId(5), Implemented, read, write, Description("Return the contents of a package")]
56+
void Get_BIOS([in, out, id(0)] Package_32 Data);
57+
58+
[WmiMethodId(6), Implemented, read, write, Description("Set the contents of a package")]
59+
void Set_BIOS([in, out, id(0)] Package_32 Data);
60+
61+
[WmiMethodId(7), Implemented, read, write, Description("Return the contents of a package")]
62+
void Get_SMBUS([in, out, id(0)] Package_32 Data);
63+
64+
[WmiMethodId(8), Implemented, read, write, Description("Set the contents of a package")]
65+
void Set_SMBUS([in, out, id(0)] Package_32 Data);
66+
67+
[WmiMethodId(9), Implemented, read, write, Description("Return the contents of a package")]
68+
void Get_MasterBattery([in, out, id(0)] Package_32 Data);
69+
70+
[WmiMethodId(10), Implemented, read, write, Description("Set the contents of a package")]
71+
void Set_MasterBattery([in, out, id(0)] Package_32 Data);
72+
73+
[WmiMethodId(11), Implemented, read, write, Description("Return the contents of a package")]
74+
void Get_SlaveBattery([in, out, id(0)] Package_32 Data);
75+
76+
[WmiMethodId(12), Implemented, read, write, Description("Set the contents of a package")]
77+
void Set_SlaveBattery([in, out, id(0)] Package_32 Data);
78+
79+
[WmiMethodId(13), Implemented, read, write, Description("Return the contents of a package")]
80+
void Get_Temperature([in, out, id(0)] Package_32 Data);
81+
82+
[WmiMethodId(14), Implemented, read, write, Description("Set the contents of a package")]
83+
void Set_Temperature([in, out, id(0)] Package_32 Data);
84+
85+
[WmiMethodId(15), Implemented, read, write, Description("Return the contents of a package")]
86+
void Get_Thermal([in, out, id(0)] Package_32 Data);
87+
88+
[WmiMethodId(16), Implemented, read, write, Description("Set the contents of a package")]
89+
void Set_Thermal([in, out, id(0)] Package_32 Data);
90+
91+
[WmiMethodId(17), Implemented, read, write, Description("Return the contents of a package")]
92+
void Get_Fan([in, out, id(0)] Package_32 Data);
93+
94+
[WmiMethodId(18), Implemented, read, write, Description("Set the contents of a package")]
95+
void Set_Fan([in, out, id(0)] Package_32 Data);
96+
97+
[WmiMethodId(19), Implemented, read, write, Description("Return the contents of a package")]
98+
void Get_Device([in, out, id(0)] Package_32 Data);
99+
100+
[WmiMethodId(20), Implemented, read, write, Description("Set the contents of a package")]
101+
void Set_Device([in, out, id(0)] Package_32 Data);
102+
103+
[WmiMethodId(21), Implemented, read, write, Description("Return the contents of a package")]
104+
void Get_Power([in, out, id(0)] Package_32 Data);
105+
106+
[WmiMethodId(22), Implemented, read, write, Description("Set the contents of a package")]
107+
void Set_Power([in, out, id(0)] Package_32 Data);
108+
109+
[WmiMethodId(23), Implemented, read, write, Description("Return the contents of a package")]
110+
void Get_Debug([in, out, id(0)] Package_32 Data);
111+
112+
[WmiMethodId(24), Implemented, read, write, Description("Set the contents of a package")]
113+
void Set_Debug([in, out, id(0)] Package_32 Data);
114+
115+
[WmiMethodId(25), Implemented, read, write, Description("Return the contents of a package")]
116+
void Get_AP([in, out, id(0)] Package_32 Data);
117+
118+
[WmiMethodId(26), Implemented, read, write, Description("Set the contents of a package")]
119+
void Set_AP([in, out, id(0)] Package_32 Data);
120+
121+
[WmiMethodId(27), Implemented, read, write, Description("Return the contents of a package")]
122+
void Get_Data([in, out, id(0)] Package_32 Data);
123+
124+
[WmiMethodId(28), Implemented, read, write, Description("Set the contents of a package")]
125+
void Set_Data([in, out, id(0)] Package_32 Data);
126+
127+
[WmiMethodId(29), Implemented, read, write, Description("Return the contents of a package")]
128+
void Get_WMI([out, id(0)] Package_32 Data);
129+
};
130+
131+
Due to a peculiarity in how Windows handles the ``CreateByteField()`` ACPI operator (errors only
132+
happen when a invalid byte field is ultimately accessed), all methods require a 32 byte input
133+
buffer, even if the Binay MOF says otherwise.
134+
135+
The input buffer contains a single byte to select the subfeature to be accessed and 31 bytes of
136+
input data, the meaning of which depends on the subfeature being accessed.
137+
138+
The output buffer contains a singe byte which signals success or failure (``0x00`` on failure)
139+
and 31 bytes of output data, the meaning if which depends on the subfeature being accessed.
140+
141+
WMI method Get_EC()
142+
-------------------
143+
144+
Returns embedded controller information, the selected subfeature does not matter. The output
145+
data contains a flag byte and a 28 byte controller firmware version string.
146+
147+
The first 4 bits of the flag byte contain the minor version of the embedded controller interface,
148+
with the next 2 bits containing the major version of the embedded controller interface.
149+
150+
The 7th bit signals if the embedded controller page chaged (exact meaning is unknown), and the
151+
last bit signals if the platform is a Tigerlake platform.
152+
153+
The MSI software seems to only use this interface when the last bit is set.
154+
155+
WMI method Get_Fan()
156+
--------------------
157+
158+
Fan speed sensors can be accessed by selecting subfeature ``0x00``. The output data contains
159+
up to four 16-bit fan speed readings in big-endian format. Most machines do not support all
160+
four fan speed sensors, so the remaining reading are hardcoded to ``0x0000``.
161+
162+
The fan RPM readings can be calculated with the following formula:
163+
164+
RPM = 480000 / <fan speed reading>
165+
166+
If the fan speed reading is zero, then the fan RPM is zero too.
167+
168+
WMI method Get_WMI()
169+
--------------------
170+
171+
Returns the version of the ACPI WMI interface, the selected subfeature does not matter.
172+
The output data contains two bytes, the first one contains the major version and the last one
173+
contains the minor revision of the ACPI WMI interface.
174+
175+
The MSI software seems to only use this interface when the major version is greater than two.
176+
177+
Reverse-Engineering the MSI WMI Platform interface
178+
==================================================
179+
180+
.. warning:: Randomly poking the embedded controller interface can potentially cause damage
181+
to the machine and other unwanted side effects, please be careful.
182+
183+
The underlying embedded controller interface is used by the ``msi-ec`` driver, and it seems
184+
that many methods just copy a part of the embedded controller memory into the output buffer.
185+
186+
This means that the remaining WMI methods can be reverse-engineered by looking which part of
187+
the embedded controller memory is accessed by the ACPI AML code. The driver also supports a
188+
debugfs interface for directly executing WMI methods. Additionally, any safety checks regarding
189+
unsupported hardware can be disabled by loading the module with ``force=true``.
190+
191+
More information about the MSI embedded controller interface can be found at the
192+
`msi-ec project <https://github.com/BeardOverflow/msi-ec>`_.
193+
194+
Special thanks go to github user `glpnk` for showing how to decode the fan speed readings.

MAINTAINERS

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15041,6 +15041,14 @@ L: [email protected]
1504115041
S: Orphan
1504215042
F: drivers/platform/x86/msi-wmi.c
1504315043

15044+
MSI WMI PLATFORM FEATURES
15045+
M: Armin Wolf <[email protected]>
15046+
15047+
S: Maintained
15048+
F: Documentation/ABI/testing/debugfs-msi-wmi-platform
15049+
F: Documentation/wmi/devices/msi-wmi-platform.rst
15050+
F: drivers/platform/x86/msi-wmi-platform.c
15051+
1504415052
MSI001 MEDIA DRIVER
1504515053
1504615054
S: Orphan

drivers/platform/x86/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,17 @@ config MSI_WMI
698698
To compile this driver as a module, choose M here: the module will
699699
be called msi-wmi.
700700

701+
config MSI_WMI_PLATFORM
702+
tristate "MSI WMI Platform features"
703+
depends on ACPI_WMI
704+
depends on HWMON
705+
help
706+
Say Y here if you want to have support for WMI-based platform features
707+
like fan sensor access on MSI machines.
708+
709+
To compile this driver as a module, choose M here: the module will
710+
be called msi-wmi-platform.
711+
701712
config XO15_EBOOK
702713
tristate "OLPC XO-1.5 ebook switch"
703714
depends on OLPC || COMPILE_TEST

drivers/platform/x86/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ obj-$(CONFIG_ACPI_QUICKSTART) += quickstart.o
7878
obj-$(CONFIG_MSI_EC) += msi-ec.o
7979
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
8080
obj-$(CONFIG_MSI_WMI) += msi-wmi.o
81+
obj-$(CONFIG_MSI_WMI_PLATFORM) += msi-wmi-platform.o
8182

8283
# OLPC
8384
obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o

0 commit comments

Comments
 (0)