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 
  35 #if EFSYS_OPT_VPD
  36 
  37 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
  38 
  39 #include "ef10_tlv_layout.h"
  40 
  41         __checkReturn           efx_rc_t
  42 ef10_vpd_init(
  43         __in                    efx_nic_t *enp)
  44 {
  45         caddr_t svpd;
  46         size_t svpd_size;
  47         uint32_t pci_pf;
  48         uint32_t tag;
  49         efx_rc_t rc;
  50 
  51         EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
  52         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
  53                     enp->en_family == EFX_FAMILY_MEDFORD);
  54 
  55         if (enp->en_nic_cfg.enc_vpd_is_global) {
  56                 tag = TLV_TAG_GLOBAL_STATIC_VPD;
  57         } else {
  58                 pci_pf = enp->en_nic_cfg.enc_pf;
  59                 tag = TLV_TAG_PF_STATIC_VPD(pci_pf);
  60         }
  61 
  62         /*
  63          * The VPD interface exposes VPD resources from the combined static and
  64          * dynamic VPD storage. As the static VPD configuration should *never*
  65          * change, we can cache it.
  66          */
  67         svpd = NULL;
  68         svpd_size = 0;
  69         rc = ef10_nvram_partn_read_tlv(enp,
  70             NVRAM_PARTITION_TYPE_STATIC_CONFIG,
  71             tag, &svpd, &svpd_size);
  72         if (rc != 0) {
  73                 if (rc == EACCES) {
  74                         /* Unprivileged functions cannot access VPD */
  75                         goto out;
  76                 }
  77                 goto fail1;
  78         }
  79 
  80         if (svpd != NULL && svpd_size > 0) {
  81                 if ((rc = efx_vpd_hunk_verify(svpd, svpd_size, NULL)) != 0)
  82                         goto fail2;
  83         }
  84 
  85         enp->en_arch.ef10.ena_svpd = svpd;
  86         enp->en_arch.ef10.ena_svpd_length = svpd_size;
  87 
  88 out:
  89         return (0);
  90 
  91 fail2:
  92         EFSYS_PROBE(fail2);
  93 
  94         EFSYS_KMEM_FREE(enp->en_esip, svpd_size, svpd);
  95 fail1:
  96         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  97 
  98         return (rc);
  99 }
 100 
 101         __checkReturn           efx_rc_t
 102 ef10_vpd_size(
 103         __in                    efx_nic_t *enp,
 104         __out                   size_t *sizep)
 105 {
 106         efx_rc_t rc;
 107 
 108         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
 109                     enp->en_family == EFX_FAMILY_MEDFORD);
 110 
 111         /*
 112          * This function returns the total size the user should allocate
 113          * for all VPD operations. We've already cached the static vpd,
 114          * so we just need to return an upper bound on the dynamic vpd,
 115          * which is the size of the DYNAMIC_CONFIG partition.
 116          */
 117         if ((rc = efx_mcdi_nvram_info(enp, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
 118                     sizep, NULL, NULL, NULL)) != 0)
 119                 goto fail1;
 120 
 121         return (0);
 122 
 123 fail1:
 124         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 125 
 126         return (rc);
 127 }
 128 
 129         __checkReturn           efx_rc_t
 130 ef10_vpd_read(
 131         __in                    efx_nic_t *enp,
 132         __out_bcount(size)      caddr_t data,
 133         __in                    size_t size)
 134 {
 135         caddr_t dvpd;
 136         size_t dvpd_size;
 137         uint32_t pci_pf;
 138         uint32_t tag;
 139         efx_rc_t rc;
 140 
 141         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
 142                     enp->en_family == EFX_FAMILY_MEDFORD);
 143 
 144         if (enp->en_nic_cfg.enc_vpd_is_global) {
 145                 tag = TLV_TAG_GLOBAL_DYNAMIC_VPD;
 146         } else {
 147                 pci_pf = enp->en_nic_cfg.enc_pf;
 148                 tag = TLV_TAG_PF_DYNAMIC_VPD(pci_pf);
 149         }
 150 
 151         if ((rc = ef10_nvram_partn_read_tlv(enp,
 152                     NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
 153                     tag, &dvpd, &dvpd_size)) != 0)
 154                 goto fail1;
 155 
 156         if (dvpd_size > size) {
 157                 rc = ENOSPC;
 158                 goto fail2;
 159         }
 160         (void) memcpy(data, dvpd, dvpd_size);
 161 
 162         /* Pad data with all-1s, consistent with update operations */
 163         (void) memset(data + dvpd_size, 0xff, size - dvpd_size);
 164 
 165         EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd);
 166 
 167         return (0);
 168 
 169 fail2:
 170         EFSYS_PROBE(fail2);
 171 
 172         EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd);
 173 fail1:
 174         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 175 
 176         return (rc);
 177 }
 178 
 179         __checkReturn           efx_rc_t
 180 ef10_vpd_verify(
 181         __in                    efx_nic_t *enp,
 182         __in_bcount(size)       caddr_t data,
 183         __in                    size_t size)
 184 {
 185         efx_vpd_tag_t stag;
 186         efx_vpd_tag_t dtag;
 187         efx_vpd_keyword_t skey;
 188         efx_vpd_keyword_t dkey;
 189         unsigned int scont;
 190         unsigned int dcont;
 191         efx_rc_t rc;
 192 
 193         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
 194                     enp->en_family == EFX_FAMILY_MEDFORD);
 195 
 196         /*
 197          * Strictly you could take the view that dynamic vpd is optional.
 198          * Instead, to conform more closely to the read/verify/reinit()
 199          * paradigm, we require dynamic vpd. ef10_vpd_reinit() will
 200          * reinitialize it as required.
 201          */
 202         if ((rc = efx_vpd_hunk_verify(data, size, NULL)) != 0)
 203                 goto fail1;
 204 
 205         /*
 206          * Verify that there is no duplication between the static and
 207          * dynamic cfg sectors.
 208          */
 209         if (enp->en_arch.ef10.ena_svpd_length == 0)
 210                 goto done;
 211 
 212         dcont = 0;
 213         _NOTE(CONSTANTCONDITION)
 214         while (1) {
 215                 if ((rc = efx_vpd_hunk_next(data, size, &dtag,
 216                     &dkey, NULL, NULL, &dcont)) != 0)
 217                         goto fail2;
 218                 if (dcont == 0)
 219                         break;
 220 
 221                 /*
 222                  * Skip the RV keyword. It should be present in both the static
 223                  * and dynamic cfg sectors.
 224                  */
 225                 if (dtag == EFX_VPD_RO && dkey == EFX_VPD_KEYWORD('R', 'V'))
 226                         continue;
 227 
 228                 scont = 0;
 229                 _NOTE(CONSTANTCONDITION)
 230                 while (1) {
 231                         if ((rc = efx_vpd_hunk_next(
 232                             enp->en_arch.ef10.ena_svpd,
 233                             enp->en_arch.ef10.ena_svpd_length, &stag, &skey,
 234                             NULL, NULL, &scont)) != 0)
 235                                 goto fail3;
 236                         if (scont == 0)
 237                                 break;
 238 
 239                         if (stag == dtag && skey == dkey) {
 240                                 rc = EEXIST;
 241                                 goto fail4;
 242                         }
 243                 }
 244         }
 245 
 246 done:
 247         return (0);
 248 
 249 fail4:
 250         EFSYS_PROBE(fail4);
 251 fail3:
 252         EFSYS_PROBE(fail3);
 253 fail2:
 254         EFSYS_PROBE(fail2);
 255 fail1:
 256         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 257 
 258         return (rc);
 259 }
 260 
 261         __checkReturn           efx_rc_t
 262 ef10_vpd_reinit(
 263         __in                    efx_nic_t *enp,
 264         __in_bcount(size)       caddr_t data,
 265         __in                    size_t size)
 266 {
 267         boolean_t wantpid;
 268         efx_rc_t rc;
 269 
 270         /*
 271          * Only create an ID string if the dynamic cfg doesn't have one
 272          */
 273         if (enp->en_arch.ef10.ena_svpd_length == 0)
 274                 wantpid = B_TRUE;
 275         else {
 276                 unsigned int offset;
 277                 uint8_t length;
 278 
 279                 rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd,
 280                                     enp->en_arch.ef10.ena_svpd_length,
 281                                     EFX_VPD_ID, 0, &offset, &length);
 282                 if (rc == 0)
 283                         wantpid = B_FALSE;
 284                 else if (rc == ENOENT)
 285                         wantpid = B_TRUE;
 286                 else
 287                         goto fail1;
 288         }
 289 
 290         if ((rc = efx_vpd_hunk_reinit(data, size, wantpid)) != 0)
 291                 goto fail2;
 292 
 293         return (0);
 294 
 295 fail2:
 296         EFSYS_PROBE(fail2);
 297 fail1:
 298         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 299 
 300         return (rc);
 301 }
 302 
 303         __checkReturn           efx_rc_t
 304 ef10_vpd_get(
 305         __in                    efx_nic_t *enp,
 306         __in_bcount(size)       caddr_t data,
 307         __in                    size_t size,
 308         __inout                 efx_vpd_value_t *evvp)
 309 {
 310         unsigned int offset;
 311         uint8_t length;
 312         efx_rc_t rc;
 313 
 314         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
 315                     enp->en_family == EFX_FAMILY_MEDFORD);
 316 
 317         /* Attempt to satisfy the request from svpd first */
 318         if (enp->en_arch.ef10.ena_svpd_length > 0) {
 319                 if ((rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd,
 320                     enp->en_arch.ef10.ena_svpd_length, evvp->evv_tag,
 321                     evvp->evv_keyword, &offset, &length)) == 0) {
 322                         evvp->evv_length = length;
 323                         (void) memcpy(evvp->evv_value,
 324                             enp->en_arch.ef10.ena_svpd + offset, length);
 325                         return (0);
 326                 } else if (rc != ENOENT)
 327                         goto fail1;
 328         }
 329 
 330         /* And then from the provided data buffer */
 331         if ((rc = efx_vpd_hunk_get(data, size, evvp->evv_tag,
 332             evvp->evv_keyword, &offset, &length)) != 0)
 333                 goto fail2;
 334 
 335         evvp->evv_length = length;
 336         (void) memcpy(evvp->evv_value, data + offset, length);
 337 
 338         return (0);
 339 
 340 fail2:
 341         EFSYS_PROBE(fail2);
 342 fail1:
 343         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 344 
 345         return (rc);
 346 }
 347 
 348         __checkReturn           efx_rc_t
 349 ef10_vpd_set(
 350         __in                    efx_nic_t *enp,
 351         __in_bcount(size)       caddr_t data,
 352         __in                    size_t size,
 353         __in                    efx_vpd_value_t *evvp)
 354 {
 355         efx_rc_t rc;
 356 
 357         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
 358                     enp->en_family == EFX_FAMILY_MEDFORD);
 359 
 360         /* If the provided (tag,keyword) exists in svpd, then it is readonly */
 361         if (enp->en_arch.ef10.ena_svpd_length > 0) {
 362                 unsigned int offset;
 363                 uint8_t length;
 364 
 365                 if ((rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd,
 366                     enp->en_arch.ef10.ena_svpd_length, evvp->evv_tag,
 367                     evvp->evv_keyword, &offset, &length)) == 0) {
 368                         rc = EACCES;
 369                         goto fail1;
 370                 }
 371         }
 372 
 373         if ((rc = efx_vpd_hunk_set(data, size, evvp)) != 0)
 374                 goto fail2;
 375 
 376         return (0);
 377 
 378 fail2:
 379         EFSYS_PROBE(fail2);
 380 fail1:
 381         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 382 
 383         return (rc);
 384 }
 385 
 386         __checkReturn           efx_rc_t
 387 ef10_vpd_next(
 388         __in                    efx_nic_t *enp,
 389         __in_bcount(size)       caddr_t data,
 390         __in                    size_t size,
 391         __out                   efx_vpd_value_t *evvp,
 392         __inout                 unsigned int *contp)
 393 {
 394         _NOTE(ARGUNUSED(enp, data, size, evvp, contp))
 395 
 396         return (ENOTSUP);
 397 }
 398 
 399         __checkReturn           efx_rc_t
 400 ef10_vpd_write(
 401         __in                    efx_nic_t *enp,
 402         __in_bcount(size)       caddr_t data,
 403         __in                    size_t size)
 404 {
 405         size_t vpd_length;
 406         uint32_t pci_pf;
 407         uint32_t tag;
 408         efx_rc_t rc;
 409 
 410         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
 411                     enp->en_family == EFX_FAMILY_MEDFORD);
 412 
 413         if (enp->en_nic_cfg.enc_vpd_is_global) {
 414                 tag = TLV_TAG_GLOBAL_DYNAMIC_VPD;
 415         } else {
 416                 pci_pf = enp->en_nic_cfg.enc_pf;
 417                 tag = TLV_TAG_PF_DYNAMIC_VPD(pci_pf);
 418         }
 419 
 420         /* Determine total length of new dynamic VPD */
 421         if ((rc = efx_vpd_hunk_length(data, size, &vpd_length)) != 0)
 422                 goto fail1;
 423 
 424         /* Store new dynamic VPD in all segments in DYNAMIC_CONFIG partition */
 425         if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
 426                     NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
 427                     tag, data, vpd_length, B_TRUE)) != 0) {
 428                 goto fail2;
 429         }
 430 
 431         return (0);
 432 
 433 fail2:
 434         EFSYS_PROBE(fail2);
 435 
 436 fail1:
 437         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 438 
 439         return (rc);
 440 }
 441 
 442                                 void
 443 ef10_vpd_fini(
 444         __in                    efx_nic_t *enp)
 445 {
 446         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
 447                     enp->en_family == EFX_FAMILY_MEDFORD);
 448 
 449         if (enp->en_arch.ef10.ena_svpd_length > 0) {
 450                 EFSYS_KMEM_FREE(enp->en_esip, enp->en_arch.ef10.ena_svpd_length,
 451                                 enp->en_arch.ef10.ena_svpd);
 452 
 453                 enp->en_arch.ef10.ena_svpd = NULL;
 454                 enp->en_arch.ef10.ena_svpd_length = 0;
 455         }
 456 }
 457 
 458 #endif  /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
 459 
 460 #endif  /* EFSYS_OPT_VPD */