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