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