Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[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_#<mac>#", where "<mac>" 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).

Reported-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Tested-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Mar 25, 2022
1 parent 614c3f4 commit f58b510
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 28 deletions.
153 changes: 125 additions & 28 deletions src/core/acpimac.c
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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 ) ) {
Expand All @@ -131,6 +193,36 @@ static int acpi_extract_mac ( userptr_t zsdt, size_t len, size_t offset,
return -ENOENT;
}

/**
* 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
*
Expand All @@ -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;
Expand Down
19 changes: 19 additions & 0 deletions src/tests/acpi_test.c
Expand Up @@ -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;

Expand Down Expand Up @@ -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 */
Expand Down

0 comments on commit f58b510

Please sign in to comment.