iPXE
Functions
acpi.c File Reference

ACPI support functions. More...

#include <errno.h>
#include <byteswap.h>
#include <ipxe/uaccess.h>
#include <ipxe/acpi.h>
#include <ipxe/interface.h>

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static uint8_t acpi_checksum (userptr_t table)
 Compute ACPI table checksum.
void acpi_fix_checksum (struct acpi_header *acpi)
 Fix up ACPI table checksum.
userptr_t acpi_find (uint32_t signature, unsigned int index)
 Locate ACPI table.
static int acpi_sx_zsdt (userptr_t zsdt, uint32_t signature)
 Extract value from DSDT/SSDT.
int acpi_sx (uint32_t signature)
 Extract value from DSDT/SSDT.
void acpi_add (struct acpi_descriptor *desc)
 Add ACPI descriptor.
void acpi_del (struct acpi_descriptor *desc)
 Remove ACPI descriptor.
struct acpi_descriptoracpi_describe (struct interface *intf)
 Get object's ACPI descriptor.
int acpi_install (int(*install)(struct acpi_header *acpi))
 Install ACPI tables.

Detailed Description

ACPI support functions.

Definition in file acpi.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static uint8_t acpi_checksum ( userptr_t  table) [static]

Compute ACPI table checksum.

Parameters:
tableAny ACPI table
Return values:
checksum0 if checksum is good

Definition at line 51 of file acpi.c.

References copy_from_user(), data, le32_to_cpu, acpi_header::length, length, and offsetof.

Referenced by acpi_find(), and acpi_fix_checksum().

                                                 {
        struct acpi_header acpi;
        uint8_t sum = 0;
        uint8_t data = 0;
        unsigned int i;

        /* Read table length */
        copy_from_user ( &acpi.length, table,
                         offsetof ( typeof ( acpi ), length ),
                         sizeof ( acpi.length ) );

        /* Compute checksum */
        for ( i = 0 ; i < le32_to_cpu ( acpi.length ) ; i++ ) {
                copy_from_user ( &data, table, i, sizeof ( data ) );
                sum += data;
        }

        return sum;
}
void acpi_fix_checksum ( struct acpi_header acpi)

Fix up ACPI table checksum.

Parameters:
acpiACPI table header

Definition at line 76 of file acpi.c.

References acpi_checksum(), acpi_header::checksum, and virt_to_user().

Referenced by efi_block_install(), and int13_install().

                                                    {

        /* Update checksum */
        acpi->checksum -= acpi_checksum ( virt_to_user ( acpi ) );
}
userptr_t acpi_find ( uint32_t  signature,
unsigned int  index 
)

Locate ACPI table.

Parameters:
signatureRequested table signature
indexRequested index of table with this signature
Return values:
tableTable, or UNULL if not found

Definition at line 89 of file acpi.c.

References acpi_rsdt::acpi, acpi_checksum(), acpi_find_rsdt(), acpi_name(), copy_from_user(), count, cpu_to_le32, DBG, DBGC, DBGC_HDA, acpi_rsdt::entry, entry, le32_to_cpu, len, acpi_header::length, offsetof, phys_to_user(), RSDT_SIGNATURE, acpi_header::signature, UNULL, and user_to_phys().

