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_bcount_opt(*paylenp) 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 if (tag == EFX_VPD_END) 693 break; 694 695 if (tag == EFX_VPD_ID) { 696 if (index == *contp) { 697 EFSYS_ASSERT3U(taglen, <, 0x100); 698 paylen = (uint8_t)MIN(taglen, 0xff); 699 700 goto done; 701 } 702 } else { 703 for (pos = 0; pos != taglen; pos += 3 + keylen) { 704 if ((rc = efx_vpd_next_keyword(data + offset, 705 taglen, pos, &keyword, &keylen)) != 0) 706 goto fail2; 707 708 if (index == *contp) { 709 offset += pos + 3; 710 paylen = keylen; 711 712 goto done; 713 } 714 } 715 } 716 717 offset += taglen; 718 } 719 720 *contp = 0; 721 return (0); 722 723 done: 724 *tagp = tag; 725 *keywordp = keyword; 726 if (payloadp != NULL) 727 *payloadp = offset; 728 if (paylenp != NULL) 729 *paylenp = paylen; 730 731 ++(*contp); 732 return (0); 733 734 fail2: 735 EFSYS_PROBE(fail2); 736 fail1: 737 EFSYS_PROBE1(fail1, efx_rc_t, rc); 738 739 return (rc); 740 } 741 742 __checkReturn efx_rc_t 743 efx_vpd_hunk_get( 744 __in_bcount(size) caddr_t data, 745 __in size_t size, 746 __in efx_vpd_tag_t tag, 747 __in efx_vpd_keyword_t keyword, 748 __out unsigned int *payloadp, 749 __out uint8_t *paylenp) 750 { 751 efx_vpd_tag_t itag; 752 efx_vpd_keyword_t ikeyword; 753 unsigned int offset; 754 unsigned int pos; 755 uint16_t taglen; 756 uint8_t keylen; 757 efx_rc_t rc; 758 759 offset = 0; 760 _NOTE(CONSTANTCONDITION) 761 while (1) { 762 if ((rc = efx_vpd_next_tag(data, size, &offset, 763 &itag, &taglen)) != 0) 764 goto fail1; 765 if (itag == EFX_VPD_END) 766 break; 767 768 if (itag == tag) { 769 if (itag == EFX_VPD_ID) { 770 EFSYS_ASSERT3U(taglen, <, 0x100); 771 772 *paylenp = (uint8_t)MIN(taglen, 0xff); 773 *payloadp = offset; 774 return (0); 775 } 776 777 for (pos = 0; pos != taglen; pos += 3 + keylen) { 778 if ((rc = efx_vpd_next_keyword(data + offset, 779 taglen, pos, &ikeyword, &keylen)) != 0) 780 goto fail2; 781 782 if (ikeyword == keyword) { 783 *paylenp = keylen; 784 *payloadp = offset + pos + 3; 785 return (0); 786 } 787 } 788 } 789 790 offset += taglen; 791 } 792 793 /* Not an error */ 794 return (ENOENT); 795 796 fail2: 797 EFSYS_PROBE(fail2); 798 fail1: 799 EFSYS_PROBE1(fail1, efx_rc_t, rc); 800 801 return (rc); 802 } 803 804 __checkReturn efx_rc_t 805 efx_vpd_hunk_set( 806 __in_bcount(size) caddr_t data, 807 __in size_t size, 808 __in efx_vpd_value_t *evvp) 809 { 810 efx_word_t word; 811 efx_vpd_tag_t tag; 812 efx_vpd_keyword_t keyword; 813 unsigned int offset; 814 unsigned int pos; 815 unsigned int taghead; 816 unsigned int source; 817 unsigned int dest; 818 unsigned int i; 819 uint16_t taglen; 820 uint8_t keylen; 821 uint8_t cksum; 822 size_t used; 823 efx_rc_t rc; 824 825 switch (evvp->evv_tag) { 826 case EFX_VPD_ID: 827 if (evvp->evv_keyword != 0) { 828 rc = EINVAL; 829 goto fail1; 830 } 831 832 /* Can't delete the ID keyword */ 833 if (evvp->evv_length == 0) { 834 rc = EINVAL; 835 goto fail1; 836 } 837 break; 838 839 case EFX_VPD_RO: 840 if (evvp->evv_keyword == EFX_VPD_KEYWORD('R', 'V')) { 841 rc = EINVAL; 842 goto fail1; 843 } 844 break; 845 846 default: 847 rc = EINVAL; 848 goto fail1; 849 } 850 851 /* Determine total size of all current tags */ 852 if ((rc = efx_vpd_hunk_length(data, size, &used)) != 0) 853 goto fail2; 854 855 offset = 0; 856 _NOTE(CONSTANTCONDITION) 857 while (1) { 858 taghead = offset; 859 if ((rc = efx_vpd_next_tag(data, size, &offset, 860 &tag, &taglen)) != 0) 861 goto fail3; 862 if (tag == EFX_VPD_END) 863 break; 864 else if (tag != evvp->evv_tag) { 865 offset += taglen; 866 continue; 867 } 868 869 /* We only support modifying large resource tags */ 870 if (offset - taghead != 3) { 871 rc = EINVAL; 872 goto fail4; 873 } 874 875 /* 876 * Work out the offset of the byte immediately after the 877 * old (=source) and new (=dest) new keyword/tag 878 */ 879 pos = 0; 880 if (tag == EFX_VPD_ID) { 881 source = offset + taglen; 882 dest = offset + evvp->evv_length; 883 goto check_space; 884 } 885 886 EFSYS_ASSERT3U(tag, ==, EFX_VPD_RO); 887 source = dest = 0; 888 for (pos = 0; pos != taglen; pos += 3 + keylen) { 889 if ((rc = efx_vpd_next_keyword(data + offset, 890 taglen, pos, &keyword, &keylen)) != 0) 891 goto fail5; 892 893 if (keyword == evvp->evv_keyword && 894 evvp->evv_length == 0) { 895 /* Deleting this keyword */ 896 source = offset + pos + 3 + keylen; 897 dest = offset + pos; 898 break; 899 900 } else if (keyword == evvp->evv_keyword) { 901 /* Adjusting this keyword */ 902 source = offset + pos + 3 + keylen; 903 dest = offset + pos + 3 + evvp->evv_length; 904 break; 905 906 } else if (keyword == EFX_VPD_KEYWORD('R', 'V')) { 907 /* The RV keyword must be at the end */ 908 EFSYS_ASSERT3U(pos + 3 + keylen, ==, taglen); 909 910 /* 911 * The keyword doesn't already exist. If the 912 * user deleting a non-existant keyword then 913 * this is a no-op. 914 */ 915 if (evvp->evv_length == 0) 916 return (0); 917 918 /* Insert this keyword before the RV keyword */ 919 source = offset + pos; 920 dest = offset + pos + 3 + evvp->evv_length; 921 break; 922 } 923 } 924 925 check_space: 926 if (used + dest > size + source) { 927 rc = ENOSPC; 928 goto fail6; 929 } 930 931 /* Move trailing data */ 932 (void) memmove(data + dest, data + source, used - source); 933 934 /* Copy contents */ 935 memcpy(data + dest - evvp->evv_length, evvp->evv_value, 936 evvp->evv_length); 937 938 /* Insert new keyword header if required */ 939 if (tag != EFX_VPD_ID && evvp->evv_length > 0) { 940 EFX_POPULATE_WORD_1(word, EFX_WORD_0, 941 evvp->evv_keyword); 942 data[offset + pos + 0] = 943 EFX_WORD_FIELD(word, EFX_BYTE_0); 944 data[offset + pos + 1] = 945 EFX_WORD_FIELD(word, EFX_BYTE_1); 946 data[offset + pos + 2] = evvp->evv_length; 947 } 948 949 /* Modify tag length (large resource type) */ 950 taglen += (dest - source); 951 EFX_POPULATE_WORD_1(word, EFX_WORD_0, taglen); 952 data[offset - 2] = EFX_WORD_FIELD(word, EFX_BYTE_0); 953 data[offset - 1] = EFX_WORD_FIELD(word, EFX_BYTE_1); 954 955 goto checksum; 956 } 957 958 /* Unable to find the matching tag */ 959 rc = ENOENT; 960 goto fail7; 961 962 checksum: 963 /* Find the RV tag, and update the checksum */ 964 offset = 0; 965 _NOTE(CONSTANTCONDITION) 966 while (1) { 967 if ((rc = efx_vpd_next_tag(data, size, &offset, 968 &tag, &taglen)) != 0) 969 goto fail8; 970 if (tag == EFX_VPD_END) 971 break; 972 if (tag == EFX_VPD_RO) { 973 for (pos = 0; pos != taglen; pos += 3 + keylen) { 974 if ((rc = efx_vpd_next_keyword(data + offset, 975 taglen, pos, &keyword, &keylen)) != 0) 976 goto fail9; 977 978 if (keyword == EFX_VPD_KEYWORD('R', 'V')) { 979 cksum = 0; 980 for (i = 0; i < offset + pos + 3; i++) 981 cksum += data[i]; 982 data[i] = -cksum; 983 break; 984 } 985 } 986 } 987 988 offset += taglen; 989 } 990 991 /* Zero out the unused portion */ 992 (void) memset(data + offset + taglen, 0xff, size - offset - taglen); 993 994 return (0); 995 996 fail9: 997 EFSYS_PROBE(fail9); 998 fail8: 999 EFSYS_PROBE(fail8); 1000 fail7: 1001 EFSYS_PROBE(fail7); 1002 fail6: 1003 EFSYS_PROBE(fail6); 1004 fail5: 1005 EFSYS_PROBE(fail5); 1006 fail4: 1007 EFSYS_PROBE(fail4); 1008 fail3: 1009 EFSYS_PROBE(fail3); 1010 fail2: 1011 EFSYS_PROBE(fail2); 1012 fail1: 1013 EFSYS_PROBE1(fail1, efx_rc_t, rc); 1014 1015 return (rc); 1016 } 1017 1018 void 1019 efx_vpd_fini( 1020 __in efx_nic_t *enp) 1021 { 1022 efx_vpd_ops_t *evpdop = enp->en_evpdop; 1023 1024 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 1025 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 1026 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 1027 1028 if (evpdop->evpdo_fini != NULL) 1029 evpdop->evpdo_fini(enp); 1030 1031 enp->en_evpdop = NULL; 1032 enp->en_mod_flags &= ~EFX_MOD_VPD; 1033 } 1034 1035 #endif /* EFSYS_OPT_VPD */ 1036