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