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