Showing posts with label Ubuntu. Show all posts
Showing posts with label Ubuntu. Show all posts

Monday, May 13, 2019

ACPI Debugging (4) -Analyse ACPI Tables in a Text File with FWTS

I often need to implement tests for new ACPI tables before they become available on real hardware. Fortunately, FWTS provides a framework to read ACPI tables' binary.

The below technique is especially convenient for ACPI firmware and OS kernel developers. It provides a simple approach to verify ACPI tables without compiling firmware and deploying it to hardware.

Using acpidump.log as an Input for FWTS

The command to read ACPI tables' binary is

 # check ACPI methods from a text file
 $ fwts method --dumpfile=acpidump.log

or

 # check ACPI FACP table from a text file
 $ fwts facp --dumpfile=acpidump.log

where acpidump.log contains ACPI tables' binary in a specific format as depicted below:
  • Table Signature - the 4-byte long ACPI table signature
  • Offset - data start from 0000 and increase by 16 bytes per line
  • Hex Data- each line has 16 hex integers of the compiled ACPI table
  • ASCII Data - the ASCII presentation of the hex data

This format may look familiar because it is not specific to FWTS. In fact, it is the same format generated by acpidump. In other words, the following commands generate the identical results.

# reading ACPI tables from memory
$ sudo fwts method

# dumping ACPI tables and testing it
$ sudo acpidump > acpidump.log
$ fwts method --dumpfile=acpidump.log

This means it is probable to debug remote systems by the following steps: 1) run sudo acpidump > acpidump.log, 2) copy acpidump.log and 3) run fwts with the --dumpfile=acpidump.log.

For developers, using --dumpfile option also means it is possible to test ACPI tables before deploying them on real hardware. The following sections present how to prepare a customized acpidump.log for such purposes.

Using a customized acpidump.log for FWTS

We can use acpica-tools to generate an acpidump.log. The following is an example of building a customized acpidump for the method test.

1. Generate a dummy FACP table

FWTS requires a FACP table in an acpidump.log so it can recognize acpidump.log as a valid input file.

  1. iasl -T FACP
  2. iasl facp.asl > /dev/zero
  3. echo "FACP @ 0x0000000000000000" >> acpidump.log
  4. xxd -c 16 -g 1 facp.aml  | sed 's/^0000/    /' >> acpidump.log
  5. echo "" >> acpidump.log
2. Generate a Customized DSDT table

A customized DSDT can be generated by the following commands
  1. Generate a DSDT -  run "iasl -T DSDT"
  2. Customize dsdt.asl - ex. adding an ACPI battery device

3. Compile the DSDT table to binary

Similar to FACP, the DSDT can be compiled and appended to acpidump.log.
  1. iasl dsdt.asl > /dev/zero
  2. echo "DSDT @ 0x0000000000000000" >> acpidump.log
  3. xxd -c 16 -g 1 dsdt.aml  | sed 's/^0000/    /' >> acpidump.log
  4. echo "" >> acpidump.log

3. Run FWTS method with acpidump.log

And finally, run fwts with the generated acpidump.log

  • fwts method --dumpfile=acpidump.log

Final Words

While we use DSDT as an example, the same technique applies to all ACPI tables. For instance, HMAT was introduced and frequently updates in recent ACPI specs, and Firmware Test Suite includes most, if not all, changes up-to-date; therefore, FWTS is able to detect errors before firmware developers integrate HMAT into their projects, and therefore reduce errors in final products.

Thursday, January 10, 2019

ACPI Debugging (2) - Debug AML (DSDT & SSDT) with ACPICA Utilities

Using acpidbg on Ubuntu 18.04 x64 can be quite handy; however, the Linux kernel with ACPI_DEBUGGER is not always available, such as on Ubuntu for ARM. In such cases, acpica also provides a set of utilities, named acpica-tools, for ACPI debugging.

Installation

The acpica-tools can be installed by
  •  sudo apt install acpica-tool
The latest source code can be downloaded from either of below, and it can be compiled and installed by “make” followed by “sudo make install“.
  • Windows: https://acpica.org/downloads/binary-tools
  • Ubuntu: sudo apt install acpica-tool

ACPICA Tools

The acpica-tools consist of the following utilities: acpibin, acpiexamples, acpihelp, acpisrc, iasl, acpidump, acpiexec, acpinames and acpixtract. This article focuses on how to use iasl, acpidump, acpiexec and acpixtract because they are commonly used in debugging.
  • acpidump - collect ACPI tables from a running system
  • acpixtract - extract ACPI tables from an acpidump file
  • acpiexec - emulate ACPI tables from extracted acpi tables
  • iasl - compile & disassemble ACPI tables

A Case Study - Airplane Mode

The airplane-mode button on laptops is usually implemented by both system BIOS and an OS driver. More specifically, it requires an ACPI device to be “present” in DSDT (or SSDT) table and a corresponding device driver. The figure below demonstrates how to debug if the airplane mode button fails to work.



Each computer brand has its specific implementation(s) and corresponding device driver(s). Some examples are listed below. Similarly, other driver source code can be found in Linux kernel.
  • Acer - acer-wireless
  • ASUS - asus-wireless
  • Dell - dell-rbtn, intel-hid, intel-vbtn
  • HP - hp-wireless
  • Lenovo - idealpad-laptop, thinkpad_acpi
  • Xiaomi - hp-wireless

Let’s use a Dell system as an example but the same technique also applies for others. To understand whether the airplane mode driver is loaded, one can run



As expected, the intel-hid is used on this particular Dell system.

As seen above, the “intel-hid” is used on this particular Dell system.
Let’s assume we know intel-hid is supposed to be used on this Dell system but it is not loaded for now. We can debug BIOS ASL/AML code using ACPICA utilities following the below steps.

Find Out ACPI Device's (intel-hid's) Hardware ID


A quick online search shows intel-hid.c is for an ACPI device with _HID “INT33D5”.

Extract and Disassemble ACPI Tables

  • Get all ACPI tables: sudo acpidump > acpi.log
  • Extract DSDT and SSDT: acpixtract acpi.log
  • Disassemble tables: iasl -d *.dat

Check Whether INT33D5 is "Present"

  • Find INT33D5 in DSDT or SSDT: grep INT33DT *.dsl

Once found, let's use vi to see more details in DSDT such as the device name (HIDD) as below:


  • Check device status, i.e. Method (_STA)
In addition to the device declaration, Linux ACPI device driver is loaded when _STA returns “present”. As in the above ASL code, _STA can be present (return 0x0F) or absent (return Zero). One can continue to trace OIDE() -> OSID() and so on to find out what _STA returns.

Alternatively, using acpiexec is easier for this task.

Evaluate _SB.HIDD._STA with acpiexec


Using acpiexec is the same as acpidbg, but it loads ACPI tables from files and runs AML in emulation mode, i.e. it does not touch hardware registers or actual memory.

  • Load ACPI tables: acpiexec *.dat
  • Find HIDD path: find HIDD
  • Execute HIDD's _STA: execute _SB.HIDD._STA
If _SB.HIDD._STA returns 0xF but intel-hid is not loaded, something is wrong with Linux kernel, ex. intel-hid is not compiled or is blacklisted. In this case, this should be forwarded to a Linux kernel engineer.

If _SB.HIDD._STA returns 0, we can continue to use acpiexec to find out OIDE() returns. In fact, we can use acpiexec to trace _SB._HIDD._STA
  • Trace _SB.HIDD._STA: debug _SB.HIDD._STA
Tutorial of using acpiexec is the same as acpidbg and can be found in the previous blog.

Final Words

ACPICA tools are very useful for debugging ACPI bugs, and using it well can save much time. This tutorial touches surface of what ACPICA tools can do and more documents can be found on acpica.org website.

Tuesday, May 1, 2018

ACPI Debugging (1) - ACPI AML Runtime Debugger in Ubuntu 18.04 (x64)

ACPICA is an open-source project that provides an operating system (OS)-independent reference implementation. It also contains a list of utilities such as ASL compiler (iasl), acpiexec (an AML emulator) and so on. However, debugging AML on Linux in runtime wasn't provided in ACPICA ... not until Linux Kernel 4.13.

Enabling AML Debugging


