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