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