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_VPD 40 41 #define TAG_TYPE_LBN 7 42 #define TAG_TYPE_WIDTH 1 43 #define TAG_TYPE_LARGE_ITEM_DECODE 1 44 #define TAG_TYPE_SMALL_ITEM_DECODE 0 45 46 #define TAG_SMALL_ITEM_NAME_LBN 3 47 #define TAG_SMALL_ITEM_NAME_WIDTH 4 48 #define TAG_SMALL_ITEM_SIZE_LBN 0 49 #define TAG_SMALL_ITEM_SIZE_WIDTH 3 50 51 #define TAG_LARGE_ITEM_NAME_LBN 0 52 #define TAG_LARGE_ITEM_NAME_WIDTH 7 53 54 #define TAG_NAME_END_DECODE 0x0f 55 #define TAG_NAME_ID_STRING_DECODE 0x02 56 #define TAG_NAME_VPD_R_DECODE 0x10 57 #define TAG_NAME_VPD_W_DECODE 0x11 58 59 #if EFSYS_OPT_SIENA 60 61 static const efx_vpd_ops_t __efx_vpd_siena_ops = { 62 siena_vpd_init, /* evpdo_init */ 63 siena_vpd_size, /* evpdo_size */ 64 siena_vpd_read, /* evpdo_read */ 65 siena_vpd_verify, /* evpdo_verify */ 66 siena_vpd_reinit, /* evpdo_reinit */ 67 siena_vpd_get, /* evpdo_get */ 68 siena_vpd_set, /* evpdo_set */ 69 siena_vpd_next, /* evpdo_next */ 70 siena_vpd_write, /* evpdo_write */ 71 siena_vpd_fini, /* evpdo_fini */ 72 }; 73 74 #endif /* EFSYS_OPT_SIENA */ 75 76 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD 77 78 static const efx_vpd_ops_t __efx_vpd_ef10_ops = { 79 ef10_vpd_init, /* evpdo_init */ 80 ef10_vpd_size, /* evpdo_size */ 81 ef10_vpd_read, /* evpdo_read */ 82 ef10_vpd_verify, /* evpdo_verify */ 83 ef10_vpd_reinit, /* evpdo_reinit */ 84 ef10_vpd_get, /* evpdo_get */ 85 ef10_vpd_set, /* evpdo_set */ 86 ef10_vpd_next, /* evpdo_next */ 87 ef10_vpd_write, /* evpdo_write */ 88 ef10_vpd_fini, /* evpdo_fini */ 89 }; 90 91 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */ 92 93 __checkReturn efx_rc_t 94 efx_vpd_init( 95 __in efx_nic_t *enp) 96 { 97 const efx_vpd_ops_t *evpdop; 98 efx_rc_t rc; 99 100 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 101 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 102 EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_VPD)); 103 104 switch (enp->en_family) { 105 #if EFSYS_OPT_SIENA 106 case EFX_FAMILY_SIENA: 107 evpdop = &__efx_vpd_siena_ops; 108 break; 109 #endif /* EFSYS_OPT_SIENA */ 110 111 #if EFSYS_OPT_HUNTINGTON 112 case EFX_FAMILY_HUNTINGTON: 113 evpdop = &__efx_vpd_ef10_ops; 114 break; 115 #endif /* EFSYS_OPT_HUNTINGTON */ 116 117 #if EFSYS_OPT_MEDFORD 118 case EFX_FAMILY_MEDFORD: 119 evpdop = &__efx_vpd_ef10_ops; 120 break; 121 #endif /* EFSYS_OPT_MEDFORD */ 122 123 default: 124 EFSYS_ASSERT(0); 125 rc = ENOTSUP; 126 goto fail1; 127 } 128 129 if (evpdop->evpdo_init != NULL) { 130 if ((rc = evpdop->evpdo_init(enp)) != 0) 131 goto fail2; 132 } 133 134 enp->en_evpdop = evpdop; 135 enp->en_mod_flags |= EFX_MOD_VPD; 136 137 return (0); 138 139 fail2: 140 EFSYS_PROBE(fail2); 141 fail1: 142 EFSYS_PROBE1(fail1, efx_rc_t, rc); 143 144 return (rc); 145 } 146 147 __checkReturn efx_rc_t 148 efx_vpd_size( 149 __in efx_nic_t *enp, 150 __out size_t *sizep) 151 { 152 const efx_vpd_ops_t *evpdop = enp->en_evpdop; 153 efx_rc_t rc; 154 155 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 156 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 157 158 if ((rc = evpdop->evpdo_size(enp, sizep)) != 0) 159 goto fail1; 160 161 return (0); 162 163 fail1: 164 EFSYS_PROBE1(fail1, efx_rc_t, rc); 165 166 return (rc); 167 } 168 169 __checkReturn efx_rc_t 170 efx_vpd_read( 171 __in efx_nic_t *enp, 172 __out_bcount(size) caddr_t data, 173 __in size_t size) 174 { 175 const efx_vpd_ops_t *evpdop = enp->en_evpdop; 176 efx_rc_t rc; 177 178 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 179 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 180 181 if ((rc = evpdop->evpdo_read(enp, data, size)) != 0) 182 goto fail1; 183 184 return (0); 185 186 fail1: 187 EFSYS_PROBE1(fail1, efx_rc_t, rc); 188 189 return (rc); 190 } 191 192 __checkReturn efx_rc_t 193 efx_vpd_verify( 194 __in efx_nic_t *enp, 195 __in_bcount(size) caddr_t data, 196 __in size_t size) 197 { 198 const efx_vpd_ops_t *evpdop = enp->en_evpdop; 199 efx_rc_t rc; 200 201 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 202 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 203 204 if ((rc = evpdop->evpdo_verify(enp, data, size)) != 0) 205 goto fail1; 206 207 return (0); 208 209 fail1: 210 EFSYS_PROBE1(fail1, efx_rc_t, rc); 211 212 return (rc); 213 } 214 215 __checkReturn efx_rc_t 216 efx_vpd_reinit( 217 __in efx_nic_t *enp, 218 __in_bcount(size) caddr_t data, 219 __in size_t size) 220 { 221 const efx_vpd_ops_t *evpdop = enp->en_evpdop; 222 efx_rc_t rc; 223 224 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 225 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 226 227 if (evpdop->evpdo_reinit == NULL) { 228 rc = ENOTSUP; 229 goto fail1; 230 } 231 232 if ((rc = evpdop->evpdo_reinit(enp, data, size)) != 0) 233 goto fail2; 234 235 return (0); 236 237 fail2: 238 EFSYS_PROBE(fail2); 239 fail1: 240 EFSYS_PROBE1(fail1, efx_rc_t, rc); 241 242 return (rc); 243 } 244 245 __checkReturn efx_rc_t 246 efx_vpd_get( 247 __in efx_nic_t *enp, 248 __in_bcount(size) caddr_t data, 249 __in size_t size, 250 __inout efx_vpd_value_t *evvp) 251 { 252 const efx_vpd_ops_t *evpdop = enp->en_evpdop; 253 efx_rc_t rc; 254 255 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 256 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 257 258 if ((rc = evpdop->evpdo_get(enp, data, size, evvp)) != 0) { 259 if (rc == ENOENT) 260 return (rc); 261 262 goto fail1; 263 } 264 265 return (0); 266 267 fail1: 268 EFSYS_PROBE1(fail1, efx_rc_t, rc); 269 270 return (rc); 271 } 272 273 __checkReturn efx_rc_t 274 efx_vpd_set( 275 __in efx_nic_t *enp, 276 __inout_bcount(size) caddr_t data, 277 __in size_t size, 278 __in efx_vpd_value_t *evvp) 279 { 280 const efx_vpd_ops_t *evpdop = enp->en_evpdop; 281 efx_rc_t rc; 282 283 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 284 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 285 286 if ((rc = evpdop->evpdo_set(enp, data, size, evvp)) != 0) 287 goto fail1; 288 289 return (0); 290 291 fail1: 292 EFSYS_PROBE1(fail1, efx_rc_t, rc); 293 294 return (rc); 295 } 296 297 __checkReturn efx_rc_t 298 efx_vpd_next( 299 __in efx_nic_t *enp, 300 __inout_bcount(size) caddr_t data, 301 __in size_t size, 302 __out efx_vpd_value_t *evvp, 303 __inout unsigned int *contp) 304 { 305 const efx_vpd_ops_t *evpdop = enp->en_evpdop; 306 efx_rc_t rc; 307 308 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 309 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 310 311 if ((rc = evpdop->evpdo_next(enp, data, size, evvp, contp)) != 0) 312 goto fail1; 313 314 return (0); 315 316 fail1: 317 EFSYS_PROBE1(fail1, efx_rc_t, rc); 318 319 return (rc); 320 } 321 322 __checkReturn efx_rc_t 323 efx_vpd_write( 324 __in efx_nic_t *enp, 325 __in_bcount(size) caddr_t data, 326 __in size_t size) 327 { 328 const efx_vpd_ops_t *evpdop = enp->en_evpdop; 329 efx_rc_t rc; 330 331 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 332 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 333 334 if ((rc = evpdop->evpdo_write(enp, data, size)) != 0) 335 goto fail1; 336 337 return (0); 338 339 fail1: 340 EFSYS_PROBE1(fail1, efx_rc_t, rc); 341 342 return (rc); 343 } 344 345 static __checkReturn efx_rc_t 346 efx_vpd_next_tag( 347 __in caddr_t data, 348 __in size_t size, 349 __inout unsigned int *offsetp, 350 __out efx_vpd_tag_t *tagp, 351 __out uint16_t *lengthp) 352 { 353 efx_byte_t byte; 354 efx_word_t word; 355 uint8_t name; 356 uint16_t length; 357 size_t headlen; 358 efx_rc_t rc; 359 360 if (*offsetp >= size) { 361 rc = EFAULT; 362 goto fail1; 363 } 364 365 EFX_POPULATE_BYTE_1(byte, EFX_BYTE_0, data[*offsetp]); 366 367 switch (EFX_BYTE_FIELD(byte, TAG_TYPE)) { 368 case TAG_TYPE_SMALL_ITEM_DECODE: 369 headlen = 1; 370 371 name = EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_NAME); 372 length = (uint16_t)EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_SIZE); 373 374 break; 375 376 case TAG_TYPE_LARGE_ITEM_DECODE: 377 headlen = 3; 378 379 if (*offsetp + headlen > size) { 380 rc = EFAULT; 381 goto fail2; 382 } 383 384 name = EFX_BYTE_FIELD(byte, TAG_LARGE_ITEM_NAME); 385 EFX_POPULATE_WORD_2(word, 386 EFX_BYTE_0, data[*offsetp + 1], 387 EFX_BYTE_1, data[*offsetp + 2]); 388 length = EFX_WORD_FIELD(word, EFX_WORD_0); 389 390 break; 391 392 default: 393 rc = EFAULT; 394 goto fail2; 395 } 396 397 if (*offsetp + headlen + length > size) { 398 rc = EFAULT; 399 goto fail3; 400 } 401 402 EFX_STATIC_ASSERT(TAG_NAME_END_DECODE == EFX_VPD_END); 403 EFX_STATIC_ASSERT(TAG_NAME_ID_STRING_DECODE == EFX_VPD_ID); 404 EFX_STATIC_ASSERT(TAG_NAME_VPD_R_DECODE == EFX_VPD_RO); 405 EFX_STATIC_ASSERT(TAG_NAME_VPD_W_DECODE == EFX_VPD_RW); 406 if (name != EFX_VPD_END && name != EFX_VPD_ID && 407 name != EFX_VPD_RO) { 408 rc = EFAULT; 409 goto fail4; 410 } 411 412 *tagp = name; 413 *lengthp = length; 414 *offsetp += headlen; 415 416 return (0); 417 418 fail4: 419 EFSYS_PROBE(fail4); 420 fail3: 421 EFSYS_PROBE(fail3); 422 fail2: 423 EFSYS_PROBE(fail2); 424 fail1: 425 EFSYS_PROBE1(fail1, efx_rc_t, rc); 426 427 return (rc); 428 } 429 430 static __checkReturn efx_rc_t 431 efx_vpd_next_keyword( 432 __in_bcount(size) caddr_t tag, 433 __in size_t size, 434 __in unsigned int pos, 435 __out efx_vpd_keyword_t *keywordp, 436 __out uint8_t *lengthp) 437 { 438 efx_vpd_keyword_t keyword; 439 uint8_t length; 440 efx_rc_t rc; 441 442 if (pos + 3U > size) { 443 rc = EFAULT; 444 goto fail1; 445 } 446 447 keyword = EFX_VPD_KEYWORD(tag[pos], tag[pos + 1]); 448 length = tag[pos + 2]; 449 450 if (length == 0 || pos + 3U + length > size) { 451 rc = EFAULT; 452 goto fail2; 453 } 454 455 *keywordp = keyword; 456 *lengthp = length; 457 458 return (0); 459 460 fail2: 461 EFSYS_PROBE(fail2); 462 fail1: 463 EFSYS_PROBE1(fail1, efx_rc_t, rc); 464 465 return (rc); 466 } 467 468 __checkReturn efx_rc_t 469 efx_vpd_hunk_length( 470 __in_bcount(size) caddr_t data, 471 __in size_t size, 472 __out size_t *lengthp) 473 { 474 efx_vpd_tag_t tag; 475 unsigned int offset; 476 uint16_t taglen; 477 efx_rc_t rc; 478 479 offset = 0; 480 _NOTE(CONSTANTCONDITION) 481 while (1) { 482 if ((rc = efx_vpd_next_tag(data, size, &offset, 483 &tag, &taglen)) != 0) 484 goto fail1; 485 offset += taglen; 486 if (tag == EFX_VPD_END) 487 break; 488 } 489 490 *lengthp = offset; 491 492 return (0); 493 494 fail1: 495 EFSYS_PROBE1(fail1, efx_rc_t, rc); 496 497 return (rc); 498 } 499 500 __checkReturn efx_rc_t 501 efx_vpd_hunk_verify( 502 __in_bcount(size) caddr_t data, 503 __in size_t size, 504 __out_opt boolean_t *cksummedp) 505 { 506 efx_vpd_tag_t tag; 507 efx_vpd_keyword_t keyword; 508 unsigned int offset; 509 unsigned int pos; 510 unsigned int i; 511 uint16_t taglen; 512 uint8_t keylen; 513 uint8_t cksum; 514 boolean_t cksummed = B_FALSE; 515 efx_rc_t rc; 516 517 /* 518 * Parse every tag,keyword in the existing VPD. If the csum is present, 519 * the assert it is correct, and is the final keyword in the RO block. 520 */ 521 offset = 0; 522 _NOTE(CONSTANTCONDITION) 523 while (1) { 524 if ((rc = efx_vpd_next_tag(data, size, &offset, 525 &tag, &taglen)) != 0) 526 goto fail1; 527 if (tag == EFX_VPD_END) 528 break; 529 else if (tag == EFX_VPD_ID) 530 goto done; 531 532 for (pos = 0; pos != taglen; pos += 3 + keylen) { 533 /* RV keyword must be the last in the block */ 534 if (cksummed) { 535 rc = EFAULT; 536 goto fail2; 537 } 538 539 if ((rc = efx_vpd_next_keyword(data + offset, 540 taglen, pos, &keyword, &keylen)) != 0) 541 goto fail3; 542 543 if (keyword == EFX_VPD_KEYWORD('R', 'V')) { 544 cksum = 0; 545 for (i = 0; i < offset + pos + 4; i++) 546 cksum += data[i]; 547 548 if (cksum != 0) { 549 rc = EFAULT; 550 goto fail4; 551 } 552 553 cksummed = B_TRUE; 554 } 555 } 556 557 done: 558 offset += taglen; 559 } 560 561 if (!cksummed) { 562 rc = EFAULT; 563 goto fail5; 564 } 565 566 if (cksummedp != NULL) 567 *cksummedp = cksummed; 568 569 return (0); 570 571 fail5: 572 EFSYS_PROBE(fail5); 573 fail4: 574 EFSYS_PROBE(fail4); 575 fail3: 576 EFSYS_PROBE(fail3); 577 fail2: 578 EFSYS_PROBE(fail2); 579 fail1: 580 EFSYS_PROBE1(fail1, efx_rc_t, rc); 581 582 return (rc); 583 } 584 585 static uint8_t __efx_vpd_blank_pid[] = { 586 /* Large resource type ID length 1 */ 587 0x82, 0x01, 0x00, 588 /* Product name ' ' */ 589 0x32, 590 }; 591 592 static uint8_t __efx_vpd_blank_r[] = { 593 /* Large resource type VPD-R length 4 */ 594 0x90, 0x04, 0x00, 595 /* RV keyword length 1 */ 596 'R', 'V', 0x01, 597 /* RV payload checksum */ 598 0x00, 599 }; 600 601 __checkReturn efx_rc_t 602 efx_vpd_hunk_reinit( 603 __in_bcount(size) caddr_t data, 604 __in size_t size, 605 __in boolean_t wantpid) 606 { 607 unsigned int offset = 0; 608 unsigned int pos; 609 efx_byte_t byte; 610 uint8_t cksum; 611 efx_rc_t rc; 612 613 if (size < 0x100) { 614 rc = ENOSPC; 615 goto fail1; 616 } 617 618 if (wantpid) { 619 memcpy(data + offset, __efx_vpd_blank_pid, 620 sizeof (__efx_vpd_blank_pid)); 621 offset += sizeof (__efx_vpd_blank_pid); 622 } 623 624 memcpy(data + offset, __efx_vpd_blank_r, sizeof (__efx_vpd_blank_r)); 625 offset += sizeof (__efx_vpd_blank_r); 626 627 /* Update checksum */ 628 cksum = 0; 629 for (pos = 0; pos < offset; pos++) 630 cksum += data[pos]; 631 data[offset - 1] -= cksum; 632 633 /* Append trailing tag */ 634 EFX_POPULATE_BYTE_3(byte, 635 TAG_TYPE, TAG_TYPE_SMALL_ITEM_DECODE, 636 TAG_SMALL_ITEM_NAME, TAG_NAME_END_DECODE, 637 TAG_SMALL_ITEM_SIZE, 0); 638 data[offset] = EFX_BYTE_FIELD(byte, EFX_BYTE_0); 639 offset++; 640 641 return (0); 642 643 fail1: 644 EFSYS_PROBE1(fail1, efx_rc_t, rc); 645 646 return (rc); 647 } 648 649 __checkReturn efx_rc_t 650 efx_vpd_hunk_next( 651 __in_bcount(size) caddr_t data, 652 __in size_t size, 653 __out efx_vpd_tag_t *tagp, 654 __out efx_vpd_keyword_t *keywordp, 655 __out_opt unsigned int *payloadp, 656 __out_opt uint8_t *paylenp, 657 __inout unsigned int *contp) 658 { 659 efx_vpd_tag_t tag; 660 efx_vpd_keyword_t keyword = 0; 661 unsigned int offset; 662 unsigned int pos; 663 unsigned int index; 664 uint16_t taglen; 665 uint8_t keylen; 666 uint8_t paylen; 667 efx_rc_t rc; 668 669 offset = index = 0; 670 _NOTE(CONSTANTCONDITION) 671 while (1) { 672 if ((rc = efx_vpd_next_tag(data, size, &offset, 673 &tag, &taglen)) != 0) 674 goto fail1; 675 676 if (tag == EFX_VPD_END) { 677 keyword = 0; 678 paylen = 0; 679 index = 0; 680 break; 681 } 682 683 if (tag == EFX_VPD_ID) { 684 if (index++ == *contp) { 685 EFSYS_ASSERT3U(taglen, <, 0x100); 686 keyword = 0; 687 paylen = (uint8_t)MIN(taglen, 0xff); 688 689 goto done; 690 } 691 } else { 692 for (pos = 0; pos != taglen; pos += 3 + keylen) { 693 if ((rc = efx_vpd_next_keyword(data + offset, 694 taglen, pos, &keyword, &keylen)) != 0) 695 goto fail2; 696 697 if (index++ == *contp) { 698 offset += pos + 3; 699 paylen = keylen; 700 701 goto done; 702 } 703 } 704 } 705 706 offset += taglen; 707 } 708 709 done: 710 *tagp = tag; 711 *keywordp = keyword; 712 if (payloadp != NULL) 713 *payloadp = offset; 714 if (paylenp != NULL) 715 *paylenp = paylen; 716 717 *contp = index; 718 return (0); 719 720 fail2: 721 EFSYS_PROBE(fail2); 722 fail1: 723 EFSYS_PROBE1(fail1, efx_rc_t, rc); 724 725 return (rc); 726 } 727 728 __checkReturn efx_rc_t 729 efx_vpd_hunk_get( 730 __in_bcount(size) caddr_t data, 731 __in size_t size, 732 __in efx_vpd_tag_t tag, 733 __in efx_vpd_keyword_t keyword, 734 __out unsigned int *payloadp, 735 __out uint8_t *paylenp) 736 { 737 efx_vpd_tag_t itag; 738 efx_vpd_keyword_t ikeyword; 739 unsigned int offset; 740 unsigned int pos; 741 uint16_t taglen; 742 uint8_t keylen; 743 efx_rc_t rc; 744 745 offset = 0; 746 _NOTE(CONSTANTCONDITION) 747 while (1) { 748 if ((rc = efx_vpd_next_tag(data, size, &offset, 749 &itag, &taglen)) != 0) 750 goto fail1; 751 if (itag == EFX_VPD_END) 752 break; 753 754 if (itag == tag) { 755 if (itag == EFX_VPD_ID) { 756 EFSYS_ASSERT3U(taglen, <, 0x100); 757 758 *paylenp = (uint8_t)MIN(taglen, 0xff); 759 *payloadp = offset; 760 return (0); 761 } 762 763 for (pos = 0; pos != taglen; pos += 3 + keylen) { 764 if ((rc = efx_vpd_next_keyword(data + offset, 765 taglen, pos, &ikeyword, &keylen)) != 0) 766 goto fail2; 767 768 if (ikeyword == keyword) { 769 *paylenp = keylen; 770 *payloadp = offset + pos + 3; 771 return (0); 772 } 773 } 774 } 775 776 offset += taglen; 777 } 778 779 /* Not an error */ 780 return (ENOENT); 781 782 fail2: 783 EFSYS_PROBE(fail2); 784 fail1: 785 EFSYS_PROBE1(fail1, efx_rc_t, rc); 786 787 return (rc); 788 } 789 790 __checkReturn efx_rc_t 791 efx_vpd_hunk_set( 792 __in_bcount(size) caddr_t data, 793 __in size_t size, 794 __in efx_vpd_value_t *evvp) 795 { 796 efx_word_t word; 797 efx_vpd_tag_t tag; 798 efx_vpd_keyword_t keyword; 799 unsigned int offset; 800 unsigned int pos; 801 unsigned int taghead; 802 unsigned int source; 803 unsigned int dest; 804 unsigned int i; 805 uint16_t taglen; 806 uint8_t keylen; 807 uint8_t cksum; 808 size_t used; 809 efx_rc_t rc; 810 811 switch (evvp->evv_tag) { 812 case EFX_VPD_ID: 813 if (evvp->evv_keyword != 0) { 814 rc = EINVAL; 815 goto fail1; 816 } 817 818 /* Can't delete the ID keyword */ 819 if (evvp->evv_length == 0) { 820 rc = EINVAL; 821 goto fail1; 822 } 823 break; 824 825 case EFX_VPD_RO: 826 if (evvp->evv_keyword == EFX_VPD_KEYWORD('R', 'V')) { 827 rc = EINVAL; 828 goto fail1; 829 } 830 break; 831 832 default: 833 rc = EINVAL; 834 goto fail1; 835 } 836 837 /* Determine total size of all current tags */ 838 if ((rc = efx_vpd_hunk_length(data, size, &used)) != 0) 839 goto fail2; 840 841 offset = 0; 842 _NOTE(CONSTANTCONDITION) 843 while (1) { 844 taghead = offset; 845 if ((rc = efx_vpd_next_tag(data, size, &offset, 846 &tag, &taglen)) != 0) 847 goto fail3; 848 if (tag == EFX_VPD_END) 849 break; 850 else if (tag != evvp->evv_tag) { 851 offset += taglen; 852 continue; 853 } 854 855 /* We only support modifying large resource tags */ 856 if (offset - taghead != 3) { 857 rc = EINVAL; 858 goto fail4; 859 } 860 861 /* 862 * Work out the offset of the byte immediately after the 863 * old (=source) and new (=dest) new keyword/tag 864 */ 865 pos = 0; 866 if (tag == EFX_VPD_ID) { 867 source = offset + taglen; 868 dest = offset + evvp->evv_length; 869 goto check_space; 870 } 871 872 EFSYS_ASSERT3U(tag, ==, EFX_VPD_RO); 873 source = dest = 0; 874 for (pos = 0; pos != taglen; pos += 3 + keylen) { 875 if ((rc = efx_vpd_next_keyword(data + offset, 876 taglen, pos, &keyword, &keylen)) != 0) 877 goto fail5; 878 879 if (keyword == evvp->evv_keyword && 880 evvp->evv_length == 0) { 881 /* Deleting this keyword */ 882 source = offset + pos + 3 + keylen; 883 dest = offset + pos; 884 break; 885 886 } else if (keyword == evvp->evv_keyword) { 887 /* Adjusting this keyword */ 888 source = offset + pos + 3 + keylen; 889 dest = offset + pos + 3 + evvp->evv_length; 890 break; 891 892 } else if (keyword == EFX_VPD_KEYWORD('R', 'V')) { 893 /* The RV keyword must be at the end */ 894 EFSYS_ASSERT3U(pos + 3 + keylen, ==, taglen); 895 896 /* 897 * The keyword doesn't already exist. If the 898 * user deleting a non-existant keyword then 899 * this is a no-op. 900 */ 901 if (evvp->evv_length == 0) 902 return (0); 903 904 /* Insert this keyword before the RV keyword */ 905 source = offset + pos; 906 dest = offset + pos + 3 + evvp->evv_length; 907 break; 908 } 909 } 910 911 check_space: 912 if (used + dest > size + source) { 913 rc = ENOSPC; 914 goto fail6; 915 } 916 917 /* Move trailing data */ 918 (void) memmove(data + dest, data + source, used - source); 919 920 /* Copy contents */ 921 memcpy(data + dest - evvp->evv_length, evvp->evv_value, 922 evvp->evv_length); 923 924 /* Insert new keyword header if required */ 925 if (tag != EFX_VPD_ID && evvp->evv_length > 0) { 926 EFX_POPULATE_WORD_1(word, EFX_WORD_0, 927 evvp->evv_keyword); 928 data[offset + pos + 0] = 929 EFX_WORD_FIELD(word, EFX_BYTE_0); 930 data[offset + pos + 1] = 931 EFX_WORD_FIELD(word, EFX_BYTE_1); 932 data[offset + pos + 2] = evvp->evv_length; 933 } 934 935 /* Modify tag length (large resource type) */ 936 taglen += (dest - source); 937 EFX_POPULATE_WORD_1(word, EFX_WORD_0, taglen); 938 data[offset - 2] = EFX_WORD_FIELD(word, EFX_BYTE_0); 939 data[offset - 1] = EFX_WORD_FIELD(word, EFX_BYTE_1); 940 941 goto checksum; 942 } 943 944 /* Unable to find the matching tag */ 945 rc = ENOENT; 946 goto fail7; 947 948 checksum: 949 /* Find the RV tag, and update the checksum */ 950 offset = 0; 951 _NOTE(CONSTANTCONDITION) 952 while (1) { 953 if ((rc = efx_vpd_next_tag(data, size, &offset, 954 &tag, &taglen)) != 0) 955 goto fail8; 956 if (tag == EFX_VPD_END) 957 break; 958 if (tag == EFX_VPD_RO) { 959 for (pos = 0; pos != taglen; pos += 3 + keylen) { 960 if ((rc = efx_vpd_next_keyword(data + offset, 961 taglen, pos, &keyword, &keylen)) != 0) 962 goto fail9; 963 964 if (keyword == EFX_VPD_KEYWORD('R', 'V')) { 965 cksum = 0; 966 for (i = 0; i < offset + pos + 3; i++) 967 cksum += data[i]; 968 data[i] = -cksum; 969 break; 970 } 971 } 972 } 973 974 offset += taglen; 975 } 976 977 /* Zero out the unused portion */ 978 (void) memset(data + offset + taglen, 0xff, size - offset - taglen); 979 980 return (0); 981 982 fail9: 983 EFSYS_PROBE(fail9); 984 fail8: 985 EFSYS_PROBE(fail8); 986 fail7: 987 EFSYS_PROBE(fail7); 988 fail6: 989 EFSYS_PROBE(fail6); 990 fail5: 991 EFSYS_PROBE(fail5); 992 fail4: 993 EFSYS_PROBE(fail4); 994 fail3: 995 EFSYS_PROBE(fail3); 996 fail2: 997 EFSYS_PROBE(fail2); 998 fail1: 999 EFSYS_PROBE1(fail1, efx_rc_t, rc); 1000 1001 return (rc); 1002 } 1003 1004 void 1005 efx_vpd_fini( 1006 __in efx_nic_t *enp) 1007 { 1008 const efx_vpd_ops_t *evpdop = enp->en_evpdop; 1009 1010 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 1011 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 1012 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 1013 1014 if (evpdop->evpdo_fini != NULL) 1015 evpdop->evpdo_fini(enp); 1016 1017 enp->en_evpdop = NULL; 1018 enp->en_mod_flags &= ~EFX_MOD_VPD; 1019 } 1020 1021 #endif /* EFSYS_OPT_VPD */ 1022