1 /*
   2  * Copyright 2009 Solarflare Communications Inc.  All rights reserved.
   3  *
   4  * Redistribution and use in source and binary forms, with or without
   5  * modification, are permitted provided that the following conditions
   6  * are met:
   7  * 1. Redistributions of source code must retain the above copyright
   8  *    notice, this list of conditions and the following disclaimer.
   9  * 2. Redistributions in binary form must reproduce the above copyright
  10  *    notice, this list of conditions and the following disclaimer in the
  11  *    documentation and/or other materials provided with the distribution.
  12  *
  13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND
  14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  23  * SUCH DAMAGE.
  24  */
  25 
  26 #include "efsys.h"
  27 #include "efx.h"
  28 #include "efx_types.h"
  29 #include "efx_regs.h"
  30 #include "efx_impl.h"
  31 
  32 #if EFSYS_OPT_SIENA
  33 
  34 #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
  35 
  36         __checkReturn           int
  37 siena_nvram_partn_size(
  38         __in                    efx_nic_t *enp,
  39         __in                    unsigned int partn,
  40         __out                   size_t *sizep)
  41 {
  42         efx_mcdi_req_t req;
  43         uint8_t payload[MAX(MC_CMD_NVRAM_INFO_IN_LEN,
  44                             MC_CMD_NVRAM_INFO_OUT_LEN)];
  45         int rc;
  46 
  47         if ((1 << partn) & ~enp->en_u.siena.enu_partn_mask) {
  48                 rc = ENOTSUP;
  49                 goto fail1;
  50         }
  51 
  52         req.emr_cmd = MC_CMD_NVRAM_INFO;
  53         req.emr_in_buf = payload;
  54         req.emr_in_length = MC_CMD_NVRAM_INFO_IN_LEN;
  55         req.emr_out_buf = payload;
  56         req.emr_out_length = MC_CMD_NVRAM_INFO_OUT_LEN;
  57 
  58         MCDI_IN_SET_DWORD(req, NVRAM_INFO_IN_TYPE, partn);
  59 
  60         efx_mcdi_execute(enp, &req);
  61 
  62         if (req.emr_rc != 0) {
  63                 rc = req.emr_rc;
  64                 goto fail2;
  65         }
  66 
  67         if (req.emr_out_length_used < MC_CMD_NVRAM_INFO_OUT_LEN) {
  68                 rc = EMSGSIZE;
  69                 goto fail3;
  70         }
  71 
  72         *sizep = MCDI_OUT_DWORD(req, NVRAM_INFO_OUT_SIZE);
  73 
  74         return (0);
  75 
  76 fail3:
  77         EFSYS_PROBE(fail3);
  78 fail2:
  79         EFSYS_PROBE(fail2);
  80 fail1:
  81         EFSYS_PROBE1(fail1, int, rc);
  82 
  83         return (rc);
  84 }
  85 
  86         __checkReturn           int
  87 siena_nvram_partn_lock(
  88         __in                    efx_nic_t *enp,
  89         __in                    unsigned int partn)
  90 {
  91         efx_mcdi_req_t req;
  92         uint8_t payload[MC_CMD_NVRAM_UPDATE_START_IN_LEN];
  93         int rc;
  94 
  95         req.emr_cmd = MC_CMD_NVRAM_UPDATE_START;
  96         req.emr_in_buf = payload;
  97         req.emr_in_length = MC_CMD_NVRAM_UPDATE_START_IN_LEN;
  98         EFX_STATIC_ASSERT(MC_CMD_NVRAM_UPDATE_START_OUT_LEN == 0);
  99         req.emr_out_buf = NULL;
 100         req.emr_out_length = 0;
 101 
 102         MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_START_IN_TYPE, partn);
 103 
 104         efx_mcdi_execute(enp, &req);
 105 
 106         if (req.emr_rc != 0) {
 107                 rc = req.emr_rc;
 108                 goto fail1;
 109         }
 110 
 111         return (0);
 112 
 113 fail1:
 114         EFSYS_PROBE1(fail1, int, rc);
 115 
 116         return (rc);
 117 }
 118 
 119         __checkReturn           int
 120 siena_nvram_partn_read(
 121         __in                    efx_nic_t *enp,
 122         __in                    unsigned int partn,
 123         __in                    unsigned int offset,
 124         __out_bcount(size)      caddr_t data,
 125         __in                    size_t size)
 126 {
 127         efx_mcdi_req_t req;
 128         uint8_t payload[MAX(MC_CMD_NVRAM_READ_IN_LEN,
 129                             MC_CMD_NVRAM_READ_OUT_LEN(SIENA_NVRAM_CHUNK))];
 130         size_t chunk;
 131         int rc;
 132 
 133         while (size > 0) {
 134                 chunk = MIN(size, SIENA_NVRAM_CHUNK);
 135 
 136                 req.emr_cmd = MC_CMD_NVRAM_READ;
 137                 req.emr_in_buf = payload;
 138                 req.emr_in_length = MC_CMD_NVRAM_READ_IN_LEN;
 139                 req.emr_out_buf = payload;
 140                 req.emr_out_length =
 141                         MC_CMD_NVRAM_READ_OUT_LEN(SIENA_NVRAM_CHUNK);
 142 
 143                 MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_TYPE, partn);
 144                 MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_OFFSET, offset);
 145                 MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_LENGTH, chunk);
 146 
 147                 efx_mcdi_execute(enp, &req);
 148 
 149                 if (req.emr_rc != 0) {
 150                         rc = req.emr_rc;
 151                         goto fail1;
 152                 }
 153 
 154                 if (req.emr_out_length_used <
 155                     MC_CMD_NVRAM_READ_OUT_LEN(chunk)) {
 156                         rc = EMSGSIZE;
 157                         goto fail2;
 158                 }
 159 
 160                 memcpy(data,
 161                     MCDI_OUT2(req, uint8_t, NVRAM_READ_OUT_READ_BUFFER),
 162                     chunk);
 163 
 164                 size -= chunk;
 165                 data += chunk;
 166                 offset += chunk;
 167         }
 168 
 169         return (0);
 170 
 171 fail2:
 172         EFSYS_PROBE(fail2);
 173 fail1:
 174         EFSYS_PROBE1(fail1, int, rc);
 175 
 176         return (rc);
 177 }
 178 
 179         __checkReturn           int
 180 siena_nvram_partn_erase(
 181         __in                    efx_nic_t *enp,
 182         __in                    unsigned int partn,
 183         __in                    unsigned int offset,
 184         __in                    size_t size)
 185 {
 186         efx_mcdi_req_t req;
 187         uint8_t payload[MC_CMD_NVRAM_ERASE_IN_LEN];
 188         int rc;
 189 
 190         req.emr_cmd = MC_CMD_NVRAM_ERASE;
 191         req.emr_in_buf = payload;
 192         req.emr_in_length = MC_CMD_NVRAM_ERASE_IN_LEN;
 193         EFX_STATIC_ASSERT(MC_CMD_NVRAM_ERASE_OUT_LEN == 0);
 194         req.emr_out_buf = NULL;
 195         req.emr_out_length = 0;
 196 
 197         MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_TYPE, partn);
 198         MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_OFFSET, offset);
 199         MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_LENGTH, size);
 200 
 201         efx_mcdi_execute(enp, &req);
 202 
 203         if (req.emr_rc != 0) {
 204                 rc = req.emr_rc;
 205                 goto fail1;
 206         }
 207 
 208         return (0);
 209 
 210 fail1:
 211         EFSYS_PROBE1(fail1, int, rc);
 212 
 213         return (rc);
 214 }
 215 
 216         __checkReturn           int
 217 siena_nvram_partn_write(
 218         __in                    efx_nic_t *enp,
 219         __in                    unsigned int partn,
 220         __in                    unsigned int offset,
 221         __out_bcount(size)      caddr_t data,
 222         __in                    size_t size)
 223 {
 224         efx_mcdi_req_t req;
 225         uint8_t payload[MC_CMD_NVRAM_WRITE_IN_LEN(SIENA_NVRAM_CHUNK)];
 226         size_t chunk;
 227         int rc;
 228 
 229         while (size > 0) {
 230                 chunk = MIN(size, SIENA_NVRAM_CHUNK);
 231 
 232                 req.emr_cmd = MC_CMD_NVRAM_WRITE;
 233                 req.emr_in_buf = payload;
 234                 req.emr_in_length = MC_CMD_NVRAM_WRITE_IN_LEN(chunk);
 235                 EFX_STATIC_ASSERT(MC_CMD_NVRAM_WRITE_OUT_LEN == 0);
 236                 req.emr_out_buf = NULL;
 237                 req.emr_out_length = 0;
 238 
 239                 MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_TYPE, partn);
 240                 MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_OFFSET, offset);
 241                 MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_LENGTH, chunk);
 242 
 243                 memcpy(MCDI_IN2(req, uint8_t, NVRAM_WRITE_IN_WRITE_BUFFER),
 244                     data, chunk);
 245 
 246                 efx_mcdi_execute(enp, &req);
 247 
 248                 if (req.emr_rc != 0) {
 249                         rc = req.emr_rc;
 250                         goto fail1;
 251                 }
 252 
 253                 size -= chunk;
 254                 data += chunk;
 255                 offset += chunk;
 256         }
 257 
 258         return (0);
 259 
 260 fail1:
 261         EFSYS_PROBE1(fail1, int, rc);
 262 
 263         return (rc);
 264 }
 265 
 266                                 void
 267 siena_nvram_partn_unlock(
 268         __in                    efx_nic_t *enp,
 269         __in                    unsigned int partn)
 270 {
 271         efx_mcdi_req_t req;
 272         uint8_t payload[MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN];
 273         uint32_t reboot;
 274         int rc;
 275 
 276         req.emr_cmd = MC_CMD_NVRAM_UPDATE_FINISH;
 277         req.emr_in_buf = payload;
 278         req.emr_in_length = MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN;
 279         EFX_STATIC_ASSERT(MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN == 0);
 280         req.emr_out_buf = NULL;
 281         req.emr_out_length = 0;
 282 
 283         /*
 284          * Reboot into the new image only for PHYs. The driver has to
 285          * explicitly cope with an MC reboot after a firmware update.
 286          */
 287         reboot = (partn == MC_CMD_NVRAM_TYPE_PHY_PORT0 ||
 288                     partn == MC_CMD_NVRAM_TYPE_PHY_PORT1 ||
 289                     partn == MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO);
 290 
 291         MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_FINISH_IN_TYPE, partn);
 292         MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_FINISH_IN_REBOOT, reboot);
 293 
 294         efx_mcdi_execute(enp, &req);
 295 
 296         if (req.emr_rc != 0) {
 297                 rc = req.emr_rc;
 298                 goto fail1;
 299         }
 300 
 301         return;
 302 
 303 fail1:
 304         EFSYS_PROBE1(fail1, int, rc);
 305 }
 306 
 307 #endif  /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
 308 
 309 #if EFSYS_OPT_NVRAM
 310 
 311 typedef struct siena_parttbl_entry_s {
 312         unsigned int            partn;
 313         unsigned int            port;
 314         efx_nvram_type_t        nvtype;
 315 } siena_parttbl_entry_t;
 316 
 317 static siena_parttbl_entry_t siena_parttbl[] = {
 318         {MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO,   1, EFX_NVRAM_NULLPHY},
 319         {MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO,   2, EFX_NVRAM_NULLPHY},
 320         {MC_CMD_NVRAM_TYPE_MC_FW,               1, EFX_NVRAM_MC_FIRMWARE},
 321         {MC_CMD_NVRAM_TYPE_MC_FW,               2, EFX_NVRAM_MC_FIRMWARE},
 322         {MC_CMD_NVRAM_TYPE_MC_FW_BACKUP,        1, EFX_NVRAM_MC_GOLDEN},
 323         {MC_CMD_NVRAM_TYPE_MC_FW_BACKUP,        2, EFX_NVRAM_MC_GOLDEN},
 324         {MC_CMD_NVRAM_TYPE_EXP_ROM,             1, EFX_NVRAM_BOOTROM},
 325         {MC_CMD_NVRAM_TYPE_EXP_ROM,             2, EFX_NVRAM_BOOTROM},
 326         {MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0,   1, EFX_NVRAM_BOOTROM_CFG},
 327         {MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1,   2, EFX_NVRAM_BOOTROM_CFG},
 328         {MC_CMD_NVRAM_TYPE_PHY_PORT0,           1, EFX_NVRAM_PHY},
 329         {MC_CMD_NVRAM_TYPE_PHY_PORT1,           2, EFX_NVRAM_PHY},
 330         {MC_CMD_NVRAM_TYPE_FPGA,                1, EFX_NVRAM_FPGA},
 331         {MC_CMD_NVRAM_TYPE_FPGA,                2, EFX_NVRAM_FPGA},
 332         {0, 0, 0},
 333 };
 334 
 335 static  __checkReturn           siena_parttbl_entry_t *
 336 siena_parttbl_entry(
 337         __in                    efx_nic_t *enp,
 338         __in                    efx_nvram_type_t type)
 339 {
 340         efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
 341         siena_parttbl_entry_t *entry;
 342 
 343         EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
 344 
 345         for (entry = siena_parttbl; entry->port > 0; ++entry) {
 346                 if (entry->port == emip->emi_port && entry->nvtype == type)
 347                         return (entry);
 348         }
 349 
 350         return (NULL);
 351 }
 352 
 353 #if EFSYS_OPT_DIAG
 354 
 355         __checkReturn           int
 356 siena_nvram_test(
 357         __in                    efx_nic_t *enp)
 358 {
 359         efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
 360         siena_parttbl_entry_t *entry;
 361         efx_mcdi_req_t req;
 362         uint8_t payload[MAX(MC_CMD_NVRAM_TEST_IN_LEN,
 363                             MC_CMD_NVRAM_TEST_OUT_LEN)];
 364         int result;
 365         int rc;
 366 
 367         req.emr_cmd = MC_CMD_NVRAM_TEST;
 368         req.emr_in_buf = payload;
 369         req.emr_in_length = MC_CMD_NVRAM_TEST_IN_LEN;
 370         req.emr_out_buf = payload;
 371         req.emr_out_length = MC_CMD_NVRAM_TEST_OUT_LEN;
 372 
 373         /*
 374          * Iterate over the list of supported partition types
 375          * applicable to *this* port
 376          */
 377         for (entry = siena_parttbl; entry->port > 0; ++entry) {
 378                 if (entry->port != emip->emi_port ||
 379                     !(enp->en_u.siena.enu_partn_mask & (1 << entry->partn)))
 380                         continue;
 381 
 382                 MCDI_IN_SET_DWORD(req, NVRAM_TEST_IN_TYPE, entry->partn);
 383 
 384                 efx_mcdi_execute(enp, &req);
 385 
 386                 if (req.emr_rc != 0) {
 387                         rc = req.emr_rc;
 388                         goto fail1;
 389                 }
 390 
 391                 if (req.emr_out_length_used < MC_CMD_NVRAM_TEST_OUT_LEN) {
 392                         rc = EMSGSIZE;
 393                         goto fail2;
 394                 }
 395 
 396                 result = MCDI_OUT_DWORD(req, NVRAM_TEST_OUT_RESULT);
 397                 if (result == MC_CMD_NVRAM_TEST_FAIL) {
 398 
 399                         EFSYS_PROBE1(nvram_test_failure, int, entry->partn);
 400 
 401                         rc = (EINVAL);
 402                         goto fail3;
 403                 }
 404         }
 405 
 406         return (0);
 407 
 408 fail3:
 409         EFSYS_PROBE(fail3);
 410 fail2:
 411         EFSYS_PROBE(fail2);
 412 fail1:
 413         EFSYS_PROBE1(fail1, int, rc);
 414 
 415         return (rc);
 416 }
 417 
 418 #endif  /* EFSYS_OPT_DIAG */
 419 
 420         __checkReturn           int
 421 siena_nvram_size(
 422         __in                    efx_nic_t *enp,
 423         __in                    efx_nvram_type_t type,
 424         __out                   size_t *sizep)
 425 {
 426         siena_parttbl_entry_t *entry;
 427         int rc;
 428 
 429         if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
 430                 rc = ENOTSUP;
 431                 goto fail1;
 432         }
 433 
 434         if ((rc = siena_nvram_partn_size(enp, entry->partn, sizep)) != 0)
 435                 goto fail2;
 436 
 437         return (0);
 438 
 439 fail2:
 440         EFSYS_PROBE(fail2);
 441 fail1:
 442         EFSYS_PROBE1(fail1, int, rc);
 443 
 444         *sizep = 0;
 445 
 446         return (rc);
 447 }
 448 
 449 #define SIENA_DYNAMIC_CFG_SIZE(_nitems)                                 \
 450         (sizeof (siena_mc_dynamic_config_hdr_t) + ((_nitems) *          \
 451         sizeof (((siena_mc_dynamic_config_hdr_t *)NULL)->fw_version[0])))
 452 
 453         __checkReturn           int
 454 siena_nvram_get_dynamic_cfg(
 455         __in                    efx_nic_t *enp,
 456         __in                    unsigned int partn,
 457         __in                    boolean_t vpd,
 458         __out                   siena_mc_dynamic_config_hdr_t **dcfgp,
 459         __out                   size_t *sizep)
 460 {
 461         siena_mc_dynamic_config_hdr_t *dcfg;
 462         size_t size;
 463         uint8_t cksum;
 464         unsigned int vpd_offset;
 465         unsigned int vpd_length;
 466         unsigned int hdr_length;
 467         unsigned int nversions;
 468         unsigned int pos;
 469         unsigned int region;
 470         int rc;
 471 
 472         EFSYS_ASSERT(partn == MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 ||
 473                     partn == MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1);
 474 
 475         /*
 476          * Allocate sufficient memory for the entire dynamiccfg area, even
 477          * if we're not actually going to read in the VPD.
 478          */
 479         if ((rc = siena_nvram_partn_size(enp, partn, &size)) != 0)
 480                 goto fail1;
 481 
 482         EFSYS_KMEM_ALLOC(enp->en_esip, size, dcfg);
 483         if (dcfg == NULL) {
 484                 rc = ENOMEM;
 485                 goto fail2;
 486         }
 487 
 488         if ((rc = siena_nvram_partn_read(enp, partn, 0,
 489             (caddr_t)dcfg, SIENA_NVRAM_CHUNK)) != 0)
 490                 goto fail3;
 491 
 492         /* Verify the magic */
 493         if (EFX_DWORD_FIELD(dcfg->magic, EFX_DWORD_0)
 494             != SIENA_MC_DYNAMIC_CONFIG_MAGIC)
 495                 goto invalid1;
 496 
 497         /* All future versions of the structure must be backwards compatable */
 498         EFX_STATIC_ASSERT(SIENA_MC_DYNAMIC_CONFIG_VERSION == 0);
 499 
 500         hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0);
 501         nversions = EFX_DWORD_FIELD(dcfg->num_fw_version_items, EFX_DWORD_0);
 502         vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0);
 503         vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0);
 504 
 505         /* Verify the hdr doesn't overflow the partn size */
 506         if (hdr_length > size || vpd_offset > size || vpd_length > size ||
 507             vpd_length + vpd_offset > size)
 508                 goto invalid2;
 509 
 510         /* Verify the header has room for all it's versions */
 511         if (hdr_length < SIENA_DYNAMIC_CFG_SIZE(0) ||
 512             hdr_length < SIENA_DYNAMIC_CFG_SIZE(nversions))
 513                 goto invalid3;
 514 
 515         /*
 516          * Read the remaining portion of the dcfg, either including
 517          * the whole of VPD (there is no vpd length in this structure,
 518          * so we have to parse each tag), or just the dcfg header itself
 519          */
 520         region = vpd ? vpd_offset + vpd_length : hdr_length;
 521         if (region > SIENA_NVRAM_CHUNK) {
 522                 if ((rc = siena_nvram_partn_read(enp, partn, SIENA_NVRAM_CHUNK,
 523                     (caddr_t)dcfg + SIENA_NVRAM_CHUNK,
 524                     region - SIENA_NVRAM_CHUNK)) != 0)
 525                         goto fail4;
 526         }
 527 
 528         /* Verify checksum */
 529         cksum = 0;
 530         for (pos = 0; pos < hdr_length; pos++)
 531                 cksum += ((uint8_t *)dcfg)[pos];
 532         if (cksum != 0)
 533                 goto invalid4;
 534 
 535         goto done;
 536 
 537 invalid4:
 538         EFSYS_PROBE(invalid4);
 539 invalid3:
 540         EFSYS_PROBE(invalid3);
 541 invalid2:
 542         EFSYS_PROBE(invalid2);
 543 invalid1:
 544         EFSYS_PROBE(invalid1);
 545 
 546         /*
 547          * Construct a new "null" dcfg, with an empty version vector,
 548          * and an empty VPD chunk trailing. This has the neat side effect
 549          * of testing the exception paths in the write path.
 550          */
 551         EFX_POPULATE_DWORD_1(dcfg->magic,
 552                             EFX_DWORD_0, SIENA_MC_DYNAMIC_CONFIG_MAGIC);
 553         EFX_POPULATE_WORD_1(dcfg->length, EFX_WORD_0, sizeof (*dcfg));
 554         EFX_POPULATE_BYTE_1(dcfg->version, EFX_BYTE_0,
 555                             SIENA_MC_DYNAMIC_CONFIG_VERSION);
 556         EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset,
 557                             EFX_DWORD_0, sizeof (*dcfg));
 558         EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_length, EFX_DWORD_0, 0);
 559         EFX_POPULATE_DWORD_1(dcfg->num_fw_version_items, EFX_DWORD_0, 0);
 560 
 561 done:
 562         *dcfgp = dcfg;
 563         *sizep = size;
 564 
 565         return (0);
 566 
 567 fail4:
 568         EFSYS_PROBE(fail4);
 569 fail3:
 570         EFSYS_PROBE(fail3);
 571 fail2:
 572         EFSYS_PROBE(fail2);
 573 
 574         EFSYS_KMEM_FREE(enp->en_esip, size, dcfg);
 575 
 576 fail1:
 577         EFSYS_PROBE1(fail1, int, rc);
 578 
 579         return (rc);
 580 }
 581 
 582 static  __checkReturn           int
 583 siena_nvram_get_subtype(
 584         __in                    efx_nic_t *enp,
 585         __in                    unsigned int partn,
 586         __out                   uint32_t *subtypep)
 587 {
 588         efx_mcdi_req_t req;
 589         uint8_t outbuf[MC_CMD_GET_BOARD_CFG_OUT_LENMAX];
 590         efx_word_t *fw_list;
 591         int rc;
 592 
 593         req.emr_cmd = MC_CMD_GET_BOARD_CFG;
 594         EFX_STATIC_ASSERT(MC_CMD_GET_BOARD_CFG_IN_LEN == 0);
 595         req.emr_in_buf = NULL;
 596         req.emr_in_length = 0;
 597         req.emr_out_buf = outbuf;
 598         req.emr_out_length = sizeof (outbuf);
 599 
 600         efx_mcdi_execute(enp, &req);
 601 
 602         if (req.emr_rc != 0) {
 603                 rc = req.emr_rc;
 604                 goto fail1;
 605         }
 606 
 607         if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) {
 608                 rc = EMSGSIZE;
 609                 goto fail2;
 610         }
 611 
 612         if (req.emr_out_length_used <
 613             MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST +
 614             (partn + 1) * sizeof (efx_word_t)) {
 615                 rc = ENOENT;
 616                 goto fail3;
 617         }
 618 
 619         fw_list = MCDI_OUT2(req, efx_word_t,
 620                             GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST);
 621         *subtypep = EFX_WORD_FIELD(fw_list[partn], EFX_WORD_0);
 622 
 623         return (0);
 624 
 625 fail3:
 626         EFSYS_PROBE(fail3);
 627 fail2:
 628         EFSYS_PROBE(fail2);
 629 fail1:
 630         EFSYS_PROBE1(fail1, int, rc);
 631 
 632         return (rc);
 633 }
 634 
 635         __checkReturn           int
 636 siena_nvram_get_version(
 637         __in                    efx_nic_t *enp,
 638         __in                    efx_nvram_type_t type,
 639         __out                   uint32_t *subtypep,
 640         __out_ecount(4)         uint16_t version[4])
 641 {
 642         siena_mc_dynamic_config_hdr_t *dcfg;
 643         siena_parttbl_entry_t *entry;
 644         unsigned int dcfg_partn;
 645         unsigned int partn;
 646         int rc;
 647 
 648         if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
 649                 rc = ENOTSUP;
 650                 goto fail1;
 651         }
 652         partn = entry->partn;
 653 
 654         if ((1 << partn) & ~enp->en_u.siena.enu_partn_mask) {
 655                 rc = ENOTSUP;
 656                 goto fail2;
 657         }
 658 
 659         if ((rc = siena_nvram_get_subtype(enp, partn, subtypep)) != 0)
 660                 goto fail3;
 661 
 662         /*
 663          * Some partitions are accessible from both ports (for instance BOOTROM)
 664          * Find the highest version reported by all dcfg structures on ports
 665          * that have access to this partition.
 666          */
 667         version[0] = version[1] = version[2] = version[3] = 0;
 668         for (entry = siena_parttbl; entry->port > 0; ++entry) {
 669                 unsigned int nitems;
 670                 uint16_t temp[4];
 671                 size_t length;
 672 
 673                 if (entry->partn != partn)
 674                         continue;
 675 
 676                 dcfg_partn = (entry->port == 1)
 677                         ? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
 678                         : MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
 679                 /*
 680                  * Ingore missing partitions on port 2, assuming they're due
 681                  * to to running on a single port part.
 682                  */
 683                 if ((1 << dcfg_partn) &  ~enp->en_u.siena.enu_partn_mask) {
 684                         if (entry->port == 2)
 685                                 continue;
 686                 }
 687 
 688                 if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
 689                     B_FALSE, &dcfg, &length)) != 0)
 690                         goto fail4;
 691 
 692                 nitems = EFX_DWORD_FIELD(dcfg->num_fw_version_items,
 693                             EFX_DWORD_0);
 694                 if (nitems < entry->partn)
 695                         goto done;
 696 
 697                 temp[0] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_w,
 698                             EFX_WORD_0);
 699                 temp[1] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_x,
 700                             EFX_WORD_0);
 701                 temp[2] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_y,
 702                             EFX_WORD_0);
 703                 temp[3] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_z,
 704                             EFX_WORD_0);
 705                 if (memcmp(version, temp, sizeof (temp)) < 0)
 706                         memcpy(version, temp, sizeof (temp));
 707 
 708         done:
 709                 EFSYS_KMEM_FREE(enp->en_esip, length, dcfg);
 710         }
 711 
 712         return (0);
 713 
 714 fail4:
 715         EFSYS_PROBE(fail4);
 716 fail3:
 717         EFSYS_PROBE(fail3);
 718 fail2:
 719         EFSYS_PROBE(fail2);
 720 fail1:
 721         EFSYS_PROBE1(fail1, int, rc);
 722 
 723         return (rc);
 724 }
 725 
 726         __checkReturn           int
 727 siena_nvram_rw_start(
 728         __in                    efx_nic_t *enp,
 729         __in                    efx_nvram_type_t type,
 730         __out                   size_t *chunk_sizep)
 731 {
 732         siena_parttbl_entry_t *entry;
 733         int rc;
 734 
 735         if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
 736                 rc = ENOTSUP;
 737                 goto fail1;
 738         }
 739 
 740         if ((rc = siena_nvram_partn_lock(enp, entry->partn)) != 0)
 741                 goto fail2;
 742 
 743         if (chunk_sizep != NULL)
 744                 *chunk_sizep = SIENA_NVRAM_CHUNK;
 745 
 746         return (0);
 747 
 748 fail2:
 749         EFSYS_PROBE(fail2);
 750 fail1:
 751         EFSYS_PROBE1(fail1, int, rc);
 752 
 753         return (rc);
 754 }
 755 
 756         __checkReturn           int
 757 siena_nvram_read_chunk(
 758         __in                    efx_nic_t *enp,
 759         __in                    efx_nvram_type_t type,
 760         __in                    unsigned int offset,
 761         __out_bcount(size)      caddr_t data,
 762         __in                    size_t size)
 763 {
 764         siena_parttbl_entry_t *entry;
 765         int rc;
 766 
 767         if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
 768                 rc = ENOTSUP;
 769                 goto fail1;
 770         }
 771 
 772         if ((rc = siena_nvram_partn_read(enp, entry->partn,
 773             offset, data, size)) != 0)
 774                 goto fail2;
 775 
 776         return (0);
 777 
 778 fail2:
 779         EFSYS_PROBE(fail2);
 780 fail1:
 781         EFSYS_PROBE1(fail1, int, rc);
 782 
 783         return (rc);
 784 }
 785 
 786         __checkReturn           int
 787 siena_nvram_erase(
 788         __in                    efx_nic_t *enp,
 789         __in                    efx_nvram_type_t type)
 790 {
 791         siena_parttbl_entry_t *entry;
 792         size_t size;
 793         int rc;
 794 
 795         if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
 796                 rc = ENOTSUP;
 797                 goto fail1;
 798         }
 799 
 800         if ((rc = siena_nvram_partn_size(enp, entry->partn, &size)) != 0)
 801                 goto fail2;
 802 
 803         if ((rc = siena_nvram_partn_erase(enp, entry->partn, 0, size)) != 0)
 804                 goto fail3;
 805 
 806         return (0);
 807 
 808 fail3:
 809         EFSYS_PROBE(fail3);
 810 fail2:
 811         EFSYS_PROBE(fail2);
 812 fail1:
 813         EFSYS_PROBE1(fail1, int, rc);
 814 
 815         return (rc);
 816 }
 817 
 818         __checkReturn           int
 819 siena_nvram_write_chunk(
 820         __in                    efx_nic_t *enp,
 821         __in                    efx_nvram_type_t type,
 822         __in                    unsigned int offset,
 823         __in_bcount(size)       caddr_t data,
 824         __in                    size_t size)
 825 {
 826         siena_parttbl_entry_t *entry;
 827         int rc;
 828 
 829         if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
 830                 rc = ENOTSUP;
 831                 goto fail1;
 832         }
 833 
 834         if ((rc = siena_nvram_partn_write(enp, entry->partn,
 835             offset, data, size)) != 0)
 836                 goto fail2;
 837 
 838         return (0);
 839 
 840 fail2:
 841         EFSYS_PROBE(fail2);
 842 fail1:
 843         EFSYS_PROBE1(fail1, int, rc);
 844 
 845         return (rc);
 846 }
 847 
 848                                 void
 849 siena_nvram_rw_finish(
 850         __in                    efx_nic_t *enp,
 851         __in                    efx_nvram_type_t type)
 852 {
 853         siena_parttbl_entry_t *entry;
 854 
 855         if ((entry = siena_parttbl_entry(enp, type)) != NULL)
 856                 siena_nvram_partn_unlock(enp, entry->partn);
 857 }
 858 
 859         __checkReturn           int
 860 siena_nvram_set_version(
 861         __in                    efx_nic_t *enp,
 862         __in                    efx_nvram_type_t type,
 863         __out                   uint16_t version[4])
 864 {
 865         siena_mc_dynamic_config_hdr_t *dcfg = NULL;
 866         siena_parttbl_entry_t *entry;
 867         unsigned int dcfg_partn;
 868         size_t partn_size;
 869         unsigned int hdr_length;
 870         unsigned int vpd_length;
 871         unsigned int vpd_offset;
 872         unsigned int nitems;
 873         unsigned int required_hdr_length;
 874         unsigned int pos;
 875         uint8_t cksum;
 876         uint32_t subtype;
 877         size_t length;
 878         int rc;
 879 
 880         if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
 881                 rc = ENOTSUP;
 882                 goto fail1;
 883         }
 884 
 885         dcfg_partn = (entry->port == 1)
 886                 ? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
 887                 : MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
 888 
 889         if ((rc = siena_nvram_partn_size(enp, dcfg_partn, &partn_size)) != 0)
 890                 goto fail2;
 891 
 892         if ((rc = siena_nvram_partn_lock(enp, dcfg_partn)) != 0)
 893                 goto fail2;
 894 
 895         if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
 896             B_TRUE, &dcfg, &length)) != 0)
 897                 goto fail3;
 898 
 899         hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0);
 900         nitems = EFX_DWORD_FIELD(dcfg->num_fw_version_items, EFX_DWORD_0);
 901         vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0);
 902         vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0);
 903 
 904         /*
 905          * NOTE: This function will blatt any fields trailing the version
 906          * vector, or the VPD chunk.
 907          */
 908         required_hdr_length = SIENA_DYNAMIC_CFG_SIZE(entry->partn + 1);
 909         if (required_hdr_length + vpd_length > length) {
 910                 rc = ENOSPC;
 911                 goto fail4;
 912         }
 913 
 914         if (vpd_offset < required_hdr_length) {
 915                 (void) memmove((caddr_t)dcfg + required_hdr_length,
 916                         (caddr_t)dcfg + vpd_offset, vpd_length);
 917                 vpd_offset = required_hdr_length;
 918                 EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset,
 919                                     EFX_DWORD_0, vpd_offset);
 920         }
 921 
 922         if (hdr_length < required_hdr_length) {
 923                 (void) memset((caddr_t)dcfg + hdr_length, 0,
 924                         required_hdr_length - hdr_length);
 925                 hdr_length = required_hdr_length;
 926                 EFX_POPULATE_WORD_1(dcfg->length,
 927                                     EFX_WORD_0, hdr_length);
 928         }
 929 
 930         /* Get the subtype to insert into the fw_subtype array */
 931         if ((rc = siena_nvram_get_subtype(enp, entry->partn, &subtype)) != 0)
 932                 goto fail5;
 933 
 934         /* Fill out the new version */
 935         EFX_POPULATE_DWORD_1(dcfg->fw_version[entry->partn].fw_subtype,
 936                             EFX_DWORD_0, subtype);
 937         EFX_POPULATE_WORD_1(dcfg->fw_version[entry->partn].version_w,
 938                             EFX_WORD_0, version[0]);
 939         EFX_POPULATE_WORD_1(dcfg->fw_version[entry->partn].version_x,
 940                             EFX_WORD_0, version[1]);
 941         EFX_POPULATE_WORD_1(dcfg->fw_version[entry->partn].version_y,
 942                             EFX_WORD_0, version[2]);
 943         EFX_POPULATE_WORD_1(dcfg->fw_version[entry->partn].version_z,
 944                             EFX_WORD_0, version[3]);
 945 
 946         /* Update the version count */
 947         if (nitems < entry->partn + 1) {
 948                 nitems = entry->partn + 1;
 949                 EFX_POPULATE_DWORD_1(dcfg->num_fw_version_items,
 950                                     EFX_DWORD_0, nitems);
 951         }
 952 
 953         /* Update the checksum */
 954         cksum = 0;
 955         for (pos = 0; pos < hdr_length; pos++)
 956                 cksum += ((uint8_t *)dcfg)[pos];
 957         dcfg->csum.eb_u8[0] -= cksum;
 958 
 959         /* Erase and write the new partition */
 960         if ((rc = siena_nvram_partn_erase(enp, dcfg_partn, 0, partn_size)) != 0)
 961                 goto fail6;
 962 
 963         /* Write out the new structure to nvram */
 964         if ((rc = siena_nvram_partn_write(enp, dcfg_partn, 0,
 965             (caddr_t)dcfg, vpd_offset + vpd_length)) != 0)
 966                 goto fail7;
 967 
 968         EFSYS_KMEM_FREE(enp->en_esip, length, dcfg);
 969 
 970         siena_nvram_partn_unlock(enp, dcfg_partn);
 971 
 972         return (0);
 973 
 974 fail7:
 975         EFSYS_PROBE(fail7);
 976 fail6:
 977         EFSYS_PROBE(fail6);
 978 fail5:
 979         EFSYS_PROBE(fail5);
 980 fail4:
 981         EFSYS_PROBE(fail4);
 982 
 983         EFSYS_KMEM_FREE(enp->en_esip, length, dcfg);
 984 fail3:
 985         EFSYS_PROBE(fail3);
 986 fail2:
 987         EFSYS_PROBE(fail2);
 988 fail1:
 989         EFSYS_PROBE1(fail1, int, rc);
 990 
 991         return (rc);
 992 }
 993 
 994 #endif  /* EFSYS_OPT_NVRAM */
 995 
 996 #endif  /* EFSYS_OPT_SIENA */