1e948693eSPhilip Paeps /*- 2929c7febSAndrew Rybchenko * Copyright (c) 2009-2016 Solarflare Communications Inc. 33c838a9fSAndrew Rybchenko * All rights reserved. 4e948693eSPhilip Paeps * 5e948693eSPhilip Paeps * Redistribution and use in source and binary forms, with or without 63c838a9fSAndrew Rybchenko * modification, are permitted provided that the following conditions are met: 7e948693eSPhilip Paeps * 83c838a9fSAndrew Rybchenko * 1. Redistributions of source code must retain the above copyright notice, 93c838a9fSAndrew Rybchenko * this list of conditions and the following disclaimer. 103c838a9fSAndrew Rybchenko * 2. Redistributions in binary form must reproduce the above copyright notice, 113c838a9fSAndrew Rybchenko * this list of conditions and the following disclaimer in the documentation 123c838a9fSAndrew Rybchenko * and/or other materials provided with the distribution. 133c838a9fSAndrew Rybchenko * 143c838a9fSAndrew Rybchenko * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 153c838a9fSAndrew Rybchenko * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 163c838a9fSAndrew Rybchenko * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 173c838a9fSAndrew Rybchenko * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 183c838a9fSAndrew Rybchenko * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 193c838a9fSAndrew Rybchenko * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 203c838a9fSAndrew Rybchenko * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 213c838a9fSAndrew Rybchenko * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 223c838a9fSAndrew Rybchenko * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 233c838a9fSAndrew Rybchenko * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 243c838a9fSAndrew Rybchenko * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 253c838a9fSAndrew Rybchenko * 263c838a9fSAndrew Rybchenko * The views and conclusions contained in the software and documentation are 273c838a9fSAndrew Rybchenko * those of the authors and should not be interpreted as representing official 283c838a9fSAndrew Rybchenko * policies, either expressed or implied, of the FreeBSD Project. 29e948693eSPhilip Paeps */ 30e948693eSPhilip Paeps 315dee87d7SPhilip Paeps #include <sys/cdefs.h> 325dee87d7SPhilip Paeps __FBSDID("$FreeBSD$"); 335dee87d7SPhilip Paeps 34e948693eSPhilip Paeps #include "efx.h" 35e948693eSPhilip Paeps #include "efx_impl.h" 36e948693eSPhilip Paeps 37e948693eSPhilip Paeps #if EFSYS_OPT_BOOTCFG 38e948693eSPhilip Paeps 39e948693eSPhilip Paeps /* 40e948693eSPhilip Paeps * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE. 41*5081d55dSAndrew Rybchenko * NOTE: This is larger than the Medford per-PF bootcfg sector. 42e948693eSPhilip Paeps */ 43e948693eSPhilip Paeps #define BOOTCFG_MAX_SIZE 0x1000 44e948693eSPhilip Paeps 4560cf15c5SAndrew Rybchenko #define DHCP_END ((uint8_t)0xff) 4660cf15c5SAndrew Rybchenko #define DHCP_PAD ((uint8_t)0) 47e948693eSPhilip Paeps 48*5081d55dSAndrew Rybchenko 49*5081d55dSAndrew Rybchenko /* Report size and offset of bootcfg sector in NVRAM partition. */ 50*5081d55dSAndrew Rybchenko static __checkReturn efx_rc_t 51*5081d55dSAndrew Rybchenko efx_bootcfg_sector( 52*5081d55dSAndrew Rybchenko __in efx_nic_t *enp, 53*5081d55dSAndrew Rybchenko __out size_t *offsetp, 54*5081d55dSAndrew Rybchenko __out size_t *max_sizep) 55*5081d55dSAndrew Rybchenko { 56*5081d55dSAndrew Rybchenko size_t max_size; 57*5081d55dSAndrew Rybchenko size_t offset; 58*5081d55dSAndrew Rybchenko int rc; 59*5081d55dSAndrew Rybchenko 60*5081d55dSAndrew Rybchenko switch (enp->en_family) { 61*5081d55dSAndrew Rybchenko #if EFSYS_OPT_SIENA 62*5081d55dSAndrew Rybchenko case EFX_FAMILY_SIENA: 63*5081d55dSAndrew Rybchenko max_size = BOOTCFG_MAX_SIZE; 64*5081d55dSAndrew Rybchenko offset = 0; 65*5081d55dSAndrew Rybchenko break; 66*5081d55dSAndrew Rybchenko #endif /* EFSYS_OPT_SIENA */ 67*5081d55dSAndrew Rybchenko 68*5081d55dSAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON 69*5081d55dSAndrew Rybchenko case EFX_FAMILY_HUNTINGTON: 70*5081d55dSAndrew Rybchenko max_size = BOOTCFG_MAX_SIZE; 71*5081d55dSAndrew Rybchenko offset = 0; 72*5081d55dSAndrew Rybchenko break; 73*5081d55dSAndrew Rybchenko #endif /* EFSYS_OPT_HUNTINGTON */ 74*5081d55dSAndrew Rybchenko 75*5081d55dSAndrew Rybchenko #if EFSYS_OPT_MEDFORD 76*5081d55dSAndrew Rybchenko case EFX_FAMILY_MEDFORD: { 77*5081d55dSAndrew Rybchenko efx_nic_cfg_t *encp = &(enp->en_nic_cfg); 78*5081d55dSAndrew Rybchenko 79*5081d55dSAndrew Rybchenko /* Shared partition (array indexed by PF) */ 80*5081d55dSAndrew Rybchenko max_size = 0x0800; 81*5081d55dSAndrew Rybchenko offset = max_size * encp->enc_pf; 82*5081d55dSAndrew Rybchenko break; 83*5081d55dSAndrew Rybchenko } 84*5081d55dSAndrew Rybchenko #endif /* EFSYS_OPT_MEDFORD */ 85*5081d55dSAndrew Rybchenko 86*5081d55dSAndrew Rybchenko default: 87*5081d55dSAndrew Rybchenko EFSYS_ASSERT(0); 88*5081d55dSAndrew Rybchenko rc = ENOTSUP; 89*5081d55dSAndrew Rybchenko goto fail1; 90*5081d55dSAndrew Rybchenko } 91*5081d55dSAndrew Rybchenko EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE); 92*5081d55dSAndrew Rybchenko 93*5081d55dSAndrew Rybchenko *offsetp = offset; 94*5081d55dSAndrew Rybchenko *max_sizep = max_size; 95*5081d55dSAndrew Rybchenko 96*5081d55dSAndrew Rybchenko return (0); 97*5081d55dSAndrew Rybchenko 98*5081d55dSAndrew Rybchenko fail1: 99*5081d55dSAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 100*5081d55dSAndrew Rybchenko return (rc); 101*5081d55dSAndrew Rybchenko } 102*5081d55dSAndrew Rybchenko 103*5081d55dSAndrew Rybchenko 104e948693eSPhilip Paeps static __checkReturn uint8_t 105e948693eSPhilip Paeps efx_bootcfg_csum( 106e948693eSPhilip Paeps __in efx_nic_t *enp, 107e948693eSPhilip Paeps __in_bcount(size) caddr_t data, 108e948693eSPhilip Paeps __in size_t size) 109e948693eSPhilip Paeps { 110e948693eSPhilip Paeps _NOTE(ARGUNUSED(enp)) 111e948693eSPhilip Paeps 112e948693eSPhilip Paeps unsigned int pos; 113e948693eSPhilip Paeps uint8_t checksum = 0; 114e948693eSPhilip Paeps 115e948693eSPhilip Paeps for (pos = 0; pos < size; pos++) 116e948693eSPhilip Paeps checksum += data[pos]; 117e948693eSPhilip Paeps return (checksum); 118e948693eSPhilip Paeps } 119e948693eSPhilip Paeps 120460cb568SAndrew Rybchenko static __checkReturn efx_rc_t 121e948693eSPhilip Paeps efx_bootcfg_verify( 122e948693eSPhilip Paeps __in efx_nic_t *enp, 123e948693eSPhilip Paeps __in_bcount(size) caddr_t data, 124e948693eSPhilip Paeps __in size_t size, 1253c838a9fSAndrew Rybchenko __out_opt size_t *usedp) 126e948693eSPhilip Paeps { 127e948693eSPhilip Paeps size_t offset = 0; 128e948693eSPhilip Paeps size_t used = 0; 129460cb568SAndrew Rybchenko efx_rc_t rc; 130e948693eSPhilip Paeps 131453130d9SPedro F. Giffuni /* Start parsing tags immediately after the checksum */ 132e948693eSPhilip Paeps for (offset = 1; offset < size; ) { 133e948693eSPhilip Paeps uint8_t tag; 134e948693eSPhilip Paeps uint8_t length; 135e948693eSPhilip Paeps 136e948693eSPhilip Paeps /* Consume tag */ 137e948693eSPhilip Paeps tag = data[offset]; 138e948693eSPhilip Paeps if (tag == DHCP_END) { 139e948693eSPhilip Paeps offset++; 140e948693eSPhilip Paeps used = offset; 141e948693eSPhilip Paeps break; 142e948693eSPhilip Paeps } 143e948693eSPhilip Paeps if (tag == DHCP_PAD) { 144e948693eSPhilip Paeps offset++; 145e948693eSPhilip Paeps continue; 146e948693eSPhilip Paeps } 147e948693eSPhilip Paeps 148e948693eSPhilip Paeps /* Consume length */ 149e948693eSPhilip Paeps if (offset + 1 >= size) { 150e948693eSPhilip Paeps rc = ENOSPC; 151e948693eSPhilip Paeps goto fail1; 152e948693eSPhilip Paeps } 153e948693eSPhilip Paeps length = data[offset + 1]; 154e948693eSPhilip Paeps 155e948693eSPhilip Paeps /* Consume *length */ 156e948693eSPhilip Paeps if (offset + 1 + length >= size) { 157e948693eSPhilip Paeps rc = ENOSPC; 158e948693eSPhilip Paeps goto fail2; 159e948693eSPhilip Paeps } 160e948693eSPhilip Paeps 161e948693eSPhilip Paeps offset += 2 + length; 162e948693eSPhilip Paeps used = offset; 163e948693eSPhilip Paeps } 164e948693eSPhilip Paeps 165e948693eSPhilip Paeps /* Checksum the entire sector, including bytes after any DHCP_END */ 166e948693eSPhilip Paeps if (efx_bootcfg_csum(enp, data, size) != 0) { 167e948693eSPhilip Paeps rc = EINVAL; 168e948693eSPhilip Paeps goto fail3; 169e948693eSPhilip Paeps } 170e948693eSPhilip Paeps 171e948693eSPhilip Paeps if (usedp != NULL) 172e948693eSPhilip Paeps *usedp = used; 173e948693eSPhilip Paeps 174e948693eSPhilip Paeps return (0); 175e948693eSPhilip Paeps 176e948693eSPhilip Paeps fail3: 177e948693eSPhilip Paeps EFSYS_PROBE(fail3); 178e948693eSPhilip Paeps fail2: 179e948693eSPhilip Paeps EFSYS_PROBE(fail2); 180e948693eSPhilip Paeps fail1: 181460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 182e948693eSPhilip Paeps 183e948693eSPhilip Paeps return (rc); 184e948693eSPhilip Paeps } 185e948693eSPhilip Paeps 186460cb568SAndrew Rybchenko efx_rc_t 187e948693eSPhilip Paeps efx_bootcfg_read( 188e948693eSPhilip Paeps __in efx_nic_t *enp, 189e948693eSPhilip Paeps __out_bcount(size) caddr_t data, 190e948693eSPhilip Paeps __in size_t size) 191e948693eSPhilip Paeps { 192e948693eSPhilip Paeps uint8_t *payload = NULL; 193e948693eSPhilip Paeps size_t used_bytes; 194*5081d55dSAndrew Rybchenko size_t partn_length; 195e948693eSPhilip Paeps size_t sector_length; 196*5081d55dSAndrew Rybchenko size_t sector_offset; 197460cb568SAndrew Rybchenko efx_rc_t rc; 198e948693eSPhilip Paeps 199*5081d55dSAndrew Rybchenko rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 200e948693eSPhilip Paeps if (rc != 0) 201e948693eSPhilip Paeps goto fail1; 202e948693eSPhilip Paeps 203*5081d55dSAndrew Rybchenko /* The bootcfg sector may be stored in a (larger) shared partition */ 204*5081d55dSAndrew Rybchenko rc = efx_bootcfg_sector(enp, §or_offset, §or_length); 205*5081d55dSAndrew Rybchenko if (rc != 0) 206*5081d55dSAndrew Rybchenko goto fail2; 207*5081d55dSAndrew Rybchenko 208*5081d55dSAndrew Rybchenko if (sector_length > BOOTCFG_MAX_SIZE) 209*5081d55dSAndrew Rybchenko sector_length = BOOTCFG_MAX_SIZE; 210*5081d55dSAndrew Rybchenko 211*5081d55dSAndrew Rybchenko if (sector_offset + sector_length > partn_length) { 212*5081d55dSAndrew Rybchenko /* Partition is too small */ 213*5081d55dSAndrew Rybchenko rc = EFBIG; 214*5081d55dSAndrew Rybchenko goto fail3; 215*5081d55dSAndrew Rybchenko } 216*5081d55dSAndrew Rybchenko 217e948693eSPhilip Paeps /* 218*5081d55dSAndrew Rybchenko * We need to read the entire BOOTCFG sector to ensure we read all the 219e948693eSPhilip Paeps * tags, because legacy bootcfg sectors are not guaranteed to end with 220e948693eSPhilip Paeps * a DHCP_END character. If the user hasn't supplied a sufficiently 221e948693eSPhilip Paeps * large buffer then use our own buffer. 222e948693eSPhilip Paeps */ 223e948693eSPhilip Paeps if (sector_length > size) { 224e948693eSPhilip Paeps EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload); 225e948693eSPhilip Paeps if (payload == NULL) { 226e948693eSPhilip Paeps rc = ENOMEM; 227*5081d55dSAndrew Rybchenko goto fail4; 228e948693eSPhilip Paeps } 229e948693eSPhilip Paeps } else 230e948693eSPhilip Paeps payload = (uint8_t *)data; 231e948693eSPhilip Paeps 232e948693eSPhilip Paeps if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 233*5081d55dSAndrew Rybchenko goto fail5; 234e948693eSPhilip Paeps 235*5081d55dSAndrew Rybchenko rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, sector_offset, 236e948693eSPhilip Paeps (caddr_t)payload, sector_length); 237e948693eSPhilip Paeps 238e948693eSPhilip Paeps efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); 239e948693eSPhilip Paeps 240e948693eSPhilip Paeps if (rc != 0) 241*5081d55dSAndrew Rybchenko goto fail6; 242e948693eSPhilip Paeps 243e948693eSPhilip Paeps /* Verify that the area is correctly formatted and checksummed */ 244e948693eSPhilip Paeps rc = efx_bootcfg_verify(enp, (caddr_t)payload, sector_length, 245e948693eSPhilip Paeps &used_bytes); 246e948693eSPhilip Paeps if (rc != 0 || used_bytes == 0) { 247e948693eSPhilip Paeps payload[0] = (uint8_t)~DHCP_END; 248e948693eSPhilip Paeps payload[1] = DHCP_END; 249e948693eSPhilip Paeps used_bytes = 2; 250e948693eSPhilip Paeps } 251e948693eSPhilip Paeps 252e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 253e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes <= sector_length); 254e948693eSPhilip Paeps 255e948693eSPhilip Paeps /* 256e948693eSPhilip Paeps * Legacy bootcfg sectors don't terminate with a DHCP_END character. 257e948693eSPhilip Paeps * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by 258e948693eSPhilip Paeps * definition large enough for any valid (per-port) bootcfg sector, 259e948693eSPhilip Paeps * so reinitialise the sector if there isn't room for the character. 260e948693eSPhilip Paeps */ 261e948693eSPhilip Paeps if (payload[used_bytes - 1] != DHCP_END) { 262e948693eSPhilip Paeps if (used_bytes + 1 > sector_length) { 263e948693eSPhilip Paeps payload[0] = 0; 264e948693eSPhilip Paeps used_bytes = 1; 265e948693eSPhilip Paeps } 266e948693eSPhilip Paeps 267e948693eSPhilip Paeps payload[used_bytes] = DHCP_END; 268e948693eSPhilip Paeps ++used_bytes; 269e948693eSPhilip Paeps } 270e948693eSPhilip Paeps 271e948693eSPhilip Paeps /* 272e948693eSPhilip Paeps * Verify that the user supplied buffer is large enough for the 273e948693eSPhilip Paeps * entire used bootcfg area, then copy into the user supplied buffer. 274e948693eSPhilip Paeps */ 275e948693eSPhilip Paeps if (used_bytes > size) { 276e948693eSPhilip Paeps rc = ENOSPC; 277*5081d55dSAndrew Rybchenko goto fail7; 278e948693eSPhilip Paeps } 279e948693eSPhilip Paeps if (sector_length > size) { 280e948693eSPhilip Paeps memcpy(data, payload, used_bytes); 281e948693eSPhilip Paeps EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 282e948693eSPhilip Paeps } 283e948693eSPhilip Paeps 284e948693eSPhilip Paeps /* Zero out the unused portion of the user buffer */ 285e948693eSPhilip Paeps if (used_bytes < size) 286e948693eSPhilip Paeps (void) memset(data + used_bytes, 0, size - used_bytes); 287e948693eSPhilip Paeps 288e948693eSPhilip Paeps /* 289e948693eSPhilip Paeps * The checksum includes trailing data after any DHCP_END character, 290e948693eSPhilip Paeps * which we've just modified (by truncation or appending DHCP_END). 291e948693eSPhilip Paeps */ 292e948693eSPhilip Paeps data[0] -= efx_bootcfg_csum(enp, data, size); 293e948693eSPhilip Paeps 294e948693eSPhilip Paeps return (0); 295e948693eSPhilip Paeps 296*5081d55dSAndrew Rybchenko fail7: 297*5081d55dSAndrew Rybchenko EFSYS_PROBE(fail7); 298*5081d55dSAndrew Rybchenko fail6: 299*5081d55dSAndrew Rybchenko EFSYS_PROBE(fail6); 300e948693eSPhilip Paeps fail5: 301e948693eSPhilip Paeps EFSYS_PROBE(fail5); 302*5081d55dSAndrew Rybchenko if (sector_length > size) 303*5081d55dSAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 304e948693eSPhilip Paeps fail4: 305e948693eSPhilip Paeps EFSYS_PROBE(fail4); 306e948693eSPhilip Paeps fail3: 307e948693eSPhilip Paeps EFSYS_PROBE(fail3); 308e948693eSPhilip Paeps fail2: 309e948693eSPhilip Paeps EFSYS_PROBE(fail2); 310e948693eSPhilip Paeps fail1: 311460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 312e948693eSPhilip Paeps 313e948693eSPhilip Paeps return (rc); 314e948693eSPhilip Paeps } 315e948693eSPhilip Paeps 316460cb568SAndrew Rybchenko efx_rc_t 317e948693eSPhilip Paeps efx_bootcfg_write( 318e948693eSPhilip Paeps __in efx_nic_t *enp, 319e948693eSPhilip Paeps __in_bcount(size) caddr_t data, 320e948693eSPhilip Paeps __in size_t size) 321e948693eSPhilip Paeps { 322*5081d55dSAndrew Rybchenko uint8_t *partn_data; 323e948693eSPhilip Paeps uint8_t checksum; 324*5081d55dSAndrew Rybchenko size_t partn_length; 325e948693eSPhilip Paeps size_t sector_length; 326*5081d55dSAndrew Rybchenko size_t sector_offset; 327e948693eSPhilip Paeps size_t used_bytes; 328460cb568SAndrew Rybchenko efx_rc_t rc; 329e948693eSPhilip Paeps 330*5081d55dSAndrew Rybchenko rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 331e948693eSPhilip Paeps if (rc != 0) 332e948693eSPhilip Paeps goto fail1; 333e948693eSPhilip Paeps 334*5081d55dSAndrew Rybchenko /* The bootcfg sector may be stored in a (larger) shared partition */ 335*5081d55dSAndrew Rybchenko rc = efx_bootcfg_sector(enp, §or_offset, §or_length); 336*5081d55dSAndrew Rybchenko if (rc != 0) 337*5081d55dSAndrew Rybchenko goto fail2; 338*5081d55dSAndrew Rybchenko 339e948693eSPhilip Paeps if (sector_length > BOOTCFG_MAX_SIZE) 340e948693eSPhilip Paeps sector_length = BOOTCFG_MAX_SIZE; 341e948693eSPhilip Paeps 342*5081d55dSAndrew Rybchenko if (sector_offset + sector_length > partn_length) { 343*5081d55dSAndrew Rybchenko /* Partition is too small */ 344*5081d55dSAndrew Rybchenko rc = EFBIG; 345*5081d55dSAndrew Rybchenko goto fail3; 346*5081d55dSAndrew Rybchenko } 347*5081d55dSAndrew Rybchenko 348e948693eSPhilip Paeps if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0) 349*5081d55dSAndrew Rybchenko goto fail4; 350e948693eSPhilip Paeps 351e948693eSPhilip Paeps /* The caller *must* terminate their block with a DHCP_END character */ 352*5081d55dSAndrew Rybchenko if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) { 353*5081d55dSAndrew Rybchenko /* Block too short or DHCP_END missing */ 354e948693eSPhilip Paeps rc = ENOENT; 355*5081d55dSAndrew Rybchenko goto fail5; 356e948693eSPhilip Paeps } 357e948693eSPhilip Paeps 358e948693eSPhilip Paeps /* Check that the hardware has support for this much data */ 359e948693eSPhilip Paeps if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) { 360e948693eSPhilip Paeps rc = ENOSPC; 361e948693eSPhilip Paeps goto fail6; 362e948693eSPhilip Paeps } 363e948693eSPhilip Paeps 364*5081d55dSAndrew Rybchenko /* 365*5081d55dSAndrew Rybchenko * If the BOOTCFG sector is stored in a shared partition, then we must 366*5081d55dSAndrew Rybchenko * read the whole partition and insert the updated bootcfg sector at the 367*5081d55dSAndrew Rybchenko * correct offset. 368*5081d55dSAndrew Rybchenko */ 369*5081d55dSAndrew Rybchenko EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data); 370*5081d55dSAndrew Rybchenko if (partn_data == NULL) { 371*5081d55dSAndrew Rybchenko rc = ENOMEM; 372e948693eSPhilip Paeps goto fail7; 373*5081d55dSAndrew Rybchenko } 374*5081d55dSAndrew Rybchenko 375*5081d55dSAndrew Rybchenko rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 376*5081d55dSAndrew Rybchenko if (rc != 0) 377*5081d55dSAndrew Rybchenko goto fail8; 378*5081d55dSAndrew Rybchenko 379*5081d55dSAndrew Rybchenko /* Read the entire partition */ 380*5081d55dSAndrew Rybchenko rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0, 381*5081d55dSAndrew Rybchenko (caddr_t)partn_data, partn_length); 382*5081d55dSAndrew Rybchenko if (rc != 0) 383*5081d55dSAndrew Rybchenko goto fail9; 384e948693eSPhilip Paeps 385e948693eSPhilip Paeps /* 386*5081d55dSAndrew Rybchenko * Insert the BOOTCFG sector into the partition, Zero out all data after 387*5081d55dSAndrew Rybchenko * the DHCP_END tag, and adjust the checksum. 388e948693eSPhilip Paeps */ 389*5081d55dSAndrew Rybchenko (void) memset(partn_data + sector_offset, 0x0, sector_length); 390*5081d55dSAndrew Rybchenko (void) memcpy(partn_data + sector_offset, data, used_bytes); 391*5081d55dSAndrew Rybchenko 392e948693eSPhilip Paeps checksum = efx_bootcfg_csum(enp, data, used_bytes); 393*5081d55dSAndrew Rybchenko partn_data[sector_offset] -= checksum; 394e948693eSPhilip Paeps 395*5081d55dSAndrew Rybchenko if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 396*5081d55dSAndrew Rybchenko goto fail10; 397e948693eSPhilip Paeps 398e948693eSPhilip Paeps if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 399*5081d55dSAndrew Rybchenko 0, partn_data, partn_length)) != 0) { 400*5081d55dSAndrew Rybchenko goto fail11; 401e948693eSPhilip Paeps } 402e948693eSPhilip Paeps 403e948693eSPhilip Paeps efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); 404e948693eSPhilip Paeps 405*5081d55dSAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 406e948693eSPhilip Paeps 407e948693eSPhilip Paeps return (0); 408e948693eSPhilip Paeps 409*5081d55dSAndrew Rybchenko fail11: 410*5081d55dSAndrew Rybchenko EFSYS_PROBE(fail11); 411*5081d55dSAndrew Rybchenko fail10: 412*5081d55dSAndrew Rybchenko EFSYS_PROBE(fail10); 413*5081d55dSAndrew Rybchenko fail9: 414*5081d55dSAndrew Rybchenko EFSYS_PROBE(fail9); 415e948693eSPhilip Paeps 416e948693eSPhilip Paeps efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); 417*5081d55dSAndrew Rybchenko fail8: 418*5081d55dSAndrew Rybchenko EFSYS_PROBE(fail8); 419*5081d55dSAndrew Rybchenko 420*5081d55dSAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 421*5081d55dSAndrew Rybchenko fail7: 422*5081d55dSAndrew Rybchenko EFSYS_PROBE(fail7); 423*5081d55dSAndrew Rybchenko fail6: 424*5081d55dSAndrew Rybchenko EFSYS_PROBE(fail6); 425e948693eSPhilip Paeps fail5: 426e948693eSPhilip Paeps EFSYS_PROBE(fail5); 427e948693eSPhilip Paeps fail4: 428e948693eSPhilip Paeps EFSYS_PROBE(fail4); 429e948693eSPhilip Paeps fail3: 430e948693eSPhilip Paeps EFSYS_PROBE(fail3); 431e948693eSPhilip Paeps fail2: 432e948693eSPhilip Paeps EFSYS_PROBE(fail2); 433e948693eSPhilip Paeps fail1: 434460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 435e948693eSPhilip Paeps 436e948693eSPhilip Paeps return (rc); 437e948693eSPhilip Paeps } 438e948693eSPhilip Paeps 439e948693eSPhilip Paeps #endif /* EFSYS_OPT_BOOTCFG */ 440