aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2021-09-08 12:53:12 +0100
committerMichael Brown <mcb30@ipxe.org>2021-09-08 14:46:30 +0100
commit02ec659b73b0998a275e79ec06a5b7d674dfad07 (patch)
tree8bcc4f8f36543cc2f3fbe88e999155c8cbc07617 /src/core
parente09e1142a3bd8bdb702efc92994c419a53e9933b (diff)
downloadipxe-02ec659b73b0998a275e79ec06a5b7d674dfad07.zip
ipxe-02ec659b73b0998a275e79ec06a5b7d674dfad07.tar.gz
ipxe-02ec659b73b0998a275e79ec06a5b7d674dfad07.tar.bz2
[acpi] Generalise DSDT/SSDT data extraction logic
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>
Diffstat (limited to 'src/core')
-rw-r--r--src/core/acpi.c94
1 files changed, 30 insertions, 64 deletions
diff --git a/src/core/acpi.c b/src/core/acpi.c
index 52eb63a..aa486da 100644
--- a/src/core/acpi.c
+++ b/src/core/acpi.c
@@ -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;
}