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