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_OPT_HAS_VALUE(opt) \ 52 (((opt) > EFX_DHCP_PAD) && ((opt) < EFX_DHCP_END)) 53 54 #define DHCP_MAX_VALUE 255 55 56 #define DHCP_ENCAPSULATOR(encap_opt) ((encap_opt) >> 8) 57 #define DHCP_ENCAPSULATED(encap_opt) ((encap_opt) & 0xff) 58 #define DHCP_IS_ENCAP_OPT(opt) DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATOR(opt)) 59 60 typedef struct efx_dhcp_tag_hdr_s { 61 uint8_t tag; 62 uint8_t length; 63 } efx_dhcp_tag_hdr_t; 64 65 /* 66 * Length calculations for tags with value field. PAD and END 67 * have a fixed length of 1, with no length or value field. 68 */ 69 #define DHCP_FULL_TAG_LENGTH(hdr) \ 70 (sizeof (efx_dhcp_tag_hdr_t) + (hdr)->length) 71 72 #define DHCP_NEXT_TAG(hdr) \ 73 ((efx_dhcp_tag_hdr_t *)(((uint8_t *)(hdr)) + \ 74 DHCP_FULL_TAG_LENGTH((hdr)))) 75 76 #define DHCP_CALC_TAG_LENGTH(payload_len) \ 77 ((payload_len) + sizeof (efx_dhcp_tag_hdr_t)) 78 79 80 /* Report the layout of bootcfg sectors in NVRAM partition. */ 81 __checkReturn efx_rc_t 82 efx_bootcfg_sector_info( 83 __in efx_nic_t *enp, 84 __in uint32_t pf, 85 __out_opt uint32_t *sector_countp, 86 __out size_t *offsetp, 87 __out size_t *max_sizep) 88 { 89 uint32_t count; 90 size_t max_size; 91 size_t offset; 92 int rc; 93 94 switch (enp->en_family) { 95 #if EFSYS_OPT_SIENA 96 case EFX_FAMILY_SIENA: 97 max_size = BOOTCFG_MAX_SIZE; 98 offset = 0; 99 count = 1; 100 break; 101 #endif /* EFSYS_OPT_SIENA */ 102 103 #if EFSYS_OPT_HUNTINGTON 104 case EFX_FAMILY_HUNTINGTON: 105 max_size = BOOTCFG_MAX_SIZE; 106 offset = 0; 107 count = 1; 108 break; 109 #endif /* EFSYS_OPT_HUNTINGTON */ 110 111 #if EFSYS_OPT_MEDFORD 112 case EFX_FAMILY_MEDFORD: { 113 /* Shared partition (array indexed by PF) */ 114 max_size = BOOTCFG_PER_PF; 115 count = BOOTCFG_PF_COUNT; 116 if (pf >= count) { 117 rc = EINVAL; 118 goto fail2; 119 } 120 offset = max_size * pf; 121 break; 122 } 123 #endif /* EFSYS_OPT_MEDFORD */ 124 125 #if EFSYS_OPT_MEDFORD2 126 case EFX_FAMILY_MEDFORD2: { 127 /* Shared partition (array indexed by PF) */ 128 max_size = BOOTCFG_PER_PF; 129 count = BOOTCFG_PF_COUNT; 130 if (pf >= count) { 131 rc = EINVAL; 132 goto fail3; 133 } 134 offset = max_size * pf; 135 break; 136 } 137 #endif /* EFSYS_OPT_MEDFORD2 */ 138 139 default: 140 EFSYS_ASSERT(0); 141 rc = ENOTSUP; 142 goto fail1; 143 } 144 EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE); 145 146 if (sector_countp != NULL) 147 *sector_countp = count; 148 *offsetp = offset; 149 *max_sizep = max_size; 150 151 return (0); 152 153 #if EFSYS_OPT_MEDFORD2 154 fail3: 155 EFSYS_PROBE(fail3); 156 #endif 157 #if EFSYS_OPT_MEDFORD 158 fail2: 159 EFSYS_PROBE(fail2); 160 #endif 161 fail1: 162 EFSYS_PROBE1(fail1, efx_rc_t, rc); 163 return (rc); 164 } 165 166 167 __checkReturn uint8_t 168 efx_dhcp_csum( 169 __in_bcount(size) uint8_t const *data, 170 __in size_t size) 171 { 172 unsigned int pos; 173 uint8_t checksum = 0; 174 175 for (pos = 0; pos < size; pos++) 176 checksum += data[pos]; 177 return (checksum); 178 } 179 180 __checkReturn efx_rc_t 181 efx_dhcp_verify( 182 __in_bcount(size) uint8_t const *data, 183 __in size_t size, 184 __out_opt size_t *usedp) 185 { 186 size_t offset = 0; 187 size_t used = 0; 188 efx_rc_t rc; 189 190 /* Start parsing tags immediately after the checksum */ 191 for (offset = 1; offset < size; ) { 192 uint8_t tag; 193 uint8_t length; 194 195 /* Consume tag */ 196 tag = data[offset]; 197 if (tag == EFX_DHCP_END) { 198 offset++; 199 used = offset; 200 break; 201 } 202 if (tag == EFX_DHCP_PAD) { 203 offset++; 204 continue; 205 } 206 207 /* Consume length */ 208 if (offset + 1 >= size) { 209 rc = ENOSPC; 210 goto fail1; 211 } 212 length = data[offset + 1]; 213 214 /* Consume *length */ 215 if (offset + 1 + length >= size) { 216 rc = ENOSPC; 217 goto fail2; 218 } 219 220 offset += 2 + length; 221 used = offset; 222 } 223 224 /* Checksum the entire sector, including bytes after any EFX_DHCP_END */ 225 if (efx_dhcp_csum(data, size) != 0) { 226 rc = EINVAL; 227 goto fail3; 228 } 229 230 if (usedp != NULL) 231 *usedp = used; 232 233 return (0); 234 235 fail3: 236 EFSYS_PROBE(fail3); 237 fail2: 238 EFSYS_PROBE(fail2); 239 fail1: 240 EFSYS_PROBE1(fail1, efx_rc_t, rc); 241 242 return (rc); 243 } 244 245 /* 246 * Walk the entire tag set looking for option. The sought option may be 247 * encapsulated. ENOENT indicates the walk completed without finding the 248 * option. If we run out of buffer during the walk the function will return 249 * ENOSPC. 250 */ 251 static efx_rc_t 252 efx_dhcp_walk_tags( 253 __deref_inout uint8_t **tagpp, 254 __inout size_t *buffer_sizep, 255 __in uint16_t opt) 256 { 257 efx_rc_t rc = 0; 258 boolean_t is_encap = B_FALSE; 259 260 if (DHCP_IS_ENCAP_OPT(opt)) { 261 /* 262 * Look for the encapsulator and, if found, limit ourselves 263 * to its payload. If it's not found then the entire tag 264 * cannot be found, so the encapsulated opt search is 265 * skipped. 266 */ 267 rc = efx_dhcp_walk_tags(tagpp, buffer_sizep, 268 DHCP_ENCAPSULATOR(opt)); 269 if (rc == 0) { 270 *buffer_sizep = ((efx_dhcp_tag_hdr_t *)*tagpp)->length; 271 (*tagpp) += sizeof (efx_dhcp_tag_hdr_t); 272 } 273 opt = DHCP_ENCAPSULATED(opt); 274 is_encap = B_TRUE; 275 } 276 277 EFSYS_ASSERT(!DHCP_IS_ENCAP_OPT(opt)); 278 279 while (rc == 0) { 280 size_t size; 281 282 if (*buffer_sizep == 0) { 283 rc = ENOSPC; 284 goto fail1; 285 } 286 287 if (DHCP_ENCAPSULATED(**tagpp) == opt) 288 break; 289 290 if ((**tagpp) == EFX_DHCP_END) { 291 rc = ENOENT; 292 break; 293 } else if ((**tagpp) == EFX_DHCP_PAD) { 294 size = 1; 295 } else { 296 if (*buffer_sizep < sizeof (efx_dhcp_tag_hdr_t)) { 297 rc = ENOSPC; 298 goto fail2; 299 } 300 301 size = 302 DHCP_FULL_TAG_LENGTH((efx_dhcp_tag_hdr_t *)*tagpp); 303 } 304 305 if (size > *buffer_sizep) { 306 rc = ENOSPC; 307 goto fail3; 308 } 309 310 (*tagpp) += size; 311 (*buffer_sizep) -= size; 312 313 if ((*buffer_sizep == 0) && is_encap) { 314 /* Search within encapulator tag finished */ 315 rc = ENOENT; 316 break; 317 } 318 } 319 320 /* 321 * Returns 0 if found otherwise ENOENT indicating search finished 322 * correctly 323 */ 324 return (rc); 325 326 fail3: 327 EFSYS_PROBE(fail3); 328 fail2: 329 EFSYS_PROBE(fail2); 330 fail1: 331 EFSYS_PROBE1(fail1, efx_rc_t, rc); 332 333 return (rc); 334 } 335 336 /* 337 * Locate value buffer for option in the given buffer. 338 * Returns 0 if found, ENOENT indicating search finished 339 * correctly, otherwise search failed before completion. 340 */ 341 __checkReturn efx_rc_t 342 efx_dhcp_find_tag( 343 __in_bcount(buffer_length) uint8_t *bufferp, 344 __in size_t buffer_length, 345 __in uint16_t opt, 346 __deref_out uint8_t **valuepp, 347 __out size_t *value_lengthp) 348 { 349 efx_rc_t rc; 350 uint8_t *tagp = bufferp; 351 size_t len = buffer_length; 352 353 rc = efx_dhcp_walk_tags(&tagp, &len, opt); 354 if (rc == 0) { 355 efx_dhcp_tag_hdr_t *hdrp; 356 357 hdrp = (efx_dhcp_tag_hdr_t *)tagp; 358 *valuepp = (uint8_t *)(&hdrp[1]); 359 *value_lengthp = hdrp->length; 360 } else if (rc != ENOENT) { 361 goto fail1; 362 } 363 364 return (rc); 365 366 fail1: 367 EFSYS_PROBE1(fail1, efx_rc_t, rc); 368 369 return (rc); 370 } 371 372 /* 373 * Locate the end tag in the given buffer. 374 * Returns 0 if found, ENOENT indicating search finished 375 * correctly but end tag was not found; otherwise search 376 * failed before completion. 377 */ 378 __checkReturn efx_rc_t 379 efx_dhcp_find_end( 380 __in_bcount(buffer_length) uint8_t *bufferp, 381 __in size_t buffer_length, 382 __deref_out uint8_t **endpp) 383 { 384 efx_rc_t rc; 385 uint8_t *endp = bufferp; 386 size_t len = buffer_length; 387 388 rc = efx_dhcp_walk_tags(&endp, &len, EFX_DHCP_END); 389 if (rc == 0) 390 *endpp = endp; 391 else if (rc != ENOENT) 392 goto fail1; 393 394 return (rc); 395 396 fail1: 397 EFSYS_PROBE1(fail1, efx_rc_t, rc); 398 399 return (rc); 400 } 401 402 403 /* 404 * Delete the given tag from anywhere in the buffer. Copes with 405 * encapsulated tags, and updates or deletes the encapsulating opt as 406 * necessary. 407 */ 408 __checkReturn efx_rc_t 409 efx_dhcp_delete_tag( 410 __inout_bcount(buffer_length) uint8_t *bufferp, 411 __in size_t buffer_length, 412 __in uint16_t opt) 413 { 414 efx_rc_t rc; 415 efx_dhcp_tag_hdr_t *hdrp; 416 size_t len; 417 uint8_t *startp; 418 uint8_t *endp; 419 420 len = buffer_length; 421 startp = bufferp; 422 423 if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) { 424 rc = EINVAL; 425 goto fail1; 426 } 427 428 rc = efx_dhcp_walk_tags(&startp, &len, opt); 429 if (rc != 0) 430 goto fail1; 431 432 hdrp = (efx_dhcp_tag_hdr_t *)startp; 433 434 if (DHCP_IS_ENCAP_OPT(opt)) { 435 uint8_t tag_length = DHCP_FULL_TAG_LENGTH(hdrp); 436 uint8_t *encapp = bufferp; 437 efx_dhcp_tag_hdr_t *encap_hdrp; 438 439 len = buffer_length; 440 rc = efx_dhcp_walk_tags(&encapp, &len, 441 DHCP_ENCAPSULATOR(opt)); 442 if (rc != 0) 443 goto fail2; 444 445 encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp; 446 if (encap_hdrp->length > tag_length) { 447 encap_hdrp->length = (uint8_t)( 448 (size_t)encap_hdrp->length - tag_length); 449 } else { 450 /* delete the encapsulating tag */ 451 hdrp = encap_hdrp; 452 } 453 } 454 455 startp = (uint8_t *)hdrp; 456 endp = (uint8_t *)DHCP_NEXT_TAG(hdrp); 457 458 if (startp < bufferp) { 459 rc = EINVAL; 460 goto fail3; 461 } 462 463 if (endp > &bufferp[buffer_length]) { 464 rc = EINVAL; 465 goto fail4; 466 } 467 468 memmove(startp, endp, 469 buffer_length - (endp - bufferp)); 470 471 return (0); 472 473 fail4: 474 EFSYS_PROBE(fail4); 475 fail3: 476 EFSYS_PROBE(fail3); 477 fail2: 478 EFSYS_PROBE(fail2); 479 fail1: 480 EFSYS_PROBE1(fail1, efx_rc_t, rc); 481 482 return (rc); 483 } 484 485 /* 486 * Write the tag header into write_pointp and optionally copies the payload 487 * into the space following. 488 */ 489 static void 490 efx_dhcp_write_tag( 491 __in uint8_t *write_pointp, 492 __in uint16_t opt, 493 __in_bcount_opt(value_length) 494 uint8_t *valuep, 495 __in size_t value_length) 496 { 497 efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp; 498 hdrp->tag = DHCP_ENCAPSULATED(opt); 499 hdrp->length = (uint8_t)value_length; 500 if ((value_length > 0) && (valuep != NULL)) 501 memcpy(&hdrp[1], valuep, value_length); 502 } 503 504 /* 505 * Add the given tag to the end of the buffer. Copes with creating an 506 * encapsulated tag, and updates or creates the encapsulating opt as 507 * necessary. 508 */ 509 __checkReturn efx_rc_t 510 efx_dhcp_add_tag( 511 __inout_bcount(buffer_length) uint8_t *bufferp, 512 __in size_t buffer_length, 513 __in uint16_t opt, 514 __in_bcount_opt(value_length) uint8_t *valuep, 515 __in size_t value_length) 516 { 517 efx_rc_t rc; 518 efx_dhcp_tag_hdr_t *encap_hdrp = NULL; 519 uint8_t *insert_pointp = NULL; 520 uint8_t *endp; 521 size_t available_space; 522 size_t added_length; 523 size_t search_size; 524 uint8_t *searchp; 525 526 if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) { 527 rc = EINVAL; 528 goto fail1; 529 } 530 531 if (value_length > DHCP_MAX_VALUE) { 532 rc = EINVAL; 533 goto fail2; 534 } 535 536 if ((value_length > 0) && (valuep == NULL)) { 537 rc = EINVAL; 538 goto fail3; 539 } 540 541 endp = bufferp; 542 available_space = buffer_length; 543 rc = efx_dhcp_walk_tags(&endp, &available_space, EFX_DHCP_END); 544 if (rc != 0) 545 goto fail4; 546 547 searchp = bufferp; 548 search_size = buffer_length; 549 if (DHCP_IS_ENCAP_OPT(opt)) { 550 rc = efx_dhcp_walk_tags(&searchp, &search_size, 551 DHCP_ENCAPSULATOR(opt)); 552 if (rc == 0) { 553 encap_hdrp = (efx_dhcp_tag_hdr_t *)searchp; 554 555 /* Check encapsulated tag is not present */ 556 search_size = encap_hdrp->length; 557 rc = efx_dhcp_walk_tags(&searchp, &search_size, 558 opt); 559 if (rc != ENOENT) { 560 rc = EINVAL; 561 goto fail5; 562 } 563 564 /* Check encapsulator will not overflow */ 565 if (((size_t)encap_hdrp->length + 566 DHCP_CALC_TAG_LENGTH(value_length)) > 567 DHCP_MAX_VALUE) { 568 rc = E2BIG; 569 goto fail6; 570 } 571 572 /* Insert at start of existing encapsulator */ 573 insert_pointp = (uint8_t *)&encap_hdrp[1]; 574 opt = DHCP_ENCAPSULATED(opt); 575 } else if (rc == ENOENT) { 576 encap_hdrp = NULL; 577 } else { 578 goto fail7; 579 } 580 } else { 581 /* Check unencapsulated tag is not present */ 582 rc = efx_dhcp_walk_tags(&searchp, &search_size, 583 opt); 584 if (rc != ENOENT) { 585 rc = EINVAL; 586 goto fail8; 587 } 588 } 589 590 if (insert_pointp == NULL) { 591 /* Insert at end of existing tags */ 592 insert_pointp = endp; 593 } 594 595 /* Includes the new encapsulator tag hdr if required */ 596 added_length = DHCP_CALC_TAG_LENGTH(value_length) + 597 (DHCP_IS_ENCAP_OPT(opt) ? sizeof (efx_dhcp_tag_hdr_t) : 0); 598 599 if (available_space <= added_length) { 600 rc = ENOMEM; 601 goto fail9; 602 } 603 604 memmove(insert_pointp + added_length, insert_pointp, 605 available_space - added_length); 606 607 if (DHCP_IS_ENCAP_OPT(opt)) { 608 /* Create new encapsulator header */ 609 added_length -= sizeof (efx_dhcp_tag_hdr_t); 610 efx_dhcp_write_tag(insert_pointp, 611 DHCP_ENCAPSULATOR(opt), NULL, added_length); 612 insert_pointp += sizeof (efx_dhcp_tag_hdr_t); 613 } else if (encap_hdrp) 614 /* Modify existing encapsulator header */ 615 encap_hdrp->length += 616 ((uint8_t)DHCP_CALC_TAG_LENGTH(value_length)); 617 618 efx_dhcp_write_tag(insert_pointp, opt, valuep, value_length); 619 620 return (0); 621 622 fail9: 623 EFSYS_PROBE(fail9); 624 fail8: 625 EFSYS_PROBE(fail8); 626 fail7: 627 EFSYS_PROBE(fail7); 628 fail6: 629 EFSYS_PROBE(fail6); 630 fail5: 631 EFSYS_PROBE(fail5); 632 fail4: 633 EFSYS_PROBE(fail4); 634 fail3: 635 EFSYS_PROBE(fail3); 636 fail2: 637 EFSYS_PROBE(fail2); 638 fail1: 639 EFSYS_PROBE1(fail1, efx_rc_t, rc); 640 641 return (rc); 642 } 643 644 /* 645 * Update an existing tag to the new value. Copes with encapsulated 646 * tags, and updates the encapsulating opt as necessary. 647 */ 648 __checkReturn efx_rc_t 649 efx_dhcp_update_tag( 650 __inout_bcount(buffer_length) uint8_t *bufferp, 651 __in size_t buffer_length, 652 __in uint16_t opt, 653 __in uint8_t *value_locationp, 654 __in_bcount_opt(value_length) uint8_t *valuep, 655 __in size_t value_length) 656 { 657 efx_rc_t rc; 658 uint8_t *write_pointp = value_locationp - sizeof (efx_dhcp_tag_hdr_t); 659 efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp; 660 efx_dhcp_tag_hdr_t *encap_hdrp = NULL; 661 size_t old_length; 662 663 if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) { 664 rc = EINVAL; 665 goto fail1; 666 } 667 668 if (value_length > DHCP_MAX_VALUE) { 669 rc = EINVAL; 670 goto fail2; 671 } 672 673 if ((value_length > 0) && (valuep == NULL)) { 674 rc = EINVAL; 675 goto fail3; 676 } 677 678 old_length = hdrp->length; 679 680 if (old_length < value_length) { 681 uint8_t *endp = bufferp; 682 size_t available_space = buffer_length; 683 684 rc = efx_dhcp_walk_tags(&endp, &available_space, 685 EFX_DHCP_END); 686 if (rc != 0) 687 goto fail4; 688 689 if (available_space < (value_length - old_length)) { 690 rc = EINVAL; 691 goto fail5; 692 } 693 } 694 695 if (DHCP_IS_ENCAP_OPT(opt)) { 696 uint8_t *encapp = bufferp; 697 size_t following_encap = buffer_length; 698 size_t new_length; 699 700 rc = efx_dhcp_walk_tags(&encapp, &following_encap, 701 DHCP_ENCAPSULATOR(opt)); 702 if (rc != 0) 703 goto fail6; 704 705 encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp; 706 707 new_length = ((size_t)encap_hdrp->length + 708 value_length - old_length); 709 /* Check encapsulator will not overflow */ 710 if (new_length > DHCP_MAX_VALUE) { 711 rc = E2BIG; 712 goto fail7; 713 } 714 715 encap_hdrp->length = (uint8_t)new_length; 716 } 717 718 /* 719 * Move the following data up/down to accommodate the new payload 720 * length. 721 */ 722 if (old_length != value_length) { 723 uint8_t *destp = (uint8_t *)DHCP_NEXT_TAG(hdrp) + 724 value_length - old_length; 725 size_t count = &bufferp[buffer_length] - 726 (uint8_t *)DHCP_NEXT_TAG(hdrp); 727 728 memmove(destp, DHCP_NEXT_TAG(hdrp), count); 729 } 730 731 EFSYS_ASSERT(hdrp->tag == DHCP_ENCAPSULATED(opt)); 732 efx_dhcp_write_tag(write_pointp, opt, valuep, value_length); 733 734 return (0); 735 736 fail7: 737 EFSYS_PROBE(fail7); 738 fail6: 739 EFSYS_PROBE(fail6); 740 fail5: 741 EFSYS_PROBE(fail5); 742 fail4: 743 EFSYS_PROBE(fail4); 744 fail3: 745 EFSYS_PROBE(fail3); 746 fail2: 747 EFSYS_PROBE(fail2); 748 fail1: 749 EFSYS_PROBE1(fail1, efx_rc_t, rc); 750 751 return (rc); 752 } 753 754 755 /* 756 * Copy bootcfg sector data to a target buffer which may differ in size. 757 * Optionally corrects format errors in source buffer. 758 */ 759 efx_rc_t 760 efx_bootcfg_copy_sector( 761 __in efx_nic_t *enp, 762 __inout_bcount(sector_length) 763 uint8_t *sector, 764 __in size_t sector_length, 765 __out_bcount(data_size) uint8_t *data, 766 __in size_t data_size, 767 __in boolean_t handle_format_errors) 768 { 769 _NOTE(ARGUNUSED(enp)) 770 771 size_t used_bytes; 772 efx_rc_t rc; 773 774 /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */ 775 if (data_size < 2) { 776 rc = ENOSPC; 777 goto fail1; 778 } 779 780 /* Verify that the area is correctly formatted and checksummed */ 781 rc = efx_dhcp_verify(sector, sector_length, 782 &used_bytes); 783 784 if (!handle_format_errors) { 785 if (rc != 0) 786 goto fail2; 787 788 if ((used_bytes < 2) || 789 (sector[used_bytes - 1] != EFX_DHCP_END)) { 790 /* Block too short, or EFX_DHCP_END missing */ 791 rc = ENOENT; 792 goto fail3; 793 } 794 } 795 796 /* Synthesize empty format on verification failure */ 797 if (rc != 0 || used_bytes == 0) { 798 sector[0] = 0; 799 sector[1] = EFX_DHCP_END; 800 used_bytes = 2; 801 } 802 EFSYS_ASSERT(used_bytes >= 2); /* checksum and EFX_DHCP_END */ 803 EFSYS_ASSERT(used_bytes <= sector_length); 804 EFSYS_ASSERT(sector_length >= 2); 805 806 /* 807 * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END 808 * character. Modify the returned payload so it does. 809 * Reinitialise the sector if there isn't room for the character. 810 */ 811 if (sector[used_bytes - 1] != EFX_DHCP_END) { 812 if (used_bytes >= sector_length) { 813 sector[0] = 0; 814 used_bytes = 1; 815 } 816 sector[used_bytes] = EFX_DHCP_END; 817 ++used_bytes; 818 } 819 820 /* 821 * Verify that the target buffer is large enough for the 822 * entire used bootcfg area, then copy into the target buffer. 823 */ 824 if (used_bytes > data_size) { 825 rc = ENOSPC; 826 goto fail4; 827 } 828 829 data[0] = 0; /* checksum, updated below */ 830 831 /* Copy all after the checksum to the target buffer */ 832 memcpy(data + 1, sector + 1, used_bytes - 1); 833 834 /* Zero out the unused portion of the target buffer */ 835 if (used_bytes < data_size) 836 (void) memset(data + used_bytes, 0, data_size - used_bytes); 837 838 /* 839 * The checksum includes trailing data after any EFX_DHCP_END 840 * character, which we've just modified (by truncation or appending 841 * EFX_DHCP_END). 842 */ 843 data[0] -= efx_dhcp_csum(data, data_size); 844 845 return (0); 846 847 fail4: 848 EFSYS_PROBE(fail4); 849 fail3: 850 EFSYS_PROBE(fail3); 851 fail2: 852 EFSYS_PROBE(fail2); 853 fail1: 854 EFSYS_PROBE1(fail1, efx_rc_t, rc); 855 856 return (rc); 857 } 858 859 efx_rc_t 860 efx_bootcfg_read( 861 __in efx_nic_t *enp, 862 __out_bcount(size) uint8_t *data, 863 __in size_t size) 864 { 865 uint8_t *payload = NULL; 866 size_t used_bytes; 867 size_t partn_length; 868 size_t sector_length; 869 size_t sector_offset; 870 efx_rc_t rc; 871 uint32_t sector_number; 872 873 /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */ 874 if (size < 2) { 875 rc = ENOSPC; 876 goto fail1; 877 } 878 879 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 880 sector_number = enp->en_nic_cfg.enc_pf; 881 #else 882 sector_number = 0; 883 #endif 884 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 885 if (rc != 0) 886 goto fail2; 887 888 /* The bootcfg sector may be stored in a (larger) shared partition */ 889 rc = efx_bootcfg_sector_info(enp, sector_number, 890 NULL, §or_offset, §or_length); 891 if (rc != 0) 892 goto fail3; 893 894 if (sector_length < 2) { 895 rc = EINVAL; 896 goto fail4; 897 } 898 899 if (sector_length > BOOTCFG_MAX_SIZE) 900 sector_length = BOOTCFG_MAX_SIZE; 901 902 if (sector_offset + sector_length > partn_length) { 903 /* Partition is too small */ 904 rc = EFBIG; 905 goto fail5; 906 } 907 908 /* 909 * We need to read the entire BOOTCFG sector to ensure we read all 910 * tags, because legacy bootcfg sectors are not guaranteed to end 911 * with an EFX_DHCP_END character. If the user hasn't supplied a 912 * sufficiently large buffer then use our own buffer. 913 */ 914 if (sector_length > size) { 915 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload); 916 if (payload == NULL) { 917 rc = ENOMEM; 918 goto fail6; 919 } 920 } else 921 payload = (uint8_t *)data; 922 923 if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 924 goto fail7; 925 926 if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 927 sector_offset, (caddr_t)payload, sector_length)) != 0) { 928 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 929 goto fail8; 930 } 931 932 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 933 goto fail9; 934 935 /* Verify that the area is correctly formatted and checksummed */ 936 rc = efx_dhcp_verify(payload, sector_length, 937 &used_bytes); 938 if (rc != 0 || used_bytes == 0) { 939 payload[0] = 0; 940 payload[1] = EFX_DHCP_END; 941 used_bytes = 2; 942 } 943 944 EFSYS_ASSERT(used_bytes >= 2); /* checksum and EFX_DHCP_END */ 945 EFSYS_ASSERT(used_bytes <= sector_length); 946 947 /* 948 * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END 949 * character. Modify the returned payload so it does. 950 * BOOTCFG_MAX_SIZE is by definition large enough for any valid 951 * (per-port) bootcfg sector, so reinitialise the sector if there 952 * isn't room for the character. 953 */ 954 if (payload[used_bytes - 1] != EFX_DHCP_END) { 955 if (used_bytes >= sector_length) 956 used_bytes = 1; 957 958 payload[used_bytes] = EFX_DHCP_END; 959 ++used_bytes; 960 } 961 962 /* 963 * Verify that the user supplied buffer is large enough for the 964 * entire used bootcfg area, then copy into the user supplied buffer. 965 */ 966 if (used_bytes > size) { 967 rc = ENOSPC; 968 goto fail10; 969 } 970 971 data[0] = 0; /* checksum, updated below */ 972 973 if (sector_length > size) { 974 /* Copy all after the checksum to the target buffer */ 975 memcpy(data + 1, payload + 1, used_bytes - 1); 976 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 977 } 978 979 /* Zero out the unused portion of the user buffer */ 980 if (used_bytes < size) 981 (void) memset(data + used_bytes, 0, size - used_bytes); 982 983 /* 984 * The checksum includes trailing data after any EFX_DHCP_END character, 985 * which we've just modified (by truncation or appending EFX_DHCP_END). 986 */ 987 data[0] -= efx_dhcp_csum(data, size); 988 989 return (0); 990 991 fail10: 992 EFSYS_PROBE(fail10); 993 fail9: 994 EFSYS_PROBE(fail9); 995 fail8: 996 EFSYS_PROBE(fail8); 997 fail7: 998 EFSYS_PROBE(fail7); 999 if (sector_length > size) 1000 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); 1001 fail6: 1002 EFSYS_PROBE(fail6); 1003 fail5: 1004 EFSYS_PROBE(fail5); 1005 fail4: 1006 EFSYS_PROBE(fail4); 1007 fail3: 1008 EFSYS_PROBE(fail3); 1009 fail2: 1010 EFSYS_PROBE(fail2); 1011 fail1: 1012 EFSYS_PROBE1(fail1, efx_rc_t, rc); 1013 1014 return (rc); 1015 } 1016 1017 efx_rc_t 1018 efx_bootcfg_write( 1019 __in efx_nic_t *enp, 1020 __in_bcount(size) uint8_t *data, 1021 __in size_t size) 1022 { 1023 uint8_t *partn_data; 1024 uint8_t checksum; 1025 size_t partn_length; 1026 size_t sector_length; 1027 size_t sector_offset; 1028 size_t used_bytes; 1029 efx_rc_t rc; 1030 uint32_t sector_number; 1031 1032 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 1033 sector_number = enp->en_nic_cfg.enc_pf; 1034 #else 1035 sector_number = 0; 1036 #endif 1037 1038 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); 1039 if (rc != 0) 1040 goto fail1; 1041 1042 /* The bootcfg sector may be stored in a (larger) shared partition */ 1043 rc = efx_bootcfg_sector_info(enp, sector_number, 1044 NULL, §or_offset, §or_length); 1045 if (rc != 0) 1046 goto fail2; 1047 1048 if (sector_length > BOOTCFG_MAX_SIZE) 1049 sector_length = BOOTCFG_MAX_SIZE; 1050 1051 if (sector_offset + sector_length > partn_length) { 1052 /* Partition is too small */ 1053 rc = EFBIG; 1054 goto fail3; 1055 } 1056 1057 if ((rc = efx_dhcp_verify(data, size, &used_bytes)) != 0) 1058 goto fail4; 1059 1060 /* 1061 * The caller *must* terminate their block with a EFX_DHCP_END 1062 * character 1063 */ 1064 if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != 1065 EFX_DHCP_END)) { 1066 /* Block too short or EFX_DHCP_END missing */ 1067 rc = ENOENT; 1068 goto fail5; 1069 } 1070 1071 /* Check that the hardware has support for this much data */ 1072 if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) { 1073 rc = ENOSPC; 1074 goto fail6; 1075 } 1076 1077 /* 1078 * If the BOOTCFG sector is stored in a shared partition, then we must 1079 * read the whole partition and insert the updated bootcfg sector at the 1080 * correct offset. 1081 */ 1082 EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data); 1083 if (partn_data == NULL) { 1084 rc = ENOMEM; 1085 goto fail7; 1086 } 1087 1088 rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 1089 if (rc != 0) 1090 goto fail8; 1091 1092 /* Read the entire partition */ 1093 rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0, 1094 (caddr_t)partn_data, partn_length); 1095 if (rc != 0) 1096 goto fail9; 1097 1098 /* 1099 * Insert the BOOTCFG sector into the partition, Zero out all data 1100 * after the EFX_DHCP_END tag, and adjust the checksum. 1101 */ 1102 (void) memset(partn_data + sector_offset, 0x0, sector_length); 1103 (void) memcpy(partn_data + sector_offset, data, used_bytes); 1104 1105 checksum = efx_dhcp_csum(data, used_bytes); 1106 partn_data[sector_offset] -= checksum; 1107 1108 if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) 1109 goto fail10; 1110 1111 if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 1112 0, (caddr_t)partn_data, partn_length)) != 0) 1113 goto fail11; 1114 1115 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) 1116 goto fail12; 1117 1118 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 1119 1120 return (0); 1121 1122 fail12: 1123 EFSYS_PROBE(fail12); 1124 fail11: 1125 EFSYS_PROBE(fail11); 1126 fail10: 1127 EFSYS_PROBE(fail10); 1128 fail9: 1129 EFSYS_PROBE(fail9); 1130 1131 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL); 1132 fail8: 1133 EFSYS_PROBE(fail8); 1134 1135 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); 1136 fail7: 1137 EFSYS_PROBE(fail7); 1138 fail6: 1139 EFSYS_PROBE(fail6); 1140 fail5: 1141 EFSYS_PROBE(fail5); 1142 fail4: 1143 EFSYS_PROBE(fail4); 1144 fail3: 1145 EFSYS_PROBE(fail3); 1146 fail2: 1147 EFSYS_PROBE(fail2); 1148 fail1: 1149 EFSYS_PROBE1(fail1, efx_rc_t, rc); 1150 1151 return (rc); 1152 } 1153 1154 #endif /* EFSYS_OPT_BOOTCFG */ 1155