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