1220281carybchik/*-
28e01f30arybchik * Copyright (c) 2012-2016 Solarflare Communications Inc.
3220281carybchik * All rights reserved.
4220281carybchik *
5220281carybchik * Redistribution and use in source and binary forms, with or without
6220281carybchik * modification, are permitted provided that the following conditions are met:
7220281carybchik *
8220281carybchik * 1. Redistributions of source code must retain the above copyright notice,
9220281carybchik *    this list of conditions and the following disclaimer.
10220281carybchik * 2. Redistributions in binary form must reproduce the above copyright notice,
11220281carybchik *    this list of conditions and the following disclaimer in the documentation
12220281carybchik *    and/or other materials provided with the distribution.
13220281carybchik *
14220281carybchik * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15220281carybchik * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16220281carybchik * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17220281carybchik * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18220281carybchik * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19220281carybchik * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20220281carybchik * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21220281carybchik * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22220281carybchik * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23220281carybchik * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24220281carybchik * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25220281carybchik *
26220281carybchik * The views and conclusions contained in the software and documentation are
27220281carybchik * those of the authors and should not be interpreted as representing official
28220281carybchik * policies, either expressed or implied, of the FreeBSD Project.
29220281carybchik */
30220281carybchik
31220281carybchik#include <sys/cdefs.h>
32220281carybchik__FBSDID("$FreeBSD$");
33220281carybchik
34220281carybchik#include "efx.h"
35220281carybchik#include "efx_impl.h"
36220281carybchik
370bf12bcarybchik#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
38220281carybchik
39220281carybchik#if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
40220281carybchik
41220281carybchik#include "ef10_tlv_layout.h"
42220281carybchik
43220281carybchik/* Cursor for TLV partition format */
44220281carybchiktypedef struct tlv_cursor_s {
45220281carybchik	uint32_t	*block;			/* Base of data block */
46220281carybchik	uint32_t	*current;		/* Cursor position */
47220281carybchik	uint32_t	*end;			/* End tag position */
48220281carybchik	uint32_t	*limit;			/* Last dword of data block */
49220281carybchik} tlv_cursor_t;
50220281carybchik
51bd836a6arybchiktypedef struct nvram_partition_s {
52bd836a6arybchik	uint16_t type;
53bd836a6arybchik	uint8_t chip_select;
54bd836a6arybchik	uint8_t flags;
55bd836a6arybchik	/*
56bd836a6arybchik	 * The full length of the NVRAM partition.
57bd836a6arybchik	 * This is different from tlv_partition_header.total_length,
58bd836a6arybchik	 *  which can be smaller.
59bd836a6arybchik	 */
60bd836a6arybchik	uint32_t length;
61bd836a6arybchik	uint32_t erase_size;
62bd836a6arybchik	uint32_t *data;
63bd836a6arybchik	tlv_cursor_t tlv_cursor;
64bd836a6arybchik} nvram_partition_t;
65bd836a6arybchik
66bd836a6arybchik
6716fdc5earybchikstatic	__checkReturn		efx_rc_t
68220281carybchiktlv_validate_state(
69bd836a6arybchik	__inout			tlv_cursor_t *cursor);
70220281carybchik
71220281carybchik
72bd836a6arybchikstatic				void
73bd836a6arybchiktlv_init_block(
74bd836a6arybchik	__out	uint32_t	*block)
75bd836a6arybchik{
76bd836a6arybchik	*block = __CPU_TO_LE_32(TLV_TAG_END);
77bd836a6arybchik}
78bd836a6arybchik
79220281carybchikstatic				uint32_t
80220281carybchiktlv_tag(
81220281carybchik	__in	tlv_cursor_t	*cursor)
82220281carybchik{
83220281carybchik	uint32_t dword, tag;
84220281carybchik
85220281carybchik	dword = cursor->current[0];
86220281carybchik	tag = __LE_TO_CPU_32(dword);
87220281carybchik
88220281carybchik	return (tag);
89220281carybchik}
90220281carybchik
91220281carybchikstatic				size_t
92220281carybchiktlv_length(
93220281carybchik	__in	tlv_cursor_t	*cursor)
94220281carybchik{
95220281carybchik	uint32_t dword, length;
96220281carybchik
97220281carybchik	if (tlv_tag(cursor) == TLV_TAG_END)
98220281carybchik		return (0);
99220281carybchik
100220281carybchik	dword = cursor->current[1];
101220281carybchik	length = __LE_TO_CPU_32(dword);
102220281carybchik
103220281carybchik	return ((size_t)length);
104220281carybchik}
105220281carybchik
106220281carybchikstatic				uint8_t *
107220281carybchiktlv_value(
108220281carybchik	__in	tlv_cursor_t	*cursor)
109220281carybchik{
110220281carybchik	if (tlv_tag(cursor) == TLV_TAG_END)
111220281carybchik		return (NULL);
112220281carybchik
113220281carybchik	return ((uint8_t *)(&cursor->current[2]));
114220281carybchik}
115220281carybchik
116220281carybchikstatic				uint8_t *
117220281carybchiktlv_item(
118220281carybchik	__in	tlv_cursor_t	*cursor)
119220281carybchik{
120220281carybchik	if (tlv_tag(cursor) == TLV_TAG_END)
121220281carybchik		return (NULL);
122220281carybchik
123220281carybchik	return ((uint8_t *)cursor->current);
124220281carybchik}
125220281carybchik
126220281carybchik/*
127220281carybchik * TLV item DWORD length is tag + length + value (rounded up to DWORD)
128220281carybchik * equivalent to tlv_n_words_for_len in mc-comms tlv.c
129220281carybchik */
130220281carybchik#define	TLV_DWORD_COUNT(length) \
131220281carybchik	(1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
132220281carybchik
133220281carybchik
134220281carybchikstatic				uint32_t *
135220281carybchiktlv_next_item_ptr(
136220281carybchik	__in	tlv_cursor_t	*cursor)
137220281carybchik{
138220281carybchik	uint32_t length;
139220281carybchik
140220281carybchik	length = tlv_length(cursor);
141220281carybchik
142220281carybchik	return (cursor->current + TLV_DWORD_COUNT(length));
143220281carybchik}
144220281carybchik
145bd836a6arybchikstatic	__checkReturn		efx_rc_t
146220281carybchiktlv_advance(
147bd836a6arybchik	__inout	tlv_cursor_t	*cursor)
148220281carybchik{
14916fdc5earybchik	efx_rc_t rc;
150220281carybchik
151220281carybchik	if ((rc = tlv_validate_state(cursor)) != 0)
152220281carybchik		goto fail1;
153220281carybchik
154220281carybchik	if (cursor->current == cursor->end) {
155220281carybchik		/* No more tags after END tag */
156220281carybchik		cursor->current = NULL;
157220281carybchik		rc = ENOENT;
158220281carybchik		goto fail2;
159220281carybchik	}
160220281carybchik
161220281carybchik	/* Advance to next item and validate */
162220281carybchik	cursor->current = tlv_next_item_ptr(cursor);
163220281carybchik
164220281carybchik	if ((rc = tlv_validate_state(cursor)) != 0)
165220281carybchik		goto fail3;
166220281carybchik
167220281carybchik	return (0);
168220281carybchik
169220281carybchikfail3:
170220281carybchik	EFSYS_PROBE(fail3);
171220281carybchikfail2:
172220281carybchik	EFSYS_PROBE(fail2);
173220281carybchikfail1:
17416fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
175220281carybchik
176220281carybchik	return (rc);
177220281carybchik}
178220281carybchik
17916fdc5earybchikstatic				efx_rc_t
180220281carybchiktlv_rewind(
181220281carybchik	__in	tlv_cursor_t	*cursor)
182220281carybchik{
18316fdc5earybchik	efx_rc_t rc;
184220281carybchik
185220281carybchik	cursor->current = cursor->block;
186220281carybchik
187220281carybchik	if ((rc = tlv_validate_state(cursor)) != 0)
188220281carybchik		goto fail1;
189220281carybchik
190220281carybchik	return (0);
191220281carybchik
192220281carybchikfail1:
19316fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
194220281carybchik
195220281carybchik	return (rc);
196220281carybchik}
197220281carybchik
19816fdc5earybchikstatic				efx_rc_t
199220281carybchiktlv_find(
200bd836a6arybchik	__inout	tlv_cursor_t	*cursor,
201220281carybchik	__in	uint32_t	tag)
202220281carybchik{
20316fdc5earybchik	efx_rc_t rc;
204220281carybchik
205220281carybchik	rc = tlv_rewind(cursor);
206220281carybchik	while (rc == 0) {
207220281carybchik		if (tlv_tag(cursor) == tag)
208220281carybchik			break;
209220281carybchik
210220281carybchik		rc = tlv_advance(cursor);
211220281carybchik	}
212220281carybchik	return (rc);
213220281carybchik}
214220281carybchik
21516fdc5earybchikstatic	__checkReturn		efx_rc_t
216220281carybchiktlv_validate_state(
217bd836a6arybchik	__inout	tlv_cursor_t	*cursor)
218220281carybchik{
21916fdc5earybchik	efx_rc_t rc;
220220281carybchik
221220281carybchik	/* Check cursor position */
222220281carybchik	if (cursor->current < cursor->block) {
223220281carybchik		rc = EINVAL;
224220281carybchik		goto fail1;
225220281carybchik	}
226220281carybchik	if (cursor->current > cursor->limit) {
227220281carybchik		rc = EINVAL;
228220281carybchik		goto fail2;
229220281carybchik	}
230220281carybchik
231220281carybchik	if (tlv_tag(cursor) != TLV_TAG_END) {
232220281carybchik		/* Check current item has space for tag and length */
233a07211barybchik		if (cursor->current > (cursor->limit - 1)) {
234220281carybchik			cursor->current = NULL;
235220281carybchik			rc = EFAULT;
236220281carybchik			goto fail3;
237220281carybchik		}
238220281carybchik
239a07211barybchik		/* Check we have value data for current item and an END tag */
240a07211barybchik		if (tlv_next_item_ptr(cursor) > cursor->limit) {
241220281carybchik			cursor->current = NULL;
242220281carybchik			rc = EFAULT;
243220281carybchik			goto fail4;
244220281carybchik		}
245220281carybchik	}
246220281carybchik
247220281carybchik	return (0);
248220281carybchik
249220281carybchikfail4:
250220281carybchik	EFSYS_PROBE(fail4);
251220281carybchikfail3:
252220281carybchik	EFSYS_PROBE(fail3);
253220281carybchikfail2:
254220281carybchik	EFSYS_PROBE(fail2);
255220281carybchikfail1:
25616fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
257220281carybchik
258220281carybchik	return (rc);
259220281carybchik}
260220281carybchik
26116fdc5earybchikstatic				efx_rc_t
262220281carybchiktlv_init_cursor(
263b06f43barybchik	__out	tlv_cursor_t	*cursor,
264220281carybchik	__in	uint32_t	*block,
265bd836a6arybchik	__in	uint32_t	*limit,
266bd836a6arybchik	__in	uint32_t	*current)
267220281carybchik{
268220281carybchik	cursor->block	= block;
269220281carybchik	cursor->limit	= limit;
270220281carybchik
271bd836a6arybchik	cursor->current	= current;
272220281carybchik	cursor->end	= NULL;
273220281carybchik
274220281carybchik	return (tlv_validate_state(cursor));
275220281carybchik}
276220281carybchik
277bd836a6arybchikstatic	__checkReturn		efx_rc_t
278220281carybchiktlv_init_cursor_from_size(
279b06f43barybchik	__out	tlv_cursor_t	*cursor,
280bd836a6arybchik	__in_bcount(size)
281bd836a6arybchik		uint8_t		*block,
282220281carybchik	__in	size_t		size)
283220281carybchik{
284220281carybchik	uint32_t *limit;
285220281carybchik	limit = (uint32_t *)(block + size - sizeof (uint32_t));
286bd836a6arybchik	return (tlv_init_cursor(cursor, (uint32_t *)block,
287bd836a6arybchik		limit, (uint32_t *)block));
288220281carybchik}
289220281carybchik
290bd836a6arybchikstatic	__checkReturn		efx_rc_t
291bd836a6arybchiktlv_init_cursor_at_offset(
292bd836a6arybchik	__out	tlv_cursor_t	*cursor,
293bd836a6arybchik	__in_bcount(size)
294bd836a6arybchik		uint8_t		*block,
295bd836a6arybchik	__in	size_t		size,
296bd836a6arybchik	__in	size_t		offset)
297bd836a6arybchik{
298bd836a6arybchik	uint32_t *limit;
299bd836a6arybchik	uint32_t *current;
300bd836a6arybchik	limit = (uint32_t *)(block + size - sizeof (uint32_t));
301bd836a6arybchik	current = (uint32_t *)(block + offset);
302bd836a6arybchik	return (tlv_init_cursor(cursor, (uint32_t *)block, limit, current));
303bd836a6arybchik}
304bd836a6arybchik
305bd836a6arybchikstatic	__checkReturn		efx_rc_t
306220281carybchiktlv_require_end(
307bd836a6arybchik	__inout	tlv_cursor_t	*cursor)
308220281carybchik{
309220281carybchik	uint32_t *pos;
31016fdc5earybchik	efx_rc_t rc;
311220281carybchik
312220281carybchik	if (cursor->end == NULL) {
313220281carybchik		pos = cursor->current;
314220281carybchik		if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
315220281carybchik			goto fail1;
316220281carybchik
317220281carybchik		cursor->end = cursor->current;
318220281carybchik		cursor->current = pos;
319220281carybchik	}
320220281carybchik
321220281carybchik	return (0);
322220281carybchik
323220281carybchikfail1:
32416fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
325220281carybchik
326220281carybchik	return (rc);
327220281carybchik}
328220281carybchik
329220281carybchikstatic				size_t
330220281carybchiktlv_block_length_used(
331bd836a6arybchik	__inout	tlv_cursor_t	*cursor)
332220281carybchik{
33316fdc5earybchik	efx_rc_t rc;
334220281carybchik
335220281carybchik	if ((rc = tlv_validate_state(cursor)) != 0)
336220281carybchik		goto fail1;
337220281carybchik
338220281carybchik	if ((rc = tlv_require_end(cursor)) != 0)
339220281carybchik		goto fail2;
340220281carybchik
341220281carybchik	/* Return space used (including the END tag) */
342220281carybchik	return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
343220281carybchik
344220281carybchikfail2:
345220281carybchik	EFSYS_PROBE(fail2);
346220281carybchikfail1:
34716fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
348220281carybchik
349220281carybchik	return (0);
350220281carybchik}
351220281carybchik
352bd836a6arybchikstatic		uint32_t *
353bd836a6arybchiktlv_last_segment_end(
354bd836a6arybchik	__in	tlv_cursor_t *cursor)
355bd836a6arybchik{
356bd836a6arybchik	tlv_cursor_t segment_cursor;
357bd836a6arybchik	uint32_t *last_segment_end = cursor->block;
358bd836a6arybchik	uint32_t *segment_start = cursor->block;
359bd836a6arybchik
360bd836a6arybchik	/*
361bd836a6arybchik	 * Go through each segment and check that it has an end tag. If there
362bd836a6arybchik	 * is no end tag then the previous segment was the last valid one,
363bd836a6arybchik	 * so return the pointer to its end tag.
364bd836a6arybchik	 */
365719977farybchik	for (;;) {
366bd836a6arybchik		if (tlv_init_cursor(&segment_cursor, segment_start,
367bd836a6arybchik		    cursor->limit, segment_start) != 0)
368bd836a6arybchik			break;
369bd836a6arybchik		if (tlv_require_end(&segment_cursor) != 0)
370bd836a6arybchik			break;
371bd836a6arybchik		last_segment_end = segment_cursor.end;
372bd836a6arybchik		segment_start = segment_cursor.end + 1;
373bd836a6arybchik	}
374bd836a6arybchik
375bd836a6arybchik	return (last_segment_end);
376bd836a6arybchik}
377bd836a6arybchik
378220281carybchik
379bd836a6arybchikstatic				uint32_t *
380220281carybchiktlv_write(
381220281carybchik	__in			tlv_cursor_t *cursor,
382220281carybchik	__in			uint32_t tag,
383220281carybchik	__in_bcount(size)	uint8_t *data,
384220281carybchik	__in			size_t size)
385220281carybchik{
386220281carybchik	uint32_t len = size;
387220281carybchik	uint32_t *ptr;
388220281carybchik
389220281carybchik	ptr = cursor->current;
390220281carybchik
391220281carybchik	*ptr++ = __CPU_TO_LE_32(tag);
392220281carybchik	*ptr++ = __CPU_TO_LE_32(len);
393220281carybchik
394220281carybchik	if (len > 0) {
395220281carybchik		ptr[(len - 1) / sizeof (uint32_t)] = 0;
396220281carybchik		memcpy(ptr, data, len);
3971edccfbarybchik		ptr += EFX_P2ROUNDUP(uint32_t, len,
3981edccfbarybchik		    sizeof (uint32_t)) / sizeof (*ptr);
399220281carybchik	}
400220281carybchik
401220281carybchik	return (ptr);
402220281carybchik}
403220281carybchik
40416fdc5earybchikstatic	__checkReturn		efx_rc_t
405220281carybchiktlv_insert(
406bd836a6arybchik	__inout	tlv_cursor_t	*cursor,
407220281carybchik	__in	uint32_t	tag,
408bd836a6arybchik	__in_bcount(size)
409bd836a6arybchik		uint8_t		*data,
410220281carybchik	__in	size_t		size)
411220281carybchik{
412220281carybchik	unsigned int delta;
413bd836a6arybchik	uint32_t *last_segment_end;
41416fdc5earybchik	efx_rc_t rc;
415220281carybchik
416220281carybchik	if ((rc = tlv_validate_state(cursor)) != 0)
417220281carybchik		goto fail1;
418220281carybchik
419220281carybchik	if ((rc = tlv_require_end(cursor)) != 0)
420220281carybchik		goto fail2;
421220281carybchik
422220281carybchik	if (tag == TLV_TAG_END) {
423220281carybchik		rc = EINVAL;
424220281carybchik		goto fail3;
425220281carybchik	}
426220281carybchik
427bd836a6arybchik	last_segment_end = tlv_last_segment_end(cursor);
428bd836a6arybchik
429220281carybchik	delta = TLV_DWORD_COUNT(size);
430bd836a6arybchik	if (last_segment_end + 1 + delta > cursor->limit) {
431220281carybchik		rc = ENOSPC;
432220281carybchik		goto fail4;
433220281carybchik	}
434220281carybchik
435220281carybchik	/* Move data up: new space at cursor->current */
436220281carybchik	memmove(cursor->current + delta, cursor->current,
437bd836a6arybchik	    (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
438220281carybchik
439220281carybchik	/* Adjust the end pointer */
440220281carybchik	cursor->end += delta;
441220281carybchik
442220281carybchik	/* Write new TLV item */
443220281carybchik	tlv_write(cursor, tag, data, size);
444220281carybchik
445220281carybchik	return (0);
446220281carybchik
447220281carybchikfail4:
448220281carybchik	EFSYS_PROBE(fail4);
449220281carybchikfail3:
450220281carybchik	EFSYS_PROBE(fail3);
451220281carybchikfail2:
452220281carybchik	EFSYS_PROBE(fail2);
453220281carybchikfail1:
45416fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
455220281carybchik
456220281carybchik	return (rc);
457220281carybchik}
458220281carybchik
45916fdc5earybchikstatic	__checkReturn		efx_rc_t
460bd836a6arybchiktlv_delete(
461bd836a6arybchik	__inout	tlv_cursor_t	*cursor)
462bd836a6arybchik{
463bd836a6arybchik	unsigned int delta;
464bd836a6arybchik	uint32_t *last_segment_end;
465bd836a6arybchik	efx_rc_t rc;
466bd836a6arybchik
467bd836a6arybchik	if ((rc = tlv_validate_state(cursor)) != 0)
468bd836a6arybchik		goto fail1;
469bd836a6arybchik
470bd836a6arybchik	if (tlv_tag(cursor) == TLV_TAG_END) {
471bd836a6arybchik		rc = EINVAL;
472bd836a6arybchik		goto fail2;
473bd836a6arybchik	}
474bd836a6arybchik
475bd836a6arybchik	delta = TLV_DWORD_COUNT(tlv_length(cursor));
476bd836a6arybchik
477bd836a6arybchik	if ((rc = tlv_require_end(cursor)) != 0)
478bd836a6arybchik		goto fail3;
479bd836a6arybchik
480bd836a6arybchik	last_segment_end = tlv_last_segment_end(cursor);
481bd836a6arybchik
482bd836a6arybchik	/* Shuffle things down, destroying the item at cursor->current */
483bd836a6arybchik	memmove(cursor->current, cursor->current + delta,
484bd836a6arybchik	    (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
485bd836a6arybchik	/* Zero the new space at the end of the TLV chain */
486bd836a6arybchik	memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t));
487bd836a6arybchik	/* Adjust the end pointer */
488bd836a6arybchik	cursor->end -= delta;
489bd836a6arybchik
490bd836a6arybchik	return (0);
491bd836a6arybchik
492bd836a6arybchikfail3:
493bd836a6arybchik	EFSYS_PROBE(fail3);
494bd836a6arybchikfail2:
495bd836a6arybchik	EFSYS_PROBE(fail2);
496bd836a6arybchikfail1:
497bd836a6arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
498bd836a6arybchik
499bd836a6arybchik	return (rc);
500bd836a6arybchik}
501bd836a6arybchik
502bd836a6arybchikstatic	__checkReturn		efx_rc_t
503220281carybchiktlv_modify(
504bd836a6arybchik	__inout	tlv_cursor_t	*cursor,
505220281carybchik	__in	uint32_t	tag,
506bd836a6arybchik	__in_bcount(size)
507bd836a6arybchik		uint8_t		*data,
508220281carybchik	__in	size_t		size)
509220281carybchik{
510220281carybchik	uint32_t *pos;
511220281carybchik	unsigned int old_ndwords;
512220281carybchik	unsigned int new_ndwords;
513220281carybchik	unsigned int delta;
514bd836a6arybchik	uint32_t *last_segment_end;
51516fdc5earybchik	efx_rc_t rc;
516220281carybchik
517220281carybchik	if ((rc = tlv_validate_state(cursor)) != 0)
518220281carybchik		goto fail1;
519220281carybchik
520220281carybchik	if (tlv_tag(cursor) == TLV_TAG_END) {
521220281carybchik		rc = EINVAL;
522220281carybchik		goto fail2;
523220281carybchik	}
524220281carybchik	if (tlv_tag(cursor) != tag) {
525220281carybchik		rc = EINVAL;
526220281carybchik		goto fail3;
527220281carybchik	}
528220281carybchik
529220281carybchik	old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
530220281carybchik	new_ndwords = TLV_DWORD_COUNT(size);
531220281carybchik
532220281carybchik	if ((rc = tlv_require_end(cursor)) != 0)
533220281carybchik		goto fail4;
534220281carybchik
535bd836a6arybchik	last_segment_end = tlv_last_segment_end(cursor);
536bd836a6arybchik
537220281carybchik	if (new_ndwords > old_ndwords) {
538220281carybchik		/* Expand space used for TLV item */
539220281carybchik		delta = new_ndwords - old_ndwords;
540220281carybchik		pos = cursor->current + old_ndwords;
541220281carybchik
542bd836a6arybchik		if (last_segment_end + 1 + delta > cursor->limit) {
543220281carybchik			rc = ENOSPC;
544220281carybchik			goto fail5;
545220281carybchik		}
546220281carybchik
547220281carybchik		/* Move up: new space at (cursor->current + old_ndwords) */
548220281carybchik		memmove(pos + delta, pos,
549bd836a6arybchik		    (last_segment_end + 1 - pos) * sizeof (uint32_t));
550220281carybchik
551220281carybchik		/* Adjust the end pointer */
552220281carybchik		cursor->end += delta;
553220281carybchik
554220281carybchik	} else if (new_ndwords < old_ndwords) {
555220281carybchik		/* Shrink space used for TLV item */
556220281carybchik		delta = old_ndwords - new_ndwords;
557220281carybchik		pos = cursor->current + new_ndwords;
558220281carybchik
559220281carybchik		/* Move down: remove words at (cursor->current + new_ndwords) */
560220281carybchik		memmove(pos, pos + delta,
561bd836a6arybchik		    (last_segment_end + 1 - pos) * sizeof (uint32_t));
562220281carybchik
563220281carybchik		/* Zero the new space at the end of the TLV chain */
564bd836a6arybchik		memset(last_segment_end + 1 - delta, 0,
565bd836a6arybchik		    delta * sizeof (uint32_t));
566220281carybchik
567220281carybchik		/* Adjust the end pointer */
568220281carybchik		cursor->end -= delta;
569220281carybchik	}
570220281carybchik
571220281carybchik	/* Write new data */
572220281carybchik	tlv_write(cursor, tag, data, size);
573220281carybchik
574220281carybchik	return (0);
575220281carybchik
576220281carybchikfail5:
577220281carybchik	EFSYS_PROBE(fail5);
578220281carybchikfail4:
579220281carybchik	EFSYS_PROBE(fail4);
580220281carybchikfail3:
581220281carybchik	EFSYS_PROBE(fail3);
582220281carybchikfail2:
583220281carybchik	EFSYS_PROBE(fail2);
584220281carybchikfail1:
58516fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
586220281carybchik
587220281carybchik	return (rc);
588220281carybchik}
589220281carybchik
590bd836a6arybchikstatic uint32_t checksum_tlv_partition(
591bd836a6arybchik	__in	nvram_partition_t *partition)
592bd836a6arybchik{
593bd836a6arybchik	tlv_cursor_t *cursor;
594bd836a6arybchik	uint32_t *ptr;
595bd836a6arybchik	uint32_t *end;
596bd836a6arybchik	uint32_t csum;
597bd836a6arybchik	size_t len;
598bd836a6arybchik
599bd836a6arybchik	cursor = &partition->tlv_cursor;
600bd836a6arybchik	len = tlv_block_length_used(cursor);
601bd836a6arybchik	EFSYS_ASSERT3U((len & 3), ==, 0);
602bd836a6arybchik
603bd836a6arybchik	csum = 0;
604bd836a6arybchik	ptr = partition->data;
605bd836a6arybchik	end = &ptr[len >> 2];
606bd836a6arybchik
607bd836a6arybchik	while (ptr < end)
608bd836a6arybchik		csum += __LE_TO_CPU_32(*ptr++);
609bd836a6arybchik
610bd836a6arybchik	return (csum);
611bd836a6arybchik}
612bd836a6arybchik
613bd836a6arybchikstatic	__checkReturn		efx_rc_t
614bd836a6arybchiktlv_update_partition_len_and_cks(
615bd836a6arybchik	__in	tlv_cursor_t *cursor)
616bd836a6arybchik{
617bd836a6arybchik	efx_rc_t rc;
618bd836a6arybchik	nvram_partition_t partition;
619bd836a6arybchik	struct tlv_partition_header *header;
620bd836a6arybchik	struct tlv_partition_trailer *trailer;
621bd836a6arybchik	size_t new_len;
622bd836a6arybchik
623bd836a6arybchik	/*
624bd836a6arybchik	 * We just modified the partition, so the total length may not be
625bd836a6arybchik	 * valid. Don't use tlv_find(), which performs some sanity checks
626bd836a6arybchik	 * that may fail here.
627bd836a6arybchik	 */
628bd836a6arybchik	partition.data = cursor->block;
629bd836a6arybchik	memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
630bd836a6arybchik	header = (struct tlv_partition_header *)partition.data;
631bd836a6arybchik	/* Sanity check. */
632bd836a6arybchik	if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
633bd836a6arybchik		rc = EFAULT;
634bd836a6arybchik		goto fail1;
635bd836a6arybchik	}
636bd836a6arybchik	new_len =  tlv_block_length_used(&partition.tlv_cursor);
637bd836a6arybchik	if (new_len == 0) {
638bd836a6arybchik		rc = EFAULT;
639bd836a6arybchik		goto fail2;
640bd836a6arybchik	}
641bd836a6arybchik	header->total_length = __CPU_TO_LE_32(new_len);
642bd836a6arybchik	/* Ensure the modified partition always has a new generation count. */
643bd836a6arybchik	header->generation = __CPU_TO_LE_32(
644bd836a6arybchik	    __LE_TO_CPU_32(header->generation) + 1);
645bd836a6arybchik
646bd836a6arybchik	trailer = (struct tlv_partition_trailer *)((uint8_t *)header +
647bd836a6arybchik	    new_len - sizeof (*trailer) - sizeof (uint32_t));
648bd836a6arybchik	trailer->generation = header->generation;
649bd836a6arybchik	trailer->checksum = __CPU_TO_LE_32(
650bd836a6arybchik	    __LE_TO_CPU_32(trailer->checksum) -
651bd836a6arybchik	    checksum_tlv_partition(&partition));
652bd836a6arybchik
653bd836a6arybchik	return (0);
654bd836a6arybchik
655bd836a6arybchikfail2:
656bd836a6arybchik	EFSYS_PROBE(fail2);
657bd836a6arybchikfail1:
658bd836a6arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
659bd836a6arybchik
660bd836a6arybchik	return (rc);
661bd836a6arybchik}
662bd836a6arybchik
663bd836a6arybchik/* Validate buffer contents (before writing to flash) */
66416fdc5earybchik	__checkReturn		efx_rc_t
6659751d26arybchikef10_nvram_buffer_validate(
666220281carybchik	__in			uint32_t partn,
667220281carybchik	__in_bcount(partn_size)	caddr_t partn_data,
668220281carybchik	__in			size_t partn_size)
669220281carybchik{
670220281carybchik	tlv_cursor_t cursor;
671220281carybchik	struct tlv_partition_header *header;
672220281carybchik	struct tlv_partition_trailer *trailer;
673220281carybchik	size_t total_length;
674220281carybchik	uint32_t cksum;
675220281carybchik	int pos;
67616fdc5earybchik	efx_rc_t rc;
677220281carybchik
678c86fe87arybchik	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
679220281carybchik
680220281carybchik	if ((partn_data == NULL) || (partn_size == 0)) {
681220281carybchik		rc = EINVAL;
682220281carybchik		goto fail1;
683220281carybchik	}
684220281carybchik
685220281carybchik	/* The partition header must be the first item (at offset zero) */
686f85d27earybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
687220281carybchik		    partn_size)) != 0) {
688220281carybchik		rc = EFAULT;
689220281carybchik		goto fail2;
690220281carybchik	}
691220281carybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
692220281carybchik		rc = EINVAL;
693220281carybchik		goto fail3;
694220281carybchik	}
695220281carybchik	header = (struct tlv_partition_header *)tlv_item(&cursor);
696220281carybchik
697220281carybchik	/* Check TLV partition length (includes the END tag) */
698220281carybchik	total_length = __LE_TO_CPU_32(header->total_length);
699220281carybchik	if (total_length > partn_size) {
700220281carybchik		rc = EFBIG;
701220281carybchik		goto fail4;
702220281carybchik	}
703220281carybchik
704a07211barybchik	/* Check partition header matches partn */
705a07211barybchik	if (__LE_TO_CPU_16(header->type_id) != partn) {
706a07211barybchik		rc = EINVAL;
707a07211barybchik		goto fail5;
708a07211barybchik	}
709a07211barybchik
710220281carybchik	/* Check partition ends with PARTITION_TRAILER and END tags */
711220281carybchik	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
712220281carybchik		rc = EINVAL;
713a07211barybchik		goto fail6;
714220281carybchik	}
715220281carybchik	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
716220281carybchik
717220281carybchik	if ((rc = tlv_advance(&cursor)) != 0) {
718220281carybchik		rc = EINVAL;
719a07211barybchik		goto fail7;
720220281carybchik	}
721220281carybchik	if (tlv_tag(&cursor) != TLV_TAG_END) {
722220281carybchik		rc = EINVAL;
723a07211barybchik		goto fail8;
724220281carybchik	}
725220281carybchik
726220281carybchik	/* Check generation counts are consistent */
727220281carybchik	if (trailer->generation != header->generation) {
728220281carybchik		rc = EINVAL;
729a07211barybchik		goto fail9;
730220281carybchik	}
731220281carybchik
732220281carybchik	/* Verify partition checksum */
733220281carybchik	cksum = 0;
734220281carybchik	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
735220281carybchik		cksum += *((uint32_t *)(partn_data + pos));
736220281carybchik	}
737220281carybchik	if (cksum != 0) {
738220281carybchik		rc = EINVAL;
739a07211barybchik		goto fail10;
740220281carybchik	}
741220281carybchik
742220281carybchik	return (0);
743220281carybchik
744a07211barybchikfail10:
745a07211barybchik	EFSYS_PROBE(fail10);
746220281carybchikfail9:
747220281carybchik	EFSYS_PROBE(fail9);
748220281carybchikfail8:
749220281carybchik	EFSYS_PROBE(fail8);
750220281carybchikfail7:
751220281carybchik	EFSYS_PROBE(fail7);
752220281carybchikfail6:
753220281carybchik	EFSYS_PROBE(fail6);
754220281carybchikfail5:
755220281carybchik	EFSYS_PROBE(fail5);
756220281carybchikfail4:
757220281carybchik	EFSYS_PROBE(fail4);
758220281carybchikfail3:
759220281carybchik	EFSYS_PROBE(fail3);
760220281carybchikfail2:
761220281carybchik	EFSYS_PROBE(fail2);
762220281carybchikfail1:
76316fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
764220281carybchik
765220281carybchik	return (rc);
766220281carybchik}
767220281carybchik
768a07211barybchik			void
769a07211barybchikef10_nvram_buffer_init(
770a07211barybchik	__out_bcount(buffer_size)
771a07211barybchik				caddr_t bufferp,
772a07211barybchik	__in			size_t buffer_size)
773a07211barybchik{
774a07211barybchik	uint32_t *buf = (uint32_t *)bufferp;
775a07211barybchik
776a07211barybchik	memset(buf, 0xff, buffer_size);
7778e30d77arybchik
778a07211barybchik	tlv_init_block(buf);
779a07211barybchik}
7808e30d77arybchik
7818e30d77arybchik	__checkReturn		efx_rc_t
7828e30d77arybchikef10_nvram_buffer_create(
783a07211barybchik	__in			uint32_t partn_type,
784a07211barybchik	__out_bcount(partn_size)
785a07211barybchik				caddr_t partn_data,
7868e30d77arybchik	__in			size_t partn_size)
7878e30d77arybchik{
7888e30d77arybchik	uint32_t *buf = (uint32_t *)partn_data;
7898e30d77arybchik	efx_rc_t rc;
7908e30d77arybchik	tlv_cursor_t cursor;
7918e30d77arybchik	struct tlv_partition_header header;
7928e30d77arybchik	struct tlv_partition_trailer trailer;
7938e30d77arybchik
794bb7138farybchik	unsigned int min_buf_size = sizeof (struct tlv_partition_header) +
7958e30d77arybchik	    sizeof (struct tlv_partition_trailer);
7968e30d77arybchik	if (partn_size < min_buf_size) {
7978e30d77arybchik		rc = EINVAL;
7988e30d77arybchik		goto fail1;
7998e30d77arybchik	}
8008e30d77arybchik
801a07211barybchik	ef10_nvram_buffer_init(partn_data, partn_size);
8028e30d77arybchik
8038e30d77arybchik	if ((rc = tlv_init_cursor(&cursor, buf,
8048e30d77arybchik	    (uint32_t *)((uint8_t *)buf + partn_size),
8058e30d77arybchik	    buf)) != 0) {
8068e30d77arybchik		goto fail2;
8078e30d77arybchik	}
8088e30d77arybchik
8098e30d77arybchik	header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
8108e30d77arybchik	header.length = __CPU_TO_LE_32(sizeof (header) - 8);
8118e30d77arybchik	header.type_id = __CPU_TO_LE_16(partn_type);
8128e30d77arybchik	header.preset = 0;
8138e30d77arybchik	header.generation = __CPU_TO_LE_32(1);
8148e30d77arybchik	header.total_length = 0;  /* This will be fixed below. */
8158e30d77arybchik	if ((rc = tlv_insert(
8168e30d77arybchik	    &cursor, TLV_TAG_PARTITION_HEADER,
8178e30d77arybchik	    (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
8188e30d77arybchik		goto fail3;
8198e30d77arybchik	if ((rc = tlv_advance(&cursor)) != 0)
8208e30d77arybchik		goto fail4;
8218e30d77arybchik
8228e30d77arybchik	trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
8238e30d77arybchik	trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
8248e30d77arybchik	trailer.generation = header.generation;
8258e30d77arybchik	trailer.checksum = 0;  /* This will be fixed below. */
8268e30d77arybchik	if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
8278e30d77arybchik	    (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
8288e30d77arybchik		goto fail5;
8298e30d77arybchik
8308e30d77arybchik	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
8318e30d77arybchik		goto fail6;
8328e30d77arybchik
8338e30d77arybchik	/* Check that the partition is valid. */
834a07211barybchik	if ((rc = ef10_nvram_buffer_validate(partn_type,
8358e30d77arybchik	    partn_data, partn_size)) != 0)
8368e30d77arybchik		goto fail7;
8378e30d77arybchik
8388e30d77arybchik	return (0);
8398e30d77arybchik
8408e30d77arybchikfail7:
8418e30d77arybchik	EFSYS_PROBE(fail7);
8428e30d77arybchikfail6:
8438e30d77arybchik	EFSYS_PROBE(fail6);
8448e30d77arybchikfail5:
8458e30d77arybchik	EFSYS_PROBE(fail5);
8468e30d77arybchikfail4:
8478e30d77arybchik	EFSYS_PROBE(fail4);
8488e30d77arybchikfail3:
8498e30d77arybchik	EFSYS_PROBE(fail3);
8508e30d77arybchikfail2:
8518e30d77arybchik	EFSYS_PROBE(fail2);
8528e30d77arybchikfail1:
8538e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
8548e30d77arybchik
8558e30d77arybchik	return (rc);
8568e30d77arybchik}
8578e30d77arybchik
8588e30d77arybchikstatic			uint32_t
8598e30d77arybchikbyte_offset(
8608e30d77arybchik	__in		uint32_t *position,
8618e30d77arybchik	__in		uint32_t *base)
8628e30d77arybchik{
8638e30d77arybchik	return (uint32_t)((uint8_t *)position - (uint8_t *)base);
8648e30d77arybchik}
8658e30d77arybchik
8668e30d77arybchik	__checkReturn		efx_rc_t
8678e30d77arybchikef10_nvram_buffer_find_item_start(
8688e30d77arybchik	__in_bcount(buffer_size)
8698e30d77arybchik				caddr_t bufferp,
8708e30d77arybchik	__in			size_t buffer_size,
8718e30d77arybchik	__out			uint32_t *startp)
8728e30d77arybchik{
87359d6b0carybchik	/* Read past partition header to find start address of the first key */
8748e30d77arybchik	tlv_cursor_t cursor;
8758e30d77arybchik	efx_rc_t rc;
8768e30d77arybchik
8778e30d77arybchik	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
8788e30d77arybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
8798e30d77arybchik			buffer_size)) != 0) {
8808e30d77arybchik		rc = EFAULT;
8818e30d77arybchik		goto fail1;
8828e30d77arybchik	}
8838e30d77arybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
8848e30d77arybchik		rc = EINVAL;
8858e30d77arybchik		goto fail2;
8868e30d77arybchik	}
8878e30d77arybchik
8888e30d77arybchik	if ((rc = tlv_advance(&cursor)) != 0) {
8898e30d77arybchik		rc = EINVAL;
8908e30d77arybchik		goto fail3;
8918e30d77arybchik	}
8928e30d77arybchik	*startp = byte_offset(cursor.current, cursor.block);
8938e30d77arybchik
8948e30d77arybchik	if ((rc = tlv_require_end(&cursor)) != 0)
8958e30d77arybchik		goto fail4;
8968e30d77arybchik
8978e30d77arybchik	return (0);
8988e30d77arybchik
8998e30d77arybchikfail4:
9008e30d77arybchik	EFSYS_PROBE(fail4);
9018e30d77arybchikfail3:
9028e30d77arybchik	EFSYS_PROBE(fail3);
9038e30d77arybchikfail2:
9048e30d77arybchik	EFSYS_PROBE(fail2);
9058e30d77arybchikfail1:
9068e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
9078e30d77arybchik
9088e30d77arybchik	return (rc);
9098e30d77arybchik}
9108e30d77arybchik
9118e30d77arybchik	__checkReturn		efx_rc_t
9128e30d77arybchikef10_nvram_buffer_find_end(
9138e30d77arybchik	__in_bcount(buffer_size)
9148e30d77arybchik				caddr_t bufferp,
9158e30d77arybchik	__in			size_t buffer_size,
9168e30d77arybchik	__in			uint32_t offset,
9178e30d77arybchik	__out			uint32_t *endp)
9188e30d77arybchik{
91959d6b0carybchik	/* Read to end of partition */
9208e30d77arybchik	tlv_cursor_t cursor;
9218e30d77arybchik	efx_rc_t rc;
922640eebaarybchik	uint32_t *segment_used;
9238e30d77arybchik
924a3be7d7arybchik	_NOTE(ARGUNUSED(offset))
925a3be7d7arybchik
9268e30d77arybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
9278e30d77arybchik			buffer_size)) != 0) {
9288e30d77arybchik		rc = EFAULT;
9298e30d77arybchik		goto fail1;
9308e30d77arybchik	}
9318e30d77arybchik
932640eebaarybchik	segment_used = cursor.block;
933640eebaarybchik
934640eebaarybchik	/*
935640eebaarybchik	 * Go through each segment and check that it has an end tag. If there
936640eebaarybchik	 * is no end tag then the previous segment was the last valid one,
937640eebaarybchik	 * so return the used space including that end tag.
938640eebaarybchik	 */
939640eebaarybchik	while (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
940640eebaarybchik		if (tlv_require_end(&cursor) != 0) {
941640eebaarybchik			if (segment_used == cursor.block) {
942640eebaarybchik				/*
943640eebaarybchik				 * First segment is corrupt, so there is
944640eebaarybchik				 * no valid data in partition.
945640eebaarybchik				 */
946640eebaarybchik				rc = EINVAL;
947640eebaarybchik				goto fail2;
948640eebaarybchik			}
949640eebaarybchik			break;
950640eebaarybchik		}
951640eebaarybchik		segment_used = cursor.end + 1;
9528e30d77arybchik
953640eebaarybchik		cursor.current = segment_used;
954640eebaarybchik	}
955640eebaarybchik	/* Return space used (including the END tag) */
956640eebaarybchik	*endp = (segment_used - cursor.block) * sizeof (uint32_t);
9578e30d77arybchik
9588e30d77arybchik	return (0);
9598e30d77arybchik
9608e30d77arybchikfail2:
9618e30d77arybchik	EFSYS_PROBE(fail2);
9628e30d77arybchikfail1:
9638e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
9648e30d77arybchik
9658e30d77arybchik	return (rc);
9668e30d77arybchik}
9678e30d77arybchik
9688e30d77arybchik	__checkReturn	__success(return != B_FALSE)	boolean_t
9698e30d77arybchikef10_nvram_buffer_find_item(
9708e30d77arybchik	__in_bcount(buffer_size)
9718e30d77arybchik				caddr_t bufferp,
9728e30d77arybchik	__in			size_t buffer_size,
9738e30d77arybchik	__in			uint32_t offset,
9748e30d77arybchik	__out			uint32_t *startp,
9758e30d77arybchik	__out			uint32_t *lengthp)
9768e30d77arybchik{
97759d6b0carybchik	/* Find TLV at offset and return key start and length */
9788e30d77arybchik	tlv_cursor_t cursor;
9798e30d77arybchik	uint8_t *key;
9808e30d77arybchik	uint32_t tag;
9818e30d77arybchik
9828e30d77arybchik	if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
9838e30d77arybchik			buffer_size, offset) != 0) {
9848e30d77arybchik		return (B_FALSE);
9858e30d77arybchik	}
9868e30d77arybchik
9878e30d77arybchik	while ((key = tlv_item(&cursor)) != NULL) {
9888e30d77arybchik		tag = tlv_tag(&cursor);
9898e30d77arybchik		if (tag == TLV_TAG_PARTITION_HEADER ||
9908e30d77arybchik		    tag == TLV_TAG_PARTITION_TRAILER) {
9918e30d77arybchik			if (tlv_advance(&cursor) != 0) {
9928e30d77arybchik				break;
9938e30d77arybchik			}
9948e30d77arybchik			continue;
9958e30d77arybchik		}
9968e30d77arybchik		*startp = byte_offset(cursor.current, cursor.block);
9978e30d77arybchik		*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
9988e30d77arybchik		    cursor.current);
9998e30d77arybchik		return (B_TRUE);
10008e30d77arybchik	}
10018e30d77arybchik
10028e30d77arybchik	return (B_FALSE);
10038e30d77arybchik}
10048e30d77arybchik
10058e30d77arybchik	__checkReturn		efx_rc_t
1006a07211barybchikef10_nvram_buffer_peek_item(
1007a07211barybchik	__in_bcount(buffer_size)
1008a07211barybchik				caddr_t bufferp,
1009a07211barybchik	__in			size_t buffer_size,
1010a07211barybchik	__in			uint32_t offset,
1011a07211barybchik	__out			uint32_t *tagp,
1012a07211barybchik	__out			uint32_t *lengthp,
1013a07211barybchik	__out			uint32_t *value_offsetp)
1014a07211barybchik{
1015a07211barybchik	efx_rc_t rc;
1016a07211barybchik	tlv_cursor_t cursor;
1017a07211barybchik	uint32_t tag;
1018a07211barybchik
1019a07211barybchik	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1020a07211barybchik			buffer_size, offset)) != 0) {
1021a07211barybchik		goto fail1;
1022a07211barybchik	}
1023a07211barybchik
1024a07211barybchik	tag = tlv_tag(&cursor);
1025a07211barybchik	*tagp = tag;
1026a07211barybchik	if (tag == TLV_TAG_END) {
1027a07211barybchik		/*
1028a07211barybchik		 * To allow stepping over the END tag, report the full tag
1029a07211barybchik		 * length and a zero length value.
1030a07211barybchik		 */
1031a07211barybchik		*lengthp = sizeof (tag);
1032a07211barybchik		*value_offsetp = sizeof (tag);
1033a07211barybchik	} else {
1034a07211barybchik		*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1035a07211barybchik			    cursor.current);
1036a07211barybchik		*value_offsetp = byte_offset((uint32_t *)tlv_value(&cursor),
1037a07211barybchik			    cursor.current);
1038a07211barybchik	}
1039a07211barybchik	return (0);
1040a07211barybchik
1041a07211barybchikfail1:
1042a07211barybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1043a07211barybchik
1044a07211barybchik	return (rc);
1045a07211barybchik}
1046a07211barybchik
1047a07211barybchik	__checkReturn		efx_rc_t
10488e30d77arybchikef10_nvram_buffer_get_item(
10498e30d77arybchik	__in_bcount(buffer_size)
10508e30d77arybchik				caddr_t bufferp,
10518e30d77arybchik	__in			size_t buffer_size,
10528e30d77arybchik	__in			uint32_t offset,
10538e30d77arybchik	__in			uint32_t length,
1054a07211barybchik	__out			uint32_t *tagp,
1055a07211barybchik	__out_bcount_part(value_max_size, *lengthp)
1056a07211barybchik				caddr_t valuep,
1057a07211barybchik	__in			size_t value_max_size,
10588e30d77arybchik	__out			uint32_t *lengthp)
10598e30d77arybchik{
10608e30d77arybchik	efx_rc_t rc;
10618e30d77arybchik	tlv_cursor_t cursor;
1062a07211barybchik	uint32_t value_length;
10638e30d77arybchik
1064a07211barybchik	if (buffer_size < (offset + length)) {
10658e30d77arybchik		rc = ENOSPC;
10668e30d77arybchik		goto fail1;
10678e30d77arybchik	}
10688e30d77arybchik
10698e30d77arybchik	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
10708e30d77arybchik			buffer_size, offset)) != 0) {
10718e30d77arybchik		goto fail2;
10728e30d77arybchik	}
10738e30d77arybchik
1074a07211barybchik	value_length = tlv_length(&cursor);
1075a07211barybchik	if (value_max_size < value_length) {
10768e30d77arybchik		rc = ENOSPC;
10778e30d77arybchik		goto fail3;
10788e30d77arybchik	}
1079a07211barybchik	memcpy(valuep, tlv_value(&cursor), value_length);
10808e30d77arybchik
1081a07211barybchik	*tagp = tlv_tag(&cursor);
1082a07211barybchik	*lengthp = value_length;
10838e30d77arybchik
10848e30d77arybchik	return (0);
10858e30d77arybchik
10868e30d77arybchikfail3:
10878e30d77arybchik	EFSYS_PROBE(fail3);
10888e30d77arybchikfail2:
10898e30d77arybchik	EFSYS_PROBE(fail2);
10908e30d77arybchikfail1:
10918e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
10928e30d77arybchik
10938e30d77arybchik	return (rc);
10948e30d77arybchik}
10958e30d77arybchik
10968e30d77arybchik	__checkReturn		efx_rc_t
10978e30d77arybchikef10_nvram_buffer_insert_item(
10988e30d77arybchik	__in_bcount(buffer_size)
10998e30d77arybchik				caddr_t bufferp,
11008e30d77arybchik	__in			size_t buffer_size,
11018e30d77arybchik	__in			uint32_t offset,
1102a07211barybchik	__in			uint32_t tag,
1103a07211barybchik	__in_bcount(length)	caddr_t valuep,
1104a07211barybchik	__in			uint32_t length,
1105a07211barybchik	__out			uint32_t *lengthp)
1106a07211barybchik{
1107a07211barybchik	efx_rc_t rc;
1108a07211barybchik	tlv_cursor_t cursor;
1109a07211barybchik
1110a07211barybchik	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1111a07211barybchik			buffer_size, offset)) != 0) {
1112a07211barybchik		goto fail1;
1113a07211barybchik	}
1114a07211barybchik
1115a07211barybchik	rc = tlv_insert(&cursor, tag, (uint8_t *)valuep, length);
1116a07211barybchik
1117a07211barybchik	if (rc != 0)
1118a07211barybchik		goto fail2;
1119a07211barybchik
1120a07211barybchik	*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1121a07211barybchik		    cursor.current);
1122a07211barybchik
1123a07211barybchik	return (0);
1124a07211barybchik
1125a07211barybchikfail2:
1126a07211barybchik	EFSYS_PROBE(fail2);
1127a07211barybchikfail1:
1128a07211barybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1129a07211barybchik
1130a07211barybchik	return (rc);
1131a07211barybchik}
1132a07211barybchik
1133a07211barybchik	__checkReturn		efx_rc_t
1134a07211barybchikef10_nvram_buffer_modify_item(
1135a07211barybchik	__in_bcount(buffer_size)
1136a07211barybchik				caddr_t bufferp,
1137a07211barybchik	__in			size_t buffer_size,
1138a07211barybchik	__in			uint32_t offset,
1139a07211barybchik	__in			uint32_t tag,
1140a07211barybchik	__in_bcount(length)	caddr_t valuep,
11418e30d77arybchik	__in			uint32_t length,
11428e30d77arybchik	__out			uint32_t *lengthp)
11438e30d77arybchik{
11448e30d77arybchik	efx_rc_t rc;
11458e30d77arybchik	tlv_cursor_t cursor;
11468e30d77arybchik
11478e30d77arybchik	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
11488e30d77arybchik			buffer_size, offset)) != 0) {
11498e30d77arybchik		goto fail1;
11508e30d77arybchik	}
11518e30d77arybchik
1152a07211barybchik	rc = tlv_modify(&cursor, tag, (uint8_t *)valuep, length);
11538e30d77arybchik
11548e30d77arybchik	if (rc != 0) {
11558e30d77arybchik		goto fail2;
11568e30d77arybchik	}
11578e30d77arybchik
11588e30d77arybchik	*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
11598e30d77arybchik		    cursor.current);
11608e30d77arybchik
11618e30d77arybchik	return (0);
11628e30d77arybchik
11638e30d77arybchikfail2:
11648e30d77arybchik	EFSYS_PROBE(fail2);
11658e30d77arybchikfail1:
11668e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
11678e30d77arybchik
11688e30d77arybchik	return (rc);
11698e30d77arybchik}
11708e30d77arybchik
1171a07211barybchik
11728e30d77arybchik	__checkReturn		efx_rc_t
11738e30d77arybchikef10_nvram_buffer_delete_item(
11748e30d77arybchik	__in_bcount(buffer_size)
11758e30d77arybchik				caddr_t bufferp,
11768e30d77arybchik	__in			size_t buffer_size,
11778e30d77arybchik	__in			uint32_t offset,
11788e30d77arybchik	__in			uint32_t length,
11798e30d77arybchik	__in			uint32_t end)
11808e30d77arybchik{
11818e30d77arybchik	efx_rc_t rc;
11828e30d77arybchik	tlv_cursor_t cursor;
11838e30d77arybchik
1184a3be7d7arybchik	_NOTE(ARGUNUSED(length, end))
1185a3be7d7arybchik
11868e30d77arybchik	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
11878e30d77arybchik			buffer_size, offset)) != 0) {
11888e30d77arybchik		goto fail1;
11898e30d77arybchik	}
11908e30d77arybchik
11918e30d77arybchik	if ((rc = tlv_delete(&cursor)) != 0)
11928e30d77arybchik		goto fail2;
11938e30d77arybchik
11948e30d77arybchik	return (0);
11958e30d77arybchik
11968e30d77arybchikfail2:
11978e30d77arybchik	EFSYS_PROBE(fail2);
11988e30d77arybchikfail1:
11998e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
12008e30d77arybchik
12018e30d77arybchik	return (rc);
12028e30d77arybchik}
12038e30d77arybchik
12048e30d77arybchik	__checkReturn		efx_rc_t
12058e30d77arybchikef10_nvram_buffer_finish(
12068e30d77arybchik	__in_bcount(buffer_size)
12078e30d77arybchik				caddr_t bufferp,
12088e30d77arybchik	__in			size_t buffer_size)
12098e30d77arybchik{
12108e30d77arybchik	efx_rc_t rc;
12118e30d77arybchik	tlv_cursor_t cursor;
12128e30d77arybchik
12138e30d77arybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
12148e30d77arybchik			buffer_size)) != 0) {
12158e30d77arybchik		rc = EFAULT;
12168e30d77arybchik		goto fail1;
12178e30d77arybchik	}
12188e30d77arybchik
12198e30d77arybchik	if ((rc = tlv_require_end(&cursor)) != 0)
12208e30d77arybchik		goto fail2;
12218e30d77arybchik
12228e30d77arybchik	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
12238e30d77arybchik		goto fail3;
12248e30d77arybchik
12258e30d77arybchik	return (0);
12268e30d77arybchik
12278e30d77arybchikfail3:
12288e30d77arybchik	EFSYS_PROBE(fail3);
12298e30d77arybchikfail2:
12308e30d77arybchik	EFSYS_PROBE(fail2);
12318e30d77arybchikfail1:
12328e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
12338e30d77arybchik
12348e30d77arybchik	return (rc);
12358e30d77arybchik}
12368e30d77arybchik
12378e30d77arybchik
12388e30d77arybchik
1239e7896dbarybchik/*
1240e7896dbarybchik * Read and validate a segment from a partition. A segment is a complete
1241e7896dbarybchik * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
1242e7896dbarybchik * be multiple segments in a partition, so seg_offset allows segments
1243e7896dbarybchik * beyond the first to be read.
1244e7896dbarybchik */
124516fdc5earybchikstatic	__checkReturn			efx_rc_t
1246c86fe87arybchikef10_nvram_read_tlv_segment(
1247e7896dbarybchik	__in				efx_nic_t *enp,
1248e7896dbarybchik	__in				uint32_t partn,
1249e7896dbarybchik	__in				size_t seg_offset,
1250e7896dbarybchik	__in_bcount(max_seg_size)	caddr_t seg_data,
1251e7896dbarybchik	__in				size_t max_seg_size)
1252220281carybchik{
1253220281carybchik	tlv_cursor_t cursor;
1254220281carybchik	struct tlv_partition_header *header;
1255220281carybchik	struct tlv_partition_trailer *trailer;
1256220281carybchik	size_t total_length;
1257220281carybchik	uint32_t cksum;
1258220281carybchik	int pos;
125916fdc5earybchik	efx_rc_t rc;
1260220281carybchik
1261c86fe87arybchik	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
1262220281carybchik
1263e7896dbarybchik	if ((seg_data == NULL) || (max_seg_size == 0)) {
1264220281carybchik		rc = EINVAL;
1265220281carybchik		goto fail1;
1266220281carybchik	}
1267220281carybchik
1268e7896dbarybchik	/* Read initial chunk of the segment, starting at offset */
1269e6feb41arybchik	if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
1270e6feb41arybchik		    EF10_NVRAM_CHUNK,
1271e6feb41arybchik		    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
1272220281carybchik		goto fail2;
1273220281carybchik	}
1274220281carybchik
1275e7896dbarybchik	/* A PARTITION_HEADER tag must be the first item at the given offset */
1276f85d27earybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1277e7896dbarybchik		    max_seg_size)) != 0) {
1278220281carybchik		rc = EFAULT;
1279220281carybchik		goto fail3;
1280220281carybchik	}
1281220281carybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1282220281carybchik		rc = EINVAL;
1283220281carybchik		goto fail4;
1284220281carybchik	}
1285220281carybchik	header = (struct tlv_partition_header *)tlv_item(&cursor);
1286220281carybchik
1287e7896dbarybchik	/* Check TLV segment length (includes the END tag) */
1288220281carybchik	total_length = __LE_TO_CPU_32(header->total_length);
1289e7896dbarybchik	if (total_length > max_seg_size) {
1290220281carybchik		rc = EFBIG;
1291220281carybchik		goto fail5;
1292220281carybchik	}
1293220281carybchik
1294e7896dbarybchik	/* Read the remaining segment content */
1295c86fe87arybchik	if (total_length > EF10_NVRAM_CHUNK) {
1296e6feb41arybchik		if ((rc = ef10_nvram_partn_read_mode(enp, partn,
1297c86fe87arybchik			    seg_offset + EF10_NVRAM_CHUNK,
1298c86fe87arybchik			    seg_data + EF10_NVRAM_CHUNK,
1299e6feb41arybchik			    total_length - EF10_NVRAM_CHUNK,
1300e6feb41arybchik			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
1301220281carybchik			goto fail6;
1302220281carybchik	}
1303220281carybchik
1304e7896dbarybchik	/* Check segment ends with PARTITION_TRAILER and END tags */
1305220281carybchik	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1306220281carybchik		rc = EINVAL;
1307220281carybchik		goto fail7;
1308220281carybchik	}
1309220281carybchik	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1310220281carybchik
1311220281carybchik	if ((rc = tlv_advance(&cursor)) != 0) {
1312220281carybchik		rc = EINVAL;
1313220281carybchik		goto fail8;
1314220281carybchik	}
1315220281carybchik	if (tlv_tag(&cursor) != TLV_TAG_END) {
1316220281carybchik		rc = EINVAL;
1317220281carybchik		goto fail9;
1318220281carybchik	}
1319220281carybchik
1320e7896dbarybchik	/* Check data read from segment is consistent */
1321220281carybchik	if (trailer->generation != header->generation) {
1322220281carybchik		/*
1323220281carybchik		 * The partition data may have been modified between successive
1324220281carybchik		 * MCDI NVRAM_READ requests by the MC or another PCI function.
1325220281carybchik		 *
1326220281carybchik		 * The caller must retry to obtain consistent partition data.
1327220281carybchik		 */
1328220281carybchik		rc = EAGAIN;
1329220281carybchik		goto fail10;
1330220281carybchik	}
1331220281carybchik
1332e7896dbarybchik	/* Verify segment checksum */
1333220281carybchik	cksum = 0;
1334220281carybchik	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
1335e7896dbarybchik		cksum += *((uint32_t *)(seg_data + pos));
1336220281carybchik	}
1337220281carybchik	if (cksum != 0) {
1338220281carybchik		rc = EINVAL;
1339220281carybchik		goto fail11;
1340220281carybchik	}
1341220281carybchik
1342220281carybchik	return (0);
1343220281carybchik
1344220281carybchikfail11:
1345220281carybchik	EFSYS_PROBE(fail11);
1346220281carybchikfail10:
1347220281carybchik	EFSYS_PROBE(fail10);
1348220281carybchikfail9:
1349220281carybchik	EFSYS_PROBE(fail9);
1350220281carybchikfail8:
1351220281carybchik	EFSYS_PROBE(fail8);
1352220281carybchikfail7:
1353220281carybchik	EFSYS_PROBE(fail7);
1354220281carybchikfail6:
1355220281carybchik	EFSYS_PROBE(fail6);
1356220281carybchikfail5:
1357220281carybchik	EFSYS_PROBE(fail5);
1358220281carybchikfail4:
1359220281carybchik	EFSYS_PROBE(fail4);
1360220281carybchikfail3:
1361220281carybchik	EFSYS_PROBE(fail3);
1362220281carybchikfail2:
1363220281carybchik	EFSYS_PROBE(fail2);
1364220281carybchikfail1:
136516fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1366220281carybchik
1367220281carybchik	return (rc);
1368220281carybchik}
1369220281carybchik
1370220281carybchik/*
1371220281carybchik * Read a single TLV item from a host memory
1372e7896dbarybchik * buffer containing a TLV formatted segment.
1373220281carybchik */
137416fdc5earybchik	__checkReturn		efx_rc_t
1375c86fe87arybchikef10_nvram_buf_read_tlv(
1376220281carybchik	__in				efx_nic_t *enp,
1377e7896dbarybchik	__in_bcount(max_seg_size)	caddr_t seg_data,
1378e7896dbarybchik	__in				size_t max_seg_size,
1379220281carybchik	__in				uint32_t tag,
1380220281carybchik	__deref_out_bcount_opt(*sizep)	caddr_t *datap,
1381220281carybchik	__out				size_t *sizep)
1382220281carybchik{
1383220281carybchik	tlv_cursor_t cursor;
1384220281carybchik	caddr_t data;
1385220281carybchik	size_t length;
1386220281carybchik	caddr_t value;
138716fdc5earybchik	efx_rc_t rc;
1388220281carybchik
1389b44a54farybchik	_NOTE(ARGUNUSED(enp))
1390b44a54farybchik
1391e7896dbarybchik	if ((seg_data == NULL) || (max_seg_size == 0)) {
1392220281carybchik		rc = EINVAL;
1393220281carybchik		goto fail1;
1394220281carybchik	}
1395220281carybchik
1396e7896dbarybchik	/* Find requested TLV tag in segment data */
1397f85d27earybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1398e7896dbarybchik		    max_seg_size)) != 0) {
1399220281carybchik		rc = EFAULT;
1400220281carybchik		goto fail2;
1401220281carybchik	}
1402220281carybchik	if ((rc = tlv_find(&cursor, tag)) != 0) {
1403220281carybchik		rc = ENOENT;
1404220281carybchik		goto fail3;
1405220281carybchik	}
1406f85d27earybchik	value = (caddr_t)tlv_value(&cursor);
1407220281carybchik	length = tlv_length(&cursor);
1408220281carybchik
1409220281carybchik	if (length == 0)
1410220281carybchik		data = NULL;
1411220281carybchik	else {
1412220281carybchik		/* Copy out data from TLV item */
1413220281carybchik		EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
1414220281carybchik		if (data == NULL) {
1415220281carybchik			rc = ENOMEM;
1416220281carybchik			goto fail4;
1417220281carybchik		}
1418220281carybchik		memcpy(data, value, length);
1419220281carybchik	}
1420220281carybchik
1421220281carybchik	*datap = data;
1422220281carybchik	*sizep = length;
1423220281carybchik
1424220281carybchik	return (0);
1425220281carybchik
1426220281carybchikfail4:
1427220281carybchik	EFSYS_PROBE(fail4);
1428220281carybchikfail3:
1429220281carybchik	EFSYS_PROBE(fail3);
1430220281carybchikfail2:
1431220281carybchik	EFSYS_PROBE(fail2);
1432220281carybchikfail1:
143316fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1434220281carybchik
1435220281carybchik	return (rc);
1436220281carybchik}
1437220281carybchik
1438e7896dbarybchik/* Read a single TLV item from the first segment in a TLV formatted partition */
143916fdc5earybchik	__checkReturn		efx_rc_t
1440c86fe87arybchikef10_nvram_partn_read_tlv(
1441e7896dbarybchik	__in					efx_nic_t *enp,
1442e7896dbarybchik	__in					uint32_t partn,
1443e7896dbarybchik	__in					uint32_t tag,
1444e7896dbarybchik	__deref_out_bcount_opt(*seg_sizep)	caddr_t *seg_datap,
1445e7896dbarybchik	__out					size_t *seg_sizep)
1446220281carybchik{
1447e7896dbarybchik	caddr_t seg_data = NULL;
1448220281carybchik	size_t partn_size = 0;
1449220281carybchik	size_t length;
1450220281carybchik	caddr_t data;
1451220281carybchik	int retry;
145216fdc5earybchik	efx_rc_t rc;
1453220281carybchik
1454220281carybchik	/* Allocate sufficient memory for the entire partition */
1455c86fe87arybchik	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1456220281carybchik		goto fail1;
1457220281carybchik
1458220281carybchik	if (partn_size == 0) {
1459220281carybchik		rc = ENOENT;
1460220281carybchik		goto fail2;
1461220281carybchik	}
1462220281carybchik
1463e7896dbarybchik	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
1464e7896dbarybchik	if (seg_data == NULL) {
1465220281carybchik		rc = ENOMEM;
1466220281carybchik		goto fail3;
1467220281carybchik	}
1468220281carybchik
1469220281carybchik	/*
1470e7896dbarybchik	 * Read the first segment in a TLV partition. Retry until consistent
1471e7896dbarybchik	 * segment contents are returned. Inconsistent data may be read if:
1472e7896dbarybchik	 *  a) the segment contents are invalid
1473220281carybchik	 *  b) the MC has rebooted while we were reading the partition
1474220281carybchik	 *  c) the partition has been modified while we were reading it
1475220281carybchik	 * Limit retry attempts to ensure forward progress.
1476220281carybchik	 */
1477220281carybchik	retry = 10;
1478220281carybchik	do {
1479782e9aaarybchik		if ((rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
1480782e9aaarybchik		    seg_data, partn_size)) != 0)
1481782e9aaarybchik			--retry;
1482782e9aaarybchik	} while ((rc == EAGAIN) && (retry > 0));
1483220281carybchik
1484220281carybchik	if (rc != 0) {
1485e7896dbarybchik		/* Failed to obtain consistent segment data */
1486782e9aaarybchik		if (rc == EAGAIN)
1487782e9aaarybchik			rc = EIO;
1488782e9aaarybchik
1489220281carybchik		goto fail4;
1490220281carybchik	}
1491220281carybchik
1492c86fe87arybchik	if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
1493220281carybchik		    tag, &data, &length)) != 0)
1494220281carybchik		goto fail5;
1495220281carybchik
1496e7896dbarybchik	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1497220281carybchik
1498e7896dbarybchik	*seg_datap = data;
1499e7896dbarybchik	*seg_sizep = length;
1500220281carybchik
1501220281carybchik	return (0);
1502220281carybchik
1503220281carybchikfail5:
1504220281carybchik	EFSYS_PROBE(fail5);
1505220281carybchikfail4:
1506220281carybchik	EFSYS_PROBE(fail4);
1507220281carybchik
1508e7896dbarybchik	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1509e7896dbarybchikfail3:
1510e7896dbarybchik	EFSYS_PROBE(fail3);
1511e7896dbarybchikfail2:
1512e7896dbarybchik	EFSYS_PROBE(fail2);
1513e7896dbarybchikfail1:
151416fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1515e7896dbarybchik
1516e7896dbarybchik	return (rc);
1517e7896dbarybchik}
1518e7896dbarybchik
1519e7896dbarybchik/* Compute the size of a segment. */
152016fdc5earybchik	static	__checkReturn	efx_rc_t
1521c86fe87arybchikef10_nvram_buf_segment_size(
1522e7896dbarybchik	__in			caddr_t seg_data,
1523e7896dbarybchik	__in			size_t max_seg_size,
1524e7896dbarybchik	__out			size_t *seg_sizep)
1525e7896dbarybchik{
152616fdc5earybchik	efx_rc_t rc;
1527e7896dbarybchik	tlv_cursor_t cursor;
1528e7896dbarybchik	struct tlv_partition_header *header;
1529e7896dbarybchik	uint32_t cksum;
1530e7896dbarybchik	int pos;
1531e7896dbarybchik	uint32_t *end_tag_position;
1532e7896dbarybchik	uint32_t segment_length;
1533e7896dbarybchik
1534e7896dbarybchik	/* A PARTITION_HEADER tag must be the first item at the given offset */
1535f85d27earybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1536e7896dbarybchik		    max_seg_size)) != 0) {
1537e7896dbarybchik		rc = EFAULT;
1538e7896dbarybchik		goto fail1;
1539e7896dbarybchik	}
1540e7896dbarybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1541e7896dbarybchik		rc = EINVAL;
1542e7896dbarybchik		goto fail2;
1543e7896dbarybchik	}
1544e7896dbarybchik	header = (struct tlv_partition_header *)tlv_item(&cursor);
1545e7896dbarybchik
1546e7896dbarybchik	/* Check TLV segment length (includes the END tag) */
1547e7896dbarybchik	*seg_sizep = __LE_TO_CPU_32(header->total_length);
1548e7896dbarybchik	if (*seg_sizep > max_seg_size) {
1549e7896dbarybchik		rc = EFBIG;
1550e7896dbarybchik		goto fail3;
1551e7896dbarybchik	}
1552e7896dbarybchik
1553e7896dbarybchik	/* Check segment ends with PARTITION_TRAILER and END tags */
1554e7896dbarybchik	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1555e7896dbarybchik		rc = EINVAL;
1556e7896dbarybchik		goto fail4;
1557e7896dbarybchik	}
1558e7896dbarybchik
1559e7896dbarybchik	if ((rc = tlv_advance(&cursor)) != 0) {
1560e7896dbarybchik		rc = EINVAL;
1561e7896dbarybchik		goto fail5;
1562e7896dbarybchik	}
1563e7896dbarybchik	if (tlv_tag(&cursor) != TLV_TAG_END) {
1564e7896dbarybchik		rc = EINVAL;
1565e7896dbarybchik		goto fail6;
1566e7896dbarybchik	}
1567e7896dbarybchik	end_tag_position = cursor.current;
1568e7896dbarybchik
1569e7896dbarybchik	/* Verify segment checksum */
1570e7896dbarybchik	cksum = 0;
1571e7896dbarybchik	for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
1572e7896dbarybchik		cksum += *((uint32_t *)(seg_data + pos));
1573e7896dbarybchik	}
1574e7896dbarybchik	if (cksum != 0) {
1575e7896dbarybchik		rc = EINVAL;
1576e7896dbarybchik		goto fail7;
1577e7896dbarybchik	}
1578e7896dbarybchik
1579e7896dbarybchik	/*
1580e7896dbarybchik	 * Calculate total length from HEADER to END tags and compare to
1581e7896dbarybchik	 * max_seg_size and the total_length field in the HEADER tag.
1582e7896dbarybchik	 */
1583e7896dbarybchik	segment_length = tlv_block_length_used(&cursor);
1584e7896dbarybchik
1585e7896dbarybchik	if (segment_length > max_seg_size) {
1586e7896dbarybchik		rc = EINVAL;
1587e7896dbarybchik		goto fail8;
1588e7896dbarybchik	}
1589e7896dbarybchik
1590e7896dbarybchik	if (segment_length != *seg_sizep) {
1591e7896dbarybchik		rc = EINVAL;
1592e7896dbarybchik		goto fail9;
1593e7896dbarybchik	}
1594e7896dbarybchik
1595e7896dbarybchik	/* Skip over the first HEADER tag. */
1596e7896dbarybchik	rc = tlv_rewind(&cursor);
1597e7896dbarybchik	rc = tlv_advance(&cursor);
1598e7896dbarybchik
1599e7896dbarybchik	while (rc == 0) {
1600e7896dbarybchik		if (tlv_tag(&cursor) == TLV_TAG_END) {
1601e7896dbarybchik			/* Check that the END tag is the one found earlier. */
1602e7896dbarybchik			if (cursor.current != end_tag_position)
1603e7896dbarybchik				goto fail10;
1604e7896dbarybchik			break;
1605e7896dbarybchik		}
1606e7896dbarybchik		/* Check for duplicate HEADER tags before the END tag. */
1607e7896dbarybchik		if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
1608e7896dbarybchik			rc = EINVAL;
1609e7896dbarybchik			goto fail11;
1610e7896dbarybchik		}
1611e7896dbarybchik
1612e7896dbarybchik		rc = tlv_advance(&cursor);
1613e7896dbarybchik	}
1614e7896dbarybchik	if (rc != 0)
1615e7896dbarybchik		goto fail12;
1616e7896dbarybchik
1617e7896dbarybchik	return (0);
1618e7896dbarybchik
1619e7896dbarybchikfail12:
1620e7896dbarybchik	EFSYS_PROBE(fail12);
1621e7896dbarybchikfail11:
1622e7896dbarybchik	EFSYS_PROBE(fail11);
1623e7896dbarybchikfail10:
1624e7896dbarybchik	EFSYS_PROBE(fail10);
1625e7896dbarybchikfail9:
1626e7896dbarybchik	EFSYS_PROBE(fail9);
1627e7896dbarybchikfail8:
1628e7896dbarybchik	EFSYS_PROBE(fail8);
1629e7896dbarybchikfail7:
1630e7896dbarybchik	EFSYS_PROBE(fail7);
1631e7896dbarybchikfail6:
1632e7896dbarybchik	EFSYS_PROBE(fail6);
1633e7896dbarybchikfail5:
1634e7896dbarybchik	EFSYS_PROBE(fail5);
1635e7896dbarybchikfail4:
1636e7896dbarybchik	EFSYS_PROBE(fail4);
1637220281carybchikfail3:
1638220281carybchik	EFSYS_PROBE(fail3);
1639220281carybchikfail2:
1640220281carybchik	EFSYS_PROBE(fail2);
1641220281carybchikfail1:
164216fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1643220281carybchik
1644220281carybchik	return (rc);
1645220281carybchik}
1646220281carybchik
1647220281carybchik/*
1648220281carybchik * Add or update a single TLV item in a host memory buffer containing a TLV
1649e7896dbarybchik * formatted segment. Historically partitions consisted of only one segment.
1650220281carybchik */
165116fdc5earybchik	__checkReturn			efx_rc_t
1652c86fe87arybchikef10_nvram_buf_write_tlv(
1653e7896dbarybchik	__inout_bcount(max_seg_size)	caddr_t seg_data,
1654e7896dbarybchik	__in				size_t max_seg_size,
1655220281carybchik	__in				uint32_t tag,
1656220281carybchik	__in_bcount(tag_size)		caddr_t tag_data,
1657220281carybchik	__in				size_t tag_size,
1658220281carybchik	__out				size_t *total_lengthp)
1659220281carybchik{
1660220281carybchik	tlv_cursor_t cursor;
1661220281carybchik	struct tlv_partition_header *header;
1662220281carybchik	struct tlv_partition_trailer *trailer;
1663220281carybchik	uint32_t generation;
1664220281carybchik	uint32_t cksum;
1665220281carybchik	int pos;
166616fdc5earybchik	efx_rc_t rc;
1667220281carybchik
1668e7896dbarybchik	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
1669f85d27earybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1670e7896dbarybchik			max_seg_size)) != 0) {
1671220281carybchik		rc = EFAULT;
1672220281carybchik		goto fail1;
1673220281carybchik	}
1674220281carybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1675220281carybchik		rc = EINVAL;
1676220281carybchik		goto fail2;
1677220281carybchik	}
1678220281carybchik	header = (struct tlv_partition_header *)tlv_item(&cursor);
1679220281carybchik
1680220281carybchik	/* Update the TLV chain to contain the new data */
1681220281carybchik	if ((rc = tlv_find(&cursor, tag)) == 0) {
1682220281carybchik		/* Modify existing TLV item */
1683220281carybchik		if ((rc = tlv_modify(&cursor, tag,
1684f85d27earybchik			    (uint8_t *)tag_data, tag_size)) != 0)
1685220281carybchik			goto fail3;
1686220281carybchik	} else {
1687220281carybchik		/* Insert a new TLV item before the PARTITION_TRAILER */
1688220281carybchik		rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1689220281carybchik		if (rc != 0) {
1690220281carybchik			rc = EINVAL;
1691220281carybchik			goto fail4;
1692220281carybchik		}
1693220281carybchik		if ((rc = tlv_insert(&cursor, tag,
1694f85d27earybchik			    (uint8_t *)tag_data, tag_size)) != 0) {
1695220281carybchik			rc = EINVAL;
1696220281carybchik			goto fail5;
1697220281carybchik		}
1698220281carybchik	}
1699220281carybchik
1700220281carybchik	/* Find the trailer tag */
1701220281carybchik	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1702220281carybchik		rc = EINVAL;
1703220281carybchik		goto fail6;
1704220281carybchik	}
1705220281carybchik	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1706220281carybchik
1707220281carybchik	/* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1708220281carybchik	*total_lengthp = tlv_block_length_used(&cursor);
1709e7896dbarybchik	if (*total_lengthp > max_seg_size) {
1710e7896dbarybchik		rc = ENOSPC;
1711e7896dbarybchik		goto fail7;
1712e7896dbarybchik	}
1713220281carybchik	generation = __LE_TO_CPU_32(header->generation) + 1;
1714220281carybchik
1715220281carybchik	header->total_length	= __CPU_TO_LE_32(*total_lengthp);
1716220281carybchik	header->generation	= __CPU_TO_LE_32(generation);
1717220281carybchik	trailer->generation	= __CPU_TO_LE_32(generation);
1718220281carybchik
1719220281carybchik	/* Recompute PARTITION_TRAILER checksum */
1720220281carybchik	trailer->checksum = 0;
1721220281carybchik	cksum = 0;
1722220281carybchik	for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1723e7896dbarybchik		cksum += *((uint32_t *)(seg_data + pos));
1724220281carybchik	}
1725220281carybchik	trailer->checksum = ~cksum + 1;
1726220281carybchik
1727220281carybchik	return (0);
1728220281carybchik
1729e7896dbarybchikfail7:
1730e7896dbarybchik	EFSYS_PROBE(fail7);
1731220281carybchikfail6:
1732220281carybchik	EFSYS_PROBE(fail6);
1733220281carybchikfail5:
1734220281carybchik	EFSYS_PROBE(fail5);
1735220281carybchikfail4:
1736220281carybchik	EFSYS_PROBE(fail4);
1737220281carybchikfail3:
1738220281carybchik	EFSYS_PROBE(fail3);
1739220281carybchikfail2:
1740220281carybchik	EFSYS_PROBE(fail2);
1741220281carybchikfail1:
174216fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1743220281carybchik
1744220281carybchik	return (rc);
1745220281carybchik}
1746220281carybchik
1747e7896dbarybchik/*
1748e7896dbarybchik * Add or update a single TLV item in the first segment of a TLV formatted
1749e7896dbarybchik * dynamic config partition. The first segment is the current active
1750e7896dbarybchik * configuration.
1751e7896dbarybchik */
175216fdc5earybchik	__checkReturn		efx_rc_t
1753c86fe87arybchikef10_nvram_partn_write_tlv(
1754220281carybchik	__in			efx_nic_t *enp,
1755220281carybchik	__in			uint32_t partn,
1756220281carybchik	__in			uint32_t tag,
1757220281carybchik	__in_bcount(size)	caddr_t data,
1758220281carybchik	__in			size_t size)
1759220281carybchik{
1760c86fe87arybchik	return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1761e7896dbarybchik	    size, B_FALSE);
1762e7896dbarybchik}
1763e7896dbarybchik
1764e7896dbarybchik/*
1765e7896dbarybchik * Read a segment from nvram at the given offset into a buffer (segment_data)
1766e7896dbarybchik * and optionally write a new tag to it.
1767e7896dbarybchik */
17686b7c88earybchikstatic	__checkReturn		efx_rc_t
1769c86fe87arybchikef10_nvram_segment_write_tlv(
1770e7896dbarybchik	__in			efx_nic_t *enp,
1771e7896dbarybchik	__in			uint32_t partn,
1772e7896dbarybchik	__in			uint32_t tag,
1773e7896dbarybchik	__in_bcount(size)	caddr_t data,
1774e7896dbarybchik	__in			size_t size,
1775e7896dbarybchik	__inout			caddr_t *seg_datap,
1776e7896dbarybchik	__inout			size_t *partn_offsetp,
1777e7896dbarybchik	__inout			size_t *src_remain_lenp,
1778e7896dbarybchik	__inout			size_t *dest_remain_lenp,
1779e7896dbarybchik	__in			boolean_t write)
1780e7896dbarybchik{
178116fdc5earybchik	efx_rc_t rc;
178267b9a0farybchik	efx_rc_t status;
1783e7896dbarybchik	size_t original_segment_size;
1784e7896dbarybchik	size_t modified_segment_size;
1785e7896dbarybchik
1786e7896dbarybchik	/*
1787e7896dbarybchik	 * Read the segment from NVRAM into the segment_data buffer and validate
1788e7896dbarybchik	 * it, returning if it does not validate. This is not a failure unless
1789e7896dbarybchik	 * this is the first segment in a partition. In this case the caller
1790eed4bd2pfg	 * must propagate the error.
1791e7896dbarybchik	 */
1792c86fe87arybchik	status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1793e7896dbarybchik	    *seg_datap, *src_remain_lenp);
17946b7c88earybchik	if (status != 0) {
17956b7c88earybchik		rc = EINVAL;
17966b7c88earybchik		goto fail1;
17976b7c88earybchik	}
1798e7896dbarybchik
1799c86fe87arybchik	status = ef10_nvram_buf_segment_size(*seg_datap,
1800e7896dbarybchik	    *src_remain_lenp, &original_segment_size);
18016b7c88earybchik	if (status != 0) {
18026b7c88earybchik		rc = EINVAL;
18036b7c88earybchik		goto fail2;
18046b7c88earybchik	}
1805e7896dbarybchik
1806e7896dbarybchik	if (write) {
1807e7896dbarybchik		/* Update the contents of the segment in the buffer */
1808c86fe87arybchik		if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1809e7896dbarybchik			*dest_remain_lenp, tag, data, size,
18106b7c88earybchik			&modified_segment_size)) != 0) {
18116b7c88earybchik			goto fail3;
18126b7c88earybchik		}
1813e7896dbarybchik		*dest_remain_lenp -= modified_segment_size;
1814e7896dbarybchik		*seg_datap += modified_segment_size;
1815e7896dbarybchik	} else {
1816e7896dbarybchik		/*
1817e7896dbarybchik		 * We won't modify this segment, but still need to update the
1818e7896dbarybchik		 * remaining lengths and pointers.
1819e7896dbarybchik		 */
1820e7896dbarybchik		*dest_remain_lenp -= original_segment_size;
1821e7896dbarybchik		*seg_datap += original_segment_size;
1822e7896dbarybchik	}
1823e7896dbarybchik
1824e7896dbarybchik	*partn_offsetp += original_segment_size;
1825e7896dbarybchik	*src_remain_lenp -= original_segment_size;
1826e7896dbarybchik
1827e7896dbarybchik	return (0);
1828e7896dbarybchik
18296b7c88earybchikfail3:
18306b7c88earybchik	EFSYS_PROBE(fail3);
18316b7c88earybchikfail2:
18326b7c88earybchik	EFSYS_PROBE(fail2);
1833e7896dbarybchikfail1:
183416fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1835e7896dbarybchik
1836e7896dbarybchik	return (rc);
1837e7896dbarybchik}
1838e7896dbarybchik
1839e7896dbarybchik/*
1840e7896dbarybchik * Add or update a single TLV item in either the first segment or in all
1841e7896dbarybchik * segments in a TLV formatted dynamic config partition. Dynamic config
1842e7896dbarybchik * partitions on boards that support RFID are divided into a number of segments,
1843e7896dbarybchik * each formatted like a partition, with header, trailer and end tags. The first
1844e7896dbarybchik * segment is the current active configuration.
1845e7896dbarybchik *
1846e7896dbarybchik * The segments are initialised by manftest and each contain a different
1847e7896dbarybchik * configuration e.g. firmware variant. The firmware can be instructed
1848e7896dbarybchik * via RFID to copy a segment to replace the first segment, hence changing the
1849e7896dbarybchik * active configuration.  This allows ops to change the configuration of a board
1850e7896dbarybchik * prior to shipment using RFID.
1851e7896dbarybchik *
1852e7896dbarybchik * Changes to the dynamic config may need to be written to all segments (e.g.
1853e7896dbarybchik * firmware versions) or just the first segment (changes to the active
1854e7896dbarybchik * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1855e7896dbarybchik * If only the first segment is written the code still needs to be aware of the
1856e7896dbarybchik * possible presence of subsequent segments as writing to a segment may cause
1857e7896dbarybchik * its size to increase, which would overwrite the subsequent segments and
1858e7896dbarybchik * invalidate them.
1859e7896dbarybchik */
186016fdc5earybchik	__checkReturn		efx_rc_t
1861c86fe87arybchikef10_nvram_partn_write_segment_tlv(
1862e7896dbarybchik	__in			efx_nic_t *enp,
1863e7896dbarybchik	__in			uint32_t partn,
1864e7896dbarybchik	__in			uint32_t tag,
1865e7896dbarybchik	__in_bcount(size)	caddr_t data,
1866e7896dbarybchik	__in			size_t size,
1867e7896dbarybchik	__in			boolean_t all_segments)
1868e7896dbarybchik{
1869e7896dbarybchik	size_t partn_size = 0;
1870220281carybchik	caddr_t partn_data;
1871e7896dbarybchik	size_t total_length = 0;
187216fdc5earybchik	efx_rc_t rc;
1873e7896dbarybchik	size_t current_offset = 0;
1874e7896dbarybchik	size_t remaining_original_length;
1875e7896dbarybchik	size_t remaining_modified_length;
1876e7896dbarybchik	caddr_t segment_data;
1877220281carybchik
1878220281carybchik	EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1879220281carybchik
1880220281carybchik	/* Allocate sufficient memory for the entire partition */
1881c86fe87arybchik	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1882220281carybchik		goto fail1;
1883220281carybchik
1884220281carybchik	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1885220281carybchik	if (partn_data == NULL) {
1886220281carybchik		rc = ENOMEM;
1887220281carybchik		goto fail2;
1888220281carybchik	}
1889220281carybchik
1890e7896dbarybchik	remaining_original_length = partn_size;
1891e7896dbarybchik	remaining_modified_length = partn_size;
1892e7896dbarybchik	segment_data = partn_data;
1893e7896dbarybchik
1894220281carybchik	/* Lock the partition */
1895c86fe87arybchik	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1896220281carybchik		goto fail3;
1897220281carybchik
1898e7896dbarybchik	/* Iterate over each (potential) segment to update it. */
1899e7896dbarybchik	do {
1900e7896dbarybchik		boolean_t write = all_segments || current_offset == 0;
1901220281carybchik
1902c86fe87arybchik		rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1903e7896dbarybchik		    &segment_data, &current_offset, &remaining_original_length,
1904e7896dbarybchik		    &remaining_modified_length, write);
1905e7896dbarybchik		if (rc != 0) {
1906e7896dbarybchik			if (current_offset == 0) {
1907e7896dbarybchik				/*
1908e7896dbarybchik				 * If no data has been read then the first
1909e7896dbarybchik				 * segment is invalid, which is an error.
1910e7896dbarybchik				 */
1911e7896dbarybchik				goto fail4;
1912e7896dbarybchik			}
1913e7896dbarybchik			break;
1914e7896dbarybchik		}
1915e7896dbarybchik	} while (current_offset < partn_size);
1916e7896dbarybchik
1917e7896dbarybchik	total_length = segment_data - partn_data;
1918e7896dbarybchik
1919e7896dbarybchik	/*
1920e7896dbarybchik	 * We've run out of space.  This should actually be dealt with by
1921c86fe87arybchik	 * ef10_nvram_buf_write_tlv returning ENOSPC.
1922e7896dbarybchik	 */
1923e7896dbarybchik	if (total_length > partn_size) {
1924e7896dbarybchik		rc = ENOSPC;
1925220281carybchik		goto fail5;
1926e7896dbarybchik	}
1927220281carybchik
1928e7896dbarybchik	/* Erase the whole partition in NVRAM */
1929c86fe87arybchik	if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1930220281carybchik		goto fail6;
1931220281carybchik
1932e7896dbarybchik	/* Write new partition contents from the buffer to NVRAM */
1933c86fe87arybchik	if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1934220281carybchik		    total_length)) != 0)
1935220281carybchik		goto fail7;
1936220281carybchik
1937220281carybchik	/* Unlock the partition */
1938002c0e7arybchik	(void) ef10_nvram_partn_unlock(enp, partn, NULL);
1939220281carybchik
1940220281carybchik	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1941220281carybchik
1942220281carybchik	return (0);
1943220281carybchik
1944220281carybchikfail7:
1945220281carybchik	EFSYS_PROBE(fail7);
1946220281carybchikfail6:
1947220281carybchik	EFSYS_PROBE(fail6);
1948220281carybchikfail5:
1949220281carybchik	EFSYS_PROBE(fail5);
1950220281carybchikfail4:
1951220281carybchik	EFSYS_PROBE(fail4);
1952220281carybchik
1953002c0e7arybchik	(void) ef10_nvram_partn_unlock(enp, partn, NULL);
1954220281carybchikfail3:
1955220281carybchik	EFSYS_PROBE(fail3);
1956220281carybchik
1957220281carybchik	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1958220281carybchikfail2:
1959220281carybchik	EFSYS_PROBE(fail2);
1960220281carybchikfail1:
196116fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1962220281carybchik
1963220281carybchik	return (rc);
1964220281carybchik}
1965220281carybchik
1966e7896dbarybchik/*
1967e7896dbarybchik * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1968e7896dbarybchik * not the data used by the segments in the partition.
1969e7896dbarybchik */
197016fdc5earybchik	__checkReturn		efx_rc_t
1971c86fe87arybchikef10_nvram_partn_size(
1972220281carybchik	__in			efx_nic_t *enp,
1973ae48f39arybchik	__in			uint32_t partn,
1974220281carybchik	__out			size_t *sizep)
1975220281carybchik{
197616fdc5earybchik	efx_rc_t rc;
1977220281carybchik
1978db96dd5arybchik	if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1979db96dd5arybchik	    NULL, NULL, NULL)) != 0)
1980220281carybchik		goto fail1;
1981220281carybchik
1982220281carybchik	return (0);
1983220281carybchik
1984220281carybchikfail1:
198516fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1986220281carybchik
1987220281carybchik	return (rc);
1988220281carybchik}
1989220281carybchik
199016fdc5earybchik	__checkReturn		efx_rc_t
1991c86fe87arybchikef10_nvram_partn_lock(
1992220281carybchik	__in			efx_nic_t *enp,
1993ae48f39arybchik	__in			uint32_t partn)
1994220281carybchik{
199516fdc5earybchik	efx_rc_t rc;
1996220281carybchik
1997220281carybchik	if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1998220281carybchik		goto fail1;
1999220281carybchik
2000220281carybchik	return (0);
2001220281carybchik
2002220281carybchikfail1:
200316fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2004220281carybchik
2005220281carybchik	return (rc);
2006220281carybchik}
2007220281carybchik
200816fdc5earybchik	__checkReturn		efx_rc_t
2009e6feb41arybchikef10_nvram_partn_read_mode(
2010220281carybchik	__in			efx_nic_t *enp,
2011ae48f39arybchik	__in			uint32_t partn,
2012220281carybchik	__in			unsigned int offset,
2013220281carybchik	__out_bcount(size)	caddr_t data,
2014e6feb41arybchik	__in			size_t size,
2015e6feb41arybchik	__in			uint32_t mode)
2016220281carybchik{
2017220281carybchik	size_t chunk;
201816fdc5earybchik	efx_rc_t rc;
2019220281carybchik
2020220281carybchik	while (size > 0) {
2021c86fe87arybchik		chunk = MIN(size, EF10_NVRAM_CHUNK);
2022220281carybchik
2023220281carybchik		if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
2024e6feb41arybchik			    data, chunk, mode)) != 0) {
2025220281carybchik			goto fail1;
2026220281carybchik		}
2027220281carybchik
2028220281carybchik		size -= chunk;
2029220281carybchik		data += chunk;
2030220281carybchik		offset += chunk;
2031220281carybchik	}
2032220281carybchik
2033220281carybchik	return (0);
2034220281carybchik
2035220281carybchikfail1:
203616fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2037220281carybchik
2038220281carybchik	return (rc);
2039220281carybchik}
2040220281carybchik
204116fdc5earybchik	__checkReturn		efx_rc_t
2042e6feb41arybchikef10_nvram_partn_read(
2043e6feb41arybchik	__in			efx_nic_t *enp,
2044e6feb41arybchik	__in			uint32_t partn,
2045e6feb41arybchik	__in			unsigned int offset,
2046e6feb41arybchik	__out_bcount(size)	caddr_t data,
2047e6feb41arybchik	__in			size_t size)
2048e6feb41arybchik{
2049e6feb41arybchik	/*
20504fc0798arybchik	 * An A/B partition has two data stores (current and backup).
20514fc0798arybchik	 * Read requests which come in through the EFX API expect to read the
20524fc0798arybchik	 * current, active store of an A/B partition. For non A/B partitions,
20534fc0798arybchik	 * there is only a single store and so the mode param is ignored.
2054e6feb41arybchik	 */
2055e6feb41arybchik	return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
2056e6feb41arybchik			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
2057e6feb41arybchik}
2058e6feb41arybchik
2059e6feb41arybchik	__checkReturn		efx_rc_t
2060