1 /*
   2  * Copyright (c) 2012-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_HUNTINGTON || EFSYS_OPT_MEDFORD
  35 
  36 #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
  37 
  38 #include "ef10_tlv_layout.h"
  39 
  40 /* Cursor for TLV partition format */
  41 typedef struct tlv_cursor_s {
  42         uint32_t        *block;                 /* Base of data block */
  43         uint32_t        *current;               /* Cursor position */
  44         uint32_t        *end;                   /* End tag position */
  45         uint32_t        *limit;                 /* Last dword of data block */
  46 } tlv_cursor_t;
  47 
  48 typedef struct nvram_partition_s {
  49         uint16_t type;
  50         uint8_t chip_select;
  51         uint8_t flags;
  52         /*
  53          * The full length of the NVRAM partition.
  54          * This is different from tlv_partition_header.total_length,
  55          *  which can be smaller.
  56          */
  57         uint32_t length;
  58         uint32_t erase_size;
  59         uint32_t *data;
  60         tlv_cursor_t tlv_cursor;
  61 } nvram_partition_t;
  62 
  63 
  64 static  __checkReturn           efx_rc_t
  65 tlv_validate_state(
  66         __inout                 tlv_cursor_t *cursor);
  67 
  68 
  69 static                          void
  70 tlv_init_block(
  71         __out   uint32_t        *block)
  72 {
  73         *block = __CPU_TO_LE_32(TLV_TAG_END);
  74 }
  75 
  76 static                          uint32_t
  77 tlv_tag(
  78         __in    tlv_cursor_t    *cursor)
  79 {
  80         uint32_t dword, tag;
  81 
  82         dword = cursor->current[0];
  83         tag = __LE_TO_CPU_32(dword);
  84 
  85         return (tag);
  86 }
  87 
  88 static                          size_t
  89 tlv_length(
  90         __in    tlv_cursor_t    *cursor)
  91 {
  92         uint32_t dword, length;
  93 
  94         if (tlv_tag(cursor) == TLV_TAG_END)
  95                 return (0);
  96 
  97         dword = cursor->current[1];
  98         length = __LE_TO_CPU_32(dword);
  99 
 100         return ((size_t)length);
 101 }
 102 
 103 static                          uint8_t *
 104 tlv_value(
 105         __in    tlv_cursor_t    *cursor)
 106 {
 107         if (tlv_tag(cursor) == TLV_TAG_END)
 108                 return (NULL);
 109 
 110         return ((uint8_t *)(&cursor->current[2]));
 111 }
 112 
 113 static                          uint8_t *
 114 tlv_item(
 115         __in    tlv_cursor_t    *cursor)
 116 {
 117         if (tlv_tag(cursor) == TLV_TAG_END)
 118                 return (NULL);
 119 
 120         return ((uint8_t *)cursor->current);
 121 }
 122 
 123 /*
 124  * TLV item DWORD length is tag + length + value (rounded up to DWORD)
 125  * equivalent to tlv_n_words_for_len in mc-comms tlv.c
 126  */
 127 #define TLV_DWORD_COUNT(length) \
 128         (1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
 129 
 130 
 131 static                          uint32_t *
 132 tlv_next_item_ptr(
 133         __in    tlv_cursor_t    *cursor)
 134 {
 135         uint32_t length;
 136 
 137         length = tlv_length(cursor);
 138 
 139         return (cursor->current + TLV_DWORD_COUNT(length));
 140 }
 141 
 142 static  __checkReturn           efx_rc_t
 143 tlv_advance(
 144         __inout tlv_cursor_t    *cursor)
 145 {
 146         efx_rc_t rc;
 147 
 148         if ((rc = tlv_validate_state(cursor)) != 0)
 149                 goto fail1;
 150 
 151         if (cursor->current == cursor->end) {
 152                 /* No more tags after END tag */
 153                 cursor->current = NULL;
 154                 rc = ENOENT;
 155                 goto fail2;
 156         }
 157 
 158         /* Advance to next item and validate */
 159         cursor->current = tlv_next_item_ptr(cursor);
 160 
 161         if ((rc = tlv_validate_state(cursor)) != 0)
 162                 goto fail3;
 163 
 164         return (0);
 165 
 166 fail3:
 167         EFSYS_PROBE(fail3);
 168 fail2:
 169         EFSYS_PROBE(fail2);
 170 fail1:
 171         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 172 
 173         return (rc);
 174 }
 175 
 176 static                          efx_rc_t
 177 tlv_rewind(
 178         __in    tlv_cursor_t    *cursor)
 179 {
 180         efx_rc_t rc;
 181 
 182         cursor->current = cursor->block;
 183 
 184         if ((rc = tlv_validate_state(cursor)) != 0)
 185                 goto fail1;
 186 
 187         return (0);
 188 
 189 fail1:
 190         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 191 
 192         return (rc);
 193 }
 194 
 195 static                          efx_rc_t
 196 tlv_find(
 197         __inout tlv_cursor_t    *cursor,
 198         __in    uint32_t        tag)
 199 {
 200         efx_rc_t rc;
 201 
 202         rc = tlv_rewind(cursor);
 203         while (rc == 0) {
 204                 if (tlv_tag(cursor) == tag)
 205                         break;
 206 
 207                 rc = tlv_advance(cursor);
 208         }
 209         return (rc);
 210 }
 211 
 212 static  __checkReturn           efx_rc_t
 213 tlv_validate_state(
 214         __inout tlv_cursor_t    *cursor)
 215 {
 216         efx_rc_t rc;
 217 
 218         /* Check cursor position */
 219         if (cursor->current < cursor->block) {
 220                 rc = EINVAL;
 221                 goto fail1;
 222         }
 223         if (cursor->current > cursor->limit) {
 224                 rc = EINVAL;
 225                 goto fail2;
 226         }
 227 
 228         if (tlv_tag(cursor) != TLV_TAG_END) {
 229                 /* Check current item has space for tag and length */
 230                 if (cursor->current > (cursor->limit - 2)) {
 231                         cursor->current = NULL;
 232                         rc = EFAULT;
 233                         goto fail3;
 234                 }
 235 
 236                 /* Check we have value data for current item and another tag */
 237                 if (tlv_next_item_ptr(cursor) > (cursor->limit - 1)) {
 238                         cursor->current = NULL;
 239                         rc = EFAULT;
 240                         goto fail4;
 241                 }
 242         }
 243 
 244         return (0);
 245 
 246 fail4:
 247         EFSYS_PROBE(fail4);
 248 fail3:
 249         EFSYS_PROBE(fail3);
 250 fail2:
 251         EFSYS_PROBE(fail2);
 252 fail1:
 253         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 254 
 255         return (rc);
 256 }
 257 
 258 static                          efx_rc_t
 259 tlv_init_cursor(
 260         __out   tlv_cursor_t    *cursor,
 261         __in    uint32_t        *block,
 262         __in    uint32_t        *limit,
 263         __in    uint32_t        *current)
 264 {
 265         cursor->block        = block;
 266         cursor->limit        = limit;
 267 
 268         cursor->current      = current;
 269         cursor->end  = NULL;
 270 
 271         return (tlv_validate_state(cursor));
 272 }
 273 
 274 static  __checkReturn           efx_rc_t
 275 tlv_init_cursor_from_size(
 276         __out   tlv_cursor_t    *cursor,
 277         __in_bcount(size)
 278                 uint8_t         *block,
 279         __in    size_t          size)
 280 {
 281         uint32_t *limit;
 282         limit = (void *)(block + size - sizeof (uint32_t));
 283         return (tlv_init_cursor(cursor, (void *)block,
 284                 limit, (void *)block));
 285 }
 286 
 287 static  __checkReturn           efx_rc_t
 288 tlv_init_cursor_at_offset(
 289         __out   tlv_cursor_t    *cursor,
 290         __in_bcount(size)
 291                 uint8_t         *block,
 292         __in    size_t          size,
 293         __in    size_t          offset)
 294 {
 295         uint32_t *limit;
 296         uint32_t *current;
 297         limit = (void *)(block + size - sizeof (uint32_t));
 298         current = (void *)(block + offset);
 299         return (tlv_init_cursor(cursor, (void *)block, limit, current));
 300 }
 301 
 302 static  __checkReturn           efx_rc_t
 303 tlv_require_end(
 304         __inout tlv_cursor_t    *cursor)
 305 {
 306         uint32_t *pos;
 307         efx_rc_t rc;
 308 
 309         if (cursor->end == NULL) {
 310                 pos = cursor->current;
 311                 if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
 312                         goto fail1;
 313 
 314                 cursor->end = cursor->current;
 315                 cursor->current = pos;
 316         }
 317 
 318         return (0);
 319 
 320 fail1:
 321         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 322 
 323         return (rc);
 324 }
 325 
 326 static                          size_t
 327 tlv_block_length_used(
 328         __inout tlv_cursor_t    *cursor)
 329 {
 330         efx_rc_t rc;
 331 
 332         if ((rc = tlv_validate_state(cursor)) != 0)
 333                 goto fail1;
 334 
 335         if ((rc = tlv_require_end(cursor)) != 0)
 336                 goto fail2;
 337 
 338         /* Return space used (including the END tag) */
 339         return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
 340 
 341 fail2:
 342         EFSYS_PROBE(fail2);
 343 fail1:
 344         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 345 
 346         return (0);
 347 }
 348 
 349 static          uint32_t *
 350 tlv_last_segment_end(
 351         __in    tlv_cursor_t *cursor)
 352 {
 353         tlv_cursor_t segment_cursor;
 354         uint32_t *last_segment_end = cursor->block;
 355         uint32_t *segment_start = cursor->block;
 356 
 357         /*
 358          * Go through each segment and check that it has an end tag. If there
 359          * is no end tag then the previous segment was the last valid one,
 360          * so return the pointer to its end tag.
 361          */
 362         for (;;) {
 363                 if (tlv_init_cursor(&segment_cursor, segment_start,
 364                     cursor->limit, segment_start) != 0)
 365                         break;
 366                 if (tlv_require_end(&segment_cursor) != 0)
 367                         break;
 368                 last_segment_end = segment_cursor.end;
 369                 segment_start = segment_cursor.end + 1;
 370         }
 371 
 372         return (last_segment_end);
 373 }
 374 
 375 
 376 static                          void
 377 tlv_write(
 378         __in                    tlv_cursor_t *cursor,
 379         __in                    uint32_t tag,
 380         __in_bcount(size)       uint8_t *data,
 381         __in                    size_t size)
 382 {
 383         uint32_t len = (uint32_t)size;
 384         uint32_t *ptr;
 385 
 386         ptr = cursor->current;
 387 
 388         *ptr++ = __CPU_TO_LE_32(tag);
 389         *ptr++ = __CPU_TO_LE_32(len);
 390 
 391         if (len > 0) {
 392                 ptr[(len - 1) / sizeof (uint32_t)] = 0;
 393                 (void) memcpy(ptr, data, len);
 394         }
 395 }
 396 
 397 static  __checkReturn           efx_rc_t
 398 tlv_insert(
 399         __inout tlv_cursor_t    *cursor,
 400         __in    uint32_t        tag,
 401         __in_bcount(size)
 402                 uint8_t         *data,
 403         __in    size_t          size)
 404 {
 405         unsigned int delta;
 406         uint32_t *last_segment_end;
 407         efx_rc_t rc;
 408 
 409         if ((rc = tlv_validate_state(cursor)) != 0)
 410                 goto fail1;
 411 
 412         if ((rc = tlv_require_end(cursor)) != 0)
 413                 goto fail2;
 414 
 415         if (tag == TLV_TAG_END) {
 416                 rc = EINVAL;
 417                 goto fail3;
 418         }
 419 
 420         last_segment_end = tlv_last_segment_end(cursor);
 421 
 422         delta = TLV_DWORD_COUNT(size);
 423         if (last_segment_end + 1 + delta > cursor->limit) {
 424                 rc = ENOSPC;
 425                 goto fail4;
 426         }
 427 
 428         /* Move data up: new space at cursor->current */
 429         (void) memmove(cursor->current + delta, cursor->current,
 430             (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
 431 
 432         /* Adjust the end pointer */
 433         cursor->end += delta;
 434 
 435         /* Write new TLV item */
 436         tlv_write(cursor, tag, data, size);
 437 
 438         return (0);
 439 
 440 fail4:
 441         EFSYS_PROBE(fail4);
 442 fail3:
 443         EFSYS_PROBE(fail3);
 444 fail2:
 445         EFSYS_PROBE(fail2);
 446 fail1:
 447         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 448 
 449         return (rc);
 450 }
 451 
 452 static  __checkReturn           efx_rc_t
 453 tlv_delete(
 454         __inout tlv_cursor_t    *cursor)
 455 {
 456         unsigned int delta;
 457         uint32_t *last_segment_end;
 458         efx_rc_t rc;
 459 
 460         if ((rc = tlv_validate_state(cursor)) != 0)
 461                 goto fail1;
 462 
 463         if (tlv_tag(cursor) == TLV_TAG_END) {
 464                 rc = EINVAL;
 465                 goto fail2;
 466         }
 467 
 468         delta = TLV_DWORD_COUNT(tlv_length(cursor));
 469 
 470         if ((rc = tlv_require_end(cursor)) != 0)
 471                 goto fail3;
 472 
 473         last_segment_end = tlv_last_segment_end(cursor);
 474 
 475         /* Shuffle things down, destroying the item at cursor->current */
 476         (void) memmove(cursor->current, cursor->current + delta,
 477             (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
 478         /* Zero the new space at the end of the TLV chain */
 479         (void) memset(last_segment_end + 1 - delta, 0,
 480             delta * sizeof (uint32_t));
 481         /* Adjust the end pointer */
 482         cursor->end -= delta;
 483 
 484         return (0);
 485 
 486 fail3:
 487         EFSYS_PROBE(fail3);
 488 fail2:
 489         EFSYS_PROBE(fail2);
 490 fail1:
 491         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 492 
 493         return (rc);
 494 }
 495 
 496 static  __checkReturn           efx_rc_t
 497 tlv_modify(
 498         __inout tlv_cursor_t    *cursor,
 499         __in    uint32_t        tag,
 500         __in_bcount(size)
 501                 uint8_t         *data,
 502         __in    size_t          size)
 503 {
 504         uint32_t *pos;
 505         unsigned int old_ndwords;
 506         unsigned int new_ndwords;
 507         unsigned int delta;
 508         uint32_t *last_segment_end;
 509         efx_rc_t rc;
 510 
 511         if ((rc = tlv_validate_state(cursor)) != 0)
 512                 goto fail1;
 513 
 514         if (tlv_tag(cursor) == TLV_TAG_END) {
 515                 rc = EINVAL;
 516                 goto fail2;
 517         }
 518         if (tlv_tag(cursor) != tag) {
 519                 rc = EINVAL;
 520                 goto fail3;
 521         }
 522 
 523         old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
 524         new_ndwords = TLV_DWORD_COUNT(size);
 525 
 526         if ((rc = tlv_require_end(cursor)) != 0)
 527                 goto fail4;
 528 
 529         last_segment_end = tlv_last_segment_end(cursor);
 530 
 531         if (new_ndwords > old_ndwords) {
 532                 /* Expand space used for TLV item */
 533                 delta = new_ndwords - old_ndwords;
 534                 pos = cursor->current + old_ndwords;
 535 
 536                 if (last_segment_end + 1 + delta > cursor->limit) {
 537                         rc = ENOSPC;
 538                         goto fail5;
 539                 }
 540 
 541                 /* Move up: new space at (cursor->current + old_ndwords) */
 542                 (void) memmove(pos + delta, pos,
 543                     (last_segment_end + 1 - pos) * sizeof (uint32_t));
 544 
 545                 /* Adjust the end pointer */
 546                 cursor->end += delta;
 547 
 548         } else if (new_ndwords < old_ndwords) {
 549                 /* Shrink space used for TLV item */
 550                 delta = old_ndwords - new_ndwords;
 551                 pos = cursor->current + new_ndwords;
 552 
 553                 /* Move down: remove words at (cursor->current + new_ndwords) */
 554                 (void) memmove(pos, pos + delta,
 555                     (last_segment_end + 1 - pos) * sizeof (uint32_t));
 556 
 557                 /* Zero the new space at the end of the TLV chain */
 558                 (void) memset(last_segment_end + 1 - delta, 0,
 559                     delta * sizeof (uint32_t));
 560 
 561                 /* Adjust the end pointer */
 562                 cursor->end -= delta;
 563         }
 564 
 565         /* Write new data */
 566         tlv_write(cursor, tag, data, size);
 567 
 568         return (0);
 569 
 570 fail5:
 571         EFSYS_PROBE(fail5);
 572 fail4:
 573         EFSYS_PROBE(fail4);
 574 fail3:
 575         EFSYS_PROBE(fail3);
 576 fail2:
 577         EFSYS_PROBE(fail2);
 578 fail1:
 579         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 580 
 581         return (rc);
 582 }
 583 
 584 static uint32_t checksum_tlv_partition(
 585         __in    nvram_partition_t *partition)
 586 {
 587         tlv_cursor_t *cursor;
 588         uint32_t *ptr;
 589         uint32_t *end;
 590         uint32_t csum;
 591         size_t len;
 592 
 593         cursor = &partition->tlv_cursor;
 594         len = tlv_block_length_used(cursor);
 595         EFSYS_ASSERT3U((len & 3), ==, 0);
 596 
 597         csum = 0;
 598         ptr = partition->data;
 599         end = &ptr[len >> 2];
 600 
 601         while (ptr < end)
 602                 csum += __LE_TO_CPU_32(*ptr++);
 603 
 604         return (csum);
 605 }
 606 
 607 static  __checkReturn           efx_rc_t
 608 tlv_update_partition_len_and_cks(
 609         __in    tlv_cursor_t *cursor)
 610 {
 611         efx_rc_t rc;
 612         nvram_partition_t partition;
 613         struct tlv_partition_header *header;
 614         struct tlv_partition_trailer *trailer;
 615         size_t new_len;
 616 
 617         /*
 618          * We just modified the partition, so the total length may not be
 619          * valid. Don't use tlv_find(), which performs some sanity checks
 620          * that may fail here.
 621          */
 622         partition.data = cursor->block;
 623         (void) memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
 624         header = (struct tlv_partition_header *)partition.data;
 625         /* Sanity check. */
 626         if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
 627                 rc = EFAULT;
 628                 goto fail1;
 629         }
 630         new_len =  tlv_block_length_used(&partition.tlv_cursor);
 631         if (new_len == 0) {
 632                 rc = EFAULT;
 633                 goto fail2;
 634         }
 635         header->total_length = __CPU_TO_LE_32(new_len);
 636         /* Ensure the modified partition always has a new generation count. */
 637         header->generation = __CPU_TO_LE_32(
 638             __LE_TO_CPU_32(header->generation) + 1);
 639 
 640         trailer = (void *)((uint8_t *)header +
 641             new_len - sizeof (*trailer) - sizeof (uint32_t));
 642         trailer->generation = header->generation;
 643         trailer->checksum = __CPU_TO_LE_32(
 644             __LE_TO_CPU_32(trailer->checksum) -
 645             checksum_tlv_partition(&partition));
 646 
 647         return (0);
 648 
 649 fail2:
 650         EFSYS_PROBE(fail2);
 651 fail1:
 652         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 653 
 654         return (rc);
 655 }
 656 
 657 /* Validate buffer contents (before writing to flash) */
 658         __checkReturn           efx_rc_t
 659 ef10_nvram_buffer_validate(
 660         __in                    efx_nic_t *enp,
 661         __in                    uint32_t partn,
 662         __in_bcount(partn_size) caddr_t partn_data,
 663         __in                    size_t partn_size)
 664 {
 665         tlv_cursor_t cursor;
 666         struct tlv_partition_header *header;
 667         struct tlv_partition_trailer *trailer;
 668         size_t total_length;
 669         uint32_t cksum;
 670         int pos;
 671         efx_rc_t rc;
 672 
 673         _NOTE(ARGUNUSED(enp, partn));
 674         EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
 675 
 676         if ((partn_data == NULL) || (partn_size == 0)) {
 677                 rc = EINVAL;
 678                 goto fail1;
 679         }
 680 
 681         /* The partition header must be the first item (at offset zero) */
 682         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
 683                     partn_size)) != 0) {
 684                 rc = EFAULT;
 685                 goto fail2;
 686         }
 687         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
 688                 rc = EINVAL;
 689                 goto fail3;
 690         }
 691         header = (void *)tlv_item(&cursor);
 692 
 693         /* Check TLV partition length (includes the END tag) */
 694         total_length = __LE_TO_CPU_32(header->total_length);
 695         if (total_length > partn_size) {
 696                 rc = EFBIG;
 697                 goto fail4;
 698         }
 699 
 700         /* Check partition ends with PARTITION_TRAILER and END tags */
 701         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
 702                 rc = EINVAL;
 703                 goto fail5;
 704         }
 705         trailer = (void *)tlv_item(&cursor);
 706 
 707         if ((rc = tlv_advance(&cursor)) != 0) {
 708                 rc = EINVAL;
 709                 goto fail6;
 710         }
 711         if (tlv_tag(&cursor) != TLV_TAG_END) {
 712                 rc = EINVAL;
 713                 goto fail7;
 714         }
 715 
 716         /* Check generation counts are consistent */
 717         if (trailer->generation != header->generation) {
 718                 rc = EINVAL;
 719                 goto fail8;
 720         }
 721 
 722         /* Verify partition checksum */
 723         cksum = 0;
 724         for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
 725                 cksum += *((uint32_t *)(void *)(partn_data + pos));
 726         }
 727         if (cksum != 0) {
 728                 rc = EINVAL;
 729                 goto fail9;
 730         }
 731 
 732         return (0);
 733 
 734 fail9:
 735         EFSYS_PROBE(fail9);
 736 fail8:
 737         EFSYS_PROBE(fail8);
 738 fail7:
 739         EFSYS_PROBE(fail7);
 740 fail6:
 741         EFSYS_PROBE(fail6);
 742 fail5:
 743         EFSYS_PROBE(fail5);
 744 fail4:
 745         EFSYS_PROBE(fail4);
 746 fail3:
 747         EFSYS_PROBE(fail3);
 748 fail2:
 749         EFSYS_PROBE(fail2);
 750 fail1:
 751         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 752 
 753         return (rc);
 754 }
 755 
 756 
 757 
 758         __checkReturn           efx_rc_t
 759 ef10_nvram_buffer_create(
 760         __in                    efx_nic_t *enp,
 761         __in                    uint16_t partn_type,
 762         __in_bcount(partn_size) caddr_t partn_data,
 763         __in                    size_t partn_size)
 764 {
 765         uint32_t *buf = (void *)partn_data;
 766         efx_rc_t rc;
 767         tlv_cursor_t cursor;
 768         struct tlv_partition_header header;
 769         struct tlv_partition_trailer trailer;
 770 
 771         unsigned min_buf_size = sizeof (struct tlv_partition_header) +
 772             sizeof (struct tlv_partition_trailer);
 773         if (partn_size < min_buf_size) {
 774                 rc = EINVAL;
 775                 goto fail1;
 776         }
 777 
 778         (void) memset(buf, 0xff, partn_size);
 779 
 780         tlv_init_block(buf);
 781         if ((rc = tlv_init_cursor(&cursor, buf,
 782             (void *)((uint8_t *)buf + partn_size),
 783             buf)) != 0) {
 784                 goto fail2;
 785         }
 786 
 787         header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
 788         header.length = __CPU_TO_LE_32(sizeof (header) - 8);
 789         header.type_id = __CPU_TO_LE_16(partn_type);
 790         header.preset = 0;
 791         header.generation = __CPU_TO_LE_32(1);
 792         header.total_length = 0;  /* This will be fixed below. */
 793         if ((rc = tlv_insert(
 794             &cursor, TLV_TAG_PARTITION_HEADER,
 795             (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
 796                 goto fail3;
 797         if ((rc = tlv_advance(&cursor)) != 0)
 798                 goto fail4;
 799 
 800         trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
 801         trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
 802         trailer.generation = header.generation;
 803         trailer.checksum = 0;  /* This will be fixed below. */
 804         if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
 805             (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
 806                 goto fail5;
 807 
 808         if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
 809                 goto fail6;
 810 
 811         /* Check that the partition is valid. */
 812         if ((rc = ef10_nvram_buffer_validate(enp, partn_type,
 813             partn_data, partn_size)) != 0)
 814                 goto fail7;
 815 
 816         return (0);
 817 
 818 fail7:
 819         EFSYS_PROBE(fail7);
 820 fail6:
 821         EFSYS_PROBE(fail6);
 822 fail5:
 823         EFSYS_PROBE(fail5);
 824 fail4:
 825         EFSYS_PROBE(fail4);
 826 fail3:
 827         EFSYS_PROBE(fail3);
 828 fail2:
 829         EFSYS_PROBE(fail2);
 830 fail1:
 831         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 832 
 833         return (rc);
 834 }
 835 
 836 static                  uint32_t
 837 byte_offset(
 838         __in            uint32_t *position,
 839         __in            uint32_t *base)
 840 {
 841         return (uint32_t)((uintptr_t)position - (uintptr_t)base);
 842 }
 843 
 844         __checkReturn           efx_rc_t
 845 ef10_nvram_buffer_find_item_start(
 846         __in_bcount(buffer_size)
 847                                 caddr_t bufferp,
 848         __in                    size_t buffer_size,
 849         __out                   uint32_t *startp)
 850 {
 851         // Read past partition header to find start address of the first key
 852         tlv_cursor_t cursor;
 853         efx_rc_t rc;
 854 
 855         /* A PARTITION_HEADER tag must be the first item (at offset zero) */
 856         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
 857                         buffer_size)) != 0) {
 858                 rc = EFAULT;
 859                 goto fail1;
 860         }
 861         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
 862                 rc = EINVAL;
 863                 goto fail2;
 864         }
 865 
 866         if ((rc = tlv_advance(&cursor)) != 0) {
 867                 rc = EINVAL;
 868                 goto fail3;
 869         }
 870         *startp = byte_offset(cursor.current, cursor.block);
 871 
 872         if ((rc = tlv_require_end(&cursor)) != 0)
 873                 goto fail4;
 874 
 875         return (0);
 876 
 877 fail4:
 878         EFSYS_PROBE(fail4);
 879 fail3:
 880         EFSYS_PROBE(fail3);
 881 fail2:
 882         EFSYS_PROBE(fail2);
 883 fail1:
 884         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 885 
 886         return (rc);
 887 }
 888 
 889         __checkReturn           efx_rc_t
 890 ef10_nvram_buffer_find_end(
 891         __in_bcount(buffer_size)
 892                                 caddr_t bufferp,
 893         __in                    size_t buffer_size,
 894         __in                    uint32_t offset,
 895         __out                   uint32_t *endp)
 896 {
 897         // Read to end of partition
 898         tlv_cursor_t cursor;
 899         efx_rc_t rc;
 900 
 901         _NOTE(ARGUNUSED(offset));
 902 
 903         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
 904                         buffer_size)) != 0) {
 905                 rc = EFAULT;
 906                 goto fail1;
 907         }
 908 
 909         if ((rc = tlv_require_end(&cursor)) != 0)
 910                 goto fail2;
 911 
 912         *endp = byte_offset(tlv_last_segment_end(&cursor)+1, cursor.block);
 913 
 914         return (0);
 915 
 916 fail2:
 917         EFSYS_PROBE(fail2);
 918 fail1:
 919         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 920 
 921         return (rc);
 922 }
 923 
 924         __checkReturn   __success(return != B_FALSE)    boolean_t
 925 ef10_nvram_buffer_find_item(
 926         __in_bcount(buffer_size)
 927                                 caddr_t bufferp,
 928         __in                    size_t buffer_size,
 929         __in                    uint32_t offset,
 930         __out                   uint32_t *startp,
 931         __out                   uint32_t *lengthp)
 932 {
 933         // Find TLV at offset and return key start and length
 934         tlv_cursor_t cursor;
 935         uint32_t tag;
 936 
 937         if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
 938                         buffer_size, offset) != 0) {
 939                 return (B_FALSE);
 940         }
 941 
 942         while (tlv_item(&cursor) != NULL) {
 943                 tag = tlv_tag(&cursor);
 944                 if (tag == TLV_TAG_PARTITION_HEADER ||
 945                     tag == TLV_TAG_PARTITION_TRAILER) {
 946                         if (tlv_advance(&cursor) != 0) {
 947                                 break;
 948                         }
 949                         continue;
 950                 }
 951                 *startp = byte_offset(cursor.current, cursor.block);
 952                 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
 953                     cursor.current);
 954                 return (B_TRUE);
 955         }
 956 
 957         return (B_FALSE);
 958 }
 959 
 960         __checkReturn           efx_rc_t
 961 ef10_nvram_buffer_get_item(
 962         __in_bcount(buffer_size)
 963                                 caddr_t bufferp,
 964         __in                    size_t buffer_size,
 965         __in                    uint32_t offset,
 966         __in                    uint32_t length,
 967         __out_bcount_part(item_max_size, *lengthp)
 968                                 caddr_t itemp,
 969         __in                    size_t item_max_size,
 970         __out                   uint32_t *lengthp)
 971 {
 972         efx_rc_t rc;
 973         tlv_cursor_t cursor;
 974         uint32_t item_length;
 975 
 976         if (item_max_size < length) {
 977                 rc = ENOSPC;
 978                 goto fail1;
 979         }
 980 
 981         if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
 982                         buffer_size, offset)) != 0) {
 983                 goto fail2;
 984         }
 985 
 986         item_length = tlv_length(&cursor);
 987         if (length < item_length) {
 988                 rc = ENOSPC;
 989                 goto fail3;
 990         }
 991         (void) memcpy(itemp, tlv_value(&cursor), item_length);
 992 
 993         *lengthp = item_length;
 994 
 995         return (0);
 996 
 997 fail3:
 998         EFSYS_PROBE(fail3);
 999 fail2:
