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