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 1005081d55dSAndrew Rybchenko default: 1015081d55dSAndrew Rybchenko EFSYS_ASSERT(0); 1025081d55dSAndrew Rybchenko rc = ENOTSUP; 1035081d55dSAndrew Rybchenko goto fail1; 1045081d55dSAndrew Rybchenko } 1055081d55dSAndrew Rybchenko EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE); 1065081d55dSAndrew Rybchenko 107662c835bSAndrew Rybchenko if (sector_countp != NULL) 108662c835bSAndrew Rybchenko *sector_countp = count; 1095081d55dSAndrew Rybchenko *offsetp = offset; 1105081d55dSAndrew Rybchenko *max_sizep = max_size; 1115081d55dSAndrew Rybchenko 1125081d55dSAndrew Rybchenko return (0); 1135081d55dSAndrew Rybchenko 114662c835bSAndrew Rybchenko #if EFSYS_OPT_MEDFORD 115662c835bSAndrew Rybchenko fail2: 116662c835bSAndrew Rybchenko EFSYS_PROBE(fail2); 117662c835bSAndrew Rybchenko #endif 1185081d55dSAndrew Rybchenko fail1: 1195081d55dSAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 1205081d55dSAndrew Rybchenko return (rc); 1215081d55dSAndrew Rybchenko } 1225081d55dSAndrew Rybchenko 1235081d55dSAndrew Rybchenko 124e948693eSPhilip Paeps static __checkReturn uint8_t 125e948693eSPhilip Paeps efx_bootcfg_csum( 126e948693eSPhilip Paeps __in efx_nic_t *enp, 127662c835bSAndrew Rybchenko __in_bcount(size) uint8_t const *data, 128e948693eSPhilip Paeps __in size_t size) 129e948693eSPhilip Paeps { 130e948693eSPhilip Paeps _NOTE(ARGUNUSED(enp)) 131e948693eSPhilip Paeps 132e948693eSPhilip Paeps unsigned int pos; 133e948693eSPhilip Paeps uint8_t checksum = 0; 134e948693eSPhilip Paeps 135e948693eSPhilip Paeps for (pos = 0; pos < size; pos++) 136e948693eSPhilip Paeps checksum += data[pos]; 137e948693eSPhilip Paeps return (checksum); 138e948693eSPhilip Paeps } 139e948693eSPhilip Paeps 140460cb568SAndrew Rybchenko static __checkReturn efx_rc_t 141e948693eSPhilip Paeps efx_bootcfg_verify( 142e948693eSPhilip Paeps __in efx_nic_t *enp, 143662c835bSAndrew Rybchenko __in_bcount(size) uint8_t const *data, 144e948693eSPhilip Paeps __in size_t size, 1453c838a9fSAndrew Rybchenko __out_opt size_t *usedp) 146e948693eSPhilip Paeps { 147e948693eSPhilip Paeps size_t offset = 0; 148e948693eSPhilip Paeps size_t used = 0; 149460cb568SAndrew Rybchenko efx_rc_t rc; 150e948693eSPhilip Paeps 151453130d9SPedro F. Giffuni /* Start parsing tags immediately after the checksum */ 152e948693eSPhilip Paeps for (offset = 1; offset < size; ) { 153e948693eSPhilip Paeps uint8_t tag; 154e948693eSPhilip Paeps uint8_t length; 155e948693eSPhilip Paeps 156e948693eSPhilip Paeps /* Consume tag */ 157e948693eSPhilip Paeps tag = data[offset]; 158e948693eSPhilip Paeps if (tag == DHCP_END) { 159e948693eSPhilip Paeps offset++; 160e948693eSPhilip Paeps used = offset; 161e948693eSPhilip Paeps break; 162e948693eSPhilip Paeps } 163e948693eSPhilip Paeps if (tag == DHCP_PAD) { 164e948693eSPhilip Paeps offset++; 165e948693eSPhilip Paeps continue; 166e948693eSPhilip Paeps } 167e948693eSPhilip Paeps 168e948693eSPhilip Paeps /* Consume length */ 169e948693eSPhilip Paeps if (offset + 1 >= size) { 170e948693eSPhilip Paeps rc = ENOSPC; 171e948693eSPhilip Paeps goto fail1; 172e948693eSPhilip Paeps } 173e948693eSPhilip Paeps length = data[offset + 1]; 174e948693eSPhilip Paeps 175e948693eSPhilip Paeps /* Consume *length */ 176e948693eSPhilip Paeps if (offset + 1 + length >= size) { 177e948693eSPhilip Paeps rc = ENOSPC; 178e948693eSPhilip Paeps goto fail2; 179e948693eSPhilip Paeps } 180e948693eSPhilip Paeps 181e948693eSPhilip Paeps offset += 2 + length; 182e948693eSPhilip Paeps used = offset; 183e948693eSPhilip Paeps } 184e948693eSPhilip Paeps 185e948693eSPhilip Paeps /* Checksum the entire sector, including bytes after any DHCP_END */ 186e948693eSPhilip Paeps if (efx_bootcfg_csum(enp, data, size) != 0) { 187e948693eSPhilip Paeps rc = EINVAL; 188e948693eSPhilip Paeps goto fail3; 189e948693eSPhilip Paeps } 190e948693eSPhilip Paeps 191e948693eSPhilip Paeps if (usedp != NULL) 192e948693eSPhilip Paeps *usedp = used; 193e948693eSPhilip Paeps 194e948693eSPhilip Paeps return (0); 195e948693eSPhilip Paeps 196e948693eSPhilip Paeps fail3: 197e948693eSPhilip Paeps EFSYS_PROBE(fail3); 198e948693eSPhilip Paeps fail2: 199e948693eSPhilip Paeps EFSYS_PROBE(fail2); 200e948693eSPhilip Paeps fail1: 201460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 202e948693eSPhilip Paeps 203e948693eSPhilip Paeps return (rc); 204e948693eSPhilip Paeps } 205e948693eSPhilip Paeps 206662c835bSAndrew Rybchenko /* 207662c835bSAndrew Rybchenko * Copy bootcfg sector data to a target buffer which may differ in size. 208662c835bSAndrew Rybchenko * Optionally corrects format errors in source buffer. 209662c835bSAndrew Rybchenko */ 210662c835bSAndrew Rybchenko efx_rc_t 211662c835bSAndrew Rybchenko efx_bootcfg_copy_sector( 212662c835bSAndrew Rybchenko __in efx_nic_t *enp, 213662c835bSAndrew Rybchenko __inout_bcount(sector_length) 214662c835bSAndrew Rybchenko uint8_t *sector, 215662c835bSAndrew Rybchenko __in size_t sector_length, 216662c835bSAndrew Rybchenko __out_bcount(data_size) uint8_t *data, 217662c835bSAndrew Rybchenko __in size_t data_size, 218662c835bSAndrew Rybchenko __in boolean_t handle_format_errors) 219662c835bSAndrew Rybchenko { 220662c835bSAndrew Rybchenko size_t used_bytes; 221662c835bSAndrew Rybchenko efx_rc_t rc; 222662c835bSAndrew Rybchenko 223662c835bSAndrew Rybchenko /* Verify that the area is correctly formatted and checksummed */ 224662c835bSAndrew Rybchenko rc = efx_bootcfg_verify(enp, sector, sector_length, 225662c835bSAndrew Rybchenko &used_bytes); 226662c835bSAndrew Rybchenko 227662c835bSAndrew Rybchenko if (!handle_format_errors) { 228662c835bSAndrew Rybchenko if (rc != 0) 229662c835bSAndrew Rybchenko goto fail1; 230662c835bSAndrew Rybchenko 231662c835bSAndrew Rybchenko if ((used_bytes < 2) || 232662c835bSAndrew Rybchenko (sector[used_bytes - 1] != DHCP_END)) { 233662c835bSAndrew Rybchenko /* Block too short, or DHCP_END missing */ 234662c835bSAndrew Rybchenko rc = ENOENT; 235662c835bSAndrew Rybchenko goto fail2; 236662c835bSAndrew Rybchenko } 237662c835bSAndrew Rybchenko } 238662c835bSAndrew Rybchenko 239662c835bSAndrew Rybchenko /* Synthesize empty format on verification failure */ 240662c835bSAndrew Rybchenko if (rc != 0 || used_bytes == 0) { 241662c835bSAndrew Rybchenko sector[0] = 0; 242662c835bSAndrew Rybchenko sector[1] = DHCP_END; 243662c835bSAndrew Rybchenko used_bytes = 2; 244662c835bSAndrew Rybchenko } 245662c835bSAndrew Rybchenko EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 246662c835bSAndrew Rybchenko EFSYS_ASSERT(used_bytes <= sector_length); 247662c835bSAndrew Rybchenko EFSYS_ASSERT(sector_length >= 2); 248662c835bSAndrew Rybchenko 249662c835bSAndrew Rybchenko /* 250662c835bSAndrew Rybchenko * Legacy bootcfg sectors don't terminate with a DHCP_END character. 251662c835bSAndrew Rybchenko * Modify the returned payload so it does. 252662c835bSAndrew Rybchenko * Reinitialise the sector if there isn't room for the character. 253662c835bSAndrew Rybchenko */ 254662c835bSAndrew Rybchenko if (sector[used_bytes - 1] != DHCP_END) { 255662c835bSAndrew Rybchenko if (used_bytes >= sector_length) { 256662c835bSAndrew Rybchenko sector[0] = 0; 257662c835bSAndrew Rybchenko used_bytes = 1; 258662c835bSAndrew Rybchenko } 259662c835bSAndrew Rybchenko sector[used_bytes] = DHCP_END; 260662c835bSAndrew Rybchenko ++used_bytes; 261662c835bSAndrew Rybchenko } 262662c835bSAndrew Rybchenko 263662c835bSAndrew Rybchenko /* 264662c835bSAndrew Rybchenko * Verify that the target buffer is large enough for the 265662c835bSAndrew Rybchenko * entire used bootcfg area, then copy into the target buffer. 266662c835bSAndrew Rybchenko */ 267662c835bSAndrew Rybchenko if (used_bytes > data_size) { 268662c835bSAndrew Rybchenko rc = ENOSPC; 269662c835bSAndrew Rybchenko goto fail3; 270662c835bSAndrew Rybchenko } 271662c835bSAndrew Rybchenko memcpy(data, sector, used_bytes); 272662c835bSAndrew Rybchenko 273662c835bSAndrew Rybchenko /* Zero out the unused portion of the target buffer */ 274662c835bSAndrew Rybchenko if (used_bytes < data_size) 275662c835bSAndrew Rybchenko (void) memset(data + used_bytes, 0, data_size - used_bytes); 276662c835bSAndrew Rybchenko 277662c835bSAndrew Rybchenko /* 278662c835bSAndrew Rybchenko * The checksum includes trailing data after any DHCP_END character, 279662c835bSAndrew Rybchenko * which we've just modified (by truncation or appending DHCP_END). 280662c835bSAndrew Rybchenko */ 281662c835bSAndrew Rybchenko data[0] -= efx_bootcfg_csum(enp, data, data_size); 282662c835bSAndrew Rybchenko 283662c835bSAndrew Rybchenko return (0); 284662c835bSAndrew Rybchenko 285662c835bSAndrew Rybchenko fail3: 286662c835bSAndrew Rybchenko EFSYS_PROBE(fail3); 287662c835bSAndrew Rybchenko fail2: 288662c835bSAndrew Rybchenko EFSYS_PROBE(fail2); 289662c835bSAndrew Rybchenko fail1: 290662c835bSAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 291662c835bSAndrew Rybchenko 292662c835bSAndrew Rybchenko return (rc); 293662c835bSAndrew Rybchenko } 294662c835bSAndrew Rybchenko 295460cb568SAndrew Rybchenko efx_rc_t 296e948693eSPhilip Paeps efx_bootcfg_read( 297e948693eSPhilip Paeps __in efx_nic_t *enp, 298*7e17c17dSAndrew Rybchenko __out_bcount(size) uint8_t *data, 299e948693eSPhilip Paeps __in size_t size) 300e948693eSPhilip Paeps { 301e948693eSPhilip Paeps uint8_t *payload = NULL; 302e948693eSPhilip Paeps size_t used_bytes; 3035081d55dSAndrew Rybchenko size_t partn_length; 304e948693eSPhilip Paeps size_t sector_length; 3055081d55dSAndrew Rybchenko size_t sector_offset; 306460cb568SAndrew Rybchenko efx_rc_t rc; 307662c835bSAndrew Rybchenko uint32_t sector_number; 308e948693eSPhilip Paeps 309662c835bSAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD 310662c835bSAndrew Rybchenko sector_number = enp->en_nic_cfg.enc_pf; 311662c835bSAndrew Rybchenko #else 312662c835bSAndrew Rybchenko sector_number = 0; 313662c835bSAndrew Rybchenko #endif 3145081d55dSAndrew Rybchenko rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 315e948693eSPhilip Paeps if (rc != 0) 316e948693eSPhilip Paeps goto fail1; 317e948693eSPhilip Paeps 3185081d55dSAndrew Rybchenko /* The bootcfg sector may be stored in a (larger) shared partition */ 319662c835bSAndrew Rybchenko rc = efx_bootcfg_sector_info(enp, sector_number, 320662c835bSAndrew Rybchenko NULL, §or_offset, §or_length); 3215081d55dSAndrew Rybchenko if (rc != 0) 3225081d55dSAndrew Rybchenko goto fail2; 3235081d55dSAndrew Rybchenko 3245081d55dSAndrew Rybchenko if (sector_length > BOOTCFG_MAX_SIZE) 3255081d55dSAndrew Rybchenko sector_length = BOOTCFG_MAX_SIZE; 3265081d55dSAndrew Rybchenko 3275081d55dSAndrew Rybchenko if (sector_offset + sector_length > partn_length) { 3285081d55dSAndrew Rybchenko /* Partition is too small */ 3295081d55dSAndrew Rybchenko rc = EFBIG; 3305081d55dSAndrew Rybchenko goto fail3; 3315081d55dSAndrew Rybchenko } 3325081d55dSAndrew Rybchenko 333e948693eSPhilip Paeps /* 3345081d55dSAndrew Rybchenko * We need to read the entire BOOTCFG sector to ensure we read all the 335e948693eSPhilip Paeps * tags, because legacy bootcfg sectors are not guaranteed to end with 336e948693eSPhilip Paeps * a DHCP_END character. If the user hasn't supplied a sufficiently 337e948693eSPhilip Paeps * large buffer then use our own buffer. 338e948693eSPhilip Paeps */ 339e948693eSPhilip Paeps if (sector_length > size) { 340e948693eSPhilip Paeps EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload); 341e948693eSPhilip Paeps if (payload == NULL) { 342e948693eSPhilip Paeps rc = ENOMEM; 3435081d55dSAndrew Rybchenko goto fail4; 344e948693eSPhilip Paeps } 345e948693eSPhilip Paeps } else 346e948693eSPhilip Paeps payload = (uint8_t *)data; 347e948693eSPhilip Paeps 348e948693eSPhilip Paeps if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 3495081d55dSAndrew Rybchenko goto fail5; 350e948693eSPhilip Paeps 351662c835bSAndrew Rybchenko if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 352662c835bSAndrew Rybchenko sector_offset, (caddr_t)payload, sector_length)) != 0) { 353d5106d05SAndrew Rybchenko (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 3545081d55dSAndrew Rybchenko goto fail6; 355662c835bSAndrew Rybchenko } 356662c835bSAndrew Rybchenko 357d5106d05SAndrew Rybchenko if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 358662c835bSAndrew Rybchenko goto fail7; 359e948693eSPhilip Paeps 360e948693eSPhilip Paeps /* Verify that the area is correctly formatted and checksummed */ 361*7e17c17dSAndrew Rybchenko rc = efx_bootcfg_verify(enp, payload, sector_length, 362e948693eSPhilip Paeps &used_bytes); 363e948693eSPhilip Paeps if (rc != 0 || used_bytes == 0) { 3645e7bb158SAndrew Rybchenko payload[0] = (uint8_t)(~DHCP_END & 0xff); 365e948693eSPhilip Paeps payload[1] = DHCP_END; 366e948693eSPhilip Paeps used_bytes = 2; 367e948693eSPhilip Paeps } 368e948693eSPhilip Paeps 369e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 370e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes <= sector_length); 371e948693eSPhilip Paeps 372e948693eSPhilip Paeps /* 373e948693eSPhilip Paeps * Legacy bootcfg sectors don't terminate with a DHCP_END character. 374e948693eSPhilip Paeps * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by 375e948693eSPhilip Paeps * definition large enough for any valid (per-port) bootcfg sector, 376e948693eSPhilip Paeps * so reinitialise the sector if there isn't room for the character. 377e948693eSPhilip Paeps */ 378e948693eSPhilip Paeps if (payload[used_bytes - 1] != DHCP_END) { 379e948693eSPhilip Paeps if (used_bytes + 1 > sector_length) { 380e948693eSPhilip Paeps payload[0] = 0; 381e948693eSPhilip Paeps used_bytes = 1; 382e948693eSPhilip Paeps } 383e948693eSPhilip Paeps 384e948693eSPhilip Paeps payload[used_bytes] = DHCP_END; 385e948693eSPhilip Paeps ++used_bytes; 386e948693eSPhilip Paeps } 387e948693eSPhilip Paeps 388e948693eSPhilip Paeps /* 389e948693eSPhilip Paeps * Verify that the user supplied buffer is large enough for the 390e948693eSPhilip Paeps * entire used bootcfg area, then copy into the user supplied buffer. 391e948693eSPhilip Paeps */ 392e948693eSPhilip Paeps if (used_bytes > size) { 393e948693eSPhilip Paeps rc = ENOSPC; 394662c835bSAndrew Rybchenko goto fail8; 395e948693eSPhilip Paeps } 396e948693eSPhilip Paeps if (sector_length > size) { 397e948693eSPhilip Paeps memcpy(data, payload, used_bytes); 398e948693eSPhilip Paeps EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 399e948693eSPhilip Paeps } 400e948693eSPhilip Paeps 401e948693eSPhilip Paeps /* Zero out the unused portion of the user buffer */ 402e948693eSPhilip Paeps if (used_bytes < size) 403e948693eSPhilip Paeps (void) memset(data + used_bytes, 0, size - used_bytes); 404e948693eSPhilip Paeps 405e948693eSPhilip Paeps /* 406e948693eSPhilip Paeps * The checksum includes trailing data after any DHCP_END character, 407e948693eSPhilip Paeps * which we've just modified (by truncation or appending DHCP_END). 408e948693eSPhilip Paeps */ 409e948693eSPhilip Paeps data[0] -= efx_bootcfg_csum(enp, data, size); 410e948693eSPhilip Paeps 411e948693eSPhilip Paeps return (0); 412e948693eSPhilip Paeps 413662c835bSAndrew Rybchenko fail8: 414662c835bSAndrew Rybchenko EFSYS_PROBE(fail8); 4155081d55dSAndrew Rybchenko fail7: 4165081d55dSAndrew Rybchenko EFSYS_PROBE(fail7); 4175081d55dSAndrew Rybchenko fail6: 4185081d55dSAndrew Rybchenko EFSYS_PROBE(fail6); 419e948693eSPhilip Paeps fail5: 420e948693eSPhilip Paeps EFSYS_PROBE(fail5); 4215081d55dSAndrew Rybchenko if (sector_length > size) 4225081d55dSAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 423e948693eSPhilip Paeps fail4: 424e948693eSPhilip Paeps EFSYS_PROBE(fail4); 425e948693eSPhilip Paeps fail3: 426e948693eSPhilip Paeps EFSYS_PROBE(fail3); 427e948693eSPhilip Paeps fail2: 428e948693eSPhilip Paeps EFSYS_PROBE(fail2); 429e948693eSPhilip Paeps fail1: 430460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 431e948693eSPhilip Paeps 432e948693eSPhilip Paeps return (rc); 433e948693eSPhilip Paeps } 434e948693eSPhilip Paeps 435460cb568SAndrew Rybchenko efx_rc_t 436e948693eSPhilip Paeps efx_bootcfg_write( 437e948693eSPhilip Paeps __in efx_nic_t *enp, 438*7e17c17dSAndrew Rybchenko __in_bcount(size) uint8_t *data, 439e948693eSPhilip Paeps __in size_t size) 440e948693eSPhilip Paeps { 4415081d55dSAndrew Rybchenko uint8_t *partn_data; 442e948693eSPhilip Paeps uint8_t checksum; 4435081d55dSAndrew Rybchenko size_t partn_length; 444e948693eSPhilip Paeps size_t sector_length; 4455081d55dSAndrew Rybchenko size_t sector_offset; 446e948693eSPhilip Paeps size_t used_bytes; 447460cb568SAndrew Rybchenko efx_rc_t rc; 448662c835bSAndrew Rybchenko uint32_t sector_number; 449662c835bSAndrew Rybchenko 450662c835bSAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD 451662c835bSAndrew Rybchenko sector_number = enp->en_nic_cfg.enc_pf; 452662c835bSAndrew Rybchenko #else 453662c835bSAndrew Rybchenko sector_number = 0; 454662c835bSAndrew Rybchenko #endif 455e948693eSPhilip Paeps 4565081d55dSAndrew Rybchenko rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 457e948693eSPhilip Paeps if (rc != 0) 458e948693eSPhilip Paeps goto fail1; 459e948693eSPhilip Paeps 4605081d55dSAndrew Rybchenko /* The bootcfg sector may be stored in a (larger) shared partition */ 461662c835bSAndrew Rybchenko rc = efx_bootcfg_sector_info(enp, sector_number, 462662c835bSAndrew Rybchenko NULL, §or_offset, §or_length); 4635081d55dSAndrew Rybchenko if (rc != 0) 4645081d55dSAndrew Rybchenko goto fail2; 4655081d55dSAndrew Rybchenko 466e948693eSPhilip Paeps if (sector_length > BOOTCFG_MAX_SIZE) 467e948693eSPhilip Paeps sector_length = BOOTCFG_MAX_SIZE; 468e948693eSPhilip Paeps 4695081d55dSAndrew Rybchenko if (sector_offset + sector_length > partn_length) { 4705081d55dSAndrew Rybchenko /* Partition is too small */ 4715081d55dSAndrew Rybchenko rc = EFBIG; 4725081d55dSAndrew Rybchenko goto fail3; 4735081d55dSAndrew Rybchenko } 4745081d55dSAndrew Rybchenko 475e948693eSPhilip Paeps if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0) 4765081d55dSAndrew Rybchenko goto fail4; 477e948693eSPhilip Paeps 478e948693eSPhilip Paeps /* The caller *must* terminate their block with a DHCP_END character */ 4795081d55dSAndrew Rybchenko if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) { 4805081d55dSAndrew Rybchenko /* Block too short or DHCP_END missing */ 481e948693eSPhilip Paeps rc = ENOENT; 4825081d55dSAndrew Rybchenko goto fail5; 483e948693eSPhilip Paeps } 484e948693eSPhilip Paeps 485e948693eSPhilip Paeps /* Check that the hardware has support for this much data */ 486e948693eSPhilip Paeps if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) { 487e948693eSPhilip Paeps rc = ENOSPC; 488e948693eSPhilip Paeps goto fail6; 489e948693eSPhilip Paeps } 490e948693eSPhilip Paeps 4915081d55dSAndrew Rybchenko /* 4925081d55dSAndrew Rybchenko * If the BOOTCFG sector is stored in a shared partition, then we must 4935081d55dSAndrew Rybchenko * read the whole partition and insert the updated bootcfg sector at the 4945081d55dSAndrew Rybchenko * correct offset. 4955081d55dSAndrew Rybchenko */ 4965081d55dSAndrew Rybchenko EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data); 4975081d55dSAndrew Rybchenko if (partn_data == NULL) { 4985081d55dSAndrew Rybchenko rc = ENOMEM; 499e948693eSPhilip Paeps goto fail7; 5005081d55dSAndrew Rybchenko } 5015081d55dSAndrew Rybchenko 5025081d55dSAndrew Rybchenko rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 5035081d55dSAndrew Rybchenko if (rc != 0) 5045081d55dSAndrew Rybchenko goto fail8; 5055081d55dSAndrew Rybchenko 5065081d55dSAndrew Rybchenko /* Read the entire partition */ 5075081d55dSAndrew Rybchenko rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0, 5085081d55dSAndrew Rybchenko (caddr_t)partn_data, partn_length); 5095081d55dSAndrew Rybchenko if (rc != 0) 5105081d55dSAndrew Rybchenko goto fail9; 511e948693eSPhilip Paeps 512e948693eSPhilip Paeps /* 5135081d55dSAndrew Rybchenko * Insert the BOOTCFG sector into the partition, Zero out all data after 5145081d55dSAndrew Rybchenko * the DHCP_END tag, and adjust the checksum. 515e948693eSPhilip Paeps */ 5165081d55dSAndrew Rybchenko (void) memset(partn_data + sector_offset, 0x0, sector_length); 5175081d55dSAndrew Rybchenko (void) memcpy(partn_data + sector_offset, data, used_bytes); 5185081d55dSAndrew Rybchenko 519e948693eSPhilip Paeps checksum = efx_bootcfg_csum(enp, data, used_bytes); 5205081d55dSAndrew Rybchenko partn_data[sector_offset] -= checksum; 521e948693eSPhilip Paeps 5225081d55dSAndrew Rybchenko if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 5235081d55dSAndrew Rybchenko goto fail10; 524e948693eSPhilip Paeps 525e948693eSPhilip Paeps if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 526662c835bSAndrew Rybchenko 0, (caddr_t)partn_data, partn_length)) != 0) 5275081d55dSAndrew Rybchenko goto fail11; 528e948693eSPhilip Paeps 529d5106d05SAndrew Rybchenko if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 530662c835bSAndrew Rybchenko goto fail12; 531e948693eSPhilip Paeps 5325081d55dSAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 533e948693eSPhilip Paeps 534e948693eSPhilip Paeps return (0); 535e948693eSPhilip Paeps 536662c835bSAndrew Rybchenko fail12: 537662c835bSAndrew Rybchenko EFSYS_PROBE(fail12); 5385081d55dSAndrew Rybchenko fail11: 5395081d55dSAndrew Rybchenko EFSYS_PROBE(fail11); 5405081d55dSAndrew Rybchenko fail10: 5415081d55dSAndrew Rybchenko EFSYS_PROBE(fail10); 5425081d55dSAndrew Rybchenko fail9: 5435081d55dSAndrew Rybchenko EFSYS_PROBE(fail9); 544e948693eSPhilip Paeps 545d5106d05SAndrew Rybchenko (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 5465081d55dSAndrew Rybchenko fail8: 5475081d55dSAndrew Rybchenko EFSYS_PROBE(fail8); 5485081d55dSAndrew Rybchenko 5495081d55dSAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 5505081d55dSAndrew Rybchenko fail7: 5515081d55dSAndrew Rybchenko EFSYS_PROBE(fail7); 5525081d55dSAndrew Rybchenko fail6: 5535081d55dSAndrew Rybchenko EFSYS_PROBE(fail6); 554e948693eSPhilip Paeps fail5: 555e948693eSPhilip Paeps EFSYS_PROBE(fail5); 556e948693eSPhilip Paeps fail4: 557e948693eSPhilip Paeps EFSYS_PROBE(fail4); 558e948693eSPhilip Paeps fail3: 559e948693eSPhilip Paeps EFSYS_PROBE(fail3); 560e948693eSPhilip Paeps fail2: 561e948693eSPhilip Paeps EFSYS_PROBE(fail2); 562e948693eSPhilip Paeps fail1: 563460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 564e948693eSPhilip Paeps 565e948693eSPhilip Paeps return (rc); 566e948693eSPhilip Paeps } 567e948693eSPhilip Paeps 568e948693eSPhilip Paeps #endif /* EFSYS_OPT_BOOTCFG */ 569