1e948693eSPhilip Paeps /*- 23c838a9fSAndrew Rybchenko * Copyright (c) 2009-2015 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. 41e948693eSPhilip Paeps * A multiple of 0x100 so trailing 0xff characters don't contrinbute to the 42e948693eSPhilip Paeps * checksum. 43e948693eSPhilip Paeps */ 44e948693eSPhilip Paeps #define BOOTCFG_MAX_SIZE 0x1000 45e948693eSPhilip Paeps 46e948693eSPhilip Paeps #define DHCP_END (uint8_t)0xff 47e948693eSPhilip Paeps #define DHCP_PAD (uint8_t)0 48e948693eSPhilip Paeps 49e948693eSPhilip Paeps static __checkReturn uint8_t 50e948693eSPhilip Paeps efx_bootcfg_csum( 51e948693eSPhilip Paeps __in efx_nic_t *enp, 52e948693eSPhilip Paeps __in_bcount(size) caddr_t data, 53e948693eSPhilip Paeps __in size_t size) 54e948693eSPhilip Paeps { 55e948693eSPhilip Paeps _NOTE(ARGUNUSED(enp)) 56e948693eSPhilip Paeps 57e948693eSPhilip Paeps unsigned int pos; 58e948693eSPhilip Paeps uint8_t checksum = 0; 59e948693eSPhilip Paeps 60e948693eSPhilip Paeps for (pos = 0; pos < size; pos++) 61e948693eSPhilip Paeps checksum += data[pos]; 62e948693eSPhilip Paeps return (checksum); 63e948693eSPhilip Paeps } 64e948693eSPhilip Paeps 65460cb568SAndrew Rybchenko static __checkReturn efx_rc_t 66e948693eSPhilip Paeps efx_bootcfg_verify( 67e948693eSPhilip Paeps __in efx_nic_t *enp, 68e948693eSPhilip Paeps __in_bcount(size) caddr_t data, 69e948693eSPhilip Paeps __in size_t size, 703c838a9fSAndrew Rybchenko __out_opt size_t *usedp) 71e948693eSPhilip Paeps { 72e948693eSPhilip Paeps size_t offset = 0; 73e948693eSPhilip Paeps size_t used = 0; 74460cb568SAndrew Rybchenko efx_rc_t rc; 75e948693eSPhilip Paeps 76*453130d9SPedro F. Giffuni /* Start parsing tags immediately after the checksum */ 77e948693eSPhilip Paeps for (offset = 1; offset < size; ) { 78e948693eSPhilip Paeps uint8_t tag; 79e948693eSPhilip Paeps uint8_t length; 80e948693eSPhilip Paeps 81e948693eSPhilip Paeps /* Consume tag */ 82e948693eSPhilip Paeps tag = data[offset]; 83e948693eSPhilip Paeps if (tag == DHCP_END) { 84e948693eSPhilip Paeps offset++; 85e948693eSPhilip Paeps used = offset; 86e948693eSPhilip Paeps break; 87e948693eSPhilip Paeps } 88e948693eSPhilip Paeps if (tag == DHCP_PAD) { 89e948693eSPhilip Paeps offset++; 90e948693eSPhilip Paeps continue; 91e948693eSPhilip Paeps } 92e948693eSPhilip Paeps 93e948693eSPhilip Paeps /* Consume length */ 94e948693eSPhilip Paeps if (offset + 1 >= size) { 95e948693eSPhilip Paeps rc = ENOSPC; 96e948693eSPhilip Paeps goto fail1; 97e948693eSPhilip Paeps } 98e948693eSPhilip Paeps length = data[offset + 1]; 99e948693eSPhilip Paeps 100e948693eSPhilip Paeps /* Consume *length */ 101e948693eSPhilip Paeps if (offset + 1 + length >= size) { 102e948693eSPhilip Paeps rc = ENOSPC; 103e948693eSPhilip Paeps goto fail2; 104e948693eSPhilip Paeps } 105e948693eSPhilip Paeps 106e948693eSPhilip Paeps offset += 2 + length; 107e948693eSPhilip Paeps used = offset; 108e948693eSPhilip Paeps } 109e948693eSPhilip Paeps 110e948693eSPhilip Paeps /* Checksum the entire sector, including bytes after any DHCP_END */ 111e948693eSPhilip Paeps if (efx_bootcfg_csum(enp, data, size) != 0) { 112e948693eSPhilip Paeps rc = EINVAL; 113e948693eSPhilip Paeps goto fail3; 114e948693eSPhilip Paeps } 115e948693eSPhilip Paeps 116e948693eSPhilip Paeps if (usedp != NULL) 117e948693eSPhilip Paeps *usedp = used; 118e948693eSPhilip Paeps 119e948693eSPhilip Paeps return (0); 120e948693eSPhilip Paeps 121e948693eSPhilip Paeps fail3: 122e948693eSPhilip Paeps EFSYS_PROBE(fail3); 123e948693eSPhilip Paeps fail2: 124e948693eSPhilip Paeps EFSYS_PROBE(fail2); 125e948693eSPhilip Paeps fail1: 126460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 127e948693eSPhilip Paeps 128e948693eSPhilip Paeps return (rc); 129e948693eSPhilip Paeps } 130e948693eSPhilip Paeps 131460cb568SAndrew Rybchenko efx_rc_t 132e948693eSPhilip Paeps efx_bootcfg_read( 133e948693eSPhilip Paeps __in efx_nic_t *enp, 134e948693eSPhilip Paeps __out_bcount(size) caddr_t data, 135e948693eSPhilip Paeps __in size_t size) 136e948693eSPhilip Paeps { 137e948693eSPhilip Paeps uint8_t *payload = NULL; 138e948693eSPhilip Paeps size_t used_bytes; 139e948693eSPhilip Paeps size_t sector_length; 140460cb568SAndrew Rybchenko efx_rc_t rc; 141e948693eSPhilip Paeps 142e948693eSPhilip Paeps rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, §or_length); 143e948693eSPhilip Paeps if (rc != 0) 144e948693eSPhilip Paeps goto fail1; 145e948693eSPhilip Paeps 146e948693eSPhilip Paeps /* 147e948693eSPhilip Paeps * We need to read the entire BOOTCFG area to ensure we read all the 148e948693eSPhilip Paeps * tags, because legacy bootcfg sectors are not guaranteed to end with 149e948693eSPhilip Paeps * a DHCP_END character. If the user hasn't supplied a sufficiently 150e948693eSPhilip Paeps * large buffer then use our own buffer. 151e948693eSPhilip Paeps */ 152e948693eSPhilip Paeps if (sector_length > BOOTCFG_MAX_SIZE) 153e948693eSPhilip Paeps sector_length = BOOTCFG_MAX_SIZE; 154e948693eSPhilip Paeps if (sector_length > size) { 155e948693eSPhilip Paeps EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload); 156e948693eSPhilip Paeps if (payload == NULL) { 157e948693eSPhilip Paeps rc = ENOMEM; 158e948693eSPhilip Paeps goto fail2; 159e948693eSPhilip Paeps } 160e948693eSPhilip Paeps } else 161e948693eSPhilip Paeps payload = (uint8_t *)data; 162e948693eSPhilip Paeps 163e948693eSPhilip Paeps if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 164e948693eSPhilip Paeps goto fail3; 165e948693eSPhilip Paeps 166e948693eSPhilip Paeps rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0, 167e948693eSPhilip Paeps (caddr_t)payload, sector_length); 168e948693eSPhilip Paeps 169e948693eSPhilip Paeps efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); 170e948693eSPhilip Paeps 171e948693eSPhilip Paeps if (rc != 0) 172e948693eSPhilip Paeps goto fail4; 173e948693eSPhilip Paeps 174e948693eSPhilip Paeps /* Verify that the area is correctly formatted and checksummed */ 175e948693eSPhilip Paeps rc = efx_bootcfg_verify(enp, (caddr_t)payload, sector_length, 176e948693eSPhilip Paeps &used_bytes); 177e948693eSPhilip Paeps if (rc != 0 || used_bytes == 0) { 178e948693eSPhilip Paeps payload[0] = (uint8_t)~DHCP_END; 179e948693eSPhilip Paeps payload[1] = DHCP_END; 180e948693eSPhilip Paeps used_bytes = 2; 181e948693eSPhilip Paeps } 182e948693eSPhilip Paeps 183e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 184e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes <= sector_length); 185e948693eSPhilip Paeps 186e948693eSPhilip Paeps /* 187e948693eSPhilip Paeps * Legacy bootcfg sectors don't terminate with a DHCP_END character. 188e948693eSPhilip Paeps * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by 189e948693eSPhilip Paeps * definition large enough for any valid (per-port) bootcfg sector, 190e948693eSPhilip Paeps * so reinitialise the sector if there isn't room for the character. 191e948693eSPhilip Paeps */ 192e948693eSPhilip Paeps if (payload[used_bytes - 1] != DHCP_END) { 193e948693eSPhilip Paeps if (used_bytes + 1 > sector_length) { 194e948693eSPhilip Paeps payload[0] = 0; 195e948693eSPhilip Paeps used_bytes = 1; 196e948693eSPhilip Paeps } 197e948693eSPhilip Paeps 198e948693eSPhilip Paeps payload[used_bytes] = DHCP_END; 199e948693eSPhilip Paeps ++used_bytes; 200e948693eSPhilip Paeps } 201e948693eSPhilip Paeps 202e948693eSPhilip Paeps /* 203e948693eSPhilip Paeps * Verify that the user supplied buffer is large enough for the 204e948693eSPhilip Paeps * entire used bootcfg area, then copy into the user supplied buffer. 205e948693eSPhilip Paeps */ 206e948693eSPhilip Paeps if (used_bytes > size) { 207e948693eSPhilip Paeps rc = ENOSPC; 208e948693eSPhilip Paeps goto fail5; 209e948693eSPhilip Paeps } 210e948693eSPhilip Paeps if (sector_length > size) { 211e948693eSPhilip Paeps memcpy(data, payload, used_bytes); 212e948693eSPhilip Paeps EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 213e948693eSPhilip Paeps } 214e948693eSPhilip Paeps 215e948693eSPhilip Paeps /* Zero out the unused portion of the user buffer */ 216e948693eSPhilip Paeps if (used_bytes < size) 217e948693eSPhilip Paeps (void) memset(data + used_bytes, 0, size - used_bytes); 218e948693eSPhilip Paeps 219e948693eSPhilip Paeps /* 220e948693eSPhilip Paeps * The checksum includes trailing data after any DHCP_END character, 221e948693eSPhilip Paeps * which we've just modified (by truncation or appending DHCP_END). 222e948693eSPhilip Paeps */ 223e948693eSPhilip Paeps data[0] -= efx_bootcfg_csum(enp, data, size); 224e948693eSPhilip Paeps 225e948693eSPhilip Paeps return (0); 226e948693eSPhilip Paeps 227e948693eSPhilip Paeps fail5: 228e948693eSPhilip Paeps EFSYS_PROBE(fail5); 229e948693eSPhilip Paeps fail4: 230e948693eSPhilip Paeps EFSYS_PROBE(fail4); 231e948693eSPhilip Paeps fail3: 232e948693eSPhilip Paeps EFSYS_PROBE(fail3); 233e948693eSPhilip Paeps 234e948693eSPhilip Paeps if (sector_length > size) 235e948693eSPhilip Paeps EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 236e948693eSPhilip Paeps fail2: 237e948693eSPhilip Paeps EFSYS_PROBE(fail2); 238e948693eSPhilip Paeps fail1: 239460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 240e948693eSPhilip Paeps 241e948693eSPhilip Paeps return (rc); 242e948693eSPhilip Paeps } 243e948693eSPhilip Paeps 244460cb568SAndrew Rybchenko efx_rc_t 245e948693eSPhilip Paeps efx_bootcfg_write( 246e948693eSPhilip Paeps __in efx_nic_t *enp, 247e948693eSPhilip Paeps __in_bcount(size) caddr_t data, 248e948693eSPhilip Paeps __in size_t size) 249e948693eSPhilip Paeps { 250e948693eSPhilip Paeps uint8_t *chunk; 251e948693eSPhilip Paeps uint8_t checksum; 252e948693eSPhilip Paeps size_t sector_length; 253e948693eSPhilip Paeps size_t chunk_length; 254e948693eSPhilip Paeps size_t used_bytes; 255e948693eSPhilip Paeps size_t offset; 256e948693eSPhilip Paeps size_t remaining; 257460cb568SAndrew Rybchenko efx_rc_t rc; 258e948693eSPhilip Paeps 259e948693eSPhilip Paeps rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, §or_length); 260e948693eSPhilip Paeps if (rc != 0) 261e948693eSPhilip Paeps goto fail1; 262e948693eSPhilip Paeps 263e948693eSPhilip Paeps if (sector_length > BOOTCFG_MAX_SIZE) 264e948693eSPhilip Paeps sector_length = BOOTCFG_MAX_SIZE; 265e948693eSPhilip Paeps 266e948693eSPhilip Paeps if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0) 267e948693eSPhilip Paeps goto fail2; 268e948693eSPhilip Paeps 269e948693eSPhilip Paeps /* The caller *must* terminate their block with a DHCP_END character */ 270e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 271e948693eSPhilip Paeps if ((uint8_t)data[used_bytes - 1] != DHCP_END) { 272e948693eSPhilip Paeps rc = ENOENT; 273e948693eSPhilip Paeps goto fail3; 274e948693eSPhilip Paeps } 275e948693eSPhilip Paeps 276e948693eSPhilip Paeps /* Check that the hardware has support for this much data */ 277e948693eSPhilip Paeps if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) { 278e948693eSPhilip Paeps rc = ENOSPC; 279e948693eSPhilip Paeps goto fail4; 280e948693eSPhilip Paeps } 281e948693eSPhilip Paeps 282e948693eSPhilip Paeps rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, &chunk_length); 283e948693eSPhilip Paeps if (rc != 0) 284e948693eSPhilip Paeps goto fail5; 285e948693eSPhilip Paeps 286e948693eSPhilip Paeps EFSYS_KMEM_ALLOC(enp->en_esip, chunk_length, chunk); 287e948693eSPhilip Paeps if (chunk == NULL) { 288e948693eSPhilip Paeps rc = ENOMEM; 289e948693eSPhilip Paeps goto fail6; 290e948693eSPhilip Paeps } 291e948693eSPhilip Paeps 292e948693eSPhilip Paeps if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 293e948693eSPhilip Paeps goto fail7; 294e948693eSPhilip Paeps 295e948693eSPhilip Paeps /* 296e948693eSPhilip Paeps * Write the entire sector_length bytes of data in chunks. Zero out 297e948693eSPhilip Paeps * all data following the DHCP_END, and adjust the checksum 298e948693eSPhilip Paeps */ 299e948693eSPhilip Paeps checksum = efx_bootcfg_csum(enp, data, used_bytes); 300e948693eSPhilip Paeps for (offset = 0; offset < sector_length; offset += remaining) { 301e948693eSPhilip Paeps remaining = MIN(chunk_length, sector_length - offset); 302e948693eSPhilip Paeps 303e948693eSPhilip Paeps /* Fill chunk */ 304e948693eSPhilip Paeps (void) memset(chunk, 0x0, chunk_length); 305e948693eSPhilip Paeps if (offset < used_bytes) 306e948693eSPhilip Paeps memcpy(chunk, data + offset, 307e948693eSPhilip Paeps MIN(remaining, used_bytes - offset)); 308e948693eSPhilip Paeps 309e948693eSPhilip Paeps /* Adjust checksum */ 310e948693eSPhilip Paeps if (offset == 0) 311e948693eSPhilip Paeps chunk[0] -= checksum; 312e948693eSPhilip Paeps 313e948693eSPhilip Paeps if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 314e948693eSPhilip Paeps offset, (caddr_t)chunk, remaining)) != 0) 315e948693eSPhilip Paeps goto fail8; 316e948693eSPhilip Paeps } 317e948693eSPhilip Paeps 318e948693eSPhilip Paeps efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); 319e948693eSPhilip Paeps 320e948693eSPhilip Paeps EFSYS_KMEM_FREE(enp->en_esip, chunk_length, chunk); 321e948693eSPhilip Paeps 322e948693eSPhilip Paeps return (0); 323e948693eSPhilip Paeps 324e948693eSPhilip Paeps fail8: 325e948693eSPhilip Paeps EFSYS_PROBE(fail8); 326e948693eSPhilip Paeps fail7: 327e948693eSPhilip Paeps EFSYS_PROBE(fail7); 328e948693eSPhilip Paeps 329e948693eSPhilip Paeps EFSYS_KMEM_FREE(enp->en_esip, chunk_length, chunk); 330e948693eSPhilip Paeps fail6: 331e948693eSPhilip Paeps EFSYS_PROBE(fail6); 332e948693eSPhilip Paeps 333e948693eSPhilip Paeps efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); 334e948693eSPhilip Paeps fail5: 335e948693eSPhilip Paeps EFSYS_PROBE(fail5); 336e948693eSPhilip Paeps fail4: 337e948693eSPhilip Paeps EFSYS_PROBE(fail4); 338e948693eSPhilip Paeps fail3: 339e948693eSPhilip Paeps EFSYS_PROBE(fail3); 340e948693eSPhilip Paeps fail2: 341e948693eSPhilip Paeps EFSYS_PROBE(fail2); 342e948693eSPhilip Paeps fail1: 343460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 344e948693eSPhilip Paeps 345e948693eSPhilip Paeps return (rc); 346e948693eSPhilip Paeps } 347e948693eSPhilip Paeps 348e948693eSPhilip Paeps #endif /* EFSYS_OPT_BOOTCFG */ 349