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
6616fdc5earybchikstatic	__checkReturn		efx_rc_t
67220281carybchiktlv_validate_state(
68bd836a6arybchik	__inout			tlv_cursor_t *cursor);
69220281carybchik
70bd836a6arybchikstatic				void
71bd836a6arybchiktlv_init_block(
72bd836a6arybchik	__out	uint32_t	*block)
73bd836a6arybchik{
74bd836a6arybchik	*block = __CPU_TO_LE_32(TLV_TAG_END);
75bd836a6arybchik}
76bd836a6arybchik
77220281carybchikstatic				uint32_t
78220281carybchiktlv_tag(
79220281carybchik	__in	tlv_cursor_t	*cursor)
80220281carybchik{
81220281carybchik	uint32_t dword, tag;
82220281carybchik
83220281carybchik	dword = cursor->current[0];
84220281carybchik	tag = __LE_TO_CPU_32(dword);
85220281carybchik
86220281carybchik	return (tag);
87220281carybchik}
88220281carybchik
89220281carybchikstatic				size_t
90220281carybchiktlv_length(
91220281carybchik	__in	tlv_cursor_t	*cursor)
92220281carybchik{
93220281carybchik	uint32_t dword, length;
94220281carybchik
95220281carybchik	if (tlv_tag(cursor) == TLV_TAG_END)
96220281carybchik		return (0);
97220281carybchik
98220281carybchik	dword = cursor->current[1];
99220281carybchik	length = __LE_TO_CPU_32(dword);
100220281carybchik
101220281carybchik	return ((size_t)length);
102220281carybchik}
103220281carybchik
104220281carybchikstatic				uint8_t *
105220281carybchiktlv_value(
106220281carybchik	__in	tlv_cursor_t	*cursor)
107220281carybchik{
108220281carybchik	if (tlv_tag(cursor) == TLV_TAG_END)
109220281carybchik		return (NULL);
110220281carybchik
111220281carybchik	return ((uint8_t *)(&cursor->current[2]));
112220281carybchik}
113220281carybchik
114220281carybchikstatic				uint8_t *
115220281carybchiktlv_item(
116220281carybchik	__in	tlv_cursor_t	*cursor)
117220281carybchik{
118220281carybchik	if (tlv_tag(cursor) == TLV_TAG_END)
119220281carybchik		return (NULL);
120220281carybchik
121220281carybchik	return ((uint8_t *)cursor->current);
122220281carybchik}
123220281carybchik
124220281carybchik/*
125220281carybchik * TLV item DWORD length is tag + length + value (rounded up to DWORD)
126220281carybchik * equivalent to tlv_n_words_for_len in mc-comms tlv.c
127220281carybchik */
128220281carybchik#define	TLV_DWORD_COUNT(length) \
129220281carybchik	(1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
130220281carybchik
131220281carybchikstatic				uint32_t *
132220281carybchiktlv_next_item_ptr(
133220281carybchik	__in	tlv_cursor_t	*cursor)
134220281carybchik{
135220281carybchik	uint32_t length;
136220281carybchik
137220281carybchik	length = tlv_length(cursor);
138220281carybchik
139220281carybchik	return (cursor->current + TLV_DWORD_COUNT(length));
140220281carybchik}
141220281carybchik
142bd836a6arybchikstatic	__checkReturn		efx_rc_t
143220281carybchiktlv_advance(
144bd836a6arybchik	__inout	tlv_cursor_t	*cursor)
145220281carybchik{
14616fdc5earybchik	efx_rc_t rc;
147220281carybchik
148220281carybchik	if ((rc = tlv_validate_state(cursor)) != 0)
149220281carybchik		goto fail1;
150220281carybchik
151220281carybchik	if (cursor->current == cursor->end) {
152220281carybchik		/* No more tags after END tag */
153220281carybchik		cursor->current = NULL;
154220281carybchik		rc = ENOENT;
155220281carybchik		goto fail2;
156220281carybchik	}
157220281carybchik
158220281carybchik	/* Advance to next item and validate */
159220281carybchik	cursor->current = tlv_next_item_ptr(cursor);
160220281carybchik
161220281carybchik	if ((rc = tlv_validate_state(cursor)) != 0)
162220281carybchik		goto fail3;
163220281carybchik
164220281carybchik	return (0);
165220281carybchik
166220281carybchikfail3:
167220281carybchik	EFSYS_PROBE(fail3);
168220281carybchikfail2:
169220281carybchik	EFSYS_PROBE(fail2);
170220281carybchikfail1:
17116fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
172220281carybchik
173220281carybchik	return (rc);
174220281carybchik}
175220281carybchik
17616fdc5earybchikstatic				efx_rc_t
177220281carybchiktlv_rewind(
178220281carybchik	__in	tlv_cursor_t	*cursor)
179220281carybchik{
18016fdc5earybchik	efx_rc_t rc;
181220281carybchik
182220281carybchik	cursor->current = cursor->block;
183220281carybchik
184220281carybchik	if ((rc = tlv_validate_state(cursor)) != 0)
185220281carybchik		goto fail1;
186220281carybchik
187220281carybchik	return (0);
188220281carybchik
189220281carybchikfail1:
19016fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
191220281carybchik
192220281carybchik	return (rc);
193220281carybchik}
194220281carybchik
19516fdc5earybchikstatic				efx_rc_t
196220281carybchiktlv_find(
197bd836a6arybchik	__inout	tlv_cursor_t	*cursor,
198220281carybchik	__in	uint32_t	tag)
199220281carybchik{
20016fdc5earybchik	efx_rc_t rc;
201220281carybchik
202220281carybchik	rc = tlv_rewind(cursor);
203220281carybchik	while (rc == 0) {
204220281carybchik		if (tlv_tag(cursor) == tag)
205220281carybchik			break;
206220281carybchik
207220281carybchik		rc = tlv_advance(cursor);
208220281carybchik	}
209220281carybchik	return (rc);
210220281carybchik}
211220281carybchik
21216fdc5earybchikstatic	__checkReturn		efx_rc_t
213220281carybchiktlv_validate_state(
214bd836a6arybchik	__inout	tlv_cursor_t	*cursor)
215220281carybchik{
21616fdc5earybchik	efx_rc_t rc;
217220281carybchik
218220281carybchik	/* Check cursor position */
219220281carybchik	if (cursor->current < cursor->block) {
220220281carybchik		rc = EINVAL;
221220281carybchik		goto fail1;
222220281carybchik	}
223220281carybchik	if (cursor->current > cursor->limit) {
224220281carybchik		rc = EINVAL;
225220281carybchik		goto fail2;
226220281carybchik	}
227220281carybchik
228220281carybchik	if (tlv_tag(cursor) != TLV_TAG_END) {
229220281carybchik		/* Check current item has space for tag and length */
230a07211barybchik		if (cursor->current > (cursor->limit - 1)) {
231220281carybchik			cursor->current = NULL;
232220281carybchik			rc = EFAULT;
233220281carybchik			goto fail3;
234220281carybchik		}
235220281carybchik
236a07211barybchik		/* Check we have value data for current item and an END tag */
237a07211barybchik		if (tlv_next_item_ptr(cursor) > cursor->limit) {
238220281carybchik			cursor->current = NULL;
239220281carybchik			rc = EFAULT;
240220281carybchik			goto fail4;
241220281carybchik		}
242220281carybchik	}
243220281carybchik
244220281carybchik	return (0);
245220281carybchik
246220281carybchikfail4:
247220281carybchik	EFSYS_PROBE(fail4);
248220281carybchikfail3:
249220281carybchik	EFSYS_PROBE(fail3);
250220281carybchikfail2:
251220281carybchik	EFSYS_PROBE(fail2);
252220281carybchikfail1:
25316fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
254220281carybchik
255220281carybchik	return (rc);
256220281carybchik}
257220281carybchik
25816fdc5earybchikstatic				efx_rc_t
259220281carybchiktlv_init_cursor(
260b06f43barybchik	__out	tlv_cursor_t	*cursor,
261220281carybchik	__in	uint32_t	*block,
262bd836a6arybchik	__in	uint32_t	*limit,
263bd836a6arybchik	__in	uint32_t	*current)
264220281carybchik{
265220281carybchik	cursor->block	= block;
266220281carybchik	cursor->limit	= limit;
267220281carybchik
268bd836a6arybchik	cursor->current	= current;
269220281carybchik	cursor->end	= NULL;
270220281carybchik
271220281carybchik	return (tlv_validate_state(cursor));
272220281carybchik}
273220281carybchik
274bd836a6arybchikstatic	__checkReturn		efx_rc_t
275220281carybchiktlv_init_cursor_from_size(
276b06f43barybchik	__out	tlv_cursor_t	*cursor,
277bd836a6arybchik	__in_bcount(size)
278bd836a6arybchik		uint8_t		*block,
279220281carybchik	__in	size_t		size)
280220281carybchik{
281220281carybchik	uint32_t *limit;
282220281carybchik	limit = (uint32_t *)(block + size - sizeof (uint32_t));
283bd836a6arybchik	return (tlv_init_cursor(cursor, (uint32_t *)block,
284bd836a6arybchik		limit, (uint32_t *)block));
285220281carybchik}
286220281carybchik
287bd836a6arybchikstatic	__checkReturn		efx_rc_t
288bd836a6arybchiktlv_init_cursor_at_offset(
289bd836a6arybchik	__out	tlv_cursor_t	*cursor,
290bd836a6arybchik	__in_bcount(size)
291bd836a6arybchik		uint8_t		*block,
292bd836a6arybchik	__in	size_t		size,
293bd836a6arybchik	__in	size_t		offset)
294bd836a6arybchik{
295bd836a6arybchik	uint32_t *limit;
296bd836a6arybchik	uint32_t *current;
297bd836a6arybchik	limit = (uint32_t *)(block + size - sizeof (uint32_t));
298bd836a6arybchik	current = (uint32_t *)(block + offset);
299bd836a6arybchik	return (tlv_init_cursor(cursor, (uint32_t *)block, limit, current));
300bd836a6arybchik}
301bd836a6arybchik
302bd836a6arybchikstatic	__checkReturn		efx_rc_t
303220281carybchiktlv_require_end(
304bd836a6arybchik	__inout	tlv_cursor_t	*cursor)
305220281carybchik{
306220281carybchik	uint32_t *pos;
30716fdc5earybchik	efx_rc_t rc;
308220281carybchik
309220281carybchik	if (cursor->end == NULL) {
310220281carybchik		pos = cursor->current;
311220281carybchik		if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
312220281carybchik			goto fail1;
313220281carybchik
314220281carybchik		cursor->end = cursor->current;
315220281carybchik		cursor->current = pos;
316220281carybchik	}
317220281carybchik
318220281carybchik	return (0);
319220281carybchik
320220281carybchikfail1:
32116fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
322220281carybchik
323220281carybchik	return (rc);
324220281carybchik}
325220281carybchik
326220281carybchikstatic				size_t
327220281carybchiktlv_block_length_used(
328bd836a6arybchik	__inout	tlv_cursor_t	*cursor)
329220281carybchik{
33016fdc5earybchik	efx_rc_t rc;
331220281carybchik
332220281carybchik	if ((rc = tlv_validate_state(cursor)) != 0)
333220281carybchik		goto fail1;
334220281carybchik
335220281carybchik	if ((rc = tlv_require_end(cursor)) != 0)
336220281carybchik		goto fail2;
337220281carybchik
338220281carybchik	/* Return space used (including the END tag) */
339220281carybchik	return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
340220281carybchik
341220281carybchikfail2:
342220281carybchik	EFSYS_PROBE(fail2);
343220281carybchikfail1:
34416fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
345220281carybchik
346220281carybchik	return (0);
347220281carybchik}
348220281carybchik
349bd836a6arybchikstatic		uint32_t *
350bd836a6arybchiktlv_last_segment_end(
351bd836a6arybchik	__in	tlv_cursor_t *cursor)
352bd836a6arybchik{
353bd836a6arybchik	tlv_cursor_t segment_cursor;
354bd836a6arybchik	uint32_t *last_segment_end = cursor->block;
355bd836a6arybchik	uint32_t *segment_start = cursor->block;
356bd836a6arybchik
357bd836a6arybchik	/*
358bd836a6arybchik	 * Go through each segment and check that it has an end tag. If there
359bd836a6arybchik	 * is no end tag then the previous segment was the last valid one,
360bd836a6arybchik	 * so return the pointer to its end tag.
361bd836a6arybchik	 */
362719977farybchik	for (;;) {
363bd836a6arybchik		if (tlv_init_cursor(&segment_cursor, segment_start,
364bd836a6arybchik		    cursor->limit, segment_start) != 0)
365bd836a6arybchik			break;
366bd836a6arybchik		if (tlv_require_end(&segment_cursor) != 0)
367bd836a6arybchik			break;
368bd836a6arybchik		last_segment_end = segment_cursor.end;
369bd836a6arybchik		segment_start = segment_cursor.end + 1;
370bd836a6arybchik	}
371bd836a6arybchik
372bd836a6arybchik	return (last_segment_end);
373bd836a6arybchik}
374bd836a6arybchik
375bd836a6arybchikstatic				uint32_t *
376220281carybchiktlv_write(
377220281carybchik	__in			tlv_cursor_t *cursor,
378220281carybchik	__in			uint32_t tag,
379220281carybchik	__in_bcount(size)	uint8_t *data,
380220281carybchik	__in			size_t size)
381220281carybchik{
382220281carybchik	uint32_t len = size;
383220281carybchik	uint32_t *ptr;
384220281carybchik
385220281carybchik	ptr = cursor->current;
386220281carybchik
387220281carybchik	*ptr++ = __CPU_TO_LE_32(tag);
388220281carybchik	*ptr++ = __CPU_TO_LE_32(len);
389220281carybchik
390220281carybchik	if (len > 0) {
391220281carybchik		ptr[(len - 1) / sizeof (uint32_t)] = 0;
392220281carybchik		memcpy(ptr, data, len);
3931edccfbarybchik		ptr += EFX_P2ROUNDUP(uint32_t, len,
3941edccfbarybchik		    sizeof (uint32_t)) / sizeof (*ptr);
395220281carybchik	}
396220281carybchik
397220281carybchik	return (ptr);
398220281carybchik}
399220281carybchik
40016fdc5earybchikstatic	__checkReturn		efx_rc_t
401220281carybchiktlv_insert(
402bd836a6arybchik	__inout	tlv_cursor_t	*cursor,
403220281carybchik	__in	uint32_t	tag,
404bd836a6arybchik	__in_bcount(size)
405bd836a6arybchik		uint8_t		*data,
406220281carybchik	__in	size_t		size)
407220281carybchik{
408220281carybchik	unsigned int delta;
409bd836a6arybchik	uint32_t *last_segment_end;
41016fdc5earybchik	efx_rc_t rc;
411220281carybchik
412220281carybchik	if ((rc = tlv_validate_state(cursor)) != 0)
413220281carybchik		goto fail1;
414220281carybchik
415220281carybchik	if ((rc = tlv_require_end(cursor)) != 0)
416220281carybchik		goto fail2;
417220281carybchik
418220281carybchik	if (tag == TLV_TAG_END) {
419220281carybchik		rc = EINVAL;
420220281carybchik		goto fail3;
421220281carybchik	}
422220281carybchik
423bd836a6arybchik	last_segment_end = tlv_last_segment_end(cursor);
424bd836a6arybchik
425220281carybchik	delta = TLV_DWORD_COUNT(size);
426bd836a6arybchik	if (last_segment_end + 1 + delta > cursor->limit) {
427220281carybchik		rc = ENOSPC;
428220281carybchik		goto fail4;
429220281carybchik	}
430220281carybchik
431220281carybchik	/* Move data up: new space at cursor->current */
432220281carybchik	memmove(cursor->current + delta, cursor->current,
433bd836a6arybchik	    (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
434220281carybchik
435220281carybchik	/* Adjust the end pointer */
436220281carybchik	cursor->end += delta;
437220281carybchik
438220281carybchik	/* Write new TLV item */
439220281carybchik	tlv_write(cursor, tag, data, size);
440220281carybchik
441220281carybchik	return (0);
442220281carybchik
443220281carybchikfail4:
444220281carybchik	EFSYS_PROBE(fail4);
445220281carybchikfail3:
446220281carybchik	EFSYS_PROBE(fail3);
447220281carybchikfail2:
448220281carybchik	EFSYS_PROBE(fail2);
449220281carybchikfail1:
45016fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
451220281carybchik
452220281carybchik	return (rc);
453220281carybchik}
454220281carybchik
45516fdc5earybchikstatic	__checkReturn		efx_rc_t
456bd836a6arybchiktlv_delete(
457bd836a6arybchik	__inout	tlv_cursor_t	*cursor)
458bd836a6arybchik{
459bd836a6arybchik	unsigned int delta;
460bd836a6arybchik	uint32_t *last_segment_end;
461bd836a6arybchik	efx_rc_t rc;
462bd836a6arybchik
463bd836a6arybchik	if ((rc = tlv_validate_state(cursor)) != 0)
464bd836a6arybchik		goto fail1;
465bd836a6arybchik
466bd836a6arybchik	if (tlv_tag(cursor) == TLV_TAG_END) {
467bd836a6arybchik		rc = EINVAL;
468bd836a6arybchik		goto fail2;
469bd836a6arybchik	}
470bd836a6arybchik
471bd836a6arybchik	delta = TLV_DWORD_COUNT(tlv_length(cursor));
472bd836a6arybchik
473bd836a6arybchik	if ((rc = tlv_require_end(cursor)) != 0)
474bd836a6arybchik		goto fail3;
475bd836a6arybchik
476bd836a6arybchik	last_segment_end = tlv_last_segment_end(cursor);
477bd836a6arybchik
478bd836a6arybchik	/* Shuffle things down, destroying the item at cursor->current */
479bd836a6arybchik	memmove(cursor->current, cursor->current + delta,
480bd836a6arybchik	    (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
481bd836a6arybchik	/* Zero the new space at the end of the TLV chain */
482bd836a6arybchik	memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t));
483bd836a6arybchik	/* Adjust the end pointer */
484bd836a6arybchik	cursor->end -= delta;
485bd836a6arybchik
486bd836a6arybchik	return (0);
487bd836a6arybchik
488bd836a6arybchikfail3:
489bd836a6arybchik	EFSYS_PROBE(fail3);
490bd836a6arybchikfail2:
491bd836a6arybchik	EFSYS_PROBE(fail2);
492bd836a6arybchikfail1:
493bd836a6arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
494bd836a6arybchik
495bd836a6arybchik	return (rc);
496bd836a6arybchik}
497bd836a6arybchik
498bd836a6arybchikstatic	__checkReturn		efx_rc_t
499220281carybchiktlv_modify(
500bd836a6arybchik	__inout	tlv_cursor_t	*cursor,
501220281carybchik	__in	uint32_t	tag,
502bd836a6arybchik	__in_bcount(size)
503bd836a6arybchik		uint8_t		*data,
504220281carybchik	__in	size_t		size)
505220281carybchik{
506220281carybchik	uint32_t *pos;
507220281carybchik	unsigned int old_ndwords;
508220281carybchik	unsigned int new_ndwords;
509220281carybchik	unsigned int delta;
510bd836a6arybchik	uint32_t *last_segment_end;
51116fdc5earybchik	efx_rc_t rc;
512220281carybchik
513220281carybchik	if ((rc = tlv_validate_state(cursor)) != 0)
514220281carybchik		goto fail1;
515220281carybchik
516220281carybchik	if (tlv_tag(cursor) == TLV_TAG_END) {
517220281carybchik		rc = EINVAL;
518220281carybchik		goto fail2;
519220281carybchik	}
520220281carybchik	if (tlv_tag(cursor) != tag) {
521220281carybchik		rc = EINVAL;
522220281carybchik		goto fail3;
523220281carybchik	}
524220281carybchik
525220281carybchik	old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
526220281carybchik	new_ndwords = TLV_DWORD_COUNT(size);
527220281carybchik
528220281carybchik	if ((rc = tlv_require_end(cursor)) != 0)
529220281carybchik		goto fail4;
530220281carybchik
531bd836a6arybchik	last_segment_end = tlv_last_segment_end(cursor);
532bd836a6arybchik
533220281carybchik	if (new_ndwords > old_ndwords) {
534220281carybchik		/* Expand space used for TLV item */
535220281carybchik		delta = new_ndwords - old_ndwords;
536220281carybchik		pos = cursor->current + old_ndwords;
537220281carybchik
538bd836a6arybchik		if (last_segment_end + 1 + delta > cursor->limit) {
539220281carybchik			rc = ENOSPC;
540220281carybchik			goto fail5;
541220281carybchik		}
542220281carybchik
543220281carybchik		/* Move up: new space at (cursor->current + old_ndwords) */
544220281carybchik		memmove(pos + delta, pos,
545bd836a6arybchik		    (last_segment_end + 1 - pos) * sizeof (uint32_t));
546220281carybchik
547220281carybchik		/* Adjust the end pointer */
548220281carybchik		cursor->end += delta;
549220281carybchik
550220281carybchik	} else if (new_ndwords < old_ndwords) {
551220281carybchik		/* Shrink space used for TLV item */
552220281carybchik		delta = old_ndwords - new_ndwords;
553220281carybchik		pos = cursor->current + new_ndwords;
554220281carybchik
555220281carybchik		/* Move down: remove words at (cursor->current + new_ndwords) */
556220281carybchik		memmove(pos, pos + delta,
557bd836a6arybchik		    (last_segment_end + 1 - pos) * sizeof (uint32_t));
558220281carybchik
559220281carybchik		/* Zero the new space at the end of the TLV chain */
560bd836a6arybchik		memset(last_segment_end + 1 - delta, 0,
561bd836a6arybchik		    delta * sizeof (uint32_t));
562220281carybchik
563220281carybchik		/* Adjust the end pointer */
564220281carybchik		cursor->end -= delta;
565220281carybchik	}
566220281carybchik
567220281carybchik	/* Write new data */
568220281carybchik	tlv_write(cursor, tag, data, size);
569220281carybchik
570220281carybchik	return (0);
571220281carybchik
572220281carybchikfail5:
573220281carybchik	EFSYS_PROBE(fail5);
574220281carybchikfail4:
575220281carybchik	EFSYS_PROBE(fail4);
576220281carybchikfail3:
577220281carybchik	EFSYS_PROBE(fail3);
578220281carybchikfail2:
579220281carybchik	EFSYS_PROBE(fail2);
580220281carybchikfail1:
58116fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
582220281carybchik
583220281carybchik	return (rc);
584220281carybchik}
585220281carybchik
586bd836a6arybchikstatic uint32_t checksum_tlv_partition(
587bd836a6arybchik	__in	nvram_partition_t *partition)
588bd836a6arybchik{
589bd836a6arybchik	tlv_cursor_t *cursor;
590bd836a6arybchik	uint32_t *ptr;
591bd836a6arybchik	uint32_t *end;
592bd836a6arybchik	uint32_t csum;
593bd836a6arybchik	size_t len;
594bd836a6arybchik
595bd836a6arybchik	cursor = &partition->tlv_cursor;
596bd836a6arybchik	len = tlv_block_length_used(cursor);
597bd836a6arybchik	EFSYS_ASSERT3U((len & 3), ==, 0);
598bd836a6arybchik
599bd836a6arybchik	csum = 0;
600bd836a6arybchik	ptr = partition->data;
601bd836a6arybchik	end = &ptr[len >> 2];
602bd836a6arybchik
603bd836a6arybchik	while (ptr < end)
604bd836a6arybchik		csum += __LE_TO_CPU_32(*ptr++);
605bd836a6arybchik
606bd836a6arybchik	return (csum);
607bd836a6arybchik}
608bd836a6arybchik
609bd836a6arybchikstatic	__checkReturn		efx_rc_t
610bd836a6arybchiktlv_update_partition_len_and_cks(
611bd836a6arybchik	__in	tlv_cursor_t *cursor)
612bd836a6arybchik{
613bd836a6arybchik	efx_rc_t rc;
614bd836a6arybchik	nvram_partition_t partition;
615bd836a6arybchik	struct tlv_partition_header *header;
616bd836a6arybchik	struct tlv_partition_trailer *trailer;
617bd836a6arybchik	size_t new_len;
618bd836a6arybchik
619bd836a6arybchik	/*
620bd836a6arybchik	 * We just modified the partition, so the total length may not be
621bd836a6arybchik	 * valid. Don't use tlv_find(), which performs some sanity checks
622bd836a6arybchik	 * that may fail here.
623bd836a6arybchik	 */
624bd836a6arybchik	partition.data = cursor->block;
625bd836a6arybchik	memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
626bd836a6arybchik	header = (struct tlv_partition_header *)partition.data;
627bd836a6arybchik	/* Sanity check. */
628bd836a6arybchik	if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
629bd836a6arybchik		rc = EFAULT;
630bd836a6arybchik		goto fail1;
631bd836a6arybchik	}
632bd836a6arybchik	new_len =  tlv_block_length_used(&partition.tlv_cursor);
633bd836a6arybchik	if (new_len == 0) {
634bd836a6arybchik		rc = EFAULT;
635bd836a6arybchik		goto fail2;
636bd836a6arybchik	}
637bd836a6arybchik	header->total_length = __CPU_TO_LE_32(new_len);
638bd836a6arybchik	/* Ensure the modified partition always has a new generation count. */
639bd836a6arybchik	header->generation = __CPU_TO_LE_32(
640bd836a6arybchik	    __LE_TO_CPU_32(header->generation) + 1);
641bd836a6arybchik
642bd836a6arybchik	trailer = (struct tlv_partition_trailer *)((uint8_t *)header +
643bd836a6arybchik	    new_len - sizeof (*trailer) - sizeof (uint32_t));
644bd836a6arybchik	trailer->generation = header->generation;
645bd836a6arybchik	trailer->checksum = __CPU_TO_LE_32(
646bd836a6arybchik	    __LE_TO_CPU_32(trailer->checksum) -
647bd836a6arybchik	    checksum_tlv_partition(&partition));
648bd836a6arybchik
649bd836a6arybchik	return (0);
650bd836a6arybchik
651bd836a6arybchikfail2:
652bd836a6arybchik	EFSYS_PROBE(fail2);
653bd836a6arybchikfail1:
654bd836a6arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
655bd836a6arybchik
656bd836a6arybchik	return (rc);
657bd836a6arybchik}
658bd836a6arybchik
659bd836a6arybchik/* Validate buffer contents (before writing to flash) */
66016fdc5earybchik	__checkReturn		efx_rc_t
6619751d26arybchikef10_nvram_buffer_validate(
662220281carybchik	__in			uint32_t partn,
663220281carybchik	__in_bcount(partn_size)	caddr_t partn_data,
664220281carybchik	__in			size_t partn_size)
665220281carybchik{
666220281carybchik	tlv_cursor_t cursor;
667220281carybchik	struct tlv_partition_header *header;
668220281carybchik	struct tlv_partition_trailer *trailer;
669220281carybchik	size_t total_length;
670220281carybchik	uint32_t cksum;
671220281carybchik	int pos;
67216fdc5earybchik	efx_rc_t rc;
673220281carybchik
674c86fe87arybchik	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
675220281carybchik
676220281carybchik	if ((partn_data == NULL) || (partn_size == 0)) {
677220281carybchik		rc = EINVAL;
678220281carybchik		goto fail1;
679220281carybchik	}
680220281carybchik
681220281carybchik	/* The partition header must be the first item (at offset zero) */
682f85d27earybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
683220281carybchik		    partn_size)) != 0) {
684220281carybchik		rc = EFAULT;
685220281carybchik		goto fail2;
686220281carybchik	}
687220281carybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
688220281carybchik		rc = EINVAL;
689220281carybchik		goto fail3;
690220281carybchik	}
691220281carybchik	header = (struct tlv_partition_header *)tlv_item(&cursor);
692220281carybchik
693220281carybchik	/* Check TLV partition length (includes the END tag) */
694220281carybchik	total_length = __LE_TO_CPU_32(header->total_length);
695220281carybchik	if (total_length > partn_size) {
696220281carybchik		rc = EFBIG;
697220281carybchik		goto fail4;
698220281carybchik	}
699220281carybchik
700a07211barybchik	/* Check partition header matches partn */
701a07211barybchik	if (__LE_TO_CPU_16(header->type_id) != partn) {
702a07211barybchik		rc = EINVAL;
703a07211barybchik		goto fail5;
704a07211barybchik	}
705a07211barybchik
706220281carybchik	/* Check partition ends with PARTITION_TRAILER and END tags */
707220281carybchik	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
708220281carybchik		rc = EINVAL;
709a07211barybchik		goto fail6;
710220281carybchik	}
711220281carybchik	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
712220281carybchik
713220281carybchik	if ((rc = tlv_advance(&cursor)) != 0) {
714220281carybchik		rc = EINVAL;
715a07211barybchik		goto fail7;
716220281carybchik	}
717220281carybchik	if (tlv_tag(&cursor) != TLV_TAG_END) {
718220281carybchik		rc = EINVAL;
719a07211barybchik		goto fail8;
720220281carybchik	}
721220281carybchik
722220281carybchik	/* Check generation counts are consistent */
723220281carybchik	if (trailer->generation != header->generation) {
724220281carybchik		rc = EINVAL;
725a07211barybchik		goto fail9;
726220281carybchik	}
727220281carybchik
728220281carybchik	/* Verify partition checksum */
729220281carybchik	cksum = 0;
730220281carybchik	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
731220281carybchik		cksum += *((uint32_t *)(partn_data + pos));
732220281carybchik	}
733220281carybchik	if (cksum != 0) {
734220281carybchik		rc = EINVAL;
735a07211barybchik		goto fail10;
736220281carybchik	}
737220281carybchik
738220281carybchik	return (0);
739220281carybchik
740a07211barybchikfail10:
741a07211barybchik	EFSYS_PROBE(fail10);
742220281carybchikfail9:
743220281carybchik	EFSYS_PROBE(fail9);
744220281carybchikfail8:
745220281carybchik	EFSYS_PROBE(fail8);
746220281carybchikfail7:
747220281carybchik	EFSYS_PROBE(fail7);
748220281carybchikfail6:
749220281carybchik	EFSYS_PROBE(fail6);
750220281carybchikfail5:
751220281carybchik	EFSYS_PROBE(fail5);
752220281carybchikfail4:
753220281carybchik	EFSYS_PROBE(fail4);
754220281carybchikfail3:
755220281carybchik	EFSYS_PROBE(fail3);
756220281carybchikfail2:
757220281carybchik	EFSYS_PROBE(fail2);
758220281carybchikfail1:
75916fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
760220281carybchik
761220281carybchik	return (rc);
762220281carybchik}
763220281carybchik
764a07211barybchik			void
765a07211barybchikef10_nvram_buffer_init(
766a07211barybchik	__out_bcount(buffer_size)
767a07211barybchik				caddr_t bufferp,
768a07211barybchik	__in			size_t buffer_size)
769a07211barybchik{
770a07211barybchik	uint32_t *buf = (uint32_t *)bufferp;
771a07211barybchik
772a07211barybchik	memset(buf, 0xff, buffer_size);
7738e30d77arybchik
774a07211barybchik	tlv_init_block(buf);
775a07211barybchik}
7768e30d77arybchik
7778e30d77arybchik	__checkReturn		efx_rc_t
7788e30d77arybchikef10_nvram_buffer_create(
779a07211barybchik	__in			uint32_t partn_type,
780a07211barybchik	__out_bcount(partn_size)
781a07211barybchik				caddr_t partn_data,
7828e30d77arybchik	__in			size_t partn_size)
7838e30d77arybchik{
7848e30d77arybchik	uint32_t *buf = (uint32_t *)partn_data;
7858e30d77arybchik	efx_rc_t rc;
7868e30d77arybchik	tlv_cursor_t cursor;
7878e30d77arybchik	struct tlv_partition_header header;
7888e30d77arybchik	struct tlv_partition_trailer trailer;
7898e30d77arybchik
790bb7138farybchik	unsigned int min_buf_size = sizeof (struct tlv_partition_header) +
7918e30d77arybchik	    sizeof (struct tlv_partition_trailer);
7928e30d77arybchik	if (partn_size < min_buf_size) {
7938e30d77arybchik		rc = EINVAL;
7948e30d77arybchik		goto fail1;
7958e30d77arybchik	}
7968e30d77arybchik
797a07211barybchik	ef10_nvram_buffer_init(partn_data, partn_size);
7988e30d77arybchik
7998e30d77arybchik	if ((rc = tlv_init_cursor(&cursor, buf,
8008e30d77arybchik	    (uint32_t *)((uint8_t *)buf + partn_size),
8018e30d77arybchik	    buf)) != 0) {
8028e30d77arybchik		goto fail2;
8038e30d77arybchik	}
8048e30d77arybchik
8058e30d77arybchik	header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
8068e30d77arybchik	header.length = __CPU_TO_LE_32(sizeof (header) - 8);
8078e30d77arybchik	header.type_id = __CPU_TO_LE_16(partn_type);
8088e30d77arybchik	header.preset = 0;
8098e30d77arybchik	header.generation = __CPU_TO_LE_32(1);
8108e30d77arybchik	header.total_length = 0;  /* This will be fixed below. */
8118e30d77arybchik	if ((rc = tlv_insert(
8128e30d77arybchik	    &cursor, TLV_TAG_PARTITION_HEADER,
8138e30d77arybchik	    (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
8148e30d77arybchik		goto fail3;
8158e30d77arybchik	if ((rc = tlv_advance(&cursor)) != 0)
8168e30d77arybchik		goto fail4;
8178e30d77arybchik
8188e30d77arybchik	trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
8198e30d77arybchik	trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
8208e30d77arybchik	trailer.generation = header.generation;
8218e30d77arybchik	trailer.checksum = 0;  /* This will be fixed below. */
8228e30d77arybchik	if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
8238e30d77arybchik	    (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
8248e30d77arybchik		goto fail5;
8258e30d77arybchik
8268e30d77arybchik	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
8278e30d77arybchik		goto fail6;
8288e30d77arybchik
8298e30d77arybchik	/* Check that the partition is valid. */
830a07211barybchik	if ((rc = ef10_nvram_buffer_validate(partn_type,
8318e30d77arybchik	    partn_data, partn_size)) != 0)
8328e30d77arybchik		goto fail7;
8338e30d77arybchik
8348e30d77arybchik	return (0);
8358e30d77arybchik
8368e30d77arybchikfail7:
8378e30d77arybchik	EFSYS_PROBE(fail7);
8388e30d77arybchikfail6:
8398e30d77arybchik	EFSYS_PROBE(fail6);
8408e30d77arybchikfail5:
8418e30d77arybchik	EFSYS_PROBE(fail5);
8428e30d77arybchikfail4:
8438e30d77arybchik	EFSYS_PROBE(fail4);
8448e30d77arybchikfail3:
8458e30d77arybchik	EFSYS_PROBE(fail3);
8468e30d77arybchikfail2:
8478e30d77arybchik	EFSYS_PROBE(fail2);
8488e30d77arybchikfail1:
8498e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
8508e30d77arybchik
8518e30d77arybchik	return (rc);
8528e30d77arybchik}
8538e30d77arybchik
8548e30d77arybchikstatic			uint32_t
8558e30d77arybchikbyte_offset(
8568e30d77arybchik	__in		uint32_t *position,
8578e30d77arybchik	__in		uint32_t *base)
8588e30d77arybchik{
8598e30d77arybchik	return (uint32_t)((uint8_t *)position - (uint8_t *)base);
8608e30d77arybchik}
8618e30d77arybchik
8628e30d77arybchik	__checkReturn		efx_rc_t
8638e30d77arybchikef10_nvram_buffer_find_item_start(
8648e30d77arybchik	__in_bcount(buffer_size)
8658e30d77arybchik				caddr_t bufferp,
8668e30d77arybchik	__in			size_t buffer_size,
8678e30d77arybchik	__out			uint32_t *startp)
8688e30d77arybchik{
86959d6b0carybchik	/* Read past partition header to find start address of the first key */
8708e30d77arybchik	tlv_cursor_t cursor;
8718e30d77arybchik	efx_rc_t rc;
8728e30d77arybchik
8738e30d77arybchik	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
8748e30d77arybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
8758e30d77arybchik			buffer_size)) != 0) {
8768e30d77arybchik		rc = EFAULT;
8778e30d77arybchik		goto fail1;
8788e30d77arybchik	}
8798e30d77arybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
8808e30d77arybchik		rc = EINVAL;
8818e30d77arybchik		goto fail2;
8828e30d77arybchik	}
8838e30d77arybchik
8848e30d77arybchik	if ((rc = tlv_advance(&cursor)) != 0) {
8858e30d77arybchik		rc = EINVAL;
8868e30d77arybchik		goto fail3;
8878e30d77arybchik	}
8888e30d77arybchik	*startp = byte_offset(cursor.current, cursor.block);
8898e30d77arybchik
8908e30d77arybchik	if ((rc = tlv_require_end(&cursor)) != 0)
8918e30d77arybchik		goto fail4;
8928e30d77arybchik
8938e30d77arybchik	return (0);
8948e30d77arybchik
8958e30d77arybchikfail4:
8968e30d77arybchik	EFSYS_PROBE(fail4);
8978e30d77arybchikfail3:
8988e30d77arybchik	EFSYS_PROBE(fail3);
8998e30d77arybchikfail2:
9008e30d77arybchik	EFSYS_PROBE(fail2);
9018e30d77arybchikfail1:
9028e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
9038e30d77arybchik
9048e30d77arybchik	return (rc);
9058e30d77arybchik}
9068e30d77arybchik
9078e30d77arybchik	__checkReturn		efx_rc_t
9088e30d77arybchikef10_nvram_buffer_find_end(
9098e30d77arybchik	__in_bcount(buffer_size)
9108e30d77arybchik				caddr_t bufferp,
9118e30d77arybchik	__in			size_t buffer_size,
9128e30d77arybchik	__in			uint32_t offset,
9138e30d77arybchik	__out			uint32_t *endp)
9148e30d77arybchik{
91559d6b0carybchik	/* Read to end of partition */
9168e30d77arybchik	tlv_cursor_t cursor;
9178e30d77arybchik	efx_rc_t rc;
918640eebaarybchik	uint32_t *segment_used;
9198e30d77arybchik
920a3be7d7arybchik	_NOTE(ARGUNUSED(offset))
921a3be7d7arybchik
9228e30d77arybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
9238e30d77arybchik			buffer_size)) != 0) {
9248e30d77arybchik		rc = EFAULT;
9258e30d77arybchik		goto fail1;
9268e30d77arybchik	}
9278e30d77arybchik
928640eebaarybchik	segment_used = cursor.block;
929640eebaarybchik
930640eebaarybchik	/*
931640eebaarybchik	 * Go through each segment and check that it has an end tag. If there
932640eebaarybchik	 * is no end tag then the previous segment was the last valid one,
933640eebaarybchik	 * so return the used space including that end tag.
934640eebaarybchik	 */
935640eebaarybchik	while (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
936640eebaarybchik		if (tlv_require_end(&cursor) != 0) {
937640eebaarybchik			if (segment_used == cursor.block) {
938640eebaarybchik				/*
939640eebaarybchik				 * First segment is corrupt, so there is
940640eebaarybchik				 * no valid data in partition.
941640eebaarybchik				 */
942640eebaarybchik				rc = EINVAL;
943640eebaarybchik				goto fail2;
944640eebaarybchik			}
945640eebaarybchik			break;
946640eebaarybchik		}
947640eebaarybchik		segment_used = cursor.end + 1;
9488e30d77arybchik
949640eebaarybchik		cursor.current = segment_used;
950640eebaarybchik	}
951640eebaarybchik	/* Return space used (including the END tag) */
952640eebaarybchik	*endp = (segment_used - cursor.block) * sizeof (uint32_t);
9538e30d77arybchik
9548e30d77arybchik	return (0);
9558e30d77arybchik
9568e30d77arybchikfail2:
9578e30d77arybchik	EFSYS_PROBE(fail2);
9588e30d77arybchikfail1:
9598e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
9608e30d77arybchik
9618e30d77arybchik	return (rc);
9628e30d77arybchik}
9638e30d77arybchik
9648e30d77arybchik	__checkReturn	__success(return != B_FALSE)	boolean_t
9658e30d77arybchikef10_nvram_buffer_find_item(
9668e30d77arybchik	__in_bcount(buffer_size)
9678e30d77arybchik				caddr_t bufferp,
9688e30d77arybchik	__in			size_t buffer_size,
9698e30d77arybchik	__in			uint32_t offset,
9708e30d77arybchik	__out			uint32_t *startp,
9718e30d77arybchik	__out			uint32_t *lengthp)
9728e30d77arybchik{
97359d6b0carybchik	/* Find TLV at offset and return key start and length */
9748e30d77arybchik	tlv_cursor_t cursor;
9758e30d77arybchik	uint8_t *key;
9768e30d77arybchik	uint32_t tag;
9778e30d77arybchik
9788e30d77arybchik	if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
9798e30d77arybchik			buffer_size, offset) != 0) {
9808e30d77arybchik		return (B_FALSE);
9818e30d77arybchik	}
9828e30d77arybchik
9838e30d77arybchik	while ((key = tlv_item(&cursor)) != NULL) {
9848e30d77arybchik		tag = tlv_tag(&cursor);
9858e30d77arybchik		if (tag == TLV_TAG_PARTITION_HEADER ||
9868e30d77arybchik		    tag == TLV_TAG_PARTITION_TRAILER) {
9878e30d77arybchik			if (tlv_advance(&cursor) != 0) {
9888e30d77arybchik				break;
9898e30d77arybchik			}
9908e30d77arybchik			continue;
9918e30d77arybchik		}
9928e30d77arybchik		*startp = byte_offset(cursor.current, cursor.block);
9938e30d77arybchik		*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
9948e30d77arybchik		    cursor.current);
9958e30d77arybchik		return (B_TRUE);
9968e30d77arybchik	}
9978e30d77arybchik
9988e30d77arybchik	return (B_FALSE);
9998e30d77arybchik}
10008e30d77arybchik
10018e30d77arybchik	__checkReturn		efx_rc_t
1002a07211barybchikef10_nvram_buffer_peek_item(
1003a07211barybchik	__in_bcount(buffer_size)
1004a07211barybchik				caddr_t bufferp,
1005a07211barybchik	__in			size_t buffer_size,
1006a07211barybchik	__in			uint32_t offset,
1007a07211barybchik	__out			uint32_t *tagp,
1008a07211barybchik	__out			uint32_t *lengthp,
1009a07211barybchik	__out			uint32_t *value_offsetp)
1010a07211barybchik{
1011a07211barybchik	efx_rc_t rc;
1012a07211barybchik	tlv_cursor_t cursor;
1013a07211barybchik	uint32_t tag;
1014a07211barybchik
1015a07211barybchik	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1016a07211barybchik			buffer_size, offset)) != 0) {
1017a07211barybchik		goto fail1;
1018a07211barybchik	}
1019a07211barybchik
1020a07211barybchik	tag = tlv_tag(&cursor);
1021a07211barybchik	*tagp = tag;
1022a07211barybchik	if (tag == TLV_TAG_END) {
1023a07211barybchik		/*
1024a07211barybchik		 * To allow stepping over the END tag, report the full tag
1025a07211barybchik		 * length and a zero length value.
1026a07211barybchik		 */
1027a07211barybchik		*lengthp = sizeof (tag);
1028a07211barybchik		*value_offsetp = sizeof (tag);
1029a07211barybchik	} else {
1030a07211barybchik		*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1031a07211barybchik			    cursor.current);
1032a07211barybchik		*value_offsetp = byte_offset((uint32_t *)tlv_value(&cursor),
1033a07211barybchik			    cursor.current);
1034a07211barybchik	}
1035a07211barybchik	return (0);
1036a07211barybchik
1037a07211barybchikfail1:
1038a07211barybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1039a07211barybchik
1040a07211barybchik	return (rc);
1041a07211barybchik}
1042a07211barybchik
1043a07211barybchik	__checkReturn		efx_rc_t
10448e30d77arybchikef10_nvram_buffer_get_item(
10458e30d77arybchik	__in_bcount(buffer_size)
10468e30d77arybchik				caddr_t bufferp,
10478e30d77arybchik	__in			size_t buffer_size,
10488e30d77arybchik	__in			uint32_t offset,
10498e30d77arybchik	__in			uint32_t length,
1050a07211barybchik	__out			uint32_t *tagp,
1051a07211barybchik	__out_bcount_part(value_max_size, *lengthp)
1052a07211barybchik				caddr_t valuep,
1053a07211barybchik	__in			size_t value_max_size,
10548e30d77arybchik	__out			uint32_t *lengthp)
10558e30d77arybchik{
10568e30d77arybchik	efx_rc_t rc;
10578e30d77arybchik	tlv_cursor_t cursor;
1058a07211barybchik	uint32_t value_length;
10598e30d77arybchik
1060a07211barybchik	if (buffer_size < (offset + length)) {
10618e30d77arybchik		rc = ENOSPC;
10628e30d77arybchik		goto fail1;
10638e30d77arybchik	}
10648e30d77arybchik
10658e30d77arybchik	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
10668e30d77arybchik			buffer_size, offset)) != 0) {
10678e30d77arybchik		goto fail2;
10688e30d77arybchik	}
10698e30d77arybchik
1070a07211barybchik	value_length = tlv_length(&cursor);
1071a07211barybchik	if (value_max_size < value_length) {
10728e30d77arybchik		rc = ENOSPC;
10738e30d77arybchik		goto fail3;
10748e30d77arybchik	}
1075a07211barybchik	memcpy(valuep, tlv_value(&cursor), value_length);
10768e30d77arybchik
1077a07211barybchik	*tagp = tlv_tag(&cursor);
1078a07211barybchik	*lengthp = value_length;
10798e30d77arybchik
10808e30d77arybchik	return (0);
10818e30d77arybchik
10828e30d77arybchikfail3:
10838e30d77arybchik	EFSYS_PROBE(fail3);
10848e30d77arybchikfail2:
10858e30d77arybchik	EFSYS_PROBE(fail2);
10868e30d77arybchikfail1:
10878e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
10888e30d77arybchik
10898e30d77arybchik	return (rc);
10908e30d77arybchik}
10918e30d77arybchik
10928e30d77arybchik	__checkReturn		efx_rc_t
10938e30d77arybchikef10_nvram_buffer_insert_item(
10948e30d77arybchik	__in_bcount(buffer_size)
10958e30d77arybchik				caddr_t bufferp,
10968e30d77arybchik	__in			size_t buffer_size,
10978e30d77arybchik	__in			uint32_t offset,
1098a07211barybchik	__in			uint32_t tag,
1099a07211barybchik	__in_bcount(length)	caddr_t valuep,
1100a07211barybchik	__in			uint32_t length,
1101a07211barybchik	__out			uint32_t *lengthp)
1102a07211barybchik{
1103a07211barybchik	efx_rc_t rc;
1104a07211barybchik	tlv_cursor_t cursor;
1105a07211barybchik
1106a07211barybchik	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1107a07211barybchik			buffer_size, offset)) != 0) {
1108a07211barybchik		goto fail1;
1109a07211barybchik	}
1110a07211barybchik
1111a07211barybchik	rc = tlv_insert(&cursor, tag, (uint8_t *)valuep, length);
1112a07211barybchik
1113a07211barybchik	if (rc != 0)
1114a07211barybchik		goto fail2;
1115a07211barybchik
1116a07211barybchik	*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1117a07211barybchik		    cursor.current);
1118a07211barybchik
1119a07211barybchik	return (0);
1120a07211barybchik
1121a07211barybchikfail2:
1122a07211barybchik	EFSYS_PROBE(fail2);
1123a07211barybchikfail1:
1124a07211barybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1125a07211barybchik
1126a07211barybchik	return (rc);
1127a07211barybchik}
1128a07211barybchik
1129a07211barybchik	__checkReturn		efx_rc_t
1130a07211barybchikef10_nvram_buffer_modify_item(
1131a07211barybchik	__in_bcount(buffer_size)
1132a07211barybchik				caddr_t bufferp,
1133a07211barybchik	__in			size_t buffer_size,
1134a07211barybchik	__in			uint32_t offset,
1135a07211barybchik	__in			uint32_t tag,
1136a07211barybchik	__in_bcount(length)	caddr_t valuep,
11378e30d77arybchik	__in			uint32_t length,
11388e30d77arybchik	__out			uint32_t *lengthp)
11398e30d77arybchik{
11408e30d77arybchik	efx_rc_t rc;
11418e30d77arybchik	tlv_cursor_t cursor;
11428e30d77arybchik
11438e30d77arybchik	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
11448e30d77arybchik			buffer_size, offset)) != 0) {
11458e30d77arybchik		goto fail1;
11468e30d77arybchik	}
11478e30d77arybchik
1148a07211barybchik	rc = tlv_modify(&cursor, tag, (uint8_t *)valuep, length);
11498e30d77arybchik
11508e30d77arybchik	if (rc != 0) {
11518e30d77arybchik		goto fail2;
11528e30d77arybchik	}
11538e30d77arybchik
11548e30d77arybchik	*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
11558e30d77arybchik		    cursor.current);
11568e30d77arybchik
11578e30d77arybchik	return (0);
11588e30d77arybchik
11598e30d77arybchikfail2:
11608e30d77arybchik	EFSYS_PROBE(fail2);
11618e30d77arybchikfail1:
11628e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
11638e30d77arybchik
11648e30d77arybchik	return (rc);
11658e30d77arybchik}
11668e30d77arybchik
11678e30d77arybchik	__checkReturn		efx_rc_t
11688e30d77arybchikef10_nvram_buffer_delete_item(
11698e30d77arybchik	__in_bcount(buffer_size)
11708e30d77arybchik				caddr_t bufferp,
11718e30d77arybchik	__in			size_t buffer_size,
11728e30d77arybchik	__in			uint32_t offset,
11738e30d77arybchik	__in			uint32_t length,
11748e30d77arybchik	__in			uint32_t end)
11758e30d77arybchik{
11768e30d77arybchik	efx_rc_t rc;
11778e30d77arybchik	tlv_cursor_t cursor;
11788e30d77arybchik
1179a3be7d7arybchik	_NOTE(ARGUNUSED(length, end))
1180a3be7d7arybchik
11818e30d77arybchik	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
11828e30d77arybchik			buffer_size, offset)) != 0) {
11838e30d77arybchik		goto fail1;
11848e30d77arybchik	}
11858e30d77arybchik
11868e30d77arybchik	if ((rc = tlv_delete(&cursor)) != 0)
11878e30d77arybchik		goto fail2;
11888e30d77arybchik
11898e30d77arybchik	return (0);
11908e30d77arybchik
11918e30d77arybchikfail2:
11928e30d77arybchik	EFSYS_PROBE(fail2);
11938e30d77arybchikfail1:
11948e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
11958e30d77arybchik
11968e30d77arybchik	return (rc);
11978e30d77arybchik}
11988e30d77arybchik
11998e30d77arybchik	__checkReturn		efx_rc_t
12008e30d77arybchikef10_nvram_buffer_finish(
12018e30d77arybchik	__in_bcount(buffer_size)
12028e30d77arybchik				caddr_t bufferp,
12038e30d77arybchik	__in			size_t buffer_size)
12048e30d77arybchik{
12058e30d77arybchik	efx_rc_t rc;
12068e30d77arybchik	tlv_cursor_t cursor;
12078e30d77arybchik
12088e30d77arybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
12098e30d77arybchik			buffer_size)) != 0) {
12108e30d77arybchik		rc = EFAULT;
12118e30d77arybchik		goto fail1;
12128e30d77arybchik	}
12138e30d77arybchik
12148e30d77arybchik	if ((rc = tlv_require_end(&cursor)) != 0)
12158e30d77arybchik		goto fail2;
12168e30d77arybchik
12178e30d77arybchik	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
12188e30d77arybchik		goto fail3;
12198e30d77arybchik
12208e30d77arybchik	return (0);
12218e30d77arybchik
12228e30d77arybchikfail3:
12238e30d77arybchik	EFSYS_PROBE(fail3);
12248e30d77arybchikfail2:
12258e30d77arybchik	EFSYS_PROBE(fail2);
12268e30d77arybchikfail1:
12278e30d77arybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
12288e30d77arybchik
12298e30d77arybchik	return (rc);
12308e30d77arybchik}
12318e30d77arybchik
1232e7896dbarybchik/*
1233e7896dbarybchik * Read and validate a segment from a partition. A segment is a complete
1234e7896dbarybchik * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
1235e7896dbarybchik * be multiple segments in a partition, so seg_offset allows segments
1236e7896dbarybchik * beyond the first to be read.
1237e7896dbarybchik */
123816fdc5earybchikstatic	__checkReturn			efx_rc_t
1239c86fe87arybchikef10_nvram_read_tlv_segment(
1240e7896dbarybchik	__in				efx_nic_t *enp,
1241e7896dbarybchik	__in				uint32_t partn,
1242e7896dbarybchik	__in				size_t seg_offset,
1243e7896dbarybchik	__in_bcount(max_seg_size)	caddr_t seg_data,
1244e7896dbarybchik	__in				size_t max_seg_size)
1245220281carybchik{
1246220281carybchik	tlv_cursor_t cursor;
1247220281carybchik	struct tlv_partition_header *header;
1248220281carybchik	struct tlv_partition_trailer *trailer;
1249220281carybchik	size_t total_length;
1250220281carybchik	uint32_t cksum;
1251220281carybchik	int pos;
125216fdc5earybchik	efx_rc_t rc;
1253220281carybchik
1254c86fe87arybchik	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
1255220281carybchik
1256e7896dbarybchik	if ((seg_data == NULL) || (max_seg_size == 0)) {
1257220281carybchik		rc = EINVAL;
1258220281carybchik		goto fail1;
1259220281carybchik	}
1260220281carybchik
1261e7896dbarybchik	/* Read initial chunk of the segment, starting at offset */
1262e6feb41arybchik	if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
1263e6feb41arybchik		    EF10_NVRAM_CHUNK,
1264e6feb41arybchik		    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
1265220281carybchik		goto fail2;
1266220281carybchik	}
1267220281carybchik
1268e7896dbarybchik	/* A PARTITION_HEADER tag must be the first item at the given offset */
1269f85d27earybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1270e7896dbarybchik		    max_seg_size)) != 0) {
1271220281carybchik		rc = EFAULT;
1272220281carybchik		goto fail3;
1273220281carybchik	}
1274220281carybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1275220281carybchik		rc = EINVAL;
1276220281carybchik		goto fail4;
1277220281carybchik	}
1278220281carybchik	header = (struct tlv_partition_header *)tlv_item(&cursor);
1279220281carybchik
1280e7896dbarybchik	/* Check TLV segment length (includes the END tag) */
1281220281carybchik	total_length = __LE_TO_CPU_32(header->total_length);
1282e7896dbarybchik	if (total_length > max_seg_size) {
1283220281carybchik		rc = EFBIG;
1284220281carybchik		goto fail5;
1285220281carybchik	}
1286220281carybchik
1287e7896dbarybchik	/* Read the remaining segment content */
1288c86fe87arybchik	if (total_length > EF10_NVRAM_CHUNK) {
1289e6feb41arybchik		if ((rc = ef10_nvram_partn_read_mode(enp, partn,
1290c86fe87arybchik			    seg_offset + EF10_NVRAM_CHUNK,
1291c86fe87arybchik			    seg_data + EF10_NVRAM_CHUNK,
1292e6feb41arybchik			    total_length - EF10_NVRAM_CHUNK,
1293e6feb41arybchik			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
1294220281carybchik			goto fail6;
1295220281carybchik	}
1296220281carybchik
1297e7896dbarybchik	/* Check segment ends with PARTITION_TRAILER and END tags */
1298220281carybchik	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1299220281carybchik		rc = EINVAL;
1300220281carybchik		goto fail7;
1301220281carybchik	}
1302220281carybchik	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1303220281carybchik
1304220281carybchik	if ((rc = tlv_advance(&cursor)) != 0) {
1305220281carybchik		rc = EINVAL;
1306220281carybchik		goto fail8;
1307220281carybchik	}
1308220281carybchik	if (tlv_tag(&cursor) != TLV_TAG_END) {
1309220281carybchik		rc = EINVAL;
1310220281carybchik		goto fail9;
1311220281carybchik	}
1312220281carybchik
1313e7896dbarybchik	/* Check data read from segment is consistent */
1314220281carybchik	if (trailer->generation != header->generation) {
1315220281carybchik		/*
1316220281carybchik		 * The partition data may have been modified between successive
1317220281carybchik		 * MCDI NVRAM_READ requests by the MC or another PCI function.
1318220281carybchik		 *
1319220281carybchik		 * The caller must retry to obtain consistent partition data.
1320220281carybchik		 */
1321220281carybchik		rc = EAGAIN;
1322220281carybchik		goto fail10;
1323220281carybchik	}
1324220281carybchik
1325e7896dbarybchik	/* Verify segment checksum */
1326220281carybchik	cksum = 0;
1327220281carybchik	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
1328e7896dbarybchik		cksum += *((uint32_t *)(seg_data + pos));
1329220281carybchik	}
1330220281carybchik	if (cksum != 0) {
1331220281carybchik		rc = EINVAL;
1332220281carybchik		goto fail11;
1333220281carybchik	}
1334220281carybchik
1335220281carybchik	return (0);
1336220281carybchik
1337220281carybchikfail11:
1338220281carybchik	EFSYS_PROBE(fail11);
1339220281carybchikfail10:
1340220281carybchik	EFSYS_PROBE(fail10);
1341220281carybchikfail9:
1342220281carybchik	EFSYS_PROBE(fail9);
1343220281carybchikfail8:
1344220281carybchik	EFSYS_PROBE(fail8);
1345220281carybchikfail7:
1346220281carybchik	EFSYS_PROBE(fail7);
1347220281carybchikfail6:
1348220281carybchik	EFSYS_PROBE(fail6);
1349220281carybchikfail5:
1350220281carybchik	EFSYS_PROBE(fail5);
1351220281carybchikfail4:
1352220281carybchik	EFSYS_PROBE(fail4);
1353220281carybchikfail3:
1354220281carybchik	EFSYS_PROBE(fail3);
1355220281carybchikfail2:
1356220281carybchik	EFSYS_PROBE(fail2);
1357220281carybchikfail1:
135816fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1359220281carybchik
1360220281carybchik	return (rc);
1361220281carybchik}
1362220281carybchik
1363220281carybchik/*
1364220281carybchik * Read a single TLV item from a host memory
1365e7896dbarybchik * buffer containing a TLV formatted segment.
1366220281carybchik */
136716fdc5earybchik	__checkReturn		efx_rc_t
1368c86fe87arybchikef10_nvram_buf_read_tlv(
1369220281carybchik	__in				efx_nic_t *enp,
1370e7896dbarybchik	__in_bcount(max_seg_size)	caddr_t seg_data,
1371e7896dbarybchik	__in				size_t max_seg_size,
1372220281carybchik	__in				uint32_t tag,
1373220281carybchik	__deref_out_bcount_opt(*sizep)	caddr_t *datap,
1374220281carybchik	__out				size_t *sizep)
1375220281carybchik{
1376220281carybchik	tlv_cursor_t cursor;
1377220281carybchik	caddr_t data;
1378220281carybchik	size_t length;
1379220281carybchik	caddr_t value;
138016fdc5earybchik	efx_rc_t rc;
1381220281carybchik
1382b44a54farybchik	_NOTE(ARGUNUSED(enp))
1383b44a54farybchik
1384e7896dbarybchik	if ((seg_data == NULL) || (max_seg_size == 0)) {
1385220281carybchik		rc = EINVAL;
1386220281carybchik		goto fail1;
1387220281carybchik	}
1388220281carybchik
1389e7896dbarybchik	/* Find requested TLV tag in segment data */
1390f85d27earybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1391e7896dbarybchik		    max_seg_size)) != 0) {
1392220281carybchik		rc = EFAULT;
1393220281carybchik		goto fail2;
1394220281carybchik	}
1395220281carybchik	if ((rc = tlv_find(&cursor, tag)) != 0) {
1396220281carybchik		rc = ENOENT;
1397220281carybchik		goto fail3;
1398220281carybchik	}
1399f85d27earybchik	value = (caddr_t)tlv_value(&cursor);
1400220281carybchik	length = tlv_length(&cursor);
1401220281carybchik
1402220281carybchik	if (length == 0)
1403220281carybchik		data = NULL;
1404220281carybchik	else {
1405220281carybchik		/* Copy out data from TLV item */
1406220281carybchik		EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
1407220281carybchik		if (data == NULL) {
1408220281carybchik			rc = ENOMEM;
1409220281carybchik			goto fail4;
1410220281carybchik		}
1411220281carybchik		memcpy(data, value, length);
1412220281carybchik	}
1413220281carybchik
1414220281carybchik	*datap = data;
1415220281carybchik	*sizep = length;
1416220281carybchik
1417220281carybchik	return (0);
1418220281carybchik
1419220281carybchikfail4:
1420220281carybchik	EFSYS_PROBE(fail4);
1421220281carybchikfail3:
1422220281carybchik	EFSYS_PROBE(fail3);
1423220281carybchikfail2:
1424220281carybchik	EFSYS_PROBE(fail2);
1425220281carybchikfail1:
142616fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1427220281carybchik
1428220281carybchik	return (rc);
1429220281carybchik}
1430220281carybchik
1431e7896dbarybchik/* Read a single TLV item from the first segment in a TLV formatted partition */
143216fdc5earybchik	__checkReturn		efx_rc_t
1433c86fe87arybchikef10_nvram_partn_read_tlv(
1434e7896dbarybchik	__in					efx_nic_t *enp,
1435e7896dbarybchik	__in					uint32_t partn,
1436e7896dbarybchik	__in					uint32_t tag,
1437e7896dbarybchik	__deref_out_bcount_opt(*seg_sizep)	caddr_t *seg_datap,
1438e7896dbarybchik	__out					size_t *seg_sizep)
1439220281carybchik{
1440e7896dbarybchik	caddr_t seg_data = NULL;
1441220281carybchik	size_t partn_size = 0;
1442220281carybchik	size_t length;
1443220281carybchik	caddr_t data;
1444220281carybchik	int retry;
144516fdc5earybchik	efx_rc_t rc;
1446220281carybchik
1447220281carybchik	/* Allocate sufficient memory for the entire partition */
1448c86fe87arybchik	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1449220281carybchik		goto fail1;
1450220281carybchik
1451220281carybchik	if (partn_size == 0) {
1452220281carybchik		rc = ENOENT;
1453220281carybchik		goto fail2;
1454220281carybchik	}
1455220281carybchik
1456e7896dbarybchik	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
1457e7896dbarybchik	if (seg_data == NULL) {
1458220281carybchik		rc = ENOMEM;
1459220281carybchik		goto fail3;
1460220281carybchik	}
1461220281carybchik
1462220281carybchik	/*
1463e7896dbarybchik	 * Read the first segment in a TLV partition. Retry until consistent
1464e7896dbarybchik	 * segment contents are returned. Inconsistent data may be read if:
1465e7896dbarybchik	 *  a) the segment contents are invalid
1466220281carybchik	 *  b) the MC has rebooted while we were reading the partition
1467220281carybchik	 *  c) the partition has been modified while we were reading it
1468220281carybchik	 * Limit retry attempts to ensure forward progress.
1469220281carybchik	 */
1470220281carybchik	retry = 10;
1471220281carybchik	do {
1472782e9aaarybchik		if ((rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
1473782e9aaarybchik		    seg_data, partn_size)) != 0)
1474782e9aaarybchik			--retry;
1475782e9aaarybchik	} while ((rc == EAGAIN) && (retry > 0));
1476220281carybchik
1477220281carybchik	if (rc != 0) {
1478e7896dbarybchik		/* Failed to obtain consistent segment data */
1479782e9aaarybchik		if (rc == EAGAIN)
1480782e9aaarybchik			rc = EIO;
1481782e9aaarybchik
1482220281carybchik		goto fail4;
1483220281carybchik	}
1484220281carybchik
1485c86fe87arybchik	if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
1486220281carybchik		    tag, &data, &length)) != 0)
1487220281carybchik		goto fail5;
1488220281carybchik
1489e7896dbarybchik	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1490220281carybchik
1491e7896dbarybchik	*seg_datap = data;
1492e7896dbarybchik	*seg_sizep = length;
1493220281carybchik
1494220281carybchik	return (0);
1495220281carybchik
1496220281carybchikfail5:
1497220281carybchik	EFSYS_PROBE(fail5);
1498220281carybchikfail4:
1499220281carybchik	EFSYS_PROBE(fail4);
1500220281carybchik
1501e7896dbarybchik	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1502e7896dbarybchikfail3:
1503e7896dbarybchik	EFSYS_PROBE(fail3);
1504e7896dbarybchikfail2:
1505e7896dbarybchik	EFSYS_PROBE(fail2);
1506e7896dbarybchikfail1:
150716fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1508e7896dbarybchik
1509e7896dbarybchik	return (rc);
1510e7896dbarybchik}
1511e7896dbarybchik
1512e7896dbarybchik/* Compute the size of a segment. */
151316fdc5earybchik	static	__checkReturn	efx_rc_t
1514c86fe87arybchikef10_nvram_buf_segment_size(
1515e7896dbarybchik	__in			caddr_t seg_data,
1516e7896dbarybchik	__in			size_t max_seg_size,
1517e7896dbarybchik	__out			size_t *seg_sizep)
1518e7896dbarybchik{
151916fdc5earybchik	efx_rc_t rc;
1520e7896dbarybchik	tlv_cursor_t cursor;
1521e7896dbarybchik	struct tlv_partition_header *header;
1522e7896dbarybchik	uint32_t cksum;
1523e7896dbarybchik	int pos;
1524e7896dbarybchik	uint32_t *end_tag_position;
1525e7896dbarybchik	uint32_t segment_length;
1526e7896dbarybchik
1527e7896dbarybchik	/* A PARTITION_HEADER tag must be the first item at the given offset */
1528f85d27earybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1529e7896dbarybchik		    max_seg_size)) != 0) {
1530e7896dbarybchik		rc = EFAULT;
1531e7896dbarybchik		goto fail1;
1532e7896dbarybchik	}
1533e7896dbarybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1534e7896dbarybchik		rc = EINVAL;
1535e7896dbarybchik		goto fail2;
1536e7896dbarybchik	}
1537e7896dbarybchik	header = (struct tlv_partition_header *)tlv_item(&cursor);
1538e7896dbarybchik
1539e7896dbarybchik	/* Check TLV segment length (includes the END tag) */
1540e7896dbarybchik	*seg_sizep = __LE_TO_CPU_32(header->total_length);
1541e7896dbarybchik	if (*seg_sizep > max_seg_size) {
1542e7896dbarybchik		rc = EFBIG;
1543e7896dbarybchik		goto fail3;
1544e7896dbarybchik	}
1545e7896dbarybchik
1546e7896dbarybchik	/* Check segment ends with PARTITION_TRAILER and END tags */
1547e7896dbarybchik	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1548e7896dbarybchik		rc = EINVAL;
1549e7896dbarybchik		goto fail4;
1550e7896dbarybchik	}
1551e7896dbarybchik
1552e7896dbarybchik	if ((rc = tlv_advance(&cursor)) != 0) {
1553e7896dbarybchik		rc = EINVAL;
1554e7896dbarybchik		goto fail5;
1555e7896dbarybchik	}
1556e7896dbarybchik	if (tlv_tag(&cursor) != TLV_TAG_END) {
1557e7896dbarybchik		rc = EINVAL;
1558e7896dbarybchik		goto fail6;
1559e7896dbarybchik	}
1560e7896dbarybchik	end_tag_position = cursor.current;
1561e7896dbarybchik
1562e7896dbarybchik	/* Verify segment checksum */
1563e7896dbarybchik	cksum = 0;
1564e7896dbarybchik	for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
1565e7896dbarybchik		cksum += *((uint32_t *)(seg_data + pos));
1566e7896dbarybchik	}
1567e7896dbarybchik	if (cksum != 0) {
1568e7896dbarybchik		rc = EINVAL;
1569e7896dbarybchik		goto fail7;
1570e7896dbarybchik	}
1571e7896dbarybchik
1572e7896dbarybchik	/*
1573e7896dbarybchik	 * Calculate total length from HEADER to END tags and compare to
1574e7896dbarybchik	 * max_seg_size and the total_length field in the HEADER tag.
1575e7896dbarybchik	 */
1576e7896dbarybchik	segment_length = tlv_block_length_used(&cursor);
1577e7896dbarybchik
1578e7896dbarybchik	if (segment_length > max_seg_size) {
1579e7896dbarybchik		rc = EINVAL;
1580e7896dbarybchik		goto fail8;
1581e7896dbarybchik	}
1582e7896dbarybchik
1583e7896dbarybchik	if (segment_length != *seg_sizep) {
1584e7896dbarybchik		rc = EINVAL;
1585e7896dbarybchik		goto fail9;
1586e7896dbarybchik	}
1587e7896dbarybchik
1588e7896dbarybchik	/* Skip over the first HEADER tag. */
1589e7896dbarybchik	rc = tlv_rewind(&cursor);
1590e7896dbarybchik	rc = tlv_advance(&cursor);
1591e7896dbarybchik
1592e7896dbarybchik	while (rc == 0) {
1593e7896dbarybchik		if (tlv_tag(&cursor) == TLV_TAG_END) {
1594e7896dbarybchik			/* Check that the END tag is the one found earlier. */
1595e7896dbarybchik			if (cursor.current != end_tag_position)
1596e7896dbarybchik				goto fail10;
1597e7896dbarybchik			break;
1598e7896dbarybchik		}
1599e7896dbarybchik		/* Check for duplicate HEADER tags before the END tag. */
1600e7896dbarybchik		if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
1601e7896dbarybchik			rc = EINVAL;
1602e7896dbarybchik			goto fail11;
1603e7896dbarybchik		}
1604e7896dbarybchik
1605e7896dbarybchik		rc = tlv_advance(&cursor);
1606e7896dbarybchik	}
1607e7896dbarybchik	if (rc != 0)
1608e7896dbarybchik		goto fail12;
1609e7896dbarybchik
1610e7896dbarybchik	return (0);
1611e7896dbarybchik
1612e7896dbarybchikfail12:
1613e7896dbarybchik	EFSYS_PROBE(fail12);
1614e7896dbarybchikfail11:
1615e7896dbarybchik	EFSYS_PROBE(fail11);
1616e7896dbarybchikfail10:
1617e7896dbarybchik	EFSYS_PROBE(fail10);
1618e7896dbarybchikfail9:
1619e7896dbarybchik	EFSYS_PROBE(fail9);
1620e7896dbarybchikfail8:
1621e7896dbarybchik	EFSYS_PROBE(fail8);
1622e7896dbarybchikfail7:
1623e7896dbarybchik	EFSYS_PROBE(fail7);
1624e7896dbarybchikfail6:
1625e7896dbarybchik	EFSYS_PROBE(fail6);
1626e7896dbarybchikfail5:
1627e7896dbarybchik	EFSYS_PROBE(fail5);
1628e7896dbarybchikfail4:
1629e7896dbarybchik	EFSYS_PROBE(fail4);
1630220281carybchikfail3:
1631220281carybchik	EFSYS_PROBE(fail3);
1632220281carybchikfail2:
1633220281carybchik	EFSYS_PROBE(fail2);
1634220281carybchikfail1:
163516fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1636220281carybchik
1637220281carybchik	return (rc);
1638220281carybchik}
1639220281carybchik
1640220281carybchik/*
1641220281carybchik * Add or update a single TLV item in a host memory buffer containing a TLV
1642e7896dbarybchik * formatted segment. Historically partitions consisted of only one segment.
1643220281carybchik */
164416fdc5earybchik	__checkReturn			efx_rc_t
1645c86fe87arybchikef10_nvram_buf_write_tlv(
1646e7896dbarybchik	__inout_bcount(max_seg_size)	caddr_t seg_data,
1647e7896dbarybchik	__in				size_t max_seg_size,
1648220281carybchik	__in				uint32_t tag,
1649220281carybchik	__in_bcount(tag_size)		caddr_t tag_data,
1650220281carybchik	__in				size_t tag_size,
1651220281carybchik	__out				size_t *total_lengthp)
1652220281carybchik{
1653220281carybchik	tlv_cursor_t cursor;
1654220281carybchik	struct tlv_partition_header *header;
1655220281carybchik	struct tlv_partition_trailer *trailer;
1656220281carybchik	uint32_t generation;
1657220281carybchik	uint32_t cksum;
1658220281carybchik	int pos;
165916fdc5earybchik	efx_rc_t rc;
1660220281carybchik
1661e7896dbarybchik	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
1662f85d27earybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1663e7896dbarybchik			max_seg_size)) != 0) {
1664220281carybchik		rc = EFAULT;
1665220281carybchik		goto fail1;
1666220281carybchik	}
1667220281carybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1668220281carybchik		rc = EINVAL;
1669220281carybchik		goto fail2;
1670220281carybchik	}
1671220281carybchik	header = (struct tlv_partition_header *)tlv_item(&cursor);
1672220281carybchik
1673220281carybchik	/* Update the TLV chain to contain the new data */
1674220281carybchik	if ((rc = tlv_find(&cursor, tag)) == 0) {
1675220281carybchik		/* Modify existing TLV item */
1676220281carybchik		if ((rc = tlv_modify(&cursor, tag,
1677f85d27earybchik			    (uint8_t *)tag_data, tag_size)) != 0)
1678220281carybchik			goto fail3;
1679220281carybchik	} else {
1680220281carybchik		/* Insert a new TLV item before the PARTITION_TRAILER */
1681220281carybchik		rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1682220281carybchik		if (rc != 0) {
1683220281carybchik			rc = EINVAL;
1684220281carybchik			goto fail4;
1685220281carybchik		}
1686220281carybchik		if ((rc = tlv_insert(&cursor, tag,
1687f85d27earybchik			    (uint8_t *)tag_data, tag_size)) != 0) {
1688220281carybchik			rc = EINVAL;
1689220281carybchik			goto fail5;
1690220281carybchik		}
1691220281carybchik	}
1692220281carybchik
1693220281carybchik	/* Find the trailer tag */
1694220281carybchik	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1695220281carybchik		rc = EINVAL;
1696220281carybchik		goto fail6;
1697220281carybchik	}
1698220281carybchik	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1699220281carybchik
1700220281carybchik	/* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1701220281carybchik	*total_lengthp = tlv_block_length_used(&cursor);
1702e7896dbarybchik	if (*total_lengthp > max_seg_size) {
1703e7896dbarybchik		rc = ENOSPC;
1704e7896dbarybchik		goto fail7;
1705e7896dbarybchik	}
1706220281carybchik	generation = __LE_TO_CPU_32(header->generation) + 1;
1707220281carybchik
1708220281carybchik	header->total_length	= __CPU_TO_LE_32(*total_lengthp);
1709220281carybchik	header->generation	= __CPU_TO_LE_32(generation);
1710220281carybchik	trailer->generation	= __CPU_TO_LE_32(generation);
1711220281carybchik
1712220281carybchik	/* Recompute PARTITION_TRAILER checksum */
1713220281carybchik	trailer->checksum = 0;
1714220281carybchik	cksum = 0;
1715220281carybchik	for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1716e7896dbarybchik		cksum += *((uint32_t *)(seg_data + pos));
1717220281carybchik	}
1718220281carybchik	trailer->checksum = ~cksum + 1;
1719220281carybchik
1720220281carybchik	return (0);
1721220281carybchik
1722e7896dbarybchikfail7:
1723e7896dbarybchik	EFSYS_PROBE(fail7);
1724220281carybchikfail6:
1725220281carybchik	EFSYS_PROBE(fail6);
1726220281carybchikfail5:
1727220281carybchik	EFSYS_PROBE(fail5);
1728220281carybchikfail4:
1729220281carybchik	EFSYS_PROBE(fail4);
1730220281carybchikfail3:
1731220281carybchik	EFSYS_PROBE(fail3);
1732220281carybchikfail2:
1733220281carybchik	EFSYS_PROBE(fail2);
1734220281carybchikfail1:
173516fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1736220281carybchik
1737220281carybchik	return (rc);
1738220281carybchik}
1739220281carybchik
1740e7896dbarybchik/*
1741e7896dbarybchik * Add or update a single TLV item in the first segment of a TLV formatted
1742e7896dbarybchik * dynamic config partition. The first segment is the current active
1743e7896dbarybchik * configuration.
1744e7896dbarybchik */
174516fdc5earybchik	__checkReturn		efx_rc_t
1746c86fe87arybchikef10_nvram_partn_write_tlv(
1747220281carybchik	__in			efx_nic_t *enp,
1748220281carybchik	__in			uint32_t partn,
1749220281carybchik	__in			uint32_t tag,
1750220281carybchik	__in_bcount(size)	caddr_t data,
1751220281carybchik	__in			size_t size)
1752220281carybchik{
1753c86fe87arybchik	return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1754e7896dbarybchik	    size, B_FALSE);
1755e7896dbarybchik}
1756e7896dbarybchik
1757e7896dbarybchik/*
1758e7896dbarybchik * Read a segment from nvram at the given offset into a buffer (segment_data)
1759e7896dbarybchik * and optionally write a new tag to it.
1760e7896dbarybchik */
17616b7c88earybchikstatic	__checkReturn		efx_rc_t
1762c86fe87arybchikef10_nvram_segment_write_tlv(
1763e7896dbarybchik	__in			efx_nic_t *enp,
1764e7896dbarybchik	__in			uint32_t partn,
1765e7896dbarybchik	__in			uint32_t tag,
1766e7896dbarybchik	__in_bcount(size)	caddr_t data,
1767e7896dbarybchik	__in			size_t size,
1768e7896dbarybchik	__inout			caddr_t *seg_datap,
1769e7896dbarybchik	__inout			size_t *partn_offsetp,
1770e7896dbarybchik	__inout			size_t *src_remain_lenp,
1771e7896dbarybchik	__inout			size_t *dest_remain_lenp,
1772e7896dbarybchik	__in			boolean_t write)
1773e7896dbarybchik{
177416fdc5earybchik	efx_rc_t rc;
177567b9a0farybchik	efx_rc_t status;
1776e7896dbarybchik	size_t original_segment_size;
1777e7896dbarybchik	size_t modified_segment_size;
1778e7896dbarybchik
1779e7896dbarybchik	/*
1780e7896dbarybchik	 * Read the segment from NVRAM into the segment_data buffer and validate
1781e7896dbarybchik	 * it, returning if it does not validate. This is not a failure unless
1782e7896dbarybchik	 * this is the first segment in a partition. In this case the caller
1783eed4bd2pfg	 * must propagate the error.
1784e7896dbarybchik	 */
1785c86fe87arybchik	status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1786e7896dbarybchik	    *seg_datap, *src_remain_lenp);
17876b7c88earybchik	if (status != 0) {
17886b7c88earybchik		rc = EINVAL;
17896b7c88earybchik		goto fail1;
17906b7c88earybchik	}
1791e7896dbarybchik
1792c86fe87arybchik	status = ef10_nvram_buf_segment_size(*seg_datap,
1793e7896dbarybchik	    *src_remain_lenp, &original_segment_size);
17946b7c88earybchik	if (status != 0) {
17956b7c88earybchik		rc = EINVAL;
17966b7c88earybchik		goto fail2;
17976b7c88earybchik	}
1798e7896dbarybchik
1799e7896dbarybchik	if (write) {
1800e7896dbarybchik		/* Update the contents of the segment in the buffer */
1801c86fe87arybchik		if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1802e7896dbarybchik			*dest_remain_lenp, tag, data, size,
18036b7c88earybchik			&modified_segment_size)) != 0) {
18046b7c88earybchik			goto fail3;
18056b7c88earybchik		}
1806e7896dbarybchik		*dest_remain_lenp -= modified_segment_size;
1807e7896dbarybchik		*seg_datap += modified_segment_size;
1808e7896dbarybchik	} else {
1809e7896dbarybchik		/*
1810e7896dbarybchik		 * We won't modify this segment, but still need to update the
1811e7896dbarybchik		 * remaining lengths and pointers.
1812e7896dbarybchik		 */
1813e7896dbarybchik		*dest_remain_lenp -= original_segment_size;
1814e7896dbarybchik		*seg_datap += original_segment_size;
1815e7896dbarybchik	}
1816e7896dbarybchik
1817e7896dbarybchik	*partn_offsetp += original_segment_size;
1818e7896dbarybchik	*src_remain_lenp -= original_segment_size;
1819e7896dbarybchik
1820e7896dbarybchik	return (0);
1821e7896dbarybchik
18226b7c88earybchikfail3:
18236b7c88earybchik	EFSYS_PROBE(fail3);
18246b7c88earybchikfail2:
18256b7c88earybchik	EFSYS_PROBE(fail2);
1826e7896dbarybchikfail1:
182716fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1828e7896dbarybchik
1829e7896dbarybchik	return (rc);
1830e7896dbarybchik}
1831e7896dbarybchik
1832e7896dbarybchik/*
1833e7896dbarybchik * Add or update a single TLV item in either the first segment or in all
1834e7896dbarybchik * segments in a TLV formatted dynamic config partition. Dynamic config
1835e7896dbarybchik * partitions on boards that support RFID are divided into a number of segments,
1836e7896dbarybchik * each formatted like a partition, with header, trailer and end tags. The first
1837e7896dbarybchik * segment is the current active configuration.
1838e7896dbarybchik *
1839e7896dbarybchik * The segments are initialised by manftest and each contain a different
1840e7896dbarybchik * configuration e.g. firmware variant. The firmware can be instructed
1841e7896dbarybchik * via RFID to copy a segment to replace the first segment, hence changing the
1842e7896dbarybchik * active configuration.  This allows ops to change the configuration of a board
1843e7896dbarybchik * prior to shipment using RFID.
1844e7896dbarybchik *
1845e7896dbarybchik * Changes to the dynamic config may need to be written to all segments (e.g.
1846e7896dbarybchik * firmware versions) or just the first segment (changes to the active
1847e7896dbarybchik * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1848e7896dbarybchik * If only the first segment is written the code still needs to be aware of the
1849e7896dbarybchik * possible presence of subsequent segments as writing to a segment may cause
1850e7896dbarybchik * its size to increase, which would overwrite the subsequent segments and
1851e7896dbarybchik * invalidate them.
1852e7896dbarybchik */
185316fdc5earybchik	__checkReturn		efx_rc_t
1854c86fe87arybchikef10_nvram_partn_write_segment_tlv(
1855e7896dbarybchik	__in			efx_nic_t *enp,
1856e7896dbarybchik	__in			uint32_t partn,
1857e7896dbarybchik	__in			uint32_t tag,
1858e7896dbarybchik	__in_bcount(size)	caddr_t data,
1859e7896dbarybchik	__in			size_t size,
1860e7896dbarybchik	__in			boolean_t all_segments)
1861e7896dbarybchik{
1862e7896dbarybchik	size_t partn_size = 0;
1863220281carybchik	caddr_t partn_data;
1864e7896dbarybchik	size_t total_length = 0;
186516fdc5earybchik	efx_rc_t rc;
1866e7896dbarybchik	size_t current_offset = 0;
1867e7896dbarybchik	size_t remaining_original_length;
1868e7896dbarybchik	size_t remaining_modified_length;
1869e7896dbarybchik	caddr_t segment_data;
1870220281carybchik
1871220281carybchik	EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1872220281carybchik
1873220281carybchik	/* Allocate sufficient memory for the entire partition */
1874c86fe87arybchik	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1875220281carybchik		goto fail1;
1876220281carybchik
1877220281carybchik	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1878220281carybchik	if (partn_data == NULL) {
1879220281carybchik		rc = ENOMEM;
1880220281carybchik		goto fail2;
1881220281carybchik	}
1882220281carybchik
1883e7896dbarybchik	remaining_original_length = partn_size;
1884e7896dbarybchik	remaining_modified_length = partn_size;
1885e7896dbarybchik	segment_data = partn_data;
1886e7896dbarybchik
1887220281carybchik	/* Lock the partition */
1888c86fe87arybchik	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1889220281carybchik		goto fail3;
1890220281carybchik
1891e7896dbarybchik	/* Iterate over each (potential) segment to update it. */
1892e7896dbarybchik	do {
1893e7896dbarybchik		boolean_t write = all_segments || current_offset == 0;
1894220281carybchik
1895c86fe87arybchik		rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1896e7896dbarybchik		    &segment_data, &current_offset, &remaining_original_length,
1897e7896dbarybchik		    &remaining_modified_length, write);
1898e7896dbarybchik		if (rc != 0) {
1899e7896dbarybchik			if (current_offset == 0) {
1900e7896dbarybchik				/*
1901e7896dbarybchik				 * If no data has been read then the first
1902e7896dbarybchik				 * segment is invalid, which is an error.
1903e7896dbarybchik				 */
1904e7896dbarybchik				goto fail4;
1905e7896dbarybchik			}
1906e7896dbarybchik			break;
1907e7896dbarybchik		}
1908e7896dbarybchik	} while (current_offset < partn_size);
1909e7896dbarybchik
1910e7896dbarybchik	total_length = segment_data - partn_data;
1911e7896dbarybchik
1912e7896dbarybchik	/*
1913e7896dbarybchik	 * We've run out of space.  This should actually be dealt with by
1914c86fe87arybchik	 * ef10_nvram_buf_write_tlv returning ENOSPC.
1915e7896dbarybchik	 */
1916e7896dbarybchik	if (total_length > partn_size) {
1917e7896dbarybchik		rc = ENOSPC;
1918220281carybchik		goto fail5;
1919e7896dbarybchik	}
1920220281carybchik
1921e7896dbarybchik	/* Erase the whole partition in NVRAM */
1922c86fe87arybchik	if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1923220281carybchik		goto fail6;
1924220281carybchik
1925e7896dbarybchik	/* Write new partition contents from the buffer to NVRAM */
1926c86fe87arybchik	if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1927220281carybchik		    total_length)) != 0)
1928220281carybchik		goto fail7;
1929220281carybchik
1930220281carybchik	/* Unlock the partition */
1931002c0e7arybchik	(void) ef10_nvram_partn_unlock(enp, partn, NULL);
1932220281carybchik
1933220281carybchik	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1934220281carybchik
1935220281carybchik	return (0);
1936220281carybchik
1937220281carybchikfail7:
1938220281carybchik	EFSYS_PROBE(fail7);
1939220281carybchikfail6:
1940220281carybchik	EFSYS_PROBE(fail6);
1941220281carybchikfail5:
1942220281carybchik	EFSYS_PROBE(fail5);
1943220281carybchikfail4:
1944220281carybchik	EFSYS_PROBE(fail4);
1945220281carybchik
1946002c0e7arybchik	(void) ef10_nvram_partn_unlock(enp, partn, NULL);
1947220281carybchikfail3:
1948220281carybchik	EFSYS_PROBE(fail3);
1949220281carybchik
1950220281carybchik	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1951220281carybchikfail2:
1952220281carybchik	EFSYS_PROBE(fail2);
1953220281carybchikfail1:
195416fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1955220281carybchik
1956220281carybchik	return (rc);
1957220281carybchik}
1958220281carybchik
1959e7896dbarybchik/*
1960e7896dbarybchik * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1961e7896dbarybchik * not the data used by the segments in the partition.
1962e7896dbarybchik */
196316fdc5earybchik	__checkReturn		efx_rc_t
1964c86fe87arybchikef10_nvram_partn_size(
1965220281carybchik	__in			efx_nic_t *enp,
1966ae48f39arybchik	__in			uint32_t partn,
1967220281carybchik	__out			size_t *sizep)
1968220281carybchik{
196916fdc5earybchik	efx_rc_t rc;
1970220281carybchik
1971db96dd5arybchik	if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1972db96dd5arybchik	    NULL, NULL, NULL)) != 0)
1973220281carybchik		goto fail1;
1974220281carybchik
1975220281carybchik	return (0);
1976220281carybchik
1977220281carybchikfail1:
197816fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1979220281carybchik
1980220281carybchik	return (rc);
1981220281carybchik}
1982220281carybchik
198316fdc5earybchik	__checkReturn		efx_rc_t
1984c86fe87arybchikef10_nvram_partn_lock(
1985220281carybchik	__in			efx_nic_t *enp,
1986ae48f39arybchik	__in			uint32_t partn)
1987220281carybchik{
198816fdc5earybchik	efx_rc_t rc;
1989220281carybchik
1990220281carybchik	if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1991220281carybchik		goto fail1;
1992220281carybchik
1993220281carybchik	return (0);
1994220281carybchik
1995220281carybchikfail1:
199616fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1997220281carybchik
1998220281carybchik	return (rc);
1999220281carybchik}
2000220281carybchik
200116fdc5earybchik	__checkReturn		efx_rc_t
2002e6feb41arybchikef10_nvram_partn_read_mode(
2003220281carybchik	__in			efx_nic_t *enp,
2004ae48f39arybchik	__in			uint32_t partn,
2005220281carybchik	__in			unsigned int offset,
2006220281carybchik	__out_bcount(size)	caddr_t data,
2007e6feb41arybchik	__in			size_t size,
2008e6feb41arybchik	__in			uint32_t mode)
2009220281carybchik{
2010220281carybchik	size_t chunk;
201116fdc5earybchik	efx_rc_t rc;
2012220281carybchik
2013220281carybchik	while (size > 0) {
2014c86fe87arybchik		chunk = MIN(size, EF10_NVRAM_CHUNK);
2015220281carybchik
2016220281carybchik		if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
2017e6feb41arybchik			    data, chunk, mode)) != 0) {
2018220281carybchik			goto fail1;
2019220281carybchik		}
2020220281carybchik
2021220281carybchik		size -= chunk;
2022220281carybchik		data += chunk;
2023220281carybchik		offset += chunk;
2024220281carybchik	}
2025220281carybchik
2026220281carybchik	return (0);
2027220281carybchik
2028220281carybchikfail1:
202916fdc5earybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2030220281carybchik
2031220281carybchik	return (rc);
2032220281carybchik}
2033220281carybchik
203416fdc5earybchik	__checkReturn		efx_rc_t
2035e6feb41arybchikef10_nvram_partn_read(
2036e6feb41arybchik	__in			efx_nic_t *enp,
2037e6feb41arybchik	__in			uint32_t partn,
2038e6feb41arybchik	__in			unsigned int offset,
2039e6feb41arybchik	__out_bcount(size)	caddr_t data,
2040e6feb41arybchik	__in			size_t size)
2041e6feb41arybchik{
2042e6feb41arybchik	/*
20434fc0798arybchik	 * An A/B partition has two data stores (current and backup).
20444fc0798arybchik	 * Read requests which come in through the EFX API expect to read the
20454fc0798arybchik	 * current, active store of an A/B partition. For non A/B partitions,
20464fc0798arybchik	 * there is only a single store and so the mode param is ignored.
2047e6feb41arybchik	 */
2048e6feb41arybchik	return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
2049e6feb41arybchik			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
2050e6feb41arybchik}
2051e6feb41arybchik
2052e6feb41arybchik	__checkReturn		efx_rc_t
20534fc0798arybchikef10_nvram_partn_read_backup(
20544fc0798arybchik	__in			efx_nic_t *enp,
2055