xref: /freebsd/sys/dev/sfxge/common/ef10_nvram.c (revision 0957b409)
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 += 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			uint32_t partn,
666 	__in_bcount(partn_size)	caddr_t partn_data,
667 	__in			size_t partn_size)
668 {
669 	tlv_cursor_t cursor;
670 	struct tlv_partition_header *header;
671 	struct tlv_partition_trailer *trailer;
672 	size_t total_length;
673 	uint32_t cksum;
674 	int pos;
675 	efx_rc_t rc;
676 
677 	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
678 
679 	if ((partn_data == NULL) || (partn_size == 0)) {
680 		rc = EINVAL;
681 		goto fail1;
682 	}
683 
684 	/* The partition header must be the first item (at offset zero) */
685 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
686 		    partn_size)) != 0) {
687 		rc = EFAULT;
688 		goto fail2;
689 	}
690 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
691 		rc = EINVAL;
692 		goto fail3;
693 	}
694 	header = (struct tlv_partition_header *)tlv_item(&cursor);
695 
696 	/* Check TLV partition length (includes the END tag) */
697 	total_length = __LE_TO_CPU_32(header->total_length);
698 	if (total_length > partn_size) {
699 		rc = EFBIG;
700 		goto fail4;
701 	}
702 
703 	/* Check partition header matches partn */
704 	if (__LE_TO_CPU_16(header->type_id) != partn) {
705 		rc = EINVAL;
706 		goto fail5;
707 	}
708 
709 	/* Check partition ends with PARTITION_TRAILER and END tags */
710 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
711 		rc = EINVAL;
712 		goto fail6;
713 	}
714 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
715 
716 	if ((rc = tlv_advance(&cursor)) != 0) {
717 		rc = EINVAL;
718 		goto fail7;
719 	}
720 	if (tlv_tag(&cursor) != TLV_TAG_END) {
721 		rc = EINVAL;
722 		goto fail8;
723 	}
724 
725 	/* Check generation counts are consistent */
726 	if (trailer->generation != header->generation) {
727 		rc = EINVAL;
728 		goto fail9;
729 	}
730 
731 	/* Verify partition checksum */
732 	cksum = 0;
733 	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
734 		cksum += *((uint32_t *)(partn_data + pos));
735 	}
736 	if (cksum != 0) {
737 		rc = EINVAL;
738 		goto fail10;
739 	}
740 
741 	return (0);
742 
743 fail10:
744 	EFSYS_PROBE(fail10);
745 fail9:
746 	EFSYS_PROBE(fail9);
747 fail8:
748 	EFSYS_PROBE(fail8);
749 fail7:
750 	EFSYS_PROBE(fail7);
751 fail6:
752 	EFSYS_PROBE(fail6);
753 fail5:
754 	EFSYS_PROBE(fail5);
755 fail4:
756 	EFSYS_PROBE(fail4);
757 fail3:
758 	EFSYS_PROBE(fail3);
759 fail2:
760 	EFSYS_PROBE(fail2);
761 fail1:
762 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
763 
764 	return (rc);
765 }
766 
767 			void
768 ef10_nvram_buffer_init(
769 	__out_bcount(buffer_size)
770 				caddr_t bufferp,
771 	__in			size_t buffer_size)
772 {
773 	uint32_t *buf = (uint32_t *)bufferp;
774 
775 	memset(buf, 0xff, buffer_size);
776 
777 	tlv_init_block(buf);
778 }
779 
780 	__checkReturn		efx_rc_t
781 ef10_nvram_buffer_create(
782 	__in			uint32_t partn_type,
783 	__out_bcount(partn_size)
784 				caddr_t partn_data,
785 	__in			size_t partn_size)
786 {
787 	uint32_t *buf = (uint32_t *)partn_data;
788 	efx_rc_t rc;
789 	tlv_cursor_t cursor;
790 	struct tlv_partition_header header;
791 	struct tlv_partition_trailer trailer;
792 
793 	unsigned int min_buf_size = sizeof (struct tlv_partition_header) +
794 	    sizeof (struct tlv_partition_trailer);
795 	if (partn_size < min_buf_size) {
796 		rc = EINVAL;
797 		goto fail1;
798 	}
799 
800 	ef10_nvram_buffer_init(partn_data, partn_size);
801 
802 	if ((rc = tlv_init_cursor(&cursor, buf,
803 	    (uint32_t *)((uint8_t *)buf + partn_size),
804 	    buf)) != 0) {
805 		goto fail2;
806 	}
807 
808 	header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
809 	header.length = __CPU_TO_LE_32(sizeof (header) - 8);
810 	header.type_id = __CPU_TO_LE_16(partn_type);
811 	header.preset = 0;
812 	header.generation = __CPU_TO_LE_32(1);
813 	header.total_length = 0;  /* This will be fixed below. */
814 	if ((rc = tlv_insert(
815 	    &cursor, TLV_TAG_PARTITION_HEADER,
816 	    (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
817 		goto fail3;
818 	if ((rc = tlv_advance(&cursor)) != 0)
819 		goto fail4;
820 
821 	trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
822 	trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
823 	trailer.generation = header.generation;
824 	trailer.checksum = 0;  /* This will be fixed below. */
825 	if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
826 	    (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
827 		goto fail5;
828 
829 	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
830 		goto fail6;
831 
832 	/* Check that the partition is valid. */
833 	if ((rc = ef10_nvram_buffer_validate(partn_type,
834 	    partn_data, partn_size)) != 0)
835 		goto fail7;
836 
837 	return (0);
838 
839 fail7:
840 	EFSYS_PROBE(fail7);
841 fail6:
842 	EFSYS_PROBE(fail6);
843 fail5:
844 	EFSYS_PROBE(fail5);
845 fail4:
846 	EFSYS_PROBE(fail4);
847 fail3:
848 	EFSYS_PROBE(fail3);
849 fail2:
850 	EFSYS_PROBE(fail2);
851 fail1:
852 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
853 
854 	return (rc);
855 }
856 
857 static			uint32_t
858 byte_offset(
859 	__in		uint32_t *position,
860 	__in		uint32_t *base)
861 {
862 	return (uint32_t)((uint8_t *)position - (uint8_t *)base);
863 }
864 
865 	__checkReturn		efx_rc_t
866 ef10_nvram_buffer_find_item_start(
867 	__in_bcount(buffer_size)
868 				caddr_t bufferp,
869 	__in			size_t buffer_size,
870 	__out			uint32_t *startp)
871 {
872 	/* Read past partition header to find start address of the first key */
873 	tlv_cursor_t cursor;
874 	efx_rc_t rc;
875 
876 	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
877 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
878 			buffer_size)) != 0) {
879 		rc = EFAULT;
880 		goto fail1;
881 	}
882 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
883 		rc = EINVAL;
884 		goto fail2;
885 	}
886 
887 	if ((rc = tlv_advance(&cursor)) != 0) {
888 		rc = EINVAL;
889 		goto fail3;
890 	}
891 	*startp = byte_offset(cursor.current, cursor.block);
892 
893 	if ((rc = tlv_require_end(&cursor)) != 0)
894 		goto fail4;
895 
896 	return (0);
897 
898 fail4:
899 	EFSYS_PROBE(fail4);
900 fail3:
901 	EFSYS_PROBE(fail3);
902 fail2:
903 	EFSYS_PROBE(fail2);
904 fail1:
905 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
906 
907 	return (rc);
908 }
909 
910 	__checkReturn		efx_rc_t
911 ef10_nvram_buffer_find_end(
912 	__in_bcount(buffer_size)
913 				caddr_t bufferp,
914 	__in			size_t buffer_size,
915 	__in			uint32_t offset,
916 	__out			uint32_t *endp)
917 {
918 	/* Read to end of partition */
919 	tlv_cursor_t cursor;
920 	efx_rc_t rc;
921 	uint32_t *segment_used;
922 
923 	_NOTE(ARGUNUSED(offset))
924 
925 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
926 			buffer_size)) != 0) {
927 		rc = EFAULT;
928 		goto fail1;
929 	}
930 
931 	segment_used = cursor.block;
932 
933 	/*
934 	 * Go through each segment and check that it has an end tag. If there
935 	 * is no end tag then the previous segment was the last valid one,
936 	 * so return the used space including that end tag.
937 	 */
938 	while (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
939 		if (tlv_require_end(&cursor) != 0) {
940 			if (segment_used == cursor.block) {
941 				/*
942 				 * First segment is corrupt, so there is
943 				 * no valid data in partition.
944 				 */
945 				rc = EINVAL;
946 				goto fail2;
947 			}
948 			break;
949 		}
950 		segment_used = cursor.end + 1;
951 
952 		cursor.current = segment_used;
953 	}
954 	/* Return space used (including the END tag) */
955 	*endp = (segment_used - cursor.block) * sizeof (uint32_t);
956 
957 	return (0);
958 
959 fail2:
960 	EFSYS_PROBE(fail2);
961 fail1:
962 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
963 
964 	return (rc);
965 }
966 
967 	__checkReturn	__success(return != B_FALSE)	boolean_t
968 ef10_nvram_buffer_find_item(
969 	__in_bcount(buffer_size)
970 				caddr_t bufferp,
971 	__in			size_t buffer_size,
972 	__in			uint32_t offset,
973 	__out			uint32_t *startp,
974 	__out			uint32_t *lengthp)
975 {
976 	/* Find TLV at offset and return key start and length */
977 	tlv_cursor_t cursor;
978 	uint8_t *key;
979 	uint32_t tag;
980 
981 	if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
982 			buffer_size, offset) != 0) {
983 		return (B_FALSE);
984 	}
985 
986 	while ((key = tlv_item(&cursor)) != NULL) {
987 		tag = tlv_tag(&cursor);
988 		if (tag == TLV_TAG_PARTITION_HEADER ||
989 		    tag == TLV_TAG_PARTITION_TRAILER) {
990 			if (tlv_advance(&cursor) != 0) {
991 				break;
992 			}
993 			continue;
994 		}
995 		*startp = byte_offset(cursor.current, cursor.block);
996 		*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
997 		    cursor.current);
998 		return (B_TRUE);
999 	}
1000 
1001 	return (B_FALSE);
1002 }
1003 
1004 	__checkReturn		efx_rc_t
1005 ef10_nvram_buffer_peek_item(
1006 	__in_bcount(buffer_size)
1007 				caddr_t bufferp,
1008 	__in			size_t buffer_size,
1009 	__in			uint32_t offset,
1010 	__out			uint32_t *tagp,
1011 	__out			uint32_t *lengthp,
1012 	__out			uint32_t *value_offsetp)
1013 {
1014 	efx_rc_t rc;
1015 	tlv_cursor_t cursor;
1016 	uint32_t tag;
1017 
1018 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1019 			buffer_size, offset)) != 0) {
1020 		goto fail1;
1021 	}
1022 
1023 	tag = tlv_tag(&cursor);
1024 	*tagp = tag;
1025 	if (tag == TLV_TAG_END) {
1026 		/*
1027 		 * To allow stepping over the END tag, report the full tag
1028 		 * length and a zero length value.
1029 		 */
1030 		*lengthp = sizeof (tag);
1031 		*value_offsetp = sizeof (tag);
1032 	} else {
1033 		*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1034 			    cursor.current);
1035 		*value_offsetp = byte_offset((uint32_t *)tlv_value(&cursor),
1036 			    cursor.current);
1037 	}
1038 	return (0);
1039 
1040 fail1:
1041 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1042 
1043 	return (rc);
1044 }
1045 
1046 	__checkReturn		efx_rc_t
1047 ef10_nvram_buffer_get_item(
1048 	__in_bcount(buffer_size)
1049 				caddr_t bufferp,
1050 	__in			size_t buffer_size,
1051 	__in			uint32_t offset,
1052 	__in			uint32_t length,
1053 	__out			uint32_t *tagp,
1054 	__out_bcount_part(value_max_size, *lengthp)
1055 				caddr_t valuep,
1056 	__in			size_t value_max_size,
1057 	__out			uint32_t *lengthp)
1058 {
1059 	efx_rc_t rc;
1060 	tlv_cursor_t cursor;
1061 	uint32_t value_length;
1062 
1063 	if (buffer_size < (offset + length)) {
1064 		rc = ENOSPC;
1065 		goto fail1;
1066 	}
1067 
1068 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1069 			buffer_size, offset)) != 0) {
1070 		goto fail2;
1071 	}
1072 
1073 	value_length = tlv_length(&cursor);
1074 	if (value_max_size < value_length) {
1075 		rc = ENOSPC;
1076 		goto fail3;
1077 	}
1078 	memcpy(valuep, tlv_value(&cursor), value_length);
1079 
1080 	*tagp = tlv_tag(&cursor);
1081 	*lengthp = value_length;
1082 
1083 	return (0);
1084 
1085 fail3:
1086 	EFSYS_PROBE(fail3);
1087 fail2:
1088 	EFSYS_PROBE(fail2);
1089 fail1:
1090 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1091 
1092 	return (rc);
1093 }
1094 
1095 	__checkReturn		efx_rc_t
1096 ef10_nvram_buffer_insert_item(
1097 	__in_bcount(buffer_size)
1098 				caddr_t bufferp,
1099 	__in			size_t buffer_size,
1100 	__in			uint32_t offset,
1101 	__in			uint32_t tag,
1102 	__in_bcount(length)	caddr_t valuep,
1103 	__in			uint32_t length,
1104 	__out			uint32_t *lengthp)
1105 {
1106 	efx_rc_t rc;
1107 	tlv_cursor_t cursor;
1108 
1109 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1110 			buffer_size, offset)) != 0) {
1111 		goto fail1;
1112 	}
1113 
1114 	rc = tlv_insert(&cursor, tag, (uint8_t *)valuep, length);
1115 
1116 	if (rc != 0)
1117 		goto fail2;
1118 
1119 	*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1120 		    cursor.current);
1121 
1122 	return (0);
1123 
1124 fail2:
1125 	EFSYS_PROBE(fail2);
1126 fail1:
1127 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1128 
1129 	return (rc);
1130 }
1131 
1132 	__checkReturn		efx_rc_t
1133 ef10_nvram_buffer_modify_item(
1134 	__in_bcount(buffer_size)
1135 				caddr_t bufferp,
1136 	__in			size_t buffer_size,
1137 	__in			uint32_t offset,
1138 	__in			uint32_t tag,
1139 	__in_bcount(length)	caddr_t valuep,
1140 	__in			uint32_t length,
1141 	__out			uint32_t *lengthp)
1142 {
1143 	efx_rc_t rc;
1144 	tlv_cursor_t cursor;
1145 
1146 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1147 			buffer_size, offset)) != 0) {
1148 		goto fail1;
1149 	}
1150 
1151 	rc = tlv_modify(&cursor, tag, (uint8_t *)valuep, length);
1152 
1153 	if (rc != 0) {
1154 		goto fail2;
1155 	}
1156 
1157 	*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1158 		    cursor.current);
1159 
1160 	return (0);
1161 
1162 fail2:
1163 	EFSYS_PROBE(fail2);
1164 fail1:
1165 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1166 
1167 	return (rc);
1168 }
1169 
1170 
1171 	__checkReturn		efx_rc_t
1172 ef10_nvram_buffer_delete_item(
1173 	__in_bcount(buffer_size)
1174 				caddr_t bufferp,
1175 	__in			size_t buffer_size,
1176 	__in			uint32_t offset,
1177 	__in			uint32_t length,
1178 	__in			uint32_t end)
1179 {
1180 	efx_rc_t rc;
1181 	tlv_cursor_t cursor;
1182 
1183 	_NOTE(ARGUNUSED(length, end))
1184 
1185 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1186 			buffer_size, offset)) != 0) {
1187 		goto fail1;
1188 	}
1189 
1190 	if ((rc = tlv_delete(&cursor)) != 0)
1191 		goto fail2;
1192 
1193 	return (0);
1194 
1195 fail2:
1196 	EFSYS_PROBE(fail2);
1197 fail1:
1198 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1199 
1200 	return (rc);
1201 }
1202 
1203 	__checkReturn		efx_rc_t
1204 ef10_nvram_buffer_finish(
1205 	__in_bcount(buffer_size)
1206 				caddr_t bufferp,
1207 	__in			size_t buffer_size)
1208 {
1209 	efx_rc_t rc;
1210 	tlv_cursor_t cursor;
1211 
1212 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
1213 			buffer_size)) != 0) {
1214 		rc = EFAULT;
1215 		goto fail1;
1216 	}
1217 
1218 	if ((rc = tlv_require_end(&cursor)) != 0)
1219 		goto fail2;
1220 
1221 	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
1222 		goto fail3;
1223 
1224 	return (0);
1225 
1226 fail3:
1227 	EFSYS_PROBE(fail3);
1228 fail2:
1229 	EFSYS_PROBE(fail2);
1230 fail1:
1231 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1232 
1233 	return (rc);
1234 }
1235 
1236 
1237 
1238 /*
1239  * Read and validate a segment from a partition. A segment is a complete
1240  * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
1241  * be multiple segments in a partition, so seg_offset allows segments
1242  * beyond the first to be read.
1243  */
1244 static	__checkReturn			efx_rc_t
1245 ef10_nvram_read_tlv_segment(
1246 	__in				efx_nic_t *enp,
1247 	__in				uint32_t partn,
1248 	__in				size_t seg_offset,
1249 	__in_bcount(max_seg_size)	caddr_t seg_data,
1250 	__in				size_t max_seg_size)
1251 {
1252 	tlv_cursor_t cursor;
1253 	struct tlv_partition_header *header;
1254 	struct tlv_partition_trailer *trailer;
1255 	size_t total_length;
1256 	uint32_t cksum;
1257 	int pos;
1258 	efx_rc_t rc;
1259 
1260 	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
1261 
1262 	if ((seg_data == NULL) || (max_seg_size == 0)) {
1263 		rc = EINVAL;
1264 		goto fail1;
1265 	}
1266 
1267 	/* Read initial chunk of the segment, starting at offset */
1268 	if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
1269 		    EF10_NVRAM_CHUNK,
1270 		    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
1271 		goto fail2;
1272 	}
1273 
1274 	/* A PARTITION_HEADER tag must be the first item at the given offset */
1275 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1276 		    max_seg_size)) != 0) {
1277 		rc = EFAULT;
1278 		goto fail3;
1279 	}
1280 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1281 		rc = EINVAL;
1282 		goto fail4;
1283 	}
1284 	header = (struct tlv_partition_header *)tlv_item(&cursor);
1285 
1286 	/* Check TLV segment length (includes the END tag) */
1287 	total_length = __LE_TO_CPU_32(header->total_length);
1288 	if (total_length > max_seg_size) {
1289 		rc = EFBIG;
1290 		goto fail5;
1291 	}
1292 
1293 	/* Read the remaining segment content */
1294 	if (total_length > EF10_NVRAM_CHUNK) {
1295 		if ((rc = ef10_nvram_partn_read_mode(enp, partn,
1296 			    seg_offset + EF10_NVRAM_CHUNK,
1297 			    seg_data + EF10_NVRAM_CHUNK,
1298 			    total_length - EF10_NVRAM_CHUNK,
1299 			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
1300 			goto fail6;
1301 	}
1302 
1303 	/* Check segment ends with PARTITION_TRAILER and END tags */
1304 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1305 		rc = EINVAL;
1306 		goto fail7;
1307 	}
1308 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1309 
1310 	if ((rc = tlv_advance(&cursor)) != 0) {
1311 		rc = EINVAL;
1312 		goto fail8;
1313 	}
1314 	if (tlv_tag(&cursor) != TLV_TAG_END) {
1315 		rc = EINVAL;
1316 		goto fail9;
1317 	}
1318 
1319 	/* Check data read from segment is consistent */
1320 	if (trailer->generation != header->generation) {
1321 		/*
1322 		 * The partition data may have been modified between successive
1323 		 * MCDI NVRAM_READ requests by the MC or another PCI function.
1324 		 *
1325 		 * The caller must retry to obtain consistent partition data.
1326 		 */
1327 		rc = EAGAIN;
1328 		goto fail10;
1329 	}
1330 
1331 	/* Verify segment checksum */
1332 	cksum = 0;
1333 	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
1334 		cksum += *((uint32_t *)(seg_data + pos));
1335 	}
1336 	if (cksum != 0) {
1337 		rc = EINVAL;
1338 		goto fail11;
1339 	}
1340 
1341 	return (0);
1342 
1343 fail11:
1344 	EFSYS_PROBE(fail11);
1345 fail10:
1346 	EFSYS_PROBE(fail10);
1347 fail9:
1348 	EFSYS_PROBE(fail9);
1349 fail8:
1350 	EFSYS_PROBE(fail8);
1351 fail7:
1352 	EFSYS_PROBE(fail7);
1353 fail6:
1354 	EFSYS_PROBE(fail6);
1355 fail5:
1356 	EFSYS_PROBE(fail5);
1357 fail4:
1358 	EFSYS_PROBE(fail4);
1359 fail3:
1360 	EFSYS_PROBE(fail3);
1361 fail2:
1362 	EFSYS_PROBE(fail2);
1363 fail1:
1364 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1365 
1366 	return (rc);
1367 }
1368 
1369 /*
1370  * Read a single TLV item from a host memory
1371  * buffer containing a TLV formatted segment.
1372  */
1373 	__checkReturn		efx_rc_t
1374 ef10_nvram_buf_read_tlv(
1375 	__in				efx_nic_t *enp,
1376 	__in_bcount(max_seg_size)	caddr_t seg_data,
1377 	__in				size_t max_seg_size,
1378 	__in				uint32_t tag,
1379 	__deref_out_bcount_opt(*sizep)	caddr_t *datap,
1380 	__out				size_t *sizep)
1381 {
1382 	tlv_cursor_t cursor;
1383 	caddr_t data;
1384 	size_t length;
1385 	caddr_t value;
1386 	efx_rc_t rc;
1387 
1388 	_NOTE(ARGUNUSED(enp))
1389 
1390 	if ((seg_data == NULL) || (max_seg_size == 0)) {
1391 		rc = EINVAL;
1392 		goto fail1;
1393 	}
1394 
1395 	/* Find requested TLV tag in segment data */
1396 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1397 		    max_seg_size)) != 0) {
1398 		rc = EFAULT;
1399 		goto fail2;
1400 	}
1401 	if ((rc = tlv_find(&cursor, tag)) != 0) {
1402 		rc = ENOENT;
1403 		goto fail3;
1404 	}
1405 	value = (caddr_t)tlv_value(&cursor);
1406 	length = tlv_length(&cursor);
1407 
1408 	if (length == 0)
1409 		data = NULL;
1410 	else {
1411 		/* Copy out data from TLV item */
1412 		EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
1413 		if (data == NULL) {
1414 			rc = ENOMEM;
1415 			goto fail4;
1416 		}
1417 		memcpy(data, value, length);
1418 	}
1419 
1420 	*datap = data;
1421 	*sizep = length;
1422 
1423 	return (0);
1424 
1425 fail4:
1426 	EFSYS_PROBE(fail4);
1427 fail3:
1428 	EFSYS_PROBE(fail3);
1429 fail2:
1430 	EFSYS_PROBE(fail2);
1431 fail1:
1432 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1433 
1434 	return (rc);
1435 }
1436 
1437 /* Read a single TLV item from the first segment in a TLV formatted partition */
1438 	__checkReturn		efx_rc_t
1439 ef10_nvram_partn_read_tlv(
1440 	__in					efx_nic_t *enp,
1441 	__in					uint32_t partn,
1442 	__in					uint32_t tag,
1443 	__deref_out_bcount_opt(*seg_sizep)	caddr_t *seg_datap,
1444 	__out					size_t *seg_sizep)
1445 {
1446 	caddr_t seg_data = NULL;
1447 	size_t partn_size = 0;
1448 	size_t length;
1449 	caddr_t data;
1450 	int retry;
1451 	efx_rc_t rc;
1452 
1453 	/* Allocate sufficient memory for the entire partition */
1454 	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1455 		goto fail1;
1456 
1457 	if (partn_size == 0) {
1458 		rc = ENOENT;
1459 		goto fail2;
1460 	}
1461 
1462 	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
1463 	if (seg_data == NULL) {
1464 		rc = ENOMEM;
1465 		goto fail3;
1466 	}
1467 
1468 	/*
1469 	 * Read the first segment in a TLV partition. Retry until consistent
1470 	 * segment contents are returned. Inconsistent data may be read if:
1471 	 *  a) the segment contents are invalid
1472 	 *  b) the MC has rebooted while we were reading the partition
1473 	 *  c) the partition has been modified while we were reading it
1474 	 * Limit retry attempts to ensure forward progress.
1475 	 */
1476 	retry = 10;
1477 	do {
1478 		if ((rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
1479 		    seg_data, partn_size)) != 0)
1480 			--retry;
1481 	} while ((rc == EAGAIN) && (retry > 0));
1482 
1483 	if (rc != 0) {
1484 		/* Failed to obtain consistent segment data */
1485 		if (rc == EAGAIN)
1486 			rc = EIO;
1487 
1488 		goto fail4;
1489 	}
1490 
1491 	if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
1492 		    tag, &data, &length)) != 0)
1493 		goto fail5;
1494 
1495 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1496 
1497 	*seg_datap = data;
1498 	*seg_sizep = length;
1499 
1500 	return (0);
1501 
1502 fail5:
1503 	EFSYS_PROBE(fail5);
1504 fail4:
1505 	EFSYS_PROBE(fail4);
1506 
1507 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1508 fail3:
1509 	EFSYS_PROBE(fail3);
1510 fail2:
1511 	EFSYS_PROBE(fail2);
1512 fail1:
1513 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1514 
1515 	return (rc);
1516 }
1517 
1518 /* Compute the size of a segment. */
1519 	static	__checkReturn	efx_rc_t
1520 ef10_nvram_buf_segment_size(
1521 	__in			caddr_t seg_data,
1522 	__in			size_t max_seg_size,
1523 	__out			size_t *seg_sizep)
1524 {
1525 	efx_rc_t rc;
1526 	tlv_cursor_t cursor;
1527 	struct tlv_partition_header *header;
1528 	uint32_t cksum;
1529 	int pos;
1530 	uint32_t *end_tag_position;
1531 	uint32_t segment_length;
1532 
1533 	/* A PARTITION_HEADER tag must be the first item at the given offset */
1534 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1535 		    max_seg_size)) != 0) {
1536 		rc = EFAULT;
1537 		goto fail1;
1538 	}
1539 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1540 		rc = EINVAL;
1541 		goto fail2;
1542 	}
1543 	header = (struct tlv_partition_header *)tlv_item(&cursor);
1544 
1545 	/* Check TLV segment length (includes the END tag) */
1546 	*seg_sizep = __LE_TO_CPU_32(header->total_length);
1547 	if (*seg_sizep > max_seg_size) {
1548 		rc = EFBIG;
1549 		goto fail3;
1550 	}
1551 
1552 	/* Check segment ends with PARTITION_TRAILER and END tags */
1553 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1554 		rc = EINVAL;
1555 		goto fail4;
1556 	}
1557 
1558 	if ((rc = tlv_advance(&cursor)) != 0) {
1559 		rc = EINVAL;
1560 		goto fail5;
1561 	}
1562 	if (tlv_tag(&cursor) != TLV_TAG_END) {
1563 		rc = EINVAL;
1564 		goto fail6;
1565 	}
1566 	end_tag_position = cursor.current;
1567 
1568 	/* Verify segment checksum */
1569 	cksum = 0;
1570 	for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
1571 		cksum += *((uint32_t *)(seg_data + pos));
1572 	}
1573 	if (cksum != 0) {
1574 		rc = EINVAL;
1575 		goto fail7;
1576 	}
1577 
1578 	/*
1579 	 * Calculate total length from HEADER to END tags and compare to
1580 	 * max_seg_size and the total_length field in the HEADER tag.
1581 	 */
1582 	segment_length = tlv_block_length_used(&cursor);
1583 
1584 	if (segment_length > max_seg_size) {
1585 		rc = EINVAL;
1586 		goto fail8;
1587 	}
1588 
1589 	if (segment_length != *seg_sizep) {
1590 		rc = EINVAL;
1591 		goto fail9;
1592 	}
1593 
1594 	/* Skip over the first HEADER tag. */
1595 	rc = tlv_rewind(&cursor);
1596 	rc = tlv_advance(&cursor);
1597 
1598 	while (rc == 0) {
1599 		if (tlv_tag(&cursor) == TLV_TAG_END) {
1600 			/* Check that the END tag is the one found earlier. */
1601 			if (cursor.current != end_tag_position)
1602 				goto fail10;
1603 			break;
1604 		}
1605 		/* Check for duplicate HEADER tags before the END tag. */
1606 		if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
1607 			rc = EINVAL;
1608 			goto fail11;
1609 		}
1610 
1611 		rc = tlv_advance(&cursor);
1612 	}
1613 	if (rc != 0)
1614 		goto fail12;
1615 
1616 	return (0);
1617 
1618 fail12:
1619 	EFSYS_PROBE(fail12);
1620 fail11:
1621 	EFSYS_PROBE(fail11);
1622 fail10:
1623 	EFSYS_PROBE(fail10);
1624 fail9:
1625 	EFSYS_PROBE(fail9);
1626 fail8:
1627 	EFSYS_PROBE(fail8);
1628 fail7:
1629 	EFSYS_PROBE(fail7);
1630 fail6:
1631 	EFSYS_PROBE(fail6);
1632 fail5:
1633 	EFSYS_PROBE(fail5);
1634 fail4:
1635 	EFSYS_PROBE(fail4);
1636 fail3:
1637 	EFSYS_PROBE(fail3);
1638 fail2:
1639 	EFSYS_PROBE(fail2);
1640 fail1:
1641 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1642 
1643 	return (rc);
1644 }
1645 
1646 /*
1647  * Add or update a single TLV item in a host memory buffer containing a TLV
1648  * formatted segment. Historically partitions consisted of only one segment.
1649  */
1650 	__checkReturn			efx_rc_t
1651 ef10_nvram_buf_write_tlv(
1652 	__inout_bcount(max_seg_size)	caddr_t seg_data,
1653 	__in				size_t max_seg_size,
1654 	__in				uint32_t tag,
1655 	__in_bcount(tag_size)		caddr_t tag_data,
1656 	__in				size_t tag_size,
1657 	__out				size_t *total_lengthp)
1658 {
1659 	tlv_cursor_t cursor;
1660 	struct tlv_partition_header *header;
1661 	struct tlv_partition_trailer *trailer;
1662 	uint32_t generation;
1663 	uint32_t cksum;
1664 	int pos;
1665 	efx_rc_t rc;
1666 
1667 	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
1668 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1669 			max_seg_size)) != 0) {
1670 		rc = EFAULT;
1671 		goto fail1;
1672 	}
1673 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1674 		rc = EINVAL;
1675 		goto fail2;
1676 	}
1677 	header = (struct tlv_partition_header *)tlv_item(&cursor);
1678 
1679 	/* Update the TLV chain to contain the new data */
1680 	if ((rc = tlv_find(&cursor, tag)) == 0) {
1681 		/* Modify existing TLV item */
1682 		if ((rc = tlv_modify(&cursor, tag,
1683 			    (uint8_t *)tag_data, tag_size)) != 0)
1684 			goto fail3;
1685 	} else {
1686 		/* Insert a new TLV item before the PARTITION_TRAILER */
1687 		rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1688 		if (rc != 0) {
1689 			rc = EINVAL;
1690 			goto fail4;
1691 		}
1692 		if ((rc = tlv_insert(&cursor, tag,
1693 			    (uint8_t *)tag_data, tag_size)) != 0) {
1694 			rc = EINVAL;
1695 			goto fail5;
1696 		}
1697 	}
1698 
1699 	/* Find the trailer tag */
1700 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1701 		rc = EINVAL;
1702 		goto fail6;
1703 	}
1704 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1705 
1706 	/* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1707 	*total_lengthp = tlv_block_length_used(&cursor);
1708 	if (*total_lengthp > max_seg_size) {
1709 		rc = ENOSPC;
1710 		goto fail7;
1711 	}
1712 	generation = __LE_TO_CPU_32(header->generation) + 1;
1713 
1714 	header->total_length	= __CPU_TO_LE_32(*total_lengthp);
1715 	header->generation	= __CPU_TO_LE_32(generation);
1716 	trailer->generation	= __CPU_TO_LE_32(generation);
1717 
1718 	/* Recompute PARTITION_TRAILER checksum */
1719 	trailer->checksum = 0;
1720 	cksum = 0;
1721 	for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1722 		cksum += *((uint32_t *)(seg_data + pos));
1723 	}
1724 	trailer->checksum = ~cksum + 1;
1725 
1726 	return (0);
1727 
1728 fail7:
1729 	EFSYS_PROBE(fail7);
1730 fail6:
1731 	EFSYS_PROBE(fail6);
1732 fail5:
1733 	EFSYS_PROBE(fail5);
1734 fail4:
1735 	EFSYS_PROBE(fail4);
1736 fail3:
1737 	EFSYS_PROBE(fail3);
1738 fail2:
1739 	EFSYS_PROBE(fail2);
1740 fail1:
1741 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1742 
1743 	return (rc);
1744 }
1745 
1746 /*
1747  * Add or update a single TLV item in the first segment of a TLV formatted
1748  * dynamic config partition. The first segment is the current active
1749  * configuration.
1750  */
1751 	__checkReturn		efx_rc_t
1752 ef10_nvram_partn_write_tlv(
1753 	__in			efx_nic_t *enp,
1754 	__in			uint32_t partn,
1755 	__in			uint32_t tag,
1756 	__in_bcount(size)	caddr_t data,
1757 	__in			size_t size)
1758 {
1759 	return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1760 	    size, B_FALSE);
1761 }
1762 
1763 /*
1764  * Read a segment from nvram at the given offset into a buffer (segment_data)
1765  * and optionally write a new tag to it.
1766  */
1767 static	__checkReturn		efx_rc_t
1768 ef10_nvram_segment_write_tlv(
1769 	__in			efx_nic_t *enp,
1770 	__in			uint32_t partn,
1771 	__in			uint32_t tag,
1772 	__in_bcount(size)	caddr_t data,
1773 	__in			size_t size,
1774 	__inout			caddr_t *seg_datap,
1775 	__inout			size_t *partn_offsetp,
1776 	__inout			size_t *src_remain_lenp,
1777 	__inout			size_t *dest_remain_lenp,
1778 	__in			boolean_t write)
1779 {
1780 	efx_rc_t rc;
1781 	efx_rc_t status;
1782 	size_t original_segment_size;
1783 	size_t modified_segment_size;
1784 
1785 	/*
1786 	 * Read the segment from NVRAM into the segment_data buffer and validate
1787 	 * it, returning if it does not validate. This is not a failure unless
1788 	 * this is the first segment in a partition. In this case the caller
1789 	 * must propagate the error.
1790 	 */
1791 	status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1792 	    *seg_datap, *src_remain_lenp);
1793 	if (status != 0) {
1794 		rc = EINVAL;
1795 		goto fail1;
1796 	}
1797 
1798 	status = ef10_nvram_buf_segment_size(*seg_datap,
1799 	    *src_remain_lenp, &original_segment_size);
1800 	if (status != 0) {
1801 		rc = EINVAL;
1802 		goto fail2;
1803 	}
1804 
1805 	if (write) {
1806 		/* Update the contents of the segment in the buffer */
1807 		if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1808 			*dest_remain_lenp, tag, data, size,
1809 			&modified_segment_size)) != 0) {
1810 			goto fail3;
1811 		}
1812 		*dest_remain_lenp -= modified_segment_size;
1813 		*seg_datap += modified_segment_size;
1814 	} else {
1815 		/*
1816 		 * We won't modify this segment, but still need to update the
1817 		 * remaining lengths and pointers.
1818 		 */
1819 		*dest_remain_lenp -= original_segment_size;
1820 		*seg_datap += original_segment_size;
1821 	}
1822 
1823 	*partn_offsetp += original_segment_size;
1824 	*src_remain_lenp -= original_segment_size;
1825 
1826 	return (0);
1827 
1828 fail3:
1829 	EFSYS_PROBE(fail3);
1830 fail2:
1831 	EFSYS_PROBE(fail2);
1832 fail1:
1833 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1834 
1835 	return (rc);
1836 }
1837 
1838 /*
1839  * Add or update a single TLV item in either the first segment or in all
1840  * segments in a TLV formatted dynamic config partition. Dynamic config
1841  * partitions on boards that support RFID are divided into a number of segments,
1842  * each formatted like a partition, with header, trailer and end tags. The first
1843  * segment is the current active configuration.
1844  *
1845  * The segments are initialised by manftest and each contain a different
1846  * configuration e.g. firmware variant. The firmware can be instructed
1847  * via RFID to copy a segment to replace the first segment, hence changing the
1848  * active configuration.  This allows ops to change the configuration of a board
1849  * prior to shipment using RFID.
1850  *
1851  * Changes to the dynamic config may need to be written to all segments (e.g.
1852  * firmware versions) or just the first segment (changes to the active
1853  * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1854  * If only the first segment is written the code still needs to be aware of the
1855  * possible presence of subsequent segments as writing to a segment may cause
1856  * its size to increase, which would overwrite the subsequent segments and
1857  * invalidate them.
1858  */
1859 	__checkReturn		efx_rc_t
1860 ef10_nvram_partn_write_segment_tlv(
1861 	__in			efx_nic_t *enp,
1862 	__in			uint32_t partn,
1863 	__in			uint32_t tag,
1864 	__in_bcount(size)	caddr_t data,
1865 	__in			size_t size,
1866 	__in			boolean_t all_segments)
1867 {
1868 	size_t partn_size = 0;
1869 	caddr_t partn_data;
1870 	size_t total_length = 0;
1871 	efx_rc_t rc;
1872 	size_t current_offset = 0;
1873 	size_t remaining_original_length;
1874 	size_t remaining_modified_length;
1875 	caddr_t segment_data;
1876 
1877 	EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1878 
1879 	/* Allocate sufficient memory for the entire partition */
1880 	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1881 		goto fail1;
1882 
1883 	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1884 	if (partn_data == NULL) {
1885 		rc = ENOMEM;
1886 		goto fail2;
1887 	}
1888 
1889 	remaining_original_length = partn_size;
1890 	remaining_modified_length = partn_size;
1891 	segment_data = partn_data;
1892 
1893 	/* Lock the partition */
1894 	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1895 		goto fail3;
1896 
1897 	/* Iterate over each (potential) segment to update it. */
1898 	do {
1899 		boolean_t write = all_segments || current_offset == 0;
1900 
1901 		rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1902 		    &segment_data, &current_offset, &remaining_original_length,
1903 		    &remaining_modified_length, write);
1904 		if (rc != 0) {
1905 			if (current_offset == 0) {
1906 				/*
1907 				 * If no data has been read then the first
1908 				 * segment is invalid, which is an error.
1909 				 */
1910 				goto fail4;
1911 			}
1912 			break;
1913 		}
1914 	} while (current_offset < partn_size);
1915 
1916 	total_length = segment_data - partn_data;
1917 
1918 	/*
1919 	 * We've run out of space.  This should actually be dealt with by
1920 	 * ef10_nvram_buf_write_tlv returning ENOSPC.
1921 	 */
1922 	if (total_length > partn_size) {
1923 		rc = ENOSPC;
1924 		goto fail5;
1925 	}
1926 
1927 	/* Erase the whole partition in NVRAM */
1928 	if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1929 		goto fail6;
1930 
1931 	/* Write new partition contents from the buffer to NVRAM */
1932 	if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1933 		    total_length)) != 0)
1934 		goto fail7;
1935 
1936 	/* Unlock the partition */
1937 	(void) ef10_nvram_partn_unlock(enp, partn, NULL);
1938 
1939 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1940 
1941 	return (0);
1942 
1943 fail7:
1944 	EFSYS_PROBE(fail7);
1945 fail6:
1946 	EFSYS_PROBE(fail6);
1947 fail5:
1948 	EFSYS_PROBE(fail5);
1949 fail4:
1950 	EFSYS_PROBE(fail4);
1951 
1952 	(void) ef10_nvram_partn_unlock(enp, partn, NULL);
1953 fail3:
1954 	EFSYS_PROBE(fail3);
1955 
1956 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1957 fail2:
1958 	EFSYS_PROBE(fail2);
1959 fail1:
1960 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1961 
1962 	return (rc);
1963 }
1964 
1965 /*
1966  * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1967  * not the data used by the segments in the partition.
1968  */
1969 	__checkReturn		efx_rc_t
1970 ef10_nvram_partn_size(
1971 	__in			efx_nic_t *enp,
1972 	__in			uint32_t partn,
1973 	__out			size_t *sizep)
1974 {
1975 	efx_rc_t rc;
1976 
1977 	if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1978 	    NULL, NULL, NULL)) != 0)
1979 		goto fail1;
1980 
1981 	return (0);
1982 
1983 fail1:
1984 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1985 
1986 	return (rc);
1987 }
1988 
1989 	__checkReturn		efx_rc_t
1990 ef10_nvram_partn_lock(
1991 	__in			efx_nic_t *enp,
1992 	__in			uint32_t partn)
1993 {
1994 	efx_rc_t rc;
1995 
1996 	if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1997 		goto fail1;
1998 
1999 	return (0);
2000 
2001 fail1:
2002 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2003 
2004 	return (rc);
2005 }
2006 
2007 	__checkReturn		efx_rc_t
2008 ef10_nvram_partn_read_mode(
2009 	__in			efx_nic_t *enp,
2010 	__in			uint32_t partn,
2011 	__in			unsigned int offset,
2012 	__out_bcount(size)	caddr_t data,
2013 	__in			size_t size,
2014 	__in			uint32_t mode)
2015 {
2016 	size_t chunk;
2017 	efx_rc_t rc;
2018 
2019 	while (size > 0) {
2020 		chunk = MIN(size, EF10_NVRAM_CHUNK);
2021 
2022 		if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
2023 			    data, chunk, mode)) != 0) {
2024 			goto fail1;
2025 		}
2026 
2027 		size -= chunk;
2028 		data += chunk;
2029 		offset += chunk;
2030 	}
2031 
2032 	return (0);
2033 
2034 fail1:
2035 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2036 
2037 	return (rc);
2038 }
2039 
2040 	__checkReturn		efx_rc_t
2041 ef10_nvram_partn_read(
2042 	__in			efx_nic_t *enp,
2043 	__in			uint32_t partn,
2044 	__in			unsigned int offset,
2045 	__out_bcount(size)	caddr_t data,
2046 	__in			size_t size)
2047 {
2048 	/*
2049 	 * An A/B partition has two data stores (current and backup).
2050 	 * Read requests which come in through the EFX API expect to read the
2051 	 * current, active store of an A/B partition. For non A/B partitions,
2052 	 * there is only a single store and so the mode param is ignored.
2053 	 */
2054 	return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
2055 			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
2056 }
2057 
2058 	__checkReturn		efx_rc_t
2059 ef10_nvram_partn_read_backup(
2060 	__in			efx_nic_t *enp,
2061 	__in			uint32_t partn,
2062 	__in			unsigned int offset,
2063 	__out_bcount(size)	caddr_t data,
2064 	__in			size_t size)
2065 {
2066 	/*
2067 	 * An A/B partition has two data stores (current and backup).
2068 	 * Read the backup store of an A/B partition (i.e. the store currently
2069 	 * being written to if the partition is locked).
2070 	 *
2071 	 * This is needed when comparing the existing partition content to avoid
2072 	 * unnecessary writes, or to read back what has been written to check
2073 	 * that the writes have succeeded.
2074 	 */
2075 	return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
2076 			    MC_CMD_NVRAM_READ_IN_V2_TARGET_BACKUP);
2077 }
2078 
2079 	__checkReturn		efx_rc_t
2080 ef10_nvram_partn_erase(
2081 	__in			efx_nic_t *enp,
2082 	__in			uint32_t partn,
2083 	__in			unsigned int offset,
2084 	__in			size_t size)
2085 {
2086 	efx_rc_t rc;
2087 	uint32_t erase_size;
2088 
2089 	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2090 	    &erase_size, NULL)) != 0)
2091 		goto fail1;
2092 
2093 	if (erase_size == 0) {
2094 		if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
2095 			goto fail2;
2096 	} else {
2097 		if (size % erase_size != 0) {
2098 			rc = EINVAL;
2099 			goto fail3;
2100 		}
2101 		while (size > 0) {
2102 			if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
2103 			    erase_size)) != 0)
2104 				goto fail4;
2105 			offset += erase_size;
2106 			size -= erase_size;
2107 		}
2108 	}
2109 
2110 	return (0);
2111 
2112 fail4:
2113 	EFSYS_PROBE(fail4);
2114 fail3:
2115 	EFSYS_PROBE(fail3);
2116 fail2:
2117 	EFSYS_PROBE(fail2);
2118 fail1:
2119 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2120 
2121 	return (rc);
2122 }
2123 
2124 	__checkReturn		efx_rc_t
2125 ef10_nvram_partn_write(
2126 	__in			efx_nic_t *enp,
2127 	__in			uint32_t partn,
2128 	__in			unsigned int offset,
2129 	__in_bcount(size)	caddr_t data,
2130 	__in			size_t size)
2131 {
2132 	size_t chunk;
2133 	uint32_t write_size;
2134 	efx_rc_t rc;
2135 
2136 	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2137 	    NULL, &write_size)) != 0)
2138 		goto fail1;
2139 
2140 	if (write_size != 0) {
2141 		/*
2142 		 * Check that the size is a multiple of the write chunk size if
2143 		 * the write chunk size is available.
2144 		 */
2145 		if (size % write_size != 0) {
2146 			rc = EINVAL;
2147 			goto fail2;
2148 		}
2149 	} else {
2150 		write_size = EF10_NVRAM_CHUNK;
2151 	}
2152 
2153 	while (size > 0) {
2154 		chunk = MIN(size, write_size);
2155 
2156 		if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
2157 			    data, chunk)) != 0) {
2158 			goto fail3;
2159 		}
2160 
2161 		size -= chunk;
2162 		data += chunk;
2163 		offset += chunk;
2164 	}
2165 
2166 	return (0);
2167 
2168 fail3:
2169 	EFSYS_PROBE(fail3);
2170 fail2:
2171 	EFSYS_PROBE(fail2);
2172 fail1:
2173 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2174 
2175 	return (rc);
2176 }
2177 
2178 	__checkReturn		efx_rc_t
2179 ef10_nvram_partn_unlock(
2180 	__in			efx_nic_t *enp,
2181 	__in			uint32_t partn,
2182 	__out_opt		uint32_t *verify_resultp)
2183 {
2184 	boolean_t reboot = B_FALSE;
2185 	efx_rc_t rc;
2186 
2187 	if (verify_resultp != NULL)
2188 		*verify_resultp = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
2189 
2190 	rc = efx_mcdi_nvram_update_finish(enp, partn, reboot, verify_resultp);
2191 	if (rc != 0)
2192 		goto fail1;
2193 
2194 	return (0);
2195 
2196 fail1:
2197 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2198 
2199 	return (rc);
2200 }
2201 
2202 	__checkReturn		efx_rc_t
2203 ef10_nvram_partn_set_version(
2204 	__in			efx_nic_t *enp,
2205 	__in			uint32_t partn,
2206 	__in_ecount(4)		uint16_t version[4])
2207 {
2208 	struct tlv_partition_version partn_version;
2209 	size_t size;
2210 	efx_rc_t rc;
2211 
2212 	/* Add or modify partition version TLV item */
2213 	partn_version.version_w = __CPU_TO_LE_16(version[0]);
2214 	partn_version.version_x = __CPU_TO_LE_16(version[1]);
2215 	partn_version.version_y = __CPU_TO_LE_16(version[2]);
2216 	partn_version.version_z = __CPU_TO_LE_16(version[3]);
2217 
2218 	size = sizeof (partn_version) - (2 * sizeof (uint32_t));
2219 
2220 	/* Write the version number to all segments in the partition */
2221 	if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
2222 		    NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
2223 		    TLV_TAG_PARTITION_VERSION(partn),
2224 		    (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
2225 		goto fail1;
2226 
2227 	return (0);
2228 
2229 fail1:
2230 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2231 
2232 	return (rc);
2233 }
2234 
2235 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
2236 
2237 #if EFSYS_OPT_NVRAM
2238 
2239 typedef struct ef10_parttbl_entry_s {
2240 	unsigned int		partn;
2241 	unsigned int		port_mask;
2242 	efx_nvram_type_t	nvtype;
2243 } ef10_parttbl_entry_t;
2244 
2245 /* Port mask values */
2246 #define	PORT_1		(1u << 1)
2247 #define	PORT_2		(1u << 2)
2248 #define	PORT_3		(1u << 3)
2249 #define	PORT_4		(1u << 4)
2250 #define	PORT_ALL	(0xffffffffu)
2251 
2252 #define	PARTN_MAP_ENTRY(partn, port_mask, nvtype)	\
2253 { (NVRAM_PARTITION_TYPE_##partn), (PORT_##port_mask), (EFX_NVRAM_##nvtype) }
2254 
2255 /* Translate EFX NVRAM types to firmware partition types */
2256 static ef10_parttbl_entry_t hunt_parttbl[] = {
2257 	/*		partn			ports	nvtype */
2258 	PARTN_MAP_ENTRY(MC_FIRMWARE,		ALL,	MC_FIRMWARE),
2259 	PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP,	ALL,	MC_GOLDEN),
2260 	PARTN_MAP_ENTRY(EXPANSION_ROM,		ALL,	BOOTROM),
2261 	PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT0,	1,	BOOTROM_CFG),
2262 	PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT1,	2,	BOOTROM_CFG),
2263 	PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT2,	3,	BOOTROM_CFG),
2264 	PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT3,	4,	BOOTROM_CFG),
2265 	PARTN_MAP_ENTRY(DYNAMIC_CONFIG,		ALL,	DYNAMIC_CFG),
2266 	PARTN_MAP_ENTRY(FPGA,			ALL,	FPGA),
2267 	PARTN_MAP_ENTRY(FPGA_BACKUP,		ALL,	FPGA_BACKUP),
2268 	PARTN_MAP_ENTRY(LICENSE,		ALL,	LICENSE),
2269 };
2270 
2271 static ef10_parttbl_entry_t medford_parttbl[] = {
2272 	/*		partn			ports	nvtype */
2273 	PARTN_MAP_ENTRY(MC_FIRMWARE,		ALL,	MC_FIRMWARE),
2274 	PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP,	ALL,	MC_GOLDEN),
2275 	PARTN_MAP_ENTRY(EXPANSION_ROM,		ALL,	BOOTROM),
2276 	PARTN_MAP_ENTRY(EXPROM_CONFIG,		ALL,	BOOTROM_CFG),
2277 	PARTN_MAP_ENTRY(DYNAMIC_CONFIG,		ALL,	DYNAMIC_CFG),
2278 	PARTN_MAP_ENTRY(FPGA,			ALL,	FPGA),
2279 	PARTN_MAP_ENTRY(FPGA_BACKUP,		ALL,	FPGA_BACKUP),
2280 	PARTN_MAP_ENTRY(LICENSE,		ALL,	LICENSE),
2281 	PARTN_MAP_ENTRY(EXPANSION_UEFI,		ALL,	UEFIROM),
2282 	PARTN_MAP_ENTRY(MUM_FIRMWARE,		ALL,	MUM_FIRMWARE),
2283 };
2284 
2285 static ef10_parttbl_entry_t medford2_parttbl[] = {
2286 	/*		partn			ports	nvtype */
2287 	PARTN_MAP_ENTRY(MC_FIRMWARE,		ALL,	MC_FIRMWARE),
2288 	PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP,	ALL,	MC_GOLDEN),
2289 	PARTN_MAP_ENTRY(EXPANSION_ROM,		ALL,	BOOTROM),
2290 	PARTN_MAP_ENTRY(EXPROM_CONFIG,		ALL,	BOOTROM_CFG),
2291 	PARTN_MAP_ENTRY(DYNAMIC_CONFIG,		ALL,	DYNAMIC_CFG),
2292 	PARTN_MAP_ENTRY(FPGA,			ALL,	FPGA),
2293 	PARTN_MAP_ENTRY(FPGA_BACKUP,		ALL,	FPGA_BACKUP),
2294 	PARTN_MAP_ENTRY(LICENSE,		ALL,	LICENSE),
2295 	PARTN_MAP_ENTRY(EXPANSION_UEFI,		ALL,	UEFIROM),
2296 	PARTN_MAP_ENTRY(MUM_FIRMWARE,		ALL,	MUM_FIRMWARE),
2297 	PARTN_MAP_ENTRY(DYNCONFIG_DEFAULTS,	ALL,	DYNCONFIG_DEFAULTS),
2298 	PARTN_MAP_ENTRY(ROMCONFIG_DEFAULTS,	ALL,	ROMCONFIG_DEFAULTS),
2299 };
2300 
2301 static	__checkReturn		efx_rc_t
2302 ef10_parttbl_get(
2303 	__in			efx_nic_t *enp,
2304 	__out			ef10_parttbl_entry_t **parttblp,
2305 	__out			size_t *parttbl_rowsp)
2306 {
2307 	switch (enp->en_family) {
2308 	case EFX_FAMILY_HUNTINGTON:
2309 		*parttblp = hunt_parttbl;
2310 		*parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
2311 		break;
2312 
2313 	case EFX_FAMILY_MEDFORD:
2314 		*parttblp = medford_parttbl;
2315 		*parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
2316 		break;
2317 
2318 	case EFX_FAMILY_MEDFORD2:
2319 		*parttblp = medford2_parttbl;
2320 		*parttbl_rowsp = EFX_ARRAY_SIZE(medford2_parttbl);
2321 		break;
2322 
2323 	default:
2324 		EFSYS_ASSERT(B_FALSE);
2325 		return (EINVAL);
2326 	}
2327 	return (0);
2328 }
2329 
2330 	__checkReturn		efx_rc_t
2331 ef10_nvram_type_to_partn(
2332 	__in			efx_nic_t *enp,
2333 	__in			efx_nvram_type_t type,
2334 	__out			uint32_t *partnp)
2335 {
2336 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2337 	ef10_parttbl_entry_t *parttbl = NULL;
2338 	size_t parttbl_rows = 0;
2339 	unsigned int i;
2340 
2341 	EFSYS_ASSERT3U(type, !=, EFX_NVRAM_INVALID);
2342 	EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
2343 	EFSYS_ASSERT(partnp != NULL);
2344 
2345 	if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2346 		for (i = 0; i < parttbl_rows; i++) {
2347 			ef10_parttbl_entry_t *entry = &parttbl[i];
2348 
2349 			if ((entry->nvtype == type) &&
2350 			    (entry->port_mask & (1u << emip->emi_port))) {
2351 				*partnp = entry->partn;
2352 				return (0);
2353 			}
2354 		}
2355 	}
2356 
2357 	return (ENOTSUP);
2358 }
2359 
2360 #if EFSYS_OPT_DIAG
2361 
2362 static	__checkReturn		efx_rc_t
2363 ef10_nvram_partn_to_type(
2364 	__in			efx_nic_t *enp,
2365 	__in			uint32_t partn,
2366 	__out			efx_nvram_type_t *typep)
2367 {
2368 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2369 	ef10_parttbl_entry_t *parttbl = NULL;
2370 	size_t parttbl_rows = 0;
2371 	unsigned int i;
2372 
2373 	EFSYS_ASSERT(typep != NULL);
2374 
2375 	if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2376 		for (i = 0; i < parttbl_rows; i++) {
2377 			ef10_parttbl_entry_t *entry = &parttbl[i];
2378 
2379 			if ((entry->partn == partn) &&
2380 			    (entry->port_mask & (1u << emip->emi_port))) {
2381 				*typep = entry->nvtype;
2382 				return (0);
2383 			}
2384 		}
2385 	}
2386 
2387 	return (ENOTSUP);
2388 }
2389 
2390 	__checkReturn		efx_rc_t
2391 ef10_nvram_test(
2392 	__in			efx_nic_t *enp)
2393 {
2394 	efx_nvram_type_t type;
2395 	unsigned int npartns = 0;
2396 	uint32_t *partns = NULL;
2397 	size_t size;
2398 	unsigned int i;
2399 	efx_rc_t rc;
2400 
2401 	/* Read available partitions from NVRAM partition map */
2402 	size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
2403 	EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
2404 	if (partns == NULL) {
2405 		rc = ENOMEM;
2406 		goto fail1;
2407 	}
2408 
2409 	if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
2410 		    &npartns)) != 0) {
2411 		goto fail2;
2412 	}
2413 
2414 	for (i = 0; i < npartns; i++) {
2415 		/* Check if the partition is supported for this port */
2416 		if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
2417 			continue;
2418 
2419 		if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
2420 			goto fail3;
2421 	}
2422 
2423 	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2424 	return (0);
2425 
2426 fail3:
2427 	EFSYS_PROBE(fail3);
2428 fail2:
2429 	EFSYS_PROBE(fail2);
2430 	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2431 fail1:
2432 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2433 	return (rc);
2434 }
2435 
2436 #endif	/* EFSYS_OPT_DIAG */
2437 
2438 	__checkReturn		efx_rc_t
2439 ef10_nvram_partn_get_version(
2440 	__in			efx_nic_t *enp,
2441 	__in			uint32_t partn,
2442 	__out			uint32_t *subtypep,
2443 	__out_ecount(4)		uint16_t version[4])
2444 {
2445 	efx_rc_t rc;
2446 
2447 	/* FIXME: get highest partn version from all ports */
2448 	/* FIXME: return partn description if available */
2449 
2450 	if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
2451 		    version, NULL, 0)) != 0)
2452 		goto fail1;
2453 
2454 	return (0);
2455 
2456 fail1:
2457 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2458 
2459 	return (rc);
2460 }
2461 
2462 	__checkReturn		efx_rc_t
2463 ef10_nvram_partn_rw_start(
2464 	__in			efx_nic_t *enp,
2465 	__in			uint32_t partn,
2466 	__out			size_t *chunk_sizep)
2467 {
2468 	uint32_t write_size = 0;
2469 	efx_rc_t rc;
2470 
2471 	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2472 	    NULL, &write_size)) != 0)
2473 		goto fail1;
2474 
2475 	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
2476 		goto fail2;
2477 
2478 	if (chunk_sizep != NULL) {
2479 		if (write_size == 0)
2480 			*chunk_sizep = EF10_NVRAM_CHUNK;
2481 		else
2482 			*chunk_sizep = write_size;
2483 	}
2484 
2485 	return (0);
2486 
2487 fail2:
2488 	EFSYS_PROBE(fail2);
2489 fail1:
2490 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2491 
2492 	return (rc);
2493 }
2494 
2495 	__checkReturn		efx_rc_t
2496 ef10_nvram_partn_rw_finish(
2497 	__in			efx_nic_t *enp,
2498 	__in			uint32_t partn,
2499 	__out_opt		uint32_t *verify_resultp)
2500 {
2501 	efx_rc_t rc;
2502 
2503 	if ((rc = ef10_nvram_partn_unlock(enp, partn, verify_resultp)) != 0)
2504 		goto fail1;
2505 
2506 	return (0);
2507 
2508 fail1:
2509 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2510 
2511 	return (rc);
2512 }
2513 
2514 #endif	/* EFSYS_OPT_NVRAM */
2515 
2516 #endif	/* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
2517