Page MenuHomeFreeBSD

D4302.diff
No OneTemporary

D4302.diff

Index: head/sys/dev/sfxge/common/hunt_impl.h
===================================================================
--- head/sys/dev/sfxge/common/hunt_impl.h
+++ head/sys/dev/sfxge/common/hunt_impl.h
@@ -295,8 +295,8 @@
extern __checkReturn int
hunt_nvram_buf_read_tlv(
__in efx_nic_t *enp,
- __in_bcount(partn_size) caddr_t partn_data,
- __in size_t partn_size,
+ __in_bcount(max_seg_size) caddr_t seg_data,
+ __in size_t max_seg_size,
__in uint32_t tag,
__deref_out_bcount_opt(*sizep) caddr_t *datap,
__out size_t *sizep);
@@ -327,6 +327,15 @@
__in size_t size);
extern __checkReturn int
+hunt_nvram_partn_write_segment_tlv(
+ __in efx_nic_t *enp,
+ __in uint32_t partn,
+ __in uint32_t tag,
+ __in_bcount(size) caddr_t data,
+ __in size_t size,
+ __in boolean_t all_segments);
+
+extern __checkReturn int
hunt_nvram_partn_size(
__in efx_nic_t *enp,
__in unsigned int partn,
Index: head/sys/dev/sfxge/common/hunt_nvram.c
===================================================================
--- head/sys/dev/sfxge/common/hunt_nvram.c
+++ head/sys/dev/sfxge/common/hunt_nvram.c
@@ -571,13 +571,19 @@
return (rc);
}
-/* Read and validate an entire TLV formatted partition */
-static __checkReturn int
-hunt_nvram_read_tlv_partition(
- __in efx_nic_t *enp,
- __in uint32_t partn,
- __in_bcount(partn_size) caddr_t partn_data,
- __in size_t partn_size)
+/*
+ * Read and validate a segment from a partition. A segment is a complete
+ * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
+ * be multiple segments in a partition, so seg_offset allows segments
+ * beyond the first to be read.
+ */
+static __checkReturn int
+hunt_nvram_read_tlv_segment(
+ __in efx_nic_t *enp,
+ __in uint32_t partn,
+ __in size_t seg_offset,
+ __in_bcount(max_seg_size) caddr_t seg_data,
+ __in size_t max_seg_size)
{
tlv_cursor_t cursor;
struct tlv_partition_header *header;
@@ -589,20 +595,20 @@
EFX_STATIC_ASSERT(sizeof (*header) <= HUNTINGTON_NVRAM_CHUNK);
- if ((partn_data == NULL) || (partn_size == 0)) {
+ if ((seg_data == NULL) || (max_seg_size == 0)) {
rc = EINVAL;
goto fail1;
}
- /* Read initial chunk of partition */
- if ((rc = hunt_nvram_partn_read(enp, partn, 0, partn_data,
+ /* Read initial chunk of the segment, starting at offset */
+ if ((rc = hunt_nvram_partn_read(enp, partn, seg_offset, seg_data,
HUNTINGTON_NVRAM_CHUNK)) != 0) {
goto fail2;
}
- /* The partition header must be the first item (at offset zero) */
- if ((rc = tlv_init_cursor_from_size(&cursor, partn_data,
- partn_size)) != 0) {
+ /* A PARTITION_HEADER tag must be the first item at the given offset */
+ if ((rc = tlv_init_cursor_from_size(&cursor, seg_data,
+ max_seg_size)) != 0) {
rc = EFAULT;
goto fail3;
}
@@ -612,23 +618,23 @@
}
header = (struct tlv_partition_header *)tlv_item(&cursor);
- /* Check TLV partition length (includes the END tag) */
+ /* Check TLV segment length (includes the END tag) */
total_length = __LE_TO_CPU_32(header->total_length);
- if (total_length > partn_size) {
+ if (total_length > max_seg_size) {
rc = EFBIG;
goto fail5;
}
- /* Read the remaining partition content */
+ /* Read the remaining segment content */
if (total_length > HUNTINGTON_NVRAM_CHUNK) {
if ((rc = hunt_nvram_partn_read(enp, partn,
- HUNTINGTON_NVRAM_CHUNK,
- partn_data + HUNTINGTON_NVRAM_CHUNK,
+ seg_offset + HUNTINGTON_NVRAM_CHUNK,
+ seg_data + HUNTINGTON_NVRAM_CHUNK,
total_length - HUNTINGTON_NVRAM_CHUNK)) != 0)
goto fail6;
}
- /* Check partition ends with PARTITION_TRAILER and END tags */
+ /* Check segment ends with PARTITION_TRAILER and END tags */
if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
rc = EINVAL;
goto fail7;
@@ -644,7 +650,7 @@
goto fail9;
}
- /* Check data read from partition is consistent */
+ /* Check data read from segment is consistent */
if (trailer->generation != header->generation) {
/*
* The partition data may have been modified between successive
@@ -656,10 +662,10 @@
goto fail10;
}
- /* Verify partition checksum */
+ /* Verify segment checksum */
cksum = 0;
for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
- cksum += *((uint32_t *)(partn_data + pos));
+ cksum += *((uint32_t *)(seg_data + pos));
}
if (cksum != 0) {
rc = EINVAL;
@@ -696,13 +702,13 @@
/*
* Read a single TLV item from a host memory
- * buffer containing a TLV formatted partition.
+ * buffer containing a TLV formatted segment.
*/
__checkReturn int
hunt_nvram_buf_read_tlv(
__in efx_nic_t *enp,
- __in_bcount(partn_size) caddr_t partn_data,
- __in size_t partn_size,
+ __in_bcount(max_seg_size) caddr_t seg_data,
+ __in size_t max_seg_size,
__in uint32_t tag,
__deref_out_bcount_opt(*sizep) caddr_t *datap,
__out size_t *sizep)
@@ -713,14 +719,14 @@
caddr_t value;
int rc;
- if ((partn_data == NULL) || (partn_size == 0)) {
+ if ((seg_data == NULL) || (max_seg_size == 0)) {
rc = EINVAL;
goto fail1;
}
- /* Find requested TLV tag in partition data */
- if ((rc = tlv_init_cursor_from_size(&cursor, partn_data,
- partn_size)) != 0) {
+ /* Find requested TLV tag in segment data */
+ if ((rc = tlv_init_cursor_from_size(&cursor, seg_data,
+ max_seg_size)) != 0) {
rc = EFAULT;
goto fail2;
}
@@ -760,18 +766,16 @@
return (rc);
}
-
-
-/* Read a single TLV item from a TLV formatted partition */
+/* Read a single TLV item from the first segment in a TLV formatted partition */
__checkReturn int
hunt_nvram_partn_read_tlv(
- __in efx_nic_t *enp,
- __in uint32_t partn,
- __in uint32_t tag,
- __deref_out_bcount_opt(*sizep) caddr_t *datap,
- __out size_t *sizep)
+ __in efx_nic_t *enp,
+ __in uint32_t partn,
+ __in uint32_t tag,
+ __deref_out_bcount_opt(*seg_sizep) caddr_t *seg_datap,
+ __out size_t *seg_sizep)
{
- caddr_t partn_data = NULL;
+ caddr_t seg_data = NULL;
size_t partn_size = 0;
size_t length;
caddr_t data;
@@ -787,39 +791,39 @@
goto fail2;
}
- EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
- if (partn_data == NULL) {
+ EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
+ if (seg_data == NULL) {
rc = ENOMEM;
goto fail3;
}
/*
- * Read the entire TLV partition. Retry until consistent partition
- * contents are returned. Inconsistent data may be read if:
- * a) the partition contents are invalid
+ * Read the first segment in a TLV partition. Retry until consistent
+ * segment contents are returned. Inconsistent data may be read if:
+ * a) the segment contents are invalid
* b) the MC has rebooted while we were reading the partition
* c) the partition has been modified while we were reading it
* Limit retry attempts to ensure forward progress.
*/
retry = 10;
do {
- rc = hunt_nvram_read_tlv_partition(enp, partn,
- partn_data, partn_size);
+ rc = hunt_nvram_read_tlv_segment(enp, partn, 0,
+ seg_data, partn_size);
} while ((rc == EAGAIN) && (--retry > 0));
if (rc != 0) {
- /* Failed to obtain consistent partition data */
+ /* Failed to obtain consistent segment data */
goto fail4;
}
- if ((rc = hunt_nvram_buf_read_tlv(enp, partn_data, partn_size,
+ if ((rc = hunt_nvram_buf_read_tlv(enp, seg_data, partn_size,
tag, &data, &length)) != 0)
goto fail5;
- EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
+ EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
- *datap = data;
- *sizep = length;
+ *seg_datap = data;
+ *seg_sizep = length;
return (0);
@@ -828,7 +832,137 @@
fail4:
EFSYS_PROBE(fail4);
- EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
+ EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
+fail3:
+ EFSYS_PROBE(fail3);
+fail2:
+ EFSYS_PROBE(fail2);
+fail1:
+ EFSYS_PROBE1(fail1, int, rc);
+
+ return (rc);
+}
+
+/* Compute the size of a segment. */
+ static __checkReturn int
+hunt_nvram_buf_segment_size(
+ __in caddr_t seg_data,
+ __in size_t max_seg_size,
+ __out size_t *seg_sizep)
+{
+ int rc;
+ tlv_cursor_t cursor;
+ struct tlv_partition_header *header;
+ struct tlv_partition_trailer *trailer;
+ uint32_t cksum;
+ int pos;
+ uint32_t *end_tag_position;
+ uint32_t segment_length;
+
+ /* A PARTITION_HEADER tag must be the first item at the given offset */
+ if ((rc = tlv_init_cursor_from_size(&cursor, seg_data,
+ max_seg_size)) != 0) {
+ rc = EFAULT;
+ goto fail1;
+ }
+ if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
+ rc = EINVAL;
+ goto fail2;
+ }
+ header = (struct tlv_partition_header *)tlv_item(&cursor);
+
+ /* Check TLV segment length (includes the END tag) */
+ *seg_sizep = __LE_TO_CPU_32(header->total_length);
+ if (*seg_sizep > max_seg_size) {
+ rc = EFBIG;
+ goto fail3;
+ }
+
+ /* Check segment ends with PARTITION_TRAILER and END tags */
+ if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
+ rc = EINVAL;
+ goto fail4;
+ }
+ trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
+
+ if ((rc = tlv_advance(&cursor)) != 0) {
+ rc = EINVAL;
+ goto fail5;
+ }
+ if (tlv_tag(&cursor) != TLV_TAG_END) {
+ rc = EINVAL;
+ goto fail6;
+ }
+ end_tag_position = cursor.current;
+
+ /* Verify segment checksum */
+ cksum = 0;
+ for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
+ cksum += *((uint32_t *)(seg_data + pos));
+ }
+ if (cksum != 0) {
+ rc = EINVAL;
+ goto fail7;
+ }
+
+ /*
+ * Calculate total length from HEADER to END tags and compare to
+ * max_seg_size and the total_length field in the HEADER tag.
+ */
+ segment_length = tlv_block_length_used(&cursor);
+
+ if (segment_length > max_seg_size) {
+ rc = EINVAL;
+ goto fail8;
+ }
+
+ if (segment_length != *seg_sizep) {
+ rc = EINVAL;
+ goto fail9;
+ }
+
+ /* Skip over the first HEADER tag. */
+ rc = tlv_rewind(&cursor);
+ rc = tlv_advance(&cursor);
+
+ while (rc == 0) {
+ if (tlv_tag(&cursor) == TLV_TAG_END) {
+ /* Check that the END tag is the one found earlier. */
+ if (cursor.current != end_tag_position)
+ goto fail10;
+ break;
+ }
+ /* Check for duplicate HEADER tags before the END tag. */
+ if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
+ rc = EINVAL;
+ goto fail11;
+ }
+
+ rc = tlv_advance(&cursor);
+ }
+ if (rc != 0)
+ goto fail12;
+
+ return (0);
+
+fail12:
+ EFSYS_PROBE(fail12);
+fail11:
+ EFSYS_PROBE(fail11);
+fail10:
+ EFSYS_PROBE(fail10);
+fail9:
+ EFSYS_PROBE(fail9);
+fail8:
+ EFSYS_PROBE(fail8);
+fail7:
+ EFSYS_PROBE(fail7);
+fail6:
+ EFSYS_PROBE(fail6);
+fail5:
+ EFSYS_PROBE(fail5);
+fail4:
+ EFSYS_PROBE(fail4);
fail3:
EFSYS_PROBE(fail3);
fail2:
@@ -841,12 +975,12 @@
/*
* Add or update a single TLV item in a host memory buffer containing a TLV
- * formatted partition.
+ * formatted segment. Historically partitions consisted of only one segment.
*/
- __checkReturn int
+ __checkReturn int
hunt_nvram_buf_write_tlv(
- __inout_bcount(partn_size) caddr_t partn_data,
- __in size_t partn_size,
+ __inout_bcount(max_seg_size) caddr_t seg_data,
+ __in size_t max_seg_size,
__in uint32_t tag,
__in_bcount(tag_size) caddr_t tag_data,
__in size_t tag_size,
@@ -860,9 +994,9 @@
int pos;
int rc;
- /* The partition header must be the first item (at offset zero) */
- if ((rc = tlv_init_cursor_from_size(&cursor, partn_data,
- partn_size)) != 0) {
+ /* A PARTITION_HEADER tag must be the first item (at offset zero) */
+ if ((rc = tlv_init_cursor_from_size(&cursor, seg_data,
+ max_seg_size)) != 0) {
rc = EFAULT;
goto fail1;
}
@@ -901,7 +1035,10 @@
/* Update PARTITION_HEADER and PARTITION_TRAILER fields */
*total_lengthp = tlv_block_length_used(&cursor);
- EFSYS_ASSERT3U(*total_lengthp, <=, partn_size);
+ if (*total_lengthp > max_seg_size) {
+ rc = ENOSPC;
+ goto fail7;
+ }
generation = __LE_TO_CPU_32(header->generation) + 1;
header->total_length = __CPU_TO_LE_32(*total_lengthp);
@@ -912,12 +1049,14 @@
trailer->checksum = 0;
cksum = 0;
for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
- cksum += *((uint32_t *)(partn_data + pos));
+ cksum += *((uint32_t *)(seg_data + pos));
}
trailer->checksum = ~cksum + 1;
return (0);
+fail7:
+ EFSYS_PROBE(fail7);
fail6:
EFSYS_PROBE(fail6);
fail5:
@@ -934,7 +1073,11 @@
return (rc);
}
-/* Add or update a single TLV item in a TLV formatted partition */
+/*
+ * Add or update a single TLV item in the first segment of a TLV formatted
+ * dynamic config partition. The first segment is the current active
+ * configuration.
+ */
__checkReturn int
hunt_nvram_partn_write_tlv(
__in efx_nic_t *enp,
@@ -943,10 +1086,114 @@
__in_bcount(size) caddr_t data,
__in size_t size)
{
- size_t partn_size;
+ return hunt_nvram_partn_write_segment_tlv(enp, partn, tag, data,
+ size, B_FALSE);
+}
+
+/*
+ * Read a segment from nvram at the given offset into a buffer (segment_data)
+ * and optionally write a new tag to it.
+ */
+ static __checkReturn int
+hunt_nvram_segment_write_tlv(
+ __in efx_nic_t *enp,
+ __in uint32_t partn,
+ __in uint32_t tag,
+ __in_bcount(size) caddr_t data,
+ __in size_t size,
+ __inout caddr_t *seg_datap,
+ __inout size_t *partn_offsetp,
+ __inout size_t *src_remain_lenp,
+ __inout size_t *dest_remain_lenp,
+ __in boolean_t write)
+{
+ int rc;
+ int status;
+ size_t original_segment_size;
+ size_t modified_segment_size;
+
+ /*
+ * Read the segment from NVRAM into the segment_data buffer and validate
+ * it, returning if it does not validate. This is not a failure unless
+ * this is the first segment in a partition. In this case the caller
+ * must propogate the error.
+ */
+ status = hunt_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
+ *seg_datap, *src_remain_lenp);
+ if (status != 0)
+ return (EINVAL);
+
+ status = hunt_nvram_buf_segment_size(*seg_datap,
+ *src_remain_lenp, &original_segment_size);
+ if (status != 0)
+ return (EINVAL);
+
+ if (write) {
+ /* Update the contents of the segment in the buffer */
+ if ((rc = hunt_nvram_buf_write_tlv(*seg_datap,
+ *dest_remain_lenp, tag, data, size,
+ &modified_segment_size)) != 0)
+ goto fail1;
+ *dest_remain_lenp -= modified_segment_size;
+ *seg_datap += modified_segment_size;
+ } else {
+ /*
+ * We won't modify this segment, but still need to update the
+ * remaining lengths and pointers.
+ */
+ *dest_remain_lenp -= original_segment_size;
+ *seg_datap += original_segment_size;
+ }
+
+ *partn_offsetp += original_segment_size;
+ *src_remain_lenp -= original_segment_size;
+
+ return (0);
+
+fail1:
+ EFSYS_PROBE1(fail1, int, rc);
+
+ return (rc);
+}
+
+/*
+ * Add or update a single TLV item in either the first segment or in all
+ * segments in a TLV formatted dynamic config partition. Dynamic config
+ * partitions on boards that support RFID are divided into a number of segments,
+ * each formatted like a partition, with header, trailer and end tags. The first
+ * segment is the current active configuration.
+ *
+ * The segments are initialised by manftest and each contain a different
+ * configuration e.g. firmware variant. The firmware can be instructed
+ * via RFID to copy a segment to replace the first segment, hence changing the
+ * active configuration. This allows ops to change the configuration of a board
+ * prior to shipment using RFID.
+ *
+ * Changes to the dynamic config may need to be written to all segments (e.g.
+ * firmware versions) or just the first segment (changes to the active
+ * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
+ * If only the first segment is written the code still needs to be aware of the
+ * possible presence of subsequent segments as writing to a segment may cause
+ * its size to increase, which would overwrite the subsequent segments and
+ * invalidate them.
+ */
+ __checkReturn int
+hunt_nvram_partn_write_segment_tlv(
+ __in efx_nic_t *enp,
+ __in uint32_t partn,
+ __in uint32_t tag,
+ __in_bcount(size) caddr_t data,
+ __in size_t size,
+ __in boolean_t all_segments)
+{
+ size_t partn_size = 0;
caddr_t partn_data;
- size_t total_length;
+ size_t total_length = 0;
int rc;
+ size_t current_offset = 0;
+ size_t remaining_original_length;
+ size_t remaining_modified_length;
+ caddr_t segment_data;
EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
@@ -960,27 +1207,49 @@
goto fail2;
}
+ remaining_original_length = partn_size;
+ remaining_modified_length = partn_size;
+ segment_data = partn_data;
+
/* Lock the partition */
if ((rc = hunt_nvram_partn_lock(enp, partn)) != 0)
goto fail3;
- /* Read the partition contents (no need to retry when locked). */
- if ((rc = hunt_nvram_read_tlv_partition(enp, partn,
- partn_data, partn_size)) != 0) {
- /* Failed to obtain consistent partition data */
- goto fail4;
- }
+ /* Iterate over each (potential) segment to update it. */
+ do {
+ boolean_t write = all_segments || current_offset == 0;
+
+ rc = hunt_nvram_segment_write_tlv(enp, partn, tag, data, size,
+ &segment_data, &current_offset, &remaining_original_length,
+ &remaining_modified_length, write);
+ if (rc != 0) {
+ if (current_offset == 0) {
+ /*
+ * If no data has been read then the first
+ * segment is invalid, which is an error.
+ */
+ goto fail4;
+ }
+ break;
+ }
+ } while (current_offset < partn_size);
+
+ total_length = segment_data - partn_data;
- /* Update the contents in memory */
- if ((rc = hunt_nvram_buf_write_tlv(partn_data, partn_size,
- tag, data, size, &total_length)) != 0)
+ /*
+ * We've run out of space. This should actually be dealt with by
+ * hunt_nvram_buf_write_tlv returning ENOSPC.
+ */
+ if (total_length > partn_size) {
+ rc = ENOSPC;
goto fail5;
+ }
- /* Erase the whole partition */
+ /* Erase the whole partition in NVRAM */
if ((rc = hunt_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
goto fail6;
- /* Write new partition contents to NVRAM */
+ /* Write new partition contents from the buffer to NVRAM */
if ((rc = hunt_nvram_partn_write(enp, partn, 0, partn_data,
total_length)) != 0)
goto fail7;
@@ -1014,6 +1283,10 @@
return (rc);
}
+/*
+ * Get the size of a NVRAM partition. This is the total size allocated in nvram,
+ * not the data used by the segments in the partition.
+ */
__checkReturn int
hunt_nvram_partn_size(
__in efx_nic_t *enp,
@@ -1171,10 +1444,11 @@
size = sizeof (partn_version) - (2 * sizeof (uint32_t));
- if ((rc = hunt_nvram_partn_write_tlv(enp,
+ /* Write the version number to all segments in the partition */
+ if ((rc = hunt_nvram_partn_write_segment_tlv(enp,
NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
TLV_TAG_PARTITION_VERSION(partn),
- (caddr_t)&partn_version.version_w, size)) != 0)
+ (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
goto fail1;
return (0);
Index: head/sys/dev/sfxge/common/hunt_vpd.c
===================================================================
--- head/sys/dev/sfxge/common/hunt_vpd.c
+++ head/sys/dev/sfxge/common/hunt_vpd.c
@@ -396,11 +396,11 @@
if ((rc = efx_vpd_hunk_length(data, size, &vpd_length)) != 0)
goto fail1;
- /* Store new dynamic VPD in DYNAMIC_CONFIG partition */
- if ((rc = hunt_nvram_partn_write_tlv(enp,
+ /* Store new dynamic VPD in all segments in DYNAMIC_CONFIG partition */
+ if ((rc = hunt_nvram_partn_write_segment_tlv(enp,
NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
TLV_TAG_PF_DYNAMIC_VPD(pci_pf),
- data, vpd_length)) != 0) {
+ data, vpd_length, B_TRUE)) != 0) {
goto fail2;
}

File Metadata

Mime Type
text/plain
Expires
Mon, Apr 13, 8:33 AM (16 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31402147
Default Alt Text
D4302.diff (19 KB)

Event Timeline