HP Pavilion laptops BIOS DSDT hack
Notice: none of what follows has been written by me. It’s here just for my personal reference.
A buggy BIOS can cause many different and subtle problems to Linux, ranging from reboot problems, incorrect battery power readings, suspend/resume not working correctly, and strange ACPI issues. This page describes some of the issues which can cause problems and may help you to trouble shoot broken BIOS issues.
Multiple DSDT
First boot normally, do “dmesg” in a terminal and copy and save it.
Re-boot and at the bootsplash, enter
acpi_osi="Linux"
and boot. then, do “dmesg” in a terminal and copy that to kwrite
Now compare the two.
If you have significant differences or see any new hardware is detected the reason why is that HP has coded (my) DSDT to detect Vista, VistaSP1, VistaSP2, etc & Linux and treats them differently when detected. This is done in code in Vista, VistaSP1, VistaSP2, etc., but we Linux users must “tell” our kernel. We do that with the kernel parameter acpi_osi=”Linux” at the bootsplash or on the kernel line in menu.lst.
Buggy DSDT
Sometimes a BIOS will contain a buggy Differentiated System Description Table (DSDT) and can cause subtle problems for the Linux ACPI driver.
The (DSDT) can be disassembled using the Intel iasl disassembler tool. Install iasl using:
sudo apt-get install iasl
and get the DSTD data and disassemble it as follows:
sudo cat /proc/acpi/dsdt > dsdt.dat iasl -d dstd.dat
More information about the DSDT can be found in the ACPI specification, which can be downloaded from http://www.acpi.info/
Although understanding the disassembled DSDT can be challenging, it can be worth the effort to understand what the ASL code is trying to do as sometimes there are coding errors which lead to very subtle ACPI problems.
Using the Intel DSDT disassembler has proved very revealing in some cases. For example, one BIOS had a race condition in the initialisation of the Embedded Controller. The Linux ACPI driver was reading values form the Embedded Controller before the controller was fully ready, and hence incorrect values were being returned to the Linux kernel for various devices, such as the smart battery etc..
Another bug found in the DSDT was from the sloppy use of the ASL Acquire() operator used to obtain a mutex. The Acquire() operator has a timeout argument, which can be 0xFFFF (wait forever to acquire a mutex) or a timeout in milliseconds. For the non-infinite wait there should be a check to see if the Acquire worked correctly or timed-out. We have seen code that does not check the mutex timeout and hence race conditions have occurred causing the corruption of settings in the embedded controller and misreadings by the Linux ACPI driver.
Rule: Always check return values from mutex Acquire() operators when using finite timeouts.
BIOS checking tools
If you suspect your BIOS is not behaving correctly, then it is worth using the Linux Firmware Kit http://www.linuxfirmwarekit.org/ to automatically check for incorrect BIOS behaviour. This tool may report some false positives, but it is an excellent automated tool for picking up bugs at an early stage.
Some issues we have found with this are as follows:
- Incorrectly set thermal trip zone values (ACPI spec 11.1) and Linux /proc/acpi/thermal_zone/THRM
- Incorrectly set HPET VendorID – defaults of 0xFFFF are not excusable
- HPET clock period not set, with a default of 0xFFFFFFFF
We suggest running this tool and carefully looking log of errors it reports as a first pass in seeing any glaring problems.
DSDT handling Operating System Variants
Most BIOS code check OS variants keyed off the _OSI and _OS objects. Most BIOS that invoke OSI(Linux) do nothing with it, but others that do cause Linux to break in different ways (supsend/resume, Video reposting, etc). The Linux ACPI driver disables OSI(Linux) by default, with the hope that it discourage BIOS writers from using it.
Linux will continue to claim OSI compatibility with Windows until the day when the majority of Linux systems have passed a Linux compatibility test rather than a Windows compatibility test.
ACPI _BIF method
The ACPI _BIF method provides the kernel with battery specific information. Hence if it is incorrect it can fool high level applications such as gnome-power-manager to shut a system down when it believes power has reached a critically low point.
A broken _BIF method has been known to caused some power management headaches. For example, it is important that the “Design Capacity of Warning” and “Design Capacity of Low” are non-zero, otherwise the gnome-power-manager cannot easily determine a correct strategy for hibernating or shutting down a PC when the battery becomes critically low.
Generally, packages should have all their fields set without relying on any defaults to zero. It is a sloppy BIOS practice to omit the setting of fields in packages – fields need to be set – missing fields lead to bugs which can fool the kernel or applications that rely on specific ACPI information being provided correctly.
Reboot Methods
A PC can be rebooted by Linux using several different strategies, selectable by the kernel reboot= boot option. These strategies are:
- Putting the processor back into real mode and jumping to the BIOS reset address
- Keyboard controller reset by writing 0xfe to port 0×64
- Forcing the processor to triple fault
- By forcing a Intel PCI reset by writing 0×2 and then 0×04 to port 0xCF9
- By writing a magic value to a port/register, as specified by ACPI FACP values RESET_VALUE and RESET_REG (an “ACPI reset”)
The last method (“ACPI reset”) has shown to be problematic with one particular BIOS, as the RESET_VALUE and RESET_REG were values 0×06 and 0xCF9 which tries to do a Intel PCI style reset but only worked in 90% of reboots. This is because the reset should be performed in two stages (a write of 0×2, a port delay, and then a write of 0×04) rather than just one write of (0×02 | 0×04).
Suspend/Resume
BIOS issues can cause suspend/resume to fail – below is an bash script for soak testing suspend/resume:
# Time to sleep during a suspend
SLEEP_SECS=30
# Time to sleep when resumed
RESUME_SECS=20
# Number of suspend resume cycles
ITERATIONS=250
loop=0
while [ $loop -lt $ITERATIONS ]
do
loop=$((loop+1))
if [ -e /proc/acpi/alarm ]
then
# old style alarm interface
DATE=`date "+%F %H:%M:%S" -d "$SLEEP_SECS sec"`
echo $DATE > /proc/acpi/alarm
elif [ -e /sys/class/rtc/rtc0/wakealarm ]
then
# new style alarm interface
SECS=`date "+%s" -d "$SLEEP_SECS sec"`
echo $SECS > /sys/class/rtc/rtc0/wakealarm
else
echo "Don't know how to set alarm"
exit 1
fi
echo Suspend Resume Test Cycle $loop - suspending
pm-suspend suspend
echo Woken up, sleeping a little before next test
sleep $RESUME_SECS
done
Restore CPU temperature indication for HP Pavilion dv5z laptops
- by: AlexanderDreyzen
- Model: HP Pavilion dv5z – 1000
CPU Temperature
The ACPI DSDT table in the BIOS is non-complient, which prevents the reading of the CPU temperature. In order to read the CPU table, the DSDT table has to be overridden by giving a modified table to the kernel in the initrd image.
CAUTION: Editting and overriding the DSDT table could damage your laptop and the necessary changes may be BIOS version dependent!
The current DSDT from the BIOS needs to be copied from /proc/acpi/dsdt to a location where it can be worked on by
cat /proc/acpi/dsdt /some-folder/dsdt.aml.old
The copied table is in a compiled form (aml) that is not easy to work with. It can be decompiled into source (dsl) which can be edited and recompiled. The compiler (iasl) can be obtained by installing the the iasl package. Decompile the aml file by
iasl -d dsdt.aml.old
This produces the file dsdt.dsl which is the source code. Edit it at around line 9400 (might be BIOS version dependent) looking for the text
Method (_HOT, 0, Serialized)
{
If (LEqual (TPOS, 0x40))
{
Return (Add (0x0AAC, Multiply (TPC, 0x0A)))
}
}
Method (_CRT, 0, Serialized)
{
If (LLess (TPOS, 0x40))
{
Return (Add (0x0AAC, Multiply (TPC, 0x0A)))
}
}
The If statements are checking the TPOS variable. This checks which operating system is being run. Windows is 0×40 or 0×41 while linux is 0×80. The DSDT table from the BIOS essentially tells ACPI that linux cannot read the temperature when it actually can. This can be fixed by commenting out the If statements and their respective brackets. Do not make any other changes unless you know what you are doing! Afterwards, it should look like this:
Method (_HOT, 0, Serialized)
{
// If (LEqual (TPOS, 0x40))
// {
Return (Add (0x0AAC, Multiply (TPC, 0x0A)))
// }
}
Method (_CRT, 0, Serialized)
{
// If (LLess (TPOS, 0x40))
// {
Return (Add (0x0AAC, Multiply (TPC, 0x0A)))
// }
}
Now the dsl file needs to be recompiled to aml. This is done by the command
iasl dsdt.dsl
Which outputs the aml file dsdt.aml hopefully as opposed to an error message. This file has to be put into your initrd. First, copy it by the following:
sudo cp dsdt.aml /etc/initramfs-tools/DSDT.aml
where it will be loaded into the initrd when it is updated. It is suggestible to have more than one kernel installed and only update the initrd for the most recent current version so that a fall back kernel using the DSDT from the BIOS is still available. Update the appropriate initrd by
sudo update-initramfs -u -k kernel-version
Reading the CPU temperature should now be possible the next time the kernel is booted.
How to fix buggy DSDT
What follows is maybe the best article written about fixing buggy DSDT BIOSĀ concerning HP Pavilion laptops. Many thanks fly to its author!
http://forums.opensuse.org/programming-scripting/402051-need-some-bios-dsdt-dsopcode-help.html
http://forums.opensuse.org/hardware/laptop/392065-request-hp-laptop-owners.html
Please re-boot and at the bootsplash, enter
acpi_osi="Linux"
and boot
then, do “dmesg” in a terminal and copy that to kwrite
Now compare the two.
If you have significant differences as I discuss below please get back & post herein.
Also, look around and see if any new hardware is detected; post that too
Discussion.
HP has coded (my) DSDT to detect Vista, VistaSP1, VistaSP2, etc & Linux and treats them differently when detected. This is done in code in Vista, VistaSP1, VistaSP2, etc., but we Linux users must “tell” our kernel. We do that with the kernel parameter acpi_osi=”Linux” at the bootsplash or on the kernel line in menu.lst.