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