The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/sfxge/common/ef10_nvram.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2012-2016 Solarflare Communications Inc.
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions are met:
    7  *
    8  * 1. Redistributions of source code must retain the above copyright notice,
    9  *    this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright notice,
   11  *    this list of conditions and the following disclaimer in the documentation
   12  *    and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
   16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
   18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
   24  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   25  *
   26  * The views and conclusions contained in the software and documentation are
   27  * those of the authors and should not be interpreted as representing official
   28  * policies, either expressed or implied, of the FreeBSD Project.
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __FBSDID("$FreeBSD$");
   33 
   34 #include "efx.h"
   35 #include "efx_impl.h"
   36 
   37 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
   38 
   39 #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
   40 
   41 #include "ef10_tlv_layout.h"
   42 
   43 /* Cursor for TLV partition format */
   44 typedef struct tlv_cursor_s {
   45         uint32_t        *block;                 /* Base of data block */
   46         uint32_t        *current;               /* Cursor position */
   47         uint32_t        *end;                   /* End tag position */
   48         uint32_t        *limit;                 /* Last dword of data block */
   49 } tlv_cursor_t;
   50 
   51 typedef struct nvram_partition_s {
   52         uint16_t type;
   53         uint8_t chip_select;
   54         uint8_t flags;
   55         /*
   56          * The full length of the NVRAM partition.
   57          * This is different from tlv_partition_header.total_length,
   58          *  which can be smaller.
   59          */
   60         uint32_t length;
   61         uint32_t erase_size;
   62         uint32_t *data;
   63         tlv_cursor_t tlv_cursor;
   64 } nvram_partition_t;
   65 
   66 static  __checkReturn           efx_rc_t
   67 tlv_validate_state(
   68         __inout                 tlv_cursor_t *cursor);
   69 
   70 static                          void
   71 tlv_init_block(
   72         __out   uint32_t        *block)
   73 {
   74         *block = __CPU_TO_LE_32(TLV_TAG_END);
   75 }
   76 
   77 static                          uint32_t
   78 tlv_tag(
   79         __in    tlv_cursor_t    *cursor)
   80 {
   81         uint32_t dword, tag;
   82 
   83         dword = cursor->current[0];
   84         tag = __LE_TO_CPU_32(dword);
   85 
   86         return (tag);
   87 }
   88 
   89 static                          size_t
   90 tlv_length(
   91         __in    tlv_cursor_t    *cursor)
   92 {
   93         uint32_t dword, length;
   94 
   95         if (tlv_tag(cursor) == TLV_TAG_END)
   96                 return (0);
   97 
   98         dword = cursor->current[1];
   99         length = __LE_TO_CPU_32(dword);
  100 
  101         return ((size_t)length);
  102 }
  103 
  104 static                          uint8_t *
  105 tlv_value(
  106         __in    tlv_cursor_t    *cursor)
  107 {
  108         if (tlv_tag(cursor) == TLV_TAG_END)
  109                 return (NULL);
  110 
  111         return ((uint8_t *)(&cursor->current[2]));
  112 }
  113 
  114 static                          uint8_t *
  115 tlv_item(
  116         __in    tlv_cursor_t    *cursor)
  117 {
  118         if (tlv_tag(cursor) == TLV_TAG_END)
  119                 return (NULL);
  120 
  121         return ((uint8_t *)cursor->current);
  122 }
  123 
  124 /*
  125  * TLV item DWORD length is tag + length + value (rounded up to DWORD)
  126  * equivalent to tlv_n_words_for_len in mc-comms tlv.c
  127  */
  128 #define TLV_DWORD_COUNT(length) \
  129         (1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
  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 - 1)) {
  231                         cursor->current = NULL;
  232                         rc = EFAULT;
  233                         goto fail3;
  234                 }
  235 
  236                 /* Check we have value data for current item and an END tag */
  237                 if (tlv_next_item_ptr(cursor) > cursor->limit) {
  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 = (uint32_t *)(block + size - sizeof (uint32_t));
  283         return (tlv_init_cursor(cursor, (uint32_t *)block,
  284                 limit, (uint32_t *)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 = (uint32_t *)(block + size - sizeof (uint32_t));
  298         current = (uint32_t *)(block + offset);
  299         return (tlv_init_cursor(cursor, (uint32_t *)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 static                          uint32_t *
  376 tlv_write(
  377         __in                    tlv_cursor_t *cursor,
  378         __in                    uint32_t tag,
  379         __in_bcount(size)       uint8_t *data,
  380         __in                    size_t size)
  381 {
  382         uint32_t len = size;
  383         uint32_t *ptr;
  384 
  385         ptr = cursor->current;
  386 
  387         *ptr++ = __CPU_TO_LE_32(tag);
  388         *ptr++ = __CPU_TO_LE_32(len);
  389 
  390         if (len > 0) {
  391                 ptr[(len - 1) / sizeof (uint32_t)] = 0;
  392                 memcpy(ptr, data, len);
  393                 ptr += EFX_P2ROUNDUP(uint32_t, len,
  394                     sizeof (uint32_t)) / sizeof (*ptr);
  395         }
  396 
  397         return (ptr);
  398 }
  399 
  400 static  __checkReturn           efx_rc_t
  401 tlv_insert(
  402         __inout tlv_cursor_t    *cursor,
  403         __in    uint32_t        tag,
  404         __in_bcount(size)
  405                 uint8_t         *data,
  406         __in    size_t          size)
  407 {
  408         unsigned int delta;
  409         uint32_t *last_segment_end;
  410         efx_rc_t rc;
  411 
  412         if ((rc = tlv_validate_state(cursor)) != 0)
  413                 goto fail1;
  414 
  415         if ((rc = tlv_require_end(cursor)) != 0)
  416                 goto fail2;
  417 
  418         if (tag == TLV_TAG_END) {
  419                 rc = EINVAL;
  420                 goto fail3;
  421         }
  422 
  423         last_segment_end = tlv_last_segment_end(cursor);
  424 
  425         delta = TLV_DWORD_COUNT(size);
  426         if (last_segment_end + 1 + delta > cursor->limit) {
  427                 rc = ENOSPC;
  428                 goto fail4;
  429         }
  430 
  431         /* Move data up: new space at cursor->current */
  432         memmove(cursor->current + delta, cursor->current,
  433             (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
  434 
  435         /* Adjust the end pointer */
  436         cursor->end += delta;
  437 
  438         /* Write new TLV item */
  439         tlv_write(cursor, tag, data, size);
  440 
  441         return (0);
  442 
  443 fail4:
  444         EFSYS_PROBE(fail4);
  445 fail3:
  446         EFSYS_PROBE(fail3);
  447 fail2:
  448         EFSYS_PROBE(fail2);
  449 fail1:
  450         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  451 
  452         return (rc);
  453 }
  454 
  455 static  __checkReturn           efx_rc_t
  456 tlv_delete(
  457         __inout tlv_cursor_t    *cursor)
  458 {
  459         unsigned int delta;
  460         uint32_t *last_segment_end;
  461         efx_rc_t rc;
  462 
  463         if ((rc = tlv_validate_state(cursor)) != 0)
  464                 goto fail1;
  465 
  466         if (tlv_tag(cursor) == TLV_TAG_END) {
  467                 rc = EINVAL;
  468                 goto fail2;
  469         }
  470 
  471         delta = TLV_DWORD_COUNT(tlv_length(cursor));
  472 
  473         if ((rc = tlv_require_end(cursor)) != 0)
  474                 goto fail3;
  475 
  476         last_segment_end = tlv_last_segment_end(cursor);
  477 
  478         /* Shuffle things down, destroying the item at cursor->current */
  479         memmove(cursor->current, cursor->current + delta,
  480             (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
  481         /* Zero the new space at the end of the TLV chain */
  482         memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t));
  483         /* Adjust the end pointer */
  484         cursor->end -= delta;
  485 
  486         return (0);
  487 
  488 fail3:
  489         EFSYS_PROBE(fail3);
  490 fail2:
  491         EFSYS_PROBE(fail2);
  492 fail1:
  493         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  494 
  495         return (rc);
  496 }
  497 
  498 static  __checkReturn           efx_rc_t
  499 tlv_modify(
  500         __inout tlv_cursor_t    *cursor,
  501         __in    uint32_t        tag,
  502         __in_bcount(size)
  503                 uint8_t         *data,
  504         __in    size_t          size)
  505 {
  506         uint32_t *pos;
  507         unsigned int old_ndwords;
  508         unsigned int new_ndwords;
  509         unsigned int delta;
  510         uint32_t *last_segment_end;
  511         efx_rc_t rc;
  512 
  513         if ((rc = tlv_validate_state(cursor)) != 0)
  514                 goto fail1;
  515 
  516         if (tlv_tag(cursor) == TLV_TAG_END) {
  517                 rc = EINVAL;
  518                 goto fail2;
  519         }
  520         if (tlv_tag(cursor) != tag) {
  521                 rc = EINVAL;
  522                 goto fail3;
  523         }
  524 
  525         old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
  526         new_ndwords = TLV_DWORD_COUNT(size);
  527 
  528         if ((rc = tlv_require_end(cursor)) != 0)
  529                 goto fail4;
  530 
  531         last_segment_end = tlv_last_segment_end(cursor);
  532 
  533         if (new_ndwords > old_ndwords) {
  534                 /* Expand space used for TLV item */
  535                 delta = new_ndwords - old_ndwords;
  536                 pos = cursor->current + old_ndwords;
  537 
  538                 if (last_segment_end + 1 + delta > cursor->limit) {
  539                         rc = ENOSPC;
  540                         goto fail5;
  541                 }
  542 
  543                 /* Move up: new space at (cursor->current + old_ndwords) */
  544                 memmove(pos + delta, pos,
  545                     (last_segment_end + 1 - pos) * sizeof (uint32_t));
  546 
  547                 /* Adjust the end pointer */
  548                 cursor->end += delta;
  549 
  550         } else if (new_ndwords < old_ndwords) {
  551                 /* Shrink space used for TLV item */
  552                 delta = old_ndwords - new_ndwords;
  553                 pos = cursor->current + new_ndwords;
  554 
  555                 /* Move down: remove words at (cursor->current + new_ndwords) */
  556                 memmove(pos, pos + delta,
  557                     (last_segment_end + 1 - pos) * sizeof (uint32_t));
  558 
  559                 /* Zero the new space at the end of the TLV chain */
  560                 memset(last_segment_end + 1 - delta, 0,
  561                     delta * sizeof (uint32_t));
  562 
  563                 /* Adjust the end pointer */
  564                 cursor->end -= delta;
  565         }
  566 
  567         /* Write new data */
  568         tlv_write(cursor, tag, data, size);
  569 
  570         return (0);
  571 
  572 fail5:
  573         EFSYS_PROBE(fail5);
  574 fail4:
  575         EFSYS_PROBE(fail4);
  576 fail3:
  577         EFSYS_PROBE(fail3);
  578 fail2:
  579         EFSYS_PROBE(fail2);
  580 fail1:
  581         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  582 
  583         return (rc);
  584 }
  585 
  586 static uint32_t checksum_tlv_partition(
  587         __in    nvram_partition_t *partition)
  588 {
  589         tlv_cursor_t *cursor;
  590         uint32_t *ptr;
  591         uint32_t *end;
  592         uint32_t csum;
  593         size_t len;
  594 
  595         cursor = &partition->tlv_cursor;
  596         len = tlv_block_length_used(cursor);
  597         EFSYS_ASSERT3U((len & 3), ==, 0);
  598 
  599         csum = 0;
  600         ptr = partition->data;
  601         end = &ptr[len >> 2];
  602 
  603         while (ptr < end)
  604                 csum += __LE_TO_CPU_32(*ptr++);
  605 
  606         return (csum);
  607 }
  608 
  609 static  __checkReturn           efx_rc_t
  610 tlv_update_partition_len_and_cks(
  611         __in    tlv_cursor_t *cursor)
  612 {
  613         efx_rc_t rc;
  614         nvram_partition_t partition;
  615         struct tlv_partition_header *header;
  616         struct tlv_partition_trailer *trailer;
  617         size_t new_len;
  618 
  619         /*
  620          * We just modified the partition, so the total length may not be
  621          * valid. Don't use tlv_find(), which performs some sanity checks
  622          * that may fail here.
  623          */
  624         partition.data = cursor->block;
  625         memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
  626         header = (struct tlv_partition_header *)partition.data;
  627         /* Sanity check. */
  628         if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
  629                 rc = EFAULT;
  630                 goto fail1;
  631         }
  632         new_len =  tlv_block_length_used(&partition.tlv_cursor);
  633         if (new_len == 0) {
  634                 rc = EFAULT;
  635                 goto fail2;
  636         }
  637         header->total_length = __CPU_TO_LE_32(new_len);
  638         /* Ensure the modified partition always has a new generation count. */
  639         header->generation = __CPU_TO_LE_32(
  640             __LE_TO_CPU_32(header->generation) + 1);
  641 
  642         trailer = (struct tlv_partition_trailer *)((uint8_t *)header +
  643             new_len - sizeof (*trailer) - sizeof (uint32_t));
  644         trailer->generation = header->generation;
  645         trailer->checksum = __CPU_TO_LE_32(
  646             __LE_TO_CPU_32(trailer->checksum) -
  647             checksum_tlv_partition(&partition));
  648 
  649         return (0);
  650 
  651 fail2:
  652         EFSYS_PROBE(fail2);
  653 fail1:
  654         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  655 
  656         return (rc);
  657 }
  658 
  659 /* Validate buffer contents (before writing to flash) */
  660         __checkReturn           efx_rc_t
  661 ef10_nvram_buffer_validate(
  662         __in                    uint32_t partn,
  663         __in_bcount(partn_size) caddr_t partn_data,
  664         __in                    size_t partn_size)
  665 {
  666         tlv_cursor_t cursor;
  667         struct tlv_partition_header *header;
  668         struct tlv_partition_trailer *trailer;
  669         size_t total_length;
  670         uint32_t cksum;
  671         int pos;
  672         efx_rc_t rc;
  673 
  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 = (struct tlv_partition_header *)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 header matches partn */
  701         if (__LE_TO_CPU_16(header->type_id) != partn) {
  702                 rc = EINVAL;
  703                 goto fail5;
  704         }
  705 
  706         /* Check partition ends with PARTITION_TRAILER and END tags */
  707         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
  708                 rc = EINVAL;
  709                 goto fail6;
  710         }
  711         trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
  712 
  713         if ((rc = tlv_advance(&cursor)) != 0) {
  714                 rc = EINVAL;
  715                 goto fail7;
  716         }
  717         if (tlv_tag(&cursor) != TLV_TAG_END) {
  718                 rc = EINVAL;
  719                 goto fail8;
  720         }
  721 
  722         /* Check generation counts are consistent */
  723         if (trailer->generation != header->generation) {
  724                 rc = EINVAL;
  725                 goto fail9;
  726         }
  727 
  728         /* Verify partition checksum */
  729         cksum = 0;
  730         for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
  731                 cksum += *((uint32_t *)(partn_data + pos));
  732         }
  733         if (cksum != 0) {
  734                 rc = EINVAL;
  735                 goto fail10;
  736         }
  737 
  738         return (0);
  739 
  740 fail10:
  741         EFSYS_PROBE(fail10);
  742 fail9:
  743         EFSYS_PROBE(fail9);
  744 fail8:
  745         EFSYS_PROBE(fail8);
  746 fail7:
  747         EFSYS_PROBE(fail7);
  748 fail6:
  749         EFSYS_PROBE(fail6);
  750 fail5:
  751         EFSYS_PROBE(fail5);
  752 fail4:
  753         EFSYS_PROBE(fail4);
  754 fail3:
  755         EFSYS_PROBE(fail3);
  756 fail2:
  757         EFSYS_PROBE(fail2);
  758 fail1:
  759         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  760 
  761         return (rc);
  762 }
  763 
  764                         void
  765 ef10_nvram_buffer_init(
  766         __out_bcount(buffer_size)
  767                                 caddr_t bufferp,
  768         __in                    size_t buffer_size)
  769 {
  770         uint32_t *buf = (uint32_t *)bufferp;
  771 
  772         memset(buf, 0xff, buffer_size);
  773 
  774         tlv_init_block(buf);
  775 }
  776 
  777         __checkReturn           efx_rc_t
  778 ef10_nvram_buffer_create(
  779         __in                    uint32_t partn_type,
  780         __out_bcount(partn_size)
  781                                 caddr_t partn_data,
  782         __in                    size_t partn_size)
  783 {
  784         uint32_t *buf = (uint32_t *)partn_data;
  785         efx_rc_t rc;
  786         tlv_cursor_t cursor;
  787         struct tlv_partition_header header;
  788         struct tlv_partition_trailer trailer;
  789 
  790         unsigned int min_buf_size = sizeof (struct tlv_partition_header) +
  791             sizeof (struct tlv_partition_trailer);
  792         if (partn_size < min_buf_size) {
  793                 rc = EINVAL;
  794                 goto fail1;
  795         }
  796 
  797         ef10_nvram_buffer_init(partn_data, partn_size);
  798 
  799         if ((rc = tlv_init_cursor(&cursor, buf,
  800             (uint32_t *)((uint8_t *)buf + partn_size),
  801             buf)) != 0) {
  802                 goto fail2;
  803         }
  804 
  805         header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
  806         header.length = __CPU_TO_LE_32(sizeof (header) - 8);
  807         header.type_id = __CPU_TO_LE_16(partn_type);
  808         header.preset = 0;
  809         header.generation = __CPU_TO_LE_32(1);
  810         header.total_length = 0;  /* This will be fixed below. */
  811         if ((rc = tlv_insert(
  812             &cursor, TLV_TAG_PARTITION_HEADER,
  813             (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
  814                 goto fail3;
  815         if ((rc = tlv_advance(&cursor)) != 0)
  816                 goto fail4;
  817 
  818         trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
  819         trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
  820         trailer.generation = header.generation;
  821         trailer.checksum = 0;  /* This will be fixed below. */
  822         if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
  823             (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
  824                 goto fail5;
  825 
  826         if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
  827                 goto fail6;
  828 
  829         /* Check that the partition is valid. */
  830         if ((rc = ef10_nvram_buffer_validate(partn_type,
  831             partn_data, partn_size)) != 0)
  832                 goto fail7;
  833 
  834         return (0);
  835 
  836 fail7:
  837         EFSYS_PROBE(fail7);
  838 fail6:
  839         EFSYS_PROBE(fail6);
  840 fail5:
  841         EFSYS_PROBE(fail5);
  842 fail4:
  843         EFSYS_PROBE(fail4);
  844 fail3:
  845         EFSYS_PROBE(fail3);
  846 fail2:
  847         EFSYS_PROBE(fail2);
  848 fail1:
  849         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  850 
  851         return (rc);
  852 }
  853 
  854 static                  uint32_t
  855 byte_offset(
  856         __in            uint32_t *position,
  857         __in            uint32_t *base)
  858 {
  859         return (uint32_t)((uint8_t *)position - (uint8_t *)base);
  860 }
  861 
  862         __checkReturn           efx_rc_t
  863 ef10_nvram_buffer_find_item_start(
  864         __in_bcount(buffer_size)
  865                                 caddr_t bufferp,
  866         __in                    size_t buffer_size,
  867         __out                   uint32_t *startp)
  868 {
  869         /* Read past partition header to find start address of the first key */
  870         tlv_cursor_t cursor;
  871         efx_rc_t rc;
  872 
  873         /* A PARTITION_HEADER tag must be the first item (at offset zero) */
  874         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
  875                         buffer_size)) != 0) {
  876                 rc = EFAULT;
  877                 goto fail1;
  878         }
  879         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
  880                 rc = EINVAL;
  881                 goto fail2;
  882         }
  883 
  884         if ((rc = tlv_advance(&cursor)) != 0) {
  885                 rc = EINVAL;
  886                 goto fail3;
  887         }
  888         *startp = byte_offset(cursor.current, cursor.block);
  889 
  890         if ((rc = tlv_require_end(&cursor)) != 0)
  891                 goto fail4;
  892 
  893         return (0);
  894 
  895 fail4:
  896         EFSYS_PROBE(fail4);
  897 fail3:
  898         EFSYS_PROBE(fail3);
  899 fail2:
  900         EFSYS_PROBE(fail2);
  901 fail1:
  902         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  903 
  904         return (rc);
  905 }
  906 
  907         __checkReturn           efx_rc_t
  908 ef10_nvram_buffer_find_end(
  909         __in_bcount(buffer_size)
  910                                 caddr_t bufferp,
  911         __in                    size_t buffer_size,
  912         __in                    uint32_t offset,
  913         __out                   uint32_t *endp)
  914 {
  915         /* Read to end of partition */
  916         tlv_cursor_t cursor;
  917         efx_rc_t rc;
  918         uint32_t *segment_used;
  919 
  920         _NOTE(ARGUNUSED(offset))
  921 
  922         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
  923                         buffer_size)) != 0) {
  924                 rc = EFAULT;
  925                 goto fail1;
  926         }
  927 
  928         segment_used = cursor.block;
  929 
  930         /*
  931          * Go through each segment and check that it has an end tag. If there
  932          * is no end tag then the previous segment was the last valid one,
  933          * so return the used space including that end tag.
  934          */
  935         while (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
  936                 if (tlv_require_end(&cursor) != 0) {
  937                         if (segment_used == cursor.block) {
  938                                 /*
  939                                  * First segment is corrupt, so there is
  940                                  * no valid data in partition.
  941                                  */
  942                                 rc = EINVAL;
  943                                 goto fail2;
  944                         }
  945                         break;
  946                 }
  947                 segment_used = cursor.end + 1;
  948 
  949                 cursor.current = segment_used;
  950         }
  951         /* Return space used (including the END tag) */
  952         *endp = (segment_used - cursor.block) * sizeof (uint32_t);
  953 
  954         return (0);
  955 
  956 fail2:
  957         EFSYS_PROBE(fail2);
  958 fail1:
  959         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  960 
  961         return (rc);
  962 }
  963 
  964         __checkReturn   __success(return != B_FALSE)    boolean_t
  965 ef10_nvram_buffer_find_item(
  966         __in_bcount(buffer_size)
  967                                 caddr_t bufferp,
  968         __in                    size_t buffer_size,
  969         __in                    uint32_t offset,
  970         __out                   uint32_t *startp,
  971         __out                   uint32_t *lengthp)
  972 {
  973         /* Find TLV at offset and return key start and length */
  974         tlv_cursor_t cursor;
  975         uint8_t *key;
  976         uint32_t tag;
  977 
  978         if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
  979                         buffer_size, offset) != 0) {
  980                 return (B_FALSE);
  981         }
  982 
  983         while ((key = tlv_item(&cursor)) != NULL) {
  984                 tag = tlv_tag(&cursor);
  985                 if (tag == TLV_TAG_PARTITION_HEADER ||
  986                     tag == TLV_TAG_PARTITION_TRAILER) {
  987                         if (tlv_advance(&cursor) != 0) {
  988                                 break;
  989                         }
  990                         continue;
  991                 }
  992                 *startp = byte_offset(cursor.current, cursor.block);
  993                 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
  994                     cursor.current);
  995                 return (B_TRUE);
  996         }
  997 
  998         return (B_FALSE);
  999 }
 1000 
 1001         __checkReturn           efx_rc_t
 1002 ef10_nvram_buffer_peek_item(
 1003         __in_bcount(buffer_size)
 1004                                 caddr_t bufferp,
 1005         __in                    size_t buffer_size,
 1006         __in                    uint32_t offset,
 1007         __out                   uint32_t *tagp,
 1008         __out                   uint32_t *lengthp,
 1009         __out                   uint32_t *value_offsetp)
 1010 {
 1011         efx_rc_t rc;
 1012         tlv_cursor_t cursor;
 1013         uint32_t tag;
 1014 
 1015         if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
 1016                         buffer_size, offset)) != 0) {
 1017                 goto fail1;
 1018         }
 1019 
 1020         tag = tlv_tag(&cursor);
 1021         *tagp = tag;
 1022         if (tag == TLV_TAG_END) {
 1023                 /*
 1024                  * To allow stepping over the END tag, report the full tag
 1025                  * length and a zero length value.
 1026                  */
 1027                 *lengthp = sizeof (tag);
 1028                 *value_offsetp = sizeof (tag);
 1029         } else {
 1030                 *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
 1031                             cursor.current);
 1032                 *value_offsetp = byte_offset((uint32_t *)tlv_value(&cursor),
 1033                             cursor.current);
 1034         }
 1035         return (0);
 1036 
 1037 fail1:
 1038         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1039 
 1040         return (rc);
 1041 }
 1042 
 1043         __checkReturn           efx_rc_t
 1044 ef10_nvram_buffer_get_item(
 1045         __in_bcount(buffer_size)
 1046                                 caddr_t bufferp,
 1047         __in                    size_t buffer_size,
 1048         __in                    uint32_t offset,
 1049         __in                    uint32_t length,
 1050         __out                   uint32_t *tagp,
 1051         __out_bcount_part(value_max_size, *lengthp)
 1052                                 caddr_t valuep,
 1053         __in                    size_t value_max_size,
 1054         __out                   uint32_t *lengthp)
 1055 {
 1056         efx_rc_t rc;
 1057         tlv_cursor_t cursor;
 1058         uint32_t value_length;
 1059 
 1060         if (buffer_size < (offset + length)) {
 1061                 rc = ENOSPC;
 1062                 goto fail1;
 1063         }
 1064 
 1065         if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
 1066                         buffer_size, offset)) != 0) {
 1067                 goto fail2;
 1068         }
 1069 
 1070         value_length = tlv_length(&cursor);
 1071         if (value_max_size < value_length) {
 1072                 rc = ENOSPC;
 1073                 goto fail3;
 1074         }
 1075         memcpy(valuep, tlv_value(&cursor), value_length);
 1076 
 1077         *tagp = tlv_tag(&cursor);
 1078         *lengthp = value_length;
 1079 
 1080         return (0);
 1081 
 1082 fail3:
 1083         EFSYS_PROBE(fail3);
 1084 fail2:
 1085         EFSYS_PROBE(fail2);
 1086 fail1:
 1087         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1088 
 1089         return (rc);
 1090 }
 1091 
 1092         __checkReturn           efx_rc_t
 1093 ef10_nvram_buffer_insert_item(
 1094         __in_bcount(buffer_size)
 1095                                 caddr_t bufferp,
 1096         __in                    size_t buffer_size,
 1097         __in                    uint32_t offset,
 1098         __in                    uint32_t tag,
 1099         __in_bcount(length)     caddr_t valuep,
 1100         __in                    uint32_t length,
 1101         __out                   uint32_t *lengthp)
 1102 {
 1103         efx_rc_t rc;
 1104         tlv_cursor_t cursor;
 1105 
 1106         if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
 1107                         buffer_size, offset)) != 0) {
 1108                 goto fail1;
 1109         }
 1110 
 1111         rc = tlv_insert(&cursor, tag, (uint8_t *)valuep, length);
 1112 
 1113         if (rc != 0)
 1114                 goto fail2;
 1115 
 1116         *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
 1117                     cursor.current);
 1118 
 1119         return (0);
 1120 
 1121 fail2:
 1122         EFSYS_PROBE(fail2);
 1123 fail1:
 1124         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1125 
 1126         return (rc);
 1127 }
 1128 
 1129         __checkReturn           efx_rc_t
 1130 ef10_nvram_buffer_modify_item(
 1131         __in_bcount(buffer_size)
 1132                                 caddr_t bufferp,
 1133         __in                    size_t buffer_size,
 1134         __in                    uint32_t offset,
 1135         __in                    uint32_t tag,
 1136         __in_bcount(length)     caddr_t valuep,
 1137         __in                    uint32_t length,
 1138         __out                   uint32_t *lengthp)
 1139 {
 1140         efx_rc_t rc;
 1141         tlv_cursor_t cursor;
 1142 
 1143         if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
 1144                         buffer_size, offset)) != 0) {
 1145                 goto fail1;
 1146         }
 1147 
 1148         rc = tlv_modify(&cursor, tag, (uint8_t *)valuep, length);
 1149 
 1150         if (rc != 0) {
 1151                 goto fail2;
 1152         }
 1153 
 1154         *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
 1155                     cursor.current);
 1156 
 1157         return (0);
 1158 
 1159 fail2:
 1160         EFSYS_PROBE(fail2);
 1161 fail1:
 1162         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1163 
 1164         return (rc);
 1165 }
 1166 
 1167         __checkReturn           efx_rc_t
 1168 ef10_nvram_buffer_delete_item(
 1169         __in_bcount(buffer_size)
 1170                                 caddr_t bufferp,
 1171         __in                    size_t buffer_size,
 1172         __in                    uint32_t offset,
 1173         __in                    uint32_t length,
 1174         __in                    uint32_t end)
 1175 {
 1176         efx_rc_t rc;
 1177         tlv_cursor_t cursor;
 1178 
 1179         _NOTE(ARGUNUSED(length, end))
 1180 
 1181         if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
 1182                         buffer_size, offset)) != 0) {
 1183                 goto fail1;
 1184         }
 1185 
 1186         if ((rc = tlv_delete(&cursor)) != 0)
 1187                 goto fail2;
 1188 
 1189         return (0);
 1190 
 1191 fail2:
 1192         EFSYS_PROBE(fail2);
 1193 fail1:
 1194         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1195 
 1196         return (rc);
 1197 }
 1198 
 1199         __checkReturn           efx_rc_t
 1200 ef10_nvram_buffer_finish(
 1201         __in_bcount(buffer_size)
 1202                                 caddr_t bufferp,
 1203         __in                    size_t buffer_size)
 1204 {
 1205         efx_rc_t rc;
 1206         tlv_cursor_t cursor;
 1207 
 1208         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
 1209                         buffer_size)) != 0) {
 1210                 rc = EFAULT;
 1211                 goto fail1;
 1212         }
 1213 
 1214         if ((rc = tlv_require_end(&cursor)) != 0)
 1215                 goto fail2;
 1216 
 1217         if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
 1218                 goto fail3;
 1219 
 1220         return (0);
 1221 
 1222 fail3:
 1223         EFSYS_PROBE(fail3);
 1224 fail2:
 1225         EFSYS_PROBE(fail2);
 1226 fail1:
 1227         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1228 
 1229         return (rc);
 1230 }
 1231 
 1232 /*
 1233  * Read and validate a segment from a partition. A segment is a complete
 1234  * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
 1235  * be multiple segments in a partition, so seg_offset allows segments
 1236  * beyond the first to be read.
 1237  */
 1238 static  __checkReturn                   efx_rc_t
 1239 ef10_nvram_read_tlv_segment(
 1240         __in                            efx_nic_t *enp,
 1241         __in                            uint32_t partn,
 1242         __in                            size_t seg_offset,
 1243         __in_bcount(max_seg_size)       caddr_t seg_data,
 1244         __in                            size_t max_seg_size)
 1245 {
 1246         tlv_cursor_t cursor;
 1247         struct tlv_partition_header *header;
 1248         struct tlv_partition_trailer *trailer;
 1249         size_t total_length;
 1250         uint32_t cksum;
 1251         int pos;
 1252         efx_rc_t rc;
 1253 
 1254         EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
 1255 
 1256         if ((seg_data == NULL) || (max_seg_size == 0)) {
 1257                 rc = EINVAL;
 1258                 goto fail1;
 1259         }
 1260 
 1261         /* Read initial chunk of the segment, starting at offset */
 1262         if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
 1263                     EF10_NVRAM_CHUNK,
 1264                     MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
 1265                 goto fail2;
 1266         }
 1267 
 1268         /* A PARTITION_HEADER tag must be the first item at the given offset */
 1269         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
 1270                     max_seg_size)) != 0) {
 1271                 rc = EFAULT;
 1272                 goto fail3;
 1273         }
 1274         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
 1275                 rc = EINVAL;
 1276                 goto fail4;
 1277         }
 1278         header = (struct tlv_partition_header *)tlv_item(&cursor);
 1279 
 1280         /* Check TLV segment length (includes the END tag) */
 1281         total_length = __LE_TO_CPU_32(header->total_length);
 1282         if (total_length > max_seg_size) {
 1283                 rc = EFBIG;
 1284                 goto fail5;
 1285         }
 1286 
 1287         /* Read the remaining segment content */
 1288         if (total_length > EF10_NVRAM_CHUNK) {
 1289                 if ((rc = ef10_nvram_partn_read_mode(enp, partn,
 1290                             seg_offset + EF10_NVRAM_CHUNK,
 1291                             seg_data + EF10_NVRAM_CHUNK,
 1292                             total_length - EF10_NVRAM_CHUNK,
 1293                             MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
 1294                         goto fail6;
 1295         }
 1296 
 1297         /* Check segment ends with PARTITION_TRAILER and END tags */
 1298         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
 1299                 rc = EINVAL;
 1300                 goto fail7;
 1301         }
 1302         trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
 1303 
 1304         if ((rc = tlv_advance(&cursor)) != 0) {
 1305                 rc = EINVAL;
 1306                 goto fail8;
 1307         }
 1308         if (tlv_tag(&cursor) != TLV_TAG_END) {
 1309                 rc = EINVAL;
 1310                 goto fail9;
 1311         }
 1312 
 1313         /* Check data read from segment is consistent */
 1314         if (trailer->generation != header->generation) {
 1315                 /*
 1316                  * The partition data may have been modified between successive
 1317                  * MCDI NVRAM_READ requests by the MC or another PCI function.
 1318                  *
 1319                  * The caller must retry to obtain consistent partition data.
 1320                  */
 1321                 rc = EAGAIN;
 1322                 goto fail10;
 1323         }
 1324 
 1325         /* Verify segment checksum */
 1326         cksum = 0;
 1327         for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
 1328                 cksum += *((uint32_t *)(seg_data + pos));
 1329         }
 1330         if (cksum != 0) {
 1331                 rc = EINVAL;
 1332                 goto fail11;
 1333         }
 1334 
 1335         return (0);
 1336 
 1337 fail11:
 1338         EFSYS_PROBE(fail11);
 1339 fail10:
 1340         EFSYS_PROBE(fail10);
 1341 fail9:
 1342         EFSYS_PROBE(fail9);
 1343 fail8:
 1344         EFSYS_PROBE(fail8);
 1345 fail7:
 1346         EFSYS_PROBE(fail7);
 1347 fail6:
 1348         EFSYS_PROBE(fail6);
 1349 fail5:
 1350         EFSYS_PROBE(fail5);
 1351 fail4:
 1352         EFSYS_PROBE(fail4);
 1353 fail3:
 1354         EFSYS_PROBE(fail3);
 1355 fail2:
 1356         EFSYS_PROBE(fail2);
 1357 fail1:
 1358         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1359 
 1360         return (rc);
 1361 }
 1362 
 1363 /*
 1364  * Read a single TLV item from a host memory
 1365  * buffer containing a TLV formatted segment.
 1366  */
 1367         __checkReturn           efx_rc_t
 1368 ef10_nvram_buf_read_tlv(
 1369         __in                            efx_nic_t *enp,
 1370         __in_bcount(max_seg_size)       caddr_t seg_data,
 1371         __in                            size_t max_seg_size,
 1372         __in                            uint32_t tag,
 1373         __deref_out_bcount_opt(*sizep)  caddr_t *datap,
 1374         __out                           size_t *sizep)
 1375 {
 1376         tlv_cursor_t cursor;
 1377         caddr_t data;
 1378         size_t length;
 1379         caddr_t value;
 1380         efx_rc_t rc;
 1381 
 1382         _NOTE(ARGUNUSED(enp))
 1383 
 1384         if ((seg_data == NULL) || (max_seg_size == 0)) {
 1385                 rc = EINVAL;
 1386                 goto fail1;
 1387         }
 1388 
 1389         /* Find requested TLV tag in segment data */
 1390         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
 1391                     max_seg_size)) != 0) {
 1392                 rc = EFAULT;
 1393                 goto fail2;
 1394         }
 1395         if ((rc = tlv_find(&cursor, tag)) != 0) {
 1396                 rc = ENOENT;
 1397                 goto fail3;
 1398         }
 1399         value = (caddr_t)tlv_value(&cursor);
 1400         length = tlv_length(&cursor);
 1401 
 1402         if (length == 0)
 1403                 data = NULL;
 1404         else {
 1405                 /* Copy out data from TLV item */
 1406                 EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
 1407                 if (data == NULL) {
 1408                         rc = ENOMEM;
 1409                         goto fail4;
 1410                 }
 1411                 memcpy(data, value, length);
 1412         }
 1413 
 1414         *datap = data;
 1415         *sizep = length;
 1416 
 1417         return (0);
 1418 
 1419 fail4:
 1420         EFSYS_PROBE(fail4);
 1421 fail3:
 1422         EFSYS_PROBE(fail3);
 1423 fail2:
 1424         EFSYS_PROBE(fail2);
 1425 fail1:
 1426         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1427 
 1428         return (rc);
 1429 }
 1430 
 1431 /* Read a single TLV item from the first segment in a TLV formatted partition */
 1432         __checkReturn           efx_rc_t
 1433 ef10_nvram_partn_read_tlv(
 1434         __in                                    efx_nic_t *enp,
 1435         __in                                    uint32_t partn,
 1436         __in                                    uint32_t tag,
 1437         __deref_out_bcount_opt(*seg_sizep)      caddr_t *seg_datap,
 1438         __out                                   size_t *seg_sizep)
 1439 {
 1440         caddr_t seg_data = NULL;
 1441         size_t partn_size = 0;
 1442         size_t length;
 1443         caddr_t data;
 1444         int retry;
 1445         efx_rc_t rc;
 1446 
 1447         /* Allocate sufficient memory for the entire partition */
 1448         if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
 1449                 goto fail1;
 1450 
 1451         if (partn_size == 0) {
 1452                 rc = ENOENT;
 1453                 goto fail2;
 1454         }
 1455 
 1456         EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
 1457         if (seg_data == NULL) {
 1458                 rc = ENOMEM;
 1459                 goto fail3;
 1460         }
 1461 
 1462         /*
 1463          * Read the first segment in a TLV partition. Retry until consistent
 1464          * segment contents are returned. Inconsistent data may be read if:
 1465          *  a) the segment contents are invalid
 1466          *  b) the MC has rebooted while we were reading the partition
 1467          *  c) the partition has been modified while we were reading it
 1468          * Limit retry attempts to ensure forward progress.
 1469          */
 1470         retry = 10;
 1471         do {
 1472                 if ((rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
 1473                     seg_data, partn_size)) != 0)
 1474                         --retry;
 1475         } while ((rc == EAGAIN) && (retry > 0));
 1476 
 1477         if (rc != 0) {
 1478                 /* Failed to obtain consistent segment data */
 1479                 if (rc == EAGAIN)
 1480                         rc = EIO;
 1481 
 1482                 goto fail4;
 1483         }
 1484 
 1485         if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
 1486                     tag, &data, &length)) != 0)
 1487                 goto fail5;
 1488 
 1489         EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
 1490 
 1491         *seg_datap = data;
 1492         *seg_sizep = length;
 1493 
 1494         return (0);
 1495 
 1496 fail5:
 1497         EFSYS_PROBE(fail5);
 1498 fail4:
 1499         EFSYS_PROBE(fail4);
 1500 
 1501         EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
 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 /* Compute the size of a segment. */
 1513         static  __checkReturn   efx_rc_t
 1514 ef10_nvram_buf_segment_size(
 1515         __in                    caddr_t seg_data,
 1516         __in                    size_t max_seg_size,
 1517         __out                   size_t *seg_sizep)
 1518 {
 1519         efx_rc_t rc;
 1520         tlv_cursor_t cursor;
 1521         struct tlv_partition_header *header;
 1522         uint32_t cksum;
 1523         int pos;
 1524         uint32_t *end_tag_position;
 1525         uint32_t segment_length;
 1526 
 1527         /* A PARTITION_HEADER tag must be the first item at the given offset */
 1528         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
 1529                     max_seg_size)) != 0) {
 1530                 rc = EFAULT;
 1531                 goto fail1;
 1532         }
 1533         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
 1534                 rc = EINVAL;
 1535                 goto fail2;
 1536         }
 1537         header = (struct tlv_partition_header *)tlv_item(&cursor);
 1538 
 1539         /* Check TLV segment length (includes the END tag) */
 1540         *seg_sizep = __LE_TO_CPU_32(header->total_length);
 1541         if (*seg_sizep > max_seg_size) {
 1542                 rc = EFBIG;
 1543                 goto fail3;
 1544         }
 1545 
 1546         /* Check segment ends with PARTITION_TRAILER and END tags */
 1547         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
 1548                 rc = EINVAL;
 1549                 goto fail4;
 1550         }
 1551 
 1552         if ((rc = tlv_advance(&cursor)) != 0) {
 1553                 rc = EINVAL;
 1554                 goto fail5;
 1555         }
 1556         if (tlv_tag(&cursor) != TLV_TAG_END) {
 1557                 rc = EINVAL;
 1558                 goto fail6;
 1559         }
 1560         end_tag_position = cursor.current;
 1561 
 1562         /* Verify segment checksum */
 1563         cksum = 0;
 1564         for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
 1565                 cksum += *((uint32_t *)(seg_data + pos));
 1566         }
 1567         if (cksum != 0) {
 1568                 rc = EINVAL;
 1569                 goto fail7;
 1570         }
 1571 
 1572         /*
 1573          * Calculate total length from HEADER to END tags and compare to
 1574          * max_seg_size and the total_length field in the HEADER tag.
 1575          */
 1576         segment_length = tlv_block_length_used(&cursor);
 1577 
 1578         if (segment_length > max_seg_size) {
 1579                 rc = EINVAL;
 1580                 goto fail8;
 1581         }
 1582 
 1583         if (segment_length != *seg_sizep) {
 1584                 rc = EINVAL;
 1585                 goto fail9;
 1586         }
 1587 
 1588         /* Skip over the first HEADER tag. */
 1589         rc = tlv_rewind(&cursor);
 1590         rc = tlv_advance(&cursor);
 1591 
 1592         while (rc == 0) {
 1593                 if (tlv_tag(&cursor) == TLV_TAG_END) {
 1594                         /* Check that the END tag is the one found earlier. */
 1595                         if (cursor.current != end_tag_position)
 1596                                 goto fail10;
 1597                         break;
 1598                 }
 1599                 /* Check for duplicate HEADER tags before the END tag. */
 1600                 if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
 1601                         rc = EINVAL;
 1602                         goto fail11;
 1603                 }
 1604 
 1605                 rc = tlv_advance(&cursor);
 1606         }
 1607         if (rc != 0)
 1608                 goto fail12;
 1609 
 1610         return (0);
 1611 
 1612 fail12:
 1613         EFSYS_PROBE(fail12);
 1614 fail11:
 1615         EFSYS_PROBE(fail11);
 1616 fail10:
 1617         EFSYS_PROBE(fail10);
 1618 fail9:
 1619         EFSYS_PROBE(fail9);
 1620 fail8:
 1621         EFSYS_PROBE(fail8);
 1622 fail7:
 1623         EFSYS_PROBE(fail7);
 1624 fail6:
 1625         EFSYS_PROBE(fail6);
 1626 fail5:
 1627         EFSYS_PROBE(fail5);
 1628 fail4:
 1629         EFSYS_PROBE(fail4);
 1630 fail3:
 1631         EFSYS_PROBE(fail3);
 1632 fail2:
 1633         EFSYS_PROBE(fail2);
 1634 fail1:
 1635         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1636 
 1637         return (rc);
 1638 }
 1639 
 1640 /*
 1641  * Add or update a single TLV item in a host memory buffer containing a TLV
 1642  * formatted segment. Historically partitions consisted of only one segment.
 1643  */
 1644         __checkReturn                   efx_rc_t
 1645 ef10_nvram_buf_write_tlv(
 1646         __inout_bcount(max_seg_size)    caddr_t seg_data,
 1647         __in                            size_t max_seg_size,
 1648         __in                            uint32_t tag,
 1649         __in_bcount(tag_size)           caddr_t tag_data,
 1650         __in                            size_t tag_size,
 1651         __out                           size_t *total_lengthp)
 1652 {
 1653         tlv_cursor_t cursor;
 1654         struct tlv_partition_header *header;
 1655         struct tlv_partition_trailer *trailer;
 1656         uint32_t generation;
 1657         uint32_t cksum;
 1658         int pos;
 1659         efx_rc_t rc;
 1660 
 1661         /* A PARTITION_HEADER tag must be the first item (at offset zero) */
 1662         if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
 1663                         max_seg_size)) != 0) {
 1664                 rc = EFAULT;
 1665                 goto fail1;
 1666         }
 1667         if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
 1668                 rc = EINVAL;
 1669                 goto fail2;
 1670         }
 1671         header = (struct tlv_partition_header *)tlv_item(&cursor);
 1672 
 1673         /* Update the TLV chain to contain the new data */
 1674         if ((rc = tlv_find(&cursor, tag)) == 0) {
 1675                 /* Modify existing TLV item */
 1676                 if ((rc = tlv_modify(&cursor, tag,
 1677                             (uint8_t *)tag_data, tag_size)) != 0)
 1678                         goto fail3;
 1679         } else {
 1680                 /* Insert a new TLV item before the PARTITION_TRAILER */
 1681                 rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
 1682                 if (rc != 0) {
 1683                         rc = EINVAL;
 1684                         goto fail4;
 1685                 }
 1686                 if ((rc = tlv_insert(&cursor, tag,
 1687                             (uint8_t *)tag_data, tag_size)) != 0) {
 1688                         rc = EINVAL;
 1689                         goto fail5;
 1690                 }
 1691         }
 1692 
 1693         /* Find the trailer tag */
 1694         if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
 1695                 rc = EINVAL;
 1696                 goto fail6;
 1697         }
 1698         trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
 1699 
 1700         /* Update PARTITION_HEADER and PARTITION_TRAILER fields */
 1701         *total_lengthp = tlv_block_length_used(&cursor);
 1702         if (*total_lengthp > max_seg_size) {
 1703                 rc = ENOSPC;
 1704                 goto fail7;
 1705         }
 1706         generation = __LE_TO_CPU_32(header->generation) + 1;
 1707 
 1708         header->total_length    = __CPU_TO_LE_32(*total_lengthp);
 1709         header->generation      = __CPU_TO_LE_32(generation);
 1710         trailer->generation     = __CPU_TO_LE_32(generation);
 1711 
 1712         /* Recompute PARTITION_TRAILER checksum */
 1713         trailer->checksum = 0;
 1714         cksum = 0;
 1715         for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
 1716                 cksum += *((uint32_t *)(seg_data + pos));
 1717         }
 1718         trailer->checksum = ~cksum + 1;
 1719 
 1720         return (0);
 1721 
 1722 fail7:
 1723         EFSYS_PROBE(fail7);
 1724 fail6:
 1725         EFSYS_PROBE(fail6);
 1726 fail5:
 1727         EFSYS_PROBE(fail5);
 1728 fail4:
 1729         EFSYS_PROBE(fail4);
 1730 fail3:
 1731         EFSYS_PROBE(fail3);
 1732 fail2:
 1733         EFSYS_PROBE(fail2);
 1734 fail1:
 1735         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1736 
 1737         return (rc);
 1738 }
 1739 
 1740 /*
 1741  * Add or update a single TLV item in the first segment of a TLV formatted
 1742  * dynamic config partition. The first segment is the current active
 1743  * configuration.
 1744  */
 1745         __checkReturn           efx_rc_t
 1746 ef10_nvram_partn_write_tlv(
 1747         __in                    efx_nic_t *enp,
 1748         __in                    uint32_t partn,
 1749         __in                    uint32_t tag,
 1750         __in_bcount(size)       caddr_t data,
 1751         __in                    size_t size)
 1752 {
 1753         return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
 1754             size, B_FALSE);
 1755 }
 1756 
 1757 /*
 1758  * Read a segment from nvram at the given offset into a buffer (segment_data)
 1759  * and optionally write a new tag to it.
 1760  */
 1761 static  __checkReturn           efx_rc_t
 1762 ef10_nvram_segment_write_tlv(
 1763         __in                    efx_nic_t *enp,
 1764         __in                    uint32_t partn,
 1765         __in                    uint32_t tag,
 1766         __in_bcount(size)       caddr_t data,
 1767         __in                    size_t size,
 1768         __inout                 caddr_t *seg_datap,
 1769         __inout                 size_t *partn_offsetp,
 1770         __inout                 size_t *src_remain_lenp,
 1771         __inout                 size_t *dest_remain_lenp,
 1772         __in                    boolean_t write)
 1773 {
 1774         efx_rc_t rc;
 1775         efx_rc_t status;
 1776         size_t original_segment_size;
 1777         size_t modified_segment_size;
 1778 
 1779         /*
 1780          * Read the segment from NVRAM into the segment_data buffer and validate
 1781          * it, returning if it does not validate. This is not a failure unless
 1782          * this is the first segment in a partition. In this case the caller
 1783          * must propagate the error.
 1784          */
 1785         status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
 1786             *seg_datap, *src_remain_lenp);
 1787         if (status != 0) {
 1788                 rc = EINVAL;
 1789                 goto fail1;
 1790         }
 1791 
 1792         status = ef10_nvram_buf_segment_size(*seg_datap,
 1793             *src_remain_lenp, &original_segment_size);
 1794         if (status != 0) {
 1795                 rc = EINVAL;
 1796                 goto fail2;
 1797         }
 1798 
 1799         if (write) {
 1800                 /* Update the contents of the segment in the buffer */
 1801                 if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
 1802                         *dest_remain_lenp, tag, data, size,
 1803                         &modified_segment_size)) != 0) {
 1804                         goto fail3;
 1805                 }
 1806                 *dest_remain_lenp -= modified_segment_size;
 1807                 *seg_datap += modified_segment_size;
 1808         } else {
 1809                 /*
 1810                  * We won't modify this segment, but still need to update the
 1811                  * remaining lengths and pointers.
 1812                  */
 1813                 *dest_remain_lenp -= original_segment_size;
 1814                 *seg_datap += original_segment_size;
 1815         }
 1816 
 1817         *partn_offsetp += original_segment_size;
 1818         *src_remain_lenp -= original_segment_size;
 1819 
 1820         return (0);
 1821 
 1822 fail3:
 1823         EFSYS_PROBE(fail3);
 1824 fail2:
 1825         EFSYS_PROBE(fail2);
 1826 fail1:
 1827         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1828 
 1829         return (rc);
 1830 }
 1831 
 1832 /*
 1833  * Add or update a single TLV item in either the first segment or in all
 1834  * segments in a TLV formatted dynamic config partition. Dynamic config
 1835  * partitions on boards that support RFID are divided into a number of segments,
 1836  * each formatted like a partition, with header, trailer and end tags. The first
 1837  * segment is the current active configuration.
 1838  *
 1839  * The segments are initialised by manftest and each contain a different
 1840  * configuration e.g. firmware variant. The firmware can be instructed
 1841  * via RFID to copy a segment to replace the first segment, hence changing the
 1842  * active configuration.  This allows ops to change the configuration of a board
 1843  * prior to shipment using RFID.
 1844  *
 1845  * Changes to the dynamic config may need to be written to all segments (e.g.
 1846  * firmware versions) or just the first segment (changes to the active
 1847  * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
 1848  * If only the first segment is written the code still needs to be aware of the
 1849  * possible presence of subsequent segments as writing to a segment may cause
 1850  * its size to increase, which would overwrite the subsequent segments and
 1851  * invalidate them.
 1852  */
 1853         __checkReturn           efx_rc_t
 1854 ef10_nvram_partn_write_segment_tlv(
 1855         __in                    efx_nic_t *enp,
 1856         __in                    uint32_t partn,
 1857         __in                    uint32_t tag,
 1858         __in_bcount(size)       caddr_t data,
 1859         __in                    size_t size,
 1860         __in                    boolean_t all_segments)
 1861 {
 1862         size_t partn_size = 0;
 1863         caddr_t partn_data;
 1864         size_t total_length = 0;
 1865         efx_rc_t rc;
 1866         size_t current_offset = 0;
 1867         size_t remaining_original_length;
 1868         size_t remaining_modified_length;
 1869         caddr_t segment_data;
 1870 
 1871         EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
 1872 
 1873         /* Allocate sufficient memory for the entire partition */
 1874         if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
 1875                 goto fail1;
 1876 
 1877         EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
 1878         if (partn_data == NULL) {
 1879                 rc = ENOMEM;
 1880                 goto fail2;
 1881         }
 1882 
 1883         remaining_original_length = partn_size;
 1884         remaining_modified_length = partn_size;
 1885         segment_data = partn_data;
 1886 
 1887         /* Lock the partition */
 1888         if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
 1889                 goto fail3;
 1890 
 1891         /* Iterate over each (potential) segment to update it. */
 1892         do {
 1893                 boolean_t write = all_segments || current_offset == 0;
 1894 
 1895                 rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
 1896                     &segment_data, &current_offset, &remaining_original_length,
 1897                     &remaining_modified_length, write);
 1898                 if (rc != 0) {
 1899                         if (current_offset == 0) {
 1900                                 /*
 1901                                  * If no data has been read then the first
 1902                                  * segment is invalid, which is an error.
 1903                                  */
 1904                                 goto fail4;
 1905                         }
 1906                         break;
 1907                 }
 1908         } while (current_offset < partn_size);
 1909 
 1910         total_length = segment_data - partn_data;
 1911 
 1912         /*
 1913          * We've run out of space.  This should actually be dealt with by
 1914          * ef10_nvram_buf_write_tlv returning ENOSPC.
 1915          */
 1916         if (total_length > partn_size) {
 1917                 rc = ENOSPC;
 1918                 goto fail5;
 1919         }
 1920 
 1921         /* Erase the whole partition in NVRAM */
 1922         if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
 1923                 goto fail6;
 1924 
 1925         /* Write new partition contents from the buffer to NVRAM */
 1926         if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
 1927                     total_length)) != 0)
 1928                 goto fail7;
 1929 
 1930         /* Unlock the partition */
 1931         (void) ef10_nvram_partn_unlock(enp, partn, NULL);
 1932 
 1933         EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
 1934 
 1935         return (0);
 1936 
 1937 fail7:
 1938         EFSYS_PROBE(fail7);
 1939 fail6:
 1940         EFSYS_PROBE(fail6);
 1941 fail5:
 1942         EFSYS_PROBE(fail5);
 1943 fail4:
 1944         EFSYS_PROBE(fail4);
 1945 
 1946         (void) ef10_nvram_partn_unlock(enp, partn, NULL);
 1947 fail3:
 1948         EFSYS_PROBE(fail3);
 1949 
 1950         EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
 1951 fail2:
 1952         EFSYS_PROBE(fail2);
 1953 fail1:
 1954         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1955 
 1956         return (rc);
 1957 }
 1958 
 1959 /*
 1960  * Get the size of a NVRAM partition. This is the total size allocated in nvram,
 1961  * not the data used by the segments in the partition.
 1962  */
 1963         __checkReturn           efx_rc_t
 1964 ef10_nvram_partn_size(
 1965         __in                    efx_nic_t *enp,
 1966         __in                    uint32_t partn,
 1967         __out                   size_t *sizep)
 1968 {
 1969         efx_rc_t rc;
 1970 
 1971         if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
 1972             NULL, NULL, NULL)) != 0)
 1973                 goto fail1;
 1974 
 1975         return (0);
 1976 
 1977 fail1:
 1978         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1979 
 1980         return (rc);
 1981 }
 1982 
 1983         __checkReturn           efx_rc_t
 1984 ef10_nvram_partn_lock(
 1985         __in                    efx_nic_t *enp,
 1986         __in                    uint32_t partn)
 1987 {
 1988         efx_rc_t rc;
 1989 
 1990         if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
 1991                 goto fail1;
 1992 
 1993         return (0);
 1994 
 1995 fail1:
 1996         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1997 
 1998         return (rc);
 1999 }
 2000 
 2001         __checkReturn           efx_rc_t
 2002 ef10_nvram_partn_read_mode(
 2003         __in                    efx_nic_t *enp,
 2004         __in                    uint32_t partn,
 2005         __in                    unsigned int offset,
 2006         __out_bcount(size)      caddr_t data,
 2007         __in                    size_t size,
 2008         __in                    uint32_t mode)
 2009 {
 2010         size_t chunk;
 2011         efx_rc_t rc;
 2012 
 2013         while (size > 0) {
 2014                 chunk = MIN(size, EF10_NVRAM_CHUNK);
 2015 
 2016                 if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
 2017                             data, chunk, mode)) != 0) {
 2018                         goto fail1;
 2019                 }
 2020 
 2021                 size -= chunk;
 2022                 data += chunk;
 2023                 offset += chunk;
 2024         }
 2025 
 2026         return (0);
 2027 
 2028 fail1:
 2029         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 2030 
 2031         return (rc);
 2032 }
 2033 
 2034         __checkReturn           efx_rc_t
 2035 ef10_nvram_partn_read(
 2036         __in                    efx_nic_t *enp,
 2037         __in                    uint32_t partn,
 2038         __in                    unsigned int offset,
 2039         __out_bcount(size)      caddr_t data,
 2040         __in                    size_t size)
 2041 {
 2042         /*
 2043          * An A/B partition has two data stores (current and backup).
 2044          * Read requests which come in through the EFX API expect to read the
 2045          * current, active store of an A/B partition. For non A/B partitions,
 2046          * there is only a single store and so the mode param is ignored.
 2047          */
 2048         return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
 2049                             MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
 2050 }
 2051 
 2052         __checkReturn           efx_rc_t
 2053 ef10_nvram_partn_read_backup(
 2054         __in                    efx_nic_t *enp,
 2055         __in                    uint32_t partn,
 2056         __in                    unsigned int offset,
 2057         __out_bcount(size)      caddr_t data,
 2058         __in                    size_t size)
 2059 {
 2060         /*
 2061          * An A/B partition has two data stores (current and backup).
 2062          * Read the backup store of an A/B partition (i.e. the store currently
 2063          * being written to if the partition is locked).
 2064          *
 2065          * This is needed when comparing the existing partition content to avoid
 2066          * unnecessary writes, or to read back what has been written to check
 2067          * that the writes have succeeded.
 2068          */
 2069         return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
 2070                             MC_CMD_NVRAM_READ_IN_V2_TARGET_BACKUP);
 2071 }
 2072 
 2073         __checkReturn           efx_rc_t
 2074 ef10_nvram_partn_erase(
 2075         __in                    efx_nic_t *enp,
 2076         __in                    uint32_t partn,
 2077         __in                    unsigned int offset,
 2078         __in                    size_t size)
 2079 {
 2080         efx_rc_t rc;
 2081         uint32_t erase_size;
 2082 
 2083         if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
 2084             &erase_size, NULL)) != 0)
 2085                 goto fail1;
 2086 
 2087         if (erase_size == 0) {
 2088                 if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
 2089                         goto fail2;
 2090         } else {
 2091                 if (size % erase_size != 0) {
 2092                         rc = EINVAL;
 2093                         goto fail3;
 2094                 }
 2095                 while (size > 0) {
 2096                         if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
 2097                             erase_size)) != 0)
 2098                                 goto fail4;
 2099                         offset += erase_size;
 2100                         size -= erase_size;
 2101                 }
 2102         }
 2103 
 2104         return (0);
 2105 
 2106 fail4:
 2107         EFSYS_PROBE(fail4);
 2108 fail3:
 2109         EFSYS_PROBE(fail3);
 2110 fail2:
 2111         EFSYS_PROBE(fail2);
 2112 fail1:
 2113         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 2114 
 2115         return (rc);
 2116 }
 2117 
 2118         __checkReturn           efx_rc_t
 2119 ef10_nvram_partn_write(
 2120         __in                    efx_nic_t *enp,
 2121         __in                    uint32_t partn,
 2122         __in                    unsigned int offset,
 2123         __in_bcount(size)       caddr_t data,
 2124         __in                    size_t size)
 2125 {
 2126         size_t chunk;
 2127         uint32_t write_size;
 2128         efx_rc_t rc;
 2129 
 2130         if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
 2131             NULL, &write_size)) != 0)
 2132                 goto fail1;
 2133 
 2134         if (write_size != 0) {
 2135                 /*
 2136                  * Check that the size is a multiple of the write chunk size if
 2137                  * the write chunk size is available.
 2138                  */
 2139                 if (size % write_size != 0) {
 2140                         rc = EINVAL;
 2141                         goto fail2;
 2142                 }
 2143         } else {
 2144                 write_size = EF10_NVRAM_CHUNK;
 2145         }
 2146 
 2147         while (size > 0) {
 2148                 chunk = MIN(size, write_size);
 2149 
 2150                 if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
 2151                             data, chunk)) != 0) {
 2152                         goto fail3;
 2153                 }
 2154 
 2155                 size -= chunk;
 2156                 data += chunk;
 2157                 offset += chunk;
 2158         }
 2159 
 2160         return (0);
 2161 
 2162 fail3:
 2163         EFSYS_PROBE(fail3);
 2164 fail2:
 2165         EFSYS_PROBE(fail2);
 2166 fail1:
 2167         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 2168 
 2169         return (rc);
 2170 }
 2171 
 2172         __checkReturn           efx_rc_t
 2173 ef10_nvram_partn_unlock(
 2174         __in                    efx_nic_t *enp,
 2175         __in                    uint32_t partn,
 2176         __out_opt               uint32_t *verify_resultp)
 2177 {
 2178         boolean_t reboot = B_FALSE;
 2179         efx_rc_t rc;
 2180 
 2181         if (verify_resultp != NULL)
 2182                 *verify_resultp = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
 2183 
 2184         rc = efx_mcdi_nvram_update_finish(enp, partn, reboot, verify_resultp);
 2185         if (rc != 0)
 2186                 goto fail1;
 2187 
 2188         return (0);
 2189 
 2190 fail1:
 2191         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 2192 
 2193         return (rc);
 2194 }
 2195 
 2196         __checkReturn           efx_rc_t
 2197 ef10_nvram_partn_set_version(
 2198         __in                    efx_nic_t *enp,
 2199         __in                    uint32_t partn,
 2200         __in_ecount(4)          uint16_t version[4])
 2201 {
 2202         struct tlv_partition_version partn_version;
 2203         size_t size;
 2204         efx_rc_t rc;
 2205 
 2206         /* Add or modify partition version TLV item */
 2207         partn_version.version_w = __CPU_TO_LE_16(version[0]);
 2208         partn_version.version_x = __CPU_TO_LE_16(version[1]);
 2209         partn_version.version_y = __CPU_TO_LE_16(version[2]);
 2210         partn_version.version_z = __CPU_TO_LE_16(version[3]);
 2211 
 2212         size = sizeof (partn_version) - (2 * sizeof (uint32_t));
 2213 
 2214         /* Write the version number to all segments in the partition */
 2215         if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
 2216                     NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
 2217                     TLV_TAG_PARTITION_VERSION(partn),
 2218                     (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
 2219                 goto fail1;
 2220 
 2221         return (0);
 2222 
 2223 fail1:
 2224         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 2225 
 2226         return (rc);
 2227 }
 2228 
 2229 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
 2230 
 2231 #if EFSYS_OPT_NVRAM
 2232 
 2233 typedef struct ef10_parttbl_entry_s {
 2234         unsigned int            partn;
 2235         unsigned int            port_mask;
 2236         efx_nvram_type_t        nvtype;
 2237 } ef10_parttbl_entry_t;
 2238 
 2239 /* Port mask values */
 2240 #define PORT_1          (1u << 1)
 2241 #define PORT_2          (1u << 2)
 2242 #define PORT_3          (1u << 3)
 2243 #define PORT_4          (1u << 4)
 2244 #define PORT_ALL        (0xffffffffu)
 2245 
 2246 #define PARTN_MAP_ENTRY(partn, port_mask, nvtype)       \
 2247 { (NVRAM_PARTITION_TYPE_##partn), (PORT_##port_mask), (EFX_NVRAM_##nvtype) }
 2248 
 2249 /* Translate EFX NVRAM types to firmware partition types */
 2250 static ef10_parttbl_entry_t hunt_parttbl[] = {
 2251         /*              partn                   ports   nvtype */
 2252         PARTN_MAP_ENTRY(MC_FIRMWARE,            ALL,    MC_FIRMWARE),
 2253         PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP,     ALL,    MC_GOLDEN),
 2254         PARTN_MAP_ENTRY(EXPANSION_ROM,          ALL,    BOOTROM),
 2255         PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT0,    1,      BOOTROM_CFG),
 2256         PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT1,    2,      BOOTROM_CFG),
 2257         PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT2,    3,      BOOTROM_CFG),
 2258         PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT3,    4,      BOOTROM_CFG),
 2259         PARTN_MAP_ENTRY(DYNAMIC_CONFIG,         ALL,    DYNAMIC_CFG),
 2260         PARTN_MAP_ENTRY(FPGA,                   ALL,    FPGA),
 2261         PARTN_MAP_ENTRY(FPGA_BACKUP,            ALL,    FPGA_BACKUP),
 2262         PARTN_MAP_ENTRY(LICENSE,                ALL,    LICENSE),
 2263 };
 2264 
 2265 static ef10_parttbl_entry_t medford_parttbl[] = {
 2266         /*              partn                   ports   nvtype */
 2267         PARTN_MAP_ENTRY(MC_FIRMWARE,            ALL,    MC_FIRMWARE),
 2268         PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP,     ALL,    MC_GOLDEN),
 2269         PARTN_MAP_ENTRY(EXPANSION_ROM,          ALL,    BOOTROM),
 2270         PARTN_MAP_ENTRY(EXPROM_CONFIG,          ALL,    BOOTROM_CFG),
 2271         PARTN_MAP_ENTRY(DYNAMIC_CONFIG,         ALL,    DYNAMIC_CFG),
 2272         PARTN_MAP_ENTRY(FPGA,                   ALL,    FPGA),
 2273         PARTN_MAP_ENTRY(FPGA_BACKUP,            ALL,    FPGA_BACKUP),
 2274         PARTN_MAP_ENTRY(LICENSE,                ALL,    LICENSE),
 2275         PARTN_MAP_ENTRY(EXPANSION_UEFI,         ALL,    UEFIROM),
 2276         PARTN_MAP_ENTRY(MUM_FIRMWARE,           ALL,    MUM_FIRMWARE),
 2277 };
 2278 
 2279 static ef10_parttbl_entry_t medford2_parttbl[] = {
 2280         /*              partn                   ports   nvtype */
 2281         PARTN_MAP_ENTRY(MC_FIRMWARE,            ALL,    MC_FIRMWARE),
 2282         PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP,     ALL,    MC_GOLDEN),
 2283         PARTN_MAP_ENTRY(EXPANSION_ROM,          ALL,    BOOTROM),
 2284         PARTN_MAP_ENTRY(EXPROM_CONFIG,          ALL,    BOOTROM_CFG),
 2285         PARTN_MAP_ENTRY(DYNAMIC_CONFIG,         ALL,    DYNAMIC_CFG),
 2286         PARTN_MAP_ENTRY(FPGA,                   ALL,    FPGA),
 2287         PARTN_MAP_ENTRY(FPGA_BACKUP,            ALL,    FPGA_BACKUP),
 2288         PARTN_MAP_ENTRY(LICENSE,                ALL,    LICENSE),
 2289         PARTN_MAP_ENTRY(EXPANSION_UEFI,         ALL,    UEFIROM),
 2290         PARTN_MAP_ENTRY(MUM_FIRMWARE,           ALL,    MUM_FIRMWARE),
 2291         PARTN_MAP_ENTRY(DYNCONFIG_DEFAULTS,     ALL,    DYNCONFIG_DEFAULTS),
 2292         PARTN_MAP_ENTRY(ROMCONFIG_DEFAULTS,     ALL,    ROMCONFIG_DEFAULTS),
 2293 };
 2294 
 2295 static  __checkReturn           efx_rc_t
 2296 ef10_parttbl_get(
 2297         __in                    efx_nic_t *enp,
 2298         __out                   ef10_parttbl_entry_t **parttblp,
 2299         __out                   size_t *parttbl_rowsp)
 2300 {
 2301         switch (enp->en_family) {
 2302         case EFX_FAMILY_HUNTINGTON:
 2303                 *parttblp = hunt_parttbl;
 2304                 *parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
 2305                 break;
 2306 
 2307         case EFX_FAMILY_MEDFORD:
 2308                 *parttblp = medford_parttbl;
 2309                 *parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
 2310                 break;
 2311 
 2312         case EFX_FAMILY_MEDFORD2:
 2313                 *parttblp = medford2_parttbl;
 2314                 *parttbl_rowsp = EFX_ARRAY_SIZE(medford2_parttbl);
 2315                 break;
 2316 
 2317         default:
 2318                 EFSYS_ASSERT(B_FALSE);
 2319                 return (EINVAL);
 2320         }
 2321         return (0);
 2322 }
 2323 
 2324         __checkReturn           efx_rc_t
 2325 ef10_nvram_type_to_partn(
 2326         __in                    efx_nic_t *enp,
 2327         __in                    efx_nvram_type_t type,
 2328         __out                   uint32_t *partnp)
 2329 {
 2330         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
 2331         ef10_parttbl_entry_t *parttbl = NULL;
 2332         size_t parttbl_rows = 0;
 2333         unsigned int i;
 2334 
 2335         EFSYS_ASSERT3U(type, !=, EFX_NVRAM_INVALID);
 2336         EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
 2337         EFSYS_ASSERT(partnp != NULL);
 2338 
 2339         if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
 2340                 for (i = 0; i < parttbl_rows; i++) {
 2341                         ef10_parttbl_entry_t *entry = &parttbl[i];
 2342 
 2343                         if ((entry->nvtype == type) &&
 2344                             (entry->port_mask & (1u << emip->emi_port))) {
 2345                                 *partnp = entry->partn;
 2346                                 return (0);
 2347                         }
 2348                 }
 2349         }
 2350 
 2351         return (ENOTSUP);
 2352 }
 2353 
 2354 #if EFSYS_OPT_DIAG
 2355 
 2356 static  __checkReturn           efx_rc_t
 2357 ef10_nvram_partn_to_type(
 2358         __in                    efx_nic_t *enp,
 2359         __in                    uint32_t partn,
 2360         __out                   efx_nvram_type_t *typep)
 2361 {
 2362         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
 2363         ef10_parttbl_entry_t *parttbl = NULL;
 2364         size_t parttbl_rows = 0;
 2365         unsigned int i;
 2366 
 2367         EFSYS_ASSERT(typep != NULL);
 2368 
 2369         if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
 2370                 for (i = 0; i < parttbl_rows; i++) {
 2371                         ef10_parttbl_entry_t *entry = &parttbl[i];
 2372 
 2373                         if ((entry->partn == partn) &&
 2374                             (entry->port_mask & (1u << emip->emi_port))) {
 2375                                 *typep = entry->nvtype;
 2376                                 return (0);
 2377                         }
 2378                 }
 2379         }
 2380 
 2381         return (ENOTSUP);
 2382 }
 2383 
 2384         __checkReturn           efx_rc_t
 2385 ef10_nvram_test(
 2386         __in                    efx_nic_t *enp)
 2387 {
 2388         efx_nvram_type_t type;
 2389         unsigned int npartns = 0;
 2390         uint32_t *partns = NULL;
 2391         size_t size;
 2392         unsigned int i;
 2393         efx_rc_t rc;
 2394 
 2395         /* Read available partitions from NVRAM partition map */
 2396         size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
 2397         EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
 2398         if (partns == NULL) {
 2399                 rc = ENOMEM;
 2400                 goto fail1;
 2401         }
 2402 
 2403         if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
 2404                     &npartns)) != 0) {
 2405                 goto fail2;
 2406         }
 2407 
 2408         for (i = 0; i < npartns; i++) {
 2409                 /* Check if the partition is supported for this port */
 2410                 if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
 2411                         continue;
 2412 
 2413                 if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
 2414                         goto fail3;
 2415         }
 2416 
 2417         EFSYS_KMEM_FREE(enp->en_esip, size, partns);
 2418         return (0);
 2419 
 2420 fail3:
 2421         EFSYS_PROBE(fail3);
 2422 fail2:
 2423         EFSYS_PROBE(fail2);
 2424         EFSYS_KMEM_FREE(enp->en_esip, size, partns);
 2425 fail1:
 2426         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 2427         return (rc);
 2428 }
 2429 
 2430 #endif  /* EFSYS_OPT_DIAG */
 2431 
 2432         __checkReturn           efx_rc_t
 2433 ef10_nvram_partn_get_version(
 2434         __in                    efx_nic_t *enp,
 2435         __in                    uint32_t partn,
 2436         __out                   uint32_t *subtypep,
 2437         __out_ecount(4)         uint16_t version[4])
 2438 {
 2439         efx_rc_t rc;
 2440 
 2441         /* FIXME: get highest partn version from all ports */
 2442         /* FIXME: return partn description if available */
 2443 
 2444         if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
 2445                     version, NULL, 0)) != 0)
 2446                 goto fail1;
 2447 
 2448         return (0);
 2449 
 2450 fail1:
 2451         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 2452 
 2453         return (rc);
 2454 }
 2455 
 2456         __checkReturn           efx_rc_t
 2457 ef10_nvram_partn_rw_start(
 2458         __in                    efx_nic_t *enp,
 2459         __in                    uint32_t partn,
 2460         __out                   size_t *chunk_sizep)
 2461 {
 2462         uint32_t write_size = 0;
 2463         efx_rc_t rc;
 2464 
 2465         if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
 2466             NULL, &write_size)) != 0)
 2467                 goto fail1;
 2468 
 2469         if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
 2470                 goto fail2;
 2471 
 2472         if (chunk_sizep != NULL) {
 2473                 if (write_size == 0)
 2474                         *chunk_sizep = EF10_NVRAM_CHUNK;
 2475                 else
 2476                         *chunk_sizep = write_size;
 2477         }
 2478 
 2479         return (0);
 2480 
 2481 fail2:
 2482         EFSYS_PROBE(fail2);
 2483 fail1:
 2484         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 2485 
 2486         return (rc);
 2487 }
 2488 
 2489         __checkReturn           efx_rc_t
 2490 ef10_nvram_partn_rw_finish(
 2491         __in                    efx_nic_t *enp,
 2492         __in                    uint32_t partn,
 2493         __out_opt               uint32_t *verify_resultp)
 2494 {
 2495         efx_rc_t rc;
 2496 
 2497         if ((rc = ef10_nvram_partn_unlock(enp, partn, verify_resultp)) != 0)
 2498                 goto fail1;
 2499 
 2500         return (0);
 2501 
 2502 fail1:
 2503         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 2504 
 2505         return (rc);
 2506 }
 2507 
 2508 #endif  /* EFSYS_OPT_NVRAM */
 2509 
 2510 #endif  /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */

Cache object: 52a96b755823207d9ca34cf33350ef3c


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.