sfxge(4): add buffer editing functions to boot config
Functions to process the DHCP option list format used by the expansion ROM config buffers, to support extracting and updating of individual options. The initial use case is the driver presenting the global and per-PF options as separate items, with the driver implementing the synchronization of global options across the configuration buffers for all PFs. Submitted by: Richard Houldsworth <rhouldsworth at solarflare.com> Sponsored by: Solarflare Communications, Inc. Differential Revision: https://reviews.freebsd.org/D18254
This commit is contained in:
parent
5e8120514e
commit
ef596990a0
@ -1690,6 +1690,87 @@ efx_bootcfg_write(
|
||||
__in_bcount(size) uint8_t *data,
|
||||
__in size_t size);
|
||||
|
||||
|
||||
/*
|
||||
* Processing routines for buffers arranged in the DHCP/BOOTP option format
|
||||
* (see https://tools.ietf.org/html/rfc1533)
|
||||
*
|
||||
* Summarising the format: the buffer is a sequence of options. All options
|
||||
* begin with a tag octet, which uniquely identifies the option. Fixed-
|
||||
* length options without data consist of only a tag octet. Only options PAD
|
||||
* (0) and END (255) are fixed length. All other options are variable-length
|
||||
* with a length octet following the tag octet. The value of the length
|
||||
* octet does not include the two octets specifying the tag and length. The
|
||||
* length octet is followed by "length" octets of data.
|
||||
*
|
||||
* Option data may be a sequence of sub-options in the same format. The data
|
||||
* content of the encapsulating option is one or more encapsulated sub-options,
|
||||
* with no terminating END tag is required.
|
||||
*
|
||||
* To be valid, the top-level sequence of options should be terminated by an
|
||||
* END tag. The buffer should be padded with the PAD byte.
|
||||
*
|
||||
* When stored to NVRAM, the DHCP option format buffer is preceded by a
|
||||
* checksum octet. The full buffer (including after the END tag) contributes
|
||||
* to the checksum, hence the need to fill the buffer to the end with PAD.
|
||||
*/
|
||||
|
||||
#define EFX_DHCP_END ((uint8_t)0xff)
|
||||
#define EFX_DHCP_PAD ((uint8_t)0)
|
||||
|
||||
#define EFX_DHCP_ENCAP_OPT(encapsulator, encapsulated) \
|
||||
(uint16_t)(((encapsulator) << 8) | (encapsulated))
|
||||
|
||||
extern __checkReturn uint8_t
|
||||
efx_dhcp_csum(
|
||||
__in_bcount(size) uint8_t const *data,
|
||||
__in size_t size);
|
||||
|
||||
extern __checkReturn efx_rc_t
|
||||
efx_dhcp_verify(
|
||||
__in_bcount(size) uint8_t const *data,
|
||||
__in size_t size,
|
||||
__out_opt size_t *usedp);
|
||||
|
||||
extern __checkReturn efx_rc_t
|
||||
efx_dhcp_find_tag(
|
||||
__in_bcount(buffer_length) uint8_t *bufferp,
|
||||
__in size_t buffer_length,
|
||||
__in uint16_t opt,
|
||||
__deref_out uint8_t **valuepp,
|
||||
__out size_t *value_lengthp);
|
||||
|
||||
extern __checkReturn efx_rc_t
|
||||
efx_dhcp_find_end(
|
||||
__in_bcount(buffer_length) uint8_t *bufferp,
|
||||
__in size_t buffer_length,
|
||||
__deref_out uint8_t **endpp);
|
||||
|
||||
|
||||
extern __checkReturn efx_rc_t
|
||||
efx_dhcp_delete_tag(
|
||||
__inout_bcount(buffer_length) uint8_t *bufferp,
|
||||
__in size_t buffer_length,
|
||||
__in uint16_t opt);
|
||||
|
||||
extern __checkReturn efx_rc_t
|
||||
efx_dhcp_add_tag(
|
||||
__inout_bcount(buffer_length) uint8_t *bufferp,
|
||||
__in size_t buffer_length,
|
||||
__in uint16_t opt,
|
||||
__in_bcount_opt(value_length) uint8_t *valuep,
|
||||
__in size_t value_length);
|
||||
|
||||
extern __checkReturn efx_rc_t
|
||||
efx_dhcp_update_tag(
|
||||
__inout_bcount(buffer_length) uint8_t *bufferp,
|
||||
__in size_t buffer_length,
|
||||
__in uint16_t opt,
|
||||
__in uint8_t *value_locationp,
|
||||
__in_bcount_opt(value_length) uint8_t *valuep,
|
||||
__in size_t value_length);
|
||||
|
||||
|
||||
#endif /* EFSYS_OPT_BOOTCFG */
|
||||
|
||||
#if EFSYS_OPT_IMAGE_LAYOUT
|
||||
|
@ -67,6 +67,7 @@
|
||||
#define __out_bcount_part_opt(_n, _l)
|
||||
|
||||
#define __deref_out
|
||||
#define __deref_inout
|
||||
|
||||
#define __inout
|
||||
#define __inout_opt
|
||||
|
@ -48,8 +48,33 @@ __FBSDID("$FreeBSD$");
|
||||
#define BOOTCFG_PER_PF 0x800
|
||||
#define BOOTCFG_PF_COUNT 16
|
||||
|
||||
#define DHCP_END ((uint8_t)0xff)
|
||||
#define DHCP_PAD ((uint8_t)0)
|
||||
#define DHCP_OPT_HAS_VALUE(opt) \
|
||||
(((opt) > EFX_DHCP_PAD) && ((opt) < EFX_DHCP_END))
|
||||
|
||||
#define DHCP_MAX_VALUE 255
|
||||
|
||||
#define DHCP_ENCAPSULATOR(encap_opt) ((encap_opt) >> 8)
|
||||
#define DHCP_ENCAPSULATED(encap_opt) ((encap_opt) & 0xff)
|
||||
#define DHCP_IS_ENCAP_OPT(opt) DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATOR(opt))
|
||||
|
||||
typedef struct efx_dhcp_tag_hdr_s {
|
||||
uint8_t tag;
|
||||
uint8_t length;
|
||||
} efx_dhcp_tag_hdr_t;
|
||||
|
||||
/*
|
||||
* Length calculations for tags with value field. PAD and END
|
||||
* have a fixed length of 1, with no length or value field.
|
||||
*/
|
||||
#define DHCP_FULL_TAG_LENGTH(hdr) \
|
||||
(sizeof (efx_dhcp_tag_hdr_t) + (hdr)->length)
|
||||
|
||||
#define DHCP_NEXT_TAG(hdr) \
|
||||
((efx_dhcp_tag_hdr_t *)(((uint8_t *)(hdr)) + \
|
||||
DHCP_FULL_TAG_LENGTH((hdr))))
|
||||
|
||||
#define DHCP_CALC_TAG_LENGTH(payload_len) \
|
||||
((payload_len) + sizeof (efx_dhcp_tag_hdr_t))
|
||||
|
||||
|
||||
/* Report the layout of bootcfg sectors in NVRAM partition. */
|
||||
@ -139,14 +164,11 @@ efx_bootcfg_sector_info(
|
||||
}
|
||||
|
||||
|
||||
static __checkReturn uint8_t
|
||||
efx_bootcfg_csum(
|
||||
__in efx_nic_t *enp,
|
||||
__checkReturn uint8_t
|
||||
efx_dhcp_csum(
|
||||
__in_bcount(size) uint8_t const *data,
|
||||
__in size_t size)
|
||||
{
|
||||
_NOTE(ARGUNUSED(enp))
|
||||
|
||||
unsigned int pos;
|
||||
uint8_t checksum = 0;
|
||||
|
||||
@ -155,9 +177,8 @@ efx_bootcfg_csum(
|
||||
return (checksum);
|
||||
}
|
||||
|
||||
static __checkReturn efx_rc_t
|
||||
efx_bootcfg_verify(
|
||||
__in efx_nic_t *enp,
|
||||
__checkReturn efx_rc_t
|
||||
efx_dhcp_verify(
|
||||
__in_bcount(size) uint8_t const *data,
|
||||
__in size_t size,
|
||||
__out_opt size_t *usedp)
|
||||
@ -173,12 +194,12 @@ efx_bootcfg_verify(
|
||||
|
||||
/* Consume tag */
|
||||
tag = data[offset];
|
||||
if (tag == DHCP_END) {
|
||||
if (tag == EFX_DHCP_END) {
|
||||
offset++;
|
||||
used = offset;
|
||||
break;
|
||||
}
|
||||
if (tag == DHCP_PAD) {
|
||||
if (tag == EFX_DHCP_PAD) {
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
@ -200,8 +221,8 @@ efx_bootcfg_verify(
|
||||
used = offset;
|
||||
}
|
||||
|
||||
/* Checksum the entire sector, including bytes after any DHCP_END */
|
||||
if (efx_bootcfg_csum(enp, data, size) != 0) {
|
||||
/* Checksum the entire sector, including bytes after any EFX_DHCP_END */
|
||||
if (efx_dhcp_csum(data, size) != 0) {
|
||||
rc = EINVAL;
|
||||
goto fail3;
|
||||
}
|
||||
@ -221,6 +242,516 @@ efx_bootcfg_verify(
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk the entire tag set looking for option. The sought option may be
|
||||
* encapsulated. ENOENT indicates the walk completed without finding the
|
||||
* option. If we run out of buffer during the walk the function will return
|
||||
* ENOSPC.
|
||||
*/
|
||||
static efx_rc_t
|
||||
efx_dhcp_walk_tags(
|
||||
__deref_inout uint8_t **tagpp,
|
||||
__inout size_t *buffer_sizep,
|
||||
__in uint16_t opt)
|
||||
{
|
||||
efx_rc_t rc = 0;
|
||||
boolean_t is_encap = B_FALSE;
|
||||
|
||||
if (DHCP_IS_ENCAP_OPT(opt)) {
|
||||
/*
|
||||
* Look for the encapsulator and, if found, limit ourselves
|
||||
* to its payload. If it's not found then the entire tag
|
||||
* cannot be found, so the encapsulated opt search is
|
||||
* skipped.
|
||||
*/
|
||||
rc = efx_dhcp_walk_tags(tagpp, buffer_sizep,
|
||||
DHCP_ENCAPSULATOR(opt));
|
||||
if (rc == 0) {
|
||||
*buffer_sizep = ((efx_dhcp_tag_hdr_t *)*tagpp)->length;
|
||||
(*tagpp) += sizeof (efx_dhcp_tag_hdr_t);
|
||||
}
|
||||
opt = DHCP_ENCAPSULATED(opt);
|
||||
is_encap = B_TRUE;
|
||||
}
|
||||
|
||||
EFSYS_ASSERT(!DHCP_IS_ENCAP_OPT(opt));
|
||||
|
||||
while (rc == 0) {
|
||||
size_t size;
|
||||
|
||||
if (*buffer_sizep == 0) {
|
||||
rc = ENOSPC;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
if (DHCP_ENCAPSULATED(**tagpp) == opt)
|
||||
break;
|
||||
|
||||
if ((**tagpp) == EFX_DHCP_END) {
|
||||
rc = ENOENT;
|
||||
break;
|
||||
} else if ((**tagpp) == EFX_DHCP_PAD) {
|
||||
size = 1;
|
||||
} else {
|
||||
if (*buffer_sizep < sizeof (efx_dhcp_tag_hdr_t)) {
|
||||
rc = ENOSPC;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
size =
|
||||
DHCP_FULL_TAG_LENGTH((efx_dhcp_tag_hdr_t *)*tagpp);
|
||||
}
|
||||
|
||||
if (size > *buffer_sizep) {
|
||||
rc = ENOSPC;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
(*tagpp) += size;
|
||||
(*buffer_sizep) -= size;
|
||||
|
||||
if ((*buffer_sizep == 0) && is_encap) {
|
||||
/* Search within encapulator tag finished */
|
||||
rc = ENOENT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0 if found otherwise ENOENT indicating search finished
|
||||
* correctly
|
||||
*/
|
||||
return (rc);
|
||||
|
||||
fail3:
|
||||
EFSYS_PROBE(fail3);
|
||||
fail2:
|
||||
EFSYS_PROBE(fail2);
|
||||
fail1:
|
||||
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Locate value buffer for option in the given buffer.
|
||||
* Returns 0 if found, ENOENT indicating search finished
|
||||
* correctly, otherwise search failed before completion.
|
||||
*/
|
||||
__checkReturn efx_rc_t
|
||||
efx_dhcp_find_tag(
|
||||
__in_bcount(buffer_length) uint8_t *bufferp,
|
||||
__in size_t buffer_length,
|
||||
__in uint16_t opt,
|
||||
__deref_out uint8_t **valuepp,
|
||||
__out size_t *value_lengthp)
|
||||
{
|
||||
efx_rc_t rc;
|
||||
uint8_t *tagp = bufferp;
|
||||
size_t len = buffer_length;
|
||||
|
||||
rc = efx_dhcp_walk_tags(&tagp, &len, opt);
|
||||
if (rc == 0) {
|
||||
efx_dhcp_tag_hdr_t *hdrp;
|
||||
|
||||
hdrp = (efx_dhcp_tag_hdr_t *)tagp;
|
||||
*valuepp = (uint8_t *)(&hdrp[1]);
|
||||
*value_lengthp = hdrp->length;
|
||||
} else if (rc != ENOENT) {
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
return (rc);
|
||||
|
||||
fail1:
|
||||
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Locate the end tag in the given buffer.
|
||||
* Returns 0 if found, ENOENT indicating search finished
|
||||
* correctly but end tag was not found; otherwise search
|
||||
* failed before completion.
|
||||
*/
|
||||
__checkReturn efx_rc_t
|
||||
efx_dhcp_find_end(
|
||||
__in_bcount(buffer_length) uint8_t *bufferp,
|
||||
__in size_t buffer_length,
|
||||
__deref_out uint8_t **endpp)
|
||||
{
|
||||
efx_rc_t rc;
|
||||
uint8_t *endp = bufferp;
|
||||
size_t len = buffer_length;
|
||||
|
||||
rc = efx_dhcp_walk_tags(&endp, &len, EFX_DHCP_END);
|
||||
if (rc == 0)
|
||||
*endpp = endp;
|
||||
else if (rc != ENOENT)
|
||||
goto fail1;
|
||||
|
||||
return (rc);
|
||||
|
||||
fail1:
|
||||
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Delete the given tag from anywhere in the buffer. Copes with
|
||||
* encapsulated tags, and updates or deletes the encapsulating opt as
|
||||
* necessary.
|
||||
*/
|
||||
__checkReturn efx_rc_t
|
||||
efx_dhcp_delete_tag(
|
||||
__inout_bcount(buffer_length) uint8_t *bufferp,
|
||||
__in size_t buffer_length,
|
||||
__in uint16_t opt)
|
||||
{
|
||||
efx_rc_t rc;
|
||||
efx_dhcp_tag_hdr_t *hdrp;
|
||||
size_t len;
|
||||
uint8_t *startp;
|
||||
uint8_t *endp;
|
||||
|
||||
len = buffer_length;
|
||||
startp = bufferp;
|
||||
|
||||
if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
|
||||
rc = EINVAL;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
rc = efx_dhcp_walk_tags(&startp, &len, opt);
|
||||
if (rc != 0)
|
||||
goto fail1;
|
||||
|
||||
hdrp = (efx_dhcp_tag_hdr_t *)startp;
|
||||
|
||||
if (DHCP_IS_ENCAP_OPT(opt)) {
|
||||
uint8_t tag_length = DHCP_FULL_TAG_LENGTH(hdrp);
|
||||
uint8_t *encapp = bufferp;
|
||||
efx_dhcp_tag_hdr_t *encap_hdrp;
|
||||
|
||||
len = buffer_length;
|
||||
rc = efx_dhcp_walk_tags(&encapp, &len,
|
||||
DHCP_ENCAPSULATOR(opt));
|
||||
if (rc != 0)
|
||||
goto fail2;
|
||||
|
||||
encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp;
|
||||
if (encap_hdrp->length > tag_length) {
|
||||
encap_hdrp->length = (uint8_t)(
|
||||
(size_t)encap_hdrp->length - tag_length);
|
||||
} else {
|
||||
/* delete the encapsulating tag */
|
||||
hdrp = encap_hdrp;
|
||||
}
|
||||
}
|
||||
|
||||
startp = (uint8_t *)hdrp;
|
||||
endp = (uint8_t *)DHCP_NEXT_TAG(hdrp);
|
||||
|
||||
if (startp < bufferp) {
|
||||
rc = EINVAL;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
if (endp > &bufferp[buffer_length]) {
|
||||
rc = EINVAL;
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
memmove(startp, endp,
|
||||
buffer_length - (endp - bufferp));
|
||||
|
||||
return (0);
|
||||
|
||||
fail4:
|
||||
EFSYS_PROBE(fail4);
|
||||
fail3:
|
||||
EFSYS_PROBE(fail3);
|
||||
fail2:
|
||||
EFSYS_PROBE(fail2);
|
||||
fail1:
|
||||
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the tag header into write_pointp and optionally copies the payload
|
||||
* into the space following.
|
||||
*/
|
||||
static void
|
||||
efx_dhcp_write_tag(
|
||||
__in uint8_t *write_pointp,
|
||||
__in uint16_t opt,
|
||||
__in_bcount_opt(value_length)
|
||||
uint8_t *valuep,
|
||||
__in size_t value_length)
|
||||
{
|
||||
efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp;
|
||||
hdrp->tag = DHCP_ENCAPSULATED(opt);
|
||||
hdrp->length = (uint8_t)value_length;
|
||||
if ((value_length > 0) && (valuep != NULL))
|
||||
memcpy(&hdrp[1], valuep, value_length);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the given tag to the end of the buffer. Copes with creating an
|
||||
* encapsulated tag, and updates or creates the encapsulating opt as
|
||||
* necessary.
|
||||
*/
|
||||
__checkReturn efx_rc_t
|
||||
efx_dhcp_add_tag(
|
||||
__inout_bcount(buffer_length) uint8_t *bufferp,
|
||||
__in size_t buffer_length,
|
||||
__in uint16_t opt,
|
||||
__in_bcount_opt(value_length) uint8_t *valuep,
|
||||
__in size_t value_length)
|
||||
{
|
||||
efx_rc_t rc;
|
||||
efx_dhcp_tag_hdr_t *encap_hdrp = NULL;
|
||||
uint8_t *insert_pointp = NULL;
|
||||
uint8_t *endp;
|
||||
size_t available_space;
|
||||
size_t added_length;
|
||||
size_t search_size;
|
||||
uint8_t *searchp;
|
||||
|
||||
if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
|
||||
rc = EINVAL;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
if (value_length > DHCP_MAX_VALUE) {
|
||||
rc = EINVAL;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
if ((value_length > 0) && (valuep == NULL)) {
|
||||
rc = EINVAL;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
endp = bufferp;
|
||||
available_space = buffer_length;
|
||||
rc = efx_dhcp_walk_tags(&endp, &available_space, EFX_DHCP_END);
|
||||
if (rc != 0)
|
||||
goto fail4;
|
||||
|
||||
searchp = bufferp;
|
||||
search_size = buffer_length;
|
||||
if (DHCP_IS_ENCAP_OPT(opt)) {
|
||||
rc = efx_dhcp_walk_tags(&searchp, &search_size,
|
||||
DHCP_ENCAPSULATOR(opt));
|
||||
if (rc == 0) {
|
||||
encap_hdrp = (efx_dhcp_tag_hdr_t *)searchp;
|
||||
|
||||
/* Check encapsulated tag is not present */
|
||||
search_size = encap_hdrp->length;
|
||||
rc = efx_dhcp_walk_tags(&searchp, &search_size,
|
||||
opt);
|
||||
if (rc != ENOENT) {
|
||||
rc = EINVAL;
|
||||
goto fail5;
|
||||
}
|
||||
|
||||
/* Check encapsulator will not overflow */
|
||||
if (((size_t)encap_hdrp->length +
|
||||
DHCP_CALC_TAG_LENGTH(value_length)) >
|
||||
DHCP_MAX_VALUE) {
|
||||
rc = E2BIG;
|
||||
goto fail6;
|
||||
}
|
||||
|
||||
/* Insert at start of existing encapsulator */
|
||||
insert_pointp = (uint8_t *)&encap_hdrp[1];
|
||||
opt = DHCP_ENCAPSULATED(opt);
|
||||
} else if (rc == ENOENT) {
|
||||
encap_hdrp = NULL;
|
||||
} else {
|
||||
goto fail7;
|
||||
}
|
||||
} else {
|
||||
/* Check unencapsulated tag is not present */
|
||||
rc = efx_dhcp_walk_tags(&searchp, &search_size,
|
||||
opt);
|
||||
if (rc != ENOENT) {
|
||||
rc = EINVAL;
|
||||
goto fail8;
|
||||
}
|
||||
}
|
||||
|
||||
if (insert_pointp == NULL) {
|
||||
/* Insert at end of existing tags */
|
||||
insert_pointp = endp;
|
||||
}
|
||||
|
||||
/* Includes the new encapsulator tag hdr if required */
|
||||
added_length = DHCP_CALC_TAG_LENGTH(value_length) +
|
||||
(DHCP_IS_ENCAP_OPT(opt) ? sizeof (efx_dhcp_tag_hdr_t) : 0);
|
||||
|
||||
if (available_space <= added_length) {
|
||||
rc = ENOMEM;
|
||||
goto fail9;
|
||||
}
|
||||
|
||||
memmove(insert_pointp + added_length, insert_pointp,
|
||||
available_space - added_length);
|
||||
|
||||
if (DHCP_IS_ENCAP_OPT(opt)) {
|
||||
/* Create new encapsulator header */
|
||||
added_length -= sizeof (efx_dhcp_tag_hdr_t);
|
||||
efx_dhcp_write_tag(insert_pointp,
|
||||
DHCP_ENCAPSULATOR(opt), NULL, added_length);
|
||||
insert_pointp += sizeof (efx_dhcp_tag_hdr_t);
|
||||
} else if (encap_hdrp)
|
||||
/* Modify existing encapsulator header */
|
||||
encap_hdrp->length +=
|
||||
((uint8_t)DHCP_CALC_TAG_LENGTH(value_length));
|
||||
|
||||
efx_dhcp_write_tag(insert_pointp, opt, valuep, value_length);
|
||||
|
||||
return (0);
|
||||
|
||||
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:
|
||||
EFSYS_PROBE(fail2);
|
||||
fail1:
|
||||
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update an existing tag to the new value. Copes with encapsulated
|
||||
* tags, and updates the encapsulating opt as necessary.
|
||||
*/
|
||||
__checkReturn efx_rc_t
|
||||
efx_dhcp_update_tag(
|
||||
__inout_bcount(buffer_length) uint8_t *bufferp,
|
||||
__in size_t buffer_length,
|
||||
__in uint16_t opt,
|
||||
__in uint8_t *value_locationp,
|
||||
__in_bcount_opt(value_length) uint8_t *valuep,
|
||||
__in size_t value_length)
|
||||
{
|
||||
efx_rc_t rc;
|
||||
uint8_t *write_pointp = value_locationp - sizeof (efx_dhcp_tag_hdr_t);
|
||||
efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp;
|
||||
efx_dhcp_tag_hdr_t *encap_hdrp = NULL;
|
||||
size_t old_length;
|
||||
|
||||
if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
|
||||
rc = EINVAL;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
if (value_length > DHCP_MAX_VALUE) {
|
||||
rc = EINVAL;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
if ((value_length > 0) && (valuep == NULL)) {
|
||||
rc = EINVAL;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
old_length = hdrp->length;
|
||||
|
||||
if (old_length < value_length) {
|
||||
uint8_t *endp = bufferp;
|
||||
size_t available_space = buffer_length;
|
||||
|
||||
rc = efx_dhcp_walk_tags(&endp, &available_space,
|
||||
EFX_DHCP_END);
|
||||
if (rc != 0)
|
||||
goto fail4;
|
||||
|
||||
if (available_space < (value_length - old_length)) {
|
||||
rc = EINVAL;
|
||||
goto fail5;
|
||||
}
|
||||
}
|
||||
|
||||
if (DHCP_IS_ENCAP_OPT(opt)) {
|
||||
uint8_t *encapp = bufferp;
|
||||
size_t following_encap = buffer_length;
|
||||
size_t new_length;
|
||||
|
||||
rc = efx_dhcp_walk_tags(&encapp, &following_encap,
|
||||
DHCP_ENCAPSULATOR(opt));
|
||||
if (rc != 0)
|
||||
goto fail6;
|
||||
|
||||
encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp;
|
||||
|
||||
new_length = ((size_t)encap_hdrp->length +
|
||||
value_length - old_length);
|
||||
/* Check encapsulator will not overflow */
|
||||
if (new_length > DHCP_MAX_VALUE) {
|
||||
rc = E2BIG;
|
||||
goto fail7;
|
||||
}
|
||||
|
||||
encap_hdrp->length = (uint8_t)new_length;
|
||||
}
|
||||
|
||||
/*
|
||||
* Move the following data up/down to accommodate the new payload
|
||||
* length.
|
||||
*/
|
||||
if (old_length != value_length) {
|
||||
uint8_t *destp = (uint8_t *)DHCP_NEXT_TAG(hdrp) +
|
||||
value_length - old_length;
|
||||
size_t count = &bufferp[buffer_length] -
|
||||
(uint8_t *)DHCP_NEXT_TAG(hdrp);
|
||||
|
||||
memmove(destp, DHCP_NEXT_TAG(hdrp), count);
|
||||
}
|
||||
|
||||
EFSYS_ASSERT(hdrp->tag == DHCP_ENCAPSULATED(opt));
|
||||
efx_dhcp_write_tag(write_pointp, opt, valuep, value_length);
|
||||
|
||||
return (0);
|
||||
|
||||
fail7:
|
||||
EFSYS_PROBE(fail7);
|
||||
fail6:
|
||||
EFSYS_PROBE(fail6);
|
||||
fail5:
|
||||
EFSYS_PROBE(fail5);
|
||||
fail4:
|
||||
EFSYS_PROBE(fail4);
|
||||
fail3:
|
||||
EFSYS_PROBE(fail3);
|
||||
fail2:
|
||||
EFSYS_PROBE(fail2);
|
||||
fail1:
|
||||
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Copy bootcfg sector data to a target buffer which may differ in size.
|
||||
* Optionally corrects format errors in source buffer.
|
||||
@ -235,17 +766,19 @@ efx_bootcfg_copy_sector(
|
||||
__in size_t data_size,
|
||||
__in boolean_t handle_format_errors)
|
||||
{
|
||||
_NOTE(ARGUNUSED(enp))
|
||||
|
||||
size_t used_bytes;
|
||||
efx_rc_t rc;
|
||||
|
||||
/* Minimum buffer is checksum byte and DHCP_END terminator */
|
||||
/* Minimum buffer is checksum byte and EFX_DHCP_END terminator */
|
||||
if (data_size < 2) {
|
||||
rc = ENOSPC;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
/* Verify that the area is correctly formatted and checksummed */
|
||||
rc = efx_bootcfg_verify(enp, sector, sector_length,
|
||||
rc = efx_dhcp_verify(sector, sector_length,
|
||||
&used_bytes);
|
||||
|
||||
if (!handle_format_errors) {
|
||||
@ -253,8 +786,8 @@ efx_bootcfg_copy_sector(
|
||||
goto fail2;
|
||||
|
||||
if ((used_bytes < 2) ||
|
||||
(sector[used_bytes - 1] != DHCP_END)) {
|
||||
/* Block too short, or DHCP_END missing */
|
||||
(sector[used_bytes - 1] != EFX_DHCP_END)) {
|
||||
/* Block too short, or EFX_DHCP_END missing */
|
||||
rc = ENOENT;
|
||||
goto fail3;
|
||||
}
|
||||
@ -263,24 +796,24 @@ efx_bootcfg_copy_sector(
|
||||
/* Synthesize empty format on verification failure */
|
||||
if (rc != 0 || used_bytes == 0) {
|
||||
sector[0] = 0;
|
||||
sector[1] = DHCP_END;
|
||||
sector[1] = EFX_DHCP_END;
|
||||
used_bytes = 2;
|
||||
}
|
||||
EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */
|
||||
EFSYS_ASSERT(used_bytes >= 2); /* checksum and EFX_DHCP_END */
|
||||
EFSYS_ASSERT(used_bytes <= sector_length);
|
||||
EFSYS_ASSERT(sector_length >= 2);
|
||||
|
||||
/*
|
||||
* Legacy bootcfg sectors don't terminate with a DHCP_END character.
|
||||
* Modify the returned payload so it does.
|
||||
* Legacy bootcfg sectors don't terminate with an EFX_DHCP_END
|
||||
* character. Modify the returned payload so it does.
|
||||
* Reinitialise the sector if there isn't room for the character.
|
||||
*/
|
||||
if (sector[used_bytes - 1] != DHCP_END) {
|
||||
if (sector[used_bytes - 1] != EFX_DHCP_END) {
|
||||
if (used_bytes >= sector_length) {
|
||||
sector[0] = 0;
|
||||
used_bytes = 1;
|
||||
}
|
||||
sector[used_bytes] = DHCP_END;
|
||||
sector[used_bytes] = EFX_DHCP_END;
|
||||
++used_bytes;
|
||||
}
|
||||
|
||||
@ -303,10 +836,11 @@ efx_bootcfg_copy_sector(
|
||||
(void) memset(data + used_bytes, 0, data_size - used_bytes);
|
||||
|
||||
/*
|
||||
* The checksum includes trailing data after any DHCP_END character,
|
||||
* which we've just modified (by truncation or appending DHCP_END).
|
||||
* The checksum includes trailing data after any EFX_DHCP_END
|
||||
* character, which we've just modified (by truncation or appending
|
||||
* EFX_DHCP_END).
|
||||
*/
|
||||
data[0] -= efx_bootcfg_csum(enp, data, data_size);
|
||||
data[0] -= efx_dhcp_csum(data, data_size);
|
||||
|
||||
return (0);
|
||||
|
||||
@ -336,7 +870,7 @@ efx_bootcfg_read(
|
||||
efx_rc_t rc;
|
||||
uint32_t sector_number;
|
||||
|
||||
/* Minimum buffer is checksum byte and DHCP_END terminator */
|
||||
/* Minimum buffer is checksum byte and EFX_DHCP_END terminator */
|
||||
if (size < 2) {
|
||||
rc = ENOSPC;
|
||||
goto fail1;
|
||||
@ -372,10 +906,10 @@ efx_bootcfg_read(
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to read the entire BOOTCFG sector to ensure we read all the
|
||||
* tags, because legacy bootcfg sectors are not guaranteed to end with
|
||||
* a DHCP_END character. If the user hasn't supplied a sufficiently
|
||||
* large buffer then use our own buffer.
|
||||
* We need to read the entire BOOTCFG sector to ensure we read all
|
||||
* tags, because legacy bootcfg sectors are not guaranteed to end
|
||||
* with an EFX_DHCP_END character. If the user hasn't supplied a
|
||||
* sufficiently large buffer then use our own buffer.
|
||||
*/
|
||||
if (sector_length > size) {
|
||||
EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload);
|
||||
@ -399,28 +933,29 @@ efx_bootcfg_read(
|
||||
goto fail9;
|
||||
|
||||
/* Verify that the area is correctly formatted and checksummed */
|
||||
rc = efx_bootcfg_verify(enp, payload, sector_length,
|
||||
rc = efx_dhcp_verify(payload, sector_length,
|
||||
&used_bytes);
|
||||
if (rc != 0 || used_bytes == 0) {
|
||||
payload[0] = 0;
|
||||
payload[1] = DHCP_END;
|
||||
payload[1] = EFX_DHCP_END;
|
||||
used_bytes = 2;
|
||||
}
|
||||
|
||||
EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */
|
||||
EFSYS_ASSERT(used_bytes >= 2); /* checksum and EFX_DHCP_END */
|
||||
EFSYS_ASSERT(used_bytes <= sector_length);
|
||||
|
||||
/*
|
||||
* Legacy bootcfg sectors don't terminate with a DHCP_END character.
|
||||
* Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by
|
||||
* definition large enough for any valid (per-port) bootcfg sector,
|
||||
* so reinitialise the sector if there isn't room for the character.
|
||||
* Legacy bootcfg sectors don't terminate with an EFX_DHCP_END
|
||||
* character. Modify the returned payload so it does.
|
||||
* BOOTCFG_MAX_SIZE is by definition large enough for any valid
|
||||
* (per-port) bootcfg sector, so reinitialise the sector if there
|
||||
* isn't room for the character.
|
||||
*/
|
||||
if (payload[used_bytes - 1] != DHCP_END) {
|
||||
if (payload[used_bytes - 1] != EFX_DHCP_END) {
|
||||
if (used_bytes >= sector_length)
|
||||
used_bytes = 1;
|
||||
|
||||
payload[used_bytes] = DHCP_END;
|
||||
payload[used_bytes] = EFX_DHCP_END;
|
||||
++used_bytes;
|
||||
}
|
||||
|
||||
@ -446,10 +981,10 @@ efx_bootcfg_read(
|
||||
(void) memset(data + used_bytes, 0, size - used_bytes);
|
||||
|
||||
/*
|
||||
* The checksum includes trailing data after any DHCP_END character,
|
||||
* which we've just modified (by truncation or appending DHCP_END).
|
||||
* The checksum includes trailing data after any EFX_DHCP_END character,
|
||||
* which we've just modified (by truncation or appending EFX_DHCP_END).
|
||||
*/
|
||||
data[0] -= efx_bootcfg_csum(enp, data, size);
|
||||
data[0] -= efx_dhcp_csum(data, size);
|
||||
|
||||
return (0);
|
||||
|
||||
@ -519,12 +1054,16 @@ efx_bootcfg_write(
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0)
|
||||
if ((rc = efx_dhcp_verify(data, size, &used_bytes)) != 0)
|
||||
goto fail4;
|
||||
|
||||
/* The caller *must* terminate their block with a DHCP_END character */
|
||||
if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) {
|
||||
/* Block too short or DHCP_END missing */
|
||||
/*
|
||||
* The caller *must* terminate their block with a EFX_DHCP_END
|
||||
* character
|
||||
*/
|
||||
if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] !=
|
||||
EFX_DHCP_END)) {
|
||||
/* Block too short or EFX_DHCP_END missing */
|
||||
rc = ENOENT;
|
||||
goto fail5;
|
||||
}
|
||||
@ -557,13 +1096,13 @@ efx_bootcfg_write(
|
||||
goto fail9;
|
||||
|
||||
/*
|
||||
* Insert the BOOTCFG sector into the partition, Zero out all data after
|
||||
* the DHCP_END tag, and adjust the checksum.
|
||||
* Insert the BOOTCFG sector into the partition, Zero out all data
|
||||
* after the EFX_DHCP_END tag, and adjust the checksum.
|
||||
*/
|
||||
(void) memset(partn_data + sector_offset, 0x0, sector_length);
|
||||
(void) memcpy(partn_data + sector_offset, data, used_bytes);
|
||||
|
||||
checksum = efx_bootcfg_csum(enp, data, used_bytes);
|
||||
checksum = efx_dhcp_csum(data, used_bytes);
|
||||
partn_data[sector_offset] -= checksum;
|
||||
|
||||
if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
|
||||
|
Loading…
Reference in New Issue
Block a user