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 5160cf15c5SAndrew Rybchenko #define DHCP_END ((uint8_t)0xff) 5260cf15c5SAndrew Rybchenko #define DHCP_PAD ((uint8_t)0) 53e948693eSPhilip Paeps 545081d55dSAndrew Rybchenko 55662c835bSAndrew Rybchenko /* Report the layout of bootcfg sectors in NVRAM partition. */ 56662c835bSAndrew Rybchenko __checkReturn efx_rc_t 57662c835bSAndrew Rybchenko efx_bootcfg_sector_info( 585081d55dSAndrew Rybchenko __in efx_nic_t *enp, 59662c835bSAndrew Rybchenko __in uint32_t pf, 60662c835bSAndrew Rybchenko __out_opt uint32_t *sector_countp, 615081d55dSAndrew Rybchenko __out size_t *offsetp, 625081d55dSAndrew Rybchenko __out size_t *max_sizep) 635081d55dSAndrew Rybchenko { 64662c835bSAndrew Rybchenko uint32_t count; 655081d55dSAndrew Rybchenko size_t max_size; 665081d55dSAndrew Rybchenko size_t offset; 675081d55dSAndrew Rybchenko int rc; 685081d55dSAndrew Rybchenko 695081d55dSAndrew Rybchenko switch (enp->en_family) { 705081d55dSAndrew Rybchenko #if EFSYS_OPT_SIENA 715081d55dSAndrew Rybchenko case EFX_FAMILY_SIENA: 725081d55dSAndrew Rybchenko max_size = BOOTCFG_MAX_SIZE; 735081d55dSAndrew Rybchenko offset = 0; 74662c835bSAndrew Rybchenko count = 1; 755081d55dSAndrew Rybchenko break; 765081d55dSAndrew Rybchenko #endif /* EFSYS_OPT_SIENA */ 775081d55dSAndrew Rybchenko 785081d55dSAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON 795081d55dSAndrew Rybchenko case EFX_FAMILY_HUNTINGTON: 805081d55dSAndrew Rybchenko max_size = BOOTCFG_MAX_SIZE; 815081d55dSAndrew Rybchenko offset = 0; 82662c835bSAndrew Rybchenko count = 1; 835081d55dSAndrew Rybchenko break; 845081d55dSAndrew Rybchenko #endif /* EFSYS_OPT_HUNTINGTON */ 855081d55dSAndrew Rybchenko 865081d55dSAndrew Rybchenko #if EFSYS_OPT_MEDFORD 875081d55dSAndrew Rybchenko case EFX_FAMILY_MEDFORD: { 885081d55dSAndrew Rybchenko /* Shared partition (array indexed by PF) */ 89662c835bSAndrew Rybchenko max_size = BOOTCFG_PER_PF; 90662c835bSAndrew Rybchenko count = BOOTCFG_PF_COUNT; 91662c835bSAndrew Rybchenko if (pf >= count) { 92662c835bSAndrew Rybchenko rc = EINVAL; 93662c835bSAndrew Rybchenko goto fail2; 94662c835bSAndrew Rybchenko } 95662c835bSAndrew Rybchenko offset = max_size * pf; 965081d55dSAndrew Rybchenko break; 975081d55dSAndrew Rybchenko } 985081d55dSAndrew Rybchenko #endif /* EFSYS_OPT_MEDFORD */ 995081d55dSAndrew Rybchenko 100*df0385e5SAndrew Rybchenko #if EFSYS_OPT_MEDFORD2 101*df0385e5SAndrew Rybchenko case EFX_FAMILY_MEDFORD2: { 102*df0385e5SAndrew Rybchenko /* Shared partition (array indexed by PF) */ 103*df0385e5SAndrew Rybchenko max_size = BOOTCFG_PER_PF; 104*df0385e5SAndrew Rybchenko count = BOOTCFG_PF_COUNT; 105*df0385e5SAndrew Rybchenko if (pf >= count) { 106*df0385e5SAndrew Rybchenko rc = EINVAL; 107*df0385e5SAndrew Rybchenko goto fail3; 108*df0385e5SAndrew Rybchenko } 109*df0385e5SAndrew Rybchenko offset = max_size * pf; 110*df0385e5SAndrew Rybchenko break; 111*df0385e5SAndrew Rybchenko } 112*df0385e5SAndrew Rybchenko #endif /* EFSYS_OPT_MEDFORD2 */ 113*df0385e5SAndrew Rybchenko 1145081d55dSAndrew Rybchenko default: 1155081d55dSAndrew Rybchenko EFSYS_ASSERT(0); 1165081d55dSAndrew Rybchenko rc = ENOTSUP; 1175081d55dSAndrew Rybchenko goto fail1; 1185081d55dSAndrew Rybchenko } 1195081d55dSAndrew Rybchenko EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE); 1205081d55dSAndrew Rybchenko 121662c835bSAndrew Rybchenko if (sector_countp != NULL) 122662c835bSAndrew Rybchenko *sector_countp = count; 1235081d55dSAndrew Rybchenko *offsetp = offset; 1245081d55dSAndrew Rybchenko *max_sizep = max_size; 1255081d55dSAndrew Rybchenko 1265081d55dSAndrew Rybchenko return (0); 1275081d55dSAndrew Rybchenko 128*df0385e5SAndrew Rybchenko #if EFSYS_OPT_MEDFORD2 129*df0385e5SAndrew Rybchenko fail3: 130*df0385e5SAndrew Rybchenko EFSYS_PROBE(fail3); 131*df0385e5SAndrew Rybchenko #endif 132662c835bSAndrew Rybchenko #if EFSYS_OPT_MEDFORD 133662c835bSAndrew Rybchenko fail2: 134662c835bSAndrew Rybchenko EFSYS_PROBE(fail2); 135662c835bSAndrew Rybchenko #endif 1365081d55dSAndrew Rybchenko fail1: 1375081d55dSAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 1385081d55dSAndrew Rybchenko return (rc); 1395081d55dSAndrew Rybchenko } 1405081d55dSAndrew Rybchenko 1415081d55dSAndrew Rybchenko 142e948693eSPhilip Paeps static __checkReturn uint8_t 143e948693eSPhilip Paeps efx_bootcfg_csum( 144e948693eSPhilip Paeps __in efx_nic_t *enp, 145662c835bSAndrew Rybchenko __in_bcount(size) uint8_t const *data, 146e948693eSPhilip Paeps __in size_t size) 147e948693eSPhilip Paeps { 148e948693eSPhilip Paeps _NOTE(ARGUNUSED(enp)) 149e948693eSPhilip Paeps 150e948693eSPhilip Paeps unsigned int pos; 151e948693eSPhilip Paeps uint8_t checksum = 0; 152e948693eSPhilip Paeps 153e948693eSPhilip Paeps for (pos = 0; pos < size; pos++) 154e948693eSPhilip Paeps checksum += data[pos]; 155e948693eSPhilip Paeps return (checksum); 156e948693eSPhilip Paeps } 157e948693eSPhilip Paeps 158460cb568SAndrew Rybchenko static __checkReturn efx_rc_t 159e948693eSPhilip Paeps efx_bootcfg_verify( 160e948693eSPhilip Paeps __in efx_nic_t *enp, 161662c835bSAndrew Rybchenko __in_bcount(size) uint8_t const *data, 162e948693eSPhilip Paeps __in size_t size, 1633c838a9fSAndrew Rybchenko __out_opt size_t *usedp) 164e948693eSPhilip Paeps { 165e948693eSPhilip Paeps size_t offset = 0; 166e948693eSPhilip Paeps size_t used = 0; 167460cb568SAndrew Rybchenko efx_rc_t rc; 168e948693eSPhilip Paeps 169453130d9SPedro F. Giffuni /* Start parsing tags immediately after the checksum */ 170e948693eSPhilip Paeps for (offset = 1; offset < size; ) { 171e948693eSPhilip Paeps uint8_t tag; 172e948693eSPhilip Paeps uint8_t length; 173e948693eSPhilip Paeps 174e948693eSPhilip Paeps /* Consume tag */ 175e948693eSPhilip Paeps tag = data[offset]; 176e948693eSPhilip Paeps if (tag == DHCP_END) { 177e948693eSPhilip Paeps offset++; 178e948693eSPhilip Paeps used = offset; 179e948693eSPhilip Paeps break; 180e948693eSPhilip Paeps } 181e948693eSPhilip Paeps if (tag == DHCP_PAD) { 182e948693eSPhilip Paeps offset++; 183e948693eSPhilip Paeps continue; 184e948693eSPhilip Paeps } 185e948693eSPhilip Paeps 186e948693eSPhilip Paeps /* Consume length */ 187e948693eSPhilip Paeps if (offset + 1 >= size) { 188e948693eSPhilip Paeps rc = ENOSPC; 189e948693eSPhilip Paeps goto fail1; 190e948693eSPhilip Paeps } 191e948693eSPhilip Paeps length = data[offset + 1]; 192e948693eSPhilip Paeps 193e948693eSPhilip Paeps /* Consume *length */ 194e948693eSPhilip Paeps if (offset + 1 + length >= size) { 195e948693eSPhilip Paeps rc = ENOSPC; 196e948693eSPhilip Paeps goto fail2; 197e948693eSPhilip Paeps } 198e948693eSPhilip Paeps 199e948693eSPhilip Paeps offset += 2 + length; 200e948693eSPhilip Paeps used = offset; 201e948693eSPhilip Paeps } 202e948693eSPhilip Paeps 203e948693eSPhilip Paeps /* Checksum the entire sector, including bytes after any DHCP_END */ 204e948693eSPhilip Paeps if (efx_bootcfg_csum(enp, data, size) != 0) { 205e948693eSPhilip Paeps rc = EINVAL; 206e948693eSPhilip Paeps goto fail3; 207e948693eSPhilip Paeps } 208e948693eSPhilip Paeps 209e948693eSPhilip Paeps if (usedp != NULL) 210e948693eSPhilip Paeps *usedp = used; 211e948693eSPhilip Paeps 212e948693eSPhilip Paeps return (0); 213e948693eSPhilip Paeps 214e948693eSPhilip Paeps fail3: 215e948693eSPhilip Paeps EFSYS_PROBE(fail3); 216e948693eSPhilip Paeps fail2: 217e948693eSPhilip Paeps EFSYS_PROBE(fail2); 218e948693eSPhilip Paeps fail1: 219460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 220e948693eSPhilip Paeps 221e948693eSPhilip Paeps return (rc); 222e948693eSPhilip Paeps } 223e948693eSPhilip Paeps 224662c835bSAndrew Rybchenko /* 225662c835bSAndrew Rybchenko * Copy bootcfg sector data to a target buffer which may differ in size. 226662c835bSAndrew Rybchenko * Optionally corrects format errors in source buffer. 227662c835bSAndrew Rybchenko */ 228662c835bSAndrew Rybchenko efx_rc_t 229662c835bSAndrew Rybchenko efx_bootcfg_copy_sector( 230662c835bSAndrew Rybchenko __in efx_nic_t *enp, 231662c835bSAndrew Rybchenko __inout_bcount(sector_length) 232662c835bSAndrew Rybchenko uint8_t *sector, 233662c835bSAndrew Rybchenko __in size_t sector_length, 234662c835bSAndrew Rybchenko __out_bcount(data_size) uint8_t *data, 235662c835bSAndrew Rybchenko __in size_t data_size, 236662c835bSAndrew Rybchenko __in boolean_t handle_format_errors) 237662c835bSAndrew Rybchenko { 238662c835bSAndrew Rybchenko size_t used_bytes; 239662c835bSAndrew Rybchenko efx_rc_t rc; 240662c835bSAndrew Rybchenko 241662c835bSAndrew Rybchenko /* Verify that the area is correctly formatted and checksummed */ 242662c835bSAndrew Rybchenko rc = efx_bootcfg_verify(enp, sector, sector_length, 243662c835bSAndrew Rybchenko &used_bytes); 244662c835bSAndrew Rybchenko 245662c835bSAndrew Rybchenko if (!handle_format_errors) { 246662c835bSAndrew Rybchenko if (rc != 0) 247662c835bSAndrew Rybchenko goto fail1; 248662c835bSAndrew Rybchenko 249662c835bSAndrew Rybchenko if ((used_bytes < 2) || 250662c835bSAndrew Rybchenko (sector[used_bytes - 1] != DHCP_END)) { 251662c835bSAndrew Rybchenko /* Block too short, or DHCP_END missing */ 252662c835bSAndrew Rybchenko rc = ENOENT; 253662c835bSAndrew Rybchenko goto fail2; 254662c835bSAndrew Rybchenko } 255662c835bSAndrew Rybchenko } 256662c835bSAndrew Rybchenko 257662c835bSAndrew Rybchenko /* Synthesize empty format on verification failure */ 258662c835bSAndrew Rybchenko if (rc != 0 || used_bytes == 0) { 259662c835bSAndrew Rybchenko sector[0] = 0; 260662c835bSAndrew Rybchenko sector[1] = DHCP_END; 261662c835bSAndrew Rybchenko used_bytes = 2; 262662c835bSAndrew Rybchenko } 263662c835bSAndrew Rybchenko EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 264662c835bSAndrew Rybchenko EFSYS_ASSERT(used_bytes <= sector_length); 265662c835bSAndrew Rybchenko EFSYS_ASSERT(sector_length >= 2); 266662c835bSAndrew Rybchenko 267662c835bSAndrew Rybchenko /* 268662c835bSAndrew Rybchenko * Legacy bootcfg sectors don't terminate with a DHCP_END character. 269662c835bSAndrew Rybchenko * Modify the returned payload so it does. 270662c835bSAndrew Rybchenko * Reinitialise the sector if there isn't room for the character. 271662c835bSAndrew Rybchenko */ 272662c835bSAndrew Rybchenko if (sector[used_bytes - 1] != DHCP_END) { 273662c835bSAndrew Rybchenko if (used_bytes >= sector_length) { 274662c835bSAndrew Rybchenko sector[0] = 0; 275662c835bSAndrew Rybchenko used_bytes = 1; 276662c835bSAndrew Rybchenko } 277662c835bSAndrew Rybchenko sector[used_bytes] = DHCP_END; 278662c835bSAndrew Rybchenko ++used_bytes; 279662c835bSAndrew Rybchenko } 280662c835bSAndrew Rybchenko 281662c835bSAndrew Rybchenko /* 282662c835bSAndrew Rybchenko * Verify that the target buffer is large enough for the 283662c835bSAndrew Rybchenko * entire used bootcfg area, then copy into the target buffer. 284662c835bSAndrew Rybchenko */ 285662c835bSAndrew Rybchenko if (used_bytes > data_size) { 286662c835bSAndrew Rybchenko rc = ENOSPC; 287662c835bSAndrew Rybchenko goto fail3; 288662c835bSAndrew Rybchenko } 289662c835bSAndrew Rybchenko memcpy(data, sector, used_bytes); 290662c835bSAndrew Rybchenko 291662c835bSAndrew Rybchenko /* Zero out the unused portion of the target buffer */ 292662c835bSAndrew Rybchenko if (used_bytes < data_size) 293662c835bSAndrew Rybchenko (void) memset(data + used_bytes, 0, data_size - used_bytes); 294662c835bSAndrew Rybchenko 295662c835bSAndrew Rybchenko /* 296662c835bSAndrew Rybchenko * The checksum includes trailing data after any DHCP_END character, 297662c835bSAndrew Rybchenko * which we've just modified (by truncation or appending DHCP_END). 298662c835bSAndrew Rybchenko */ 299662c835bSAndrew Rybchenko data[0] -= efx_bootcfg_csum(enp, data, data_size); 300662c835bSAndrew Rybchenko 301662c835bSAndrew Rybchenko return (0); 302662c835bSAndrew Rybchenko 303662c835bSAndrew Rybchenko fail3: 304662c835bSAndrew Rybchenko EFSYS_PROBE(fail3); 305662c835bSAndrew Rybchenko fail2: 306662c835bSAndrew Rybchenko EFSYS_PROBE(fail2); 307662c835bSAndrew Rybchenko fail1: 308662c835bSAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 309662c835bSAndrew Rybchenko 310662c835bSAndrew Rybchenko return (rc); 311662c835bSAndrew Rybchenko } 312662c835bSAndrew Rybchenko 313460cb568SAndrew Rybchenko efx_rc_t 314e948693eSPhilip Paeps efx_bootcfg_read( 315e948693eSPhilip Paeps __in efx_nic_t *enp, 3167e17c17dSAndrew Rybchenko __out_bcount(size) uint8_t *data, 317e948693eSPhilip Paeps __in size_t size) 318e948693eSPhilip Paeps { 319e948693eSPhilip Paeps uint8_t *payload = NULL; 320e948693eSPhilip Paeps size_t used_bytes; 3215081d55dSAndrew Rybchenko size_t partn_length; 322e948693eSPhilip Paeps size_t sector_length; 3235081d55dSAndrew Rybchenko size_t sector_offset; 324460cb568SAndrew Rybchenko efx_rc_t rc; 325662c835bSAndrew Rybchenko uint32_t sector_number; 326e948693eSPhilip Paeps 327*df0385e5SAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 328662c835bSAndrew Rybchenko sector_number = enp->en_nic_cfg.enc_pf; 329662c835bSAndrew Rybchenko #else 330662c835bSAndrew Rybchenko sector_number = 0; 331662c835bSAndrew Rybchenko #endif 3325081d55dSAndrew Rybchenko rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 333e948693eSPhilip Paeps if (rc != 0) 334e948693eSPhilip Paeps goto fail1; 335e948693eSPhilip Paeps 3365081d55dSAndrew Rybchenko /* The bootcfg sector may be stored in a (larger) shared partition */ 337662c835bSAndrew Rybchenko rc = efx_bootcfg_sector_info(enp, sector_number, 338662c835bSAndrew Rybchenko NULL, §or_offset, §or_length); 3395081d55dSAndrew Rybchenko if (rc != 0) 3405081d55dSAndrew Rybchenko goto fail2; 3415081d55dSAndrew Rybchenko 3425081d55dSAndrew Rybchenko if (sector_length > BOOTCFG_MAX_SIZE) 3435081d55dSAndrew Rybchenko sector_length = BOOTCFG_MAX_SIZE; 3445081d55dSAndrew Rybchenko 3455081d55dSAndrew Rybchenko if (sector_offset + sector_length > partn_length) { 3465081d55dSAndrew Rybchenko /* Partition is too small */ 3475081d55dSAndrew Rybchenko rc = EFBIG; 3485081d55dSAndrew Rybchenko goto fail3; 3495081d55dSAndrew Rybchenko } 3505081d55dSAndrew Rybchenko 351e948693eSPhilip Paeps /* 3525081d55dSAndrew Rybchenko * We need to read the entire BOOTCFG sector to ensure we read all the 353e948693eSPhilip Paeps * tags, because legacy bootcfg sectors are not guaranteed to end with 354e948693eSPhilip Paeps * a DHCP_END character. If the user hasn't supplied a sufficiently 355e948693eSPhilip Paeps * large buffer then use our own buffer. 356e948693eSPhilip Paeps */ 357e948693eSPhilip Paeps if (sector_length > size) { 358e948693eSPhilip Paeps EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload); 359e948693eSPhilip Paeps if (payload == NULL) { 360e948693eSPhilip Paeps rc = ENOMEM; 3615081d55dSAndrew Rybchenko goto fail4; 362e948693eSPhilip Paeps } 363e948693eSPhilip Paeps } else 364e948693eSPhilip Paeps payload = (uint8_t *)data; 365e948693eSPhilip Paeps 366e948693eSPhilip Paeps if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 3675081d55dSAndrew Rybchenko goto fail5; 368e948693eSPhilip Paeps 369662c835bSAndrew Rybchenko if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 370662c835bSAndrew Rybchenko sector_offset, (caddr_t)payload, sector_length)) != 0) { 371d5106d05SAndrew Rybchenko (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 3725081d55dSAndrew Rybchenko goto fail6; 373662c835bSAndrew Rybchenko } 374662c835bSAndrew Rybchenko 375d5106d05SAndrew Rybchenko if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 376662c835bSAndrew Rybchenko goto fail7; 377e948693eSPhilip Paeps 378e948693eSPhilip Paeps /* Verify that the area is correctly formatted and checksummed */ 3797e17c17dSAndrew Rybchenko rc = efx_bootcfg_verify(enp, payload, sector_length, 380e948693eSPhilip Paeps &used_bytes); 381e948693eSPhilip Paeps if (rc != 0 || used_bytes == 0) { 3825e7bb158SAndrew Rybchenko payload[0] = (uint8_t)(~DHCP_END & 0xff); 383e948693eSPhilip Paeps payload[1] = DHCP_END; 384e948693eSPhilip Paeps used_bytes = 2; 385e948693eSPhilip Paeps } 386e948693eSPhilip Paeps 387e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 388e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes <= sector_length); 389e948693eSPhilip Paeps 390e948693eSPhilip Paeps /* 391e948693eSPhilip Paeps * Legacy bootcfg sectors don't terminate with a DHCP_END character. 392e948693eSPhilip Paeps * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by 393e948693eSPhilip Paeps * definition large enough for any valid (per-port) bootcfg sector, 394e948693eSPhilip Paeps * so reinitialise the sector if there isn't room for the character. 395e948693eSPhilip Paeps */ 396e948693eSPhilip Paeps if (payload[used_bytes - 1] != DHCP_END) { 397e948693eSPhilip Paeps if (used_bytes + 1 > sector_length) { 398e948693eSPhilip Paeps payload[0] = 0; 399e948693eSPhilip Paeps used_bytes = 1; 400e948693eSPhilip Paeps } 401e948693eSPhilip Paeps 402e948693eSPhilip Paeps payload[used_bytes] = DHCP_END; 403e948693eSPhilip Paeps ++used_bytes; 404e948693eSPhilip Paeps } 405e948693eSPhilip Paeps 406e948693eSPhilip Paeps /* 407e948693eSPhilip Paeps * Verify that the user supplied buffer is large enough for the 408e948693eSPhilip Paeps * entire used bootcfg area, then copy into the user supplied buffer. 409e948693eSPhilip Paeps */ 410e948693eSPhilip Paeps if (used_bytes > size) { 411e948693eSPhilip Paeps rc = ENOSPC; 412662c835bSAndrew Rybchenko goto fail8; 413e948693eSPhilip Paeps } 414e948693eSPhilip Paeps if (sector_length > size) { 415e948693eSPhilip Paeps memcpy(data, payload, used_bytes); 416e948693eSPhilip Paeps EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 417e948693eSPhilip Paeps } 418e948693eSPhilip Paeps 419e948693eSPhilip Paeps /* Zero out the unused portion of the user buffer */ 420e948693eSPhilip Paeps if (used_bytes < size) 421e948693eSPhilip Paeps (void) memset(data + used_bytes, 0, size - used_bytes); 422e948693eSPhilip Paeps 423e948693eSPhilip Paeps /* 424e948693eSPhilip Paeps * The checksum includes trailing data after any DHCP_END character, 425e948693eSPhilip Paeps * which we've just modified (by truncation or appending DHCP_END). 426e948693eSPhilip Paeps */ 427e948693eSPhilip Paeps data[0] -= efx_bootcfg_csum(enp, data, size); 428e948693eSPhilip Paeps 429e948693eSPhilip Paeps return (0); 430e948693eSPhilip Paeps 431662c835bSAndrew Rybchenko fail8: 432662c835bSAndrew Rybchenko EFSYS_PROBE(fail8); 4335081d55dSAndrew Rybchenko fail7: 4345081d55dSAndrew Rybchenko EFSYS_PROBE(fail7); 4355081d55dSAndrew Rybchenko fail6: 4365081d55dSAndrew Rybchenko EFSYS_PROBE(fail6); 437e948693eSPhilip Paeps fail5: 438e948693eSPhilip Paeps EFSYS_PROBE(fail5); 4395081d55dSAndrew Rybchenko if (sector_length > size) 4405081d55dSAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 441e948693eSPhilip Paeps fail4: 442e948693eSPhilip Paeps EFSYS_PROBE(fail4); 443e948693eSPhilip Paeps fail3: 444e948693eSPhilip Paeps EFSYS_PROBE(fail3); 445e948693eSPhilip Paeps fail2: 446e948693eSPhilip Paeps EFSYS_PROBE(fail2); 447e948693eSPhilip Paeps fail1: 448460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 449e948693eSPhilip Paeps 450e948693eSPhilip Paeps return (rc); 451e948693eSPhilip Paeps } 452e948693eSPhilip Paeps 453460cb568SAndrew Rybchenko efx_rc_t 454e948693eSPhilip Paeps efx_bootcfg_write( 455e948693eSPhilip Paeps __in efx_nic_t *enp, 4567e17c17dSAndrew Rybchenko __in_bcount(size) uint8_t *data, 457e948693eSPhilip Paeps __in size_t size) 458e948693eSPhilip Paeps { 4595081d55dSAndrew Rybchenko uint8_t *partn_data; 460e948693eSPhilip Paeps uint8_t checksum; 4615081d55dSAndrew Rybchenko size_t partn_length; 462e948693eSPhilip Paeps size_t sector_length; 4635081d55dSAndrew Rybchenko size_t sector_offset; 464e948693eSPhilip Paeps size_t used_bytes; 465460cb568SAndrew Rybchenko efx_rc_t rc; 466662c835bSAndrew Rybchenko uint32_t sector_number; 467662c835bSAndrew Rybchenko 468*df0385e5SAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 469662c835bSAndrew Rybchenko sector_number = enp->en_nic_cfg.enc_pf; 470662c835bSAndrew Rybchenko #else 471662c835bSAndrew Rybchenko sector_number = 0; 472662c835bSAndrew Rybchenko #endif 473e948693eSPhilip Paeps 4745081d55dSAndrew Rybchenko rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 475e948693eSPhilip Paeps if (rc != 0) 476e948693eSPhilip Paeps goto fail1; 477e948693eSPhilip Paeps 4785081d55dSAndrew Rybchenko /* The bootcfg sector may be stored in a (larger) shared partition */ 479662c835bSAndrew Rybchenko rc = efx_bootcfg_sector_info(enp, sector_number, 480662c835bSAndrew Rybchenko NULL, §or_offset, §or_length); 4815081d55dSAndrew Rybchenko if (rc != 0) 4825081d55dSAndrew Rybchenko goto fail2; 4835081d55dSAndrew Rybchenko 484e948693eSPhilip Paeps if (sector_length > BOOTCFG_MAX_SIZE) 485e948693eSPhilip Paeps sector_length = BOOTCFG_MAX_SIZE; 486e948693eSPhilip Paeps 4875081d55dSAndrew Rybchenko if (sector_offset + sector_length > partn_length) { 4885081d55dSAndrew Rybchenko /* Partition is too small */ 4895081d55dSAndrew Rybchenko rc = EFBIG; 4905081d55dSAndrew Rybchenko goto fail3; 4915081d55dSAndrew Rybchenko } 4925081d55dSAndrew Rybchenko 493e948693eSPhilip Paeps if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0) 4945081d55dSAndrew Rybchenko goto fail4; 495e948693eSPhilip Paeps 496e948693eSPhilip Paeps /* The caller *must* terminate their block with a DHCP_END character */ 4975081d55dSAndrew Rybchenko if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) { 4985081d55dSAndrew Rybchenko /* Block too short or DHCP_END missing */ 499e948693eSPhilip Paeps rc = ENOENT; 5005081d55dSAndrew Rybchenko goto fail5; 501e948693eSPhilip Paeps } 502e948693eSPhilip Paeps 503e948693eSPhilip Paeps /* Check that the hardware has support for this much data */ 504e948693eSPhilip Paeps if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) { 505e948693eSPhilip Paeps rc = ENOSPC; 506e948693eSPhilip Paeps goto fail6; 507e948693eSPhilip Paeps } 508e948693eSPhilip Paeps 5095081d55dSAndrew Rybchenko /* 5105081d55dSAndrew Rybchenko * If the BOOTCFG sector is stored in a shared partition, then we must 5115081d55dSAndrew Rybchenko * read the whole partition and insert the updated bootcfg sector at the 5125081d55dSAndrew Rybchenko * correct offset. 5135081d55dSAndrew Rybchenko */ 5145081d55dSAndrew Rybchenko EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data); 5155081d55dSAndrew Rybchenko if (partn_data == NULL) { 5165081d55dSAndrew Rybchenko rc = ENOMEM; 517e948693eSPhilip Paeps goto fail7; 5185081d55dSAndrew Rybchenko } 5195081d55dSAndrew Rybchenko 5205081d55dSAndrew Rybchenko rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 5215081d55dSAndrew Rybchenko if (rc != 0) 5225081d55dSAndrew Rybchenko goto fail8; 5235081d55dSAndrew Rybchenko 5245081d55dSAndrew Rybchenko /* Read the entire partition */ 5255081d55dSAndrew Rybchenko rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0, 5265081d55dSAndrew Rybchenko (caddr_t)partn_data, partn_length); 5275081d55dSAndrew Rybchenko if (rc != 0) 5285081d55dSAndrew Rybchenko goto fail9; 529e948693eSPhilip Paeps 530e948693eSPhilip Paeps /* 5315081d55dSAndrew Rybchenko * Insert the BOOTCFG sector into the partition, Zero out all data after 5325081d55dSAndrew Rybchenko * the DHCP_END tag, and adjust the checksum. 533e948693eSPhilip Paeps */ 5345081d55dSAndrew Rybchenko (void) memset(partn_data + sector_offset, 0x0, sector_length); 5355081d55dSAndrew Rybchenko (void) memcpy(partn_data + sector_offset, data, used_bytes); 5365081d55dSAndrew Rybchenko 537e948693eSPhilip Paeps checksum = efx_bootcfg_csum(enp, data, used_bytes); 5385081d55dSAndrew Rybchenko partn_data[sector_offset] -= checksum; 539e948693eSPhilip Paeps 5405081d55dSAndrew Rybchenko if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 5415081d55dSAndrew Rybchenko goto fail10; 542e948693eSPhilip Paeps 543e948693eSPhilip Paeps if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 544662c835bSAndrew Rybchenko 0, (caddr_t)partn_data, partn_length)) != 0) 5455081d55dSAndrew Rybchenko goto fail11; 546e948693eSPhilip Paeps 547d5106d05SAndrew Rybchenko if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 548662c835bSAndrew Rybchenko goto fail12; 549e948693eSPhilip Paeps 5505081d55dSAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 551e948693eSPhilip Paeps 552e948693eSPhilip Paeps return (0); 553e948693eSPhilip Paeps 554662c835bSAndrew Rybchenko fail12: 555662c835bSAndrew Rybchenko EFSYS_PROBE(fail12); 5565081d55dSAndrew Rybchenko fail11: 5575081d55dSAndrew Rybchenko EFSYS_PROBE(fail11); 5585081d55dSAndrew Rybchenko fail10: 5595081d55dSAndrew Rybchenko EFSYS_PROBE(fail10); 5605081d55dSAndrew Rybchenko fail9: 5615081d55dSAndrew Rybchenko EFSYS_PROBE(fail9); 562e948693eSPhilip Paeps 563d5106d05SAndrew Rybchenko (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 5645081d55dSAndrew Rybchenko fail8: 5655081d55dSAndrew Rybchenko EFSYS_PROBE(fail8); 5665081d55dSAndrew Rybchenko 5675081d55dSAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 5685081d55dSAndrew Rybchenko fail7: 5695081d55dSAndrew Rybchenko EFSYS_PROBE(fail7); 5705081d55dSAndrew Rybchenko fail6: 5715081d55dSAndrew Rybchenko EFSYS_PROBE(fail6); 572e948693eSPhilip Paeps fail5: 573e948693eSPhilip Paeps EFSYS_PROBE(fail5); 574e948693eSPhilip Paeps fail4: 575e948693eSPhilip Paeps EFSYS_PROBE(fail4); 576e948693eSPhilip Paeps fail3: 577e948693eSPhilip Paeps EFSYS_PROBE(fail3); 578e948693eSPhilip Paeps fail2: 579e948693eSPhilip Paeps EFSYS_PROBE(fail2); 580e948693eSPhilip Paeps fail1: 581460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 582e948693eSPhilip Paeps 583e948693eSPhilip Paeps return (rc); 584e948693eSPhilip Paeps } 585e948693eSPhilip Paeps 586e948693eSPhilip Paeps #endif /* EFSYS_OPT_BOOTCFG */ 587