Reboot from Haiku resets ACPI GPU state on Asus 1015PN (Optimus)

Hello Haiku Community,

I’m running Haiku (and antiX Linux) on a rather peculiar netbook, an Asus EEE PC 1015PN, and have encountered a hardware-specific issue related to its hybrid graphics system.

Hardware Background:

  • Model: Asus EEE PC 1015PN
  • CPU / iGPU: Intel Atom N550 with Intel GMA 3150 Graphics
  • dGPU: Nvidia ION 2
  • Graphics Switching: This model has a very early Optimus implementation of hybrid graphics. There is no BIOS option to select the GPU. Switching must be done via software (specifically, via acpi_call methods in Linux). The BIOS default GPU is the Nvidia dGPU.
  • CMOS battery dead (BIOS settings reset on power-loss)

Haiku Details:

  • Haiku version: haiku-master-hrev58969-x86_gcc2h-anyboot (nightly)
  • Bootloader: BIOS mode

The Issue:

When I use Linux, I can switch to the power-saving Intel iGPU via acpi_call. This selection correctly persists across warm reboots (e.g., running the reboot command). The same is true for Windows (7/8.1/10/11 via vendor utility).

However, when I perform a warm reboot from Haiku, the GPU selection is lost, and the system reverts to the default Nvidia dGPU, exactly as it would after a cold boot or a complete power loss. I found this behavior only in Haiku’s reboot process.

Steps to Reproduce:

  1. Start with a cold boot of the netbook. By default, the Nvidia dGPU is active.
  2. Boot into my antiX Linux installation.
  3. Use an acpi_call script to switch the active GPU to the integrated Intel GMA 3150. I can verify the switch was successful.
  4. From Linux, perform a warm reboot (sudo reboot).
  5. Boot back into Linux. Result: The system correctly retains the Intel iGPU as the active graphics card.
  6. Now, from Linux (while the Intel iGPU is still active), perform another warm reboot, but this time boot into Haiku.
  7. From the Haiku Deskbar, select “Restart system”.
  8. At the bootloader, select my antiX Linux installation to boot into it.
  9. Expected Result: The system should still be using the Intel iGPU, as it was a warm reboot.
  10. Actual Result: The system has reverted to the BIOS default Nvidia dGPU. The ACPI state set by Linux was cleared by the reboot initiated from Haiku.

My Hypothesis:

It seems that Haiku’s ACPI shutdown/reboot sequence is sending a different command or triggering a more complete hardware reset signal than Linux or Windows do. This “deeper” reboot clears the register that holds the GPU selection.

Could the developers shed some light on how Haiku handles ACPI on reboot? Is there a known difference in its implementation that might cause this? More importantly, is there any way to modify this behavior, perhaps through a kernel setting or a configuration file, to perform a “softer” reboot that preserves this ACPI state? Or could this be a missing ACPI method in Haiku’s power manager?

Thank you for your time and for this wonderful OS. Tho my time and knowledge is somewhat limited, I’m happy to provide any logs or perform any tests that might help diagnose this further.

I’m not familiar with the Haiku reboot process, but please note that such issues were frequent under Linux some years ago before the reboot sequence of Windows was reverse engineered and implemented under Linux.
The reboot sequence is not standardized, several things must/can be attempted, I guess Haiku does not try the exact same sequence.

Thank you for your input. I am using Linux for over 20 years now, but really wasn’t aware about this distinction. Since it always simply worked, I guess I never looked into the reboot process of any OS before, and didn’t pay close attentiont to it. And am also aware that mine could be a niche interest, but at the same time I’m also sure I’m not alone in this. There might be some quirks that someone knows about. :slight_smile:

So, I’ve looked around a bit… It is soo pretty…

Rebooting: mjg59 — LiveJournal is the RE work I mentioned earlier.
The way Windows works depends on the presence of an ACPI reboot vector.
If there is one:

  • poke the ACPI reboot vector
  • try to reboot through the keyboard controller
  • poke the ACPI reboot vector again
  • try the keyboard controller, again

If there is no ACPI reboot vector:

  • try the keyboard controller
  • wait a bit
  • try the keyboard controller again

That’s what was implemented in Linux 3.0 to fix reboot issues on some systems, excluding the EFI method that may be available now.

Now, let’s look at Haiku, in src/system/kernel/arch/x86/arch_cpu.cpp and 64/arch.S or 32/arch.S for an x86_reboot function. The x86_reboot function is the “triple fault” method described by mjg59, that doesn’t work on some systems but work on others. YMMV, let’s ignore this one.
arch_cpu_shutdown will call acpi_shutdown first; If it doesn’t work, it tries the keyboard controller. If it didn’t work after 0.5 s, it calls the triple fault function.

