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  *
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 
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