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