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