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