1 /*- 2 * Copyright (c) 2009-2015 Solarflare Communications Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 * The views and conclusions contained in the software and documentation are 27 * those of the authors and should not be interpreted as representing official 28 * policies, either expressed or implied, of the FreeBSD Project. 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include "efsys.h" 35 #include "efx.h" 36 #include "efx_types.h" 37 #include "efx_regs.h" 38 #include "efx_impl.h" 39 40 #if EFSYS_OPT_VPD 41 42 #define TAG_TYPE_LBN 7 43 #define TAG_TYPE_WIDTH 1 44 #define TAG_TYPE_LARGE_ITEM_DECODE 1 45 #define TAG_TYPE_SMALL_ITEM_DECODE 0 46 47 #define TAG_SMALL_ITEM_NAME_LBN 3 48 #define TAG_SMALL_ITEM_NAME_WIDTH 4 49 #define TAG_SMALL_ITEM_SIZE_LBN 0 50 #define TAG_SMALL_ITEM_SIZE_WIDTH 3 51 52 #define TAG_LARGE_ITEM_NAME_LBN 0 53 #define TAG_LARGE_ITEM_NAME_WIDTH 7 54 55 #define TAG_NAME_END_DECODE 0x0f 56 #define TAG_NAME_ID_STRING_DECODE 0x02 57 #define TAG_NAME_VPD_R_DECODE 0x10 58 #define TAG_NAME_VPD_W_DECODE 0x11 59 60 #if EFSYS_OPT_FALCON 61 62 static efx_vpd_ops_t __efx_vpd_falcon_ops = { 63 NULL, /* evpdo_init */ 64 falcon_vpd_size, /* evpdo_size */ 65 falcon_vpd_read, /* evpdo_read */ 66 falcon_vpd_verify, /* evpdo_verify */ 67 NULL, /* evpdo_reinit */ 68 falcon_vpd_get, /* evpdo_get */ 69 falcon_vpd_set, /* evpdo_set */ 70 falcon_vpd_next, /* evpdo_next */ 71 falcon_vpd_write, /* evpdo_write */ 72 NULL, /* evpdo_fini */ 73 }; 74 75 #endif /* EFSYS_OPT_FALCON */ 76 77 #if EFSYS_OPT_SIENA 78 79 static efx_vpd_ops_t __efx_vpd_siena_ops = { 80 siena_vpd_init, /* evpdo_init */ 81 siena_vpd_size, /* evpdo_size */ 82 siena_vpd_read, /* evpdo_read */ 83 siena_vpd_verify, /* evpdo_verify */ 84 siena_vpd_reinit, /* evpdo_reinit */ 85 siena_vpd_get, /* evpdo_get */ 86 siena_vpd_set, /* evpdo_set */ 87 siena_vpd_next, /* evpdo_next */ 88 siena_vpd_write, /* evpdo_write */ 89 siena_vpd_fini, /* evpdo_fini */ 90 }; 91 92 #endif /* EFSYS_OPT_SIENA */ 93 94 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD 95 96 static efx_vpd_ops_t __efx_vpd_ef10_ops = { 97 ef10_vpd_init, /* evpdo_init */ 98 ef10_vpd_size, /* evpdo_size */ 99 ef10_vpd_read, /* evpdo_read */ 100 ef10_vpd_verify, /* evpdo_verify */ 101 ef10_vpd_reinit, /* evpdo_reinit */ 102 ef10_vpd_get, /* evpdo_get */ 103 ef10_vpd_set, /* evpdo_set */ 104 ef10_vpd_next, /* evpdo_next */ 105 ef10_vpd_write, /* evpdo_write */ 106 ef10_vpd_fini, /* evpdo_fini */ 107 }; 108 109 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */ 110 111 __checkReturn efx_rc_t 112 efx_vpd_init( 113 __in efx_nic_t *enp) 114 { 115 efx_vpd_ops_t *evpdop; 116 efx_rc_t rc; 117 118 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 119 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 120 EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_VPD)); 121 122 switch (enp->en_family) { 123 #if EFSYS_OPT_FALCON 124 case EFX_FAMILY_FALCON: 125 evpdop = (efx_vpd_ops_t *)&__efx_vpd_falcon_ops; 126 break; 127 #endif /* EFSYS_OPT_FALCON */ 128 129 #if EFSYS_OPT_SIENA 130 case EFX_FAMILY_SIENA: 131 evpdop = (efx_vpd_ops_t *)&__efx_vpd_siena_ops; 132 break; 133 #endif /* EFSYS_OPT_SIENA */ 134 135 #if EFSYS_OPT_HUNTINGTON 136 case EFX_FAMILY_HUNTINGTON: 137 evpdop = (efx_vpd_ops_t *)&__efx_vpd_ef10_ops; 138 break; 139 #endif /* EFSYS_OPT_HUNTINGTON */ 140 141 #if EFSYS_OPT_MEDFORD 142 case EFX_FAMILY_MEDFORD: 143 evpdop = (efx_vpd_ops_t *)&__efx_vpd_ef10_ops; 144 break; 145 #endif /* EFSYS_OPT_MEDFORD */ 146 147 default: 148 EFSYS_ASSERT(0); 149 rc = ENOTSUP; 150 goto fail1; 151 } 152 153 if (evpdop->evpdo_init != NULL) { 154 if ((rc = evpdop->evpdo_init(enp)) != 0) 155 goto fail2; 156 } 157 158 enp->en_evpdop = evpdop; 159 enp->en_mod_flags |= EFX_MOD_VPD; 160 161 return (0); 162 163 fail2: 164 EFSYS_PROBE(fail2); 165 fail1: 166 EFSYS_PROBE1(fail1, efx_rc_t, rc); 167 168 return (rc); 169 } 170 171 __checkReturn efx_rc_t 172 efx_vpd_size( 173 __in efx_nic_t *enp, 174 __out size_t *sizep) 175 { 176 efx_vpd_ops_t *evpdop = enp->en_evpdop; 177 efx_rc_t rc; 178 179 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 180 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 181 182 if ((rc = evpdop->evpdo_size(enp, sizep)) != 0) 183 goto fail1; 184 185 return (0); 186 187 fail1: 188 EFSYS_PROBE1(fail1, efx_rc_t, rc); 189 190 return (rc); 191 } 192 193 __checkReturn efx_rc_t 194 efx_vpd_read( 195 __in efx_nic_t *enp, 196 __out_bcount(size) caddr_t data, 197 __in size_t size) 198 { 199 efx_vpd_ops_t *evpdop = enp->en_evpdop; 200 efx_rc_t rc; 201 202 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 203 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 204 205 if ((rc = evpdop->evpdo_read(enp, data, size)) != 0) 206 goto fail1; 207 208 return (0); 209 210 fail1: 211 EFSYS_PROBE1(fail1, efx_rc_t, rc); 212 213 return (rc); 214 } 215 216 __checkReturn efx_rc_t 217 efx_vpd_verify( 218 __in efx_nic_t *enp, 219 __in_bcount(size) caddr_t data, 220 __in size_t size) 221 { 222 efx_vpd_ops_t *evpdop = enp->en_evpdop; 223 efx_rc_t rc; 224 225 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 226 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 227 228 if ((rc = evpdop->evpdo_verify(enp, data, size)) != 0) 229 goto fail1; 230 231 return (0); 232 233 fail1: 234 EFSYS_PROBE1(fail1, efx_rc_t, rc); 235 236 return (rc); 237 } 238 239 __checkReturn efx_rc_t 240 efx_vpd_reinit( 241 __in efx_nic_t *enp, 242 __in_bcount(size) caddr_t data, 243 __in size_t size) 244 { 245 efx_vpd_ops_t *evpdop = enp->en_evpdop; 246 efx_rc_t rc; 247 248 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 249 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 250 251 if (evpdop->evpdo_reinit == NULL) { 252 rc = ENOTSUP; 253 goto fail1; 254 } 255 256 if ((rc = evpdop->evpdo_reinit(enp, data, size)) != 0) 257 goto fail2; 258 259 return (0); 260 261 fail2: 262 EFSYS_PROBE(fail2); 263 fail1: 264 EFSYS_PROBE1(fail1, efx_rc_t, rc); 265 266 return (rc); 267 } 268 269 __checkReturn efx_rc_t 270 efx_vpd_get( 271 __in efx_nic_t *enp, 272 __in_bcount(size) caddr_t data, 273 __in size_t size, 274 __inout efx_vpd_value_t *evvp) 275 { 276 efx_vpd_ops_t *evpdop = enp->en_evpdop; 277 efx_rc_t rc; 278 279 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 280 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 281 282 if ((rc = evpdop->evpdo_get(enp, data, size, evvp)) != 0) 283 goto fail1; 284 285 return (0); 286 287 fail1: 288 EFSYS_PROBE1(fail1, efx_rc_t, rc); 289 290 return (rc); 291 } 292 293 __checkReturn efx_rc_t 294 efx_vpd_set( 295 __in efx_nic_t *enp, 296 __inout_bcount(size) caddr_t data, 297 __in size_t size, 298 __in efx_vpd_value_t *evvp) 299 { 300 efx_vpd_ops_t *evpdop = enp->en_evpdop; 301 efx_rc_t rc; 302 303 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 304 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 305 306 if ((rc = evpdop->evpdo_set(enp, data, size, evvp)) != 0) 307 goto fail1; 308 309 return (0); 310 311 fail1: 312 EFSYS_PROBE1(fail1, efx_rc_t, rc); 313 314 return (rc); 315 } 316 317 __checkReturn efx_rc_t 318 efx_vpd_next( 319 __in efx_nic_t *enp, 320 __inout_bcount(size) caddr_t data, 321 __in size_t size, 322 __out efx_vpd_value_t *evvp, 323 __inout unsigned int *contp) 324 { 325 efx_vpd_ops_t *evpdop = enp->en_evpdop; 326 efx_rc_t rc; 327 328 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 329 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 330 331 if ((rc = evpdop->evpdo_next(enp, data, size, evvp, contp)) != 0) 332 goto fail1; 333 334 return (0); 335 336 fail1: 337 EFSYS_PROBE1(fail1, efx_rc_t, rc); 338 339 return (rc); 340 } 341 342 __checkReturn efx_rc_t 343 efx_vpd_write( 344 __in efx_nic_t *enp, 345 __in_bcount(size) caddr_t data, 346 __in size_t size) 347 { 348 efx_vpd_ops_t *evpdop = enp->en_evpdop; 349 efx_rc_t rc; 350 351 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 352 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); 353 354 if ((rc = evpdop->evpdo_write(enp, data, size)) != 0) 355 goto fail1; 356 357 return (0); 358 359 fail1: 360 EFSYS_PROBE1(fail1, efx_rc_t, rc); 361 362 return (rc); 363 } 364 365 static __checkReturn efx_rc_t 366 efx_vpd_next_tag( 367 __in caddr_t data, 368 __in size_t size, 369 __inout unsigned int *offsetp, 370 __out efx_vpd_tag_t *tagp, 371 __out uint16_t *lengthp) 372 { 373 efx_byte_t byte; 374 efx_word_t word; 375 uint8_t name; 376 uint16_t length; 377 size_t headlen; 378 efx_rc_t rc; 379 380 if (*offsetp >= size) { 381 rc = EFAULT; 382 goto fail1; 383 } 384 385 EFX_POPULATE_BYTE_1(byte, EFX_BYTE_0, data[*offsetp]); 386 387 switch (EFX_BYTE_FIELD(byte, TAG_TYPE)) { 388 case TAG_TYPE_SMALL_ITEM_DECODE: 389 headlen = 1; 390 391 name = EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_NAME); 392 length = (uint16_t)EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_SIZE); 393 394 break; 395 396 case TAG_TYPE_LARGE_ITEM_DECODE: 397 headlen = 3; 398 399 if (*offsetp + headlen > size) { 400 rc = EFAULT; 401 goto fail2; 402 } 403 404 name = EFX_BYTE_FIELD(byte, TAG_LARGE_ITEM_NAME); 405 EFX_POPULATE_WORD_2(word, 406 EFX_BYTE_0, data[*offsetp + 1], 407 EFX_BYTE_1, data[*offsetp + 2]); 408 length = EFX_WORD_FIELD(word, EFX_WORD_0); 409 410 break; 411 412 default: 413 rc = EFAULT; 414 goto fail2; 415 } 416 417 if (*offsetp + headlen + length > size) { 418 rc = EFAULT; 419 goto fail3; 420 } 421 422 EFX_STATIC_ASSERT(TAG_NAME_END_DECODE == EFX_VPD_END); 423 EFX_STATIC_ASSERT(TAG_NAME_ID_STRING_DECODE == EFX_VPD_ID); 424 EFX_STATIC_ASSERT(TAG_NAME_VPD_R_DECODE == EFX_VPD_RO); 425 EFX_STATIC_ASSERT(TAG_NAME_VPD_W_DECODE == EFX_VPD_RW); 426 if (name != EFX_VPD_END && name != EFX_VPD_ID && 427 name != EFX_VPD_RO) { 428 rc = EFAULT; 429 goto fail4; 430 } 431 432 *tagp = name; 433 *lengthp = length; 434 *offsetp += headlen; 435 436 return (0); 437 438 fail4: 439 EFSYS_PROBE(fail4); 440 fail3: 441 EFSYS_PROBE(fail3); 442 fail2: 443 EFSYS_PROBE(fail2); 444 fail1: 445 EFSYS_PROBE1(fail1, efx_rc_t, rc); 446 447 return (rc); 448 } 449 450 static __checkReturn efx_rc_t 451 efx_vpd_next_keyword( 452 __in_bcount(size) caddr_t tag, 453 __in size_t size, 454 __in unsigned int pos, 455 __out efx_vpd_keyword_t *keywordp, 456 __out uint8_t *lengthp) 457 { 458 efx_vpd_keyword_t keyword; 459 uint8_t length; 460 efx_rc_t rc; 461 462 if (pos + 3U > size) { 463 rc = EFAULT; 464 goto fail1; 465 } 466 467 keyword = EFX_VPD_KEYWORD(tag[pos], tag[pos + 1]); 468 length = tag[pos + 2]; 469 470 if (length == 0 || pos + 3U + length > size) { 471 rc = EFAULT; 472 goto fail2; 473 } 474 475 *keywordp = keyword; 476 *lengthp = length; 477 478 return (0); 479 480 fail2: 481 EFSYS_PROBE(fail2); 482 fail1: 483 EFSYS_PROBE1(fail1, efx_rc_t, rc); 484 485 return (rc); 486 } 487 488 __checkReturn efx_rc_t 489 efx_vpd_hunk_length( 490 __in_bcount(size) caddr_t data, 491 __in size_t size, 492 __out size_t *lengthp) 493 { 494 efx_vpd_tag_t tag; 495 unsigned int offset; 496 uint16_t taglen; 497 efx_rc_t rc; 498 499 offset = 0; 500 _NOTE(CONSTANTCONDITION) 501 while (1) { 502 if ((rc = efx_vpd_next_tag(data, size, &offset, 503 &tag, &taglen)) != 0) 504 goto fail1; 505 offset += taglen; 506 if (tag == EFX_VPD_END) 507 break; 508 } 509 510 *lengthp = offset; 511 512 return (0); 513 514 fail1: 515 EFSYS_PROBE1(fail1, efx_rc_t, rc); 516 517 return (rc); 518 } 519 520 __checkReturn efx_rc_t 521 efx_vpd_hunk_verify( 522 __in_bcount(size) caddr_t data, 523 __in size_t size, 524 __out_opt boolean_t *cksummedp) 525 { 526 efx_vpd_tag_t tag; 527 efx_vpd_keyword_t keyword; 528 unsigned int offset; 529 unsigned int pos; 530 unsigned int i; 531 uint16_t taglen; 532 uint8_t keylen; 533 uint8_t cksum; 534 boolean_t cksummed = B_FALSE; 535 efx_rc_t rc; 536 537 /* 538 * Parse every tag,keyword in the existing VPD. If the csum is present, 539 * the assert it is correct, and is the final keyword in the RO block. 540 */ 541 offset = 0; 542 _NOTE(CONSTANTCONDITION) 543 while (1) { 544 if ((rc = efx_vpd_next_tag(data, size, &offset, 545 &tag, &taglen)) != 0) 546 goto fail1; 547 if (tag == EFX_VPD_END) 548 break; 549 else if (tag == EFX_VPD_ID) 550 goto done; 551 552 for (pos = 0; pos != taglen; pos += 3 + keylen) { 553 /* RV keyword must be the last in the block */ 554 if (cksummed) { 555 rc = EFAULT; 556 goto fail2; 557 } 558 559 if ((rc = efx_vpd_next_keyword(data + offset, 560 taglen, pos, &keyword, &keylen)) != 0) 561 goto fail3; 562 563 if (keyword == EFX_VPD_KEYWORD('R', 'V')) { 564 cksum = 0; 565 for (i = 0; i < offset + pos + 4; i++) 566 cksum += data[i]; 567 568 if (cksum != 0) { 569 rc = EFAULT; 570 goto fail4; 571 } 572 573 cksummed = B_TRUE; 574 } 575 } 576 577 done: 578 offset += taglen; 579 } 580 581 if (!cksummed) { 582 rc = EFAULT; 583 goto fail5; 584 } 585 586 if (cksummedp != NULL) 587 *cksummedp = cksummed; 588 589 return (0); 590 591 fail5: 592 EFSYS_PROBE(fail5); 593 fail4: 594 EFSYS_PROBE(fail4); 595 fail3: 596 EFSYS_PROBE(fail3); 597 fail2: 598 EFSYS_PROBE(fail2); 599 fail1: 600 EFSYS_PROBE1(fail1, efx_rc_t, rc); 601 602 return (rc); 603 } 604 605 static uint8_t __efx_vpd_blank_pid[] = { 606 /* Large resource type ID length 1 */ 607 0x82, 0x01, 0x00, 608 /* Product name ' ' */ 609 0x32, 610 }; 611 612 static uint8_t __efx_vpd_blank_r[] = { 613 /* Large resource type VPD-R length 4 */ 614 0x90, 0x04, 0x00, 615 /* RV keyword length 1 */ 616 'R', 'V', 0x01, 617 /* RV payload checksum */ 618 0x00, 619 }; 620 621 __checkReturn efx_rc_t 622 efx_vpd_hunk_reinit( 623 __in_bcount(size) caddr_t data, 624 __in size_t size, 625 __in boolean_t wantpid) 626 { 627 unsigned int offset = 0; 628 unsigned int pos; 629 efx_byte_t byte; 630 uint8_t cksum; 631 efx_rc_t rc; 632 633 if (size < 0x100) { 634 rc = ENOSPC; 635 goto fail1; 636 } 637 638 if (wantpid) { 639 memcpy(data + offset, __efx_vpd_blank_pid, 640 sizeof (__efx_vpd_blank_pid)); 641 offset += sizeof (__efx_vpd_blank_pid); 642 } 643 644 memcpy(data + offset, __efx_vpd_blank_r, sizeof (__efx_vpd_blank_r)); 645 offset += sizeof (__efx_vpd_blank_r); 646 647 /* Update checksum */ 648 cksum = 0; 649 for (pos = 0; pos < offset; pos++) 650 cksum += data[pos]; 651 data[offset - 1] -= cksum; 652 653 /* Append trailing tag */ 654 EFX_POPULATE_BYTE_3(byte, 655 TAG_TYPE, TAG_TYPE_SMALL_ITEM_DECODE, 656 TAG_SMALL_ITEM_NAME, TAG_NAME_END_DECODE, 657 TAG_SMALL_ITEM_SIZE, 0); 658 data[offset] = EFX_BYTE_FIELD(byte, EFX_BYTE_0); 659 offset++; 660 661 return (0); 662 663 fail1: 664 EFSYS_PROBE1(fail1, efx_rc_t, rc); 665 666 return (rc); 667 } 668 669 __checkReturn efx_rc_t 670 efx_vpd_hunk_next( 671 __in_bcount(size) caddr_t data, 672 __in size_t size, 673 __out efx_vpd_tag_t *tagp, 674 __out efx_vpd_keyword_t *keywordp, 675 __out_bcount_opt(*paylenp) unsigned int *payloadp, 676 __out_opt uint8_t *paylenp, 677 __inout unsigned int *contp) 678 { 679 efx_vpd_tag_t tag; 680 efx_vpd_keyword_t keyword = 0; 681 unsigned int offset; 682 unsigned int pos; 683 unsigned int index; 684 uint16_t taglen; 685 uint8_t keylen; 686 uint8_t paylen; 687 efx_rc_t rc; 688 689 offset = index = 0; 690 _NOTE(CONSTANTCONDITION) 691 while (1) { 692 if ((rc = efx_vpd_next_tag(data, size, &offset, 693 &tag, &taglen)) != 0) 694 goto fail1; 695 if (tag == EFX_VPD_END) 696 break; 697 698 if (tag == EFX_VPD_ID) { 699 if (index == *contp) { 700 EFSYS_ASSERT3U(taglen, <, 0x100); 701 paylen = (uint8_t)MIN(taglen, 0xff); 702 703 goto done; 704 } 705 } else { 706 for (pos = 0; pos != taglen; pos += 3 + keylen) { 707 if ((rc = efx_vpd_next_keyword(data + offset, 708 taglen, pos, &keyword, &keylen)) != 0) 709 goto fail2; 710 711 if (index == *contp) { 712 offset += pos + 3; 713 paylen = keylen; 714 715 goto done; 716 } 717 } 718 } 719 720 offset += taglen; 721 } 722 723 *contp = 0; 724 return (0); 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); 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