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