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/efx_bootcfg.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2009-2016 Solarflare Communications Inc.
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions are met:
    9  *
   10  * 1. Redistributions of source code must retain the above copyright notice,
   11  *    this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright notice,
   13  *    this list of conditions and the following disclaimer in the documentation
   14  *    and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
   18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
   20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
   26  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   27  *
   28  * The views and conclusions contained in the software and documentation are
   29  * those of the authors and should not be interpreted as representing official
   30  * policies, either expressed or implied, of the FreeBSD Project.
   31  */
   32 
   33 #include <sys/cdefs.h>
   34 __FBSDID("$FreeBSD$");
   35 
   36 #include "efx.h"
   37 #include "efx_impl.h"
   38 
   39 #if EFSYS_OPT_BOOTCFG
   40 
   41 /*
   42  * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE.
   43  * NOTE: This is larger than the Medford per-PF bootcfg sector.
   44  */
   45 #define BOOTCFG_MAX_SIZE 0x1000
   46 
   47 /* Medford per-PF bootcfg sector */
   48 #define BOOTCFG_PER_PF   0x800
   49 #define BOOTCFG_PF_COUNT 16
   50 
   51 #define DHCP_OPT_HAS_VALUE(opt) \
   52         (((opt) > EFX_DHCP_PAD) && ((opt) < EFX_DHCP_END))
   53 
   54 #define DHCP_MAX_VALUE 255
   55 
   56 #define DHCP_ENCAPSULATOR(encap_opt) ((encap_opt) >> 8)
   57 #define DHCP_ENCAPSULATED(encap_opt) ((encap_opt) & 0xff)
   58 #define DHCP_IS_ENCAP_OPT(opt) DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATOR(opt))
   59 
   60 typedef struct efx_dhcp_tag_hdr_s {
   61         uint8_t         tag;
   62         uint8_t         length;
   63 } efx_dhcp_tag_hdr_t;
   64 
   65 /*
   66  * Length calculations for tags with value field. PAD and END
   67  * have a fixed length of 1, with no length or value field.
   68  */
   69 #define DHCP_FULL_TAG_LENGTH(hdr) \
   70         (sizeof (efx_dhcp_tag_hdr_t) + (hdr)->length)
   71 
   72 #define DHCP_NEXT_TAG(hdr) \
   73         ((efx_dhcp_tag_hdr_t *)(((uint8_t *)(hdr)) + \
   74         DHCP_FULL_TAG_LENGTH((hdr))))
   75 
   76 #define DHCP_CALC_TAG_LENGTH(payload_len) \
   77         ((payload_len) + sizeof (efx_dhcp_tag_hdr_t))
   78 
   79 /* Report the layout of bootcfg sectors in NVRAM partition. */
   80         __checkReturn           efx_rc_t
   81 efx_bootcfg_sector_info(
   82         __in                    efx_nic_t *enp,
   83         __in                    uint32_t pf,
   84         __out_opt               uint32_t *sector_countp,
   85         __out                   size_t *offsetp,
   86         __out                   size_t *max_sizep)
   87 {
   88         uint32_t count;
   89         size_t max_size;
   90         size_t offset;
   91         int rc;
   92 
   93         switch (enp->en_family) {
   94 #if EFSYS_OPT_SIENA
   95         case EFX_FAMILY_SIENA:
   96                 max_size = BOOTCFG_MAX_SIZE;
   97                 offset = 0;
   98                 count = 1;
   99                 break;
  100 #endif /* EFSYS_OPT_SIENA */
  101 
  102 #if EFSYS_OPT_HUNTINGTON
  103         case EFX_FAMILY_HUNTINGTON:
  104                 max_size = BOOTCFG_MAX_SIZE;
  105                 offset = 0;
  106                 count = 1;
  107                 break;
  108 #endif /* EFSYS_OPT_HUNTINGTON */
  109 
  110 #if EFSYS_OPT_MEDFORD
  111         case EFX_FAMILY_MEDFORD: {
  112                 /* Shared partition (array indexed by PF) */
  113                 max_size = BOOTCFG_PER_PF;
  114                 count = BOOTCFG_PF_COUNT;
  115                 if (pf >= count) {
  116                         rc = EINVAL;
  117                         goto fail2;
  118                 }
  119                 offset = max_size * pf;
  120                 break;
  121         }
  122 #endif /* EFSYS_OPT_MEDFORD */
  123 
  124 #if EFSYS_OPT_MEDFORD2
  125         case EFX_FAMILY_MEDFORD2: {
  126                 /* Shared partition (array indexed by PF) */
  127                 max_size = BOOTCFG_PER_PF;
  128                 count = BOOTCFG_PF_COUNT;
  129                 if (pf >= count) {
  130                         rc = EINVAL;
  131                         goto fail3;
  132                 }
  133                 offset = max_size * pf;
  134                 break;
  135         }
  136 #endif /* EFSYS_OPT_MEDFORD2 */
  137 
  138         default:
  139                 EFSYS_ASSERT(0);
  140                 rc = ENOTSUP;
  141                 goto fail1;
  142         }
  143         EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE);
  144 
  145         if (sector_countp != NULL)
  146                 *sector_countp = count;
  147         *offsetp = offset;
  148         *max_sizep = max_size;
  149 
  150         return (0);
  151 
  152 #if EFSYS_OPT_MEDFORD2
  153 fail3:
  154         EFSYS_PROBE(fail3);
  155 #endif
  156 #if EFSYS_OPT_MEDFORD
  157 fail2:
  158         EFSYS_PROBE(fail2);
  159 #endif
  160 fail1:
  161         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  162         return (rc);
  163 }
  164 
  165         __checkReturn           uint8_t
  166 efx_dhcp_csum(
  167         __in_bcount(size)       uint8_t const *data,
  168         __in                    size_t size)
  169 {
  170         unsigned int pos;
  171         uint8_t checksum = 0;
  172 
  173         for (pos = 0; pos < size; pos++)
  174                 checksum += data[pos];
  175         return (checksum);
  176 }
  177 
  178         __checkReturn           efx_rc_t
  179 efx_dhcp_verify(
  180         __in_bcount(size)       uint8_t const *data,
  181         __in                    size_t size,
  182         __out_opt               size_t *usedp)
  183 {
  184         size_t offset = 0;
  185         size_t used = 0;
  186         efx_rc_t rc;
  187 
  188         /* Start parsing tags immediately after the checksum */
  189         for (offset = 1; offset < size; ) {
  190                 uint8_t tag;
  191                 uint8_t length;
  192 
  193                 /* Consume tag */
  194                 tag = data[offset];
  195                 if (tag == EFX_DHCP_END) {
  196                         offset++;
  197                         used = offset;
  198                         break;
  199                 }
  200                 if (tag == EFX_DHCP_PAD) {
  201                         offset++;
  202                         continue;
  203                 }
  204 
  205                 /* Consume length */
  206                 if (offset + 1 >= size) {
  207                         rc = ENOSPC;
  208                         goto fail1;
  209                 }
  210                 length = data[offset + 1];
  211 
  212                 /* Consume *length */
  213                 if (offset + 1 + length >= size) {
  214                         rc = ENOSPC;
  215                         goto fail2;
  216                 }
  217 
  218                 offset += 2 + length;
  219                 used = offset;
  220         }
  221 
  222         /* Checksum the entire sector, including bytes after any EFX_DHCP_END */
  223         if (efx_dhcp_csum(data, size) != 0) {
  224                 rc = EINVAL;
  225                 goto fail3;
  226         }
  227 
  228         if (usedp != NULL)
  229                 *usedp = used;
  230 
  231         return (0);
  232 
  233 fail3:
  234         EFSYS_PROBE(fail3);
  235 fail2:
  236         EFSYS_PROBE(fail2);
  237 fail1:
  238         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  239 
  240         return (rc);
  241 }
  242 
  243 /*
  244  * Walk the entire tag set looking for option. The sought option may be
  245  * encapsulated. ENOENT indicates the walk completed without finding the
  246  * option. If we run out of buffer during the walk the function will return
  247  * ENOSPC.
  248  */
  249 static  efx_rc_t
  250 efx_dhcp_walk_tags(
  251         __deref_inout   uint8_t **tagpp,
  252         __inout         size_t *buffer_sizep,
  253         __in            uint16_t opt)
  254 {
  255         efx_rc_t rc = 0;
  256         boolean_t is_encap = B_FALSE;
  257 
  258         if (DHCP_IS_ENCAP_OPT(opt)) {
  259                 /*
  260                  * Look for the encapsulator and, if found, limit ourselves
  261                  * to its payload. If it's not found then the entire tag
  262                  * cannot be found, so the encapsulated opt search is
  263                  * skipped.
  264                  */
  265                 rc = efx_dhcp_walk_tags(tagpp, buffer_sizep,
  266                     DHCP_ENCAPSULATOR(opt));
  267                 if (rc == 0) {
  268                         *buffer_sizep = ((efx_dhcp_tag_hdr_t *)*tagpp)->length;
  269                         (*tagpp) += sizeof (efx_dhcp_tag_hdr_t);
  270                 }
  271                 opt = DHCP_ENCAPSULATED(opt);
  272                 is_encap = B_TRUE;
  273         }
  274 
  275         EFSYS_ASSERT(!DHCP_IS_ENCAP_OPT(opt));
  276 
  277         while (rc == 0) {
  278                 size_t size;
  279 
  280                 if (*buffer_sizep == 0) {
  281                         rc = ENOSPC;
  282                         goto fail1;
  283                 }
  284 
  285                 if (DHCP_ENCAPSULATED(**tagpp) == opt)
  286                         break;
  287 
  288                 if ((**tagpp) == EFX_DHCP_END) {
  289                         rc = ENOENT;
  290                         break;
  291                 } else if ((**tagpp) == EFX_DHCP_PAD) {
  292                         size = 1;
  293                 } else {
  294                         if (*buffer_sizep < sizeof (efx_dhcp_tag_hdr_t)) {
  295                                 rc = ENOSPC;
  296                                 goto fail2;
  297                         }
  298 
  299                         size =
  300                             DHCP_FULL_TAG_LENGTH((efx_dhcp_tag_hdr_t *)*tagpp);
  301                 }
  302 
  303                 if (size > *buffer_sizep) {
  304                         rc = ENOSPC;
  305                         goto fail3;
  306                 }
  307 
  308                 (*tagpp) += size;
  309                 (*buffer_sizep) -= size;
  310 
  311                 if ((*buffer_sizep == 0) && is_encap) {
  312                         /* Search within encapulator tag finished */
  313                         rc = ENOENT;
  314                         break;
  315                 }
  316         }
  317 
  318         /*
  319          * Returns 0 if found otherwise ENOENT indicating search finished
  320          * correctly
  321          */
  322         return (rc);
  323 
  324 fail3:
  325         EFSYS_PROBE(fail3);
  326 fail2:
  327         EFSYS_PROBE(fail2);
  328 fail1:
  329         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  330 
  331         return (rc);
  332 }
  333 
  334 /*
  335  * Locate value buffer for option in the given buffer.
  336  * Returns 0 if found, ENOENT indicating search finished
  337  * correctly, otherwise search failed before completion.
  338  */
  339         __checkReturn   efx_rc_t
  340 efx_dhcp_find_tag(
  341         __in_bcount(buffer_length)      uint8_t *bufferp,
  342         __in                            size_t buffer_length,
  343         __in                            uint16_t opt,
  344         __deref_out                     uint8_t **valuepp,
  345         __out                           size_t *value_lengthp)
  346 {
  347         efx_rc_t rc;
  348         uint8_t *tagp = bufferp;
  349         size_t len = buffer_length;
  350 
  351         rc = efx_dhcp_walk_tags(&tagp, &len, opt);
  352         if (rc == 0) {
  353                 efx_dhcp_tag_hdr_t *hdrp;
  354 
  355                 hdrp = (efx_dhcp_tag_hdr_t *)tagp;
  356                 *valuepp = (uint8_t *)(&hdrp[1]);
  357                 *value_lengthp = hdrp->length;
  358         } else if (rc != ENOENT) {
  359                 goto fail1;
  360         }
  361 
  362         return (rc);
  363 
  364 fail1:
  365         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  366 
  367         return (rc);
  368 }
  369 
  370 /*
  371  * Locate the end tag in the given buffer.
  372  * Returns 0 if found, ENOENT indicating search finished
  373  * correctly but end tag was not found; otherwise search
  374  * failed before completion.
  375  */
  376         __checkReturn   efx_rc_t
  377 efx_dhcp_find_end(
  378         __in_bcount(buffer_length)      uint8_t *bufferp,
  379         __in                            size_t buffer_length,
  380         __deref_out                     uint8_t **endpp)
  381 {
  382         efx_rc_t rc;
  383         uint8_t *endp = bufferp;
  384         size_t len = buffer_length;
  385 
  386         rc = efx_dhcp_walk_tags(&endp, &len, EFX_DHCP_END);
  387         if (rc == 0)
  388                 *endpp = endp;
  389         else if (rc != ENOENT)
  390                 goto fail1;
  391 
  392         return (rc);
  393 
  394 fail1:
  395         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  396 
  397         return (rc);
  398 }
  399 
  400 /*
  401  * Delete the given tag from anywhere in the buffer. Copes with
  402  * encapsulated tags, and updates or deletes the encapsulating opt as
  403  * necessary.
  404  */
  405         __checkReturn   efx_rc_t
  406 efx_dhcp_delete_tag(
  407         __inout_bcount(buffer_length)   uint8_t *bufferp,
  408         __in                            size_t buffer_length,
  409         __in                            uint16_t opt)
  410 {
  411         efx_rc_t rc;
  412         efx_dhcp_tag_hdr_t *hdrp;
  413         size_t len;
  414         uint8_t *startp;
  415         uint8_t *endp;
  416 
  417         len = buffer_length;
  418         startp = bufferp;
  419 
  420         if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
  421                 rc = EINVAL;
  422                 goto fail1;
  423         }
  424 
  425         rc = efx_dhcp_walk_tags(&startp, &len, opt);
  426         if (rc != 0)
  427                 goto fail1;
  428 
  429         hdrp = (efx_dhcp_tag_hdr_t *)startp;
  430 
  431         if (DHCP_IS_ENCAP_OPT(opt)) {
  432                 uint8_t tag_length = DHCP_FULL_TAG_LENGTH(hdrp);
  433                 uint8_t *encapp = bufferp;
  434                 efx_dhcp_tag_hdr_t *encap_hdrp;
  435 
  436                 len = buffer_length;
  437                 rc = efx_dhcp_walk_tags(&encapp, &len,
  438                     DHCP_ENCAPSULATOR(opt));
  439                 if (rc != 0)
  440                         goto fail2;
  441 
  442                 encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp;
  443                 if (encap_hdrp->length > tag_length) {
  444                         encap_hdrp->length = (uint8_t)(
  445                             (size_t)encap_hdrp->length - tag_length);
  446                 } else {
  447                         /* delete the encapsulating tag */
  448                         hdrp = encap_hdrp;
  449                 }
  450         }
  451 
  452         startp = (uint8_t *)hdrp;
  453         endp = (uint8_t *)DHCP_NEXT_TAG(hdrp);
  454 
  455         if (startp < bufferp) {
  456                 rc = EINVAL;
  457                 goto fail3;
  458         }
  459 
  460         if (endp > &bufferp[buffer_length]) {
  461                 rc = EINVAL;
  462                 goto fail4;
  463         }
  464 
  465         memmove(startp, endp,
  466                 buffer_length - (endp - bufferp));
  467 
  468         return (0);
  469 
  470 fail4:
  471         EFSYS_PROBE(fail4);
  472 fail3:
  473         EFSYS_PROBE(fail3);
  474 fail2:
  475         EFSYS_PROBE(fail2);
  476 fail1:
  477         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  478 
  479         return (rc);
  480 }
  481 
  482 /*
  483  * Write the tag header into write_pointp and optionally copies the payload
  484  * into the space following.
  485  */
  486 static  void
  487 efx_dhcp_write_tag(
  488         __in            uint8_t *write_pointp,
  489         __in            uint16_t opt,
  490         __in_bcount_opt(value_length)
  491                         uint8_t *valuep,
  492         __in            size_t value_length)
  493 {
  494         efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp;
  495         hdrp->tag = DHCP_ENCAPSULATED(opt);
  496         hdrp->length = (uint8_t)value_length;
  497         if ((value_length > 0) && (valuep != NULL))
  498                 memcpy(&hdrp[1], valuep, value_length);
  499 }
  500 
  501 /*
  502  * Add the given tag to the end of the buffer. Copes with creating an
  503  * encapsulated tag, and updates or creates the encapsulating opt as
  504  * necessary.
  505  */
  506         __checkReturn   efx_rc_t
  507 efx_dhcp_add_tag(
  508         __inout_bcount(buffer_length)   uint8_t *bufferp,
  509         __in                            size_t buffer_length,
  510         __in                            uint16_t opt,
  511         __in_bcount_opt(value_length)   uint8_t *valuep,
  512         __in                            size_t value_length)
  513 {
  514         efx_rc_t rc;
  515         efx_dhcp_tag_hdr_t *encap_hdrp = NULL;
  516         uint8_t *insert_pointp = NULL;
  517         uint8_t *endp;
  518         size_t available_space;
  519         size_t added_length;
  520         size_t search_size;
  521         uint8_t *searchp;
  522 
  523         if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
  524                 rc = EINVAL;
  525                 goto fail1;
  526         }
  527 
  528         if (value_length > DHCP_MAX_VALUE) {
  529                 rc = EINVAL;
  530                 goto fail2;
  531         }
  532 
  533         if ((value_length > 0) && (valuep == NULL)) {
  534                 rc = EINVAL;
  535                 goto fail3;
  536         }
  537 
  538         endp = bufferp;
  539         available_space = buffer_length;
  540         rc = efx_dhcp_walk_tags(&endp, &available_space, EFX_DHCP_END);
  541         if (rc != 0)
  542                 goto fail4;
  543 
  544         searchp = bufferp;
  545         search_size = buffer_length;
  546         if (DHCP_IS_ENCAP_OPT(opt)) {
  547                 rc = efx_dhcp_walk_tags(&searchp, &search_size,
  548                     DHCP_ENCAPSULATOR(opt));
  549                 if (rc == 0) {
  550                         encap_hdrp = (efx_dhcp_tag_hdr_t *)searchp;
  551 
  552                         /* Check encapsulated tag is not present */
  553                         search_size = encap_hdrp->length;
  554                         rc = efx_dhcp_walk_tags(&searchp, &search_size,
  555                             opt);
  556                         if (rc != ENOENT) {
  557                                 rc = EINVAL;
  558                                 goto fail5;
  559                         }
  560 
  561                         /* Check encapsulator will not overflow */
  562                         if (((size_t)encap_hdrp->length +
  563                             DHCP_CALC_TAG_LENGTH(value_length)) >
  564                             DHCP_MAX_VALUE) {
  565                                 rc = E2BIG;
  566                                 goto fail6;
  567                         }
  568 
  569                         /* Insert at start of existing encapsulator */
  570                         insert_pointp = (uint8_t *)&encap_hdrp[1];
  571                         opt = DHCP_ENCAPSULATED(opt);
  572                 } else if (rc == ENOENT) {
  573                         encap_hdrp = NULL;
  574                 } else {
  575                         goto fail7;
  576                 }
  577         } else {
  578                 /* Check unencapsulated tag is not present */
  579                 rc = efx_dhcp_walk_tags(&searchp, &search_size,
  580                     opt);
  581                 if (rc != ENOENT) {
  582                         rc = EINVAL;
  583                         goto fail8;
  584                 }
  585         }
  586 
  587         if (insert_pointp == NULL) {
  588                 /* Insert at end of existing tags */
  589                 insert_pointp = endp;
  590         }
  591 
  592         /* Includes the new encapsulator tag hdr if required */
  593         added_length = DHCP_CALC_TAG_LENGTH(value_length) +
  594             (DHCP_IS_ENCAP_OPT(opt) ? sizeof (efx_dhcp_tag_hdr_t) : 0);
  595 
  596         if (available_space <= added_length) {
  597                 rc = ENOMEM;
  598                 goto fail9;
  599         }
  600 
  601         memmove(insert_pointp + added_length, insert_pointp,
  602             available_space - added_length);
  603 
  604         if (DHCP_IS_ENCAP_OPT(opt)) {
  605                 /* Create new encapsulator header */
  606                 added_length -= sizeof (efx_dhcp_tag_hdr_t);
  607                 efx_dhcp_write_tag(insert_pointp,
  608                     DHCP_ENCAPSULATOR(opt), NULL, added_length);
  609                 insert_pointp += sizeof (efx_dhcp_tag_hdr_t);
  610         } else if (encap_hdrp)
  611                 /* Modify existing encapsulator header */
  612                 encap_hdrp->length +=
  613                     ((uint8_t)DHCP_CALC_TAG_LENGTH(value_length));
  614 
  615         efx_dhcp_write_tag(insert_pointp, opt, valuep, value_length);
  616 
  617         return (0);
  618 
  619 fail9:
  620         EFSYS_PROBE(fail9);
  621 fail8:
  622         EFSYS_PROBE(fail8);
  623 fail7:
  624         EFSYS_PROBE(fail7);
  625 fail6:
  626         EFSYS_PROBE(fail6);
  627 fail5:
  628         EFSYS_PROBE(fail5);
  629 fail4:
  630         EFSYS_PROBE(fail4);
  631 fail3:
  632         EFSYS_PROBE(fail3);
  633 fail2:
  634         EFSYS_PROBE(fail2);
  635 fail1:
  636         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  637 
  638         return (rc);
  639 }
  640 
  641 /*
  642  * Update an existing tag to the new value. Copes with encapsulated
  643  * tags, and updates the encapsulating opt as necessary.
  644  */
  645         __checkReturn   efx_rc_t
  646 efx_dhcp_update_tag(
  647         __inout_bcount(buffer_length)   uint8_t *bufferp,
  648         __in                            size_t buffer_length,
  649         __in                            uint16_t opt,
  650         __in                            uint8_t *value_locationp,
  651         __in_bcount_opt(value_length)   uint8_t *valuep,
  652         __in                            size_t value_length)
  653 {
  654         efx_rc_t rc;
  655         uint8_t *write_pointp = value_locationp - sizeof (efx_dhcp_tag_hdr_t);
  656         efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp;
  657         efx_dhcp_tag_hdr_t *encap_hdrp = NULL;
  658         size_t old_length;
  659 
  660         if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
  661                 rc = EINVAL;
  662                 goto fail1;
  663         }
  664 
  665         if (value_length > DHCP_MAX_VALUE) {
  666                 rc = EINVAL;
  667                 goto fail2;
  668         }
  669 
  670         if ((value_length > 0) && (valuep == NULL)) {
  671                 rc = EINVAL;
  672                 goto fail3;
  673         }
  674 
  675         old_length = hdrp->length;
  676 
  677         if (old_length < value_length) {
  678                 uint8_t *endp = bufferp;
  679                 size_t available_space = buffer_length;
  680 
  681                 rc = efx_dhcp_walk_tags(&endp, &available_space,
  682                     EFX_DHCP_END);
  683                 if (rc != 0)
  684                         goto fail4;
  685 
  686                 if (available_space < (value_length - old_length)) {
  687                         rc = EINVAL;
  688                         goto fail5;
  689                 }
  690         }
  691 
  692         if (DHCP_IS_ENCAP_OPT(opt)) {
  693                 uint8_t *encapp = bufferp;
  694                 size_t following_encap = buffer_length;
  695                 size_t new_length;
  696 
  697                 rc = efx_dhcp_walk_tags(&encapp, &following_encap,
  698                     DHCP_ENCAPSULATOR(opt));
  699                 if (rc != 0)
  700                         goto fail6;
  701 
  702                 encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp;
  703 
  704                 new_length = ((size_t)encap_hdrp->length +
  705                     value_length - old_length);
  706                 /* Check encapsulator will not overflow */
  707                 if (new_length > DHCP_MAX_VALUE) {
  708                         rc = E2BIG;
  709                         goto fail7;
  710                 }
  711 
  712                 encap_hdrp->length = (uint8_t)new_length;
  713         }
  714 
  715         /*
  716          * Move the following data up/down to accommodate the new payload
  717          * length.
  718          */
  719         if (old_length != value_length) {
  720                 uint8_t *destp = (uint8_t *)DHCP_NEXT_TAG(hdrp) +
  721                     value_length - old_length;
  722                 size_t count = &bufferp[buffer_length] -
  723                     (uint8_t *)DHCP_NEXT_TAG(hdrp);
  724 
  725                 memmove(destp, DHCP_NEXT_TAG(hdrp), count);
  726         }
  727 
  728         EFSYS_ASSERT(hdrp->tag == DHCP_ENCAPSULATED(opt));
  729         efx_dhcp_write_tag(write_pointp, opt, valuep, value_length);
  730 
  731         return (0);
  732 
  733 fail7:
  734         EFSYS_PROBE(fail7);
  735 fail6:
  736         EFSYS_PROBE(fail6);
  737 fail5:
  738         EFSYS_PROBE(fail5);
  739 fail4:
  740         EFSYS_PROBE(fail4);
  741 fail3:
  742         EFSYS_PROBE(fail3);
  743 fail2:
  744         EFSYS_PROBE(fail2);
  745 fail1:
  746         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  747 
  748         return (rc);
  749 }
  750 
  751 /*
  752  * Copy bootcfg sector data to a target buffer which may differ in size.
  753  * Optionally corrects format errors in source buffer.
  754  */
  755                                 efx_rc_t
  756 efx_bootcfg_copy_sector(
  757         __in                    efx_nic_t *enp,
  758         __inout_bcount(sector_length)
  759                                 uint8_t *sector,
  760         __in                    size_t sector_length,
  761         __out_bcount(data_size) uint8_t *data,
  762         __in                    size_t data_size,
  763         __in                    boolean_t handle_format_errors)
  764 {
  765         _NOTE(ARGUNUSED(enp))
  766 
  767         size_t used_bytes;
  768         efx_rc_t rc;
  769 
  770         /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */
  771         if (data_size < 2) {
  772                 rc = ENOSPC;
  773                 goto fail1;
  774         }
  775 
  776         /* Verify that the area is correctly formatted and checksummed */
  777         rc = efx_dhcp_verify(sector, sector_length,
  778                                     &used_bytes);
  779 
  780         if (!handle_format_errors) {
  781                 if (rc != 0)
  782                         goto fail2;
  783 
  784                 if ((used_bytes < 2) ||
  785                     (sector[used_bytes - 1] != EFX_DHCP_END)) {
  786                         /* Block too short, or EFX_DHCP_END missing */
  787                         rc = ENOENT;
  788                         goto fail3;
  789                 }
  790         }
  791 
  792         /* Synthesize empty format on verification failure */
  793         if (rc != 0 || used_bytes == 0) {
  794                 sector[0] = 0;
  795                 sector[1] = EFX_DHCP_END;
  796                 used_bytes = 2;
  797         }
  798         EFSYS_ASSERT(used_bytes >= 2);  /* checksum and EFX_DHCP_END */
  799         EFSYS_ASSERT(used_bytes <= sector_length);
  800         EFSYS_ASSERT(sector_length >= 2);
  801 
  802         /*
  803          * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END
  804          * character. Modify the returned payload so it does.
  805          * Reinitialise the sector if there isn't room for the character.
  806          */
  807         if (sector[used_bytes - 1] != EFX_DHCP_END) {
  808                 if (used_bytes >= sector_length) {
  809                         sector[0] = 0;
  810                         used_bytes = 1;
  811                 }
  812                 sector[used_bytes] = EFX_DHCP_END;
  813                 ++used_bytes;
  814         }
  815 
  816         /*
  817          * Verify that the target buffer is large enough for the
  818          * entire used bootcfg area, then copy into the target buffer.
  819          */
  820         if (used_bytes > data_size) {
  821                 rc = ENOSPC;
  822                 goto fail4;
  823         }
  824 
  825         data[0] = 0; /* checksum, updated below */
  826 
  827         /* Copy all after the checksum to the target buffer */
  828         memcpy(data + 1, sector + 1, used_bytes - 1);
  829 
  830         /* Zero out the unused portion of the target buffer */
  831         if (used_bytes < data_size)
  832                 (void) memset(data + used_bytes, 0, data_size - used_bytes);
  833 
  834         /*
  835          * The checksum includes trailing data after any EFX_DHCP_END
  836          * character, which we've just modified (by truncation or appending
  837          * EFX_DHCP_END).
  838          */
  839         data[0] -= efx_dhcp_csum(data, data_size);
  840 
  841         return (0);
  842 
  843 fail4:
  844         EFSYS_PROBE(fail4);
  845 fail3:
  846         EFSYS_PROBE(fail3);
  847 fail2:
  848         EFSYS_PROBE(fail2);
  849 fail1:
  850         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  851 
  852         return (rc);
  853 }
  854 
  855                                 efx_rc_t
  856 efx_bootcfg_read(
  857         __in                    efx_nic_t *enp,
  858         __out_bcount(size)      uint8_t *data,
  859         __in                    size_t size)
  860 {
  861         uint8_t *payload = NULL;
  862         size_t used_bytes;
  863         size_t partn_length;
  864         size_t sector_length;
  865         size_t sector_offset;
  866         efx_rc_t rc;
  867         uint32_t sector_number;
  868 
  869         /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */
  870         if (size < 2) {
  871                 rc = ENOSPC;
  872                 goto fail1;
  873         }
  874 
  875 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
  876         sector_number = enp->en_nic_cfg.enc_pf;
  877 #else
  878         sector_number = 0;
  879 #endif
  880         rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
  881         if (rc != 0)
  882                 goto fail2;
  883 
  884         /* The bootcfg sector may be stored in a (larger) shared partition */
  885         rc = efx_bootcfg_sector_info(enp, sector_number,
  886             NULL, &sector_offset, &sector_length);
  887         if (rc != 0)
  888                 goto fail3;
  889 
  890         if (sector_length < 2) {
  891                 rc = EINVAL;
  892                 goto fail4;
  893         }
  894 
  895         if (sector_length > BOOTCFG_MAX_SIZE)
  896                 sector_length = BOOTCFG_MAX_SIZE;
  897 
  898         if (sector_offset + sector_length > partn_length) {
  899                 /* Partition is too small */
  900                 rc = EFBIG;
  901                 goto fail5;
  902         }
  903 
  904         /*
  905          * We need to read the entire BOOTCFG sector to ensure we read all
  906          * tags, because legacy bootcfg sectors are not guaranteed to end
  907          * with an EFX_DHCP_END character. If the user hasn't supplied a
  908          * sufficiently large buffer then use our own buffer.
  909          */
  910         if (sector_length > size) {
  911                 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload);
  912                 if (payload == NULL) {
  913                         rc = ENOMEM;
  914                         goto fail6;
  915                 }
  916         } else
  917                 payload = (uint8_t *)data;
  918 
  919         if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
  920                 goto fail7;
  921 
  922         if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
  923             sector_offset, (caddr_t)payload, sector_length)) != 0) {
  924                 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
  925                 goto fail8;
  926         }
  927 
  928         if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
  929                 goto fail9;
  930 
  931         /* Verify that the area is correctly formatted and checksummed */
  932         rc = efx_dhcp_verify(payload, sector_length,
  933             &used_bytes);
  934         if (rc != 0 || used_bytes == 0) {
  935                 payload[0] = 0;
  936                 payload[1] = EFX_DHCP_END;
  937                 used_bytes = 2;
  938         }
  939 
  940         EFSYS_ASSERT(used_bytes >= 2);  /* checksum and EFX_DHCP_END */
  941         EFSYS_ASSERT(used_bytes <= sector_length);
  942 
  943         /*
  944          * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END
  945          * character. Modify the returned payload so it does.
  946          * BOOTCFG_MAX_SIZE is by definition large enough for any valid
  947          * (per-port) bootcfg sector, so reinitialise the sector if there
  948          * isn't room for the character.
  949          */
  950         if (payload[used_bytes - 1] != EFX_DHCP_END) {
  951                 if (used_bytes >= sector_length)
  952                         used_bytes = 1;
  953 
  954                 payload[used_bytes] = EFX_DHCP_END;
  955                 ++used_bytes;
  956         }
  957 
  958         /*
  959          * Verify that the user supplied buffer is large enough for the
  960          * entire used bootcfg area, then copy into the user supplied buffer.
  961          */
  962         if (used_bytes > size) {
  963                 rc = ENOSPC;
  964                 goto fail10;
  965         }
  966 
  967         data[0] = 0; /* checksum, updated below */
  968 
  969         if (sector_length > size) {
  970                 /* Copy all after the checksum to the target buffer */
  971                 memcpy(data + 1, payload + 1, used_bytes - 1);
  972                 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
  973         }
  974 
  975         /* Zero out the unused portion of the user buffer */
  976         if (used_bytes < size)
  977                 (void) memset(data + used_bytes, 0, size - used_bytes);
  978 
  979         /*
  980          * The checksum includes trailing data after any EFX_DHCP_END character,
  981          * which we've just modified (by truncation or appending EFX_DHCP_END).
  982          */
  983         data[0] -= efx_dhcp_csum(data, size);
  984 
  985         return (0);
  986 
  987 fail10:
  988         EFSYS_PROBE(fail10);
  989 fail9:
  990         EFSYS_PROBE(fail9);
  991 fail8:
  992         EFSYS_PROBE(fail8);
  993 fail7:
  994         EFSYS_PROBE(fail7);
  995         if (sector_length > size)
  996                 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
  997 fail6:
  998         EFSYS_PROBE(fail6);
  999 fail5:
 1000         EFSYS_PROBE(fail5);
 1001 fail4:
 1002         EFSYS_PROBE(fail4);
 1003 fail3:
 1004         EFSYS_PROBE(fail3);
 1005 fail2:
 1006         EFSYS_PROBE(fail2);
 1007 fail1:
 1008         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1009 
 1010         return (rc);
 1011 }
 1012 
 1013                                 efx_rc_t
 1014 efx_bootcfg_write(
 1015         __in                    efx_nic_t *enp,
 1016         __in_bcount(size)       uint8_t *data,
 1017         __in                    size_t size)
 1018 {
 1019         uint8_t *partn_data;
 1020         uint8_t checksum;
 1021         size_t partn_length;
 1022         size_t sector_length;
 1023         size_t sector_offset;
 1024         size_t used_bytes;
 1025         efx_rc_t rc;
 1026         uint32_t sector_number;
 1027 
 1028 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
 1029         sector_number = enp->en_nic_cfg.enc_pf;
 1030 #else
 1031         sector_number = 0;
 1032 #endif
 1033 
 1034         rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
 1035         if (rc != 0)
 1036                 goto fail1;
 1037 
 1038         /* The bootcfg sector may be stored in a (larger) shared partition */
 1039         rc = efx_bootcfg_sector_info(enp, sector_number,
 1040             NULL, &sector_offset, &sector_length);
 1041         if (rc != 0)
 1042                 goto fail2;
 1043 
 1044         if (sector_length > BOOTCFG_MAX_SIZE)
 1045                 sector_length = BOOTCFG_MAX_SIZE;
 1046 
 1047         if (sector_offset + sector_length > partn_length) {
 1048                 /* Partition is too small */
 1049                 rc = EFBIG;
 1050                 goto fail3;
 1051         }
 1052 
 1053         if ((rc = efx_dhcp_verify(data, size, &used_bytes)) != 0)
 1054                 goto fail4;
 1055 
 1056         /*
 1057          * The caller *must* terminate their block with a EFX_DHCP_END
 1058          * character
 1059          */
 1060         if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] !=
 1061             EFX_DHCP_END)) {
 1062                 /* Block too short or EFX_DHCP_END missing */
 1063                 rc = ENOENT;
 1064                 goto fail5;
 1065         }
 1066 
 1067         /* Check that the hardware has support for this much data */
 1068         if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) {
 1069                 rc = ENOSPC;
 1070                 goto fail6;
 1071         }
 1072 
 1073         /*
 1074          * If the BOOTCFG sector is stored in a shared partition, then we must
 1075          * read the whole partition and insert the updated bootcfg sector at the
 1076          * correct offset.
 1077          */
 1078         EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data);
 1079         if (partn_data == NULL) {
 1080                 rc = ENOMEM;
 1081                 goto fail7;
 1082         }
 1083 
 1084         rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
 1085         if (rc != 0)
 1086                 goto fail8;
 1087 
 1088         /* Read the entire partition */
 1089         rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0,
 1090                                     (caddr_t)partn_data, partn_length);
 1091         if (rc != 0)
 1092                 goto fail9;
 1093 
 1094         /*
 1095          * Insert the BOOTCFG sector into the partition, Zero out all data
 1096          * after the EFX_DHCP_END tag, and adjust the checksum.
 1097          */
 1098         (void) memset(partn_data + sector_offset, 0x0, sector_length);
 1099         (void) memcpy(partn_data + sector_offset, data, used_bytes);
 1100 
 1101         checksum = efx_dhcp_csum(data, used_bytes);
 1102         partn_data[sector_offset] -= checksum;
 1103 
 1104         if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
 1105                 goto fail10;
 1106 
 1107         if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
 1108                     0, (caddr_t)partn_data, partn_length)) != 0)
 1109                 goto fail11;
 1110 
 1111         if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
 1112                 goto fail12;
 1113 
 1114         EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
 1115 
 1116         return (0);
 1117 
 1118 fail12:
 1119         EFSYS_PROBE(fail12);
 1120 fail11:
 1121         EFSYS_PROBE(fail11);
 1122 fail10:
 1123         EFSYS_PROBE(fail10);
 1124 fail9:
 1125         EFSYS_PROBE(fail9);
 1126 
 1127         (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
 1128 fail8:
 1129         EFSYS_PROBE(fail8);
 1130 
 1131         EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
 1132 fail7:
 1133         EFSYS_PROBE(fail7);
 1134 fail6:
 1135         EFSYS_PROBE(fail6);
 1136 fail5:
 1137         EFSYS_PROBE(fail5);
 1138 fail4:
 1139         EFSYS_PROBE(fail4);
 1140 fail3:
 1141         EFSYS_PROBE(fail3);
 1142 fail2:
 1143         EFSYS_PROBE(fail2);
 1144 fail1:
 1145         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 1146 
 1147         return (rc);
 1148 }
 1149 
 1150 #endif  /* EFSYS_OPT_BOOTCFG */

Cache object: 5a921e5f6f0765bb2b15c4b96fc9fc78


[ 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.