From my understanding, the arch_cpu_shutdown should be rewritten as is:

if (rebootSystem) {
  if (acpi_shutdown(rebootSystem) == B_OK)
    return B_OK;
  out8(0xfe, 0x64);
  if (acpi_shutdown(rebootSystem) != B_OK)
    snooze(500000);
  out8(0xfe, 0x64);
  snooze(500000);
  return B_ERROR;
} else {
// current code here, to support APM too...
}

Note that more recent Linux kernels added another step that could be tried too (calling EFI boot services), and a set of quirks for weird boards, cf. linux/arch/x86/kernel/reboot.c at master · torvalds/linux · GitHub

If there is any interest in this, I can go further and submit a patch to Haiku to implement this behaviour.

5 Likes

Seems ok to me. I don’t know if it will fix the issue, as there could also be problems in drivers shutdown code, but it is worth a try.

For the moment we don’t have support for efi boot services. But we should definetely get support at some point. (alone for features like reboot to firmware… or even cooler reboot to $otherOS in multiboot situations; and also to add an entry for the haiku bootloader on efi)

Hello! Wow, @pinaraf that is really promising! Thank you very much for doing research on this. Reading your message, it occurred to me that I never tried to reboot via keyboard to see if the behavior is the same (I always used the proper reboot option from the menu), so I tried that (holding the keys down for 4 seconds). But sadly, there was no change. I also tried shutdown -r from the terminal, but obviously the outcome is the same. A force reboot does the same thing.

At this point, I’m not sure if it would make sense to make major changes, but I would be happy if it’s solvable. To be honest, I would be also perfectly happy with a runnable script or something to make a “softer/warmer” reboot, though I’m not sure if that is possible at all.

@PulkoMandy, @nephele, there is another possibility which I haven’t mentioned in my original message, though I was thinking about it. What if it’s not the reboot process itself that is changing something in the ACPI values, but rather the bootup process - specifically when either the kernel or the video driver is loading something? Could I be barking up the wrong tree here? Could this also be a possibility?

The problem with such issues is, it may happen for many reasons and it will be quite hard to find exactly what it is.

So, yes, it could be some initialization in the drivers as well. ACPI is unusual in that aspect because we run some bytecode provided by the BIOS. We also have to identify ourselves and depending on the Windows version we declare (of course all OS pretend to be some version of Windows, otherwise nothing works because the hardware manufacturers don’t care about anything else), the ACPI BIOS may use different code paths! In theory this allows the hardware manufacturer to apply some special cases depending on the Windows version (for example disable a feature if the OS doesn’t support it, or manage it on their own).

I don’t think other non-ACPI related things could be at play, but you can never be too sure…

That is actually very revealing (to me at least). I never thought about the “impersonation” aspect of things, and how the BIOS/firmware may respond differently based on that. Clearly I have much to learn.

Even though I come from a programming background, I have almost zero knowledge about C/C++, so thinking I could make some tests with the source coce is far beyond my possibilities at the moment.

Furthermore, without the specific hardware at hand (for anyone) to test with, everything becomes a guessing game. Sure, educated guesses could be made, but I’m not sure it would be the right path, even if it’s the most obvious one. Unfortunately, I don’t have enough time on my hands to delve into the depths of C/C++, so I have no choice but to ask, wait, and hope that someone will crack this, if anyone ever does. Thank you.

Submitted, https://review.haiku-os.org/c/haiku/+/9528

@andrew2221 reboot through the keyboard controller doesn’t mean triggering the reboot using the keyboard, it means the kernel communicates with the keyboard controller to reboot the system… Because that’s how this pile of hack of a computer system works: there was some room available on the keyboard controller, they added some features there, including rebooting the system.

1 Like

Test builds of that change available here, you can see if it makes any difference on your system: Index of /testbuild/I92ff29c2ccae18fbc18fffbc6405ff4ce2f2be96/1/hrev58982/x86_64/

1 Like

Hi there.
@pinaraf, I misinterpreted what you said previously, sorry about that. I see now, thank you for the clarification. I learned something today as well.

Last night I played around a bit with the boot loader menu from Haiku (pressing the Shift key while booting), and systematically tried various combinations of single/multiple settings between safe mode and debug options. No matter what I selected, the result was always the same. The GPU selection reverted to the default Nvidia after reboots.

