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