2 * Copyright (c) 2012-2015 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
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 static __checkReturn efx_rc_t
53 __in tlv_cursor_t *cursor);
57 * Operations on TLV formatted partition data.
61 __in tlv_cursor_t *cursor)
65 dword = cursor->current[0];
66 tag = __LE_TO_CPU_32(dword);
73 __in tlv_cursor_t *cursor)
75 uint32_t dword, length;
77 if (tlv_tag(cursor) == TLV_TAG_END)
80 dword = cursor->current[1];
81 length = __LE_TO_CPU_32(dword);
83 return ((size_t)length);
88 __in tlv_cursor_t *cursor)
90 if (tlv_tag(cursor) == TLV_TAG_END)
93 return ((uint8_t *)(&cursor->current[2]));
98 __in tlv_cursor_t *cursor)
100 if (tlv_tag(cursor) == TLV_TAG_END)
103 return ((uint8_t *)cursor->current);
107 * TLV item DWORD length is tag + length + value (rounded up to DWORD)
108 * equivalent to tlv_n_words_for_len in mc-comms tlv.c
110 #define TLV_DWORD_COUNT(length) \
111 (1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
116 __in tlv_cursor_t *cursor)
120 length = tlv_length(cursor);
122 return (cursor->current + TLV_DWORD_COUNT(length));
127 __in tlv_cursor_t *cursor)
131 if ((rc = tlv_validate_state(cursor)) != 0)
134 if (cursor->current == cursor->end) {
135 /* No more tags after END tag */
136 cursor->current = NULL;
141 /* Advance to next item and validate */
142 cursor->current = tlv_next_item_ptr(cursor);
144 if ((rc = tlv_validate_state(cursor)) != 0)
154 EFSYS_PROBE1(fail1, efx_rc_t, rc);
161 __in tlv_cursor_t *cursor)
165 cursor->current = cursor->block;
167 if ((rc = tlv_validate_state(cursor)) != 0)
173 EFSYS_PROBE1(fail1, efx_rc_t, rc);
180 __in tlv_cursor_t *cursor,
185 rc = tlv_rewind(cursor);
187 if (tlv_tag(cursor) == tag)
190 rc = tlv_advance(cursor);
195 static __checkReturn efx_rc_t
197 __in tlv_cursor_t *cursor)
201 /* Check cursor position */
202 if (cursor->current < cursor->block) {
206 if (cursor->current > cursor->limit) {
211 if (tlv_tag(cursor) != TLV_TAG_END) {
212 /* Check current item has space for tag and length */
213 if (cursor->current > (cursor->limit - 2)) {
214 cursor->current = NULL;
219 /* Check we have value data for current item and another tag */
220 if (tlv_next_item_ptr(cursor) > (cursor->limit - 1)) {
221 cursor->current = NULL;
236 EFSYS_PROBE1(fail1, efx_rc_t, rc);
243 __out tlv_cursor_t *cursor,
244 __in uint32_t *block,
245 __in uint32_t *limit)
247 cursor->block = block;
248 cursor->limit = limit;
250 cursor->current = cursor->block;
253 return (tlv_validate_state(cursor));
257 tlv_init_cursor_from_size(
258 __out tlv_cursor_t *cursor,
263 limit = (uint32_t *)(block + size - sizeof (uint32_t));
264 return (tlv_init_cursor(cursor, (uint32_t *)block, limit));
269 __in tlv_cursor_t *cursor)
274 if (cursor->end == NULL) {
275 pos = cursor->current;
276 if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
279 cursor->end = cursor->current;
280 cursor->current = pos;
286 EFSYS_PROBE1(fail1, efx_rc_t, rc);
292 tlv_block_length_used(
293 __in tlv_cursor_t *cursor)
297 if ((rc = tlv_validate_state(cursor)) != 0)
300 if ((rc = tlv_require_end(cursor)) != 0)
303 /* Return space used (including the END tag) */
304 return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
309 EFSYS_PROBE1(fail1, efx_rc_t, rc);
315 static __checkReturn uint32_t *
317 __in tlv_cursor_t *cursor,
319 __in_bcount(size) uint8_t *data,
325 ptr = cursor->current;
327 *ptr++ = __CPU_TO_LE_32(tag);
328 *ptr++ = __CPU_TO_LE_32(len);
331 ptr[(len - 1) / sizeof (uint32_t)] = 0;
332 memcpy(ptr, data, len);
333 ptr += P2ROUNDUP(len, sizeof (uint32_t)) / sizeof (*ptr);
339 static __checkReturn efx_rc_t
341 __in tlv_cursor_t *cursor,
349 if ((rc = tlv_validate_state(cursor)) != 0)
352 if ((rc = tlv_require_end(cursor)) != 0)
355 if (tag == TLV_TAG_END) {
360 delta = TLV_DWORD_COUNT(size);
361 if (cursor->end + 1 + delta > cursor->limit) {
366 /* Move data up: new space at cursor->current */
367 memmove(cursor->current + delta, cursor->current,
368 (cursor->end + 1 - cursor->current) * sizeof (uint32_t));
370 /* Adjust the end pointer */
371 cursor->end += delta;
373 /* Write new TLV item */
374 tlv_write(cursor, tag, data, size);
385 EFSYS_PROBE1(fail1, efx_rc_t, rc);
390 static __checkReturn efx_rc_t
392 __in tlv_cursor_t *cursor,
398 unsigned int old_ndwords;
399 unsigned int new_ndwords;
403 if ((rc = tlv_validate_state(cursor)) != 0)
406 if (tlv_tag(cursor) == TLV_TAG_END) {
410 if (tlv_tag(cursor) != tag) {
415 old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
416 new_ndwords = TLV_DWORD_COUNT(size);
418 if ((rc = tlv_require_end(cursor)) != 0)
421 if (new_ndwords > old_ndwords) {
422 /* Expand space used for TLV item */
423 delta = new_ndwords - old_ndwords;
424 pos = cursor->current + old_ndwords;
426 if (cursor->end + 1 + delta > cursor->limit) {
431 /* Move up: new space at (cursor->current + old_ndwords) */
432 memmove(pos + delta, pos,
433 (cursor->end + 1 - pos) * sizeof (uint32_t));
435 /* Adjust the end pointer */
436 cursor->end += delta;
438 } else if (new_ndwords < old_ndwords) {
439 /* Shrink space used for TLV item */
440 delta = old_ndwords - new_ndwords;
441 pos = cursor->current + new_ndwords;
443 /* Move down: remove words at (cursor->current + new_ndwords) */
444 memmove(pos, pos + delta,
445 (cursor->end + 1 - pos) * sizeof (uint32_t));
447 /* Zero the new space at the end of the TLV chain */
448 memset(cursor->end + 1 - delta, 0, delta * sizeof (uint32_t));
450 /* Adjust the end pointer */
451 cursor->end -= delta;
455 tlv_write(cursor, tag, data, size);
468 EFSYS_PROBE1(fail1, efx_rc_t, rc);
473 /* Validate TLV formatted partition contents (before writing to flash) */
474 __checkReturn efx_rc_t
475 efx_nvram_tlv_validate(
478 __in_bcount(partn_size) caddr_t partn_data,
479 __in size_t partn_size)
482 struct tlv_partition_header *header;
483 struct tlv_partition_trailer *trailer;
489 EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
491 if ((partn_data == NULL) || (partn_size == 0)) {
496 /* The partition header must be the first item (at offset zero) */
497 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
502 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
506 header = (struct tlv_partition_header *)tlv_item(&cursor);
508 /* Check TLV partition length (includes the END tag) */
509 total_length = __LE_TO_CPU_32(header->total_length);
510 if (total_length > partn_size) {
515 /* Check partition ends with PARTITION_TRAILER and END tags */
516 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
520 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
522 if ((rc = tlv_advance(&cursor)) != 0) {
526 if (tlv_tag(&cursor) != TLV_TAG_END) {
531 /* Check generation counts are consistent */
532 if (trailer->generation != header->generation) {
537 /* Verify partition checksum */
539 for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
540 cksum += *((uint32_t *)(partn_data + pos));
566 EFSYS_PROBE1(fail1, efx_rc_t, rc);
572 * Read and validate a segment from a partition. A segment is a complete
573 * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
574 * be multiple segments in a partition, so seg_offset allows segments
575 * beyond the first to be read.
577 static __checkReturn efx_rc_t
578 ef10_nvram_read_tlv_segment(
581 __in size_t seg_offset,
582 __in_bcount(max_seg_size) caddr_t seg_data,
583 __in size_t max_seg_size)
586 struct tlv_partition_header *header;
587 struct tlv_partition_trailer *trailer;
593 EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
595 if ((seg_data == NULL) || (max_seg_size == 0)) {
600 /* Read initial chunk of the segment, starting at offset */
601 if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
603 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
607 /* A PARTITION_HEADER tag must be the first item at the given offset */
608 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
609 max_seg_size)) != 0) {
613 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
617 header = (struct tlv_partition_header *)tlv_item(&cursor);
619 /* Check TLV segment length (includes the END tag) */
620 total_length = __LE_TO_CPU_32(header->total_length);
621 if (total_length > max_seg_size) {
626 /* Read the remaining segment content */
627 if (total_length > EF10_NVRAM_CHUNK) {
628 if ((rc = ef10_nvram_partn_read_mode(enp, partn,
629 seg_offset + EF10_NVRAM_CHUNK,
630 seg_data + EF10_NVRAM_CHUNK,
631 total_length - EF10_NVRAM_CHUNK,
632 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
636 /* Check segment ends with PARTITION_TRAILER and END tags */
637 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
641 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
643 if ((rc = tlv_advance(&cursor)) != 0) {
647 if (tlv_tag(&cursor) != TLV_TAG_END) {
652 /* Check data read from segment is consistent */
653 if (trailer->generation != header->generation) {
655 * The partition data may have been modified between successive
656 * MCDI NVRAM_READ requests by the MC or another PCI function.
658 * The caller must retry to obtain consistent partition data.
664 /* Verify segment checksum */
666 for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
667 cksum += *((uint32_t *)(seg_data + pos));
697 EFSYS_PROBE1(fail1, efx_rc_t, rc);
703 * Read a single TLV item from a host memory
704 * buffer containing a TLV formatted segment.
706 __checkReturn efx_rc_t
707 ef10_nvram_buf_read_tlv(
709 __in_bcount(max_seg_size) caddr_t seg_data,
710 __in size_t max_seg_size,
712 __deref_out_bcount_opt(*sizep) caddr_t *datap,
721 if ((seg_data == NULL) || (max_seg_size == 0)) {
726 /* Find requested TLV tag in segment data */
727 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
728 max_seg_size)) != 0) {
732 if ((rc = tlv_find(&cursor, tag)) != 0) {
736 value = (caddr_t)tlv_value(&cursor);
737 length = tlv_length(&cursor);
742 /* Copy out data from TLV item */
743 EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
748 memcpy(data, value, length);
763 EFSYS_PROBE1(fail1, efx_rc_t, rc);
768 /* Read a single TLV item from the first segment in a TLV formatted partition */
769 __checkReturn efx_rc_t
770 ef10_nvram_partn_read_tlv(
774 __deref_out_bcount_opt(*seg_sizep) caddr_t *seg_datap,
775 __out size_t *seg_sizep)
777 caddr_t seg_data = NULL;
778 size_t partn_size = 0;
784 /* Allocate sufficient memory for the entire partition */
785 if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
788 if (partn_size == 0) {
793 EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
794 if (seg_data == NULL) {
800 * Read the first segment in a TLV partition. Retry until consistent
801 * segment contents are returned. Inconsistent data may be read if:
802 * a) the segment contents are invalid
803 * b) the MC has rebooted while we were reading the partition
804 * c) the partition has been modified while we were reading it
805 * Limit retry attempts to ensure forward progress.
809 rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
810 seg_data, partn_size);
811 } while ((rc == EAGAIN) && (--retry > 0));
814 /* Failed to obtain consistent segment data */
818 if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
819 tag, &data, &length)) != 0)
822 EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
834 EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
840 EFSYS_PROBE1(fail1, efx_rc_t, rc);
845 /* Compute the size of a segment. */
846 static __checkReturn efx_rc_t
847 ef10_nvram_buf_segment_size(
848 __in caddr_t seg_data,
849 __in size_t max_seg_size,
850 __out size_t *seg_sizep)
854 struct tlv_partition_header *header;
857 uint32_t *end_tag_position;
858 uint32_t segment_length;
860 /* A PARTITION_HEADER tag must be the first item at the given offset */
861 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
862 max_seg_size)) != 0) {
866 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
870 header = (struct tlv_partition_header *)tlv_item(&cursor);
872 /* Check TLV segment length (includes the END tag) */
873 *seg_sizep = __LE_TO_CPU_32(header->total_length);
874 if (*seg_sizep > max_seg_size) {
879 /* Check segment ends with PARTITION_TRAILER and END tags */
880 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
885 if ((rc = tlv_advance(&cursor)) != 0) {
889 if (tlv_tag(&cursor) != TLV_TAG_END) {
893 end_tag_position = cursor.current;
895 /* Verify segment checksum */
897 for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
898 cksum += *((uint32_t *)(seg_data + pos));
906 * Calculate total length from HEADER to END tags and compare to
907 * max_seg_size and the total_length field in the HEADER tag.
909 segment_length = tlv_block_length_used(&cursor);
911 if (segment_length > max_seg_size) {
916 if (segment_length != *seg_sizep) {
921 /* Skip over the first HEADER tag. */
922 rc = tlv_rewind(&cursor);
923 rc = tlv_advance(&cursor);
926 if (tlv_tag(&cursor) == TLV_TAG_END) {
927 /* Check that the END tag is the one found earlier. */
928 if (cursor.current != end_tag_position)
932 /* Check for duplicate HEADER tags before the END tag. */
933 if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
938 rc = tlv_advance(&cursor);
968 EFSYS_PROBE1(fail1, efx_rc_t, rc);
974 * Add or update a single TLV item in a host memory buffer containing a TLV
975 * formatted segment. Historically partitions consisted of only one segment.
977 __checkReturn efx_rc_t
978 ef10_nvram_buf_write_tlv(
979 __inout_bcount(max_seg_size) caddr_t seg_data,
980 __in size_t max_seg_size,
982 __in_bcount(tag_size) caddr_t tag_data,
983 __in size_t tag_size,
984 __out size_t *total_lengthp)
987 struct tlv_partition_header *header;
988 struct tlv_partition_trailer *trailer;
994 /* A PARTITION_HEADER tag must be the first item (at offset zero) */
995 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
996 max_seg_size)) != 0) {
1000 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1004 header = (struct tlv_partition_header *)tlv_item(&cursor);
1006 /* Update the TLV chain to contain the new data */
1007 if ((rc = tlv_find(&cursor, tag)) == 0) {
1008 /* Modify existing TLV item */
1009 if ((rc = tlv_modify(&cursor, tag,
1010 (uint8_t *)tag_data, tag_size)) != 0)
1013 /* Insert a new TLV item before the PARTITION_TRAILER */
1014 rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1019 if ((rc = tlv_insert(&cursor, tag,
1020 (uint8_t *)tag_data, tag_size)) != 0) {
1026 /* Find the trailer tag */
1027 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1031 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1033 /* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1034 *total_lengthp = tlv_block_length_used(&cursor);
1035 if (*total_lengthp > max_seg_size) {
1039 generation = __LE_TO_CPU_32(header->generation) + 1;
1041 header->total_length = __CPU_TO_LE_32(*total_lengthp);
1042 header->generation = __CPU_TO_LE_32(generation);
1043 trailer->generation = __CPU_TO_LE_32(generation);
1045 /* Recompute PARTITION_TRAILER checksum */
1046 trailer->checksum = 0;
1048 for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1049 cksum += *((uint32_t *)(seg_data + pos));
1051 trailer->checksum = ~cksum + 1;
1068 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1074 * Add or update a single TLV item in the first segment of a TLV formatted
1075 * dynamic config partition. The first segment is the current active
1078 __checkReturn efx_rc_t
1079 ef10_nvram_partn_write_tlv(
1080 __in efx_nic_t *enp,
1081 __in uint32_t partn,
1083 __in_bcount(size) caddr_t data,
1086 return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1091 * Read a segment from nvram at the given offset into a buffer (segment_data)
1092 * and optionally write a new tag to it.
1094 static __checkReturn efx_rc_t
1095 ef10_nvram_segment_write_tlv(
1096 __in efx_nic_t *enp,
1097 __in uint32_t partn,
1099 __in_bcount(size) caddr_t data,
1101 __inout caddr_t *seg_datap,
1102 __inout size_t *partn_offsetp,
1103 __inout size_t *src_remain_lenp,
1104 __inout size_t *dest_remain_lenp,
1105 __in boolean_t write)
1109 size_t original_segment_size;
1110 size_t modified_segment_size;
1113 * Read the segment from NVRAM into the segment_data buffer and validate
1114 * it, returning if it does not validate. This is not a failure unless
1115 * this is the first segment in a partition. In this case the caller
1116 * must propogate the error.
1118 status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1119 *seg_datap, *src_remain_lenp);
1123 status = ef10_nvram_buf_segment_size(*seg_datap,
1124 *src_remain_lenp, &original_segment_size);
1129 /* Update the contents of the segment in the buffer */
1130 if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1131 *dest_remain_lenp, tag, data, size,
1132 &modified_segment_size)) != 0)
1134 *dest_remain_lenp -= modified_segment_size;
1135 *seg_datap += modified_segment_size;
1138 * We won't modify this segment, but still need to update the
1139 * remaining lengths and pointers.
1141 *dest_remain_lenp -= original_segment_size;
1142 *seg_datap += original_segment_size;
1145 *partn_offsetp += original_segment_size;
1146 *src_remain_lenp -= original_segment_size;
1151 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1157 * Add or update a single TLV item in either the first segment or in all
1158 * segments in a TLV formatted dynamic config partition. Dynamic config
1159 * partitions on boards that support RFID are divided into a number of segments,
1160 * each formatted like a partition, with header, trailer and end tags. The first
1161 * segment is the current active configuration.
1163 * The segments are initialised by manftest and each contain a different
1164 * configuration e.g. firmware variant. The firmware can be instructed
1165 * via RFID to copy a segment to replace the first segment, hence changing the
1166 * active configuration. This allows ops to change the configuration of a board
1167 * prior to shipment using RFID.
1169 * Changes to the dynamic config may need to be written to all segments (e.g.
1170 * firmware versions) or just the first segment (changes to the active
1171 * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1172 * If only the first segment is written the code still needs to be aware of the
1173 * possible presence of subsequent segments as writing to a segment may cause
1174 * its size to increase, which would overwrite the subsequent segments and
1177 __checkReturn efx_rc_t
1178 ef10_nvram_partn_write_segment_tlv(
1179 __in efx_nic_t *enp,
1180 __in uint32_t partn,
1182 __in_bcount(size) caddr_t data,
1184 __in boolean_t all_segments)
1186 size_t partn_size = 0;
1188 size_t total_length = 0;
1190 size_t current_offset = 0;
1191 size_t remaining_original_length;
1192 size_t remaining_modified_length;
1193 caddr_t segment_data;
1195 EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1197 /* Allocate sufficient memory for the entire partition */
1198 if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1201 EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1202 if (partn_data == NULL) {
1207 remaining_original_length = partn_size;
1208 remaining_modified_length = partn_size;
1209 segment_data = partn_data;
1211 /* Lock the partition */
1212 if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1215 /* Iterate over each (potential) segment to update it. */
1217 boolean_t write = all_segments || current_offset == 0;
1219 rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1220 &segment_data, ¤t_offset, &remaining_original_length,
1221 &remaining_modified_length, write);
1223 if (current_offset == 0) {
1225 * If no data has been read then the first
1226 * segment is invalid, which is an error.
1232 } while (current_offset < partn_size);
1234 total_length = segment_data - partn_data;
1237 * We've run out of space. This should actually be dealt with by
1238 * ef10_nvram_buf_write_tlv returning ENOSPC.
1240 if (total_length > partn_size) {
1245 /* Erase the whole partition in NVRAM */
1246 if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1249 /* Write new partition contents from the buffer to NVRAM */
1250 if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1251 total_length)) != 0)
1254 /* Unlock the partition */
1255 ef10_nvram_partn_unlock(enp, partn);
1257 EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1270 ef10_nvram_partn_unlock(enp, partn);
1274 EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1278 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1284 * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1285 * not the data used by the segments in the partition.
1287 __checkReturn efx_rc_t
1288 ef10_nvram_partn_size(
1289 __in efx_nic_t *enp,
1290 __in uint32_t partn,
1291 __out size_t *sizep)
1295 if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1296 NULL, NULL, NULL)) != 0)
1302 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1307 __checkReturn efx_rc_t
1308 ef10_nvram_partn_lock(
1309 __in efx_nic_t *enp,
1310 __in uint32_t partn)
1314 if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1320 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1325 __checkReturn efx_rc_t
1326 ef10_nvram_partn_read_mode(
1327 __in efx_nic_t *enp,
1328 __in uint32_t partn,
1329 __in unsigned int offset,
1330 __out_bcount(size) caddr_t data,
1338 chunk = MIN(size, EF10_NVRAM_CHUNK);
1340 if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
1341 data, chunk, mode)) != 0) {
1353 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1358 __checkReturn efx_rc_t
1359 ef10_nvram_partn_read(
1360 __in efx_nic_t *enp,
1361 __in uint32_t partn,
1362 __in unsigned int offset,
1363 __out_bcount(size) caddr_t data,
1367 * Read requests which come in through the EFX API expect to
1368 * read the current, active partition.
1370 return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
1371 MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
1374 __checkReturn efx_rc_t
1375 ef10_nvram_partn_erase(
1376 __in efx_nic_t *enp,
1377 __in uint32_t partn,
1378 __in unsigned int offset,
1382 uint32_t erase_size;
1384 if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1385 &erase_size, NULL)) != 0)
1388 if (erase_size == 0) {
1389 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
1392 if (size % erase_size != 0) {
1397 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
1400 offset += erase_size;
1414 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1419 __checkReturn efx_rc_t
1420 ef10_nvram_partn_write(
1421 __in efx_nic_t *enp,
1422 __in uint32_t partn,
1423 __in unsigned int offset,
1424 __out_bcount(size) caddr_t data,
1428 uint32_t write_size;
1431 if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1432 NULL, &write_size)) != 0)
1435 if (write_size != 0) {
1437 * Check that the size is a multiple of the write chunk size if
1438 * the write chunk size is available.
1440 if (size % write_size != 0) {
1445 write_size = EF10_NVRAM_CHUNK;
1449 chunk = MIN(size, write_size);
1451 if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
1452 data, chunk)) != 0) {
1468 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1474 ef10_nvram_partn_unlock(
1475 __in efx_nic_t *enp,
1476 __in uint32_t partn)
1482 if ((rc = efx_mcdi_nvram_update_finish(enp, partn, reboot)) != 0)
1488 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1491 __checkReturn efx_rc_t
1492 ef10_nvram_partn_set_version(
1493 __in efx_nic_t *enp,
1494 __in uint32_t partn,
1495 __in_ecount(4) uint16_t version[4])
1497 struct tlv_partition_version partn_version;
1501 /* Add or modify partition version TLV item */
1502 partn_version.version_w = __CPU_TO_LE_16(version[0]);
1503 partn_version.version_x = __CPU_TO_LE_16(version[1]);
1504 partn_version.version_y = __CPU_TO_LE_16(version[2]);
1505 partn_version.version_z = __CPU_TO_LE_16(version[3]);
1507 size = sizeof (partn_version) - (2 * sizeof (uint32_t));
1509 /* Write the version number to all segments in the partition */
1510 if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
1511 NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
1512 TLV_TAG_PARTITION_VERSION(partn),
1513 (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
1519 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1524 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
1528 typedef struct ef10_parttbl_entry_s {
1531 efx_nvram_type_t nvtype;
1532 } ef10_parttbl_entry_t;
1534 /* Translate EFX NVRAM types to firmware partition types */
1535 static ef10_parttbl_entry_t hunt_parttbl[] = {
1536 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 1, EFX_NVRAM_MC_FIRMWARE},
1537 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 2, EFX_NVRAM_MC_FIRMWARE},
1538 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 3, EFX_NVRAM_MC_FIRMWARE},
1539 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 4, EFX_NVRAM_MC_FIRMWARE},
1540 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 1, EFX_NVRAM_MC_GOLDEN},
1541 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 2, EFX_NVRAM_MC_GOLDEN},
1542 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 3, EFX_NVRAM_MC_GOLDEN},
1543 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 4, EFX_NVRAM_MC_GOLDEN},
1544 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 1, EFX_NVRAM_BOOTROM},
1545 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 2, EFX_NVRAM_BOOTROM},
1546 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 3, EFX_NVRAM_BOOTROM},
1547 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 4, EFX_NVRAM_BOOTROM},
1548 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
1549 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1, 2, EFX_NVRAM_BOOTROM_CFG},
1550 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2, 3, EFX_NVRAM_BOOTROM_CFG},
1551 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 4, EFX_NVRAM_BOOTROM_CFG},
1552 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 1, EFX_NVRAM_DYNAMIC_CFG},
1553 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 2, EFX_NVRAM_DYNAMIC_CFG},
1554 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 3, EFX_NVRAM_DYNAMIC_CFG},
1555 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 4, EFX_NVRAM_DYNAMIC_CFG},
1556 {NVRAM_PARTITION_TYPE_FPGA, 1, EFX_NVRAM_FPGA},
1557 {NVRAM_PARTITION_TYPE_FPGA, 2, EFX_NVRAM_FPGA},
1558 {NVRAM_PARTITION_TYPE_FPGA, 3, EFX_NVRAM_FPGA},
1559 {NVRAM_PARTITION_TYPE_FPGA, 4, EFX_NVRAM_FPGA},
1560 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 1, EFX_NVRAM_FPGA_BACKUP},
1561 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 2, EFX_NVRAM_FPGA_BACKUP},
1562 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 3, EFX_NVRAM_FPGA_BACKUP},
1563 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 4, EFX_NVRAM_FPGA_BACKUP},
1564 {NVRAM_PARTITION_TYPE_LICENSE, 1, EFX_NVRAM_LICENSE},
1565 {NVRAM_PARTITION_TYPE_LICENSE, 2, EFX_NVRAM_LICENSE},
1566 {NVRAM_PARTITION_TYPE_LICENSE, 3, EFX_NVRAM_LICENSE},
1567 {NVRAM_PARTITION_TYPE_LICENSE, 4, EFX_NVRAM_LICENSE}
1570 static ef10_parttbl_entry_t medford_parttbl[] = {
1571 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 1, EFX_NVRAM_MC_FIRMWARE},
1572 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 2, EFX_NVRAM_MC_FIRMWARE},
1573 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 3, EFX_NVRAM_MC_FIRMWARE},
1574 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 4, EFX_NVRAM_MC_FIRMWARE},
1575 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 1, EFX_NVRAM_MC_GOLDEN},
1576 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 2, EFX_NVRAM_MC_GOLDEN},
1577 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 3, EFX_NVRAM_MC_GOLDEN},
1578 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 4, EFX_NVRAM_MC_GOLDEN},
1579 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 1, EFX_NVRAM_BOOTROM},
1580 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 2, EFX_NVRAM_BOOTROM},
1581 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 3, EFX_NVRAM_BOOTROM},
1582 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 4, EFX_NVRAM_BOOTROM},
1583 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
1584 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 2, EFX_NVRAM_BOOTROM_CFG},
1585 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 3, EFX_NVRAM_BOOTROM_CFG},
1586 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 4, EFX_NVRAM_BOOTROM_CFG},
1587 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 1, EFX_NVRAM_DYNAMIC_CFG},
1588 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 2, EFX_NVRAM_DYNAMIC_CFG},
1589 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 3, EFX_NVRAM_DYNAMIC_CFG},
1590 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 4, EFX_NVRAM_DYNAMIC_CFG},
1591 {NVRAM_PARTITION_TYPE_FPGA, 1, EFX_NVRAM_FPGA},
1592 {NVRAM_PARTITION_TYPE_FPGA, 2, EFX_NVRAM_FPGA},
1593 {NVRAM_PARTITION_TYPE_FPGA, 3, EFX_NVRAM_FPGA},
1594 {NVRAM_PARTITION_TYPE_FPGA, 4, EFX_NVRAM_FPGA},
1595 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 1, EFX_NVRAM_FPGA_BACKUP},
1596 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 2, EFX_NVRAM_FPGA_BACKUP},
1597 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 3, EFX_NVRAM_FPGA_BACKUP},
1598 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 4, EFX_NVRAM_FPGA_BACKUP},
1599 {NVRAM_PARTITION_TYPE_LICENSE, 1, EFX_NVRAM_LICENSE},
1600 {NVRAM_PARTITION_TYPE_LICENSE, 2, EFX_NVRAM_LICENSE},
1601 {NVRAM_PARTITION_TYPE_LICENSE, 3, EFX_NVRAM_LICENSE},
1602 {NVRAM_PARTITION_TYPE_LICENSE, 4, EFX_NVRAM_LICENSE}
1605 static __checkReturn efx_rc_t
1607 __in efx_nic_t *enp,
1608 __out ef10_parttbl_entry_t **parttblp,
1609 __out size_t *parttbl_rowsp)
1611 switch (enp->en_family) {
1612 case EFX_FAMILY_HUNTINGTON:
1613 *parttblp = hunt_parttbl;
1614 *parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
1617 case EFX_FAMILY_MEDFORD:
1618 *parttblp = medford_parttbl;
1619 *parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
1623 EFSYS_ASSERT(B_FALSE);
1629 __checkReturn efx_rc_t
1630 ef10_nvram_type_to_partn(
1631 __in efx_nic_t *enp,
1632 __in efx_nvram_type_t type,
1633 __out uint32_t *partnp)
1635 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1636 ef10_parttbl_entry_t *parttbl = NULL;
1637 size_t parttbl_rows = 0;
1640 EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
1641 EFSYS_ASSERT(partnp != NULL);
1643 if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
1644 for (i = 0; i < parttbl_rows; i++) {
1645 ef10_parttbl_entry_t *entry = &parttbl[i];
1647 if (entry->nvtype == type &&
1648 entry->port == emip->emi_port) {
1649 *partnp = entry->partn;
1660 static __checkReturn efx_rc_t
1661 ef10_nvram_partn_to_type(
1662 __in efx_nic_t *enp,
1663 __in uint32_t partn,
1664 __out efx_nvram_type_t *typep)
1666 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1667 ef10_parttbl_entry_t *parttbl = NULL;
1668 size_t parttbl_rows = 0;
1671 EFSYS_ASSERT(typep != NULL);
1673 if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
1674 for (i = 0; i < parttbl_rows; i++) {
1675 ef10_parttbl_entry_t *entry = &parttbl[i];
1677 if (entry->partn == partn &&
1678 entry->port == emip->emi_port) {
1679 *typep = entry->nvtype;
1688 __checkReturn efx_rc_t
1690 __in efx_nic_t *enp)
1692 efx_nvram_type_t type;
1693 unsigned int npartns = 0;
1694 uint32_t *partns = NULL;
1699 /* Read available partitions from NVRAM partition map */
1700 size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
1701 EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
1702 if (partns == NULL) {
1707 if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
1712 for (i = 0; i < npartns; i++) {
1713 /* Check if the partition is supported for this port */
1714 if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
1717 if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
1721 EFSYS_KMEM_FREE(enp->en_esip, size, partns);
1728 EFSYS_KMEM_FREE(enp->en_esip, size, partns);
1730 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1734 #endif /* EFSYS_OPT_DIAG */
1736 __checkReturn efx_rc_t
1737 ef10_nvram_partn_get_version(
1738 __in efx_nic_t *enp,
1739 __in uint32_t partn,
1740 __out uint32_t *subtypep,
1741 __out_ecount(4) uint16_t version[4])
1745 /* FIXME: get highest partn version from all ports */
1746 /* FIXME: return partn description if available */
1748 if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
1749 version, NULL, 0)) != 0)
1755 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1760 __checkReturn efx_rc_t
1761 ef10_nvram_partn_rw_start(
1762 __in efx_nic_t *enp,
1763 __in uint32_t partn,
1764 __out size_t *chunk_sizep)
1768 if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1771 if (chunk_sizep != NULL)
1772 *chunk_sizep = EF10_NVRAM_CHUNK;
1777 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1783 ef10_nvram_partn_rw_finish(
1784 __in efx_nic_t *enp,
1785 __in uint32_t partn)
1787 ef10_nvram_partn_unlock(enp, partn);
1790 #endif /* EFSYS_OPT_NVRAM */
1792 #endif /* EFSYS_OPT_HUNTINGTON */