Jan 262015

FreeBSD battery logo[1] While I’m still using the free HP/Compaq nx6310 notebook a friend has given me, I ran into a little problem during actual productive use with FreeBSD 10.1 UNIX. A problem that also affects more modern HP notebooks like the EliteBook or ProBook series as well as certain Macbooks as I learned only later. The issue lies within the battery status reporting. While the very first state transition will be recognized, as in say you disconnect the AC cable, or the charge drops by 1% or whatever, subsequent changes are completely ignored.

An example: You want to join a meeting with 100% charge. You disconnect the AC cable, an event that is still recognized by the system. The charge may then stand at 99%. It would however not update further, but just stay there until either a reboot or until the lights just go dark. Not good if you intend to actually use the system on the go. The user just needs to be aware of the battery level!

1.) The right direction, but still a fruitless attempt

So I read that HP has quite some share of trouble with Windows-specific and also buggy ACPI BIOS code, and thus, this has been a pain for some time, not just on FreeBSD, but also Linux and in some cases even Windows.

At first I [reported the issue in the FreeBSD forums], after which I was asked to report the problem on the [freebsd-acpi@freebsd.org] mailing list. Problem only was that one can’t post anonymously and the registration never worked for me. So I kept digging in the dirt, and my first attempt was to fix the ACPI code of course, or DSDT as it’s called – the Differentiated System Description Table, written in ASL – the ACPI Source Language. Basically a pretty creepy byte code language providing a bidirectional interface between the operating system and the systems BIOS/UEFI, which in turn controls certain aspects of the hardware. Think display brightness adjustments, WiFi on/off switches, audio volume control via keyboard, sleep buttons etc. – all that is ACPI. You can find the specs [here] if you really want to take a look.

HP compiled the code using [Microsofts ASL compiler], which is known to be rather sloppy and ignore tons of bugs in the code when compiling. So my first step was to dump the active DSDT from the systems memory using acpidump and tell the tool to disassemble it using Intels ASL compiler iasl in the same step:

# acpidump -d > ~/nx6310-stock.asl

Then, I attempted to recompile using the Intel ASL compiler again:

# iasl ~/nx6310-stock.asl

And then came the errors and warnings. Plus some nasty OS-specific code. Since an operating system can and will identify itself to the BIOS via ACPI, one can unfortunately filter by operating system on the BIOS level. Very bad stuff. Like this, taken from the ASL code directly, as written by HP:

expand/collapse source code
  1. Name (C015, Package (0x08)
  2. {
  3.      "Microsoft Windows",
  4.      "Microsoft WindowsME: Millennium Edition",
  5.      "Microsoft Windows NT"
  6. })


  1. If (LOr (LEqual (C014, 0x00), LEqual (C014, 0x03)))
  2. {
  3.      If (CondRefOf (\_OSI, Local0))
  4.      {
  5.           If (\_OSI ("Windows 2001"))
  6.           {
  7.                Store (0x04, C014)
  8.           }
  10.           If (\_OSI ("Windows 2001 SP1"))
  11.           {
  12.                Store (0x04, C014)
  13.           }
  15.           If (\_OSI ("Windows 2001 SP2"))
  16.           {
  17.                Store (0x05, C014)
  18.           }
  20.           If (\_OSI ("Windows 2006"))
  21.           {
  22.                Store (0x06, C014)
  23.           }
  24.      }
  25. }

Next logical step was to fake the OS FreeBSD was reporting to the ACPI subsystem. You can either do that by changing and embedding the ASL (non-MS OS strings are for instance “FreeBSD” or “Linux”), or luckily also by adding something like the following line to /boot/loader.conf, after which you need to reboot:

hw.acpi.osname="Windows 2006"

That didn’t do the trick for me however, so I tried to run my modified ACPI code after all. To do that you need to recompile the ASL, and then have loader pick it up in the early boot sequence of the kernel. First, I compiled the fixed code:

# iasl -tc ~/nx6310.asl

The result will be /tmp/acpidump.aml (for whatever reason). I placed it in /boot/ and added the following to /boot/loader.conf:

  1. acpi_dsdt_load="YES"
  2. acpi_dsdt_name="/boot/nx6310.aml"

Now another reboot. To verify the AML gets loaded, boot the kernel in verbose mode, you can choose that option in the boot loader. It’ll show something like Preloaded acpi_dsdt "/boot/nx6310.aml" at 0xc18dcc34. in /var/log/messages.

In my case however, while the AML was indeed being loaded, the BIOS’ tables were strangely not overwritten. I never found out why. Dumping them again would just give me the bugged code by HP. So that seemed to be dead end.

2.) The solution

Then I discovered that this might actually be a problem with the FreeBSD kernel itself by stumbling over [problem report 162859] in FreeBSDs bugzilla. It seems there was a problematic commit to the kernels ACPI subsystem written by Jung-uk Kim, initially for FreeBSD 9.0 in 2011: [r216942]. By now, he had provided two patches for it, of which I tried the [newer one] unsuccessfully, making the problem even worse. You can see my replies under my real name “Michael Lackner” in that PR.

Luckily, it was seemingly my complaint on top of the others that made Jung-uk Kim revert the [original patch], all back to how it was in FreeBSD 8.0, which some people reported to work just fine. So I got the now reverted/fixed code from [r277579] – a file named acpi_ec.c, and replaced /usr/src/sys/dev/acpica/acpi_ec.c with it. Then, I recompiled the FreeBSD kernel [as described in the FreeBSD handbook] once more (fear not, it’s actually very easy).

Reboot, and everything just worked! No need for a DSDT replacement or operating system faking even. I tested charge, discharge, AC cable dis-/reconnect and full charge states. All ok now! Finally, that book is now 100% ready for production use! :)

I mean, let’s not make any mistake here, the ACPI code from HP is still buggy. But like the Linux kernel, the FreeBSD kernel knows how to sail around the most severe bugs, including Microsoft-specific code in certain cases, at least now that this issue is fixed.

Thanks fly out to Jung-uk Kim for reverting the problematic patch! It should be merged from current any day now. If you want to get it done faster than that, just fetch acpi_ec.c, place it in the right folder and recompile the kernel and with it all of its modules, just like I did.

[1] Original battery icon used for the logo is © Freepik from Flaticon and is licensed under CC BY 3.0