1 /* SPDX-License-Identifier: BSD-3-Clause
3 * Copyright(c) 2019-2021 Xilinx, Inc.
4 * Copyright(c) 2009-2019 Solarflare Communications Inc.
13 * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE.
14 * NOTE: This is larger than the Medford per-PF bootcfg sector.
16 #define BOOTCFG_MAX_SIZE 0x1000
18 /* Medford per-PF bootcfg sector */
19 #define BOOTCFG_PER_PF 0x800
20 #define BOOTCFG_PF_COUNT 16
22 #define DHCP_OPT_HAS_VALUE(opt) \
23 (((opt) > EFX_DHCP_PAD) && ((opt) < EFX_DHCP_END))
25 #define DHCP_MAX_VALUE 255
27 #define DHCP_ENCAPSULATOR(encap_opt) ((encap_opt) >> 8)
28 #define DHCP_ENCAPSULATED(encap_opt) ((encap_opt) & 0xff)
29 #define DHCP_IS_ENCAP_OPT(opt) DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATOR(opt))
31 typedef struct efx_dhcp_tag_hdr_s {
37 * Length calculations for tags with value field. PAD and END
38 * have a fixed length of 1, with no length or value field.
40 #define DHCP_FULL_TAG_LENGTH(hdr) \
41 (sizeof (efx_dhcp_tag_hdr_t) + (hdr)->length)
43 #define DHCP_NEXT_TAG(hdr) \
44 ((efx_dhcp_tag_hdr_t *)(((uint8_t *)(hdr)) + \
45 DHCP_FULL_TAG_LENGTH((hdr))))
47 #define DHCP_CALC_TAG_LENGTH(payload_len) \
48 ((payload_len) + sizeof (efx_dhcp_tag_hdr_t))
51 /* Report the layout of bootcfg sectors in NVRAM partition. */
52 __checkReturn efx_rc_t
53 efx_bootcfg_sector_info(
56 __out_opt uint32_t *sector_countp,
57 __out size_t *offsetp,
58 __out size_t *max_sizep)
65 switch (enp->en_family) {
67 case EFX_FAMILY_SIENA:
68 max_size = BOOTCFG_MAX_SIZE;
72 #endif /* EFSYS_OPT_SIENA */
74 #if EFSYS_OPT_HUNTINGTON
75 case EFX_FAMILY_HUNTINGTON:
76 max_size = BOOTCFG_MAX_SIZE;
80 #endif /* EFSYS_OPT_HUNTINGTON */
83 case EFX_FAMILY_MEDFORD: {
84 /* Shared partition (array indexed by PF) */
85 max_size = BOOTCFG_PER_PF;
86 count = BOOTCFG_PF_COUNT;
91 offset = max_size * pf;
94 #endif /* EFSYS_OPT_MEDFORD */
96 #if EFSYS_OPT_MEDFORD2
97 case EFX_FAMILY_MEDFORD2: {
98 /* Shared partition (array indexed by PF) */
99 max_size = BOOTCFG_PER_PF;
100 count = BOOTCFG_PF_COUNT;
105 offset = max_size * pf;
108 #endif /* EFSYS_OPT_MEDFORD2 */
115 EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE);
117 if (sector_countp != NULL)
118 *sector_countp = count;
120 *max_sizep = max_size;
124 #if EFSYS_OPT_MEDFORD2
128 #if EFSYS_OPT_MEDFORD
133 EFSYS_PROBE1(fail1, efx_rc_t, rc);
138 __checkReturn uint8_t
140 __in_bcount(size) uint8_t const *data,
144 uint8_t checksum = 0;
146 for (pos = 0; pos < size; pos++)
147 checksum += data[pos];
151 __checkReturn efx_rc_t
153 __in_bcount(size) uint8_t const *data,
155 __out_opt size_t *usedp)
161 /* Start parsing tags immediately after the checksum */
162 for (offset = 1; offset < size; ) {
168 if (tag == EFX_DHCP_END) {
173 if (tag == EFX_DHCP_PAD) {
179 if (offset + 1 >= size) {
183 length = data[offset + 1];
185 /* Consume *length */
186 if (offset + 1 + length >= size) {
191 offset += 2 + length;
195 /* Checksum the entire sector, including bytes after any EFX_DHCP_END */
196 if (efx_dhcp_csum(data, size) != 0) {
211 EFSYS_PROBE1(fail1, efx_rc_t, rc);
217 * Walk the entire tag set looking for option. The sought option may be
218 * encapsulated. ENOENT indicates the walk completed without finding the
219 * option. If we run out of buffer during the walk the function will return
224 __deref_inout uint8_t **tagpp,
225 __inout size_t *buffer_sizep,
229 boolean_t is_encap = B_FALSE;
231 if (DHCP_IS_ENCAP_OPT(opt)) {
233 * Look for the encapsulator and, if found, limit ourselves
234 * to its payload. If it's not found then the entire tag
235 * cannot be found, so the encapsulated opt search is
238 rc = efx_dhcp_walk_tags(tagpp, buffer_sizep,
239 DHCP_ENCAPSULATOR(opt));
241 *buffer_sizep = ((efx_dhcp_tag_hdr_t *)*tagpp)->length;
242 (*tagpp) += sizeof (efx_dhcp_tag_hdr_t);
244 opt = DHCP_ENCAPSULATED(opt);
248 EFSYS_ASSERT(!DHCP_IS_ENCAP_OPT(opt));
253 if (*buffer_sizep == 0) {
258 if (DHCP_ENCAPSULATED(**tagpp) == opt)
261 if ((**tagpp) == EFX_DHCP_END) {
264 } else if ((**tagpp) == EFX_DHCP_PAD) {
267 if (*buffer_sizep < sizeof (efx_dhcp_tag_hdr_t)) {
273 DHCP_FULL_TAG_LENGTH((efx_dhcp_tag_hdr_t *)*tagpp);
276 if (size > *buffer_sizep) {
282 (*buffer_sizep) -= size;
284 if ((*buffer_sizep == 0) && is_encap) {
285 /* Search within encapulator tag finished */
292 * Returns 0 if found otherwise ENOENT indicating search finished
302 EFSYS_PROBE1(fail1, efx_rc_t, rc);
308 * Locate value buffer for option in the given buffer.
309 * Returns 0 if found, ENOENT indicating search finished
310 * correctly, otherwise search failed before completion.
312 __checkReturn efx_rc_t
314 __in_bcount(buffer_length) uint8_t *bufferp,
315 __in size_t buffer_length,
317 __deref_out uint8_t **valuepp,
318 __out size_t *value_lengthp)
321 uint8_t *tagp = bufferp;
322 size_t len = buffer_length;
324 rc = efx_dhcp_walk_tags(&tagp, &len, opt);
326 efx_dhcp_tag_hdr_t *hdrp;
328 hdrp = (efx_dhcp_tag_hdr_t *)tagp;
329 *valuepp = (uint8_t *)(&hdrp[1]);
330 *value_lengthp = hdrp->length;
331 } else if (rc != ENOENT) {
338 EFSYS_PROBE1(fail1, efx_rc_t, rc);
344 * Locate the end tag in the given buffer.
345 * Returns 0 if found, ENOENT indicating search finished
346 * correctly but end tag was not found; otherwise search
347 * failed before completion.
349 __checkReturn efx_rc_t
351 __in_bcount(buffer_length) uint8_t *bufferp,
352 __in size_t buffer_length,
353 __deref_out uint8_t **endpp)
356 uint8_t *endp = bufferp;
357 size_t len = buffer_length;
359 rc = efx_dhcp_walk_tags(&endp, &len, EFX_DHCP_END);
362 else if (rc != ENOENT)
368 EFSYS_PROBE1(fail1, efx_rc_t, rc);
375 * Delete the given tag from anywhere in the buffer. Copes with
376 * encapsulated tags, and updates or deletes the encapsulating opt as
379 __checkReturn efx_rc_t
381 __inout_bcount(buffer_length) uint8_t *bufferp,
382 __in size_t buffer_length,
386 efx_dhcp_tag_hdr_t *hdrp;
394 if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
399 rc = efx_dhcp_walk_tags(&startp, &len, opt);
403 hdrp = (efx_dhcp_tag_hdr_t *)startp;
405 if (DHCP_IS_ENCAP_OPT(opt)) {
406 uint8_t tag_length = DHCP_FULL_TAG_LENGTH(hdrp);
407 uint8_t *encapp = bufferp;
408 efx_dhcp_tag_hdr_t *encap_hdrp;
411 rc = efx_dhcp_walk_tags(&encapp, &len,
412 DHCP_ENCAPSULATOR(opt));
416 encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp;
417 if (encap_hdrp->length > tag_length) {
418 encap_hdrp->length = (uint8_t)(
419 (size_t)encap_hdrp->length - tag_length);
421 /* delete the encapsulating tag */
426 startp = (uint8_t *)hdrp;
427 endp = (uint8_t *)DHCP_NEXT_TAG(hdrp);
429 if (startp < bufferp) {
434 if (endp > &bufferp[buffer_length]) {
439 memmove(startp, endp,
440 buffer_length - (endp - bufferp));
451 EFSYS_PROBE1(fail1, efx_rc_t, rc);
457 * Write the tag header into write_pointp and optionally copies the payload
458 * into the space following.
462 __in uint8_t *write_pointp,
464 __in_bcount_opt(value_length)
466 __in size_t value_length)
468 efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp;
469 hdrp->tag = DHCP_ENCAPSULATED(opt);
470 hdrp->length = (uint8_t)value_length;
471 if ((value_length > 0) && (valuep != NULL))
472 memcpy(&hdrp[1], valuep, value_length);
476 * Add the given tag to the end of the buffer. Copes with creating an
477 * encapsulated tag, and updates or creates the encapsulating opt as
480 __checkReturn efx_rc_t
482 __inout_bcount(buffer_length) uint8_t *bufferp,
483 __in size_t buffer_length,
485 __in_bcount_opt(value_length) uint8_t *valuep,
486 __in size_t value_length)
489 efx_dhcp_tag_hdr_t *encap_hdrp = NULL;
490 uint8_t *insert_pointp = NULL;
492 size_t available_space;
497 if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
502 if (value_length > DHCP_MAX_VALUE) {
507 if ((value_length > 0) && (valuep == NULL)) {
513 available_space = buffer_length;
514 rc = efx_dhcp_walk_tags(&endp, &available_space, EFX_DHCP_END);
519 search_size = buffer_length;
520 if (DHCP_IS_ENCAP_OPT(opt)) {
521 rc = efx_dhcp_walk_tags(&searchp, &search_size,
522 DHCP_ENCAPSULATOR(opt));
524 encap_hdrp = (efx_dhcp_tag_hdr_t *)searchp;
526 /* Check encapsulated tag is not present */
527 search_size = encap_hdrp->length;
528 rc = efx_dhcp_walk_tags(&searchp, &search_size,
535 /* Check encapsulator will not overflow */
536 if (((size_t)encap_hdrp->length +
537 DHCP_CALC_TAG_LENGTH(value_length)) >
543 /* Insert at start of existing encapsulator */
544 insert_pointp = (uint8_t *)&encap_hdrp[1];
545 opt = DHCP_ENCAPSULATED(opt);
546 } else if (rc == ENOENT) {
552 /* Check unencapsulated tag is not present */
553 rc = efx_dhcp_walk_tags(&searchp, &search_size,
561 if (insert_pointp == NULL) {
562 /* Insert at end of existing tags */
563 insert_pointp = endp;
566 /* Includes the new encapsulator tag hdr if required */
567 added_length = DHCP_CALC_TAG_LENGTH(value_length) +
568 (DHCP_IS_ENCAP_OPT(opt) ? sizeof (efx_dhcp_tag_hdr_t) : 0);
570 if (available_space <= added_length) {
575 memmove(insert_pointp + added_length, insert_pointp,
576 available_space - added_length);
578 if (DHCP_IS_ENCAP_OPT(opt)) {
579 /* Create new encapsulator header */
580 added_length -= sizeof (efx_dhcp_tag_hdr_t);
581 efx_dhcp_write_tag(insert_pointp,
582 DHCP_ENCAPSULATOR(opt), NULL, added_length);
583 insert_pointp += sizeof (efx_dhcp_tag_hdr_t);
584 } else if (encap_hdrp)
585 /* Modify existing encapsulator header */
586 encap_hdrp->length +=
587 ((uint8_t)DHCP_CALC_TAG_LENGTH(value_length));
589 efx_dhcp_write_tag(insert_pointp, opt, valuep, value_length);
610 EFSYS_PROBE1(fail1, efx_rc_t, rc);
616 * Update an existing tag to the new value. Copes with encapsulated
617 * tags, and updates the encapsulating opt as necessary.
619 __checkReturn efx_rc_t
621 __inout_bcount(buffer_length) uint8_t *bufferp,
622 __in size_t buffer_length,
624 __in uint8_t *value_locationp,
625 __in_bcount_opt(value_length) uint8_t *valuep,
626 __in size_t value_length)
629 uint8_t *write_pointp = value_locationp - sizeof (efx_dhcp_tag_hdr_t);
630 efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp;
631 efx_dhcp_tag_hdr_t *encap_hdrp = NULL;
634 if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
639 if (value_length > DHCP_MAX_VALUE) {
644 if ((value_length > 0) && (valuep == NULL)) {
649 old_length = hdrp->length;
651 if (old_length < value_length) {
652 uint8_t *endp = bufferp;
653 size_t available_space = buffer_length;
655 rc = efx_dhcp_walk_tags(&endp, &available_space,
660 if (available_space < (value_length - old_length)) {
666 if (DHCP_IS_ENCAP_OPT(opt)) {
667 uint8_t *encapp = bufferp;
668 size_t following_encap = buffer_length;
671 rc = efx_dhcp_walk_tags(&encapp, &following_encap,
672 DHCP_ENCAPSULATOR(opt));
676 encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp;
678 new_length = ((size_t)encap_hdrp->length +
679 value_length - old_length);
680 /* Check encapsulator will not overflow */
681 if (new_length > DHCP_MAX_VALUE) {
686 encap_hdrp->length = (uint8_t)new_length;
690 * Move the following data up/down to accomodate the new payload
693 if (old_length != value_length) {
694 uint8_t *destp = (uint8_t *)DHCP_NEXT_TAG(hdrp) +
695 value_length - old_length;
696 size_t count = &bufferp[buffer_length] -
697 (uint8_t *)DHCP_NEXT_TAG(hdrp);
699 memmove(destp, DHCP_NEXT_TAG(hdrp), count);
702 EFSYS_ASSERT(hdrp->tag == DHCP_ENCAPSULATED(opt));
703 efx_dhcp_write_tag(write_pointp, opt, valuep, value_length);
720 EFSYS_PROBE1(fail1, efx_rc_t, rc);
727 * Copy bootcfg sector data to a target buffer which may differ in size.
728 * Optionally corrects format errors in source buffer.
731 efx_bootcfg_copy_sector(
733 __inout_bcount(sector_length)
735 __in size_t sector_length,
736 __out_bcount(data_size) uint8_t *data,
737 __in size_t data_size,
738 __in boolean_t handle_format_errors)
740 _NOTE(ARGUNUSED(enp))
745 /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */
751 /* Verify that the area is correctly formatted and checksummed */
752 rc = efx_dhcp_verify(sector, sector_length,
755 if (!handle_format_errors) {
759 if ((used_bytes < 2) ||
760 (sector[used_bytes - 1] != EFX_DHCP_END)) {
761 /* Block too short, or EFX_DHCP_END missing */
767 /* Synthesize empty format on verification failure */
768 if (rc != 0 || used_bytes == 0) {
770 sector[1] = EFX_DHCP_END;
773 EFSYS_ASSERT(used_bytes >= 2); /* checksum and EFX_DHCP_END */
774 EFSYS_ASSERT(used_bytes <= sector_length);
775 EFSYS_ASSERT(sector_length >= 2);
778 * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END
779 * character. Modify the returned payload so it does.
780 * Reinitialise the sector if there isn't room for the character.
782 if (sector[used_bytes - 1] != EFX_DHCP_END) {
783 if (used_bytes >= sector_length) {
787 sector[used_bytes] = EFX_DHCP_END;
792 * Verify that the target buffer is large enough for the
793 * entire used bootcfg area, then copy into the target buffer.
795 if (used_bytes > data_size) {
800 data[0] = 0; /* checksum, updated below */
802 /* Copy all after the checksum to the target buffer */
803 memcpy(data + 1, sector + 1, used_bytes - 1);
805 /* Zero out the unused portion of the target buffer */
806 if (used_bytes < data_size)
807 (void) memset(data + used_bytes, 0, data_size - used_bytes);
810 * The checksum includes trailing data after any EFX_DHCP_END
811 * character, which we've just modified (by truncation or appending
814 data[0] -= efx_dhcp_csum(data, data_size);
825 EFSYS_PROBE1(fail1, efx_rc_t, rc);
833 __out_bcount(size) uint8_t *data,
836 uint8_t *payload = NULL;
839 size_t sector_length;
840 size_t sector_offset;
842 uint32_t sector_number;
844 /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */
851 sector_number = enp->en_nic_cfg.enc_pf;
855 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
859 /* The bootcfg sector may be stored in a (larger) shared partition */
860 rc = efx_bootcfg_sector_info(enp, sector_number,
861 NULL, §or_offset, §or_length);
865 if (sector_length < 2) {
870 if (sector_length > BOOTCFG_MAX_SIZE)
871 sector_length = BOOTCFG_MAX_SIZE;
873 if (sector_offset + sector_length > partn_length) {
874 /* Partition is too small */
880 * We need to read the entire BOOTCFG sector to ensure we read all
881 * tags, because legacy bootcfg sectors are not guaranteed to end
882 * with an EFX_DHCP_END character. If the user hasn't supplied a
883 * sufficiently large buffer then use our own buffer.
885 if (sector_length > size) {
886 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload);
887 if (payload == NULL) {
892 payload = (uint8_t *)data;
894 if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
897 if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
898 sector_offset, (caddr_t)payload, sector_length)) != 0) {
899 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
903 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
906 /* Verify that the area is correctly formatted and checksummed */
907 rc = efx_dhcp_verify(payload, sector_length,
909 if (rc != 0 || used_bytes == 0) {
911 payload[1] = EFX_DHCP_END;
915 EFSYS_ASSERT(used_bytes >= 2); /* checksum and EFX_DHCP_END */
916 EFSYS_ASSERT(used_bytes <= sector_length);
919 * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END
920 * character. Modify the returned payload so it does.
921 * BOOTCFG_MAX_SIZE is by definition large enough for any valid
922 * (per-port) bootcfg sector, so reinitialise the sector if there
923 * isn't room for the character.
925 if (payload[used_bytes - 1] != EFX_DHCP_END) {
926 if (used_bytes >= sector_length)
929 payload[used_bytes] = EFX_DHCP_END;
934 * Verify that the user supplied buffer is large enough for the
935 * entire used bootcfg area, then copy into the user supplied buffer.
937 if (used_bytes > size) {
942 data[0] = 0; /* checksum, updated below */
944 if (sector_length > size) {
945 /* Copy all after the checksum to the target buffer */
946 memcpy(data + 1, payload + 1, used_bytes - 1);
947 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
950 /* Zero out the unused portion of the user buffer */
951 if (used_bytes < size)
952 (void) memset(data + used_bytes, 0, size - used_bytes);
955 * The checksum includes trailing data after any EFX_DHCP_END character,
956 * which we've just modified (by truncation or appending EFX_DHCP_END).
958 data[0] -= efx_dhcp_csum(data, size);
970 if (sector_length > size)
971 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
983 EFSYS_PROBE1(fail1, efx_rc_t, rc);
991 __in_bcount(size) uint8_t *data,
997 size_t sector_length;
998 size_t sector_offset;
1001 uint32_t sector_number;
1004 sector_number = enp->en_nic_cfg.enc_pf;
1009 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
1013 /* The bootcfg sector may be stored in a (larger) shared partition */
1014 rc = efx_bootcfg_sector_info(enp, sector_number,
1015 NULL, §or_offset, §or_length);
1019 if (sector_length > BOOTCFG_MAX_SIZE)
1020 sector_length = BOOTCFG_MAX_SIZE;
1022 if (sector_offset + sector_length > partn_length) {
1023 /* Partition is too small */
1028 if ((rc = efx_dhcp_verify(data, size, &used_bytes)) != 0)
1032 * The caller *must* terminate their block with a EFX_DHCP_END
1035 if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] !=
1037 /* Block too short or EFX_DHCP_END missing */
1042 /* Check that the hardware has support for this much data */
1043 if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) {
1049 * If the BOOTCFG sector is stored in a shared partition, then we must
1050 * read the whole partition and insert the updated bootcfg sector at the
1053 EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data);
1054 if (partn_data == NULL) {
1059 rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
1063 /* Read the entire partition */
1064 rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0,
1065 (caddr_t)partn_data, partn_length);
1070 * Insert the BOOTCFG sector into the partition, Zero out all data
1071 * after the EFX_DHCP_END tag, and adjust the checksum.
1073 (void) memset(partn_data + sector_offset, 0x0, sector_length);
1074 (void) memcpy(partn_data + sector_offset, data, used_bytes);
1076 checksum = efx_dhcp_csum(data, used_bytes);
1077 partn_data[sector_offset] -= checksum;
1079 if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
1082 if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
1083 0, (caddr_t)partn_data, partn_length)) != 0)
1086 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
1089 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
1094 EFSYS_PROBE(fail12);
1096 EFSYS_PROBE(fail11);
1098 EFSYS_PROBE(fail10);
1102 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
1106 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
1120 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1125 #endif /* EFSYS_OPT_BOOTCFG */