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