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