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