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