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