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