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_SIENA
  35 
  36 #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
  37 
  38         __checkReturn           efx_rc_t
  39 siena_nvram_partn_size(
  40         __in                    efx_nic_t *enp,
  41         __in                    uint32_t partn,
  42         __out                   size_t *sizep)
  43 {
  44         efx_rc_t rc;
  45 
  46         if ((1 << partn) & ~enp->en_u.siena.enu_partn_mask) {
  47                 rc = ENOTSUP;
  48                 goto fail1;
  49         }
  50 
  51         if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
  52             NULL, NULL, NULL)) != 0) {
  53                 goto fail2;
  54         }
  55 
  56         return (0);
  57 
  58 fail2:
  59         EFSYS_PROBE(fail2);
  60 fail1:
  61         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  62 
  63         return (rc);
  64 }
  65 
  66         __checkReturn           efx_rc_t
  67 siena_nvram_partn_lock(
  68         __in                    efx_nic_t *enp,
  69         __in                    uint32_t partn)
  70 {
  71         efx_rc_t rc;
  72 
  73         if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0) {
  74                 goto fail1;
  75         }
  76 
  77         return (0);
  78 
  79 fail1:
  80         EFSYS_PROBE1(fail1, efx_rc_t, rc);
  81 
  82         return (rc);
  83 }
  84 
  85         __checkReturn           efx_rc_t
  86 siena_nvram_partn_read(
  87         __in                    efx_nic_t *enp,
  88         __in                    uint32_t partn,
  89         __in                    unsigned int offset,
  90         __out_bcount(size)      caddr_t data,
  91         __in                    size_t size)
  92 {
  93         size_t chunk;
  94         efx_rc_t rc;
  95 
  96         while (size > 0) {
  97                 chunk = MIN(size, SIENA_NVRAM_CHUNK);
  98 
  99                 if ((rc = efx_mcdi_nvram_read(enp, partn, offset, data, chunk,
 100                             MC_CMD_NVRAM_READ_IN_V2_DEFAULT)) != 0) {
 101                         goto fail1;
 102                 }
 103 
 104                 size -= chunk;
 105                 data += chunk;
 106                 offset += chunk;
 107         }
 108 
 109         return (0);
 110 
 111 fail1:
 112         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 113 
 114         return (rc);
 115 }
 116 
 117         __checkReturn           efx_rc_t
 118 siena_nvram_partn_erase(
 119         __in                    efx_nic_t *enp,
 120         __in                    uint32_t partn,
 121         __in                    unsigned int offset,
 122         __in                    size_t size)
 123 {
 124         efx_rc_t rc;
 125 
 126         if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0) {
 127                 goto fail1;
 128         }
 129 
 130         return (0);
 131 
 132 fail1:
 133         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 134 
 135         return (rc);
 136 }
 137 
 138         __checkReturn           efx_rc_t
 139 siena_nvram_partn_write(
 140         __in                    efx_nic_t *enp,
 141         __in                    uint32_t partn,
 142         __in                    unsigned int offset,
 143         __out_bcount(size)      caddr_t data,
 144         __in                    size_t size)
 145 {
 146         size_t chunk;
 147         efx_rc_t rc;
 148 
 149         while (size > 0) {
 150                 chunk = MIN(size, SIENA_NVRAM_CHUNK);
 151 
 152                 if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
 153                             data, chunk)) != 0) {
 154                         goto fail1;
 155                 }
 156 
 157                 size -= chunk;
 158                 data += chunk;
 159                 offset += chunk;
 160         }
 161 
 162         return (0);
 163 
 164 fail1:
 165         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 166 
 167         return (rc);
 168 }
 169 
 170                                 void
 171 siena_nvram_partn_unlock(
 172         __in                    efx_nic_t *enp,
 173         __in                    uint32_t partn)
 174 {
 175         boolean_t reboot;
 176         efx_rc_t rc;
 177 
 178         /*
 179          * Reboot into the new image only for PHYs. The driver has to
 180          * explicitly cope with an MC reboot after a firmware update.
 181          */
 182         reboot = (partn == MC_CMD_NVRAM_TYPE_PHY_PORT0 ||
 183                     partn == MC_CMD_NVRAM_TYPE_PHY_PORT1 ||
 184                     partn == MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO);
 185 
 186         if ((rc = efx_mcdi_nvram_update_finish(enp, partn, reboot)) != 0) {
 187                 goto fail1;
 188         }
 189 
 190         return;
 191 
 192 fail1:
 193         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 194 }
 195 
 196 #endif  /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
 197 
 198 #if EFSYS_OPT_NVRAM
 199 
 200 typedef struct siena_parttbl_entry_s {
 201         unsigned int            partn;
 202         unsigned int            port;
 203         efx_nvram_type_t        nvtype;
 204 } siena_parttbl_entry_t;
 205 
 206 static siena_parttbl_entry_t siena_parttbl[] = {
 207         {MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO,   1, EFX_NVRAM_NULLPHY},
 208         {MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO,   2, EFX_NVRAM_NULLPHY},
 209         {MC_CMD_NVRAM_TYPE_MC_FW,               1, EFX_NVRAM_MC_FIRMWARE},
 210         {MC_CMD_NVRAM_TYPE_MC_FW,               2, EFX_NVRAM_MC_FIRMWARE},
 211         {MC_CMD_NVRAM_TYPE_MC_FW_BACKUP,        1, EFX_NVRAM_MC_GOLDEN},
 212         {MC_CMD_NVRAM_TYPE_MC_FW_BACKUP,        2, EFX_NVRAM_MC_GOLDEN},
 213         {MC_CMD_NVRAM_TYPE_EXP_ROM,             1, EFX_NVRAM_BOOTROM},
 214         {MC_CMD_NVRAM_TYPE_EXP_ROM,             2, EFX_NVRAM_BOOTROM},
 215         {MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0,   1, EFX_NVRAM_BOOTROM_CFG},
 216         {MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1,   2, EFX_NVRAM_BOOTROM_CFG},
 217         {MC_CMD_NVRAM_TYPE_PHY_PORT0,           1, EFX_NVRAM_PHY},
 218         {MC_CMD_NVRAM_TYPE_PHY_PORT1,           2, EFX_NVRAM_PHY},
 219         {MC_CMD_NVRAM_TYPE_FPGA,                1, EFX_NVRAM_FPGA},
 220         {MC_CMD_NVRAM_TYPE_FPGA,                2, EFX_NVRAM_FPGA},
 221         {MC_CMD_NVRAM_TYPE_FPGA_BACKUP,         1, EFX_NVRAM_FPGA_BACKUP},
 222         {MC_CMD_NVRAM_TYPE_FPGA_BACKUP,         2, EFX_NVRAM_FPGA_BACKUP},
 223         {MC_CMD_NVRAM_TYPE_FC_FW,               1, EFX_NVRAM_FCFW},
 224         {MC_CMD_NVRAM_TYPE_FC_FW,               2, EFX_NVRAM_FCFW},
 225         {MC_CMD_NVRAM_TYPE_CPLD,                1, EFX_NVRAM_CPLD},
 226         {MC_CMD_NVRAM_TYPE_CPLD,                2, EFX_NVRAM_CPLD},
 227         {MC_CMD_NVRAM_TYPE_LICENSE,             1, EFX_NVRAM_LICENSE},
 228         {MC_CMD_NVRAM_TYPE_LICENSE,             2, EFX_NVRAM_LICENSE}
 229 };
 230 
 231         __checkReturn           efx_rc_t
 232 siena_nvram_type_to_partn(
 233         __in                    efx_nic_t *enp,
 234         __in                    efx_nvram_type_t type,
 235         __out                   uint32_t *partnp)
 236 {
 237         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
 238         unsigned int i;
 239 
 240         EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
 241         EFSYS_ASSERT(partnp != NULL);
 242 
 243         for (i = 0; i < EFX_ARRAY_SIZE(siena_parttbl); i++) {
 244                 siena_parttbl_entry_t *entry = &siena_parttbl[i];
 245 
 246                 if (entry->port == emip->emi_port && entry->nvtype == type) {
 247                         *partnp = entry->partn;
 248                         return (0);
 249                 }
 250         }
 251 
 252         return (ENOTSUP);
 253 }
 254 
 255 
 256 #if EFSYS_OPT_DIAG
 257 
 258         __checkReturn           efx_rc_t
 259 siena_nvram_test(
 260         __in                    efx_nic_t *enp)
 261 {
 262         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
 263         siena_parttbl_entry_t *entry;
 264         unsigned int i;
 265         efx_rc_t rc;
 266 
 267         /*
 268          * Iterate over the list of supported partition types
 269          * applicable to *this* port
 270          */
 271         for (i = 0; i < EFX_ARRAY_SIZE(siena_parttbl); i++) {
 272                 entry = &siena_parttbl[i];
 273 
 274                 if (entry->port != emip->emi_port ||
 275                     !(enp->en_u.siena.enu_partn_mask & (1 << entry->partn)))
 276                         continue;
 277 
 278                 if ((rc = efx_mcdi_nvram_test(enp, entry->partn)) != 0) {
 279                         goto fail1;
 280                 }
 281         }
 282 
 283         return (0);
 284 
 285 fail1:
 286         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 287 
 288         return (rc);
 289 }
 290 
 291 #endif  /* EFSYS_OPT_DIAG */
 292 
 293 
 294 #define SIENA_DYNAMIC_CFG_SIZE(_nitems)                                 \
 295         (sizeof (siena_mc_dynamic_config_hdr_t) + ((_nitems) *          \
 296         sizeof (((siena_mc_dynamic_config_hdr_t *)NULL)->fw_version[0])))
 297 
 298         __checkReturn           efx_rc_t
 299 siena_nvram_get_dynamic_cfg(
 300         __in                    efx_nic_t *enp,
 301         __in                    uint32_t partn,
 302         __in                    boolean_t vpd,
 303         __out                   siena_mc_dynamic_config_hdr_t **dcfgp,
 304         __out                   size_t *sizep)
 305 {
 306         siena_mc_dynamic_config_hdr_t *dcfg = NULL;
 307         size_t size;
 308         uint8_t cksum;
 309         unsigned int vpd_offset;
 310         unsigned int vpd_length;
 311         unsigned int hdr_length;
 312         unsigned int nversions;
 313         unsigned int pos;
 314         unsigned int region;
 315         efx_rc_t rc;
 316 
 317         EFSYS_ASSERT(partn == MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 ||
 318                     partn == MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1);
 319 
 320         /*
 321          * Allocate sufficient memory for the entire dynamiccfg area, even
 322          * if we're not actually going to read in the VPD.
 323          */
 324         if ((rc = siena_nvram_partn_size(enp, partn, &size)) != 0)
 325                 goto fail1;
 326 
 327         EFSYS_KMEM_ALLOC(enp->en_esip, size, dcfg);
 328         if (dcfg == NULL) {
 329                 rc = ENOMEM;
 330                 goto fail2;
 331         }
 332 
 333         if ((rc = siena_nvram_partn_read(enp, partn, 0,
 334             (caddr_t)dcfg, SIENA_NVRAM_CHUNK)) != 0)
 335                 goto fail3;
 336 
 337         /* Verify the magic */
 338         if (EFX_DWORD_FIELD(dcfg->magic, EFX_DWORD_0)
 339             != SIENA_MC_DYNAMIC_CONFIG_MAGIC)
 340                 goto invalid1;
 341 
 342         /* All future versions of the structure must be backwards compatable */
 343         EFX_STATIC_ASSERT(SIENA_MC_DYNAMIC_CONFIG_VERSION == 0);
 344 
 345         hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0);
 346         nversions = EFX_DWORD_FIELD(dcfg->num_fw_version_items, EFX_DWORD_0);
 347         vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0);
 348         vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0);
 349 
 350         /* Verify the hdr doesn't overflow the partn size */
 351         if (hdr_length > size || vpd_offset > size || vpd_length > size ||
 352             vpd_length + vpd_offset > size)
 353                 goto invalid2;
 354 
 355         /* Verify the header has room for all it's versions */
 356         if (hdr_length < SIENA_DYNAMIC_CFG_SIZE(0) ||
 357             hdr_length < SIENA_DYNAMIC_CFG_SIZE(nversions))
 358                 goto invalid3;
 359 
 360         /*
 361          * Read the remaining portion of the dcfg, either including
 362          * the whole of VPD (there is no vpd length in this structure,
 363          * so we have to parse each tag), or just the dcfg header itself
 364          */
 365         region = vpd ? vpd_offset + vpd_length : hdr_length;
 366         if (region > SIENA_NVRAM_CHUNK) {
 367                 if ((rc = siena_nvram_partn_read(enp, partn, SIENA_NVRAM_CHUNK,
 368                     (caddr_t)dcfg + SIENA_NVRAM_CHUNK,
 369                     region - SIENA_NVRAM_CHUNK)) != 0)
 370                         goto fail4;
 371         }
 372 
 373         /* Verify checksum */
 374         cksum = 0;
 375         for (pos = 0; pos < hdr_length; pos++)
 376                 cksum += ((uint8_t *)dcfg)[pos];
 377         if (cksum != 0)
 378                 goto invalid4;
 379 
 380         goto done;
 381 
 382 invalid4:
 383         EFSYS_PROBE(invalid4);
 384 invalid3:
 385         EFSYS_PROBE(invalid3);
 386 invalid2:
 387         EFSYS_PROBE(invalid2);
 388 invalid1:
 389         EFSYS_PROBE(invalid1);
 390 
 391         /*
 392          * Construct a new "null" dcfg, with an empty version vector,
 393          * and an empty VPD chunk trailing. This has the neat side effect
 394          * of testing the exception paths in the write path.
 395          */
 396         EFX_POPULATE_DWORD_1(dcfg->magic,
 397                             EFX_DWORD_0, SIENA_MC_DYNAMIC_CONFIG_MAGIC);
 398         EFX_POPULATE_WORD_1(dcfg->length, EFX_WORD_0, sizeof (*dcfg));
 399         EFX_POPULATE_BYTE_1(dcfg->version, EFX_BYTE_0,
 400                             SIENA_MC_DYNAMIC_CONFIG_VERSION);
 401         EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset,
 402                             EFX_DWORD_0, sizeof (*dcfg));
 403         EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_length, EFX_DWORD_0, 0);
 404         EFX_POPULATE_DWORD_1(dcfg->num_fw_version_items, EFX_DWORD_0, 0);
 405 
 406 done:
 407         *dcfgp = dcfg;
 408         *sizep = size;
 409 
 410         return (0);
 411 
 412 fail4:
 413         EFSYS_PROBE(fail4);
 414 fail3:
 415         EFSYS_PROBE(fail3);
 416 
 417         EFSYS_KMEM_FREE(enp->en_esip, size, dcfg);
 418 
 419 fail2:
 420         EFSYS_PROBE(fail2);
 421 fail1:
 422         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 423 
 424         return (rc);
 425 }
 426 
 427         __checkReturn           efx_rc_t
 428 siena_nvram_get_subtype(
 429         __in                    efx_nic_t *enp,
 430         __in                    uint32_t partn,
 431         __out                   uint32_t *subtypep)
 432 {
 433         efx_mcdi_req_t req;
 434         uint8_t payload[MAX(MC_CMD_GET_BOARD_CFG_IN_LEN,
 435                             MC_CMD_GET_BOARD_CFG_OUT_LENMAX)];
 436         efx_word_t *fw_list;
 437         efx_rc_t rc;
 438 
 439         (void) memset(payload, 0, sizeof (payload));
 440         req.emr_cmd = MC_CMD_GET_BOARD_CFG;
 441         req.emr_in_buf = payload;
 442         req.emr_in_length = MC_CMD_GET_BOARD_CFG_IN_LEN;
 443         req.emr_out_buf = payload;
 444         req.emr_out_length = MC_CMD_GET_BOARD_CFG_OUT_LENMAX;
 445 
 446         efx_mcdi_execute(enp, &req);
 447 
 448         if (req.emr_rc != 0) {
 449                 rc = req.emr_rc;
 450                 goto fail1;
 451         }
 452 
 453         if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) {
 454                 rc = EMSGSIZE;
 455                 goto fail2;
 456         }
 457 
 458         if (req.emr_out_length_used <
 459             MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST +
 460             (partn + 1) * sizeof (efx_word_t)) {
 461                 rc = ENOENT;
 462                 goto fail3;
 463         }
 464 
 465         fw_list = MCDI_OUT2(req, efx_word_t,
 466                             GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST);
 467         *subtypep = EFX_WORD_FIELD(fw_list[partn], EFX_WORD_0);
 468 
 469         return (0);
 470 
 471 fail3:
 472         EFSYS_PROBE(fail3);
 473 fail2:
 474         EFSYS_PROBE(fail2);
 475 fail1:
 476         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 477 
 478         return (rc);
 479 }
 480 
 481         __checkReturn           efx_rc_t
 482 siena_nvram_partn_get_version(
 483         __in                    efx_nic_t *enp,
 484         __in                    uint32_t partn,
 485         __out                   uint32_t *subtypep,
 486         __out_ecount(4)         uint16_t version[4])
 487 {
 488         siena_mc_dynamic_config_hdr_t *dcfg;
 489         siena_parttbl_entry_t *entry;
 490         uint32_t dcfg_partn;
 491         unsigned int i;
 492         efx_rc_t rc;
 493 
 494         if ((1 << partn) & ~enp->en_u.siena.enu_partn_mask) {
 495                 rc = ENOTSUP;
 496                 goto fail1;
 497         }
 498 
 499         if ((rc = siena_nvram_get_subtype(enp, partn, subtypep)) != 0)
 500                 goto fail2;
 501 
 502         /*
 503          * Some partitions are accessible from both ports (for instance BOOTROM)
 504          * Find the highest version reported by all dcfg structures on ports
 505          * that have access to this partition.
 506          */
 507         version[0] = version[1] = version[2] = version[3] = 0;
 508         for (i = 0; i < EFX_ARRAY_SIZE(siena_parttbl); i++) {
 509                 siena_mc_fw_version_t *verp;
 510                 unsigned int nitems;
 511                 uint16_t temp[4];
 512                 size_t length;
 513 
 514                 entry = &siena_parttbl[i];
 515                 if (entry->partn != partn)
 516                         continue;
 517 
 518                 dcfg_partn = (entry->port == 1)
 519                         ? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
 520                         : MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
 521                 /*
 522                  * Ingore missing partitions on port 2, assuming they're due
 523                  * to to running on a single port part.
 524                  */
 525                 if ((1 << dcfg_partn) &  ~enp->en_u.siena.enu_partn_mask) {
 526                         if (entry->port == 2)
 527                                 continue;
 528                 }
 529 
 530                 if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
 531                     B_FALSE, &dcfg, &length)) != 0)
 532                         goto fail3;
 533 
 534                 nitems = EFX_DWORD_FIELD(dcfg->num_fw_version_items,
 535                             EFX_DWORD_0);
 536                 if (nitems < entry->partn)
 537                         goto done;
 538 
 539                 verp = &dcfg->fw_version[partn];
 540                 temp[0] = EFX_WORD_FIELD(verp->version_w, EFX_WORD_0);
 541                 temp[1] = EFX_WORD_FIELD(verp->version_x, EFX_WORD_0);
 542                 temp[2] = EFX_WORD_FIELD(verp->version_y, EFX_WORD_0);
 543                 temp[3] = EFX_WORD_FIELD(verp->version_z, EFX_WORD_0);
 544                 if (memcmp(version, temp, sizeof (temp)) < 0)
 545                         (void) memcpy(version, temp, sizeof (temp));
 546 
 547 done:
 548                 EFSYS_KMEM_FREE(enp->en_esip, length, dcfg);
 549         }
 550 
 551         return (0);
 552 
 553 fail3:
 554         EFSYS_PROBE(fail3);
 555 fail2:
 556         EFSYS_PROBE(fail2);
 557 fail1:
 558         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 559 
 560         return (rc);
 561 }
 562 
 563         __checkReturn           efx_rc_t
 564 siena_nvram_partn_rw_start(
 565         __in                    efx_nic_t *enp,
 566         __in                    uint32_t partn,
 567         __out                   size_t *chunk_sizep)
 568 {
 569         efx_rc_t rc;
 570 
 571         if ((rc = siena_nvram_partn_lock(enp, partn)) != 0)
 572                 goto fail1;
 573 
 574         if (chunk_sizep != NULL)
 575                 *chunk_sizep = SIENA_NVRAM_CHUNK;
 576 
 577         return (0);
 578 
 579 fail1:
 580         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 581 
 582         return (rc);
 583 }
 584 
 585                                 void
 586 siena_nvram_partn_rw_finish(
 587         __in                    efx_nic_t *enp,
 588         __in                    uint32_t partn)
 589 {
 590         siena_nvram_partn_unlock(enp, partn);
 591 }
 592 
 593         __checkReturn           efx_rc_t
 594 siena_nvram_partn_set_version(
 595         __in                    efx_nic_t *enp,
 596         __in                    uint32_t partn,
 597         __in_ecount(4)          uint16_t version[4])
 598 {
 599         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
 600         siena_mc_dynamic_config_hdr_t *dcfg = NULL;
 601         siena_mc_fw_version_t *fwverp;
 602         uint32_t dcfg_partn;
 603         size_t dcfg_size;
 604         unsigned int hdr_length;
 605         unsigned int vpd_length;
 606         unsigned int vpd_offset;
 607         unsigned int nitems;
 608         unsigned int required_hdr_length;
 609         unsigned int pos;
 610         uint8_t cksum;
 611         uint32_t subtype;
 612         size_t length;
 613         efx_rc_t rc;
 614 
 615         dcfg_partn = (emip->emi_port == 1)
 616                 ? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
 617                 : MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
 618 
 619         if ((rc = siena_nvram_partn_size(enp, dcfg_partn, &dcfg_size)) != 0)
 620                 goto fail1;
 621 
 622         if ((rc = siena_nvram_partn_lock(enp, dcfg_partn)) != 0)
 623                 goto fail2;
 624 
 625         if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
 626             B_TRUE, &dcfg, &length)) != 0)
 627                 goto fail3;
 628 
 629         hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0);
 630         nitems = EFX_DWORD_FIELD(dcfg->num_fw_version_items, EFX_DWORD_0);
 631         vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0);
 632         vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0);
 633 
 634         /*
 635          * NOTE: This function will blatt any fields trailing the version
 636          * vector, or the VPD chunk.
 637          */
 638         required_hdr_length = SIENA_DYNAMIC_CFG_SIZE(partn + 1);
 639         if (required_hdr_length + vpd_length > length) {
 640                 rc = ENOSPC;
 641                 goto fail4;
 642         }
 643 
 644         if (vpd_offset < required_hdr_length) {
 645                 (void) memmove((caddr_t)dcfg + required_hdr_length,
 646                         (caddr_t)dcfg + vpd_offset, vpd_length);
 647                 vpd_offset = required_hdr_length;
 648                 EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset,
 649                                     EFX_DWORD_0, vpd_offset);
 650         }
 651 
 652         if (hdr_length < required_hdr_length) {
 653                 (void) memset((caddr_t)dcfg + hdr_length, 0,
 654                         required_hdr_length - hdr_length);
 655                 hdr_length = required_hdr_length;
 656                 EFX_POPULATE_WORD_1(dcfg->length,
 657                                     EFX_WORD_0, hdr_length);
 658         }
 659 
 660         /* Get the subtype to insert into the fw_subtype array */
 661         if ((rc = siena_nvram_get_subtype(enp, partn, &subtype)) != 0)
 662                 goto fail5;
 663 
 664         /* Fill out the new version */
 665         fwverp = &dcfg->fw_version[partn];
 666         EFX_POPULATE_DWORD_1(fwverp->fw_subtype, EFX_DWORD_0, subtype);
 667         EFX_POPULATE_WORD_1(fwverp->version_w, EFX_WORD_0, version[0]);
 668         EFX_POPULATE_WORD_1(fwverp->version_x, EFX_WORD_0, version[1]);
 669         EFX_POPULATE_WORD_1(fwverp->version_y, EFX_WORD_0, version[2]);
 670         EFX_POPULATE_WORD_1(fwverp->version_z, EFX_WORD_0, version[3]);
 671 
 672         /* Update the version count */
 673         if (nitems < partn + 1) {
 674                 nitems = partn + 1;
 675                 EFX_POPULATE_DWORD_1(dcfg->num_fw_version_items,
 676                                     EFX_DWORD_0, nitems);
 677         }
 678 
 679         /* Update the checksum */
 680         cksum = 0;
 681         for (pos = 0; pos < hdr_length; pos++)
 682                 cksum += ((uint8_t *)dcfg)[pos];
 683         dcfg->csum.eb_u8[0] -= cksum;
 684 
 685         /* Erase and write the new partition */
 686         if ((rc = siena_nvram_partn_erase(enp, dcfg_partn, 0, dcfg_size)) != 0)
 687                 goto fail6;
 688 
 689         /* Write out the new structure to nvram */
 690         if ((rc = siena_nvram_partn_write(enp, dcfg_partn, 0,
 691             (caddr_t)dcfg, vpd_offset + vpd_length)) != 0)
 692                 goto fail7;
 693 
 694         EFSYS_KMEM_FREE(enp->en_esip, length, dcfg);
 695 
 696         siena_nvram_partn_unlock(enp, dcfg_partn);
 697 
 698         return (0);
 699 
 700 fail7:
 701         EFSYS_PROBE(fail7);
 702 fail6:
 703         EFSYS_PROBE(fail6);
 704 fail5:
 705         EFSYS_PROBE(fail5);
 706 fail4:
 707         EFSYS_PROBE(fail4);
 708 
 709         EFSYS_KMEM_FREE(enp->en_esip, length, dcfg);
 710 fail3:
 711         EFSYS_PROBE(fail3);
 712 fail2:
 713         EFSYS_PROBE(fail2);
 714 fail1:
 715         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 716 
 717         return (rc);
 718 }
 719 
 720 #endif  /* EFSYS_OPT_NVRAM */
 721 
 722 #endif  /* EFSYS_OPT_SIENA */