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