@pinaraf, @waddlesplash, I greatly appreciate your efforts trying to resolve this pesky issue of mine! I grabbed the ISO file from the link provided, and after reformatting the Haiku partition on my netbook (to avoid any “contamination”), I installed it from a USB stick. Not connected to the internet, and not updated.

Rebooting both from the installation media and the internal SSD, using all the previously mentioned methods (Deskbar menu, keyboard, from the terminal, force reboot from Team Monitor), all produce the same outcome: sadly, no change in the behavior.

If there is something else you can think of that I haven’t tried, especially with this new test ISO and patch, please let me know. Either way, this is a very pleasant surprise that all of you are involved in this. It’s a nice feeling. Thank you very much. Really.

Not sure I think it is a good idea to copy this.
You can try and see if it helps, but to me it seems like a legacy hack that most likely should not be needed anymore. As we do ACPI reboot and shutdown, it is not likely a problem with that. More likely it is how we handle hardware, memory and perhaps lack of certain services (nvram, acpi runtime service).

I’ve tried to do some more research myself on where and how the GPU selection might be saved. But I found contradictory information, rather than a real conclusion. Some say that netbooks around 2010 most likely stored the setting in volatile hardware registers (either within the chipset or the GPU itself) or within a dedicated region of volatile RAM managed by the system firmware, specifically System Management RAM (SMRAM). Others say they more likely have an Embedded Controller (EC) chip which holds other data as well (like keyboard, power management, etc.). I don’t know what to think.

This is how I interpret the flow of actions (could be way off): there is a temporary memory of some sort, which is read by the BIOS on power-up/reboot process. If there is data there (e.g., warm reboot), the BIOS reads it, applies the GPU setting, and boots into the OS. If there is no/corrupted data present (e.g., cold boot, power loss because of the dead CMOS battery), the BIOS relies on its default data set, which I assume it then copies during POST to that temporary memory (for it to have on the next reboot). And that temporary memory can be written to both by the BIOS and the OS (well, not really the OS itself, but rather through the OS), though basically only the BIOS uses it, and only during POST.

Another thing I haven’t mentioned before, but I think now is the time to do it… I poked around the BIOS ROM a bit, and using MMTool, extracted the ACPI_AML portion of the ROM and decompiled it with iasl. I know that the GPU selection is done via this call in Linux:

echo '\OSGS 0x01' > /proc/acpi/call

So I searched in the extracted DSDT and found this:

    Method (OSGS, 1, Serialized)
    {
        Local0 = (Arg0 & 0x03)
        If (Local0)
        {
            Local0--
            SNVS (0x2484, Local0)
        }
        Else
        {
            SNVS (0x2484, One)
        }

        Return (One)
    }

I understand the logic of it, but to me this is still not conclusive as to where those bits are saved, as in what type of memory/registry, but clearly they are written by this. The whole DSL is rather lengthy, but I could try to upload it somewhere if someone wants to take a look at it.

I also tried to identify the hex value portion in the ROM via hex editors, which holds the default GPU selection/data bit, but obviously I failed miserably, as my knowledge is far too limited for this kind of investigation.

Could you dump the SNVS function too ?

We do have a /bin/acpi_call command available (in the haiku_extras package: pkgman install haiku_extras) that maybe you can use to replicate that what Linux does?.

I only toyed around that command once or twice, so not sure about how to actually use it properly. :slight_smile:

If your hw is that old, it might that Linux and Windows does some patching of ACPI. Acpica should do most of that as well nowadays, but it might be some patching that fixes this. If it is EC, our EC code is heavily inspired from FreeBSD, and I spent a lot of time there to make it behave and a bit more readable. Linux and Windows most likely has their own code, so I wouldn’t be surprised if our code is not great. I kind of wish there was a proper reference impl for EC controllers.

@pinaraf, Sure thing. This is it:

    Method (SNVS, 2, Serialized)
    {
        PINX = 0x80000001
        PAR0 = Arg0
        PAR1 = Arg1
        ISMI (0x70)
    }

