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 ( userptr_t zsdt, size_t len, size_t offset,
146  void *data, struct acpimac_extractor *extractor ){
147  size_t prefix_len = strlen ( extractor->prefix );
148  uint8_t *hw_addr = data;
149  size_t skip = 0;
150  char buf[ prefix_len + extractor->len + 1 /* "#" */ + 1 /* NUL */ ];
151  char *mac = &buf[prefix_len];
152  int rc;
153 
154  /* Skip signature and at least one tag byte */
155  offset += ( 4 /* signature */ + 1 /* tag byte */ );
156 
157  /* Scan for suitable string close to signature */
158  for ( skip = 0 ;
159  ( ( skip < ACPIMAC_MAX_SKIP ) &&
160  ( offset + skip + sizeof ( buf ) ) <= len ) ;
161  skip++ ) {
162 
163  /* Read value */
164  copy_from_user ( buf, zsdt, ( offset + skip ),
165  sizeof ( buf ) );
166 
167  /* Check for expected format */
168  if ( memcmp ( buf, extractor->prefix, prefix_len ) != 0 )
169  continue;
170  if ( buf[ sizeof ( buf ) - 2 ] != '#' )
171  continue;
172  if ( buf[ sizeof ( buf ) - 1 ] != '\0' )
173  continue;
174  DBGC ( colour, "ACPI found MAC:\n" );
175  DBGC_HDA ( colour, ( offset + skip ), buf, sizeof ( buf ) );
176 
177  /* Terminate MAC address string */
178  mac[extractor->len] = '\0';
179 
180  /* Decode MAC address */
181  if ( ( rc = extractor->decode ( mac, hw_addr ) ) != 0 )
182  return rc;
183 
184  /* Check MAC address validity */
185  if ( ! is_valid_ether_addr ( hw_addr ) ) {
186  DBGC ( colour, "ACPI has invalid MAC %s\n",
187  eth_ntoa ( hw_addr ) );
188  return -EINVAL;
189  }
190 
191  return 0;
192  }
193 
194  return -ENOENT;
195 }
196 
197 /**
198  * Extract "_AUXMAC_" MAC address from DSDT/SSDT
199  *
200  * @v zsdt DSDT or SSDT
201  * @v len Length of DSDT/SSDT
202  * @v offset Offset of signature within DSDT/SSDT
203  * @v data Data buffer
204  * @ret rc Return status code
205  */
206 static int acpimac_extract_auxmac ( userptr_t zsdt, size_t len, size_t offset,
207  void *data ) {
208 
209  return acpimac_extract ( zsdt, len, offset, data, &acpimac_auxmac );
210 }
211 
212 /**
213  * Extract "_RTXMAC_" MAC address from DSDT/SSDT
214  *
215  * @v zsdt DSDT or SSDT
216  * @v len Length of DSDT/SSDT
217  * @v offset Offset of signature within DSDT/SSDT
218  * @v data Data buffer
219  * @ret rc Return status code
220  */
221 static int acpimac_extract_rtxmac ( userptr_t zsdt, size_t len, size_t offset,
222  void *data ) {
223 
224  return acpimac_extract ( zsdt, len, offset, data, &acpimac_rtxmac );
225 }
226 
227 /**
228  * Extract MAC address from DSDT/SSDT
229  *
230  * @v hw_addr MAC address to fill in
231  * @ret rc Return status code
232  */
233 int acpi_mac ( uint8_t *hw_addr ) {
234  int rc;
235 
236  /* Look for an "AMAC" address */
237  if ( ( rc = acpi_extract ( AMAC_SIGNATURE, hw_addr,
238  acpimac_extract_auxmac ) ) == 0 )
239  return 0;
240 
241  /* Look for a "MACA" address */
242  if ( ( rc = acpi_extract ( MACA_SIGNATURE, hw_addr,
243  acpimac_extract_auxmac ) ) == 0 )
244  return 0;
245 
246  /* Look for a "RTMA" address */
247  if ( ( rc = acpi_extract ( RTMA_SIGNATURE, hw_addr,
248  acpimac_extract_rtxmac ) ) == 0 )
249  return 0;
250 
251  return -ENOENT;
252 }
253 
254 /**
255  * Fetch system MAC address setting
256  *
257  * @v data Buffer to fill with setting data
258  * @v len Length of buffer
259  * @ret len Length of setting data, or negative error
260  */
261 static int sysmac_fetch ( void *data, size_t len ) {
263  int rc;
264 
265  /* Try fetching ACPI MAC address */
266  if ( ( rc = acpi_mac ( mac ) ) != 0 )
267  return rc;
268 
269  /* Return MAC address */
270  if ( len > sizeof ( mac ) )
271  len = sizeof ( mac );
272  memcpy ( data, mac, len );
273  return ( sizeof ( mac ) );
274 }
275 
276 /** System MAC address setting */
277 const struct setting sysmac_setting __setting ( SETTING_MISC, sysmac ) = {
278  .name = "sysmac",
279  .description = "System MAC",
280  .type = &setting_type_hex,
281  .scope = &builtin_scope,
282 };
283 
284 /** System MAC address built-in setting */
285 struct builtin_setting sysmac_builtin_setting __builtin_setting = {
286  .setting = &sysmac_setting,
287  .fetch = sysmac_fetch,
288 };
#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
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 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_auxmac(userptr_t zsdt, size_t len, size_t offset, void *data)
Extract "_AUXMAC_" MAC address from DSDT/SSDT.
Definition: acpimac.c:206
#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
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:240
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:233
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
static userptr_t size_t offset
Offset of the first segment within the content.
Definition: deflate.h:259
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
static int is_valid_ether_addr(const void *addr)
Check if Ethernet address is valid.
Definition: ethernet.h:77
static int acpimac_extract(userptr_t zsdt, size_t len, size_t offset, void *data, struct acpimac_extractor *extractor)
Extract MAC address from DSDT/SSDT.
Definition: acpimac.c:145
A setting.
Definition: settings.h:23
const struct settings_scope builtin_scope
Built-in setting scope.
Definition: settings.c:2505
uint32_t len
Length.
Definition: ena.h:14
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:285
#define RTMA_SIGNATURE
RTMA signature.
Definition: acpimac.c:51
uint8_t data[48]
Additional event data.
Definition: ena.h:22
ACPI MAC address.
static struct acpimac_extractor acpimac_rtxmac
"_RTXMAC_" extraction mechanism
Definition: acpimac.c:118
static int sysmac_fetch(void *data, size_t len)
Fetch system MAC address setting.
Definition: acpimac.c:261
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)
static int acpimac_extract_rtxmac(userptr_t zsdt, size_t len, size_t offset, void *data)
Extract "_RTXMAC_" MAC address from DSDT/SSDT.
Definition: acpimac.c:221
unsigned long userptr_t
A pointer to a user buffer.
Definition: uaccess.h:33
Base16 encoding.