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