1 /*
   2  * Copyright (c) 2009-2015 Solarflare Communications Inc.
   3  * All rights reserved.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions are met:
   7  *
   8  * 1. Redistributions of source code must retain the above copyright notice,
   9  *    this list of conditions and the following disclaimer.
  10  * 2. Redistributions in binary form must reproduce the above copyright notice,
  11  *    this list of conditions and the following disclaimer in the documentation
  12  *    and/or other materials provided with the distribution.
  13  *
  14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  24  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25  *
  26  * The views and conclusions contained in the software and documentation are
  27  * those of the authors and should not be interpreted as representing official
  28  * policies, either expressed or implied, of the FreeBSD Project.
  29  */
  30 
  31 #include "efx.h"
  32 #include "efx_impl.h"
  33 
  34 #if EFSYS_OPT_BOOTCFG
  35 
  36 /*
  37  * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE.
  38  * A multiple of 0x100 so trailing 0xff characters don't contrinbute to the
  39  * checksum.
  40  */
  41 #define BOOTCFG_MAX_SIZE 0x1000
  42 
  43 #define DHCP_END (uint8_t)0xff
  44 #define DHCP_PAD (uint8_t)0
  45 
  46 static  __checkReturn           uint8_t
  47 efx_bootcfg_csum(
  48         __in                    efx_nic_t *enp,
  49         __in_bcount(size)       caddr_t data,
  50         __in                    size_t size)
  51 {
  52         _NOTE(ARGUNUSED(enp))
  53 
  54         unsigned int pos;
  55         uint8_t checksum = 0;
  56 
  57         for (pos = 0; pos < size; pos++)
  58                 checksum += data[pos];
  59         return (checksum);
  60 }
  61 
  62 static  __checkReturn           efx_rc_t
  63 efx_bootcfg_verify(
  64         __in                    efx_nic_t *enp,
  65         __in_bcount(size)       caddr_t data,
  66         __in                    size_t size,
  67         __out_opt               size_t *usedp)
  68 {
  69         size_t offset = 0;
  70         size_t used = 0;
  71         efx_rc_t rc;
  72 
  73         /* Start parsing tags immediatly after the checksum */
  74         for (offset = 1; offset < size; ) {
  75                 uint8_t tag;
  76                 uint8_t length;
  77 
  78                 /* Consume tag */
  79                 tag = data[offset];
  80                 if (tag == DHCP_END) {
  81                         offset++;
  82                         used = offset;
  83                         break;
  84                 }
  85                 if (tag == DHCP_PAD) {
  86                         offset++;
  87                         continue;
  88                 }
  89 
  90                 /* Consume length */
  91                 if (offset + 1 >= size) {
  92                         rc = ENOSPC;
  93                         goto fail1;
  94                 }
  95                 length = data[offset + 1];
  96 
  97                 /* Consume *length */
  98                 if (offset + 1 + length >= size) {
  99                         rc = ENOSPC;
 100                         goto fail2;
 101                 }
 102 
 103                 offset += 2 + length;
 104                 used = offset;
 105         }
 106 
 107         /* Checksum the entire sector, including bytes after any DHCP_END */
 108         if (efx_bootcfg_csum(enp, data, size) != 0) {
 109                 rc = EINVAL;
 110                 goto fail3;
 111         }
 112 
 113         if (usedp != NULL)
 114                 *usedp = used;
 115 
 116         return (0);
 117 
 118 fail3:
 119         EFSYS_PROBE(fail3);
 120 fail2:
 121         EFSYS_PROBE(fail2);
 122 fail1:
 123         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 124 
 125         return (rc);
 126 }
 127 
 128                                 efx_rc_t
 129 efx_bootcfg_read(
 130         __in                    efx_nic_t *enp,
 131         __out_bcount(size)      caddr_t data,
 132         __in                    size_t size)
 133 {
 134         uint8_t *payload = NULL;
 135         size_t used_bytes;
 136         size_t sector_length;
 137         efx_rc_t rc;
 138 
 139         rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &sector_length);
 140         if (rc != 0)
 141                 goto fail1;
 142 
 143         /*
 144          * We need to read the entire BOOTCFG area to ensure we read all the
 145          * tags, because legacy bootcfg sectors are not guaranteed to end with
 146          * a DHCP_END character. If the user hasn't supplied a sufficiently
 147          * large buffer then use our own buffer.
 148          */
 149         if (sector_length > BOOTCFG_MAX_SIZE)
 150                 sector_length = BOOTCFG_MAX_SIZE;
 151         if (sector_length > size) {
 152                 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload);
 153                 if (payload == NULL) {
 154                         rc = ENOMEM;
 155                         goto fail2;
 156                 }
 157         } else
 158                 payload = (uint8_t *)data;
 159 
 160         if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
 161                 goto fail3;
 162 
 163         rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0,
 164                                     (caddr_t)payload, sector_length);
 165 
 166         efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG);
 167 
 168         if (rc != 0)
 169                 goto fail4;
 170 
 171         /* Verify that the area is correctly formatted and checksummed */
 172         rc = efx_bootcfg_verify(enp, (caddr_t)payload, sector_length,
 173                                     &used_bytes);
 174         if (rc != 0 || used_bytes == 0) {
 175                 payload[0] = (uint8_t)~DHCP_END;
 176                 payload[1] = DHCP_END;
 177                 used_bytes = 2;
 178         }
 179 
 180         EFSYS_ASSERT(used_bytes >= 2);       /* checksum and DHCP_END */
 181         EFSYS_ASSERT(used_bytes <= sector_length);
 182 
 183         /*
 184          * Legacy bootcfg sectors don't terminate with a DHCP_END character.
 185          * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by
 186          * definition large enough for any valid (per-port) bootcfg sector,
 187          * so reinitialise the sector if there isn't room for the character.
 188          */
 189         if (payload[used_bytes - 1] != DHCP_END) {
 190                 if (used_bytes + 1 > sector_length) {
 191                         payload[0] = 0;
 192                         used_bytes = 1;
 193                 }
 194 
 195                 payload[used_bytes] = DHCP_END;
 196                 ++used_bytes;
 197         }
 198 
 199         /*
 200          * Verify that the user supplied buffer is large enough for the
 201          * entire used bootcfg area, then copy into the user supplied buffer.
 202          */
 203         if (used_bytes > size) {
 204                 rc = ENOSPC;
 205                 goto fail5;
 206         }
 207         if (sector_length > size) {
 208                 (void) memcpy(data, payload, used_bytes);
 209                 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
 210         }
 211 
 212         /* Zero out the unused portion of the user buffer */
 213         if (used_bytes < size)
 214                 (void) memset(data + used_bytes, 0, size - used_bytes);
 215 
 216         /*
 217          * The checksum includes trailing data after any DHCP_END character,
 218          * which we've just modified (by truncation or appending DHCP_END).
 219          */
 220         data[0] -= efx_bootcfg_csum(enp, data, size);
 221 
 222         return (0);
 223 
 224 fail5:
 225         EFSYS_PROBE(fail5);
 226 fail4:
 227         EFSYS_PROBE(fail4);
 228 fail3:
 229         EFSYS_PROBE(fail3);
 230 
 231         if (sector_length > size)
 232                 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
 233 fail2:
 234         EFSYS_PROBE(fail2);
 235 fail1:
 236         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 237 
 238         return (rc);
 239 }
 240 
 241                                 efx_rc_t
 242 efx_bootcfg_write(
 243         __in                    efx_nic_t *enp,
 244         __in_bcount(size)       caddr_t data,
 245         __in                    size_t size)
 246 {
 247         uint8_t *chunk;
 248         uint8_t checksum;
 249         size_t sector_length;
 250         size_t chunk_length;
 251         size_t used_bytes;
 252         size_t offset;
 253         size_t remaining;
 254         efx_rc_t rc;
 255 
 256         rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &sector_length);
 257         if (rc != 0)
 258                 goto fail1;
 259 
 260         if (sector_length > BOOTCFG_MAX_SIZE)
 261                 sector_length = BOOTCFG_MAX_SIZE;
 262 
 263         if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0)
 264                 goto fail2;
 265 
 266         /* The caller *must* terminate their block with a DHCP_END character */
 267         EFSYS_ASSERT(used_bytes >= 2);               /* checksum and DHCP_END */
 268         if ((uint8_t)data[used_bytes - 1] != DHCP_END) {
 269                 rc = ENOENT;
 270                 goto fail3;
 271         }
 272 
 273         /* Check that the hardware has support for this much data */
 274         if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) {
 275                 rc = ENOSPC;
 276                 goto fail4;
 277         }
 278 
 279         rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, &chunk_length);
 280         if (rc != 0)
 281                 goto fail5;
 282 
 283         EFSYS_KMEM_ALLOC(enp->en_esip, chunk_length, chunk);
 284         if (chunk == NULL) {
 285                 rc = ENOMEM;
 286                 goto fail6;
 287         }
 288 
 289         if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
 290                 goto fail7;
 291 
 292         /*
 293          * Write the entire sector_length bytes of data in chunks. Zero out
 294          * all data following the DHCP_END, and adjust the checksum
 295          */
 296         checksum = efx_bootcfg_csum(enp, data, used_bytes);
 297         for (offset = 0; offset < sector_length; offset += remaining) {
 298                 remaining = MIN(chunk_length, sector_length - offset);
 299 
 300                 /* Fill chunk */
 301                 (void) memset(chunk, 0x0, chunk_length);
 302                 if (offset < used_bytes)
 303                         (void) memcpy(chunk, data + offset,
 304                             MIN(remaining, used_bytes - offset));
 305 
 306                 /* Adjust checksum */
 307                 if (offset == 0)
 308                         chunk[0] -= checksum;
 309 
 310                 if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
 311                             offset, (caddr_t)chunk, remaining)) != 0)
 312                         goto fail8;
 313         }
 314 
 315         efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG);
 316 
 317         EFSYS_KMEM_FREE(enp->en_esip, chunk_length, chunk);
 318 
 319         return (0);
 320 
 321 fail8:
 322         EFSYS_PROBE(fail8);
 323 fail7:
 324         EFSYS_PROBE(fail7);
 325 
 326         EFSYS_KMEM_FREE(enp->en_esip, chunk_length, chunk);
 327 fail6:
 328         EFSYS_PROBE(fail6);
 329 
 330         efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG);
 331 fail5:
 332         EFSYS_PROBE(fail5);
 333 fail4:
 334         EFSYS_PROBE(fail4);
 335 fail3:
 336         EFSYS_PROBE(fail3);
 337 fail2:
 338         EFSYS_PROBE(fail2);
 339 fail1:
 340         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 341 
 342         return (rc);
 343 }
 344 
 345 #endif  /* EFSYS_OPT_BOOTCFG */