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