The aml-debugger.txt, the instruction for enabling AML runtime debugger, is available at Documentation/acpi/ in Linux kernel source code. In short, two things are required for AML runtime debugging
  • Enable AML debugging (CONFIG_ACPI_DEBUGGER=y & CONFIG_ACPI_DEBUGGER_USER=m) when compiling Linux kernel
  • Compile an utility, acpidbg from Linux kernel (I uploaded a copy here)
While compiling a custom-build kernel is nothing new to kernel developers, it is often inconvenient for system firmware / BIOS developers who need to verify ACPI AML implementation in their firmware. Fortunately, Ubuntu 18.04 (x64) and later enable these config by default, and one can simply execute acpidbg on Ubuntu 18.04 - even on Ubuntu Live from USB too!

Running AML Debugging


Executing acpidbg on Ubuntu 18.04 (x64) is straight-forward


and "help" shows a list supported commands



Examples


acpidbg is particularly handy when one needs to evaluate any ACPI AML objects during runtime - especially ones that change under different conditions. This may be explained by some examples below:

Example 1 - To determine AC power status in runtime.
  1. Find _PSR objects
  2. Evaluate _PSR when AC is disconnected
  3. Evaluate _PSR when AC is connected.


acpidbg also works with complex objects such as packages.
Example 2 - To determine battery information and status.
  1. Find _BIF and _BST objects
  2. Evaluate _BIF and _BST objects (note _BST changes dynamically).


acpidbg can decode bit fields and save time on counting bits. Try to run acpidbg to evaluate any _PLD objects and you will see what I mean :).

Friday, July 7, 2017

Why cannot ACPI _REV object be used to detect Linux anymore?

