xref: /freebsd/sys/dev/sfxge/common/ef10_nvram.c (revision abd87254)
1 /*-
2  * Copyright (c) 2012-2016 Solarflare Communications Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  *    this list of conditions and the following disclaimer in the documentation
12  *    and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * The views and conclusions contained in the software and documentation are
27  * those of the authors and should not be interpreted as representing official
28  * policies, either expressed or implied, of the FreeBSD Project.
29  */
30 
31 #include <sys/cdefs.h>
32 #include "efx.h"
33 #include "efx_impl.h"
34 
35 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
36 
37 #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
38 
39 #include "ef10_tlv_layout.h"
40 
41 /* Cursor for TLV partition format */
42 typedef struct tlv_cursor_s {
43 	uint32_t	*block;			/* Base of data block */
44 	uint32_t	*current;		/* Cursor position */
45 	uint32_t	*end;			/* End tag position */
46 	uint32_t	*limit;			/* Last dword of data block */
47 } tlv_cursor_t;
48 
49 typedef struct nvram_partition_s {
50 	uint16_t type;
51 	uint8_t chip_select;
52 	uint8_t flags;
53 	/*
54 	 * The full length of the NVRAM partition.
55 	 * This is different from tlv_partition_header.total_length,
56 	 *  which can be smaller.
57 	 */
58 	uint32_t length;
59 	uint32_t erase_size;
60 	uint32_t *data;
61 	tlv_cursor_t tlv_cursor;
62 } nvram_partition_t;
63 
64 static	__checkReturn		efx_rc_t
65 tlv_validate_state(
66 	__inout			tlv_cursor_t *cursor);
67 
68 static				void
69 tlv_init_block(
70 	__out	uint32_t	*block)
71 {
72 	*block = __CPU_TO_LE_32(TLV_TAG_END);
73 }
74 
75 static				uint32_t
76 tlv_tag(
77 	__in	tlv_cursor_t	*cursor)
78 {
79 	uint32_t dword, tag;
80 
81 	dword = cursor->current[0];
82 	tag = __LE_TO_CPU_32(dword);
83 
84 	return (tag);
85 }
86 
87 static				size_t
88 tlv_length(
89 	__in	tlv_cursor_t	*cursor)
90 {
91 	uint32_t dword, length;
92 
93 	if (tlv_tag(cursor) == TLV_TAG_END)
94 		return (0);
95 
96 	dword = cursor->current[1];
97 	length = __LE_TO_CPU_32(dword);
98 
99 	return ((size_t)length);
100 }
101 
102 static				uint8_t *
103 tlv_value(
104 	__in	tlv_cursor_t	*cursor)
105 {
106 	if (tlv_tag(cursor) == TLV_TAG_END)
107 		return (NULL);
108 
109 	return ((uint8_t *)(&cursor->current[2]));
110 }
111 
112 static				uint8_t *
113 tlv_item(
114 	__in	tlv_cursor_t	*cursor)
115 {
116 	if (tlv_tag(cursor) == TLV_TAG_END)
117 		return (NULL);
118 
119 	return ((uint8_t *)cursor->current);
120 }
121 
122 /*
123  * TLV item DWORD length is tag + length + value (rounded up to DWORD)
124  * equivalent to tlv_n_words_for_len in mc-comms tlv.c
125  */
126 #define	TLV_DWORD_COUNT(length) \
127 	(1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
128 
129 static				uint32_t *
130 tlv_next_item_ptr(
131 	__in	tlv_cursor_t	*cursor)
132 {
133 	uint32_t length;
134 
135 	length = tlv_length(cursor);
136 
137 	return (cursor->current + TLV_DWORD_COUNT(length));
138 }
139 
140 static	__checkReturn		efx_rc_t
141 tlv_advance(
142 	__inout	tlv_cursor_t	*cursor)
143 {
144 	efx_rc_t rc;
145 
146 	if ((rc = tlv_validate_state(cursor)) != 0)
147 		goto fail1;
148 
149 	if (cursor->current == cursor->end) {
150 		/* No more tags after END tag */
151 		cursor->current = NULL;
152 		rc = ENOENT;
153 		goto fail2;
154 	}
155 
156 	/* Advance to next item and validate */
157 	cursor->current = tlv_next_item_ptr(cursor);
158 
159 	if ((rc = tlv_validate_state(cursor)) != 0)
160 		goto fail3;
161 
162 	return (0);
163 
164 fail3:
165 	EFSYS_PROBE(fail3);
166 fail2:
167 	EFSYS_PROBE(fail2);
168 fail1:
169 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
170 
171 	return (rc);
172 }
173 
174 static				efx_rc_t
175 tlv_rewind(
176 	__in	tlv_cursor_t	*cursor)
177 {
178 	efx_rc_t rc;
179 
180 	cursor->current = cursor->block;
181 
182 	if ((rc = tlv_validate_state(cursor)) != 0)
183 		goto fail1;
184 
185 	return (0);
186 
187 fail1:
188 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
189 
190 	return (rc);
191 }
192 
193 static				efx_rc_t
194 tlv_find(
195 	__inout	tlv_cursor_t	*cursor,
196 	__in	uint32_t	tag)
197 {
198 	efx_rc_t rc;
199 
200 	rc = tlv_rewind(cursor);
201 	while (rc == 0) {
202 		if (tlv_tag(cursor) == tag)
203 			break;
204 
205 		rc = tlv_advance(cursor);
206 	}
207 	return (rc);
208 }
209 
210 static	__checkReturn		efx_rc_t
211 tlv_validate_state(
212 	__inout	tlv_cursor_t	*cursor)
213 {
214 	efx_rc_t rc;
215 
216 	/* Check cursor position */
217 	if (cursor->current < cursor->block) {
218 		rc = EINVAL;
219 		goto fail1;
220 	}
221 	if (cursor->current > cursor->limit) {
222 		rc = EINVAL;
223 		goto fail2;
224 	}
225 
226 	if (tlv_tag(cursor) != TLV_TAG_END) {
227 		/* Check current item has space for tag and length */
228 		if (cursor->current > (cursor->limit - 1)) {
229 			cursor->current = NULL;
230 			rc = EFAULT;
231 			goto fail3;
232 		}
233 
234 		/* Check we have value data for current item and an END tag */
235 		if (tlv_next_item_ptr(cursor) > cursor->limit) {
236 			cursor->current = NULL;
237 			rc = EFAULT;
238 			goto fail4;
239 		}
240 	}
241 
242 	return (0);
243 
244 fail4:
245 	EFSYS_PROBE(fail4);
246 fail3:
247 	EFSYS_PROBE(fail3);
248 fail2:
249 	EFSYS_PROBE(fail2);
250 fail1:
251 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
252 
253 	return (rc);
254 }
255 
256 static				efx_rc_t
257 tlv_init_cursor(
258 	__out	tlv_cursor_t	*cursor,
259 	__in	uint32_t	*block,
260 	__in	uint32_t	*limit,
261 	__in	uint32_t	*current)
262 {
263 	cursor->block	= block;
264 	cursor->limit	= limit;
265 
266 	cursor->current	= current;
267 	cursor->end	= NULL;
268 
269 	return (tlv_validate_state(cursor));
270 }
271 
272 static	__checkReturn		efx_rc_t
273 tlv_init_cursor_from_size(
274 	__out	tlv_cursor_t	*cursor,
275 	__in_bcount(size)
276 		uint8_t		*block,
277 	__in	size_t		size)
278 {
279 	uint32_t *limit;
280 	limit = (uint32_t *)(block + size - sizeof (uint32_t));
281 	return (tlv_init_cursor(cursor, (uint32_t *)block,
282 		limit, (uint32_t *)block));
283 }
284 
285 static	__checkReturn		efx_rc_t
286 tlv_init_cursor_at_offset(
287 	__out	tlv_cursor_t	*cursor,
288 	__in_bcount(size)
289 		uint8_t		*block,
290 	__in	size_t		size,
291 	__in	size_t		offset)
292 {
293 	uint32_t *limit;
294 	uint32_t *current;
295 	limit = (uint32_t *)(block + size - sizeof (uint32_t));
296 	current = (uint32_t *)(block + offset);
297 	return (tlv_init_cursor(cursor, (uint32_t *)block, limit, current));
298 }
299 
300 static	__checkReturn		efx_rc_t
301 tlv_require_end(
302 	__inout	tlv_cursor_t	*cursor)
303 {
304 	uint32_t *pos;
305 	efx_rc_t rc;
306 
307 	if (cursor->end == NULL) {
308 		pos = cursor->current;
309 		if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
310 			goto fail1;
311 
312 		cursor->end = cursor->current;
313 		cursor->current = pos;
314 	}
315 
316 	return (0);
317 
318 fail1:
319 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
320 
321 	return (rc);
322 }
323 
324 static				size_t
325 tlv_block_length_used(
326 	__inout	tlv_cursor_t	*cursor)
327 {
328 	efx_rc_t rc;
329 
330 	if ((rc = tlv_validate_state(cursor)) != 0)
331 		goto fail1;
332 
333 	if ((rc = tlv_require_end(cursor)) != 0)
334 		goto fail2;
335 
336 	/* Return space used (including the END tag) */
337 	return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
338 
339 fail2:
340 	EFSYS_PROBE(fail2);
341 fail1:
342 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
343 
344 	return (0);
345 }
346 
347 static		uint32_t *
348 tlv_last_segment_end(
349 	__in	tlv_cursor_t *cursor)
350 {
351 	tlv_cursor_t segment_cursor;
352 	uint32_t *last_segment_end = cursor->block;
353 	uint32_t *segment_start = cursor->block;
354 
355 	/*
356 	 * Go through each segment and check that it has an end tag. If there
357 	 * is no end tag then the previous segment was the last valid one,
358 	 * so return the pointer to its end tag.
359 	 */
360 	for (;;) {
361 		if (tlv_init_cursor(&segment_cursor, segment_start,
362 		    cursor->limit, segment_start) != 0)
363 			break;
364 		if (tlv_require_end(&segment_cursor) != 0)
365 			break;
366 		last_segment_end = segment_cursor.end;
367 		segment_start = segment_cursor.end + 1;
368 	}
369 
370 	return (last_segment_end);
371 }
372 
373 static				uint32_t *
374 tlv_write(
375 	__in			tlv_cursor_t *cursor,
376 	__in			uint32_t tag,
377 	__in_bcount(size)	uint8_t *data,
378 	__in			size_t size)
379 {
380 	uint32_t len = size;
381 	uint32_t *ptr;
382 
383 	ptr = cursor->current;
384 
385 	*ptr++ = __CPU_TO_LE_32(tag);
386 	*ptr++ = __CPU_TO_LE_32(len);
387 
388 	if (len > 0) {
389 		ptr[(len - 1) / sizeof (uint32_t)] = 0;
390 		memcpy(ptr, data, len);
391 		ptr += EFX_P2ROUNDUP(uint32_t, len,
392 		    sizeof (uint32_t)) / sizeof (*ptr);
393 	}
394 
395 	return (ptr);
396 }
397 
398 static	__checkReturn		efx_rc_t
399 tlv_insert(
400 	__inout	tlv_cursor_t	*cursor,
401 	__in	uint32_t	tag,
402 	__in_bcount(size)
403 		uint8_t		*data,
404 	__in	size_t		size)
405 {
406 	unsigned int delta;
407 	uint32_t *last_segment_end;
408 	efx_rc_t rc;
409 
410 	if ((rc = tlv_validate_state(cursor)) != 0)
411 		goto fail1;
412 
413 	if ((rc = tlv_require_end(cursor)) != 0)
414 		goto fail2;
415 
416 	if (tag == TLV_TAG_END) {
417 		rc = EINVAL;
418 		goto fail3;
419 	}
420 
421 	last_segment_end = tlv_last_segment_end(cursor);
422 
423 	delta = TLV_DWORD_COUNT(size);
424 	if (last_segment_end + 1 + delta > cursor->limit) {
425 		rc = ENOSPC;
426 		goto fail4;
427 	}
428 
429 	/* Move data up: new space at cursor->current */
430 	memmove(cursor->current + delta, cursor->current,
431 	    (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
432 
433 	/* Adjust the end pointer */
434 	cursor->end += delta;
435 
436 	/* Write new TLV item */
437 	tlv_write(cursor, tag, data, size);
438 
439 	return (0);
440 
441 fail4:
442 	EFSYS_PROBE(fail4);
443 fail3:
444 	EFSYS_PROBE(fail3);
445 fail2:
446 	EFSYS_PROBE(fail2);
447 fail1:
448 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
449 
450 	return (rc);
451 }
452 
453 static	__checkReturn		efx_rc_t
454 tlv_delete(
455 	__inout	tlv_cursor_t	*cursor)
456 {
457 	unsigned int delta;
458 	uint32_t *last_segment_end;
459 	efx_rc_t rc;
460 
461 	if ((rc = tlv_validate_state(cursor)) != 0)
462 		goto fail1;
463 
464 	if (tlv_tag(cursor) == TLV_TAG_END) {
465 		rc = EINVAL;
466 		goto fail2;
467 	}
468 
469 	delta = TLV_DWORD_COUNT(tlv_length(cursor));
470 
471 	if ((rc = tlv_require_end(cursor)) != 0)
472 		goto fail3;
473 
474 	last_segment_end = tlv_last_segment_end(cursor);
475 
476 	/* Shuffle things down, destroying the item at cursor->current */
477 	memmove(cursor->current, cursor->current + delta,
478 	    (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
479 	/* Zero the new space at the end of the TLV chain */
480 	memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t));
481 	/* Adjust the end pointer */
482 	cursor->end -= delta;
483 
484 	return (0);
485 
486 fail3:
487 	EFSYS_PROBE(fail3);
488 fail2:
489 	EFSYS_PROBE(fail2);
490 fail1:
491 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
492 
493 	return (rc);
494 }
495 
496 static	__checkReturn		efx_rc_t
497 tlv_modify(
498 	__inout	tlv_cursor_t	*cursor,
499 	__in	uint32_t	tag,
500 	__in_bcount(size)
501 		uint8_t		*data,
502 	__in	size_t		size)
503 {
504 	uint32_t *pos;
505 	unsigned int old_ndwords;
506 	unsigned int new_ndwords;
507 	unsigned int delta;
508 	uint32_t *last_segment_end;
509 	efx_rc_t rc;
510 
511 	if ((rc = tlv_validate_state(cursor)) != 0)
512 		goto fail1;
513 
514 	if (tlv_tag(cursor) == TLV_TAG_END) {
515 		rc = EINVAL;
516 		goto fail2;
517 	}
518 	if (tlv_tag(cursor) != tag) {
519 		rc = EINVAL;
520 		goto fail3;
521 	}
522 
523 	old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
524 	new_ndwords = TLV_DWORD_COUNT(size);
525 
526 	if ((rc = tlv_require_end(cursor)) != 0)
527 		goto fail4;
528 
529 	last_segment_end = tlv_last_segment_end(cursor);
530 
531 	if (new_ndwords > old_ndwords) {
532 		/* Expand space used for TLV item */
533 		delta = new_ndwords - old_ndwords;
534 		pos = cursor->current + old_ndwords;
535 
536 		if (last_segment_end + 1 + delta > cursor->limit) {
537 			rc = ENOSPC;
538 			goto fail5;
539 		}
540 
541 		/* Move up: new space at (cursor->current + old_ndwords) */
542 		memmove(pos + delta, pos,
543 		    (last_segment_end + 1 - pos) * sizeof (uint32_t));
544 
545 		/* Adjust the end pointer */
546 		cursor->end += delta;
547 
548 	} else if (new_ndwords < old_ndwords) {
549 		/* Shrink space used for TLV item */
550 		delta = old_ndwords - new_ndwords;
551 		pos = cursor->current + new_ndwords;
552 
553 		/* Move down: remove words at (cursor->current + new_ndwords) */
554 		memmove(pos, pos + delta,
555 		    (last_segment_end + 1 - pos) * sizeof (uint32_t));
556 
557 		/* Zero the new space at the end of the TLV chain */
558 		memset(last_segment_end + 1 - delta, 0,
559 		    delta * sizeof (uint32_t));
560 
561 		/* Adjust the end pointer */
562 		cursor->end -= delta;
563 	}
564 
565 	/* Write new data */
566 	tlv_write(cursor, tag, data, size);
567 
568 	return (0);
569 
570 fail5:
571 	EFSYS_PROBE(fail5);
572 fail4:
573 	EFSYS_PROBE(fail4);
574 fail3:
575 	EFSYS_PROBE(fail3);
576 fail2:
577 	EFSYS_PROBE(fail2);
578 fail1:
579 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
580 
581 	return (rc);
582 }
583 
584 static uint32_t checksum_tlv_partition(
585 	__in	nvram_partition_t *partition)
586 {
587 	tlv_cursor_t *cursor;
588 	uint32_t *ptr;
589 	uint32_t *end;
590 	uint32_t csum;
591 	size_t len;
592 
593 	cursor = &partition->tlv_cursor;
594 	len = tlv_block_length_used(cursor);
595 	EFSYS_ASSERT3U((len & 3), ==, 0);
596 
597 	csum = 0;
598 	ptr = partition->data;
599 	end = &ptr[len >> 2];
600 
601 	while (ptr < end)
602 		csum += __LE_TO_CPU_32(*ptr++);
603 
604 	return (csum);
605 }
606 
607 static	__checkReturn		efx_rc_t
608 tlv_update_partition_len_and_cks(
609 	__in	tlv_cursor_t *cursor)
610 {
611 	efx_rc_t rc;
612 	nvram_partition_t partition;
613 	struct tlv_partition_header *header;
614 	struct tlv_partition_trailer *trailer;
615 	size_t new_len;
616 
617 	/*
618 	 * We just modified the partition, so the total length may not be
619 	 * valid. Don't use tlv_find(), which performs some sanity checks
620 	 * that may fail here.
621 	 */
622 	partition.data = cursor->block;
623 	memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
624 	header = (struct tlv_partition_header *)partition.data;
625 	/* Sanity check. */
626 	if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
627 		rc = EFAULT;
628 		goto fail1;
629 	}
630 	new_len =  tlv_block_length_used(&partition.tlv_cursor);
631 	if (new_len == 0) {
632 		rc = EFAULT;
633 		goto fail2;
634 	}
635 	header->total_length = __CPU_TO_LE_32(new_len);
636 	/* Ensure the modified partition always has a new generation count. */
637 	header->generation = __CPU_TO_LE_32(
638 	    __LE_TO_CPU_32(header->generation) + 1);
639 
640 	trailer = (struct tlv_partition_trailer *)((uint8_t *)header +
641 	    new_len - sizeof (*trailer) - sizeof (uint32_t));
642 	trailer->generation = header->generation;
643 	trailer->checksum = __CPU_TO_LE_32(
644 	    __LE_TO_CPU_32(trailer->checksum) -
645 	    checksum_tlv_partition(&partition));
646 
647 	return (0);
648 
649 fail2:
650 	EFSYS_PROBE(fail2);
651 fail1:
652 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
653 
654 	return (rc);
655 }
656 
657 /* Validate buffer contents (before writing to flash) */
658 	__checkReturn		efx_rc_t
659 ef10_nvram_buffer_validate(
660 	__in			uint32_t partn,
661 	__in_bcount(partn_size)	caddr_t partn_data,
662 	__in			size_t partn_size)
663 {
664 	tlv_cursor_t cursor;
665 	struct tlv_partition_header *header;
666 	struct tlv_partition_trailer *trailer;
667 	size_t total_length;
668 	uint32_t cksum;
669 	int pos;
670 	efx_rc_t rc;
671 
672 	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
673 
674 	if ((partn_data == NULL) || (partn_size == 0)) {
675 		rc = EINVAL;
676 		goto fail1;
677 	}
678 
679 	/* The partition header must be the first item (at offset zero) */
680 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
681 		    partn_size)) != 0) {
682 		rc = EFAULT;
683 		goto fail2;
684 	}
685 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
686 		rc = EINVAL;
687 		goto fail3;
688 	}
689 	header = (struct tlv_partition_header *)tlv_item(&cursor);
690 
691 	/* Check TLV partition length (includes the END tag) */
692 	total_length = __LE_TO_CPU_32(header->total_length);
693 	if (total_length > partn_size) {
694 		rc = EFBIG;
695 		goto fail4;
696 	}
697 
698 	/* Check partition header matches partn */
699 	if (__LE_TO_CPU_16(header->type_id) != partn) {
700 		rc = EINVAL;
701 		goto fail5;
702 	}
703 
704 	/* Check partition ends with PARTITION_TRAILER and END tags */
705 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
706 		rc = EINVAL;
707 		goto fail6;
708 	}
709 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
710 
711 	if ((rc = tlv_advance(&cursor)) != 0) {
712 		rc = EINVAL;
713 		goto fail7;
714 	}
715 	if (tlv_tag(&cursor) != TLV_TAG_END) {
716 		rc = EINVAL;
717 		goto fail8;
718 	}
719 
720 	/* Check generation counts are consistent */
721 	if (trailer->generation != header->generation) {
722 		rc = EINVAL;
723 		goto fail9;
724 	}
725 
726 	/* Verify partition checksum */
727 	cksum = 0;
728 	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
729 		cksum += *((uint32_t *)(partn_data + pos));
730 	}
731 	if (cksum != 0) {
732 		rc = EINVAL;
733 		goto fail10;
734 	}
735 
736 	return (0);
737 
738 fail10:
739 	EFSYS_PROBE(fail10);
740 fail9:
741 	EFSYS_PROBE(fail9);
742 fail8:
743 	EFSYS_PROBE(fail8);
744 fail7:
745 	EFSYS_PROBE(fail7);
746 fail6:
747 	EFSYS_PROBE(fail6);
748 fail5:
749 	EFSYS_PROBE(fail5);
750 fail4:
751 	EFSYS_PROBE(fail4);
752 fail3:
753 	EFSYS_PROBE(fail3);
754 fail2:
755 	EFSYS_PROBE(fail2);
756 fail1:
757 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
758 
759 	return (rc);
760 }
761 
762 			void
763 ef10_nvram_buffer_init(
764 	__out_bcount(buffer_size)
765 				caddr_t bufferp,
766 	__in			size_t buffer_size)
767 {
768 	uint32_t *buf = (uint32_t *)bufferp;
769 
770 	memset(buf, 0xff, buffer_size);
771 
772 	tlv_init_block(buf);
773 }
774 
775 	__checkReturn		efx_rc_t
776 ef10_nvram_buffer_create(
777 	__in			uint32_t partn_type,
778 	__out_bcount(partn_size)
779 				caddr_t partn_data,
780 	__in			size_t partn_size)
781 {
782 	uint32_t *buf = (uint32_t *)partn_data;
783 	efx_rc_t rc;
784 	tlv_cursor_t cursor;
785 	struct tlv_partition_header header;
786 	struct tlv_partition_trailer trailer;
787 
788 	unsigned int min_buf_size = sizeof (struct tlv_partition_header) +
789 	    sizeof (struct tlv_partition_trailer);
790 	if (partn_size < min_buf_size) {
791 		rc = EINVAL;
792 		goto fail1;
793 	}
794 
795 	ef10_nvram_buffer_init(partn_data, partn_size);
796 
797 	if ((rc = tlv_init_cursor(&cursor, buf,
798 	    (uint32_t *)((uint8_t *)buf + partn_size),
799 	    buf)) != 0) {
800 		goto fail2;
801 	}
802 
803 	header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
804 	header.length = __CPU_TO_LE_32(sizeof (header) - 8);
805 	header.type_id = __CPU_TO_LE_16(partn_type);
806 	header.preset = 0;
807 	header.generation = __CPU_TO_LE_32(1);
808 	header.total_length = 0;  /* This will be fixed below. */
809 	if ((rc = tlv_insert(
810 	    &cursor, TLV_TAG_PARTITION_HEADER,
811 	    (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
812 		goto fail3;
813 	if ((rc = tlv_advance(&cursor)) != 0)
814 		goto fail4;
815 
816 	trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
817 	trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
818 	trailer.generation = header.generation;
819 	trailer.checksum = 0;  /* This will be fixed below. */
820 	if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
821 	    (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
822 		goto fail5;
823 
824 	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
825 		goto fail6;
826 
827 	/* Check that the partition is valid. */
828 	if ((rc = ef10_nvram_buffer_validate(partn_type,
829 	    partn_data, partn_size)) != 0)
830 		goto fail7;
831 
832 	return (0);
833 
834 fail7:
835 	EFSYS_PROBE(fail7);
836 fail6:
837 	EFSYS_PROBE(fail6);
838 fail5:
839 	EFSYS_PROBE(fail5);
840 fail4:
841 	EFSYS_PROBE(fail4);
842 fail3:
843 	EFSYS_PROBE(fail3);
844 fail2:
845 	EFSYS_PROBE(fail2);
846 fail1:
847 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
848 
849 	return (rc);
850 }
851 
852 static			uint32_t
853 byte_offset(
854 	__in		uint32_t *position,
855 	__in		uint32_t *base)
856 {
857 	return (uint32_t)((uint8_t *)position - (uint8_t *)base);
858 }
859 
860 	__checkReturn		efx_rc_t
861 ef10_nvram_buffer_find_item_start(
862 	__in_bcount(buffer_size)
863 				caddr_t bufferp,
864 	__in			size_t buffer_size,
865 	__out			uint32_t *startp)
866 {
867 	/* Read past partition header to find start address of the first key */
868 	tlv_cursor_t cursor;
869 	efx_rc_t rc;
870 
871 	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
872 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
873 			buffer_size)) != 0) {
874 		rc = EFAULT;
875 		goto fail1;
876 	}
877 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
878 		rc = EINVAL;
879 		goto fail2;
880 	}
881 
882 	if ((rc = tlv_advance(&cursor)) != 0) {
883 		rc = EINVAL;
884 		goto fail3;
885 	}
886 	*startp = byte_offset(cursor.current, cursor.block);
887 
888 	if ((rc = tlv_require_end(&cursor)) != 0)
889 		goto fail4;
890 
891 	return (0);
892 
893 fail4:
894 	EFSYS_PROBE(fail4);
895 fail3:
896 	EFSYS_PROBE(fail3);
897 fail2:
898 	EFSYS_PROBE(fail2);
899 fail1:
900 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
901 
902 	return (rc);
903 }
904 
905 	__checkReturn		efx_rc_t
906 ef10_nvram_buffer_find_end(
907 	__in_bcount(buffer_size)
908 				caddr_t bufferp,
909 	__in			size_t buffer_size,
910 	__in			uint32_t offset,
911 	__out			uint32_t *endp)
912 {
913 	/* Read to end of partition */
914 	tlv_cursor_t cursor;
915 	efx_rc_t rc;
916 	uint32_t *segment_used;
917 
918 	_NOTE(ARGUNUSED(offset))
919 
920 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
921 			buffer_size)) != 0) {
922 		rc = EFAULT;
923 		goto fail1;
924 	}
925 
926 	segment_used = cursor.block;
927 
928 	/*
929 	 * Go through each segment and check that it has an end tag. If there
930 	 * is no end tag then the previous segment was the last valid one,
931 	 * so return the used space including that end tag.
932 	 */
933 	while (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
934 		if (tlv_require_end(&cursor) != 0) {
935 			if (segment_used == cursor.block) {
936 				/*
937 				 * First segment is corrupt, so there is
938 				 * no valid data in partition.
939 				 */
940 				rc = EINVAL;
941 				goto fail2;
942 			}
943 			break;
944 		}
945 		segment_used = cursor.end + 1;
946 
947 		cursor.current = segment_used;
948 	}
949 	/* Return space used (including the END tag) */
950 	*endp = (segment_used - cursor.block) * sizeof (uint32_t);
951 
952 	return (0);
953 
954 fail2:
955 	EFSYS_PROBE(fail2);
956 fail1:
957 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
958 
959 	return (rc);
960 }
961 
962 	__checkReturn	__success(return != B_FALSE)	boolean_t
963 ef10_nvram_buffer_find_item(
964 	__in_bcount(buffer_size)
965 				caddr_t bufferp,
966 	__in			size_t buffer_size,
967 	__in			uint32_t offset,
968 	__out			uint32_t *startp,
969 	__out			uint32_t *lengthp)
970 {
971 	/* Find TLV at offset and return key start and length */
972 	tlv_cursor_t cursor;
973 	uint8_t *key;
974 	uint32_t tag;
975 
976 	if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
977 			buffer_size, offset) != 0) {
978 		return (B_FALSE);
979 	}
980 
981 	while ((key = tlv_item(&cursor)) != NULL) {
982 		tag = tlv_tag(&cursor);
983 		if (tag == TLV_TAG_PARTITION_HEADER ||
984 		    tag == TLV_TAG_PARTITION_TRAILER) {
985 			if (tlv_advance(&cursor) != 0) {
986 				break;
987 			}
988 			continue;
989 		}
990 		*startp = byte_offset(cursor.current, cursor.block);
991 		*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
992 		    cursor.current);
993 		return (B_TRUE);
994 	}
995 
996 	return (B_FALSE);
997 }
998 
999 	__checkReturn		efx_rc_t
1000 ef10_nvram_buffer_peek_item(
1001 	__in_bcount(buffer_size)
1002 				caddr_t bufferp,
1003 	__in			size_t buffer_size,
1004 	__in			uint32_t offset,
1005 	__out			uint32_t *tagp,
1006 	__out			uint32_t *lengthp,
1007 	__out			uint32_t *value_offsetp)
1008 {
1009 	efx_rc_t rc;
1010 	tlv_cursor_t cursor;
1011 	uint32_t tag;
1012 
1013 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1014 			buffer_size, offset)) != 0) {
1015 		goto fail1;
1016 	}
1017 
1018 	tag = tlv_tag(&cursor);
1019 	*tagp = tag;
1020 	if (tag == TLV_TAG_END) {
1021 		/*
1022 		 * To allow stepping over the END tag, report the full tag
1023 		 * length and a zero length value.
1024 		 */
1025 		*lengthp = sizeof (tag);
1026 		*value_offsetp = sizeof (tag);
1027 	} else {
1028 		*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1029 			    cursor.current);
1030 		*value_offsetp = byte_offset((uint32_t *)tlv_value(&cursor),
1031 			    cursor.current);
1032 	}
1033 	return (0);
1034 
1035 fail1:
1036 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1037 
1038 	return (rc);
1039 }
1040 
1041 	__checkReturn		efx_rc_t
1042 ef10_nvram_buffer_get_item(
1043 	__in_bcount(buffer_size)
1044 				caddr_t bufferp,
1045 	__in			size_t buffer_size,
1046 	__in			uint32_t offset,
1047 	__in			uint32_t length,
1048 	__out			uint32_t *tagp,
1049 	__out_bcount_part(value_max_size, *lengthp)
1050 				caddr_t valuep,
1051 	__in			size_t value_max_size,
1052 	__out			uint32_t *lengthp)
1053 {
1054 	efx_rc_t rc;
1055 	tlv_cursor_t cursor;
1056 	uint32_t value_length;
1057 
1058 	if (buffer_size < (offset + length)) {
1059 		rc = ENOSPC;
1060 		goto fail1;
1061 	}
1062 
1063 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1064 			buffer_size, offset)) != 0) {
1065 		goto fail2;
1066 	}
1067 
1068 	value_length = tlv_length(&cursor);
1069 	if (value_max_size < value_length) {
1070 		rc = ENOSPC;
1071 		goto fail3;
1072 	}
1073 	memcpy(valuep, tlv_value(&cursor), value_length);
1074 
1075 	*tagp = tlv_tag(&cursor);
1076 	*lengthp = value_length;
1077 
1078 	return (0);
1079 
1080 fail3:
1081 	EFSYS_PROBE(fail3);
1082 fail2:
1083 	EFSYS_PROBE(fail2);
1084 fail1:
1085 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1086 
1087 	return (rc);
1088 }
1089 
1090 	__checkReturn		efx_rc_t
1091 ef10_nvram_buffer_insert_item(
1092 	__in_bcount(buffer_size)
1093 				caddr_t bufferp,
1094 	__in			size_t buffer_size,
1095 	__in			uint32_t offset,
1096 	__in			uint32_t tag,
1097 	__in_bcount(length)	caddr_t valuep,
1098 	__in			uint32_t length,
1099 	__out			uint32_t *lengthp)
1100 {
1101 	efx_rc_t rc;
1102 	tlv_cursor_t cursor;
1103 
1104 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1105 			buffer_size, offset)) != 0) {
1106 		goto fail1;
1107 	}
1108 
1109 	rc = tlv_insert(&cursor, tag, (uint8_t *)valuep, length);
1110 
1111 	if (rc != 0)
1112 		goto fail2;
1113 
1114 	*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1115 		    cursor.current);
1116 
1117 	return (0);
1118 
1119 fail2:
1120 	EFSYS_PROBE(fail2);
1121 fail1:
1122 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1123 
1124 	return (rc);
1125 }
1126 
1127 	__checkReturn		efx_rc_t
1128 ef10_nvram_buffer_modify_item(
1129 	__in_bcount(buffer_size)
1130 				caddr_t bufferp,
1131 	__in			size_t buffer_size,
1132 	__in			uint32_t offset,
1133 	__in			uint32_t tag,
1134 	__in_bcount(length)	caddr_t valuep,
1135 	__in			uint32_t length,
1136 	__out			uint32_t *lengthp)
1137 {
1138 	efx_rc_t rc;
1139 	tlv_cursor_t cursor;
1140 
1141 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1142 			buffer_size, offset)) != 0) {
1143 		goto fail1;
1144 	}
1145 
1146 	rc = tlv_modify(&cursor, tag, (uint8_t *)valuep, length);
1147 
1148 	if (rc != 0) {
1149 		goto fail2;
1150 	}
1151 
1152 	*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1153 		    cursor.current);
1154 
1155 	return (0);
1156 
1157 fail2:
1158 	EFSYS_PROBE(fail2);
1159 fail1:
1160 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1161 
1162 	return (rc);
1163 }
1164 
1165 	__checkReturn		efx_rc_t
1166 ef10_nvram_buffer_delete_item(
1167 	__in_bcount(buffer_size)
1168 				caddr_t bufferp,
1169 	__in			size_t buffer_size,
1170 	__in			uint32_t offset,
1171 	__in			uint32_t length,
1172 	__in			uint32_t end)
1173 {
1174 	efx_rc_t rc;
1175 	tlv_cursor_t cursor;
1176 
1177 	_NOTE(ARGUNUSED(length, end))
1178 
1179 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1180 			buffer_size, offset)) != 0) {
1181 		goto fail1;
1182 	}
1183 
1184 	if ((rc = tlv_delete(&cursor)) != 0)
1185 		goto fail2;
1186 
1187 	return (0);
1188 
1189 fail2:
1190 	EFSYS_PROBE(fail2);
1191 fail1:
1192 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1193 
1194 	return (rc);
1195 }
1196 
1197 	__checkReturn		efx_rc_t
1198 ef10_nvram_buffer_finish(
1199 	__in_bcount(buffer_size)
1200 				caddr_t bufferp,
1201 	__in			size_t buffer_size)
1202 {
1203 	efx_rc_t rc;
1204 	tlv_cursor_t cursor;
1205 
1206 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
1207 			buffer_size)) != 0) {
1208 		rc = EFAULT;
1209 		goto fail1;
1210 	}
1211 
1212 	if ((rc = tlv_require_end(&cursor)) != 0)
1213 		goto fail2;
1214 
1215 	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
1216 		goto fail3;
1217 
1218 	return (0);
1219 
1220 fail3:
1221 	EFSYS_PROBE(fail3);
1222 fail2:
1223 	EFSYS_PROBE(fail2);
1224 fail1:
1225 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1226 
1227 	return (rc);
1228 }
1229 
1230 /*
1231  * Read and validate a segment from a partition. A segment is a complete
1232  * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
1233  * be multiple segments in a partition, so seg_offset allows segments
1234  * beyond the first to be read.
1235  */
1236 static	__checkReturn			efx_rc_t
1237 ef10_nvram_read_tlv_segment(
1238 	__in				efx_nic_t *enp,
1239 	__in				uint32_t partn,
1240 	__in				size_t seg_offset,
1241 	__in_bcount(max_seg_size)	caddr_t seg_data,
1242 	__in				size_t max_seg_size)
1243 {
1244 	tlv_cursor_t cursor;
1245 	struct tlv_partition_header *header;
1246 	struct tlv_partition_trailer *trailer;
1247 	size_t total_length;
1248 	uint32_t cksum;
1249 	int pos;
1250 	efx_rc_t rc;
1251 
1252 	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
1253 
1254 	if ((seg_data == NULL) || (max_seg_size == 0)) {
1255 		rc = EINVAL;
1256 		goto fail1;
1257 	}
1258 
1259 	/* Read initial chunk of the segment, starting at offset */
1260 	if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
1261 		    EF10_NVRAM_CHUNK,
1262 		    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
1263 		goto fail2;
1264 	}
1265 
1266 	/* A PARTITION_HEADER tag must be the first item at the given offset */
1267 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1268 		    max_seg_size)) != 0) {
1269 		rc = EFAULT;
1270 		goto fail3;
1271 	}
1272 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1273 		rc = EINVAL;
1274 		goto fail4;
1275 	}
1276 	header = (struct tlv_partition_header *)tlv_item(&cursor);
1277 
1278 	/* Check TLV segment length (includes the END tag) */
1279 	total_length = __LE_TO_CPU_32(header->total_length);
1280 	if (total_length > max_seg_size) {
1281 		rc = EFBIG;
1282 		goto fail5;
1283 	}
1284 
1285 	/* Read the remaining segment content */
1286 	if (total_length > EF10_NVRAM_CHUNK) {
1287 		if ((rc = ef10_nvram_partn_read_mode(enp, partn,
1288 			    seg_offset + EF10_NVRAM_CHUNK,
1289 			    seg_data + EF10_NVRAM_CHUNK,
1290 			    total_length - EF10_NVRAM_CHUNK,
1291 			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
1292 			goto fail6;
1293 	}
1294 
1295 	/* Check segment ends with PARTITION_TRAILER and END tags */
1296 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1297 		rc = EINVAL;
1298 		goto fail7;
1299 	}
1300 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1301 
1302 	if ((rc = tlv_advance(&cursor)) != 0) {
1303 		rc = EINVAL;
1304 		goto fail8;
1305 	}
1306 	if (tlv_tag(&cursor) != TLV_TAG_END) {
1307 		rc = EINVAL;
1308 		goto fail9;
1309 	}
1310 
1311 	/* Check data read from segment is consistent */
1312 	if (trailer->generation != header->generation) {
1313 		/*
1314 		 * The partition data may have been modified between successive
1315 		 * MCDI NVRAM_READ requests by the MC or another PCI function.
1316 		 *
1317 		 * The caller must retry to obtain consistent partition data.
1318 		 */
1319 		rc = EAGAIN;
1320 		goto fail10;
1321 	}
1322 
1323 	/* Verify segment checksum */
1324 	cksum = 0;
1325 	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
1326 		cksum += *((uint32_t *)(seg_data + pos));
1327 	}
1328 	if (cksum != 0) {
1329 		rc = EINVAL;
1330 		goto fail11;
1331 	}
1332 
1333 	return (0);
1334 
1335 fail11:
1336 	EFSYS_PROBE(fail11);
1337 fail10:
1338 	EFSYS_PROBE(fail10);
1339 fail9:
1340 	EFSYS_PROBE(fail9);
1341 fail8:
1342 	EFSYS_PROBE(fail8);
1343 fail7:
1344 	EFSYS_PROBE(fail7);
1345 fail6:
1346 	EFSYS_PROBE(fail6);
1347 fail5:
1348 	EFSYS_PROBE(fail5);
1349 fail4:
1350 	EFSYS_PROBE(fail4);
1351 fail3:
1352 	EFSYS_PROBE(fail3);
1353 fail2:
1354 	EFSYS_PROBE(fail2);
1355 fail1:
1356 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1357 
1358 	return (rc);
1359 }
1360 
1361 /*
1362  * Read a single TLV item from a host memory
1363  * buffer containing a TLV formatted segment.
1364  */
1365 	__checkReturn		efx_rc_t
1366 ef10_nvram_buf_read_tlv(
1367 	__in				efx_nic_t *enp,
1368 	__in_bcount(max_seg_size)	caddr_t seg_data,
1369 	__in				size_t max_seg_size,
1370 	__in				uint32_t tag,
1371 	__deref_out_bcount_opt(*sizep)	caddr_t *datap,
1372 	__out				size_t *sizep)
1373 {
1374 	tlv_cursor_t cursor;
1375 	caddr_t data;
1376 	size_t length;
1377 	caddr_t value;
1378 	efx_rc_t rc;
1379 
1380 	_NOTE(ARGUNUSED(enp))
1381 
1382 	if ((seg_data == NULL) || (max_seg_size == 0)) {
1383 		rc = EINVAL;
1384 		goto fail1;
1385 	}
1386 
1387 	/* Find requested TLV tag in segment data */
1388 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1389 		    max_seg_size)) != 0) {
1390 		rc = EFAULT;
1391 		goto fail2;
1392 	}
1393 	if ((rc = tlv_find(&cursor, tag)) != 0) {
1394 		rc = ENOENT;
1395 		goto fail3;
1396 	}
1397 	value = (caddr_t)tlv_value(&cursor);
1398 	length = tlv_length(&cursor);
1399 
1400 	if (length == 0)
1401 		data = NULL;
1402 	else {
1403 		/* Copy out data from TLV item */
1404 		EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
1405 		if (data == NULL) {
1406 			rc = ENOMEM;
1407 			goto fail4;
1408 		}
1409 		memcpy(data, value, length);
1410 	}
1411 
1412 	*datap = data;
1413 	*sizep = length;
1414 
1415 	return (0);
1416 
1417 fail4:
1418 	EFSYS_PROBE(fail4);
1419 fail3:
1420 	EFSYS_PROBE(fail3);
1421 fail2:
1422 	EFSYS_PROBE(fail2);
1423 fail1:
1424 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1425 
1426 	return (rc);
1427 }
1428 
1429 /* Read a single TLV item from the first segment in a TLV formatted partition */
1430 	__checkReturn		efx_rc_t
1431 ef10_nvram_partn_read_tlv(
1432 	__in					efx_nic_t *enp,
1433 	__in					uint32_t partn,
1434 	__in					uint32_t tag,
1435 	__deref_out_bcount_opt(*seg_sizep)	caddr_t *seg_datap,
1436 	__out					size_t *seg_sizep)
1437 {
1438 	caddr_t seg_data = NULL;
1439 	size_t partn_size = 0;
1440 	size_t length;
1441 	caddr_t data;
1442 	int retry;
1443 	efx_rc_t rc;
1444 
1445 	/* Allocate sufficient memory for the entire partition */
1446 	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1447 		goto fail1;
1448 
1449 	if (partn_size == 0) {
1450 		rc = ENOENT;
1451 		goto fail2;
1452 	}
1453 
1454 	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
1455 	if (seg_data == NULL) {
1456 		rc = ENOMEM;
1457 		goto fail3;
1458 	}
1459 
1460 	/*
1461 	 * Read the first segment in a TLV partition. Retry until consistent
1462 	 * segment contents are returned. Inconsistent data may be read if:
1463 	 *  a) the segment contents are invalid
1464 	 *  b) the MC has rebooted while we were reading the partition
1465 	 *  c) the partition has been modified while we were reading it
1466 	 * Limit retry attempts to ensure forward progress.
1467 	 */
1468 	retry = 10;
1469 	do {
1470 		if ((rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
1471 		    seg_data, partn_size)) != 0)
1472 			--retry;
1473 	} while ((rc == EAGAIN) && (retry > 0));
1474 
1475 	if (rc != 0) {
1476 		/* Failed to obtain consistent segment data */
1477 		if (rc == EAGAIN)
1478 			rc = EIO;
1479 
1480 		goto fail4;
1481 	}
1482 
1483 	if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
1484 		    tag, &data, &length)) != 0)
1485 		goto fail5;
1486 
1487 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1488 
1489 	*seg_datap = data;
1490 	*seg_sizep = length;
1491 
1492 	return (0);
1493 
1494 fail5:
1495 	EFSYS_PROBE(fail5);
1496 fail4:
1497 	EFSYS_PROBE(fail4);
1498 
1499 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1500 fail3:
1501 	EFSYS_PROBE(fail3);
1502 fail2:
1503 	EFSYS_PROBE(fail2);
1504 fail1:
1505 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1506 
1507 	return (rc);
1508 }
1509 
1510 /* Compute the size of a segment. */
1511 	static	__checkReturn	efx_rc_t
1512 ef10_nvram_buf_segment_size(
1513 	__in			caddr_t seg_data,
1514 	__in			size_t max_seg_size,
1515 	__out			size_t *seg_sizep)
1516 {
1517 	efx_rc_t rc;
1518 	tlv_cursor_t cursor;
1519 	struct tlv_partition_header *header;
1520 	uint32_t cksum;
1521 	int pos;
1522 	uint32_t *end_tag_position;
1523 	uint32_t segment_length;
1524 
1525 	/* A PARTITION_HEADER tag must be the first item at the given offset */
1526 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1527 		    max_seg_size)) != 0) {
1528 		rc = EFAULT;
1529 		goto fail1;
1530 	}
1531 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1532 		rc = EINVAL;
1533 		goto fail2;
1534 	}
1535 	header = (struct tlv_partition_header *)tlv_item(&cursor);
1536 
1537 	/* Check TLV segment length (includes the END tag) */
1538 	*seg_sizep = __LE_TO_CPU_32(header->total_length);
1539 	if (*seg_sizep > max_seg_size) {
1540 		rc = EFBIG;
1541 		goto fail3;
1542 	}
1543 
1544 	/* Check segment ends with PARTITION_TRAILER and END tags */
1545 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1546 		rc = EINVAL;
1547 		goto fail4;
1548 	}
1549 
1550 	if ((rc = tlv_advance(&cursor)) != 0) {
1551 		rc = EINVAL;
1552 		goto fail5;
1553 	}
1554 	if (tlv_tag(&cursor) != TLV_TAG_END) {
1555 		rc = EINVAL;
1556 		goto fail6;
1557 	}
1558 	end_tag_position = cursor.current;
1559 
1560 	/* Verify segment checksum */
1561 	cksum = 0;
1562 	for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
1563 		cksum += *((uint32_t *)(seg_data + pos));
1564 	}
1565 	if (cksum != 0) {
1566 		rc = EINVAL;
1567 		goto fail7;
1568 	}
1569 
1570 	/*
1571 	 * Calculate total length from HEADER to END tags and compare to
1572 	 * max_seg_size and the total_length field in the HEADER tag.
1573 	 */
1574 	segment_length = tlv_block_length_used(&cursor);
1575 
1576 	if (segment_length > max_seg_size) {
1577 		rc = EINVAL;
1578 		goto fail8;
1579 	}
1580 
1581 	if (segment_length != *seg_sizep) {
1582 		rc = EINVAL;
1583 		goto fail9;
1584 	}
1585 
1586 	/* Skip over the first HEADER tag. */
1587 	rc = tlv_rewind(&cursor);
1588 	rc = tlv_advance(&cursor);
1589 
1590 	while (rc == 0) {
1591 		if (tlv_tag(&cursor) == TLV_TAG_END) {
1592 			/* Check that the END tag is the one found earlier. */
1593 			if (cursor.current != end_tag_position)
1594 				goto fail10;
1595 			break;
1596 		}
1597 		/* Check for duplicate HEADER tags before the END tag. */
1598 		if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
1599 			rc = EINVAL;
1600 			goto fail11;
1601 		}
1602 
1603 		rc = tlv_advance(&cursor);
1604 	}
1605 	if (rc != 0)
1606 		goto fail12;
1607 
1608 	return (0);
1609 
1610 fail12:
1611 	EFSYS_PROBE(fail12);
1612 fail11:
1613 	EFSYS_PROBE(fail11);
1614 fail10:
1615 	EFSYS_PROBE(fail10);
1616 fail9:
1617 	EFSYS_PROBE(fail9);
1618 fail8:
1619 	EFSYS_PROBE(fail8);
1620 fail7:
1621 	EFSYS_PROBE(fail7);
1622 fail6:
1623 	EFSYS_PROBE(fail6);
1624 fail5:
1625 	EFSYS_PROBE(fail5);
1626 fail4:
1627 	EFSYS_PROBE(fail4);
1628 fail3:
1629 	EFSYS_PROBE(fail3);
1630 fail2:
1631 	EFSYS_PROBE(fail2);
1632 fail1:
1633 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1634 
1635 	return (rc);
1636 }
1637 
1638 /*
1639  * Add or update a single TLV item in a host memory buffer containing a TLV
1640  * formatted segment. Historically partitions consisted of only one segment.
1641  */
1642 	__checkReturn			efx_rc_t
1643 ef10_nvram_buf_write_tlv(
1644 	__inout_bcount(max_seg_size)	caddr_t seg_data,
1645 	__in				size_t max_seg_size,
1646 	__in				uint32_t tag,
1647 	__in_bcount(tag_size)		caddr_t tag_data,
1648 	__in				size_t tag_size,
1649 	__out				size_t *total_lengthp)
1650 {
1651 	tlv_cursor_t cursor;
1652 	struct tlv_partition_header *header;
1653 	struct tlv_partition_trailer *trailer;
1654 	uint32_t generation;
1655 	uint32_t cksum;
1656 	int pos;
1657 	efx_rc_t rc;
1658 
1659 	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
1660 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1661 			max_seg_size)) != 0) {
1662 		rc = EFAULT;
1663 		goto fail1;
1664 	}
1665 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1666 		rc = EINVAL;
1667 		goto fail2;
1668 	}
1669 	header = (struct tlv_partition_header *)tlv_item(&cursor);
1670 
1671 	/* Update the TLV chain to contain the new data */
1672 	if ((rc = tlv_find(&cursor, tag)) == 0) {
1673 		/* Modify existing TLV item */
1674 		if ((rc = tlv_modify(&cursor, tag,
1675 			    (uint8_t *)tag_data, tag_size)) != 0)
1676 			goto fail3;
1677 	} else {
1678 		/* Insert a new TLV item before the PARTITION_TRAILER */
1679 		rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1680 		if (rc != 0) {
1681 			rc = EINVAL;
1682 			goto fail4;
1683 		}
1684 		if ((rc = tlv_insert(&cursor, tag,
1685 			    (uint8_t *)tag_data, tag_size)) != 0) {
1686 			rc = EINVAL;
1687 			goto fail5;
1688 		}
1689 	}
1690 
1691 	/* Find the trailer tag */
1692 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1693 		rc = EINVAL;
1694 		goto fail6;
1695 	}
1696 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1697 
1698 	/* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1699 	*total_lengthp = tlv_block_length_used(&cursor);
1700 	if (*total_lengthp > max_seg_size) {
1701 		rc = ENOSPC;
1702 		goto fail7;
1703 	}
1704 	generation = __LE_TO_CPU_32(header->generation) + 1;
1705 
1706 	header->total_length	= __CPU_TO_LE_32(*total_lengthp);
1707 	header->generation	= __CPU_TO_LE_32(generation);
1708 	trailer->generation	= __CPU_TO_LE_32(generation);
1709 
1710 	/* Recompute PARTITION_TRAILER checksum */
1711 	trailer->checksum = 0;
1712 	cksum = 0;
1713 	for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1714 		cksum += *((uint32_t *)(seg_data + pos));
1715 	}
1716 	trailer->checksum = ~cksum + 1;
1717 
1718 	return (0);
1719 
1720 fail7:
1721 	EFSYS_PROBE(fail7);
1722 fail6:
1723 	EFSYS_PROBE(fail6);
1724 fail5:
1725 	EFSYS_PROBE(fail5);
1726 fail4:
1727 	EFSYS_PROBE(fail4);
1728 fail3:
1729 	EFSYS_PROBE(fail3);
1730 fail2:
1731 	EFSYS_PROBE(fail2);
1732 fail1:
1733 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1734 
1735 	return (rc);
1736 }
1737 
1738 /*
1739  * Add or update a single TLV item in the first segment of a TLV formatted
1740  * dynamic config partition. The first segment is the current active
1741  * configuration.
1742  */
1743 	__checkReturn		efx_rc_t
1744 ef10_nvram_partn_write_tlv(
1745 	__in			efx_nic_t *enp,
1746 	__in			uint32_t partn,
1747 	__in			uint32_t tag,
1748 	__in_bcount(size)	caddr_t data,
1749 	__in			size_t size)
1750 {
1751 	return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1752 	    size, B_FALSE);
1753 }
1754 
1755 /*
1756  * Read a segment from nvram at the given offset into a buffer (segment_data)
1757  * and optionally write a new tag to it.
1758  */
1759 static	__checkReturn		efx_rc_t
1760 ef10_nvram_segment_write_tlv(
1761 	__in			efx_nic_t *enp,
1762 	__in			uint32_t partn,
1763 	__in			uint32_t tag,
1764 	__in_bcount(size)	caddr_t data,
1765 	__in			size_t size,
1766 	__inout			caddr_t *seg_datap,
1767 	__inout			size_t *partn_offsetp,
1768 	__inout			size_t *src_remain_lenp,
1769 	__inout			size_t *dest_remain_lenp,
1770 	__in			boolean_t write)
1771 {
1772 	efx_rc_t rc;
1773 	efx_rc_t status;
1774 	size_t original_segment_size;
1775 	size_t modified_segment_size;
1776 
1777 	/*
1778 	 * Read the segment from NVRAM into the segment_data buffer and validate
1779 	 * it, returning if it does not validate. This is not a failure unless
1780 	 * this is the first segment in a partition. In this case the caller
1781 	 * must propagate the error.
1782 	 */
1783 	status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1784 	    *seg_datap, *src_remain_lenp);
1785 	if (status != 0) {
1786 		rc = EINVAL;
1787 		goto fail1;
1788 	}
1789 
1790 	status = ef10_nvram_buf_segment_size(*seg_datap,
1791 	    *src_remain_lenp, &original_segment_size);
1792 	if (status != 0) {
1793 		rc = EINVAL;
1794 		goto fail2;
1795 	}
1796 
1797 	if (write) {
1798 		/* Update the contents of the segment in the buffer */
1799 		if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1800 			*dest_remain_lenp, tag, data, size,
1801 			&modified_segment_size)) != 0) {
1802 			goto fail3;
1803 		}
1804 		*dest_remain_lenp -= modified_segment_size;
1805 		*seg_datap += modified_segment_size;
1806 	} else {
1807 		/*
1808 		 * We won't modify this segment, but still need to update the
1809 		 * remaining lengths and pointers.
1810 		 */
1811 		*dest_remain_lenp -= original_segment_size;
1812 		*seg_datap += original_segment_size;
1813 	}
1814 
1815 	*partn_offsetp += original_segment_size;
1816 	*src_remain_lenp -= original_segment_size;
1817 
1818 	return (0);
1819 
1820 fail3:
1821 	EFSYS_PROBE(fail3);
1822 fail2:
1823 	EFSYS_PROBE(fail2);
1824 fail1:
1825 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1826 
1827 	return (rc);
1828 }
1829 
1830 /*
1831  * Add or update a single TLV item in either the first segment or in all
1832  * segments in a TLV formatted dynamic config partition. Dynamic config
1833  * partitions on boards that support RFID are divided into a number of segments,
1834  * each formatted like a partition, with header, trailer and end tags. The first
1835  * segment is the current active configuration.
1836  *
1837  * The segments are initialised by manftest and each contain a different
1838  * configuration e.g. firmware variant. The firmware can be instructed
1839  * via RFID to copy a segment to replace the first segment, hence changing the
1840  * active configuration.  This allows ops to change the configuration of a board
1841  * prior to shipment using RFID.
1842  *
1843  * Changes to the dynamic config may need to be written to all segments (e.g.
1844  * firmware versions) or just the first segment (changes to the active
1845  * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1846  * If only the first segment is written the code still needs to be aware of the
1847  * possible presence of subsequent segments as writing to a segment may cause
1848  * its size to increase, which would overwrite the subsequent segments and
1849  * invalidate them.
1850  */
1851 	__checkReturn		efx_rc_t
1852 ef10_nvram_partn_write_segment_tlv(
1853 	__in			efx_nic_t *enp,
1854 	__in			uint32_t partn,
1855 	__in			uint32_t tag,
1856 	__in_bcount(size)	caddr_t data,
1857 	__in			size_t size,
1858 	__in			boolean_t all_segments)
1859 {
1860 	size_t partn_size = 0;
1861 	caddr_t partn_data;
1862 	size_t total_length = 0;
1863 	efx_rc_t rc;
1864 	size_t current_offset = 0;
1865 	size_t remaining_original_length;
1866 	size_t remaining_modified_length;
1867 	caddr_t segment_data;
1868 
1869 	EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1870 
1871 	/* Allocate sufficient memory for the entire partition */
1872 	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1873 		goto fail1;
1874 
1875 	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1876 	if (partn_data == NULL) {
1877 		rc = ENOMEM;
1878 		goto fail2;
1879 	}
1880 
1881 	remaining_original_length = partn_size;
1882 	remaining_modified_length = partn_size;
1883 	segment_data = partn_data;
1884 
1885 	/* Lock the partition */
1886 	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1887 		goto fail3;
1888 
1889 	/* Iterate over each (potential) segment to update it. */
1890 	do {
1891 		boolean_t write = all_segments || current_offset == 0;
1892 
1893 		rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1894 		    &segment_data, &current_offset, &remaining_original_length,
1895 		    &remaining_modified_length, write);
1896 		if (rc != 0) {
1897 			if (current_offset == 0) {
1898 				/*
1899 				 * If no data has been read then the first
1900 				 * segment is invalid, which is an error.
1901 				 */
1902 				goto fail4;
1903 			}
1904 			break;
1905 		}
1906 	} while (current_offset < partn_size);
1907 
1908 	total_length = segment_data - partn_data;
1909 
1910 	/*
1911 	 * We've run out of space.  This should actually be dealt with by
1912 	 * ef10_nvram_buf_write_tlv returning ENOSPC.
1913 	 */
1914 	if (total_length > partn_size) {
1915 		rc = ENOSPC;
1916 		goto fail5;
1917 	}
1918 
1919 	/* Erase the whole partition in NVRAM */
1920 	if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1921 		goto fail6;
1922 
1923 	/* Write new partition contents from the buffer to NVRAM */
1924 	if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1925 		    total_length)) != 0)
1926 		goto fail7;
1927 
1928 	/* Unlock the partition */
1929 	(void) ef10_nvram_partn_unlock(enp, partn, NULL);
1930 
1931 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1932 
1933 	return (0);
1934 
1935 fail7:
1936 	EFSYS_PROBE(fail7);
1937 fail6:
1938 	EFSYS_PROBE(fail6);
1939 fail5:
1940 	EFSYS_PROBE(fail5);
1941 fail4:
1942 	EFSYS_PROBE(fail4);
1943 
1944 	(void) ef10_nvram_partn_unlock(enp, partn, NULL);
1945 fail3:
1946 	EFSYS_PROBE(fail3);
1947 
1948 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1949 fail2:
1950 	EFSYS_PROBE(fail2);
1951 fail1:
1952 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1953 
1954 	return (rc);
1955 }
1956 
1957 /*
1958  * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1959  * not the data used by the segments in the partition.
1960  */
1961 	__checkReturn		efx_rc_t
1962 ef10_nvram_partn_size(
1963 	__in			efx_nic_t *enp,
1964 	__in			uint32_t partn,
1965 	__out			size_t *sizep)
1966 {
1967 	efx_rc_t rc;
1968 
1969 	if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1970 	    NULL, NULL, NULL)) != 0)
1971 		goto fail1;
1972 
1973 	return (0);
1974 
1975 fail1:
1976 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1977 
1978 	return (rc);
1979 }
1980 
1981 	__checkReturn		efx_rc_t
1982 ef10_nvram_partn_lock(
1983 	__in			efx_nic_t *enp,
1984 	__in			uint32_t partn)
1985 {
1986 	efx_rc_t rc;
1987 
1988 	if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1989 		goto fail1;
1990 
1991 	return (0);
1992 
1993 fail1:
1994 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1995 
1996 	return (rc);
1997 }
1998 
1999 	__checkReturn		efx_rc_t
2000 ef10_nvram_partn_read_mode(
2001 	__in			efx_nic_t *enp,
2002 	__in			uint32_t partn,
2003 	__in			unsigned int offset,
2004 	__out_bcount(size)	caddr_t data,
2005 	__in			size_t size,
2006 	__in			uint32_t mode)
2007 {
2008 	size_t chunk;
2009 	efx_rc_t rc;
2010 
2011 	while (size > 0) {
2012 		chunk = MIN(size, EF10_NVRAM_CHUNK);
2013 
2014 		if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
2015 			    data, chunk, mode)) != 0) {
2016 			goto fail1;
2017 		}
2018 
2019 		size -= chunk;
2020 		data += chunk;
2021 		offset += chunk;
2022 	}
2023 
2024 	return (0);
2025 
2026 fail1:
2027 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2028 
2029 	return (rc);
2030 }
2031 
2032 	__checkReturn		efx_rc_t
2033 ef10_nvram_partn_read(
2034 	__in			efx_nic_t *enp,
2035 	__in			uint32_t partn,
2036 	__in			unsigned int offset,
2037 	__out_bcount(size)	caddr_t data,
2038 	__in			size_t size)
2039 {
2040 	/*
2041 	 * An A/B partition has two data stores (current and backup).
2042 	 * Read requests which come in through the EFX API expect to read the
2043 	 * current, active store of an A/B partition. For non A/B partitions,
2044 	 * there is only a single store and so the mode param is ignored.
2045 	 */
2046 	return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
2047 			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
2048 }
2049 
2050 	__checkReturn		efx_rc_t
2051 ef10_nvram_partn_read_backup(
2052 	__in			efx_nic_t *enp,
2053 	__in			uint32_t partn,
2054 	__in			unsigned int offset,
2055 	__out_bcount(size)	caddr_t data,
2056 	__in			size_t size)
2057 {
2058 	/*
2059 	 * An A/B partition has two data stores (current and backup).
2060 	 * Read the backup store of an A/B partition (i.e. the store currently
2061 	 * being written to if the partition is locked).
2062 	 *
2063 	 * This is needed when comparing the existing partition content to avoid
2064 	 * unnecessary writes, or to read back what has been written to check
2065 	 * that the writes have succeeded.
2066 	 */
2067 	return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
2068 			    MC_CMD_NVRAM_READ_IN_V2_TARGET_BACKUP);
2069 }
2070 
2071 	__checkReturn		efx_rc_t
2072 ef10_nvram_partn_erase(
2073 	__in			efx_nic_t *enp,
2074 	__in			uint32_t partn,
2075 	__in			unsigned int offset,
2076 	__in			size_t size)
2077 {
2078 	efx_rc_t rc;
2079 	uint32_t erase_size;
2080 
2081 	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2082 	    &erase_size, NULL)) != 0)
2083 		goto fail1;
2084 
2085 	if (erase_size == 0) {
2086 		if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
2087 			goto fail2;
2088 	} else {
2089 		if (size % erase_size != 0) {
2090 			rc = EINVAL;
2091 			goto fail3;
2092 		}
2093 		while (size > 0) {
2094 			if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
2095 			    erase_size)) != 0)
2096 				goto fail4;
2097 			offset += erase_size;
2098 			size -= erase_size;
2099 		}
2100 	}
2101 
2102 	return (0);
2103 
2104 fail4:
2105 	EFSYS_PROBE(fail4);
2106 fail3:
2107 	EFSYS_PROBE(fail3);
2108 fail2:
2109 	EFSYS_PROBE(fail2);
2110 fail1:
2111 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2112 
2113 	return (rc);
2114 }
2115 
2116 	__checkReturn		efx_rc_t
2117 ef10_nvram_partn_write(
2118 	__in			efx_nic_t *enp,
2119 	__in			uint32_t partn,
2120 	__in			unsigned int offset,
2121 	__in_bcount(size)	caddr_t data,
2122 	__in			size_t size)
2123 {
2124 	size_t chunk;
2125 	uint32_t write_size;
2126 	efx_rc_t rc;
2127 
2128 	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2129 	    NULL, &write_size)) != 0)
2130 		goto fail1;
2131 
2132 	if (write_size != 0) {
2133 		/*
2134 		 * Check that the size is a multiple of the write chunk size if
2135 		 * the write chunk size is available.
2136 		 */
2137 		if (size % write_size != 0) {
2138 			rc = EINVAL;
2139 			goto fail2;
2140 		}
2141 	} else {
2142 		write_size = EF10_NVRAM_CHUNK;
2143 	}
2144 
2145 	while (size > 0) {
2146 		chunk = MIN(size, write_size);
2147 
2148 		if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
2149 			    data, chunk)) != 0) {
2150 			goto fail3;
2151 		}
2152 
2153 		size -= chunk;
2154 		data += chunk;
2155 		offset += chunk;
2156 	}
2157 
2158 	return (0);
2159 
2160 fail3:
2161 	EFSYS_PROBE(fail3);
2162 fail2:
2163 	EFSYS_PROBE(fail2);
2164 fail1:
2165 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2166 
2167 	return (rc);
2168 }
2169 
2170 	__checkReturn		efx_rc_t
2171 ef10_nvram_partn_unlock(
2172 	__in			efx_nic_t *enp,
2173 	__in			uint32_t partn,
2174 	__out_opt		uint32_t *verify_resultp)
2175 {
2176 	boolean_t reboot = B_FALSE;
2177 	efx_rc_t rc;
2178 
2179 	if (verify_resultp != NULL)
2180 		*verify_resultp = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
2181 
2182 	rc = efx_mcdi_nvram_update_finish(enp, partn, reboot, verify_resultp);
2183 	if (rc != 0)
2184 		goto fail1;
2185 
2186 	return (0);
2187 
2188 fail1:
2189 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2190 
2191 	return (rc);
2192 }
2193 
2194 	__checkReturn		efx_rc_t
2195 ef10_nvram_partn_set_version(
2196 	__in			efx_nic_t *enp,
2197 	__in			uint32_t partn,
2198 	__in_ecount(4)		uint16_t version[4])
2199 {
2200 	struct tlv_partition_version partn_version;
2201 	size_t size;
2202 	efx_rc_t rc;
2203 
2204 	/* Add or modify partition version TLV item */
2205 	partn_version.version_w = __CPU_TO_LE_16(version[0]);
2206 	partn_version.version_x = __CPU_TO_LE_16(version[1]);
2207 	partn_version.version_y = __CPU_TO_LE_16(version[2]);
2208 	partn_version.version_z = __CPU_TO_LE_16(version[3]);
2209 
2210 	size = sizeof (partn_version) - (2 * sizeof (uint32_t));
2211 
2212 	/* Write the version number to all segments in the partition */
2213 	if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
2214 		    NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
2215 		    TLV_TAG_PARTITION_VERSION(partn),
2216 		    (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
2217 		goto fail1;
2218 
2219 	return (0);
2220 
2221 fail1:
2222 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2223 
2224 	return (rc);
2225 }
2226 
2227 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
2228 
2229 #if EFSYS_OPT_NVRAM
2230 
2231 typedef struct ef10_parttbl_entry_s {
2232 	unsigned int		partn;
2233 	unsigned int		port_mask;
2234 	efx_nvram_type_t	nvtype;
2235 } ef10_parttbl_entry_t;
2236 
2237 /* Port mask values */
2238 #define	PORT_1		(1u << 1)
2239 #define	PORT_2		(1u << 2)
2240 #define	PORT_3		(1u << 3)
2241 #define	PORT_4		(1u << 4)
2242 #define	PORT_ALL	(0xffffffffu)
2243 
2244 #define	PARTN_MAP_ENTRY(partn, port_mask, nvtype)	\
2245 { (NVRAM_PARTITION_TYPE_##partn), (PORT_##port_mask), (EFX_NVRAM_##nvtype) }
2246 
2247 /* Translate EFX NVRAM types to firmware partition types */
2248 static ef10_parttbl_entry_t hunt_parttbl[] = {
2249 	/*		partn			ports	nvtype */
2250 	PARTN_MAP_ENTRY(MC_FIRMWARE,		ALL,	MC_FIRMWARE),
2251 	PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP,	ALL,	MC_GOLDEN),
2252 	PARTN_MAP_ENTRY(EXPANSION_ROM,		ALL,	BOOTROM),
2253 	PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT0,	1,	BOOTROM_CFG),
2254 	PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT1,	2,	BOOTROM_CFG),
2255 	PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT2,	3,	BOOTROM_CFG),
2256 	PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT3,	4,	BOOTROM_CFG),
2257 	PARTN_MAP_ENTRY(DYNAMIC_CONFIG,		ALL,	DYNAMIC_CFG),
2258 	PARTN_MAP_ENTRY(FPGA,			ALL,	FPGA),
2259 	PARTN_MAP_ENTRY(FPGA_BACKUP,		ALL,	FPGA_BACKUP),
2260 	PARTN_MAP_ENTRY(LICENSE,		ALL,	LICENSE),
2261 };
2262 
2263 static ef10_parttbl_entry_t medford_parttbl[] = {
2264 	/*		partn			ports	nvtype */
2265 	PARTN_MAP_ENTRY(MC_FIRMWARE,		ALL,	MC_FIRMWARE),
2266 	PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP,	ALL,	MC_GOLDEN),
2267 	PARTN_MAP_ENTRY(EXPANSION_ROM,		ALL,	BOOTROM),
2268 	PARTN_MAP_ENTRY(EXPROM_CONFIG,		ALL,	BOOTROM_CFG),
2269 	PARTN_MAP_ENTRY(DYNAMIC_CONFIG,		ALL,	DYNAMIC_CFG),
2270 	PARTN_MAP_ENTRY(FPGA,			ALL,	FPGA),
2271 	PARTN_MAP_ENTRY(FPGA_BACKUP,		ALL,	FPGA_BACKUP),
2272 	PARTN_MAP_ENTRY(LICENSE,		ALL,	LICENSE),
2273 	PARTN_MAP_ENTRY(EXPANSION_UEFI,		ALL,	UEFIROM),
2274 	PARTN_MAP_ENTRY(MUM_FIRMWARE,		ALL,	MUM_FIRMWARE),
2275 };
2276 
2277 static ef10_parttbl_entry_t medford2_parttbl[] = {
2278 	/*		partn			ports	nvtype */
2279 	PARTN_MAP_ENTRY(MC_FIRMWARE,		ALL,	MC_FIRMWARE),
2280 	PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP,	ALL,	MC_GOLDEN),
2281 	PARTN_MAP_ENTRY(EXPANSION_ROM,		ALL,	BOOTROM),
2282 	PARTN_MAP_ENTRY(EXPROM_CONFIG,		ALL,	BOOTROM_CFG),
2283 	PARTN_MAP_ENTRY(DYNAMIC_CONFIG,		ALL,	DYNAMIC_CFG),
2284 	PARTN_MAP_ENTRY(FPGA,			ALL,	FPGA),
2285 	PARTN_MAP_ENTRY(FPGA_BACKUP,		ALL,	FPGA_BACKUP),
2286 	PARTN_MAP_ENTRY(LICENSE,		ALL,	LICENSE),
2287 	PARTN_MAP_ENTRY(EXPANSION_UEFI,		ALL,	UEFIROM),
2288 	PARTN_MAP_ENTRY(MUM_FIRMWARE,		ALL,	MUM_FIRMWARE),
2289 	PARTN_MAP_ENTRY(DYNCONFIG_DEFAULTS,	ALL,	DYNCONFIG_DEFAULTS),
2290 	PARTN_MAP_ENTRY(ROMCONFIG_DEFAULTS,	ALL,	ROMCONFIG_DEFAULTS),
2291 };
2292 
2293 static	__checkReturn		efx_rc_t
2294 ef10_parttbl_get(
2295 	__in			efx_nic_t *enp,
2296 	__out			ef10_parttbl_entry_t **parttblp,
2297 	__out			size_t *parttbl_rowsp)
2298 {
2299 	switch (enp->en_family) {
2300 	case EFX_FAMILY_HUNTINGTON:
2301 		*parttblp = hunt_parttbl;
2302 		*parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
2303 		break;
2304 
2305 	case EFX_FAMILY_MEDFORD:
2306 		*parttblp = medford_parttbl;
2307 		*parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
2308 		break;
2309 
2310 	case EFX_FAMILY_MEDFORD2:
2311 		*parttblp = medford2_parttbl;
2312 		*parttbl_rowsp = EFX_ARRAY_SIZE(medford2_parttbl);
2313 		break;
2314 
2315 	default:
2316 		EFSYS_ASSERT(B_FALSE);
2317 		return (EINVAL);
2318 	}
2319 	return (0);
2320 }
2321 
2322 	__checkReturn		efx_rc_t
2323 ef10_nvram_type_to_partn(
2324 	__in			efx_nic_t *enp,
2325 	__in			efx_nvram_type_t type,
2326 	__out			uint32_t *partnp)
2327 {
2328 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2329 	ef10_parttbl_entry_t *parttbl = NULL;
2330 	size_t parttbl_rows = 0;
2331 	unsigned int i;
2332 
2333 	EFSYS_ASSERT3U(type, !=, EFX_NVRAM_INVALID);
2334 	EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
2335 	EFSYS_ASSERT(partnp != NULL);
2336 
2337 	if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2338 		for (i = 0; i < parttbl_rows; i++) {
2339 			ef10_parttbl_entry_t *entry = &parttbl[i];
2340 
2341 			if ((entry->nvtype == type) &&
2342 			    (entry->port_mask & (1u << emip->emi_port))) {
2343 				*partnp = entry->partn;
2344 				return (0);
2345 			}
2346 		}
2347 	}
2348 
2349 	return (ENOTSUP);
2350 }
2351 
2352 #if EFSYS_OPT_DIAG
2353 
2354 static	__checkReturn		efx_rc_t
2355 ef10_nvram_partn_to_type(
2356 	__in			efx_nic_t *enp,
2357 	__in			uint32_t partn,
2358 	__out			efx_nvram_type_t *typep)
2359 {
2360 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2361 	ef10_parttbl_entry_t *parttbl = NULL;
2362 	size_t parttbl_rows = 0;
2363 	unsigned int i;
2364 
2365 	EFSYS_ASSERT(typep != NULL);
2366 
2367 	if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2368 		for (i = 0; i < parttbl_rows; i++) {
2369 			ef10_parttbl_entry_t *entry = &parttbl[i];
2370 
2371 			if ((entry->partn == partn) &&
2372 			    (entry->port_mask & (1u << emip->emi_port))) {
2373 				*typep = entry->nvtype;
2374 				return (0);
2375 			}
2376 		}
2377 	}
2378 
2379 	return (ENOTSUP);
2380 }
2381 
2382 	__checkReturn		efx_rc_t
2383 ef10_nvram_test(
2384 	__in			efx_nic_t *enp)
2385 {
2386 	efx_nvram_type_t type;
2387 	unsigned int npartns = 0;
2388 	uint32_t *partns = NULL;
2389 	size_t size;
2390 	unsigned int i;
2391 	efx_rc_t rc;
2392 
2393 	/* Read available partitions from NVRAM partition map */
2394 	size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
2395 	EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
2396 	if (partns == NULL) {
2397 		rc = ENOMEM;
2398 		goto fail1;
2399 	}
2400 
2401 	if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
2402 		    &npartns)) != 0) {
2403 		goto fail2;
2404 	}
2405 
2406 	for (i = 0; i < npartns; i++) {
2407 		/* Check if the partition is supported for this port */
2408 		if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
2409 			continue;
2410 
2411 		if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
2412 			goto fail3;
2413 	}
2414 
2415 	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2416 	return (0);
2417 
2418 fail3:
2419 	EFSYS_PROBE(fail3);
2420 fail2:
2421 	EFSYS_PROBE(fail2);
2422 	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2423 fail1:
2424 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2425 	return (rc);
2426 }
2427 
2428 #endif	/* EFSYS_OPT_DIAG */
2429 
2430 	__checkReturn		efx_rc_t
2431 ef10_nvram_partn_get_version(
2432 	__in			efx_nic_t *enp,
2433 	__in			uint32_t partn,
2434 	__out			uint32_t *subtypep,
2435 	__out_ecount(4)		uint16_t version[4])
2436 {
2437 	efx_rc_t rc;
2438 
2439 	/* FIXME: get highest partn version from all ports */
2440 	/* FIXME: return partn description if available */
2441 
2442 	if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
2443 		    version, NULL, 0)) != 0)
2444 		goto fail1;
2445 
2446 	return (0);
2447 
2448 fail1:
2449 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2450 
2451 	return (rc);
2452 }
2453 
2454 	__checkReturn		efx_rc_t
2455 ef10_nvram_partn_rw_start(
2456 	__in			efx_nic_t *enp,
2457 	__in			uint32_t partn,
2458 	__out			size_t *chunk_sizep)
2459 {
2460 	uint32_t write_size = 0;
2461 	efx_rc_t rc;
2462 
2463 	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2464 	    NULL, &write_size)) != 0)
2465 		goto fail1;
2466 
2467 	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
2468 		goto fail2;
2469 
2470 	if (chunk_sizep != NULL) {
2471 		if (write_size == 0)
2472 			*chunk_sizep = EF10_NVRAM_CHUNK;
2473 		else
2474 			*chunk_sizep = write_size;
2475 	}
2476 
2477 	return (0);
2478 
2479 fail2:
2480 	EFSYS_PROBE(fail2);
2481 fail1:
2482 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2483 
2484 	return (rc);
2485 }
2486 
2487 	__checkReturn		efx_rc_t
2488 ef10_nvram_partn_rw_finish(
2489 	__in			efx_nic_t *enp,
2490 	__in			uint32_t partn,
2491 	__out_opt		uint32_t *verify_resultp)
2492 {
2493 	efx_rc_t rc;
2494 
2495 	if ((rc = ef10_nvram_partn_unlock(enp, partn, verify_resultp)) != 0)
2496 		goto fail1;
2497 
2498 	return (0);
2499 
2500 fail1:
2501 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2502 
2503 	return (rc);
2504 }
2505 
2506 #endif	/* EFSYS_OPT_NVRAM */
2507 
2508 #endif	/* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
2509