]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[acpi] Support the "_RTXMAC_" format for ACPI-based MAC addresses
authorMichael Brown <mcb30@ipxe.org>
Wed, 23 Mar 2022 15:02:17 +0000 (15:02 +0000)
committerMichael Brown <mcb30@ipxe.org>
Fri, 25 Mar 2022 16:47:06 +0000 (16:47 +0000)
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>
src/core/acpimac.c
src/tests/acpi_test.c

index 1cc8220b170995b91f757658489c9db7264dbd6f..5920480ddc569acb2903cb84f866984e16e783a6 100644 (file)
@@ -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 ) ) {
@@ -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
  *
@@ -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;
index 972067ee2ab4a68cdad2428cbb50a113c6672d41..1ca5befaf27f97cb2c34da29203501163d2d7b0e 100644 (file)
@@ -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 */