Linux community is never a fan of distinguishing Windows & Linux in BIOS (see my 2012's blog Why does Linux Say It Is not Linux in _OSI("Linux")?). However, many BIOS engineers seem to see the needs of to workaround BIOS bugs for Linux regardlessly. After realising _OSI("Linux") cannot be used by default, some creative BIOS engineers figured ACPI's _REV object could be used for this purpose some years ago, based on the following facts:

1. _REV definition in ACPI spec 5.1


2. Windows returns 2 & Linux returns ACPI revision


While it is obvious it was Windows which did not follow ACPI spec, nothing can stop BIOS engineers from mis-using _REV as a mechanism to detect Linux in BIOS ASL codes.

It did not take long when ACPI Spec Work Group (ASWG), the owner and the publisher of ACPI spec, realised that many systems are mis-using _REV objects in their firmware. Eventually a spec contributer submitted a change to _REV object in ACPI 6.0 and later, and the _REV object is now defined as below:



After ACPI 6.0 was published, the corresponding changes are also made to Linux kernel - the evaluation of _REV now returns 2 in kernel 4.1 and later (for example, Ubuntu 15.10 with kernel 4.2).

In order to provide backward compatibility, a new kernel parameter "acpi_rev_override" was introduced for those who still want to use _REV = 5 in their BIOS ASL code, but most of major Linux distributions do not include this kernel parameter by default, and it should be used with cautions.

If you are using one of the systems with _REV in the firmware, you can develop a kernel patch to add your system to the _REV quirk list acpi_rev_dmi_table in drivers/acpi/blacklist.c as below, like the patch I submitted.

static struct dmi_system_id acpi_rev_dmi_table[] __initdata = {
        {
         .callback = dmi_enable_rev_override,
         .ident = "DELL XPS 13 (2015)",
         .matches = {
                      DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                      DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343"),
                },
        },
        ...
  • DMI_SYS_VENDOR can be identified by sudo dmidecode -s system-manufacturer 
  • DMI_PRODUCT_NAME can be identified by sudo dmidecode -s system-product-name.

Thursday, June 4, 2015

Uitlity: Reading and Writing PCI, IO and memory with Linux Firmware Debug Kit (lfdk) in Linux

Many BIOS engineers are familiar with ru.exe in DOS and R/W everything in Windows.  One frequently asked question is whether there is an equivalent utility in Linux.  The answer is "sort of".

Linux Firmware Debug Kit (lfdk) provides read/write functions to PCI[1], IO and memory spaces.

lfdk's deb package is available but it is outdated. I usually download [3] and compile source code, and run ldfk manually in a terminal (ctrl + alt + T if you are running Ubuntu +Unity) as below:

  1. sudo apt-get install -f git libncurses-dev
  2. git clone https://github.com/alexhungce/lfdk1.git
  3. cd lfdk
  4. make
  5. cd bin
  6. sudo insmod ./lfdd_drv.ko
  7. sudo ./lfdk

A familiar screen will pop up


The navigation  is to use arrow keys and hotkeys L, M and I to different spaces as instructed on the bottom of the lfdk.



[1] Built-in lspci command can be used to read PCI configuration space, too
[2] This is tested in Ubuntu Linux 12.04 to 15.04.
[3] One can also download the source code from sourceforge.net above.

Thursday, June 20, 2013

ACPI Brightness Control (4) - Workaround for Brightness Problems in Ubuntu Linux

This blog discusses some common errors for brightness control and hotkeys. It will be updated from time to time.

Initial Zero Brightness

Some system BIOS reports zero in _BQC at boot-up, and it causes Linux kernel to dim the screen to minimum. In some cases, the panel is completely black out. This is because Linux's ACPI VGA driver does the following when it starts:

  1. Read _BQC for current brightness level
  2. Set brightness level to maximum - it is to verify whether brightness work
  3. Restore brightness level from 1
Aside from getting BIOS fixes which is usually close to impossible as the product is shipped (but I do encourage people to call customer services to complain!), there are three ways to fix it:

Kernel parameter

Edit /etc/default/grub
Add "video.use_bios_initl_backlight=0" to GRUB_CMDLINE_LINUX_DEFAULT
Run "sudo update-grub"
Reboot

Bootup script

Edit the file /etc/rc.local
Add a line "echo 4 > /sys/class/backlight/acpi_video0/brightness" before "exit 0"
Reboot

Quirk to acpi/video.c

If you are familiar with Linux kernel, you can develop a patch as in https://bugs.launchpad.net/bugs/1184501. If not, it is more than welcomed to file a bug like LP#1184501, run "sudo dmidecode > dmi.log", attach dmi.log and assign the bug to me. I am very happy to develop a patch for it, and you may help many others using Linux!

Brightness Fails to Work

If you are have laptop PC that BIOS fails to adjust brightness via acpi_video, you can try add the kernel parameter "acpi_backlight=vendor".

What the parameter does is to stop using acpi video to adjust the brightness - also skips creating sysfs nodes for acpi_videoX. As a result, a desktop daemon (like GSD) will find other brightness sysfs nodes created by other drivers, such as Intel_backlight, thinkpad_backlight and so on.

A Hotkey Press Changes Two Levels

In Figure 2 of ACPI Brightness Control (3) - Hotkeys in Ubuntu Linux, a solid and a dash line were draw to call to BIOS _BCM. This implies a hotkey can and may change a brightness twice. This is the case with Gnome-Setting-Daemon in Ubuntu.

If seeing this problem, one can add an kernel parameter to workaround it:
  • sudo vi /etc/default/grub
  • add brightness_switch_enabled=N to GRUB_CMDLINE_LINUX_DEFAULT
  • sudo update-grub
  • reboot

This will skip the dashed line call in ACPI Video driver:


References

  • https://wiki.archlinux.org/index.php/Backlight

ACPI Brightness Control (3) - Hotkeys in Ubuntu Linux

In ACPI Brightness Control (1) - Control Methods and ACPI Brightness Control (2) - Hotkeys, we understand how ACPI designs brightness from BIOS's perspectives. In this blog, we will see them in action using Ubuntu as an example.

Brightness Sysfs Nodes in Linux Kernel


When booting up, Linux's ACPI VGA driver (drivers/acpi/video.c) enumerates ACPI AML (DSDT and SSDT) to look for output devices that support brightness control. Once identified, it creates sysfs nodes in /sys/class/backlight/acpi_video0. If more than one output device is found, it creates acpi_video1 and so on.

FIgure 1 - Sysfs Nodes for Brightness
Figure 1 shows files in acpi_video0 directory. Three files are particularly important:

  • actual_brightness - implements _BQC
  • brightness - implements _BCM, and also record current brightness level
  • max_brightness - implements _BCL and converts it to number of levels
For example, the _BCL in this Thinkpad reports 16 brightness levels with Ubuntu 12.04, and therefore "cat max_brightness" returns 15. Current brightness can be read by executing "cat actual_brightness" or "cat brightness". In most cases, both max_brightness and brightness return the same value (this should not be surprising) - they are only different if something breaks in kernel or BIOS - such as _BQC does not change with _BCM.

To change brightness level, one writes a value to brightness, ranged from 0 to max_brightness. In this system, it is from 0 to 15. ACPI VGA driver will convert it to the actual value in _BCL and write to _BCM. 

Hotkey Implementation in Ubuntu Linux


Figure 2 - Overall Process for Brightness Hotkeys
Figure 2 is an expansion of ACPI Brightness Control (2) - Hotkeys. It shows that many tasks are done even with a single hotkey press, and it also shows separation between OS and BIOS is clear - the clean interface makes hotkey development easy and that's exactly what ACPI is for. Let's add more details to OS level as it is in Figure 3.

Figure 3 - Brightness and Desktop Daemon
Ubuntu ships with gnome-setting-daemon (GSD) that listens to keycode events. ACPI VGA driver converts ACPI notification events to keycodes, and GSD changes brightness via sysfs nodes according. GSD is also responsible for reading brightness levels from sysfs nodes to show OSD for users and to create slider for changing brightness. It is also worthy noting that ACPI VGA driver can also calls _BCM to change brightness without interacting with GSD, but no OSD may be shown.

As GSD is designed to listen to keycode, System vendor may design BIOS to generate other none-standard ACPI events, such as WMI events, as long as there is a kernel driver to do the conversion; however, this is not a suggested.

PS. Please note keycodes are not the same as KBC's scancode.

Debugging Brightness (Hotkey) Failures


From Figure 3, it becomes obvious brightness hotkeys can fail for two primary reasons:
  1. BIOS does not generate events
  2. _BCM fails to change brightness
In Linux, it is easy to verify whether ACPI VGA events and keycodes are generated:
  • Run "acpi_listen" to show ACPI standard events
  • Run "sudo showkey" to show keycodes
One can trigger writes to _BCM as in "Brightness Sysfs Nodes in Linux Kernel", if brightness is changed but LCD panel is not, BIOS did not implement _BCM correctly (or graphics drivers do not respond requests from BIOS in some cases).


Thursday, May 10, 2012

How to Dump ACPI Tables in Ubuntu

Dumping ACPI tables in Ubuntu is as easy as 1, 2, 3 (and 4, 5). Please note you need root privileges for some of the below tasks.

Install Linux utilities

sudo apt-get update && sudo apt-get install acpidump iasl

Dump ACPI tables from memory by acpidump

sudo acpidump > acpi.log

Extract tables by acpixtract

If you are intending to extract the DSDT table, you can use:
acpixtract -sDSDT acpi.log
If table type is not specified, acpixtract extracts DSDT and all SSDTs by default:
acpixtract acpi.log 

Decode an ACPI table by Intel's disassembler

iasl -d DSDT.dat

View result with your favorite text editor

I use: vi DSDT.dsl

Monday, February 13, 2012

Why does Linux Say It Is not Linux in _OSI("Linux")?

First, let me explain what _OSI is.


Section 5.7.2 of ACPI specification defines it as below:
===================================================
_OSI (Operating System Interfaces)

This object provides the platform with the ability to query OSPM to determine the set of ACPI related interfaces, behaviors, or features that the operating system supports.
...
_OSI provides the platform with the ability to support new operating system versions and their associated features when they become available. OSPM can choose to expose new functionality based on the _OSI argument string. That is, OSPM can use the strings passed into _OSI to ensure compatibility between older platforms and newer operating systems by maintaining known compatible behavior for a platform.
===================================================

In theory, BIOS can use _OSI to provide different functionality for different operating systems to a specific platform.

Now, the answer to the question "Why does Linux Say It Is not Linux in _OSI("Linux")?" is - because Linux kernel wants to provide better quality and user-experiences.

Why is the differentiation is not desirable for Linux? Linux source code (drivers/acpi/osl.c) gives a history how the design decision was made as below:

/*
 * The story of _OSI(Linux)
 *
 * From pre-history through Linux-2.6.22,
 * Linux responded TRUE upon a BIOS OSI(Linux) query.
 *
 * Unfortunately, reference BIOS writers got wind of this
 * and put OSI(Linux) in their example code, quickly exposing
 * this string as ill-conceived and opening the door to
 * an un-bounded number of BIOS incompatibilities.
 *
 * For example, OSI(Linux) was used on resume to re-POST a
 * video card on one system, because Linux at that time
 * could not do a speedy restore in its native driver.
 * But then upon gaining quick native restore capability,
 * Linux has no way to tell the BIOS to skip the time-consuming
 * POST -- putting Linux at a permanent performance disadvantage.
 * On another system, the BIOS writer used OSI(Linux)
 * to infer native OS support for IPMI!  On other systems,
 * OSI(Linux) simply got in the way of Linux claiming to
 * be compatible with other operating systems, exposing
 * BIOS issues such as skipped device initialization.
 *
 * So "Linux" turned out to be a really poor chose of
 * OSI string, and from Linux-2.6.23 onward we respond FALSE.
 *
 * BIOS writers should NOT query _OSI(Linux) on future systems.
 * Linux will complain on the console when it sees it, and return FALSE.
 * To get Linux to return TRUE for your system  will require
 * a kernel source update to add a DMI entry,
 * or boot with "acpi_osi=Linux"
 */ 

ACPICA User Guide and Programmer Reference also has a similar discussion in Section 8.1.7.2 "Why ACPICA responds TRUE to _OSI (Windows)"
===================================================
ACPICA responds TRUE to all known Windows strings because ACPICA attempts to be fully compatible with the Windows implementation of ACPI. On the other hand, ACPICA responds FALSE to other operating system strings (such as “Linux”, “FreeBSD”, or “HP-UX”) because doing so has been seen to often cause serious problems. For example, on many platforms, the only path through the ASL code that has been fully tested by the manufacturer is in fact the path for “Windows”. By responding TRUE to other operating system strings, the ASL may execute paths that have had only limited or even no evaluation.
===================================================

To summarize, returning FALSE in _OSI("Linux") forces better software quality from two perspectives.

1. Not to "workaround" kernel errors in BIOS code - if Linux bugs are found, it should be updated to Linux kernel developers to have them fixed. 
2. To ensure Linux kernel executes code path that is well-tested - the same path Windows would execute. 

In the case of that _OSI("Linux") is really needed, it can be forced with kernel parameter acpi_osi=Linux as in the above comments.

Some OEM/ODM ship their systems with acpi_osi=Linux in their customized images; however, the solution has a downside. If a particular bug is not fixed in future kernel, or it is not a kernel bug but a hardware compatibility bug and cannot be fixed, a clean Linux installation by an end-user without adding this parameters will expose this bug. 

Instead, a better solution would be to have a BIOS setup option for workaround. If the bug is fixed, an end-user can  disable the workaround. A clean installation without acpi_osi=Linux will not expose the bug, either.

_OSI Strings in Ubuntu 

_OSI does not return true for a specific string.  Instead, it returns true for a list of strings.  As in Windows, _OSI in Linux returns true up to a Windows version.  For example, kernel 3.13 (used in Ubuntu 14.04) would report _OSI = true for Windows 7 (ex. "Windows 2012" and all versions before.  The below table lists Windows versions and their supports in Ubuntu Linux

_OSI argument Windows version Supported in Ubuntu
Windows 2000 Windows 2000 Pre-Dapper 6.06 LTS
Windows 2001 Windows XP Pre-Dapper 6.06 LTS
Windows 2001 Windows XP SP1 Pre-Dapper 6.06 LTS
Windows 2001.1 Windows Server 2003 Pre-Dapper 6.06 LTS

Windows 2001 SP2
Windows XP SP2 Pre-Dapper 6.06 LTS

Windows 2001.1 SP1

Windows 2001.1 SP1

Hardy 8.04 LTS

Windows 2006

Windows Vista

Hardy 8.04 LTS

Windows 2006.1

Windows Server 2008

Lucid 10.04 LTS

Windows 2006 SP1

Windows Vista SP1

Lucid 10.04 LTS

Windows 2006 SP2

Windows Vista SP2

Natty 11.04

Windows 2009

Windows 7 and Server 2008 R2

Lucid 10.04 LTS

Windows 2012

Windows 8 and Server 2012

Raring 13.04

Windows 2013

Windows 8.1 and Server 2012 R2

Utopic 14.10

Windows 2015

Windows 10

Wily 15.10