xref: /freebsd/sys/dev/sfxge/common/ef10_nvram.c (revision 273c26a3c3bea87a241d6879abd4f991db180bf0)
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
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 - 2)) {
234 			cursor->current = NULL;
235 			rc = EFAULT;
236 			goto fail3;
237 		}
238 
239 		/* Check we have value data for current item and another tag */
240 		if (tlv_next_item_ptr(cursor) > (cursor->limit - 1)) {
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 += P2ROUNDUP(len, sizeof (uint32_t)) / sizeof (*ptr);
398 	}
399 
400 	return (ptr);
401 }
402 
403 static	__checkReturn		efx_rc_t
404 tlv_insert(
405 	__inout	tlv_cursor_t	*cursor,
406 	__in	uint32_t	tag,
407 	__in_bcount(size)
408 		uint8_t		*data,
409 	__in	size_t		size)
410 {
411 	unsigned int delta;
412 	uint32_t *last_segment_end;
413 	efx_rc_t rc;
414 
415 	if ((rc = tlv_validate_state(cursor)) != 0)
416 		goto fail1;
417 
418 	if ((rc = tlv_require_end(cursor)) != 0)
419 		goto fail2;
420 
421 	if (tag == TLV_TAG_END) {
422 		rc = EINVAL;
423 		goto fail3;
424 	}
425 
426 	last_segment_end = tlv_last_segment_end(cursor);
427 
428 	delta = TLV_DWORD_COUNT(size);
429 	if (last_segment_end + 1 + delta > cursor->limit) {
430 		rc = ENOSPC;
431 		goto fail4;
432 	}
433 
434 	/* Move data up: new space at cursor->current */
435 	memmove(cursor->current + delta, cursor->current,
436 	    (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
437 
438 	/* Adjust the end pointer */
439 	cursor->end += delta;
440 
441 	/* Write new TLV item */
442 	tlv_write(cursor, tag, data, size);
443 
444 	return (0);
445 
446 fail4:
447 	EFSYS_PROBE(fail4);
448 fail3:
449 	EFSYS_PROBE(fail3);
450 fail2:
451 	EFSYS_PROBE(fail2);
452 fail1:
453 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
454 
455 	return (rc);
456 }
457 
458 static	__checkReturn		efx_rc_t
459 tlv_delete(
460 	__inout	tlv_cursor_t	*cursor)
461 {
462 	unsigned int delta;
463 	uint32_t *last_segment_end;
464 	efx_rc_t rc;
465 
466 	if ((rc = tlv_validate_state(cursor)) != 0)
467 		goto fail1;
468 
469 	if (tlv_tag(cursor) == TLV_TAG_END) {
470 		rc = EINVAL;
471 		goto fail2;
472 	}
473 
474 	delta = TLV_DWORD_COUNT(tlv_length(cursor));
475 
476 	if ((rc = tlv_require_end(cursor)) != 0)
477 		goto fail3;
478 
479 	last_segment_end = tlv_last_segment_end(cursor);
480 
481 	/* Shuffle things down, destroying the item at cursor->current */
482 	memmove(cursor->current, cursor->current + delta,
483 	    (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
484 	/* Zero the new space at the end of the TLV chain */
485 	memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t));
486 	/* Adjust the end pointer */
487 	cursor->end -= delta;
488 
489 	return (0);
490 
491 fail3:
492 	EFSYS_PROBE(fail3);
493 fail2:
494 	EFSYS_PROBE(fail2);
495 fail1:
496 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
497 
498 	return (rc);
499 }
500 
501 static	__checkReturn		efx_rc_t
502 tlv_modify(
503 	__inout	tlv_cursor_t	*cursor,
504 	__in	uint32_t	tag,
505 	__in_bcount(size)
506 		uint8_t		*data,
507 	__in	size_t		size)
508 {
509 	uint32_t *pos;
510 	unsigned int old_ndwords;
511 	unsigned int new_ndwords;
512 	unsigned int delta;
513 	uint32_t *last_segment_end;
514 	efx_rc_t rc;
515 
516 	if ((rc = tlv_validate_state(cursor)) != 0)
517 		goto fail1;
518 
519 	if (tlv_tag(cursor) == TLV_TAG_END) {
520 		rc = EINVAL;
521 		goto fail2;
522 	}
523 	if (tlv_tag(cursor) != tag) {
524 		rc = EINVAL;
525 		goto fail3;
526 	}
527 
528 	old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
529 	new_ndwords = TLV_DWORD_COUNT(size);
530 
531 	if ((rc = tlv_require_end(cursor)) != 0)
532 		goto fail4;
533 
534 	last_segment_end = tlv_last_segment_end(cursor);
535 
536 	if (new_ndwords > old_ndwords) {
537 		/* Expand space used for TLV item */
538 		delta = new_ndwords - old_ndwords;
539 		pos = cursor->current + old_ndwords;
540 
541 		if (last_segment_end + 1 + delta > cursor->limit) {
542 			rc = ENOSPC;
543 			goto fail5;
544 		}
545 
546 		/* Move up: new space at (cursor->current + old_ndwords) */
547 		memmove(pos + delta, pos,
548 		    (last_segment_end + 1 - pos) * sizeof (uint32_t));
549 
550 		/* Adjust the end pointer */
551 		cursor->end += delta;
552 
553 	} else if (new_ndwords < old_ndwords) {
554 		/* Shrink space used for TLV item */
555 		delta = old_ndwords - new_ndwords;
556 		pos = cursor->current + new_ndwords;
557 
558 		/* Move down: remove words at (cursor->current + new_ndwords) */
559 		memmove(pos, pos + delta,
560 		    (last_segment_end + 1 - pos) * sizeof (uint32_t));
561 
562 		/* Zero the new space at the end of the TLV chain */
563 		memset(last_segment_end + 1 - delta, 0,
564 		    delta * sizeof (uint32_t));
565 
566 		/* Adjust the end pointer */
567 		cursor->end -= delta;
568 	}
569 
570 	/* Write new data */
571 	tlv_write(cursor, tag, data, size);
572 
573 	return (0);
574 
575 fail5:
576 	EFSYS_PROBE(fail5);
577 fail4:
578 	EFSYS_PROBE(fail4);
579 fail3:
580 	EFSYS_PROBE(fail3);
581 fail2:
582 	EFSYS_PROBE(fail2);
583 fail1:
584 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
585 
586 	return (rc);
587 }
588 
589 static uint32_t checksum_tlv_partition(
590 	__in	nvram_partition_t *partition)
591 {
592 	tlv_cursor_t *cursor;
593 	uint32_t *ptr;
594 	uint32_t *end;
595 	uint32_t csum;
596 	size_t len;
597 
598 	cursor = &partition->tlv_cursor;
599 	len = tlv_block_length_used(cursor);
600 	EFSYS_ASSERT3U((len & 3), ==, 0);
601 
602 	csum = 0;
603 	ptr = partition->data;
604 	end = &ptr[len >> 2];
605 
606 	while (ptr < end)
607 		csum += __LE_TO_CPU_32(*ptr++);
608 
609 	return (csum);
610 }
611 
612 static	__checkReturn		efx_rc_t
613 tlv_update_partition_len_and_cks(
614 	__in	tlv_cursor_t *cursor)
615 {
616 	efx_rc_t rc;
617 	nvram_partition_t partition;
618 	struct tlv_partition_header *header;
619 	struct tlv_partition_trailer *trailer;
620 	size_t new_len;
621 
622 	/*
623 	 * We just modified the partition, so the total length may not be
624 	 * valid. Don't use tlv_find(), which performs some sanity checks
625 	 * that may fail here.
626 	 */
627 	partition.data = cursor->block;
628 	memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
629 	header = (struct tlv_partition_header *)partition.data;
630 	/* Sanity check. */
631 	if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
632 		rc = EFAULT;
633 		goto fail1;
634 	}
635 	new_len =  tlv_block_length_used(&partition.tlv_cursor);
636 	if (new_len == 0) {
637 		rc = EFAULT;
638 		goto fail2;
639 	}
640 	header->total_length = __CPU_TO_LE_32(new_len);
641 	/* Ensure the modified partition always has a new generation count. */
642 	header->generation = __CPU_TO_LE_32(
643 	    __LE_TO_CPU_32(header->generation) + 1);
644 
645 	trailer = (struct tlv_partition_trailer *)((uint8_t *)header +
646 	    new_len - sizeof (*trailer) - sizeof (uint32_t));
647 	trailer->generation = header->generation;
648 	trailer->checksum = __CPU_TO_LE_32(
649 	    __LE_TO_CPU_32(trailer->checksum) -
650 	    checksum_tlv_partition(&partition));
651 
652 	return (0);
653 
654 fail2:
655 	EFSYS_PROBE(fail2);
656 fail1:
657 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
658 
659 	return (rc);
660 }
661 
662 /* Validate buffer contents (before writing to flash) */
663 	__checkReturn		efx_rc_t
664 ef10_nvram_buffer_validate(
665 	__in			efx_nic_t *enp,
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 ends with PARTITION_TRAILER and END tags */
705 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
706 		rc = EINVAL;
707 		goto fail5;
708 	}
709 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
710 
711 	if ((rc = tlv_advance(&cursor)) != 0) {
712 		rc = EINVAL;
713 		goto fail6;
714 	}
715 	if (tlv_tag(&cursor) != TLV_TAG_END) {
716 		rc = EINVAL;
717 		goto fail7;
718 	}
719 
720 	/* Check generation counts are consistent */
721 	if (trailer->generation != header->generation) {
722 		rc = EINVAL;
723 		goto fail8;
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 fail9;
734 	}
735 
736 	return (0);
737 
738 fail9:
739 	EFSYS_PROBE(fail9);
740 fail8:
741 	EFSYS_PROBE(fail8);
742 fail7:
743 	EFSYS_PROBE(fail7);
744 fail6:
745 	EFSYS_PROBE(fail6);
746 fail5:
747 	EFSYS_PROBE(fail5);
748 fail4:
749 	EFSYS_PROBE(fail4);
750 fail3:
751 	EFSYS_PROBE(fail3);
752 fail2:
753 	EFSYS_PROBE(fail2);
754 fail1:
755 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
756 
757 	return (rc);
758 }
759 
760 
761 
762 	__checkReturn		efx_rc_t
763 ef10_nvram_buffer_create(
764 	__in			efx_nic_t *enp,
765 	__in			uint16_t partn_type,
766 	__in_bcount(partn_size)	caddr_t partn_data,
767 	__in			size_t partn_size)
768 {
769 	uint32_t *buf = (uint32_t *)partn_data;
770 	efx_rc_t rc;
771 	tlv_cursor_t cursor;
772 	struct tlv_partition_header header;
773 	struct tlv_partition_trailer trailer;
774 
775 	unsigned min_buf_size = sizeof (struct tlv_partition_header) +
776 	    sizeof (struct tlv_partition_trailer);
777 	if (partn_size < min_buf_size) {
778 		rc = EINVAL;
779 		goto fail1;
780 	}
781 
782 	memset(buf, 0xff, partn_size);
783 
784 	tlv_init_block(buf);
785 	if ((rc = tlv_init_cursor(&cursor, buf,
786 	    (uint32_t *)((uint8_t *)buf + partn_size),
787 	    buf)) != 0) {
788 		goto fail2;
789 	}
790 
791 	header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
792 	header.length = __CPU_TO_LE_32(sizeof (header) - 8);
793 	header.type_id = __CPU_TO_LE_16(partn_type);
794 	header.preset = 0;
795 	header.generation = __CPU_TO_LE_32(1);
796 	header.total_length = 0;  /* This will be fixed below. */
797 	if ((rc = tlv_insert(
798 	    &cursor, TLV_TAG_PARTITION_HEADER,
799 	    (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
800 		goto fail3;
801 	if ((rc = tlv_advance(&cursor)) != 0)
802 		goto fail4;
803 
804 	trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
805 	trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
806 	trailer.generation = header.generation;
807 	trailer.checksum = 0;  /* This will be fixed below. */
808 	if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
809 	    (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
810 		goto fail5;
811 
812 	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
813 		goto fail6;
814 
815 	/* Check that the partition is valid. */
816 	if ((rc = ef10_nvram_buffer_validate(enp, partn_type,
817 	    partn_data, partn_size)) != 0)
818 		goto fail7;
819 
820 	return (0);
821 
822 fail7:
823 	EFSYS_PROBE(fail7);
824 fail6:
825 	EFSYS_PROBE(fail6);
826 fail5:
827 	EFSYS_PROBE(fail5);
828 fail4:
829 	EFSYS_PROBE(fail4);
830 fail3:
831 	EFSYS_PROBE(fail3);
832 fail2:
833 	EFSYS_PROBE(fail2);
834 fail1:
835 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
836 
837 	return (rc);
838 }
839 
840 static			uint32_t
841 byte_offset(
842 	__in		uint32_t *position,
843 	__in		uint32_t *base)
844 {
845 	return (uint32_t)((uint8_t *)position - (uint8_t *)base);
846 }
847 
848 	__checkReturn		efx_rc_t
849 ef10_nvram_buffer_find_item_start(
850 	__in_bcount(buffer_size)
851 				caddr_t bufferp,
852 	__in			size_t buffer_size,
853 	__out			uint32_t *startp)
854 {
855 	// Read past partition header to find start address of the first key
856 	tlv_cursor_t cursor;
857 	efx_rc_t rc;
858 
859 	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
860 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
861 			buffer_size)) != 0) {
862 		rc = EFAULT;
863 		goto fail1;
864 	}
865 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
866 		rc = EINVAL;
867 		goto fail2;
868 	}
869 
870 	if ((rc = tlv_advance(&cursor)) != 0) {
871 		rc = EINVAL;
872 		goto fail3;
873 	}
874 	*startp = byte_offset(cursor.current, cursor.block);
875 
876 	if ((rc = tlv_require_end(&cursor)) != 0)
877 		goto fail4;
878 
879 	return (0);
880 
881 fail4:
882 	EFSYS_PROBE(fail4);
883 fail3:
884 	EFSYS_PROBE(fail3);
885 fail2:
886 	EFSYS_PROBE(fail2);
887 fail1:
888 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
889 
890 	return (rc);
891 }
892 
893 	__checkReturn		efx_rc_t
894 ef10_nvram_buffer_find_end(
895 	__in_bcount(buffer_size)
896 				caddr_t bufferp,
897 	__in			size_t buffer_size,
898 	__in			uint32_t offset,
899 	__out			uint32_t *endp)
900 {
901 	// Read to end of partition
902 	tlv_cursor_t cursor;
903 	efx_rc_t rc;
904 	uint32_t *segment_used;
905 
906 	_NOTE(ARGUNUSED(offset))
907 
908 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
909 			buffer_size)) != 0) {
910 		rc = EFAULT;
911 		goto fail1;
912 	}
913 
914 	segment_used = cursor.block;
915 
916 	/*
917 	 * Go through each segment and check that it has an end tag. If there
918 	 * is no end tag then the previous segment was the last valid one,
919 	 * so return the used space including that end tag.
920 	 */
921 	while (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
922 		if (tlv_require_end(&cursor) != 0) {
923 			if (segment_used == cursor.block) {
924 				/*
925 				 * First segment is corrupt, so there is
926 				 * no valid data in partition.
927 				 */
928 				rc = EINVAL;
929 				goto fail2;
930 			}
931 			break;
932 		}
933 		segment_used = cursor.end + 1;
934 
935 		cursor.current = segment_used;
936 	}
937 	/* Return space used (including the END tag) */
938 	*endp = (segment_used - cursor.block) * sizeof (uint32_t);
939 
940 	return (0);
941 
942 fail2:
943 	EFSYS_PROBE(fail2);
944 fail1:
945 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
946 
947 	return (rc);
948 }
949 
950 	__checkReturn	__success(return != B_FALSE)	boolean_t
951 ef10_nvram_buffer_find_item(
952 	__in_bcount(buffer_size)
953 				caddr_t bufferp,
954 	__in			size_t buffer_size,
955 	__in			uint32_t offset,
956 	__out			uint32_t *startp,
957 	__out			uint32_t *lengthp)
958 {
959 	// Find TLV at offset and return key start and length
960 	tlv_cursor_t cursor;
961 	uint8_t *key;
962 	uint32_t tag;
963 
964 	if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
965 			buffer_size, offset) != 0) {
966 		return (B_FALSE);
967 	}
968 
969 	while ((key = tlv_item(&cursor)) != NULL) {
970 		tag = tlv_tag(&cursor);
971 		if (tag == TLV_TAG_PARTITION_HEADER ||
972 		    tag == TLV_TAG_PARTITION_TRAILER) {
973 			if (tlv_advance(&cursor) != 0) {
974 				break;
975 			}
976 			continue;
977 		}
978 		*startp = byte_offset(cursor.current, cursor.block);
979 		*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
980 		    cursor.current);
981 		return (B_TRUE);
982 	}
983 
984 	return (B_FALSE);
985 }
986 
987 	__checkReturn		efx_rc_t
988 ef10_nvram_buffer_get_item(
989 	__in_bcount(buffer_size)
990 				caddr_t bufferp,
991 	__in			size_t buffer_size,
992 	__in			uint32_t offset,
993 	__in			uint32_t length,
994 	__out_bcount_part(item_max_size, *lengthp)
995 				caddr_t itemp,
996 	__in			size_t item_max_size,
997 	__out			uint32_t *lengthp)
998 {
999 	efx_rc_t rc;
1000 	tlv_cursor_t cursor;
1001 	uint32_t item_length;
1002 
1003 	if (item_max_size < length) {
1004 		rc = ENOSPC;
1005 		goto fail1;
1006 	}
1007 
1008 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1009 			buffer_size, offset)) != 0) {
1010 		goto fail2;
1011 	}
1012 
1013 	item_length = tlv_length(&cursor);
1014 	if (length < item_length) {
1015 		rc = ENOSPC;
1016 		goto fail3;
1017 	}
1018 	memcpy(itemp, tlv_value(&cursor), item_length);
1019 
1020 	*lengthp = item_length;
1021 
1022 	return (0);
1023 
1024 fail3:
1025 	EFSYS_PROBE(fail3);
1026 fail2:
1027 	EFSYS_PROBE(fail2);
1028 fail1:
1029 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1030 
1031 	return (rc);
1032 }
1033 
1034 	__checkReturn		efx_rc_t
1035 ef10_nvram_buffer_insert_item(
1036 	__in_bcount(buffer_size)
1037 				caddr_t bufferp,
1038 	__in			size_t buffer_size,
1039 	__in			uint32_t offset,
1040 	__in_bcount(length)	caddr_t keyp,
1041 	__in			uint32_t length,
1042 	__out			uint32_t *lengthp)
1043 {
1044 	efx_rc_t rc;
1045 	tlv_cursor_t cursor;
1046 
1047 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1048 			buffer_size, offset)) != 0) {
1049 		goto fail1;
1050 	}
1051 
1052 	rc = tlv_insert(&cursor, TLV_TAG_LICENSE, (uint8_t *)keyp, length);
1053 
1054 	if (rc != 0) {
1055 		goto fail2;
1056 	}
1057 
1058 	*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1059 		    cursor.current);
1060 
1061 	return (0);
1062 
1063 fail2:
1064 	EFSYS_PROBE(fail2);
1065 fail1:
1066 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1067 
1068 	return (rc);
1069 }
1070 
1071 	__checkReturn		efx_rc_t
1072 ef10_nvram_buffer_delete_item(
1073 	__in_bcount(buffer_size)
1074 				caddr_t bufferp,
1075 	__in			size_t buffer_size,
1076 	__in			uint32_t offset,
1077 	__in			uint32_t length,
1078 	__in			uint32_t end)
1079 {
1080 	efx_rc_t rc;
1081 	tlv_cursor_t cursor;
1082 
1083 	_NOTE(ARGUNUSED(length, end))
1084 
1085 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1086 			buffer_size, offset)) != 0) {
1087 		goto fail1;
1088 	}
1089 
1090 	if ((rc = tlv_delete(&cursor)) != 0)
1091 		goto fail2;
1092 
1093 	return (0);
1094 
1095 fail2:
1096 	EFSYS_PROBE(fail2);
1097 fail1:
1098 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1099 
1100 	return (rc);
1101 }
1102 
1103 	__checkReturn		efx_rc_t
1104 ef10_nvram_buffer_finish(
1105 	__in_bcount(buffer_size)
1106 				caddr_t bufferp,
1107 	__in			size_t buffer_size)
1108 {
1109 	efx_rc_t rc;
1110 	tlv_cursor_t cursor;
1111 
1112 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
1113 			buffer_size)) != 0) {
1114 		rc = EFAULT;
1115 		goto fail1;
1116 	}
1117 
1118 	if ((rc = tlv_require_end(&cursor)) != 0)
1119 		goto fail2;
1120 
1121 	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
1122 		goto fail3;
1123 
1124 	return (0);
1125 
1126 fail3:
1127 	EFSYS_PROBE(fail3);
1128 fail2:
1129 	EFSYS_PROBE(fail2);
1130 fail1:
1131 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1132 
1133 	return (rc);
1134 }
1135 
1136 
1137 
1138 /*
1139  * Read and validate a segment from a partition. A segment is a complete
1140  * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
1141  * be multiple segments in a partition, so seg_offset allows segments
1142  * beyond the first to be read.
1143  */
1144 static	__checkReturn			efx_rc_t
1145 ef10_nvram_read_tlv_segment(
1146 	__in				efx_nic_t *enp,
1147 	__in				uint32_t partn,
1148 	__in				size_t seg_offset,
1149 	__in_bcount(max_seg_size)	caddr_t seg_data,
1150 	__in				size_t max_seg_size)
1151 {
1152 	tlv_cursor_t cursor;
1153 	struct tlv_partition_header *header;
1154 	struct tlv_partition_trailer *trailer;
1155 	size_t total_length;
1156 	uint32_t cksum;
1157 	int pos;
1158 	efx_rc_t rc;
1159 
1160 	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
1161 
1162 	if ((seg_data == NULL) || (max_seg_size == 0)) {
1163 		rc = EINVAL;
1164 		goto fail1;
1165 	}
1166 
1167 	/* Read initial chunk of the segment, starting at offset */
1168 	if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
1169 		    EF10_NVRAM_CHUNK,
1170 		    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
1171 		goto fail2;
1172 	}
1173 
1174 	/* A PARTITION_HEADER tag must be the first item at the given offset */
1175 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1176 		    max_seg_size)) != 0) {
1177 		rc = EFAULT;
1178 		goto fail3;
1179 	}
1180 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1181 		rc = EINVAL;
1182 		goto fail4;
1183 	}
1184 	header = (struct tlv_partition_header *)tlv_item(&cursor);
1185 
1186 	/* Check TLV segment length (includes the END tag) */
1187 	total_length = __LE_TO_CPU_32(header->total_length);
1188 	if (total_length > max_seg_size) {
1189 		rc = EFBIG;
1190 		goto fail5;
1191 	}
1192 
1193 	/* Read the remaining segment content */
1194 	if (total_length > EF10_NVRAM_CHUNK) {
1195 		if ((rc = ef10_nvram_partn_read_mode(enp, partn,
1196 			    seg_offset + EF10_NVRAM_CHUNK,
1197 			    seg_data + EF10_NVRAM_CHUNK,
1198 			    total_length - EF10_NVRAM_CHUNK,
1199 			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
1200 			goto fail6;
1201 	}
1202 
1203 	/* Check segment ends with PARTITION_TRAILER and END tags */
1204 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1205 		rc = EINVAL;
1206 		goto fail7;
1207 	}
1208 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1209 
1210 	if ((rc = tlv_advance(&cursor)) != 0) {
1211 		rc = EINVAL;
1212 		goto fail8;
1213 	}
1214 	if (tlv_tag(&cursor) != TLV_TAG_END) {
1215 		rc = EINVAL;
1216 		goto fail9;
1217 	}
1218 
1219 	/* Check data read from segment is consistent */
1220 	if (trailer->generation != header->generation) {
1221 		/*
1222 		 * The partition data may have been modified between successive
1223 		 * MCDI NVRAM_READ requests by the MC or another PCI function.
1224 		 *
1225 		 * The caller must retry to obtain consistent partition data.
1226 		 */
1227 		rc = EAGAIN;
1228 		goto fail10;
1229 	}
1230 
1231 	/* Verify segment checksum */
1232 	cksum = 0;
1233 	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
1234 		cksum += *((uint32_t *)(seg_data + pos));
1235 	}
1236 	if (cksum != 0) {
1237 		rc = EINVAL;
1238 		goto fail11;
1239 	}
1240 
1241 	return (0);
1242 
1243 fail11:
1244 	EFSYS_PROBE(fail11);
1245 fail10:
1246 	EFSYS_PROBE(fail10);
1247 fail9:
1248 	EFSYS_PROBE(fail9);
1249 fail8:
1250 	EFSYS_PROBE(fail8);
1251 fail7:
1252 	EFSYS_PROBE(fail7);
1253 fail6:
1254 	EFSYS_PROBE(fail6);
1255 fail5:
1256 	EFSYS_PROBE(fail5);
1257 fail4:
1258 	EFSYS_PROBE(fail4);
1259 fail3:
1260 	EFSYS_PROBE(fail3);
1261 fail2:
1262 	EFSYS_PROBE(fail2);
1263 fail1:
1264 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1265 
1266 	return (rc);
1267 }
1268 
1269 /*
1270  * Read a single TLV item from a host memory
1271  * buffer containing a TLV formatted segment.
1272  */
1273 	__checkReturn		efx_rc_t
1274 ef10_nvram_buf_read_tlv(
1275 	__in				efx_nic_t *enp,
1276 	__in_bcount(max_seg_size)	caddr_t seg_data,
1277 	__in				size_t max_seg_size,
1278 	__in				uint32_t tag,
1279 	__deref_out_bcount_opt(*sizep)	caddr_t *datap,
1280 	__out				size_t *sizep)
1281 {
1282 	tlv_cursor_t cursor;
1283 	caddr_t data;
1284 	size_t length;
1285 	caddr_t value;
1286 	efx_rc_t rc;
1287 
1288 	if ((seg_data == NULL) || (max_seg_size == 0)) {
1289 		rc = EINVAL;
1290 		goto fail1;
1291 	}
1292 
1293 	/* Find requested TLV tag in segment data */
1294 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1295 		    max_seg_size)) != 0) {
1296 		rc = EFAULT;
1297 		goto fail2;
1298 	}
1299 	if ((rc = tlv_find(&cursor, tag)) != 0) {
1300 		rc = ENOENT;
1301 		goto fail3;
1302 	}
1303 	value = (caddr_t)tlv_value(&cursor);
1304 	length = tlv_length(&cursor);
1305 
1306 	if (length == 0)
1307 		data = NULL;
1308 	else {
1309 		/* Copy out data from TLV item */
1310 		EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
1311 		if (data == NULL) {
1312 			rc = ENOMEM;
1313 			goto fail4;
1314 		}
1315 		memcpy(data, value, length);
1316 	}
1317 
1318 	*datap = data;
1319 	*sizep = length;
1320 
1321 	return (0);
1322 
1323 fail4:
1324 	EFSYS_PROBE(fail4);
1325 fail3:
1326 	EFSYS_PROBE(fail3);
1327 fail2:
1328 	EFSYS_PROBE(fail2);
1329 fail1:
1330 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1331 
1332 	return (rc);
1333 }
1334 
1335 /* Read a single TLV item from the first segment in a TLV formatted partition */
1336 	__checkReturn		efx_rc_t
1337 ef10_nvram_partn_read_tlv(
1338 	__in					efx_nic_t *enp,
1339 	__in					uint32_t partn,
1340 	__in					uint32_t tag,
1341 	__deref_out_bcount_opt(*seg_sizep)	caddr_t *seg_datap,
1342 	__out					size_t *seg_sizep)
1343 {
1344 	caddr_t seg_data = NULL;
1345 	size_t partn_size = 0;
1346 	size_t length;
1347 	caddr_t data;
1348 	int retry;
1349 	efx_rc_t rc;
1350 
1351 	/* Allocate sufficient memory for the entire partition */
1352 	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1353 		goto fail1;
1354 
1355 	if (partn_size == 0) {
1356 		rc = ENOENT;
1357 		goto fail2;
1358 	}
1359 
1360 	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
1361 	if (seg_data == NULL) {
1362 		rc = ENOMEM;
1363 		goto fail3;
1364 	}
1365 
1366 	/*
1367 	 * Read the first segment in a TLV partition. Retry until consistent
1368 	 * segment contents are returned. Inconsistent data may be read if:
1369 	 *  a) the segment contents are invalid
1370 	 *  b) the MC has rebooted while we were reading the partition
1371 	 *  c) the partition has been modified while we were reading it
1372 	 * Limit retry attempts to ensure forward progress.
1373 	 */
1374 	retry = 10;
1375 	do {
1376 		rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
1377 		    seg_data, partn_size);
1378 	} while ((rc == EAGAIN) && (--retry > 0));
1379 
1380 	if (rc != 0) {
1381 		/* Failed to obtain consistent segment data */
1382 		goto fail4;
1383 	}
1384 
1385 	if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
1386 		    tag, &data, &length)) != 0)
1387 		goto fail5;
1388 
1389 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1390 
1391 	*seg_datap = data;
1392 	*seg_sizep = length;
1393 
1394 	return (0);
1395 
1396 fail5:
1397 	EFSYS_PROBE(fail5);
1398 fail4:
1399 	EFSYS_PROBE(fail4);
1400 
1401 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1402 fail3:
1403 	EFSYS_PROBE(fail3);
1404 fail2:
1405 	EFSYS_PROBE(fail2);
1406 fail1:
1407 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1408 
1409 	return (rc);
1410 }
1411 
1412 /* Compute the size of a segment. */
1413 	static	__checkReturn	efx_rc_t
1414 ef10_nvram_buf_segment_size(
1415 	__in			caddr_t seg_data,
1416 	__in			size_t max_seg_size,
1417 	__out			size_t *seg_sizep)
1418 {
1419 	efx_rc_t rc;
1420 	tlv_cursor_t cursor;
1421 	struct tlv_partition_header *header;
1422 	uint32_t cksum;
1423 	int pos;
1424 	uint32_t *end_tag_position;
1425 	uint32_t segment_length;
1426 
1427 	/* A PARTITION_HEADER tag must be the first item at the given offset */
1428 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1429 		    max_seg_size)) != 0) {
1430 		rc = EFAULT;
1431 		goto fail1;
1432 	}
1433 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1434 		rc = EINVAL;
1435 		goto fail2;
1436 	}
1437 	header = (struct tlv_partition_header *)tlv_item(&cursor);
1438 
1439 	/* Check TLV segment length (includes the END tag) */
1440 	*seg_sizep = __LE_TO_CPU_32(header->total_length);
1441 	if (*seg_sizep > max_seg_size) {
1442 		rc = EFBIG;
1443 		goto fail3;
1444 	}
1445 
1446 	/* Check segment ends with PARTITION_TRAILER and END tags */
1447 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1448 		rc = EINVAL;
1449 		goto fail4;
1450 	}
1451 
1452 	if ((rc = tlv_advance(&cursor)) != 0) {
1453 		rc = EINVAL;
1454 		goto fail5;
1455 	}
1456 	if (tlv_tag(&cursor) != TLV_TAG_END) {
1457 		rc = EINVAL;
1458 		goto fail6;
1459 	}
1460 	end_tag_position = cursor.current;
1461 
1462 	/* Verify segment checksum */
1463 	cksum = 0;
1464 	for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
1465 		cksum += *((uint32_t *)(seg_data + pos));
1466 	}
1467 	if (cksum != 0) {
1468 		rc = EINVAL;
1469 		goto fail7;
1470 	}
1471 
1472 	/*
1473 	 * Calculate total length from HEADER to END tags and compare to
1474 	 * max_seg_size and the total_length field in the HEADER tag.
1475 	 */
1476 	segment_length = tlv_block_length_used(&cursor);
1477 
1478 	if (segment_length > max_seg_size) {
1479 		rc = EINVAL;
1480 		goto fail8;
1481 	}
1482 
1483 	if (segment_length != *seg_sizep) {
1484 		rc = EINVAL;
1485 		goto fail9;
1486 	}
1487 
1488 	/* Skip over the first HEADER tag. */
1489 	rc = tlv_rewind(&cursor);
1490 	rc = tlv_advance(&cursor);
1491 
1492 	while (rc == 0) {
1493 		if (tlv_tag(&cursor) == TLV_TAG_END) {
1494 			/* Check that the END tag is the one found earlier. */
1495 			if (cursor.current != end_tag_position)
1496 				goto fail10;
1497 			break;
1498 		}
1499 		/* Check for duplicate HEADER tags before the END tag. */
1500 		if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
1501 			rc = EINVAL;
1502 			goto fail11;
1503 		}
1504 
1505 		rc = tlv_advance(&cursor);
1506 	}
1507 	if (rc != 0)
1508 		goto fail12;
1509 
1510 	return (0);
1511 
1512 fail12:
1513 	EFSYS_PROBE(fail12);
1514 fail11:
1515 	EFSYS_PROBE(fail11);
1516 fail10:
1517 	EFSYS_PROBE(fail10);
1518 fail9:
1519 	EFSYS_PROBE(fail9);
1520 fail8:
1521 	EFSYS_PROBE(fail8);
1522 fail7:
1523 	EFSYS_PROBE(fail7);
1524 fail6:
1525 	EFSYS_PROBE(fail6);
1526 fail5:
1527 	EFSYS_PROBE(fail5);
1528 fail4:
1529 	EFSYS_PROBE(fail4);
1530 fail3:
1531 	EFSYS_PROBE(fail3);
1532 fail2:
1533 	EFSYS_PROBE(fail2);
1534 fail1:
1535 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1536 
1537 	return (rc);
1538 }
1539 
1540 /*
1541  * Add or update a single TLV item in a host memory buffer containing a TLV
1542  * formatted segment. Historically partitions consisted of only one segment.
1543  */
1544 	__checkReturn			efx_rc_t
1545 ef10_nvram_buf_write_tlv(
1546 	__inout_bcount(max_seg_size)	caddr_t seg_data,
1547 	__in				size_t max_seg_size,
1548 	__in				uint32_t tag,
1549 	__in_bcount(tag_size)		caddr_t tag_data,
1550 	__in				size_t tag_size,
1551 	__out				size_t *total_lengthp)
1552 {
1553 	tlv_cursor_t cursor;
1554 	struct tlv_partition_header *header;
1555 	struct tlv_partition_trailer *trailer;
1556 	uint32_t generation;
1557 	uint32_t cksum;
1558 	int pos;
1559 	efx_rc_t rc;
1560 
1561 	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
1562 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1563 			max_seg_size)) != 0) {
1564 		rc = EFAULT;
1565 		goto fail1;
1566 	}
1567 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1568 		rc = EINVAL;
1569 		goto fail2;
1570 	}
1571 	header = (struct tlv_partition_header *)tlv_item(&cursor);
1572 
1573 	/* Update the TLV chain to contain the new data */
1574 	if ((rc = tlv_find(&cursor, tag)) == 0) {
1575 		/* Modify existing TLV item */
1576 		if ((rc = tlv_modify(&cursor, tag,
1577 			    (uint8_t *)tag_data, tag_size)) != 0)
1578 			goto fail3;
1579 	} else {
1580 		/* Insert a new TLV item before the PARTITION_TRAILER */
1581 		rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1582 		if (rc != 0) {
1583 			rc = EINVAL;
1584 			goto fail4;
1585 		}
1586 		if ((rc = tlv_insert(&cursor, tag,
1587 			    (uint8_t *)tag_data, tag_size)) != 0) {
1588 			rc = EINVAL;
1589 			goto fail5;
1590 		}
1591 	}
1592 
1593 	/* Find the trailer tag */
1594 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1595 		rc = EINVAL;
1596 		goto fail6;
1597 	}
1598 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1599 
1600 	/* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1601 	*total_lengthp = tlv_block_length_used(&cursor);
1602 	if (*total_lengthp > max_seg_size) {
1603 		rc = ENOSPC;
1604 		goto fail7;
1605 	}
1606 	generation = __LE_TO_CPU_32(header->generation) + 1;
1607 
1608 	header->total_length	= __CPU_TO_LE_32(*total_lengthp);
1609 	header->generation	= __CPU_TO_LE_32(generation);
1610 	trailer->generation	= __CPU_TO_LE_32(generation);
1611 
1612 	/* Recompute PARTITION_TRAILER checksum */
1613 	trailer->checksum = 0;
1614 	cksum = 0;
1615 	for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1616 		cksum += *((uint32_t *)(seg_data + pos));
1617 	}
1618 	trailer->checksum = ~cksum + 1;
1619 
1620 	return (0);
1621 
1622 fail7:
1623 	EFSYS_PROBE(fail7);
1624 fail6:
1625 	EFSYS_PROBE(fail6);
1626 fail5:
1627 	EFSYS_PROBE(fail5);
1628 fail4:
1629 	EFSYS_PROBE(fail4);
1630 fail3:
1631 	EFSYS_PROBE(fail3);
1632 fail2:
1633 	EFSYS_PROBE(fail2);
1634 fail1:
1635 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1636 
1637 	return (rc);
1638 }
1639 
1640 /*
1641  * Add or update a single TLV item in the first segment of a TLV formatted
1642  * dynamic config partition. The first segment is the current active
1643  * configuration.
1644  */
1645 	__checkReturn		efx_rc_t
1646 ef10_nvram_partn_write_tlv(
1647 	__in			efx_nic_t *enp,
1648 	__in			uint32_t partn,
1649 	__in			uint32_t tag,
1650 	__in_bcount(size)	caddr_t data,
1651 	__in			size_t size)
1652 {
1653 	return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1654 	    size, B_FALSE);
1655 }
1656 
1657 /*
1658  * Read a segment from nvram at the given offset into a buffer (segment_data)
1659  * and optionally write a new tag to it.
1660  */
1661 static	__checkReturn		efx_rc_t
1662 ef10_nvram_segment_write_tlv(
1663 	__in			efx_nic_t *enp,
1664 	__in			uint32_t partn,
1665 	__in			uint32_t tag,
1666 	__in_bcount(size)	caddr_t data,
1667 	__in			size_t size,
1668 	__inout			caddr_t *seg_datap,
1669 	__inout			size_t *partn_offsetp,
1670 	__inout			size_t *src_remain_lenp,
1671 	__inout			size_t *dest_remain_lenp,
1672 	__in			boolean_t write)
1673 {
1674 	efx_rc_t rc;
1675 	efx_rc_t status;
1676 	size_t original_segment_size;
1677 	size_t modified_segment_size;
1678 
1679 	/*
1680 	 * Read the segment from NVRAM into the segment_data buffer and validate
1681 	 * it, returning if it does not validate. This is not a failure unless
1682 	 * this is the first segment in a partition. In this case the caller
1683 	 * must propagate the error.
1684 	 */
1685 	status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1686 	    *seg_datap, *src_remain_lenp);
1687 	if (status != 0) {
1688 		rc = EINVAL;
1689 		goto fail1;
1690 	}
1691 
1692 	status = ef10_nvram_buf_segment_size(*seg_datap,
1693 	    *src_remain_lenp, &original_segment_size);
1694 	if (status != 0) {
1695 		rc = EINVAL;
1696 		goto fail2;
1697 	}
1698 
1699 	if (write) {
1700 		/* Update the contents of the segment in the buffer */
1701 		if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1702 			*dest_remain_lenp, tag, data, size,
1703 			&modified_segment_size)) != 0) {
1704 			goto fail3;
1705 		}
1706 		*dest_remain_lenp -= modified_segment_size;
1707 		*seg_datap += modified_segment_size;
1708 	} else {
1709 		/*
1710 		 * We won't modify this segment, but still need to update the
1711 		 * remaining lengths and pointers.
1712 		 */
1713 		*dest_remain_lenp -= original_segment_size;
1714 		*seg_datap += original_segment_size;
1715 	}
1716 
1717 	*partn_offsetp += original_segment_size;
1718 	*src_remain_lenp -= original_segment_size;
1719 
1720 	return (0);
1721 
1722 fail3:
1723 	EFSYS_PROBE(fail3);
1724 fail2:
1725 	EFSYS_PROBE(fail2);
1726 fail1:
1727 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1728 
1729 	return (rc);
1730 }
1731 
1732 /*
1733  * Add or update a single TLV item in either the first segment or in all
1734  * segments in a TLV formatted dynamic config partition. Dynamic config
1735  * partitions on boards that support RFID are divided into a number of segments,
1736  * each formatted like a partition, with header, trailer and end tags. The first
1737  * segment is the current active configuration.
1738  *
1739  * The segments are initialised by manftest and each contain a different
1740  * configuration e.g. firmware variant. The firmware can be instructed
1741  * via RFID to copy a segment to replace the first segment, hence changing the
1742  * active configuration.  This allows ops to change the configuration of a board
1743  * prior to shipment using RFID.
1744  *
1745  * Changes to the dynamic config may need to be written to all segments (e.g.
1746  * firmware versions) or just the first segment (changes to the active
1747  * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1748  * If only the first segment is written the code still needs to be aware of the
1749  * possible presence of subsequent segments as writing to a segment may cause
1750  * its size to increase, which would overwrite the subsequent segments and
1751  * invalidate them.
1752  */
1753 	__checkReturn		efx_rc_t
1754 ef10_nvram_partn_write_segment_tlv(
1755 	__in			efx_nic_t *enp,
1756 	__in			uint32_t partn,
1757 	__in			uint32_t tag,
1758 	__in_bcount(size)	caddr_t data,
1759 	__in			size_t size,
1760 	__in			boolean_t all_segments)
1761 {
1762 	size_t partn_size = 0;
1763 	caddr_t partn_data;
1764 	size_t total_length = 0;
1765 	efx_rc_t rc;
1766 	size_t current_offset = 0;
1767 	size_t remaining_original_length;
1768 	size_t remaining_modified_length;
1769 	caddr_t segment_data;
1770 
1771 	EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1772 
1773 	/* Allocate sufficient memory for the entire partition */
1774 	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1775 		goto fail1;
1776 
1777 	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1778 	if (partn_data == NULL) {
1779 		rc = ENOMEM;
1780 		goto fail2;
1781 	}
1782 
1783 	remaining_original_length = partn_size;
1784 	remaining_modified_length = partn_size;
1785 	segment_data = partn_data;
1786 
1787 	/* Lock the partition */
1788 	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1789 		goto fail3;
1790 
1791 	/* Iterate over each (potential) segment to update it. */
1792 	do {
1793 		boolean_t write = all_segments || current_offset == 0;
1794 
1795 		rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1796 		    &segment_data, &current_offset, &remaining_original_length,
1797 		    &remaining_modified_length, write);
1798 		if (rc != 0) {
1799 			if (current_offset == 0) {
1800 				/*
1801 				 * If no data has been read then the first
1802 				 * segment is invalid, which is an error.
1803 				 */
1804 				goto fail4;
1805 			}
1806 			break;
1807 		}
1808 	} while (current_offset < partn_size);
1809 
1810 	total_length = segment_data - partn_data;
1811 
1812 	/*
1813 	 * We've run out of space.  This should actually be dealt with by
1814 	 * ef10_nvram_buf_write_tlv returning ENOSPC.
1815 	 */
1816 	if (total_length > partn_size) {
1817 		rc = ENOSPC;
1818 		goto fail5;
1819 	}
1820 
1821 	/* Erase the whole partition in NVRAM */
1822 	if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1823 		goto fail6;
1824 
1825 	/* Write new partition contents from the buffer to NVRAM */
1826 	if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1827 		    total_length)) != 0)
1828 		goto fail7;
1829 
1830 	/* Unlock the partition */
1831 	ef10_nvram_partn_unlock(enp, partn);
1832 
1833 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1834 
1835 	return (0);
1836 
1837 fail7:
1838 	EFSYS_PROBE(fail7);
1839 fail6:
1840 	EFSYS_PROBE(fail6);
1841 fail5:
1842 	EFSYS_PROBE(fail5);
1843 fail4:
1844 	EFSYS_PROBE(fail4);
1845 
1846 	ef10_nvram_partn_unlock(enp, partn);
1847 fail3:
1848 	EFSYS_PROBE(fail3);
1849 
1850 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1851 fail2:
1852 	EFSYS_PROBE(fail2);
1853 fail1:
1854 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1855 
1856 	return (rc);
1857 }
1858 
1859 /*
1860  * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1861  * not the data used by the segments in the partition.
1862  */
1863 	__checkReturn		efx_rc_t
1864 ef10_nvram_partn_size(
1865 	__in			efx_nic_t *enp,
1866 	__in			uint32_t partn,
1867 	__out			size_t *sizep)
1868 {
1869 	efx_rc_t rc;
1870 
1871 	if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1872 	    NULL, NULL, NULL)) != 0)
1873 		goto fail1;
1874 
1875 	return (0);
1876 
1877 fail1:
1878 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1879 
1880 	return (rc);
1881 }
1882 
1883 	__checkReturn		efx_rc_t
1884 ef10_nvram_partn_lock(
1885 	__in			efx_nic_t *enp,
1886 	__in			uint32_t partn)
1887 {
1888 	efx_rc_t rc;
1889 
1890 	if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1891 		goto fail1;
1892 
1893 	return (0);
1894 
1895 fail1:
1896 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1897 
1898 	return (rc);
1899 }
1900 
1901 	__checkReturn		efx_rc_t
1902 ef10_nvram_partn_read_mode(
1903 	__in			efx_nic_t *enp,
1904 	__in			uint32_t partn,
1905 	__in			unsigned int offset,
1906 	__out_bcount(size)	caddr_t data,
1907 	__in			size_t size,
1908 	__in			uint32_t mode)
1909 {
1910 	size_t chunk;
1911 	efx_rc_t rc;
1912 
1913 	while (size > 0) {
1914 		chunk = MIN(size, EF10_NVRAM_CHUNK);
1915 
1916 		if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
1917 			    data, chunk, mode)) != 0) {
1918 			goto fail1;
1919 		}
1920 
1921 		size -= chunk;
1922 		data += chunk;
1923 		offset += chunk;
1924 	}
1925 
1926 	return (0);
1927 
1928 fail1:
1929 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1930 
1931 	return (rc);
1932 }
1933 
1934 	__checkReturn		efx_rc_t
1935 ef10_nvram_partn_read(
1936 	__in			efx_nic_t *enp,
1937 	__in			uint32_t partn,
1938 	__in			unsigned int offset,
1939 	__out_bcount(size)	caddr_t data,
1940 	__in			size_t size)
1941 {
1942 	/*
1943 	 * Read requests which come in through the EFX API expect to
1944 	 * read the current, active partition.
1945 	 */
1946 	return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
1947 			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
1948 }
1949 
1950 	__checkReturn		efx_rc_t
1951 ef10_nvram_partn_erase(
1952 	__in			efx_nic_t *enp,
1953 	__in			uint32_t partn,
1954 	__in			unsigned int offset,
1955 	__in			size_t size)
1956 {
1957 	efx_rc_t rc;
1958 	uint32_t erase_size;
1959 
1960 	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1961 	    &erase_size, NULL)) != 0)
1962 		goto fail1;
1963 
1964 	if (erase_size == 0) {
1965 		if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
1966 			goto fail2;
1967 	} else {
1968 		if (size % erase_size != 0) {
1969 			rc = EINVAL;
1970 			goto fail3;
1971 		}
1972 		while (size > 0) {
1973 			if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
1974 			    erase_size)) != 0)
1975 				goto fail4;
1976 			offset += erase_size;
1977 			size -= erase_size;
1978 		}
1979 	}
1980 
1981 	return (0);
1982 
1983 fail4:
1984 	EFSYS_PROBE(fail4);
1985 fail3:
1986 	EFSYS_PROBE(fail3);
1987 fail2:
1988 	EFSYS_PROBE(fail2);
1989 fail1:
1990 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1991 
1992 	return (rc);
1993 }
1994 
1995 	__checkReturn		efx_rc_t
1996 ef10_nvram_partn_write(
1997 	__in			efx_nic_t *enp,
1998 	__in			uint32_t partn,
1999 	__in			unsigned int offset,
2000 	__out_bcount(size)	caddr_t data,
2001 	__in			size_t size)
2002 {
2003 	size_t chunk;
2004 	uint32_t write_size;
2005 	efx_rc_t rc;
2006 
2007 	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2008 	    NULL, &write_size)) != 0)
2009 		goto fail1;
2010 
2011 	if (write_size != 0) {
2012 		/*
2013 		 * Check that the size is a multiple of the write chunk size if
2014 		 * the write chunk size is available.
2015 		 */
2016 		if (size % write_size != 0) {
2017 			rc = EINVAL;
2018 			goto fail2;
2019 		}
2020 	} else {
2021 		write_size = EF10_NVRAM_CHUNK;
2022 	}
2023 
2024 	while (size > 0) {
2025 		chunk = MIN(size, write_size);
2026 
2027 		if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
2028 			    data, chunk)) != 0) {
2029 			goto fail3;
2030 		}
2031 
2032 		size -= chunk;
2033 		data += chunk;
2034 		offset += chunk;
2035 	}
2036 
2037 	return (0);
2038 
2039 fail3:
2040 	EFSYS_PROBE(fail3);
2041 fail2:
2042 	EFSYS_PROBE(fail2);
2043 fail1:
2044 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2045 
2046 	return (rc);
2047 }
2048 
2049 				void
2050 ef10_nvram_partn_unlock(
2051 	__in			efx_nic_t *enp,
2052 	__in			uint32_t partn)
2053 {
2054 	boolean_t reboot;
2055 	efx_rc_t rc;
2056 
2057 	reboot = B_FALSE;
2058 	if ((rc = efx_mcdi_nvram_update_finish(enp, partn, reboot)) != 0)
2059 		goto fail1;
2060 
2061 	return;
2062 
2063 fail1:
2064 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2065 }
2066 
2067 	__checkReturn		efx_rc_t
2068 ef10_nvram_partn_set_version(
2069 	__in			efx_nic_t *enp,
2070 	__in			uint32_t partn,
2071 	__in_ecount(4)		uint16_t version[4])
2072 {
2073 	struct tlv_partition_version partn_version;
2074 	size_t size;
2075 	efx_rc_t rc;
2076 
2077 	/* Add or modify partition version TLV item */
2078 	partn_version.version_w = __CPU_TO_LE_16(version[0]);
2079 	partn_version.version_x = __CPU_TO_LE_16(version[1]);
2080 	partn_version.version_y = __CPU_TO_LE_16(version[2]);
2081 	partn_version.version_z = __CPU_TO_LE_16(version[3]);
2082 
2083 	size = sizeof (partn_version) - (2 * sizeof (uint32_t));
2084 
2085 	/* Write the version number to all segments in the partition */
2086 	if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
2087 		    NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
2088 		    TLV_TAG_PARTITION_VERSION(partn),
2089 		    (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
2090 		goto fail1;
2091 
2092 	return (0);
2093 
2094 fail1:
2095 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2096 
2097 	return (rc);
2098 }
2099 
2100 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
2101 
2102 #if EFSYS_OPT_NVRAM
2103 
2104 typedef struct ef10_parttbl_entry_s {
2105 	unsigned int		partn;
2106 	unsigned int		port;
2107 	efx_nvram_type_t	nvtype;
2108 } ef10_parttbl_entry_t;
2109 
2110 /* Translate EFX NVRAM types to firmware partition types */
2111 static ef10_parttbl_entry_t hunt_parttbl[] = {
2112 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   1, EFX_NVRAM_MC_FIRMWARE},
2113 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   2, EFX_NVRAM_MC_FIRMWARE},
2114 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   3, EFX_NVRAM_MC_FIRMWARE},
2115 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   4, EFX_NVRAM_MC_FIRMWARE},
2116 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
2117 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
2118 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
2119 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
2120 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   1, EFX_NVRAM_BOOTROM},
2121 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   2, EFX_NVRAM_BOOTROM},
2122 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   3, EFX_NVRAM_BOOTROM},
2123 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   4, EFX_NVRAM_BOOTROM},
2124 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
2125 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1, 2, EFX_NVRAM_BOOTROM_CFG},
2126 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2, 3, EFX_NVRAM_BOOTROM_CFG},
2127 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 4, EFX_NVRAM_BOOTROM_CFG},
2128 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   1, EFX_NVRAM_DYNAMIC_CFG},
2129 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   2, EFX_NVRAM_DYNAMIC_CFG},
2130 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   3, EFX_NVRAM_DYNAMIC_CFG},
2131 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   4, EFX_NVRAM_DYNAMIC_CFG},
2132 	{NVRAM_PARTITION_TYPE_FPGA,		   1, EFX_NVRAM_FPGA},
2133 	{NVRAM_PARTITION_TYPE_FPGA,		   2, EFX_NVRAM_FPGA},
2134 	{NVRAM_PARTITION_TYPE_FPGA,		   3, EFX_NVRAM_FPGA},
2135 	{NVRAM_PARTITION_TYPE_FPGA,		   4, EFX_NVRAM_FPGA},
2136 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   1, EFX_NVRAM_FPGA_BACKUP},
2137 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   2, EFX_NVRAM_FPGA_BACKUP},
2138 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   3, EFX_NVRAM_FPGA_BACKUP},
2139 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   4, EFX_NVRAM_FPGA_BACKUP},
2140 	{NVRAM_PARTITION_TYPE_LICENSE,		   1, EFX_NVRAM_LICENSE},
2141 	{NVRAM_PARTITION_TYPE_LICENSE,		   2, EFX_NVRAM_LICENSE},
2142 	{NVRAM_PARTITION_TYPE_LICENSE,		   3, EFX_NVRAM_LICENSE},
2143 	{NVRAM_PARTITION_TYPE_LICENSE,		   4, EFX_NVRAM_LICENSE}
2144 };
2145 
2146 static ef10_parttbl_entry_t medford_parttbl[] = {
2147 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   1, EFX_NVRAM_MC_FIRMWARE},
2148 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   2, EFX_NVRAM_MC_FIRMWARE},
2149 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   3, EFX_NVRAM_MC_FIRMWARE},
2150 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   4, EFX_NVRAM_MC_FIRMWARE},
2151 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
2152 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
2153 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
2154 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
2155 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   1, EFX_NVRAM_BOOTROM},
2156 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   2, EFX_NVRAM_BOOTROM},
2157 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   3, EFX_NVRAM_BOOTROM},
2158 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   4, EFX_NVRAM_BOOTROM},
2159 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
2160 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 2, EFX_NVRAM_BOOTROM_CFG},
2161 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 3, EFX_NVRAM_BOOTROM_CFG},
2162 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 4, EFX_NVRAM_BOOTROM_CFG},
2163 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   1, EFX_NVRAM_DYNAMIC_CFG},
2164 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   2, EFX_NVRAM_DYNAMIC_CFG},
2165 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   3, EFX_NVRAM_DYNAMIC_CFG},
2166 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   4, EFX_NVRAM_DYNAMIC_CFG},
2167 	{NVRAM_PARTITION_TYPE_FPGA,		   1, EFX_NVRAM_FPGA},
2168 	{NVRAM_PARTITION_TYPE_FPGA,		   2, EFX_NVRAM_FPGA},
2169 	{NVRAM_PARTITION_TYPE_FPGA,		   3, EFX_NVRAM_FPGA},
2170 	{NVRAM_PARTITION_TYPE_FPGA,		   4, EFX_NVRAM_FPGA},
2171 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   1, EFX_NVRAM_FPGA_BACKUP},
2172 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   2, EFX_NVRAM_FPGA_BACKUP},
2173 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   3, EFX_NVRAM_FPGA_BACKUP},
2174 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   4, EFX_NVRAM_FPGA_BACKUP},
2175 	{NVRAM_PARTITION_TYPE_LICENSE,		   1, EFX_NVRAM_LICENSE},
2176 	{NVRAM_PARTITION_TYPE_LICENSE,		   2, EFX_NVRAM_LICENSE},
2177 	{NVRAM_PARTITION_TYPE_LICENSE,		   3, EFX_NVRAM_LICENSE},
2178 	{NVRAM_PARTITION_TYPE_LICENSE,		   4, EFX_NVRAM_LICENSE}
2179 };
2180 
2181 static	__checkReturn		efx_rc_t
2182 ef10_parttbl_get(
2183 	__in			efx_nic_t *enp,
2184 	__out			ef10_parttbl_entry_t **parttblp,
2185 	__out			size_t *parttbl_rowsp)
2186 {
2187 	switch (enp->en_family) {
2188 	case EFX_FAMILY_HUNTINGTON:
2189 		*parttblp = hunt_parttbl;
2190 		*parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
2191 		break;
2192 
2193 	case EFX_FAMILY_MEDFORD:
2194 		*parttblp = medford_parttbl;
2195 		*parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
2196 		break;
2197 
2198 	default:
2199 		EFSYS_ASSERT(B_FALSE);
2200 		return (EINVAL);
2201 	}
2202 	return (0);
2203 }
2204 
2205 	__checkReturn		efx_rc_t
2206 ef10_nvram_type_to_partn(
2207 	__in			efx_nic_t *enp,
2208 	__in			efx_nvram_type_t type,
2209 	__out			uint32_t *partnp)
2210 {
2211 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2212 	ef10_parttbl_entry_t *parttbl = NULL;
2213 	size_t parttbl_rows = 0;
2214 	unsigned int i;
2215 
2216 	EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
2217 	EFSYS_ASSERT(partnp != NULL);
2218 
2219 	if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2220 		for (i = 0; i < parttbl_rows; i++) {
2221 			ef10_parttbl_entry_t *entry = &parttbl[i];
2222 
2223 			if (entry->nvtype == type &&
2224 			    entry->port == emip->emi_port) {
2225 				*partnp = entry->partn;
2226 				return (0);
2227 			}
2228 		}
2229 	}
2230 
2231 	return (ENOTSUP);
2232 }
2233 
2234 #if EFSYS_OPT_DIAG
2235 
2236 static	__checkReturn		efx_rc_t
2237 ef10_nvram_partn_to_type(
2238 	__in			efx_nic_t *enp,
2239 	__in			uint32_t partn,
2240 	__out			efx_nvram_type_t *typep)
2241 {
2242 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2243 	ef10_parttbl_entry_t *parttbl = NULL;
2244 	size_t parttbl_rows = 0;
2245 	unsigned int i;
2246 
2247 	EFSYS_ASSERT(typep != NULL);
2248 
2249 	if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2250 		for (i = 0; i < parttbl_rows; i++) {
2251 			ef10_parttbl_entry_t *entry = &parttbl[i];
2252 
2253 			if (entry->partn == partn &&
2254 			    entry->port == emip->emi_port) {
2255 				*typep = entry->nvtype;
2256 				return (0);
2257 			}
2258 		}
2259 	}
2260 
2261 	return (ENOTSUP);
2262 }
2263 
2264 	__checkReturn		efx_rc_t
2265 ef10_nvram_test(
2266 	__in			efx_nic_t *enp)
2267 {
2268 	efx_nvram_type_t type;
2269 	unsigned int npartns = 0;
2270 	uint32_t *partns = NULL;
2271 	size_t size;
2272 	unsigned int i;
2273 	efx_rc_t rc;
2274 
2275 	/* Read available partitions from NVRAM partition map */
2276 	size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
2277 	EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
2278 	if (partns == NULL) {
2279 		rc = ENOMEM;
2280 		goto fail1;
2281 	}
2282 
2283 	if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
2284 		    &npartns)) != 0) {
2285 		goto fail2;
2286 	}
2287 
2288 	for (i = 0; i < npartns; i++) {
2289 		/* Check if the partition is supported for this port */
2290 		if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
2291 			continue;
2292 
2293 		if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
2294 			goto fail3;
2295 	}
2296 
2297 	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2298 	return (0);
2299 
2300 fail3:
2301 	EFSYS_PROBE(fail3);
2302 fail2:
2303 	EFSYS_PROBE(fail2);
2304 	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2305 fail1:
2306 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2307 	return (rc);
2308 }
2309 
2310 #endif	/* EFSYS_OPT_DIAG */
2311 
2312 	__checkReturn		efx_rc_t
2313 ef10_nvram_partn_get_version(
2314 	__in			efx_nic_t *enp,
2315 	__in			uint32_t partn,
2316 	__out			uint32_t *subtypep,
2317 	__out_ecount(4)		uint16_t version[4])
2318 {
2319 	efx_rc_t rc;
2320 
2321 	/* FIXME: get highest partn version from all ports */
2322 	/* FIXME: return partn description if available */
2323 
2324 	if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
2325 		    version, NULL, 0)) != 0)
2326 		goto fail1;
2327 
2328 	return (0);
2329 
2330 fail1:
2331 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2332 
2333 	return (rc);
2334 }
2335 
2336 	__checkReturn		efx_rc_t
2337 ef10_nvram_partn_rw_start(
2338 	__in			efx_nic_t *enp,
2339 	__in			uint32_t partn,
2340 	__out			size_t *chunk_sizep)
2341 {
2342 	efx_rc_t rc;
2343 
2344 	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
2345 		goto fail1;
2346 
2347 	if (chunk_sizep != NULL)
2348 		*chunk_sizep = EF10_NVRAM_CHUNK;
2349 
2350 	return (0);
2351 
2352 fail1:
2353 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2354 
2355 	return (rc);
2356 }
2357 
2358 				void
2359 ef10_nvram_partn_rw_finish(
2360 	__in			efx_nic_t *enp,
2361 	__in			uint32_t partn)
2362 {
2363 	ef10_nvram_partn_unlock(enp, partn);
2364 }
2365 
2366 #endif	/* EFSYS_OPT_NVRAM */
2367 
2368 #endif	/* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
2369