Skip to content
Eduard Nicodei edited this page Jul 22, 2016 · 2 revisions

Disclaimer

Information contained in this page is purely for educational purposes. I can only say this solution worked only for me. It might work for you or it might not. If it works, it will likely decrease the lifespan of your laptop's fan. I will not take any responsibility for anything that happens after using software referenced on this page. Use at your own risk!

Abstract

The Acer 5750G laptop can sometimes overheat during periods of intense CPU/GPU usage. Normally, the hardware automatically adjusts the fan speed to compensate. However, even when the sensors report temperatures of about 80^^o^^C, the fan is only spinning at about 70% of its maximum RPM. Allegedly, this is intentional: Acer designed the hardware such that the fan spins at maximum RPM only when the temperature reaches 100 C (source); indeed, this seems to be indicated in the service manual (see "Acer Aspire 5750G Service Manual, Page 35, CPU Fan True Value Table (Tj = 105)").

Users have complained about their laptops (at least 5750G and 5755G suffer from this problem) getting hot and how the fan can go faster and it does spin at 100% power when performing a BIOS update. Acer then released a solution in the form of a simple .exe that forces the fan to maximum RPM.

No equivalent application was released for GNU/Linux. Acer seem to support only OEM version of Windows (source). One could have asked them but that would have implied waiting and it definitely wouldn't have been as fun. As such, the assembly code of the FanController.exe utility was analysed (reverse engineered) and the behaviour replicated to allow the same functionality on non-Windows platforms.

Motivation

Why did I decide to write this document?

  • provide a set of references and information to anybody using GNU/Linux and interested in resolving overheating issue with their Acer 5750 (or similar) laptop
  • give a complete description of what was done, to hopefully ease future cases of EC firmware reverse engineering
  • provide an account for education purposes of how simple reverse engineering can be accomplished

Disassembling FanController.exe

The FanController utility was originally posted here. It consists of a simple executable (FanController.exe) with two buttons: one that sets fan speed to "Normal" and one that sets it to "Full Speed".

The reverse engineering was done almost entirely using OllyDbg. Address randomization (ASLR) was disabled in order to ease remembering and working with offsets. Tutorials here and here. The first tutorial was followed: Use CFF Explorer to enable "Relocation info stripped from file" flag (Nt Headers -> File Headers -> Characteristics).

Dynamic loading of eblib.dll and importing EC_686CPort_ReceiveCOMM

The first step was to notice that FanController.exe dynamically imported eblib.dll and that it also got a reference to "EC_686CPort_ReceiveCOMM".

That function was called from two places at 0x00401B40 and 0x00401BB0. These seem like callback they are called respectively when the two buttons are pressed. From then it was just a matter of determining what information was passed to eblib.EC_686CPort_ReceiveCOMM and what it did with it.

It turns out the "magic" function takes just 4 arguments: an in buffer and an out buffer. When called from the "Normal speed" callback, the in buffer contained the value 0x76 and when called from the "Full speed" callback, the value was 0x77. Otherwise, both handler functions were identical and discarded all bytes from the out buffer (which was of size 0x14 and was always filled of 0xE0 values).

Inside eblib.dll

The next step was to trace the execution of EC_686CPort_ReceiveCOMM. We end up calling KERNEL32.DeviceIoControl. However, in nearby (error handling) code, we have references to strings such as "IO_Port_Write_UCHAR—>DeviceIoControl Fail!". There are probably from printf statements in the form of:

printf("%s: error", __func__);

Therefore they gave away information about the functions.

Then, starting from these leaf functions (ones that call into KERNEL32), we can work our way back. We can also note that DeviceIoControl take a HANDLE as an argument. Looking again through the trace, we see a call to CreateFileA. The filename is \\.\LPCFilter. Looking through Acer 5750G schematics we see the EC is connected to the CPU via an LPC bus.

Starting from leaf functions working our way back.

Quite some time was spent then piecing together all of the various functions that do reads, writes and any other things. Starting from what arguments they received (the values pushed on the stack) and ending up tracing step by step to see what was the role of each function.

One interesting note, the function defined at eblib.0x4930, contains the following instruction at address 0x495B: CMP ESI, 2710, 0x2710 in hex is 10000 in decimal meaning that this was very likely an "arbitrary" value. Indeed the function goes in a loop, incrementing ESI each time.

But because this value, it becomes probable that the exact number of iterations required isn't actually known, which hints at the fact that this is a busy loop, reading a port and exiting either when all 10000 iterations have been done, or the required value is read.

The function that does the fun stuff is eblib.0x4A90. Note1: all waits on bitmasks are from port 0x6C. Note2: indentation here means "stuff happening inside subfunction" eblib.0x4A90:

  • wait until mask 0x80 is 0x0, if fail/timeout, exit
  • do some sort of intro sequence:
    • read byte from 0x68
    • wait until mask 0x02 is 0x0 do not check result.
    • write 0x59 to 0x6C
  • call function that "does the magic":
    • wait until mask 0x02 is 0x0
    • write CMD (76 or 77) to port 0x68
  • call function to read rx bytes (there are however discarded by fancontroller):
    • wait until mask 0x01 is 0x01
    • read byte from port 0x68
    • repeat until all 0x14 rx bytes have been read.
  • do some sort of outro sequence (similar to one where we write 0x59 to 0x6C)
    • read byte from 0x68
    • wait until mask 0x02 is 0x0 do not check result.
    • write 0xFF to 0x6C

Write equivalent in Visual C

it was easy because we had essentially all WinAPI calls that were being performed to the LPCDriver This was easy because I had all the WinAPI (KERNEL32) that were being performed to the LPCDriver and all their arguments ("\.\LPCDriver", RX buffer, TX buffer, buffers lengths, etc).

Port to GNU/Linux and similar platforms

EC normally is 0x62 and 0x66, but notice here we used ports 0x68 and 0x6C. Previous Acer fan controls on Linux (acer_acpi / acerhdf). Used acer_ec.pl as base. Notice some similarities. But essentially switched the two sets of ports and stuff worked.

Further work

More info can be found in the project's issues, but the main thing would be:

EC firmware identification (and for bonus points what does its firmware do?) this will allow us to determine on what does models does this work.