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 * NOTE: This is larger than the Medford per-PF bootcfg sector. 42 */ 43 #define BOOTCFG_MAX_SIZE 0x1000 44 45 /* Medford per-PF bootcfg sector */ 46 #define BOOTCFG_PER_PF 0x800 47 #define BOOTCFG_PF_COUNT 16 48 49 #define DHCP_END ((uint8_t)0xff) 50 #define DHCP_PAD ((uint8_t)0) 51 52 53 /* Report the layout of bootcfg sectors in NVRAM partition. */ 54 __checkReturn efx_rc_t 55 efx_bootcfg_sector_info( 56 __in efx_nic_t *enp, 57 __in uint32_t pf, 58 __out_opt uint32_t *sector_countp, 59 __out size_t *offsetp, 60 __out size_t *max_sizep) 61 { 62 uint32_t count; 63 size_t max_size; 64 size_t offset; 65 int rc; 66 67 switch (enp->en_family) { 68 #if EFSYS_OPT_SIENA 69 case EFX_FAMILY_SIENA: 70 max_size = BOOTCFG_MAX_SIZE; 71 offset = 0; 72 count = 1; 73 break; 74 #endif /* EFSYS_OPT_SIENA */ 75 76 #if EFSYS_OPT_HUNTINGTON 77 case EFX_FAMILY_HUNTINGTON: 78 max_size = BOOTCFG_MAX_SIZE; 79 offset = 0; 80 count = 1; 81 break; 82 #endif /* EFSYS_OPT_HUNTINGTON */ 83 84 #if EFSYS_OPT_MEDFORD 85 case EFX_FAMILY_MEDFORD: { 86 /* Shared partition (array indexed by PF) */ 87 max_size = BOOTCFG_PER_PF; 88 count = BOOTCFG_PF_COUNT; 89 if (pf >= count) { 90 rc = EINVAL; 91 goto fail2; 92 } 93 offset = max_size * pf; 94 break; 95 } 96 #endif /* EFSYS_OPT_MEDFORD */ 97 98 default: 99 EFSYS_ASSERT(0); 100 rc = ENOTSUP; 101 goto fail1; 102 } 103 EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE); 104 105 if (sector_countp != NULL) 106 *sector_countp = count; 107 *offsetp = offset; 108 *max_sizep = max_size; 109 110 return (0); 111 112 #if EFSYS_OPT_MEDFORD 113 fail2: 114 EFSYS_PROBE(fail2); 115 #endif 116 fail1: 117 EFSYS_PROBE1(fail1, efx_rc_t, rc); 118 return (rc); 119 } 120 121 122 static __checkReturn uint8_t 123 efx_bootcfg_csum( 124 __in efx_nic_t *enp, 125 __in_bcount(size) uint8_t const *data, 126 __in size_t size) 127 { 128 _NOTE(ARGUNUSED(enp)) 129 130 unsigned int pos; 131 uint8_t checksum = 0; 132 133 for (pos = 0; pos < size; pos++) 134 checksum += data[pos]; 135 return (checksum); 136 } 137 138 static __checkReturn efx_rc_t 139 efx_bootcfg_verify( 140 __in efx_nic_t *enp, 141 __in_bcount(size) uint8_t const *data, 142 __in size_t size, 143 __out_opt size_t *usedp) 144 { 145 size_t offset = 0; 146 size_t used = 0; 147 efx_rc_t rc; 148 149 /* Start parsing tags immediately after the checksum */ 150 for (offset = 1; offset < size; ) { 151 uint8_t tag; 152 uint8_t length; 153 154 /* Consume tag */ 155 tag = data[offset]; 156 if (tag == DHCP_END) { 157 offset++; 158 used = offset; 159 break; 160 } 161 if (tag == DHCP_PAD) { 162 offset++; 163 continue; 164 } 165 166 /* Consume length */ 167 if (offset + 1 >= size) { 168 rc = ENOSPC; 169 goto fail1; 170 } 171 length = data[offset + 1]; 172 173 /* Consume *length */ 174 if (offset + 1 + length >= size) { 175 rc = ENOSPC; 176 goto fail2; 177 } 178 179 offset += 2 + length; 180 used = offset; 181 } 182 183 /* Checksum the entire sector, including bytes after any DHCP_END */ 184 if (efx_bootcfg_csum(enp, data, size) != 0) { 185 rc = EINVAL; 186 goto fail3; 187 } 188 189 if (usedp != NULL) 190 *usedp = used; 191 192 return (0); 193 194 fail3: 195 EFSYS_PROBE(fail3); 196 fail2: 197 EFSYS_PROBE(fail2); 198 fail1: 199 EFSYS_PROBE1(fail1, efx_rc_t, rc); 200 201 return (rc); 202 } 203 204 /* 205 * Copy bootcfg sector data to a target buffer which may differ in size. 206 * Optionally corrects format errors in source buffer. 207 */ 208 efx_rc_t 209 efx_bootcfg_copy_sector( 210 __in efx_nic_t *enp, 211 __inout_bcount(sector_length) 212 uint8_t *sector, 213 __in size_t sector_length, 214 __out_bcount(data_size) uint8_t *data, 215 __in size_t data_size, 216 __in boolean_t handle_format_errors) 217 { 218 size_t used_bytes; 219 efx_rc_t rc; 220 221 /* Verify that the area is correctly formatted and checksummed */ 222 rc = efx_bootcfg_verify(enp, sector, sector_length, 223 &used_bytes); 224 225 if (!handle_format_errors) { 226 if (rc != 0) 227 goto fail1; 228 229 if ((used_bytes < 2) || 230 (sector[used_bytes - 1] != DHCP_END)) { 231 /* Block too short, or DHCP_END missing */ 232 rc = ENOENT; 233 goto fail2; 234 } 235 } 236 237 /* Synthesize empty format on verification failure */ 238 if (rc != 0 || used_bytes == 0) { 239 sector[0] = 0; 240 sector[1] = DHCP_END; 241 used_bytes = 2; 242 } 243 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 244 EFSYS_ASSERT(used_bytes <= sector_length); 245 EFSYS_ASSERT(sector_length >= 2); 246 247 /* 248 * Legacy bootcfg sectors don't terminate with a DHCP_END character. 249 * Modify the returned payload so it does. 250 * Reinitialise the sector if there isn't room for the character. 251 */ 252 if (sector[used_bytes - 1] != DHCP_END) { 253 if (used_bytes >= sector_length) { 254 sector[0] = 0; 255 used_bytes = 1; 256 } 257 sector[used_bytes] = DHCP_END; 258 ++used_bytes; 259 } 260 261 /* 262 * Verify that the target buffer is large enough for the 263 * entire used bootcfg area, then copy into the target buffer. 264 */ 265 if (used_bytes > data_size) { 266 rc = ENOSPC; 267 goto fail3; 268 } 269 memcpy(data, sector, used_bytes); 270 271 /* Zero out the unused portion of the target buffer */ 272 if (used_bytes < data_size) 273 (void) memset(data + used_bytes, 0, data_size - used_bytes); 274 275 /* 276 * The checksum includes trailing data after any DHCP_END character, 277 * which we've just modified (by truncation or appending DHCP_END). 278 */ 279 data[0] -= efx_bootcfg_csum(enp, data, data_size); 280 281 return (0); 282 283 fail3: 284 EFSYS_PROBE(fail3); 285 fail2: 286 EFSYS_PROBE(fail2); 287 fail1: 288 EFSYS_PROBE1(fail1, efx_rc_t, rc); 289 290 return (rc); 291 } 292 293 efx_rc_t 294 efx_bootcfg_read( 295 __in efx_nic_t *enp, 296 __out_bcount(size) caddr_t data, 297 __in size_t size) 298 { 299 uint8_t *payload = NULL; 300 size_t used_bytes; 301 size_t partn_length; 302 size_t sector_length; 303 size_t sector_offset; 304 efx_rc_t rc; 305 uint32_t sector_number; 306 307 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD 308 sector_number = enp->en_nic_cfg.enc_pf; 309 #else 310 sector_number = 0; 311 #endif 312 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 313 if (rc != 0) 314 goto fail1; 315 316 /* The bootcfg sector may be stored in a (larger) shared partition */ 317 rc = efx_bootcfg_sector_info(enp, sector_number, 318 NULL, §or_offset, §or_length); 319 if (rc != 0) 320 goto fail2; 321 322 if (sector_length > BOOTCFG_MAX_SIZE) 323 sector_length = BOOTCFG_MAX_SIZE; 324 325 if (sector_offset + sector_length > partn_length) { 326 /* Partition is too small */ 327 rc = EFBIG; 328 goto fail3; 329 } 330 331 /* 332 * We need to read the entire BOOTCFG sector to ensure we read all the 333 * tags, because legacy bootcfg sectors are not guaranteed to end with 334 * a DHCP_END character. If the user hasn't supplied a sufficiently 335 * large buffer then use our own buffer. 336 */ 337 if (sector_length > size) { 338 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload); 339 if (payload == NULL) { 340 rc = ENOMEM; 341 goto fail4; 342 } 343 } else 344 payload = (uint8_t *)data; 345 346 if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 347 goto fail5; 348 349 if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 350 sector_offset, (caddr_t)payload, sector_length)) != 0) { 351 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); 352 goto fail6; 353 } 354 355 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 356 goto fail7; 357 358 /* Verify that the area is correctly formatted and checksummed */ 359 rc = efx_bootcfg_verify(enp, (caddr_t)payload, sector_length, 360 &used_bytes); 361 if (rc != 0 || used_bytes == 0) { 362 payload[0] = (uint8_t)~DHCP_END; 363 payload[1] = DHCP_END; 364 used_bytes = 2; 365 } 366 367 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 368 EFSYS_ASSERT(used_bytes <= sector_length); 369 370 /* 371 * Legacy bootcfg sectors don't terminate with a DHCP_END character. 372 * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by 373 * definition large enough for any valid (per-port) bootcfg sector, 374 * so reinitialise the sector if there isn't room for the character. 375 */ 376 if (payload[used_bytes - 1] != DHCP_END) { 377 if (used_bytes + 1 > sector_length) { 378 payload[0] = 0; 379 used_bytes = 1; 380 } 381 382 payload[used_bytes] = DHCP_END; 383 ++used_bytes; 384 } 385 386 /* 387 * Verify that the user supplied buffer is large enough for the 388 * entire used bootcfg area, then copy into the user supplied buffer. 389 */ 390 if (used_bytes > size) { 391 rc = ENOSPC; 392 goto fail8; 393 } 394 if (sector_length > size) { 395 memcpy(data, payload, used_bytes); 396 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 397 } 398 399 /* Zero out the unused portion of the user buffer */ 400 if (used_bytes < size) 401 (void) memset(data + used_bytes, 0, size - used_bytes); 402 403 /* 404 * The checksum includes trailing data after any DHCP_END character, 405 * which we've just modified (by truncation or appending DHCP_END). 406 */ 407 data[0] -= efx_bootcfg_csum(enp, data, size); 408 409 return (0); 410 411 fail8: 412 EFSYS_PROBE(fail8); 413 fail7: 414 EFSYS_PROBE(fail7); 415 fail6: 416 EFSYS_PROBE(fail6); 417 fail5: 418 EFSYS_PROBE(fail5); 419 if (sector_length > size) 420 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 421 fail4: 422 EFSYS_PROBE(fail4); 423 fail3: 424 EFSYS_PROBE(fail3); 425 fail2: 426 EFSYS_PROBE(fail2); 427 fail1: 428 EFSYS_PROBE1(fail1, efx_rc_t, rc); 429 430 return (rc); 431 } 432 433 efx_rc_t 434 efx_bootcfg_write( 435 __in efx_nic_t *enp, 436 __in_bcount(size) caddr_t data, 437 __in size_t size) 438 { 439 uint8_t *partn_data; 440 uint8_t checksum; 441 size_t partn_length; 442 size_t sector_length; 443 size_t sector_offset; 444 size_t used_bytes; 445 efx_rc_t rc; 446 uint32_t sector_number; 447 448 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD 449 sector_number = enp->en_nic_cfg.enc_pf; 450 #else 451 sector_number = 0; 452 #endif 453 454 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 455 if (rc != 0) 456 goto fail1; 457 458 /* The bootcfg sector may be stored in a (larger) shared partition */ 459 rc = efx_bootcfg_sector_info(enp, sector_number, 460 NULL, §or_offset, §or_length); 461 if (rc != 0) 462 goto fail2; 463 464 if (sector_length > BOOTCFG_MAX_SIZE) 465 sector_length = BOOTCFG_MAX_SIZE; 466 467 if (sector_offset + sector_length > partn_length) { 468 /* Partition is too small */ 469 rc = EFBIG; 470 goto fail3; 471 } 472 473 if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0) 474 goto fail4; 475 476 /* The caller *must* terminate their block with a DHCP_END character */ 477 if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) { 478 /* Block too short or DHCP_END missing */ 479 rc = ENOENT; 480 goto fail5; 481 } 482 483 /* Check that the hardware has support for this much data */ 484 if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) { 485 rc = ENOSPC; 486 goto fail6; 487 } 488 489 /* 490 * If the BOOTCFG sector is stored in a shared partition, then we must 491 * read the whole partition and insert the updated bootcfg sector at the 492 * correct offset. 493 */ 494 EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data); 495 if (partn_data == NULL) { 496 rc = ENOMEM; 497 goto fail7; 498 } 499 500 rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 501 if (rc != 0) 502 goto fail8; 503 504 /* Read the entire partition */ 505 rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0, 506 (caddr_t)partn_data, partn_length); 507 if (rc != 0) 508 goto fail9; 509 510 /* 511 * Insert the BOOTCFG sector into the partition, Zero out all data after 512 * the DHCP_END tag, and adjust the checksum. 513 */ 514 (void) memset(partn_data + sector_offset, 0x0, sector_length); 515 (void) memcpy(partn_data + sector_offset, data, used_bytes); 516 517 checksum = efx_bootcfg_csum(enp, data, used_bytes); 518 partn_data[sector_offset] -= checksum; 519 520 if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 521 goto fail10; 522 523 if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 524 0, (caddr_t)partn_data, partn_length)) != 0) 525 goto fail11; 526 527 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 528 goto fail12; 529 530 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 531 532 return (0); 533 534 fail12: 535 EFSYS_PROBE(fail12); 536 fail11: 537 EFSYS_PROBE(fail11); 538 fail10: 539 EFSYS_PROBE(fail10); 540 fail9: 541 EFSYS_PROBE(fail9); 542 543 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); 544 fail8: 545 EFSYS_PROBE(fail8); 546 547 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 548 fail7: 549 EFSYS_PROBE(fail7); 550 fail6: 551 EFSYS_PROBE(fail6); 552 fail5: 553 EFSYS_PROBE(fail5); 554 fail4: 555 EFSYS_PROBE(fail4); 556 fail3: 557 EFSYS_PROBE(fail3); 558 fail2: 559 EFSYS_PROBE(fail2); 560 fail1: 561 EFSYS_PROBE1(fail1, efx_rc_t, rc); 562 563 return (rc); 564 } 565 566 #endif /* EFSYS_OPT_BOOTCFG */ 567