1e948693eSPhilip Paeps /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 51*d86bef48SAndrew Rybchenko #define DHCP_OPT_HAS_VALUE(opt) \ 52*d86bef48SAndrew Rybchenko (((opt) > EFX_DHCP_PAD) && ((opt) < EFX_DHCP_END)) 53*d86bef48SAndrew Rybchenko 54*d86bef48SAndrew Rybchenko #define DHCP_MAX_VALUE 255 55*d86bef48SAndrew Rybchenko 56*d86bef48SAndrew Rybchenko #define DHCP_ENCAPSULATOR(encap_opt) ((encap_opt) >> 8) 57*d86bef48SAndrew Rybchenko #define DHCP_ENCAPSULATED(encap_opt) ((encap_opt) & 0xff) 58*d86bef48SAndrew Rybchenko #define DHCP_IS_ENCAP_OPT(opt) DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATOR(opt)) 59*d86bef48SAndrew Rybchenko 60*d86bef48SAndrew Rybchenko typedef struct efx_dhcp_tag_hdr_s { 61*d86bef48SAndrew Rybchenko uint8_t tag; 62*d86bef48SAndrew Rybchenko uint8_t length; 63*d86bef48SAndrew Rybchenko } efx_dhcp_tag_hdr_t; 64*d86bef48SAndrew Rybchenko 65*d86bef48SAndrew Rybchenko /* 66*d86bef48SAndrew Rybchenko * Length calculations for tags with value field. PAD and END 67*d86bef48SAndrew Rybchenko * have a fixed length of 1, with no length or value field. 68*d86bef48SAndrew Rybchenko */ 69*d86bef48SAndrew Rybchenko #define DHCP_FULL_TAG_LENGTH(hdr) \ 70*d86bef48SAndrew Rybchenko (sizeof (efx_dhcp_tag_hdr_t) + (hdr)->length) 71*d86bef48SAndrew Rybchenko 72*d86bef48SAndrew Rybchenko #define DHCP_NEXT_TAG(hdr) \ 73*d86bef48SAndrew Rybchenko ((efx_dhcp_tag_hdr_t *)(((uint8_t *)(hdr)) + \ 74*d86bef48SAndrew Rybchenko DHCP_FULL_TAG_LENGTH((hdr)))) 75*d86bef48SAndrew Rybchenko 76*d86bef48SAndrew Rybchenko #define DHCP_CALC_TAG_LENGTH(payload_len) \ 77*d86bef48SAndrew Rybchenko ((payload_len) + sizeof (efx_dhcp_tag_hdr_t)) 78e948693eSPhilip Paeps 795081d55dSAndrew Rybchenko 80662c835bSAndrew Rybchenko /* Report the layout of bootcfg sectors in NVRAM partition. */ 81662c835bSAndrew Rybchenko __checkReturn efx_rc_t 82662c835bSAndrew Rybchenko efx_bootcfg_sector_info( 835081d55dSAndrew Rybchenko __in efx_nic_t *enp, 84662c835bSAndrew Rybchenko __in uint32_t pf, 85662c835bSAndrew Rybchenko __out_opt uint32_t *sector_countp, 865081d55dSAndrew Rybchenko __out size_t *offsetp, 875081d55dSAndrew Rybchenko __out size_t *max_sizep) 885081d55dSAndrew Rybchenko { 89662c835bSAndrew Rybchenko uint32_t count; 905081d55dSAndrew Rybchenko size_t max_size; 915081d55dSAndrew Rybchenko size_t offset; 925081d55dSAndrew Rybchenko int rc; 935081d55dSAndrew Rybchenko 945081d55dSAndrew Rybchenko switch (enp->en_family) { 955081d55dSAndrew Rybchenko #if EFSYS_OPT_SIENA 965081d55dSAndrew Rybchenko case EFX_FAMILY_SIENA: 975081d55dSAndrew Rybchenko max_size = BOOTCFG_MAX_SIZE; 985081d55dSAndrew Rybchenko offset = 0; 99662c835bSAndrew Rybchenko count = 1; 1005081d55dSAndrew Rybchenko break; 1015081d55dSAndrew Rybchenko #endif /* EFSYS_OPT_SIENA */ 1025081d55dSAndrew Rybchenko 1035081d55dSAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON 1045081d55dSAndrew Rybchenko case EFX_FAMILY_HUNTINGTON: 1055081d55dSAndrew Rybchenko max_size = BOOTCFG_MAX_SIZE; 1065081d55dSAndrew Rybchenko offset = 0; 107662c835bSAndrew Rybchenko count = 1; 1085081d55dSAndrew Rybchenko break; 1095081d55dSAndrew Rybchenko #endif /* EFSYS_OPT_HUNTINGTON */ 1105081d55dSAndrew Rybchenko 1115081d55dSAndrew Rybchenko #if EFSYS_OPT_MEDFORD 1125081d55dSAndrew Rybchenko case EFX_FAMILY_MEDFORD: { 1135081d55dSAndrew Rybchenko /* Shared partition (array indexed by PF) */ 114662c835bSAndrew Rybchenko max_size = BOOTCFG_PER_PF; 115662c835bSAndrew Rybchenko count = BOOTCFG_PF_COUNT; 116662c835bSAndrew Rybchenko if (pf >= count) { 117662c835bSAndrew Rybchenko rc = EINVAL; 118662c835bSAndrew Rybchenko goto fail2; 119662c835bSAndrew Rybchenko } 120662c835bSAndrew Rybchenko offset = max_size * pf; 1215081d55dSAndrew Rybchenko break; 1225081d55dSAndrew Rybchenko } 1235081d55dSAndrew Rybchenko #endif /* EFSYS_OPT_MEDFORD */ 1245081d55dSAndrew Rybchenko 125df0385e5SAndrew Rybchenko #if EFSYS_OPT_MEDFORD2 126df0385e5SAndrew Rybchenko case EFX_FAMILY_MEDFORD2: { 127df0385e5SAndrew Rybchenko /* Shared partition (array indexed by PF) */ 128df0385e5SAndrew Rybchenko max_size = BOOTCFG_PER_PF; 129df0385e5SAndrew Rybchenko count = BOOTCFG_PF_COUNT; 130df0385e5SAndrew Rybchenko if (pf >= count) { 131df0385e5SAndrew Rybchenko rc = EINVAL; 132df0385e5SAndrew Rybchenko goto fail3; 133df0385e5SAndrew Rybchenko } 134df0385e5SAndrew Rybchenko offset = max_size * pf; 135df0385e5SAndrew Rybchenko break; 136df0385e5SAndrew Rybchenko } 137df0385e5SAndrew Rybchenko #endif /* EFSYS_OPT_MEDFORD2 */ 138df0385e5SAndrew Rybchenko 1395081d55dSAndrew Rybchenko default: 1405081d55dSAndrew Rybchenko EFSYS_ASSERT(0); 1415081d55dSAndrew Rybchenko rc = ENOTSUP; 1425081d55dSAndrew Rybchenko goto fail1; 1435081d55dSAndrew Rybchenko } 1445081d55dSAndrew Rybchenko EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE); 1455081d55dSAndrew Rybchenko 146662c835bSAndrew Rybchenko if (sector_countp != NULL) 147662c835bSAndrew Rybchenko *sector_countp = count; 1485081d55dSAndrew Rybchenko *offsetp = offset; 1495081d55dSAndrew Rybchenko *max_sizep = max_size; 1505081d55dSAndrew Rybchenko 1515081d55dSAndrew Rybchenko return (0); 1525081d55dSAndrew Rybchenko 153df0385e5SAndrew Rybchenko #if EFSYS_OPT_MEDFORD2 154df0385e5SAndrew Rybchenko fail3: 155df0385e5SAndrew Rybchenko EFSYS_PROBE(fail3); 156df0385e5SAndrew Rybchenko #endif 157662c835bSAndrew Rybchenko #if EFSYS_OPT_MEDFORD 158662c835bSAndrew Rybchenko fail2: 159662c835bSAndrew Rybchenko EFSYS_PROBE(fail2); 160662c835bSAndrew Rybchenko #endif 1615081d55dSAndrew Rybchenko fail1: 1625081d55dSAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 1635081d55dSAndrew Rybchenko return (rc); 1645081d55dSAndrew Rybchenko } 1655081d55dSAndrew Rybchenko 1665081d55dSAndrew Rybchenko 167*d86bef48SAndrew Rybchenko __checkReturn uint8_t 168*d86bef48SAndrew Rybchenko efx_dhcp_csum( 169662c835bSAndrew Rybchenko __in_bcount(size) uint8_t const *data, 170e948693eSPhilip Paeps __in size_t size) 171e948693eSPhilip Paeps { 172e948693eSPhilip Paeps unsigned int pos; 173e948693eSPhilip Paeps uint8_t checksum = 0; 174e948693eSPhilip Paeps 175e948693eSPhilip Paeps for (pos = 0; pos < size; pos++) 176e948693eSPhilip Paeps checksum += data[pos]; 177e948693eSPhilip Paeps return (checksum); 178e948693eSPhilip Paeps } 179e948693eSPhilip Paeps 180*d86bef48SAndrew Rybchenko __checkReturn efx_rc_t 181*d86bef48SAndrew Rybchenko efx_dhcp_verify( 182662c835bSAndrew Rybchenko __in_bcount(size) uint8_t const *data, 183e948693eSPhilip Paeps __in size_t size, 1843c838a9fSAndrew Rybchenko __out_opt size_t *usedp) 185e948693eSPhilip Paeps { 186e948693eSPhilip Paeps size_t offset = 0; 187e948693eSPhilip Paeps size_t used = 0; 188460cb568SAndrew Rybchenko efx_rc_t rc; 189e948693eSPhilip Paeps 190453130d9SPedro F. Giffuni /* Start parsing tags immediately after the checksum */ 191e948693eSPhilip Paeps for (offset = 1; offset < size; ) { 192e948693eSPhilip Paeps uint8_t tag; 193e948693eSPhilip Paeps uint8_t length; 194e948693eSPhilip Paeps 195e948693eSPhilip Paeps /* Consume tag */ 196e948693eSPhilip Paeps tag = data[offset]; 197*d86bef48SAndrew Rybchenko if (tag == EFX_DHCP_END) { 198e948693eSPhilip Paeps offset++; 199e948693eSPhilip Paeps used = offset; 200e948693eSPhilip Paeps break; 201e948693eSPhilip Paeps } 202*d86bef48SAndrew Rybchenko if (tag == EFX_DHCP_PAD) { 203e948693eSPhilip Paeps offset++; 204e948693eSPhilip Paeps continue; 205e948693eSPhilip Paeps } 206e948693eSPhilip Paeps 207e948693eSPhilip Paeps /* Consume length */ 208e948693eSPhilip Paeps if (offset + 1 >= size) { 209e948693eSPhilip Paeps rc = ENOSPC; 210e948693eSPhilip Paeps goto fail1; 211e948693eSPhilip Paeps } 212e948693eSPhilip Paeps length = data[offset + 1]; 213e948693eSPhilip Paeps 214e948693eSPhilip Paeps /* Consume *length */ 215e948693eSPhilip Paeps if (offset + 1 + length >= size) { 216e948693eSPhilip Paeps rc = ENOSPC; 217e948693eSPhilip Paeps goto fail2; 218e948693eSPhilip Paeps } 219e948693eSPhilip Paeps 220e948693eSPhilip Paeps offset += 2 + length; 221e948693eSPhilip Paeps used = offset; 222e948693eSPhilip Paeps } 223e948693eSPhilip Paeps 224*d86bef48SAndrew Rybchenko /* Checksum the entire sector, including bytes after any EFX_DHCP_END */ 225*d86bef48SAndrew Rybchenko if (efx_dhcp_csum(data, size) != 0) { 226e948693eSPhilip Paeps rc = EINVAL; 227e948693eSPhilip Paeps goto fail3; 228e948693eSPhilip Paeps } 229e948693eSPhilip Paeps 230e948693eSPhilip Paeps if (usedp != NULL) 231e948693eSPhilip Paeps *usedp = used; 232e948693eSPhilip Paeps 233e948693eSPhilip Paeps return (0); 234e948693eSPhilip Paeps 235e948693eSPhilip Paeps fail3: 236e948693eSPhilip Paeps EFSYS_PROBE(fail3); 237e948693eSPhilip Paeps fail2: 238e948693eSPhilip Paeps EFSYS_PROBE(fail2); 239e948693eSPhilip Paeps fail1: 240460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 241e948693eSPhilip Paeps 242e948693eSPhilip Paeps return (rc); 243e948693eSPhilip Paeps } 244e948693eSPhilip Paeps 245662c835bSAndrew Rybchenko /* 246*d86bef48SAndrew Rybchenko * Walk the entire tag set looking for option. The sought option may be 247*d86bef48SAndrew Rybchenko * encapsulated. ENOENT indicates the walk completed without finding the 248*d86bef48SAndrew Rybchenko * option. If we run out of buffer during the walk the function will return 249*d86bef48SAndrew Rybchenko * ENOSPC. 250*d86bef48SAndrew Rybchenko */ 251*d86bef48SAndrew Rybchenko static efx_rc_t 252*d86bef48SAndrew Rybchenko efx_dhcp_walk_tags( 253*d86bef48SAndrew Rybchenko __deref_inout uint8_t **tagpp, 254*d86bef48SAndrew Rybchenko __inout size_t *buffer_sizep, 255*d86bef48SAndrew Rybchenko __in uint16_t opt) 256*d86bef48SAndrew Rybchenko { 257*d86bef48SAndrew Rybchenko efx_rc_t rc = 0; 258*d86bef48SAndrew Rybchenko boolean_t is_encap = B_FALSE; 259*d86bef48SAndrew Rybchenko 260*d86bef48SAndrew Rybchenko if (DHCP_IS_ENCAP_OPT(opt)) { 261*d86bef48SAndrew Rybchenko /* 262*d86bef48SAndrew Rybchenko * Look for the encapsulator and, if found, limit ourselves 263*d86bef48SAndrew Rybchenko * to its payload. If it's not found then the entire tag 264*d86bef48SAndrew Rybchenko * cannot be found, so the encapsulated opt search is 265*d86bef48SAndrew Rybchenko * skipped. 266*d86bef48SAndrew Rybchenko */ 267*d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(tagpp, buffer_sizep, 268*d86bef48SAndrew Rybchenko DHCP_ENCAPSULATOR(opt)); 269*d86bef48SAndrew Rybchenko if (rc == 0) { 270*d86bef48SAndrew Rybchenko *buffer_sizep = ((efx_dhcp_tag_hdr_t *)*tagpp)->length; 271*d86bef48SAndrew Rybchenko (*tagpp) += sizeof (efx_dhcp_tag_hdr_t); 272*d86bef48SAndrew Rybchenko } 273*d86bef48SAndrew Rybchenko opt = DHCP_ENCAPSULATED(opt); 274*d86bef48SAndrew Rybchenko is_encap = B_TRUE; 275*d86bef48SAndrew Rybchenko } 276*d86bef48SAndrew Rybchenko 277*d86bef48SAndrew Rybchenko EFSYS_ASSERT(!DHCP_IS_ENCAP_OPT(opt)); 278*d86bef48SAndrew Rybchenko 279*d86bef48SAndrew Rybchenko while (rc == 0) { 280*d86bef48SAndrew Rybchenko size_t size; 281*d86bef48SAndrew Rybchenko 282*d86bef48SAndrew Rybchenko if (*buffer_sizep == 0) { 283*d86bef48SAndrew Rybchenko rc = ENOSPC; 284*d86bef48SAndrew Rybchenko goto fail1; 285*d86bef48SAndrew Rybchenko } 286*d86bef48SAndrew Rybchenko 287*d86bef48SAndrew Rybchenko if (DHCP_ENCAPSULATED(**tagpp) == opt) 288*d86bef48SAndrew Rybchenko break; 289*d86bef48SAndrew Rybchenko 290*d86bef48SAndrew Rybchenko if ((**tagpp) == EFX_DHCP_END) { 291*d86bef48SAndrew Rybchenko rc = ENOENT; 292*d86bef48SAndrew Rybchenko break; 293*d86bef48SAndrew Rybchenko } else if ((**tagpp) == EFX_DHCP_PAD) { 294*d86bef48SAndrew Rybchenko size = 1; 295*d86bef48SAndrew Rybchenko } else { 296*d86bef48SAndrew Rybchenko if (*buffer_sizep < sizeof (efx_dhcp_tag_hdr_t)) { 297*d86bef48SAndrew Rybchenko rc = ENOSPC; 298*d86bef48SAndrew Rybchenko goto fail2; 299*d86bef48SAndrew Rybchenko } 300*d86bef48SAndrew Rybchenko 301*d86bef48SAndrew Rybchenko size = 302*d86bef48SAndrew Rybchenko DHCP_FULL_TAG_LENGTH((efx_dhcp_tag_hdr_t *)*tagpp); 303*d86bef48SAndrew Rybchenko } 304*d86bef48SAndrew Rybchenko 305*d86bef48SAndrew Rybchenko if (size > *buffer_sizep) { 306*d86bef48SAndrew Rybchenko rc = ENOSPC; 307*d86bef48SAndrew Rybchenko goto fail3; 308*d86bef48SAndrew Rybchenko } 309*d86bef48SAndrew Rybchenko 310*d86bef48SAndrew Rybchenko (*tagpp) += size; 311*d86bef48SAndrew Rybchenko (*buffer_sizep) -= size; 312*d86bef48SAndrew Rybchenko 313*d86bef48SAndrew Rybchenko if ((*buffer_sizep == 0) && is_encap) { 314*d86bef48SAndrew Rybchenko /* Search within encapulator tag finished */ 315*d86bef48SAndrew Rybchenko rc = ENOENT; 316*d86bef48SAndrew Rybchenko break; 317*d86bef48SAndrew Rybchenko } 318*d86bef48SAndrew Rybchenko } 319*d86bef48SAndrew Rybchenko 320*d86bef48SAndrew Rybchenko /* 321*d86bef48SAndrew Rybchenko * Returns 0 if found otherwise ENOENT indicating search finished 322*d86bef48SAndrew Rybchenko * correctly 323*d86bef48SAndrew Rybchenko */ 324*d86bef48SAndrew Rybchenko return (rc); 325*d86bef48SAndrew Rybchenko 326*d86bef48SAndrew Rybchenko fail3: 327*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail3); 328*d86bef48SAndrew Rybchenko fail2: 329*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail2); 330*d86bef48SAndrew Rybchenko fail1: 331*d86bef48SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 332*d86bef48SAndrew Rybchenko 333*d86bef48SAndrew Rybchenko return (rc); 334*d86bef48SAndrew Rybchenko } 335*d86bef48SAndrew Rybchenko 336*d86bef48SAndrew Rybchenko /* 337*d86bef48SAndrew Rybchenko * Locate value buffer for option in the given buffer. 338*d86bef48SAndrew Rybchenko * Returns 0 if found, ENOENT indicating search finished 339*d86bef48SAndrew Rybchenko * correctly, otherwise search failed before completion. 340*d86bef48SAndrew Rybchenko */ 341*d86bef48SAndrew Rybchenko __checkReturn efx_rc_t 342*d86bef48SAndrew Rybchenko efx_dhcp_find_tag( 343*d86bef48SAndrew Rybchenko __in_bcount(buffer_length) uint8_t *bufferp, 344*d86bef48SAndrew Rybchenko __in size_t buffer_length, 345*d86bef48SAndrew Rybchenko __in uint16_t opt, 346*d86bef48SAndrew Rybchenko __deref_out uint8_t **valuepp, 347*d86bef48SAndrew Rybchenko __out size_t *value_lengthp) 348*d86bef48SAndrew Rybchenko { 349*d86bef48SAndrew Rybchenko efx_rc_t rc; 350*d86bef48SAndrew Rybchenko uint8_t *tagp = bufferp; 351*d86bef48SAndrew Rybchenko size_t len = buffer_length; 352*d86bef48SAndrew Rybchenko 353*d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&tagp, &len, opt); 354*d86bef48SAndrew Rybchenko if (rc == 0) { 355*d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *hdrp; 356*d86bef48SAndrew Rybchenko 357*d86bef48SAndrew Rybchenko hdrp = (efx_dhcp_tag_hdr_t *)tagp; 358*d86bef48SAndrew Rybchenko *valuepp = (uint8_t *)(&hdrp[1]); 359*d86bef48SAndrew Rybchenko *value_lengthp = hdrp->length; 360*d86bef48SAndrew Rybchenko } else if (rc != ENOENT) { 361*d86bef48SAndrew Rybchenko goto fail1; 362*d86bef48SAndrew Rybchenko } 363*d86bef48SAndrew Rybchenko 364*d86bef48SAndrew Rybchenko return (rc); 365*d86bef48SAndrew Rybchenko 366*d86bef48SAndrew Rybchenko fail1: 367*d86bef48SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 368*d86bef48SAndrew Rybchenko 369*d86bef48SAndrew Rybchenko return (rc); 370*d86bef48SAndrew Rybchenko } 371*d86bef48SAndrew Rybchenko 372*d86bef48SAndrew Rybchenko /* 373*d86bef48SAndrew Rybchenko * Locate the end tag in the given buffer. 374*d86bef48SAndrew Rybchenko * Returns 0 if found, ENOENT indicating search finished 375*d86bef48SAndrew Rybchenko * correctly but end tag was not found; otherwise search 376*d86bef48SAndrew Rybchenko * failed before completion. 377*d86bef48SAndrew Rybchenko */ 378*d86bef48SAndrew Rybchenko __checkReturn efx_rc_t 379*d86bef48SAndrew Rybchenko efx_dhcp_find_end( 380*d86bef48SAndrew Rybchenko __in_bcount(buffer_length) uint8_t *bufferp, 381*d86bef48SAndrew Rybchenko __in size_t buffer_length, 382*d86bef48SAndrew Rybchenko __deref_out uint8_t **endpp) 383*d86bef48SAndrew Rybchenko { 384*d86bef48SAndrew Rybchenko efx_rc_t rc; 385*d86bef48SAndrew Rybchenko uint8_t *endp = bufferp; 386*d86bef48SAndrew Rybchenko size_t len = buffer_length; 387*d86bef48SAndrew Rybchenko 388*d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&endp, &len, EFX_DHCP_END); 389*d86bef48SAndrew Rybchenko if (rc == 0) 390*d86bef48SAndrew Rybchenko *endpp = endp; 391*d86bef48SAndrew Rybchenko else if (rc != ENOENT) 392*d86bef48SAndrew Rybchenko goto fail1; 393*d86bef48SAndrew Rybchenko 394*d86bef48SAndrew Rybchenko return (rc); 395*d86bef48SAndrew Rybchenko 396*d86bef48SAndrew Rybchenko fail1: 397*d86bef48SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 398*d86bef48SAndrew Rybchenko 399*d86bef48SAndrew Rybchenko return (rc); 400*d86bef48SAndrew Rybchenko } 401*d86bef48SAndrew Rybchenko 402*d86bef48SAndrew Rybchenko 403*d86bef48SAndrew Rybchenko /* 404*d86bef48SAndrew Rybchenko * Delete the given tag from anywhere in the buffer. Copes with 405*d86bef48SAndrew Rybchenko * encapsulated tags, and updates or deletes the encapsulating opt as 406*d86bef48SAndrew Rybchenko * necessary. 407*d86bef48SAndrew Rybchenko */ 408*d86bef48SAndrew Rybchenko __checkReturn efx_rc_t 409*d86bef48SAndrew Rybchenko efx_dhcp_delete_tag( 410*d86bef48SAndrew Rybchenko __inout_bcount(buffer_length) uint8_t *bufferp, 411*d86bef48SAndrew Rybchenko __in size_t buffer_length, 412*d86bef48SAndrew Rybchenko __in uint16_t opt) 413*d86bef48SAndrew Rybchenko { 414*d86bef48SAndrew Rybchenko efx_rc_t rc; 415*d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *hdrp; 416*d86bef48SAndrew Rybchenko size_t len; 417*d86bef48SAndrew Rybchenko uint8_t *startp; 418*d86bef48SAndrew Rybchenko uint8_t *endp; 419*d86bef48SAndrew Rybchenko 420*d86bef48SAndrew Rybchenko len = buffer_length; 421*d86bef48SAndrew Rybchenko startp = bufferp; 422*d86bef48SAndrew Rybchenko 423*d86bef48SAndrew Rybchenko if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) { 424*d86bef48SAndrew Rybchenko rc = EINVAL; 425*d86bef48SAndrew Rybchenko goto fail1; 426*d86bef48SAndrew Rybchenko } 427*d86bef48SAndrew Rybchenko 428*d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&startp, &len, opt); 429*d86bef48SAndrew Rybchenko if (rc != 0) 430*d86bef48SAndrew Rybchenko goto fail1; 431*d86bef48SAndrew Rybchenko 432*d86bef48SAndrew Rybchenko hdrp = (efx_dhcp_tag_hdr_t *)startp; 433*d86bef48SAndrew Rybchenko 434*d86bef48SAndrew Rybchenko if (DHCP_IS_ENCAP_OPT(opt)) { 435*d86bef48SAndrew Rybchenko uint8_t tag_length = DHCP_FULL_TAG_LENGTH(hdrp); 436*d86bef48SAndrew Rybchenko uint8_t *encapp = bufferp; 437*d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *encap_hdrp; 438*d86bef48SAndrew Rybchenko 439*d86bef48SAndrew Rybchenko len = buffer_length; 440*d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&encapp, &len, 441*d86bef48SAndrew Rybchenko DHCP_ENCAPSULATOR(opt)); 442*d86bef48SAndrew Rybchenko if (rc != 0) 443*d86bef48SAndrew Rybchenko goto fail2; 444*d86bef48SAndrew Rybchenko 445*d86bef48SAndrew Rybchenko encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp; 446*d86bef48SAndrew Rybchenko if (encap_hdrp->length > tag_length) { 447*d86bef48SAndrew Rybchenko encap_hdrp->length = (uint8_t)( 448*d86bef48SAndrew Rybchenko (size_t)encap_hdrp->length - tag_length); 449*d86bef48SAndrew Rybchenko } else { 450*d86bef48SAndrew Rybchenko /* delete the encapsulating tag */ 451*d86bef48SAndrew Rybchenko hdrp = encap_hdrp; 452*d86bef48SAndrew Rybchenko } 453*d86bef48SAndrew Rybchenko } 454*d86bef48SAndrew Rybchenko 455*d86bef48SAndrew Rybchenko startp = (uint8_t *)hdrp; 456*d86bef48SAndrew Rybchenko endp = (uint8_t *)DHCP_NEXT_TAG(hdrp); 457*d86bef48SAndrew Rybchenko 458*d86bef48SAndrew Rybchenko if (startp < bufferp) { 459*d86bef48SAndrew Rybchenko rc = EINVAL; 460*d86bef48SAndrew Rybchenko goto fail3; 461*d86bef48SAndrew Rybchenko } 462*d86bef48SAndrew Rybchenko 463*d86bef48SAndrew Rybchenko if (endp > &bufferp[buffer_length]) { 464*d86bef48SAndrew Rybchenko rc = EINVAL; 465*d86bef48SAndrew Rybchenko goto fail4; 466*d86bef48SAndrew Rybchenko } 467*d86bef48SAndrew Rybchenko 468*d86bef48SAndrew Rybchenko memmove(startp, endp, 469*d86bef48SAndrew Rybchenko buffer_length - (endp - bufferp)); 470*d86bef48SAndrew Rybchenko 471*d86bef48SAndrew Rybchenko return (0); 472*d86bef48SAndrew Rybchenko 473*d86bef48SAndrew Rybchenko fail4: 474*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail4); 475*d86bef48SAndrew Rybchenko fail3: 476*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail3); 477*d86bef48SAndrew Rybchenko fail2: 478*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail2); 479*d86bef48SAndrew Rybchenko fail1: 480*d86bef48SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 481*d86bef48SAndrew Rybchenko 482*d86bef48SAndrew Rybchenko return (rc); 483*d86bef48SAndrew Rybchenko } 484*d86bef48SAndrew Rybchenko 485*d86bef48SAndrew Rybchenko /* 486*d86bef48SAndrew Rybchenko * Write the tag header into write_pointp and optionally copies the payload 487*d86bef48SAndrew Rybchenko * into the space following. 488*d86bef48SAndrew Rybchenko */ 489*d86bef48SAndrew Rybchenko static void 490*d86bef48SAndrew Rybchenko efx_dhcp_write_tag( 491*d86bef48SAndrew Rybchenko __in uint8_t *write_pointp, 492*d86bef48SAndrew Rybchenko __in uint16_t opt, 493*d86bef48SAndrew Rybchenko __in_bcount_opt(value_length) 494*d86bef48SAndrew Rybchenko uint8_t *valuep, 495*d86bef48SAndrew Rybchenko __in size_t value_length) 496*d86bef48SAndrew Rybchenko { 497*d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp; 498*d86bef48SAndrew Rybchenko hdrp->tag = DHCP_ENCAPSULATED(opt); 499*d86bef48SAndrew Rybchenko hdrp->length = (uint8_t)value_length; 500*d86bef48SAndrew Rybchenko if ((value_length > 0) && (valuep != NULL)) 501*d86bef48SAndrew Rybchenko memcpy(&hdrp[1], valuep, value_length); 502*d86bef48SAndrew Rybchenko } 503*d86bef48SAndrew Rybchenko 504*d86bef48SAndrew Rybchenko /* 505*d86bef48SAndrew Rybchenko * Add the given tag to the end of the buffer. Copes with creating an 506*d86bef48SAndrew Rybchenko * encapsulated tag, and updates or creates the encapsulating opt as 507*d86bef48SAndrew Rybchenko * necessary. 508*d86bef48SAndrew Rybchenko */ 509*d86bef48SAndrew Rybchenko __checkReturn efx_rc_t 510*d86bef48SAndrew Rybchenko efx_dhcp_add_tag( 511*d86bef48SAndrew Rybchenko __inout_bcount(buffer_length) uint8_t *bufferp, 512*d86bef48SAndrew Rybchenko __in size_t buffer_length, 513*d86bef48SAndrew Rybchenko __in uint16_t opt, 514*d86bef48SAndrew Rybchenko __in_bcount_opt(value_length) uint8_t *valuep, 515*d86bef48SAndrew Rybchenko __in size_t value_length) 516*d86bef48SAndrew Rybchenko { 517*d86bef48SAndrew Rybchenko efx_rc_t rc; 518*d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *encap_hdrp = NULL; 519*d86bef48SAndrew Rybchenko uint8_t *insert_pointp = NULL; 520*d86bef48SAndrew Rybchenko uint8_t *endp; 521*d86bef48SAndrew Rybchenko size_t available_space; 522*d86bef48SAndrew Rybchenko size_t added_length; 523*d86bef48SAndrew Rybchenko size_t search_size; 524*d86bef48SAndrew Rybchenko uint8_t *searchp; 525*d86bef48SAndrew Rybchenko 526*d86bef48SAndrew Rybchenko if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) { 527*d86bef48SAndrew Rybchenko rc = EINVAL; 528*d86bef48SAndrew Rybchenko goto fail1; 529*d86bef48SAndrew Rybchenko } 530*d86bef48SAndrew Rybchenko 531*d86bef48SAndrew Rybchenko if (value_length > DHCP_MAX_VALUE) { 532*d86bef48SAndrew Rybchenko rc = EINVAL; 533*d86bef48SAndrew Rybchenko goto fail2; 534*d86bef48SAndrew Rybchenko } 535*d86bef48SAndrew Rybchenko 536*d86bef48SAndrew Rybchenko if ((value_length > 0) && (valuep == NULL)) { 537*d86bef48SAndrew Rybchenko rc = EINVAL; 538*d86bef48SAndrew Rybchenko goto fail3; 539*d86bef48SAndrew Rybchenko } 540*d86bef48SAndrew Rybchenko 541*d86bef48SAndrew Rybchenko endp = bufferp; 542*d86bef48SAndrew Rybchenko available_space = buffer_length; 543*d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&endp, &available_space, EFX_DHCP_END); 544*d86bef48SAndrew Rybchenko if (rc != 0) 545*d86bef48SAndrew Rybchenko goto fail4; 546*d86bef48SAndrew Rybchenko 547*d86bef48SAndrew Rybchenko searchp = bufferp; 548*d86bef48SAndrew Rybchenko search_size = buffer_length; 549*d86bef48SAndrew Rybchenko if (DHCP_IS_ENCAP_OPT(opt)) { 550*d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&searchp, &search_size, 551*d86bef48SAndrew Rybchenko DHCP_ENCAPSULATOR(opt)); 552*d86bef48SAndrew Rybchenko if (rc == 0) { 553*d86bef48SAndrew Rybchenko encap_hdrp = (efx_dhcp_tag_hdr_t *)searchp; 554*d86bef48SAndrew Rybchenko 555*d86bef48SAndrew Rybchenko /* Check encapsulated tag is not present */ 556*d86bef48SAndrew Rybchenko search_size = encap_hdrp->length; 557*d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&searchp, &search_size, 558*d86bef48SAndrew Rybchenko opt); 559*d86bef48SAndrew Rybchenko if (rc != ENOENT) { 560*d86bef48SAndrew Rybchenko rc = EINVAL; 561*d86bef48SAndrew Rybchenko goto fail5; 562*d86bef48SAndrew Rybchenko } 563*d86bef48SAndrew Rybchenko 564*d86bef48SAndrew Rybchenko /* Check encapsulator will not overflow */ 565*d86bef48SAndrew Rybchenko if (((size_t)encap_hdrp->length + 566*d86bef48SAndrew Rybchenko DHCP_CALC_TAG_LENGTH(value_length)) > 567*d86bef48SAndrew Rybchenko DHCP_MAX_VALUE) { 568*d86bef48SAndrew Rybchenko rc = E2BIG; 569*d86bef48SAndrew Rybchenko goto fail6; 570*d86bef48SAndrew Rybchenko } 571*d86bef48SAndrew Rybchenko 572*d86bef48SAndrew Rybchenko /* Insert at start of existing encapsulator */ 573*d86bef48SAndrew Rybchenko insert_pointp = (uint8_t *)&encap_hdrp[1]; 574*d86bef48SAndrew Rybchenko opt = DHCP_ENCAPSULATED(opt); 575*d86bef48SAndrew Rybchenko } else if (rc == ENOENT) { 576*d86bef48SAndrew Rybchenko encap_hdrp = NULL; 577*d86bef48SAndrew Rybchenko } else { 578*d86bef48SAndrew Rybchenko goto fail7; 579*d86bef48SAndrew Rybchenko } 580*d86bef48SAndrew Rybchenko } else { 581*d86bef48SAndrew Rybchenko /* Check unencapsulated tag is not present */ 582*d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&searchp, &search_size, 583*d86bef48SAndrew Rybchenko opt); 584*d86bef48SAndrew Rybchenko if (rc != ENOENT) { 585*d86bef48SAndrew Rybchenko rc = EINVAL; 586*d86bef48SAndrew Rybchenko goto fail8; 587*d86bef48SAndrew Rybchenko } 588*d86bef48SAndrew Rybchenko } 589*d86bef48SAndrew Rybchenko 590*d86bef48SAndrew Rybchenko if (insert_pointp == NULL) { 591*d86bef48SAndrew Rybchenko /* Insert at end of existing tags */ 592*d86bef48SAndrew Rybchenko insert_pointp = endp; 593*d86bef48SAndrew Rybchenko } 594*d86bef48SAndrew Rybchenko 595*d86bef48SAndrew Rybchenko /* Includes the new encapsulator tag hdr if required */ 596*d86bef48SAndrew Rybchenko added_length = DHCP_CALC_TAG_LENGTH(value_length) + 597*d86bef48SAndrew Rybchenko (DHCP_IS_ENCAP_OPT(opt) ? sizeof (efx_dhcp_tag_hdr_t) : 0); 598*d86bef48SAndrew Rybchenko 599*d86bef48SAndrew Rybchenko if (available_space <= added_length) { 600*d86bef48SAndrew Rybchenko rc = ENOMEM; 601*d86bef48SAndrew Rybchenko goto fail9; 602*d86bef48SAndrew Rybchenko } 603*d86bef48SAndrew Rybchenko 604*d86bef48SAndrew Rybchenko memmove(insert_pointp + added_length, insert_pointp, 605*d86bef48SAndrew Rybchenko available_space - added_length); 606*d86bef48SAndrew Rybchenko 607*d86bef48SAndrew Rybchenko if (DHCP_IS_ENCAP_OPT(opt)) { 608*d86bef48SAndrew Rybchenko /* Create new encapsulator header */ 609*d86bef48SAndrew Rybchenko added_length -= sizeof (efx_dhcp_tag_hdr_t); 610*d86bef48SAndrew Rybchenko efx_dhcp_write_tag(insert_pointp, 611*d86bef48SAndrew Rybchenko DHCP_ENCAPSULATOR(opt), NULL, added_length); 612*d86bef48SAndrew Rybchenko insert_pointp += sizeof (efx_dhcp_tag_hdr_t); 613*d86bef48SAndrew Rybchenko } else if (encap_hdrp) 614*d86bef48SAndrew Rybchenko /* Modify existing encapsulator header */ 615*d86bef48SAndrew Rybchenko encap_hdrp->length += 616*d86bef48SAndrew Rybchenko ((uint8_t)DHCP_CALC_TAG_LENGTH(value_length)); 617*d86bef48SAndrew Rybchenko 618*d86bef48SAndrew Rybchenko efx_dhcp_write_tag(insert_pointp, opt, valuep, value_length); 619*d86bef48SAndrew Rybchenko 620*d86bef48SAndrew Rybchenko return (0); 621*d86bef48SAndrew Rybchenko 622*d86bef48SAndrew Rybchenko fail9: 623*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail9); 624*d86bef48SAndrew Rybchenko fail8: 625*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail8); 626*d86bef48SAndrew Rybchenko fail7: 627*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail7); 628*d86bef48SAndrew Rybchenko fail6: 629*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail6); 630*d86bef48SAndrew Rybchenko fail5: 631*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail5); 632*d86bef48SAndrew Rybchenko fail4: 633*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail4); 634*d86bef48SAndrew Rybchenko fail3: 635*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail3); 636*d86bef48SAndrew Rybchenko fail2: 637*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail2); 638*d86bef48SAndrew Rybchenko fail1: 639*d86bef48SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 640*d86bef48SAndrew Rybchenko 641*d86bef48SAndrew Rybchenko return (rc); 642*d86bef48SAndrew Rybchenko } 643*d86bef48SAndrew Rybchenko 644*d86bef48SAndrew Rybchenko /* 645*d86bef48SAndrew Rybchenko * Update an existing tag to the new value. Copes with encapsulated 646*d86bef48SAndrew Rybchenko * tags, and updates the encapsulating opt as necessary. 647*d86bef48SAndrew Rybchenko */ 648*d86bef48SAndrew Rybchenko __checkReturn efx_rc_t 649*d86bef48SAndrew Rybchenko efx_dhcp_update_tag( 650*d86bef48SAndrew Rybchenko __inout_bcount(buffer_length) uint8_t *bufferp, 651*d86bef48SAndrew Rybchenko __in size_t buffer_length, 652*d86bef48SAndrew Rybchenko __in uint16_t opt, 653*d86bef48SAndrew Rybchenko __in uint8_t *value_locationp, 654*d86bef48SAndrew Rybchenko __in_bcount_opt(value_length) uint8_t *valuep, 655*d86bef48SAndrew Rybchenko __in size_t value_length) 656*d86bef48SAndrew Rybchenko { 657*d86bef48SAndrew Rybchenko efx_rc_t rc; 658*d86bef48SAndrew Rybchenko uint8_t *write_pointp = value_locationp - sizeof (efx_dhcp_tag_hdr_t); 659*d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp; 660*d86bef48SAndrew Rybchenko efx_dhcp_tag_hdr_t *encap_hdrp = NULL; 661*d86bef48SAndrew Rybchenko size_t old_length; 662*d86bef48SAndrew Rybchenko 663*d86bef48SAndrew Rybchenko if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) { 664*d86bef48SAndrew Rybchenko rc = EINVAL; 665*d86bef48SAndrew Rybchenko goto fail1; 666*d86bef48SAndrew Rybchenko } 667*d86bef48SAndrew Rybchenko 668*d86bef48SAndrew Rybchenko if (value_length > DHCP_MAX_VALUE) { 669*d86bef48SAndrew Rybchenko rc = EINVAL; 670*d86bef48SAndrew Rybchenko goto fail2; 671*d86bef48SAndrew Rybchenko } 672*d86bef48SAndrew Rybchenko 673*d86bef48SAndrew Rybchenko if ((value_length > 0) && (valuep == NULL)) { 674*d86bef48SAndrew Rybchenko rc = EINVAL; 675*d86bef48SAndrew Rybchenko goto fail3; 676*d86bef48SAndrew Rybchenko } 677*d86bef48SAndrew Rybchenko 678*d86bef48SAndrew Rybchenko old_length = hdrp->length; 679*d86bef48SAndrew Rybchenko 680*d86bef48SAndrew Rybchenko if (old_length < value_length) { 681*d86bef48SAndrew Rybchenko uint8_t *endp = bufferp; 682*d86bef48SAndrew Rybchenko size_t available_space = buffer_length; 683*d86bef48SAndrew Rybchenko 684*d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&endp, &available_space, 685*d86bef48SAndrew Rybchenko EFX_DHCP_END); 686*d86bef48SAndrew Rybchenko if (rc != 0) 687*d86bef48SAndrew Rybchenko goto fail4; 688*d86bef48SAndrew Rybchenko 689*d86bef48SAndrew Rybchenko if (available_space < (value_length - old_length)) { 690*d86bef48SAndrew Rybchenko rc = EINVAL; 691*d86bef48SAndrew Rybchenko goto fail5; 692*d86bef48SAndrew Rybchenko } 693*d86bef48SAndrew Rybchenko } 694*d86bef48SAndrew Rybchenko 695*d86bef48SAndrew Rybchenko if (DHCP_IS_ENCAP_OPT(opt)) { 696*d86bef48SAndrew Rybchenko uint8_t *encapp = bufferp; 697*d86bef48SAndrew Rybchenko size_t following_encap = buffer_length; 698*d86bef48SAndrew Rybchenko size_t new_length; 699*d86bef48SAndrew Rybchenko 700*d86bef48SAndrew Rybchenko rc = efx_dhcp_walk_tags(&encapp, &following_encap, 701*d86bef48SAndrew Rybchenko DHCP_ENCAPSULATOR(opt)); 702*d86bef48SAndrew Rybchenko if (rc != 0) 703*d86bef48SAndrew Rybchenko goto fail6; 704*d86bef48SAndrew Rybchenko 705*d86bef48SAndrew Rybchenko encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp; 706*d86bef48SAndrew Rybchenko 707*d86bef48SAndrew Rybchenko new_length = ((size_t)encap_hdrp->length + 708*d86bef48SAndrew Rybchenko value_length - old_length); 709*d86bef48SAndrew Rybchenko /* Check encapsulator will not overflow */ 710*d86bef48SAndrew Rybchenko if (new_length > DHCP_MAX_VALUE) { 711*d86bef48SAndrew Rybchenko rc = E2BIG; 712*d86bef48SAndrew Rybchenko goto fail7; 713*d86bef48SAndrew Rybchenko } 714*d86bef48SAndrew Rybchenko 715*d86bef48SAndrew Rybchenko encap_hdrp->length = (uint8_t)new_length; 716*d86bef48SAndrew Rybchenko } 717*d86bef48SAndrew Rybchenko 718*d86bef48SAndrew Rybchenko /* 719*d86bef48SAndrew Rybchenko * Move the following data up/down to accommodate the new payload 720*d86bef48SAndrew Rybchenko * length. 721*d86bef48SAndrew Rybchenko */ 722*d86bef48SAndrew Rybchenko if (old_length != value_length) { 723*d86bef48SAndrew Rybchenko uint8_t *destp = (uint8_t *)DHCP_NEXT_TAG(hdrp) + 724*d86bef48SAndrew Rybchenko value_length - old_length; 725*d86bef48SAndrew Rybchenko size_t count = &bufferp[buffer_length] - 726*d86bef48SAndrew Rybchenko (uint8_t *)DHCP_NEXT_TAG(hdrp); 727*d86bef48SAndrew Rybchenko 728*d86bef48SAndrew Rybchenko memmove(destp, DHCP_NEXT_TAG(hdrp), count); 729*d86bef48SAndrew Rybchenko } 730*d86bef48SAndrew Rybchenko 731*d86bef48SAndrew Rybchenko EFSYS_ASSERT(hdrp->tag == DHCP_ENCAPSULATED(opt)); 732*d86bef48SAndrew Rybchenko efx_dhcp_write_tag(write_pointp, opt, valuep, value_length); 733*d86bef48SAndrew Rybchenko 734*d86bef48SAndrew Rybchenko return (0); 735*d86bef48SAndrew Rybchenko 736*d86bef48SAndrew Rybchenko fail7: 737*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail7); 738*d86bef48SAndrew Rybchenko fail6: 739*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail6); 740*d86bef48SAndrew Rybchenko fail5: 741*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail5); 742*d86bef48SAndrew Rybchenko fail4: 743*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail4); 744*d86bef48SAndrew Rybchenko fail3: 745*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail3); 746*d86bef48SAndrew Rybchenko fail2: 747*d86bef48SAndrew Rybchenko EFSYS_PROBE(fail2); 748*d86bef48SAndrew Rybchenko fail1: 749*d86bef48SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 750*d86bef48SAndrew Rybchenko 751*d86bef48SAndrew Rybchenko return (rc); 752*d86bef48SAndrew Rybchenko } 753*d86bef48SAndrew Rybchenko 754*d86bef48SAndrew Rybchenko 755*d86bef48SAndrew Rybchenko /* 756662c835bSAndrew Rybchenko * Copy bootcfg sector data to a target buffer which may differ in size. 757662c835bSAndrew Rybchenko * Optionally corrects format errors in source buffer. 758662c835bSAndrew Rybchenko */ 759662c835bSAndrew Rybchenko efx_rc_t 760662c835bSAndrew Rybchenko efx_bootcfg_copy_sector( 761662c835bSAndrew Rybchenko __in efx_nic_t *enp, 762662c835bSAndrew Rybchenko __inout_bcount(sector_length) 763662c835bSAndrew Rybchenko uint8_t *sector, 764662c835bSAndrew Rybchenko __in size_t sector_length, 765662c835bSAndrew Rybchenko __out_bcount(data_size) uint8_t *data, 766662c835bSAndrew Rybchenko __in size_t data_size, 767662c835bSAndrew Rybchenko __in boolean_t handle_format_errors) 768662c835bSAndrew Rybchenko { 769*d86bef48SAndrew Rybchenko _NOTE(ARGUNUSED(enp)) 770*d86bef48SAndrew Rybchenko 771662c835bSAndrew Rybchenko size_t used_bytes; 772662c835bSAndrew Rybchenko efx_rc_t rc; 773662c835bSAndrew Rybchenko 774*d86bef48SAndrew Rybchenko /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */ 775a98c7b04SAndrew Rybchenko if (data_size < 2) { 776a98c7b04SAndrew Rybchenko rc = ENOSPC; 777a98c7b04SAndrew Rybchenko goto fail1; 778a98c7b04SAndrew Rybchenko } 779a98c7b04SAndrew Rybchenko 780662c835bSAndrew Rybchenko /* Verify that the area is correctly formatted and checksummed */ 781*d86bef48SAndrew Rybchenko rc = efx_dhcp_verify(sector, sector_length, 782662c835bSAndrew Rybchenko &used_bytes); 783662c835bSAndrew Rybchenko 784662c835bSAndrew Rybchenko if (!handle_format_errors) { 785662c835bSAndrew Rybchenko if (rc != 0) 786a98c7b04SAndrew Rybchenko goto fail2; 787662c835bSAndrew Rybchenko 788662c835bSAndrew Rybchenko if ((used_bytes < 2) || 789*d86bef48SAndrew Rybchenko (sector[used_bytes - 1] != EFX_DHCP_END)) { 790*d86bef48SAndrew Rybchenko /* Block too short, or EFX_DHCP_END missing */ 791662c835bSAndrew Rybchenko rc = ENOENT; 792a98c7b04SAndrew Rybchenko goto fail3; 793662c835bSAndrew Rybchenko } 794662c835bSAndrew Rybchenko } 795662c835bSAndrew Rybchenko 796662c835bSAndrew Rybchenko /* Synthesize empty format on verification failure */ 797662c835bSAndrew Rybchenko if (rc != 0 || used_bytes == 0) { 798662c835bSAndrew Rybchenko sector[0] = 0; 799*d86bef48SAndrew Rybchenko sector[1] = EFX_DHCP_END; 800662c835bSAndrew Rybchenko used_bytes = 2; 801662c835bSAndrew Rybchenko } 802*d86bef48SAndrew Rybchenko EFSYS_ASSERT(used_bytes >= 2); /* checksum and EFX_DHCP_END */ 803662c835bSAndrew Rybchenko EFSYS_ASSERT(used_bytes <= sector_length); 804662c835bSAndrew Rybchenko EFSYS_ASSERT(sector_length >= 2); 805662c835bSAndrew Rybchenko 806662c835bSAndrew Rybchenko /* 807*d86bef48SAndrew Rybchenko * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END 808*d86bef48SAndrew Rybchenko * character. Modify the returned payload so it does. 809662c835bSAndrew Rybchenko * Reinitialise the sector if there isn't room for the character. 810662c835bSAndrew Rybchenko */ 811*d86bef48SAndrew Rybchenko if (sector[used_bytes - 1] != EFX_DHCP_END) { 812662c835bSAndrew Rybchenko if (used_bytes >= sector_length) { 813662c835bSAndrew Rybchenko sector[0] = 0; 814662c835bSAndrew Rybchenko used_bytes = 1; 815662c835bSAndrew Rybchenko } 816*d86bef48SAndrew Rybchenko sector[used_bytes] = EFX_DHCP_END; 817662c835bSAndrew Rybchenko ++used_bytes; 818662c835bSAndrew Rybchenko } 819662c835bSAndrew Rybchenko 820662c835bSAndrew Rybchenko /* 821662c835bSAndrew Rybchenko * Verify that the target buffer is large enough for the 822662c835bSAndrew Rybchenko * entire used bootcfg area, then copy into the target buffer. 823662c835bSAndrew Rybchenko */ 824662c835bSAndrew Rybchenko if (used_bytes > data_size) { 825662c835bSAndrew Rybchenko rc = ENOSPC; 826a98c7b04SAndrew Rybchenko goto fail4; 827662c835bSAndrew Rybchenko } 828a98c7b04SAndrew Rybchenko 829a98c7b04SAndrew Rybchenko data[0] = 0; /* checksum, updated below */ 830a98c7b04SAndrew Rybchenko 831a98c7b04SAndrew Rybchenko /* Copy all after the checksum to the target buffer */ 832a98c7b04SAndrew Rybchenko memcpy(data + 1, sector + 1, used_bytes - 1); 833662c835bSAndrew Rybchenko 834662c835bSAndrew Rybchenko /* Zero out the unused portion of the target buffer */ 835662c835bSAndrew Rybchenko if (used_bytes < data_size) 836662c835bSAndrew Rybchenko (void) memset(data + used_bytes, 0, data_size - used_bytes); 837662c835bSAndrew Rybchenko 838662c835bSAndrew Rybchenko /* 839*d86bef48SAndrew Rybchenko * The checksum includes trailing data after any EFX_DHCP_END 840*d86bef48SAndrew Rybchenko * character, which we've just modified (by truncation or appending 841*d86bef48SAndrew Rybchenko * EFX_DHCP_END). 842662c835bSAndrew Rybchenko */ 843*d86bef48SAndrew Rybchenko data[0] -= efx_dhcp_csum(data, data_size); 844662c835bSAndrew Rybchenko 845662c835bSAndrew Rybchenko return (0); 846662c835bSAndrew Rybchenko 847a98c7b04SAndrew Rybchenko fail4: 848a98c7b04SAndrew Rybchenko EFSYS_PROBE(fail4); 849662c835bSAndrew Rybchenko fail3: 850662c835bSAndrew Rybchenko EFSYS_PROBE(fail3); 851662c835bSAndrew Rybchenko fail2: 852662c835bSAndrew Rybchenko EFSYS_PROBE(fail2); 853662c835bSAndrew Rybchenko fail1: 854662c835bSAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 855662c835bSAndrew Rybchenko 856662c835bSAndrew Rybchenko return (rc); 857662c835bSAndrew Rybchenko } 858662c835bSAndrew Rybchenko 859460cb568SAndrew Rybchenko efx_rc_t 860e948693eSPhilip Paeps efx_bootcfg_read( 861e948693eSPhilip Paeps __in efx_nic_t *enp, 8627e17c17dSAndrew Rybchenko __out_bcount(size) uint8_t *data, 863e948693eSPhilip Paeps __in size_t size) 864e948693eSPhilip Paeps { 865e948693eSPhilip Paeps uint8_t *payload = NULL; 866e948693eSPhilip Paeps size_t used_bytes; 8675081d55dSAndrew Rybchenko size_t partn_length; 868e948693eSPhilip Paeps size_t sector_length; 8695081d55dSAndrew Rybchenko size_t sector_offset; 870460cb568SAndrew Rybchenko efx_rc_t rc; 871662c835bSAndrew Rybchenko uint32_t sector_number; 872e948693eSPhilip Paeps 873*d86bef48SAndrew Rybchenko /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */ 874a98c7b04SAndrew Rybchenko if (size < 2) { 875a98c7b04SAndrew Rybchenko rc = ENOSPC; 876a98c7b04SAndrew Rybchenko goto fail1; 877a98c7b04SAndrew Rybchenko } 878a98c7b04SAndrew Rybchenko 879df0385e5SAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 880662c835bSAndrew Rybchenko sector_number = enp->en_nic_cfg.enc_pf; 881662c835bSAndrew Rybchenko #else 882662c835bSAndrew Rybchenko sector_number = 0; 883662c835bSAndrew Rybchenko #endif 8845081d55dSAndrew Rybchenko rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 885e948693eSPhilip Paeps if (rc != 0) 886a98c7b04SAndrew Rybchenko goto fail2; 887e948693eSPhilip Paeps 8885081d55dSAndrew Rybchenko /* The bootcfg sector may be stored in a (larger) shared partition */ 889662c835bSAndrew Rybchenko rc = efx_bootcfg_sector_info(enp, sector_number, 890662c835bSAndrew Rybchenko NULL, §or_offset, §or_length); 8915081d55dSAndrew Rybchenko if (rc != 0) 892a98c7b04SAndrew Rybchenko goto fail3; 893a98c7b04SAndrew Rybchenko 894a98c7b04SAndrew Rybchenko if (sector_length < 2) { 895a98c7b04SAndrew Rybchenko rc = EINVAL; 896a98c7b04SAndrew Rybchenko goto fail4; 897a98c7b04SAndrew Rybchenko } 8985081d55dSAndrew Rybchenko 8995081d55dSAndrew Rybchenko if (sector_length > BOOTCFG_MAX_SIZE) 9005081d55dSAndrew Rybchenko sector_length = BOOTCFG_MAX_SIZE; 9015081d55dSAndrew Rybchenko 9025081d55dSAndrew Rybchenko if (sector_offset + sector_length > partn_length) { 9035081d55dSAndrew Rybchenko /* Partition is too small */ 9045081d55dSAndrew Rybchenko rc = EFBIG; 905a98c7b04SAndrew Rybchenko goto fail5; 9065081d55dSAndrew Rybchenko } 9075081d55dSAndrew Rybchenko 908e948693eSPhilip Paeps /* 909*d86bef48SAndrew Rybchenko * We need to read the entire BOOTCFG sector to ensure we read all 910*d86bef48SAndrew Rybchenko * tags, because legacy bootcfg sectors are not guaranteed to end 911*d86bef48SAndrew Rybchenko * with an EFX_DHCP_END character. If the user hasn't supplied a 912*d86bef48SAndrew Rybchenko * sufficiently large buffer then use our own buffer. 913e948693eSPhilip Paeps */ 914e948693eSPhilip Paeps if (sector_length > size) { 915e948693eSPhilip Paeps EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload); 916e948693eSPhilip Paeps if (payload == NULL) { 917e948693eSPhilip Paeps rc = ENOMEM; 918a98c7b04SAndrew Rybchenko goto fail6; 919e948693eSPhilip Paeps } 920e948693eSPhilip Paeps } else 921e948693eSPhilip Paeps payload = (uint8_t *)data; 922e948693eSPhilip Paeps 923e948693eSPhilip Paeps if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 924a98c7b04SAndrew Rybchenko goto fail7; 925e948693eSPhilip Paeps 926662c835bSAndrew Rybchenko if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 927662c835bSAndrew Rybchenko sector_offset, (caddr_t)payload, sector_length)) != 0) { 928d5106d05SAndrew Rybchenko (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 929a98c7b04SAndrew Rybchenko goto fail8; 930662c835bSAndrew Rybchenko } 931662c835bSAndrew Rybchenko 932d5106d05SAndrew Rybchenko if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 933a98c7b04SAndrew Rybchenko goto fail9; 934e948693eSPhilip Paeps 935e948693eSPhilip Paeps /* Verify that the area is correctly formatted and checksummed */ 936*d86bef48SAndrew Rybchenko rc = efx_dhcp_verify(payload, sector_length, 937e948693eSPhilip Paeps &used_bytes); 938e948693eSPhilip Paeps if (rc != 0 || used_bytes == 0) { 939a98c7b04SAndrew Rybchenko payload[0] = 0; 940*d86bef48SAndrew Rybchenko payload[1] = EFX_DHCP_END; 941e948693eSPhilip Paeps used_bytes = 2; 942e948693eSPhilip Paeps } 943e948693eSPhilip Paeps 944*d86bef48SAndrew Rybchenko EFSYS_ASSERT(used_bytes >= 2); /* checksum and EFX_DHCP_END */ 945e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes <= sector_length); 946e948693eSPhilip Paeps 947e948693eSPhilip Paeps /* 948*d86bef48SAndrew Rybchenko * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END 949*d86bef48SAndrew Rybchenko * character. Modify the returned payload so it does. 950*d86bef48SAndrew Rybchenko * BOOTCFG_MAX_SIZE is by definition large enough for any valid 951*d86bef48SAndrew Rybchenko * (per-port) bootcfg sector, so reinitialise the sector if there 952*d86bef48SAndrew Rybchenko * isn't room for the character. 953e948693eSPhilip Paeps */ 954*d86bef48SAndrew Rybchenko if (payload[used_bytes - 1] != EFX_DHCP_END) { 955a98c7b04SAndrew Rybchenko if (used_bytes >= sector_length) 956e948693eSPhilip Paeps used_bytes = 1; 957e948693eSPhilip Paeps 958*d86bef48SAndrew Rybchenko payload[used_bytes] = EFX_DHCP_END; 959e948693eSPhilip Paeps ++used_bytes; 960e948693eSPhilip Paeps } 961e948693eSPhilip Paeps 962e948693eSPhilip Paeps /* 963e948693eSPhilip Paeps * Verify that the user supplied buffer is large enough for the 964e948693eSPhilip Paeps * entire used bootcfg area, then copy into the user supplied buffer. 965e948693eSPhilip Paeps */ 966e948693eSPhilip Paeps if (used_bytes > size) { 967e948693eSPhilip Paeps rc = ENOSPC; 968a98c7b04SAndrew Rybchenko goto fail10; 969e948693eSPhilip Paeps } 970a98c7b04SAndrew Rybchenko 971a98c7b04SAndrew Rybchenko data[0] = 0; /* checksum, updated below */ 972a98c7b04SAndrew Rybchenko 973e948693eSPhilip Paeps if (sector_length > size) { 974a98c7b04SAndrew Rybchenko /* Copy all after the checksum to the target buffer */ 975a98c7b04SAndrew Rybchenko memcpy(data + 1, payload + 1, used_bytes - 1); 976e948693eSPhilip Paeps EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 977e948693eSPhilip Paeps } 978e948693eSPhilip Paeps 979e948693eSPhilip Paeps /* Zero out the unused portion of the user buffer */ 980e948693eSPhilip Paeps if (used_bytes < size) 981e948693eSPhilip Paeps (void) memset(data + used_bytes, 0, size - used_bytes); 982e948693eSPhilip Paeps 983e948693eSPhilip Paeps /* 984*d86bef48SAndrew Rybchenko * The checksum includes trailing data after any EFX_DHCP_END character, 985*d86bef48SAndrew Rybchenko * which we've just modified (by truncation or appending EFX_DHCP_END). 986e948693eSPhilip Paeps */ 987*d86bef48SAndrew Rybchenko data[0] -= efx_dhcp_csum(data, size); 988e948693eSPhilip Paeps 989e948693eSPhilip Paeps return (0); 990e948693eSPhilip Paeps 991a98c7b04SAndrew Rybchenko fail10: 992a98c7b04SAndrew Rybchenko EFSYS_PROBE(fail10); 993a98c7b04SAndrew Rybchenko fail9: 994a98c7b04SAndrew Rybchenko EFSYS_PROBE(fail9); 995662c835bSAndrew Rybchenko fail8: 996662c835bSAndrew Rybchenko EFSYS_PROBE(fail8); 9975081d55dSAndrew Rybchenko fail7: 9985081d55dSAndrew Rybchenko EFSYS_PROBE(fail7); 999a98c7b04SAndrew Rybchenko if (sector_length > size) 1000a98c7b04SAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 10015081d55dSAndrew Rybchenko fail6: 10025081d55dSAndrew Rybchenko EFSYS_PROBE(fail6); 1003e948693eSPhilip Paeps fail5: 1004e948693eSPhilip Paeps EFSYS_PROBE(fail5); 1005e948693eSPhilip Paeps fail4: 1006e948693eSPhilip Paeps EFSYS_PROBE(fail4); 1007e948693eSPhilip Paeps fail3: 1008e948693eSPhilip Paeps EFSYS_PROBE(fail3); 1009e948693eSPhilip Paeps fail2: 1010e948693eSPhilip Paeps EFSYS_PROBE(fail2); 1011e948693eSPhilip Paeps fail1: 1012460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 1013e948693eSPhilip Paeps 1014e948693eSPhilip Paeps return (rc); 1015e948693eSPhilip Paeps } 1016e948693eSPhilip Paeps 1017460cb568SAndrew Rybchenko efx_rc_t 1018e948693eSPhilip Paeps efx_bootcfg_write( 1019e948693eSPhilip Paeps __in efx_nic_t *enp, 10207e17c17dSAndrew Rybchenko __in_bcount(size) uint8_t *data, 1021e948693eSPhilip Paeps __in size_t size) 1022e948693eSPhilip Paeps { 10235081d55dSAndrew Rybchenko uint8_t *partn_data; 1024e948693eSPhilip Paeps uint8_t checksum; 10255081d55dSAndrew Rybchenko size_t partn_length; 1026e948693eSPhilip Paeps size_t sector_length; 10275081d55dSAndrew Rybchenko size_t sector_offset; 1028e948693eSPhilip Paeps size_t used_bytes; 1029460cb568SAndrew Rybchenko efx_rc_t rc; 1030662c835bSAndrew Rybchenko uint32_t sector_number; 1031662c835bSAndrew Rybchenko 1032df0385e5SAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 1033662c835bSAndrew Rybchenko sector_number = enp->en_nic_cfg.enc_pf; 1034662c835bSAndrew Rybchenko #else 1035662c835bSAndrew Rybchenko sector_number = 0; 1036662c835bSAndrew Rybchenko #endif 1037e948693eSPhilip Paeps 10385081d55dSAndrew Rybchenko rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 1039e948693eSPhilip Paeps if (rc != 0) 1040e948693eSPhilip Paeps goto fail1; 1041e948693eSPhilip Paeps 10425081d55dSAndrew Rybchenko /* The bootcfg sector may be stored in a (larger) shared partition */ 1043662c835bSAndrew Rybchenko rc = efx_bootcfg_sector_info(enp, sector_number, 1044662c835bSAndrew Rybchenko NULL, §or_offset, §or_length); 10455081d55dSAndrew Rybchenko if (rc != 0) 10465081d55dSAndrew Rybchenko goto fail2; 10475081d55dSAndrew Rybchenko 1048e948693eSPhilip Paeps if (sector_length > BOOTCFG_MAX_SIZE) 1049e948693eSPhilip Paeps sector_length = BOOTCFG_MAX_SIZE; 1050e948693eSPhilip Paeps 10515081d55dSAndrew Rybchenko if (sector_offset + sector_length > partn_length) { 10525081d55dSAndrew Rybchenko /* Partition is too small */ 10535081d55dSAndrew Rybchenko rc = EFBIG; 10545081d55dSAndrew Rybchenko goto fail3; 10555081d55dSAndrew Rybchenko } 10565081d55dSAndrew Rybchenko 1057*d86bef48SAndrew Rybchenko if ((rc = efx_dhcp_verify(data, size, &used_bytes)) != 0) 10585081d55dSAndrew Rybchenko goto fail4; 1059e948693eSPhilip Paeps 1060*d86bef48SAndrew Rybchenko /* 1061*d86bef48SAndrew Rybchenko * The caller *must* terminate their block with a EFX_DHCP_END 1062*d86bef48SAndrew Rybchenko * character 1063*d86bef48SAndrew Rybchenko */ 1064*d86bef48SAndrew Rybchenko if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != 1065*d86bef48SAndrew Rybchenko EFX_DHCP_END)) { 1066*d86bef48SAndrew Rybchenko /* Block too short or EFX_DHCP_END missing */ 1067e948693eSPhilip Paeps rc = ENOENT; 10685081d55dSAndrew Rybchenko goto fail5; 1069e948693eSPhilip Paeps } 1070e948693eSPhilip Paeps 1071e948693eSPhilip Paeps /* Check that the hardware has support for this much data */ 1072e948693eSPhilip Paeps if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) { 1073e948693eSPhilip Paeps rc = ENOSPC; 1074e948693eSPhilip Paeps goto fail6; 1075e948693eSPhilip Paeps } 1076e948693eSPhilip Paeps 10775081d55dSAndrew Rybchenko /* 10785081d55dSAndrew Rybchenko * If the BOOTCFG sector is stored in a shared partition, then we must 10795081d55dSAndrew Rybchenko * read the whole partition and insert the updated bootcfg sector at the 10805081d55dSAndrew Rybchenko * correct offset. 10815081d55dSAndrew Rybchenko */ 10825081d55dSAndrew Rybchenko EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data); 10835081d55dSAndrew Rybchenko if (partn_data == NULL) { 10845081d55dSAndrew Rybchenko rc = ENOMEM; 1085e948693eSPhilip Paeps goto fail7; 10865081d55dSAndrew Rybchenko } 10875081d55dSAndrew Rybchenko 10885081d55dSAndrew Rybchenko rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 10895081d55dSAndrew Rybchenko if (rc != 0) 10905081d55dSAndrew Rybchenko goto fail8; 10915081d55dSAndrew Rybchenko 10925081d55dSAndrew Rybchenko /* Read the entire partition */ 10935081d55dSAndrew Rybchenko rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0, 10945081d55dSAndrew Rybchenko (caddr_t)partn_data, partn_length); 10955081d55dSAndrew Rybchenko if (rc != 0) 10965081d55dSAndrew Rybchenko goto fail9; 1097e948693eSPhilip Paeps 1098e948693eSPhilip Paeps /* 1099*d86bef48SAndrew Rybchenko * Insert the BOOTCFG sector into the partition, Zero out all data 1100*d86bef48SAndrew Rybchenko * after the EFX_DHCP_END tag, and adjust the checksum. 1101e948693eSPhilip Paeps */ 11025081d55dSAndrew Rybchenko (void) memset(partn_data + sector_offset, 0x0, sector_length); 11035081d55dSAndrew Rybchenko (void) memcpy(partn_data + sector_offset, data, used_bytes); 11045081d55dSAndrew Rybchenko 1105*d86bef48SAndrew Rybchenko checksum = efx_dhcp_csum(data, used_bytes); 11065081d55dSAndrew Rybchenko partn_data[sector_offset] -= checksum; 1107e948693eSPhilip Paeps 11085081d55dSAndrew Rybchenko if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 11095081d55dSAndrew Rybchenko goto fail10; 1110e948693eSPhilip Paeps 1111e948693eSPhilip Paeps if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 1112662c835bSAndrew Rybchenko 0, (caddr_t)partn_data, partn_length)) != 0) 11135081d55dSAndrew Rybchenko goto fail11; 1114e948693eSPhilip Paeps 1115d5106d05SAndrew Rybchenko if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 1116662c835bSAndrew Rybchenko goto fail12; 1117e948693eSPhilip Paeps 11185081d55dSAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 1119e948693eSPhilip Paeps 1120e948693eSPhilip Paeps return (0); 1121e948693eSPhilip Paeps 1122662c835bSAndrew Rybchenko fail12: 1123662c835bSAndrew Rybchenko EFSYS_PROBE(fail12); 11245081d55dSAndrew Rybchenko fail11: 11255081d55dSAndrew Rybchenko EFSYS_PROBE(fail11); 11265081d55dSAndrew Rybchenko fail10: 11275081d55dSAndrew Rybchenko EFSYS_PROBE(fail10); 11285081d55dSAndrew Rybchenko fail9: 11295081d55dSAndrew Rybchenko EFSYS_PROBE(fail9); 1130e948693eSPhilip Paeps 1131d5106d05SAndrew Rybchenko (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 11325081d55dSAndrew Rybchenko fail8: 11335081d55dSAndrew Rybchenko EFSYS_PROBE(fail8); 11345081d55dSAndrew Rybchenko 11355081d55dSAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 11365081d55dSAndrew Rybchenko fail7: 11375081d55dSAndrew Rybchenko EFSYS_PROBE(fail7); 11385081d55dSAndrew Rybchenko fail6: 11395081d55dSAndrew Rybchenko EFSYS_PROBE(fail6); 1140e948693eSPhilip Paeps fail5: 1141e948693eSPhilip Paeps EFSYS_PROBE(fail5); 1142e948693eSPhilip Paeps fail4: 1143e948693eSPhilip Paeps EFSYS_PROBE(fail4); 1144e948693eSPhilip Paeps fail3: 1145e948693eSPhilip Paeps EFSYS_PROBE(fail3); 1146e948693eSPhilip Paeps fail2: 1147e948693eSPhilip Paeps EFSYS_PROBE(fail2); 1148e948693eSPhilip Paeps fail1: 1149460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 1150e948693eSPhilip Paeps 1151e948693eSPhilip Paeps return (rc); 1152e948693eSPhilip Paeps } 1153e948693eSPhilip Paeps 1154e948693eSPhilip Paeps #endif /* EFSYS_OPT_BOOTCFG */ 1155