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