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