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