BeardOverflow / msi-ec

GNU General Public License v2.0
132 stars 40 forks source link

Help in reverse engineering the MSI WMI interface #108

Open Wer-Wolf opened 4 months ago

Wer-Wolf commented 4 months ago

Hello,

i am working on reverse engineering the MSI WMI interface, which does provide similar access to the EC:

class WMIEvent : __ExtrinsicEvent { };

[WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"), Description("Class to ECRam Query/Set Software"), guid("{24418D6A-0A79-524C-9AB1-18B78CA68CE7}")] class MSI_Software { [key, read] string InstanceName; [read] boolean Active; [WmiDataId(1), read, write, Description("Software")] uint8 Software; };

[WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"), Description("Class to ECRam Query/Set Device"), guid("{4AFBD56B-9F91-8f49-81F5-995BA73822AF}")] class MSI_Device { [key, read] string InstanceName; [read] boolean Active; [WmiDataId(1), read, write, Description("Device")] uint8 Device; };

[WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"), Description("Class to ECRam Query/Set Power"), guid("{2D3CBA6C-1C9C-7f41-B54C-F5D5D580D482}")] class MSI_Power { [key, read] string InstanceName; [read] boolean Active; [WmiDataId(1), read, write, Description("Power")] uint8 Power; };

[WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"), Description("Class to ECRam Query/Set Master_Battery"), guid("{40BA026D-075D-cd4a-9710-F7C57347CAC9}")] class MSI_Master_Battery { [key, read] string InstanceName; [read] boolean Active; [WmiDataId(1), read, write, Description("Master_Battery")] sint16 Master_Battery; };

[WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"), Description("Class to ECRam Query/Set Slave_Battery"), guid("{8DBCCF6E-9DB4-0e46-A3F2-99AFAAA77A7A}")] class MSI_Slave_Battery { [key, read] string InstanceName; [read] boolean Active; [WmiDataId(1), read, write, Description("Slave_Battery")] sint16 Slave_Battery; };

[WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"), Description("Class to ECRam Query/Set CPU"), guid("{BD2A216F-2FB9-a640-B807-DDDBAD656891}")] class MSI_CPU { [key, read] string InstanceName; [read] boolean Active; [WmiDataId(1), read, write, Description("CPU")] uint8 CPU; };

[WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"), Description("Class to ECRam Query/Set VGA"), guid("{1EC3EC7A-1E9B-e74a-9026-CF122B0BBD21}")] class MSI_VGA { [key, read] string InstanceName; [read] boolean Active; [WmiDataId(1), read, write, Description("VGA")] uint8 VGA; };

[WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"), Description("Class to ECRam Query/Set System"), guid("{A1753D7B-B621-de4a-B41A-55716A0ECE7A}")] class MSI_System { [key, read] string InstanceName; [read] boolean Active; [WmiDataId(1), read, write, Description("System")] uint8 System; };

[WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"), Description("Class to ECRam Query/Set AP"), guid("{A1753D7C-B621-de4a-B41A-55716A0ECE7A}")] class MSI_AP { [key, read] string InstanceName; [read] boolean Active; [WmiDataId(1), read, write, Description("AP")] uint8 AP; };

[WMI, Dynamic, Provider("WmiProv"), Local("MS\0x409"), Description("Event defined by MSI"), guid("{5B3CC38A-40D9-7245-8AE6-1145B751BE3F}")] class MSI_Event : WMIEvent { [key, read] string InstanceName; [read] boolean Active; [WmiDataId(1), read, write, Description("Event defined by MSI")] uint32 MSIEvt; };

[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("{ABBC0F61-8EA1-11d1-00A0-C90629100000}")] class Package_1 { [WmiDataId(1), read, write, Description("1 bytes of data")] uint8 Bytes[1]; };

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

[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); };

A user asked me for reverse engineering the Get_Fan method, see here for additional data.

Since you guys have seen a look of MSI EC register layouts, maybe you can help us decode the fan data?

The WMI method read the following registers:

Since we suspect that other MSI ECs use a similar register layout, maybe you can help us in finding out what those registers contain.

Thanks, Armin Wolf

glpnk commented 4 months ago

Hi @Wer-Wolf, I've analyzing WMI and WMI2 interface too. Look at #98 and #101 (TLDR about versions). I guess that all WMI2 layout is identical, at least inside specific version. Get_WMI returns this WMI2 version. You can check DSDT Device (SCM0) which has Name (MAJR, 0x02); Name (MINR, 0x08) or similar. WMI1 don't have these parameters.

Do you use ImHex? I wrote hexpat for WMI1 and WMI2 based on DSDT content. WMI2 map include ThePBone pattern. Also, I've planned to add a map from MControlCenter

How you decompiled BMOF? MS mofcomp or pali's bmfdec

glpnk commented 4 months ago

Since you guys have seen a look of MSI EC register layouts, maybe you can help us decode the fan data?

I think that exists 2-3 different layout generation, 2 of them I call WMI1 and WMI2

According to DSDT, and some text files from app folder, looks like MSI laptop cold have up to 4 cooler.

image

This is Get/Set_Fan, RPM (or derivative) is on 0 argument [C8-CF], CPU is 1 [71-78], GPU is [89-90].

So you need to call [fully qualified ACPI path to SCM (ends on WMAM)] [any] [WMI id for Get_Fan] [[0, ....]], where [[]] is Package with length 0x20, and the first byte is the argument from sentence above.

glpnk commented 4 months ago

Are you implementing thermal curve?

You should look for Get/Set_Temperature. It is define temperature levels for speed levels from Fan

image

[0]: 68 - CPUt, 80 GPUt, 9F unused? [1]: 69-70 CPU thermal curve [2]: 81-88 GPU thermal curve

In WMI2 temps in 1,2 lists is mixed for some reason.

The WMI method read the following registers:

Parameter 0x00: 0xC8 - 0xCF Parameter 0x01: 0x71 - 0x78 Parameter 0x02: 0x89 - 0x90

0 - RPMs, 1 - CPU fan curve, 2 - GPU fan curve Fan curves in range 0-150%

Also, exists Get/Set_Thermal but it's content looks weird

image

UPD: you can check how implemented RPM conversion in MControlCenter it uses some averaged value. Result in MSI app is slightly different. Report

Wer-Wolf commented 4 months ago

Many thanks, i can confirm that this WMI interface is called WMI2 by MSI Center. Is there a way to detect how many fans are available (MSI Center shows a single fan)?

glpnk commented 4 months ago

I've analyzed apps a little, but drop it because of questionable legality.

I can share my hexpats if you want

Wer-Wolf commented 4 months ago

Sure

glpnk commented 4 months ago

https://github.com/glpnk/hexpats

You need to use nightly version or Web because of some new features for comfort pattern toggle

glpnk commented 4 months ago

@Wer-Wolf Which tool you use to decompile BMOF?

Wer-Wolf commented 4 months ago

I used bmfdec, but it needs a patch in order to fully decode the MOF file.

Wer-Wolf commented 4 months ago

The owner cannot currently access the repository, so it will take a while

Wer-Wolf commented 4 months ago

I also found a possible formula to calculate the fan rpm from the bytes in buffer 0x00:

Fan RPM = (1 / Byte ) * 480000

Wer-Wolf commented 4 months ago

Seems the fan speed is measured in clock circles of some internal reference clock.

glpnk commented 4 months ago

On WMI1 "RPM" registries is called AP. I also can't imagine the laptop with 4 coolers

glpnk commented 4 months ago

On kernel /drivers/platform/x86/msi-wmi.c listed some different from "SCM"(WMI1/2) GUIDs.

BMOFs on latest apps is the same, older devices use app with only WMI1 part

According to dll metadata, some components are pretty old

Wer-Wolf commented 4 months ago

Yes, msi-wmi seems to control a different WMI interface.

glpnk commented 4 months ago

I also found a possible formula to calculate the fan rpm from the bytes in buffer 0x00:

Fan RPM = (1 / Byte ) * 480000

https://github.com/dmitry-s93/MControlCenter/blob/a23f483faf4ec9190a99c2a1eaf72bce194eefd0/src/operate.cpp#L164-L169

They use different const, but close in value

glpnk commented 4 months ago

Will be great to analyze how events works. @Wer-Wolf do you know some dummy ACPI-WMI events handler? According to DSDT table, there are not many different values are returned, just with different header value/event id(?)

Wer-Wolf commented 4 months ago

You mean a driver which just logs the received WMI events to console?

Wer-Wolf commented 4 months ago

I can write such a driver, if you wish.

glpnk commented 4 months ago

Yeah, something like you write GUID to sysfs file and read events from it. Or something easier to implement. With just built-in GUID and log to file.

Wer-Wolf commented 4 months ago

I can create a generic WMI event debugging driver which logs all received events to dmesg. This driver can then be selected with the standard driver_override sysfs attribute.

glpnk commented 4 months ago

Awesome!

Wer-Wolf commented 4 months ago

I finished the necessary patches, but they depend on some other patches to the WMI driver core. I will notify you when i finished upstreaming those patches.

glpnk commented 3 months ago

Hi, @Wer-Wolf do you know which driver is used for accessing SystemMemory Region in DSDT? Or it just mapping kernel memory space without any driver?

Wer-Wolf commented 3 months ago

AFAIK no special driver is used for this, just the general purpose memory mapping subsystem. Why are you asking?

glpnk commented 3 months ago

Thanks. My friend have Xiaomi laptop with, probably, software disabled camera and microphone, and it's EC mapped as SystemMemory