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
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25FILE_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 */
82static 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 */
105static 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 */
146static 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 */
208static 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 */
223static 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 */
235int 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 */
263static 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 */
279const 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 */
287struct builtin_setting sysmac_builtin_setting __builtin_setting = {
288 .setting = &sysmac_setting,
289 .fetch = sysmac_fetch,
290};
#define colour
Colour for debug messages.
Definition acpi.c:42
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
#define RTMA_SIGNATURE
RTMA signature.
Definition acpimac.c:52
static struct acpimac_extractor acpimac_auxmac
"_AUXMAC_" extraction mechanism
Definition acpimac.c:112
static int acpimac_decode_raw(const char *mac, uint8_t *hw_addr)
Decode raw MAC address.
Definition acpimac.c:105
#define ACPIMAC_MAX_SKIP
Maximum number of bytes to skip after ACPI signature.
Definition acpimac.c:58
static int acpimac_decode_base16(const char *mac, uint8_t *hw_addr)
Decode Base16-encoded MAC address.
Definition acpimac.c:82
#define AMAC_SIGNATURE
AMAC signature.
Definition acpimac.c:46
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
static int sysmac_fetch(void *data, size_t len)
Fetch system MAC address setting.
Definition acpimac.c:263
int acpi_mac(uint8_t *hw_addr)
Extract MAC address from DSDT/SSDT.
Definition acpimac.c:235
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
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
#define MACA_SIGNATURE
MACA signature.
Definition acpimac.c:49
ACPI MAC address.
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
unsigned char uint8_t
Definition stdint.h:10
Base16 encoding.
uint16_t offset
Offset to command line.
Definition bzimage.h:3
ring len
Length.
Definition dwmac.h:226
uint8_t data[48]
Additional event data.
Definition ena.h:11
uint8_t mac[ETH_ALEN]
MAC address.
Definition ena.h:13
Error codes.
const char * eth_ntoa(const void *ll_addr)
Transcribe Ethernet address.
Definition ethernet.c:176
Ethernet protocol.
static int is_valid_ether_addr(const void *addr)
Check if Ethernet address is valid.
Definition ethernet.h:78
#define DBGC(...)
Definition compiler.h:505
#define DBGC_HDA(...)
Definition compiler.h:506
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define ENOENT
No such file or directory.
Definition errno.h:515
#define EINVAL
Invalid argument.
Definition errno.h:429
#define FILE_SECBOOT(_status)
Declare a file's UEFI Secure Boot permission status.
Definition compiler.h:926
#define SETTING_MISC
Miscellaneous settings.
Definition settings.h:81
#define ETH_ALEN
Definition if_ether.h:9
ACPI data structures.
Configuration settings.
#define __setting(setting_order, name)
Declare a configuration setting.
Definition settings.h:57
#define __builtin_setting
Declare a built-in setting.
Definition settings.h:284
String functions.
void * memcpy(void *dest, const void *src, size_t len) __nonnull
const struct settings_scope builtin_scope
Built-in setting scope.
Definition settings.c:2506
char * strerror(int errno)
Retrieve string representation of error number.
Definition strerror.c:79
int memcmp(const void *first, const void *second, size_t len)
Compare memory regions.
Definition string.c:115
size_t strlen(const char *src)
Get length of string.
Definition string.c:244
An ACPI description header.
Definition acpi.h:180
An ACPI MAC extraction mechanism.
Definition acpimac.c:61
const char * prefix
Prefix string.
Definition acpimac.c:63
size_t len
Encoded MAC length.
Definition acpimac.c:65
int(* decode)(const char *mac, uint8_t *hw_addr)
Decode MAC.
Definition acpimac.c:72
A built-in setting.
Definition settings.h:268
A setting.
Definition settings.h:24