xref: /freebsd/sys/dev/sfxge/common/efx_bootcfg.c (revision d91f8db5f1822c43cd256f19aae1d059e4b25a26)
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 __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, &sector_offset, &sector_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, &sector_offset, &sector_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 */
1151