]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[acpi] Generalise DSDT/SSDT data extraction logic
authorMichael Brown <mcb30@ipxe.org>
Wed, 8 Sep 2021 11:53:12 +0000 (12:53 +0100)
committerMichael Brown <mcb30@ipxe.org>
Wed, 8 Sep 2021 13:46:30 +0000 (14:46 +0100)
Allow for the DSDT/SSDT signature-scanning and value extraction code
to be reused for extracting a pass-through MAC address.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/x86/interface/pcbios/acpipwr.c
src/core/acpi.c
src/include/ipxe/acpi.h

index dc164c7d52cfed7aea4f62329f104f86dc3251e0..3dac6b6051d9a543067a34e9fb7317fc37528459 100644 (file)
@@ -42,6 +42,69 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 /** _S5_ signature */
 #define S5_SIGNATURE ACPI_SIGNATURE ( '_', 'S', '5', '_' )
 
+/**
+ * Extract \_Sx value from DSDT/SSDT
+ *
+ * @v zsdt             DSDT or SSDT
+ * @v len              Length of DSDT/SSDT
+ * @v offset           Offset of signature within DSDT/SSDT
+ * @v data             Data buffer
+ * @ret rc             Return status code
+ *
+ * In theory, extracting the \_Sx 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.
+ */
+static int acpi_extract_sx ( userptr_t zsdt, size_t len, size_t offset,
+                            void *data ) {
+       unsigned int *sx = data;
+       uint8_t bytes[4];
+       uint8_t *byte;
+
+       /* Skip signature and package header */
+       offset += ( 4 /* signature */ + 3 /* package header */ );
+
+       /* Sanity check */
+       if ( ( offset + sizeof ( bytes ) /* value */ ) > len ) {
+               return -EINVAL;
+       }
+
+       /* Read first four bytes of value */
+       copy_from_user ( bytes, zsdt, offset, sizeof ( bytes ) );
+       DBGC ( colour, "ACPI found \\_Sx containing %02x:%02x:%02x:%02x\n",
+              bytes[0], bytes[1], bytes[2], bytes[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 = bytes;
+       if ( *byte & 0x08 )
+               byte++;
+       *sx = *(byte++);
+       if ( *byte & 0x08 )
+               byte++;
+       *sx |= ( *byte << 8 );
+
+       return 0;
+}
+
 /**
  * Power off the computer using ACPI
  *
@@ -56,7 +119,7 @@ int acpi_poweroff ( void ) {
        unsigned int pm1b_cnt;
        unsigned int slp_typa;
        unsigned int slp_typb;
-       int s5;
+       unsigned int s5;
        int rc;
 
        /* Locate FADT */
@@ -74,9 +137,8 @@ int acpi_poweroff ( void ) {
        pm1b_cnt = ( pm1b_cnt_blk + ACPI_PM1_CNT );
 
        /* Extract \_S5 from DSDT or any SSDT */
-       s5 = acpi_sx ( S5_SIGNATURE );
-       if ( s5 < 0 ) {
-               rc = s5;
+       if ( ( rc = acpi_extract ( S5_SIGNATURE, &s5,
+                                  acpi_extract_sx ) ) != 0 ) {
                DBGC ( colour, "ACPI could not extract \\_S5: %s\n",
                       strerror ( rc ) );
                return rc;
index 52eb63a0419c856b2f83af15d56aaeeeade38af1..aa486da939051a55dfe13bf4294d9a2c30a42cfc 100644 (file)
@@ -169,33 +169,22 @@ userptr_t acpi_find_via_rsdt ( uint32_t signature, unsigned int index ) {
 }
 
 /**
- * Extract \_Sx value from DSDT/SSDT
+ * Extract value from DSDT/SSDT
  *
  * @v zsdt             DSDT or SSDT
  * @v signature                Signature (e.g. "_S5_")
- * @ret sx             \_Sx value, or negative error
- *
- * In theory, extracting the \_Sx 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.
+ * @v data             Data buffer
+ * @v extract          Extraction method
+ * @ret rc             Return status code
  */
-static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
+static int acpi_zsdt ( userptr_t zsdt, uint32_t signature, void *data,
+                      int ( * extract ) ( userptr_t zsdt, size_t len,
+                                          size_t offset, void *data ) ) {
        struct acpi_header acpi;
-       union {
-               uint32_t dword;
-               uint8_t byte[4];
-       } buf;
+       uint32_t buf;
        size_t offset;
        size_t len;
-       unsigned int sx;
-       uint8_t *byte;
+       int rc;
 
        /* Read table header */
        copy_from_user ( &acpi, zsdt, 0, sizeof ( acpi ) );
@@ -203,75 +192,51 @@ static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
 
        /* Locate signature */
        for ( offset = sizeof ( acpi ) ;
-             ( ( offset + sizeof ( buf ) /* signature */ + 3 /* pkg header */
-                 + sizeof ( buf ) /* value */ ) < len ) ;
+             ( ( offset + sizeof ( buf ) /* signature */ ) < len ) ;
              offset++ ) {
 
                /* Check signature */
                copy_from_user ( &buf, zsdt, offset, sizeof ( buf ) );
-               if ( buf.dword != cpu_to_le32 ( signature ) )
+               if ( buf != 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;
+
+               /* Attempt to extract data */
+               if ( ( rc = extract ( zsdt, len, offset, data ) ) == 0 )
+                       return 0;
        }
 
        return -ENOENT;
 }
 
 /**
- * Extract \_Sx value from DSDT/SSDT
+ * Extract value from DSDT/SSDT
  *
  * @v signature                Signature (e.g. "_S5_")
- * @ret sx             \_Sx value, or negative error
+ * @v data             Data buffer
+ * @v extract          Extraction method
+ * @ret rc             Return status code
  */
-int acpi_sx ( uint32_t signature ) {
+int acpi_extract ( uint32_t signature, void *data,
+                  int ( * extract ) ( userptr_t zsdt, size_t len,
+                                      size_t offset, void *data ) ) {
        struct acpi_fadt fadtab;
        userptr_t fadt;
        userptr_t dsdt;
        userptr_t ssdt;
        unsigned int i;
-       int sx;
+       int rc;
 
        /* 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;
+               if ( ( rc = acpi_zsdt ( dsdt, signature, data,
+                                       extract ) ) == 0 )
+                       return 0;
        }
 
        /* Try all SSDTs */
@@ -279,11 +244,12 @@ int acpi_sx ( uint32_t signature ) {
                ssdt = acpi_find ( SSDT_SIGNATURE, i );
                if ( ! ssdt )
                        break;
-               if ( ( sx = acpi_sx_zsdt ( ssdt, signature ) ) >= 0 )
-                       return sx;
+               if ( ( rc = acpi_zsdt ( ssdt, signature, data,
+                                       extract ) ) == 0 )
+                       return 0;
        }
 
-       DBGC ( colour, "ACPI could not find \\_Sx \"%s\"\n",
+       DBGC ( colour, "ACPI could not find \"%s\"\n",
               acpi_name ( signature ) );
        return -ENOENT;
 }
index 81ef7ff76ecee70fd790116f74d0edd4d7356e0d..7df3ec21c60e6672e75e616209e0760151d1b335 100644 (file)
@@ -387,7 +387,9 @@ acpi_describe ( struct interface *interface );
        typeof ( struct acpi_descriptor * ( object_type ) )
 
 extern void acpi_fix_checksum ( struct acpi_header *acpi );
-extern int acpi_sx ( uint32_t signature );
+extern int acpi_extract ( uint32_t signature, void *data,
+                         int ( * extract ) ( userptr_t zsdt, size_t len,
+                                             size_t offset, void *data ) );
 extern void acpi_add ( struct acpi_descriptor *desc );
 extern void acpi_del ( struct acpi_descriptor *desc );
 extern int acpi_install ( int ( * install ) ( struct acpi_header *acpi ) );