2 * Copyright (c) 2012-2016 Solarflare Communications Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
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.
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
37 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
39 #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
41 #include "ef10_tlv_layout.h"
43 /* Cursor for TLV partition format */
44 typedef struct tlv_cursor_s {
45 uint32_t *block; /* Base of data block */
46 uint32_t *current; /* Cursor position */
47 uint32_t *end; /* End tag position */
48 uint32_t *limit; /* Last dword of data block */
51 typedef struct nvram_partition_s {
56 * The full length of the NVRAM partition.
57 * This is different from tlv_partition_header.total_length,
58 * which can be smaller.
63 tlv_cursor_t tlv_cursor;
67 static __checkReturn efx_rc_t
69 __inout tlv_cursor_t *cursor);
74 __out uint32_t *block)
76 *block = __CPU_TO_LE_32(TLV_TAG_END);
81 __in tlv_cursor_t *cursor)
85 dword = cursor->current[0];
86 tag = __LE_TO_CPU_32(dword);
93 __in tlv_cursor_t *cursor)
95 uint32_t dword, length;
97 if (tlv_tag(cursor) == TLV_TAG_END)
100 dword = cursor->current[1];
101 length = __LE_TO_CPU_32(dword);
103 return ((size_t)length);
108 __in tlv_cursor_t *cursor)
110 if (tlv_tag(cursor) == TLV_TAG_END)
113 return ((uint8_t *)(&cursor->current[2]));
118 __in tlv_cursor_t *cursor)
120 if (tlv_tag(cursor) == TLV_TAG_END)
123 return ((uint8_t *)cursor->current);
127 * TLV item DWORD length is tag + length + value (rounded up to DWORD)
128 * equivalent to tlv_n_words_for_len in mc-comms tlv.c
130 #define TLV_DWORD_COUNT(length) \
131 (1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
136 __in tlv_cursor_t *cursor)
140 length = tlv_length(cursor);
142 return (cursor->current + TLV_DWORD_COUNT(length));
145 static __checkReturn efx_rc_t
147 __inout tlv_cursor_t *cursor)
151 if ((rc = tlv_validate_state(cursor)) != 0)
154 if (cursor->current == cursor->end) {
155 /* No more tags after END tag */
156 cursor->current = NULL;
161 /* Advance to next item and validate */
162 cursor->current = tlv_next_item_ptr(cursor);
164 if ((rc = tlv_validate_state(cursor)) != 0)
174 EFSYS_PROBE1(fail1, efx_rc_t, rc);
181 __in tlv_cursor_t *cursor)
185 cursor->current = cursor->block;
187 if ((rc = tlv_validate_state(cursor)) != 0)
193 EFSYS_PROBE1(fail1, efx_rc_t, rc);
200 __inout tlv_cursor_t *cursor,
205 rc = tlv_rewind(cursor);
207 if (tlv_tag(cursor) == tag)
210 rc = tlv_advance(cursor);
215 static __checkReturn efx_rc_t
217 __inout tlv_cursor_t *cursor)
221 /* Check cursor position */
222 if (cursor->current < cursor->block) {
226 if (cursor->current > cursor->limit) {
231 if (tlv_tag(cursor) != TLV_TAG_END) {
232 /* Check current item has space for tag and length */
233 if (cursor->current > (cursor->limit - 1)) {
234 cursor->current = NULL;
239 /* Check we have value data for current item and an END tag */
240 if (tlv_next_item_ptr(cursor) > cursor->limit) {
241 cursor->current = NULL;
256 EFSYS_PROBE1(fail1, efx_rc_t, rc);
263 __out tlv_cursor_t *cursor,
264 __in uint32_t *block,
265 __in uint32_t *limit,
266 __in uint32_t *current)
268 cursor->block = block;
269 cursor->limit = limit;
271 cursor->current = current;
274 return (tlv_validate_state(cursor));
277 static __checkReturn efx_rc_t
278 tlv_init_cursor_from_size(
279 __out tlv_cursor_t *cursor,
285 limit = (uint32_t *)(block + size - sizeof (uint32_t));
286 return (tlv_init_cursor(cursor, (uint32_t *)block,
287 limit, (uint32_t *)block));
290 static __checkReturn efx_rc_t
291 tlv_init_cursor_at_offset(
292 __out tlv_cursor_t *cursor,
300 limit = (uint32_t *)(block + size - sizeof (uint32_t));
301 current = (uint32_t *)(block + offset);
302 return (tlv_init_cursor(cursor, (uint32_t *)block, limit, current));
305 static __checkReturn efx_rc_t
307 __inout tlv_cursor_t *cursor)
312 if (cursor->end == NULL) {
313 pos = cursor->current;
314 if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
317 cursor->end = cursor->current;
318 cursor->current = pos;
324 EFSYS_PROBE1(fail1, efx_rc_t, rc);
330 tlv_block_length_used(
331 __inout tlv_cursor_t *cursor)
335 if ((rc = tlv_validate_state(cursor)) != 0)
338 if ((rc = tlv_require_end(cursor)) != 0)
341 /* Return space used (including the END tag) */
342 return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
347 EFSYS_PROBE1(fail1, efx_rc_t, rc);
353 tlv_last_segment_end(
354 __in tlv_cursor_t *cursor)
356 tlv_cursor_t segment_cursor;
357 uint32_t *last_segment_end = cursor->block;
358 uint32_t *segment_start = cursor->block;
361 * Go through each segment and check that it has an end tag. If there
362 * is no end tag then the previous segment was the last valid one,
363 * so return the pointer to its end tag.
366 if (tlv_init_cursor(&segment_cursor, segment_start,
367 cursor->limit, segment_start) != 0)
369 if (tlv_require_end(&segment_cursor) != 0)
371 last_segment_end = segment_cursor.end;
372 segment_start = segment_cursor.end + 1;
375 return (last_segment_end);
381 __in tlv_cursor_t *cursor,
383 __in_bcount(size) uint8_t *data,
389 ptr = cursor->current;
391 *ptr++ = __CPU_TO_LE_32(tag);
392 *ptr++ = __CPU_TO_LE_32(len);
395 ptr[(len - 1) / sizeof (uint32_t)] = 0;
396 memcpy(ptr, data, len);
397 ptr += EFX_P2ROUNDUP(uint32_t, len,
398 sizeof (uint32_t)) / sizeof (*ptr);
404 static __checkReturn efx_rc_t
406 __inout tlv_cursor_t *cursor,
413 uint32_t *last_segment_end;
416 if ((rc = tlv_validate_state(cursor)) != 0)
419 if ((rc = tlv_require_end(cursor)) != 0)
422 if (tag == TLV_TAG_END) {
427 last_segment_end = tlv_last_segment_end(cursor);
429 delta = TLV_DWORD_COUNT(size);
430 if (last_segment_end + 1 + delta > cursor->limit) {
435 /* Move data up: new space at cursor->current */
436 memmove(cursor->current + delta, cursor->current,
437 (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
439 /* Adjust the end pointer */
440 cursor->end += delta;
442 /* Write new TLV item */
443 tlv_write(cursor, tag, data, size);
454 EFSYS_PROBE1(fail1, efx_rc_t, rc);
459 static __checkReturn efx_rc_t
461 __inout tlv_cursor_t *cursor)
464 uint32_t *last_segment_end;
467 if ((rc = tlv_validate_state(cursor)) != 0)
470 if (tlv_tag(cursor) == TLV_TAG_END) {
475 delta = TLV_DWORD_COUNT(tlv_length(cursor));
477 if ((rc = tlv_require_end(cursor)) != 0)
480 last_segment_end = tlv_last_segment_end(cursor);
482 /* Shuffle things down, destroying the item at cursor->current */
483 memmove(cursor->current, cursor->current + delta,
484 (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
485 /* Zero the new space at the end of the TLV chain */
486 memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t));
487 /* Adjust the end pointer */
488 cursor->end -= delta;
497 EFSYS_PROBE1(fail1, efx_rc_t, rc);
502 static __checkReturn efx_rc_t
504 __inout tlv_cursor_t *cursor,
511 unsigned int old_ndwords;
512 unsigned int new_ndwords;
514 uint32_t *last_segment_end;
517 if ((rc = tlv_validate_state(cursor)) != 0)
520 if (tlv_tag(cursor) == TLV_TAG_END) {
524 if (tlv_tag(cursor) != tag) {
529 old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
530 new_ndwords = TLV_DWORD_COUNT(size);
532 if ((rc = tlv_require_end(cursor)) != 0)
535 last_segment_end = tlv_last_segment_end(cursor);
537 if (new_ndwords > old_ndwords) {
538 /* Expand space used for TLV item */
539 delta = new_ndwords - old_ndwords;
540 pos = cursor->current + old_ndwords;
542 if (last_segment_end + 1 + delta > cursor->limit) {
547 /* Move up: new space at (cursor->current + old_ndwords) */
548 memmove(pos + delta, pos,
549 (last_segment_end + 1 - pos) * sizeof (uint32_t));
551 /* Adjust the end pointer */
552 cursor->end += delta;
554 } else if (new_ndwords < old_ndwords) {
555 /* Shrink space used for TLV item */
556 delta = old_ndwords - new_ndwords;
557 pos = cursor->current + new_ndwords;
559 /* Move down: remove words at (cursor->current + new_ndwords) */
560 memmove(pos, pos + delta,
561 (last_segment_end + 1 - pos) * sizeof (uint32_t));
563 /* Zero the new space at the end of the TLV chain */
564 memset(last_segment_end + 1 - delta, 0,
565 delta * sizeof (uint32_t));
567 /* Adjust the end pointer */
568 cursor->end -= delta;
572 tlv_write(cursor, tag, data, size);
585 EFSYS_PROBE1(fail1, efx_rc_t, rc);
590 static uint32_t checksum_tlv_partition(
591 __in nvram_partition_t *partition)
593 tlv_cursor_t *cursor;
599 cursor = &partition->tlv_cursor;
600 len = tlv_block_length_used(cursor);
601 EFSYS_ASSERT3U((len & 3), ==, 0);
604 ptr = partition->data;
605 end = &ptr[len >> 2];
608 csum += __LE_TO_CPU_32(*ptr++);
613 static __checkReturn efx_rc_t
614 tlv_update_partition_len_and_cks(
615 __in tlv_cursor_t *cursor)
618 nvram_partition_t partition;
619 struct tlv_partition_header *header;
620 struct tlv_partition_trailer *trailer;
624 * We just modified the partition, so the total length may not be
625 * valid. Don't use tlv_find(), which performs some sanity checks
626 * that may fail here.
628 partition.data = cursor->block;
629 memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
630 header = (struct tlv_partition_header *)partition.data;
632 if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
636 new_len = tlv_block_length_used(&partition.tlv_cursor);
641 header->total_length = __CPU_TO_LE_32(new_len);
642 /* Ensure the modified partition always has a new generation count. */
643 header->generation = __CPU_TO_LE_32(
644 __LE_TO_CPU_32(header->generation) + 1);
646 trailer = (struct tlv_partition_trailer *)((uint8_t *)header +
647 new_len - sizeof (*trailer) - sizeof (uint32_t));
648 trailer->generation = header->generation;
649 trailer->checksum = __CPU_TO_LE_32(
650 __LE_TO_CPU_32(trailer->checksum) -
651 checksum_tlv_partition(&partition));
658 EFSYS_PROBE1(fail1, efx_rc_t, rc);
663 /* Validate buffer contents (before writing to flash) */
664 __checkReturn efx_rc_t
665 ef10_nvram_buffer_validate(
667 __in_bcount(partn_size) caddr_t partn_data,
668 __in size_t partn_size)
671 struct tlv_partition_header *header;
672 struct tlv_partition_trailer *trailer;
678 EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
680 if ((partn_data == NULL) || (partn_size == 0)) {
685 /* The partition header must be the first item (at offset zero) */
686 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
691 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
695 header = (struct tlv_partition_header *)tlv_item(&cursor);
697 /* Check TLV partition length (includes the END tag) */
698 total_length = __LE_TO_CPU_32(header->total_length);
699 if (total_length > partn_size) {
704 /* Check partition header matches partn */
705 if (__LE_TO_CPU_16(header->type_id) != partn) {
710 /* Check partition ends with PARTITION_TRAILER and END tags */
711 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
715 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
717 if ((rc = tlv_advance(&cursor)) != 0) {
721 if (tlv_tag(&cursor) != TLV_TAG_END) {
726 /* Check generation counts are consistent */
727 if (trailer->generation != header->generation) {
732 /* Verify partition checksum */
734 for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
735 cksum += *((uint32_t *)(partn_data + pos));
763 EFSYS_PROBE1(fail1, efx_rc_t, rc);
769 ef10_nvram_buffer_init(
770 __out_bcount(buffer_size)
772 __in size_t buffer_size)
774 uint32_t *buf = (uint32_t *)bufferp;
776 memset(buf, 0xff, buffer_size);
781 __checkReturn efx_rc_t
782 ef10_nvram_buffer_create(
783 __in uint32_t partn_type,
784 __out_bcount(partn_size)
786 __in size_t partn_size)
788 uint32_t *buf = (uint32_t *)partn_data;
791 struct tlv_partition_header header;
792 struct tlv_partition_trailer trailer;
794 unsigned int min_buf_size = sizeof (struct tlv_partition_header) +
795 sizeof (struct tlv_partition_trailer);
796 if (partn_size < min_buf_size) {
801 ef10_nvram_buffer_init(partn_data, partn_size);
803 if ((rc = tlv_init_cursor(&cursor, buf,
804 (uint32_t *)((uint8_t *)buf + partn_size),
809 header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
810 header.length = __CPU_TO_LE_32(sizeof (header) - 8);
811 header.type_id = __CPU_TO_LE_16(partn_type);
813 header.generation = __CPU_TO_LE_32(1);
814 header.total_length = 0; /* This will be fixed below. */
815 if ((rc = tlv_insert(
816 &cursor, TLV_TAG_PARTITION_HEADER,
817 (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
819 if ((rc = tlv_advance(&cursor)) != 0)
822 trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
823 trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
824 trailer.generation = header.generation;
825 trailer.checksum = 0; /* This will be fixed below. */
826 if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
827 (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
830 if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
833 /* Check that the partition is valid. */
834 if ((rc = ef10_nvram_buffer_validate(partn_type,
835 partn_data, partn_size)) != 0)
853 EFSYS_PROBE1(fail1, efx_rc_t, rc);
860 __in uint32_t *position,
863 return (uint32_t)((uint8_t *)position - (uint8_t *)base);
866 __checkReturn efx_rc_t
867 ef10_nvram_buffer_find_item_start(
868 __in_bcount(buffer_size)
870 __in size_t buffer_size,
871 __out uint32_t *startp)
873 /* Read past partition header to find start address of the first key */
877 /* A PARTITION_HEADER tag must be the first item (at offset zero) */
878 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
879 buffer_size)) != 0) {
883 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
888 if ((rc = tlv_advance(&cursor)) != 0) {
892 *startp = byte_offset(cursor.current, cursor.block);
894 if ((rc = tlv_require_end(&cursor)) != 0)
906 EFSYS_PROBE1(fail1, efx_rc_t, rc);
911 __checkReturn efx_rc_t
912 ef10_nvram_buffer_find_end(
913 __in_bcount(buffer_size)
915 __in size_t buffer_size,
916 __in uint32_t offset,
917 __out uint32_t *endp)
919 /* Read to end of partition */
922 uint32_t *segment_used;
924 _NOTE(ARGUNUSED(offset))
926 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
927 buffer_size)) != 0) {
932 segment_used = cursor.block;
935 * Go through each segment and check that it has an end tag. If there
936 * is no end tag then the previous segment was the last valid one,
937 * so return the used space including that end tag.
939 while (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
940 if (tlv_require_end(&cursor) != 0) {
941 if (segment_used == cursor.block) {
943 * First segment is corrupt, so there is
944 * no valid data in partition.
951 segment_used = cursor.end + 1;
953 cursor.current = segment_used;
955 /* Return space used (including the END tag) */
956 *endp = (segment_used - cursor.block) * sizeof (uint32_t);
963 EFSYS_PROBE1(fail1, efx_rc_t, rc);
968 __checkReturn __success(return != B_FALSE) boolean_t
969 ef10_nvram_buffer_find_item(
970 __in_bcount(buffer_size)
972 __in size_t buffer_size,
973 __in uint32_t offset,
974 __out uint32_t *startp,
975 __out uint32_t *lengthp)
977 /* Find TLV at offset and return key start and length */
982 if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
983 buffer_size, offset) != 0) {
987 while ((key = tlv_item(&cursor)) != NULL) {
988 tag = tlv_tag(&cursor);
989 if (tag == TLV_TAG_PARTITION_HEADER ||
990 tag == TLV_TAG_PARTITION_TRAILER) {
991 if (tlv_advance(&cursor) != 0) {
996 *startp = byte_offset(cursor.current, cursor.block);
997 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1005 __checkReturn efx_rc_t
1006 ef10_nvram_buffer_peek_item(
1007 __in_bcount(buffer_size)
1009 __in size_t buffer_size,
1010 __in uint32_t offset,
1011 __out uint32_t *tagp,
1012 __out uint32_t *lengthp,
1013 __out uint32_t *value_offsetp)
1016 tlv_cursor_t cursor;
1019 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1020 buffer_size, offset)) != 0) {
1024 tag = tlv_tag(&cursor);
1026 if (tag == TLV_TAG_END) {
1028 * To allow stepping over the END tag, report the full tag
1029 * length and a zero length value.
1031 *lengthp = sizeof (tag);
1032 *value_offsetp = sizeof (tag);
1034 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1036 *value_offsetp = byte_offset((uint32_t *)tlv_value(&cursor),
1042 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1047 __checkReturn efx_rc_t
1048 ef10_nvram_buffer_get_item(
1049 __in_bcount(buffer_size)
1051 __in size_t buffer_size,
1052 __in uint32_t offset,
1053 __in uint32_t length,
1054 __out uint32_t *tagp,
1055 __out_bcount_part(value_max_size, *lengthp)
1057 __in size_t value_max_size,
1058 __out uint32_t *lengthp)
1061 tlv_cursor_t cursor;
1062 uint32_t value_length;
1064 if (buffer_size < (offset + length)) {
1069 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1070 buffer_size, offset)) != 0) {
1074 value_length = tlv_length(&cursor);
1075 if (value_max_size < value_length) {
1079 memcpy(valuep, tlv_value(&cursor), value_length);
1081 *tagp = tlv_tag(&cursor);
1082 *lengthp = value_length;
1091 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1096 __checkReturn efx_rc_t
1097 ef10_nvram_buffer_insert_item(
1098 __in_bcount(buffer_size)
1100 __in size_t buffer_size,
1101 __in uint32_t offset,
1103 __in_bcount(length) caddr_t valuep,
1104 __in uint32_t length,
1105 __out uint32_t *lengthp)
1108 tlv_cursor_t cursor;
1110 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1111 buffer_size, offset)) != 0) {
1115 rc = tlv_insert(&cursor, tag, (uint8_t *)valuep, length);
1120 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1128 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1133 __checkReturn efx_rc_t
1134 ef10_nvram_buffer_modify_item(
1135 __in_bcount(buffer_size)
1137 __in size_t buffer_size,
1138 __in uint32_t offset,
1140 __in_bcount(length) caddr_t valuep,
1141 __in uint32_t length,
1142 __out uint32_t *lengthp)
1145 tlv_cursor_t cursor;
1147 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1148 buffer_size, offset)) != 0) {
1152 rc = tlv_modify(&cursor, tag, (uint8_t *)valuep, length);
1158 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1166 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1172 __checkReturn efx_rc_t
1173 ef10_nvram_buffer_delete_item(
1174 __in_bcount(buffer_size)
1176 __in size_t buffer_size,
1177 __in uint32_t offset,
1178 __in uint32_t length,
1182 tlv_cursor_t cursor;
1184 _NOTE(ARGUNUSED(length, end))
1186 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1187 buffer_size, offset)) != 0) {
1191 if ((rc = tlv_delete(&cursor)) != 0)
1199 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1204 __checkReturn efx_rc_t
1205 ef10_nvram_buffer_finish(
1206 __in_bcount(buffer_size)
1208 __in size_t buffer_size)
1211 tlv_cursor_t cursor;
1213 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
1214 buffer_size)) != 0) {
1219 if ((rc = tlv_require_end(&cursor)) != 0)
1222 if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
1232 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1240 * Read and validate a segment from a partition. A segment is a complete
1241 * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
1242 * be multiple segments in a partition, so seg_offset allows segments
1243 * beyond the first to be read.
1245 static __checkReturn efx_rc_t
1246 ef10_nvram_read_tlv_segment(
1247 __in efx_nic_t *enp,
1248 __in uint32_t partn,
1249 __in size_t seg_offset,
1250 __in_bcount(max_seg_size) caddr_t seg_data,
1251 __in size_t max_seg_size)
1253 tlv_cursor_t cursor;
1254 struct tlv_partition_header *header;
1255 struct tlv_partition_trailer *trailer;
1256 size_t total_length;
1261 EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
1263 if ((seg_data == NULL) || (max_seg_size == 0)) {
1268 /* Read initial chunk of the segment, starting at offset */
1269 if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
1271 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
1275 /* A PARTITION_HEADER tag must be the first item at the given offset */
1276 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1277 max_seg_size)) != 0) {
1281 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1285 header = (struct tlv_partition_header *)tlv_item(&cursor);
1287 /* Check TLV segment length (includes the END tag) */
1288 total_length = __LE_TO_CPU_32(header->total_length);
1289 if (total_length > max_seg_size) {
1294 /* Read the remaining segment content */
1295 if (total_length > EF10_NVRAM_CHUNK) {
1296 if ((rc = ef10_nvram_partn_read_mode(enp, partn,
1297 seg_offset + EF10_NVRAM_CHUNK,
1298 seg_data + EF10_NVRAM_CHUNK,
1299 total_length - EF10_NVRAM_CHUNK,
1300 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
1304 /* Check segment ends with PARTITION_TRAILER and END tags */
1305 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1309 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1311 if ((rc = tlv_advance(&cursor)) != 0) {
1315 if (tlv_tag(&cursor) != TLV_TAG_END) {
1320 /* Check data read from segment is consistent */
1321 if (trailer->generation != header->generation) {
1323 * The partition data may have been modified between successive
1324 * MCDI NVRAM_READ requests by the MC or another PCI function.
1326 * The caller must retry to obtain consistent partition data.
1332 /* Verify segment checksum */
1334 for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
1335 cksum += *((uint32_t *)(seg_data + pos));
1345 EFSYS_PROBE(fail11);
1347 EFSYS_PROBE(fail10);
1365 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1371 * Read a single TLV item from a host memory
1372 * buffer containing a TLV formatted segment.
1374 __checkReturn efx_rc_t
1375 ef10_nvram_buf_read_tlv(
1376 __in efx_nic_t *enp,
1377 __in_bcount(max_seg_size) caddr_t seg_data,
1378 __in size_t max_seg_size,
1380 __deref_out_bcount_opt(*sizep) caddr_t *datap,
1381 __out size_t *sizep)
1383 tlv_cursor_t cursor;
1389 _NOTE(ARGUNUSED(enp))
1391 if ((seg_data == NULL) || (max_seg_size == 0)) {
1396 /* Find requested TLV tag in segment data */
1397 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1398 max_seg_size)) != 0) {
1402 if ((rc = tlv_find(&cursor, tag)) != 0) {
1406 value = (caddr_t)tlv_value(&cursor);
1407 length = tlv_length(&cursor);
1412 /* Copy out data from TLV item */
1413 EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
1418 memcpy(data, value, length);
1433 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1438 /* Read a single TLV item from the first segment in a TLV formatted partition */
1439 __checkReturn efx_rc_t
1440 ef10_nvram_partn_read_tlv(
1441 __in efx_nic_t *enp,
1442 __in uint32_t partn,
1444 __deref_out_bcount_opt(*seg_sizep) caddr_t *seg_datap,
1445 __out size_t *seg_sizep)
1447 caddr_t seg_data = NULL;
1448 size_t partn_size = 0;
1454 /* Allocate sufficient memory for the entire partition */
1455 if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1458 if (partn_size == 0) {
1463 EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
1464 if (seg_data == NULL) {
1470 * Read the first segment in a TLV partition. Retry until consistent
1471 * segment contents are returned. Inconsistent data may be read if:
1472 * a) the segment contents are invalid
1473 * b) the MC has rebooted while we were reading the partition
1474 * c) the partition has been modified while we were reading it
1475 * Limit retry attempts to ensure forward progress.
1479 if ((rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
1480 seg_data, partn_size)) != 0)
1482 } while ((rc == EAGAIN) && (retry > 0));
1485 /* Failed to obtain consistent segment data */
1492 if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
1493 tag, &data, &length)) != 0)
1496 EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1499 *seg_sizep = length;
1508 EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1514 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1519 /* Compute the size of a segment. */
1520 static __checkReturn efx_rc_t
1521 ef10_nvram_buf_segment_size(
1522 __in caddr_t seg_data,
1523 __in size_t max_seg_size,
1524 __out size_t *seg_sizep)
1527 tlv_cursor_t cursor;
1528 struct tlv_partition_header *header;
1531 uint32_t *end_tag_position;
1532 uint32_t segment_length;
1534 /* A PARTITION_HEADER tag must be the first item at the given offset */
1535 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1536 max_seg_size)) != 0) {
1540 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1544 header = (struct tlv_partition_header *)tlv_item(&cursor);
1546 /* Check TLV segment length (includes the END tag) */
1547 *seg_sizep = __LE_TO_CPU_32(header->total_length);
1548 if (*seg_sizep > max_seg_size) {
1553 /* Check segment ends with PARTITION_TRAILER and END tags */
1554 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1559 if ((rc = tlv_advance(&cursor)) != 0) {
1563 if (tlv_tag(&cursor) != TLV_TAG_END) {
1567 end_tag_position = cursor.current;
1569 /* Verify segment checksum */
1571 for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
1572 cksum += *((uint32_t *)(seg_data + pos));
1580 * Calculate total length from HEADER to END tags and compare to
1581 * max_seg_size and the total_length field in the HEADER tag.
1583 segment_length = tlv_block_length_used(&cursor);
1585 if (segment_length > max_seg_size) {
1590 if (segment_length != *seg_sizep) {
1595 /* Skip over the first HEADER tag. */
1596 rc = tlv_rewind(&cursor);
1597 rc = tlv_advance(&cursor);
1600 if (tlv_tag(&cursor) == TLV_TAG_END) {
1601 /* Check that the END tag is the one found earlier. */
1602 if (cursor.current != end_tag_position)
1606 /* Check for duplicate HEADER tags before the END tag. */
1607 if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
1612 rc = tlv_advance(&cursor);
1620 EFSYS_PROBE(fail12);
1622 EFSYS_PROBE(fail11);
1624 EFSYS_PROBE(fail10);
1642 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1648 * Add or update a single TLV item in a host memory buffer containing a TLV
1649 * formatted segment. Historically partitions consisted of only one segment.
1651 __checkReturn efx_rc_t
1652 ef10_nvram_buf_write_tlv(
1653 __inout_bcount(max_seg_size) caddr_t seg_data,
1654 __in size_t max_seg_size,
1656 __in_bcount(tag_size) caddr_t tag_data,
1657 __in size_t tag_size,
1658 __out size_t *total_lengthp)
1660 tlv_cursor_t cursor;
1661 struct tlv_partition_header *header;
1662 struct tlv_partition_trailer *trailer;
1663 uint32_t generation;
1668 /* A PARTITION_HEADER tag must be the first item (at offset zero) */
1669 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1670 max_seg_size)) != 0) {
1674 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1678 header = (struct tlv_partition_header *)tlv_item(&cursor);
1680 /* Update the TLV chain to contain the new data */
1681 if ((rc = tlv_find(&cursor, tag)) == 0) {
1682 /* Modify existing TLV item */
1683 if ((rc = tlv_modify(&cursor, tag,
1684 (uint8_t *)tag_data, tag_size)) != 0)
1687 /* Insert a new TLV item before the PARTITION_TRAILER */
1688 rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1693 if ((rc = tlv_insert(&cursor, tag,
1694 (uint8_t *)tag_data, tag_size)) != 0) {
1700 /* Find the trailer tag */
1701 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1705 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1707 /* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1708 *total_lengthp = tlv_block_length_used(&cursor);
1709 if (*total_lengthp > max_seg_size) {
1713 generation = __LE_TO_CPU_32(header->generation) + 1;
1715 header->total_length = __CPU_TO_LE_32(*total_lengthp);
1716 header->generation = __CPU_TO_LE_32(generation);
1717 trailer->generation = __CPU_TO_LE_32(generation);
1719 /* Recompute PARTITION_TRAILER checksum */
1720 trailer->checksum = 0;
1722 for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1723 cksum += *((uint32_t *)(seg_data + pos));
1725 trailer->checksum = ~cksum + 1;
1742 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1748 * Add or update a single TLV item in the first segment of a TLV formatted
1749 * dynamic config partition. The first segment is the current active
1752 __checkReturn efx_rc_t
1753 ef10_nvram_partn_write_tlv(
1754 __in efx_nic_t *enp,
1755 __in uint32_t partn,
1757 __in_bcount(size) caddr_t data,
1760 return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1765 * Read a segment from nvram at the given offset into a buffer (segment_data)
1766 * and optionally write a new tag to it.
1768 static __checkReturn efx_rc_t
1769 ef10_nvram_segment_write_tlv(
1770 __in efx_nic_t *enp,
1771 __in uint32_t partn,
1773 __in_bcount(size) caddr_t data,
1775 __inout caddr_t *seg_datap,
1776 __inout size_t *partn_offsetp,
1777 __inout size_t *src_remain_lenp,
1778 __inout size_t *dest_remain_lenp,
1779 __in boolean_t write)
1783 size_t original_segment_size;
1784 size_t modified_segment_size;
1787 * Read the segment from NVRAM into the segment_data buffer and validate
1788 * it, returning if it does not validate. This is not a failure unless
1789 * this is the first segment in a partition. In this case the caller
1790 * must propagate the error.
1792 status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1793 *seg_datap, *src_remain_lenp);
1799 status = ef10_nvram_buf_segment_size(*seg_datap,
1800 *src_remain_lenp, &original_segment_size);
1807 /* Update the contents of the segment in the buffer */
1808 if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1809 *dest_remain_lenp, tag, data, size,
1810 &modified_segment_size)) != 0) {
1813 *dest_remain_lenp -= modified_segment_size;
1814 *seg_datap += modified_segment_size;
1817 * We won't modify this segment, but still need to update the
1818 * remaining lengths and pointers.
1820 *dest_remain_lenp -= original_segment_size;
1821 *seg_datap += original_segment_size;
1824 *partn_offsetp += original_segment_size;
1825 *src_remain_lenp -= original_segment_size;
1834 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1840 * Add or update a single TLV item in either the first segment or in all
1841 * segments in a TLV formatted dynamic config partition. Dynamic config
1842 * partitions on boards that support RFID are divided into a number of segments,
1843 * each formatted like a partition, with header, trailer and end tags. The first
1844 * segment is the current active configuration.
1846 * The segments are initialised by manftest and each contain a different
1847 * configuration e.g. firmware variant. The firmware can be instructed
1848 * via RFID to copy a segment to replace the first segment, hence changing the
1849 * active configuration. This allows ops to change the configuration of a board
1850 * prior to shipment using RFID.
1852 * Changes to the dynamic config may need to be written to all segments (e.g.
1853 * firmware versions) or just the first segment (changes to the active
1854 * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1855 * If only the first segment is written the code still needs to be aware of the
1856 * possible presence of subsequent segments as writing to a segment may cause
1857 * its size to increase, which would overwrite the subsequent segments and
1860 __checkReturn efx_rc_t
1861 ef10_nvram_partn_write_segment_tlv(
1862 __in efx_nic_t *enp,
1863 __in uint32_t partn,
1865 __in_bcount(size) caddr_t data,
1867 __in boolean_t all_segments)
1869 size_t partn_size = 0;
1871 size_t total_length = 0;
1873 size_t current_offset = 0;
1874 size_t remaining_original_length;
1875 size_t remaining_modified_length;
1876 caddr_t segment_data;
1878 EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1880 /* Allocate sufficient memory for the entire partition */
1881 if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1884 EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1885 if (partn_data == NULL) {
1890 remaining_original_length = partn_size;
1891 remaining_modified_length = partn_size;
1892 segment_data = partn_data;
1894 /* Lock the partition */
1895 if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1898 /* Iterate over each (potential) segment to update it. */
1900 boolean_t write = all_segments || current_offset == 0;
1902 rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1903 &segment_data, ¤t_offset, &remaining_original_length,
1904 &remaining_modified_length, write);
1906 if (current_offset == 0) {
1908 * If no data has been read then the first
1909 * segment is invalid, which is an error.
1915 } while (current_offset < partn_size);
1917 total_length = segment_data - partn_data;
1920 * We've run out of space. This should actually be dealt with by
1921 * ef10_nvram_buf_write_tlv returning ENOSPC.
1923 if (total_length > partn_size) {
1928 /* Erase the whole partition in NVRAM */
1929 if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1932 /* Write new partition contents from the buffer to NVRAM */
1933 if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1934 total_length)) != 0)
1937 /* Unlock the partition */
1938 (void) ef10_nvram_partn_unlock(enp, partn, NULL);
1940 EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1953 (void) ef10_nvram_partn_unlock(enp, partn, NULL);
1957 EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1961 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1967 * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1968 * not the data used by the segments in the partition.
1970 __checkReturn efx_rc_t
1971 ef10_nvram_partn_size(
1972 __in efx_nic_t *enp,
1973 __in uint32_t partn,
1974 __out size_t *sizep)
1978 if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1979 NULL, NULL, NULL)) != 0)
1985 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1990 __checkReturn efx_rc_t
1991 ef10_nvram_partn_lock(
1992 __in efx_nic_t *enp,
1993 __in uint32_t partn)
1997 if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
2003 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2008 __checkReturn efx_rc_t
2009 ef10_nvram_partn_read_mode(
2010 __in efx_nic_t *enp,
2011 __in uint32_t partn,
2012 __in unsigned int offset,
2013 __out_bcount(size) caddr_t data,
2021 chunk = MIN(size, EF10_NVRAM_CHUNK);
2023 if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
2024 data, chunk, mode)) != 0) {
2036 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2041 __checkReturn efx_rc_t
2042 ef10_nvram_partn_read(
2043 __in efx_nic_t *enp,
2044 __in uint32_t partn,
2045 __in unsigned int offset,
2046 __out_bcount(size) caddr_t data,
2050 * An A/B partition has two data stores (current and backup).
2051 * Read requests which come in through the EFX API expect to read the
2052 * current, active store of an A/B partition. For non A/B partitions,
2053 * there is only a single store and so the mode param is ignored.
2055 return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
2056 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
2059 __checkReturn efx_rc_t
2060 ef10_nvram_partn_read_backup(
2061 __in efx_nic_t *enp,
2062 __in uint32_t partn,
2063 __in unsigned int offset,
2064 __out_bcount(size) caddr_t data,
2068 * An A/B partition has two data stores (current and backup).
2069 * Read the backup store of an A/B partition (i.e. the store currently
2070 * being written to if the partition is locked).
2072 * This is needed when comparing the existing partition content to avoid
2073 * unnecessary writes, or to read back what has been written to check
2074 * that the writes have succeeded.
2076 return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
2077 MC_CMD_NVRAM_READ_IN_V2_TARGET_BACKUP);
2080 __checkReturn efx_rc_t
2081 ef10_nvram_partn_erase(
2082 __in efx_nic_t *enp,
2083 __in uint32_t partn,
2084 __in unsigned int offset,
2088 uint32_t erase_size;
2090 if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2091 &erase_size, NULL)) != 0)
2094 if (erase_size == 0) {
2095 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
2098 if (size % erase_size != 0) {
2103 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
2106 offset += erase_size;
2120 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2125 __checkReturn efx_rc_t
2126 ef10_nvram_partn_write(
2127 __in efx_nic_t *enp,
2128 __in uint32_t partn,
2129 __in unsigned int offset,
2130 __in_bcount(size) caddr_t data,
2134 uint32_t write_size;
2137 if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2138 NULL, &write_size)) != 0)
2141 if (write_size != 0) {
2143 * Check that the size is a multiple of the write chunk size if
2144 * the write chunk size is available.
2146 if (size % write_size != 0) {
2151 write_size = EF10_NVRAM_CHUNK;
2155 chunk = MIN(size, write_size);
2157 if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
2158 data, chunk)) != 0) {
2174 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2179 __checkReturn efx_rc_t
2180 ef10_nvram_partn_unlock(
2181 __in efx_nic_t *enp,
2182 __in uint32_t partn,
2183 __out_opt uint32_t *verify_resultp)
2185 boolean_t reboot = B_FALSE;
2188 if (verify_resultp != NULL)
2189 *verify_resultp = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
2191 rc = efx_mcdi_nvram_update_finish(enp, partn, reboot, verify_resultp);
2198 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2203 __checkReturn efx_rc_t
2204 ef10_nvram_partn_set_version(
2205 __in efx_nic_t *enp,
2206 __in uint32_t partn,
2207 __in_ecount(4) uint16_t version[4])
2209 struct tlv_partition_version partn_version;
2213 /* Add or modify partition version TLV item */
2214 partn_version.version_w = __CPU_TO_LE_16(version[0]);
2215 partn_version.version_x = __CPU_TO_LE_16(version[1]);
2216 partn_version.version_y = __CPU_TO_LE_16(version[2]);
2217 partn_version.version_z = __CPU_TO_LE_16(version[3]);
2219 size = sizeof (partn_version) - (2 * sizeof (uint32_t));
2221 /* Write the version number to all segments in the partition */
2222 if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
2223 NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
2224 TLV_TAG_PARTITION_VERSION(partn),
2225 (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
2231 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2236 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
2240 typedef struct ef10_parttbl_entry_s {
2242 unsigned int port_mask;
2243 efx_nvram_type_t nvtype;
2244 } ef10_parttbl_entry_t;
2246 /* Port mask values */
2247 #define PORT_1 (1u << 1)
2248 #define PORT_2 (1u << 2)
2249 #define PORT_3 (1u << 3)
2250 #define PORT_4 (1u << 4)
2251 #define PORT_ALL (0xffffffffu)
2253 #define PARTN_MAP_ENTRY(partn, port_mask, nvtype) \
2254 { (NVRAM_PARTITION_TYPE_##partn), (PORT_##port_mask), (EFX_NVRAM_##nvtype) }
2256 /* Translate EFX NVRAM types to firmware partition types */
2257 static ef10_parttbl_entry_t hunt_parttbl[] = {
2258 /* partn ports nvtype */
2259 PARTN_MAP_ENTRY(MC_FIRMWARE, ALL, MC_FIRMWARE),
2260 PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP, ALL, MC_GOLDEN),
2261 PARTN_MAP_ENTRY(EXPANSION_ROM, ALL, BOOTROM),
2262 PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT0, 1, BOOTROM_CFG),
2263 PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT1, 2, BOOTROM_CFG),
2264 PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT2, 3, BOOTROM_CFG),
2265 PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT3, 4, BOOTROM_CFG),
2266 PARTN_MAP_ENTRY(DYNAMIC_CONFIG, ALL, DYNAMIC_CFG),
2267 PARTN_MAP_ENTRY(FPGA, ALL, FPGA),
2268 PARTN_MAP_ENTRY(FPGA_BACKUP, ALL, FPGA_BACKUP),
2269 PARTN_MAP_ENTRY(LICENSE, ALL, LICENSE),
2272 static ef10_parttbl_entry_t medford_parttbl[] = {
2273 /* partn ports nvtype */
2274 PARTN_MAP_ENTRY(MC_FIRMWARE, ALL, MC_FIRMWARE),
2275 PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP, ALL, MC_GOLDEN),
2276 PARTN_MAP_ENTRY(EXPANSION_ROM, ALL, BOOTROM),
2277 PARTN_MAP_ENTRY(EXPROM_CONFIG, ALL, BOOTROM_CFG),
2278 PARTN_MAP_ENTRY(DYNAMIC_CONFIG, ALL, DYNAMIC_CFG),
2279 PARTN_MAP_ENTRY(FPGA, ALL, FPGA),
2280 PARTN_MAP_ENTRY(FPGA_BACKUP, ALL, FPGA_BACKUP),
2281 PARTN_MAP_ENTRY(LICENSE, ALL, LICENSE),
2282 PARTN_MAP_ENTRY(EXPANSION_UEFI, ALL, UEFIROM),
2283 PARTN_MAP_ENTRY(MUM_FIRMWARE, ALL, MUM_FIRMWARE),
2286 static ef10_parttbl_entry_t medford2_parttbl[] = {
2287 /* partn ports nvtype */
2288 PARTN_MAP_ENTRY(MC_FIRMWARE, ALL, MC_FIRMWARE),
2289 PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP, ALL, MC_GOLDEN),
2290 PARTN_MAP_ENTRY(EXPANSION_ROM, ALL, BOOTROM),
2291 PARTN_MAP_ENTRY(EXPROM_CONFIG, ALL, BOOTROM_CFG),
2292 PARTN_MAP_ENTRY(DYNAMIC_CONFIG, ALL, DYNAMIC_CFG),
2293 PARTN_MAP_ENTRY(FPGA, ALL, FPGA),
2294 PARTN_MAP_ENTRY(FPGA_BACKUP, ALL, FPGA_BACKUP),
2295 PARTN_MAP_ENTRY(LICENSE, ALL, LICENSE),
2296 PARTN_MAP_ENTRY(EXPANSION_UEFI, ALL, UEFIROM),
2297 PARTN_MAP_ENTRY(MUM_FIRMWARE, ALL, MUM_FIRMWARE),
2298 PARTN_MAP_ENTRY(DYNCONFIG_DEFAULTS, ALL, DYNCONFIG_DEFAULTS),
2299 PARTN_MAP_ENTRY(ROMCONFIG_DEFAULTS, ALL, ROMCONFIG_DEFAULTS),
2302 static __checkReturn efx_rc_t
2304 __in efx_nic_t *enp,
2305 __out ef10_parttbl_entry_t **parttblp,
2306 __out size_t *parttbl_rowsp)
2308 switch (enp->en_family) {
2309 case EFX_FAMILY_HUNTINGTON:
2310 *parttblp = hunt_parttbl;
2311 *parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
2314 case EFX_FAMILY_MEDFORD:
2315 *parttblp = medford_parttbl;
2316 *parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
2319 case EFX_FAMILY_MEDFORD2:
2320 *parttblp = medford2_parttbl;
2321 *parttbl_rowsp = EFX_ARRAY_SIZE(medford2_parttbl);
2325 EFSYS_ASSERT(B_FALSE);
2331 __checkReturn efx_rc_t
2332 ef10_nvram_type_to_partn(
2333 __in efx_nic_t *enp,
2334 __in efx_nvram_type_t type,
2335 __out uint32_t *partnp)
2337 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2338 ef10_parttbl_entry_t *parttbl = NULL;
2339 size_t parttbl_rows = 0;
2342 EFSYS_ASSERT3U(type, !=, EFX_NVRAM_INVALID);
2343 EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
2344 EFSYS_ASSERT(partnp != NULL);
2346 if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2347 for (i = 0; i < parttbl_rows; i++) {
2348 ef10_parttbl_entry_t *entry = &parttbl[i];
2350 if ((entry->nvtype == type) &&
2351 (entry->port_mask & (1u << emip->emi_port))) {
2352 *partnp = entry->partn;
2363 static __checkReturn efx_rc_t
2364 ef10_nvram_partn_to_type(
2365 __in efx_nic_t *enp,
2366 __in uint32_t partn,
2367 __out efx_nvram_type_t *typep)
2369 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2370 ef10_parttbl_entry_t *parttbl = NULL;
2371 size_t parttbl_rows = 0;
2374 EFSYS_ASSERT(typep != NULL);
2376 if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2377 for (i = 0; i < parttbl_rows; i++) {
2378 ef10_parttbl_entry_t *entry = &parttbl[i];
2380 if ((entry->partn == partn) &&
2381 (entry->port_mask & (1u << emip->emi_port))) {
2382 *typep = entry->nvtype;
2391 __checkReturn efx_rc_t
2393 __in efx_nic_t *enp)
2395 efx_nvram_type_t type;
2396 unsigned int npartns = 0;
2397 uint32_t *partns = NULL;
2402 /* Read available partitions from NVRAM partition map */
2403 size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
2404 EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
2405 if (partns == NULL) {
2410 if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
2415 for (i = 0; i < npartns; i++) {
2416 /* Check if the partition is supported for this port */
2417 if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
2420 if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
2424 EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2431 EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2433 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2437 #endif /* EFSYS_OPT_DIAG */
2439 __checkReturn efx_rc_t
2440 ef10_nvram_partn_get_version(
2441 __in efx_nic_t *enp,
2442 __in uint32_t partn,
2443 __out uint32_t *subtypep,
2444 __out_ecount(4) uint16_t version[4])
2448 /* FIXME: get highest partn version from all ports */
2449 /* FIXME: return partn description if available */
2451 if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
2452 version, NULL, 0)) != 0)
2458 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2463 __checkReturn efx_rc_t
2464 ef10_nvram_partn_rw_start(
2465 __in efx_nic_t *enp,
2466 __in uint32_t partn,
2467 __out size_t *chunk_sizep)
2469 uint32_t write_size = 0;
2472 if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2473 NULL, &write_size)) != 0)
2476 if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
2479 if (chunk_sizep != NULL) {
2480 if (write_size == 0)
2481 *chunk_sizep = EF10_NVRAM_CHUNK;
2483 *chunk_sizep = write_size;
2491 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2496 __checkReturn efx_rc_t
2497 ef10_nvram_partn_rw_finish(
2498 __in efx_nic_t *enp,
2499 __in uint32_t partn,
2500 __out_opt uint32_t *verify_resultp)
2504 if ((rc = ef10_nvram_partn_unlock(enp, partn, verify_resultp)) != 0)
2510 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2515 #endif /* EFSYS_OPT_NVRAM */
2517 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */