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