From 739206bf2b8c6d7d0276260a82a23cc5bb1cba43 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 23 Mar 2022 15:02:17 +0000 Subject: [acpi] Support the "_RTXMAC_" format for ACPI-based MAC addresses Some newer HP products expose the host-based MAC (HBMAC) address using an ACPI method named "RTMA" returning a part-binary string of the form "_RTXMAC_##", where "" comprises the raw MAC address bytes. Extend the existing support to handle this format alongside the older "_AUXMAC_" format (which uses a base16-encoded MAC address). Signed-off-by: Michael Brown --- src/core/acpimac.c | 153 +++++++++++++++++++++++++++++++++++++++++--------- src/tests/acpi_test.c | 19 +++++++ 2 files changed, 144 insertions(+), 28 deletions(-) diff --git a/src/core/acpimac.c b/src/core/acpimac.c index 1cc8220..5920480 100644 --- a/src/core/acpimac.c +++ b/src/core/acpimac.c @@ -46,11 +46,79 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** MACA signature */ #define MACA_SIGNATURE ACPI_SIGNATURE ( 'M', 'A', 'C', 'A' ) -/** Maximum number of bytes to skip after AMAC/MACA signature +/** RTMA signature */ +#define RTMA_SIGNATURE ACPI_SIGNATURE ( 'R', 'T', 'M', 'A' ) + +/** Maximum number of bytes to skip after ACPI signature * * This is entirely empirical. */ -#define AUXMAC_MAX_SKIP 8 +#define ACPIMAC_MAX_SKIP 8 + +/** An ACPI MAC extraction mechanism */ +struct acpimac_extractor { + /** Prefix string */ + const char *prefix; + /** Encoded MAC length */ + size_t len; + /** Decode MAC + * + * @v mac Encoded MAC + * @v hw_addr MAC address to fill in + * @ret rc Return status code + */ + int ( * decode ) ( const char *mac, uint8_t *hw_addr ); +}; + +/** + * Decode Base16-encoded MAC address + * + * @v mac Encoded MAC + * @v hw_addr MAC address to fill in + * @ret rc Return status code + */ +static int acpimac_decode_base16 ( const char *mac, uint8_t *hw_addr ) { + int len; + int rc; + + /* Attempt to base16-decode MAC address */ + len = base16_decode ( mac, hw_addr, ETH_ALEN ); + if ( len < 0 ) { + rc = len; + DBGC ( colour, "ACPI could not decode base16 MAC \"%s\": %s\n", + mac, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Decode raw MAC address + * + * @v mac Encoded MAC + * @v hw_addr MAC address to fill in + * @ret rc Return status code + */ +static int acpimac_decode_raw ( const char *mac, uint8_t *hw_addr ) { + + memcpy ( hw_addr, mac, ETH_ALEN ); + return 0; +} + +/** "_AUXMAC_" extraction mechanism */ +static struct acpimac_extractor acpimac_auxmac = { + .prefix = "_AUXMAC_#", + .len = ( ETH_ALEN * 2 ), + .decode = acpimac_decode_base16, +}; + +/** "_RTXMAC_" extraction mechanism */ +static struct acpimac_extractor acpimac_rtxmac = { + .prefix = "_RTXMAC_#", + .len = ETH_ALEN, + .decode = acpimac_decode_raw, +}; /** * Extract MAC address from DSDT/SSDT @@ -59,6 +127,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v len Length of DSDT/SSDT * @v offset Offset of signature within DSDT/SSDT * @v data Data buffer + * @v extractor ACPI MAC address extractor * @ret rc Return status code * * Some vendors provide a "system MAC address" within the DSDT/SSDT, @@ -72,51 +141,44 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * string that appears shortly after an "AMAC" or "MACA" signature. * This should work for most implementations encountered in practice. */ -static int acpi_extract_mac ( userptr_t zsdt, size_t len, size_t offset, - void *data ) { - static const char prefix[9] = "_AUXMAC_#"; +static int acpimac_extract ( userptr_t zsdt, size_t len, size_t offset, + void *data, struct acpimac_extractor *extractor ){ + size_t prefix_len = strlen ( extractor->prefix ); uint8_t *hw_addr = data; size_t skip = 0; - char auxmac[ sizeof ( prefix ) /* "_AUXMAC_#" */ + - ( ETH_ALEN * 2 ) /* MAC */ + 1 /* "#" */ + 1 /* NUL */ ]; - char *mac = &auxmac[ sizeof ( prefix ) ]; - int decoded_len; + char buf[ prefix_len + extractor->len + 1 /* "#" */ + 1 /* NUL */ ]; + char *mac = &buf[prefix_len]; int rc; /* Skip signature and at least one tag byte */ offset += ( 4 /* signature */ + 1 /* tag byte */ ); - /* Scan for "_AUXMAC_#.....#" close to signature */ + /* Scan for suitable string close to signature */ for ( skip = 0 ; - ( ( skip < AUXMAC_MAX_SKIP ) && - ( offset + skip + sizeof ( auxmac ) ) < len ) ; + ( ( skip < ACPIMAC_MAX_SKIP ) && + ( offset + skip + sizeof ( buf ) ) <= len ) ; skip++ ) { /* Read value */ - copy_from_user ( auxmac, zsdt, ( offset + skip ), - sizeof ( auxmac ) ); + copy_from_user ( buf, zsdt, ( offset + skip ), + sizeof ( buf ) ); /* Check for expected format */ - if ( memcmp ( auxmac, prefix, sizeof ( prefix ) ) != 0 ) + if ( memcmp ( buf, extractor->prefix, prefix_len ) != 0 ) continue; - if ( auxmac[ sizeof ( auxmac ) - 2 ] != '#' ) + if ( buf[ sizeof ( buf ) - 2 ] != '#' ) continue; - if ( auxmac[ sizeof ( auxmac ) - 1 ] != '\0' ) + if ( buf[ sizeof ( buf ) - 1 ] != '\0' ) continue; - DBGC ( colour, "ACPI found MAC string \"%s\"\n", auxmac ); + DBGC ( colour, "ACPI found MAC:\n" ); + DBGC_HDA ( colour, ( offset + skip ), buf, sizeof ( buf ) ); /* Terminate MAC address string */ - mac = &auxmac[ sizeof ( prefix ) ]; - mac[ ETH_ALEN * 2 ] = '\0'; + mac[extractor->len] = '\0'; /* Decode MAC address */ - decoded_len = base16_decode ( mac, hw_addr, ETH_ALEN ); - if ( decoded_len < 0 ) { - rc = decoded_len; - DBGC ( colour, "ACPI could not decode MAC \"%s\": %s\n", - mac, strerror ( rc ) ); + if ( ( rc = extractor->decode ( mac, hw_addr ) ) != 0 ) return rc; - } /* Check MAC address validity */ if ( ! is_valid_ether_addr ( hw_addr ) ) { @@ -132,6 +194,36 @@ static int acpi_extract_mac ( userptr_t zsdt, size_t len, size_t offset, } /** + * Extract "_AUXMAC_" MAC address 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 + */ +static int acpimac_extract_auxmac ( userptr_t zsdt, size_t len, size_t offset, + void *data ) { + + return acpimac_extract ( zsdt, len, offset, data, &acpimac_auxmac ); +} + +/** + * Extract "_RTXMAC_" MAC address 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 + */ +static int acpimac_extract_rtxmac ( userptr_t zsdt, size_t len, size_t offset, + void *data ) { + + return acpimac_extract ( zsdt, len, offset, data, &acpimac_rtxmac ); +} + +/** * Extract MAC address from DSDT/SSDT * * @v hw_addr MAC address to fill in @@ -142,12 +234,17 @@ int acpi_mac ( uint8_t *hw_addr ) { /* Look for an "AMAC" address */ if ( ( rc = acpi_extract ( AMAC_SIGNATURE, hw_addr, - acpi_extract_mac ) ) == 0 ) + acpimac_extract_auxmac ) ) == 0 ) return 0; /* Look for a "MACA" address */ if ( ( rc = acpi_extract ( MACA_SIGNATURE, hw_addr, - acpi_extract_mac ) ) == 0 ) + acpimac_extract_auxmac ) ) == 0 ) + return 0; + + /* Look for a "RTMA" address */ + if ( ( rc = acpi_extract ( RTMA_SIGNATURE, hw_addr, + acpimac_extract_rtxmac ) ) == 0 ) return 0; return -ENOENT; diff --git a/src/tests/acpi_test.c b/src/tests/acpi_test.c index 972067e..1ca5bef 100644 --- a/src/tests/acpi_test.c +++ b/src/tests/acpi_test.c @@ -159,6 +159,24 @@ ACPI_TABLES ( maca_tables, &maca_ssdt1, &maca_ssdt2 ); ACPI_MAC ( maca, &maca_tables, DATA ( 0x52, 0x54, 0x00, 0x11, 0x22, 0x33 ) ); +/** "RTMA" SSDT */ +ACPI_TABLE ( rtma_ssdt, "SSDT", + DATA ( 0x53, 0x53, 0x44, 0x54, 0x44, 0x00, 0x00, 0x00, 0x02, + 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x4e, 0x54, 0x4c, 0x04, 0x06, 0x21, 0x20, + 0x10, 0x1f, 0x5c, 0x5f, 0x53, 0x42, 0x5f, 0x14, 0x18, + 0x52, 0x54, 0x4d, 0x41, 0x08, 0x0d, 0x5f, 0x52, 0x54, + 0x58, 0x4d, 0x41, 0x43, 0x5f, 0x23, 0x52, 0x54, 0x30, + 0x30, 0x30, 0x31, 0x23, 0x00 ) ); + +/** "RTMA" test tables */ +ACPI_TABLES ( rtma_tables, &rtma_ssdt ); + +/** "RTMA" test */ +ACPI_MAC ( rtma, &rtma_tables, + DATA ( 0x52, 0x54, 0x30, 0x30, 0x30, 0x31 ) ); + /** Current ACPI test table set */ static struct acpi_test_tables *acpi_test_tables; @@ -229,6 +247,7 @@ static void acpi_test_exec ( void ) { /* MAC extraction tests */ acpi_mac_ok ( &amac ); acpi_mac_ok ( &maca ); + acpi_mac_ok ( &rtma ); } /** ACPI self-test */ -- cgit v1.1