diff options
author | Michael Brown <mcb30@ipxe.org> | 2021-09-08 12:53:12 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2021-09-08 14:46:30 +0100 |
commit | 02ec659b73b0998a275e79ec06a5b7d674dfad07 (patch) | |
tree | 8bcc4f8f36543cc2f3fbe88e999155c8cbc07617 /src/core | |
parent | e09e1142a3bd8bdb702efc92994c419a53e9933b (diff) | |
download | ipxe-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.c | 94 |
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; } |