Tuesday 22 February 2011

Firmware Test Suite reference guide.

I've been working away polishing up the Firmware Test Suite for Ubuntu Natty 11.04 and to complement the tool I have eventually got around to writing up a reference guide for the tool.   The guide can be found at:

https://wiki.ubuntu.com/Kernel/Reference/fwts

This complements the rather terse man page and ever terser fwts --help output.

Sunday 20 February 2011

Turning off a PC using the Intel 82801 I/O Controller Hub

Shutting down a PC is normally performed by transitioning the machine into an ACPI S5 state which is achieved by writing some magic value into an ACPI PM control register.  Implementing this is very straight forward once one has navigated a couple of rather information dense documents.

Armed with a copy of an Intel ICH manual one see that section 8.8.3.3 the PM1_CNT (Power Management 1 Control) register is the direct implementation of the ACPI PM1a_CNT_BLK register as described in section 4.7.3.2.1 of version 4.0 of the ACPI specification.


Bits 10:12 control the sleep state type (SLP_TYP), and setting these to 111 selects soft power off. To signal this, one has to set bit 13 to transition into the required sleep state.

On an Intel system, finding the PM1_CNT register is simple, use:

cat /proc/ioports | grep PM1a_CNT_BLK

..on my Dell 1525 this results in:

1004-1005 : ACPI PM1a_CNT_BLK

..so a soft shutdown can be invoked running the following code with root privileges:

#include <unistd.h>
#include <sys/io.h>
#include <stdint.h>
#include <stdio.h>

/* PM1 Sleep types */
#define SLP_ON       0
#define SLP_ST_PCLK  1
#define SLP_S3       5
#define SLP_S5       6
#define SLP_SOFT_OFF 7

int main(int argc, char **argv)
{
        uint16_t val;

        if (ioperm(0x1004, 2, 1) < 0) {
                printf("Cannot access port 0x1004\n");
                exit(0);
        }
        val = inw(0x1004);
        val &= ~(7 << 10); /* Clear SLP_TYPE */
        val |= (SLP_SOFT_OFF << 10); /* Soft power off */
        val |= (1 << 13);  /* Trigger SLP_EN */
        outw(val, 0x1004);
}

..this is akin to the Vulcan death grip, so make sure that one has sync'd and unmounted file systems first! It's quite impressive to see how quickly can shutdown a machine using this method.

Of course, more rugged implementation would be to determine the PM1a_CNT_BLK register from the Fixed ACPI Description Table (FADT), but happily for us Linux reports this register in /proc/ioports, so it's simple to do from user space.

Friday 11 February 2011

Video Mode Interrogation via BIOS calls.

While investigating the video modes on my laptop to do some sanity checking I thought I'd have a go at using libx86 to allow me to call into the BIOS directly from userspace.  After short consultation with the trusty Ralph Brown Interrupt List, I hacked up some code to get the list of all the video modes and interrogate each one to get the resolution.

I was getting random crashes before I realised that clearing the LRMI_regs struct before populating it with the desired x86 register settings fixed the problem.

Anyhow, the code is available in my git repository here - and it requires one to install libx86-dev and run with root privileges.   I'm still amazed that the code can jump from userspace into the BIOS, gather data and return without totally messing up the system.

Interestingly, a lot of UEFI systems still provide a legacy mode BIOS, so this code still works on these newer systems.

Thursday 3 February 2011

Resetting a PC using the Reset Control Register

Last night I was reading a data sheet about the ICH10 I/O Controller Hub (Section 13.7.5) and got a little more insight on the workings of the Reset Control Register (port 0xcf9).

Bits 1 and 3 determine the type of reset being requested and bit 2 initiates the reset.   When bit 2 (SYS_RST) transitions from 0 to 1 a reset is initiated as determined by the policy of bits 1 and 3.

Bit 1, System Reset (SYS_RST) determines a soft reset (0) or hard reset (1).
Bit 3, Full Reset (FULL_RST) if set to 1 causes a full power cycle.

On some systems, the ACPI FACP RESET_REG and RESET_VALUE are set 0xcf9 and 0x06 respectively which essentially triggers a hard system reset when doing a reboot using the reboot=acpi kernel option.

The kernel also has a reboot=pci option that will force a reset via the Reset Control Register and does this in three stages. First it sets bit 1 (SYS_RST=hard reset), waits 50 microseconds and then transitions SYS_RST from 0 to 1 to initiate the reset. 

However, a full system reset can be initiated by also setting bit three by writing 0x0e to port 0xcf9.  I've noticed that some newer laptops on the market seem to be doing a full system reset on reboot, so I wonder if this is the mechanism they are using nowadays.

The beauty of Linux is that one can string a bunch of commands together to do this from user space:

echo -e '\xe' | sudo dd of=/dev/port bs=1 seek=3321

..so make sure you data is sync'd before hand as this is destructive as power cycling the machine while it's on.