It all started when my laptop wouldn’t go to sleep. If I closed the lid, it simply kept chugging away at full power until the battery was dead. Using systemctl suspend helps a little, but it still uses too much power and often when I try and wake it back up, it freezes and forces me to reboot the laptop anyway.

The first thing that I tried was update the kernel. Supposedly newer model Zen CPUs had some trouble with linux that had been fixed in later kernels. I tried upgrading to 5.16 but this didn’t fix my issue.

After some research, it appeared as if the issue was caused by Microsoft and a new fancy sleep state that Windows uses where it does not completely shut the CPU off. Since I am not running Windows, I would like to be able to disable this mode, but Lenovo has declined to give me that option. On other Lenovo models it appears that you can do some sort of magical key combination to unlock an advanced mode in the BIOS, but none of those worked for me. One of the tutorials I was trying to follow was using a 14ARE05, which funny enough, I owned for about 2 days before the WiFi broke and I sent it back to purchase the laptop I have now, a 16ACH6. Overall its better in about every way except for this it seems.

Anyway, the thing that I am missing is the S3 sleep state. More research showed that it is possible to hack the ACPI tables to allow the state, but the standard solutions for that laptop did not work for mine, nor did recompiling the AML files work either.

After receiving error after error trying to use IASL to recompile the tables, I decided I would edit them myself. Using the decompiled tables as a reference, I modified the AML bytecode in such a way to enable the S3 state.

This is the ASL I was using as a reference:

    If ((CNSB == Zero))
    {
        If ((DAS3 == One))
        {
            Name (_S3, Package (0x04)  // _S3_: S3 System State
            {
                0x03, 
                0x03, 
                Zero, 
                Zero
            })
        }
    }

and this is the raw AML:

dsdt_unpatched.png

Here it is prettied up a bit:

A0  -- IfOp
1D  -- PkgLength: 29
93  -- Predicate: LEqualOp
43  -- CNSB
4E
53
42
00  -- ZeroOp
A0  -- IfOp
15  -- PkgLength: 21
93  -- Predicate: LEqualOp
44  -- DAS3
41
53
33
01  -- OneOp
08  -- NameOp
5F  -- _S3_
53
33
5F
12  -- PackageOp
08  -- Length: 8
04  -- NumElements: 4
0A  -- BytePrefix
03  -- 0x03
0A  -- BytePrefix
03  -- 0x03
00  -- ZeroOp
00  -- ZeroOp

The guide I was following says to delete the if statements. Since I can’t recompile the ASL and have to edit the raw binary, I decide to instead replace the if statements with nops so as to not mess up the length of any blocks. The AML code for nop is 0xA3, so I simply replaced all of the bytes from the if statements with 0xA3. Now the raw AML looks like this:

dsdt_patched.png

And the disassembled code looks like this:

    Noop
    Noop
    Noop
    Noop
    Noop
    Noop
    Noop
    Noop
    Noop
    Noop
    Noop
    Noop
    Noop
    Noop
    Noop
    Noop
    Name (_S3, Package (0x04)  // _S3_: S3 System State
    {
        0x03, 
        0x03, 
        Zero, 
        Zero
    })

I also need to patch the version number at the top from 1 to 2 so that when we override the table later it chooses the new one instead of the old one.

As I was disassembling my patched AML table it also complained about the checksum. I didn’t know what the checksum should be, but iasl told me what it was expecting so I changed it to that. I tried again and it was still wrong, but closer. One more try and I had guessed the correct checksum.

From this point I could keep following the guide . In the guide they mention GRUB_EARLY_INITRD_LINUX_CUSTOM=acpi_override, however I believe this is wrong and should be GRUB_EARLY_INITRD_LINUX_CUSTOM="acpi_override" instead.

One more problem: Due to security issues, loading custom AML tables is not allowed in secure boot, so I had to disable that in my BIOS. Luckily that is still an option.

With all that said, I rebooted and checked dmesg to see if it worked:

new_acpi_states.png

It worked! Prior to this there was no S3, just S0, S4, and S5.

Then I checked to see if deep sleep was an option:

deep_sleep

and it is!.

Since part of the guide was changing the command line arguments to use deep sleep, I don’t need to do anything else to enable it. In fact, now it will enter S3 whenever I close the lid. Previously I had to type out sudo systemctl suspend every time I closed my laptop, and even the it didn’t even work most of the time. I left my laptop in suspend overnight and instead of being dead in the morning, it had only lost a few percent.

So to all my fellow 16ACH6 users out there: There is hope!

This is also probably Lenovo’s third strike after the WiFi issues (I had the exact same WiFi issue on this one too but it went away after a day).