1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2009-2016 Solarflare Communications Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * The views and conclusions contained in the software and documentation are 29 * those of the authors and should not be interpreted as representing official 30 * policies, either expressed or implied, of the FreeBSD Project. 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include "efx.h" 37 #include "efx_impl.h" 38 39 #if EFSYS_OPT_BOOTCFG 40 41 /* 42 * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE. 43 * NOTE: This is larger than the Medford per-PF bootcfg sector. 44 */ 45 #define BOOTCFG_MAX_SIZE 0x1000 46 47 /* Medford per-PF bootcfg sector */ 48 #define BOOTCFG_PER_PF 0x800 49 #define BOOTCFG_PF_COUNT 16 50 51 #define DHCP_END ((uint8_t)0xff) 52 #define DHCP_PAD ((uint8_t)0) 53 54 55 /* Report the layout of bootcfg sectors in NVRAM partition. */ 56 __checkReturn efx_rc_t 57 efx_bootcfg_sector_info( 58 __in efx_nic_t *enp, 59 __in uint32_t pf, 60 __out_opt uint32_t *sector_countp, 61 __out size_t *offsetp, 62 __out size_t *max_sizep) 63 { 64 uint32_t count; 65 size_t max_size; 66 size_t offset; 67 int rc; 68 69 switch (enp->en_family) { 70 #if EFSYS_OPT_SIENA 71 case EFX_FAMILY_SIENA: 72 max_size = BOOTCFG_MAX_SIZE; 73 offset = 0; 74 count = 1; 75 break; 76 #endif /* EFSYS_OPT_SIENA */ 77 78 #if EFSYS_OPT_HUNTINGTON 79 case EFX_FAMILY_HUNTINGTON: 80 max_size = BOOTCFG_MAX_SIZE; 81 offset = 0; 82 count = 1; 83 break; 84 #endif /* EFSYS_OPT_HUNTINGTON */ 85 86 #if EFSYS_OPT_MEDFORD 87 case EFX_FAMILY_MEDFORD: { 88 /* Shared partition (array indexed by PF) */ 89 max_size = BOOTCFG_PER_PF; 90 count = BOOTCFG_PF_COUNT; 91 if (pf >= count) { 92 rc = EINVAL; 93 goto fail2; 94 } 95 offset = max_size * pf; 96 break; 97 } 98 #endif /* EFSYS_OPT_MEDFORD */ 99 100 #if EFSYS_OPT_MEDFORD2 101 case EFX_FAMILY_MEDFORD2: { 102 /* Shared partition (array indexed by PF) */ 103 max_size = BOOTCFG_PER_PF; 104 count = BOOTCFG_PF_COUNT; 105 if (pf >= count) { 106 rc = EINVAL; 107 goto fail3; 108 } 109 offset = max_size * pf; 110 break; 111 } 112 #endif /* EFSYS_OPT_MEDFORD2 */ 113 114 default: 115 EFSYS_ASSERT(0); 116 rc = ENOTSUP; 117 goto fail1; 118 } 119 EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE); 120 121 if (sector_countp != NULL) 122 *sector_countp = count; 123 *offsetp = offset; 124 *max_sizep = max_size; 125 126 return (0); 127 128 #if EFSYS_OPT_MEDFORD2 129 fail3: 130 EFSYS_PROBE(fail3); 131 #endif 132 #if EFSYS_OPT_MEDFORD 133 fail2: 134 EFSYS_PROBE(fail2); 135 #endif 136 fail1: 137 EFSYS_PROBE1(fail1, efx_rc_t, rc); 138 return (rc); 139 } 140 141 142 static __checkReturn uint8_t 143 efx_bootcfg_csum( 144 __in efx_nic_t *enp, 145 __in_bcount(size) uint8_t const *data, 146 __in size_t size) 147 { 148 _NOTE(ARGUNUSED(enp)) 149 150 unsigned int pos; 151 uint8_t checksum = 0; 152 153 for (pos = 0; pos < size; pos++) 154 checksum += data[pos]; 155 return (checksum); 156 } 157 158 static __checkReturn efx_rc_t 159 efx_bootcfg_verify( 160 __in efx_nic_t *enp, 161 __in_bcount(size) uint8_t const *data, 162 __in size_t size, 163 __out_opt size_t *usedp) 164 { 165 size_t offset = 0; 166 size_t used = 0; 167 efx_rc_t rc; 168 169 /* Start parsing tags immediately after the checksum */ 170 for (offset = 1; offset < size; ) { 171 uint8_t tag; 172 uint8_t length; 173 174 /* Consume tag */ 175 tag = data[offset]; 176 if (tag == DHCP_END) { 177 offset++; 178 used = offset; 179 break; 180 } 181 if (tag == DHCP_PAD) { 182 offset++; 183 continue; 184 } 185 186 /* Consume length */ 187 if (offset + 1 >= size) { 188 rc = ENOSPC; 189 goto fail1; 190 } 191 length = data[offset + 1]; 192 193 /* Consume *length */ 194 if (offset + 1 + length >= size) { 195 rc = ENOSPC; 196 goto fail2; 197 } 198 199 offset += 2 + length; 200 used = offset; 201 } 202 203 /* Checksum the entire sector, including bytes after any DHCP_END */ 204 if (efx_bootcfg_csum(enp, data, size) != 0) { 205 rc = EINVAL; 206 goto fail3; 207 } 208 209 if (usedp != NULL) 210 *usedp = used; 211 212 return (0); 213 214 fail3: 215 EFSYS_PROBE(fail3); 216 fail2: 217 EFSYS_PROBE(fail2); 218 fail1: 219 EFSYS_PROBE1(fail1, efx_rc_t, rc); 220 221 return (rc); 222 } 223 224 /* 225 * Copy bootcfg sector data to a target buffer which may differ in size. 226 * Optionally corrects format errors in source buffer. 227 */ 228 efx_rc_t 229 efx_bootcfg_copy_sector( 230 __in efx_nic_t *enp, 231 __inout_bcount(sector_length) 232 uint8_t *sector, 233 __in size_t sector_length, 234 __out_bcount(data_size) uint8_t *data, 235 __in size_t data_size, 236 __in boolean_t handle_format_errors) 237 { 238 size_t used_bytes; 239 efx_rc_t rc; 240 241 /* Minimum buffer is checksum byte and DHCP_END terminator */ 242 if (data_size < 2) { 243 rc = ENOSPC; 244 goto fail1; 245 } 246 247 /* Verify that the area is correctly formatted and checksummed */ 248 rc = efx_bootcfg_verify(enp, sector, sector_length, 249 &used_bytes); 250 251 if (!handle_format_errors) { 252 if (rc != 0) 253 goto fail2; 254 255 if ((used_bytes < 2) || 256 (sector[used_bytes - 1] != DHCP_END)) { 257 /* Block too short, or DHCP_END missing */ 258 rc = ENOENT; 259 goto fail3; 260 } 261 } 262 263 /* Synthesize empty format on verification failure */ 264 if (rc != 0 || used_bytes == 0) { 265 sector[0] = 0; 266 sector[1] = DHCP_END; 267 used_bytes = 2; 268 } 269 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 270 EFSYS_ASSERT(used_bytes <= sector_length); 271 EFSYS_ASSERT(sector_length >= 2); 272 273 /* 274 * Legacy bootcfg sectors don't terminate with a DHCP_END character. 275 * Modify the returned payload so it does. 276 * Reinitialise the sector if there isn't room for the character. 277 */ 278 if (sector[used_bytes - 1] != DHCP_END) { 279 if (used_bytes >= sector_length) { 280 sector[0] = 0; 281 used_bytes = 1; 282 } 283 sector[used_bytes] = DHCP_END; 284 ++used_bytes; 285 } 286 287 /* 288 * Verify that the target buffer is large enough for the 289 * entire used bootcfg area, then copy into the target buffer. 290 */ 291 if (used_bytes > data_size) { 292 rc = ENOSPC; 293 goto fail4; 294 } 295 296 data[0] = 0; /* checksum, updated below */ 297 298 /* Copy all after the checksum to the target buffer */ 299 memcpy(data + 1, sector + 1, used_bytes - 1); 300 301 /* Zero out the unused portion of the target buffer */ 302 if (used_bytes < data_size) 303 (void) memset(data + used_bytes, 0, data_size - used_bytes); 304 305 /* 306 * The checksum includes trailing data after any DHCP_END character, 307 * which we've just modified (by truncation or appending DHCP_END). 308 */ 309 data[0] -= efx_bootcfg_csum(enp, data, data_size); 310 311 return (0); 312 313 fail4: 314 EFSYS_PROBE(fail4); 315 fail3: 316 EFSYS_PROBE(fail3); 317 fail2: 318 EFSYS_PROBE(fail2); 319 fail1: 320 EFSYS_PROBE1(fail1, efx_rc_t, rc); 321 322 return (rc); 323 } 324 325 efx_rc_t 326 efx_bootcfg_read( 327 __in efx_nic_t *enp, 328 __out_bcount(size) uint8_t *data, 329 __in size_t size) 330 { 331 uint8_t *payload = NULL; 332 size_t used_bytes; 333 size_t partn_length; 334 size_t sector_length; 335 size_t sector_offset; 336 efx_rc_t rc; 337 uint32_t sector_number; 338 339 /* Minimum buffer is checksum byte and DHCP_END terminator */ 340 if (size < 2) { 341 rc = ENOSPC; 342 goto fail1; 343 } 344 345 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 346 sector_number = enp->en_nic_cfg.enc_pf; 347 #else 348 sector_number = 0; 349 #endif 350 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 351 if (rc != 0) 352 goto fail2; 353 354 /* The bootcfg sector may be stored in a (larger) shared partition */ 355 rc = efx_bootcfg_sector_info(enp, sector_number, 356 NULL, §or_offset, §or_length); 357 if (rc != 0) 358 goto fail3; 359 360 if (sector_length < 2) { 361 rc = EINVAL; 362 goto fail4; 363 } 364 365 if (sector_length > BOOTCFG_MAX_SIZE) 366 sector_length = BOOTCFG_MAX_SIZE; 367 368 if (sector_offset + sector_length > partn_length) { 369 /* Partition is too small */ 370 rc = EFBIG; 371 goto fail5; 372 } 373 374 /* 375 * We need to read the entire BOOTCFG sector to ensure we read all the 376 * tags, because legacy bootcfg sectors are not guaranteed to end with 377 * a DHCP_END character. If the user hasn't supplied a sufficiently 378 * large buffer then use our own buffer. 379 */ 380 if (sector_length > size) { 381 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload); 382 if (payload == NULL) { 383 rc = ENOMEM; 384 goto fail6; 385 } 386 } else 387 payload = (uint8_t *)data; 388 389 if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 390 goto fail7; 391 392 if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 393 sector_offset, (caddr_t)payload, sector_length)) != 0) { 394 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 395 goto fail8; 396 } 397 398 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 399 goto fail9; 400 401 /* Verify that the area is correctly formatted and checksummed */ 402 rc = efx_bootcfg_verify(enp, payload, sector_length, 403 &used_bytes); 404 if (rc != 0 || used_bytes == 0) { 405 payload[0] = 0; 406 payload[1] = DHCP_END; 407 used_bytes = 2; 408 } 409 410 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ 411 EFSYS_ASSERT(used_bytes <= sector_length); 412 413 /* 414 * Legacy bootcfg sectors don't terminate with a DHCP_END character. 415 * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by 416 * definition large enough for any valid (per-port) bootcfg sector, 417 * so reinitialise the sector if there isn't room for the character. 418 */ 419 if (payload[used_bytes - 1] != DHCP_END) { 420 if (used_bytes >= sector_length) 421 used_bytes = 1; 422 423 payload[used_bytes] = DHCP_END; 424 ++used_bytes; 425 } 426 427 /* 428 * Verify that the user supplied buffer is large enough for the 429 * entire used bootcfg area, then copy into the user supplied buffer. 430 */ 431 if (used_bytes > size) { 432 rc = ENOSPC; 433 goto fail10; 434 } 435 436 data[0] = 0; /* checksum, updated below */ 437 438 if (sector_length > size) { 439 /* Copy all after the checksum to the target buffer */ 440 memcpy(data + 1, payload + 1, used_bytes - 1); 441 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 442 } 443 444 /* Zero out the unused portion of the user buffer */ 445 if (used_bytes < size) 446 (void) memset(data + used_bytes, 0, size - used_bytes); 447 448 /* 449 * The checksum includes trailing data after any DHCP_END character, 450 * which we've just modified (by truncation or appending DHCP_END). 451 */ 452 data[0] -= efx_bootcfg_csum(enp, data, size); 453 454 return (0); 455 456 fail10: 457 EFSYS_PROBE(fail10); 458 fail9: 459 EFSYS_PROBE(fail9); 460 fail8: 461 EFSYS_PROBE(fail8); 462 fail7: 463 EFSYS_PROBE(fail7); 464 if (sector_length > size) 465 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 466 fail6: 467 EFSYS_PROBE(fail6); 468 fail5: 469 EFSYS_PROBE(fail5); 470 fail4: 471 EFSYS_PROBE(fail4); 472 fail3: 473 EFSYS_PROBE(fail3); 474 fail2: 475 EFSYS_PROBE(fail2); 476 fail1: 477 EFSYS_PROBE1(fail1, efx_rc_t, rc); 478 479 return (rc); 480 } 481 482 efx_rc_t 483 efx_bootcfg_write( 484 __in efx_nic_t *enp, 485 __in_bcount(size) uint8_t *data, 486 __in size_t size) 487 { 488 uint8_t *partn_data; 489 uint8_t checksum; 490 size_t partn_length; 491 size_t sector_length; 492 size_t sector_offset; 493 size_t used_bytes; 494 efx_rc_t rc; 495 uint32_t sector_number; 496 497 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 498 sector_number = enp->en_nic_cfg.enc_pf; 499 #else 500 sector_number = 0; 501 #endif 502 503 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 504 if (rc != 0) 505 goto fail1; 506 507 /* The bootcfg sector may be stored in a (larger) shared partition */ 508 rc = efx_bootcfg_sector_info(enp, sector_number, 509 NULL, §or_offset, §or_length); 510 if (rc != 0) 511 goto fail2; 512 513 if (sector_length > BOOTCFG_MAX_SIZE) 514 sector_length = BOOTCFG_MAX_SIZE; 515 516 if (sector_offset + sector_length > partn_length) { 517 /* Partition is too small */ 518 rc = EFBIG; 519 goto fail3; 520 } 521 522 if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0) 523 goto fail4; 524 525 /* The caller *must* terminate their block with a DHCP_END character */ 526 if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) { 527 /* Block too short or DHCP_END missing */ 528 rc = ENOENT; 529 goto fail5; 530 } 531 532 /* Check that the hardware has support for this much data */ 533 if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) { 534 rc = ENOSPC; 535 goto fail6; 536 } 537 538 /* 539 * If the BOOTCFG sector is stored in a shared partition, then we must 540 * read the whole partition and insert the updated bootcfg sector at the 541 * correct offset. 542 */ 543 EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data); 544 if (partn_data == NULL) { 545 rc = ENOMEM; 546 goto fail7; 547 } 548 549 rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 550 if (rc != 0) 551 goto fail8; 552 553 /* Read the entire partition */ 554 rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0, 555 (caddr_t)partn_data, partn_length); 556 if (rc != 0) 557 goto fail9; 558 559 /* 560 * Insert the BOOTCFG sector into the partition, Zero out all data after 561 * the DHCP_END tag, and adjust the checksum. 562 */ 563 (void) memset(partn_data + sector_offset, 0x0, sector_length); 564 (void) memcpy(partn_data + sector_offset, data, used_bytes); 565 566 checksum = efx_bootcfg_csum(enp, data, used_bytes); 567 partn_data[sector_offset] -= checksum; 568 569 if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 570 goto fail10; 571 572 if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 573 0, (caddr_t)partn_data, partn_length)) != 0) 574 goto fail11; 575 576 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 577 goto fail12; 578 579 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 580 581 return (0); 582 583 fail12: 584 EFSYS_PROBE(fail12); 585 fail11: 586 EFSYS_PROBE(fail11); 587 fail10: 588 EFSYS_PROBE(fail10); 589 fail9: 590 EFSYS_PROBE(fail9); 591 592 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 593 fail8: 594 EFSYS_PROBE(fail8); 595 596 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 597 fail7: 598 EFSYS_PROBE(fail7); 599 fail6: 600 EFSYS_PROBE(fail6); 601 fail5: 602 EFSYS_PROBE(fail5); 603 fail4: 604 EFSYS_PROBE(fail4); 605 fail3: 606 EFSYS_PROBE(fail3); 607 fail2: 608 EFSYS_PROBE(fail2); 609 fail1: 610 EFSYS_PROBE1(fail1, efx_rc_t, rc); 611 612 return (rc); 613 } 614 615 #endif /* EFSYS_OPT_BOOTCFG */ 616