Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 30 Nov 2018 07:04:37 +0000 (UTC)
From:      Andrew Rybchenko <arybchik@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r341292 - head/sys/dev/sfxge/common
Message-ID:  <201811300704.wAU74bm4081775@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: arybchik
Date: Fri Nov 30 07:04:37 2018
New Revision: 341292
URL: https://svnweb.freebsd.org/changeset/base/341292

Log:
  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

Modified:
  head/sys/dev/sfxge/common/efx.h
  head/sys/dev/sfxge/common/efx_annote.h   (contents, props changed)
  head/sys/dev/sfxge/common/efx_bootcfg.c

Modified: head/sys/dev/sfxge/common/efx.h
==============================================================================
--- head/sys/dev/sfxge/common/efx.h	Fri Nov 30 07:04:25 2018	(r341291)
+++ head/sys/dev/sfxge/common/efx.h	Fri Nov 30 07:04:37 2018	(r341292)
@@ -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

Modified: head/sys/dev/sfxge/common/efx_annote.h
==============================================================================
--- head/sys/dev/sfxge/common/efx_annote.h	Fri Nov 30 07:04:25 2018	(r341291)
+++ head/sys/dev/sfxge/common/efx_annote.h	Fri Nov 30 07:04:37 2018	(r341292)
@@ -67,6 +67,7 @@
 #define	__out_bcount_part_opt(_n, _l)
 
 #define	__deref_out
+#define	__deref_inout
 
 #define	__inout
 #define	__inout_opt

Modified: head/sys/dev/sfxge/common/efx_bootcfg.c
==============================================================================
--- head/sys/dev/sfxge/common/efx_bootcfg.c	Fri Nov 30 07:04:25 2018	(r341291)
+++ head/sys/dev/sfxge/common/efx_bootcfg.c	Fri Nov 30 07:04:37 2018	(r341292)
@@ -48,10 +48,35 @@ __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. */
 	__checkReturn		efx_rc_t
 efx_bootcfg_sector_info(
@@ -139,14 +164,11 @@ fail1:
 }
 
 
-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;
 	}
@@ -222,6 +243,516 @@ fail1:
 }
 
 /*
+ * 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)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201811300704.wAU74bm4081775>