iPXE
acpimac.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <string.h>
27 #include <errno.h>
28 #include <ipxe/acpi.h>
29 #include <ipxe/base16.h>
30 #include <ipxe/ethernet.h>
31 #include <ipxe/if_ether.h>
32 #include <ipxe/acpimac.h>
33 
34 /** @file
35  *
36  * ACPI MAC address
37  *
38  */
39 
40 /** Colour for debug messages */
41 #define colour FADT_SIGNATURE
42 
43 /** AMAC signature */
44 #define AMAC_SIGNATURE ACPI_SIGNATURE ( 'A', 'M', 'A', 'C' )
45 
46 /** MACA signature */
47 #define MACA_SIGNATURE ACPI_SIGNATURE ( 'M', 'A', 'C', 'A' )
48 
49 /** Maximum number of bytes to skip after AMAC/MACA signature
50  *
51  * This is entirely empirical.
52  */
53 #define AUXMAC_MAX_SKIP 8
54 
55 /**
56  * Extract MAC address from DSDT/SSDT
57  *
58  * @v zsdt DSDT or SSDT
59  * @v len Length of DSDT/SSDT
60  * @v offset Offset of signature within DSDT/SSDT
61  * @v data Data buffer
62  * @ret rc Return status code
63  *
64  * Some vendors provide a "system MAC address" within the DSDT/SSDT,
65  * to be used to override the MAC address for a USB docking station.
66  *
67  * A full implementation would require an ACPI bytecode interpreter,
68  * since at least one OEM allows the MAC address to be constructed by
69  * executable ACPI bytecode (rather than a fixed data structure).
70  *
71  * We instead attempt to extract a plausible-looking "_AUXMAC_#.....#"
72  * string that appears shortly after an "AMAC" or "MACA" signature.
73  * This should work for most implementations encountered in practice.
74  */
75 static int acpi_extract_mac ( userptr_t zsdt, size_t len, size_t offset,
76  void *data ) {
77  static const char prefix[9] = "_AUXMAC_#";
78  uint8_t *hw_addr = data;
79  size_t skip = 0;
80  char auxmac[ sizeof ( prefix ) /* "_AUXMAC_#" */ +
81  ( ETH_ALEN * 2 ) /* MAC */ + 1 /* "#" */ + 1 /* NUL */ ];
82  char *mac = &auxmac[ sizeof ( prefix ) ];
83  int decoded_len;
84  int rc;
85 
86  /* Skip signature and at least one tag byte */
87  offset += ( 4 /* signature */ + 1 /* tag byte */ );
88 
89  /* Scan for "_AUXMAC_#.....#" close to signature */
90  for ( skip = 0 ;
91  ( ( skip < AUXMAC_MAX_SKIP ) &&
92  ( offset + skip + sizeof ( auxmac ) ) < len ) ;
93  skip++ ) {
94 
95  /* Read value */
96  copy_from_user ( auxmac, zsdt, ( offset + skip ),
97  sizeof ( auxmac ) );
98 
99  /* Check for expected format */
100  if ( memcmp ( auxmac, prefix, sizeof ( prefix ) ) != 0 )
101  continue;
102  if ( auxmac[ sizeof ( auxmac ) - 2 ] != '#' )
103  continue;
104  if ( auxmac[ sizeof ( auxmac ) - 1 ] != '\0' )
105  continue;
106  DBGC ( colour, "ACPI found MAC string \"%s\"\n", auxmac );
107 
108  /* Terminate MAC address string */
109  mac = &auxmac[ sizeof ( prefix ) ];
110  mac[ ETH_ALEN * 2 ] = '\0';
111 
112  /* Decode MAC address */
113  decoded_len = base16_decode ( mac, hw_addr, ETH_ALEN );
114  if ( decoded_len < 0 ) {
115  rc = decoded_len;
116  DBGC ( colour, "ACPI could not decode MAC \"%s\": %s\n",
117  mac, strerror ( rc ) );
118  return rc;
119  }
120 
121  /* Check MAC address validity */
122  if ( ! is_valid_ether_addr ( hw_addr ) ) {
123  DBGC ( colour, "ACPI has invalid MAC %s\n",
124  eth_ntoa ( hw_addr ) );
125  return -EINVAL;
126  }
127 
128  return 0;
129  }
130 
131  return -ENOENT;
132 }
133 
134 /**
135  * Extract MAC address from DSDT/SSDT
136  *
137  * @v hw_addr MAC address to fill in
138  * @ret rc Return status code
139  */
140 int acpi_mac ( uint8_t *hw_addr ) {
141  int rc;
142 
143  /* Look for an "AMAC" address */
144  if ( ( rc = acpi_extract ( AMAC_SIGNATURE, hw_addr,
145  acpi_extract_mac ) ) == 0 )
146  return 0;
147 
148  /* Look for a "MACA" address */
149  if ( ( rc = acpi_extract ( MACA_SIGNATURE, hw_addr,
150  acpi_extract_mac ) ) == 0 )
151  return 0;
152 
153  return -ENOENT;
154 }
#define EINVAL
Invalid argument.
Definition: errno.h:428
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
Error codes.
static __always_inline void copy_from_user(void *dest, userptr_t src, off_t src_off, size_t len)
Copy data from user buffer.
Definition: uaccess.h:337
#define MACA_SIGNATURE
MACA signature.
Definition: acpimac.c:47
#define DBGC(...)
Definition: compiler.h:505
#define AMAC_SIGNATURE
AMAC signature.
Definition: acpimac.c:44
#define ENOENT
No such file or directory.
Definition: errno.h:514
char prefix[4]
Definition: vmconsole.c:53
uint8_t mac[ETH_ALEN]
MAC address.
Definition: ena.h:24
#define AUXMAC_MAX_SKIP
Maximum number of bytes to skip after AMAC/MACA signature.
Definition: acpimac.c:53
int acpi_extract(uint32_t signature, void *data, int(*extract)(userptr_t zsdt, size_t len, size_t offset, void *data))
Extract value from DSDT/SSDT.
Definition: acpi.c:222
int acpi_mac(uint8_t *hw_addr)
Extract MAC address from DSDT/SSDT.
Definition: acpimac.c:140
Ethernet protocol.
static userptr_t size_t offset
Offset of the first segment within the content.
Definition: deflate.h:259
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
ACPI data structures.
#define colour
Colour for debug messages.
Definition: acpimac.c:41
const char * eth_ntoa(const void *ll_addr)
Transcribe Ethernet address.
Definition: ethernet.c:175
unsigned char uint8_t
Definition: stdint.h:10
#define ETH_ALEN
Definition: if_ether.h:8
static int is_valid_ether_addr(const void *addr)
Check if Ethernet address is valid.
Definition: ethernet.h:77
uint32_t len
Length.
Definition: ena.h:14
static int acpi_extract_mac(userptr_t zsdt, size_t len, size_t offset, void *data)
Extract MAC address from DSDT/SSDT.
Definition: acpimac.c:75
ACPI MAC address.
struct arbelprm_port_state_change_st data
Message.
Definition: arbel.h:12
int memcmp(const void *first, const void *second, size_t len)
Compare memory regions.
Definition: string.c:114
String functions.
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
unsigned long userptr_t
A pointer to a user buffer.
Definition: uaccess.h:33
Base16 encoding.