Some systems implement a system MAC address object in the ACPI table,
using either \\_SB.AMAC or \\MACA object names. This system MAC address,
when enabled, is intended to override the permanent MAC address of the
network controller.

Implement lookup of the relevant ACPI object names and use them to
initialize the MAC address.

On systems where the feature is disabled or unsupported, the ACPI objects
do not exist or do not contain a valid Ethernet MAC, causing a fallback
to the existing MAC address initialization path.

Assisted-by: GitHub-Copilot:claude-opus-4.7
Signed-off-by: Dima Ruinskiy <[email protected]>
---
 drivers/net/ethernet/intel/igc/igc_main.c | 64 ++++++++++++++++++++++-
 1 file changed, 62 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/intel/igc/igc_main.c 
b/drivers/net/ethernet/intel/igc/igc_main.c
index 82800a4a6d6c..83eb8953e954 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -11,6 +11,8 @@
 #include <net/pkt_sched.h>
 #include <linux/bpf_trace.h>
 #include <net/xdp_sock_drv.h>
+#include <linux/acpi.h>
+#include <linux/hex.h>
 #include <linux/pci.h>
 #include <linux/mdio.h>
 
@@ -7116,6 +7118,57 @@ static enum hrtimer_restart 
igc_qbv_scheduling_timer(struct hrtimer *timer)
        return HRTIMER_NORESTART;
 }
 
+static bool igc_get_acpi_mac_passthru(u8 *mac)
+{
+       static const struct {
+               const char *name;
+               acpi_object_type type;
+               u32 length;
+       } sources[] = {
+               { "\\_SB.AMAC", ACPI_TYPE_BUFFER, 23 },
+               { "\\MACA",     ACPI_TYPE_STRING, 22 },
+       };
+       struct acpi_buffer buffer;
+       union acpi_object *obj;
+       bool mac_found = false;
+       acpi_status status;
+       u8 buf[ETH_ALEN];
+       int i;
+
+       if (!IS_ENABLED(CONFIG_ACPI))
+               return false;
+
+       for (i = 0; i < ARRAY_SIZE(sources) && !mac_found; i++) {
+               buffer.length = ACPI_ALLOCATE_BUFFER;
+               buffer.pointer = NULL;
+
+               status = acpi_evaluate_object(NULL, (char *)sources[i].name,
+                                             NULL, &buffer);
+               if (ACPI_FAILURE(status))
+                       continue;
+
+               obj = buffer.pointer;
+               if (!obj || obj->type != sources[i].type ||
+                   obj->string.length != sources[i].length)
+                       goto free_obj;
+
+               if (strncmp(obj->string.pointer, "_AUXMAC_#", 9) ||
+                   obj->string.pointer[21] != '#')
+                       goto free_obj;
+
+               if (hex2bin(buf, obj->string.pointer + 9, ETH_ALEN) ||
+                   !is_valid_ether_addr(buf))
+                       goto free_obj;
+
+               ether_addr_copy(mac, buf);
+               mac_found = true;
+free_obj:
+               kfree(obj);
+       }
+
+       return mac_found;
+}
+
 /**
  * igc_probe - Device Initialization Routine
  * @pdev: PCI device information struct
@@ -7279,9 +7332,16 @@ static int igc_probe(struct pci_dev *pdev,
        }
 
        if (eth_platform_get_mac_address(&pdev->dev, hw->mac.addr)) {
-               /* copy the MAC address out of the NVM */
-               if (hw->mac.ops.read_mac_addr(hw))
+               /* Look for a system-provided MAC in the ACPI table before
+                * falling back to reading the address from the NVM.
+                */
+               if (igc_get_acpi_mac_passthru(hw->mac.addr)) {
+                       netdev->addr_assign_type = NET_ADDR_STOLEN;
+                       dev_info(&pdev->dev, "Using ACPI pass-thru MAC addr 
%pM\n",
+                                hw->mac.addr);
+               } else if (hw->mac.ops.read_mac_addr(hw)) {
                        dev_err(&pdev->dev, "NVM Read Error\n");
+               }
        }
 
        eth_hw_addr_set(netdev, hw->mac.addr);
-- 
2.44.0

Reply via email to