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