[intelxl] Explicitly request a single queue pair for virtual functions
[ipxe.git] / src / core / acpimac.c
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 */
60 struct acpimac_extractor {
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 */
111 static struct acpimac_extractor acpimac_auxmac = {
112 .prefix = "_AUXMAC_#",
113 .len = ( ETH_ALEN * 2 ),
114 .decode = acpimac_decode_base16,
115 };
116
117 /** "_RTXMAC_" extraction mechanism */
118 static struct acpimac_extractor acpimac_rtxmac = {
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 ) {
262 uint8_t mac[ETH_ALEN];
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 };