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