And here is another bigger portion, I think it may be of interest:

    Scope (_SB.PCI0.VGA)
    {
        Method (_DSM, 4, NotSerialized)  // _DSM: Device-Specific Method
        {
            If ((Arg0 == ToUUID ("a486d8f8-0bda-471b-a72b-6042a6b5bee0") /* Unknown UUID */))
            {
                Local0 = Zero
                Local0 = (DerefOf (Arg3 [0x03]) << 0x18)
                Local0 += (DerefOf (Arg3 [0x02]) << 0x10)
                Local0 += (DerefOf (Arg3 [One]) << 0x08)
                Local0 += (DerefOf (Arg3 [Zero]) << Zero)
                If ((Arg1 != 0x0100))
                {
                    Return (Buffer (0x04)
                    {
                         0x02, 0x00, 0x00, 0x80                           // ....
                    })
                }

                Debug = "Optimus _DSM is called "
                Name (_T_0, Zero)  // _T_x: Emitted by ASL Compiler, x=0-9, A-Z
                _T_0 = Arg2
                If ((_T_0 == Zero))
                {
                    Debug = "Optimus _DSM subfunc 0, return 0x04000001 "
                    Return (Buffer (0x04)
                    {
                         0x21, 0x00, 0x01, 0x04                           // !...
                    })
                }
                ElseIf ((_T_0 == 0x05))
                {
                    Name (NFBU, Buffer (0x04){})
                    CreateField (NFBU, 0x04, One, LIDF)
                    CreateField (NFBU, 0x08, 0x06, DTOG)
                    CreateField (NFBU, 0x14, One, DHPS)
                    CreateField (NFBU, 0x15, 0x08, DHPE)
                    DHPE = 0x03
                    DHPS = HHPS /* \HHPS */
                    Return (NFBU) /* \_SB_.PCI0.VGA_._DSM.NFBU */
                }
                ElseIf ((_T_0 == 0x10))
                {
                    CreateWordField (Arg3, 0x02, USRG)
                    If ((USRG == 0x564B))
                    {
                        Return (OPVK) /* \_SB_.PCI0.VGA_.OPVK */
                    }

                    Return (Zero)
                }
                ElseIf ((_T_0 == 0x1A))
                {
                    Debug = "Optimus _DSM subfunc 26 "
                    If ((Local0 & One))
                    {
                        Local2 = (Local0 >> 0x18)
                        If ((Local2 == 0x03))
                        {
                            Debug = "Optimus _DSM subfunc 26, OMPR=3"
                            OMPR = 0x03
                        }
                    }

                    If (^^P0P4.DGPU.DGPS)
                    {
                        Return (Buffer (0x04)
                        {
                             0x01, 0x00, 0x00, 0x01                           // ....
                        })
                    }
                    Else
                    {
                        Return (Buffer (0x04)
                        {
                             0x19, 0x00, 0x00, 0x01                           // ....
                        })
                    }
                }
                Else
                {
                    Return (Buffer (0x04)
                    {
                         0x02, 0x00, 0x00, 0x80                           // ....
                    })
                }
            }

            Return (Zero)
        }

        Name (OPVK, Buffer (0xE6)
        {
            /* 0000 */  0xB7, 0xC2, 0x5F, 0xC3, 0x02, 0x6E, 0xB2, 0x64,  // .._..n.d
            /* 0008 */  0x4B, 0x56, 0xE6, 0x00, 0x00, 0x00, 0x01, 0x00,  // KV......
            /* 0010 */  0x31, 0x34, 0x38, 0x35, 0x39, 0x37, 0x34, 0x35,  // 14859745
            /* 0018 */  0x36, 0x39, 0x38, 0x35, 0x47, 0x65, 0x6E, 0x75,  // 6985Genu
            /* 0020 */  0x69, 0x6E, 0x65, 0x20, 0x4E, 0x56, 0x49, 0x44,  // ine NVID
            /* 0028 */  0x49, 0x41, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,  // IA Certi
            /* 0030 */  0x66, 0x69, 0x65, 0x64, 0x20, 0x4F, 0x70, 0x74,  // fied Opt
            /* 0038 */  0x69, 0x6D, 0x75, 0x73, 0x20, 0x52, 0x65, 0x61,  // imus Rea
            /* 0040 */  0x64, 0x79, 0x20, 0x4D, 0x6F, 0x74, 0x68, 0x65,  // dy Mothe
            /* 0048 */  0x72, 0x62, 0x6F, 0x61, 0x72, 0x64, 0x20, 0x66,  // rboard f
            /* 0050 */  0x6F, 0x72, 0x20, 0x61, 0x73, 0x75, 0x73, 0x20,  // or asus 
            /* 0058 */  0x31, 0x30, 0x31, 0x35, 0x70, 0x6E, 0x20, 0x62,  // 1015pn b
            /* 0060 */  0x75, 0x67, 0x20, 0x37, 0x31, 0x32, 0x30, 0x20,  // ug 7120 
            /* 0068 */  0x2D, 0x20, 0x34, 0x26, 0x43, 0x4B, 0x58, 0x23,  // - 4&CKX#
            /* 0070 */  0x5D, 0x52, 0x48, 0x43, 0x39, 0x38, 0x52, 0x43,  // ]RHC98RC
            /* 0078 */  0x5F, 0x33, 0x54, 0x3F, 0x25, 0x2E, 0x3D, 0x20,  // _3T?%.= 
            /* 0080 */  0x3E, 0x4F, 0x2D, 0x5C, 0x58, 0x52, 0x34, 0x5A,  // >O-\XR4Z
            /* 0088 */  0x3F, 0x4A, 0x3C, 0x38, 0x3A, 0x2B, 0x2C, 0x44,  // ?J<8:+,D
            /* 0090 */  0x5B, 0x55, 0x4B, 0x51, 0x38, 0x44, 0x51, 0x2A,  // [UKQ8DQ*
            /* 0098 */  0x2D, 0x5B, 0x59, 0x4C, 0x2C, 0x3D, 0x20, 0x2D,  // -[YL,= -
            /* 00A0 */  0x20, 0x43, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67,  //  Copyrig
            /* 00A8 */  0x68, 0x74, 0x20, 0x32, 0x30, 0x31, 0x30, 0x20,  // ht 2010 
            /* 00B0 */  0x4E, 0x56, 0x49, 0x44, 0x49, 0x41, 0x20, 0x43,  // NVIDIA C
            /* 00B8 */  0x6F, 0x72, 0x70, 0x6F, 0x72, 0x61, 0x74, 0x69,  // orporati
            /* 00C0 */  0x6F, 0x6E, 0x20, 0x41, 0x6C, 0x6C, 0x20, 0x52,  // on All R
            /* 00C8 */  0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x52, 0x65,  // ights Re
            /* 00D0 */  0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x2D, 0x31,  // served-1
            /* 00D8 */  0x34, 0x38, 0x35, 0x39, 0x37, 0x34, 0x35, 0x36,  // 48597456
            /* 00E0 */  0x39, 0x38, 0x35, 0x28, 0x52, 0x29               // 985(R)
        })
        Name (RST0, Buffer (0x0100)
        {
             0x00                                             // .
        })
        Name (RST1, Buffer (0x0100)
        {
             0x00                                             // .
        })
        Method (RSTO, 0, NotSerialized)
        {
            OperationRegion (VGAR, SystemMemory, 0xB0400000, 0x0100)
            Field (VGAR, ByteAcc, NoLock, Preserve)
            {
                VREG,   2048
            }

            VREG = RST0 /* \_SB_.PCI0.VGA_.RST0 */
            OperationRegion (HDAR, SystemMemory, 0xB0401000, 0x0100)
            Field (HDAR, ByteAcc, NoLock, Preserve)
            {
                HREG,   2048
            }

            HREG = RST1 /* \_SB_.PCI0.VGA_.RST1 */
        }

        Method (SAVO, 0, NotSerialized)
        {
            OperationRegion (VGAR, SystemMemory, 0xB0400000, 0x0100)
            Field (VGAR, ByteAcc, NoLock, Preserve)
            {
                VREG,   2048
            }

            RST0 = VREG /* \_SB_.PCI0.VGA_.SAVO.VREG */
            OperationRegion (HDAR, SystemMemory, 0xB0401000, 0x0100)
            Field (HDAR, ByteAcc, NoLock, Preserve)
            {
                HREG,   2048
            }

            RST1 = HREG /* \_SB_.PCI0.VGA_.SAVO.HREG */
        }

        Method (HINI, 0, NotSerialized)
        {
            SAVO ()
        }
    }

And here is the whole file, for anyone (I’ve put a one week TTL). Pass: 1015PN

Edit: reason: updated link

@BiPolar Thank you for the heads up. Before I came here, I’ve done some searching, and I knew about this already. Unfortunately, I couldn’t figure out, how to use it (in regards to what I need it for), besides I think it has a more limited usecase, might not be a full acpi_call implementation. The package description mentions it as a “debugging tool”, but nothing more. The help menu is not very descriptive, and it doesn’t have a man page. I might need to look into the source code and figure it out how to execute it. But thank you. This is not a dead end just yet.

@tqh, That is absolutely possible. Do you think I should try to install BSD, to see if it behaves the same as Haiku? Or the other factors/context is so vastly different, that it wouldn’t help to draw any meaningful conclusions?

Another thing I just noticed now. Honest to God, I’ve seen it so many times, that it just simply passed me by… When powering on the netbook, after the supposed temporary memory is reset, right after pressing F2 to load the BIOS defaults, for a brief second, the message: Checking NVRAM... appears, then the bootloader takes over. I am not any closer to a conclusion, but might be important, so I mentioned it.