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