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: releng/10.3/sys/dev/sfxge/common/hunt_nvram.c 294403 2016-01-20 08:26:58Z arybchik $");
33
34#include "efx.h"
35#include "efx_impl.h"
36
37#if EFSYS_OPT_HUNTINGTON
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
51static	__checkReturn		efx_rc_t
52tlv_validate_state(
53	__in			tlv_cursor_t *cursor);
54
55
56/*
57 * Operations on TLV formatted partition data.
58 */
59static				uint32_t
60tlv_tag(
61	__in	tlv_cursor_t	*cursor)
62{
63	uint32_t dword, tag;
64
65	dword = cursor->current[0];
66	tag = __LE_TO_CPU_32(dword);
67
68	return (tag);
69}
70
71static				size_t
72tlv_length(
73	__in	tlv_cursor_t	*cursor)
74{
75	uint32_t dword, length;
76
77	if (tlv_tag(cursor) == TLV_TAG_END)
78		return (0);
79
80	dword = cursor->current[1];
81	length = __LE_TO_CPU_32(dword);
82
83	return ((size_t)length);
84}
85
86static				uint8_t *
87tlv_value(
88	__in	tlv_cursor_t	*cursor)
89{
90	if (tlv_tag(cursor) == TLV_TAG_END)
91		return (NULL);
92
93	return ((uint8_t *)(&cursor->current[2]));
94}
95
96static				uint8_t *
97tlv_item(
98	__in	tlv_cursor_t	*cursor)
99{
100	if (tlv_tag(cursor) == TLV_TAG_END)
101		return (NULL);
102
103	return ((uint8_t *)cursor->current);
104}
105
106/*
107 * TLV item DWORD length is tag + length + value (rounded up to DWORD)
108 * equivalent to tlv_n_words_for_len in mc-comms tlv.c
109 */
110#define	TLV_DWORD_COUNT(length) \
111	(1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
112
113
114static				uint32_t *
115tlv_next_item_ptr(
116	__in	tlv_cursor_t	*cursor)
117{
118	uint32_t length;
119
120	length = tlv_length(cursor);
121
122	return (cursor->current + TLV_DWORD_COUNT(length));
123}
124
125static				efx_rc_t
126tlv_advance(
127	__in	tlv_cursor_t	*cursor)
128{
129	efx_rc_t rc;
130
131	if ((rc = tlv_validate_state(cursor)) != 0)
132		goto fail1;
133
134	if (cursor->current == cursor->end) {
135		/* No more tags after END tag */
136		cursor->current = NULL;
137		rc = ENOENT;
138		goto fail2;
139	}
140
141	/* Advance to next item and validate */
142	cursor->current = tlv_next_item_ptr(cursor);
143
144	if ((rc = tlv_validate_state(cursor)) != 0)
145		goto fail3;
146
147	return (0);
148
149fail3:
150	EFSYS_PROBE(fail3);
151fail2:
152	EFSYS_PROBE(fail2);
153fail1:
154	EFSYS_PROBE1(fail1, efx_rc_t, rc);
155
156	return (rc);
157}
158
159static				efx_rc_t
160tlv_rewind(
161	__in	tlv_cursor_t	*cursor)
162{
163	efx_rc_t rc;
164
165	cursor->current = cursor->block;
166
167	if ((rc = tlv_validate_state(cursor)) != 0)
168		goto fail1;
169
170	return (0);
171
172fail1:
173	EFSYS_PROBE1(fail1, efx_rc_t, rc);
174
175	return (rc);
176}
177
178static				efx_rc_t
179tlv_find(
180	__in	tlv_cursor_t	*cursor,
181	__in	uint32_t	tag)
182{
183	efx_rc_t rc;
184
185	rc = tlv_rewind(cursor);
186	while (rc == 0) {
187		if (tlv_tag(cursor) == tag)
188			break;
189
190		rc = tlv_advance(cursor);
191	}
192	return (rc);
193}
194
195static	__checkReturn		efx_rc_t
196tlv_validate_state(
197	__in	tlv_cursor_t	*cursor)
198{
199	efx_rc_t rc;
200
201	/* Check cursor position */
202	if (cursor->current < cursor->block) {
203		rc = EINVAL;
204		goto fail1;
205	}
206	if (cursor->current > cursor->limit) {
207		rc = EINVAL;
208		goto fail2;
209	}
210
211	if (tlv_tag(cursor) != TLV_TAG_END) {
212		/* Check current item has space for tag and length */
213		if (cursor->current > (cursor->limit - 2)) {
214			cursor->current = NULL;
215			rc = EFAULT;
216			goto fail3;
217		}
218
219		/* Check we have value data for current item and another tag */
220		if (tlv_next_item_ptr(cursor) > (cursor->limit - 1)) {
221			cursor->current = NULL;
222			rc = EFAULT;
223			goto fail4;
224		}
225	}
226
227	return (0);
228
229fail4:
230	EFSYS_PROBE(fail4);
231fail3:
232	EFSYS_PROBE(fail3);
233fail2:
234	EFSYS_PROBE(fail2);
235fail1:
236	EFSYS_PROBE1(fail1, efx_rc_t, rc);
237
238	return (rc);
239}
240
241static				efx_rc_t
242tlv_init_cursor(
243	__out	tlv_cursor_t	*cursor,
244	__in	uint32_t	*block,
245	__in	uint32_t	*limit)
246{
247	cursor->block	= block;
248	cursor->limit	= limit;
249
250	cursor->current	= cursor->block;
251	cursor->end	= NULL;
252
253	return (tlv_validate_state(cursor));
254}
255
256static				efx_rc_t
257tlv_init_cursor_from_size(
258	__out	tlv_cursor_t	*cursor,
259	__in	uint8_t	*block,
260	__in	size_t		size)
261{
262	uint32_t *limit;
263	limit = (uint32_t *)(block + size - sizeof (uint32_t));
264	return (tlv_init_cursor(cursor, (uint32_t *)block, limit));
265}
266
267static				efx_rc_t
268tlv_require_end(
269	__in	tlv_cursor_t	*cursor)
270{
271	uint32_t *pos;
272	efx_rc_t rc;
273
274	if (cursor->end == NULL) {
275		pos = cursor->current;
276		if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
277			goto fail1;
278
279		cursor->end = cursor->current;
280		cursor->current = pos;
281	}
282
283	return (0);
284
285fail1:
286	EFSYS_PROBE1(fail1, efx_rc_t, rc);
287
288	return (rc);
289}
290
291static				size_t
292tlv_block_length_used(
293	__in	tlv_cursor_t	*cursor)
294{
295	efx_rc_t rc;
296
297	if ((rc = tlv_validate_state(cursor)) != 0)
298		goto fail1;
299
300	if ((rc = tlv_require_end(cursor)) != 0)
301		goto fail2;
302
303	/* Return space used (including the END tag) */
304	return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
305
306fail2:
307	EFSYS_PROBE(fail2);
308fail1:
309	EFSYS_PROBE1(fail1, efx_rc_t, rc);
310
311	return (0);
312}
313
314
315static	__checkReturn		uint32_t *
316tlv_write(
317	__in			tlv_cursor_t *cursor,
318	__in			uint32_t tag,
319	__in_bcount(size)	uint8_t *data,
320	__in			size_t size)
321{
322	uint32_t len = size;
323	uint32_t *ptr;
324
325	ptr = cursor->current;
326
327	*ptr++ = __CPU_TO_LE_32(tag);
328	*ptr++ = __CPU_TO_LE_32(len);
329
330	if (len > 0) {
331		ptr[(len - 1) / sizeof (uint32_t)] = 0;
332		memcpy(ptr, data, len);
333		ptr += P2ROUNDUP(len, sizeof (uint32_t)) / sizeof (*ptr);
334	}
335
336	return (ptr);
337}
338
339static	__checkReturn		efx_rc_t
340tlv_insert(
341	__in	tlv_cursor_t	*cursor,
342	__in	uint32_t	tag,
343	__in	uint8_t		*data,
344	__in	size_t		size)
345{
346	unsigned int delta;
347	efx_rc_t rc;
348
349	if ((rc = tlv_validate_state(cursor)) != 0)
350		goto fail1;
351
352	if ((rc = tlv_require_end(cursor)) != 0)
353		goto fail2;
354
355	if (tag == TLV_TAG_END) {
356		rc = EINVAL;
357		goto fail3;
358	}
359
360	delta = TLV_DWORD_COUNT(size);
361	if (cursor->end + 1 + delta > cursor->limit) {
362		rc = ENOSPC;
363		goto fail4;
364	}
365
366	/* Move data up: new space at cursor->current */
367	memmove(cursor->current + delta, cursor->current,
368	    (cursor->end + 1 - cursor->current) * sizeof (uint32_t));
369
370	/* Adjust the end pointer */
371	cursor->end += delta;
372
373	/* Write new TLV item */
374	tlv_write(cursor, tag, data, size);
375
376	return (0);
377
378fail4:
379	EFSYS_PROBE(fail4);
380fail3:
381	EFSYS_PROBE(fail3);
382fail2:
383	EFSYS_PROBE(fail2);
384fail1:
385	EFSYS_PROBE1(fail1, efx_rc_t, rc);
386
387	return (rc);
388}
389
390static	__checkReturn		efx_rc_t
391tlv_modify(
392	__in	tlv_cursor_t	*cursor,
393	__in	uint32_t	tag,
394	__in	uint8_t		*data,
395	__in	size_t		size)
396{
397	uint32_t *pos;
398	unsigned int old_ndwords;
399	unsigned int new_ndwords;
400	unsigned int delta;
401	efx_rc_t rc;
402
403	if ((rc = tlv_validate_state(cursor)) != 0)
404		goto fail1;
405
406	if (tlv_tag(cursor) == TLV_TAG_END) {
407		rc = EINVAL;
408		goto fail2;
409	}
410	if (tlv_tag(cursor) != tag) {
411		rc = EINVAL;
412		goto fail3;
413	}
414
415	old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
416	new_ndwords = TLV_DWORD_COUNT(size);
417
418	if ((rc = tlv_require_end(cursor)) != 0)
419		goto fail4;
420
421	if (new_ndwords > old_ndwords) {
422		/* Expand space used for TLV item */
423		delta = new_ndwords - old_ndwords;
424		pos = cursor->current + old_ndwords;
425
426		if (cursor->end + 1 + delta > cursor->limit) {
427			rc = ENOSPC;
428			goto fail5;
429		}
430
431		/* Move up: new space at (cursor->current + old_ndwords) */
432		memmove(pos + delta, pos,
433		    (cursor->end + 1 - pos) * sizeof (uint32_t));
434
435		/* Adjust the end pointer */
436		cursor->end += delta;
437
438	} else if (new_ndwords < old_ndwords) {
439		/* Shrink space used for TLV item */
440		delta = old_ndwords - new_ndwords;
441		pos = cursor->current + new_ndwords;
442
443		/* Move down: remove words at (cursor->current + new_ndwords) */
444		memmove(pos, pos + delta,
445		    (cursor->end + 1 - pos) * sizeof (uint32_t));
446
447		/* Zero the new space at the end of the TLV chain */
448		memset(cursor->end + 1 - delta, 0, delta * sizeof (uint32_t));
449
450		/* Adjust the end pointer */
451		cursor->end -= delta;
452	}
453
454	/* Write new data */
455	tlv_write(cursor, tag, data, size);
456
457	return (0);
458
459fail5:
460	EFSYS_PROBE(fail5);
461fail4:
462	EFSYS_PROBE(fail4);
463fail3:
464	EFSYS_PROBE(fail3);
465fail2:
466	EFSYS_PROBE(fail2);
467fail1:
468	EFSYS_PROBE1(fail1, efx_rc_t, rc);
469
470	return (rc);
471}
472
473/* Validate TLV formatted partition contents (before writing to flash) */
474	__checkReturn		efx_rc_t
475efx_nvram_tlv_validate(
476	__in			efx_nic_t *enp,
477	__in			uint32_t partn,
478	__in_bcount(partn_size)	caddr_t partn_data,
479	__in			size_t partn_size)
480{
481	tlv_cursor_t cursor;
482	struct tlv_partition_header *header;
483	struct tlv_partition_trailer *trailer;
484	size_t total_length;
485	uint32_t cksum;
486	int pos;
487	efx_rc_t rc;
488
489	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
490
491	if ((partn_data == NULL) || (partn_size == 0)) {
492		rc = EINVAL;
493		goto fail1;
494	}
495
496	/* The partition header must be the first item (at offset zero) */
497	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
498		    partn_size)) != 0) {
499		rc = EFAULT;
500		goto fail2;
501	}
502	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
503		rc = EINVAL;
504		goto fail3;
505	}
506	header = (struct tlv_partition_header *)tlv_item(&cursor);
507
508	/* Check TLV partition length (includes the END tag) */
509	total_length = __LE_TO_CPU_32(header->total_length);
510	if (total_length > partn_size) {
511		rc = EFBIG;
512		goto fail4;
513	}
514
515	/* Check partition ends with PARTITION_TRAILER and END tags */
516	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
517		rc = EINVAL;
518		goto fail5;
519	}
520	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
521
522	if ((rc = tlv_advance(&cursor)) != 0) {
523		rc = EINVAL;
524		goto fail6;
525	}
526	if (tlv_tag(&cursor) != TLV_TAG_END) {
527		rc = EINVAL;
528		goto fail7;
529	}
530
531	/* Check generation counts are consistent */
532	if (trailer->generation != header->generation) {
533		rc = EINVAL;
534		goto fail8;
535	}
536
537	/* Verify partition checksum */
538	cksum = 0;
539	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
540		cksum += *((uint32_t *)(partn_data + pos));
541	}
542	if (cksum != 0) {
543		rc = EINVAL;
544		goto fail9;
545	}
546
547	return (0);
548
549fail9:
550	EFSYS_PROBE(fail9);
551fail8:
552	EFSYS_PROBE(fail8);
553fail7:
554	EFSYS_PROBE(fail7);
555fail6:
556	EFSYS_PROBE(fail6);
557fail5:
558	EFSYS_PROBE(fail5);
559fail4:
560	EFSYS_PROBE(fail4);
561fail3:
562	EFSYS_PROBE(fail3);
563fail2:
564	EFSYS_PROBE(fail2);
565fail1:
566	EFSYS_PROBE1(fail1, efx_rc_t, rc);
567
568	return (rc);
569}
570
571/*
572 * Read and validate a segment from a partition. A segment is a complete
573 * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
574 * be multiple segments in a partition, so seg_offset allows segments
575 * beyond the first to be read.
576 */
577static	__checkReturn			efx_rc_t
578ef10_nvram_read_tlv_segment(
579	__in				efx_nic_t *enp,
580	__in				uint32_t partn,
581	__in				size_t seg_offset,
582	__in_bcount(max_seg_size)	caddr_t seg_data,
583	__in				size_t max_seg_size)
584{
585	tlv_cursor_t cursor;
586	struct tlv_partition_header *header;
587	struct tlv_partition_trailer *trailer;
588	size_t total_length;
589	uint32_t cksum;
590	int pos;
591	efx_rc_t rc;
592
593	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
594
595	if ((seg_data == NULL) || (max_seg_size == 0)) {
596		rc = EINVAL;
597		goto fail1;
598	}
599
600	/* Read initial chunk of the segment, starting at offset */
601	if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
602		    EF10_NVRAM_CHUNK,
603		    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
604		goto fail2;
605	}
606
607	/* A PARTITION_HEADER tag must be the first item at the given offset */
608	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
609		    max_seg_size)) != 0) {
610		rc = EFAULT;
611		goto fail3;
612	}
613	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
614		rc = EINVAL;
615		goto fail4;
616	}
617	header = (struct tlv_partition_header *)tlv_item(&cursor);
618
619	/* Check TLV segment length (includes the END tag) */
620	total_length = __LE_TO_CPU_32(header->total_length);
621	if (total_length > max_seg_size) {
622		rc = EFBIG;
623		goto fail5;
624	}
625
626	/* Read the remaining segment content */
627	if (total_length > EF10_NVRAM_CHUNK) {
628		if ((rc = ef10_nvram_partn_read_mode(enp, partn,
629			    seg_offset + EF10_NVRAM_CHUNK,
630			    seg_data + EF10_NVRAM_CHUNK,
631			    total_length - EF10_NVRAM_CHUNK,
632			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
633			goto fail6;
634	}
635
636	/* Check segment ends with PARTITION_TRAILER and END tags */
637	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
638		rc = EINVAL;
639		goto fail7;
640	}
641	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
642
643	if ((rc = tlv_advance(&cursor)) != 0) {
644		rc = EINVAL;
645		goto fail8;
646	}
647	if (tlv_tag(&cursor) != TLV_TAG_END) {
648		rc = EINVAL;
649		goto fail9;
650	}
651
652	/* Check data read from segment is consistent */
653	if (trailer->generation != header->generation) {
654		/*
655		 * The partition data may have been modified between successive
656		 * MCDI NVRAM_READ requests by the MC or another PCI function.
657		 *
658		 * The caller must retry to obtain consistent partition data.
659		 */
660		rc = EAGAIN;
661		goto fail10;
662	}
663
664	/* Verify segment checksum */
665	cksum = 0;
666	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
667		cksum += *((uint32_t *)(seg_data + pos));
668	}
669	if (cksum != 0) {
670		rc = EINVAL;
671		goto fail11;
672	}
673
674	return (0);
675
676fail11:
677	EFSYS_PROBE(fail11);
678fail10:
679	EFSYS_PROBE(fail10);
680fail9:
681	EFSYS_PROBE(fail9);
682fail8:
683	EFSYS_PROBE(fail8);
684fail7:
685	EFSYS_PROBE(fail7);
686fail6:
687	EFSYS_PROBE(fail6);
688fail5:
689	EFSYS_PROBE(fail5);
690fail4:
691	EFSYS_PROBE(fail4);
692fail3:
693	EFSYS_PROBE(fail3);
694fail2:
695	EFSYS_PROBE(fail2);
696fail1:
697	EFSYS_PROBE1(fail1, efx_rc_t, rc);
698
699	return (rc);
700}
701
702/*
703 * Read a single TLV item from a host memory
704 * buffer containing a TLV formatted segment.
705 */
706	__checkReturn		efx_rc_t
707ef10_nvram_buf_read_tlv(
708	__in				efx_nic_t *enp,
709	__in_bcount(max_seg_size)	caddr_t seg_data,
710	__in				size_t max_seg_size,
711	__in				uint32_t tag,
712	__deref_out_bcount_opt(*sizep)	caddr_t *datap,
713	__out				size_t *sizep)
714{
715	tlv_cursor_t cursor;
716	caddr_t data;
717	size_t length;
718	caddr_t value;
719	efx_rc_t rc;
720
721	if ((seg_data == NULL) || (max_seg_size == 0)) {
722		rc = EINVAL;
723		goto fail1;
724	}
725
726	/* Find requested TLV tag in segment data */
727	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
728		    max_seg_size)) != 0) {
729		rc = EFAULT;
730		goto fail2;
731	}
732	if ((rc = tlv_find(&cursor, tag)) != 0) {
733		rc = ENOENT;
734		goto fail3;
735	}
736	value = (caddr_t)tlv_value(&cursor);
737	length = tlv_length(&cursor);
738
739	if (length == 0)
740		data = NULL;
741	else {
742		/* Copy out data from TLV item */
743		EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
744		if (data == NULL) {
745			rc = ENOMEM;
746			goto fail4;
747		}
748		memcpy(data, value, length);
749	}
750
751	*datap = data;
752	*sizep = length;
753
754	return (0);
755
756fail4:
757	EFSYS_PROBE(fail4);
758fail3:
759	EFSYS_PROBE(fail3);
760fail2:
761	EFSYS_PROBE(fail2);
762fail1:
763	EFSYS_PROBE1(fail1, efx_rc_t, rc);
764
765	return (rc);
766}
767
768/* Read a single TLV item from the first segment in a TLV formatted partition */
769	__checkReturn		efx_rc_t
770ef10_nvram_partn_read_tlv(
771	__in					efx_nic_t *enp,
772	__in					uint32_t partn,
773	__in					uint32_t tag,
774	__deref_out_bcount_opt(*seg_sizep)	caddr_t *seg_datap,
775	__out					size_t *seg_sizep)
776{
777	caddr_t seg_data = NULL;
778	size_t partn_size = 0;
779	size_t length;
780	caddr_t data;
781	int retry;
782	efx_rc_t rc;
783
784	/* Allocate sufficient memory for the entire partition */
785	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
786		goto fail1;
787
788	if (partn_size == 0) {
789		rc = ENOENT;
790		goto fail2;
791	}
792
793	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
794	if (seg_data == NULL) {
795		rc = ENOMEM;
796		goto fail3;
797	}
798
799	/*
800	 * Read the first segment in a TLV partition. Retry until consistent
801	 * segment contents are returned. Inconsistent data may be read if:
802	 *  a) the segment contents are invalid
803	 *  b) the MC has rebooted while we were reading the partition
804	 *  c) the partition has been modified while we were reading it
805	 * Limit retry attempts to ensure forward progress.
806	 */
807	retry = 10;
808	do {
809		rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
810		    seg_data, partn_size);
811	} while ((rc == EAGAIN) && (--retry > 0));
812
813	if (rc != 0) {
814		/* Failed to obtain consistent segment data */
815		goto fail4;
816	}
817
818	if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
819		    tag, &data, &length)) != 0)
820		goto fail5;
821
822	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
823
824	*seg_datap = data;
825	*seg_sizep = length;
826
827	return (0);
828
829fail5:
830	EFSYS_PROBE(fail5);
831fail4:
832	EFSYS_PROBE(fail4);
833
834	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
835fail3:
836	EFSYS_PROBE(fail3);
837fail2:
838	EFSYS_PROBE(fail2);
839fail1:
840	EFSYS_PROBE1(fail1, efx_rc_t, rc);
841
842	return (rc);
843}
844
845/* Compute the size of a segment. */
846	static	__checkReturn	efx_rc_t
847ef10_nvram_buf_segment_size(
848	__in			caddr_t seg_data,
849	__in			size_t max_seg_size,
850	__out			size_t *seg_sizep)
851{
852	efx_rc_t rc;
853	tlv_cursor_t cursor;
854	struct tlv_partition_header *header;
855	uint32_t cksum;
856	int pos;
857	uint32_t *end_tag_position;
858	uint32_t segment_length;
859
860	/* A PARTITION_HEADER tag must be the first item at the given offset */
861	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
862		    max_seg_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	header = (struct tlv_partition_header *)tlv_item(&cursor);
871
872	/* Check TLV segment length (includes the END tag) */
873	*seg_sizep = __LE_TO_CPU_32(header->total_length);
874	if (*seg_sizep > max_seg_size) {
875		rc = EFBIG;
876		goto fail3;
877	}
878
879	/* Check segment ends with PARTITION_TRAILER and END tags */
880	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
881		rc = EINVAL;
882		goto fail4;
883	}
884
885	if ((rc = tlv_advance(&cursor)) != 0) {
886		rc = EINVAL;
887		goto fail5;
888	}
889	if (tlv_tag(&cursor) != TLV_TAG_END) {
890		rc = EINVAL;
891		goto fail6;
892	}
893	end_tag_position = cursor.current;
894
895	/* Verify segment checksum */
896	cksum = 0;
897	for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
898		cksum += *((uint32_t *)(seg_data + pos));
899	}
900	if (cksum != 0) {
901		rc = EINVAL;
902		goto fail7;
903	}
904
905	/*
906	 * Calculate total length from HEADER to END tags and compare to
907	 * max_seg_size and the total_length field in the HEADER tag.
908	 */
909	segment_length = tlv_block_length_used(&cursor);
910
911	if (segment_length > max_seg_size) {
912		rc = EINVAL;
913		goto fail8;
914	}
915
916	if (segment_length != *seg_sizep) {
917		rc = EINVAL;
918		goto fail9;
919	}
920
921	/* Skip over the first HEADER tag. */
922	rc = tlv_rewind(&cursor);
923	rc = tlv_advance(&cursor);
924
925	while (rc == 0) {
926		if (tlv_tag(&cursor) == TLV_TAG_END) {
927			/* Check that the END tag is the one found earlier. */
928			if (cursor.current != end_tag_position)
929				goto fail10;
930			break;
931		}
932		/* Check for duplicate HEADER tags before the END tag. */
933		if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
934			rc = EINVAL;
935			goto fail11;
936		}
937
938		rc = tlv_advance(&cursor);
939	}
940	if (rc != 0)
941		goto fail12;
942
943	return (0);
944
945fail12:
946	EFSYS_PROBE(fail12);
947fail11:
948	EFSYS_PROBE(fail11);
949fail10:
950	EFSYS_PROBE(fail10);
951fail9:
952	EFSYS_PROBE(fail9);
953fail8:
954	EFSYS_PROBE(fail8);
955fail7:
956	EFSYS_PROBE(fail7);
957fail6:
958	EFSYS_PROBE(fail6);
959fail5:
960	EFSYS_PROBE(fail5);
961fail4:
962	EFSYS_PROBE(fail4);
963fail3:
964	EFSYS_PROBE(fail3);
965fail2:
966	EFSYS_PROBE(fail2);
967fail1:
968	EFSYS_PROBE1(fail1, efx_rc_t, rc);
969
970	return (rc);
971}
972
973/*
974 * Add or update a single TLV item in a host memory buffer containing a TLV
975 * formatted segment. Historically partitions consisted of only one segment.
976 */
977	__checkReturn			efx_rc_t
978ef10_nvram_buf_write_tlv(
979	__inout_bcount(max_seg_size)	caddr_t seg_data,
980	__in				size_t max_seg_size,
981	__in				uint32_t tag,
982	__in_bcount(tag_size)		caddr_t tag_data,
983	__in				size_t tag_size,
984	__out				size_t *total_lengthp)
985{
986	tlv_cursor_t cursor;
987	struct tlv_partition_header *header;
988	struct tlv_partition_trailer *trailer;
989	uint32_t generation;
990	uint32_t cksum;
991	int pos;
992	efx_rc_t rc;
993
994	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
995	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
996			max_seg_size)) != 0) {
997		rc = EFAULT;
998		goto fail1;
999	}
1000	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1001		rc = EINVAL;
1002		goto fail2;
1003	}
1004	header = (struct tlv_partition_header *)tlv_item(&cursor);
1005
1006	/* Update the TLV chain to contain the new data */
1007	if ((rc = tlv_find(&cursor, tag)) == 0) {
1008		/* Modify existing TLV item */
1009		if ((rc = tlv_modify(&cursor, tag,
1010			    (uint8_t *)tag_data, tag_size)) != 0)
1011			goto fail3;
1012	} else {
1013		/* Insert a new TLV item before the PARTITION_TRAILER */
1014		rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1015		if (rc != 0) {
1016			rc = EINVAL;
1017			goto fail4;
1018		}
1019		if ((rc = tlv_insert(&cursor, tag,
1020			    (uint8_t *)tag_data, tag_size)) != 0) {
1021			rc = EINVAL;
1022			goto fail5;
1023		}
1024	}
1025
1026	/* Find the trailer tag */
1027	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1028		rc = EINVAL;
1029		goto fail6;
1030	}
1031	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1032
1033	/* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1034	*total_lengthp = tlv_block_length_used(&cursor);
1035	if (*total_lengthp > max_seg_size) {
1036		rc = ENOSPC;
1037		goto fail7;
1038	}
1039	generation = __LE_TO_CPU_32(header->generation) + 1;
1040
1041	header->total_length	= __CPU_TO_LE_32(*total_lengthp);
1042	header->generation	= __CPU_TO_LE_32(generation);
1043	trailer->generation	= __CPU_TO_LE_32(generation);
1044
1045	/* Recompute PARTITION_TRAILER checksum */
1046	trailer->checksum = 0;
1047	cksum = 0;
1048	for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1049		cksum += *((uint32_t *)(seg_data + pos));
1050	}
1051	trailer->checksum = ~cksum + 1;
1052
1053	return (0);
1054
1055fail7:
1056	EFSYS_PROBE(fail7);
1057fail6:
1058	EFSYS_PROBE(fail6);
1059fail5:
1060	EFSYS_PROBE(fail5);
1061fail4:
1062	EFSYS_PROBE(fail4);
1063fail3:
1064	EFSYS_PROBE(fail3);
1065fail2:
1066	EFSYS_PROBE(fail2);
1067fail1:
1068	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1069
1070	return (rc);
1071}
1072
1073/*
1074 * Add or update a single TLV item in the first segment of a TLV formatted
1075 * dynamic config partition. The first segment is the current active
1076 * configuration.
1077 */
1078	__checkReturn		efx_rc_t
1079ef10_nvram_partn_write_tlv(
1080	__in			efx_nic_t *enp,
1081	__in			uint32_t partn,
1082	__in			uint32_t tag,
1083	__in_bcount(size)	caddr_t data,
1084	__in			size_t size)
1085{
1086	return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1087	    size, B_FALSE);
1088}
1089
1090/*
1091 * Read a segment from nvram at the given offset into a buffer (segment_data)
1092 * and optionally write a new tag to it.
1093 */
1094	static	__checkReturn	efx_rc_t
1095ef10_nvram_segment_write_tlv(
1096	__in			efx_nic_t *enp,
1097	__in			uint32_t partn,
1098	__in			uint32_t tag,
1099	__in_bcount(size)	caddr_t data,
1100	__in			size_t size,
1101	__inout			caddr_t *seg_datap,
1102	__inout			size_t *partn_offsetp,
1103	__inout			size_t *src_remain_lenp,
1104	__inout			size_t *dest_remain_lenp,
1105	__in			boolean_t write)
1106{
1107	efx_rc_t rc;
1108	efx_rc_t status;
1109	size_t original_segment_size;
1110	size_t modified_segment_size;
1111
1112	/*
1113	 * Read the segment from NVRAM into the segment_data buffer and validate
1114	 * it, returning if it does not validate. This is not a failure unless
1115	 * this is the first segment in a partition. In this case the caller
1116	 * must propogate the error.
1117	 */
1118	status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1119	    *seg_datap, *src_remain_lenp);
1120	if (status != 0)
1121		return (EINVAL);
1122
1123	status = ef10_nvram_buf_segment_size(*seg_datap,
1124	    *src_remain_lenp, &original_segment_size);
1125	if (status != 0)
1126		return (EINVAL);
1127
1128	if (write) {
1129		/* Update the contents of the segment in the buffer */
1130		if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1131			*dest_remain_lenp, tag, data, size,
1132			&modified_segment_size)) != 0)
1133			goto fail1;
1134		*dest_remain_lenp -= modified_segment_size;
1135		*seg_datap += modified_segment_size;
1136	} else {
1137		/*
1138		 * We won't modify this segment, but still need to update the
1139		 * remaining lengths and pointers.
1140		 */
1141		*dest_remain_lenp -= original_segment_size;
1142		*seg_datap += original_segment_size;
1143	}
1144
1145	*partn_offsetp += original_segment_size;
1146	*src_remain_lenp -= original_segment_size;
1147
1148	return (0);
1149
1150fail1:
1151	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1152
1153	return (rc);
1154}
1155
1156/*
1157 * Add or update a single TLV item in either the first segment or in all
1158 * segments in a TLV formatted dynamic config partition. Dynamic config
1159 * partitions on boards that support RFID are divided into a number of segments,
1160 * each formatted like a partition, with header, trailer and end tags. The first
1161 * segment is the current active configuration.
1162 *
1163 * The segments are initialised by manftest and each contain a different
1164 * configuration e.g. firmware variant. The firmware can be instructed
1165 * via RFID to copy a segment to replace the first segment, hence changing the
1166 * active configuration.  This allows ops to change the configuration of a board
1167 * prior to shipment using RFID.
1168 *
1169 * Changes to the dynamic config may need to be written to all segments (e.g.
1170 * firmware versions) or just the first segment (changes to the active
1171 * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1172 * If only the first segment is written the code still needs to be aware of the
1173 * possible presence of subsequent segments as writing to a segment may cause
1174 * its size to increase, which would overwrite the subsequent segments and
1175 * invalidate them.
1176 */
1177	__checkReturn		efx_rc_t
1178ef10_nvram_partn_write_segment_tlv(
1179	__in			efx_nic_t *enp,
1180	__in			uint32_t partn,
1181	__in			uint32_t tag,
1182	__in_bcount(size)	caddr_t data,
1183	__in			size_t size,
1184	__in			boolean_t all_segments)
1185{
1186	size_t partn_size = 0;
1187	caddr_t partn_data;
1188	size_t total_length = 0;
1189	efx_rc_t rc;
1190	size_t current_offset = 0;
1191	size_t remaining_original_length;
1192	size_t remaining_modified_length;
1193	caddr_t segment_data;
1194
1195	EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1196
1197	/* Allocate sufficient memory for the entire partition */
1198	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1199		goto fail1;
1200
1201	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1202	if (partn_data == NULL) {
1203		rc = ENOMEM;
1204		goto fail2;
1205	}
1206
1207	remaining_original_length = partn_size;
1208	remaining_modified_length = partn_size;
1209	segment_data = partn_data;
1210
1211	/* Lock the partition */
1212	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1213		goto fail3;
1214
1215	/* Iterate over each (potential) segment to update it. */
1216	do {
1217		boolean_t write = all_segments || current_offset == 0;
1218
1219		rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1220		    &segment_data, &current_offset, &remaining_original_length,
1221		    &remaining_modified_length, write);
1222		if (rc != 0) {
1223			if (current_offset == 0) {
1224				/*
1225				 * If no data has been read then the first
1226				 * segment is invalid, which is an error.
1227				 */
1228				goto fail4;
1229			}
1230			break;
1231		}
1232	} while (current_offset < partn_size);
1233
1234	total_length = segment_data - partn_data;
1235
1236	/*
1237	 * We've run out of space.  This should actually be dealt with by
1238	 * ef10_nvram_buf_write_tlv returning ENOSPC.
1239	 */
1240	if (total_length > partn_size) {
1241		rc = ENOSPC;
1242		goto fail5;
1243	}
1244
1245	/* Erase the whole partition in NVRAM */
1246	if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1247		goto fail6;
1248
1249	/* Write new partition contents from the buffer to NVRAM */
1250	if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1251		    total_length)) != 0)
1252		goto fail7;
1253
1254	/* Unlock the partition */
1255	ef10_nvram_partn_unlock(enp, partn);
1256
1257	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1258
1259	return (0);
1260
1261fail7:
1262	EFSYS_PROBE(fail7);
1263fail6:
1264	EFSYS_PROBE(fail6);
1265fail5:
1266	EFSYS_PROBE(fail5);
1267fail4:
1268	EFSYS_PROBE(fail4);
1269
1270	ef10_nvram_partn_unlock(enp, partn);
1271fail3:
1272	EFSYS_PROBE(fail3);
1273
1274	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1275fail2:
1276	EFSYS_PROBE(fail2);
1277fail1:
1278	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1279
1280	return (rc);
1281}
1282
1283/*
1284 * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1285 * not the data used by the segments in the partition.
1286 */
1287	__checkReturn		efx_rc_t
1288ef10_nvram_partn_size(
1289	__in			efx_nic_t *enp,
1290	__in			uint32_t partn,
1291	__out			size_t *sizep)
1292{
1293	efx_rc_t rc;
1294
1295	if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1296	    NULL, NULL, NULL)) != 0)
1297		goto fail1;
1298
1299	return (0);
1300
1301fail1:
1302	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1303
1304	return (rc);
1305}
1306
1307	__checkReturn		efx_rc_t
1308ef10_nvram_partn_lock(
1309	__in			efx_nic_t *enp,
1310	__in			uint32_t partn)
1311{
1312	efx_rc_t rc;
1313
1314	if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1315		goto fail1;
1316
1317	return (0);
1318
1319fail1:
1320	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1321
1322	return (rc);
1323}
1324
1325	__checkReturn		efx_rc_t
1326ef10_nvram_partn_read_mode(
1327	__in			efx_nic_t *enp,
1328	__in			uint32_t partn,
1329	__in			unsigned int offset,
1330	__out_bcount(size)	caddr_t data,
1331	__in			size_t size,
1332	__in			uint32_t mode)
1333{
1334	size_t chunk;
1335	efx_rc_t rc;
1336
1337	while (size > 0) {
1338		chunk = MIN(size, EF10_NVRAM_CHUNK);
1339
1340		if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
1341			    data, chunk, mode)) != 0) {
1342			goto fail1;
1343		}
1344
1345		size -= chunk;
1346		data += chunk;
1347		offset += chunk;
1348	}
1349
1350	return (0);
1351
1352fail1:
1353	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1354
1355	return (rc);
1356}
1357
1358	__checkReturn		efx_rc_t
1359ef10_nvram_partn_read(
1360	__in			efx_nic_t *enp,
1361	__in			uint32_t partn,
1362	__in			unsigned int offset,
1363	__out_bcount(size)	caddr_t data,
1364	__in			size_t size)
1365{
1366	/*
1367	 * Read requests which come in through the EFX API expect to
1368	 * read the current, active partition.
1369	 */
1370	return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
1371			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
1372}
1373
1374	__checkReturn		efx_rc_t
1375ef10_nvram_partn_erase(
1376	__in			efx_nic_t *enp,
1377	__in			uint32_t partn,
1378	__in			unsigned int offset,
1379	__in			size_t size)
1380{
1381	efx_rc_t rc;
1382	uint32_t erase_size;
1383
1384	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1385	    &erase_size, NULL)) != 0)
1386		goto fail1;
1387
1388	if (erase_size == 0) {
1389		if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
1390			goto fail2;
1391	} else {
1392		if (size % erase_size != 0) {
1393			rc = EINVAL;
1394			goto fail3;
1395		}
1396		while (size > 0) {
1397			if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
1398			    erase_size)) != 0)
1399				goto fail4;
1400			offset += erase_size;
1401			size -= erase_size;
1402		}
1403	}
1404
1405	return (0);
1406
1407fail4:
1408	EFSYS_PROBE(fail4);
1409fail3:
1410	EFSYS_PROBE(fail3);
1411fail2:
1412	EFSYS_PROBE(fail2);
1413fail1:
1414	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1415
1416	return (rc);
1417}
1418
1419	__checkReturn		efx_rc_t
1420ef10_nvram_partn_write(
1421	__in			efx_nic_t *enp,
1422	__in			uint32_t partn,
1423	__in			unsigned int offset,
1424	__out_bcount(size)	caddr_t data,
1425	__in			size_t size)
1426{
1427	size_t chunk;
1428	uint32_t write_size;
1429	efx_rc_t rc;
1430
1431	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1432	    NULL, &write_size)) != 0)
1433		goto fail1;
1434
1435	if (write_size != 0) {
1436		/*
1437		 * Check that the size is a multiple of the write chunk size if
1438		 * the write chunk size is available.
1439		 */
1440		if (size % write_size != 0) {
1441			rc = EINVAL;
1442			goto fail2;
1443		}
1444	} else {
1445		write_size = EF10_NVRAM_CHUNK;
1446	}
1447
1448	while (size > 0) {
1449		chunk = MIN(size, write_size);
1450
1451		if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
1452			    data, chunk)) != 0) {
1453			goto fail3;
1454		}
1455
1456		size -= chunk;
1457		data += chunk;
1458		offset += chunk;
1459	}
1460
1461	return (0);
1462
1463fail3:
1464	EFSYS_PROBE(fail3);
1465fail2:
1466	EFSYS_PROBE(fail2);
1467fail1:
1468	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1469
1470	return (rc);
1471}
1472
1473				void
1474ef10_nvram_partn_unlock(
1475	__in			efx_nic_t *enp,
1476	__in			uint32_t partn)
1477{
1478	boolean_t reboot;
1479	efx_rc_t rc;
1480
1481	reboot = B_FALSE;
1482	if ((rc = efx_mcdi_nvram_update_finish(enp, partn, reboot)) != 0)
1483		goto fail1;
1484
1485	return;
1486
1487fail1:
1488	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1489}
1490
1491	__checkReturn		efx_rc_t
1492ef10_nvram_partn_set_version(
1493	__in			efx_nic_t *enp,
1494	__in			uint32_t partn,
1495	__in_ecount(4)		uint16_t version[4])
1496{
1497	struct tlv_partition_version partn_version;
1498	size_t size;
1499	efx_rc_t rc;
1500
1501	/* Add or modify partition version TLV item */
1502	partn_version.version_w = __CPU_TO_LE_16(version[0]);
1503	partn_version.version_x = __CPU_TO_LE_16(version[1]);
1504	partn_version.version_y = __CPU_TO_LE_16(version[2]);
1505	partn_version.version_z = __CPU_TO_LE_16(version[3]);
1506
1507	size = sizeof (partn_version) - (2 * sizeof (uint32_t));
1508
1509	/* Write the version number to all segments in the partition */
1510	if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
1511		    NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
1512		    TLV_TAG_PARTITION_VERSION(partn),
1513		    (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
1514		goto fail1;
1515
1516	return (0);
1517
1518fail1:
1519	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1520
1521	return (rc);
1522}
1523
1524#endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
1525
1526#if EFSYS_OPT_NVRAM
1527
1528typedef struct ef10_parttbl_entry_s {
1529	unsigned int		partn;
1530	unsigned int		port;
1531	efx_nvram_type_t	nvtype;
1532} ef10_parttbl_entry_t;
1533
1534/* Translate EFX NVRAM types to firmware partition types */
1535static ef10_parttbl_entry_t hunt_parttbl[] = {
1536	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   1, EFX_NVRAM_MC_FIRMWARE},
1537	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   2, EFX_NVRAM_MC_FIRMWARE},
1538	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   3, EFX_NVRAM_MC_FIRMWARE},
1539	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   4, EFX_NVRAM_MC_FIRMWARE},
1540	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
1541	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
1542	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
1543	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
1544	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   1, EFX_NVRAM_BOOTROM},
1545	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   2, EFX_NVRAM_BOOTROM},
1546	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   3, EFX_NVRAM_BOOTROM},
1547	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   4, EFX_NVRAM_BOOTROM},
1548	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
1549	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1, 2, EFX_NVRAM_BOOTROM_CFG},
1550	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2, 3, EFX_NVRAM_BOOTROM_CFG},
1551	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 4, EFX_NVRAM_BOOTROM_CFG},
1552	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   1, EFX_NVRAM_DYNAMIC_CFG},
1553	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   2, EFX_NVRAM_DYNAMIC_CFG},
1554	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   3, EFX_NVRAM_DYNAMIC_CFG},
1555	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   4, EFX_NVRAM_DYNAMIC_CFG},
1556	{NVRAM_PARTITION_TYPE_FPGA,		   1, EFX_NVRAM_FPGA},
1557	{NVRAM_PARTITION_TYPE_FPGA,		   2, EFX_NVRAM_FPGA},
1558	{NVRAM_PARTITION_TYPE_FPGA,		   3, EFX_NVRAM_FPGA},
1559	{NVRAM_PARTITION_TYPE_FPGA,		   4, EFX_NVRAM_FPGA},
1560	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   1, EFX_NVRAM_FPGA_BACKUP},
1561	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   2, EFX_NVRAM_FPGA_BACKUP},
1562	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   3, EFX_NVRAM_FPGA_BACKUP},
1563	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   4, EFX_NVRAM_FPGA_BACKUP},
1564	{NVRAM_PARTITION_TYPE_LICENSE,		   1, EFX_NVRAM_LICENSE},
1565	{NVRAM_PARTITION_TYPE_LICENSE,		   2, EFX_NVRAM_LICENSE},
1566	{NVRAM_PARTITION_TYPE_LICENSE,		   3, EFX_NVRAM_LICENSE},
1567	{NVRAM_PARTITION_TYPE_LICENSE,		   4, EFX_NVRAM_LICENSE}
1568};
1569
1570static ef10_parttbl_entry_t medford_parttbl[] = {
1571	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   1, EFX_NVRAM_MC_FIRMWARE},
1572	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   2, EFX_NVRAM_MC_FIRMWARE},
1573	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   3, EFX_NVRAM_MC_FIRMWARE},
1574	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   4, EFX_NVRAM_MC_FIRMWARE},
1575	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
1576	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
1577	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
1578	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
1579	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   1, EFX_NVRAM_BOOTROM},
1580	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   2, EFX_NVRAM_BOOTROM},
1581	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   3, EFX_NVRAM_BOOTROM},
1582	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   4, EFX_NVRAM_BOOTROM},
1583	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
1584	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 2, EFX_NVRAM_BOOTROM_CFG},
1585	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 3, EFX_NVRAM_BOOTROM_CFG},
1586	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 4, EFX_NVRAM_BOOTROM_CFG},
1587	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   1, EFX_NVRAM_DYNAMIC_CFG},
1588	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   2, EFX_NVRAM_DYNAMIC_CFG},
1589	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   3, EFX_NVRAM_DYNAMIC_CFG},
1590	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   4, EFX_NVRAM_DYNAMIC_CFG},
1591	{NVRAM_PARTITION_TYPE_FPGA,		   1, EFX_NVRAM_FPGA},
1592	{NVRAM_PARTITION_TYPE_FPGA,		   2, EFX_NVRAM_FPGA},
1593	{NVRAM_PARTITION_TYPE_FPGA,		   3, EFX_NVRAM_FPGA},
1594	{NVRAM_PARTITION_TYPE_FPGA,		   4, EFX_NVRAM_FPGA},
1595	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   1, EFX_NVRAM_FPGA_BACKUP},
1596	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   2, EFX_NVRAM_FPGA_BACKUP},
1597	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   3, EFX_NVRAM_FPGA_BACKUP},
1598	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   4, EFX_NVRAM_FPGA_BACKUP},
1599	{NVRAM_PARTITION_TYPE_LICENSE,		   1, EFX_NVRAM_LICENSE},
1600	{NVRAM_PARTITION_TYPE_LICENSE,		   2, EFX_NVRAM_LICENSE},
1601	{NVRAM_PARTITION_TYPE_LICENSE,		   3, EFX_NVRAM_LICENSE},
1602	{NVRAM_PARTITION_TYPE_LICENSE,		   4, EFX_NVRAM_LICENSE}
1603};
1604
1605static	__checkReturn		efx_rc_t
1606ef10_parttbl_get(
1607	__in			efx_nic_t *enp,
1608	__out			ef10_parttbl_entry_t **parttblp,
1609	__out			size_t *parttbl_rowsp)
1610{
1611	switch (enp->en_family) {
1612	case EFX_FAMILY_HUNTINGTON:
1613		*parttblp = hunt_parttbl;
1614		*parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
1615		break;
1616
1617	case EFX_FAMILY_MEDFORD:
1618		*parttblp = medford_parttbl;
1619		*parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
1620		break;
1621
1622	default:
1623		EFSYS_ASSERT(B_FALSE);
1624		return (EINVAL);
1625	}
1626	return (0);
1627}
1628
1629	__checkReturn		efx_rc_t
1630ef10_nvram_type_to_partn(
1631	__in			efx_nic_t *enp,
1632	__in			efx_nvram_type_t type,
1633	__out			uint32_t *partnp)
1634{
1635	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1636	ef10_parttbl_entry_t *parttbl = NULL;
1637	size_t parttbl_rows = 0;
1638	unsigned int i;
1639
1640	EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
1641	EFSYS_ASSERT(partnp != NULL);
1642
1643	if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
1644		for (i = 0; i < parttbl_rows; i++) {
1645			ef10_parttbl_entry_t *entry = &parttbl[i];
1646
1647			if (entry->nvtype == type &&
1648			    entry->port == emip->emi_port) {
1649				*partnp = entry->partn;
1650				return (0);
1651			}
1652		}
1653	}
1654
1655	return (ENOTSUP);
1656}
1657
1658#if EFSYS_OPT_DIAG
1659
1660static	__checkReturn		efx_rc_t
1661ef10_nvram_partn_to_type(
1662	__in			efx_nic_t *enp,
1663	__in			uint32_t partn,
1664	__out			efx_nvram_type_t *typep)
1665{
1666	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1667	ef10_parttbl_entry_t *parttbl = NULL;
1668	size_t parttbl_rows = 0;
1669	unsigned int i;
1670
1671	EFSYS_ASSERT(typep != NULL);
1672
1673	if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
1674		for (i = 0; i < parttbl_rows; i++) {
1675			ef10_parttbl_entry_t *entry = &parttbl[i];
1676
1677			if (entry->partn == partn &&
1678			    entry->port == emip->emi_port) {
1679				*typep = entry->nvtype;
1680				return (0);
1681			}
1682		}
1683	}
1684
1685	return (ENOTSUP);
1686}
1687
1688	__checkReturn		efx_rc_t
1689ef10_nvram_test(
1690	__in			efx_nic_t *enp)
1691{
1692	efx_nvram_type_t type;
1693	unsigned int npartns = 0;
1694	uint32_t *partns = NULL;
1695	size_t size;
1696	unsigned int i;
1697	efx_rc_t rc;
1698
1699	/* Read available partitions from NVRAM partition map */
1700	size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
1701	EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
1702	if (partns == NULL) {
1703		rc = ENOMEM;
1704		goto fail1;
1705	}
1706
1707	if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
1708		    &npartns)) != 0) {
1709		goto fail2;
1710	}
1711
1712	for (i = 0; i < npartns; i++) {
1713		/* Check if the partition is supported for this port */
1714		if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
1715			continue;
1716
1717		if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
1718			goto fail3;
1719	}
1720
1721	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
1722	return (0);
1723
1724fail3:
1725	EFSYS_PROBE(fail3);
1726fail2:
1727	EFSYS_PROBE(fail2);
1728	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
1729fail1:
1730	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1731	return (rc);
1732}
1733
1734#endif	/* EFSYS_OPT_DIAG */
1735
1736	__checkReturn		efx_rc_t
1737ef10_nvram_partn_get_version(
1738	__in			efx_nic_t *enp,
1739	__in			uint32_t partn,
1740	__out			uint32_t *subtypep,
1741	__out_ecount(4)		uint16_t version[4])
1742{
1743	efx_rc_t rc;
1744
1745	/* FIXME: get highest partn version from all ports */
1746	/* FIXME: return partn description if available */
1747
1748	if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
1749		    version, NULL, 0)) != 0)
1750		goto fail1;
1751
1752	return (0);
1753
1754fail1:
1755	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1756
1757	return (rc);
1758}
1759
1760	__checkReturn		efx_rc_t
1761ef10_nvram_partn_rw_start(
1762	__in			efx_nic_t *enp,
1763	__in			uint32_t partn,
1764	__out			size_t *chunk_sizep)
1765{
1766	efx_rc_t rc;
1767
1768	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1769		goto fail1;
1770
1771	if (chunk_sizep != NULL)
1772		*chunk_sizep = EF10_NVRAM_CHUNK;
1773
1774	return (0);
1775
1776fail1:
1777	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1778
1779	return (rc);
1780}
1781
1782				void
1783ef10_nvram_partn_rw_finish(
1784	__in			efx_nic_t *enp,
1785	__in			uint32_t partn)
1786{
1787	ef10_nvram_partn_unlock(enp, partn);
1788}
1789
1790#endif	/* EFSYS_OPT_NVRAM */
1791
1792#endif	/* EFSYS_OPT_HUNTINGTON */
1793