1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 * 21 * 22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * This file contains various support routines. 28 */ 29 30 #include <sys/scsi/adapters/pmcs/pmcs.h> 31 32 /* 33 * SAS Topology Configuration 34 */ 35 static int pmcs_flash_chunk(pmcs_hw_t *, uint8_t *); 36 37 /* 38 * Check current firmware version for correctness 39 * and try to flash the correct firmware if what is 40 * running isn't correct. 41 * 42 * Must be called after setup and MPI setup and 43 * interrupts are enabled. 44 */ 45 46 int 47 pmcs_firmware_update(pmcs_hw_t *pwp) 48 { 49 ddi_modhandle_t modhp; 50 char buf[64], *bufp; 51 int errno; 52 uint8_t *cstart, *cend; /* Firmware image file */ 53 uint8_t *istart, *iend; /* ila */ 54 uint8_t *sstart, *send; /* SPCBoot */ 55 uint32_t *fwvp; 56 int defret = 0; 57 long fw_version, ila_version; 58 uint8_t *fw_verp, *ila_verp; 59 60 /* 61 * If updating is disabled, we're done. 62 */ 63 if (pwp->fw_disable_update) { 64 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 65 "Firmware update disabled by conf file"); 66 return (0); 67 } 68 69 /* 70 * If we're already running the right firmware, we're done. 71 */ 72 if (pwp->fw == PMCS_FIRMWARE_VERSION) { 73 if (pwp->fw_force_update == 0) { 74 return (0); 75 } 76 77 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 78 "Firmware version matches, but still forcing update"); 79 } 80 81 modhp = ddi_modopen(PMCS_FIRMWARE_FILENAME, KRTLD_MODE_FIRST, &errno); 82 if (errno) { 83 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 84 "%s: Firmware module not available; will not upgrade", 85 __func__); 86 return (defret); 87 } 88 89 fwvp = ddi_modsym(modhp, PMCS_FIRMWARE_VERSION_NAME, &errno); 90 if (errno) { 91 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 92 "%s: unable to find symbol '%s'", 93 __func__, PMCS_FIRMWARE_VERSION_NAME); 94 (void) ddi_modclose(modhp); 95 return (defret); 96 } 97 98 /* 99 * If the firmware version from the module isn't what we expect, 100 * and force updating is disabled, return the default (for this 101 * mode of operation) value. 102 */ 103 if (*fwvp != PMCS_FIRMWARE_VERSION) { 104 if (pwp->fw_force_update == 0) { 105 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 106 "%s: firmware module version wrong (0x%x)", 107 __func__, *fwvp); 108 (void) ddi_modclose(modhp); 109 return (defret); 110 } 111 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 112 "%s: firmware module version wrong (0x%x) - update forced", 113 __func__, *fwvp); 114 } 115 116 (void) snprintf(buf, sizeof (buf), 117 PMCS_FIRMWARE_CODE_NAME PMCS_FIRMWARE_START_SUF); 118 cstart = ddi_modsym(modhp, buf, &errno); 119 if (errno) { 120 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 121 "%s: unable to find symbol '%s'", __func__, buf); 122 (void) ddi_modclose(modhp); 123 return (defret); 124 } 125 126 (void) snprintf(buf, sizeof (buf), 127 PMCS_FIRMWARE_CODE_NAME PMCS_FIRMWARE_END_SUF); 128 cend = ddi_modsym(modhp, buf, &errno); 129 if (errno) { 130 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 131 "%s: unable to find symbol '%s'", __func__, buf); 132 (void) ddi_modclose(modhp); 133 return (defret); 134 } 135 136 (void) snprintf(buf, sizeof (buf), 137 PMCS_FIRMWARE_ILA_NAME PMCS_FIRMWARE_START_SUF); 138 istart = ddi_modsym(modhp, buf, &errno); 139 if (errno) { 140 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 141 "%s: unable to find symbol '%s'", __func__, buf); 142 (void) ddi_modclose(modhp); 143 return (defret); 144 } 145 146 (void) snprintf(buf, sizeof (buf), 147 PMCS_FIRMWARE_ILA_NAME PMCS_FIRMWARE_END_SUF); 148 iend = ddi_modsym(modhp, buf, &errno); 149 if (errno) { 150 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 151 "%s: unable to find symbol '%s'", __func__, buf); 152 (void) ddi_modclose(modhp); 153 return (defret); 154 } 155 156 (void) snprintf(buf, sizeof (buf), 157 PMCS_FIRMWARE_SPCBOOT_NAME PMCS_FIRMWARE_START_SUF); 158 sstart = ddi_modsym(modhp, buf, &errno); 159 if (errno) { 160 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 161 "%s: unable to find symbol '%s'", __func__, buf); 162 (void) ddi_modclose(modhp); 163 return (defret); 164 } 165 166 (void) snprintf(buf, sizeof (buf), 167 PMCS_FIRMWARE_SPCBOOT_NAME PMCS_FIRMWARE_END_SUF); 168 send = ddi_modsym(modhp, buf, &errno); 169 if (errno) { 170 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 171 "%s: unable to find symbol '%s'", __func__, buf); 172 (void) ddi_modclose(modhp); 173 return (defret); 174 } 175 176 /* 177 * Get the ILA and firmware versions from the modules themselves 178 */ 179 ila_verp = iend - PMCS_ILA_VER_OFFSET; 180 (void) ddi_strtol((const char *)ila_verp, &bufp, 16, &ila_version); 181 fw_verp = cend - PMCS_FW_VER_OFFSET; 182 (void) ddi_strtol((const char *)fw_verp, &bufp, 16, &fw_version); 183 184 /* 185 * If force update is not set, verify that what we're loading is 186 * what we expect. 187 */ 188 if (pwp->fw_force_update == 0) { 189 if (fw_version != PMCS_FIRMWARE_VERSION) { 190 pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 191 "Expected fw version 0x%x, not 0x%lx: not " 192 "updating", PMCS_FIRMWARE_VERSION, fw_version); 193 (void) ddi_modclose(modhp); 194 return (defret); 195 } 196 } 197 198 pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 199 "Upgrading firmware on card from 0x%x to 0x%lx (ILA version 0x%lx)", 200 pwp->fw, fw_version, ila_version); 201 202 /* 203 * The SPCBoot image must be updated first, and this is written to 204 * SEEPROM, not flash. 205 */ 206 if (pmcs_set_nvmd(pwp, PMCS_NVMD_SPCBOOT, sstart, 207 (size_t)((size_t)send - (size_t)sstart)) == B_FALSE) { 208 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 209 "%s: unable to flash '%s' segment", 210 __func__, PMCS_FIRMWARE_SPCBOOT_NAME); 211 (void) ddi_modclose(modhp); 212 return (-1); 213 } 214 215 if (pmcs_fw_flash(pwp, (void *)istart, 216 (uint32_t)((size_t)iend - (size_t)istart))) { 217 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 218 "%s: unable to flash '%s' segment", 219 __func__, PMCS_FIRMWARE_ILA_NAME); 220 (void) ddi_modclose(modhp); 221 return (-1); 222 } 223 224 if (pmcs_fw_flash(pwp, (void *)cstart, 225 (uint32_t)((size_t)cend - (size_t)cstart))) { 226 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 227 "%s: unable to flash '%s' segment", 228 __func__, PMCS_FIRMWARE_CODE_NAME); 229 (void) ddi_modclose(modhp); 230 return (-1); 231 } 232 233 (void) ddi_modclose(modhp); 234 235 if (pmcs_soft_reset(pwp, B_FALSE)) { 236 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 237 "%s: soft reset after flash update failed", __func__); 238 return (-1); 239 } else { 240 pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 241 "%s: Firmware successfully upgraded.", __func__); 242 pwp->last_reset_reason = PMCS_LAST_RST_FW_UPGRADE; 243 } 244 return (0); 245 } 246 247 /* 248 * Flash firmware support 249 * Called unlocked. 250 */ 251 int 252 pmcs_fw_flash(pmcs_hw_t *pwp, pmcs_fw_hdr_t *hdr, uint32_t length) 253 { 254 pmcs_fw_hdr_t *hp; 255 uint8_t *wrk, *base; 256 257 /* 258 * Step 1- Validate firmware chunks within passed pointer. 259 */ 260 hp = hdr; 261 wrk = (uint8_t *)hdr; 262 base = wrk; 263 for (;;) { 264 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL, 265 "%s: partition 0x%x, Length 0x%x", __func__, 266 hp->destination_partition, ntohl(hp->firmware_length)); 267 if (ntohl(hp->firmware_length) == 0) { 268 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 269 "%s: bad firmware length 0x%x", 270 __func__, ntohl(hp->firmware_length)); 271 return (EINVAL); 272 } 273 wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length)); 274 if (wrk == base + length) { 275 break; 276 } 277 if (wrk > base + length) { 278 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 279 "%s: out of bounds firmware length", __func__); 280 return (EINVAL); 281 } 282 hp = (void *)wrk; 283 } 284 285 /* 286 * Step 2- acquire scratch 287 */ 288 (void) pmcs_acquire_scratch(pwp, B_TRUE); 289 290 /* 291 * Step 3- loop through firmware chunks and send each one 292 * down to be flashed. 293 */ 294 hp = hdr; 295 wrk = (uint8_t *)hdr; 296 base = wrk; 297 for (;;) { 298 if (pmcs_flash_chunk(pwp, wrk)) { 299 pmcs_release_scratch(pwp); 300 return (EIO); 301 } 302 wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length)); 303 if (wrk == base + length) { 304 break; 305 } 306 hp = (void *) wrk; 307 } 308 pmcs_release_scratch(pwp); 309 return (0); 310 } 311 312 static int 313 pmcs_flash_chunk(pmcs_hw_t *pwp, uint8_t *chunk) 314 { 315 pmcs_fw_hdr_t *hp; 316 pmcwork_t *pwrk; 317 uint32_t len, seg, off, result, amt, msg[PMCS_MSG_SIZE], *ptr; 318 319 hp = (void *)chunk; 320 len = sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length); 321 322 seg = off = 0; 323 while (off < len) { 324 amt = PMCS_SCRATCH_SIZE; 325 if (off + amt > len) { 326 amt = len - off; 327 } 328 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL, 329 "%s: segment %d offset %u length %u", 330 __func__, seg, off, amt); 331 (void) memcpy(pwp->scratch, &chunk[off], amt); 332 pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL); 333 if (pwrk == NULL) { 334 return (ENOMEM); 335 } 336 pwrk->arg = msg; 337 msg[0] = LE_32(PMCS_HIPRI(pwp, 338 PMCS_OQ_EVENTS, PMCIN_FW_FLASH_UPDATE)); 339 msg[1] = LE_32(pwrk->htag); 340 msg[2] = LE_32(off); 341 msg[3] = LE_32(amt); 342 if (off == 0) { 343 msg[4] = LE_32(len); 344 } else { 345 msg[4] = 0; 346 } 347 msg[5] = 0; 348 msg[6] = 0; 349 msg[7] = 0; 350 msg[8] = 0; 351 msg[9] = 0; 352 msg[10] = 0; 353 msg[11] = 0; 354 msg[12] = LE_32(DWORD0(pwp->scratch_dma)); 355 msg[13] = LE_32(DWORD1(pwp->scratch_dma)); 356 msg[14] = LE_32(amt); 357 msg[15] = 0; 358 mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 359 ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 360 if (ptr == NULL) { 361 mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 362 pmcs_pwork(pwp, pwrk); 363 pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 364 pmcs_nomsg, __func__); 365 return (ENOMEM); 366 } 367 COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE); 368 (void) memset(msg, 0xaf, sizeof (msg)); 369 pwrk->state = PMCS_WORK_STATE_ONCHIP; 370 INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 371 WAIT_FOR(pwrk, 5000, result); 372 pmcs_pwork(pwp, pwrk); 373 if (result) { 374 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 375 pmcs_timeo, __func__); 376 return (EIO); 377 } 378 switch (LE_32(msg[2])) { 379 case FLASH_UPDATE_COMPLETE_PENDING_REBOOT: 380 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL, 381 "%s: segment %d complete pending reboot", 382 __func__, seg); 383 break; 384 case FLASH_UPDATE_IN_PROGRESS: 385 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL, 386 "%s: segment %d downloaded", __func__, seg); 387 break; 388 case FLASH_UPDATE_HDR_ERR: 389 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 390 "%s: segment %d header error", __func__, seg); 391 return (EIO); 392 case FLASH_UPDATE_OFFSET_ERR: 393 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 394 "%s: segment %d offset error", __func__, seg); 395 return (EIO); 396 case FLASH_UPDATE_UPDATE_CRC_ERR: 397 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 398 "%s: segment %d update crc error", __func__, seg); 399 return (EIO); 400 case FLASH_UPDATE_LENGTH_ERR: 401 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 402 "%s: segment %d length error", __func__, seg); 403 return (EIO); 404 case FLASH_UPDATE_HW_ERR: 405 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 406 "%s: segment %d hw error", __func__, seg); 407 return (EIO); 408 case FLASH_UPDATE_DNLD_NOT_SUPPORTED: 409 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 410 "%s: segment %d download not supported error", 411 __func__, seg); 412 return (EIO); 413 case FLASH_UPDATE_DISABLED: 414 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 415 "%s: segment %d update disabled error", 416 __func__, seg); 417 return (EIO); 418 default: 419 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 420 "%s: segment %d unknown error %x", 421 __func__, seg, msg[2]); 422 return (EIO); 423 } 424 off += amt; 425 seg++; 426 } 427 return (0); 428 } 429 430 /* 431 * pmcs_validate_vpd 432 * 433 * Input: softstate pointer and pointer to vpd data buffer 434 * Returns: B_TRUE if VPD data looks OK, B_FALSE otherwise 435 */ 436 static boolean_t 437 pmcs_validate_vpd(pmcs_hw_t *pwp, uint8_t *data) 438 { 439 pmcs_vpd_header_t *vpd_header; 440 uint8_t *bufp, kv_len, *chksump, chksum = 0; 441 char tbuf[80]; 442 char prop[24]; 443 int idx, str_len; 444 uint16_t strid_length, chksum_len; 445 uint64_t wwid; 446 pmcs_vpd_kv_t *vkvp; 447 448 vpd_header = (pmcs_vpd_header_t *)data; 449 450 /* 451 * Make sure we understand the format of this data 452 */ 453 454 if (vpd_header->eeprom_version < PMCS_VPD_VERSION) { 455 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 456 "%s: VPD version(%d) out-of-date; (%d) required." 457 " Thebe card needs to be flashed.", 458 __func__, vpd_header->eeprom_version, PMCS_VPD_VERSION); 459 } 460 if ((vpd_header->eeprom_version != PMCS_VPD_VERSION) && 461 (vpd_header->eeprom_version != (PMCS_VPD_VERSION - 1))) { 462 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 463 "%s: VPD version mismatch (%d != %d)", 464 __func__, vpd_header->eeprom_version, PMCS_VPD_VERSION); 465 return (B_FALSE); 466 } 467 468 /* 469 * Do we have a valid SAS WWID? 470 */ 471 if (((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4) != NAA_IEEE_REG) { 472 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 473 "%s: SAS WWN has invalid NAA (%d)", __func__, 474 ((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4)); 475 return (B_FALSE); 476 } 477 wwid = pmcs_barray2wwn(vpd_header->hba_sas_wwid); 478 for (idx = 0; idx < PMCS_MAX_PORTS; idx++) { 479 pwp->sas_wwns[idx] = wwid + idx; 480 } 481 482 if (vpd_header->vpd_start_byte != PMCS_VPD_START) { 483 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 484 "%s: Didn't see VPD start byte", __func__); 485 return (B_FALSE); 486 } 487 488 /* 489 * We only checksum the VPD data between (and including) VPD Start byte 490 * and the checksum value byte. The length of this data for CRC is 491 * 15 less than the length indicated in vpd_length field of the header. 492 * 8 (SAS WWN) + 2 (subsystem ID) + 2 (subsystem vendor ID) + 493 * 1 (end tag) + 2 (hex byte CRC, different from this one) = 15 bytes 494 */ 495 /* 496 * VPD length (little endian format) is represented as byte-array field 497 * & read the following way to avoid alignment issues (in SPARC) 498 */ 499 chksum_len = ((vpd_header->vpd_length[1] << 8) | 500 (vpd_header->vpd_length[0])) - 15; 501 /* Validate VPD data checksum */ 502 chksump = (uint8_t *)&vpd_header->vpd_start_byte; 503 ASSERT (*chksump == PMCS_VPD_START); 504 for (idx = 0; idx < chksum_len; idx++, chksump++) { 505 chksum += *chksump; 506 } 507 ASSERT (*chksump == PMCS_VPD_END); 508 if (chksum) { 509 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: VPD checksum(%d)" 510 " non-zero. Checksum validation failed.", __func__, chksum); 511 } 512 513 /* 514 * Get length of string ID tag and read it. 515 */ 516 bufp = (uint8_t *)&vpd_header->vpd_start_byte; 517 bufp += 3; /* Skip the start byte and length */ 518 /* 519 * String ID tag length (little endian format) is represented as 520 * byte-array & read the following way to avoid alignment issues 521 * (in SPARC) 522 */ 523 strid_length = (vpd_header->strid_length[1] << 8) | 524 (vpd_header->strid_length[0]); 525 if (strid_length > 79) { 526 strid_length = 79; 527 } 528 bcopy(bufp, tbuf, strid_length); 529 tbuf[strid_length] = 0; 530 531 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 532 "%s: Product Name: '%s'", __func__, tbuf); 533 pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_MODEL_NAME, tbuf); 534 535 /* 536 * Skip VPD-R tag and length of read-only tag, then start reading 537 * keyword/value pairs 538 */ 539 bufp += strid_length; /* Skip to VPD-R tag */ 540 bufp += 3; /* Skip VPD-R tag and length of VPD-R data */ 541 542 vkvp = (pmcs_vpd_kv_t *)bufp; 543 544 while (vkvp->keyword[0] != PMCS_VPD_END) { 545 tbuf[0] = 0; 546 str_len = snprintf(tbuf, 80, "VPD: %c%c = <", 547 vkvp->keyword[0], vkvp->keyword[1]); 548 549 kv_len = vkvp->value_length; 550 for (idx = 0; idx < kv_len; idx++) { 551 tbuf[str_len + idx] = vkvp->value[idx]; 552 prop[idx] = vkvp->value[idx]; 553 } 554 prop[idx] = '\0'; 555 str_len += kv_len; 556 tbuf[str_len] = '>'; 557 tbuf[str_len + 1] = 0; 558 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s (Len: 0x%x)", 559 tbuf, kv_len); 560 561 /* Keyword is Manufacturer */ 562 if ((vkvp->keyword[0] == 'M') && (vkvp->keyword[1] == 'N')) { 563 pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, 564 PMCS_MANUFACTURER, prop); 565 } 566 /* Keyword is Serial Number */ 567 if ((vkvp->keyword[0] == 'S') && (vkvp->keyword[1] == 'N')) { 568 pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, 569 PMCS_SERIAL_NUMBER, prop); 570 } 571 572 vkvp = (pmcs_vpd_kv_t *)(bufp + 3 + kv_len); 573 bufp += kv_len + 3; 574 } 575 576 return (B_TRUE); 577 } 578 579 /* 580 * pmcs_get_nvmd 581 * 582 * This function will read the requested data from the non-volatile 583 * storage on the card. This could mean SEEPROM, VPD, or other areas 584 * as defined by the PM8001 programmer's manual. 585 * 586 * nvmd_type: The data type being requested 587 * nvmd: NVM device to access (IOP/AAP1) 588 * offset: Must be 4K alignment 589 * buf: Pointer to memory region for retrieved data 590 * size_left: Total available bytes left in buf 591 * 592 * Returns: non-negative on success, -1 on failure 593 */ 594 595 /*ARGSUSED*/ 596 int 597 pmcs_get_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t nvmd, 598 uint32_t offset, char *buf, uint32_t size_left) 599 { 600 pmcs_get_nvmd_cmd_t iomb; 601 pmcwork_t *workp; 602 uint8_t *chunkp; 603 uint32_t *ptr, ibq, *iombp; 604 uint32_t dlen; 605 uint16_t status; 606 uint8_t tdas_nvmd, ip, tda, tbn_tdps; 607 uint8_t doa[3]; 608 int32_t result = -1, i = 0; 609 610 switch (nvmd_type) { 611 case PMCS_NVMD_VPD: 612 tdas_nvmd = PMCIN_NVMD_TDPS_1 | PMCIN_NVMD_TWI; 613 tda = PMCIN_TDA_PAGE(2); 614 tbn_tdps = PMCIN_NVMD_TBN(0) | PMCIN_NVMD_TDPS_8; 615 ip = PMCIN_NVMD_INDIRECT_PLD; 616 dlen = LE_32(PMCS_SEEPROM_PAGE_SIZE); 617 doa[0] = 0; 618 doa[1] = 0; 619 doa[2] = 0; 620 break; 621 case PMCS_NVMD_REG_DUMP: 622 tdas_nvmd = nvmd; 623 tda = 0; 624 tbn_tdps = 0; 625 ip = PMCIN_NVMD_INDIRECT_PLD; 626 dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE); 627 doa[0] = offset & 0xff; 628 doa[1] = (offset >> 8) & 0xff; 629 doa[2] = (offset >> 16) & 0xff; 630 break; 631 case PMCS_NVMD_EVENT_LOG: 632 tdas_nvmd = nvmd; 633 tda = 0; 634 tbn_tdps = 0; 635 ip = PMCIN_NVMD_INDIRECT_PLD; 636 dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE); 637 offset = offset + PMCS_NVMD_EVENT_LOG_OFFSET; 638 doa[0] = offset & 0xff; 639 doa[1] = (offset >> 8) & 0xff; 640 doa[2] = (offset >> 16) & 0xff; 641 break; 642 default: 643 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 644 "%s: Invalid nvmd type: %d", __func__, nvmd_type); 645 return (-1); 646 } 647 648 workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL); 649 if (workp == NULL) { 650 pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 651 "%s: Unable to get work struct", __func__); 652 return (-1); 653 } 654 655 ptr = &iomb.header; 656 bzero(ptr, sizeof (pmcs_get_nvmd_cmd_t)); 657 *ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_GET_NVMD_DATA)); 658 workp->arg = (void *)&iomb; 659 iomb.htag = LE_32(workp->htag); 660 iomb.ip = ip; 661 iomb.tbn_tdps = tbn_tdps; 662 iomb.tda = tda; 663 iomb.tdas_nvmd = tdas_nvmd; 664 iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr)); 665 iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr)); 666 iomb.ipdl = dlen; 667 iomb.doa[0] = doa[0]; 668 iomb.doa[1] = doa[1]; 669 iomb.doa[2] = doa[2]; 670 671 /* 672 * ptr will now point to the inbound queue message 673 */ 674 GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq); 675 if (ptr == NULL) { 676 pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 677 "!%s: Unable to get IQ entry", __func__); 678 pmcs_pwork(pwp, workp); 679 return (-1); 680 } 681 682 bzero(ptr, PMCS_MSG_SIZE << 2); /* PMCS_MSG_SIZE is in dwords */ 683 iombp = (uint32_t *)&iomb; 684 COPY_MESSAGE(ptr, iombp, sizeof (pmcs_get_nvmd_cmd_t) >> 2); 685 workp->state = PMCS_WORK_STATE_ONCHIP; 686 INC_IQ_ENTRY(pwp, ibq); 687 688 WAIT_FOR(workp, 1000, result); 689 ptr = workp->arg; 690 if (result) { 691 pmcs_timed_out(pwp, workp->htag, __func__); 692 pmcs_pwork(pwp, workp); 693 return (-1); 694 } 695 status = LE_32(*(ptr + 3)) & 0xffff; 696 if (status != PMCS_NVMD_STAT_SUCCESS) { 697 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 698 "%s: Error, status = 0x%04x", __func__, status); 699 pmcs_pwork(pwp, workp); 700 return (-1); 701 } 702 703 pmcs_pwork(pwp, workp); 704 705 if (ddi_dma_sync(pwp->cip_handles, 0, 0, 706 DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS) { 707 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 708 "Condition check failed at %s():%d", __func__, __LINE__); 709 } 710 chunkp = (uint8_t *)pwp->flash_chunkp; 711 712 switch (nvmd) { 713 case PMCIN_NVMD_VPD: 714 if (pmcs_validate_vpd(pwp, chunkp)) { 715 result = 0; 716 } else { 717 result = -1; 718 } 719 break; 720 case PMCIN_NVMD_AAP1: 721 case PMCIN_NVMD_IOP: 722 ASSERT(buf); 723 i = 0; 724 if (nvmd_type == PMCS_NVMD_REG_DUMP) { 725 while ((i < PMCS_FLASH_CHUNK_SIZE) && 726 (chunkp[i] != 0xff) && (chunkp[i] != '\0')) { 727 (void) snprintf(&buf[i], (size_left - i), 728 "%c", chunkp[i]); 729 i++; 730 } 731 } else if (nvmd_type == PMCS_NVMD_EVENT_LOG) { 732 i = pmcs_dump_binary(pwp, pwp->flash_chunkp, 0, 733 (PMCS_FLASH_CHUNK_SIZE >> 2), buf, size_left); 734 } 735 result = i; 736 break; 737 default: 738 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 739 "UNKNOWN NVMD DEVICE"); 740 return (-1); 741 } 742 743 return (result); 744 } 745 746 /* 747 * pmcs_set_nvmd 748 * 749 * This function will write the requested data to non-volatile storage 750 * on the HBA. This could mean SEEPROM, VPD, or other areas as defined by 751 * the PM8001 programmer's manual. 752 * 753 * nvmd_type: The data type to be written 754 * buf: Pointer to memory region for data to write 755 * len: Length of the data buffer 756 * 757 * Returns: B_TRUE on success, B_FALSE on failure 758 */ 759 760 boolean_t 761 pmcs_set_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t *buf, 762 size_t len) 763 { 764 pmcs_set_nvmd_cmd_t iomb; 765 pmcwork_t *workp; 766 uint32_t *ptr, ibq, *iombp; 767 uint32_t dlen; 768 uint16_t status; 769 uint8_t tdas_nvmd, ip; 770 int result; 771 772 switch (nvmd_type) { 773 case PMCS_NVMD_SPCBOOT: 774 tdas_nvmd = PMCIN_NVMD_SEEPROM; 775 ip = PMCIN_NVMD_INDIRECT_PLD; 776 ASSERT((len >= PMCS_SPCBOOT_MIN_SIZE) && 777 (len <= PMCS_SPCBOOT_MAX_SIZE)); 778 dlen = LE_32(len); 779 break; 780 default: 781 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 782 "%s: Invalid nvmd type: %d", __func__, nvmd_type); 783 return (B_FALSE); 784 } 785 786 pmcs_prt(pwp, PMCS_PRT_DEBUG_DEVEL, NULL, NULL, 787 "%s: Request for nvmd type: %d", __func__, nvmd_type); 788 789 workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL); 790 if (workp == NULL) { 791 pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 792 "%s: Unable to get work struct", __func__); 793 return (B_FALSE); 794 } 795 796 ptr = &iomb.header; 797 bzero(ptr, sizeof (pmcs_set_nvmd_cmd_t)); 798 *ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_SET_NVMD_DATA)); 799 workp->arg = (void *)&iomb; 800 iomb.htag = LE_32(workp->htag); 801 iomb.ip = ip; 802 iomb.tdas_nvmd = tdas_nvmd; 803 iomb.signature = LE_32(PMCS_SEEPROM_SIGNATURE); 804 iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr)); 805 iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr)); 806 iomb.ipdl = dlen; 807 808 pmcs_print_entry(pwp, PMCS_PRT_DEBUG_DEVEL, 809 "PMCIN_SET_NVMD_DATA iomb", (void *)&iomb); 810 811 bcopy(buf, pwp->flash_chunkp, len); 812 if (ddi_dma_sync(pwp->cip_handles, 0, 0, 813 DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) { 814 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 815 "Condition check failed at %s():%d", __func__, __LINE__); 816 } 817 818 /* 819 * ptr will now point to the inbound queue message 820 */ 821 GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq); 822 if (ptr == NULL) { 823 pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 824 "!%s: Unable to get IQ entry", __func__); 825 pmcs_pwork(pwp, workp); 826 return (B_FALSE); 827 } 828 829 bzero(ptr, PMCS_MSG_SIZE << 2); /* PMCS_MSG_SIZE is in dwords */ 830 iombp = (uint32_t *)&iomb; 831 COPY_MESSAGE(ptr, iombp, sizeof (pmcs_set_nvmd_cmd_t) >> 2); 832 workp->state = PMCS_WORK_STATE_ONCHIP; 833 INC_IQ_ENTRY(pwp, ibq); 834 835 WAIT_FOR(workp, 2000, result); 836 837 if (result) { 838 pmcs_timed_out(pwp, workp->htag, __func__); 839 pmcs_pwork(pwp, workp); 840 return (B_FALSE); 841 } 842 843 pmcs_pwork(pwp, workp); 844 845 status = LE_32(*(ptr + 3)) & 0xffff; 846 if (status != PMCS_NVMD_STAT_SUCCESS) { 847 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 848 "%s: Error, status = 0x%04x", __func__, status); 849 return (B_FALSE); 850 } 851 852 return (B_TRUE); 853 } 854