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$");
36 #include "efx_types.h"
40 #if EFSYS_OPT_HUNTINGTON
42 #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
44 #include "ef10_tlv_layout.h"
46 /* Cursor for TLV partition format */
47 typedef struct tlv_cursor_s {
48 uint32_t *block; /* Base of data block */
49 uint32_t *current; /* Cursor position */
50 uint32_t *end; /* End tag position */
51 uint32_t *limit; /* Last dword of data block */
54 static __checkReturn efx_rc_t
56 __in tlv_cursor_t *cursor);
60 * Operations on TLV formatted partition data.
64 __in tlv_cursor_t *cursor)
68 dword = cursor->current[0];
69 tag = __LE_TO_CPU_32(dword);
76 __in tlv_cursor_t *cursor)
78 uint32_t dword, length;
80 if (tlv_tag(cursor) == TLV_TAG_END)
83 dword = cursor->current[1];
84 length = __LE_TO_CPU_32(dword);
86 return ((size_t)length);
91 __in tlv_cursor_t *cursor)
93 if (tlv_tag(cursor) == TLV_TAG_END)
96 return ((uint8_t *)(&cursor->current[2]));
101 __in tlv_cursor_t *cursor)
103 if (tlv_tag(cursor) == TLV_TAG_END)
106 return ((uint8_t *)cursor->current);
110 * TLV item DWORD length is tag + length + value (rounded up to DWORD)
111 * equivalent to tlv_n_words_for_len in mc-comms tlv.c
113 #define TLV_DWORD_COUNT(length) \
114 (1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
119 __in tlv_cursor_t *cursor)
123 length = tlv_length(cursor);
125 return (cursor->current + TLV_DWORD_COUNT(length));
130 __in tlv_cursor_t *cursor)
134 if ((rc = tlv_validate_state(cursor)) != 0)
137 if (cursor->current == cursor->end) {
138 /* No more tags after END tag */
139 cursor->current = NULL;
144 /* Advance to next item and validate */
145 cursor->current = tlv_next_item_ptr(cursor);
147 if ((rc = tlv_validate_state(cursor)) != 0)
157 EFSYS_PROBE1(fail1, efx_rc_t, rc);
164 __in tlv_cursor_t *cursor)
168 cursor->current = cursor->block;
170 if ((rc = tlv_validate_state(cursor)) != 0)
176 EFSYS_PROBE1(fail1, efx_rc_t, rc);
183 __in tlv_cursor_t *cursor,
188 rc = tlv_rewind(cursor);
190 if (tlv_tag(cursor) == tag)
193 rc = tlv_advance(cursor);
198 static __checkReturn efx_rc_t
200 __in tlv_cursor_t *cursor)
204 /* Check cursor position */
205 if (cursor->current < cursor->block) {
209 if (cursor->current > cursor->limit) {
214 if (tlv_tag(cursor) != TLV_TAG_END) {
215 /* Check current item has space for tag and length */
216 if (cursor->current > (cursor->limit - 2)) {
217 cursor->current = NULL;
222 /* Check we have value data for current item and another tag */
223 if (tlv_next_item_ptr(cursor) > (cursor->limit - 1)) {
224 cursor->current = NULL;
239 EFSYS_PROBE1(fail1, efx_rc_t, rc);
246 __in tlv_cursor_t *cursor,
247 __in uint32_t *block,
248 __in uint32_t *limit)
250 cursor->block = block;
251 cursor->limit = limit;
253 cursor->current = cursor->block;
256 return (tlv_validate_state(cursor));
260 tlv_init_cursor_from_size(
261 __in tlv_cursor_t *cursor,
266 limit = (uint32_t *)(block + size - sizeof (uint32_t));
267 return (tlv_init_cursor(cursor, (uint32_t *)block, limit));
272 __in tlv_cursor_t *cursor)
277 if (cursor->end == NULL) {
278 pos = cursor->current;
279 if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
282 cursor->end = cursor->current;
283 cursor->current = pos;
289 EFSYS_PROBE1(fail1, efx_rc_t, rc);
295 tlv_block_length_used(
296 __in tlv_cursor_t *cursor)
300 if ((rc = tlv_validate_state(cursor)) != 0)
303 if ((rc = tlv_require_end(cursor)) != 0)
306 /* Return space used (including the END tag) */
307 return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
312 EFSYS_PROBE1(fail1, efx_rc_t, rc);
318 static __checkReturn uint32_t *
320 __in tlv_cursor_t *cursor,
322 __in_bcount(size) uint8_t *data,
328 ptr = cursor->current;
330 *ptr++ = __CPU_TO_LE_32(tag);
331 *ptr++ = __CPU_TO_LE_32(len);
334 ptr[(len - 1) / sizeof (uint32_t)] = 0;
335 memcpy(ptr, data, len);
336 ptr += P2ROUNDUP(len, sizeof (uint32_t)) / sizeof (*ptr);
342 static __checkReturn efx_rc_t
344 __in tlv_cursor_t *cursor,
352 if ((rc = tlv_validate_state(cursor)) != 0)
355 if ((rc = tlv_require_end(cursor)) != 0)
358 if (tag == TLV_TAG_END) {
363 delta = TLV_DWORD_COUNT(size);
364 if (cursor->end + 1 + delta > cursor->limit) {
369 /* Move data up: new space at cursor->current */
370 memmove(cursor->current + delta, cursor->current,
371 (cursor->end + 1 - cursor->current) * sizeof (uint32_t));
373 /* Adjust the end pointer */
374 cursor->end += delta;
376 /* Write new TLV item */
377 tlv_write(cursor, tag, data, size);
388 EFSYS_PROBE1(fail1, efx_rc_t, rc);
393 static __checkReturn efx_rc_t
395 __in tlv_cursor_t *cursor,
401 unsigned int old_ndwords;
402 unsigned int new_ndwords;
406 if ((rc = tlv_validate_state(cursor)) != 0)
409 if (tlv_tag(cursor) == TLV_TAG_END) {
413 if (tlv_tag(cursor) != tag) {
418 old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
419 new_ndwords = TLV_DWORD_COUNT(size);
421 if ((rc = tlv_require_end(cursor)) != 0)
424 if (new_ndwords > old_ndwords) {
425 /* Expand space used for TLV item */
426 delta = new_ndwords - old_ndwords;
427 pos = cursor->current + old_ndwords;
429 if (cursor->end + 1 + delta > cursor->limit) {
434 /* Move up: new space at (cursor->current + old_ndwords) */
435 memmove(pos + delta, pos,
436 (cursor->end + 1 - pos) * sizeof (uint32_t));
438 /* Adjust the end pointer */
439 cursor->end += delta;
441 } else if (new_ndwords < old_ndwords) {
442 /* Shrink space used for TLV item */
443 delta = old_ndwords - new_ndwords;
444 pos = cursor->current + new_ndwords;
446 /* Move down: remove words at (cursor->current + new_ndwords) */
447 memmove(pos, pos + delta,
448 (cursor->end + 1 - pos) * sizeof (uint32_t));
450 /* Zero the new space at the end of the TLV chain */
451 memset(cursor->end + 1 - delta, 0, delta * sizeof (uint32_t));
453 /* Adjust the end pointer */
454 cursor->end -= delta;
458 tlv_write(cursor, tag, data, size);
471 EFSYS_PROBE1(fail1, efx_rc_t, rc);
476 /* Validate TLV formatted partition contents (before writing to flash) */
477 __checkReturn efx_rc_t
478 efx_nvram_tlv_validate(
481 __in_bcount(partn_size) caddr_t partn_data,
482 __in size_t partn_size)
485 struct tlv_partition_header *header;
486 struct tlv_partition_trailer *trailer;
492 EFX_STATIC_ASSERT(sizeof (*header) <= HUNTINGTON_NVRAM_CHUNK);
494 if ((partn_data == NULL) || (partn_size == 0)) {
499 /* The partition header must be the first item (at offset zero) */
500 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
505 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
509 header = (struct tlv_partition_header *)tlv_item(&cursor);
511 /* Check TLV partition length (includes the END tag) */
512 total_length = __LE_TO_CPU_32(header->total_length);
513 if (total_length > partn_size) {
518 /* Check partition ends with PARTITION_TRAILER and END tags */
519 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
523 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
525 if ((rc = tlv_advance(&cursor)) != 0) {
529 if (tlv_tag(&cursor) != TLV_TAG_END) {
534 /* Check generation counts are consistent */
535 if (trailer->generation != header->generation) {
540 /* Verify partition checksum */
542 for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
543 cksum += *((uint32_t *)(partn_data + pos));
569 EFSYS_PROBE1(fail1, efx_rc_t, rc);
575 * Read and validate a segment from a partition. A segment is a complete
576 * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
577 * be multiple segments in a partition, so seg_offset allows segments
578 * beyond the first to be read.
580 static __checkReturn efx_rc_t
581 hunt_nvram_read_tlv_segment(
584 __in size_t seg_offset,
585 __in_bcount(max_seg_size) caddr_t seg_data,
586 __in size_t max_seg_size)
589 struct tlv_partition_header *header;
590 struct tlv_partition_trailer *trailer;
596 EFX_STATIC_ASSERT(sizeof (*header) <= HUNTINGTON_NVRAM_CHUNK);
598 if ((seg_data == NULL) || (max_seg_size == 0)) {
603 /* Read initial chunk of the segment, starting at offset */
604 if ((rc = hunt_nvram_partn_read(enp, partn, seg_offset, seg_data,
605 HUNTINGTON_NVRAM_CHUNK)) != 0) {
609 /* A PARTITION_HEADER tag must be the first item at the given offset */
610 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
611 max_seg_size)) != 0) {
615 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
619 header = (struct tlv_partition_header *)tlv_item(&cursor);
621 /* Check TLV segment length (includes the END tag) */
622 total_length = __LE_TO_CPU_32(header->total_length);
623 if (total_length > max_seg_size) {
628 /* Read the remaining segment content */
629 if (total_length > HUNTINGTON_NVRAM_CHUNK) {
630 if ((rc = hunt_nvram_partn_read(enp, partn,
631 seg_offset + HUNTINGTON_NVRAM_CHUNK,
632 seg_data + HUNTINGTON_NVRAM_CHUNK,
633 total_length - HUNTINGTON_NVRAM_CHUNK)) != 0)
637 /* Check segment ends with PARTITION_TRAILER and END tags */
638 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
642 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
644 if ((rc = tlv_advance(&cursor)) != 0) {
648 if (tlv_tag(&cursor) != TLV_TAG_END) {
653 /* Check data read from segment is consistent */
654 if (trailer->generation != header->generation) {
656 * The partition data may have been modified between successive
657 * MCDI NVRAM_READ requests by the MC or another PCI function.
659 * The caller must retry to obtain consistent partition data.
665 /* Verify segment checksum */
667 for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
668 cksum += *((uint32_t *)(seg_data + pos));
698 EFSYS_PROBE1(fail1, efx_rc_t, rc);
704 * Read a single TLV item from a host memory
705 * buffer containing a TLV formatted segment.
707 __checkReturn efx_rc_t
708 hunt_nvram_buf_read_tlv(
710 __in_bcount(max_seg_size) caddr_t seg_data,
711 __in size_t max_seg_size,
713 __deref_out_bcount_opt(*sizep) caddr_t *datap,
722 if ((seg_data == NULL) || (max_seg_size == 0)) {
727 /* Find requested TLV tag in segment data */
728 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
729 max_seg_size)) != 0) {
733 if ((rc = tlv_find(&cursor, tag)) != 0) {
737 value = (caddr_t)tlv_value(&cursor);
738 length = tlv_length(&cursor);
743 /* Copy out data from TLV item */
744 EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
749 memcpy(data, value, length);
764 EFSYS_PROBE1(fail1, efx_rc_t, rc);
769 /* Read a single TLV item from the first segment in a TLV formatted partition */
770 __checkReturn efx_rc_t
771 hunt_nvram_partn_read_tlv(
775 __deref_out_bcount_opt(*seg_sizep) caddr_t *seg_datap,
776 __out size_t *seg_sizep)
778 caddr_t seg_data = NULL;
779 size_t partn_size = 0;
785 /* Allocate sufficient memory for the entire partition */
786 if ((rc = hunt_nvram_partn_size(enp, partn, &partn_size)) != 0)
789 if (partn_size == 0) {
794 EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
795 if (seg_data == NULL) {
801 * Read the first segment in a TLV partition. Retry until consistent
802 * segment contents are returned. Inconsistent data may be read if:
803 * a) the segment contents are invalid
804 * b) the MC has rebooted while we were reading the partition
805 * c) the partition has been modified while we were reading it
806 * Limit retry attempts to ensure forward progress.
810 rc = hunt_nvram_read_tlv_segment(enp, partn, 0,
811 seg_data, partn_size);
812 } while ((rc == EAGAIN) && (--retry > 0));
815 /* Failed to obtain consistent segment data */
819 if ((rc = hunt_nvram_buf_read_tlv(enp, seg_data, partn_size,
820 tag, &data, &length)) != 0)
823 EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
835 EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
841 EFSYS_PROBE1(fail1, efx_rc_t, rc);
846 /* Compute the size of a segment. */
847 static __checkReturn efx_rc_t
848 hunt_nvram_buf_segment_size(
849 __in caddr_t seg_data,
850 __in size_t max_seg_size,
851 __out size_t *seg_sizep)
855 struct tlv_partition_header *header;
858 uint32_t *end_tag_position;
859 uint32_t segment_length;
861 /* A PARTITION_HEADER tag must be the first item at the given offset */
862 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
863 max_seg_size)) != 0) {
867 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
871 header = (struct tlv_partition_header *)tlv_item(&cursor);
873 /* Check TLV segment length (includes the END tag) */
874 *seg_sizep = __LE_TO_CPU_32(header->total_length);
875 if (*seg_sizep > max_seg_size) {
880 /* Check segment ends with PARTITION_TRAILER and END tags */
881 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
886 if ((rc = tlv_advance(&cursor)) != 0) {
890 if (tlv_tag(&cursor) != TLV_TAG_END) {
894 end_tag_position = cursor.current;
896 /* Verify segment checksum */
898 for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
899 cksum += *((uint32_t *)(seg_data + pos));
907 * Calculate total length from HEADER to END tags and compare to
908 * max_seg_size and the total_length field in the HEADER tag.
910 segment_length = tlv_block_length_used(&cursor);
912 if (segment_length > max_seg_size) {
917 if (segment_length != *seg_sizep) {
922 /* Skip over the first HEADER tag. */
923 rc = tlv_rewind(&cursor);
924 rc = tlv_advance(&cursor);
927 if (tlv_tag(&cursor) == TLV_TAG_END) {
928 /* Check that the END tag is the one found earlier. */
929 if (cursor.current != end_tag_position)
933 /* Check for duplicate HEADER tags before the END tag. */
934 if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
939 rc = tlv_advance(&cursor);
969 EFSYS_PROBE1(fail1, efx_rc_t, rc);
975 * Add or update a single TLV item in a host memory buffer containing a TLV
976 * formatted segment. Historically partitions consisted of only one segment.
978 __checkReturn efx_rc_t
979 hunt_nvram_buf_write_tlv(
980 __inout_bcount(max_seg_size) caddr_t seg_data,
981 __in size_t max_seg_size,
983 __in_bcount(tag_size) caddr_t tag_data,
984 __in size_t tag_size,
985 __out size_t *total_lengthp)
988 struct tlv_partition_header *header;
989 struct tlv_partition_trailer *trailer;
995 /* A PARTITION_HEADER tag must be the first item (at offset zero) */
996 if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
997 max_seg_size)) != 0) {
1001 if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1005 header = (struct tlv_partition_header *)tlv_item(&cursor);
1007 /* Update the TLV chain to contain the new data */
1008 if ((rc = tlv_find(&cursor, tag)) == 0) {
1009 /* Modify existing TLV item */
1010 if ((rc = tlv_modify(&cursor, tag,
1011 (uint8_t *)tag_data, tag_size)) != 0)
1014 /* Insert a new TLV item before the PARTITION_TRAILER */
1015 rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1020 if ((rc = tlv_insert(&cursor, tag,
1021 (uint8_t *)tag_data, tag_size)) != 0) {
1027 /* Find the trailer tag */
1028 if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1032 trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1034 /* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1035 *total_lengthp = tlv_block_length_used(&cursor);
1036 if (*total_lengthp > max_seg_size) {
1040 generation = __LE_TO_CPU_32(header->generation) + 1;
1042 header->total_length = __CPU_TO_LE_32(*total_lengthp);
1043 header->generation = __CPU_TO_LE_32(generation);
1044 trailer->generation = __CPU_TO_LE_32(generation);
1046 /* Recompute PARTITION_TRAILER checksum */
1047 trailer->checksum = 0;
1049 for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1050 cksum += *((uint32_t *)(seg_data + pos));
1052 trailer->checksum = ~cksum + 1;
1069 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1075 * Add or update a single TLV item in the first segment of a TLV formatted
1076 * dynamic config partition. The first segment is the current active
1079 __checkReturn efx_rc_t
1080 hunt_nvram_partn_write_tlv(
1081 __in efx_nic_t *enp,
1082 __in uint32_t partn,
1084 __in_bcount(size) caddr_t data,
1087 return hunt_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1092 * Read a segment from nvram at the given offset into a buffer (segment_data)
1093 * and optionally write a new tag to it.
1095 static __checkReturn efx_rc_t
1096 hunt_nvram_segment_write_tlv(
1097 __in efx_nic_t *enp,
1098 __in uint32_t partn,
1100 __in_bcount(size) caddr_t data,
1102 __inout caddr_t *seg_datap,
1103 __inout size_t *partn_offsetp,
1104 __inout size_t *src_remain_lenp,
1105 __inout size_t *dest_remain_lenp,
1106 __in boolean_t write)
1110 size_t original_segment_size;
1111 size_t modified_segment_size;
1114 * Read the segment from NVRAM into the segment_data buffer and validate
1115 * it, returning if it does not validate. This is not a failure unless
1116 * this is the first segment in a partition. In this case the caller
1117 * must propogate the error.
1119 status = hunt_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1120 *seg_datap, *src_remain_lenp);
1124 status = hunt_nvram_buf_segment_size(*seg_datap,
1125 *src_remain_lenp, &original_segment_size);
1130 /* Update the contents of the segment in the buffer */
1131 if ((rc = hunt_nvram_buf_write_tlv(*seg_datap,
1132 *dest_remain_lenp, tag, data, size,
1133 &modified_segment_size)) != 0)
1135 *dest_remain_lenp -= modified_segment_size;
1136 *seg_datap += modified_segment_size;
1139 * We won't modify this segment, but still need to update the
1140 * remaining lengths and pointers.
1142 *dest_remain_lenp -= original_segment_size;
1143 *seg_datap += original_segment_size;
1146 *partn_offsetp += original_segment_size;
1147 *src_remain_lenp -= original_segment_size;
1152 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1158 * Add or update a single TLV item in either the first segment or in all
1159 * segments in a TLV formatted dynamic config partition. Dynamic config
1160 * partitions on boards that support RFID are divided into a number of segments,
1161 * each formatted like a partition, with header, trailer and end tags. The first
1162 * segment is the current active configuration.
1164 * The segments are initialised by manftest and each contain a different
1165 * configuration e.g. firmware variant. The firmware can be instructed
1166 * via RFID to copy a segment to replace the first segment, hence changing the
1167 * active configuration. This allows ops to change the configuration of a board
1168 * prior to shipment using RFID.
1170 * Changes to the dynamic config may need to be written to all segments (e.g.
1171 * firmware versions) or just the first segment (changes to the active
1172 * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1173 * If only the first segment is written the code still needs to be aware of the
1174 * possible presence of subsequent segments as writing to a segment may cause
1175 * its size to increase, which would overwrite the subsequent segments and
1178 __checkReturn efx_rc_t
1179 hunt_nvram_partn_write_segment_tlv(
1180 __in efx_nic_t *enp,
1181 __in uint32_t partn,
1183 __in_bcount(size) caddr_t data,
1185 __in boolean_t all_segments)
1187 size_t partn_size = 0;
1189 size_t total_length = 0;
1191 size_t current_offset = 0;
1192 size_t remaining_original_length;
1193 size_t remaining_modified_length;
1194 caddr_t segment_data;
1196 EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1198 /* Allocate sufficient memory for the entire partition */
1199 if ((rc = hunt_nvram_partn_size(enp, partn, &partn_size)) != 0)
1202 EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1203 if (partn_data == NULL) {
1208 remaining_original_length = partn_size;
1209 remaining_modified_length = partn_size;
1210 segment_data = partn_data;
1212 /* Lock the partition */
1213 if ((rc = hunt_nvram_partn_lock(enp, partn)) != 0)
1216 /* Iterate over each (potential) segment to update it. */
1218 boolean_t write = all_segments || current_offset == 0;
1220 rc = hunt_nvram_segment_write_tlv(enp, partn, tag, data, size,
1221 &segment_data, ¤t_offset, &remaining_original_length,
1222 &remaining_modified_length, write);
1224 if (current_offset == 0) {
1226 * If no data has been read then the first
1227 * segment is invalid, which is an error.
1233 } while (current_offset < partn_size);
1235 total_length = segment_data - partn_data;
1238 * We've run out of space. This should actually be dealt with by
1239 * hunt_nvram_buf_write_tlv returning ENOSPC.
1241 if (total_length > partn_size) {
1246 /* Erase the whole partition in NVRAM */
1247 if ((rc = hunt_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1250 /* Write new partition contents from the buffer to NVRAM */
1251 if ((rc = hunt_nvram_partn_write(enp, partn, 0, partn_data,
1252 total_length)) != 0)
1255 /* Unlock the partition */
1256 hunt_nvram_partn_unlock(enp, partn);
1258 EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1271 hunt_nvram_partn_unlock(enp, partn);
1275 EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1279 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1285 * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1286 * not the data used by the segments in the partition.
1288 __checkReturn efx_rc_t
1289 hunt_nvram_partn_size(
1290 __in efx_nic_t *enp,
1291 __in unsigned int partn,
1292 __out size_t *sizep)
1296 if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1297 NULL, NULL, NULL)) != 0)
1303 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1308 __checkReturn efx_rc_t
1309 hunt_nvram_partn_lock(
1310 __in efx_nic_t *enp,
1311 __in unsigned int partn)
1315 if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1321 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1326 __checkReturn efx_rc_t
1327 hunt_nvram_partn_read(
1328 __in efx_nic_t *enp,
1329 __in unsigned int partn,
1330 __in unsigned int offset,
1331 __out_bcount(size) caddr_t data,
1338 chunk = MIN(size, HUNTINGTON_NVRAM_CHUNK);
1340 if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
1341 data, chunk)) != 0) {
1353 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1358 __checkReturn efx_rc_t
1359 hunt_nvram_partn_erase(
1360 __in efx_nic_t *enp,
1361 __in unsigned int partn,
1362 __in unsigned int offset,
1366 uint32_t erase_size;
1368 if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1369 &erase_size, NULL)) != 0)
1372 if (erase_size == 0) {
1373 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
1376 if (size % erase_size != 0) {
1381 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
1384 offset += erase_size;
1398 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1403 __checkReturn efx_rc_t
1404 hunt_nvram_partn_write(
1405 __in efx_nic_t *enp,
1406 __in unsigned int partn,
1407 __in unsigned int offset,
1408 __out_bcount(size) caddr_t data,
1412 uint32_t write_size;
1415 if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1416 NULL, &write_size)) != 0)
1419 if (write_size != 0) {
1421 * Check that the size is a multiple of the write chunk size if
1422 * the write chunk size is available.
1424 if (size % write_size != 0) {
1429 write_size = HUNTINGTON_NVRAM_CHUNK;
1433 chunk = MIN(size, write_size);
1435 if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
1436 data, chunk)) != 0) {
1452 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1458 hunt_nvram_partn_unlock(
1459 __in efx_nic_t *enp,
1460 __in unsigned int partn)
1466 if ((rc = efx_mcdi_nvram_update_finish(enp, partn, reboot)) != 0)
1472 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1475 __checkReturn efx_rc_t
1476 hunt_nvram_partn_set_version(
1477 __in efx_nic_t *enp,
1478 __in unsigned int partn,
1479 __in_ecount(4) uint16_t version[4])
1481 struct tlv_partition_version partn_version;
1485 /* Add or modify partition version TLV item */
1486 partn_version.version_w = __CPU_TO_LE_16(version[0]);
1487 partn_version.version_x = __CPU_TO_LE_16(version[1]);
1488 partn_version.version_y = __CPU_TO_LE_16(version[2]);
1489 partn_version.version_z = __CPU_TO_LE_16(version[3]);
1491 size = sizeof (partn_version) - (2 * sizeof (uint32_t));
1493 /* Write the version number to all segments in the partition */
1494 if ((rc = hunt_nvram_partn_write_segment_tlv(enp,
1495 NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
1496 TLV_TAG_PARTITION_VERSION(partn),
1497 (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
1503 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1508 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
1512 typedef struct hunt_parttbl_entry_s {
1515 efx_nvram_type_t nvtype;
1516 } hunt_parttbl_entry_t;
1518 /* Translate EFX NVRAM types to firmware partition types */
1519 static hunt_parttbl_entry_t hunt_parttbl[] = {
1520 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 1, EFX_NVRAM_MC_FIRMWARE},
1521 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 2, EFX_NVRAM_MC_FIRMWARE},
1522 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 3, EFX_NVRAM_MC_FIRMWARE},
1523 {NVRAM_PARTITION_TYPE_MC_FIRMWARE, 4, EFX_NVRAM_MC_FIRMWARE},
1524 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 1, EFX_NVRAM_MC_GOLDEN},
1525 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 2, EFX_NVRAM_MC_GOLDEN},
1526 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 3, EFX_NVRAM_MC_GOLDEN},
1527 {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP, 4, EFX_NVRAM_MC_GOLDEN},
1528 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 1, EFX_NVRAM_BOOTROM},
1529 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 2, EFX_NVRAM_BOOTROM},
1530 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 3, EFX_NVRAM_BOOTROM},
1531 {NVRAM_PARTITION_TYPE_EXPANSION_ROM, 4, EFX_NVRAM_BOOTROM},
1532 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
1533 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1, 2, EFX_NVRAM_BOOTROM_CFG},
1534 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2, 3, EFX_NVRAM_BOOTROM_CFG},
1535 {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 4, EFX_NVRAM_BOOTROM_CFG},
1536 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 1, EFX_NVRAM_DYNAMIC_CFG},
1537 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 2, EFX_NVRAM_DYNAMIC_CFG},
1538 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 3, EFX_NVRAM_DYNAMIC_CFG},
1539 {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG, 4, EFX_NVRAM_DYNAMIC_CFG},
1540 {NVRAM_PARTITION_TYPE_FPGA, 1, EFX_NVRAM_FPGA},
1541 {NVRAM_PARTITION_TYPE_FPGA, 2, EFX_NVRAM_FPGA},
1542 {NVRAM_PARTITION_TYPE_FPGA, 3, EFX_NVRAM_FPGA},
1543 {NVRAM_PARTITION_TYPE_FPGA, 4, EFX_NVRAM_FPGA},
1544 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 1, EFX_NVRAM_FPGA_BACKUP},
1545 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 2, EFX_NVRAM_FPGA_BACKUP},
1546 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 3, EFX_NVRAM_FPGA_BACKUP},
1547 {NVRAM_PARTITION_TYPE_FPGA_BACKUP, 4, EFX_NVRAM_FPGA_BACKUP}
1550 static __checkReturn hunt_parttbl_entry_t *
1552 __in efx_nic_t *enp,
1553 __in efx_nvram_type_t type)
1555 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1556 hunt_parttbl_entry_t *entry;
1559 EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
1561 for (i = 0; i < EFX_ARRAY_SIZE(hunt_parttbl); i++) {
1562 entry = &hunt_parttbl[i];
1564 if (entry->port == emip->emi_port && entry->nvtype == type)
1574 __checkReturn efx_rc_t
1576 __in efx_nic_t *enp)
1578 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1579 hunt_parttbl_entry_t *entry;
1580 unsigned int npartns = 0;
1581 uint32_t *partns = NULL;
1587 /* Find supported partitions */
1588 size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
1589 EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
1590 if (partns == NULL) {
1595 if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
1601 * Iterate over the list of supported partition types
1602 * applicable to *this* port
1604 for (i = 0; i < EFX_ARRAY_SIZE(hunt_parttbl); i++) {
1605 entry = &hunt_parttbl[i];
1607 if (entry->port != emip->emi_port)
1610 for (j = 0; j < npartns; j++) {
1611 if (entry->partn == partns[j]) {
1612 rc = efx_mcdi_nvram_test(enp, entry->partn);
1619 EFSYS_KMEM_FREE(enp->en_esip, size, partns);
1626 EFSYS_KMEM_FREE(enp->en_esip, size, partns);
1628 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1632 #endif /* EFSYS_OPT_DIAG */
1634 __checkReturn efx_rc_t
1636 __in efx_nic_t *enp,
1637 __in efx_nvram_type_t type,
1638 __out size_t *sizep)
1640 hunt_parttbl_entry_t *entry;
1644 if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1648 partn = entry->partn;
1650 if ((rc = hunt_nvram_partn_size(enp, partn, sizep)) != 0)
1658 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1665 __checkReturn efx_rc_t
1666 hunt_nvram_get_version(
1667 __in efx_nic_t *enp,
1668 __in efx_nvram_type_t type,
1669 __out uint32_t *subtypep,
1670 __out_ecount(4) uint16_t version[4])
1672 hunt_parttbl_entry_t *entry;
1676 if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1680 partn = entry->partn;
1682 /* FIXME: get highest partn version from all ports */
1683 /* FIXME: return partn description if available */
1685 if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
1686 version, NULL, 0)) != 0)
1694 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1699 __checkReturn efx_rc_t
1700 hunt_nvram_rw_start(
1701 __in efx_nic_t *enp,
1702 __in efx_nvram_type_t type,
1703 __out size_t *chunk_sizep)
1705 hunt_parttbl_entry_t *entry;
1709 if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1713 partn = entry->partn;
1715 if ((rc = hunt_nvram_partn_lock(enp, partn)) != 0)
1718 if (chunk_sizep != NULL)
1719 *chunk_sizep = HUNTINGTON_NVRAM_CHUNK;
1726 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1731 __checkReturn efx_rc_t
1732 hunt_nvram_read_chunk(
1733 __in efx_nic_t *enp,
1734 __in efx_nvram_type_t type,
1735 __in unsigned int offset,
1736 __out_bcount(size) caddr_t data,
1739 hunt_parttbl_entry_t *entry;
1742 if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1747 if ((rc = hunt_nvram_partn_read(enp, entry->partn,
1748 offset, data, size)) != 0)
1756 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1761 __checkReturn efx_rc_t
1763 __in efx_nic_t *enp,
1764 __in efx_nvram_type_t type)
1766 hunt_parttbl_entry_t *entry;
1770 if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1775 if ((rc = hunt_nvram_partn_size(enp, entry->partn, &size)) != 0)
1778 if ((rc = hunt_nvram_partn_erase(enp, entry->partn, 0, size)) != 0)
1788 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1793 __checkReturn efx_rc_t
1794 hunt_nvram_write_chunk(
1795 __in efx_nic_t *enp,
1796 __in efx_nvram_type_t type,
1797 __in unsigned int offset,
1798 __in_bcount(size) caddr_t data,
1801 hunt_parttbl_entry_t *entry;
1804 if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1809 if ((rc = hunt_nvram_partn_write(enp, entry->partn,
1810 offset, data, size)) != 0)
1818 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1824 hunt_nvram_rw_finish(
1825 __in efx_nic_t *enp,
1826 __in efx_nvram_type_t type)
1828 hunt_parttbl_entry_t *entry;
1830 if ((entry = hunt_parttbl_entry(enp, type)) != NULL)
1831 hunt_nvram_partn_unlock(enp, entry->partn);
1834 __checkReturn efx_rc_t
1835 hunt_nvram_set_version(
1836 __in efx_nic_t *enp,
1837 __in efx_nvram_type_t type,
1838 __in_ecount(4) uint16_t version[4])
1840 hunt_parttbl_entry_t *entry;
1844 if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1848 partn = entry->partn;
1850 if ((rc = hunt_nvram_partn_set_version(enp, partn, version)) != 0)
1859 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1864 #endif /* EFSYS_OPT_NVRAM */
1866 #endif /* EFSYS_OPT_HUNTINGTON */