]> git.proxmox.com Git - ceph.git/blame - ceph/src/spdk/dpdk/drivers/net/sfc/base/efx_bootcfg.c
import 15.2.0 Octopus source
[ceph.git] / ceph / src / spdk / dpdk / drivers / net / sfc / base / efx_bootcfg.c
CommitLineData
11fdf7f2
TL
1/* SPDX-License-Identifier: BSD-3-Clause
2 *
3 * Copyright (c) 2009-2018 Solarflare Communications Inc.
4 * All rights reserved.
5 */
6
7#include "efx.h"
8#include "efx_impl.h"
9
10#if EFSYS_OPT_BOOTCFG
11
12/*
13 * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE.
14 * NOTE: This is larger than the Medford per-PF bootcfg sector.
15 */
16#define BOOTCFG_MAX_SIZE 0x1000
17
18/* Medford per-PF bootcfg sector */
19#define BOOTCFG_PER_PF 0x800
20#define BOOTCFG_PF_COUNT 16
21
9f95a23c
TL
22#define DHCP_OPT_HAS_VALUE(opt) \
23 (((opt) > EFX_DHCP_PAD) && ((opt) < EFX_DHCP_END))
24
25#define DHCP_MAX_VALUE 255
26
27#define DHCP_ENCAPSULATOR(encap_opt) ((encap_opt) >> 8)
28#define DHCP_ENCAPSULATED(encap_opt) ((encap_opt) & 0xff)
29#define DHCP_IS_ENCAP_OPT(opt) DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATOR(opt))
30
31typedef struct efx_dhcp_tag_hdr_s {
32 uint8_t tag;
33 uint8_t length;
34} efx_dhcp_tag_hdr_t;
35
36/*
37 * Length calculations for tags with value field. PAD and END
38 * have a fixed length of 1, with no length or value field.
39 */
40#define DHCP_FULL_TAG_LENGTH(hdr) \
41 (sizeof (efx_dhcp_tag_hdr_t) + (hdr)->length)
42
43#define DHCP_NEXT_TAG(hdr) \
44 ((efx_dhcp_tag_hdr_t *)(((uint8_t *)(hdr)) + \
45 DHCP_FULL_TAG_LENGTH((hdr))))
46
47#define DHCP_CALC_TAG_LENGTH(payload_len) \
48 ((payload_len) + sizeof (efx_dhcp_tag_hdr_t))
11fdf7f2
TL
49
50
51/* Report the layout of bootcfg sectors in NVRAM partition. */
52 __checkReturn efx_rc_t
53efx_bootcfg_sector_info(
54 __in efx_nic_t *enp,
55 __in uint32_t pf,
56 __out_opt uint32_t *sector_countp,
57 __out size_t *offsetp,
58 __out size_t *max_sizep)
59{
60 uint32_t count;
61 size_t max_size;
62 size_t offset;
63 int rc;
64
65 switch (enp->en_family) {
66#if EFSYS_OPT_SIENA
67 case EFX_FAMILY_SIENA:
68 max_size = BOOTCFG_MAX_SIZE;
69 offset = 0;
70 count = 1;
71 break;
72#endif /* EFSYS_OPT_SIENA */
73
74#if EFSYS_OPT_HUNTINGTON
75 case EFX_FAMILY_HUNTINGTON:
76 max_size = BOOTCFG_MAX_SIZE;
77 offset = 0;
78 count = 1;
79 break;
80#endif /* EFSYS_OPT_HUNTINGTON */
81
82#if EFSYS_OPT_MEDFORD
83 case EFX_FAMILY_MEDFORD: {
84 /* Shared partition (array indexed by PF) */
85 max_size = BOOTCFG_PER_PF;
86 count = BOOTCFG_PF_COUNT;
87 if (pf >= count) {
88 rc = EINVAL;
89 goto fail2;
90 }
91 offset = max_size * pf;
92 break;
93 }
94#endif /* EFSYS_OPT_MEDFORD */
95
96#if EFSYS_OPT_MEDFORD2
97 case EFX_FAMILY_MEDFORD2: {
98 /* Shared partition (array indexed by PF) */
99 max_size = BOOTCFG_PER_PF;
100 count = BOOTCFG_PF_COUNT;
101 if (pf >= count) {
102 rc = EINVAL;
103 goto fail3;
104 }
105 offset = max_size * pf;
106 break;
107 }
108#endif /* EFSYS_OPT_MEDFORD2 */
109
110 default:
111 EFSYS_ASSERT(0);
112 rc = ENOTSUP;
113 goto fail1;
114 }
115 EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE);
116
117 if (sector_countp != NULL)
118 *sector_countp = count;
119 *offsetp = offset;
120 *max_sizep = max_size;
121
122 return (0);
123
124#if EFSYS_OPT_MEDFORD2
125fail3:
126 EFSYS_PROBE(fail3);
127#endif
128#if EFSYS_OPT_MEDFORD
129fail2:
130 EFSYS_PROBE(fail2);
131#endif
132fail1:
133 EFSYS_PROBE1(fail1, efx_rc_t, rc);
134 return (rc);
135}
136
137
9f95a23c
TL
138 __checkReturn uint8_t
139efx_dhcp_csum(
11fdf7f2
TL
140 __in_bcount(size) uint8_t const *data,
141 __in size_t size)
142{
11fdf7f2
TL
143 unsigned int pos;
144 uint8_t checksum = 0;
145
146 for (pos = 0; pos < size; pos++)
147 checksum += data[pos];
148 return (checksum);
149}
150
9f95a23c
TL
151 __checkReturn efx_rc_t
152efx_dhcp_verify(
11fdf7f2
TL
153 __in_bcount(size) uint8_t const *data,
154 __in size_t size,
155 __out_opt size_t *usedp)
156{
157 size_t offset = 0;
158 size_t used = 0;
159 efx_rc_t rc;
160
161 /* Start parsing tags immediately after the checksum */
162 for (offset = 1; offset < size; ) {
163 uint8_t tag;
164 uint8_t length;
165
166 /* Consume tag */
167 tag = data[offset];
9f95a23c 168 if (tag == EFX_DHCP_END) {
11fdf7f2
TL
169 offset++;
170 used = offset;
171 break;
172 }
9f95a23c 173 if (tag == EFX_DHCP_PAD) {
11fdf7f2
TL
174 offset++;
175 continue;
176 }
177
178 /* Consume length */
179 if (offset + 1 >= size) {
180 rc = ENOSPC;
181 goto fail1;
182 }
183 length = data[offset + 1];
184
185 /* Consume *length */
186 if (offset + 1 + length >= size) {
187 rc = ENOSPC;
188 goto fail2;
189 }
190
191 offset += 2 + length;
192 used = offset;
193 }
194
9f95a23c
TL
195 /* Checksum the entire sector, including bytes after any EFX_DHCP_END */
196 if (efx_dhcp_csum(data, size) != 0) {
11fdf7f2
TL
197 rc = EINVAL;
198 goto fail3;
199 }
200
201 if (usedp != NULL)
202 *usedp = used;
203
204 return (0);
205
206fail3:
207 EFSYS_PROBE(fail3);
208fail2:
209 EFSYS_PROBE(fail2);
210fail1:
211 EFSYS_PROBE1(fail1, efx_rc_t, rc);
212
213 return (rc);
214}
215
9f95a23c
TL
216/*
217 * Walk the entire tag set looking for option. The sought option may be
218 * encapsulated. ENOENT indicates the walk completed without finding the
219 * option. If we run out of buffer during the walk the function will return
220 * ENOSPC.
221 */
222static efx_rc_t
223efx_dhcp_walk_tags(
224 __deref_inout uint8_t **tagpp,
225 __inout size_t *buffer_sizep,
226 __in uint16_t opt)
227{
228 efx_rc_t rc = 0;
229 boolean_t is_encap = B_FALSE;
230
231 if (DHCP_IS_ENCAP_OPT(opt)) {
232 /*
233 * Look for the encapsulator and, if found, limit ourselves
234 * to its payload. If it's not found then the entire tag
235 * cannot be found, so the encapsulated opt search is
236 * skipped.
237 */
238 rc = efx_dhcp_walk_tags(tagpp, buffer_sizep,
239 DHCP_ENCAPSULATOR(opt));
240 if (rc == 0) {
241 *buffer_sizep = ((efx_dhcp_tag_hdr_t *)*tagpp)->length;
242 (*tagpp) += sizeof (efx_dhcp_tag_hdr_t);
243 }
244 opt = DHCP_ENCAPSULATED(opt);
245 is_encap = B_TRUE;
246 }
247
248 EFSYS_ASSERT(!DHCP_IS_ENCAP_OPT(opt));
249
250 while (rc == 0) {
251 size_t size;
252
253 if (*buffer_sizep == 0) {
254 rc = ENOSPC;
255 goto fail1;
256 }
257
258 if (DHCP_ENCAPSULATED(**tagpp) == opt)
259 break;
260
261 if ((**tagpp) == EFX_DHCP_END) {
262 rc = ENOENT;
263 break;
264 } else if ((**tagpp) == EFX_DHCP_PAD) {
265 size = 1;
266 } else {
267 if (*buffer_sizep < sizeof (efx_dhcp_tag_hdr_t)) {
268 rc = ENOSPC;
269 goto fail2;
270 }
271
272 size =
273 DHCP_FULL_TAG_LENGTH((efx_dhcp_tag_hdr_t *)*tagpp);
274 }
275
276 if (size > *buffer_sizep) {
277 rc = ENOSPC;
278 goto fail3;
279 }
280
281 (*tagpp) += size;
282 (*buffer_sizep) -= size;
283
284 if ((*buffer_sizep == 0) && is_encap) {
285 /* Search within encapulator tag finished */
286 rc = ENOENT;
287 break;
288 }
289 }
290
291 /*
292 * Returns 0 if found otherwise ENOENT indicating search finished
293 * correctly
294 */
295 return (rc);
296
297fail3:
298 EFSYS_PROBE(fail3);
299fail2:
300 EFSYS_PROBE(fail2);
301fail1:
302 EFSYS_PROBE1(fail1, efx_rc_t, rc);
303
304 return (rc);
305}
306
307/*
308 * Locate value buffer for option in the given buffer.
309 * Returns 0 if found, ENOENT indicating search finished
310 * correctly, otherwise search failed before completion.
311 */
312 __checkReturn efx_rc_t
313efx_dhcp_find_tag(
314 __in_bcount(buffer_length) uint8_t *bufferp,
315 __in size_t buffer_length,
316 __in uint16_t opt,
317 __deref_out uint8_t **valuepp,
318 __out size_t *value_lengthp)
319{
320 efx_rc_t rc;
321 uint8_t *tagp = bufferp;
322 size_t len = buffer_length;
323
324 rc = efx_dhcp_walk_tags(&tagp, &len, opt);
325 if (rc == 0) {
326 efx_dhcp_tag_hdr_t *hdrp;
327
328 hdrp = (efx_dhcp_tag_hdr_t *)tagp;
329 *valuepp = (uint8_t *)(&hdrp[1]);
330 *value_lengthp = hdrp->length;
331 } else if (rc != ENOENT) {
332 goto fail1;
333 }
334
335 return (rc);
336
337fail1:
338 EFSYS_PROBE1(fail1, efx_rc_t, rc);
339
340 return (rc);
341}
342
343/*
344 * Locate the end tag in the given buffer.
345 * Returns 0 if found, ENOENT indicating search finished
346 * correctly but end tag was not found; otherwise search
347 * failed before completion.
348 */
349 __checkReturn efx_rc_t
350efx_dhcp_find_end(
351 __in_bcount(buffer_length) uint8_t *bufferp,
352 __in size_t buffer_length,
353 __deref_out uint8_t **endpp)
354{
355 efx_rc_t rc;
356 uint8_t *endp = bufferp;
357 size_t len = buffer_length;
358
359 rc = efx_dhcp_walk_tags(&endp, &len, EFX_DHCP_END);
360 if (rc == 0)
361 *endpp = endp;
362 else if (rc != ENOENT)
363 goto fail1;
364
365 return (rc);
366
367fail1:
368 EFSYS_PROBE1(fail1, efx_rc_t, rc);
369
370 return (rc);
371}
372
373
374/*
375 * Delete the given tag from anywhere in the buffer. Copes with
376 * encapsulated tags, and updates or deletes the encapsulating opt as
377 * necessary.
378 */
379 __checkReturn efx_rc_t
380efx_dhcp_delete_tag(
381 __inout_bcount(buffer_length) uint8_t *bufferp,
382 __in size_t buffer_length,
383 __in uint16_t opt)
384{
385 efx_rc_t rc;
386 efx_dhcp_tag_hdr_t *hdrp;
387 size_t len;
388 uint8_t *startp;
389 uint8_t *endp;
390
391 len = buffer_length;
392 startp = bufferp;
393
394 if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
395 rc = EINVAL;
396 goto fail1;
397 }
398
399 rc = efx_dhcp_walk_tags(&startp, &len, opt);
400 if (rc != 0)
401 goto fail1;
402
403 hdrp = (efx_dhcp_tag_hdr_t *)startp;
404
405 if (DHCP_IS_ENCAP_OPT(opt)) {
406 uint8_t tag_length = DHCP_FULL_TAG_LENGTH(hdrp);
407 uint8_t *encapp = bufferp;
408 efx_dhcp_tag_hdr_t *encap_hdrp;
409
410 len = buffer_length;
411 rc = efx_dhcp_walk_tags(&encapp, &len,
412 DHCP_ENCAPSULATOR(opt));
413 if (rc != 0)
414 goto fail2;
415
416 encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp;
417 if (encap_hdrp->length > tag_length) {
418 encap_hdrp->length = (uint8_t)(
419 (size_t)encap_hdrp->length - tag_length);
420 } else {
421 /* delete the encapsulating tag */
422 hdrp = encap_hdrp;
423 }
424 }
425
426 startp = (uint8_t *)hdrp;
427 endp = (uint8_t *)DHCP_NEXT_TAG(hdrp);
428
429 if (startp < bufferp) {
430 rc = EINVAL;
431 goto fail3;
432 }
433
434 if (endp > &bufferp[buffer_length]) {
435 rc = EINVAL;
436 goto fail4;
437 }
438
439 memmove(startp, endp,
440 buffer_length - (endp - bufferp));
441
442 return (0);
443
444fail4:
445 EFSYS_PROBE(fail4);
446fail3:
447 EFSYS_PROBE(fail3);
448fail2:
449 EFSYS_PROBE(fail2);
450fail1:
451 EFSYS_PROBE1(fail1, efx_rc_t, rc);
452
453 return (rc);
454}
455
456/*
457 * Write the tag header into write_pointp and optionally copies the payload
458 * into the space following.
459 */
460static void
461efx_dhcp_write_tag(
462 __in uint8_t *write_pointp,
463 __in uint16_t opt,
464 __in_bcount_opt(value_length)
465 uint8_t *valuep,
466 __in size_t value_length)
467{
468 efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp;
469 hdrp->tag = DHCP_ENCAPSULATED(opt);
470 hdrp->length = (uint8_t)value_length;
471 if ((value_length > 0) && (valuep != NULL))
472 memcpy(&hdrp[1], valuep, value_length);
473}
474
475/*
476 * Add the given tag to the end of the buffer. Copes with creating an
477 * encapsulated tag, and updates or creates the encapsulating opt as
478 * necessary.
479 */
480 __checkReturn efx_rc_t
481efx_dhcp_add_tag(
482 __inout_bcount(buffer_length) uint8_t *bufferp,
483 __in size_t buffer_length,
484 __in uint16_t opt,
485 __in_bcount_opt(value_length) uint8_t *valuep,
486 __in size_t value_length)
487{
488 efx_rc_t rc;
489 efx_dhcp_tag_hdr_t *encap_hdrp = NULL;
490 uint8_t *insert_pointp = NULL;
491 uint8_t *endp;
492 size_t available_space;
493 size_t added_length;
494 size_t search_size;
495 uint8_t *searchp;
496
497 if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
498 rc = EINVAL;
499 goto fail1;
500 }
501
502 if (value_length > DHCP_MAX_VALUE) {
503 rc = EINVAL;
504 goto fail2;
505 }
506
507 if ((value_length > 0) && (valuep == NULL)) {
508 rc = EINVAL;
509 goto fail3;
510 }
511
512 endp = bufferp;
513 available_space = buffer_length;
514 rc = efx_dhcp_walk_tags(&endp, &available_space, EFX_DHCP_END);
515 if (rc != 0)
516 goto fail4;
517
518 searchp = bufferp;
519 search_size = buffer_length;
520 if (DHCP_IS_ENCAP_OPT(opt)) {
521 rc = efx_dhcp_walk_tags(&searchp, &search_size,
522 DHCP_ENCAPSULATOR(opt));
523 if (rc == 0) {
524 encap_hdrp = (efx_dhcp_tag_hdr_t *)searchp;
525
526 /* Check encapsulated tag is not present */
527 search_size = encap_hdrp->length;
528 rc = efx_dhcp_walk_tags(&searchp, &search_size,
529 opt);
530 if (rc != ENOENT) {
531 rc = EINVAL;
532 goto fail5;
533 }
534
535 /* Check encapsulator will not overflow */
536 if (((size_t)encap_hdrp->length +
537 DHCP_CALC_TAG_LENGTH(value_length)) >
538 DHCP_MAX_VALUE) {
539 rc = E2BIG;
540 goto fail6;
541 }
542
543 /* Insert at start of existing encapsulator */
544 insert_pointp = (uint8_t *)&encap_hdrp[1];
545 opt = DHCP_ENCAPSULATED(opt);
546 } else if (rc == ENOENT) {
547 encap_hdrp = NULL;
548 } else {
549 goto fail7;
550 }
551 } else {
552 /* Check unencapsulated tag is not present */
553 rc = efx_dhcp_walk_tags(&searchp, &search_size,
554 opt);
555 if (rc != ENOENT) {
556 rc = EINVAL;
557 goto fail8;
558 }
559 }
560
561 if (insert_pointp == NULL) {
562 /* Insert at end of existing tags */
563 insert_pointp = endp;
564 }
565
566 /* Includes the new encapsulator tag hdr if required */
567 added_length = DHCP_CALC_TAG_LENGTH(value_length) +
568 (DHCP_IS_ENCAP_OPT(opt) ? sizeof (efx_dhcp_tag_hdr_t) : 0);
569
570 if (available_space <= added_length) {
571 rc = ENOMEM;
572 goto fail9;
573 }
574
575 memmove(insert_pointp + added_length, insert_pointp,
576 available_space - added_length);
577
578 if (DHCP_IS_ENCAP_OPT(opt)) {
579 /* Create new encapsulator header */
580 added_length -= sizeof (efx_dhcp_tag_hdr_t);
581 efx_dhcp_write_tag(insert_pointp,
582 DHCP_ENCAPSULATOR(opt), NULL, added_length);
583 insert_pointp += sizeof (efx_dhcp_tag_hdr_t);
584 } else if (encap_hdrp)
585 /* Modify existing encapsulator header */
586 encap_hdrp->length +=
587 ((uint8_t)DHCP_CALC_TAG_LENGTH(value_length));
588
589 efx_dhcp_write_tag(insert_pointp, opt, valuep, value_length);
590
591 return (0);
592
593fail9:
594 EFSYS_PROBE(fail9);
595fail8:
596 EFSYS_PROBE(fail8);
597fail7:
598 EFSYS_PROBE(fail7);
599fail6:
600 EFSYS_PROBE(fail6);
601fail5:
602 EFSYS_PROBE(fail5);
603fail4:
604 EFSYS_PROBE(fail4);
605fail3:
606 EFSYS_PROBE(fail3);
607fail2:
608 EFSYS_PROBE(fail2);
609fail1:
610 EFSYS_PROBE1(fail1, efx_rc_t, rc);
611
612 return (rc);
613}
614
615/*
616 * Update an existing tag to the new value. Copes with encapsulated
617 * tags, and updates the encapsulating opt as necessary.
618 */
619 __checkReturn efx_rc_t
620efx_dhcp_update_tag(
621 __inout_bcount(buffer_length) uint8_t *bufferp,
622 __in size_t buffer_length,
623 __in uint16_t opt,
624 __in uint8_t *value_locationp,
625 __in_bcount_opt(value_length) uint8_t *valuep,
626 __in size_t value_length)
627{
628 efx_rc_t rc;
629 uint8_t *write_pointp = value_locationp - sizeof (efx_dhcp_tag_hdr_t);
630 efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp;
631 efx_dhcp_tag_hdr_t *encap_hdrp = NULL;
632 size_t old_length;
633
634 if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
635 rc = EINVAL;
636 goto fail1;
637 }
638
639 if (value_length > DHCP_MAX_VALUE) {
640 rc = EINVAL;
641 goto fail2;
642 }
643
644 if ((value_length > 0) && (valuep == NULL)) {
645 rc = EINVAL;
646 goto fail3;
647 }
648
649 old_length = hdrp->length;
650
651 if (old_length < value_length) {
652 uint8_t *endp = bufferp;
653 size_t available_space = buffer_length;
654
655 rc = efx_dhcp_walk_tags(&endp, &available_space,
656 EFX_DHCP_END);
657 if (rc != 0)
658 goto fail4;
659
660 if (available_space < (value_length - old_length)) {
661 rc = EINVAL;
662 goto fail5;
663 }
664 }
665
666 if (DHCP_IS_ENCAP_OPT(opt)) {
667 uint8_t *encapp = bufferp;
668 size_t following_encap = buffer_length;
669 size_t new_length;
670
671 rc = efx_dhcp_walk_tags(&encapp, &following_encap,
672 DHCP_ENCAPSULATOR(opt));
673 if (rc != 0)
674 goto fail6;
675
676 encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp;
677
678 new_length = ((size_t)encap_hdrp->length +
679 value_length - old_length);
680 /* Check encapsulator will not overflow */
681 if (new_length > DHCP_MAX_VALUE) {
682 rc = E2BIG;
683 goto fail7;
684 }
685
686 encap_hdrp->length = (uint8_t)new_length;
687 }
688
689 /*
690 * Move the following data up/down to accomodate the new payload
691 * length.
692 */
693 if (old_length != value_length) {
694 uint8_t *destp = (uint8_t *)DHCP_NEXT_TAG(hdrp) +
695 value_length - old_length;
696 size_t count = &bufferp[buffer_length] -
697 (uint8_t *)DHCP_NEXT_TAG(hdrp);
698
699 memmove(destp, DHCP_NEXT_TAG(hdrp), count);
700 }
701
702 EFSYS_ASSERT(hdrp->tag == DHCP_ENCAPSULATED(opt));
703 efx_dhcp_write_tag(write_pointp, opt, valuep, value_length);
704
705 return (0);
706
707fail7:
708 EFSYS_PROBE(fail7);
709fail6:
710 EFSYS_PROBE(fail6);
711fail5:
712 EFSYS_PROBE(fail5);
713fail4:
714 EFSYS_PROBE(fail4);
715fail3:
716 EFSYS_PROBE(fail3);
717fail2:
718 EFSYS_PROBE(fail2);
719fail1:
720 EFSYS_PROBE1(fail1, efx_rc_t, rc);
721
722 return (rc);
723}
724
725
11fdf7f2
TL
726/*
727 * Copy bootcfg sector data to a target buffer which may differ in size.
728 * Optionally corrects format errors in source buffer.
729 */
730 efx_rc_t
731efx_bootcfg_copy_sector(
732 __in efx_nic_t *enp,
733 __inout_bcount(sector_length)
734 uint8_t *sector,
735 __in size_t sector_length,
736 __out_bcount(data_size) uint8_t *data,
737 __in size_t data_size,
738 __in boolean_t handle_format_errors)
739{
9f95a23c
TL
740 _NOTE(ARGUNUSED(enp))
741
11fdf7f2
TL
742 size_t used_bytes;
743 efx_rc_t rc;
744
9f95a23c 745 /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */
11fdf7f2
TL
746 if (data_size < 2) {
747 rc = ENOSPC;
748 goto fail1;
749 }
750
751 /* Verify that the area is correctly formatted and checksummed */
9f95a23c 752 rc = efx_dhcp_verify(sector, sector_length,
11fdf7f2
TL
753 &used_bytes);
754
755 if (!handle_format_errors) {
756 if (rc != 0)
757 goto fail2;
758
759 if ((used_bytes < 2) ||
9f95a23c
TL
760 (sector[used_bytes - 1] != EFX_DHCP_END)) {
761 /* Block too short, or EFX_DHCP_END missing */
11fdf7f2
TL
762 rc = ENOENT;
763 goto fail3;
764 }
765 }
766
767 /* Synthesize empty format on verification failure */
768 if (rc != 0 || used_bytes == 0) {
769 sector[0] = 0;
9f95a23c 770 sector[1] = EFX_DHCP_END;
11fdf7f2
TL
771 used_bytes = 2;
772 }
9f95a23c 773 EFSYS_ASSERT(used_bytes >= 2); /* checksum and EFX_DHCP_END */
11fdf7f2
TL
774 EFSYS_ASSERT(used_bytes <= sector_length);
775 EFSYS_ASSERT(sector_length >= 2);
776
777 /*
9f95a23c
TL
778 * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END
779 * character. Modify the returned payload so it does.
11fdf7f2
TL
780 * Reinitialise the sector if there isn't room for the character.
781 */
9f95a23c 782 if (sector[used_bytes - 1] != EFX_DHCP_END) {
11fdf7f2
TL
783 if (used_bytes >= sector_length) {
784 sector[0] = 0;
785 used_bytes = 1;
786 }
9f95a23c 787 sector[used_bytes] = EFX_DHCP_END;
11fdf7f2
TL
788 ++used_bytes;
789 }
790
791 /*
792 * Verify that the target buffer is large enough for the
793 * entire used bootcfg area, then copy into the target buffer.
794 */
795 if (used_bytes > data_size) {
796 rc = ENOSPC;
797 goto fail4;
798 }
799
800 data[0] = 0; /* checksum, updated below */
801
802 /* Copy all after the checksum to the target buffer */
803 memcpy(data + 1, sector + 1, used_bytes - 1);
804
805 /* Zero out the unused portion of the target buffer */
806 if (used_bytes < data_size)
807 (void) memset(data + used_bytes, 0, data_size - used_bytes);
808
809 /*
9f95a23c
TL
810 * The checksum includes trailing data after any EFX_DHCP_END
811 * character, which we've just modified (by truncation or appending
812 * EFX_DHCP_END).
11fdf7f2 813 */
9f95a23c 814 data[0] -= efx_dhcp_csum(data, data_size);
11fdf7f2
TL
815
816 return (0);
817
818fail4:
819 EFSYS_PROBE(fail4);
820fail3:
821 EFSYS_PROBE(fail3);
822fail2:
823 EFSYS_PROBE(fail2);
824fail1:
825 EFSYS_PROBE1(fail1, efx_rc_t, rc);
826
827 return (rc);
828}
829
830 efx_rc_t
831efx_bootcfg_read(
832 __in efx_nic_t *enp,
833 __out_bcount(size) uint8_t *data,
834 __in size_t size)
835{
836 uint8_t *payload = NULL;
837 size_t used_bytes;
838 size_t partn_length;
839 size_t sector_length;
840 size_t sector_offset;
841 efx_rc_t rc;
842 uint32_t sector_number;
843
9f95a23c 844 /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */
11fdf7f2
TL
845 if (size < 2) {
846 rc = ENOSPC;
847 goto fail1;
848 }
849
9f95a23c 850#if EFX_OPTS_EF10()
11fdf7f2
TL
851 sector_number = enp->en_nic_cfg.enc_pf;
852#else
853 sector_number = 0;
854#endif
855 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
856 if (rc != 0)
857 goto fail2;
858
859 /* The bootcfg sector may be stored in a (larger) shared partition */
860 rc = efx_bootcfg_sector_info(enp, sector_number,
861 NULL, &sector_offset, &sector_length);
862 if (rc != 0)
863 goto fail3;
864
865 if (sector_length < 2) {
866 rc = EINVAL;
867 goto fail4;
868 }
869
870 if (sector_length > BOOTCFG_MAX_SIZE)
871 sector_length = BOOTCFG_MAX_SIZE;
872
873 if (sector_offset + sector_length > partn_length) {
874 /* Partition is too small */
875 rc = EFBIG;
876 goto fail5;
877 }
878
879 /*
9f95a23c
TL
880 * We need to read the entire BOOTCFG sector to ensure we read all
881 * tags, because legacy bootcfg sectors are not guaranteed to end
882 * with an EFX_DHCP_END character. If the user hasn't supplied a
883 * sufficiently large buffer then use our own buffer.
11fdf7f2
TL
884 */
885 if (sector_length > size) {
886 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload);
887 if (payload == NULL) {
888 rc = ENOMEM;
889 goto fail6;
890 }
891 } else
892 payload = (uint8_t *)data;
893
894 if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
895 goto fail7;
896
897 if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
898 sector_offset, (caddr_t)payload, sector_length)) != 0) {
899 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
900 goto fail8;
901 }
902
903 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
904 goto fail9;
905
906 /* Verify that the area is correctly formatted and checksummed */
9f95a23c 907 rc = efx_dhcp_verify(payload, sector_length,
11fdf7f2
TL
908 &used_bytes);
909 if (rc != 0 || used_bytes == 0) {
910 payload[0] = 0;
9f95a23c 911 payload[1] = EFX_DHCP_END;
11fdf7f2
TL
912 used_bytes = 2;
913 }
914
9f95a23c 915 EFSYS_ASSERT(used_bytes >= 2); /* checksum and EFX_DHCP_END */
11fdf7f2
TL
916 EFSYS_ASSERT(used_bytes <= sector_length);
917
918 /*
9f95a23c
TL
919 * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END
920 * character. Modify the returned payload so it does.
921 * BOOTCFG_MAX_SIZE is by definition large enough for any valid
922 * (per-port) bootcfg sector, so reinitialise the sector if there
923 * isn't room for the character.
11fdf7f2 924 */
9f95a23c 925 if (payload[used_bytes - 1] != EFX_DHCP_END) {
11fdf7f2
TL
926 if (used_bytes >= sector_length)
927 used_bytes = 1;
928
9f95a23c 929 payload[used_bytes] = EFX_DHCP_END;
11fdf7f2
TL
930 ++used_bytes;
931 }
932
933 /*
934 * Verify that the user supplied buffer is large enough for the
935 * entire used bootcfg area, then copy into the user supplied buffer.
936 */
937 if (used_bytes > size) {
938 rc = ENOSPC;
939 goto fail10;
940 }
941
942 data[0] = 0; /* checksum, updated below */
943
944 if (sector_length > size) {
945 /* Copy all after the checksum to the target buffer */
946 memcpy(data + 1, payload + 1, used_bytes - 1);
947 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
948 }
949
950 /* Zero out the unused portion of the user buffer */
951 if (used_bytes < size)
952 (void) memset(data + used_bytes, 0, size - used_bytes);
953
954 /*
9f95a23c
TL
955 * The checksum includes trailing data after any EFX_DHCP_END character,
956 * which we've just modified (by truncation or appending EFX_DHCP_END).
11fdf7f2 957 */
9f95a23c 958 data[0] -= efx_dhcp_csum(data, size);
11fdf7f2
TL
959
960 return (0);
961
962fail10:
963 EFSYS_PROBE(fail10);
964fail9:
965 EFSYS_PROBE(fail9);
966fail8:
967 EFSYS_PROBE(fail8);
968fail7:
969 EFSYS_PROBE(fail7);
970 if (sector_length > size)
971 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
972fail6:
973 EFSYS_PROBE(fail6);
974fail5:
975 EFSYS_PROBE(fail5);
976fail4:
977 EFSYS_PROBE(fail4);
978fail3:
979 EFSYS_PROBE(fail3);
980fail2:
981 EFSYS_PROBE(fail2);
982fail1:
983 EFSYS_PROBE1(fail1, efx_rc_t, rc);
984
985 return (rc);
986}
987
988 efx_rc_t
989efx_bootcfg_write(
990 __in efx_nic_t *enp,
991 __in_bcount(size) uint8_t *data,
992 __in size_t size)
993{
994 uint8_t *partn_data;
995 uint8_t checksum;
996 size_t partn_length;
997 size_t sector_length;
998 size_t sector_offset;
999 size_t used_bytes;
1000 efx_rc_t rc;
1001 uint32_t sector_number;
1002
9f95a23c 1003#if EFX_OPTS_EF10()
11fdf7f2
TL
1004 sector_number = enp->en_nic_cfg.enc_pf;
1005#else
1006 sector_number = 0;
1007#endif
1008
1009 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
1010 if (rc != 0)
1011 goto fail1;
1012
1013 /* The bootcfg sector may be stored in a (larger) shared partition */
1014 rc = efx_bootcfg_sector_info(enp, sector_number,
1015 NULL, &sector_offset, &sector_length);
1016 if (rc != 0)
1017 goto fail2;
1018
1019 if (sector_length > BOOTCFG_MAX_SIZE)
1020 sector_length = BOOTCFG_MAX_SIZE;
1021
1022 if (sector_offset + sector_length > partn_length) {
1023 /* Partition is too small */
1024 rc = EFBIG;
1025 goto fail3;
1026 }
1027
9f95a23c 1028 if ((rc = efx_dhcp_verify(data, size, &used_bytes)) != 0)
11fdf7f2
TL
1029 goto fail4;
1030
9f95a23c
TL
1031 /*
1032 * The caller *must* terminate their block with a EFX_DHCP_END
1033 * character
1034 */
1035 if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] !=
1036 EFX_DHCP_END)) {
1037 /* Block too short or EFX_DHCP_END missing */
11fdf7f2
TL
1038 rc = ENOENT;
1039 goto fail5;
1040 }
1041
1042 /* Check that the hardware has support for this much data */
1043 if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) {
1044 rc = ENOSPC;
1045 goto fail6;
1046 }
1047
1048 /*
1049 * If the BOOTCFG sector is stored in a shared partition, then we must
1050 * read the whole partition and insert the updated bootcfg sector at the
1051 * correct offset.
1052 */
1053 EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data);
1054 if (partn_data == NULL) {
1055 rc = ENOMEM;
1056 goto fail7;
1057 }
1058
1059 rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
1060 if (rc != 0)
1061 goto fail8;
1062
1063 /* Read the entire partition */
1064 rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0,
1065 (caddr_t)partn_data, partn_length);
1066 if (rc != 0)
1067 goto fail9;
1068
1069 /*
9f95a23c
TL
1070 * Insert the BOOTCFG sector into the partition, Zero out all data
1071 * after the EFX_DHCP_END tag, and adjust the checksum.
11fdf7f2
TL
1072 */
1073 (void) memset(partn_data + sector_offset, 0x0, sector_length);
1074 (void) memcpy(partn_data + sector_offset, data, used_bytes);
1075
9f95a23c 1076 checksum = efx_dhcp_csum(data, used_bytes);
11fdf7f2
TL
1077 partn_data[sector_offset] -= checksum;
1078
1079 if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
1080 goto fail10;
1081
1082 if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
1083 0, (caddr_t)partn_data, partn_length)) != 0)
1084 goto fail11;
1085
1086 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
1087 goto fail12;
1088
1089 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
1090
1091 return (0);
1092
1093fail12:
1094 EFSYS_PROBE(fail12);
1095fail11:
1096 EFSYS_PROBE(fail11);
1097fail10:
1098 EFSYS_PROBE(fail10);
1099fail9:
1100 EFSYS_PROBE(fail9);
1101
1102 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
1103fail8:
1104 EFSYS_PROBE(fail8);
1105
1106 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
1107fail7:
1108 EFSYS_PROBE(fail7);
1109fail6:
1110 EFSYS_PROBE(fail6);
1111fail5:
1112 EFSYS_PROBE(fail5);
1113fail4:
1114 EFSYS_PROBE(fail4);
1115fail3:
1116 EFSYS_PROBE(fail3);
1117fail2:
1118 EFSYS_PROBE(fail2);
1119fail1:
1120 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1121
1122 return (rc);
1123}
1124
1125#endif /* EFSYS_OPT_BOOTCFG */