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