iPXE
acpi.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <errno.h>
00027 #include <byteswap.h>
00028 #include <ipxe/uaccess.h>
00029 #include <ipxe/acpi.h>
00030 #include <ipxe/interface.h>
00031 
00032 /** @file
00033  *
00034  * ACPI support functions
00035  *
00036  */
00037 
00038 /******************************************************************************
00039  *
00040  * Utility functions
00041  *
00042  ******************************************************************************
00043  */
00044 
00045 /**
00046  * Compute ACPI table checksum
00047  *
00048  * @v table             Any ACPI table
00049  * @ret checksum        0 if checksum is good
00050  */
00051 static uint8_t acpi_checksum ( userptr_t table ) {
00052         struct acpi_header acpi;
00053         uint8_t sum = 0;
00054         uint8_t data = 0;
00055         unsigned int i;
00056 
00057         /* Read table length */
00058         copy_from_user ( &acpi.length, table,
00059                          offsetof ( typeof ( acpi ), length ),
00060                          sizeof ( acpi.length ) );
00061 
00062         /* Compute checksum */
00063         for ( i = 0 ; i < le32_to_cpu ( acpi.length ) ; i++ ) {
00064                 copy_from_user ( &data, table, i, sizeof ( data ) );
00065                 sum += data;
00066         }
00067 
00068         return sum;
00069 }
00070 
00071 /**
00072  * Fix up ACPI table checksum
00073  *
00074  * @v acpi              ACPI table header
00075  */
00076 void acpi_fix_checksum ( struct acpi_header *acpi ) {
00077 
00078         /* Update checksum */
00079         acpi->checksum -= acpi_checksum ( virt_to_user ( acpi ) );
00080 }
00081 
00082 /**
00083  * Locate ACPI table
00084  *
00085  * @v signature         Requested table signature
00086  * @v index             Requested index of table with this signature
00087  * @ret table           Table, or UNULL if not found
00088  */
00089 userptr_t acpi_find ( uint32_t signature, unsigned int index ) {
00090         struct acpi_header acpi;
00091         struct acpi_rsdt *rsdtab;
00092         typeof ( rsdtab->entry[0] ) entry;
00093         userptr_t rsdt;
00094         userptr_t table;
00095         size_t len;
00096         unsigned int count;
00097         unsigned int i;
00098 
00099         /* Locate RSDT */
00100         rsdt = acpi_find_rsdt();
00101         if ( ! rsdt ) {
00102                 DBG ( "RSDT not found\n" );
00103                 return UNULL;
00104         }
00105 
00106         /* Read RSDT header */
00107         copy_from_user ( &acpi, rsdt, 0, sizeof ( acpi ) );
00108         if ( acpi.signature != cpu_to_le32 ( RSDT_SIGNATURE ) ) {
00109                 DBGC ( rsdt, "RSDT %#08lx has invalid signature:\n",
00110                        user_to_phys ( rsdt, 0 ) );
00111                 DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi,
00112                            sizeof ( acpi ) );
00113                 return UNULL;
00114         }
00115         len = le32_to_cpu ( acpi.length );
00116         if ( len < sizeof ( rsdtab->acpi ) ) {
00117                 DBGC ( rsdt, "RSDT %#08lx has invalid length:\n",
00118                        user_to_phys ( rsdt, 0 ) );
00119                 DBGC_HDA ( rsdt, user_to_phys ( rsdt, 0 ), &acpi,
00120                            sizeof ( acpi ) );
00121                 return UNULL;
00122         }
00123 
00124         /* Calculate number of entries */
00125         count = ( ( len - sizeof ( rsdtab->acpi ) ) / sizeof ( entry ) );
00126 
00127         /* Search through entries */
00128         for ( i = 0 ; i < count ; i++ ) {
00129 
00130                 /* Get table address */
00131                 copy_from_user ( &entry, rsdt,
00132                                  offsetof ( typeof ( *rsdtab ), entry[i] ),
00133                                  sizeof ( entry ) );
00134 
00135                 /* Read table header */
00136                 table = phys_to_user ( entry );
00137                 copy_from_user ( &acpi.signature, table, 0,
00138                                  sizeof ( acpi.signature ) );
00139 
00140                 /* Check table signature */
00141                 if ( acpi.signature != cpu_to_le32 ( signature ) )
00142                         continue;
00143 
00144                 /* Check index */
00145                 if ( index-- )
00146                         continue;
00147 
00148                 /* Check table integrity */
00149                 if ( acpi_checksum ( table ) != 0 ) {
00150                         DBGC ( rsdt, "RSDT %#08lx found %s with bad checksum "
00151                                "at %08lx\n", user_to_phys ( rsdt, 0 ),
00152                                acpi_name ( signature ),
00153                                user_to_phys ( table, 0 ) );
00154                         break;
00155                 }
00156 
00157                 DBGC ( rsdt, "RSDT %#08lx found %s at %08lx\n",
00158                        user_to_phys ( rsdt, 0 ), acpi_name ( signature ),
00159                        user_to_phys ( table, 0 ) );
00160                 return table;
00161         }
00162 
00163         DBGC ( rsdt, "RSDT %#08lx could not find %s\n",
00164                user_to_phys ( rsdt, 0 ), acpi_name ( signature ) );
00165         return UNULL;
00166 }
00167 
00168 /**
00169  * Extract \_Sx value from DSDT/SSDT
00170  *
00171  * @v zsdt              DSDT or SSDT
00172  * @v signature         Signature (e.g. "_S5_")
00173  * @ret sx              \_Sx value, or negative error
00174  *
00175  * In theory, extracting the \_Sx value from the DSDT/SSDT requires a
00176  * full ACPI parser plus some heuristics to work around the various
00177  * broken encodings encountered in real ACPI implementations.
00178  *
00179  * In practice, we can get the same result by scanning through the
00180  * DSDT/SSDT for the signature (e.g. "_S5_"), extracting the first
00181  * four bytes, removing any bytes with bit 3 set, and treating
00182  * whatever is left as a little-endian value.  This is one of the
00183  * uglier hacks I have ever implemented, but it's still prettier than
00184  * the ACPI specification itself.
00185  */
00186 static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
00187         struct acpi_header acpi;
00188         union {
00189                 uint32_t dword;
00190                 uint8_t byte[4];
00191         } buf;
00192         size_t offset;
00193         size_t len;
00194         unsigned int sx;
00195         uint8_t *byte;
00196 
00197         /* Read table header */
00198         copy_from_user ( &acpi, zsdt, 0, sizeof ( acpi ) );
00199         len = le32_to_cpu ( acpi.length );
00200 
00201         /* Locate signature */
00202         for ( offset = sizeof ( acpi ) ;
00203               ( ( offset + sizeof ( buf ) /* signature */ + 3 /* pkg header */
00204                   + sizeof ( buf ) /* value */ ) < len ) ;
00205               offset++ ) {
00206 
00207                 /* Check signature */
00208                 copy_from_user ( &buf, zsdt, offset, sizeof ( buf ) );
00209                 if ( buf.dword != cpu_to_le32 ( signature ) )
00210                         continue;
00211                 DBGC ( zsdt, "DSDT/SSDT %#08lx found %s at offset %#zx\n",
00212                        user_to_phys ( zsdt, 0 ), acpi_name ( signature ),
00213                        offset );
00214                 offset += sizeof ( buf );
00215 
00216                 /* Read first four bytes of value */
00217                 copy_from_user ( &buf, zsdt, ( offset + 3 /* pkg header */ ),
00218                                  sizeof ( buf ) );
00219                 DBGC ( zsdt, "DSDT/SSDT %#08lx found %s containing "
00220                        "%02x:%02x:%02x:%02x\n", user_to_phys ( zsdt, 0 ),
00221                        acpi_name ( signature ), buf.byte[0], buf.byte[1],
00222                        buf.byte[2], buf.byte[3] );
00223 
00224                 /* Extract \Sx value.  There are three potential
00225                  * encodings that we might encounter:
00226                  *
00227                  * - SLP_TYPa, SLP_TYPb, rsvd, rsvd
00228                  *
00229                  * - <byteprefix>, SLP_TYPa, <byteprefix>, SLP_TYPb, ...
00230                  *
00231                  * - <dwordprefix>, SLP_TYPa, SLP_TYPb, 0, 0
00232                  *
00233                  * Since <byteprefix> and <dwordprefix> both have bit
00234                  * 3 set, and valid SLP_TYPx must have bit 3 clear
00235                  * (since SLP_TYPx is a 3-bit field), we can just skip
00236                  * any bytes with bit 3 set.
00237                  */
00238                 byte = &buf.byte[0];
00239                 if ( *byte & 0x08 )
00240                         byte++;
00241                 sx = *(byte++);
00242                 if ( *byte & 0x08 )
00243                         byte++;
00244                 sx |= ( *byte << 8 );
00245                 return sx;
00246         }
00247 
00248         return -ENOENT;
00249 }
00250 
00251 /**
00252  * Extract \_Sx value from DSDT/SSDT
00253  *
00254  * @v signature         Signature (e.g. "_S5_")
00255  * @ret sx              \_Sx value, or negative error
00256  */
00257 int acpi_sx ( uint32_t signature ) {
00258         struct acpi_fadt fadtab;
00259         userptr_t rsdt;
00260         userptr_t fadt;
00261         userptr_t dsdt;
00262         userptr_t ssdt;
00263         unsigned int i;
00264         int sx;
00265 
00266         /* Locate RSDT */
00267         rsdt = acpi_find_rsdt();
00268         if ( ! rsdt ) {
00269                 DBG ( "RSDT not found\n" );
00270                 return -ENOENT;
00271         }
00272 
00273         /* Try DSDT first */
00274         fadt = acpi_find ( FADT_SIGNATURE, 0 );
00275         if ( fadt ) {
00276                 copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) );
00277                 dsdt = phys_to_user ( fadtab.dsdt );
00278                 if ( ( sx = acpi_sx_zsdt ( dsdt, signature ) ) >= 0 )
00279                         return sx;
00280         }
00281 
00282         /* Try all SSDTs */
00283         for ( i = 0 ; ; i++ ) {
00284                 ssdt = acpi_find ( SSDT_SIGNATURE, i );
00285                 if ( ! ssdt )
00286                         break;
00287                 if ( ( sx = acpi_sx_zsdt ( ssdt, signature ) ) >= 0 )
00288                         return sx;
00289         }
00290 
00291         DBGC ( rsdt, "RSDT %#08lx could not find \\_Sx \"%s\"\n",
00292                user_to_phys ( rsdt, 0 ), acpi_name ( signature ) );
00293         return -ENOENT;
00294 }
00295 
00296 /******************************************************************************
00297  *
00298  * Descriptors
00299  *
00300  ******************************************************************************
00301  */
00302 
00303 /**
00304  * Add ACPI descriptor
00305  *
00306  * @v desc              ACPI descriptor
00307  */
00308 void acpi_add ( struct acpi_descriptor *desc ) {
00309 
00310         /* Add to list of descriptors */
00311         ref_get ( desc->refcnt );
00312         list_add_tail ( &desc->list, &desc->model->descs );
00313 }
00314 
00315 /**
00316  * Remove ACPI descriptor
00317  *
00318  * @v desc              ACPI descriptor
00319  */
00320 void acpi_del ( struct acpi_descriptor *desc ) {
00321 
00322         /* Remove from list of descriptors */
00323         list_check_contains_entry ( desc, &desc->model->descs, list );
00324         list_del ( &desc->list );
00325         ref_put ( desc->refcnt );
00326 }
00327 
00328 /**
00329  * Get object's ACPI descriptor
00330  *
00331  * @v intf              Interface
00332  * @ret desc            ACPI descriptor, or NULL
00333  */
00334 struct acpi_descriptor * acpi_describe ( struct interface *intf ) {
00335         struct interface *dest;
00336         acpi_describe_TYPE ( void * ) *op =
00337                 intf_get_dest_op ( intf, acpi_describe, &dest );
00338         void *object = intf_object ( dest );
00339         struct acpi_descriptor *desc;
00340 
00341         if ( op ) {
00342                 desc = op ( object );
00343         } else {
00344                 desc = NULL;
00345         }
00346 
00347         intf_put ( dest );
00348         return desc;
00349 }
00350 
00351 /**
00352  * Install ACPI tables
00353  *
00354  * @v install           Table installation method
00355  * @ret rc              Return status code
00356  */
00357 int acpi_install ( int ( * install ) ( struct acpi_header *acpi ) ){
00358         struct acpi_model *model;
00359         int rc;
00360 
00361         for_each_table_entry ( model, ACPI_MODELS ) {
00362                 if ( ( rc = model->install ( install ) ) != 0 )
00363                         return rc;
00364         }
00365 
00366         return 0;
00367 }