1000         EFSYS_PROBE(fail2);
1001 fail1:
1002         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1003 
1004         return (rc);
1005 }
1006 
1007         __checkReturn           efx_rc_t
1008 ef10_nvram_buffer_insert_item(
1009         __in_bcount(buffer_size)
1010                                 caddr_t bufferp,
1011         __in                    size_t buffer_size,
1012         __in                    uint32_t offset,
1013         __in_bcount(length)     caddr_t keyp,
1014         __in                    uint32_t length,
1015         __out                   uint32_t *lengthp)
1016 {
1017         efx_rc_t rc;
1018         tlv_cursor_t cursor;
1019 
1020         if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1021                         buffer_size, offset)) != 0) {
1022                 goto fail1;
1023         }
1024 
1025         rc = tlv_insert(&cursor, TLV_TAG_LICENSE, (uint8_t *)keyp, length);
1026 
1027         if (rc != 0) {
1028                 goto fail2;
1029         }
1030 
1031         *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1032                     cursor.current);
1033 
1034         return (0);
1035 
1036 fail2:
1037         EFSYS_PROBE(fail2);
1038 fail1:
1039         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1040 
1041         return (rc);
1042 }
1043 
1044         __checkReturn           efx_rc_t
1045 ef10_nvram_buffer_delete_item(
1046         __in_bcount(buffer_size)
1047                                 caddr_t bufferp,
1048         __in                    size_t buffer_size,
1049         __in                    uint32_t offset,
1050         __in                    uint32_t length,
1051         __in                    uint32_t end)
1052 {
1053         efx_rc_t rc;
1054         tlv_cursor_t cursor;
1055         _NOTE(ARGUNUSED(length, end))
1056 
1057         if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1058                         buffer_size, offset)) != 0) {
1059                 goto fail1;
1060         }
1061 
1062         if ((rc = tlv_delete(&cursor)) != 0)
1063                 goto fail2;
1064 
1065         return (0);
1066 
1067 fail2:
1068         EFSYS_PROBE(fail2);
1069 fail1:
1070         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1071 
1072         return (rc);
1073 }
1074 
1075         __checkReturn           efx_rc_t
1076 ef10_nvram_buffer_finish(
1077         __in_bcount(buffer_size)
1078                                 caddr_t bufferp,
1079         __in                    size_t buffer_size)
1080 {
1081         efx_rc_t rc;
1082         tlv_cursor_t cursor;
1083 
1084         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
1085                         buffer_size)) != 0) {
1086                 rc = EFAULT;
1087                 goto fail1;
1088         }
1089 
1090         if ((rc = tlv_require_end(&cursor)) != 0)
1091                 goto fail2;
1092 
1093         if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
1094                 goto fail3;
1095 
1096         return (0);
1097 
1098 fail3:
1099         EFSYS_PROBE(fail3);
1100 fail2:
1101         EFSYS_PROBE(fail2);
1102 fail1:
1103         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1104 
1105         return (rc);
1106 }
1107 
1108 
1109 
1110 /*
1111  * Read and validate a segment from a partition. A segment is a complete
1112  * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
1113  * be multiple segments in a partition, so seg_offset allows segments
1114  * beyond the first to be read.
1115  */
1116 static  __checkReturn                   efx_rc_t
1117 ef10_nvram_read_tlv_segment(
1118         __in                            efx_nic_t *enp,
1119         __in                            uint32_t partn,
1120         __in                            size_t seg_offset,
1121         __in_bcount(max_seg_size)       caddr_t seg_data,
1122         __in                            size_t max_seg_size)
1123 {
1124         tlv_cursor_t cursor;
1125         struct tlv_partition_header *header;
1126         struct tlv_partition_trailer *trailer;
1127         size_t total_length;
1128         uint32_t cksum;
1129         int pos;
1130         efx_rc_t rc;
1131 
1132         EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
1133 
1134         if ((seg_data == NULL) || (max_seg_size == 0)) {
1135                 rc = EINVAL;
1136                 goto fail1;
1137         }
1138 
1139         /* Read initial chunk of the segment, starting at offset */
1140         if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
1141                     EF10_NVRAM_CHUNK,
1142                     MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
1143                 goto fail2;
1144         }
1145 
1146         /* A PARTITION_HEADER tag must be the first item at the given offset */
1147         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1148                     max_seg_size)) != 0) {
1149                 rc = EFAULT;
1150                 goto fail3;
1151         }
1152         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1153                 rc = EINVAL;
1154                 goto fail4;
1155         }
1156         header = (void *)tlv_item(&cursor);
1157 
1158         /* Check TLV segment length (includes the END tag) */
1159         total_length = __LE_TO_CPU_32(header->total_length);
1160         if (total_length > max_seg_size) {
1161                 rc = EFBIG;
1162                 goto fail5;
1163         }
1164 
1165         /* Read the remaining segment content */
1166         if (total_length > EF10_NVRAM_CHUNK) {
1167                 if ((rc = ef10_nvram_partn_read_mode(enp, partn,
1168                             seg_offset + EF10_NVRAM_CHUNK,
1169                             seg_data + EF10_NVRAM_CHUNK,
1170                             total_length - EF10_NVRAM_CHUNK,
1171                             MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
1172                         goto fail6;
1173         }
1174 
1175         /* Check segment ends with PARTITION_TRAILER and END tags */
1176         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1177                 rc = EINVAL;
1178                 goto fail7;
1179         }
1180         trailer = (void *)tlv_item(&cursor);
1181 
1182         if ((rc = tlv_advance(&cursor)) != 0) {
1183                 rc = EINVAL;
1184                 goto fail8;
1185         }
1186         if (tlv_tag(&cursor) != TLV_TAG_END) {
1187                 rc = EINVAL;
1188                 goto fail9;
1189         }
1190 
1191         /* Check data read from segment is consistent */
1192         if (trailer->generation != header->generation) {
1193                 /*
1194                  * The partition data may have been modified between successive
1195                  * MCDI NVRAM_READ requests by the MC or another PCI function.
1196                  *
1197                  * The caller must retry to obtain consistent partition data.
1198                  */
1199                 rc = EAGAIN;
1200                 goto fail10;
1201         }
1202 
1203         /* Verify segment checksum */
1204         cksum = 0;
1205         for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
1206                 cksum += *((uint32_t *)(void *)(seg_data + pos));
1207         }
1208         if (cksum != 0) {
1209                 rc = EINVAL;
1210                 goto fail11;
1211         }
1212 
1213         return (0);
1214 
1215 fail11:
1216         EFSYS_PROBE(fail11);
1217 fail10:
1218         EFSYS_PROBE(fail10);
1219 fail9:
1220         EFSYS_PROBE(fail9);
1221 fail8:
1222         EFSYS_PROBE(fail8);
1223 fail7:
1224         EFSYS_PROBE(fail7);
1225 fail6:
1226         EFSYS_PROBE(fail6);
1227 fail5:
1228         EFSYS_PROBE(fail5);
1229 fail4:
1230         EFSYS_PROBE(fail4);
1231 fail3:
1232         EFSYS_PROBE(fail3);
1233 fail2:
1234         EFSYS_PROBE(fail2);
1235 fail1:
1236         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1237 
1238         return (rc);
1239 }
1240 
1241 /*
1242  * Read a single TLV item from a host memory
1243  * buffer containing a TLV formatted segment.
1244  */
1245         __checkReturn           efx_rc_t
1246 ef10_nvram_buf_read_tlv(
1247         __in                            efx_nic_t *enp,
1248         __in_bcount(max_seg_size)       caddr_t seg_data,
1249         __in                            size_t max_seg_size,
1250         __in                            uint32_t tag,
1251         __deref_out_bcount_opt(*sizep)  caddr_t *datap,
1252         __out                           size_t *sizep)
1253 {
1254         tlv_cursor_t cursor;
1255         caddr_t data;
1256         size_t length;
1257         caddr_t value;
1258         efx_rc_t rc;
1259 
1260         if ((seg_data == NULL) || (max_seg_size == 0)) {
1261                 rc = EINVAL;
1262                 goto fail1;
1263         }
1264 
1265         /* Find requested TLV tag in segment data */
1266         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1267                     max_seg_size)) != 0) {
1268                 rc = EFAULT;
1269                 goto fail2;
1270         }
1271         if ((rc = tlv_find(&cursor, tag)) != 0) {
1272                 rc = ENOENT;
1273                 goto fail3;
1274         }
1275         value = (caddr_t)tlv_value(&cursor);
1276         length = tlv_length(&cursor);
1277 
1278         if (length == 0)
1279                 data = NULL;
1280         else {
1281                 /* Copy out data from TLV item */
1282                 EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
1283                 if (data == NULL) {
1284                         rc = ENOMEM;
1285                         goto fail4;
1286                 }
1287                 (void) memcpy(data, value, length);
1288         }
1289 
1290         *datap = data;
1291         *sizep = length;
1292 
1293         return (0);
1294 
1295 fail4:
1296         EFSYS_PROBE(fail4);
1297 fail3:
1298         EFSYS_PROBE(fail3);
1299 fail2:
1300         EFSYS_PROBE(fail2);
1301 fail1:
1302         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1303 
1304         return (rc);
1305 }
1306 
1307 /* Read a single TLV item from the first segment in a TLV formatted partition */
1308         __checkReturn           efx_rc_t
1309 ef10_nvram_partn_read_tlv(
1310         __in                                    efx_nic_t *enp,
1311         __in                                    uint32_t partn,
1312         __in                                    uint32_t tag,
1313         __deref_out_bcount_opt(*seg_sizep)      caddr_t *seg_datap,
1314         __out                                   size_t *seg_sizep)
1315 {
1316         caddr_t seg_data = NULL;
1317         size_t partn_size = 0;
1318         size_t length;
1319         caddr_t data;
1320         int retry;
1321         efx_rc_t rc;
1322 
1323         /* Allocate sufficient memory for the entire partition */
1324         if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1325                 goto fail1;
1326 
1327         if (partn_size == 0) {
1328                 rc = ENOENT;
1329                 goto fail2;
1330         }
1331 
1332         EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
1333         if (seg_data == NULL) {
1334                 rc = ENOMEM;
1335                 goto fail3;
1336         }
1337 
1338         /*
1339          * Read the first segment in a TLV partition. Retry until consistent
1340          * segment contents are returned. Inconsistent data may be read if:
1341          *  a) the segment contents are invalid
1342          *  b) the MC has rebooted while we were reading the partition
1343          *  c) the partition has been modified while we were reading it
1344          * Limit retry attempts to ensure forward progress.
1345          */
1346         retry = 10;
1347         do {
1348                 rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
1349                     seg_data, partn_size);
1350         } while ((rc == EAGAIN) && (--retry > 0));
1351 
1352         if (rc != 0) {
1353                 /* Failed to obtain consistent segment data */
1354                 goto fail4;
1355         }
1356 
1357         if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
1358                     tag, &data, &length)) != 0)
1359                 goto fail5;
1360 
1361         EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1362 
1363         *seg_datap = data;
1364         *seg_sizep = length;
1365 
1366         return (0);
1367 
1368 fail5:
1369         EFSYS_PROBE(fail5);
1370 fail4:
1371         EFSYS_PROBE(fail4);
1372 
1373         EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1374 fail3:
1375         EFSYS_PROBE(fail3);
1376 fail2:
1377         EFSYS_PROBE(fail2);
1378 fail1:
1379         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1380 
1381         return (rc);
1382 }
1383 
1384 /* Compute the size of a segment. */
1385         static  __checkReturn   efx_rc_t
1386 ef10_nvram_buf_segment_size(
1387         __in                    caddr_t seg_data,
1388         __in                    size_t max_seg_size,
1389         __out                   size_t *seg_sizep)
1390 {
1391         efx_rc_t rc;
1392         tlv_cursor_t cursor;
1393         struct tlv_partition_header *header;
1394         uint32_t cksum;
1395         int pos;
1396         uint32_t *end_tag_position;
1397         uint32_t segment_length;
1398 
1399         /* A PARTITION_HEADER tag must be the first item at the given offset */
1400         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1401                     max_seg_size)) != 0) {
1402                 rc = EFAULT;
1403                 goto fail1;
1404         }
1405         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1406                 rc = EINVAL;
1407                 goto fail2;
1408         }
1409         header = (void *)tlv_item(&cursor);
1410 
1411         /* Check TLV segment length (includes the END tag) */
1412         *seg_sizep = __LE_TO_CPU_32(header->total_length);
1413         if (*seg_sizep > max_seg_size) {
1414                 rc = EFBIG;
1415                 goto fail3;
1416         }
1417 
1418         /* Check segment ends with PARTITION_TRAILER and END tags */
1419         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1420                 rc = EINVAL;
1421                 goto fail4;
1422         }
1423 
1424         if ((rc = tlv_advance(&cursor)) != 0) {
1425                 rc = EINVAL;
1426                 goto fail5;
1427         }
1428         if (tlv_tag(&cursor) != TLV_TAG_END) {
1429                 rc = EINVAL;
1430                 goto fail6;
1431         }
1432         end_tag_position = cursor.current;
1433 
1434         /* Verify segment checksum */
1435         cksum = 0;
1436         for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
1437                 cksum += *((uint32_t *)(void *)(seg_data + pos));
1438         }
1439         if (cksum != 0) {
1440                 rc = EINVAL;
1441                 goto fail7;
1442         }
1443 
1444         /*
1445          * Calculate total length from HEADER to END tags and compare to
1446          * max_seg_size and the total_length field in the HEADER tag.
1447          */
1448         segment_length = tlv_block_length_used(&cursor);
1449 
1450         if (segment_length > max_seg_size) {
1451                 rc = EINVAL;
1452                 goto fail8;
1453         }
1454 
1455         if (segment_length != *seg_sizep) {
1456                 rc = EINVAL;
1457                 goto fail9;
1458         }
1459 
1460         /* Skip over the first HEADER tag. */
1461         rc = tlv_rewind(&cursor);
1462         rc = tlv_advance(&cursor);
1463 
1464         while (rc == 0) {
1465                 if (tlv_tag(&cursor) == TLV_TAG_END) {
1466                         /* Check that the END tag is the one found earlier. */
1467                         if (cursor.current != end_tag_position)
1468                                 goto fail10;
1469                         break;
1470                 }
1471                 /* Check for duplicate HEADER tags before the END tag. */
1472                 if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
1473                         rc = EINVAL;
1474                         goto fail11;
1475                 }
1476 
1477                 rc = tlv_advance(&cursor);
1478         }
1479         if (rc != 0)
1480                 goto fail12;
1481 
1482         return (0);
1483 
1484 fail12:
1485         EFSYS_PROBE(fail12);
1486 fail11:
1487         EFSYS_PROBE(fail11);
1488 fail10:
1489         EFSYS_PROBE(fail10);
1490 fail9:
1491         EFSYS_PROBE(fail9);
1492 fail8:
1493         EFSYS_PROBE(fail8);
1494 fail7:
1495         EFSYS_PROBE(fail7);
1496 fail6:
1497         EFSYS_PROBE(fail6);
1498 fail5:
1499         EFSYS_PROBE(fail5);
1500 fail4:
1501         EFSYS_PROBE(fail4);
1502 fail3:
1503         EFSYS_PROBE(fail3);
1504 fail2:
1505         EFSYS_PROBE(fail2);
1506 fail1:
1507         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1508 
1509         return (rc);
1510 }
1511 
1512 /*
1513  * Add or update a single TLV item in a host memory buffer containing a TLV
1514  * formatted segment. Historically partitions consisted of only one segment.
1515  */
1516         __checkReturn                   efx_rc_t
1517 ef10_nvram_buf_write_tlv(
1518         __inout_bcount(max_seg_size)    caddr_t seg_data,
1519         __in                            size_t max_seg_size,
1520         __in                            uint32_t tag,
1521         __in_bcount(tag_size)           caddr_t tag_data,
1522         __in                            size_t tag_size,
1523         __out                           size_t *total_lengthp)
1524 {
1525         tlv_cursor_t cursor;
1526         struct tlv_partition_header *header;
1527         struct tlv_partition_trailer *trailer;
1528         uint32_t generation;
1529         uint32_t cksum;
1530         int pos;
1531         efx_rc_t rc;
1532 
1533         /* A PARTITION_HEADER tag must be the first item (at offset zero) */
1534         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1535                         max_seg_size)) != 0) {
1536                 rc = EFAULT;
1537                 goto fail1;
1538         }
1539         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1540                 rc = EINVAL;
1541                 goto fail2;
1542         }
1543         header = (void *)tlv_item(&cursor);
1544 
1545         /* Update the TLV chain to contain the new data */
1546         if ((rc = tlv_find(&cursor, tag)) == 0) {
1547                 /* Modify existing TLV item */
1548                 if ((rc = tlv_modify(&cursor, tag,
1549                             (uint8_t *)tag_data, tag_size)) != 0)
1550                         goto fail3;
1551         } else {
1552                 /* Insert a new TLV item before the PARTITION_TRAILER */
1553                 rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1554                 if (rc != 0) {
1555                         rc = EINVAL;
1556                         goto fail4;
1557                 }
1558                 if ((rc = tlv_insert(&cursor, tag,
1559                             (uint8_t *)tag_data, tag_size)) != 0) {
1560                         rc = EINVAL;
1561                         goto fail5;
1562                 }
1563         }
1564 
1565         /* Find the trailer tag */
1566         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1567                 rc = EINVAL;
1568                 goto fail6;
1569         }
1570         trailer = (void *)tlv_item(&cursor);
1571 
1572         /* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1573         *total_lengthp = tlv_block_length_used(&cursor);
1574         if (*total_lengthp > max_seg_size) {
1575                 rc = ENOSPC;
1576                 goto fail7;
1577         }
1578         generation = __LE_TO_CPU_32(header->generation) + 1;
1579 
1580         header->total_length = __CPU_TO_LE_32(*total_lengthp);
1581         header->generation   = __CPU_TO_LE_32(generation);
1582         trailer->generation  = __CPU_TO_LE_32(generation);
1583 
1584         /* Recompute PARTITION_TRAILER checksum */
1585         trailer->checksum = 0;
1586         cksum = 0;
1587         for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1588                 cksum += *((uint32_t *)(void *)(seg_data + pos));
1589         }
1590         trailer->checksum = ~cksum + 1;
1591 
1592         return (0);
1593 
1594 fail7:
1595         EFSYS_PROBE(fail7);
1596 fail6:
1597         EFSYS_PROBE(fail6);
1598 fail5:
1599         EFSYS_PROBE(fail5);
1600 fail4:
1601         EFSYS_PROBE(fail4);
1602 fail3:
1603         EFSYS_PROBE(fail3);
1604 fail2:
1605         EFSYS_PROBE(fail2);
1606 fail1:
1607         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1608 
1609         return (rc);
1610 }
1611 
1612 /*
1613  * Add or update a single TLV item in the first segment of a TLV formatted
1614  * dynamic config partition. The first segment is the current active
1615  * configuration.
1616  */
1617         __checkReturn           efx_rc_t
1618 ef10_nvram_partn_write_tlv(
1619         __in                    efx_nic_t *enp,
1620         __in                    uint32_t partn,
1621         __in                    uint32_t tag,
1622         __in_bcount(size)       caddr_t data,
1623         __in                    size_t size)
1624 {
1625         return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1626             size, B_FALSE);
1627 }
1628 
1629 /*
1630  * Read a segment from nvram at the given offset into a buffer (segment_data)
1631  * and optionally write a new tag to it.
1632  */
1633         static  __checkReturn   efx_rc_t
1634 ef10_nvram_segment_write_tlv(
1635         __in                    efx_nic_t *enp,
1636         __in                    uint32_t partn,
1637         __in                    uint32_t tag,
1638         __in_bcount(size)       caddr_t data,
1639         __in                    size_t size,
1640         __inout                 caddr_t *seg_datap,
1641         __inout                 size_t *partn_offsetp,
1642         __inout                 size_t *src_remain_lenp,
1643         __inout                 size_t *dest_remain_lenp,
1644         __in                    boolean_t write)
1645 {
1646         efx_rc_t rc;
1647         efx_rc_t status;
1648         size_t original_segment_size;
1649         size_t modified_segment_size;
1650 
1651         /*
1652          * Read the segment from NVRAM into the segment_data buffer and validate
1653          * it, returning if it does not validate. This is not a failure unless
1654          * this is the first segment in a partition. In this case the caller
1655          * must propogate the error.
1656          */
1657         status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1658             *seg_datap, *src_remain_lenp);
1659         if (status != 0)
1660                 return (EINVAL);
1661 
1662         status = ef10_nvram_buf_segment_size(*seg_datap,
1663             *src_remain_lenp, &original_segment_size);
1664         if (status != 0)
1665                 return (EINVAL);
1666 
1667         if (write) {
1668                 /* Update the contents of the segment in the buffer */
1669                 if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1670                         *dest_remain_lenp, tag, data, size,
1671                         &modified_segment_size)) != 0)
1672                         goto fail1;
1673                 *dest_remain_lenp -= modified_segment_size;
1674                 *seg_datap += modified_segment_size;
1675         } else {
1676                 /*
1677                  * We won't modify this segment, but still need to update the
1678                  * remaining lengths and pointers.
1679                  */
1680                 *dest_remain_lenp -= original_segment_size;
1681                 *seg_datap += original_segment_size;
1682         }
1683 
1684         *partn_offsetp += original_segment_size;
1685         *src_remain_lenp -= original_segment_size;
1686 
1687         return (0);
1688 
1689 fail1:
1690         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1691 
1692         return (rc);
1693 }
1694 
1695 /*
1696  * Add or update a single TLV item in either the first segment or in all
1697  * segments in a TLV formatted dynamic config partition. Dynamic config
1698  * partitions on boards that support RFID are divided into a number of segments,
1699  * each formatted like a partition, with header, trailer and end tags. The first
1700  * segment is the current active configuration.
1701  *
1702  * The segments are initialised by manftest and each contain a different
1703  * configuration e.g. firmware variant. The firmware can be instructed
1704  * via RFID to copy a segment to replace the first segment, hence changing the
1705  * active configuration.  This allows ops to change the configuration of a board
1706  * prior to shipment using RFID.
1707  *
1708  * Changes to the dynamic config may need to be written to all segments (e.g.
1709  * firmware versions) or just the first segment (changes to the active
1710  * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1711  * If only the first segment is written the code still needs to be aware of the
1712  * possible presence of subsequent segments as writing to a segment may cause
1713  * its size to increase, which would overwrite the subsequent segments and
1714  * invalidate them.
1715  */
1716         __checkReturn           efx_rc_t
1717 ef10_nvram_partn_write_segment_tlv(
1718         __in                    efx_nic_t *enp,
1719         __in                    uint32_t partn,
1720         __in                    uint32_t tag,
1721         __in_bcount(size)       caddr_t data,
1722         __in                    size_t size,
1723         __in                    boolean_t all_segments)
1724 {
1725         size_t partn_size = 0;
1726         caddr_t partn_data;
1727         size_t total_length = 0;
1728         efx_rc_t rc;
1729         size_t current_offset = 0;
1730         size_t remaining_original_length;
1731         size_t remaining_modified_length;
1732         caddr_t segment_data;
1733 
1734         EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1735 
1736         /* Allocate sufficient memory for the entire partition */
1737         if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1738                 goto fail1;
1739 
1740         EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1741         if (partn_data == NULL) {
1742                 rc = ENOMEM;
1743                 goto fail2;
1744         }
1745 
1746         remaining_original_length = partn_size;
1747         remaining_modified_length = partn_size;
1748         segment_data = partn_data;
1749 
1750         /* Lock the partition */
1751         if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1752                 goto fail3;
1753 
1754         /* Iterate over each (potential) segment to update it. */
1755         do {
1756                 boolean_t write = all_segments || current_offset == 0;
1757 
1758                 rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1759                     &segment_data, &current_offset, &remaining_original_length,
1760                     &remaining_modified_length, write);
1761                 if (rc != 0) {
1762                         if (current_offset == 0) {
1763                                 /*
1764                                  * If no data has been read then the first
1765                                  * segment is invalid, which is an error.
1766                                  */
1767                                 goto fail4;
1768                         }
1769                         break;
1770                 }
1771         } while (current_offset < partn_size);
1772 
1773         total_length = (uintptr_t)segment_data - (uintptr_t)partn_data;
1774 
1775         /*
1776          * We've run out of space.  This should actually be dealt with by
1777          * ef10_nvram_buf_write_tlv returning ENOSPC.
1778          */
1779         if (total_length > partn_size) {
1780                 rc = ENOSPC;
1781                 goto fail5;
1782         }
1783 
1784         /* Erase the whole partition in NVRAM */
1785         if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1786                 goto fail6;
1787 
1788         /* Write new partition contents from the buffer to NVRAM */
1789         if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1790                     total_length)) != 0)
1791                 goto fail7;
1792 
1793         /* Unlock the partition */
1794         ef10_nvram_partn_unlock(enp, partn);
1795 
1796         EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1797 
1798         return (0);
1799 
1800 fail7:
1801         EFSYS_PROBE(fail7);
1802 fail6:
1803         EFSYS_PROBE(fail6);
1804 fail5:
1805         EFSYS_PROBE(fail5);
1806 fail4:
1807         EFSYS_PROBE(fail4);
1808 
1809         ef10_nvram_partn_unlock(enp, partn);
1810 fail3:
1811         EFSYS_PROBE(fail3);
1812 
1813         EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1814 fail2:
1815         EFSYS_PROBE(fail2);
1816 fail1:
1817         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1818 
1819         return (rc);
1820 }
1821 
1822 /*
1823  * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1824  * not the data used by the segments in the partition.
1825  */
1826         __checkReturn           efx_rc_t
1827 ef10_nvram_partn_size(
1828         __in                    efx_nic_t *enp,
1829         __in                    uint32_t partn,
1830         __out                   size_t *sizep)
1831 {
1832         efx_rc_t rc;
1833 
1834         if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1835             NULL, NULL, NULL)) != 0)
1836                 goto fail1;
1837 
1838         return (0);
1839 
1840 fail1:
1841         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1842 
1843         return (rc);
1844 }
1845 
1846         __checkReturn           efx_rc_t
1847 ef10_nvram_partn_lock(
1848         __in                    efx_nic_t *enp,
1849         __in                    uint32_t partn)
1850 {
1851         efx_rc_t rc;
1852 
1853         if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1854                 goto fail1;
1855 
1856         return (0);
1857 
1858 fail1:
1859         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1860 
1861         return (rc);
1862 }
1863 
1864         __checkReturn           efx_rc_t
1865 ef10_nvram_partn_read_mode(
1866         __in                    efx_nic_t *enp,
1867         __in                    uint32_t partn,
1868         __in                    unsigned int offset,
1869         __out_bcount(size)      caddr_t data,
1870         __in                    size_t size,
1871         __in                    uint32_t mode)
1872 {
1873         size_t chunk;
1874         efx_rc_t rc;
1875 
1876         while (size > 0) {
1877                 chunk = MIN(size, EF10_NVRAM_CHUNK);
1878 
1879                 if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
1880                             data, chunk, mode)) != 0) {
1881                         goto fail1;
1882                 }
1883 
1884                 size -= chunk;
1885                 data += chunk;
1886                 offset += chunk;
1887         }
1888 
1889         return (0);
1890 
1891 fail1:
1892         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1893 
1894         return (rc);
1895 }
1896 
1897         __checkReturn           efx_rc_t
1898 ef10_nvram_partn_read(
1899         __in                    efx_nic_t *enp,
1900         __in                    uint32_t partn,
1901         __in                    unsigned int offset,
1902         __out_bcount(size)      caddr_t data,
1903         __in                    size_t size)
1904 {
1905         /*
1906          * Read requests which come in through the EFX API expect to
1907          * read the current, active partition.
1908          */
1909         return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
1910                             MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
1911 }
1912 
1913         __checkReturn           efx_rc_t
1914 ef10_nvram_partn_erase(
1915         __in                    efx_nic_t *enp,
1916         __in                    uint32_t partn,
1917         __in                    unsigned int offset,
1918         __in                    size_t size)
1919 {
1920         efx_rc_t rc;
1921         uint32_t erase_size;
1922 
1923         if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1924             &erase_size, NULL)) != 0)
1925                 goto fail1;
1926 
1927         if (erase_size == 0) {
1928                 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
1929                         goto fail2;
1930         } else {
1931                 if (size % erase_size != 0) {
1932                         rc = EINVAL;
1933                         goto fail3;
1934                 }
1935                 while (size > 0) {
1936                         if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
1937                             erase_size)) != 0)
1938                                 goto fail4;
1939                         offset += erase_size;
1940                         size -= erase_size;
1941                 }
1942         }
1943 
1944         return (0);
1945 
1946 fail4:
1947         EFSYS_PROBE(fail4);
1948 fail3:
1949         EFSYS_PROBE(fail3);
1950 fail2:
1951         EFSYS_PROBE(fail2);
1952 fail1:
1953         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1954 
1955         return (rc);
1956 }
1957 
1958         __checkReturn           efx_rc_t
1959 ef10_nvram_partn_write(
1960         __in                    efx_nic_t *enp,
1961         __in                    uint32_t partn,
1962         __in                    unsigned int offset,
1963         __out_bcount(size)      caddr_t data,
1964         __in                    size_t size)
1965 {
1966         size_t chunk;
1967         uint32_t write_size;
1968         efx_rc_t rc;
1969 
1970         if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1971             NULL, &write_size)) != 0)
1972                 goto fail1;
1973 
1974         if (write_size != 0) {
1975                 /*
1976                  * Check that the size is a multiple of the write chunk size if
1977                  * the write chunk size is available.
1978                  */
1979                 if (size % write_size != 0) {
1980                         rc = EINVAL;
1981                         goto fail2;
1982                 }
1983         } else {
1984                 write_size = EF10_NVRAM_CHUNK;
1985         }
1986 
1987         while (size > 0) {
1988                 chunk = MIN(size, write_size);
1989 
1990                 if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
1991                             data, chunk)) != 0) {
1992                         goto fail3;
1993                 }
1994 
1995                 size -= chunk;
1996                 data += chunk;
1997                 offset += chunk;
1998         }
1999 
2000         return (0);
2001 
2002 fail3:
2003         EFSYS_PROBE(fail3);
2004 fail2:
2005         EFSYS_PROBE(fail2);
2006 fail1:
2007         EFSYS_PROBE1(fail1, efx_rc_t, rc);
2008 
2009         return (rc);
2010 }
2011 
2012                                 void
2013 ef10_nvram_partn_unlock(
2014         __in                    efx_nic_t *enp,
2015         __in                    uint32_t partn)
2016 {
2017         boolean_t reboot;
2018         efx_rc_t rc;
2019 
2020         reboot = B_FALSE;
2021         if ((rc = efx_mcdi_nvram_update_finish(enp, partn, reboot)) != 0)
2022                 goto fail1;
2023 
2024         return;
2025 
2026 fail1:
2027         EFSYS_PROBE1(fail1, efx_rc_t, rc);
2028 }
2029 
2030         __checkReturn           efx_rc_t
2031 ef10_nvram_partn_set_version(
2032         __in                    efx_nic_t *enp,
2033         __in                    uint32_t partn,
2034         __in_ecount(4)          uint16_t version[4])
2035 {
2036         struct tlv_partition_version partn_version;
2037         size_t size;
2038         efx_rc_t rc;
2039 
2040         /* Add or modify partition version TLV item */
2041         partn_version.version_w = __CPU_TO_LE_16(version[0]);
2042         partn_version.version_x = __CPU_TO_LE_16(version[1]);
2043         partn_version.version_y = __CPU_TO_LE_16(version[2]);
2044         partn_version.version_z = __CPU_TO_LE_16(version[3]);
2045 
2046         size = sizeof (partn_version) - (2 * sizeof (uint32_t));
2047 
2048         /* Write the version number to all segments in the partition */
2049         if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
2050                     NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
2051                     TLV_TAG_PARTITION_VERSION(partn),
2052                     (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
2053                 goto fail1;
2054 
2055         return (0);
2056 
2057 fail1:
2058         EFSYS_PROBE1(fail1, efx_rc_t, rc);
2059 
2060         return (rc);
2061 }
2062 
2063 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
2064 
2065 #if EFSYS_OPT_NVRAM
2066 
2067 typedef struct ef10_parttbl_entry_s {
2068         unsigned int            partn;
2069         unsigned int            port;
2070         efx_nvram_type_t        nvtype;
2071 } ef10_parttbl_entry_t;
2072 
2073 /* Translate EFX NVRAM types to firmware partition types */
2074 static ef10_parttbl_entry_t hunt_parttbl[] = {
2075         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         1, EFX_NVRAM_MC_FIRMWARE},
2076         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         2, EFX_NVRAM_MC_FIRMWARE},
2077         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         3, EFX_NVRAM_MC_FIRMWARE},
2078         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         4, EFX_NVRAM_MC_FIRMWARE},
2079         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
2080         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
2081         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
2082         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
2083         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       1, EFX_NVRAM_BOOTROM},
2084         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       2, EFX_NVRAM_BOOTROM},
2085         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       3, EFX_NVRAM_BOOTROM},
2086         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       4, EFX_NVRAM_BOOTROM},
2087         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
2088         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1, 2, EFX_NVRAM_BOOTROM_CFG},
2089         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2, 3, EFX_NVRAM_BOOTROM_CFG},
2090         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 4, EFX_NVRAM_BOOTROM_CFG},
2091         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      1, EFX_NVRAM_DYNAMIC_CFG},
2092         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      2, EFX_NVRAM_DYNAMIC_CFG},
2093         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      3, EFX_NVRAM_DYNAMIC_CFG},
2094         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      4, EFX_NVRAM_DYNAMIC_CFG},
2095         {NVRAM_PARTITION_TYPE_FPGA,                1, EFX_NVRAM_FPGA},
2096         {NVRAM_PARTITION_TYPE_FPGA,                2, EFX_NVRAM_FPGA},
2097         {NVRAM_PARTITION_TYPE_FPGA,                3, EFX_NVRAM_FPGA},
2098         {NVRAM_PARTITION_TYPE_FPGA,                4, EFX_NVRAM_FPGA},
2099         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         1, EFX_NVRAM_FPGA_BACKUP},
2100         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         2, EFX_NVRAM_FPGA_BACKUP},
2101         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         3, EFX_NVRAM_FPGA_BACKUP},
2102         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         4, EFX_NVRAM_FPGA_BACKUP},
2103         {NVRAM_PARTITION_TYPE_LICENSE,             1, EFX_NVRAM_LICENSE},
2104         {NVRAM_PARTITION_TYPE_LICENSE,             2, EFX_NVRAM_LICENSE},
2105         {NVRAM_PARTITION_TYPE_LICENSE,             3, EFX_NVRAM_LICENSE},
2106         {NVRAM_PARTITION_TYPE_LICENSE,             4, EFX_NVRAM_LICENSE}
2107 };
2108 
2109 static ef10_parttbl_entry_t medford_parttbl[] = {
2110         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         1, EFX_NVRAM_MC_FIRMWARE},
2111         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         2, EFX_NVRAM_MC_FIRMWARE},
2112         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         3, EFX_NVRAM_MC_FIRMWARE},
2113         {NVRAM_PARTITION_TYPE_MC_FIRMWARE,         4, EFX_NVRAM_MC_FIRMWARE},
2114         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
2115         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
2116         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
2117         {NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
2118         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       1, EFX_NVRAM_BOOTROM},
2119         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       2, EFX_NVRAM_BOOTROM},
2120         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       3, EFX_NVRAM_BOOTROM},
2121         {NVRAM_PARTITION_TYPE_EXPANSION_ROM,       4, EFX_NVRAM_BOOTROM},
2122         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
2123         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 2, EFX_NVRAM_BOOTROM_CFG},
2124         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 3, EFX_NVRAM_BOOTROM_CFG},
2125         {NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 4, EFX_NVRAM_BOOTROM_CFG},
2126         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      1, EFX_NVRAM_DYNAMIC_CFG},
2127         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      2, EFX_NVRAM_DYNAMIC_CFG},
2128         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      3, EFX_NVRAM_DYNAMIC_CFG},
2129         {NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,      4, EFX_NVRAM_DYNAMIC_CFG},
2130         {NVRAM_PARTITION_TYPE_FPGA,                1, EFX_NVRAM_FPGA},
2131         {NVRAM_PARTITION_TYPE_FPGA,                2, EFX_NVRAM_FPGA},
2132         {NVRAM_PARTITION_TYPE_FPGA,                3, EFX_NVRAM_FPGA},
2133         {NVRAM_PARTITION_TYPE_FPGA,                4, EFX_NVRAM_FPGA},
2134         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         1, EFX_NVRAM_FPGA_BACKUP},
2135         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         2, EFX_NVRAM_FPGA_BACKUP},
2136         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         3, EFX_NVRAM_FPGA_BACKUP},
2137         {NVRAM_PARTITION_TYPE_FPGA_BACKUP,         4, EFX_NVRAM_FPGA_BACKUP},
2138         {NVRAM_PARTITION_TYPE_LICENSE,             1, EFX_NVRAM_LICENSE},
2139         {NVRAM_PARTITION_TYPE_LICENSE,             2, EFX_NVRAM_LICENSE},
2140         {NVRAM_PARTITION_TYPE_LICENSE,             3, EFX_NVRAM_LICENSE},
2141         {NVRAM_PARTITION_TYPE_LICENSE,             4, EFX_NVRAM_LICENSE}
2142 };
2143 
2144 static  __checkReturn           efx_rc_t
2145 ef10_parttbl_get(
2146         __in                    efx_nic_t *enp,
2147         __out                   ef10_parttbl_entry_t **parttblp,
2148         __out                   size_t *parttbl_rowsp)
2149 {
2150         switch (enp->en_family) {
2151         case EFX_FAMILY_HUNTINGTON:
2152                 *parttblp = hunt_parttbl;
2153                 *parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
2154                 break;
2155 
2156         case EFX_FAMILY_MEDFORD:
2157                 *parttblp = medford_parttbl;
2158                 *parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
2159                 break;
2160 
2161         default:
2162                 EFSYS_ASSERT(B_FALSE);
2163                 return (EINVAL);
2164         }
2165         return (0);
2166 }
2167 
2168         __checkReturn           efx_rc_t
2169 ef10_nvram_type_to_partn(
2170         __in                    efx_nic_t *enp,
2171         __in                    efx_nvram_type_t type,
2172         __out                   uint32_t *partnp)
2173 {
2174         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2175         ef10_parttbl_entry_t *parttbl = NULL;
2176         size_t parttbl_rows = 0;
2177         unsigned int i;
2178 
2179         EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
2180         EFSYS_ASSERT(partnp != NULL);
2181 
2182         if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2183                 for (i = 0; i < parttbl_rows; i++) {
2184                         ef10_parttbl_entry_t *entry = &parttbl[i];
2185 
2186                         if (entry->nvtype == type &&
2187                             entry->port == emip->emi_port) {
2188                                 *partnp = entry->partn;
2189                                 return (0);
2190                         }
2191                 }
2192         }
2193 
2194         return (ENOTSUP);
2195 }
2196 
2197 #if EFSYS_OPT_DIAG
2198 
2199 static  __checkReturn           efx_rc_t
2200 ef10_nvram_partn_to_type(
2201         __in                    efx_nic_t *enp,
2202         __in                    uint32_t partn,
2203         __out                   efx_nvram_type_t *typep)
2204 {
2205         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2206         ef10_parttbl_entry_t *parttbl = NULL;
2207         size_t parttbl_rows = 0;
2208         unsigned int i;
2209 
2210         EFSYS_ASSERT(typep != NULL);
2211 
2212         if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2213                 for (i = 0; i < parttbl_rows; i++) {
2214                         ef10_parttbl_entry_t *entry = &parttbl[i];
2215 
2216                         if (entry->partn == partn &&
2217                             entry->port == emip->emi_port) {
2218                                 *typep = entry->nvtype;
2219                                 return (0);
2220                         }
2221                 }
2222         }
2223 
2224         return (ENOTSUP);
2225 }
2226 
2227         __checkReturn           efx_rc_t
2228 ef10_nvram_test(
2229         __in                    efx_nic_t *enp)
2230 {
2231         efx_nvram_type_t type;
2232         unsigned int npartns = 0;
2233         uint32_t *partns = NULL;
2234         size_t size;
2235         unsigned int i;
2236         efx_rc_t rc;
2237 
2238         /* Read available partitions from NVRAM partition map */
2239         size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
2240         EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
2241         if (partns == NULL) {
2242                 rc = ENOMEM;
2243                 goto fail1;
2244         }
2245 
2246         if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
2247                     &npartns)) != 0) {
2248                 goto fail2;
2249         }
2250 
2251         for (i = 0; i < npartns; i++) {
2252                 /* Check if the partition is supported for this port */
2253                 if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
2254                         continue;
2255 
2256                 if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
2257                         goto fail3;
2258         }
2259 
2260         EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2261         return (0);
2262 
2263 fail3:
2264         EFSYS_PROBE(fail3);
2265 fail2:
2266         EFSYS_PROBE(fail2);
2267         EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2268 fail1:
2269         EFSYS_PROBE1(fail1, efx_rc_t, rc);
2270         return (rc);
2271 }
2272 
2273 #endif  /* EFSYS_OPT_DIAG */
2274 
2275         __checkReturn           efx_rc_t
2276 ef10_nvram_partn_get_version(
2277         __in                    efx_nic_t *enp,
2278         __in                    uint32_t partn,
2279         __out                   uint32_t *subtypep,
2280         __out_ecount(4)         uint16_t version[4])
2281 {
2282         efx_rc_t rc;
2283 
2284         /* FIXME: get highest partn version from all ports */
2285         /* FIXME: return partn description if available */
2286 
2287         if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
2288                     version, NULL, 0)) != 0)
2289                 goto fail1;
2290 
2291         return (0);
2292 
2293 fail1:
2294         EFSYS_PROBE1(fail1, efx_rc_t, rc);
2295 
2296         return (rc);
2297 }
2298 
2299         __checkReturn           efx_rc_t
2300 ef10_nvram_partn_rw_start(
2301         __in                    efx_nic_t *enp,
2302         __in                    uint32_t partn,
2303         __out                   size_t *chunk_sizep)
2304 {
2305         efx_rc_t rc;
2306 
2307         if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
2308                 goto fail1;
2309 
2310         if (chunk_sizep != NULL)
2311                 *chunk_sizep = EF10_NVRAM_CHUNK;
2312 
2313         return (0);
2314 
2315 fail1:
2316         EFSYS_PROBE1(fail1, efx_rc_t, rc);
2317 
2318         return (rc);
2319 }
2320 
2321                                 void
2322 ef10_nvram_partn_rw_finish(
2323         __in                    efx_nic_t *enp,
2324         __in                    uint32_t partn)
2325 {
2326         ef10_nvram_partn_unlock(enp, partn);
2327 }
2328 
2329 #endif  /* EFSYS_OPT_NVRAM */
2330 
2331 #endif  /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */