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