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