1 /*-
2 * Copyright (c) 2009-2016 Solarflare Communications Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * The views and conclusions contained in the software and documentation are
27 * those of the authors and should not be interpreted as representing official
28 * policies, either expressed or implied, of the FreeBSD Project.
29 */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD: stable/10/sys/dev/sfxge/common/efx_bootcfg.c 342503 2018-12-26 10:13:42Z arybchik $");
33
34 #include "efx.h"
35 #include "efx_impl.h"
36
37 #if EFSYS_OPT_BOOTCFG
38
39 /*
40 * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE.
41 * NOTE: This is larger than the Medford per-PF bootcfg sector.
42 */
43 #define BOOTCFG_MAX_SIZE 0x1000
44
45 /* Medford per-PF bootcfg sector */
46 #define BOOTCFG_PER_PF 0x800
47 #define BOOTCFG_PF_COUNT 16
48
49 #define DHCP_END ((uint8_t)0xff)
50 #define DHCP_PAD ((uint8_t)0)
51
52
53 /* Report the layout of bootcfg sectors in NVRAM partition. */
54 __checkReturn efx_rc_t
efx_bootcfg_sector_info(__in efx_nic_t * enp,__in uint32_t pf,__out_opt uint32_t * sector_countp,__out size_t * offsetp,__out size_t * max_sizep)55 efx_bootcfg_sector_info(
56 __in efx_nic_t *enp,
57 __in uint32_t pf,
58 __out_opt uint32_t *sector_countp,
59 __out size_t *offsetp,
60 __out size_t *max_sizep)
61 {
62 uint32_t count;
63 size_t max_size;
64 size_t offset;
65 int rc;
66
67 switch (enp->en_family) {
68 #if EFSYS_OPT_SIENA
69 case EFX_FAMILY_SIENA:
70 max_size = BOOTCFG_MAX_SIZE;
71 offset = 0;
72 count = 1;
73 break;
74 #endif /* EFSYS_OPT_SIENA */
75
76 #if EFSYS_OPT_HUNTINGTON
77 case EFX_FAMILY_HUNTINGTON:
78 max_size = BOOTCFG_MAX_SIZE;
79 offset = 0;
80 count = 1;
81 break;
82 #endif /* EFSYS_OPT_HUNTINGTON */
83
84 #if EFSYS_OPT_MEDFORD
85 case EFX_FAMILY_MEDFORD: {
86 /* Shared partition (array indexed by PF) */
87 max_size = BOOTCFG_PER_PF;
88 count = BOOTCFG_PF_COUNT;
89 if (pf >= count) {
90 rc = EINVAL;
91 goto fail2;
92 }
93 offset = max_size * pf;
94 break;
95 }
96 #endif /* EFSYS_OPT_MEDFORD */
97
98 default:
99 EFSYS_ASSERT(0);
100 rc = ENOTSUP;
101 goto fail1;
102 }
103 EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE);
104
105 if (sector_countp != NULL)
106 *sector_countp = count;
107 *offsetp = offset;
108 *max_sizep = max_size;
109
110 return (0);
111
112 #if EFSYS_OPT_MEDFORD
113 fail2:
114 EFSYS_PROBE(fail2);
115 #endif
116 fail1:
117 EFSYS_PROBE1(fail1, efx_rc_t, rc);
118 return (rc);
119 }
120
121
122 static __checkReturn uint8_t
efx_bootcfg_csum(__in efx_nic_t * enp,__in_bcount (size)uint8_t const * data,__in size_t size)123 efx_bootcfg_csum(
124 __in efx_nic_t *enp,
125 __in_bcount(size) uint8_t const *data,
126 __in size_t size)
127 {
128 _NOTE(ARGUNUSED(enp))
129
130 unsigned int pos;
131 uint8_t checksum = 0;
132
133 for (pos = 0; pos < size; pos++)
134 checksum += data[pos];
135 return (checksum);
136 }
137
138 static __checkReturn efx_rc_t
efx_bootcfg_verify(__in efx_nic_t * enp,__in_bcount (size)uint8_t const * data,__in size_t size,__out_opt size_t * usedp)139 efx_bootcfg_verify(
140 __in efx_nic_t *enp,
141 __in_bcount(size) uint8_t const *data,
142 __in size_t size,
143 __out_opt size_t *usedp)
144 {
145 size_t offset = 0;
146 size_t used = 0;
147 efx_rc_t rc;
148
149 /* Start parsing tags immediatly after the checksum */
150 for (offset = 1; offset < size; ) {
151 uint8_t tag;
152 uint8_t length;
153
154 /* Consume tag */
155 tag = data[offset];
156 if (tag == DHCP_END) {
157 offset++;
158 used = offset;
159 break;
160 }
161 if (tag == DHCP_PAD) {
162 offset++;
163 continue;
164 }
165
166 /* Consume length */
167 if (offset + 1 >= size) {
168 rc = ENOSPC;
169 goto fail1;
170 }
171 length = data[offset + 1];
172
173 /* Consume *length */
174 if (offset + 1 + length >= size) {
175 rc = ENOSPC;
176 goto fail2;
177 }
178
179 offset += 2 + length;
180 used = offset;
181 }
182
183 /* Checksum the entire sector, including bytes after any DHCP_END */
184 if (efx_bootcfg_csum(enp, data, size) != 0) {
185 rc = EINVAL;
186 goto fail3;
187 }
188
189 if (usedp != NULL)
190 *usedp = used;
191
192 return (0);
193
194 fail3:
195 EFSYS_PROBE(fail3);
196 fail2:
197 EFSYS_PROBE(fail2);
198 fail1:
199 EFSYS_PROBE1(fail1, efx_rc_t, rc);
200
201 return (rc);
202 }
203
204 /*
205 * Copy bootcfg sector data to a target buffer which may differ in size.
206 * Optionally corrects format errors in source buffer.
207 */
208 efx_rc_t
efx_bootcfg_copy_sector(__in efx_nic_t * enp,__inout_bcount (sector_length)uint8_t * sector,__in size_t sector_length,__out_bcount (data_size)uint8_t * data,__in size_t data_size,__in boolean_t handle_format_errors)209 efx_bootcfg_copy_sector(
210 __in efx_nic_t *enp,
211 __inout_bcount(sector_length)
212 uint8_t *sector,
213 __in size_t sector_length,
214 __out_bcount(data_size) uint8_t *data,
215 __in size_t data_size,
216 __in boolean_t handle_format_errors)
217 {
218 size_t used_bytes;
219 efx_rc_t rc;
220
221 /* Verify that the area is correctly formatted and checksummed */
222 rc = efx_bootcfg_verify(enp, sector, sector_length,
223 &used_bytes);
224
225 if (!handle_format_errors) {
226 if (rc != 0)
227 goto fail1;
228
229 if ((used_bytes < 2) ||
230 (sector[used_bytes - 1] != DHCP_END)) {
231 /* Block too short, or DHCP_END missing */
232 rc = ENOENT;
233 goto fail2;
234 }
235 }
236
237 /* Synthesize empty format on verification failure */
238 if (rc != 0 || used_bytes == 0) {
239 sector[0] = 0;
240 sector[1] = DHCP_END;
241 used_bytes = 2;
242 }
243 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */
244 EFSYS_ASSERT(used_bytes <= sector_length);
245 EFSYS_ASSERT(sector_length >= 2);
246
247 /*
248 * Legacy bootcfg sectors don't terminate with a DHCP_END character.
249 * Modify the returned payload so it does.
250 * Reinitialise the sector if there isn't room for the character.
251 */
252 if (sector[used_bytes - 1] != DHCP_END) {
253 if (used_bytes >= sector_length) {
254 sector[0] = 0;
255 used_bytes = 1;
256 }
257 sector[used_bytes] = DHCP_END;
258 ++used_bytes;
259 }
260
261 /*
262 * Verify that the target buffer is large enough for the
263 * entire used bootcfg area, then copy into the target buffer.
264 */
265 if (used_bytes > data_size) {
266 rc = ENOSPC;
267 goto fail3;
268 }
269 memcpy(data, sector, used_bytes);
270
271 /* Zero out the unused portion of the target buffer */
272 if (used_bytes < data_size)
273 (void) memset(data + used_bytes, 0, data_size - used_bytes);
274
275 /*
276 * The checksum includes trailing data after any DHCP_END character,
277 * which we've just modified (by truncation or appending DHCP_END).
278 */
279 data[0] -= efx_bootcfg_csum(enp, data, data_size);
280
281 return (0);
282
283 fail3:
284 EFSYS_PROBE(fail3);
285 fail2:
286 EFSYS_PROBE(fail2);
287 fail1:
288 EFSYS_PROBE1(fail1, efx_rc_t, rc);
289
290 return (rc);
291 }
292
293 efx_rc_t
efx_bootcfg_read(__in efx_nic_t * enp,__out_bcount (size)uint8_t * data,__in size_t size)294 efx_bootcfg_read(
295 __in efx_nic_t *enp,
296 __out_bcount(size) uint8_t *data,
297 __in size_t size)
298 {
299 uint8_t *payload = NULL;
300 size_t used_bytes;
301 size_t partn_length;
302 size_t sector_length;
303 size_t sector_offset;
304 efx_rc_t rc;
305 uint32_t sector_number;
306
307 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
308 sector_number = enp->en_nic_cfg.enc_pf;
309 #else
310 sector_number = 0;
311 #endif
312 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
313 if (rc != 0)
314 goto fail1;
315
316 /* The bootcfg sector may be stored in a (larger) shared partition */
317 rc = efx_bootcfg_sector_info(enp, sector_number,
318 NULL, §or_offset, §or_length);
319 if (rc != 0)
320 goto fail2;
321
322 if (sector_length > BOOTCFG_MAX_SIZE)
323 sector_length = BOOTCFG_MAX_SIZE;
324
325 if (sector_offset + sector_length > partn_length) {
326 /* Partition is too small */
327 rc = EFBIG;
328 goto fail3;
329 }
330
331 /*
332 * We need to read the entire BOOTCFG sector to ensure we read all the
333 * tags, because legacy bootcfg sectors are not guaranteed to end with
334 * a DHCP_END character. If the user hasn't supplied a sufficiently
335 * large buffer then use our own buffer.
336 */
337 if (sector_length > size) {
338 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload);
339 if (payload == NULL) {
340 rc = ENOMEM;
341 goto fail4;
342 }
343 } else
344 payload = (uint8_t *)data;
345
346 if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
347 goto fail5;
348
349 if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
350 sector_offset, (caddr_t)payload, sector_length)) != 0) {
351 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG);
352 goto fail6;
353 }
354
355 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
356 goto fail7;
357
358 /* Verify that the area is correctly formatted and checksummed */
359 rc = efx_bootcfg_verify(enp, payload, sector_length,
360 &used_bytes);
361 if (rc != 0 || used_bytes == 0) {
362 payload[0] = (uint8_t)(~DHCP_END & 0xff);
363 payload[1] = DHCP_END;
364 used_bytes = 2;
365 }
366
367 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */
368 EFSYS_ASSERT(used_bytes <= sector_length);
369
370 /*
371 * Legacy bootcfg sectors don't terminate with a DHCP_END character.
372 * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by
373 * definition large enough for any valid (per-port) bootcfg sector,
374 * so reinitialise the sector if there isn't room for the character.
375 */
376 if (payload[used_bytes - 1] != DHCP_END) {
377 if (used_bytes + 1 > sector_length) {
378 payload[0] = 0;
379 used_bytes = 1;
380 }
381
382 payload[used_bytes] = DHCP_END;
383 ++used_bytes;
384 }
385
386 /*
387 * Verify that the user supplied buffer is large enough for the
388 * entire used bootcfg area, then copy into the user supplied buffer.
389 */
390 if (used_bytes > size) {
391 rc = ENOSPC;
392 goto fail8;
393 }
394 if (sector_length > size) {
395 memcpy(data, payload, used_bytes);
396 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
397 }
398
399 /* Zero out the unused portion of the user buffer */
400 if (used_bytes < size)
401 (void) memset(data + used_bytes, 0, size - used_bytes);
402
403 /*
404 * The checksum includes trailing data after any DHCP_END character,
405 * which we've just modified (by truncation or appending DHCP_END).
406 */
407 data[0] -= efx_bootcfg_csum(enp, data, size);
408
409 return (0);
410
411 fail8:
412 EFSYS_PROBE(fail8);
413 fail7:
414 EFSYS_PROBE(fail7);
415 fail6:
416 EFSYS_PROBE(fail6);
417 fail5:
418 EFSYS_PROBE(fail5);
419 if (sector_length > size)
420 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
421 fail4:
422 EFSYS_PROBE(fail4);
423 fail3:
424 EFSYS_PROBE(fail3);
425 fail2:
426 EFSYS_PROBE(fail2);
427 fail1:
428 EFSYS_PROBE1(fail1, efx_rc_t, rc);
429
430 return (rc);
431 }
432
433 efx_rc_t
efx_bootcfg_write(__in efx_nic_t * enp,__in_bcount (size)uint8_t * data,__in size_t size)434 efx_bootcfg_write(
435 __in efx_nic_t *enp,
436 __in_bcount(size) uint8_t *data,
437 __in size_t size)
438 {
439 uint8_t *partn_data;
440 uint8_t checksum;
441 size_t partn_length;
442 size_t sector_length;
443 size_t sector_offset;
444 size_t used_bytes;
445 efx_rc_t rc;
446 uint32_t sector_number;
447
448 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
449 sector_number = enp->en_nic_cfg.enc_pf;
450 #else
451 sector_number = 0;
452 #endif
453
454 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
455 if (rc != 0)
456 goto fail1;
457
458 /* The bootcfg sector may be stored in a (larger) shared partition */
459 rc = efx_bootcfg_sector_info(enp, sector_number,
460 NULL, §or_offset, §or_length);
461 if (rc != 0)
462 goto fail2;
463
464 if (sector_length > BOOTCFG_MAX_SIZE)
465 sector_length = BOOTCFG_MAX_SIZE;
466
467 if (sector_offset + sector_length > partn_length) {
468 /* Partition is too small */
469 rc = EFBIG;
470 goto fail3;
471 }
472
473 if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0)
474 goto fail4;
475
476 /* The caller *must* terminate their block with a DHCP_END character */
477 if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) {
478 /* Block too short or DHCP_END missing */
479 rc = ENOENT;
480 goto fail5;
481 }
482
483 /* Check that the hardware has support for this much data */
484 if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) {
485 rc = ENOSPC;
486 goto fail6;
487 }
488
489 /*
490 * If the BOOTCFG sector is stored in a shared partition, then we must
491 * read the whole partition and insert the updated bootcfg sector at the
492 * correct offset.
493 */
494 EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data);
495 if (partn_data == NULL) {
496 rc = ENOMEM;
497 goto fail7;
498 }
499
500 rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
501 if (rc != 0)
502 goto fail8;
503
504 /* Read the entire partition */
505 rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0,
506 (caddr_t)partn_data, partn_length);
507 if (rc != 0)
508 goto fail9;
509
510 /*
511 * Insert the BOOTCFG sector into the partition, Zero out all data after
512 * the DHCP_END tag, and adjust the checksum.
513 */
514 (void) memset(partn_data + sector_offset, 0x0, sector_length);
515 (void) memcpy(partn_data + sector_offset, data, used_bytes);
516
517 checksum = efx_bootcfg_csum(enp, data, used_bytes);
518 partn_data[sector_offset] -= checksum;
519
520 if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
521 goto fail10;
522
523 if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
524 0, (caddr_t)partn_data, partn_length)) != 0)
525 goto fail11;
526
527 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
528 goto fail12;
529
530 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
531
532 return (0);
533
534 fail12:
535 EFSYS_PROBE(fail12);
536 fail11:
537 EFSYS_PROBE(fail11);
538 fail10:
539 EFSYS_PROBE(fail10);
540 fail9:
541 EFSYS_PROBE(fail9);
542
543 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG);
544 fail8:
545 EFSYS_PROBE(fail8);
546
547 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
548 fail7:
549 EFSYS_PROBE(fail7);
550 fail6:
551 EFSYS_PROBE(fail6);
552 fail5:
553 EFSYS_PROBE(fail5);
554 fail4:
555 EFSYS_PROBE(fail4);
556 fail3:
557 EFSYS_PROBE(fail3);
558 fail2:
559 EFSYS_PROBE(fail2);
560 fail1:
561 EFSYS_PROBE1(fail1, efx_rc_t, rc);
562
563 return (rc);
564 }
565
566 #endif /* EFSYS_OPT_BOOTCFG */
567