1283514Sarybchik/*-
2301388Sarybchik * Copyright (c) 2012-2016 Solarflare Communications Inc.
3283514Sarybchik * All rights reserved.
4283514Sarybchik *
5283514Sarybchik * Redistribution and use in source and binary forms, with or without
6283514Sarybchik * modification, are permitted provided that the following conditions are met:
7283514Sarybchik *
8283514Sarybchik * 1. Redistributions of source code must retain the above copyright notice,
9283514Sarybchik *    this list of conditions and the following disclaimer.
10283514Sarybchik * 2. Redistributions in binary form must reproduce the above copyright notice,
11283514Sarybchik *    this list of conditions and the following disclaimer in the documentation
12283514Sarybchik *    and/or other materials provided with the distribution.
13283514Sarybchik *
14283514Sarybchik * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15283514Sarybchik * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16283514Sarybchik * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17283514Sarybchik * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18283514Sarybchik * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19283514Sarybchik * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20283514Sarybchik * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21283514Sarybchik * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22283514Sarybchik * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23283514Sarybchik * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24283514Sarybchik * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25283514Sarybchik *
26283514Sarybchik * The views and conclusions contained in the software and documentation are
27283514Sarybchik * those of the authors and should not be interpreted as representing official
28283514Sarybchik * policies, either expressed or implied, of the FreeBSD Project.
29283514Sarybchik */
30283514Sarybchik
31283514Sarybchik#include <sys/cdefs.h>
32283514Sarybchik__FBSDID("$FreeBSD: stable/10/sys/dev/sfxge/common/ef10_nvram.c 342512 2018-12-26 10:21:40Z arybchik $");
33283514Sarybchik
34283514Sarybchik#include "efx.h"
35283514Sarybchik#include "efx_impl.h"
36283514Sarybchik
37299602Sarybchik#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
38283514Sarybchik
39283514Sarybchik#if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
40283514Sarybchik
41283514Sarybchik#include "ef10_tlv_layout.h"
42283514Sarybchik
43283514Sarybchik/* Cursor for TLV partition format */
44283514Sarybchiktypedef struct tlv_cursor_s {
45283514Sarybchik	uint32_t	*block;			/* Base of data block */
46283514Sarybchik	uint32_t	*current;		/* Cursor position */
47283514Sarybchik	uint32_t	*end;			/* End tag position */
48283514Sarybchik	uint32_t	*limit;			/* Last dword of data block */
49283514Sarybchik} tlv_cursor_t;
50283514Sarybchik
51299319Sarybchiktypedef struct nvram_partition_s {
52299319Sarybchik	uint16_t type;
53299319Sarybchik	uint8_t chip_select;
54299319Sarybchik	uint8_t flags;
55299319Sarybchik	/*
56299319Sarybchik	 * The full length of the NVRAM partition.
57299319Sarybchik	 * This is different from tlv_partition_header.total_length,
58299319Sarybchik	 *  which can be smaller.
59299319Sarybchik	 */
60299319Sarybchik	uint32_t length;
61299319Sarybchik	uint32_t erase_size;
62299319Sarybchik	uint32_t *data;
63299319Sarybchik	tlv_cursor_t tlv_cursor;
64299319Sarybchik} nvram_partition_t;
65299319Sarybchik
66299319Sarybchik
67291436Sarybchikstatic	__checkReturn		efx_rc_t
68283514Sarybchiktlv_validate_state(
69299319Sarybchik	__inout			tlv_cursor_t *cursor);
70283514Sarybchik
71283514Sarybchik
72299319Sarybchikstatic				void
73299319Sarybchiktlv_init_block(
74299319Sarybchik	__out	uint32_t	*block)
75299319Sarybchik{
76299319Sarybchik	*block = __CPU_TO_LE_32(TLV_TAG_END);
77299319Sarybchik}
78299319Sarybchik
79283514Sarybchikstatic				uint32_t
80283514Sarybchiktlv_tag(
81283514Sarybchik	__in	tlv_cursor_t	*cursor)
82283514Sarybchik{
83283514Sarybchik	uint32_t dword, tag;
84283514Sarybchik
85283514Sarybchik	dword = cursor->current[0];
86283514Sarybchik	tag = __LE_TO_CPU_32(dword);
87283514Sarybchik
88283514Sarybchik	return (tag);
89283514Sarybchik}
90283514Sarybchik
91283514Sarybchikstatic				size_t
92283514Sarybchiktlv_length(
93283514Sarybchik	__in	tlv_cursor_t	*cursor)
94283514Sarybchik{
95283514Sarybchik	uint32_t dword, length;
96283514Sarybchik
97283514Sarybchik	if (tlv_tag(cursor) == TLV_TAG_END)
98283514Sarybchik		return (0);
99283514Sarybchik
100283514Sarybchik	dword = cursor->current[1];
101283514Sarybchik	length = __LE_TO_CPU_32(dword);
102283514Sarybchik
103283514Sarybchik	return ((size_t)length);
104283514Sarybchik}
105283514Sarybchik
106283514Sarybchikstatic				uint8_t *
107283514Sarybchiktlv_value(
108283514Sarybchik	__in	tlv_cursor_t	*cursor)
109283514Sarybchik{
110283514Sarybchik	if (tlv_tag(cursor) == TLV_TAG_END)
111283514Sarybchik		return (NULL);
112283514Sarybchik
113283514Sarybchik	return ((uint8_t *)(&cursor->current[2]));
114283514Sarybchik}
115283514Sarybchik
116283514Sarybchikstatic				uint8_t *
117283514Sarybchiktlv_item(
118283514Sarybchik	__in	tlv_cursor_t	*cursor)
119283514Sarybchik{
120283514Sarybchik	if (tlv_tag(cursor) == TLV_TAG_END)
121283514Sarybchik		return (NULL);
122283514Sarybchik
123283514Sarybchik	return ((uint8_t *)cursor->current);
124283514Sarybchik}
125283514Sarybchik
126283514Sarybchik/*
127283514Sarybchik * TLV item DWORD length is tag + length + value (rounded up to DWORD)
128283514Sarybchik * equivalent to tlv_n_words_for_len in mc-comms tlv.c
129283514Sarybchik */
130283514Sarybchik#define	TLV_DWORD_COUNT(length) \
131283514Sarybchik	(1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
132283514Sarybchik
133283514Sarybchik
134283514Sarybchikstatic				uint32_t *
135283514Sarybchiktlv_next_item_ptr(
136283514Sarybchik	__in	tlv_cursor_t	*cursor)
137283514Sarybchik{
138283514Sarybchik	uint32_t length;
139283514Sarybchik
140283514Sarybchik	length = tlv_length(cursor);
141283514Sarybchik
142283514Sarybchik	return (cursor->current + TLV_DWORD_COUNT(length));
143283514Sarybchik}
144283514Sarybchik
145299319Sarybchikstatic	__checkReturn		efx_rc_t
146283514Sarybchiktlv_advance(
147299319Sarybchik	__inout	tlv_cursor_t	*cursor)
148283514Sarybchik{
149291436Sarybchik	efx_rc_t rc;
150283514Sarybchik
151283514Sarybchik	if ((rc = tlv_validate_state(cursor)) != 0)
152283514Sarybchik		goto fail1;
153283514Sarybchik
154283514Sarybchik	if (cursor->current == cursor->end) {
155283514Sarybchik		/* No more tags after END tag */
156283514Sarybchik		cursor->current = NULL;
157283514Sarybchik		rc = ENOENT;
158283514Sarybchik		goto fail2;
159283514Sarybchik	}
160283514Sarybchik
161283514Sarybchik	/* Advance to next item and validate */
162283514Sarybchik	cursor->current = tlv_next_item_ptr(cursor);
163283514Sarybchik
164283514Sarybchik	if ((rc = tlv_validate_state(cursor)) != 0)
165283514Sarybchik		goto fail3;
166283514Sarybchik
167283514Sarybchik	return (0);
168283514Sarybchik
169283514Sarybchikfail3:
170283514Sarybchik	EFSYS_PROBE(fail3);
171283514Sarybchikfail2:
172283514Sarybchik	EFSYS_PROBE(fail2);
173283514Sarybchikfail1:
174291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
175283514Sarybchik
176283514Sarybchik	return (rc);
177283514Sarybchik}
178283514Sarybchik
179291436Sarybchikstatic				efx_rc_t
180283514Sarybchiktlv_rewind(
181283514Sarybchik	__in	tlv_cursor_t	*cursor)
182283514Sarybchik{
183291436Sarybchik	efx_rc_t rc;
184283514Sarybchik
185283514Sarybchik	cursor->current = cursor->block;
186283514Sarybchik
187283514Sarybchik	if ((rc = tlv_validate_state(cursor)) != 0)
188283514Sarybchik		goto fail1;
189283514Sarybchik
190283514Sarybchik	return (0);
191283514Sarybchik
192283514Sarybchikfail1:
193291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
194283514Sarybchik
195283514Sarybchik	return (rc);
196283514Sarybchik}
197283514Sarybchik
198291436Sarybchikstatic				efx_rc_t
199283514Sarybchiktlv_find(
200299319Sarybchik	__inout	tlv_cursor_t	*cursor,
201283514Sarybchik	__in	uint32_t	tag)
202283514Sarybchik{
203291436Sarybchik	efx_rc_t rc;
204283514Sarybchik
205283514Sarybchik	rc = tlv_rewind(cursor);
206283514Sarybchik	while (rc == 0) {
207283514Sarybchik		if (tlv_tag(cursor) == tag)
208283514Sarybchik			break;
209283514Sarybchik
210283514Sarybchik		rc = tlv_advance(cursor);
211283514Sarybchik	}
212283514Sarybchik	return (rc);
213283514Sarybchik}
214283514Sarybchik
215291436Sarybchikstatic	__checkReturn		efx_rc_t
216283514Sarybchiktlv_validate_state(
217299319Sarybchik	__inout	tlv_cursor_t	*cursor)
218283514Sarybchik{
219291436Sarybchik	efx_rc_t rc;
220283514Sarybchik
221283514Sarybchik	/* Check cursor position */
222283514Sarybchik	if (cursor->current < cursor->block) {
223283514Sarybchik		rc = EINVAL;
224283514Sarybchik		goto fail1;
225283514Sarybchik	}
226283514Sarybchik	if (cursor->current > cursor->limit) {
227283514Sarybchik		rc = EINVAL;
228283514Sarybchik		goto fail2;
229283514Sarybchik	}
230283514Sarybchik
231283514Sarybchik	if (tlv_tag(cursor) != TLV_TAG_END) {
232283514Sarybchik		/* Check current item has space for tag and length */
233283514Sarybchik		if (cursor->current > (cursor->limit - 2)) {
234283514Sarybchik			cursor->current = NULL;
235283514Sarybchik			rc = EFAULT;
236283514Sarybchik			goto fail3;
237283514Sarybchik		}
238283514Sarybchik
239283514Sarybchik		/* Check we have value data for current item and another tag */
240283514Sarybchik		if (tlv_next_item_ptr(cursor) > (cursor->limit - 1)) {
241283514Sarybchik			cursor->current = NULL;
242283514Sarybchik			rc = EFAULT;
243283514Sarybchik			goto fail4;
244283514Sarybchik		}
245283514Sarybchik	}
246283514Sarybchik
247283514Sarybchik	return (0);
248283514Sarybchik
249283514Sarybchikfail4:
250283514Sarybchik	EFSYS_PROBE(fail4);
251283514Sarybchikfail3:
252283514Sarybchik	EFSYS_PROBE(fail3);
253283514Sarybchikfail2:
254283514Sarybchik	EFSYS_PROBE(fail2);
255283514Sarybchikfail1:
256291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
257283514Sarybchik
258283514Sarybchik	return (rc);
259283514Sarybchik}
260283514Sarybchik
261291436Sarybchikstatic				efx_rc_t
262283514Sarybchiktlv_init_cursor(
263293817Sarybchik	__out	tlv_cursor_t	*cursor,
264283514Sarybchik	__in	uint32_t	*block,
265299319Sarybchik	__in	uint32_t	*limit,
266299319Sarybchik	__in	uint32_t	*current)
267283514Sarybchik{
268283514Sarybchik	cursor->block	= block;
269283514Sarybchik	cursor->limit	= limit;
270283514Sarybchik
271299319Sarybchik	cursor->current	= current;
272283514Sarybchik	cursor->end	= NULL;
273283514Sarybchik
274283514Sarybchik	return (tlv_validate_state(cursor));
275283514Sarybchik}
276283514Sarybchik
277299319Sarybchikstatic	__checkReturn		efx_rc_t
278283514Sarybchiktlv_init_cursor_from_size(
279293817Sarybchik	__out	tlv_cursor_t	*cursor,
280299319Sarybchik	__in_bcount(size)
281299319Sarybchik		uint8_t		*block,
282283514Sarybchik	__in	size_t		size)
283283514Sarybchik{
284283514Sarybchik	uint32_t *limit;
285283514Sarybchik	limit = (uint32_t *)(block + size - sizeof (uint32_t));
286299319Sarybchik	return (tlv_init_cursor(cursor, (uint32_t *)block,
287299319Sarybchik		limit, (uint32_t *)block));
288283514Sarybchik}
289283514Sarybchik
290299319Sarybchikstatic	__checkReturn		efx_rc_t
291299319Sarybchiktlv_init_cursor_at_offset(
292299319Sarybchik	__out	tlv_cursor_t	*cursor,
293299319Sarybchik	__in_bcount(size)
294299319Sarybchik		uint8_t		*block,
295299319Sarybchik	__in	size_t		size,
296299319Sarybchik	__in	size_t		offset)
297299319Sarybchik{
298299319Sarybchik	uint32_t *limit;
299299319Sarybchik	uint32_t *current;
300299319Sarybchik	limit = (uint32_t *)(block + size - sizeof (uint32_t));
301299319Sarybchik	current = (uint32_t *)(block + offset);
302299319Sarybchik	return (tlv_init_cursor(cursor, (uint32_t *)block, limit, current));
303299319Sarybchik}
304299319Sarybchik
305299319Sarybchikstatic	__checkReturn		efx_rc_t
306283514Sarybchiktlv_require_end(
307299319Sarybchik	__inout	tlv_cursor_t	*cursor)
308283514Sarybchik{
309283514Sarybchik	uint32_t *pos;
310291436Sarybchik	efx_rc_t rc;
311283514Sarybchik
312283514Sarybchik	if (cursor->end == NULL) {
313283514Sarybchik		pos = cursor->current;
314283514Sarybchik		if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
315283514Sarybchik			goto fail1;
316283514Sarybchik
317283514Sarybchik		cursor->end = cursor->current;
318283514Sarybchik		cursor->current = pos;
319283514Sarybchik	}
320283514Sarybchik
321283514Sarybchik	return (0);
322283514Sarybchik
323283514Sarybchikfail1:
324291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
325283514Sarybchik
326283514Sarybchik	return (rc);
327283514Sarybchik}
328283514Sarybchik
329283514Sarybchikstatic				size_t
330283514Sarybchiktlv_block_length_used(
331299319Sarybchik	__inout	tlv_cursor_t	*cursor)
332283514Sarybchik{
333291436Sarybchik	efx_rc_t rc;
334283514Sarybchik
335283514Sarybchik	if ((rc = tlv_validate_state(cursor)) != 0)
336283514Sarybchik		goto fail1;
337283514Sarybchik
338283514Sarybchik	if ((rc = tlv_require_end(cursor)) != 0)
339283514Sarybchik		goto fail2;
340283514Sarybchik
341283514Sarybchik	/* Return space used (including the END tag) */
342283514Sarybchik	return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
343283514Sarybchik
344283514Sarybchikfail2:
345283514Sarybchik	EFSYS_PROBE(fail2);
346283514Sarybchikfail1:
347291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
348283514Sarybchik
349283514Sarybchik	return (0);
350283514Sarybchik}
351283514Sarybchik
352299319Sarybchikstatic		uint32_t *
353299319Sarybchiktlv_last_segment_end(
354299319Sarybchik	__in	tlv_cursor_t *cursor)
355299319Sarybchik{
356299319Sarybchik	tlv_cursor_t segment_cursor;
357299319Sarybchik	uint32_t *last_segment_end = cursor->block;
358299319Sarybchik	uint32_t *segment_start = cursor->block;
359283514Sarybchik
360299319Sarybchik	/*
361299319Sarybchik	 * Go through each segment and check that it has an end tag. If there
362299319Sarybchik	 * is no end tag then the previous segment was the last valid one,
363299319Sarybchik	 * so return the pointer to its end tag.
364299319Sarybchik	 */
365301400Sarybchik	for (;;) {
366299319Sarybchik		if (tlv_init_cursor(&segment_cursor, segment_start,
367299319Sarybchik		    cursor->limit, segment_start) != 0)
368299319Sarybchik			break;
369299319Sarybchik		if (tlv_require_end(&segment_cursor) != 0)
370299319Sarybchik			break;
371299319Sarybchik		last_segment_end = segment_cursor.end;
372299319Sarybchik		segment_start = segment_cursor.end + 1;
373299319Sarybchik	}
374299319Sarybchik
375299319Sarybchik	return (last_segment_end);
376299319Sarybchik}
377299319Sarybchik
378299319Sarybchik
379299319Sarybchikstatic				uint32_t *
380283514Sarybchiktlv_write(
381283514Sarybchik	__in			tlv_cursor_t *cursor,
382283514Sarybchik	__in			uint32_t tag,
383283514Sarybchik	__in_bcount(size)	uint8_t *data,
384283514Sarybchik	__in			size_t size)
385283514Sarybchik{
386283514Sarybchik	uint32_t len = size;
387283514Sarybchik	uint32_t *ptr;
388283514Sarybchik
389283514Sarybchik	ptr = cursor->current;
390283514Sarybchik
391283514Sarybchik	*ptr++ = __CPU_TO_LE_32(tag);
392283514Sarybchik	*ptr++ = __CPU_TO_LE_32(len);
393283514Sarybchik
394283514Sarybchik	if (len > 0) {
395283514Sarybchik		ptr[(len - 1) / sizeof (uint32_t)] = 0;
396283514Sarybchik		memcpy(ptr, data, len);
397283514Sarybchik		ptr += P2ROUNDUP(len, sizeof (uint32_t)) / sizeof (*ptr);
398283514Sarybchik	}
399283514Sarybchik
400283514Sarybchik	return (ptr);
401283514Sarybchik}
402283514Sarybchik
403291436Sarybchikstatic	__checkReturn		efx_rc_t
404283514Sarybchiktlv_insert(
405299319Sarybchik	__inout	tlv_cursor_t	*cursor,
406283514Sarybchik	__in	uint32_t	tag,
407299319Sarybchik	__in_bcount(size)
408299319Sarybchik		uint8_t		*data,
409283514Sarybchik	__in	size_t		size)
410283514Sarybchik{
411283514Sarybchik	unsigned int delta;
412299319Sarybchik	uint32_t *last_segment_end;
413291436Sarybchik	efx_rc_t rc;
414283514Sarybchik
415283514Sarybchik	if ((rc = tlv_validate_state(cursor)) != 0)
416283514Sarybchik		goto fail1;
417283514Sarybchik
418283514Sarybchik	if ((rc = tlv_require_end(cursor)) != 0)
419283514Sarybchik		goto fail2;
420283514Sarybchik
421283514Sarybchik	if (tag == TLV_TAG_END) {
422283514Sarybchik		rc = EINVAL;
423283514Sarybchik		goto fail3;
424283514Sarybchik	}
425283514Sarybchik
426299319Sarybchik	last_segment_end = tlv_last_segment_end(cursor);
427299319Sarybchik
428283514Sarybchik	delta = TLV_DWORD_COUNT(size);
429299319Sarybchik	if (last_segment_end + 1 + delta > cursor->limit) {
430283514Sarybchik		rc = ENOSPC;
431283514Sarybchik		goto fail4;
432283514Sarybchik	}
433283514Sarybchik
434283514Sarybchik	/* Move data up: new space at cursor->current */
435283514Sarybchik	memmove(cursor->current + delta, cursor->current,
436299319Sarybchik	    (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
437283514Sarybchik
438283514Sarybchik	/* Adjust the end pointer */
439283514Sarybchik	cursor->end += delta;
440283514Sarybchik
441283514Sarybchik	/* Write new TLV item */
442283514Sarybchik	tlv_write(cursor, tag, data, size);
443283514Sarybchik
444283514Sarybchik	return (0);
445283514Sarybchik
446283514Sarybchikfail4:
447283514Sarybchik	EFSYS_PROBE(fail4);
448283514Sarybchikfail3:
449283514Sarybchik	EFSYS_PROBE(fail3);
450283514Sarybchikfail2:
451283514Sarybchik	EFSYS_PROBE(fail2);
452283514Sarybchikfail1:
453291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
454283514Sarybchik
455283514Sarybchik	return (rc);
456283514Sarybchik}
457283514Sarybchik
458291436Sarybchikstatic	__checkReturn		efx_rc_t
459299319Sarybchiktlv_delete(
460299319Sarybchik	__inout	tlv_cursor_t	*cursor)
461299319Sarybchik{
462299319Sarybchik	unsigned int delta;
463299319Sarybchik	uint32_t *last_segment_end;
464299319Sarybchik	efx_rc_t rc;
465299319Sarybchik
466299319Sarybchik	if ((rc = tlv_validate_state(cursor)) != 0)
467299319Sarybchik		goto fail1;
468299319Sarybchik
469299319Sarybchik	if (tlv_tag(cursor) == TLV_TAG_END) {
470299319Sarybchik		rc = EINVAL;
471299319Sarybchik		goto fail2;
472299319Sarybchik	}
473299319Sarybchik
474299319Sarybchik	delta = TLV_DWORD_COUNT(tlv_length(cursor));
475299319Sarybchik
476299319Sarybchik	if ((rc = tlv_require_end(cursor)) != 0)
477299319Sarybchik		goto fail3;
478299319Sarybchik
479299319Sarybchik	last_segment_end = tlv_last_segment_end(cursor);
480299319Sarybchik
481299319Sarybchik	/* Shuffle things down, destroying the item at cursor->current */
482299319Sarybchik	memmove(cursor->current, cursor->current + delta,
483299319Sarybchik	    (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
484299319Sarybchik	/* Zero the new space at the end of the TLV chain */
485299319Sarybchik	memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t));
486299319Sarybchik	/* Adjust the end pointer */
487299319Sarybchik	cursor->end -= delta;
488299319Sarybchik
489299319Sarybchik	return (0);
490299319Sarybchik
491299319Sarybchikfail3:
492299319Sarybchik	EFSYS_PROBE(fail3);
493299319Sarybchikfail2:
494299319Sarybchik	EFSYS_PROBE(fail2);
495299319Sarybchikfail1:
496299319Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
497299319Sarybchik
498299319Sarybchik	return (rc);
499299319Sarybchik}
500299319Sarybchik
501299319Sarybchikstatic	__checkReturn		efx_rc_t
502283514Sarybchiktlv_modify(
503299319Sarybchik	__inout	tlv_cursor_t	*cursor,
504283514Sarybchik	__in	uint32_t	tag,
505299319Sarybchik	__in_bcount(size)
506299319Sarybchik		uint8_t		*data,
507283514Sarybchik	__in	size_t		size)
508283514Sarybchik{
509283514Sarybchik	uint32_t *pos;
510283514Sarybchik	unsigned int old_ndwords;
511283514Sarybchik	unsigned int new_ndwords;
512283514Sarybchik	unsigned int delta;
513299319Sarybchik	uint32_t *last_segment_end;
514291436Sarybchik	efx_rc_t rc;
515283514Sarybchik
516283514Sarybchik	if ((rc = tlv_validate_state(cursor)) != 0)
517283514Sarybchik		goto fail1;
518283514Sarybchik
519283514Sarybchik	if (tlv_tag(cursor) == TLV_TAG_END) {
520283514Sarybchik		rc = EINVAL;
521283514Sarybchik		goto fail2;
522283514Sarybchik	}
523283514Sarybchik	if (tlv_tag(cursor) != tag) {
524283514Sarybchik		rc = EINVAL;
525283514Sarybchik		goto fail3;
526283514Sarybchik	}
527283514Sarybchik
528283514Sarybchik	old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
529283514Sarybchik	new_ndwords = TLV_DWORD_COUNT(size);
530283514Sarybchik
531283514Sarybchik	if ((rc = tlv_require_end(cursor)) != 0)
532283514Sarybchik		goto fail4;
533283514Sarybchik
534299319Sarybchik	last_segment_end = tlv_last_segment_end(cursor);
535299319Sarybchik
536283514Sarybchik	if (new_ndwords > old_ndwords) {
537283514Sarybchik		/* Expand space used for TLV item */
538283514Sarybchik		delta = new_ndwords - old_ndwords;
539283514Sarybchik		pos = cursor->current + old_ndwords;
540283514Sarybchik
541299319Sarybchik		if (last_segment_end + 1 + delta > cursor->limit) {
542283514Sarybchik			rc = ENOSPC;
543283514Sarybchik			goto fail5;
544283514Sarybchik		}
545283514Sarybchik
546283514Sarybchik		/* Move up: new space at (cursor->current + old_ndwords) */
547283514Sarybchik		memmove(pos + delta, pos,
548299319Sarybchik		    (last_segment_end + 1 - pos) * sizeof (uint32_t));
549283514Sarybchik
550283514Sarybchik		/* Adjust the end pointer */
551283514Sarybchik		cursor->end += delta;
552283514Sarybchik
553283514Sarybchik	} else if (new_ndwords < old_ndwords) {
554283514Sarybchik		/* Shrink space used for TLV item */
555283514Sarybchik		delta = old_ndwords - new_ndwords;
556283514Sarybchik		pos = cursor->current + new_ndwords;
557283514Sarybchik
558283514Sarybchik		/* Move down: remove words at (cursor->current + new_ndwords) */
559283514Sarybchik		memmove(pos, pos + delta,
560299319Sarybchik		    (last_segment_end + 1 - pos) * sizeof (uint32_t));
561283514Sarybchik
562283514Sarybchik		/* Zero the new space at the end of the TLV chain */
563299319Sarybchik		memset(last_segment_end + 1 - delta, 0,
564299319Sarybchik		    delta * sizeof (uint32_t));
565283514Sarybchik
566283514Sarybchik		/* Adjust the end pointer */
567283514Sarybchik		cursor->end -= delta;
568283514Sarybchik	}
569283514Sarybchik
570283514Sarybchik	/* Write new data */
571283514Sarybchik	tlv_write(cursor, tag, data, size);
572283514Sarybchik
573283514Sarybchik	return (0);
574283514Sarybchik
575283514Sarybchikfail5:
576283514Sarybchik	EFSYS_PROBE(fail5);
577283514Sarybchikfail4:
578283514Sarybchik	EFSYS_PROBE(fail4);
579283514Sarybchikfail3:
580283514Sarybchik	EFSYS_PROBE(fail3);
581283514Sarybchikfail2:
582283514Sarybchik	EFSYS_PROBE(fail2);
583283514Sarybchikfail1:
584291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
585283514Sarybchik
586283514Sarybchik	return (rc);
587283514Sarybchik}
588283514Sarybchik
589299319Sarybchikstatic uint32_t checksum_tlv_partition(
590299319Sarybchik	__in	nvram_partition_t *partition)
591299319Sarybchik{
592299319Sarybchik	tlv_cursor_t *cursor;
593299319Sarybchik	uint32_t *ptr;
594299319Sarybchik	uint32_t *end;
595299319Sarybchik	uint32_t csum;
596299319Sarybchik	size_t len;
597299319Sarybchik
598299319Sarybchik	cursor = &partition->tlv_cursor;
599299319Sarybchik	len = tlv_block_length_used(cursor);
600299319Sarybchik	EFSYS_ASSERT3U((len & 3), ==, 0);
601299319Sarybchik
602299319Sarybchik	csum = 0;
603299319Sarybchik	ptr = partition->data;
604299319Sarybchik	end = &ptr[len >> 2];
605299319Sarybchik
606299319Sarybchik	while (ptr < end)
607299319Sarybchik		csum += __LE_TO_CPU_32(*ptr++);
608299319Sarybchik
609299319Sarybchik	return (csum);
610299319Sarybchik}
611299319Sarybchik
612299319Sarybchikstatic	__checkReturn		efx_rc_t
613299319Sarybchiktlv_update_partition_len_and_cks(
614299319Sarybchik	__in	tlv_cursor_t *cursor)
615299319Sarybchik{
616299319Sarybchik	efx_rc_t rc;
617299319Sarybchik	nvram_partition_t partition;
618299319Sarybchik	struct tlv_partition_header *header;
619299319Sarybchik	struct tlv_partition_trailer *trailer;
620299319Sarybchik	size_t new_len;
621299319Sarybchik
622299319Sarybchik	/*
623299319Sarybchik	 * We just modified the partition, so the total length may not be
624299319Sarybchik	 * valid. Don't use tlv_find(), which performs some sanity checks
625299319Sarybchik	 * that may fail here.
626299319Sarybchik	 */
627299319Sarybchik	partition.data = cursor->block;
628299319Sarybchik	memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
629299319Sarybchik	header = (struct tlv_partition_header *)partition.data;
630299319Sarybchik	/* Sanity check. */
631299319Sarybchik	if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
632299319Sarybchik		rc = EFAULT;
633299319Sarybchik		goto fail1;
634299319Sarybchik	}
635299319Sarybchik	new_len =  tlv_block_length_used(&partition.tlv_cursor);
636299319Sarybchik	if (new_len == 0) {
637299319Sarybchik		rc = EFAULT;
638299319Sarybchik		goto fail2;
639299319Sarybchik	}
640299319Sarybchik	header->total_length = __CPU_TO_LE_32(new_len);
641299319Sarybchik	/* Ensure the modified partition always has a new generation count. */
642299319Sarybchik	header->generation = __CPU_TO_LE_32(
643299319Sarybchik	    __LE_TO_CPU_32(header->generation) + 1);
644299319Sarybchik
645299319Sarybchik	trailer = (struct tlv_partition_trailer *)((uint8_t *)header +
646299319Sarybchik	    new_len - sizeof (*trailer) - sizeof (uint32_t));
647299319Sarybchik	trailer->generation = header->generation;
648299319Sarybchik	trailer->checksum = __CPU_TO_LE_32(
649299319Sarybchik	    __LE_TO_CPU_32(trailer->checksum) -
650299319Sarybchik	    checksum_tlv_partition(&partition));
651299319Sarybchik
652299319Sarybchik	return (0);
653299319Sarybchik
654299319Sarybchikfail2:
655299319Sarybchik	EFSYS_PROBE(fail2);
656299319Sarybchikfail1:
657299319Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
658299319Sarybchik
659299319Sarybchik	return (rc);
660299319Sarybchik}
661299319Sarybchik
662299319Sarybchik/* Validate buffer contents (before writing to flash) */
663291436Sarybchik	__checkReturn		efx_rc_t
664299318Sarybchikef10_nvram_buffer_validate(
665283514Sarybchik	__in			efx_nic_t *enp,
666283514Sarybchik	__in			uint32_t partn,
667283514Sarybchik	__in_bcount(partn_size)	caddr_t partn_data,
668283514Sarybchik	__in			size_t partn_size)
669283514Sarybchik{
670283514Sarybchik	tlv_cursor_t cursor;
671283514Sarybchik	struct tlv_partition_header *header;
672283514Sarybchik	struct tlv_partition_trailer *trailer;
673283514Sarybchik	size_t total_length;
674283514Sarybchik	uint32_t cksum;
675283514Sarybchik	int pos;
676291436Sarybchik	efx_rc_t rc;
677283514Sarybchik
678342501Sarybchik	_NOTE(ARGUNUSED(enp, partn))
679293756Sarybchik	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
680283514Sarybchik
681283514Sarybchik	if ((partn_data == NULL) || (partn_size == 0)) {
682283514Sarybchik		rc = EINVAL;
683283514Sarybchik		goto fail1;
684283514Sarybchik	}
685283514Sarybchik
686283514Sarybchik	/* The partition header must be the first item (at offset zero) */
687291926Sarybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
688283514Sarybchik		    partn_size)) != 0) {
689283514Sarybchik		rc = EFAULT;
690283514Sarybchik		goto fail2;
691283514Sarybchik	}
692283514Sarybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
693283514Sarybchik		rc = EINVAL;
694283514Sarybchik		goto fail3;
695283514Sarybchik	}
696283514Sarybchik	header = (struct tlv_partition_header *)tlv_item(&cursor);
697283514Sarybchik
698283514Sarybchik	/* Check TLV partition length (includes the END tag) */
699283514Sarybchik	total_length = __LE_TO_CPU_32(header->total_length);
700283514Sarybchik	if (total_length > partn_size) {
701283514Sarybchik		rc = EFBIG;
702283514Sarybchik		goto fail4;
703283514Sarybchik	}
704283514Sarybchik
705283514Sarybchik	/* Check partition ends with PARTITION_TRAILER and END tags */
706283514Sarybchik	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
707283514Sarybchik		rc = EINVAL;
708283514Sarybchik		goto fail5;
709283514Sarybchik	}
710283514Sarybchik	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
711283514Sarybchik
712283514Sarybchik	if ((rc = tlv_advance(&cursor)) != 0) {
713283514Sarybchik		rc = EINVAL;
714283514Sarybchik		goto fail6;
715283514Sarybchik	}
716283514Sarybchik	if (tlv_tag(&cursor) != TLV_TAG_END) {
717283514Sarybchik		rc = EINVAL;
718283514Sarybchik		goto fail7;
719283514Sarybchik	}
720283514Sarybchik
721283514Sarybchik	/* Check generation counts are consistent */
722283514Sarybchik	if (trailer->generation != header->generation) {
723283514Sarybchik		rc = EINVAL;
724283514Sarybchik		goto fail8;
725283514Sarybchik	}
726283514Sarybchik
727283514Sarybchik	/* Verify partition checksum */
728283514Sarybchik	cksum = 0;
729283514Sarybchik	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
730283514Sarybchik		cksum += *((uint32_t *)(partn_data + pos));
731283514Sarybchik	}
732283514Sarybchik	if (cksum != 0) {
733283514Sarybchik		rc = EINVAL;
734283514Sarybchik		goto fail9;
735283514Sarybchik	}
736283514Sarybchik
737283514Sarybchik	return (0);
738283514Sarybchik
739283514Sarybchikfail9:
740283514Sarybchik	EFSYS_PROBE(fail9);
741283514Sarybchikfail8:
742283514Sarybchik	EFSYS_PROBE(fail8);
743283514Sarybchikfail7:
744283514Sarybchik	EFSYS_PROBE(fail7);
745283514Sarybchikfail6:
746283514Sarybchik	EFSYS_PROBE(fail6);
747283514Sarybchikfail5:
748283514Sarybchik	EFSYS_PROBE(fail5);
749283514Sarybchikfail4:
750283514Sarybchik	EFSYS_PROBE(fail4);
751283514Sarybchikfail3:
752283514Sarybchik	EFSYS_PROBE(fail3);
753283514Sarybchikfail2:
754283514Sarybchik	EFSYS_PROBE(fail2);
755283514Sarybchikfail1:
756291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
757283514Sarybchik
758283514Sarybchik	return (rc);
759283514Sarybchik}
760283514Sarybchik
761299402Sarybchik
762299402Sarybchik
763299402Sarybchik	__checkReturn		efx_rc_t
764299402Sarybchikef10_nvram_buffer_create(
765299402Sarybchik	__in			efx_nic_t *enp,
766299402Sarybchik	__in			uint16_t partn_type,
767299402Sarybchik	__in_bcount(partn_size)	caddr_t partn_data,
768299402Sarybchik	__in			size_t partn_size)
769299402Sarybchik{
770299402Sarybchik	uint32_t *buf = (uint32_t *)partn_data;
771299402Sarybchik	efx_rc_t rc;
772299402Sarybchik	tlv_cursor_t cursor;
773299402Sarybchik	struct tlv_partition_header header;
774299402Sarybchik	struct tlv_partition_trailer trailer;
775299402Sarybchik
776311062Sarybchik	unsigned int min_buf_size = sizeof (struct tlv_partition_header) +
777299402Sarybchik	    sizeof (struct tlv_partition_trailer);
778299402Sarybchik	if (partn_size < min_buf_size) {
779299402Sarybchik		rc = EINVAL;
780299402Sarybchik		goto fail1;
781299402Sarybchik	}
782299402Sarybchik
783299402Sarybchik	memset(buf, 0xff, partn_size);
784299402Sarybchik
785299402Sarybchik	tlv_init_block(buf);
786299402Sarybchik	if ((rc = tlv_init_cursor(&cursor, buf,
787299402Sarybchik	    (uint32_t *)((uint8_t *)buf + partn_size),
788299402Sarybchik	    buf)) != 0) {
789299402Sarybchik		goto fail2;
790299402Sarybchik	}
791299402Sarybchik
792299402Sarybchik	header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
793299402Sarybchik	header.length = __CPU_TO_LE_32(sizeof (header) - 8);
794299402Sarybchik	header.type_id = __CPU_TO_LE_16(partn_type);
795299402Sarybchik	header.preset = 0;
796299402Sarybchik	header.generation = __CPU_TO_LE_32(1);
797299402Sarybchik	header.total_length = 0;  /* This will be fixed below. */
798299402Sarybchik	if ((rc = tlv_insert(
799299402Sarybchik	    &cursor, TLV_TAG_PARTITION_HEADER,
800299402Sarybchik	    (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
801299402Sarybchik		goto fail3;
802299402Sarybchik	if ((rc = tlv_advance(&cursor)) != 0)
803299402Sarybchik		goto fail4;
804299402Sarybchik
805299402Sarybchik	trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
806299402Sarybchik	trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
807299402Sarybchik	trailer.generation = header.generation;
808299402Sarybchik	trailer.checksum = 0;  /* This will be fixed below. */
809299402Sarybchik	if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
810299402Sarybchik	    (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
811299402Sarybchik		goto fail5;
812299402Sarybchik
813299402Sarybchik	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
814299402Sarybchik		goto fail6;
815299402Sarybchik
816299402Sarybchik	/* Check that the partition is valid. */
817299402Sarybchik	if ((rc = ef10_nvram_buffer_validate(enp, partn_type,
818299402Sarybchik	    partn_data, partn_size)) != 0)
819299402Sarybchik		goto fail7;
820299402Sarybchik
821299402Sarybchik	return (0);
822299402Sarybchik
823299402Sarybchikfail7:
824299402Sarybchik	EFSYS_PROBE(fail7);
825299402Sarybchikfail6:
826299402Sarybchik	EFSYS_PROBE(fail6);
827299402Sarybchikfail5:
828299402Sarybchik	EFSYS_PROBE(fail5);
829299402Sarybchikfail4:
830299402Sarybchik	EFSYS_PROBE(fail4);
831299402Sarybchikfail3:
832299402Sarybchik	EFSYS_PROBE(fail3);
833299402Sarybchikfail2:
834299402Sarybchik	EFSYS_PROBE(fail2);
835299402Sarybchikfail1:
836299402Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
837299402Sarybchik
838299402Sarybchik	return (rc);
839299402Sarybchik}
840299402Sarybchik
841299402Sarybchikstatic			uint32_t
842299402Sarybchikbyte_offset(
843299402Sarybchik	__in		uint32_t *position,
844299402Sarybchik	__in		uint32_t *base)
845299402Sarybchik{
846299402Sarybchik	return (uint32_t)((uint8_t *)position - (uint8_t *)base);
847299402Sarybchik}
848299402Sarybchik
849299402Sarybchik	__checkReturn		efx_rc_t
850299402Sarybchikef10_nvram_buffer_find_item_start(
851299402Sarybchik	__in_bcount(buffer_size)
852299402Sarybchik				caddr_t bufferp,
853299402Sarybchik	__in			size_t buffer_size,
854299402Sarybchik	__out			uint32_t *startp)
855299402Sarybchik{
856311050Sarybchik	/* Read past partition header to find start address of the first key */
857299402Sarybchik	tlv_cursor_t cursor;
858299402Sarybchik	efx_rc_t rc;
859299402Sarybchik
860299402Sarybchik	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
861299402Sarybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
862299402Sarybchik			buffer_size)) != 0) {
863299402Sarybchik		rc = EFAULT;
864299402Sarybchik		goto fail1;
865299402Sarybchik	}
866299402Sarybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
867299402Sarybchik		rc = EINVAL;
868299402Sarybchik		goto fail2;
869299402Sarybchik	}
870299402Sarybchik
871299402Sarybchik	if ((rc = tlv_advance(&cursor)) != 0) {
872299402Sarybchik		rc = EINVAL;
873299402Sarybchik		goto fail3;
874299402Sarybchik	}
875299402Sarybchik	*startp = byte_offset(cursor.current, cursor.block);
876299402Sarybchik
877299402Sarybchik	if ((rc = tlv_require_end(&cursor)) != 0)
878299402Sarybchik		goto fail4;
879299402Sarybchik
880299402Sarybchik	return (0);
881299402Sarybchik
882299402Sarybchikfail4:
883299402Sarybchik	EFSYS_PROBE(fail4);
884299402Sarybchikfail3:
885299402Sarybchik	EFSYS_PROBE(fail3);
886299402Sarybchikfail2:
887299402Sarybchik	EFSYS_PROBE(fail2);
888299402Sarybchikfail1:
889299402Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
890299402Sarybchik
891299402Sarybchik	return (rc);
892299402Sarybchik}
893299402Sarybchik
894299402Sarybchik	__checkReturn		efx_rc_t
895299402Sarybchikef10_nvram_buffer_find_end(
896299402Sarybchik	__in_bcount(buffer_size)
897299402Sarybchik				caddr_t bufferp,
898299402Sarybchik	__in			size_t buffer_size,
899299402Sarybchik	__in			uint32_t offset,
900299402Sarybchik	__out			uint32_t *endp)
901299402Sarybchik{
902311050Sarybchik	/* Read to end of partition */
903299402Sarybchik	tlv_cursor_t cursor;
904299402Sarybchik	efx_rc_t rc;
905301364Sarybchik	uint32_t *segment_used;
906299402Sarybchik
907301392Sarybchik	_NOTE(ARGUNUSED(offset))
908301392Sarybchik
909299402Sarybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
910299402Sarybchik			buffer_size)) != 0) {
911299402Sarybchik		rc = EFAULT;
912299402Sarybchik		goto fail1;
913299402Sarybchik	}
914299402Sarybchik
915301364Sarybchik	segment_used = cursor.block;
916299402Sarybchik
917301364Sarybchik	/*
918301364Sarybchik	 * Go through each segment and check that it has an end tag. If there
919301364Sarybchik	 * is no end tag then the previous segment was the last valid one,
920301364Sarybchik	 * so return the used space including that end tag.
921301364Sarybchik	 */
922301364Sarybchik	while (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
923301364Sarybchik		if (tlv_require_end(&cursor) != 0) {
924301364Sarybchik			if (segment_used == cursor.block) {
925301364Sarybchik				/*
926301364Sarybchik				 * First segment is corrupt, so there is
927301364Sarybchik				 * no valid data in partition.
928301364Sarybchik				 */
929301364Sarybchik				rc = EINVAL;
930301364Sarybchik				goto fail2;
931301364Sarybchik			}
932301364Sarybchik			break;
933301364Sarybchik		}
934301364Sarybchik		segment_used = cursor.end + 1;
935299402Sarybchik
936301364Sarybchik		cursor.current = segment_used;
937301364Sarybchik	}
938301364Sarybchik	/* Return space used (including the END tag) */
939301364Sarybchik	*endp = (segment_used - cursor.block) * sizeof (uint32_t);
940301364Sarybchik
941299402Sarybchik	return (0);
942299402Sarybchik
943299402Sarybchikfail2:
944299402Sarybchik	EFSYS_PROBE(fail2);
945299402Sarybchikfail1:
946299402Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
947299402Sarybchik
948299402Sarybchik	return (rc);
949299402Sarybchik}
950299402Sarybchik
951299402Sarybchik	__checkReturn	__success(return != B_FALSE)	boolean_t
952299402Sarybchikef10_nvram_buffer_find_item(
953299402Sarybchik	__in_bcount(buffer_size)
954299402Sarybchik				caddr_t bufferp,
955299402Sarybchik	__in			size_t buffer_size,
956299402Sarybchik	__in			uint32_t offset,
957299402Sarybchik	__out			uint32_t *startp,
958299402Sarybchik	__out			uint32_t *lengthp)
959299402Sarybchik{
960311050Sarybchik	/* Find TLV at offset and return key start and length */
961299402Sarybchik	tlv_cursor_t cursor;
962299402Sarybchik	uint8_t *key;
963299402Sarybchik	uint32_t tag;
964299402Sarybchik
965299402Sarybchik	if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
966299402Sarybchik			buffer_size, offset) != 0) {
967299402Sarybchik		return (B_FALSE);
968299402Sarybchik	}
969299402Sarybchik
970299402Sarybchik	while ((key = tlv_item(&cursor)) != NULL) {
971299402Sarybchik		tag = tlv_tag(&cursor);
972299402Sarybchik		if (tag == TLV_TAG_PARTITION_HEADER ||
973299402Sarybchik		    tag == TLV_TAG_PARTITION_TRAILER) {
974299402Sarybchik			if (tlv_advance(&cursor) != 0) {
975299402Sarybchik				break;
976299402Sarybchik			}
977299402Sarybchik			continue;
978299402Sarybchik		}
979299402Sarybchik		*startp = byte_offset(cursor.current, cursor.block);
980299402Sarybchik		*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
981299402Sarybchik		    cursor.current);
982299402Sarybchik		return (B_TRUE);
983299402Sarybchik	}
984299402Sarybchik
985299402Sarybchik	return (B_FALSE);
986299402Sarybchik}
987299402Sarybchik
988299402Sarybchik	__checkReturn		efx_rc_t
989299402Sarybchikef10_nvram_buffer_get_item(
990299402Sarybchik	__in_bcount(buffer_size)
991299402Sarybchik				caddr_t bufferp,
992299402Sarybchik	__in			size_t buffer_size,
993299402Sarybchik	__in			uint32_t offset,
994299402Sarybchik	__in			uint32_t length,
995299402Sarybchik	__out_bcount_part(item_max_size, *lengthp)
996299402Sarybchik				caddr_t itemp,
997299402Sarybchik	__in			size_t item_max_size,
998299402Sarybchik	__out			uint32_t *lengthp)
999299402Sarybchik{
1000299402Sarybchik	efx_rc_t rc;
1001299402Sarybchik	tlv_cursor_t cursor;
1002299402Sarybchik	uint32_t item_length;
1003299402Sarybchik
1004299402Sarybchik	if (item_max_size < length) {
1005299402Sarybchik		rc = ENOSPC;
1006299402Sarybchik		goto fail1;
1007299402Sarybchik	}
1008299402Sarybchik
1009299402Sarybchik	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1010299402Sarybchik			buffer_size, offset)) != 0) {
1011299402Sarybchik		goto fail2;
1012299402Sarybchik	}
1013299402Sarybchik
1014299402Sarybchik	item_length = tlv_length(&cursor);
1015299402Sarybchik	if (length < item_length) {
1016299402Sarybchik		rc = ENOSPC;
1017299402Sarybchik		goto fail3;
1018299402Sarybchik	}
1019299402Sarybchik	memcpy(itemp, tlv_value(&cursor), item_length);
1020299402Sarybchik
1021299402Sarybchik	*lengthp = item_length;
1022299402Sarybchik
1023299402Sarybchik	return (0);
1024299402Sarybchik
1025299402Sarybchikfail3:
1026299402Sarybchik	EFSYS_PROBE(fail3);
1027299402Sarybchikfail2:
1028299402Sarybchik	EFSYS_PROBE(fail2);
1029299402Sarybchikfail1:
1030299402Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1031299402Sarybchik
1032299402Sarybchik	return (rc);
1033299402Sarybchik}
1034299402Sarybchik
1035299402Sarybchik	__checkReturn		efx_rc_t
1036299402Sarybchikef10_nvram_buffer_insert_item(
1037299402Sarybchik	__in_bcount(buffer_size)
1038299402Sarybchik				caddr_t bufferp,
1039299402Sarybchik	__in			size_t buffer_size,
1040299402Sarybchik	__in			uint32_t offset,
1041299402Sarybchik	__in_bcount(length)	caddr_t keyp,
1042299402Sarybchik	__in			uint32_t length,
1043299402Sarybchik	__out			uint32_t *lengthp)
1044299402Sarybchik{
1045299402Sarybchik	efx_rc_t rc;
1046299402Sarybchik	tlv_cursor_t cursor;
1047299402Sarybchik
1048299402Sarybchik	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1049299402Sarybchik			buffer_size, offset)) != 0) {
1050299402Sarybchik		goto fail1;
1051299402Sarybchik	}
1052299402Sarybchik
1053301356Sarybchik	rc = tlv_insert(&cursor, TLV_TAG_LICENSE, (uint8_t *)keyp, length);
1054299402Sarybchik
1055299402Sarybchik	if (rc != 0) {
1056299402Sarybchik		goto fail2;
1057299402Sarybchik	}
1058299402Sarybchik
1059299402Sarybchik	*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1060299402Sarybchik		    cursor.current);
1061299402Sarybchik
1062299402Sarybchik	return (0);
1063299402Sarybchik
1064299402Sarybchikfail2:
1065299402Sarybchik	EFSYS_PROBE(fail2);
1066299402Sarybchikfail1:
1067299402Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1068299402Sarybchik
1069299402Sarybchik	return (rc);
1070299402Sarybchik}
1071299402Sarybchik
1072299402Sarybchik	__checkReturn		efx_rc_t
1073299402Sarybchikef10_nvram_buffer_delete_item(
1074299402Sarybchik	__in_bcount(buffer_size)
1075299402Sarybchik				caddr_t bufferp,
1076299402Sarybchik	__in			size_t buffer_size,
1077299402Sarybchik	__in			uint32_t offset,
1078299402Sarybchik	__in			uint32_t length,
1079299402Sarybchik	__in			uint32_t end)
1080299402Sarybchik{
1081299402Sarybchik	efx_rc_t rc;
1082299402Sarybchik	tlv_cursor_t cursor;
1083299402Sarybchik
1084301392Sarybchik	_NOTE(ARGUNUSED(length, end))
1085301392Sarybchik
1086299402Sarybchik	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1087299402Sarybchik			buffer_size, offset)) != 0) {
1088299402Sarybchik		goto fail1;
1089299402Sarybchik	}
1090299402Sarybchik
1091299402Sarybchik	if ((rc = tlv_delete(&cursor)) != 0)
1092299402Sarybchik		goto fail2;
1093299402Sarybchik
1094299402Sarybchik	return (0);
1095299402Sarybchik
1096299402Sarybchikfail2:
1097299402Sarybchik	EFSYS_PROBE(fail2);
1098299402Sarybchikfail1:
1099299402Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1100299402Sarybchik
1101299402Sarybchik	return (rc);
1102299402Sarybchik}
1103299402Sarybchik
1104299402Sarybchik	__checkReturn		efx_rc_t
1105299402Sarybchikef10_nvram_buffer_finish(
1106299402Sarybchik	__in_bcount(buffer_size)
1107299402Sarybchik				caddr_t bufferp,
1108299402Sarybchik	__in			size_t buffer_size)
1109299402Sarybchik{
1110299402Sarybchik	efx_rc_t rc;
1111299402Sarybchik	tlv_cursor_t cursor;
1112299402Sarybchik
1113299402Sarybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
1114299402Sarybchik			buffer_size)) != 0) {
1115299402Sarybchik		rc = EFAULT;
1116299402Sarybchik		goto fail1;
1117299402Sarybchik	}
1118299402Sarybchik
1119299402Sarybchik	if ((rc = tlv_require_end(&cursor)) != 0)
1120299402Sarybchik		goto fail2;
1121299402Sarybchik
1122299402Sarybchik	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
1123299402Sarybchik		goto fail3;
1124299402Sarybchik
1125299402Sarybchik	return (0);
1126299402Sarybchik
1127299402Sarybchikfail3:
1128299402Sarybchik	EFSYS_PROBE(fail3);
1129299402Sarybchikfail2:
1130299402Sarybchik	EFSYS_PROBE(fail2);
1131299402Sarybchikfail1:
1132299402Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1133299402Sarybchik
1134299402Sarybchik	return (rc);
1135299402Sarybchik}
1136299402Sarybchik
1137299402Sarybchik
1138299402Sarybchik
1139291432Sarybchik/*
1140291432Sarybchik * Read and validate a segment from a partition. A segment is a complete
1141291432Sarybchik * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
1142291432Sarybchik * be multiple segments in a partition, so seg_offset allows segments
1143291432Sarybchik * beyond the first to be read.
1144291432Sarybchik */
1145291436Sarybchikstatic	__checkReturn			efx_rc_t
1146293756Sarybchikef10_nvram_read_tlv_segment(
1147291432Sarybchik	__in				efx_nic_t *enp,
1148291432Sarybchik	__in				uint32_t partn,
1149291432Sarybchik	__in				size_t seg_offset,
1150291432Sarybchik	__in_bcount(max_seg_size)	caddr_t seg_data,
1151291432Sarybchik	__in				size_t max_seg_size)
1152283514Sarybchik{
1153283514Sarybchik	tlv_cursor_t cursor;
1154283514Sarybchik	struct tlv_partition_header *header;
1155283514Sarybchik	struct tlv_partition_trailer *trailer;
1156283514Sarybchik	size_t total_length;
1157283514Sarybchik	uint32_t cksum;
1158283514Sarybchik	int pos;
1159291436Sarybchik	efx_rc_t rc;
1160283514Sarybchik
1161293756Sarybchik	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
1162283514Sarybchik
1163291432Sarybchik	if ((seg_data == NULL) || (max_seg_size == 0)) {
1164283514Sarybchik		rc = EINVAL;
1165283514Sarybchik		goto fail1;
1166283514Sarybchik	}
1167283514Sarybchik
1168291432Sarybchik	/* Read initial chunk of the segment, starting at offset */
1169294309Sarybchik	if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
1170294309Sarybchik		    EF10_NVRAM_CHUNK,
1171294309Sarybchik		    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
1172283514Sarybchik		goto fail2;
1173283514Sarybchik	}
1174283514Sarybchik
1175291432Sarybchik	/* A PARTITION_HEADER tag must be the first item at the given offset */
1176291926Sarybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1177291432Sarybchik		    max_seg_size)) != 0) {
1178283514Sarybchik		rc = EFAULT;
1179283514Sarybchik		goto fail3;
1180283514Sarybchik	}
1181283514Sarybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1182283514Sarybchik		rc = EINVAL;
1183283514Sarybchik		goto fail4;
1184283514Sarybchik	}
1185283514Sarybchik	header = (struct tlv_partition_header *)tlv_item(&cursor);
1186283514Sarybchik
1187291432Sarybchik	/* Check TLV segment length (includes the END tag) */
1188283514Sarybchik	total_length = __LE_TO_CPU_32(header->total_length);
1189291432Sarybchik	if (total_length > max_seg_size) {
1190283514Sarybchik		rc = EFBIG;
1191283514Sarybchik		goto fail5;
1192283514Sarybchik	}
1193283514Sarybchik
1194291432Sarybchik	/* Read the remaining segment content */
1195293756Sarybchik	if (total_length > EF10_NVRAM_CHUNK) {
1196294309Sarybchik		if ((rc = ef10_nvram_partn_read_mode(enp, partn,
1197293756Sarybchik			    seg_offset + EF10_NVRAM_CHUNK,
1198293756Sarybchik			    seg_data + EF10_NVRAM_CHUNK,
1199294309Sarybchik			    total_length - EF10_NVRAM_CHUNK,
1200294309Sarybchik			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
1201283514Sarybchik			goto fail6;
1202283514Sarybchik	}
1203283514Sarybchik
1204291432Sarybchik	/* Check segment ends with PARTITION_TRAILER and END tags */
1205283514Sarybchik	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1206283514Sarybchik		rc = EINVAL;
1207283514Sarybchik		goto fail7;
1208283514Sarybchik	}
1209283514Sarybchik	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1210283514Sarybchik
1211283514Sarybchik	if ((rc = tlv_advance(&cursor)) != 0) {
1212283514Sarybchik		rc = EINVAL;
1213283514Sarybchik		goto fail8;
1214283514Sarybchik	}
1215283514Sarybchik	if (tlv_tag(&cursor) != TLV_TAG_END) {
1216283514Sarybchik		rc = EINVAL;
1217283514Sarybchik		goto fail9;
1218283514Sarybchik	}
1219283514Sarybchik
1220291432Sarybchik	/* Check data read from segment is consistent */
1221283514Sarybchik	if (trailer->generation != header->generation) {
1222283514Sarybchik		/*
1223283514Sarybchik		 * The partition data may have been modified between successive
1224283514Sarybchik		 * MCDI NVRAM_READ requests by the MC or another PCI function.
1225283514Sarybchik		 *
1226283514Sarybchik		 * The caller must retry to obtain consistent partition data.
1227283514Sarybchik		 */
1228283514Sarybchik		rc = EAGAIN;
1229283514Sarybchik		goto fail10;
1230283514Sarybchik	}
1231283514Sarybchik
1232291432Sarybchik	/* Verify segment checksum */
1233283514Sarybchik	cksum = 0;
1234283514Sarybchik	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
1235291432Sarybchik		cksum += *((uint32_t *)(seg_data + pos));
1236283514Sarybchik	}
1237283514Sarybchik	if (cksum != 0) {
1238283514Sarybchik		rc = EINVAL;
1239283514Sarybchik		goto fail11;
1240283514Sarybchik	}
1241283514Sarybchik
1242283514Sarybchik	return (0);
1243283514Sarybchik
1244283514Sarybchikfail11:
1245283514Sarybchik	EFSYS_PROBE(fail11);
1246283514Sarybchikfail10:
1247283514Sarybchik	EFSYS_PROBE(fail10);
1248283514Sarybchikfail9:
1249283514Sarybchik	EFSYS_PROBE(fail9);
1250283514Sarybchikfail8:
1251283514Sarybchik	EFSYS_PROBE(fail8);
1252283514Sarybchikfail7:
1253283514Sarybchik	EFSYS_PROBE(fail7);
1254283514Sarybchikfail6:
1255283514Sarybchik	EFSYS_PROBE(fail6);
1256283514Sarybchikfail5:
1257283514Sarybchik	EFSYS_PROBE(fail5);
1258283514Sarybchikfail4:
1259283514Sarybchik	EFSYS_PROBE(fail4);
1260283514Sarybchikfail3:
1261283514Sarybchik	EFSYS_PROBE(fail3);
1262283514Sarybchikfail2:
1263283514Sarybchik	EFSYS_PROBE(fail2);
1264283514Sarybchikfail1:
1265291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1266283514Sarybchik
1267283514Sarybchik	return (rc);
1268283514Sarybchik}
1269283514Sarybchik
1270283514Sarybchik/*
1271283514Sarybchik * Read a single TLV item from a host memory
1272291432Sarybchik * buffer containing a TLV formatted segment.
1273283514Sarybchik */
1274291436Sarybchik	__checkReturn		efx_rc_t
1275293756Sarybchikef10_nvram_buf_read_tlv(
1276283514Sarybchik	__in				efx_nic_t *enp,
1277291432Sarybchik	__in_bcount(max_seg_size)	caddr_t seg_data,
1278291432Sarybchik	__in				size_t max_seg_size,
1279283514Sarybchik	__in				uint32_t tag,
1280283514Sarybchik	__deref_out_bcount_opt(*sizep)	caddr_t *datap,
1281283514Sarybchik	__out				size_t *sizep)
1282283514Sarybchik{
1283283514Sarybchik	tlv_cursor_t cursor;
1284283514Sarybchik	caddr_t data;
1285283514Sarybchik	size_t length;
1286283514Sarybchik	caddr_t value;
1287291436Sarybchik	efx_rc_t rc;
1288283514Sarybchik
1289342501Sarybchik	_NOTE(ARGUNUSED(enp))
1290342501Sarybchik
1291291432Sarybchik	if ((seg_data == NULL) || (max_seg_size == 0)) {
1292283514Sarybchik		rc = EINVAL;
1293283514Sarybchik		goto fail1;
1294283514Sarybchik	}
1295283514Sarybchik
1296291432Sarybchik	/* Find requested TLV tag in segment data */
1297291926Sarybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1298291432Sarybchik		    max_seg_size)) != 0) {
1299283514Sarybchik		rc = EFAULT;
1300283514Sarybchik		goto fail2;
1301283514Sarybchik	}
1302283514Sarybchik	if ((rc = tlv_find(&cursor, tag)) != 0) {
1303283514Sarybchik		rc = ENOENT;
1304283514Sarybchik		goto fail3;
1305283514Sarybchik	}
1306291926Sarybchik	value = (caddr_t)tlv_value(&cursor);
1307283514Sarybchik	length = tlv_length(&cursor);
1308283514Sarybchik
1309283514Sarybchik	if (length == 0)
1310283514Sarybchik		data = NULL;
1311283514Sarybchik	else {
1312283514Sarybchik		/* Copy out data from TLV item */
1313283514Sarybchik		EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
1314283514Sarybchik		if (data == NULL) {
1315283514Sarybchik			rc = ENOMEM;
1316283514Sarybchik			goto fail4;
1317283514Sarybchik		}
1318283514Sarybchik		memcpy(data, value, length);
1319283514Sarybchik	}
1320283514Sarybchik
1321283514Sarybchik	*datap = data;
1322283514Sarybchik	*sizep = length;
1323283514Sarybchik
1324283514Sarybchik	return (0);
1325283514Sarybchik
1326283514Sarybchikfail4:
1327283514Sarybchik	EFSYS_PROBE(fail4);
1328283514Sarybchikfail3:
1329283514Sarybchik	EFSYS_PROBE(fail3);
1330283514Sarybchikfail2:
1331283514Sarybchik	EFSYS_PROBE(fail2);
1332283514Sarybchikfail1:
1333291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1334283514Sarybchik
1335283514Sarybchik	return (rc);
1336283514Sarybchik}
1337283514Sarybchik
1338291432Sarybchik/* Read a single TLV item from the first segment in a TLV formatted partition */
1339291436Sarybchik	__checkReturn		efx_rc_t
1340293756Sarybchikef10_nvram_partn_read_tlv(
1341291432Sarybchik	__in					efx_nic_t *enp,
1342291432Sarybchik	__in					uint32_t partn,
1343291432Sarybchik	__in					uint32_t tag,
1344291432Sarybchik	__deref_out_bcount_opt(*seg_sizep)	caddr_t *seg_datap,
1345291432Sarybchik	__out					size_t *seg_sizep)
1346283514Sarybchik{
1347291432Sarybchik	caddr_t seg_data = NULL;
1348283514Sarybchik	size_t partn_size = 0;
1349283514Sarybchik	size_t length;
1350283514Sarybchik	caddr_t data;
1351283514Sarybchik	int retry;
1352291436Sarybchik	efx_rc_t rc;
1353283514Sarybchik
1354283514Sarybchik	/* Allocate sufficient memory for the entire partition */
1355293756Sarybchik	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1356283514Sarybchik		goto fail1;
1357283514Sarybchik
1358283514Sarybchik	if (partn_size == 0) {
1359283514Sarybchik		rc = ENOENT;
1360283514Sarybchik		goto fail2;
1361283514Sarybchik	}
1362283514Sarybchik
1363291432Sarybchik	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
1364291432Sarybchik	if (seg_data == NULL) {
1365283514Sarybchik		rc = ENOMEM;
1366283514Sarybchik		goto fail3;
1367283514Sarybchik	}
1368283514Sarybchik
1369283514Sarybchik	/*
1370291432Sarybchik	 * Read the first segment in a TLV partition. Retry until consistent
1371291432Sarybchik	 * segment contents are returned. Inconsistent data may be read if:
1372291432Sarybchik	 *  a) the segment contents are invalid
1373283514Sarybchik	 *  b) the MC has rebooted while we were reading the partition
1374283514Sarybchik	 *  c) the partition has been modified while we were reading it
1375283514Sarybchik	 * Limit retry attempts to ensure forward progress.
1376283514Sarybchik	 */
1377283514Sarybchik	retry = 10;
1378283514Sarybchik	do {
1379293756Sarybchik		rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
1380291432Sarybchik		    seg_data, partn_size);
1381283514Sarybchik	} while ((rc == EAGAIN) && (--retry > 0));
1382283514Sarybchik
1383283514Sarybchik	if (rc != 0) {
1384291432Sarybchik		/* Failed to obtain consistent segment data */
1385283514Sarybchik		goto fail4;
1386283514Sarybchik	}
1387283514Sarybchik
1388293756Sarybchik	if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
1389283514Sarybchik		    tag, &data, &length)) != 0)
1390283514Sarybchik		goto fail5;
1391283514Sarybchik
1392291432Sarybchik	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1393283514Sarybchik
1394291432Sarybchik	*seg_datap = data;
1395291432Sarybchik	*seg_sizep = length;
1396283514Sarybchik
1397283514Sarybchik	return (0);
1398283514Sarybchik
1399283514Sarybchikfail5:
1400283514Sarybchik	EFSYS_PROBE(fail5);
1401283514Sarybchikfail4:
1402283514Sarybchik	EFSYS_PROBE(fail4);
1403283514Sarybchik
1404291432Sarybchik	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1405283514Sarybchikfail3:
1406283514Sarybchik	EFSYS_PROBE(fail3);
1407283514Sarybchikfail2:
1408283514Sarybchik	EFSYS_PROBE(fail2);
1409283514Sarybchikfail1:
1410291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1411283514Sarybchik
1412283514Sarybchik	return (rc);
1413283514Sarybchik}
1414283514Sarybchik
1415291432Sarybchik/* Compute the size of a segment. */
1416291436Sarybchik	static	__checkReturn	efx_rc_t
1417293756Sarybchikef10_nvram_buf_segment_size(
1418291432Sarybchik	__in			caddr_t seg_data,
1419291432Sarybchik	__in			size_t max_seg_size,
1420291432Sarybchik	__out			size_t *seg_sizep)
1421291432Sarybchik{
1422291436Sarybchik	efx_rc_t rc;
1423291432Sarybchik	tlv_cursor_t cursor;
1424291432Sarybchik	struct tlv_partition_header *header;
1425291432Sarybchik	uint32_t cksum;
1426291432Sarybchik	int pos;
1427291432Sarybchik	uint32_t *end_tag_position;
1428291432Sarybchik	uint32_t segment_length;
1429291432Sarybchik
1430291432Sarybchik	/* A PARTITION_HEADER tag must be the first item at the given offset */
1431291926Sarybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1432291432Sarybchik		    max_seg_size)) != 0) {
1433291432Sarybchik		rc = EFAULT;
1434291432Sarybchik		goto fail1;
1435291432Sarybchik	}
1436291432Sarybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1437291432Sarybchik		rc = EINVAL;
1438291432Sarybchik		goto fail2;
1439291432Sarybchik	}
1440291432Sarybchik	header = (struct tlv_partition_header *)tlv_item(&cursor);
1441291432Sarybchik
1442291432Sarybchik	/* Check TLV segment length (includes the END tag) */
1443291432Sarybchik	*seg_sizep = __LE_TO_CPU_32(header->total_length);
1444291432Sarybchik	if (*seg_sizep > max_seg_size) {
1445291432Sarybchik		rc = EFBIG;
1446291432Sarybchik		goto fail3;
1447291432Sarybchik	}
1448291432Sarybchik
1449291432Sarybchik	/* Check segment ends with PARTITION_TRAILER and END tags */
1450291432Sarybchik	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1451291432Sarybchik		rc = EINVAL;
1452291432Sarybchik		goto fail4;
1453291432Sarybchik	}
1454291432Sarybchik
1455291432Sarybchik	if ((rc = tlv_advance(&cursor)) != 0) {
1456291432Sarybchik		rc = EINVAL;
1457291432Sarybchik		goto fail5;
1458291432Sarybchik	}
1459291432Sarybchik	if (tlv_tag(&cursor) != TLV_TAG_END) {
1460291432Sarybchik		rc = EINVAL;
1461291432Sarybchik		goto fail6;
1462291432Sarybchik	}
1463291432Sarybchik	end_tag_position = cursor.current;
1464291432Sarybchik
1465291432Sarybchik	/* Verify segment checksum */
1466291432Sarybchik	cksum = 0;
1467291432Sarybchik	for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
1468291432Sarybchik		cksum += *((uint32_t *)(seg_data + pos));
1469291432Sarybchik	}
1470291432Sarybchik	if (cksum != 0) {
1471291432Sarybchik		rc = EINVAL;
1472291432Sarybchik		goto fail7;
1473291432Sarybchik	}
1474291432Sarybchik
1475291432Sarybchik	/*
1476291432Sarybchik	 * Calculate total length from HEADER to END tags and compare to
1477291432Sarybchik	 * max_seg_size and the total_length field in the HEADER tag.
1478291432Sarybchik	 */
1479291432Sarybchik	segment_length = tlv_block_length_used(&cursor);
1480291432Sarybchik
1481291432Sarybchik	if (segment_length > max_seg_size) {
1482291432Sarybchik		rc = EINVAL;
1483291432Sarybchik		goto fail8;
1484291432Sarybchik	}
1485291432Sarybchik
1486291432Sarybchik	if (segment_length != *seg_sizep) {
1487291432Sarybchik		rc = EINVAL;
1488291432Sarybchik		goto fail9;
1489291432Sarybchik	}
1490291432Sarybchik
1491291432Sarybchik	/* Skip over the first HEADER tag. */
1492291432Sarybchik	rc = tlv_rewind(&cursor);
1493291432Sarybchik	rc = tlv_advance(&cursor);
1494291432Sarybchik
1495291432Sarybchik	while (rc == 0) {
1496291432Sarybchik		if (tlv_tag(&cursor) == TLV_TAG_END) {
1497291432Sarybchik			/* Check that the END tag is the one found earlier. */
1498291432Sarybchik			if (cursor.current != end_tag_position)
1499291432Sarybchik				goto fail10;
1500291432Sarybchik			break;
1501291432Sarybchik		}
1502291432Sarybchik		/* Check for duplicate HEADER tags before the END tag. */
1503291432Sarybchik		if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
1504291432Sarybchik			rc = EINVAL;
1505291432Sarybchik			goto fail11;
1506291432Sarybchik		}
1507291432Sarybchik
1508291432Sarybchik		rc = tlv_advance(&cursor);
1509291432Sarybchik	}
1510291432Sarybchik	if (rc != 0)
1511291432Sarybchik		goto fail12;
1512291432Sarybchik
1513291432Sarybchik	return (0);
1514291432Sarybchik
1515291432Sarybchikfail12:
1516291432Sarybchik	EFSYS_PROBE(fail12);
1517291432Sarybchikfail11:
1518291432Sarybchik	EFSYS_PROBE(fail11);
1519291432Sarybchikfail10:
1520291432Sarybchik	EFSYS_PROBE(fail10);
1521291432Sarybchikfail9:
1522291432Sarybchik	EFSYS_PROBE(fail9);
1523291432Sarybchikfail8:
1524291432Sarybchik	EFSYS_PROBE(fail8);
1525291432Sarybchikfail7:
1526291432Sarybchik	EFSYS_PROBE(fail7);
1527291432Sarybchikfail6:
1528291432Sarybchik	EFSYS_PROBE(fail6);
1529291432Sarybchikfail5:
1530291432Sarybchik	EFSYS_PROBE(fail5);
1531291432Sarybchikfail4:
1532291432Sarybchik	EFSYS_PROBE(fail4);
1533291432Sarybchikfail3:
1534291432Sarybchik	EFSYS_PROBE(fail3);
1535291432Sarybchikfail2:
1536291432Sarybchik	EFSYS_PROBE(fail2);
1537291432Sarybchikfail1:
1538291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1539291432Sarybchik
1540291432Sarybchik	return (rc);
1541291432Sarybchik}
1542291432Sarybchik
1543283514Sarybchik/*
1544283514Sarybchik * Add or update a single TLV item in a host memory buffer containing a TLV
1545291432Sarybchik * formatted segment. Historically partitions consisted of only one segment.
1546283514Sarybchik */
1547291436Sarybchik	__checkReturn			efx_rc_t
1548293756Sarybchikef10_nvram_buf_write_tlv(
1549291432Sarybchik	__inout_bcount(max_seg_size)	caddr_t seg_data,
1550291432Sarybchik	__in				size_t max_seg_size,
1551283514Sarybchik	__in				uint32_t tag,
1552283514Sarybchik	__in_bcount(tag_size)		caddr_t tag_data,
1553283514Sarybchik	__in				size_t tag_size,
1554283514Sarybchik	__out				size_t *total_lengthp)
1555283514Sarybchik{
1556283514Sarybchik	tlv_cursor_t cursor;
1557283514Sarybchik	struct tlv_partition_header *header;
1558283514Sarybchik	struct tlv_partition_trailer *trailer;
1559283514Sarybchik	uint32_t generation;
1560283514Sarybchik	uint32_t cksum;
1561283514Sarybchik	int pos;
1562291436Sarybchik	efx_rc_t rc;
1563283514Sarybchik
1564291432Sarybchik	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
1565291926Sarybchik	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1566291432Sarybchik			max_seg_size)) != 0) {
1567283514Sarybchik		rc = EFAULT;
1568283514Sarybchik		goto fail1;
1569283514Sarybchik	}
1570283514Sarybchik	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1571283514Sarybchik		rc = EINVAL;
1572283514Sarybchik		goto fail2;
1573283514Sarybchik	}
1574283514Sarybchik	header = (struct tlv_partition_header *)tlv_item(&cursor);
1575283514Sarybchik
1576283514Sarybchik	/* Update the TLV chain to contain the new data */
1577283514Sarybchik	if ((rc = tlv_find(&cursor, tag)) == 0) {
1578283514Sarybchik		/* Modify existing TLV item */
1579283514Sarybchik		if ((rc = tlv_modify(&cursor, tag,
1580291926Sarybchik			    (uint8_t *)tag_data, tag_size)) != 0)
1581283514Sarybchik			goto fail3;
1582283514Sarybchik	} else {
1583283514Sarybchik		/* Insert a new TLV item before the PARTITION_TRAILER */
1584283514Sarybchik		rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1585283514Sarybchik		if (rc != 0) {
1586283514Sarybchik			rc = EINVAL;
1587283514Sarybchik			goto fail4;
1588283514Sarybchik		}
1589283514Sarybchik		if ((rc = tlv_insert(&cursor, tag,
1590291926Sarybchik			    (uint8_t *)tag_data, tag_size)) != 0) {
1591283514Sarybchik			rc = EINVAL;
1592283514Sarybchik			goto fail5;
1593283514Sarybchik		}
1594283514Sarybchik	}
1595283514Sarybchik
1596283514Sarybchik	/* Find the trailer tag */
1597283514Sarybchik	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1598283514Sarybchik		rc = EINVAL;
1599283514Sarybchik		goto fail6;
1600283514Sarybchik	}
1601283514Sarybchik	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1602283514Sarybchik
1603283514Sarybchik	/* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1604283514Sarybchik	*total_lengthp = tlv_block_length_used(&cursor);
1605291432Sarybchik	if (*total_lengthp > max_seg_size) {
1606291432Sarybchik		rc = ENOSPC;
1607291432Sarybchik		goto fail7;
1608291432Sarybchik	}
1609283514Sarybchik	generation = __LE_TO_CPU_32(header->generation) + 1;
1610283514Sarybchik
1611283514Sarybchik	header->total_length	= __CPU_TO_LE_32(*total_lengthp);
1612283514Sarybchik	header->generation	= __CPU_TO_LE_32(generation);
1613283514Sarybchik	trailer->generation	= __CPU_TO_LE_32(generation);
1614283514Sarybchik
1615283514Sarybchik	/* Recompute PARTITION_TRAILER checksum */
1616283514Sarybchik	trailer->checksum = 0;
1617283514Sarybchik	cksum = 0;
1618283514Sarybchik	for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1619291432Sarybchik		cksum += *((uint32_t *)(seg_data + pos));
1620283514Sarybchik	}
1621283514Sarybchik	trailer->checksum = ~cksum + 1;
1622283514Sarybchik
1623283514Sarybchik	return (0);
1624283514Sarybchik
1625291432Sarybchikfail7:
1626291432Sarybchik	EFSYS_PROBE(fail7);
1627283514Sarybchikfail6:
1628283514Sarybchik	EFSYS_PROBE(fail6);
1629283514Sarybchikfail5:
1630283514Sarybchik	EFSYS_PROBE(fail5);
1631283514Sarybchikfail4:
1632283514Sarybchik	EFSYS_PROBE(fail4);
1633283514Sarybchikfail3:
1634283514Sarybchik	EFSYS_PROBE(fail3);
1635283514Sarybchikfail2:
1636283514Sarybchik	EFSYS_PROBE(fail2);
1637283514Sarybchikfail1:
1638291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1639283514Sarybchik
1640283514Sarybchik	return (rc);
1641283514Sarybchik}
1642283514Sarybchik
1643291432Sarybchik/*
1644291432Sarybchik * Add or update a single TLV item in the first segment of a TLV formatted
1645291432Sarybchik * dynamic config partition. The first segment is the current active
1646291432Sarybchik * configuration.
1647291432Sarybchik */
1648291436Sarybchik	__checkReturn		efx_rc_t
1649293756Sarybchikef10_nvram_partn_write_tlv(
1650283514Sarybchik	__in			efx_nic_t *enp,
1651283514Sarybchik	__in			uint32_t partn,
1652283514Sarybchik	__in			uint32_t tag,
1653283514Sarybchik	__in_bcount(size)	caddr_t data,
1654283514Sarybchik	__in			size_t size)
1655283514Sarybchik{
1656293756Sarybchik	return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1657291432Sarybchik	    size, B_FALSE);
1658291432Sarybchik}
1659291432Sarybchik
1660291432Sarybchik/*
1661291432Sarybchik * Read a segment from nvram at the given offset into a buffer (segment_data)
1662291432Sarybchik * and optionally write a new tag to it.
1663291432Sarybchik */
1664301988Sarybchikstatic	__checkReturn		efx_rc_t
1665293756Sarybchikef10_nvram_segment_write_tlv(
1666291432Sarybchik	__in			efx_nic_t *enp,
1667291432Sarybchik	__in			uint32_t partn,
1668291432Sarybchik	__in			uint32_t tag,
1669291432Sarybchik	__in_bcount(size)	caddr_t data,
1670291432Sarybchik	__in			size_t size,
1671291432Sarybchik	__inout			caddr_t *seg_datap,
1672291432Sarybchik	__inout			size_t *partn_offsetp,
1673291432Sarybchik	__inout			size_t *src_remain_lenp,
1674291432Sarybchik	__inout			size_t *dest_remain_lenp,
1675291432Sarybchik	__in			boolean_t write)
1676291432Sarybchik{
1677291436Sarybchik	efx_rc_t rc;
1678293819Sarybchik	efx_rc_t status;
1679291432Sarybchik	size_t original_segment_size;
1680291432Sarybchik	size_t modified_segment_size;
1681291432Sarybchik
1682291432Sarybchik	/*
1683291432Sarybchik	 * Read the segment from NVRAM into the segment_data buffer and validate
1684291432Sarybchik	 * it, returning if it does not validate. This is not a failure unless
1685291432Sarybchik	 * this is the first segment in a partition. In this case the caller
1686298955Spfg	 * must propagate the error.
1687291432Sarybchik	 */
1688293756Sarybchik	status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1689291432Sarybchik	    *seg_datap, *src_remain_lenp);
1690301988Sarybchik	if (status != 0) {
1691301988Sarybchik		rc = EINVAL;
1692301988Sarybchik		goto fail1;
1693301988Sarybchik	}
1694291432Sarybchik
1695293756Sarybchik	status = ef10_nvram_buf_segment_size(*seg_datap,
1696291432Sarybchik	    *src_remain_lenp, &original_segment_size);
1697301988Sarybchik	if (status != 0) {
1698301988Sarybchik		rc = EINVAL;
1699301988Sarybchik		goto fail2;
1700301988Sarybchik	}
1701291432Sarybchik
1702291432Sarybchik	if (write) {
1703291432Sarybchik		/* Update the contents of the segment in the buffer */
1704293756Sarybchik		if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1705291432Sarybchik			*dest_remain_lenp, tag, data, size,
1706301988Sarybchik			&modified_segment_size)) != 0) {
1707301988Sarybchik			goto fail3;
1708301988Sarybchik		}
1709291432Sarybchik		*dest_remain_lenp -= modified_segment_size;
1710291432Sarybchik		*seg_datap += modified_segment_size;
1711291432Sarybchik	} else {
1712291432Sarybchik		/*
1713291432Sarybchik		 * We won't modify this segment, but still need to update the
1714291432Sarybchik		 * remaining lengths and pointers.
1715291432Sarybchik		 */
1716291432Sarybchik		*dest_remain_lenp -= original_segment_size;
1717291432Sarybchik		*seg_datap += original_segment_size;
1718291432Sarybchik	}
1719291432Sarybchik
1720291432Sarybchik	*partn_offsetp += original_segment_size;
1721291432Sarybchik	*src_remain_lenp -= original_segment_size;
1722291432Sarybchik
1723291432Sarybchik	return (0);
1724291432Sarybchik
1725301988Sarybchikfail3:
1726301988Sarybchik	EFSYS_PROBE(fail3);
1727301988Sarybchikfail2:
1728301988Sarybchik	EFSYS_PROBE(fail2);
1729291432Sarybchikfail1:
1730291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1731291432Sarybchik
1732291432Sarybchik	return (rc);
1733291432Sarybchik}
1734291432Sarybchik
1735291432Sarybchik/*
1736291432Sarybchik * Add or update a single TLV item in either the first segment or in all
1737291432Sarybchik * segments in a TLV formatted dynamic config partition. Dynamic config
1738291432Sarybchik * partitions on boards that support RFID are divided into a number of segments,
1739291432Sarybchik * each formatted like a partition, with header, trailer and end tags. The first
1740291432Sarybchik * segment is the current active configuration.
1741291432Sarybchik *
1742291432Sarybchik * The segments are initialised by manftest and each contain a different
1743291432Sarybchik * configuration e.g. firmware variant. The firmware can be instructed
1744291432Sarybchik * via RFID to copy a segment to replace the first segment, hence changing the
1745291432Sarybchik * active configuration.  This allows ops to change the configuration of a board
1746291432Sarybchik * prior to shipment using RFID.
1747291432Sarybchik *
1748291432Sarybchik * Changes to the dynamic config may need to be written to all segments (e.g.
1749291432Sarybchik * firmware versions) or just the first segment (changes to the active
1750291432Sarybchik * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1751291432Sarybchik * If only the first segment is written the code still needs to be aware of the
1752291432Sarybchik * possible presence of subsequent segments as writing to a segment may cause
1753291432Sarybchik * its size to increase, which would overwrite the subsequent segments and
1754291432Sarybchik * invalidate them.
1755291432Sarybchik */
1756291436Sarybchik	__checkReturn		efx_rc_t
1757293756Sarybchikef10_nvram_partn_write_segment_tlv(
1758291432Sarybchik	__in			efx_nic_t *enp,
1759291432Sarybchik	__in			uint32_t partn,
1760291432Sarybchik	__in			uint32_t tag,
1761291432Sarybchik	__in_bcount(size)	caddr_t data,
1762291432Sarybchik	__in			size_t size,
1763291432Sarybchik	__in			boolean_t all_segments)
1764291432Sarybchik{
1765291432Sarybchik	size_t partn_size = 0;
1766283514Sarybchik	caddr_t partn_data;
1767291432Sarybchik	size_t total_length = 0;
1768291436Sarybchik	efx_rc_t rc;
1769291432Sarybchik	size_t current_offset = 0;
1770291432Sarybchik	size_t remaining_original_length;
1771291432Sarybchik	size_t remaining_modified_length;
1772291432Sarybchik	caddr_t segment_data;
1773283514Sarybchik
1774283514Sarybchik	EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1775283514Sarybchik
1776283514Sarybchik	/* Allocate sufficient memory for the entire partition */
1777293756Sarybchik	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1778283514Sarybchik		goto fail1;
1779283514Sarybchik
1780283514Sarybchik	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1781283514Sarybchik	if (partn_data == NULL) {
1782283514Sarybchik		rc = ENOMEM;
1783283514Sarybchik		goto fail2;
1784283514Sarybchik	}
1785283514Sarybchik
1786291432Sarybchik	remaining_original_length = partn_size;
1787291432Sarybchik	remaining_modified_length = partn_size;
1788291432Sarybchik	segment_data = partn_data;
1789291432Sarybchik
1790283514Sarybchik	/* Lock the partition */
1791293756Sarybchik	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1792283514Sarybchik		goto fail3;
1793283514Sarybchik
1794291432Sarybchik	/* Iterate over each (potential) segment to update it. */
1795291432Sarybchik	do {
1796291432Sarybchik		boolean_t write = all_segments || current_offset == 0;
1797283514Sarybchik
1798293756Sarybchik		rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1799291432Sarybchik		    &segment_data, &current_offset, &remaining_original_length,
1800291432Sarybchik		    &remaining_modified_length, write);
1801291432Sarybchik		if (rc != 0) {
1802291432Sarybchik			if (current_offset == 0) {
1803291432Sarybchik				/*
1804291432Sarybchik				 * If no data has been read then the first
1805291432Sarybchik				 * segment is invalid, which is an error.
1806291432Sarybchik				 */
1807291432Sarybchik				goto fail4;
1808291432Sarybchik			}
1809291432Sarybchik			break;
1810291432Sarybchik		}
1811291432Sarybchik	} while (current_offset < partn_size);
1812291432Sarybchik
1813291432Sarybchik	total_length = segment_data - partn_data;
1814291432Sarybchik
1815291432Sarybchik	/*
1816291432Sarybchik	 * We've run out of space.  This should actually be dealt with by
1817293756Sarybchik	 * ef10_nvram_buf_write_tlv returning ENOSPC.
1818291432Sarybchik	 */
1819291432Sarybchik	if (total_length > partn_size) {
1820291432Sarybchik		rc = ENOSPC;
1821283514Sarybchik		goto fail5;
1822291432Sarybchik	}
1823283514Sarybchik
1824291432Sarybchik	/* Erase the whole partition in NVRAM */
1825293756Sarybchik	if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1826283514Sarybchik		goto fail6;
1827283514Sarybchik
1828291432Sarybchik	/* Write new partition contents from the buffer to NVRAM */
1829293756Sarybchik	if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1830283514Sarybchik		    total_length)) != 0)
1831283514Sarybchik		goto fail7;
1832283514Sarybchik
1833283514Sarybchik	/* Unlock the partition */
1834342511Sarybchik	(void) ef10_nvram_partn_unlock(enp, partn, NULL);
1835283514Sarybchik
1836283514Sarybchik	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1837283514Sarybchik
1838283514Sarybchik	return (0);
1839283514Sarybchik
1840283514Sarybchikfail7:
1841283514Sarybchik	EFSYS_PROBE(fail7);
1842283514Sarybchikfail6:
1843283514Sarybchik	EFSYS_PROBE(fail6);
1844283514Sarybchikfail5:
1845283514Sarybchik	EFSYS_PROBE(fail5);
1846283514Sarybchikfail4:
1847283514Sarybchik	EFSYS_PROBE(fail4);
1848283514Sarybchik
1849342511Sarybchik	(void) ef10_nvram_partn_unlock(enp, partn, NULL);
1850283514Sarybchikfail3:
1851283514Sarybchik	EFSYS_PROBE(fail3);
1852283514Sarybchik
1853283514Sarybchik	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1854283514Sarybchikfail2:
1855283514Sarybchik	EFSYS_PROBE(fail2);
1856283514Sarybchikfail1:
1857291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1858283514Sarybchik
1859283514Sarybchik	return (rc);
1860283514Sarybchik}
1861283514Sarybchik
1862291432Sarybchik/*
1863291432Sarybchik * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1864291432Sarybchik * not the data used by the segments in the partition.
1865291432Sarybchik */
1866291436Sarybchik	__checkReturn		efx_rc_t
1867293756Sarybchikef10_nvram_partn_size(
1868283514Sarybchik	__in			efx_nic_t *enp,
1869293770Sarybchik	__in			uint32_t partn,
1870283514Sarybchik	__out			size_t *sizep)
1871283514Sarybchik{
1872291436Sarybchik	efx_rc_t rc;
1873283514Sarybchik
1874291746Sarybchik	if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1875291746Sarybchik	    NULL, NULL, NULL)) != 0)
1876283514Sarybchik		goto fail1;
1877283514Sarybchik
1878283514Sarybchik	return (0);
1879283514Sarybchik
1880283514Sarybchikfail1:
1881291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1882283514Sarybchik
1883283514Sarybchik	return (rc);
1884283514Sarybchik}
1885283514Sarybchik
1886291436Sarybchik	__checkReturn		efx_rc_t
1887293756Sarybchikef10_nvram_partn_lock(
1888283514Sarybchik	__in			efx_nic_t *enp,
1889293770Sarybchik	__in			uint32_t partn)
1890283514Sarybchik{
1891291436Sarybchik	efx_rc_t rc;
1892283514Sarybchik
1893283514Sarybchik	if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1894283514Sarybchik		goto fail1;
1895283514Sarybchik
1896283514Sarybchik	return (0);
1897283514Sarybchik
1898283514Sarybchikfail1:
1899291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1900283514Sarybchik
1901283514Sarybchik	return (rc);
1902283514Sarybchik}
1903283514Sarybchik
1904291436Sarybchik	__checkReturn		efx_rc_t
1905294309Sarybchikef10_nvram_partn_read_mode(
1906283514Sarybchik	__in			efx_nic_t *enp,
1907293770Sarybchik	__in			uint32_t partn,
1908283514Sarybchik	__in			unsigned int offset,
1909283514Sarybchik	__out_bcount(size)	caddr_t data,
1910294309Sarybchik	__in			size_t size,
1911294309Sarybchik	__in			uint32_t mode)
1912283514Sarybchik{
1913283514Sarybchik	size_t chunk;
1914291436Sarybchik	efx_rc_t rc;
1915283514Sarybchik
1916283514Sarybchik	while (size > 0) {
1917293756Sarybchik		chunk = MIN(size, EF10_NVRAM_CHUNK);
1918283514Sarybchik
1919283514Sarybchik		if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
1920294309Sarybchik			    data, chunk, mode)) != 0) {
1921283514Sarybchik			goto fail1;
1922283514Sarybchik		}
1923283514Sarybchik
1924283514Sarybchik		size -= chunk;
1925283514Sarybchik		data += chunk;
1926283514Sarybchik		offset += chunk;
1927283514Sarybchik	}
1928283514Sarybchik
1929283514Sarybchik	return (0);
1930283514Sarybchik
1931283514Sarybchikfail1:
1932291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1933283514Sarybchik
1934283514Sarybchik	return (rc);
1935283514Sarybchik}
1936283514Sarybchik
1937291436Sarybchik	__checkReturn		efx_rc_t
1938294309Sarybchikef10_nvram_partn_read(
1939294309Sarybchik	__in			efx_nic_t *enp,
1940294309Sarybchik	__in			uint32_t partn,
1941294309Sarybchik	__in			unsigned int offset,
1942294309Sarybchik	__out_bcount(size)	caddr_t data,
1943294309Sarybchik	__in			size_t size)
1944294309Sarybchik{
1945294309Sarybchik	/*
1946294309Sarybchik	 * Read requests which come in through the EFX API expect to
1947294309Sarybchik	 * read the current, active partition.
1948294309Sarybchik	 */
1949294309Sarybchik	return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
1950294309Sarybchik			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
1951294309Sarybchik}
1952294309Sarybchik
1953294309Sarybchik	__checkReturn		efx_rc_t
1954293756Sarybchikef10_nvram_partn_erase(
1955283514Sarybchik	__in			efx_nic_t *enp,
1956293770Sarybchik	__in			uint32_t partn,
1957283514Sarybchik	__in			unsigned int offset,
1958283514Sarybchik	__in			size_t size)
1959283514Sarybchik{
1960291436Sarybchik	efx_rc_t rc;
1961291862Sarybchik	uint32_t erase_size;
1962283514Sarybchik
1963291862Sarybchik	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1964291862Sarybchik	    &erase_size, NULL)) != 0)
1965283514Sarybchik		goto fail1;
1966283514Sarybchik
1967291862Sarybchik	if (erase_size == 0) {
1968291862Sarybchik		if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
1969291862Sarybchik			goto fail2;
1970291862Sarybchik	} else {
1971291862Sarybchik		if (size % erase_size != 0) {
1972291862Sarybchik			rc = EINVAL;
1973291862Sarybchik			goto fail3;
1974291862Sarybchik		}
1975291862Sarybchik		while (size > 0) {
1976291862Sarybchik			if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
1977291862Sarybchik			    erase_size)) != 0)
1978291862Sarybchik				goto fail4;
1979291862Sarybchik			offset += erase_size;
1980291862Sarybchik			size -= erase_size;
1981291862Sarybchik		}
1982291862Sarybchik	}
1983291862Sarybchik
1984283514Sarybchik	return (0);
1985283514Sarybchik
1986291862Sarybchikfail4:
1987291862Sarybchik	EFSYS_PROBE(fail4);
1988291862Sarybchikfail3:
1989291862Sarybchik	EFSYS_PROBE(fail3);
1990291862Sarybchikfail2:
1991291862Sarybchik	EFSYS_PROBE(fail2);
1992283514Sarybchikfail1:
1993291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1994283514Sarybchik
1995283514Sarybchik	return (rc);
1996283514Sarybchik}
1997283514Sarybchik
1998291436Sarybchik	__checkReturn		efx_rc_t
1999293756Sarybchikef10_nvram_partn_write(
2000283514Sarybchik	__in			efx_nic_t *enp,
2001293770Sarybchik	__in			uint32_t partn,
2002283514Sarybchik	__in			unsigned int offset,
2003342512Sarybchik	__in_bcount(size)	caddr_t data,
2004283514Sarybchik	__in			size_t size)
2005283514Sarybchik{
2006283514Sarybchik	size_t chunk;
2007291923Sarybchik	uint32_t write_size;
2008291436Sarybchik	efx_rc_t rc;
2009283514Sarybchik
2010291923Sarybchik	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2011291923Sarybchik	    NULL, &write_size)) != 0)
2012291923Sarybchik		goto fail1;
2013291923Sarybchik
2014291923Sarybchik	if (write_size != 0) {
2015291923Sarybchik		/*
2016291923Sarybchik		 * Check that the size is a multiple of the write chunk size if
2017291923Sarybchik		 * the write chunk size is available.
2018291923Sarybchik		 */
2019291923Sarybchik		if (size % write_size != 0) {
2020291923Sarybchik			rc = EINVAL;
2021291923Sarybchik			goto fail2;
2022291923Sarybchik		}
2023291923Sarybchik	} else {
2024293756Sarybchik		write_size = EF10_NVRAM_CHUNK;
2025291923Sarybchik	}
2026291923Sarybchik
2027283514Sarybchik	while (size > 0) {
2028291923Sarybchik		chunk = MIN(size, write_size);
2029283514Sarybchik
2030283514Sarybchik		if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
2031283514Sarybchik			    data, chunk)) != 0) {
2032291923Sarybchik			goto fail3;
2033283514Sarybchik		}
2034283514Sarybchik
2035283514Sarybchik		size -= chunk;
2036283514Sarybchik		data += chunk;
2037283514Sarybchik		offset += chunk;
2038283514Sarybchik	}
2039283514Sarybchik
2040283514Sarybchik	return (0);
2041283514Sarybchik
2042291923Sarybchikfail3:
2043291923Sarybchik	EFSYS_PROBE(fail3);
2044291923Sarybchikfail2:
2045291923Sarybchik	EFSYS_PROBE(fail2);
2046283514Sarybchikfail1:
2047291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2048283514Sarybchik
2049283514Sarybchik	return (rc);
2050283514Sarybchik}
2051283514Sarybchik
2052311495Sarybchik	__checkReturn		efx_rc_t
2053293756Sarybchikef10_nvram_partn_unlock(
2054283514Sarybchik	__in			efx_nic_t *enp,
2055311498Sarybchik	__in			uint32_t partn,
2056311498Sarybchik	__out_opt		uint32_t *resultp)
2057283514Sarybchik{
2058311495Sarybchik	boolean_t reboot = B_FALSE;
2059291436Sarybchik	efx_rc_t rc;
2060283514Sarybchik
2061311498Sarybchik	if (resultp != NULL)
2062311498Sarybchik		*resultp = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
2063311498Sarybchik
2064311498Sarybchik	rc = efx_mcdi_nvram_update_finish(enp, partn, reboot, resultp);
2065311495Sarybchik	if (rc != 0)
2066283514Sarybchik		goto fail1;
2067283514Sarybchik
2068311495Sarybchik	return (0);
2069283514Sarybchik
2070283514Sarybchikfail1:
2071291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2072311495Sarybchik
2073311495Sarybchik	return (rc);
2074283514Sarybchik}
2075283514Sarybchik
2076291436Sarybchik	__checkReturn		efx_rc_t
2077293756Sarybchikef10_nvram_partn_set_version(
2078283514Sarybchik	__in			efx_nic_t *enp,
2079293770Sarybchik	__in			uint32_t partn,
2080283514Sarybchik	__in_ecount(4)		uint16_t version[4])
2081283514Sarybchik{
2082283514Sarybchik	struct tlv_partition_version partn_version;
2083283514Sarybchik	size_t size;
2084291436Sarybchik	efx_rc_t rc;
2085283514Sarybchik
2086283514Sarybchik	/* Add or modify partition version TLV item */
2087283514Sarybchik	partn_version.version_w = __CPU_TO_LE_16(version[0]);
2088283514Sarybchik	partn_version.version_x = __CPU_TO_LE_16(version[1]);
2089283514Sarybchik	partn_version.version_y = __CPU_TO_LE_16(version[2]);
2090283514Sarybchik	partn_version.version_z = __CPU_TO_LE_16(version[3]);
2091283514Sarybchik
2092283514Sarybchik	size = sizeof (partn_version) - (2 * sizeof (uint32_t));
2093283514Sarybchik
2094291432Sarybchik	/* Write the version number to all segments in the partition */
2095293756Sarybchik	if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
2096283514Sarybchik		    NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
2097283514Sarybchik		    TLV_TAG_PARTITION_VERSION(partn),
2098291432Sarybchik		    (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
2099283514Sarybchik		goto fail1;
2100283514Sarybchik
2101283514Sarybchik	return (0);
2102283514Sarybchik
2103283514Sarybchikfail1:
2104291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2105283514Sarybchik
2106283514Sarybchik	return (rc);
2107283514Sarybchik}
2108283514Sarybchik
2109283514Sarybchik#endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
2110283514Sarybchik
2111283514Sarybchik#if EFSYS_OPT_NVRAM
2112283514Sarybchik
2113293756Sarybchiktypedef struct ef10_parttbl_entry_s {
2114283514Sarybchik	unsigned int		partn;
2115283514Sarybchik	unsigned int		port;
2116283514Sarybchik	efx_nvram_type_t	nvtype;
2117293756Sarybchik} ef10_parttbl_entry_t;
2118283514Sarybchik
2119283514Sarybchik/* Translate EFX NVRAM types to firmware partition types */
2120293768Sarybchikstatic ef10_parttbl_entry_t hunt_parttbl[] = {
2121283514Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   1, EFX_NVRAM_MC_FIRMWARE},
2122283514Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   2, EFX_NVRAM_MC_FIRMWARE},
2123283514Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   3, EFX_NVRAM_MC_FIRMWARE},
2124283514Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   4, EFX_NVRAM_MC_FIRMWARE},
2125283514Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
2126283514Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
2127283514Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
2128283514Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
2129283514Sarybchik	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   1, EFX_NVRAM_BOOTROM},
2130283514Sarybchik	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   2, EFX_NVRAM_BOOTROM},
2131283514Sarybchik	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   3, EFX_NVRAM_BOOTROM},
2132283514Sarybchik	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   4, EFX_NVRAM_BOOTROM},
2133283514Sarybchik	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
2134283514Sarybchik	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1, 2, EFX_NVRAM_BOOTROM_CFG},
2135283514Sarybchik	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2, 3, EFX_NVRAM_BOOTROM_CFG},
2136283514Sarybchik	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 4, EFX_NVRAM_BOOTROM_CFG},
2137283514Sarybchik	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   1, EFX_NVRAM_DYNAMIC_CFG},
2138283514Sarybchik	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   2, EFX_NVRAM_DYNAMIC_CFG},
2139283514Sarybchik	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   3, EFX_NVRAM_DYNAMIC_CFG},
2140291587Sarybchik	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   4, EFX_NVRAM_DYNAMIC_CFG},
2141291587Sarybchik	{NVRAM_PARTITION_TYPE_FPGA,		   1, EFX_NVRAM_FPGA},
2142291587Sarybchik	{NVRAM_PARTITION_TYPE_FPGA,		   2, EFX_NVRAM_FPGA},
2143291587Sarybchik	{NVRAM_PARTITION_TYPE_FPGA,		   3, EFX_NVRAM_FPGA},
2144291587Sarybchik	{NVRAM_PARTITION_TYPE_FPGA,		   4, EFX_NVRAM_FPGA},
2145291587Sarybchik	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   1, EFX_NVRAM_FPGA_BACKUP},
2146291587Sarybchik	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   2, EFX_NVRAM_FPGA_BACKUP},
2147291587Sarybchik	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   3, EFX_NVRAM_FPGA_BACKUP},
2148293900Sarybchik	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   4, EFX_NVRAM_FPGA_BACKUP},
2149293900Sarybchik	{NVRAM_PARTITION_TYPE_LICENSE,		   1, EFX_NVRAM_LICENSE},
2150293900Sarybchik	{NVRAM_PARTITION_TYPE_LICENSE,		   2, EFX_NVRAM_LICENSE},
2151293900Sarybchik	{NVRAM_PARTITION_TYPE_LICENSE,		   3, EFX_NVRAM_LICENSE},
2152293900Sarybchik	{NVRAM_PARTITION_TYPE_LICENSE,		   4, EFX_NVRAM_LICENSE}
2153283514Sarybchik};
2154283514Sarybchik
2155293768Sarybchikstatic ef10_parttbl_entry_t medford_parttbl[] = {
2156293768Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   1, EFX_NVRAM_MC_FIRMWARE},
2157293768Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   2, EFX_NVRAM_MC_FIRMWARE},
2158293768Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   3, EFX_NVRAM_MC_FIRMWARE},
2159293768Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   4, EFX_NVRAM_MC_FIRMWARE},
2160293768Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
2161293768Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
2162293768Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
2163293768Sarybchik	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
2164293768Sarybchik	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   1, EFX_NVRAM_BOOTROM},
2165293768Sarybchik	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   2, EFX_NVRAM_BOOTROM},
2166293768Sarybchik	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   3, EFX_NVRAM_BOOTROM},
2167293768Sarybchik	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   4, EFX_NVRAM_BOOTROM},
2168293768Sarybchik	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
2169293768Sarybchik	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 2, EFX_NVRAM_BOOTROM_CFG},
2170293768Sarybchik	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 3, EFX_NVRAM_BOOTROM_CFG},
2171293768Sarybchik	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 4, EFX_NVRAM_BOOTROM_CFG},
2172293768Sarybchik	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   1, EFX_NVRAM_DYNAMIC_CFG},
2173293768Sarybchik	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   2, EFX_NVRAM_DYNAMIC_CFG},
2174293768Sarybchik	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   3, EFX_NVRAM_DYNAMIC_CFG},
2175293768Sarybchik	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   4, EFX_NVRAM_DYNAMIC_CFG},
2176293768Sarybchik	{NVRAM_PARTITION_TYPE_FPGA,		   1, EFX_NVRAM_FPGA},
2177293768Sarybchik	{NVRAM_PARTITION_TYPE_FPGA,		   2, EFX_NVRAM_FPGA},
2178293768Sarybchik	{NVRAM_PARTITION_TYPE_FPGA,		   3, EFX_NVRAM_FPGA},
2179293768Sarybchik	{NVRAM_PARTITION_TYPE_FPGA,		   4, EFX_NVRAM_FPGA},
2180293768Sarybchik	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   1, EFX_NVRAM_FPGA_BACKUP},
2181293768Sarybchik	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   2, EFX_NVRAM_FPGA_BACKUP},
2182293768Sarybchik	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   3, EFX_NVRAM_FPGA_BACKUP},
2183293900Sarybchik	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   4, EFX_NVRAM_FPGA_BACKUP},
2184293900Sarybchik	{NVRAM_PARTITION_TYPE_LICENSE,		   1, EFX_NVRAM_LICENSE},
2185293900Sarybchik	{NVRAM_PARTITION_TYPE_LICENSE,		   2, EFX_NVRAM_LICENSE},
2186293900Sarybchik	{NVRAM_PARTITION_TYPE_LICENSE,		   3, EFX_NVRAM_LICENSE},
2187311073Sarybchik	{NVRAM_PARTITION_TYPE_LICENSE,		   4, EFX_NVRAM_LICENSE},
2188311073Sarybchik	{NVRAM_PARTITION_TYPE_EXPANSION_UEFI,	   1, EFX_NVRAM_UEFIROM},
2189311073Sarybchik	{NVRAM_PARTITION_TYPE_EXPANSION_UEFI,	   2, EFX_NVRAM_UEFIROM},
2190311073Sarybchik	{NVRAM_PARTITION_TYPE_EXPANSION_UEFI,	   3, EFX_NVRAM_UEFIROM},
2191311073Sarybchik	{NVRAM_PARTITION_TYPE_EXPANSION_UEFI,	   4, EFX_NVRAM_UEFIROM}
2192293768Sarybchik};
2193293768Sarybchik
2194293810Sarybchikstatic	__checkReturn		efx_rc_t
2195293810Sarybchikef10_parttbl_get(
2196283514Sarybchik	__in			efx_nic_t *enp,
2197293810Sarybchik	__out			ef10_parttbl_entry_t **parttblp,
2198293810Sarybchik	__out			size_t *parttbl_rowsp)
2199283514Sarybchik{
2200293768Sarybchik	switch (enp->en_family) {
2201293768Sarybchik	case EFX_FAMILY_HUNTINGTON:
2202293810Sarybchik		*parttblp = hunt_parttbl;
2203293810Sarybchik		*parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
2204293768Sarybchik		break;
2205283514Sarybchik
2206293768Sarybchik	case EFX_FAMILY_MEDFORD:
2207293810Sarybchik		*parttblp = medford_parttbl;
2208293810Sarybchik		*parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
2209293768Sarybchik		break;
2210293768Sarybchik
2211293768Sarybchik	default:
2212293768Sarybchik		EFSYS_ASSERT(B_FALSE);
2213293810Sarybchik		return (EINVAL);
2214283514Sarybchik	}
2215293810Sarybchik	return (0);
2216293810Sarybchik}
2217283514Sarybchik
2218293810Sarybchik	__checkReturn		efx_rc_t
2219293810Sarybchikef10_nvram_type_to_partn(
2220293810Sarybchik	__in			efx_nic_t *enp,
2221293810Sarybchik	__in			efx_nvram_type_t type,
2222293810Sarybchik	__out			uint32_t *partnp)
2223293810Sarybchik{
2224293810Sarybchik	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2225293810Sarybchik	ef10_parttbl_entry_t *parttbl = NULL;
2226293810Sarybchik	size_t parttbl_rows = 0;
2227293810Sarybchik	unsigned int i;
2228293768Sarybchik
2229293810Sarybchik	EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
2230293810Sarybchik	EFSYS_ASSERT(partnp != NULL);
2231293810Sarybchik
2232293810Sarybchik	if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2233293810Sarybchik		for (i = 0; i < parttbl_rows; i++) {
2234293810Sarybchik			ef10_parttbl_entry_t *entry = &parttbl[i];
2235293810Sarybchik
2236293810Sarybchik			if (entry->nvtype == type &&
2237293810Sarybchik			    entry->port == emip->emi_port) {
2238293810Sarybchik				*partnp = entry->partn;
2239293810Sarybchik				return (0);
2240293768Sarybchik			}
2241293768Sarybchik		}
2242293768Sarybchik	}
2243293810Sarybchik
2244293810Sarybchik	return (ENOTSUP);
2245283514Sarybchik}
2246283514Sarybchik
2247294258Sarybchik#if EFSYS_OPT_DIAG
2248283514Sarybchik
2249293810Sarybchikstatic	__checkReturn		efx_rc_t
2250293810Sarybchikef10_nvram_partn_to_type(
2251293810Sarybchik	__in			efx_nic_t *enp,
2252293810Sarybchik	__in			uint32_t partn,
2253293810Sarybchik	__out			efx_nvram_type_t *typep)
2254293810Sarybchik{
2255293810Sarybchik	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2256293810Sarybchik	ef10_parttbl_entry_t *parttbl = NULL;
2257293810Sarybchik	size_t parttbl_rows = 0;
2258293810Sarybchik	unsigned int i;
2259293810Sarybchik
2260293810Sarybchik	EFSYS_ASSERT(typep != NULL);
2261293810Sarybchik
2262293810Sarybchik	if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2263293810Sarybchik		for (i = 0; i < parttbl_rows; i++) {
2264293810Sarybchik			ef10_parttbl_entry_t *entry = &parttbl[i];
2265293810Sarybchik
2266293810Sarybchik			if (entry->partn == partn &&
2267293810Sarybchik			    entry->port == emip->emi_port) {
2268293810Sarybchik				*typep = entry->nvtype;
2269293810Sarybchik				return (0);
2270293810Sarybchik			}
2271293810Sarybchik		}
2272293810Sarybchik	}
2273293810Sarybchik
2274293810Sarybchik	return (ENOTSUP);
2275293810Sarybchik}
2276293810Sarybchik
2277291436Sarybchik	__checkReturn		efx_rc_t
2278293756Sarybchikef10_nvram_test(
2279283514Sarybchik	__in			efx_nic_t *enp)
2280283514Sarybchik{
2281293810Sarybchik	efx_nvram_type_t type;
2282283514Sarybchik	unsigned int npartns = 0;
2283283514Sarybchik	uint32_t *partns = NULL;
2284283514Sarybchik	size_t size;
2285293768Sarybchik	unsigned int i;
2286291436Sarybchik	efx_rc_t rc;
2287283514Sarybchik
2288293810Sarybchik	/* Read available partitions from NVRAM partition map */
2289283514Sarybchik	size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
2290283514Sarybchik	EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
2291283514Sarybchik	if (partns == NULL) {
2292283514Sarybchik		rc = ENOMEM;
2293283514Sarybchik		goto fail1;
2294283514Sarybchik	}
2295283514Sarybchik
2296283514Sarybchik	if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
2297283514Sarybchik		    &npartns)) != 0) {
2298283514Sarybchik		goto fail2;
2299283514Sarybchik	}
2300283514Sarybchik
2301293810Sarybchik	for (i = 0; i < npartns; i++) {
2302293810Sarybchik		/* Check if the partition is supported for this port */
2303293810Sarybchik		if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
2304283514Sarybchik			continue;
2305283514Sarybchik
2306293810Sarybchik		if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
2307293810Sarybchik			goto fail3;
2308283514Sarybchik	}
2309283514Sarybchik
2310283514Sarybchik	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2311283514Sarybchik	return (0);
2312283514Sarybchik
2313283514Sarybchikfail3:
2314283514Sarybchik	EFSYS_PROBE(fail3);
2315283514Sarybchikfail2:
2316283514Sarybchik	EFSYS_PROBE(fail2);
2317283514Sarybchik	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2318283514Sarybchikfail1:
2319291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2320283514Sarybchik	return (rc);
2321283514Sarybchik}
2322283514Sarybchik
2323283514Sarybchik#endif	/* EFSYS_OPT_DIAG */
2324283514Sarybchik
2325291436Sarybchik	__checkReturn		efx_rc_t
2326294251Sarybchikef10_nvram_partn_get_version(
2327283514Sarybchik	__in			efx_nic_t *enp,
2328294251Sarybchik	__in			uint32_t partn,
2329283514Sarybchik	__out			uint32_t *subtypep,
2330283514Sarybchik	__out_ecount(4)		uint16_t version[4])
2331283514Sarybchik{
2332291436Sarybchik	efx_rc_t rc;
2333283514Sarybchik
2334283514Sarybchik	/* FIXME: get highest partn version from all ports */
2335283514Sarybchik	/* FIXME: return partn description if available */
2336283514Sarybchik
2337283514Sarybchik	if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
2338283514Sarybchik		    version, NULL, 0)) != 0)
2339294251Sarybchik		goto fail1;
2340283514Sarybchik
2341283514Sarybchik	return (0);
2342283514Sarybchik
2343283514Sarybchikfail1:
2344291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2345283514Sarybchik
2346283514Sarybchik	return (rc);
2347283514Sarybchik}
2348283514Sarybchik
2349291436Sarybchik	__checkReturn		efx_rc_t
2350294080Sarybchikef10_nvram_partn_rw_start(
2351283514Sarybchik	__in			efx_nic_t *enp,
2352294080Sarybchik	__in			uint32_t partn,
2353283514Sarybchik	__out			size_t *chunk_sizep)
2354283514Sarybchik{
2355291436Sarybchik	efx_rc_t rc;
2356283514Sarybchik
2357294080Sarybchik	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
2358283514Sarybchik		goto fail1;
2359283514Sarybchik
2360283514Sarybchik	if (chunk_sizep != NULL)
2361293756Sarybchik		*chunk_sizep = EF10_NVRAM_CHUNK;
2362283514Sarybchik
2363283514Sarybchik	return (0);
2364283514Sarybchik
2365283514Sarybchikfail1:
2366291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2367283514Sarybchik
2368283514Sarybchik	return (rc);
2369283514Sarybchik}
2370283514Sarybchik
2371311495Sarybchik	__checkReturn		efx_rc_t
2372294250Sarybchikef10_nvram_partn_rw_finish(
2373283514Sarybchik	__in			efx_nic_t *enp,
2374294250Sarybchik	__in			uint32_t partn)
2375283514Sarybchik{
2376311495Sarybchik	efx_rc_t rc;
2377311495Sarybchik
2378311498Sarybchik	if ((rc = ef10_nvram_partn_unlock(enp, partn, NULL)) != 0)
2379311495Sarybchik		goto fail1;
2380311495Sarybchik
2381311495Sarybchik	return (0);
2382311495Sarybchik
2383311495Sarybchikfail1:
2384311495Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2385311495Sarybchik
2386311495Sarybchik	return (rc);
2387283514Sarybchik}
2388283514Sarybchik
2389283514Sarybchik#endif	/* EFSYS_OPT_NVRAM */
2390283514Sarybchik
2391299602Sarybchik#endif	/* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
2392