1e948693eSPhilip Paeps /*- 2e948693eSPhilip Paeps * Copyright 2009 Solarflare Communications Inc. All rights reserved. 3e948693eSPhilip Paeps * 4e948693eSPhilip Paeps * Redistribution and use in source and binary forms, with or without 5e948693eSPhilip Paeps * modification, are permitted provided that the following conditions 6e948693eSPhilip Paeps * are met: 7e948693eSPhilip Paeps * 1. Redistributions of source code must retain the above copyright 8e948693eSPhilip Paeps * notice, this list of conditions and the following disclaimer. 9e948693eSPhilip Paeps * 2. Redistributions in binary form must reproduce the above copyright 10e948693eSPhilip Paeps * notice, this list of conditions and the following disclaimer in the 11e948693eSPhilip Paeps * documentation and/or other materials provided with the distribution. 12e948693eSPhilip Paeps * 13e948693eSPhilip Paeps * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND 14e948693eSPhilip Paeps * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15e948693eSPhilip Paeps * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16e948693eSPhilip Paeps * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17e948693eSPhilip Paeps * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18e948693eSPhilip Paeps * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19e948693eSPhilip Paeps * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20e948693eSPhilip Paeps * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21e948693eSPhilip Paeps * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22e948693eSPhilip Paeps * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23e948693eSPhilip Paeps * SUCH DAMAGE. 24e948693eSPhilip Paeps */ 25e948693eSPhilip Paeps 26*5dee87d7SPhilip Paeps #include <sys/cdefs.h> 27*5dee87d7SPhilip Paeps __FBSDID("$FreeBSD$"); 28*5dee87d7SPhilip Paeps 29e948693eSPhilip Paeps #include "efsys.h" 30e948693eSPhilip Paeps #include "efx.h" 31e948693eSPhilip Paeps #include "efx_types.h" 32e948693eSPhilip Paeps #include "efx_impl.h" 33e948693eSPhilip Paeps 34e948693eSPhilip Paeps #if EFSYS_OPT_BOOTCFG 35e948693eSPhilip Paeps 36e948693eSPhilip Paeps /* 37e948693eSPhilip Paeps * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE. 38e948693eSPhilip Paeps * A multiple of 0x100 so trailing 0xff characters don't contrinbute to the 39e948693eSPhilip Paeps * checksum. 40e948693eSPhilip Paeps */ 41e948693eSPhilip Paeps #define BOOTCFG_MAX_SIZE 0x1000 42e948693eSPhilip Paeps 43e948693eSPhilip Paeps #define DHCP_END (uint8_t)0xff 44e948693eSPhilip Paeps #define DHCP_PAD (uint8_t)0 45e948693eSPhilip Paeps 46e948693eSPhilip Paeps static __checkReturn uint8_t 47e948693eSPhilip Paeps efx_bootcfg_csum( 48e948693eSPhilip Paeps __in efx_nic_t *enp, 49e948693eSPhilip Paeps __in_bcount(size) caddr_t data, 50e948693eSPhilip Paeps __in size_t size) 51e948693eSPhilip Paeps { 52e948693eSPhilip Paeps _NOTE(ARGUNUSED(enp)) 53e948693eSPhilip Paeps 54e948693eSPhilip Paeps unsigned int pos; 55e948693eSPhilip Paeps uint8_t checksum = 0; 56e948693eSPhilip Paeps 57e948693eSPhilip Paeps for (pos = 0; pos < size; pos++) 58e948693eSPhilip Paeps checksum += data[pos]; 59e948693eSPhilip Paeps return (checksum); 60e948693eSPhilip Paeps } 61e948693eSPhilip Paeps 62e948693eSPhilip Paeps static __checkReturn int 63e948693eSPhilip Paeps efx_bootcfg_verify( 64e948693eSPhilip Paeps __in efx_nic_t *enp, 65e948693eSPhilip Paeps __in_bcount(size) caddr_t data, 66e948693eSPhilip Paeps __in size_t size, 67e948693eSPhilip Paeps __out size_t *usedp) 68e948693eSPhilip Paeps { 69e948693eSPhilip Paeps size_t offset = 0; 70e948693eSPhilip Paeps size_t used = 0; 71e948693eSPhilip Paeps int rc; 72e948693eSPhilip Paeps 73e948693eSPhilip Paeps /* Start parsing tags immediatly after the checksum */ 74e948693eSPhilip Paeps for (offset = 1; offset < size; ) { 75e948693eSPhilip Paeps uint8_t tag; 76e948693eSPhilip Paeps uint8_t length; 77e948693eSPhilip Paeps 78e948693eSPhilip Paeps /* Consume tag */ 79e948693eSPhilip Paeps tag = data[offset]; 80e948693eSPhilip Paeps if (tag == DHCP_END) { 81e948693eSPhilip Paeps offset++; 82e948693eSPhilip Paeps used = offset; 83e948693eSPhilip Paeps break; 84e948693eSPhilip Paeps } 85e948693eSPhilip Paeps if (tag == DHCP_PAD) { 86e948693eSPhilip Paeps offset++; 87e948693eSPhilip Paeps continue; 88e948693eSPhilip Paeps } 89e948693eSPhilip Paeps 90e948693eSPhilip Paeps /* Consume length */ 91e948693eSPhilip Paeps if (offset + 1 >= size) { 92e948693eSPhilip Paeps rc = ENOSPC; 93e948693eSPhilip Paeps goto fail1; 94e948693eSPhilip Paeps } 95e948693eSPhilip Paeps length = data[offset + 1]; 96e948693eSPhilip Paeps 97e948693eSPhilip Paeps /* Consume *length */ 98e948693eSPhilip Paeps if (offset + 1 + length >= size) { 99e948693eSPhilip Paeps rc = ENOSPC; 100e948693eSPhilip Paeps goto fail2; 101e948693eSPhilip Paeps } 102e948693eSPhilip Paeps 103e948693eSPhilip Paeps offset += 2 + length; 104e948693eSPhilip Paeps used = offset; 105e948693eSPhilip Paeps } 106e948693eSPhilip Paeps 107e948693eSPhilip Paeps /* Checksum the entire sector, including bytes after any DHCP_END */ 108e948693eSPhilip Paeps if (efx_bootcfg_csum(enp, data, size) != 0) { 109e948693eSPhilip Paeps rc = EINVAL; 110e948693eSPhilip Paeps goto fail3; 111e948693eSPhilip Paeps } 112e948693eSPhilip Paeps 113e948693eSPhilip Paeps if (usedp != NULL) 114e948693eSPhilip Paeps *usedp = used; 115e948693eSPhilip Paeps 116e948693eSPhilip Paeps return (0); 117e948693eSPhilip Paeps 118e948693eSPhilip Paeps fail3: 119e948693eSPhilip Paeps EFSYS_PROBE(fail3); 120e948693eSPhilip Paeps fail2: 121e948693eSPhilip Paeps EFSYS_PROBE(fail2); 122e948693eSPhilip Paeps fail1: 123e948693eSPhilip Paeps EFSYS_PROBE1(fail1, int, rc); 124e948693eSPhilip Paeps 125e948693eSPhilip Paeps return (rc); 126e948693eSPhilip Paeps } 127e948693eSPhilip Paeps 128e948693eSPhilip Paeps int 129e948693eSPhilip Paeps efx_bootcfg_read( 130e948693eSPhilip Paeps __in efx_nic_t *enp, 131e948693eSPhilip Paeps __out_bcount(size) caddr_t data, 132e948693eSPhilip Paeps __in size_t size) 133e948693eSPhilip Paeps { 134e948693eSPhilip Paeps uint8_t *payload = NULL; 135e948693eSPhilip Paeps size_t used_bytes; 136e948693eSPhilip Paeps size_t sector_length; 137e948693eSPhilip Paeps int rc; 138e948693eSPhilip Paeps 139e948693eSPhilip Paeps rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, §or_length); 140e948693eSPhilip Paeps if (rc != 0) 141e948693eSPhilip Paeps goto fail1; 142e948693eSPhilip Paeps 143e948693eSPhilip Paeps /* 144e948693eSPhilip Paeps * We need to read the entire BOOTCFG area to ensure we read all the 145e948693eSPhilip Paeps * tags, because legacy bootcfg sectors are not guaranteed to end with 146e948693eSPhilip Paeps * a DHCP_END character. If the user hasn't supplied a sufficiently 147e948693eSPhilip Paeps * large buffer then use our own buffer. 148e948693eSPhilip Paeps */ 149e948693eSPhilip Paeps if (sector_length > BOOTCFG_MAX_SIZE) 150e948693eSPhilip Paeps sector_length = BOOTCFG_MAX_SIZE; 151e948693eSPhilip Paeps if (sector_length > size) { 152e948693eSPhilip Paeps EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload); 153e948693eSPhilip Paeps if (payload == NULL) { 154e948693eSPhilip Paeps rc = ENOMEM; 155e948693eSPhilip Paeps goto fail2; 156e948693eSPhilip Paeps } 157e948693eSPhilip Paeps } else 158e948693eSPhilip Paeps payload = (uint8_t *)data; 159e948693eSPhilip Paeps 160e948693eSPhilip Paeps if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 161e948693eSPhilip Paeps goto fail3; 162e948693eSPhilip Paeps 163e948693eSPhilip Paeps rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0, 164e948693eSPhilip Paeps (caddr_t)payload, sector_length); 165e948693eSPhilip Paeps 166e948693eSPhilip Paeps efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); 167e948693eSPhilip Paeps 168e948693eSPhilip Paeps if (rc != 0) 169e948693eSPhilip Paeps goto fail4; 170e948693eSPhilip Paeps 171e948693eSPhilip Paeps /* Verify that the area is correctly formatted and checksummed */ 172e948693eSPhilip Paeps rc = efx_bootcfg_verify(enp, (caddr_t)payload, sector_length, 173e948693eSPhilip Paeps &used_bytes); 174e948693eSPhilip Paeps if (rc != 0 || used_bytes == 0) { 175e948693eSPhilip Paeps payload[0] = (uint8_t)~DHCP_END; 176e948693eSPhilip Paeps payload[1] = DHCP_END; 177e948693eSPhilip Paeps used_bytes = 2; 178e948693eSPhilip Paeps } 179e948693eSPhilip Paeps 180e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 181e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes <= sector_length); 182e948693eSPhilip Paeps 183e948693eSPhilip Paeps /* 184e948693eSPhilip Paeps * Legacy bootcfg sectors don't terminate with a DHCP_END character. 185e948693eSPhilip Paeps * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by 186e948693eSPhilip Paeps * definition large enough for any valid (per-port) bootcfg sector, 187e948693eSPhilip Paeps * so reinitialise the sector if there isn't room for the character. 188e948693eSPhilip Paeps */ 189e948693eSPhilip Paeps if (payload[used_bytes - 1] != DHCP_END) { 190e948693eSPhilip Paeps if (used_bytes + 1 > sector_length) { 191e948693eSPhilip Paeps payload[0] = 0; 192e948693eSPhilip Paeps used_bytes = 1; 193e948693eSPhilip Paeps } 194e948693eSPhilip Paeps 195e948693eSPhilip Paeps payload[used_bytes] = DHCP_END; 196e948693eSPhilip Paeps ++used_bytes; 197e948693eSPhilip Paeps } 198e948693eSPhilip Paeps 199e948693eSPhilip Paeps /* 200e948693eSPhilip Paeps * Verify that the user supplied buffer is large enough for the 201e948693eSPhilip Paeps * entire used bootcfg area, then copy into the user supplied buffer. 202e948693eSPhilip Paeps */ 203e948693eSPhilip Paeps if (used_bytes > size) { 204e948693eSPhilip Paeps rc = ENOSPC; 205e948693eSPhilip Paeps goto fail5; 206e948693eSPhilip Paeps } 207e948693eSPhilip Paeps if (sector_length > size) { 208e948693eSPhilip Paeps memcpy(data, payload, used_bytes); 209e948693eSPhilip Paeps EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 210e948693eSPhilip Paeps } 211e948693eSPhilip Paeps 212e948693eSPhilip Paeps /* Zero out the unused portion of the user buffer */ 213e948693eSPhilip Paeps if (used_bytes < size) 214e948693eSPhilip Paeps (void) memset(data + used_bytes, 0, size - used_bytes); 215e948693eSPhilip Paeps 216e948693eSPhilip Paeps /* 217e948693eSPhilip Paeps * The checksum includes trailing data after any DHCP_END character, 218e948693eSPhilip Paeps * which we've just modified (by truncation or appending DHCP_END). 219e948693eSPhilip Paeps */ 220e948693eSPhilip Paeps data[0] -= efx_bootcfg_csum(enp, data, size); 221e948693eSPhilip Paeps 222e948693eSPhilip Paeps return (0); 223e948693eSPhilip Paeps 224e948693eSPhilip Paeps fail5: 225e948693eSPhilip Paeps EFSYS_PROBE(fail5); 226e948693eSPhilip Paeps fail4: 227e948693eSPhilip Paeps EFSYS_PROBE(fail4); 228e948693eSPhilip Paeps fail3: 229e948693eSPhilip Paeps EFSYS_PROBE(fail3); 230e948693eSPhilip Paeps 231e948693eSPhilip Paeps if (sector_length > size) 232e948693eSPhilip Paeps EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 233e948693eSPhilip Paeps fail2: 234e948693eSPhilip Paeps EFSYS_PROBE(fail2); 235e948693eSPhilip Paeps fail1: 236e948693eSPhilip Paeps EFSYS_PROBE1(fail1, int, rc); 237e948693eSPhilip Paeps 238e948693eSPhilip Paeps return (rc); 239e948693eSPhilip Paeps } 240e948693eSPhilip Paeps 241e948693eSPhilip Paeps int 242e948693eSPhilip Paeps efx_bootcfg_write( 243e948693eSPhilip Paeps __in efx_nic_t *enp, 244e948693eSPhilip Paeps __in_bcount(size) caddr_t data, 245e948693eSPhilip Paeps __in size_t size) 246e948693eSPhilip Paeps { 247e948693eSPhilip Paeps uint8_t *chunk; 248e948693eSPhilip Paeps uint8_t checksum; 249e948693eSPhilip Paeps size_t sector_length; 250e948693eSPhilip Paeps size_t chunk_length; 251e948693eSPhilip Paeps size_t used_bytes; 252e948693eSPhilip Paeps size_t offset; 253e948693eSPhilip Paeps size_t remaining; 254e948693eSPhilip Paeps int rc; 255e948693eSPhilip Paeps 256e948693eSPhilip Paeps rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, §or_length); 257e948693eSPhilip Paeps if (rc != 0) 258e948693eSPhilip Paeps goto fail1; 259e948693eSPhilip Paeps 260e948693eSPhilip Paeps if (sector_length > BOOTCFG_MAX_SIZE) 261e948693eSPhilip Paeps sector_length = BOOTCFG_MAX_SIZE; 262e948693eSPhilip Paeps 263e948693eSPhilip Paeps if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0) 264e948693eSPhilip Paeps goto fail2; 265e948693eSPhilip Paeps 266e948693eSPhilip Paeps /* The caller *must* terminate their block with a DHCP_END character */ 267e948693eSPhilip Paeps EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 268e948693eSPhilip Paeps if ((uint8_t)data[used_bytes - 1] != DHCP_END) { 269e948693eSPhilip Paeps rc = ENOENT; 270e948693eSPhilip Paeps goto fail3; 271e948693eSPhilip Paeps } 272e948693eSPhilip Paeps 273e948693eSPhilip Paeps /* Check that the hardware has support for this much data */ 274e948693eSPhilip Paeps if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) { 275e948693eSPhilip Paeps rc = ENOSPC; 276e948693eSPhilip Paeps goto fail4; 277e948693eSPhilip Paeps } 278e948693eSPhilip Paeps 279e948693eSPhilip Paeps rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, &chunk_length); 280e948693eSPhilip Paeps if (rc != 0) 281e948693eSPhilip Paeps goto fail5; 282e948693eSPhilip Paeps 283e948693eSPhilip Paeps EFSYS_KMEM_ALLOC(enp->en_esip, chunk_length, chunk); 284e948693eSPhilip Paeps if (chunk == NULL) { 285e948693eSPhilip Paeps rc = ENOMEM; 286e948693eSPhilip Paeps goto fail6; 287e948693eSPhilip Paeps } 288e948693eSPhilip Paeps 289e948693eSPhilip Paeps if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 290e948693eSPhilip Paeps goto fail7; 291e948693eSPhilip Paeps 292e948693eSPhilip Paeps /* 293e948693eSPhilip Paeps * Write the entire sector_length bytes of data in chunks. Zero out 294e948693eSPhilip Paeps * all data following the DHCP_END, and adjust the checksum 295e948693eSPhilip Paeps */ 296e948693eSPhilip Paeps checksum = efx_bootcfg_csum(enp, data, used_bytes); 297e948693eSPhilip Paeps for (offset = 0; offset < sector_length; offset += remaining) { 298e948693eSPhilip Paeps remaining = MIN(chunk_length, sector_length - offset); 299e948693eSPhilip Paeps 300e948693eSPhilip Paeps /* Fill chunk */ 301e948693eSPhilip Paeps (void) memset(chunk, 0x0, chunk_length); 302e948693eSPhilip Paeps if (offset < used_bytes) 303e948693eSPhilip Paeps memcpy(chunk, data + offset, 304e948693eSPhilip Paeps MIN(remaining, used_bytes - offset)); 305e948693eSPhilip Paeps 306e948693eSPhilip Paeps /* Adjust checksum */ 307e948693eSPhilip Paeps if (offset == 0) 308e948693eSPhilip Paeps chunk[0] -= checksum; 309e948693eSPhilip Paeps 310e948693eSPhilip Paeps if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 311e948693eSPhilip Paeps offset, (caddr_t)chunk, remaining)) != 0) 312e948693eSPhilip Paeps goto fail8; 313e948693eSPhilip Paeps } 314e948693eSPhilip Paeps 315e948693eSPhilip Paeps efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); 316e948693eSPhilip Paeps 317e948693eSPhilip Paeps EFSYS_KMEM_FREE(enp->en_esip, chunk_length, chunk); 318e948693eSPhilip Paeps 319e948693eSPhilip Paeps return (0); 320e948693eSPhilip Paeps 321e948693eSPhilip Paeps fail8: 322e948693eSPhilip Paeps EFSYS_PROBE(fail8); 323e948693eSPhilip Paeps fail7: 324e948693eSPhilip Paeps EFSYS_PROBE(fail7); 325e948693eSPhilip Paeps 326e948693eSPhilip Paeps EFSYS_KMEM_FREE(enp->en_esip, chunk_length, chunk); 327e948693eSPhilip Paeps fail6: 328e948693eSPhilip Paeps EFSYS_PROBE(fail6); 329e948693eSPhilip Paeps 330e948693eSPhilip Paeps efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); 331e948693eSPhilip Paeps fail5: 332e948693eSPhilip Paeps EFSYS_PROBE(fail5); 333e948693eSPhilip Paeps fail4: 334e948693eSPhilip Paeps EFSYS_PROBE(fail4); 335e948693eSPhilip Paeps fail3: 336e948693eSPhilip Paeps EFSYS_PROBE(fail3); 337e948693eSPhilip Paeps fail2: 338e948693eSPhilip Paeps EFSYS_PROBE(fail2); 339e948693eSPhilip Paeps fail1: 340e948693eSPhilip Paeps EFSYS_PROBE1(fail1, int, rc); 341e948693eSPhilip Paeps 342e948693eSPhilip Paeps return (rc); 343e948693eSPhilip Paeps } 344e948693eSPhilip Paeps 345e948693eSPhilip Paeps #endif /* EFSYS_OPT_BOOTCFG */ 346