Cannot get ACPI GPEs and notifications to work

372 views Asked by At

Recently, I've been working to get ACPI fully working on my x86 educational Linux-compatible kernel called Tilck. In order to that, I've integrated the latest version of ACPICA (ver. 20200717) and I've implemented the whole OS services layer. After fixing several bugs, I believe that now my OSL layer works fine and all the ACPI initialization steps pass without any errors. I've tried that on VMs and on multiple physical HW machines. Just to give you an idea, I'm able to walk through the whole ACPI namespace, read tables, evaluate method objects, handle fixed ACPI events, reboot and power-off the machine etc.

But, my problem is that I'm unable to get any GPEs (general purpose events) nor notifications which, in my understanding, are built on the top of GPEs. I believe I must be missing something important to setup/configure/enable, but I can't figure it out what that might be. (Or, at least, not without spending a huge amount of time on that.). What I'm looking for here is one or more hints/ideas for where to look for a problem. In other words: "what could possibly be the problem?".

Clearly, I'm not expecting anyone to debug my code. Therefore, here below I'm going to explain in more detail the ACPI initialization steps I've used and I'll add plenty of info about the debugging I've already done.

First, in Tilck, I initialize ACPI (using ACPICA) in the following steps (in order):

1. AcpiInitializeSubsystem()
2. AcpiInitializeTables()
3. AcpiLoadTables()
4. AcpiEnableSubsystem()
5. AcpiInitializeObjects()
6. Here I walk the whole namespace and I install notify handlers for
   HID=PNP0C0C (power button) and HID=PNP0C0D (lid switch)
   using AcpiInstallNotifyHandler(), without errors, of course.
7. Register a fixed event handlers with AcpiInstallFixedEventHandler() 
8. Install a global event handler with AcpiInstallGlobalEventHandler()
9. AcpiUpdateAllGpes()

Now a series of comments with additional details, enumerated for convenience.

First. I have to remark that in none of those steps and I get any errors and, believe me, when I had some subtle bugs in my OSL layer, I saw plenty of complains from ACPICA. Still, the problem could be a bug in my OSL layer, but I have no warnings/errors from ACPICA to help with that (even when AcpiDbgLevel is set to ACPI_DEBUG_DEFAULT or higher).

Second. I believe step 7 where I install a fixed event handler for the power button should be irrelevant here. Without it, it's the same thing. Actually, the whole story is that I should not even try to install a fixed event handler for the power button if the flag ACPI_FADT_POWER_BUTTON is set in the FADT table, because when it's set means that the power button is implemented as a "control method" and I should get notifications (or GPEs), instead of fixed events. The problem is that, despite the value of that flag, I can still get powerbtn fixed events, if I install the handler, probably for "legacy compatibility" reasons, I don't know. The point is that I never get ACPI notifications, of any kind.

Third. Because of the whole story with fixed events vs notifications for the power button, I installed a notify handler for the lid switch using the AcpiInstallNotifyHandler() ACPICA function. Note: the lid switch (HID PNP0C0D) has no corresponding fixed event, so I should necessarily get a notification, while I never do: that's my problem.

Forth. As part of the debugging, I've installed also a global event handler and I've registered an IRQ handler of my own (in addition to ACPICA's one) to see when I get SCI interrupts. Well, for fixed events that happens regularly: I observe both the IRQ and the event in the global event handler but, I get absolutely nothing when I close and re-open my laptop's lid. (Also, I get nothing after pushing the power button when the fixed event handler is not installed, despite FADT says I should get notifications because it's a control method.) Therefore, I strongly believe that doesn't hold the theory in which the lid switch causes interrupts but, for some reason, my AcpiOsExecute() implementation or my scheduler doesn't work or there's some kind of dead lock or whatever.

Fifth. After calling AcpiUpdateAllGpes() I'm getting a message from ACPICA confirming that GPEs are enabled, like:

ACPI: Enabled 2 GPEs in block 00 to 0F

Typically, on HW machines, there are much more than 2 GPEs enabled, according to this message. Therefore, in theory GPEs are enabled, according to ACPICA, even if I cannot receive them.

Sixth. Following my theory that something must be enabled/configured, my last long-shot attempt to fix the problem was to call AcpiSetupGpeForWake() on the lid switch ACPI object. To do that, I had to first evaluate it's _PRW sub-object (if any) with AcpiEvaluateObject(), then carefully type-switch on its type, extract the GpeDevice and GpeNumber fields and finally pass them to AcpiSetupGpeForWake(). My hope was that would somehow unblock GPEs from the lid switch. Of course it didn't because the problem has nothing to do with that.

Conclusion. That's all I tried. What can be the problem here? Or at least, where should I look for the problem? I have no idea what I'm doing wrong at this point. I'd appreciate so much any kind of hints/ideas/suggestions from people with plenty of experience with ACPI. If I'm lucky, an educated guess from an expert will quickly lead me to the solution.


P.S.: For really curious people, the whole ACPI code is in the acpi8 unstable branch. The ACPI initialization is in a file called acpi_module.c, the registration of notify handlers in powerbtn.c.


Update 1

After Margaret Bloom's comment asking if the bits in GPEx_EN are set, I started checking directly at low-level if they are, bypassing completely ACPICA. It wasn't hard to do: the machines I have use just a few GPEs, so all of them were in the static GPE blocks described by FADT.

Anyway, all of them, but one had ST = 0 and EN = 1. The exception was a particular GPE (its number depends on the machine) which had ST=1 and EN=0. After instrumenting ACPICA's code and looking carefully at the logs, I noticed that particular GPE (let's say it was #15) actually fired once, immediately after AcpiUpdateAllGpes() and, after my handler, ACPICA decided to disable it. Note: this (GPE 15 fired once) was happening before as well, I just missed it.

Now, after several hours of debugging, instrumenting ACPICA and reading the ACPI spec, I decided to try the "nuclear option": after AcpiUpdateAllGpes() I enabled ALL the possible GPEs with a for-loop from 0 to AcpiCurrentGpeCount, by calling first AcpiGetGpeDevice() and then AcpiEnableGpe(). After observing that GPE #15 was still not enabled at hardware-level, I went even further: In case the GPE exists, I called AcpiGetGpeStatus() and if ACPI_EVENT_FLAG_ENABLE_SET is not set, I called AcpiSetGpe(), which will always forcibly enable the given GPE at HW-level. Result? My global event handler got called immediately (GPE #15) and then, after it run, ACPICA disabled GPE #15 again.

Therefore, it looks like I've discovered a special GPE that's always ready to fire, as soon as enabled. Maybe it's a periodic timer or something like that? Can that be related to my problem?

Final remark: I've just noticed 0andriy's comment as well, but I had no time to work in that direction yet. It's totally possible that GPEs and notifications work properly but the power button and the lid switch are particularly complex to handle when implemented using control methods. I'll spend some time on that.

0

There are 0 answers