sfxge(4): add TLV item manipulation functions to common code
Add creation, deletion and checksumming operations to the private copy of TLV functions in the common code. Functions added in preparation for V3 licensing support, as licensing keys are stored in the TLV format. Missing support for multiple segment partitions added. Annotations for Windows code analysis also updated. Submitted by: Richard Houldsworth <rhouldsworth at solarflare.com> Sponsored by: Solarflare Communications, Inc. MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D6264
This commit is contained in:
parent
9751d26325
commit
bd836a6e8e
@ -48,14 +48,34 @@ typedef struct tlv_cursor_s {
|
|||||||
uint32_t *limit; /* Last dword of data block */
|
uint32_t *limit; /* Last dword of data block */
|
||||||
} tlv_cursor_t;
|
} tlv_cursor_t;
|
||||||
|
|
||||||
|
typedef struct nvram_partition_s {
|
||||||
|
uint16_t type;
|
||||||
|
uint8_t chip_select;
|
||||||
|
uint8_t flags;
|
||||||
|
/*
|
||||||
|
* The full length of the NVRAM partition.
|
||||||
|
* This is different from tlv_partition_header.total_length,
|
||||||
|
* which can be smaller.
|
||||||
|
*/
|
||||||
|
uint32_t length;
|
||||||
|
uint32_t erase_size;
|
||||||
|
uint32_t *data;
|
||||||
|
tlv_cursor_t tlv_cursor;
|
||||||
|
} nvram_partition_t;
|
||||||
|
|
||||||
|
|
||||||
static __checkReturn efx_rc_t
|
static __checkReturn efx_rc_t
|
||||||
tlv_validate_state(
|
tlv_validate_state(
|
||||||
__in tlv_cursor_t *cursor);
|
__inout tlv_cursor_t *cursor);
|
||||||
|
|
||||||
|
|
||||||
/*
|
static void
|
||||||
* Operations on TLV formatted partition data.
|
tlv_init_block(
|
||||||
*/
|
__out uint32_t *block)
|
||||||
|
{
|
||||||
|
*block = __CPU_TO_LE_32(TLV_TAG_END);
|
||||||
|
}
|
||||||
|
|
||||||
static uint32_t
|
static uint32_t
|
||||||
tlv_tag(
|
tlv_tag(
|
||||||
__in tlv_cursor_t *cursor)
|
__in tlv_cursor_t *cursor)
|
||||||
@ -122,9 +142,9 @@ tlv_next_item_ptr(
|
|||||||
return (cursor->current + TLV_DWORD_COUNT(length));
|
return (cursor->current + TLV_DWORD_COUNT(length));
|
||||||
}
|
}
|
||||||
|
|
||||||
static efx_rc_t
|
static __checkReturn efx_rc_t
|
||||||
tlv_advance(
|
tlv_advance(
|
||||||
__in tlv_cursor_t *cursor)
|
__inout tlv_cursor_t *cursor)
|
||||||
{
|
{
|
||||||
efx_rc_t rc;
|
efx_rc_t rc;
|
||||||
|
|
||||||
@ -177,7 +197,7 @@ tlv_rewind(
|
|||||||
|
|
||||||
static efx_rc_t
|
static efx_rc_t
|
||||||
tlv_find(
|
tlv_find(
|
||||||
__in tlv_cursor_t *cursor,
|
__inout tlv_cursor_t *cursor,
|
||||||
__in uint32_t tag)
|
__in uint32_t tag)
|
||||||
{
|
{
|
||||||
efx_rc_t rc;
|
efx_rc_t rc;
|
||||||
@ -194,7 +214,7 @@ tlv_find(
|
|||||||
|
|
||||||
static __checkReturn efx_rc_t
|
static __checkReturn efx_rc_t
|
||||||
tlv_validate_state(
|
tlv_validate_state(
|
||||||
__in tlv_cursor_t *cursor)
|
__inout tlv_cursor_t *cursor)
|
||||||
{
|
{
|
||||||
efx_rc_t rc;
|
efx_rc_t rc;
|
||||||
|
|
||||||
@ -242,31 +262,49 @@ static efx_rc_t
|
|||||||
tlv_init_cursor(
|
tlv_init_cursor(
|
||||||
__out tlv_cursor_t *cursor,
|
__out tlv_cursor_t *cursor,
|
||||||
__in uint32_t *block,
|
__in uint32_t *block,
|
||||||
__in uint32_t *limit)
|
__in uint32_t *limit,
|
||||||
|
__in uint32_t *current)
|
||||||
{
|
{
|
||||||
cursor->block = block;
|
cursor->block = block;
|
||||||
cursor->limit = limit;
|
cursor->limit = limit;
|
||||||
|
|
||||||
cursor->current = cursor->block;
|
cursor->current = current;
|
||||||
cursor->end = NULL;
|
cursor->end = NULL;
|
||||||
|
|
||||||
return (tlv_validate_state(cursor));
|
return (tlv_validate_state(cursor));
|
||||||
}
|
}
|
||||||
|
|
||||||
static efx_rc_t
|
static __checkReturn efx_rc_t
|
||||||
tlv_init_cursor_from_size(
|
tlv_init_cursor_from_size(
|
||||||
__out tlv_cursor_t *cursor,
|
__out tlv_cursor_t *cursor,
|
||||||
__in uint8_t *block,
|
__in_bcount(size)
|
||||||
|
uint8_t *block,
|
||||||
__in size_t size)
|
__in size_t size)
|
||||||
{
|
{
|
||||||
uint32_t *limit;
|
uint32_t *limit;
|
||||||
limit = (uint32_t *)(block + size - sizeof (uint32_t));
|
limit = (uint32_t *)(block + size - sizeof (uint32_t));
|
||||||
return (tlv_init_cursor(cursor, (uint32_t *)block, limit));
|
return (tlv_init_cursor(cursor, (uint32_t *)block,
|
||||||
|
limit, (uint32_t *)block));
|
||||||
}
|
}
|
||||||
|
|
||||||
static efx_rc_t
|
static __checkReturn efx_rc_t
|
||||||
|
tlv_init_cursor_at_offset(
|
||||||
|
__out tlv_cursor_t *cursor,
|
||||||
|
__in_bcount(size)
|
||||||
|
uint8_t *block,
|
||||||
|
__in size_t size,
|
||||||
|
__in size_t offset)
|
||||||
|
{
|
||||||
|
uint32_t *limit;
|
||||||
|
uint32_t *current;
|
||||||
|
limit = (uint32_t *)(block + size - sizeof (uint32_t));
|
||||||
|
current = (uint32_t *)(block + offset);
|
||||||
|
return (tlv_init_cursor(cursor, (uint32_t *)block, limit, current));
|
||||||
|
}
|
||||||
|
|
||||||
|
static __checkReturn efx_rc_t
|
||||||
tlv_require_end(
|
tlv_require_end(
|
||||||
__in tlv_cursor_t *cursor)
|
__inout tlv_cursor_t *cursor)
|
||||||
{
|
{
|
||||||
uint32_t *pos;
|
uint32_t *pos;
|
||||||
efx_rc_t rc;
|
efx_rc_t rc;
|
||||||
@ -290,7 +328,7 @@ tlv_require_end(
|
|||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
tlv_block_length_used(
|
tlv_block_length_used(
|
||||||
__in tlv_cursor_t *cursor)
|
__inout tlv_cursor_t *cursor)
|
||||||
{
|
{
|
||||||
efx_rc_t rc;
|
efx_rc_t rc;
|
||||||
|
|
||||||
@ -311,8 +349,34 @@ tlv_block_length_used(
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t *
|
||||||
|
tlv_last_segment_end(
|
||||||
|
__in tlv_cursor_t *cursor)
|
||||||
|
{
|
||||||
|
tlv_cursor_t segment_cursor;
|
||||||
|
uint32_t *last_segment_end = cursor->block;
|
||||||
|
uint32_t *segment_start = cursor->block;
|
||||||
|
|
||||||
static __checkReturn uint32_t *
|
/*
|
||||||
|
* Go through each segment and check that it has an end tag. If there
|
||||||
|
* is no end tag then the previous segment was the last valid one,
|
||||||
|
* so return the pointer to its end tag.
|
||||||
|
*/
|
||||||
|
while (1) {
|
||||||
|
if (tlv_init_cursor(&segment_cursor, segment_start,
|
||||||
|
cursor->limit, segment_start) != 0)
|
||||||
|
break;
|
||||||
|
if (tlv_require_end(&segment_cursor) != 0)
|
||||||
|
break;
|
||||||
|
last_segment_end = segment_cursor.end;
|
||||||
|
segment_start = segment_cursor.end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (last_segment_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint32_t *
|
||||||
tlv_write(
|
tlv_write(
|
||||||
__in tlv_cursor_t *cursor,
|
__in tlv_cursor_t *cursor,
|
||||||
__in uint32_t tag,
|
__in uint32_t tag,
|
||||||
@ -338,12 +402,14 @@ tlv_write(
|
|||||||
|
|
||||||
static __checkReturn efx_rc_t
|
static __checkReturn efx_rc_t
|
||||||
tlv_insert(
|
tlv_insert(
|
||||||
__in tlv_cursor_t *cursor,
|
__inout tlv_cursor_t *cursor,
|
||||||
__in uint32_t tag,
|
__in uint32_t tag,
|
||||||
__in uint8_t *data,
|
__in_bcount(size)
|
||||||
|
uint8_t *data,
|
||||||
__in size_t size)
|
__in size_t size)
|
||||||
{
|
{
|
||||||
unsigned int delta;
|
unsigned int delta;
|
||||||
|
uint32_t *last_segment_end;
|
||||||
efx_rc_t rc;
|
efx_rc_t rc;
|
||||||
|
|
||||||
if ((rc = tlv_validate_state(cursor)) != 0)
|
if ((rc = tlv_validate_state(cursor)) != 0)
|
||||||
@ -357,15 +423,17 @@ tlv_insert(
|
|||||||
goto fail3;
|
goto fail3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
last_segment_end = tlv_last_segment_end(cursor);
|
||||||
|
|
||||||
delta = TLV_DWORD_COUNT(size);
|
delta = TLV_DWORD_COUNT(size);
|
||||||
if (cursor->end + 1 + delta > cursor->limit) {
|
if (last_segment_end + 1 + delta > cursor->limit) {
|
||||||
rc = ENOSPC;
|
rc = ENOSPC;
|
||||||
goto fail4;
|
goto fail4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Move data up: new space at cursor->current */
|
/* Move data up: new space at cursor->current */
|
||||||
memmove(cursor->current + delta, cursor->current,
|
memmove(cursor->current + delta, cursor->current,
|
||||||
(cursor->end + 1 - cursor->current) * sizeof (uint32_t));
|
(last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
|
||||||
|
|
||||||
/* Adjust the end pointer */
|
/* Adjust the end pointer */
|
||||||
cursor->end += delta;
|
cursor->end += delta;
|
||||||
@ -387,17 +455,62 @@ tlv_insert(
|
|||||||
return (rc);
|
return (rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __checkReturn efx_rc_t
|
||||||
|
tlv_delete(
|
||||||
|
__inout tlv_cursor_t *cursor)
|
||||||
|
{
|
||||||
|
unsigned int delta;
|
||||||
|
uint32_t *last_segment_end;
|
||||||
|
efx_rc_t rc;
|
||||||
|
|
||||||
|
if ((rc = tlv_validate_state(cursor)) != 0)
|
||||||
|
goto fail1;
|
||||||
|
|
||||||
|
if (tlv_tag(cursor) == TLV_TAG_END) {
|
||||||
|
rc = EINVAL;
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta = TLV_DWORD_COUNT(tlv_length(cursor));
|
||||||
|
|
||||||
|
if ((rc = tlv_require_end(cursor)) != 0)
|
||||||
|
goto fail3;
|
||||||
|
|
||||||
|
last_segment_end = tlv_last_segment_end(cursor);
|
||||||
|
|
||||||
|
/* Shuffle things down, destroying the item at cursor->current */
|
||||||
|
memmove(cursor->current, cursor->current + delta,
|
||||||
|
(last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
|
||||||
|
/* Zero the new space at the end of the TLV chain */
|
||||||
|
memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t));
|
||||||
|
/* Adjust the end pointer */
|
||||||
|
cursor->end -= delta;
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
fail3:
|
||||||
|
EFSYS_PROBE(fail3);
|
||||||
|
fail2:
|
||||||
|
EFSYS_PROBE(fail2);
|
||||||
|
fail1:
|
||||||
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
||||||
|
|
||||||
|
return (rc);
|
||||||
|
}
|
||||||
|
|
||||||
static __checkReturn efx_rc_t
|
static __checkReturn efx_rc_t
|
||||||
tlv_modify(
|
tlv_modify(
|
||||||
__in tlv_cursor_t *cursor,
|
__inout tlv_cursor_t *cursor,
|
||||||
__in uint32_t tag,
|
__in uint32_t tag,
|
||||||
__in uint8_t *data,
|
__in_bcount(size)
|
||||||
|
uint8_t *data,
|
||||||
__in size_t size)
|
__in size_t size)
|
||||||
{
|
{
|
||||||
uint32_t *pos;
|
uint32_t *pos;
|
||||||
unsigned int old_ndwords;
|
unsigned int old_ndwords;
|
||||||
unsigned int new_ndwords;
|
unsigned int new_ndwords;
|
||||||
unsigned int delta;
|
unsigned int delta;
|
||||||
|
uint32_t *last_segment_end;
|
||||||
efx_rc_t rc;
|
efx_rc_t rc;
|
||||||
|
|
||||||
if ((rc = tlv_validate_state(cursor)) != 0)
|
if ((rc = tlv_validate_state(cursor)) != 0)
|
||||||
@ -418,19 +531,21 @@ tlv_modify(
|
|||||||
if ((rc = tlv_require_end(cursor)) != 0)
|
if ((rc = tlv_require_end(cursor)) != 0)
|
||||||
goto fail4;
|
goto fail4;
|
||||||
|
|
||||||
|
last_segment_end = tlv_last_segment_end(cursor);
|
||||||
|
|
||||||
if (new_ndwords > old_ndwords) {
|
if (new_ndwords > old_ndwords) {
|
||||||
/* Expand space used for TLV item */
|
/* Expand space used for TLV item */
|
||||||
delta = new_ndwords - old_ndwords;
|
delta = new_ndwords - old_ndwords;
|
||||||
pos = cursor->current + old_ndwords;
|
pos = cursor->current + old_ndwords;
|
||||||
|
|
||||||
if (cursor->end + 1 + delta > cursor->limit) {
|
if (last_segment_end + 1 + delta > cursor->limit) {
|
||||||
rc = ENOSPC;
|
rc = ENOSPC;
|
||||||
goto fail5;
|
goto fail5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Move up: new space at (cursor->current + old_ndwords) */
|
/* Move up: new space at (cursor->current + old_ndwords) */
|
||||||
memmove(pos + delta, pos,
|
memmove(pos + delta, pos,
|
||||||
(cursor->end + 1 - pos) * sizeof (uint32_t));
|
(last_segment_end + 1 - pos) * sizeof (uint32_t));
|
||||||
|
|
||||||
/* Adjust the end pointer */
|
/* Adjust the end pointer */
|
||||||
cursor->end += delta;
|
cursor->end += delta;
|
||||||
@ -442,10 +557,11 @@ tlv_modify(
|
|||||||
|
|
||||||
/* Move down: remove words at (cursor->current + new_ndwords) */
|
/* Move down: remove words at (cursor->current + new_ndwords) */
|
||||||
memmove(pos, pos + delta,
|
memmove(pos, pos + delta,
|
||||||
(cursor->end + 1 - pos) * sizeof (uint32_t));
|
(last_segment_end + 1 - pos) * sizeof (uint32_t));
|
||||||
|
|
||||||
/* Zero the new space at the end of the TLV chain */
|
/* Zero the new space at the end of the TLV chain */
|
||||||
memset(cursor->end + 1 - delta, 0, delta * sizeof (uint32_t));
|
memset(last_segment_end + 1 - delta, 0,
|
||||||
|
delta * sizeof (uint32_t));
|
||||||
|
|
||||||
/* Adjust the end pointer */
|
/* Adjust the end pointer */
|
||||||
cursor->end -= delta;
|
cursor->end -= delta;
|
||||||
@ -470,7 +586,80 @@ tlv_modify(
|
|||||||
return (rc);
|
return (rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate TLV formatted partition contents (before writing to flash) */
|
static uint32_t checksum_tlv_partition(
|
||||||
|
__in nvram_partition_t *partition)
|
||||||
|
{
|
||||||
|
tlv_cursor_t *cursor;
|
||||||
|
uint32_t *ptr;
|
||||||
|
uint32_t *end;
|
||||||
|
uint32_t csum;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
cursor = &partition->tlv_cursor;
|
||||||
|
len = tlv_block_length_used(cursor);
|
||||||
|
EFSYS_ASSERT3U((len & 3), ==, 0);
|
||||||
|
|
||||||
|
csum = 0;
|
||||||
|
ptr = partition->data;
|
||||||
|
end = &ptr[len >> 2];
|
||||||
|
|
||||||
|
while (ptr < end)
|
||||||
|
csum += __LE_TO_CPU_32(*ptr++);
|
||||||
|
|
||||||
|
return (csum);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __checkReturn efx_rc_t
|
||||||
|
tlv_update_partition_len_and_cks(
|
||||||
|
__in tlv_cursor_t *cursor)
|
||||||
|
{
|
||||||
|
efx_rc_t rc;
|
||||||
|
nvram_partition_t partition;
|
||||||
|
struct tlv_partition_header *header;
|
||||||
|
struct tlv_partition_trailer *trailer;
|
||||||
|
size_t new_len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We just modified the partition, so the total length may not be
|
||||||
|
* valid. Don't use tlv_find(), which performs some sanity checks
|
||||||
|
* that may fail here.
|
||||||
|
*/
|
||||||
|
partition.data = cursor->block;
|
||||||
|
memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
|
||||||
|
header = (struct tlv_partition_header *)partition.data;
|
||||||
|
/* Sanity check. */
|
||||||
|
if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
|
||||||
|
rc = EFAULT;
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
new_len = tlv_block_length_used(&partition.tlv_cursor);
|
||||||
|
if (new_len == 0) {
|
||||||
|
rc = EFAULT;
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
header->total_length = __CPU_TO_LE_32(new_len);
|
||||||
|
/* Ensure the modified partition always has a new generation count. */
|
||||||
|
header->generation = __CPU_TO_LE_32(
|
||||||
|
__LE_TO_CPU_32(header->generation) + 1);
|
||||||
|
|
||||||
|
trailer = (struct tlv_partition_trailer *)((uint8_t *)header +
|
||||||
|
new_len - sizeof (*trailer) - sizeof (uint32_t));
|
||||||
|
trailer->generation = header->generation;
|
||||||
|
trailer->checksum = __CPU_TO_LE_32(
|
||||||
|
__LE_TO_CPU_32(trailer->checksum) -
|
||||||
|
checksum_tlv_partition(&partition));
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
fail2:
|
||||||
|
EFSYS_PROBE(fail2);
|
||||||
|
fail1:
|
||||||
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
||||||
|
|
||||||
|
return (rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate buffer contents (before writing to flash) */
|
||||||
__checkReturn efx_rc_t
|
__checkReturn efx_rc_t
|
||||||
ef10_nvram_buffer_validate(
|
ef10_nvram_buffer_validate(
|
||||||
__in efx_nic_t *enp,
|
__in efx_nic_t *enp,
|
||||||
|
Loading…
Reference in New Issue
Block a user