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