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