1e948693eSPhilip Paeps /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 4929c7febSAndrew Rybchenko * Copyright (c) 2009-2016 Solarflare Communications Inc. 53c838a9fSAndrew Rybchenko * All rights reserved. 6e948693eSPhilip Paeps * 7e948693eSPhilip Paeps * Redistribution and use in source and binary forms, with or without 83c838a9fSAndrew Rybchenko * modification, are permitted provided that the following conditions are met: 9e948693eSPhilip Paeps * 103c838a9fSAndrew Rybchenko * 1. Redistributions of source code must retain the above copyright notice, 113c838a9fSAndrew Rybchenko * this list of conditions and the following disclaimer. 123c838a9fSAndrew Rybchenko * 2. Redistributions in binary form must reproduce the above copyright notice, 133c838a9fSAndrew Rybchenko * this list of conditions and the following disclaimer in the documentation 143c838a9fSAndrew Rybchenko * and/or other materials provided with the distribution. 153c838a9fSAndrew Rybchenko * 163c838a9fSAndrew Rybchenko * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 173c838a9fSAndrew Rybchenko * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 183c838a9fSAndrew Rybchenko * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 193c838a9fSAndrew Rybchenko * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 203c838a9fSAndrew Rybchenko * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 213c838a9fSAndrew Rybchenko * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 223c838a9fSAndrew Rybchenko * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 233c838a9fSAndrew Rybchenko * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 243c838a9fSAndrew Rybchenko * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 253c838a9fSAndrew Rybchenko * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 263c838a9fSAndrew Rybchenko * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 273c838a9fSAndrew Rybchenko * 283c838a9fSAndrew Rybchenko * The views and conclusions contained in the software and documentation are 293c838a9fSAndrew Rybchenko * those of the authors and should not be interpreted as representing official 303c838a9fSAndrew Rybchenko * policies, either expressed or implied, of the FreeBSD Project. 31e948693eSPhilip Paeps */ 32e948693eSPhilip Paeps 335dee87d7SPhilip Paeps #include <sys/cdefs.h> 345dee87d7SPhilip Paeps __FBSDID("$FreeBSD$"); 355dee87d7SPhilip Paeps 36e948693eSPhilip Paeps #include "efx.h" 37e948693eSPhilip Paeps #include "efx_impl.h" 38e948693eSPhilip Paeps 39e948693eSPhilip Paeps #if EFSYS_OPT_BOOTCFG 40e948693eSPhilip Paeps 41e948693eSPhilip Paeps /* 42e948693eSPhilip Paeps * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE. 435081d55dSAndrew Rybchenko * NOTE: This is larger than the Medford per-PF bootcfg sector. 44e948693eSPhilip Paeps */ 45e948693eSPhilip Paeps #define BOOTCFG_MAX_SIZE 0x1000 46e948693eSPhilip Paeps 47662c835bSAndrew Rybchenko /* Medford per-PF bootcfg sector */ 48662c835bSAndrew Rybchenko #define BOOTCFG_PER_PF 0x800 49662c835bSAndrew Rybchenko #define BOOTCFG_PF_COUNT 16 50662c835bSAndrew Rybchenko 51d86bef48SAndrew Rybchenko #define DHCP_OPT_HAS_VALUE(opt) \ 52d86bef48SAndrew Rybchenko (((opt) > EFX_DHCP_PAD) && ((opt) < EFX_DHCP_END)) 53d86bef48SAndrew Rybchenko 54d86bef48SAndrew Rybchenko #define DHCP_MAX_VALUE 255 55d86bef48SAndrew Rybchenko 56d86bef48SAndrew Rybchenko #define DHCP_ENCAPSULATOR(encap_opt) ((encap_opt) >> 8) 57d86bef48SAndrew Rybchenko #define DHCP_ENCAPSULATED(encap_opt) ((encap_opt) & 0xff) 58d86bef48SAndrew Rybchenko #define DHCP_IS_ENCAP_OPT(opt) DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATOR(opt)) 59d86bef48SAndrew Rybchenko 60d86bef48SAndrew Rybchenko typedef struct efx_dhcp_tag_hdr_s { 61d86bef48SAndrew Rybchenko uint8_t tag; 62d86bef48SAndrew Rybchenko uint8_t length; 63d86bef48SAndrew Rybchenko } efx_dhcp_tag_hdr_t; 64d86bef48SAndrew Rybchenko 65d86bef48SAndrew Rybchenko /* 66d86bef48SAndrew Rybchenko * Length calculations for tags with value field. PAD and END 67d86bef48SAndrew Rybchenko * have a fixed length of 1, with no length or value field. 68d86bef48SAndrew Rybchenko */ 69d86bef48SAndrew Rybchenko #define DHCP_FULL_TAG_LENGTH(hdr) \ 70d86bef48SAndrew Rybchenko (sizeof (efx_dhcp_tag_hdr_t) + (hdr)->length) 71d86bef48SAndrew Rybchenko 72d86bef48SAndrew Rybchenko #define DHCP_NEXT_TAG(hdr) \ 73d86bef48SAndrew Rybchenko ((efx_dhcp_tag_hdr_t *)(((uint8_t *)(hdr)) + \ 74d86bef48SAndrew Rybchenko DHCP_FULL_TAG_LENGTH((hdr)))) 75d86bef48SAndrew Rybchenko 76d86bef48SAndrew Rybchenko #define DHCP_CALC_TAG_LENGTH(payload_len) \ 77d86bef48SAndrew Rybchenko ((payload_len) + sizeof (efx_dhcp_tag_hdr_t)) 78e948693eSPhilip Paeps 79662c835bSAndrew Rybchenko /* Report the layout of bootcfg sectors in NVRAM partition. */ 80662c835bSAndrew Rybchenko __checkReturn efx_rc_t 81662c835bSAndrew Rybchenko efx_bootcfg_sector_info( 825081d55dSAndrew Rybchenko __in efx_nic_t *enp, 83662c835bSAndrew Rybchenko __in uint32_t pf, 84662c835bSAndrew Rybchenko __out_opt uint32_t *sector_countp, 855081d55dSAndrew Rybchenko __out size_t *offsetp, 865081d55dSAndrew Rybchenko __out size_t *max_sizep) 875081d55dSAndrew Rybchenko { 88662c835bSAndrew Rybchenko uint32_t count; 895081d55dSAndrew Rybchenko size_t max_size; 905081d55dSAndrew Rybchenko size_t offset; 915081d55dSAndrew Rybchenko int rc; 925081d55dSAndrew Rybchenko 935081d55dSAndrew Rybchenko switch (enp->en_family) { 945081d55dSAndrew Rybchenko #if EFSYS_OPT_SIENA 955081d55dSAndrew Rybchenko case EFX_FAMILY_SIENA: 965081d55dSAndrew Rybchenko max_size = BOOTCFG_MAX_SIZE; 975081d55dSAndrew Rybchenko offset = 0; 98662c835bSAndrew Rybchenko count = 1; 995081d55dSAndrew Rybchenko break; 1005081d55dSAndrew Rybchenko #endif /* EFSYS_OPT_SIENA */ 1015081d55dSAndrew Rybchenko 1025081d55dSAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON 1035081d55dSAndrew Rybchenko case EFX_FAMILY_HUNTINGTON: 1045081d55dSAndrew Rybchenko max_size = BOOTCFG_MAX_SIZE; 1055081d55dSAndrew Rybchenko offset = 0; 106662c835bSAndrew Rybchenko count = 1; 1075081d55dSAndrew Rybchenko break; 1085081d55dSAndrew Rybchenko #endif /* EFSYS_OPT_HUNTINGTON */ 1095081d55dSAndrew Rybchenko 1105081d55dSAndrew Rybchenko #if EFSYS_OPT_MEDFORD 1115081d55dSAndrew Rybchenko case EFX_FAMILY_MEDFORD: { 1125081d55dSAndrew Rybchenko /* Shared partition (array indexed by PF) */ 113662c835bSAndrew Rybchenko max_size = BOOTCFG_PER_PF; 114662c835bSAndrew Rybchenko count = BOOTCFG_PF_COUNT; 115662c835bSAndrew Rybchenko if (pf >= count) { 116662c835bSAndrew Rybchenko rc = EINVAL; 117662c835bSAndrew Rybchenko goto fail2; 118662c835bSAndrew Rybchenko } 119662c835bSAndrew Rybchenko offset = max_size * pf; 1205081d55dSAndrew Rybchenko break; 1215081d55dSAndrew Rybchenko } 1225081d55dSAndrew Rybchenko #endif /* EFSYS_OPT_MEDFORD */ 1235081d55dSAndrew Rybchenko 124df0385e5SAndrew Rybchenko #if EFSYS_OPT_MEDFORD2 125df0385e5SAndrew Rybchenko case EFX_FAMILY_MEDFORD2: { 126df0385e5SAndrew Rybchenko /* Shared partition (array indexed by PF) */ 127df0385e5SAndrew Rybchenko max_size = BOOTCFG_PER_PF; 128df0385e5SAndrew Rybchenko count = BOOTCFG_PF_COUNT; 129df0385e5SAndrew Rybchenko if (pf >= count) { 130df0385e5SAndrew Rybchenko rc = EINVAL; 131df0385e5SAndrew Rybchenko goto fail3; 132df0385e5SAndrew Rybchenko } 133df0385e5SAndrew Rybchenko offset = max_size * pf; 134df0385e5SAndrew Rybchenko break; 135df0385e5SAndrew Rybchenko } 136df0385e5SAndrew Rybchenko #endif /* EFSYS_OPT_MEDFORD2 */ 137df0385e5SAndrew Rybchenko 1385081d55dSAndrew Rybchenko default: 1395081d55dSAndrew Rybchenko EFSYS_ASSERT(0); 1405081d55dSAndrew Rybchenko rc = ENOTSUP; 1415081d55dSAndrew Rybchenko goto fail1; 1425081d55dSAndrew Rybchenko } 1435081d55dSAndrew Rybchenko EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE); 1445081d55dSAndrew Rybchenko 145662c835bSAndrew Rybchenko if (sector_countp != NULL) 146662c835bSAndrew Rybchenko *sector_countp = count; 1475081d55dSAndrew Rybchenko *offsetp = offset; 1485081d55dSAndrew Rybchenko *max_sizep = max_size; 1495081d55dSAndrew Rybchenko 1505081d55dSAndrew Rybchenko return (0); 1515081d55dSAndrew Rybchenko 152df0385e5SAndrew Rybchenko #if EFSYS_OPT_MEDFORD2 153df0385e5SAndrew Rybchenko fail3: 154df0385e5SAndrew Rybchenko EFSYS_PROBE(fail3); 155df0385e5SAndrew Rybchenko #endif 156662c835bSAndrew Rybchenko #if EFSYS_OPT_MEDFORD 157662c835bSAndrew Rybchenko fail2: 158662c835bSAndrew Rybchenko EFSYS_PROBE(fail2); 159662c835bSAndrew Rybchenko #endif 1605081d55dSAndrew Rybchenko fail1: 1615081d55dSAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 1625081d55dSAndrew Rybchenko return (rc); 1635081d55dSAndrew Rybchenko } 1645081d55dSAndrew Rybchenko 165d86bef48SAndrew Rybchenko __checkReturn uint8_t 166d86bef48SAndrew Rybchenko efx_dhcp_csum( 167662c835bSAndrew Rybchenko __in_bcount(size) uint8_t const *data, 168e948693eSPhilip Paeps __in size_t size) 169e948693eSPhilip Paeps { 170e948693eSPhilip Paeps unsigned int pos; 171e948693eSPhilip Paeps uint8_t checksum = 0; 172e948693eSPhilip Paeps 173e948693eSPhilip Paeps for (pos = 0; pos < size; pos++) 174e948693eSPhilip Paeps checksum += data[pos]; 175e948693eSPhilip Paeps return (checksum); 176e948693eSPhilip Paeps } 177e948693eSPhilip Paeps 178d86bef48SAndrew Rybchenko __checkReturn efx_rc_t 179d86bef48SAndrew Rybchenko efx_dhcp_verify( 180662c835bSAndrew Rybchenko __in_bcount(size) uint8_t const *data, 181e948693eSPhilip Paeps __in size_t size, 1823c838a9fSAndrew Rybchenko __out_opt size_t *usedp) 183e948693eSPhilip Paeps { 184e948693eSPhilip Paeps size_t offset = 0; 185e948693eSPhilip Paeps size_t used = 0; 186460cb568SAndrew Rybchenko efx_rc_t rc; 187e948693eSPhilip Paeps 188453130d9SPedro F. Giffuni /* Start parsing tags immediately after the checksum */ 189e948693eSPhilip Paeps for (offset = 1; offset < size; ) { 190e948693eSPhilip Paeps uint8_t tag; 191e948693eSPhilip Paeps uint8_t length; 192e948693eSPhilip Paeps 193e948693eSPhilip Paeps /* Consume tag */ 194e948693eSPhilip Paeps tag = data[offset]; 195d86bef48SAndrew Rybchenko if (tag == EFX_DHCP_END) { 196e948693eSPhilip Paeps offset++; 197e948693eSPhilip Paeps used = offset; 198e948693eSPhilip Paeps break; 199e948693eSPhilip Paeps } 200d86bef48SAndrew Rybchenko if (tag == EFX_DHCP_PAD) { 201e948693eSPhilip Paeps offset++; 202e948693eSPhilip Paeps continue; 203e948693eSPhilip Paeps } 204e948693eSPhilip Paeps 205e948693eSPhilip Paeps /* Consume length */ 206e948693eSPhilip Paeps if (offset + 1 >= size) { 207e948693eSPhilip Paeps rc = ENOSPC; 208e948693eSPhilip Paeps goto fail1; 209e948693eSPhilip Paeps } 210e948693eSPhilip Paeps length = data[offset + 1]; 211e948693eSPhilip Paeps 212e948693eSPhilip Paeps /* Consume *length */ 213e948693eSPhilip Paeps if (offset + 1 + length >= size) { 214e948693eSPhilip Paeps rc = ENOSPC; 215e948693eSPhilip Paeps goto fail2; 216e948693eSPhilip Paeps } 217e948693eSPhilip Paeps 218e948693eSPhilip Paeps offset += 2 + length; 219e948693eSPhilip Paeps used = offset; 220e948693eSPhilip Paeps } 221e948693eSPhilip Paeps 222d86bef48SAndrew Rybchenko /* Checksum the entire sector, including bytes after any EFX_DHCP_END */ 223d86bef48SAndrew Rybchenko if (efx_dhcp_csum(data, size) != 0) { 224e948693eSPhilip Paeps rc = EINVAL; 225e948693eSPhilip Paeps goto fail3; 226e948693eSPhilip Paeps } 227e948693eSPhilip Paeps 228e948693eSPhilip Paeps if (usedp != NULL) 229e948693eSPhilip Paeps *usedp = used; 230e948693eSPhilip Paeps 231e948693eSPhilip Paeps return (0); 232e948693eSPhilip Paeps 233e948693eSPhilip Paeps fail3: 234e948693eSPhilip Paeps EFSYS_PROBE(fail3); 235e948693eSPhilip Paeps fail2: 236e948693eSPhilip Paeps EFSYS_PROBE(fail2); 237e948693eSPhilip Paeps fail1: 238460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 239e948693eSPhilip Paeps 240e948693eSPhilip Paeps return (rc); 241e948693eSPhilip Paeps } 242e948693eSPhilip Paeps 243662c835bSAndrew Rybchenko /* 244d86bef48SAndrew Rybchenko * Walk the entire tag set looking for option. The sought option may be 245d86bef48SAndrew Rybchenko * encapsulated. ENOENT indicates the walk completed without finding the 246d86bef48SAndrew Rybchenko * option. If we run out of buffer during the walk the function will return 247d86bef48SAndrew Rybchenko * ENOSPC. 248d86bef48SAndrew Rybchenko */ 249d86bef48SAndrew Rybchenko static efx_rc_t 250d86bef48SAndrew Rybchenko efx_dhcp_walk_tags( 251d86bef48SAndrew Rybchenko __deref_inout uint8_t **tagpp, 252d86bef48SAndrew Rybchenko __inout size_t *buffer_sizep, 253d86bef48SAndrew Rybchenko __in uint16_t opt) 254d86bef48SAndrew Rybchenko { 255d86bef48SAndrew Rybchenko efx_rc_t rc = 0; 256d86bef48SAndrew Rybchenko boolean_t is_encap = B_FALSE; 257d86bef48SAndrew Rybchenko 258d86bef48SAndrew Rybchenko if (DHCP_IS_ENCAP_OPT(opt)) { 259d86bef48SAndrew Rybchenko /* 260d86bef48SAndrew Rybchenko * Look for the encapsulator and, if found, limit ourselves 261d86bef48SAndrew Rybchenko * to its payload. If it's not found then the entire tag 262d86bef48SAndrew Rybchenko * cannot be found, so the encapsulated opt search is 263d86bef48SAndrew Rybchenko * skipped. 264d86bef48SAndrew Rybchenko */ 265d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(tagpp, buffer_sizep, 266d86bef48SAndrew Rybchenko DHCP_ENCAPSULATOR(opt)); 267d86bef48SAndrew Rybchenko if (rc == 0) { 268d86bef48SAndrew Rybchenko *buffer_sizep = ((efx_dhcp_tag_hdr_t *)*tagpp)->length; 269d86bef48SAndrew Rybchenko (*tagpp) += sizeof (efx_dhcp_tag_hdr_t); 270d86bef48SAndrew Rybchenko } 271d86bef48SAndrew Rybchenko opt = DHCP_ENCAPSULATED(opt); 272d86bef48SAndrew Rybchenko is_encap = B_TRUE; 273d86bef48SAndrew Rybchenko } 274d86bef48SAndrew Rybchenko 275d86bef48SAndrew Rybchenko EFSYS_ASSERT(!DHCP_IS_ENCAP_OPT(opt)); 276d86bef48SAndrew Rybchenko 277d86bef48SAndrew Rybchenko while (rc == 0) { 278d86bef48SAndrew Rybchenko size_t size; 279d86bef48SAndrew Rybchenko 280d86bef48SAndrew Rybchenko if (*buffer_sizep == 0) { 281d86bef48SAndrew Rybchenko rc = ENOSPC; 282d86bef48SAndrew Rybchenko goto fail1; 283d86bef48SAndrew Rybchenko } 284d86bef48SAndrew Rybchenko 285d86bef48SAndrew Rybchenko if (DHCP_ENCAPSULATED(**tagpp) == opt) 286d86bef48SAndrew Rybchenko break; 287d86bef48SAndrew Rybchenko 288d86bef48SAndrew Rybchenko if ((**tagpp) == EFX_DHCP_END) { 289d86bef48SAndrew Rybchenko rc = ENOENT; 290d86bef48SAndrew Rybchenko break; 291d86bef48SAndrew Rybchenko } else if ((**tagpp) == EFX_DHCP_PAD) { 292d86bef48SAndrew Rybchenko size = 1; 293d86bef48SAndrew Rybchenko } else { 294d86bef48SAndrew Rybchenko if (*buffer_sizep < sizeof (efx_dhcp_tag_hdr_t)) { 295d86bef48SAndrew Rybchenko rc = ENOSPC; 296d86bef48SAndrew Rybchenko goto fail2; 297d86bef48SAndrew Rybchenko } 298d86bef48SAndrew Rybchenko 299d86bef48SAndrew Rybchenko size = 300d86bef48SAndrew Rybchenko DHCP_FULL_TAG_LENGTH((efx_dhcp_tag_hdr_t *)*tagpp); 301d86bef48SAndrew Rybchenko } 302d86bef48SAndrew Rybchenko 303d86bef48SAndrew Rybchenko if (size > *buffer_sizep) { 304d86bef48SAndrew Rybchenko rc = ENOSPC; 305d86bef48SAndrew Rybchenko goto fail3; 306d86bef48SAndrew Rybchenko } 307d86bef48SAndrew Rybchenko 308d86bef48SAndrew Rybchenko (*tagpp) += size; 309d86bef48SAndrew Rybchenko (*buffer_sizep) -= size; 310d86bef48SAndrew Rybchenko 311d86bef48SAndrew Rybchenko if ((*buffer_sizep == 0) && is_encap) { 312d86bef48SAndrew Rybchenko /* Search within encapulator tag finished */ 313d86bef48SAndrew Rybchenko rc = ENOENT; 314d86bef48SAndrew Rybchenko break; 315d86bef48SAndrew Rybchenko } 316d86bef48SAndrew Rybchenko } 317d86bef48SAndrew Rybchenko 318d86bef48SAndrew Rybchenko /* 319d86bef48SAndrew Rybchenko * Returns 0 if found otherwise ENOENT indicating search finished 320d86bef48SAndrew Rybchenko * correctly 321d86bef48SAndrew Rybchenko */ 322d86bef48SAndrew Rybchenko return (rc); 323d86bef48SAndrew Rybchenko 324d86bef48SAndrew Rybchenko fail3: 325d86bef48SAndrew Rybchenko EFSYS_PROBE(fail3); 326d86bef48SAndrew Rybchenko fail2: 327d86bef48SAndrew Rybchenko EFSYS_PROBE(fail2); 328d86bef48SAndrew Rybchenko fail1: 329d86bef48SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 330d86bef48SAndrew Rybchenko 331d86bef48SAndrew Rybchenko return (rc); 332d86bef48SAndrew Rybchenko } 333d86bef48SAndrew Rybchenko 334d86bef48SAndrew Rybchenko /* 335d86bef48SAndrew Rybchenko * Locate value buffer for option in the given buffer. 336d86bef48SAndrew Rybchenko * Returns 0 if found, ENOENT indicating search finished 337d86bef48SAndrew Rybchenko * correctly, otherwise search failed before completion. 338d86bef48SAndrew Rybchenko */ 339d86bef48SAndrew Rybchenko __checkReturn efx_rc_t 340d86bef48SAndrew Rybchenko efx_dhcp_find_tag( 341d86bef48SAndrew Rybchenko __in_bcount(buffer_length) uint8_t *bufferp, 342d86bef48SAndrew Rybchenko __in size_t buffer_length, 343d86bef48SAndrew Rybchenko __in uint16_t opt, 344d86bef48SAndrew Rybchenko __deref_out uint8_t **valuepp, 345d86bef48SAndrew Rybchenko __out size_t *value_lengthp) 346d86bef48SAndrew Rybchenko { 347d86bef48SAndrew Rybchenko efx_rc_t rc; 348d86bef48SAndrew Rybchenko uint8_t *tagp = bufferp; 349d86bef48SAndrew Rybchenko size_t len = buffer_length; 350d86bef48SAndrew Rybchenko 351d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&tagp, &len, opt); 352d86bef48SAndrew Rybchenko if (rc == 0) { 353d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *hdrp; 354d86bef48SAndrew Rybchenko 355d86bef48SAndrew Rybchenko hdrp = (efx_dhcp_tag_hdr_t *)tagp; 356d86bef48SAndrew Rybchenko *valuepp = (uint8_t *)(&hdrp[1]); 357d86bef48SAndrew Rybchenko *value_lengthp = hdrp->length; 358d86bef48SAndrew Rybchenko } else if (rc != ENOENT) { 359d86bef48SAndrew Rybchenko goto fail1; 360d86bef48SAndrew Rybchenko } 361d86bef48SAndrew Rybchenko 362d86bef48SAndrew Rybchenko return (rc); 363d86bef48SAndrew Rybchenko 364d86bef48SAndrew Rybchenko fail1: 365d86bef48SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 366d86bef48SAndrew Rybchenko 367d86bef48SAndrew Rybchenko return (rc); 368d86bef48SAndrew Rybchenko } 369d86bef48SAndrew Rybchenko 370d86bef48SAndrew Rybchenko /* 371d86bef48SAndrew Rybchenko * Locate the end tag in the given buffer. 372d86bef48SAndrew Rybchenko * Returns 0 if found, ENOENT indicating search finished 373d86bef48SAndrew Rybchenko * correctly but end tag was not found; otherwise search 374d86bef48SAndrew Rybchenko * failed before completion. 375d86bef48SAndrew Rybchenko */ 376d86bef48SAndrew Rybchenko __checkReturn efx_rc_t 377d86bef48SAndrew Rybchenko efx_dhcp_find_end( 378d86bef48SAndrew Rybchenko __in_bcount(buffer_length) uint8_t *bufferp, 379d86bef48SAndrew Rybchenko __in size_t buffer_length, 380d86bef48SAndrew Rybchenko __deref_out uint8_t **endpp) 381d86bef48SAndrew Rybchenko { 382d86bef48SAndrew Rybchenko efx_rc_t rc; 383d86bef48SAndrew Rybchenko uint8_t *endp = bufferp; 384d86bef48SAndrew Rybchenko size_t len = buffer_length; 385d86bef48SAndrew Rybchenko 386d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&endp, &len, EFX_DHCP_END); 387d86bef48SAndrew Rybchenko if (rc == 0) 388d86bef48SAndrew Rybchenko *endpp = endp; 389d86bef48SAndrew Rybchenko else if (rc != ENOENT) 390d86bef48SAndrew Rybchenko goto fail1; 391d86bef48SAndrew Rybchenko 392d86bef48SAndrew Rybchenko return (rc); 393d86bef48SAndrew Rybchenko 394d86bef48SAndrew Rybchenko fail1: 395d86bef48SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 396d86bef48SAndrew Rybchenko 397d86bef48SAndrew Rybchenko return (rc); 398d86bef48SAndrew Rybchenko } 399d86bef48SAndrew Rybchenko 400d86bef48SAndrew Rybchenko /* 401d86bef48SAndrew Rybchenko * Delete the given tag from anywhere in the buffer. Copes with 402d86bef48SAndrew Rybchenko * encapsulated tags, and updates or deletes the encapsulating opt as 403d86bef48SAndrew Rybchenko * necessary. 404d86bef48SAndrew Rybchenko */ 405d86bef48SAndrew Rybchenko __checkReturn efx_rc_t 406d86bef48SAndrew Rybchenko efx_dhcp_delete_tag( 407d86bef48SAndrew Rybchenko __inout_bcount(buffer_length) uint8_t *bufferp, 408d86bef48SAndrew Rybchenko __in size_t buffer_length, 409d86bef48SAndrew Rybchenko __in uint16_t opt) 410d86bef48SAndrew Rybchenko { 411d86bef48SAndrew Rybchenko efx_rc_t rc; 412d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *hdrp; 413d86bef48SAndrew Rybchenko size_t len; 414d86bef48SAndrew Rybchenko uint8_t *startp; 415d86bef48SAndrew Rybchenko uint8_t *endp; 416d86bef48SAndrew Rybchenko 417d86bef48SAndrew Rybchenko len = buffer_length; 418d86bef48SAndrew Rybchenko startp = bufferp; 419d86bef48SAndrew Rybchenko 420d86bef48SAndrew Rybchenko if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) { 421d86bef48SAndrew Rybchenko rc = EINVAL; 422d86bef48SAndrew Rybchenko goto fail1; 423d86bef48SAndrew Rybchenko } 424d86bef48SAndrew Rybchenko 425d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&startp, &len, opt); 426d86bef48SAndrew Rybchenko if (rc != 0) 427d86bef48SAndrew Rybchenko goto fail1; 428d86bef48SAndrew Rybchenko 429d86bef48SAndrew Rybchenko hdrp = (efx_dhcp_tag_hdr_t *)startp; 430d86bef48SAndrew Rybchenko 431d86bef48SAndrew Rybchenko if (DHCP_IS_ENCAP_OPT(opt)) { 432d86bef48SAndrew Rybchenko uint8_t tag_length = DHCP_FULL_TAG_LENGTH(hdrp); 433d86bef48SAndrew Rybchenko uint8_t *encapp = bufferp; 434d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *encap_hdrp; 435d86bef48SAndrew Rybchenko 436d86bef48SAndrew Rybchenko len = buffer_length; 437d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&encapp, &len, 438d86bef48SAndrew Rybchenko DHCP_ENCAPSULATOR(opt)); 439d86bef48SAndrew Rybchenko if (rc != 0) 440d86bef48SAndrew Rybchenko goto fail2; 441d86bef48SAndrew Rybchenko 442d86bef48SAndrew Rybchenko encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp; 443d86bef48SAndrew Rybchenko if (encap_hdrp->length > tag_length) { 444d86bef48SAndrew Rybchenko encap_hdrp->length = (uint8_t)( 445d86bef48SAndrew Rybchenko (size_t)encap_hdrp->length - tag_length); 446d86bef48SAndrew Rybchenko } else { 447d86bef48SAndrew Rybchenko /* delete the encapsulating tag */ 448d86bef48SAndrew Rybchenko hdrp = encap_hdrp; 449d86bef48SAndrew Rybchenko } 450d86bef48SAndrew Rybchenko } 451d86bef48SAndrew Rybchenko 452d86bef48SAndrew Rybchenko startp = (uint8_t *)hdrp; 453d86bef48SAndrew Rybchenko endp = (uint8_t *)DHCP_NEXT_TAG(hdrp); 454d86bef48SAndrew Rybchenko 455d86bef48SAndrew Rybchenko if (startp < bufferp) { 456d86bef48SAndrew Rybchenko rc = EINVAL; 457d86bef48SAndrew Rybchenko goto fail3; 458d86bef48SAndrew Rybchenko } 459d86bef48SAndrew Rybchenko 460d86bef48SAndrew Rybchenko if (endp > &bufferp[buffer_length]) { 461d86bef48SAndrew Rybchenko rc = EINVAL; 462d86bef48SAndrew Rybchenko goto fail4; 463d86bef48SAndrew Rybchenko } 464d86bef48SAndrew Rybchenko 465d86bef48SAndrew Rybchenko memmove(startp, endp, 466d86bef48SAndrew Rybchenko buffer_length - (endp - bufferp)); 467d86bef48SAndrew Rybchenko 468d86bef48SAndrew Rybchenko return (0); 469d86bef48SAndrew Rybchenko 470d86bef48SAndrew Rybchenko fail4: 471d86bef48SAndrew Rybchenko EFSYS_PROBE(fail4); 472d86bef48SAndrew Rybchenko fail3: 473d86bef48SAndrew Rybchenko EFSYS_PROBE(fail3); 474d86bef48SAndrew Rybchenko fail2: 475d86bef48SAndrew Rybchenko EFSYS_PROBE(fail2); 476d86bef48SAndrew Rybchenko fail1: 477d86bef48SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 478d86bef48SAndrew Rybchenko 479d86bef48SAndrew Rybchenko return (rc); 480d86bef48SAndrew Rybchenko } 481d86bef48SAndrew Rybchenko 482d86bef48SAndrew Rybchenko /* 483d86bef48SAndrew Rybchenko * Write the tag header into write_pointp and optionally copies the payload 484d86bef48SAndrew Rybchenko * into the space following. 485d86bef48SAndrew Rybchenko */ 486d86bef48SAndrew Rybchenko static void 487d86bef48SAndrew Rybchenko efx_dhcp_write_tag( 488d86bef48SAndrew Rybchenko __in uint8_t *write_pointp, 489d86bef48SAndrew Rybchenko __in uint16_t opt, 490d86bef48SAndrew Rybchenko __in_bcount_opt(value_length) 491d86bef48SAndrew Rybchenko uint8_t *valuep, 492d86bef48SAndrew Rybchenko __in size_t value_length) 493d86bef48SAndrew Rybchenko { 494d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp; 495d86bef48SAndrew Rybchenko hdrp->tag = DHCP_ENCAPSULATED(opt); 496d86bef48SAndrew Rybchenko hdrp->length = (uint8_t)value_length; 497d86bef48SAndrew Rybchenko if ((value_length > 0) && (valuep != NULL)) 498d86bef48SAndrew Rybchenko memcpy(&hdrp[1], valuep, value_length); 499d86bef48SAndrew Rybchenko } 500d86bef48SAndrew Rybchenko 501d86bef48SAndrew Rybchenko /* 502d86bef48SAndrew Rybchenko * Add the given tag to the end of the buffer. Copes with creating an 503d86bef48SAndrew Rybchenko * encapsulated tag, and updates or creates the encapsulating opt as 504d86bef48SAndrew Rybchenko * necessary. 505d86bef48SAndrew Rybchenko */ 506d86bef48SAndrew Rybchenko __checkReturn efx_rc_t 507d86bef48SAndrew Rybchenko efx_dhcp_add_tag( 508d86bef48SAndrew Rybchenko __inout_bcount(buffer_length) uint8_t *bufferp, 509d86bef48SAndrew Rybchenko __in size_t buffer_length, 510d86bef48SAndrew Rybchenko __in uint16_t opt, 511d86bef48SAndrew Rybchenko __in_bcount_opt(value_length) uint8_t *valuep, 512d86bef48SAndrew Rybchenko __in size_t value_length) 513d86bef48SAndrew Rybchenko { 514d86bef48SAndrew Rybchenko efx_rc_t rc; 515d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *encap_hdrp = NULL; 516d86bef48SAndrew Rybchenko uint8_t *insert_pointp = NULL; 517d86bef48SAndrew Rybchenko uint8_t *endp; 518d86bef48SAndrew Rybchenko size_t available_space; 519d86bef48SAndrew Rybchenko size_t added_length; 520d86bef48SAndrew Rybchenko size_t search_size; 521d86bef48SAndrew Rybchenko uint8_t *searchp; 522d86bef48SAndrew Rybchenko 523d86bef48SAndrew Rybchenko if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) { 524d86bef48SAndrew Rybchenko rc = EINVAL; 525d86bef48SAndrew Rybchenko goto fail1; 526d86bef48SAndrew Rybchenko } 527d86bef48SAndrew Rybchenko 528d86bef48SAndrew Rybchenko if (value_length > DHCP_MAX_VALUE) { 529d86bef48SAndrew Rybchenko rc = EINVAL; 530d86bef48SAndrew Rybchenko goto fail2; 531d86bef48SAndrew Rybchenko } 532d86bef48SAndrew Rybchenko 533d86bef48SAndrew Rybchenko if ((value_length > 0) && (valuep == NULL)) { 534d86bef48SAndrew Rybchenko rc = EINVAL; 535d86bef48SAndrew Rybchenko goto fail3; 536d86bef48SAndrew Rybchenko } 537d86bef48SAndrew Rybchenko 538d86bef48SAndrew Rybchenko endp = bufferp; 539d86bef48SAndrew Rybchenko available_space = buffer_length; 540d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&endp, &available_space, EFX_DHCP_END); 541d86bef48SAndrew Rybchenko if (rc != 0) 542d86bef48SAndrew Rybchenko goto fail4; 543d86bef48SAndrew Rybchenko 544d86bef48SAndrew Rybchenko searchp = bufferp; 545d86bef48SAndrew Rybchenko search_size = buffer_length; 546d86bef48SAndrew Rybchenko if (DHCP_IS_ENCAP_OPT(opt)) { 547d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&searchp, &search_size, 548d86bef48SAndrew Rybchenko DHCP_ENCAPSULATOR(opt)); 549d86bef48SAndrew Rybchenko if (rc == 0) { 550d86bef48SAndrew Rybchenko encap_hdrp = (efx_dhcp_tag_hdr_t *)searchp; 551d86bef48SAndrew Rybchenko 552d86bef48SAndrew Rybchenko /* Check encapsulated tag is not present */ 553d86bef48SAndrew Rybchenko search_size = encap_hdrp->length; 554d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&searchp, &search_size, 555d86bef48SAndrew Rybchenko opt); 556d86bef48SAndrew Rybchenko if (rc != ENOENT) { 557d86bef48SAndrew Rybchenko rc = EINVAL; 558d86bef48SAndrew Rybchenko goto fail5; 559d86bef48SAndrew Rybchenko } 560d86bef48SAndrew Rybchenko 561d86bef48SAndrew Rybchenko /* Check encapsulator will not overflow */ 562d86bef48SAndrew Rybchenko if (((size_t)encap_hdrp->length + 563d86bef48SAndrew Rybchenko DHCP_CALC_TAG_LENGTH(value_length)) > 564d86bef48SAndrew Rybchenko DHCP_MAX_VALUE) { 565d86bef48SAndrew Rybchenko rc = E2BIG; 566d86bef48SAndrew Rybchenko goto fail6; 567d86bef48SAndrew Rybchenko } 568d86bef48SAndrew Rybchenko 569d86bef48SAndrew Rybchenko /* Insert at start of existing encapsulator */ 570d86bef48SAndrew Rybchenko insert_pointp = (uint8_t *)&encap_hdrp[1]; 571d86bef48SAndrew Rybchenko opt = DHCP_ENCAPSULATED(opt); 572d86bef48SAndrew Rybchenko } else if (rc == ENOENT) { 573d86bef48SAndrew Rybchenko encap_hdrp = NULL; 574d86bef48SAndrew Rybchenko } else { 575d86bef48SAndrew Rybchenko goto fail7; 576d86bef48SAndrew Rybchenko } 577d86bef48SAndrew Rybchenko } else { 578d86bef48SAndrew Rybchenko /* Check unencapsulated tag is not present */ 579d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&searchp, &search_size, 580d86bef48SAndrew Rybchenko opt); 581d86bef48SAndrew Rybchenko if (rc != ENOENT) { 582d86bef48SAndrew Rybchenko rc = EINVAL; 583d86bef48SAndrew Rybchenko goto fail8; 584d86bef48SAndrew Rybchenko } 585d86bef48SAndrew Rybchenko } 586d86bef48SAndrew Rybchenko 587d86bef48SAndrew Rybchenko if (insert_pointp == NULL) { 588d86bef48SAndrew Rybchenko /* Insert at end of existing tags */ 589d86bef48SAndrew Rybchenko insert_pointp = endp; 590d86bef48SAndrew Rybchenko } 591d86bef48SAndrew Rybchenko 592d86bef48SAndrew Rybchenko /* Includes the new encapsulator tag hdr if required */ 593d86bef48SAndrew Rybchenko added_length = DHCP_CALC_TAG_LENGTH(value_length) + 594d86bef48SAndrew Rybchenko (DHCP_IS_ENCAP_OPT(opt) ? sizeof (efx_dhcp_tag_hdr_t) : 0); 595d86bef48SAndrew Rybchenko 596d86bef48SAndrew Rybchenko if (available_space <= added_length) { 597d86bef48SAndrew Rybchenko rc = ENOMEM; 598d86bef48SAndrew Rybchenko goto fail9; 599d86bef48SAndrew Rybchenko } 600d86bef48SAndrew Rybchenko 601d86bef48SAndrew Rybchenko memmove(insert_pointp + added_length, insert_pointp, 602d86bef48SAndrew Rybchenko available_space - added_length); 603d86bef48SAndrew Rybchenko 604d86bef48SAndrew Rybchenko if (DHCP_IS_ENCAP_OPT(opt)) { 605d86bef48SAndrew Rybchenko /* Create new encapsulator header */ 606d86bef48SAndrew Rybchenko added_length -= sizeof (efx_dhcp_tag_hdr_t); 607d86bef48SAndrew Rybchenko efx_dhcp_write_tag(insert_pointp, 608d86bef48SAndrew Rybchenko DHCP_ENCAPSULATOR(opt), NULL, added_length); 609d86bef48SAndrew Rybchenko insert_pointp += sizeof (efx_dhcp_tag_hdr_t); 610d86bef48SAndrew Rybchenko } else if (encap_hdrp) 611d86bef48SAndrew Rybchenko /* Modify existing encapsulator header */ 612d86bef48SAndrew Rybchenko encap_hdrp->length += 613d86bef48SAndrew Rybchenko ((uint8_t)DHCP_CALC_TAG_LENGTH(value_length)); 614d86bef48SAndrew Rybchenko 615d86bef48SAndrew Rybchenko efx_dhcp_write_tag(insert_pointp, opt, valuep, value_length); 616d86bef48SAndrew Rybchenko 617d86bef48SAndrew Rybchenko return (0); 618d86bef48SAndrew Rybchenko 619d86bef48SAndrew Rybchenko fail9: 620d86bef48SAndrew Rybchenko EFSYS_PROBE(fail9); 621d86bef48SAndrew Rybchenko fail8: 622d86bef48SAndrew Rybchenko EFSYS_PROBE(fail8); 623d86bef48SAndrew Rybchenko fail7: 624d86bef48SAndrew Rybchenko EFSYS_PROBE(fail7); 625d86bef48SAndrew Rybchenko fail6: 626d86bef48SAndrew Rybchenko EFSYS_PROBE(fail6); 627d86bef48SAndrew Rybchenko fail5: 628d86bef48SAndrew Rybchenko EFSYS_PROBE(fail5); 629d86bef48SAndrew Rybchenko fail4: 630d86bef48SAndrew Rybchenko EFSYS_PROBE(fail4); 631d86bef48SAndrew Rybchenko fail3: 632d86bef48SAndrew Rybchenko EFSYS_PROBE(fail3); 633d86bef48SAndrew Rybchenko fail2: 634d86bef48SAndrew Rybchenko EFSYS_PROBE(fail2); 635d86bef48SAndrew Rybchenko fail1: 636d86bef48SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 637d86bef48SAndrew Rybchenko 638d86bef48SAndrew Rybchenko return (rc); 639d86bef48SAndrew Rybchenko } 640d86bef48SAndrew Rybchenko 641d86bef48SAndrew Rybchenko /* 642d86bef48SAndrew Rybchenko * Update an existing tag to the new value. Copes with encapsulated 643d86bef48SAndrew Rybchenko * tags, and updates the encapsulating opt as necessary. 644d86bef48SAndrew Rybchenko */ 645d86bef48SAndrew Rybchenko __checkReturn efx_rc_t 646d86bef48SAndrew Rybchenko efx_dhcp_update_tag( 647d86bef48SAndrew Rybchenko __inout_bcount(buffer_length) uint8_t *bufferp, 648d86bef48SAndrew Rybchenko __in size_t buffer_length, 649d86bef48SAndrew Rybchenko __in uint16_t opt, 650d86bef48SAndrew Rybchenko __in uint8_t *value_locationp, 651d86bef48SAndrew Rybchenko __in_bcount_opt(value_length) uint8_t *valuep, 652d86bef48SAndrew Rybchenko __in size_t value_length) 653d86bef48SAndrew Rybchenko { 654d86bef48SAndrew Rybchenko efx_rc_t rc; 655d86bef48SAndrew Rybchenko uint8_t *write_pointp = value_locationp - sizeof (efx_dhcp_tag_hdr_t); 656d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp; 657d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *encap_hdrp = NULL; 658d86bef48SAndrew Rybchenko size_t old_length; 659d86bef48SAndrew Rybchenko 660d86bef48SAndrew Rybchenko if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) { 661d86bef48SAndrew Rybchenko rc = EINVAL; 662d86bef48SAndrew Rybchenko goto fail1; 663d86bef48SAndrew Rybchenko } 664d86bef48SAndrew Rybchenko 665d86bef48SAndrew Rybchenko if (value_length > DHCP_MAX_VALUE) { 666d86bef48SAndrew Rybchenko rc = EINVAL; 667d86bef48SAndrew Rybchenko goto fail2; 668d86bef48SAndrew Rybchenko } 669d86bef48SAndrew Rybchenko 670d86bef48SAndrew Rybchenko if ((value_length > 0) && (valuep == NULL)) { 671d86bef48SAndrew Rybchenko rc = EINVAL; 672d86bef48SAndrew Rybchenko goto fail3; 673d86bef48SAndrew Rybchenko } 674d86bef48SAndrew Rybchenko 675d86bef48SAndrew Rybchenko old_length = hdrp->length; 676d86bef48SAndrew Rybchenko 677d86bef48SAndrew Rybchenko if (old_length < value_length) { 678d86bef48SAndrew Rybchenko uint8_t *endp = bufferp; 679d86bef48SAndrew Rybchenko size_t available_space = buffer_length; 680d86bef48SAndrew Rybchenko 681d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&endp, &available_space, 682d86bef48SAndrew Rybchenko EFX_DHCP_END); 683d86bef48SAndrew Rybchenko if (rc != 0) 684d86bef48SAndrew Rybchenko goto fail4; 685d86bef48SAndrew Rybchenko 686d86bef48SAndrew Rybchenko if (available_space < (value_length - old_length)) { 687d86bef48SAndrew Rybchenko rc = EINVAL; 688d86bef48SAndrew Rybchenko goto fail5; 689d86bef48SAndrew Rybchenko } 690d86bef48SAndrew Rybchenko } 691d86bef48SAndrew Rybchenko 692d86bef48SAndrew Rybchenko if (DHCP_IS_ENCAP_OPT(opt)) { 693d86bef48SAndrew Rybchenko uint8_t *encapp = bufferp; 694d86bef48SAndrew Rybchenko size_t following_encap = buffer_length; 695d86bef48SAndrew Rybchenko size_t new_length; 696d86bef48SAndrew Rybchenko 697d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&encapp, &following_encap, 698d86bef48SAndrew Rybchenko DHCP_ENCAPSULATOR(opt)); 699d86bef48SAndrew Rybchenko if (rc != 0) 700d86bef48SAndrew Rybchenko goto fail6; 701d86bef48SAndrew Rybchenko 702d86bef48SAndrew Rybchenko encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp; 703d86bef48SAndrew Rybchenko 704d86bef48SAndrew Rybchenko new_length = ((size_t)encap_hdrp->length + 705d86bef48SAndrew Rybchenko value_length - old_length); 706d86bef48SAndrew Rybchenko /* Check encapsulator will not overflow */ 707d86bef48SAndrew Rybchenko if (new_length > DHCP_MAX_VALUE) { 708d86bef48SAndrew Rybchenko rc = E2BIG; 709d86bef48SAndrew Rybchenko goto fail7; 710d86bef48SAndrew Rybchenko } 711d86bef48SAndrew Rybchenko 712d86bef48SAndrew Rybchenko encap_hdrp->length = (uint8_t)new_length; 713d86bef48SAndrew Rybchenko } 714d86bef48SAndrew Rybchenko 715d86bef48SAndrew Rybchenko /* 716d86bef48SAndrew Rybchenko * Move the following data up/down to accommodate the new payload 717d86bef48SAndrew Rybchenko * length. 718d86bef48SAndrew Rybchenko */ 719d86bef48SAndrew Rybchenko if (old_length != value_length) { 720d86bef48SAndrew Rybchenko uint8_t *destp = (uint8_t *)DHCP_NEXT_TAG(hdrp) + 721d86bef48SAndrew Rybchenko value_length - old_length; 722d86bef48SAndrew Rybchenko size_t count = &bufferp[buffer_length] - 723d86bef48SAndrew Rybchenko (uint8_t *)DHCP_NEXT_TAG(hdrp); 724d86bef48SAndrew Rybchenko 725d86bef48SAndrew Rybchenko memmove(destp, DHCP_NEXT_TAG(hdrp), count); 726d86bef48SAndrew Rybchenko } 727d86bef48SAndrew Rybchenko 728d86bef48SAndrew Rybchenko EFSYS_ASSERT(hdrp->tag == DHCP_ENCAPSULATED(opt)); 729d86bef48SAndrew Rybchenko efx_dhcp_write_tag(write_pointp, opt, valuep, value_length); 730d86bef48SAndrew Rybchenko 731d86bef48SAndrew Rybchenko return (0); 732d86bef48SAndrew Rybchenko 733d86bef48SAndrew Rybchenko fail7: 734d86bef48SAndrew Rybchenko EFSYS_PROBE(fail7); 735d86bef48SAndrew Rybchenko fail6: 736d86bef48SAndrew Rybchenko EFSYS_PROBE(fail6); 737d86bef48SAndrew Rybchenko fail5: 738d86bef48SAndrew Rybchenko EFSYS_PROBE(fail5); 739d86bef48SAndrew Rybchenko fail4: 740d86bef48SAndrew Rybchenko EFSYS_PROBE(fail4); 741d86bef48SAndrew Rybchenko fail3: 742d86bef48SAndrew Rybchenko EFSYS_PROBE(fail3); 743d86bef48SAndrew Rybchenko fail2: 744d86bef48SAndrew Rybchenko EFSYS_PROBE(fail2); 745d86bef48SAndrew Rybchenko fail1: 746d86bef48SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 747d86bef48SAndrew Rybchenko 748d86bef48SAndrew Rybchenko return (rc); 749d86bef48SAndrew Rybchenko } 750d86bef48SAndrew Rybchenko 751d86bef48SAndrew Rybchenko /* 752662c835bSAndrew Rybchenko * Copy bootcfg sector data to a target buffer which may differ in size. 753662c835bSAndrew Rybchenko * Optionally corrects format errors in source buffer. 754662c835bSAndrew Rybchenko */ 755662c835bSAndrew Rybchenko efx_rc_t 756662c835bSAndrew Rybchenko efx_bootcfg_copy_sector( 757662c835bSAndrew Rybchenko __in efx_nic_t *enp, 758662c835bSAndrew Rybchenko __inout_bcount(sector_length) 759662c835bSAndrew Rybchenko uint8_t *sector, 760662c835bSAndrew Rybchenko __in size_t sector_length, 761662c835bSAndrew Rybchenko __out_bcount(data_size) uint8_t *data, 762662c835bSAndrew Rybchenko __in size_t data_size, 763662c835bSAndrew Rybchenko __in boolean_t handle_format_errors) 764662c835bSAndrew Rybchenko { 765d86bef48SAndrew Rybchenko _NOTE(ARGUNUSED(enp)) 766d86bef48SAndrew Rybchenko 767662c835bSAndrew Rybchenko size_t used_bytes; 768662c835bSAndrew Rybchenko efx_rc_t rc; 769662c835bSAndrew Rybchenko 770d86bef48SAndrew Rybchenko /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */ 771a98c7b04SAndrew Rybchenko if (data_size < 2) { 772a98c7b04SAndrew Rybchenko rc = ENOSPC; 773a98c7b04SAndrew Rybchenko goto fail1; 774a98c7b04SAndrew Rybchenko } 775a98c7b04SAndrew Rybchenko 776662c835bSAndrew Rybchenko /* Verify that the area is correctly formatted and checksummed */ 777d86bef48SAndrew Rybchenko rc = efx_dhcp_verify(sector, sector_length, 778662c835bSAndrew Rybchenko &used_bytes); 779662c835bSAndrew Rybchenko 780662c835bSAndrew Rybchenko if (!handle_format_errors) { 781662c835bSAndrew Rybchenko if (rc != 0) 782a98c7b04SAndrew Rybchenko goto fail2; 783662c835bSAndrew Rybchenko 784662c835bSAndrew Rybchenko if ((used_bytes < 2) || 785d86bef48SAndrew Rybchenko (sector[used_bytes - 1] != EFX_DHCP_END)) { 786d86bef48SAndrew Rybchenko /* Block too short, or EFX_DHCP_END missing */ 787662c835bSAndrew Rybchenko rc = ENOENT; 788a98c7b04SAndrew Rybchenko goto fail3; 789662c835bSAndrew Rybchenko } 790662c835bSAndrew Rybchenko } 791662c835bSAndrew Rybchenko 792662c835bSAndrew Rybchenko /* Synthesize empty format on verification failure */ 793662c835bSAndrew Rybchenko if (rc != 0 || used_bytes == 0) { 794662c835bSAndrew Rybchenko sector[0] = 0; 795d86bef48SAndrew Rybchenko sector[1] = EFX_DHCP_END; 796662c835bSAndrew Rybchenko used_bytes = 2; 797662c835bSAndrew Rybchenko } 798d86bef48SAndrew Rybchenko EFSYS_ASSERT(used_bytes >= 2); /* checksum and EFX_DHCP_END */ 799662c835bSAndrew Rybchenko EFSYS_ASSERT(used_bytes <= sector_length); 800662c835bSAndrew Rybchenko EFSYS_ASSERT(sector_length >= 2); 801662c835bSAndrew Rybchenko 802662c835bSAndrew Rybchenko /* 803d86bef48SAndrew Rybchenko * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END 804d86bef48SAndrew Rybchenko * character. Modify the returned payload so it does. 805662c835bSAndrew Rybchenko * Reinitialise the sector if there isn't room for the character. 806662c835bSAndrew Rybchenko */ 807d86bef48SAndrew Rybchenko if (sector[used_bytes - 1] != EFX_DHCP_END) { 808662c835bSAndrew Rybchenko if (used_bytes >= sector_length) { 809662c835bSAndrew Rybchenko sector[0] = 0; 810662c835bSAndrew Rybchenko used_bytes = 1; 811662c835bSAndrew Rybchenko } 812d86bef48SAndrew Rybchenko sector[used_bytes] = EFX_DHCP_END; 813662c835bSAndrew Rybchenko ++used_bytes; 814662c835bSAndrew Rybchenko } 815662c835bSAndrew Rybchenko 816662c835bSAndrew Rybchenko /* 817662c835bSAndrew Rybchenko * Verify that the target buffer is large enough for the 818662c835bSAndrew Rybchenko * entire used bootcfg area, then copy into the target buffer. 819662c835bSAndrew Rybchenko */ 820662c835bSAndrew Rybchenko if (used_bytes > data_size) { 821662c835bSAndrew Rybchenko rc = ENOSPC; 822a98c7b04SAndrew Rybchenko goto fail4; 823662c835bSAndrew Rybchenko } 824a98c7b04SAndrew Rybchenko 825a98c7b04SAndrew Rybchenko data[0] = 0; /* checksum, updated below */ 826a98c7b04SAndrew Rybchenko 827a98c7b04SAndrew Rybchenko /* Copy all after the checksum to the target buffer */ 828a98c7b04SAndrew Rybchenko memcpy(data + 1, sector + 1, used_bytes - 1); 829662c835bSAndrew Rybchenko 830662c835bSAndrew Rybchenko /* Zero out the unused portion of the target buffer */ 831662c835bSAndrew Rybchenko if (used_bytes < data_size) 832662c835bSAndrew Rybchenko (void) memset(data + used_bytes, 0, data_size - used_bytes); 833662c835bSAndrew Rybchenko 834662c835bSAndrew Rybchenko /* 835d86bef48SAndrew Rybchenko * The checksum includes trailing data after any EFX_DHCP_END 836d86bef48SAndrew Rybchenko * character, which we've just modified (by truncation or appending 837d86bef48SAndrew Rybchenko * EFX_DHCP_END). 838662c835bSAndrew Rybchenko */ 839d86bef48SAndrew Rybchenko data[0] -= efx_dhcp_csum(data, data_size); 840662c835bSAndrew Rybchenko 841662c835bSAndrew Rybchenko return (0); 842662c835bSAndrew Rybchenko 843a98c7b04SAndrew Rybchenko fail4: 844a98c7b04SAndrew Rybchenko EFSYS_PROBE(fail4); 845662c835bSAndrew Rybchenko fail3: 846662c835bSAndrew Rybchenko EFSYS_PROBE(fail3); 847662c835bSAndrew Rybchenko fail2: 848662c835bSAndrew Rybchenko EFSYS_PROBE(fail2); 849662c835bSAndrew Rybchenko fail1: 850662c835bSAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 851662c835bSAndrew Rybchenko 852662c835bSAndrew Rybchenko return (rc); 853662c835bSAndrew Rybchenko } 854662c835bSAndrew Rybchenko 855460cb568SAndrew Rybchenko efx_rc_t 856e948693eSPhilip Paeps efx_bootcfg_read( 857e948693eSPhilip Paeps __in efx_nic_t *enp, 8587e17c17dSAndrew Rybchenko __out_bcount(size) uint8_t *data, 859e948693eSPhilip Paeps __in size_t size) 860e948693eSPhilip Paeps { 861e948693eSPhilip Paeps uint8_t *payload = NULL; 862e948693eSPhilip Paeps size_t used_bytes; 8635081d55dSAndrew Rybchenko size_t partn_length; 864e948693eSPhilip Paeps size_t sector_length; 8655081d55dSAndrew Rybchenko size_t sector_offset; 866460cb568SAndrew Rybchenko efx_rc_t rc; 867662c835bSAndrew Rybchenko uint32_t sector_number; 868e948693eSPhilip Paeps 869d86bef48SAndrew Rybchenko /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */ 870a98c7b04SAndrew Rybchenko if (size < 2) { 871a98c7b04SAndrew Rybchenko rc = ENOSPC; 872a98c7b04SAndrew Rybchenko goto fail1; 873a98c7b04SAndrew Rybchenko } 874a98c7b04SAndrew Rybchenko 875df0385e5SAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 876662c835bSAndrew Rybchenko sector_number = enp->en_nic_cfg.enc_pf; 877662c835bSAndrew Rybchenko #else 878662c835bSAndrew Rybchenko sector_number = 0; 879662c835bSAndrew Rybchenko #endif 8805081d55dSAndrew Rybchenko rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 881e948693eSPhilip Paeps if (rc != 0) 882a98c7b04SAndrew Rybchenko goto fail2; 883e948693eSPhilip Paeps 8845081d55dSAndrew Rybchenko /* The bootcfg sector may be stored in a (larger) shared partition */ 885662c835bSAndrew Rybchenko rc = efx_bootcfg_sector_info(enp, sector_number, 886662c835bSAndrew Rybchenko NULL, §or_offset, §or_length); 8875081d55dSAndrew Rybchenko if (rc != 0) 888a98c7b04SAndrew Rybchenko goto fail3; 889a98c7b04SAndrew Rybchenko 890a98c7b04SAndrew Rybchenko if (sector_length < 2) { 891a98c7b04SAndrew Rybchenko rc = EINVAL; 892a98c7b04SAndrew Rybchenko goto fail4; 893a98c7b04SAndrew Rybchenko } 8945081d55dSAndrew Rybchenko 8955081d55dSAndrew Rybchenko if (sector_length > BOOTCFG_MAX_SIZE) 8965081d55dSAndrew Rybchenko sector_length = BOOTCFG_MAX_SIZE; 8975081d55dSAndrew Rybchenko 8985081d55dSAndrew Rybchenko if (sector_offset + sector_length > partn_length) { 8995081d55dSAndrew Rybchenko /* Partition is too small */ 9005081d55dSAndrew Rybchenko rc = EFBIG; 901a98c7b04SAndrew Rybchenko goto fail5; 9025081d55dSAndrew Rybchenko } 9035081d55dSAndrew Rybchenko 904e948693eSPhilip Paeps /* 905d86bef48SAndrew Rybchenko * We need to read the entire BOOTCFG sector to ensure we read all 906d86bef48SAndrew Rybchenko * tags, because legacy bootcfg sectors are not guaranteed to end 907d86bef48SAndrew Rybchenko * with an EFX_DHCP_END character. If the user hasn't supplied a 908d86bef48SAndrew Rybchenko * sufficiently large buffer then use our own buffer. 909e948693eSPhilip Paeps */ 910e948693eSPhilip Paeps if (sector_length > size) { 911e948693eSPhilip Paeps EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload); 912e948693eSPhilip Paeps if (payload == NULL) { 913e948693eSPhilip Paeps rc = ENOMEM; 914a98c7b04SAndrew Rybchenko goto fail6; 915e948693eSPhilip Paeps } 916e948693eSPhilip Paeps } else 917e948693eSPhilip Paeps payload = (uint8_t *)data; 918e948693eSPhilip Paeps 919e948693eSPhilip Paeps if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 920a98c7b04SAndrew Rybchenko goto fail7; 921e948693eSPhilip Paeps 922662c835bSAndrew Rybchenko if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 923662c835bSAndrew Rybchenko sector_offset, (caddr_t)payload, sector_length)) != 0) { 924d5106d05SAndrew Rybchenko (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 925a98c7b04SAndrew Rybchenko goto fail8; 926662c835bSAndrew Rybchenko } 927662c835bSAndrew Rybchenko 928d5106d05SAndrew Rybchenko if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 929a98c7b04SAndrew Rybchenko goto fail9; 930e948693eSPhilip Paeps 931e948693eSPhilip Paeps /* Verify that the area is correctly formatted and checksummed */ 932d86bef48SAndrew Rybchenko rc = efx_dhcp_verify(payload, sector_length, 933e948693eSPhilip Paeps &used_bytes); 934e948693eSPhilip Paeps if (rc != 0 || used_bytes == 0) { 935a98c7b04SAndrew Rybchenko payload[0] = 0; 936d86bef48SAndrew Rybchenko payload[1] = EFX_DHCP_END; 937e948693eSPhilip Paeps used_bytes = 2; 938e948693eSPhilip Paeps } 939e948693eSPhilip Paeps 940d86bef48SAndrew Rybchenko EFSYS_ASSERT(used_bytes >= 2); /* checksum and EFX_DHCP_END */ 941e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes <= sector_length); 942e948693eSPhilip Paeps 943e948693eSPhilip Paeps /* 944d86bef48SAndrew Rybchenko * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END 945d86bef48SAndrew Rybchenko * character. Modify the returned payload so it does. 946d86bef48SAndrew Rybchenko * BOOTCFG_MAX_SIZE is by definition large enough for any valid 947d86bef48SAndrew Rybchenko * (per-port) bootcfg sector, so reinitialise the sector if there 948d86bef48SAndrew Rybchenko * isn't room for the character. 949e948693eSPhilip Paeps */ 950d86bef48SAndrew Rybchenko if (payload[used_bytes - 1] != EFX_DHCP_END) { 951a98c7b04SAndrew Rybchenko if (used_bytes >= sector_length) 952e948693eSPhilip Paeps used_bytes = 1; 953e948693eSPhilip Paeps 954d86bef48SAndrew Rybchenko payload[used_bytes] = EFX_DHCP_END; 955e948693eSPhilip Paeps ++used_bytes; 956e948693eSPhilip Paeps } 957e948693eSPhilip Paeps 958e948693eSPhilip Paeps /* 959e948693eSPhilip Paeps * Verify that the user supplied buffer is large enough for the 960e948693eSPhilip Paeps * entire used bootcfg area, then copy into the user supplied buffer. 961e948693eSPhilip Paeps */ 962e948693eSPhilip Paeps if (used_bytes > size) { 963e948693eSPhilip Paeps rc = ENOSPC; 964a98c7b04SAndrew Rybchenko goto fail10; 965e948693eSPhilip Paeps } 966a98c7b04SAndrew Rybchenko 967a98c7b04SAndrew Rybchenko data[0] = 0; /* checksum, updated below */ 968a98c7b04SAndrew Rybchenko 969e948693eSPhilip Paeps if (sector_length > size) { 970a98c7b04SAndrew Rybchenko /* Copy all after the checksum to the target buffer */ 971a98c7b04SAndrew Rybchenko memcpy(data + 1, payload + 1, used_bytes - 1); 972e948693eSPhilip Paeps EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 973e948693eSPhilip Paeps } 974e948693eSPhilip Paeps 975e948693eSPhilip Paeps /* Zero out the unused portion of the user buffer */ 976e948693eSPhilip Paeps if (used_bytes < size) 977e948693eSPhilip Paeps (void) memset(data + used_bytes, 0, size - used_bytes); 978e948693eSPhilip Paeps 979e948693eSPhilip Paeps /* 980d86bef48SAndrew Rybchenko * The checksum includes trailing data after any EFX_DHCP_END character, 981d86bef48SAndrew Rybchenko * which we've just modified (by truncation or appending EFX_DHCP_END). 982e948693eSPhilip Paeps */ 983d86bef48SAndrew Rybchenko data[0] -= efx_dhcp_csum(data, size); 984e948693eSPhilip Paeps 985e948693eSPhilip Paeps return (0); 986e948693eSPhilip Paeps 987a98c7b04SAndrew Rybchenko fail10: 988a98c7b04SAndrew Rybchenko EFSYS_PROBE(fail10); 989a98c7b04SAndrew Rybchenko fail9: 990a98c7b04SAndrew Rybchenko EFSYS_PROBE(fail9); 991662c835bSAndrew Rybchenko fail8: 992662c835bSAndrew Rybchenko EFSYS_PROBE(fail8); 9935081d55dSAndrew Rybchenko fail7: 9945081d55dSAndrew Rybchenko EFSYS_PROBE(fail7); 995a98c7b04SAndrew Rybchenko if (sector_length > size) 996a98c7b04SAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 9975081d55dSAndrew Rybchenko fail6: 9985081d55dSAndrew Rybchenko EFSYS_PROBE(fail6); 999e948693eSPhilip Paeps fail5: 1000e948693eSPhilip Paeps EFSYS_PROBE(fail5); 1001e948693eSPhilip Paeps fail4: 1002e948693eSPhilip Paeps EFSYS_PROBE(fail4); 1003e948693eSPhilip Paeps fail3: 1004e948693eSPhilip Paeps EFSYS_PROBE(fail3); 1005e948693eSPhilip Paeps fail2: 1006e948693eSPhilip Paeps EFSYS_PROBE(fail2); 1007e948693eSPhilip Paeps fail1: 1008460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 1009e948693eSPhilip Paeps 1010e948693eSPhilip Paeps return (rc); 1011e948693eSPhilip Paeps } 1012e948693eSPhilip Paeps 1013460cb568SAndrew Rybchenko efx_rc_t 1014e948693eSPhilip Paeps efx_bootcfg_write( 1015e948693eSPhilip Paeps __in efx_nic_t *enp, 10167e17c17dSAndrew Rybchenko __in_bcount(size) uint8_t *data, 1017e948693eSPhilip Paeps __in size_t size) 1018e948693eSPhilip Paeps { 10195081d55dSAndrew Rybchenko uint8_t *partn_data; 1020e948693eSPhilip Paeps uint8_t checksum; 10215081d55dSAndrew Rybchenko size_t partn_length; 1022e948693eSPhilip Paeps size_t sector_length; 10235081d55dSAndrew Rybchenko size_t sector_offset; 1024e948693eSPhilip Paeps size_t used_bytes; 1025460cb568SAndrew Rybchenko efx_rc_t rc; 1026662c835bSAndrew Rybchenko uint32_t sector_number; 1027662c835bSAndrew Rybchenko 1028df0385e5SAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 1029662c835bSAndrew Rybchenko sector_number = enp->en_nic_cfg.enc_pf; 1030662c835bSAndrew Rybchenko #else 1031662c835bSAndrew Rybchenko sector_number = 0; 1032662c835bSAndrew Rybchenko #endif 1033e948693eSPhilip Paeps 10345081d55dSAndrew Rybchenko rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 1035e948693eSPhilip Paeps if (rc != 0) 1036e948693eSPhilip Paeps goto fail1; 1037e948693eSPhilip Paeps 10385081d55dSAndrew Rybchenko /* The bootcfg sector may be stored in a (larger) shared partition */ 1039662c835bSAndrew Rybchenko rc = efx_bootcfg_sector_info(enp, sector_number, 1040662c835bSAndrew Rybchenko NULL, §or_offset, §or_length); 10415081d55dSAndrew Rybchenko if (rc != 0) 10425081d55dSAndrew Rybchenko goto fail2; 10435081d55dSAndrew Rybchenko 1044e948693eSPhilip Paeps if (sector_length > BOOTCFG_MAX_SIZE) 1045e948693eSPhilip Paeps sector_length = BOOTCFG_MAX_SIZE; 1046e948693eSPhilip Paeps 10475081d55dSAndrew Rybchenko if (sector_offset + sector_length > partn_length) { 10485081d55dSAndrew Rybchenko /* Partition is too small */ 10495081d55dSAndrew Rybchenko rc = EFBIG; 10505081d55dSAndrew Rybchenko goto fail3; 10515081d55dSAndrew Rybchenko } 10525081d55dSAndrew Rybchenko 1053d86bef48SAndrew Rybchenko if ((rc = efx_dhcp_verify(data, size, &used_bytes)) != 0) 10545081d55dSAndrew Rybchenko goto fail4; 1055e948693eSPhilip Paeps 1056d86bef48SAndrew Rybchenko /* 1057d86bef48SAndrew Rybchenko * The caller *must* terminate their block with a EFX_DHCP_END 1058d86bef48SAndrew Rybchenko * character 1059d86bef48SAndrew Rybchenko */ 1060d86bef48SAndrew Rybchenko if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != 1061d86bef48SAndrew Rybchenko EFX_DHCP_END)) { 1062d86bef48SAndrew Rybchenko /* Block too short or EFX_DHCP_END missing */ 1063e948693eSPhilip Paeps rc = ENOENT; 10645081d55dSAndrew Rybchenko goto fail5; 1065e948693eSPhilip Paeps } 1066e948693eSPhilip Paeps 1067e948693eSPhilip Paeps /* Check that the hardware has support for this much data */ 1068e948693eSPhilip Paeps if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) { 1069e948693eSPhilip Paeps rc = ENOSPC; 1070e948693eSPhilip Paeps goto fail6; 1071e948693eSPhilip Paeps } 1072e948693eSPhilip Paeps 10735081d55dSAndrew Rybchenko /* 10745081d55dSAndrew Rybchenko * If the BOOTCFG sector is stored in a shared partition, then we must 10755081d55dSAndrew Rybchenko * read the whole partition and insert the updated bootcfg sector at the 10765081d55dSAndrew Rybchenko * correct offset. 10775081d55dSAndrew Rybchenko */ 10785081d55dSAndrew Rybchenko EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data); 10795081d55dSAndrew Rybchenko if (partn_data == NULL) { 10805081d55dSAndrew Rybchenko rc = ENOMEM; 1081e948693eSPhilip Paeps goto fail7; 10825081d55dSAndrew Rybchenko } 10835081d55dSAndrew Rybchenko 10845081d55dSAndrew Rybchenko rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 10855081d55dSAndrew Rybchenko if (rc != 0) 10865081d55dSAndrew Rybchenko goto fail8; 10875081d55dSAndrew Rybchenko 10885081d55dSAndrew Rybchenko /* Read the entire partition */ 10895081d55dSAndrew Rybchenko rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0, 10905081d55dSAndrew Rybchenko (caddr_t)partn_data, partn_length); 10915081d55dSAndrew Rybchenko if (rc != 0) 10925081d55dSAndrew Rybchenko goto fail9; 1093e948693eSPhilip Paeps 1094e948693eSPhilip Paeps /* 1095d86bef48SAndrew Rybchenko * Insert the BOOTCFG sector into the partition, Zero out all data 1096d86bef48SAndrew Rybchenko * after the EFX_DHCP_END tag, and adjust the checksum. 1097e948693eSPhilip Paeps */ 10985081d55dSAndrew Rybchenko (void) memset(partn_data + sector_offset, 0x0, sector_length); 10995081d55dSAndrew Rybchenko (void) memcpy(partn_data + sector_offset, data, used_bytes); 11005081d55dSAndrew Rybchenko 1101d86bef48SAndrew Rybchenko checksum = efx_dhcp_csum(data, used_bytes); 11025081d55dSAndrew Rybchenko partn_data[sector_offset] -= checksum; 1103e948693eSPhilip Paeps 11045081d55dSAndrew Rybchenko if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 11055081d55dSAndrew Rybchenko goto fail10; 1106e948693eSPhilip Paeps 1107e948693eSPhilip Paeps if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 1108662c835bSAndrew Rybchenko 0, (caddr_t)partn_data, partn_length)) != 0) 11095081d55dSAndrew Rybchenko goto fail11; 1110e948693eSPhilip Paeps 1111d5106d05SAndrew Rybchenko if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 1112662c835bSAndrew Rybchenko goto fail12; 1113e948693eSPhilip Paeps 11145081d55dSAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 1115e948693eSPhilip Paeps 1116e948693eSPhilip Paeps return (0); 1117e948693eSPhilip Paeps 1118662c835bSAndrew Rybchenko fail12: 1119662c835bSAndrew Rybchenko EFSYS_PROBE(fail12); 11205081d55dSAndrew Rybchenko fail11: 11215081d55dSAndrew Rybchenko EFSYS_PROBE(fail11); 11225081d55dSAndrew Rybchenko fail10: 11235081d55dSAndrew Rybchenko EFSYS_PROBE(fail10); 11245081d55dSAndrew Rybchenko fail9: 11255081d55dSAndrew Rybchenko EFSYS_PROBE(fail9); 1126e948693eSPhilip Paeps 1127d5106d05SAndrew Rybchenko (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 11285081d55dSAndrew Rybchenko fail8: 11295081d55dSAndrew Rybchenko EFSYS_PROBE(fail8); 11305081d55dSAndrew Rybchenko 11315081d55dSAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 11325081d55dSAndrew Rybchenko fail7: 11335081d55dSAndrew Rybchenko EFSYS_PROBE(fail7); 11345081d55dSAndrew Rybchenko fail6: 11355081d55dSAndrew Rybchenko EFSYS_PROBE(fail6); 1136e948693eSPhilip Paeps fail5: 1137e948693eSPhilip Paeps EFSYS_PROBE(fail5); 1138e948693eSPhilip Paeps fail4: 1139e948693eSPhilip Paeps EFSYS_PROBE(fail4); 1140e948693eSPhilip Paeps fail3: 1141e948693eSPhilip Paeps EFSYS_PROBE(fail3); 1142e948693eSPhilip Paeps fail2: 1143e948693eSPhilip Paeps EFSYS_PROBE(fail2); 1144e948693eSPhilip Paeps fail1: 1145460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 1146e948693eSPhilip Paeps 1147e948693eSPhilip Paeps return (rc); 1148e948693eSPhilip Paeps } 1149e948693eSPhilip Paeps 1150e948693eSPhilip Paeps #endif /* EFSYS_OPT_BOOTCFG */ 1151