요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0-or-later
===================================================
MSI WMI Platform Features driver (msi-wmi-platform)
===================================================
Introduction
============
Many MSI notebooks support various features like reading fan sensors. This features are controlled
by the embedded controller, with the ACPI firmware exposing a standard ACPI WMI interface on top
of the embedded controller interface.
WMI interface description
=========================
The WMI interface description can be decoded from the embedded binary MOF (bmof)
data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
::
[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);
};
Due to a peculiarity in how Windows handles the ``CreateByteField()`` ACPI operator (errors only
happen when a invalid byte field is ultimately accessed), all methods require a 32 byte input
buffer, even if the Binary MOF says otherwise.
The input buffer contains a single byte to select the subfeature to be accessed and 31 bytes of
input data, the meaning of which depends on the subfeature being accessed.
The output buffer contains a single byte which signals success or failure (``0x00`` on failure)
and 31 bytes of output data, the meaning if which depends on the subfeature being accessed.
.. note::
The ACPI control method responsible for handling the WMI method calls is not thread-safe.
This is a firmware bug that needs to be handled inside the driver itself.
WMI method Get_EC()
-------------------
Returns embedded controller information, the selected subfeature does not matter. The output
data contains a flag byte and a 28 byte controller firmware version string.
The first 4 bits of the flag byte contain the minor version of the embedded controller interface,
with the next 2 bits containing the major version of the embedded controller interface.
The 7th bit signals if the embedded controller page changed (exact meaning is unknown), and the
last bit signals if the platform is a Tigerlake platform.
The MSI software seems to only use this interface when the last bit is set.
WMI method Get_Fan()
--------------------
Fan speed sensors can be accessed by selecting subfeature ``0x00``. The output data contains
up to four 16-bit fan speed readings in big-endian format. Most machines do not support all
four fan speed sensors, so the remaining reading are hardcoded to ``0x0000``.
The fan RPM readings can be calculated with the following formula:
RPM = 480000 / <fan speed reading>
If the fan speed reading is zero, then the fan RPM is zero too.
WMI method Get_WMI()
--------------------
Returns the version of the ACPI WMI interface, the selected subfeature does not matter.
The output data contains two bytes, the first one contains the major version and the last one
contains the minor revision of the ACPI WMI interface.
The MSI software seems to only use this interface when the major version is greater than two.
Reverse-Engineering the MSI WMI Platform interface
==================================================
.. warning:: Randomly poking the embedded controller interface can potentially cause damage
to the machine and other unwanted side effects, please be careful.
The underlying embedded controller interface is used by the ``msi-ec`` driver, and it seems
that many methods just copy a part of the embedded controller memory into the output buffer.
This means that the remaining WMI methods can be reverse-engineered by looking which part of
the embedded controller memory is accessed by the ACPI AML code. The driver also supports a
debugfs interface for directly executing WMI methods. Additionally, any safety checks regarding
unsupported hardware can be disabled by loading the module with ``force=true``.
More information about the MSI embedded controller interface can be found at the
`msi-ec project <https://github.com/BeardOverflow/msi-ec>`_.
Special thanks go to github user `glpnk` for showing how to decode the fan speed readings.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
EC 위의 ACPI WMI layer
1-13많은 MSI notebook은 fan sensor 읽기 같은 기능을 지원합니다. embedded controller가 기능을 제어하고 ACPI firmware가 EC interface 위에 표준 ACPI WMI interface를 노출합니다.
.. SPDX-License-Identifier: GPL-2.0-or-later
===================================================
MSI WMI Platform Features driver (msi-wmi-platform)
===================================================
Introduction
============
Many MSI notebooks support various features like reading fan sensors. This features are controlled
by the embedded controller, with the ACPI firmware exposing a standard ACPI WMI interface on top
of the embedded controller interface.
16·32바이트 package class
14-35embedded binary MOF는 `bmfdec`로 decode할 수 있습니다. `Package` class는 16바이트, `Package_32`는 32바이트 byte array를 담고 다른 class의 method I/O에 사용됩니다.
WMI method가 전달하는 고정 크기 buffer입니다.
WMI interface description
=========================
The WMI interface description can be decoded from the embedded binary MOF (bmof)
data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
::
[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];
};
MSI_ACPI method
36-130GUID `ABBC0F6E-8EA1-11d1-00A0-C90629100000`의 `MSI_ACPI` class는 package, EC, BIOS, SMBus, battery, temperature, thermal, fan, device, power, debug, AP, generic data와 WMI version을 다루는 29개 method를 선언합니다.
각 WmiMethodId가 조회하거나 설정하는 package입니다.
[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);
};
32바이트 I/O protocol과 직렬화
131-144Windows가 ACPI `CreateByteField()` 오류를 실제 잘못된 byte field에 접근할 때만 발생시키는 특성 때문에 binary MOF의 선언과 관계없이 모든 method에는 32바이트 입력 buffer가 필요합니다.
입력은 subfeature를 고르는 1바이트와 subfeature별 의미를 갖는 data 31바이트입니다. 출력은 성공·실패를 나타내는 1바이트와 output data 31바이트이며, status `0x00`은 실패를 뜻합니다.
WMI method call을 처리하는 ACPI control method는 thread-safe하지 않습니다. 이는 driver 내부에서 직렬화해 처리해야 하는 firmware bug입니다.
고정 32바이트 입력·출력 layout입니다.
Due to a peculiarity in how Windows handles the ``CreateByteField()`` ACPI operator (errors only
happen when a invalid byte field is ultimately accessed), all methods require a 32 byte input
buffer, even if the Binary MOF says otherwise.
The input buffer contains a single byte to select the subfeature to be accessed and 31 bytes of
input data, the meaning of which depends on the subfeature being accessed.
The output buffer contains a single byte which signals success or failure (``0x00`` on failure)
and 31 bytes of output data, the meaning if which depends on the subfeature being accessed.
.. note::
The ACPI control method responsible for handling the WMI method calls is not thread-safe.
This is a firmware bug that needs to be handled inside the driver itself.
Get_EC controller 정보
145-158`Get_EC`는 선택한 subfeature와 관계없이 EC 정보를 반환합니다. output에는 flag 1바이트와 28바이트 controller firmware version 문자열이 들어 있습니다.
flag의 처음 4bit는 EC interface minor version, 다음 2bit는 major version입니다. 7번째 bit는 EC page 변경을 나타내지만 정확한 의미는 알려지지 않았고, 마지막 bit는 Tigerlake platform 여부를 나타냅니다. MSI software는 마지막 bit가 설정된 경우에만 이 interface를 사용하는 것으로 보입니다.
flag byte의 알려진 bit 의미입니다.
WMI method Get_EC()
-------------------
Returns embedded controller information, the selected subfeature does not matter. The output
data contains a flag byte and a 28 byte controller firmware version string.
The first 4 bits of the flag byte contain the minor version of the embedded controller interface,
with the next 2 bits containing the major version of the embedded controller interface.
The 7th bit signals if the embedded controller page changed (exact meaning is unknown), and the
last bit signals if the platform is a Tigerlake platform.
The MSI software seems to only use this interface when the last bit is set.
Get_Fan RPM 계산
159-171`Get_Fan`은 subfeature `0x00`으로 fan speed sensor를 읽습니다. output은 최대 네 개의 big-endian 16비트 fan speed reading을 담습니다. 지원하지 않는 sensor 자리는 `0x0000`으로 고정됩니다.
RPM은 `RPM = 480000 / <fan speed reading>`으로 계산합니다. reading이 0이면 RPM도 0으로 처리해 0으로 나누지 않습니다.
raw reading을 RPM으로 바꾸는 조건입니다.
WMI method Get_Fan()
--------------------
Fan speed sensors can be accessed by selecting subfeature ``0x00``. The output data contains
up to four 16-bit fan speed readings in big-endian format. Most machines do not support all
four fan speed sensors, so the remaining reading are hardcoded to ``0x0000``.
The fan RPM readings can be calculated with the following formula:
RPM = 480000 / <fan speed reading>
If the fan speed reading is zero, then the fan RPM is zero too.
Get_WMI interface version
172-180`Get_WMI`는 subfeature와 관계없이 ACPI WMI interface version을 반환합니다. output 첫 byte는 major version, 두 번째이자 마지막 byte는 minor revision입니다.
MSI software는 major version이 2보다 큰 경우에만 이 interface를 사용하는 것으로 보입니다.
WMI method Get_WMI()
--------------------
Returns the version of the ACPI WMI interface, the selected subfeature does not matter.
The output data contains two bytes, the first one contains the major version and the last one
contains the minor revision of the ACPI WMI interface.
The MSI software seems to only use this interface when the major version is greater than two.
MSI WMI reverse engineering
181-198embedded controller interface를 무작위로 건드리면 장치 손상과 원치 않는 부작용이 생길 수 있으므로 매우 주의해야 합니다.
기반 EC interface는 `msi-ec` 드라이버도 사용하며 많은 method가 EC memory 일부를 output buffer로 복사하는 것으로 보입니다. 따라서 ACPI AML이 어느 EC memory 영역에 접근하는지 조사해 나머지 method를 분석할 수 있습니다.
driver의 debugfs interface로 WMI method를 직접 실행할 수 있으며 `force=true`로 load하면 미지원 hardware 안전 검사를 끌 수 있습니다. 이는 분석용 기능으로 위험성을 이해하고 사용해야 합니다.
MSI EC interface의 추가 정보는 `msi-ec` project에 있으며 fan speed reading 해석 방법을 알려 준 GitHub 사용자 `glpnk`에게 감사를 표합니다.
Reverse-Engineering the MSI WMI Platform interface
==================================================
.. warning:: Randomly poking the embedded controller interface can potentially cause damage
to the machine and other unwanted side effects, please be careful.
The underlying embedded controller interface is used by the ``msi-ec`` driver, and it seems
that many methods just copy a part of the embedded controller memory into the output buffer.
This means that the remaining WMI methods can be reverse-engineered by looking which part of
the embedded controller memory is accessed by the ACPI AML code. The driver also supports a
debugfs interface for directly executing WMI methods. Additionally, any safety checks regarding
unsupported hardware can be disabled by loading the module with ``force=true``.
More information about the MSI embedded controller interface can be found at the
`msi-ec project <https://github.com/BeardOverflow/msi-ec>`_.
Special thanks go to github user `glpnk` for showing how to decode the fan speed readings.
요약·해설
msi-wmi-platform.rst:1-198MSI notebook EC 위의 32바이트 WMI protocol, fan RPM과 interface version 조회를 설명합니다.