1 /* SPDX-License-Identifier: BSD-3-Clause
3 * Copyright (c) 2012-2018 Solarflare Communications Inc.
12 #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
14 #include "ef10_tlv_layout.h"
16 /* Cursor for TLV partition format */
17 typedef struct tlv_cursor_s {
18 uint32_t *block; /* Base of data block */
19 uint32_t *current; /* Cursor position */
20 uint32_t *end; /* End tag position */
21 uint32_t *limit; /* Last dword of data block */
24 typedef struct nvram_partition_s {
29 * The full length of the NVRAM partition.
30 * This is different from tlv_partition_header.total_length,
31 * which can be smaller.
36 tlv_cursor_t tlv_cursor;
40 static __checkReturn efx_rc_t
42 __inout tlv_cursor_t *cursor);
47 __out uint32_t *block)
49 *block = __CPU_TO_LE_32(TLV_TAG_END);
54 __in tlv_cursor_t *cursor)
58 dword = cursor->current[0];
59 tag = __LE_TO_CPU_32(dword);
66 __in tlv_cursor_t *cursor)
68 uint32_t dword, length;
70 if (tlv_tag(cursor) == TLV_TAG_END)
73 dword = cursor->current[1];
74 length = __LE_TO_CPU_32(dword);
76 return ((size_t)length);
81 __in tlv_cursor_t *cursor)
83 if (tlv_tag(cursor) == TLV_TAG_END)
86 return ((uint8_t *)(&cursor->current[2]));
91 __in tlv_cursor_t *cursor)
93 if (tlv_tag(cursor) == TLV_TAG_END)
96 return ((uint8_t *)cursor->current);
100 * TLV item DWORD length is tag + length + value (rounded up to DWORD)
101 * equivalent to tlv_n_words_for_len in mc-comms tlv.c
103 #define TLV_DWORD_COUNT(length) \
104 (1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
109 __in tlv_cursor_t *cursor)
113 length = tlv_length(cursor);
115 return (cursor->current + TLV_DWORD_COUNT(length));
118 static __checkReturn efx_rc_t
120 __inout tlv_cursor_t *cursor)
124 if ((rc = tlv_validate_state(cursor)) != 0)
127 if (cursor->current == cursor->end) {
128 /* No more tags after END tag */
129 cursor->current = NULL;
134 /* Advance to next item and validate */
135 cursor->current = tlv_next_item_ptr(cursor);
137 if ((rc = tlv_validate_state(cursor)) != 0)
147 EFSYS_PROBE1(fail1, efx_rc_t, rc);
154 __in tlv_cursor_t *cursor)
158 cursor->current = cursor->block;
160 if ((rc = tlv_validate_state(cursor)) != 0)
166 EFSYS_PROBE1(fail1, efx_rc_t, rc);
173 __inout tlv_cursor_t *cursor,
178 rc = tlv_rewind(cursor);
180 if (tlv_tag(cursor) == tag)
183 rc = tlv_advance(cursor);
188 static __checkReturn efx_rc_t
190 __inout tlv_cursor_t *cursor)
194 /* Check cursor position */
195 if (cursor->current < cursor->block) {
199 if (cursor->current > cursor->limit) {
204 if (tlv_tag(cursor) != TLV_TAG_END) {
205 /* Check current item has space for tag and length */
206 if (cursor->current > (cursor->limit - 1)) {
207 cursor->current = NULL;
212 /* Check we have value data for current item and an END tag */
213 if (tlv_next_item_ptr(cursor) > cursor->limit) {
214 cursor->current = NULL;
229 EFSYS_PROBE1(fail1, efx_rc_t, rc);
236 __out tlv_cursor_t *cursor,
237 __in uint32_t *block,
238 __in uint32_t *limit,
239 __in uint32_t *current)
241 cursor->block = block;
242 cursor->limit = limit;
244 cursor->current = current;
247 return (tlv_validate_state(cursor));
250 static __checkReturn efx_rc_t
251 tlv_init_cursor_from_size(
252 __out tlv_cursor_t *cursor,
258 limit = (uint32_t *)(block + size - sizeof (uint32_t));
259 return (tlv_init_cursor(cursor, (uint32_t *)block,
260 limit, (uint32_t *)block));
263 static __checkReturn efx_rc_t
264 tlv_init_cursor_at_offset(
265 __out tlv_cursor_t *cursor,
273 limit = (uint32_t *)(block + size - sizeof (uint32_t));
274 current = (uint32_t *)(block + offset);
275 return (tlv_init_cursor(cursor, (uint32_t *)block, limit, current));
278 static __checkReturn efx_rc_t
280 __inout tlv_cursor_t *cursor)
285 if (cursor->end == NULL) {
286 pos = cursor->current;
287 if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
290 cursor->end = cursor->current;
291 cursor->current = pos;
297 EFSYS_PROBE1(fail1, efx_rc_t, rc);
303 tlv_block_length_used(
304 __inout tlv_cursor_t *cursor)
308 if ((rc = tlv_validate_state(cursor)) != 0)
311 if ((rc = tlv_require_end(cursor)) != 0)
314 /* Return space used (including the END tag) */
315 return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
320 EFSYS_PROBE1(fail1, efx_rc_t, rc);
326 tlv_last_segment_end(
327 __in tlv_cursor_t *cursor)
329 tlv_cursor_t segment_cursor;
330 uint32_t *last_segment_end = cursor->block;
331 uint32_t *segment_start = cursor->block;
334 * Go through each segment and check that it has an end tag. If there
335 * is no end tag then the previous segment was the last valid one,
336 * so return the pointer to its end tag.
339 if (tlv_init_cursor(&segment_cursor, segment_start,
340 cursor->limit, segment_start) != 0)
342 if (tlv_require_end(&segment_cursor) != 0)
344 last_segment_end = segment_cursor.end;
345 segment_start = segment_cursor.end + 1;
348 return (last_segment_end);
354 __in tlv_cursor_t *cursor,
356 __in_bcount(size) uint8_t *data,
362 ptr = cursor->current;
364 *ptr++ = __CPU_TO_LE_32(tag);
365 *ptr++ = __CPU_TO_LE_32(len);
368 ptr[(len - 1) / sizeof (uint32_t)] = 0;
369 memcpy(ptr, data, len);
370 ptr += P2ROUNDUP(len, sizeof (uint32_t)) / sizeof (*ptr);
376 static __checkReturn efx_rc_t
378 __inout tlv_cursor_t *cursor,
385 uint32_t *last_segment_end;
388 if ((rc = tlv_validate_state(cursor)) != 0)
391 if ((rc = tlv_require_end(cursor)) != 0)
394 if (tag == TLV_TAG_END) {
399 last_segment_end = tlv_last_segment_end(cursor);
401 delta = TLV_DWORD_COUNT(size);
402 if (last_segment_end + 1 + delta > cursor->limit) {
407 /* Move data up: new space at cursor->current */
408 memmove(cursor->current + delta, cursor->current,
409 (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
411 /* Adjust the end pointer */
412 cursor->end += delta;
414 /* Write new TLV item */
415 tlv_write(cursor, tag, data, size);
426 EFSYS_PROBE1(fail1, efx_rc_t, rc);
431 static __checkReturn efx_rc_t
433 __inout tlv_cursor_t *cursor)
436 uint32_t *last_segment_end;
439 if ((rc = tlv_validate_state(cursor)) != 0)
442 if (tlv_tag(cursor) == TLV_TAG_END) {
447 delta = TLV_DWORD_COUNT(tlv_length(cursor));
449 if ((rc = tlv_require_end(cursor)) != 0)
452 last_segment_end = tlv_last_segment_end(cursor);
454 /* Shuffle things down, destroying the item at cursor->current */
455 memmove(cursor->current, cursor->current + delta,
456 (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
457 /* Zero the new space at the end of the TLV chain */
458 memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t));
459 /* Adjust the end pointer */
460 cursor->end -= delta;
469 EFSYS_PROBE1(fail1, efx_rc_t, rc);
474 static __checkReturn efx_rc_t
476 __inout tlv_cursor_t *cursor,
483 unsigned int old_ndwords;
484 unsigned int new_ndwords;
486 uint32_t *last_segment_end;
489 if ((rc = tlv_validate_state(cursor)) != 0)
492 if (tlv_tag(cursor) == TLV_TAG_END) {
496 if (tlv_tag(cursor) != tag) {
501 old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
502 new_ndwords = TLV_DWORD_COUNT(size);
504 if ((rc = tlv_require_end(cursor)) != 0)
507 last_segment_end = tlv_last_segment_end(cursor);
509 if (new_ndwords > old_ndwords) {
510 /* Expand space used for TLV item */
511 delta = new_ndwords - old_ndwords;
512 pos = cursor->current + old_ndwords;
514 if (last_segment_end + 1 + delta > cursor->limit) {
519 /* Move up: new space at (cursor->current + old_ndwords) */
520 memmove(pos + delta, pos,
521 (last_segment_end + 1 - pos) * sizeof (uint32_t));
523 /* Adjust the end pointer */
524 cursor->end += delta;
526 } else if (new_ndwords < old_ndwords) {
527 /* Shrink space used for TLV item */
528 delta = old_ndwords - new_ndwords;
529 pos = cursor->current + new_ndwords;
531 /* Move down: remove words at (cursor->current + new_ndwords) */
532 memmove(pos, pos + delta,
533 (last_segment_end + 1 - pos) * sizeof (uint32_t));
535 /* Zero the new space at the end of the TLV chain */
536 memset(last_segment_end + 1 - delta, 0,
537 delta * sizeof (uint32_t));
539 /* Adjust the end pointer */
540 cursor->end -= delta;
544 tlv_write(cursor, tag, data, size);
557 EFSYS_PROBE1(fail1, efx_rc_t, rc);
562 static uint32_t checksum_tlv_partition(
563 __in nvram_partition_t *partition)
565 tlv_cursor_t *cursor;
571 cursor = &partition->tlv_cursor;
572 len = tlv_block_length_used(cursor);
573 EFSYS_ASSERT3U((len & 3), ==, 0);
576 ptr = partition->data;
577 end = &ptr[len >> 2];
580 csum += __LE_TO_CPU_32(*ptr++);
585 static __checkReturn efx_rc_t
586 tlv_update_partition_len_and_cks(
587 __in tlv_cursor_t *cursor)
590 nvram_partition_t partition;
591 struct tlv_partition_header *header;
592 struct tlv_partition_trailer *trailer;
596 * We just modified the partition, so the total length may not be
597 * valid. Don't use tlv_find(), which performs some sanity checks
598 * that may fail here.
600 partition.data = cursor->block;
601 memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
602 header = (struct tlv_partition_header *)partition.data;
604 if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
608 new_len = tlv_block_length_used(&partition.tlv_cursor);
613 header->total_length = __CPU_TO_LE_32(new_len);
614 /* Ensure the modified partition always has a new generation count. */
615 header->generation = __CPU_TO_LE_32(
616 __LE_TO_CPU_32(header->generation) + 1);
618 trailer = (struct tlv_partition_trailer *)((uint8_t *)header +
619 new_len - sizeof (*trailer) - sizeof (uint32_t));
620 trailer->generation = header->generation;
621 trailer->checksum = __CPU_TO_LE_32(
622 __LE_TO_CPU_32(trailer->checksum) -
623 checksum_tlv_partition(&partition));
630 EFSYS_PROBE1(fail1, efx_rc_t, rc);
635 /* Validate buffer contents (before writing to flash) */
636 __checkReturn efx_rc_t
637 ef10_nvram_buffer_validate(
639 __in_bcount(partn_size) caddr_t partn_data,
640 __in size_t partn_size)
643 struct tlv_partition_header *header;
644 struct tlv_partition_trailer *trailer;
650 EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
652 if ((partn_data == NULL) || (partn_size == 0)) {
657 /* The partition header must be the first item (at offset zero) */
658 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
663 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
667 header = (struct tlv_partition_header *)tlv_item(&cursor);
669 /* Check TLV partition length (includes the END tag) */
670 total_length = __LE_TO_CPU_32(header->total_length);
671 if (total_length > partn_size) {
676 /* Check partition header matches partn */
677 if (__LE_TO_CPU_16(header->type_id) != partn) {
682 /* Check partition ends with PARTITION_TRAILER and END tags */
683 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
687 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
689 if ((rc = tlv_advance(&cursor)) != 0) {
693 if (tlv_tag(&cursor) != TLV_TAG_END) {
698 /* Check generation counts are consistent */
699 if (trailer->generation != header->generation) {
704 /* Verify partition checksum */
706 for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
707 cksum += *((uint32_t *)(partn_data + pos));
735 EFSYS_PROBE1(fail1, efx_rc_t, rc);
741 ef10_nvram_buffer_init(
742 __out_bcount(buffer_size)
744 __in size_t buffer_size)
746 uint32_t *buf = (uint32_t *)bufferp;
748 memset(buf, 0xff, buffer_size);
753 __checkReturn efx_rc_t
754 ef10_nvram_buffer_create(
755 __in uint32_t partn_type,
756 __out_bcount(partn_size)
758 __in size_t partn_size)
760 uint32_t *buf = (uint32_t *)partn_data;
763 struct tlv_partition_header header;
764 struct tlv_partition_trailer trailer;
766 unsigned int min_buf_size = sizeof (struct tlv_partition_header) +
767 sizeof (struct tlv_partition_trailer);
768 if (partn_size < min_buf_size) {
773 ef10_nvram_buffer_init(partn_data, partn_size);
775 if ((rc = tlv_init_cursor(&cursor, buf,
776 (uint32_t *)((uint8_t *)buf + partn_size),
781 header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
782 header.length = __CPU_TO_LE_32(sizeof (header) - 8);
783 header.type_id = __CPU_TO_LE_16(partn_type);
785 header.generation = __CPU_TO_LE_32(1);
786 header.total_length = 0; /* This will be fixed below. */
787 if ((rc = tlv_insert(
788 &cursor, TLV_TAG_PARTITION_HEADER,
789 (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
791 if ((rc = tlv_advance(&cursor)) != 0)
794 trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
795 trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
796 trailer.generation = header.generation;
797 trailer.checksum = 0; /* This will be fixed below. */
798 if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
799 (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
802 if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
805 /* Check that the partition is valid. */
806 if ((rc = ef10_nvram_buffer_validate(partn_type,
807 partn_data, partn_size)) != 0)
825 EFSYS_PROBE1(fail1, efx_rc_t, rc);
832 __in uint32_t *position,
835 return (uint32_t)((uint8_t *)position - (uint8_t *)base);
838 __checkReturn efx_rc_t
839 ef10_nvram_buffer_find_item_start(
840 __in_bcount(buffer_size)
842 __in size_t buffer_size,
843 __out uint32_t *startp)
845 /* Read past partition header to find start address of the first key */
849 /* A PARTITION_HEADER tag must be the first item (at offset zero) */
850 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
851 buffer_size)) != 0) {
855 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
860 if ((rc = tlv_advance(&cursor)) != 0) {
864 *startp = byte_offset(cursor.current, cursor.block);
866 if ((rc = tlv_require_end(&cursor)) != 0)
878 EFSYS_PROBE1(fail1, efx_rc_t, rc);
883 __checkReturn efx_rc_t
884 ef10_nvram_buffer_find_end(
885 __in_bcount(buffer_size)
887 __in size_t buffer_size,
888 __in uint32_t offset,
889 __out uint32_t *endp)
891 /* Read to end of partition */
894 uint32_t *segment_used;
896 _NOTE(ARGUNUSED(offset))
898 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
899 buffer_size)) != 0) {
904 segment_used = cursor.block;
907 * Go through each segment and check that it has an end tag. If there
908 * is no end tag then the previous segment was the last valid one,
909 * so return the used space including that end tag.
911 while (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
912 if (tlv_require_end(&cursor) != 0) {
913 if (segment_used == cursor.block) {
915 * First segment is corrupt, so there is
916 * no valid data in partition.
923 segment_used = cursor.end + 1;
925 cursor.current = segment_used;
927 /* Return space used (including the END tag) */
928 *endp = (segment_used - cursor.block) * sizeof (uint32_t);
935 EFSYS_PROBE1(fail1, efx_rc_t, rc);
940 __checkReturn __success(return != B_FALSE) boolean_t
941 ef10_nvram_buffer_find_item(
942 __in_bcount(buffer_size)
944 __in size_t buffer_size,
945 __in uint32_t offset,
946 __out uint32_t *startp,
947 __out uint32_t *lengthp)
949 /* Find TLV at offset and return key start and length */
954 if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
955 buffer_size, offset) != 0) {
959 while ((key = tlv_item(&cursor)) != NULL) {
960 tag = tlv_tag(&cursor);
961 if (tag == TLV_TAG_PARTITION_HEADER ||
962 tag == TLV_TAG_PARTITION_TRAILER) {
963 if (tlv_advance(&cursor) != 0) {
968 *startp = byte_offset(cursor.current, cursor.block);
969 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
977 __checkReturn efx_rc_t
978 ef10_nvram_buffer_peek_item(
979 __in_bcount(buffer_size)
981 __in size_t buffer_size,
982 __in uint32_t offset,
983 __out uint32_t *tagp,
984 __out uint32_t *lengthp,
985 __out uint32_t *value_offsetp)
991 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
992 buffer_size, offset)) != 0) {
996 tag = tlv_tag(&cursor);
998 if (tag == TLV_TAG_END) {
1000 * To allow stepping over the END tag, report the full tag
1001 * length and a zero length value.
1003 *lengthp = sizeof (tag);
1004 *value_offsetp = sizeof (tag);
1006 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1008 *value_offsetp = byte_offset((uint32_t *)tlv_value(&cursor),
1014 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1019 __checkReturn efx_rc_t
1020 ef10_nvram_buffer_get_item(
1021 __in_bcount(buffer_size)
1023 __in size_t buffer_size,
1024 __in uint32_t offset,
1025 __in uint32_t length,
1026 __out uint32_t *tagp,
1027 __out_bcount_part(value_max_size, *lengthp)
1029 __in size_t value_max_size,
1030 __out uint32_t *lengthp)
1033 tlv_cursor_t cursor;
1034 uint32_t value_length;
1036 if (buffer_size < (offset + length)) {
1041 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1042 buffer_size, offset)) != 0) {
1046 value_length = tlv_length(&cursor);
1047 if (value_max_size < value_length) {
1051 memcpy(valuep, tlv_value(&cursor), value_length);
1053 *tagp = tlv_tag(&cursor);
1054 *lengthp = value_length;
1063 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1068 __checkReturn efx_rc_t
1069 ef10_nvram_buffer_insert_item(
1070 __in_bcount(buffer_size)
1072 __in size_t buffer_size,
1073 __in uint32_t offset,
1075 __in_bcount(length) caddr_t valuep,
1076 __in uint32_t length,
1077 __out uint32_t *lengthp)
1080 tlv_cursor_t cursor;
1082 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1083 buffer_size, offset)) != 0) {
1087 rc = tlv_insert(&cursor, tag, (uint8_t *)valuep, length);
1092 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1100 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1105 __checkReturn efx_rc_t
1106 ef10_nvram_buffer_modify_item(
1107 __in_bcount(buffer_size)
1109 __in size_t buffer_size,
1110 __in uint32_t offset,
1112 __in_bcount(length) caddr_t valuep,
1113 __in uint32_t length,
1114 __out uint32_t *lengthp)
1117 tlv_cursor_t cursor;
1119 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1120 buffer_size, offset)) != 0) {
1124 rc = tlv_modify(&cursor, tag, (uint8_t *)valuep, length);
1130 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1138 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1144 __checkReturn efx_rc_t
1145 ef10_nvram_buffer_delete_item(
1146 __in_bcount(buffer_size)
1148 __in size_t buffer_size,
1149 __in uint32_t offset,
1150 __in uint32_t length,
1154 tlv_cursor_t cursor;
1156 _NOTE(ARGUNUSED(length, end))
1158 if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1159 buffer_size, offset)) != 0) {
1163 if ((rc = tlv_delete(&cursor)) != 0)
1171 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1176 __checkReturn efx_rc_t
1177 ef10_nvram_buffer_finish(
1178 __in_bcount(buffer_size)
1180 __in size_t buffer_size)
1183 tlv_cursor_t cursor;
1185 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
1186 buffer_size)) != 0) {
1191 if ((rc = tlv_require_end(&cursor)) != 0)
1194 if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
1204 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1212 * Read and validate a segment from a partition. A segment is a complete
1213 * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
1214 * be multiple segments in a partition, so seg_offset allows segments
1215 * beyond the first to be read.
1217 static __checkReturn efx_rc_t
1218 ef10_nvram_read_tlv_segment(
1219 __in efx_nic_t *enp,
1220 __in uint32_t partn,
1221 __in size_t seg_offset,
1222 __in_bcount(max_seg_size) caddr_t seg_data,
1223 __in size_t max_seg_size)
1225 tlv_cursor_t cursor;
1226 struct tlv_partition_header *header;
1227 struct tlv_partition_trailer *trailer;
1228 size_t total_length;
1233 EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
1235 if ((seg_data == NULL) || (max_seg_size == 0)) {
1240 /* Read initial chunk of the segment, starting at offset */
1241 if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
1243 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
1247 /* A PARTITION_HEADER tag must be the first item at the given offset */
1248 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1249 max_seg_size)) != 0) {
1253 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1257 header = (struct tlv_partition_header *)tlv_item(&cursor);
1259 /* Check TLV segment length (includes the END tag) */
1260 total_length = __LE_TO_CPU_32(header->total_length);
1261 if (total_length > max_seg_size) {
1266 /* Read the remaining segment content */
1267 if (total_length > EF10_NVRAM_CHUNK) {
1268 if ((rc = ef10_nvram_partn_read_mode(enp, partn,
1269 seg_offset + EF10_NVRAM_CHUNK,
1270 seg_data + EF10_NVRAM_CHUNK,
1271 total_length - EF10_NVRAM_CHUNK,
1272 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
1276 /* Check segment ends with PARTITION_TRAILER and END tags */
1277 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1281 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1283 if ((rc = tlv_advance(&cursor)) != 0) {
1287 if (tlv_tag(&cursor) != TLV_TAG_END) {
1292 /* Check data read from segment is consistent */
1293 if (trailer->generation != header->generation) {
1295 * The partition data may have been modified between successive
1296 * MCDI NVRAM_READ requests by the MC or another PCI function.
1298 * The caller must retry to obtain consistent partition data.
1304 /* Verify segment checksum */
1306 for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
1307 cksum += *((uint32_t *)(seg_data + pos));
1317 EFSYS_PROBE(fail11);
1319 EFSYS_PROBE(fail10);
1337 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1343 * Read a single TLV item from a host memory
1344 * buffer containing a TLV formatted segment.
1346 __checkReturn efx_rc_t
1347 ef10_nvram_buf_read_tlv(
1348 __in efx_nic_t *enp,
1349 __in_bcount(max_seg_size) caddr_t seg_data,
1350 __in size_t max_seg_size,
1352 __deref_out_bcount_opt(*sizep) caddr_t *datap,
1353 __out size_t *sizep)
1355 tlv_cursor_t cursor;
1361 _NOTE(ARGUNUSED(enp))
1363 if ((seg_data == NULL) || (max_seg_size == 0)) {
1368 /* Find requested TLV tag in segment data */
1369 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1370 max_seg_size)) != 0) {
1374 if ((rc = tlv_find(&cursor, tag)) != 0) {
1378 value = (caddr_t)tlv_value(&cursor);
1379 length = tlv_length(&cursor);
1384 /* Copy out data from TLV item */
1385 EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
1390 memcpy(data, value, length);
1405 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1410 /* Read a single TLV item from the first segment in a TLV formatted partition */
1411 __checkReturn efx_rc_t
1412 ef10_nvram_partn_read_tlv(
1413 __in efx_nic_t *enp,
1414 __in uint32_t partn,
1416 __deref_out_bcount_opt(*seg_sizep) caddr_t *seg_datap,
1417 __out size_t *seg_sizep)
1419 caddr_t seg_data = NULL;
1420 size_t partn_size = 0;
1426 /* Allocate sufficient memory for the entire partition */
1427 if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1430 if (partn_size == 0) {
1435 EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
1436 if (seg_data == NULL) {
1442 * Read the first segment in a TLV partition. Retry until consistent
1443 * segment contents are returned. Inconsistent data may be read if:
1444 * a) the segment contents are invalid
1445 * b) the MC has rebooted while we were reading the partition
1446 * c) the partition has been modified while we were reading it
1447 * Limit retry attempts to ensure forward progress.
1451 if ((rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
1452 seg_data, partn_size)) != 0)
1454 } while ((rc == EAGAIN) && (retry > 0));
1457 /* Failed to obtain consistent segment data */
1464 if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
1465 tag, &data, &length)) != 0)
1468 EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1471 *seg_sizep = length;
1480 EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1486 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1491 /* Compute the size of a segment. */
1492 static __checkReturn efx_rc_t
1493 ef10_nvram_buf_segment_size(
1494 __in caddr_t seg_data,
1495 __in size_t max_seg_size,
1496 __out size_t *seg_sizep)
1499 tlv_cursor_t cursor;
1500 struct tlv_partition_header *header;
1503 uint32_t *end_tag_position;
1504 uint32_t segment_length;
1506 /* A PARTITION_HEADER tag must be the first item at the given offset */
1507 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1508 max_seg_size)) != 0) {
1512 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1516 header = (struct tlv_partition_header *)tlv_item(&cursor);
1518 /* Check TLV segment length (includes the END tag) */
1519 *seg_sizep = __LE_TO_CPU_32(header->total_length);
1520 if (*seg_sizep > max_seg_size) {
1525 /* Check segment ends with PARTITION_TRAILER and END tags */
1526 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1531 if ((rc = tlv_advance(&cursor)) != 0) {
1535 if (tlv_tag(&cursor) != TLV_TAG_END) {
1539 end_tag_position = cursor.current;
1541 /* Verify segment checksum */
1543 for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
1544 cksum += *((uint32_t *)(seg_data + pos));
1552 * Calculate total length from HEADER to END tags and compare to
1553 * max_seg_size and the total_length field in the HEADER tag.
1555 segment_length = tlv_block_length_used(&cursor);
1557 if (segment_length > max_seg_size) {
1562 if (segment_length != *seg_sizep) {
1567 /* Skip over the first HEADER tag. */
1568 rc = tlv_rewind(&cursor);
1569 rc = tlv_advance(&cursor);
1572 if (tlv_tag(&cursor) == TLV_TAG_END) {
1573 /* Check that the END tag is the one found earlier. */
1574 if (cursor.current != end_tag_position)
1578 /* Check for duplicate HEADER tags before the END tag. */
1579 if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
1584 rc = tlv_advance(&cursor);
1592 EFSYS_PROBE(fail12);
1594 EFSYS_PROBE(fail11);
1596 EFSYS_PROBE(fail10);
1614 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1620 * Add or update a single TLV item in a host memory buffer containing a TLV
1621 * formatted segment. Historically partitions consisted of only one segment.
1623 __checkReturn efx_rc_t
1624 ef10_nvram_buf_write_tlv(
1625 __inout_bcount(max_seg_size) caddr_t seg_data,
1626 __in size_t max_seg_size,
1628 __in_bcount(tag_size) caddr_t tag_data,
1629 __in size_t tag_size,
1630 __out size_t *total_lengthp)
1632 tlv_cursor_t cursor;
1633 struct tlv_partition_header *header;
1634 struct tlv_partition_trailer *trailer;
1635 uint32_t generation;
1640 /* A PARTITION_HEADER tag must be the first item (at offset zero) */
1641 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1642 max_seg_size)) != 0) {
1646 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1650 header = (struct tlv_partition_header *)tlv_item(&cursor);
1652 /* Update the TLV chain to contain the new data */
1653 if ((rc = tlv_find(&cursor, tag)) == 0) {
1654 /* Modify existing TLV item */
1655 if ((rc = tlv_modify(&cursor, tag,
1656 (uint8_t *)tag_data, tag_size)) != 0)
1659 /* Insert a new TLV item before the PARTITION_TRAILER */
1660 rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1665 if ((rc = tlv_insert(&cursor, tag,
1666 (uint8_t *)tag_data, tag_size)) != 0) {
1672 /* Find the trailer tag */
1673 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1677 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1679 /* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1680 *total_lengthp = tlv_block_length_used(&cursor);
1681 if (*total_lengthp > max_seg_size) {
1685 generation = __LE_TO_CPU_32(header->generation) + 1;
1687 header->total_length = __CPU_TO_LE_32(*total_lengthp);
1688 header->generation = __CPU_TO_LE_32(generation);
1689 trailer->generation = __CPU_TO_LE_32(generation);
1691 /* Recompute PARTITION_TRAILER checksum */
1692 trailer->checksum = 0;
1694 for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1695 cksum += *((uint32_t *)(seg_data + pos));
1697 trailer->checksum = ~cksum + 1;
1714 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1720 * Add or update a single TLV item in the first segment of a TLV formatted
1721 * dynamic config partition. The first segment is the current active
1724 __checkReturn efx_rc_t
1725 ef10_nvram_partn_write_tlv(
1726 __in efx_nic_t *enp,
1727 __in uint32_t partn,
1729 __in_bcount(size) caddr_t data,
1732 return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1737 * Read a segment from nvram at the given offset into a buffer (segment_data)
1738 * and optionally write a new tag to it.
1740 static __checkReturn efx_rc_t
1741 ef10_nvram_segment_write_tlv(
1742 __in efx_nic_t *enp,
1743 __in uint32_t partn,
1745 __in_bcount(size) caddr_t data,
1747 __inout caddr_t *seg_datap,
1748 __inout size_t *partn_offsetp,
1749 __inout size_t *src_remain_lenp,
1750 __inout size_t *dest_remain_lenp,
1751 __in boolean_t write)
1755 size_t original_segment_size;
1756 size_t modified_segment_size;
1759 * Read the segment from NVRAM into the segment_data buffer and validate
1760 * it, returning if it does not validate. This is not a failure unless
1761 * this is the first segment in a partition. In this case the caller
1762 * must propagate the error.
1764 status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1765 *seg_datap, *src_remain_lenp);
1771 status = ef10_nvram_buf_segment_size(*seg_datap,
1772 *src_remain_lenp, &original_segment_size);
1779 /* Update the contents of the segment in the buffer */
1780 if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1781 *dest_remain_lenp, tag, data, size,
1782 &modified_segment_size)) != 0) {
1785 *dest_remain_lenp -= modified_segment_size;
1786 *seg_datap += modified_segment_size;
1789 * We won't modify this segment, but still need to update the
1790 * remaining lengths and pointers.
1792 *dest_remain_lenp -= original_segment_size;
1793 *seg_datap += original_segment_size;
1796 *partn_offsetp += original_segment_size;
1797 *src_remain_lenp -= original_segment_size;
1806 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1812 * Add or update a single TLV item in either the first segment or in all
1813 * segments in a TLV formatted dynamic config partition. Dynamic config
1814 * partitions on boards that support RFID are divided into a number of segments,
1815 * each formatted like a partition, with header, trailer and end tags. The first
1816 * segment is the current active configuration.
1818 * The segments are initialised by manftest and each contain a different
1819 * configuration e.g. firmware variant. The firmware can be instructed
1820 * via RFID to copy a segment to replace the first segment, hence changing the
1821 * active configuration. This allows ops to change the configuration of a board
1822 * prior to shipment using RFID.
1824 * Changes to the dynamic config may need to be written to all segments (e.g.
1825 * firmware versions) or just the first segment (changes to the active
1826 * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1827 * If only the first segment is written the code still needs to be aware of the
1828 * possible presence of subsequent segments as writing to a segment may cause
1829 * its size to increase, which would overwrite the subsequent segments and
1832 __checkReturn efx_rc_t
1833 ef10_nvram_partn_write_segment_tlv(
1834 __in efx_nic_t *enp,
1835 __in uint32_t partn,
1837 __in_bcount(size) caddr_t data,
1839 __in boolean_t all_segments)
1841 size_t partn_size = 0;
1843 size_t total_length = 0;
1845 size_t current_offset = 0;
1846 size_t remaining_original_length;
1847 size_t remaining_modified_length;
1848 caddr_t segment_data;
1850 EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1852 /* Allocate sufficient memory for the entire partition */
1853 if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1856 EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1857 if (partn_data == NULL) {
1862 remaining_original_length = partn_size;
1863 remaining_modified_length = partn_size;
1864 segment_data = partn_data;
1866 /* Lock the partition */
1867 if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1870 /* Iterate over each (potential) segment to update it. */
1872 boolean_t write = all_segments || current_offset == 0;
1874 rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1875 &segment_data, ¤t_offset, &remaining_original_length,
1876 &remaining_modified_length, write);
1878 if (current_offset == 0) {
1880 * If no data has been read then the first
1881 * segment is invalid, which is an error.
1887 } while (current_offset < partn_size);
1889 total_length = segment_data - partn_data;
1892 * We've run out of space. This should actually be dealt with by
1893 * ef10_nvram_buf_write_tlv returning ENOSPC.
1895 if (total_length > partn_size) {
1900 /* Erase the whole partition in NVRAM */
1901 if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1904 /* Write new partition contents from the buffer to NVRAM */
1905 if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1906 total_length)) != 0)
1909 /* Unlock the partition */
1910 (void) ef10_nvram_partn_unlock(enp, partn, NULL);
1912 EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1925 (void) ef10_nvram_partn_unlock(enp, partn, NULL);
1929 EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1933 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1939 * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1940 * not the data used by the segments in the partition.
1942 __checkReturn efx_rc_t
1943 ef10_nvram_partn_size(
1944 __in efx_nic_t *enp,
1945 __in uint32_t partn,
1946 __out size_t *sizep)
1949 efx_nvram_info_t eni = { 0 };
1951 if ((rc = efx_mcdi_nvram_info(enp, partn, &eni)) != 0)
1954 *sizep = eni.eni_partn_size;
1959 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1964 __checkReturn efx_rc_t
1965 ef10_nvram_partn_info(
1966 __in efx_nic_t *enp,
1967 __in uint32_t partn,
1968 __out efx_nvram_info_t *enip)
1972 if ((rc = efx_mcdi_nvram_info(enp, partn, enip)) != 0)
1975 if (enip->eni_write_size == 0)
1976 enip->eni_write_size = EF10_NVRAM_CHUNK;
1981 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1987 __checkReturn efx_rc_t
1988 ef10_nvram_partn_lock(
1989 __in efx_nic_t *enp,
1990 __in uint32_t partn)
1994 if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
2000 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2005 __checkReturn efx_rc_t
2006 ef10_nvram_partn_read_mode(
2007 __in efx_nic_t *enp,
2008 __in uint32_t partn,
2009 __in unsigned int offset,
2010 __out_bcount(size) caddr_t data,
2018 chunk = MIN(size, EF10_NVRAM_CHUNK);
2020 if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
2021 data, chunk, mode)) != 0) {
2033 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2038 __checkReturn efx_rc_t
2039 ef10_nvram_partn_read(
2040 __in efx_nic_t *enp,
2041 __in uint32_t partn,
2042 __in unsigned int offset,
2043 __out_bcount(size) caddr_t data,
2047 * An A/B partition has two data stores (current and backup).
2048 * Read requests which come in through the EFX API expect to read the
2049 * current, active store of an A/B partition. For non A/B partitions,
2050 * there is only a single store and so the mode param is ignored.
2052 return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
2053 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
2056 __checkReturn efx_rc_t
2057 ef10_nvram_partn_read_backup(
2058 __in efx_nic_t *enp,
2059 __in uint32_t partn,
2060 __in unsigned int offset,
2061 __out_bcount(size) caddr_t data,
2065 * An A/B partition has two data stores (current and backup).
2066 * Read the backup store of an A/B partition (i.e. the store currently
2067 * being written to if the partition is locked).
2069 * This is needed when comparing the existing partition content to avoid
2070 * unnecessary writes, or to read back what has been written to check
2071 * that the writes have succeeded.
2073 return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
2074 MC_CMD_NVRAM_READ_IN_V2_TARGET_BACKUP);
2077 __checkReturn efx_rc_t
2078 ef10_nvram_partn_erase(
2079 __in efx_nic_t *enp,
2080 __in uint32_t partn,
2081 __in unsigned int offset,
2085 efx_nvram_info_t eni = { 0 };
2086 uint32_t erase_size;
2088 if ((rc = efx_mcdi_nvram_info(enp, partn, &eni)) != 0)
2091 erase_size = eni.eni_erase_size;
2093 if (erase_size == 0) {
2094 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
2097 if (size % erase_size != 0) {
2102 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
2105 offset += erase_size;
2119 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2124 __checkReturn efx_rc_t
2125 ef10_nvram_partn_write(
2126 __in efx_nic_t *enp,
2127 __in uint32_t partn,
2128 __in unsigned int offset,
2129 __in_bcount(size) caddr_t data,
2133 efx_nvram_info_t eni = { 0 };
2134 uint32_t write_size;
2137 if ((rc = efx_mcdi_nvram_info(enp, partn, &eni)) != 0)
2140 write_size = eni.eni_write_size;
2142 if (write_size != 0) {
2144 * Check that the size is a multiple of the write chunk size if
2145 * the write chunk size is available.
2147 if (size % write_size != 0) {
2152 write_size = EF10_NVRAM_CHUNK;
2156 chunk = MIN(size, write_size);
2158 if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
2159 data, chunk)) != 0) {
2175 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2180 #define EF10_NVRAM_INITIAL_POLL_DELAY_US 10000
2181 #define EF10_NVRAM_MAX_POLL_DELAY_US 1000000
2182 #define EF10_NVRAM_POLL_RETRIES 100
2184 __checkReturn efx_rc_t
2185 ef10_nvram_partn_unlock(
2186 __in efx_nic_t *enp,
2187 __in uint32_t partn,
2188 __out_opt uint32_t *verify_resultp)
2190 boolean_t reboot = B_FALSE;
2191 uint32_t poll_delay_us = EF10_NVRAM_INITIAL_POLL_DELAY_US;
2192 uint32_t poll_retry = 0;
2193 uint32_t verify_result = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
2196 rc = efx_mcdi_nvram_update_finish(enp, partn, reboot,
2197 EFX_NVRAM_UPDATE_FLAGS_BACKGROUND, &verify_result);
2200 * NVRAM updates can take a long time (e.g. up to 1 minute for bundle
2201 * images). Polling for NVRAM update completion ensures that other MCDI
2202 * commands can be issued before the background NVRAM update completes.
2204 * Without polling, other MCDI commands can only be issued before the
2205 * NVRAM update completes if the MCDI transport and the firmware
2206 * support the Asynchronous MCDI protocol extensions in SF-116575-PS.
2208 * The initial call either completes the update synchronously, or
2209 * returns RC_PENDING to indicate processing is continuing. In the
2210 * latter case, we poll for at least 1 minute, at increasing intervals
2211 * (10ms, 100ms, 1s).
2213 while (verify_result == MC_CMD_NVRAM_VERIFY_RC_PENDING) {
2215 if (poll_retry > EF10_NVRAM_POLL_RETRIES) {
2221 EFSYS_SLEEP(poll_delay_us);
2222 if (poll_delay_us < EF10_NVRAM_MAX_POLL_DELAY_US)
2223 poll_delay_us *= 10;
2225 /* Poll for completion of background NVRAM update. */
2226 verify_result = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
2228 rc = efx_mcdi_nvram_update_finish(enp, partn, reboot,
2229 EFX_NVRAM_UPDATE_FLAGS_POLL, &verify_result);
2231 /* Poll failed, so assume NVRAM update failed. */
2236 if (verify_resultp != NULL)
2237 *verify_resultp = verify_result;
2244 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2249 __checkReturn efx_rc_t
2250 ef10_nvram_partn_set_version(
2251 __in efx_nic_t *enp,
2252 __in uint32_t partn,
2253 __in_ecount(4) uint16_t version[4])
2255 struct tlv_partition_version partn_version;
2259 /* Add or modify partition version TLV item */
2260 partn_version.version_w = __CPU_TO_LE_16(version[0]);
2261 partn_version.version_x = __CPU_TO_LE_16(version[1]);
2262 partn_version.version_y = __CPU_TO_LE_16(version[2]);
2263 partn_version.version_z = __CPU_TO_LE_16(version[3]);
2265 size = sizeof (partn_version) - (2 * sizeof (uint32_t));
2267 /* Write the version number to all segments in the partition */
2268 if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
2269 NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
2270 TLV_TAG_PARTITION_VERSION(partn),
2271 (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
2277 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2282 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
2286 typedef struct ef10_parttbl_entry_s {
2288 unsigned int port_mask;
2289 efx_nvram_type_t nvtype;
2290 } ef10_parttbl_entry_t;
2292 /* Port mask values */
2293 #define PORT_1 (1u << 1)
2294 #define PORT_2 (1u << 2)
2295 #define PORT_3 (1u << 3)
2296 #define PORT_4 (1u << 4)
2297 #define PORT_ALL (0xffffffffu)
2299 #define PARTN_MAP_ENTRY(partn, port_mask, nvtype) \
2300 { (NVRAM_PARTITION_TYPE_##partn), (PORT_##port_mask), (EFX_NVRAM_##nvtype) }
2302 /* Translate EFX NVRAM types to firmware partition types */
2303 static ef10_parttbl_entry_t hunt_parttbl[] = {
2304 /* partn ports nvtype */
2305 PARTN_MAP_ENTRY(MC_FIRMWARE, ALL, MC_FIRMWARE),
2306 PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP, ALL, MC_GOLDEN),
2307 PARTN_MAP_ENTRY(EXPANSION_ROM, ALL, BOOTROM),
2308 PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT0, 1, BOOTROM_CFG),
2309 PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT1, 2, BOOTROM_CFG),
2310 PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT2, 3, BOOTROM_CFG),
2311 PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT3, 4, BOOTROM_CFG),
2312 PARTN_MAP_ENTRY(DYNAMIC_CONFIG, ALL, DYNAMIC_CFG),
2313 PARTN_MAP_ENTRY(FPGA, ALL, FPGA),
2314 PARTN_MAP_ENTRY(FPGA_BACKUP, ALL, FPGA_BACKUP),
2315 PARTN_MAP_ENTRY(LICENSE, ALL, LICENSE),
2318 static ef10_parttbl_entry_t medford_parttbl[] = {
2319 /* partn ports nvtype */
2320 PARTN_MAP_ENTRY(MC_FIRMWARE, ALL, MC_FIRMWARE),
2321 PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP, ALL, MC_GOLDEN),
2322 PARTN_MAP_ENTRY(EXPANSION_ROM, ALL, BOOTROM),
2323 PARTN_MAP_ENTRY(EXPROM_CONFIG, ALL, BOOTROM_CFG),
2324 PARTN_MAP_ENTRY(DYNAMIC_CONFIG, ALL, DYNAMIC_CFG),
2325 PARTN_MAP_ENTRY(FPGA, ALL, FPGA),
2326 PARTN_MAP_ENTRY(FPGA_BACKUP, ALL, FPGA_BACKUP),
2327 PARTN_MAP_ENTRY(LICENSE, ALL, LICENSE),
2328 PARTN_MAP_ENTRY(EXPANSION_UEFI, ALL, UEFIROM),
2329 PARTN_MAP_ENTRY(MUM_FIRMWARE, ALL, MUM_FIRMWARE),
2332 static ef10_parttbl_entry_t medford2_parttbl[] = {
2333 /* partn ports nvtype */
2334 PARTN_MAP_ENTRY(MC_FIRMWARE, ALL, MC_FIRMWARE),
2335 PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP, ALL, MC_GOLDEN),
2336 PARTN_MAP_ENTRY(EXPANSION_ROM, ALL, BOOTROM),
2337 PARTN_MAP_ENTRY(EXPROM_CONFIG, ALL, BOOTROM_CFG),
2338 PARTN_MAP_ENTRY(DYNAMIC_CONFIG, ALL, DYNAMIC_CFG),
2339 PARTN_MAP_ENTRY(FPGA, ALL, FPGA),
2340 PARTN_MAP_ENTRY(FPGA_BACKUP, ALL, FPGA_BACKUP),
2341 PARTN_MAP_ENTRY(LICENSE, ALL, LICENSE),
2342 PARTN_MAP_ENTRY(EXPANSION_UEFI, ALL, UEFIROM),
2343 PARTN_MAP_ENTRY(MUM_FIRMWARE, ALL, MUM_FIRMWARE),
2344 PARTN_MAP_ENTRY(DYNCONFIG_DEFAULTS, ALL, DYNCONFIG_DEFAULTS),
2345 PARTN_MAP_ENTRY(ROMCONFIG_DEFAULTS, ALL, ROMCONFIG_DEFAULTS),
2346 PARTN_MAP_ENTRY(BUNDLE, ALL, BUNDLE),
2349 static __checkReturn efx_rc_t
2351 __in efx_nic_t *enp,
2352 __out ef10_parttbl_entry_t **parttblp,
2353 __out size_t *parttbl_rowsp)
2355 switch (enp->en_family) {
2356 case EFX_FAMILY_HUNTINGTON:
2357 *parttblp = hunt_parttbl;
2358 *parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
2361 case EFX_FAMILY_MEDFORD:
2362 *parttblp = medford_parttbl;
2363 *parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
2366 case EFX_FAMILY_MEDFORD2:
2367 *parttblp = medford2_parttbl;
2368 *parttbl_rowsp = EFX_ARRAY_SIZE(medford2_parttbl);
2372 EFSYS_ASSERT(B_FALSE);
2378 __checkReturn efx_rc_t
2379 ef10_nvram_type_to_partn(
2380 __in efx_nic_t *enp,
2381 __in efx_nvram_type_t type,
2382 __out uint32_t *partnp)
2384 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2385 ef10_parttbl_entry_t *parttbl = NULL;
2386 size_t parttbl_rows = 0;
2389 EFSYS_ASSERT3U(type, !=, EFX_NVRAM_INVALID);
2390 EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
2391 EFSYS_ASSERT(partnp != NULL);
2393 if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2394 for (i = 0; i < parttbl_rows; i++) {
2395 ef10_parttbl_entry_t *entry = &parttbl[i];
2397 if ((entry->nvtype == type) &&
2398 (entry->port_mask & (1u << emip->emi_port))) {
2399 *partnp = entry->partn;
2410 static __checkReturn efx_rc_t
2411 ef10_nvram_partn_to_type(
2412 __in efx_nic_t *enp,
2413 __in uint32_t partn,
2414 __out efx_nvram_type_t *typep)
2416 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2417 ef10_parttbl_entry_t *parttbl = NULL;
2418 size_t parttbl_rows = 0;
2421 EFSYS_ASSERT(typep != NULL);
2423 if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2424 for (i = 0; i < parttbl_rows; i++) {
2425 ef10_parttbl_entry_t *entry = &parttbl[i];
2427 if ((entry->partn == partn) &&
2428 (entry->port_mask & (1u << emip->emi_port))) {
2429 *typep = entry->nvtype;
2438 __checkReturn efx_rc_t
2440 __in efx_nic_t *enp)
2442 efx_nvram_type_t type;
2443 unsigned int npartns = 0;
2444 uint32_t *partns = NULL;
2449 /* Read available partitions from NVRAM partition map */
2450 size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
2451 EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
2452 if (partns == NULL) {
2457 if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
2462 for (i = 0; i < npartns; i++) {
2463 /* Check if the partition is supported for this port */
2464 if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
2467 if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
2471 EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2478 EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2480 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2484 #endif /* EFSYS_OPT_DIAG */
2486 __checkReturn efx_rc_t
2487 ef10_nvram_partn_get_version(
2488 __in efx_nic_t *enp,
2489 __in uint32_t partn,
2490 __out uint32_t *subtypep,
2491 __out_ecount(4) uint16_t version[4])
2495 /* FIXME: get highest partn version from all ports */
2496 /* FIXME: return partn description if available */
2498 if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
2499 version, NULL, 0)) != 0)
2505 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2510 __checkReturn efx_rc_t
2511 ef10_nvram_partn_rw_start(
2512 __in efx_nic_t *enp,
2513 __in uint32_t partn,
2514 __out size_t *chunk_sizep)
2516 efx_nvram_info_t eni = { 0 };
2519 if ((rc = ef10_nvram_partn_info(enp, partn, &eni)) != 0)
2522 if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
2525 if (chunk_sizep != NULL)
2526 *chunk_sizep = eni.eni_write_size;
2533 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2538 __checkReturn efx_rc_t
2539 ef10_nvram_partn_rw_finish(
2540 __in efx_nic_t *enp,
2541 __in uint32_t partn,
2542 __out_opt uint32_t *verify_resultp)
2546 if ((rc = ef10_nvram_partn_unlock(enp, partn, verify_resultp)) != 0)
2552 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2557 #endif /* EFSYS_OPT_NVRAM */
2559 #endif /* EFX_OPTS_EF10() */