Referenced by acpi_poweroff(), acpi_settings_fetch(), acpi_sx(), and acpi_timer_probe().

                                                               {
        struct acpi_header acpi;
        struct acpi_rsdt *rsdtab;
        typeof ( rsdtab->entry[0] ) entry;
        userptr_t rsdt;
        userptr_t table;
        size_t len;
        unsigned int count;
        unsigned int i;

        /* Locate RSDT */
        rsdt = acpi_find_rsdt();
        if ( ! rsdt ) {
                DBG ( "RSDT not found\n" );
                return UNULL;
        }

        /* Read RSDT header */
        copy_from_user ( &acpi, rsdt, 0, sizeof ( acpi ) );
        if ( acpi.signature != cpu_to_le32 ( RSDT_SIGNATURE ) ) {
                DBGC ( rsdt, "RSDT %#08lx has invalid signature:\n",
                       user_to_phys ( rsdt, 0 ) );
                DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi,
                           sizeof ( acpi ) );
                return UNULL;
        }
        len = le32_to_cpu ( acpi.length );
        if ( len < sizeof ( rsdtab->acpi ) ) {
                DBGC ( rsdt, "RSDT %#08lx has invalid length:\n",
                       user_to_phys ( rsdt, 0 ) );
                DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi,
                           sizeof ( acpi ) );
                return UNULL;
        }

        /* Calculate number of entries */
        count = ( ( len - sizeof ( rsdtab->acpi ) ) / sizeof ( entry ) );

        /* Search through entries */
        for ( i = 0 ; i < count ; i++ ) {

                /* Get table address */
                copy_from_user ( &entry, rsdt,
                                 offsetof ( typeof ( *rsdtab ), entry[i] ),
                                 sizeof ( entry ) );

                /* Read table header */
                table = phys_to_user ( entry );
                copy_from_user ( &acpi.signature, table, 0,
                                 sizeof ( acpi.signature ) );

                /* Check table signature */
                if ( acpi.signature != cpu_to_le32 ( signature ) )
                        continue;

                /* Check index */
                if ( index-- )
                        continue;

                /* Check table integrity */
                if ( acpi_checksum ( table ) != 0 ) {
                        DBGC ( rsdt, "RSDT %#08lx found %s with bad checksum "
                               "at %08lx\n", user_to_phys ( rsdt, 0 ),
                               acpi_name ( signature ),
                               user_to_phys ( table, 0 ) );
                        break;
                }

                DBGC ( rsdt, "RSDT %#08lx found %s at %08lx\n",
                       user_to_phys ( rsdt, 0 ), acpi_name ( signature ),
                       user_to_phys ( table, 0 ) );
                return table;
        }

        DBGC ( rsdt, "RSDT %#08lx could not find %s\n",
               user_to_phys ( rsdt, 0 ), acpi_name ( signature ) );
        return UNULL;
}
static int acpi_sx_zsdt ( userptr_t  zsdt,
uint32_t  signature 
) [static]

Extract value from DSDT/SSDT.

Parameters:
zsdtDSDT or SSDT
signatureSignature (e.g. "_S5_")
Return values:
sxvalue, or negative error

In theory, extracting the value from the DSDT/SSDT requires a full ACPI parser plus some heuristics to work around the various broken encodings encountered in real ACPI implementations.

In practice, we can get the same result by scanning through the DSDT/SSDT for the signature (e.g. "_S5_"), extracting the first four bytes, removing any bytes with bit 3 set, and treating whatever is left as a little-endian value. This is one of the uglier hacks I have ever implemented, but it's still prettier than the ACPI specification itself.

Definition at line 186 of file acpi.c.

References acpi_name(), byte, copy_from_user(), cpu_to_le32, DBGC, dword, ENOENT, le32_to_cpu, len, acpi_header::length, offset, and user_to_phys().

Referenced by acpi_sx().

                                                               {
        struct acpi_header acpi;
        union {
                uint32_t dword;
                uint8_t byte[4];
        } buf;
        size_t offset;
        size_t len;
        unsigned int sx;
        uint8_t *byte;

        /* Read table header */
        copy_from_user ( &acpi, zsdt, 0, sizeof ( acpi ) );
        len = le32_to_cpu ( acpi.length );

        /* Locate signature */
        for ( offset = sizeof ( acpi ) ;
              ( ( offset + sizeof ( buf ) /* signature */ + 3 /* pkg header */
                  + sizeof ( buf ) /* value */ ) < len ) ;
              offset++ ) {

                /* Check signature */
                copy_from_user ( &buf, zsdt, offset, sizeof ( buf ) );
                if ( buf.dword != cpu_to_le32 ( signature ) )
                        continue;
                DBGC ( zsdt, "DSDT/SSDT %#08lx found %s at offset %#zx\n",
                       user_to_phys ( zsdt, 0 ), acpi_name ( signature ),
                       offset );
                offset += sizeof ( buf );

                /* Read first four bytes of value */
                copy_from_user ( &buf, zsdt, ( offset + 3 /* pkg header */ ),
                                 sizeof ( buf ) );
                DBGC ( zsdt, "DSDT/SSDT %#08lx found %s containing "
                       "%02x:%02x:%02x:%02x\n", user_to_phys ( zsdt, 0 ),
                       acpi_name ( signature ), buf.byte[0], buf.byte[1],
                       buf.byte[2], buf.byte[3] );

                /* Extract \Sx value.  There are three potential
                 * encodings that we might encounter:
                 *
                 * - SLP_TYPa, SLP_TYPb, rsvd, rsvd
                 *
                 * - <byteprefix>, SLP_TYPa, <byteprefix>, SLP_TYPb, ...
                 *
                 * - <dwordprefix>, SLP_TYPa, SLP_TYPb, 0, 0
                 *
                 * Since <byteprefix> and <dwordprefix> both have bit
                 * 3 set, and valid SLP_TYPx must have bit 3 clear
                 * (since SLP_TYPx is a 3-bit field), we can just skip
                 * any bytes with bit 3 set.
                 */
                byte = &buf.byte[0];
                if ( *byte & 0x08 )
                        byte++;
                sx = *(byte++);
                if ( *byte & 0x08 )
                        byte++;
                sx |= ( *byte << 8 );
                return sx;
        }

        return -ENOENT;
}
int acpi_sx ( uint32_t  signature)

Extract value from DSDT/SSDT.

Parameters:
signatureSignature (e.g. "_S5_")
Return values:
sxvalue, or negative error

Definition at line 257 of file acpi.c.

References acpi_find(), acpi_find_rsdt(), acpi_name(), acpi_sx_zsdt(), copy_from_user(), DBG, DBGC, acpi_fadt::dsdt, ENOENT, FADT_SIGNATURE, phys_to_user(), SSDT_SIGNATURE, and user_to_phys().

Referenced by acpi_poweroff().

                                   {
        struct acpi_fadt fadtab;
        userptr_t rsdt;
        userptr_t fadt;
        userptr_t dsdt;
        userptr_t ssdt;
        unsigned int i;
        int sx;

        /* Locate RSDT */
        rsdt = acpi_find_rsdt();
        if ( ! rsdt ) {
                DBG ( "RSDT not found\n" );
                return -ENOENT;
        }

        /* Try DSDT first */
        fadt = acpi_find ( FADT_SIGNATURE, 0 );
        if ( fadt ) {
                copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) );
                dsdt = phys_to_user ( fadtab.dsdt );
                if ( ( sx = acpi_sx_zsdt ( dsdt, signature ) ) >= 0 )
                        return sx;
        }

        /* Try all SSDTs */
        for ( i = 0 ; ; i++ ) {
                ssdt = acpi_find ( SSDT_SIGNATURE, i );
                if ( ! ssdt )
                        break;
                if ( ( sx = acpi_sx_zsdt ( ssdt, signature ) ) >= 0 )
                        return sx;
        }

        DBGC ( rsdt, "RSDT %#08lx could not find \\_Sx \"%s\"\n",
               user_to_phys ( rsdt, 0 ), acpi_name ( signature ) );
        return -ENOENT;
}
void acpi_add ( struct acpi_descriptor desc)

Add ACPI descriptor.

Parameters:
descACPI descriptor

Definition at line 308 of file acpi.c.

References acpi_model::descs, acpi_descriptor::list, list_add_tail, acpi_descriptor::model, ref_get, and acpi_descriptor::refcnt.

Referenced by sanpath_open().

                                               {

        /* Add to list of descriptors */
        ref_get ( desc->refcnt );
        list_add_tail ( &desc->list, &desc->model->descs );
}
void acpi_del ( struct acpi_descriptor desc)

Remove ACPI descriptor.

Parameters:
descACPI descriptor

Definition at line 320 of file acpi.c.

References acpi_model::descs, acpi_descriptor::list, list_check_contains_entry, list_del, acpi_descriptor::model, ref_put, and acpi_descriptor::refcnt.

Referenced by sandev_undescribe(), and sanpath_open().

                                               {

        /* Remove from list of descriptors */
        list_check_contains_entry ( desc, &desc->model->descs, list );
        list_del ( &desc->list );
        ref_put ( desc->refcnt );
}
struct acpi_descriptor* acpi_describe ( struct interface intf) [read]

Get object's ACPI descriptor.

Parameters:
intfInterface
Return values:
descACPI descriptor, or NULL

Definition at line 334 of file acpi.c.

References acpi_describe(), acpi_describe_TYPE, dest, intf_get_dest_op, intf_object(), intf_put(), NULL, and op.

Referenced by acpi_describe(), and sanpath_open().

                                                                  {
        struct interface *dest;
        acpi_describe_TYPE ( void * ) *op =
                intf_get_dest_op ( intf, acpi_describe, &dest );
        void *object = intf_object ( dest );
        struct acpi_descriptor *desc;

        if ( op ) {
                desc = op ( object );
        } else {
                desc = NULL;
        }

        intf_put ( dest );
        return desc;
}
int acpi_install ( int(*)(struct acpi_header *acpi install)

Install ACPI tables.

Parameters:
installTable installation method
Return values:
rcReturn status code

Definition at line 357 of file acpi.c.

References ACPI_MODELS, for_each_table_entry, acpi_model::install, and rc.

Referenced by dummy_san_describe(), efi_block_describe(), and int13_describe().

                                                                   {
        struct acpi_model *model;
        int rc;

        for_each_table_entry ( model, ACPI_MODELS ) {
                if ( ( rc = model->install ( install ) ) != 0 )
                        return rc;
        }

        return 0;
}