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 FILE_SECBOOT ( PERMITTED );
26 
27 #include <string.h>
28 #include <errno.h>
29 #include <ipxe/acpi.h>
30 #include <ipxe/base16.h>
31 #include <ipxe/ethernet.h>
32 #include <ipxe/if_ether.h>
33 #include <ipxe/settings.h>
34 #include <ipxe/acpimac.h>
35 
36 /** @file
37  *
38  * ACPI MAC address
39  *
40  */
41 
42 /** Colour for debug messages */
43 #define colour FADT_SIGNATURE
44 
45 /** AMAC signature */
46 #define AMAC_SIGNATURE ACPI_SIGNATURE ( 'A', 'M', 'A', 'C' )
47 
48 /** MACA signature */
49 #define MACA_SIGNATURE ACPI_SIGNATURE ( 'M', 'A', 'C', 'A' )
50 
51 /** RTMA signature */
52 #define RTMA_SIGNATURE ACPI_SIGNATURE ( 'R', 'T', 'M', 'A' )
53 
54 /** Maximum number of bytes to skip after ACPI signature
55  *
56  * This is entirely empirical.
57  */
58 #define ACPIMAC_MAX_SKIP 8
59 
60 /** An ACPI MAC extraction mechanism */
62  /** Prefix string */
63  const char *prefix;
64  /** Encoded MAC length */
65  size_t len;
66  /** Decode MAC
67  *
68  * @v mac Encoded MAC
69  * @v hw_addr MAC address to fill in
70  * @ret rc Return status code
71  */
72  int ( * decode ) ( const char *mac, uint8_t *hw_addr );
73 };
74 
75 /**
76  * Decode Base16-encoded MAC address
77  *
78  * @v mac Encoded MAC
79  * @v hw_addr MAC address to fill in
80  * @ret rc Return status code
81  */
82 static int acpimac_decode_base16 ( const char *mac, uint8_t *hw_addr ) {
83  int len;
84  int rc;
85 
86  /* Attempt to base16-decode MAC address */
87  len = base16_decode ( mac, hw_addr, ETH_ALEN );
88  if ( len < 0 ) {
89  rc = len;
90  DBGC ( colour, "ACPI could not decode base16 MAC \"%s\": %s\n",
91  mac, strerror ( rc ) );
92  return rc;
93  }
94 
95  return 0;
96 }
97 
98 /**
99  * Decode raw MAC address
100  *
101  * @v mac Encoded MAC
102  * @v hw_addr MAC address to fill in
103  * @ret rc Return status code
104  */
105 static int acpimac_decode_raw ( const char *mac, uint8_t *hw_addr ) {
106 
107  memcpy ( hw_addr, mac, ETH_ALEN );
108  return 0;
109 }
110 
111 /** "_AUXMAC_" extraction mechanism */
113  .prefix = "_AUXMAC_#",
114  .len = ( ETH_ALEN * 2 ),
116 };
117 
118 /** "_RTXMAC_" extraction mechanism */
120  .prefix = "_RTXMAC_#",
121  .len = ETH_ALEN,
122  .decode = acpimac_decode_raw,
123 };
124 
125 /**
126  * Extract MAC address from DSDT/SSDT
127  *
128  * @v zsdt DSDT or SSDT
129  * @v len Length of DSDT/SSDT
130  * @v offset Offset of signature within DSDT/SSDT
131  * @v data Data buffer
132  * @v extractor ACPI MAC address extractor
133  * @ret rc Return status code
134  *
135  * Some vendors provide a "system MAC address" within the DSDT/SSDT,
136  * to be used to override the MAC address for a USB docking station.
137  *
138  * A full implementation would require an ACPI bytecode interpreter,
139  * since at least one OEM allows the MAC address to be constructed by
140  * executable ACPI bytecode (rather than a fixed data structure).
141  *
142  * We instead attempt to extract a plausible-looking "_AUXMAC_#.....#"
143  * string that appears shortly after an "AMAC" or "MACA" signature.
144  * This should work for most implementations encountered in practice.
145  */
146 static int acpimac_extract ( const struct acpi_header *zsdt, size_t len,
147  size_t offset, void *data,
148  struct acpimac_extractor *extractor ) {
149  size_t prefix_len = strlen ( extractor->prefix );
150  uint8_t *hw_addr = data;
151  size_t skip = 0;
152  char buf[ prefix_len + extractor->len + 1 /* "#" */ + 1 /* NUL */ ];
153  char *mac = &buf[prefix_len];
154  int rc;
155 
156  /* Skip signature and at least one tag byte */
157  offset += ( 4 /* signature */ + 1 /* tag byte */ );
158 
159  /* Scan for suitable string close to signature */
160  for ( skip = 0 ;
161  ( ( skip < ACPIMAC_MAX_SKIP ) &&
162  ( offset + skip + sizeof ( buf ) ) <= len ) ;
163  skip++ ) {
164 
165  /* Read value */
166  memcpy ( buf, ( ( ( const void * ) zsdt ) + offset + skip ),
167  sizeof ( buf ) );
168 
169  /* Check for expected format */
170  if ( memcmp ( buf, extractor->prefix, prefix_len ) != 0 )
171  continue;
172  if ( buf[ sizeof ( buf ) - 2 ] != '#' )
173  continue;
174  if ( buf[ sizeof ( buf ) - 1 ] != '\0' )
175  continue;
176  DBGC ( colour, "ACPI found MAC:\n" );
177  DBGC_HDA ( colour, ( offset + skip ), buf, sizeof ( buf ) );
178 
179  /* Terminate MAC address string */
180  mac[extractor->len] = '\0';
181 
182  /* Decode MAC address */
183  if ( ( rc = extractor->decode ( mac, hw_addr ) ) != 0 )
184  return rc;
185 
186  /* Check MAC address validity */
187  if ( ! is_valid_ether_addr ( hw_addr ) ) {
188  DBGC ( colour, "ACPI has invalid MAC %s\n",
189  eth_ntoa ( hw_addr ) );
190  return -EINVAL;
191  }
192 
193  return 0;
194  }
195 
196  return -ENOENT;
197 }
198 
199 /**
200  * Extract "_AUXMAC_" MAC address from DSDT/SSDT
201  *
202  * @v zsdt DSDT or SSDT
203  * @v len Length of DSDT/SSDT
204  * @v offset Offset of signature within DSDT/SSDT
205  * @v data Data buffer
206  * @ret rc Return status code
207  */
208 static int acpimac_extract_auxmac ( const struct acpi_header *zsdt,
209  size_t len, size_t offset, void *data ) {
210 
211  return acpimac_extract ( zsdt, len, offset, data, &acpimac_auxmac );
212 }
213 
214 /**
215  * Extract "_RTXMAC_" MAC address from DSDT/SSDT
216  *
217  * @v zsdt DSDT or SSDT
218  * @v len Length of DSDT/SSDT
219  * @v offset Offset of signature within DSDT/SSDT
220  * @v data Data buffer
221  * @ret rc Return status code
222  */
223 static int acpimac_extract_rtxmac ( const struct acpi_header *zsdt,
224  size_t len, size_t offset, void *data ) {
225 
226  return acpimac_extract ( zsdt, len, offset, data, &acpimac_rtxmac );
227 }
228 
229 /**
230  * Extract MAC address from DSDT/SSDT
231  *
232  * @v hw_addr MAC address to fill in
233  * @ret rc Return status code
234  */
235 int acpi_mac ( uint8_t *hw_addr ) {
236  int rc;
237 
238  /* Look for an "AMAC" address */
239  if ( ( rc = acpi_extract ( AMAC_SIGNATURE, hw_addr,
240  acpimac_extract_auxmac ) ) == 0 )
241  return 0;
242 
243  /* Look for a "MACA" address */
244  if ( ( rc = acpi_extract ( MACA_SIGNATURE, hw_addr,
245  acpimac_extract_auxmac ) ) == 0 )
246  return 0;
247 
248  /* Look for a "RTMA" address */
249  if ( ( rc = acpi_extract ( RTMA_SIGNATURE, hw_addr,
250  acpimac_extract_rtxmac ) ) == 0 )
251  return 0;
252 
253  return -ENOENT;
254 }
255 
256 /**
257  * Fetch system MAC address setting
258  *
259  * @v data Buffer to fill with setting data
260  * @v len Length of buffer
261  * @ret len Length of setting data, or negative error
262  */
263 static int sysmac_fetch ( void *data, size_t len ) {
265  int rc;
266 
267  /* Try fetching ACPI MAC address */
268  if ( ( rc = acpi_mac ( mac ) ) != 0 )
269  return rc;
270 
271  /* Return MAC address */
272  if ( len > sizeof ( mac ) )
273  len = sizeof ( mac );
274  memcpy ( data, mac, len );
275  return ( sizeof ( mac ) );
276 }
277 
278 /** System MAC address setting */
279 const struct setting sysmac_setting __setting ( SETTING_MISC, sysmac ) = {
280  .name = "sysmac",
281  .description = "System MAC",
282  .type = &setting_type_hex,
283  .scope = &builtin_scope,
284 };
285 
286 /** System MAC address built-in setting */
287 struct builtin_setting sysmac_builtin_setting __builtin_setting = {
288  .setting = &sysmac_setting,
289  .fetch = sysmac_fetch,
290 };
#define EINVAL
Invalid argument.
Definition: errno.h:429
FILE_SECBOOT(PERMITTED)
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
const char * prefix
Prefix string.
Definition: acpimac.c:63
static int acpimac_extract_rtxmac(const struct acpi_header *zsdt, size_t len, size_t offset, void *data)
Extract "_RTXMAC_" MAC address from DSDT/SSDT.
Definition: acpimac.c:223
Error codes.
static int acpimac_extract_auxmac(const struct acpi_header *zsdt, size_t len, size_t offset, void *data)
Extract "_AUXMAC_" MAC address from DSDT/SSDT.
Definition: acpimac.c:208
#define ACPIMAC_MAX_SKIP
Maximum number of bytes to skip after ACPI signature.
Definition: acpimac.c:58
#define MACA_SIGNATURE
MACA signature.
Definition: acpimac.c:49
#define DBGC(...)
Definition: compiler.h:505
#define AMAC_SIGNATURE
AMAC signature.
Definition: acpimac.c:46
#define ENOENT
No such file or directory.
Definition: errno.h:515
static int acpimac_extract(const struct acpi_header *zsdt, size_t len, size_t offset, void *data, struct acpimac_extractor *extractor)
Extract MAC address from DSDT/SSDT.
Definition: acpimac.c:146
#define SETTING_MISC
Miscellaneous settings.
Definition: settings.h:81
uint8_t mac[ETH_ALEN]
MAC address.
Definition: ena.h:24
const struct setting sysmac_setting __setting(SETTING_MISC, sysmac)
System MAC address setting.
int(* decode)(const char *mac, uint8_t *hw_addr)
Decode MAC.
Definition: acpimac.c:72
const char * name
Name.
Definition: settings.h:29
void * memcpy(void *dest, const void *src, size_t len) __nonnull
int acpi_mac(uint8_t *hw_addr)
Extract MAC address from DSDT/SSDT.
Definition: acpimac.c:235
const struct setting * setting
Setting.
Definition: settings.h:270
Ethernet protocol.
static int acpimac_decode_base16(const char *mac, uint8_t *hw_addr)
Decode Base16-encoded MAC address.
Definition: acpimac.c:82
#define DBGC_HDA(...)
Definition: compiler.h:506
ring len
Length.
Definition: dwmac.h:231
Configuration settings.
static int acpimac_decode_raw(const char *mac, uint8_t *hw_addr)
Decode raw MAC address.
Definition: acpimac.c:105
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:79
ACPI data structures.
#define colour
Colour for debug messages.
Definition: acpimac.c:43
An ACPI MAC extraction mechanism.
Definition: acpimac.c:61
const char * eth_ntoa(const void *ll_addr)
Transcribe Ethernet address.
Definition: ethernet.c:176
A built-in setting.
Definition: settings.h:268
size_t strlen(const char *src)
Get length of string.
Definition: string.c:244
unsigned char uint8_t
Definition: stdint.h:10
size_t len
Encoded MAC length.
Definition: acpimac.c:65
#define ETH_ALEN
Definition: if_ether.h:9
An ACPI description header.
Definition: acpi.h:180
static int is_valid_ether_addr(const void *addr)
Check if Ethernet address is valid.
Definition: ethernet.h:78
A setting.
Definition: settings.h:24
const struct settings_scope builtin_scope
Built-in setting scope.
Definition: settings.c:2506
static struct acpimac_extractor acpimac_auxmac
"_AUXMAC_" extraction mechanism
Definition: acpimac.c:112
struct builtin_setting sysmac_builtin_setting __builtin_setting
System MAC address built-in setting.
Definition: acpimac.c:287
#define RTMA_SIGNATURE
RTMA signature.
Definition: acpimac.c:52
uint8_t data[48]
Additional event data.
Definition: ena.h:22
ACPI MAC address.
int acpi_extract(uint32_t signature, void *data, int(*extract)(const struct acpi_header *zsdt, size_t len, size_t offset, void *data))
Extract value from DSDT/SSDT.
Definition: acpi.c:228
static struct acpimac_extractor acpimac_rtxmac
"_RTXMAC_" extraction mechanism
Definition: acpimac.c:119
uint16_t offset
Offset to command line.
Definition: bzimage.h:8
static int sysmac_fetch(void *data, size_t len)
Fetch system MAC address setting.
Definition: acpimac.c:263
int memcmp(const void *first, const void *second, size_t len)
Compare memory regions.
Definition: string.c:115
String functions.
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
Base16 encoding.