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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * This file contains various support routines.
27 */
28
29 #include <sys/scsi/adapters/pmcs/pmcs.h>
30
31 /*
32 * SAS Topology Configuration
33 */
34 static int pmcs_flash_chunk(pmcs_hw_t *, uint8_t *);
35
36 /*
37 * Check current firmware version for correctness
38 * and try to flash the correct firmware if what is
39 * running isn't correct.
40 *
41 * Must be called after setup and MPI setup and
42 * interrupts are enabled.
43 */
44
45 int
pmcs_firmware_update(pmcs_hw_t * pwp)46 pmcs_firmware_update(pmcs_hw_t *pwp)
47 {
48 ddi_modhandle_t modhp;
49 char buf[64], *bufp;
50 int errno;
51 uint8_t *cstart, *cend; /* Firmware image file */
52 uint8_t *istart, *iend; /* ila */
53 uint8_t *sstart, *send; /* SPCBoot */
54 uint32_t *fwvp;
55 int defret = 0;
56 int first_pass = 1;
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 repeat:
216 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
217 "%s: Beginning firmware update of %s image.",
218 __func__, (first_pass ? "first" : "second"));
219
220 if (pmcs_fw_flash(pwp, (void *)istart,
221 (uint32_t)((size_t)iend - (size_t)istart))) {
222 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
223 "%s: unable to flash '%s' segment",
224 __func__, PMCS_FIRMWARE_ILA_NAME);
225 (void) ddi_modclose(modhp);
226 return (-1);
227 }
228
229 if (pmcs_fw_flash(pwp, (void *)cstart,
230 (uint32_t)((size_t)cend - (size_t)cstart))) {
231 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
232 "%s: unable to flash '%s' segment",
233 __func__, PMCS_FIRMWARE_CODE_NAME);
234 (void) ddi_modclose(modhp);
235 return (-1);
236 }
237
238 if (pmcs_soft_reset(pwp, B_FALSE)) {
239 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
240 "%s: soft reset after flash update failed", __func__);
241 (void) ddi_modclose(modhp);
242 return (-1);
243 } else {
244 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
245 "%s: %s image successfully upgraded.",
246 __func__, (first_pass ? "First" : "Second"));
247 pwp->last_reset_reason = PMCS_LAST_RST_FW_UPGRADE;
248 }
249
250 if (first_pass) {
251 first_pass = 0;
252 goto repeat;
253 }
254
255 pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
256 "%s: Firmware successfully upgraded", __func__);
257
258 (void) ddi_modclose(modhp);
259 return (0);
260 }
261
262 /*
263 * Flash firmware support
264 * Called unlocked.
265 */
266 int
pmcs_fw_flash(pmcs_hw_t * pwp,pmcs_fw_hdr_t * hdr,uint32_t length)267 pmcs_fw_flash(pmcs_hw_t *pwp, pmcs_fw_hdr_t *hdr, uint32_t length)
268 {
269 pmcs_fw_hdr_t *hp;
270 uint8_t *wrk, *base;
271
272 /*
273 * Step 1- Validate firmware chunks within passed pointer.
274 */
275 hp = hdr;
276 wrk = (uint8_t *)hdr;
277 base = wrk;
278 for (;;) {
279 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
280 "%s: partition 0x%x, Length 0x%x", __func__,
281 hp->destination_partition, ntohl(hp->firmware_length));
282 if (ntohl(hp->firmware_length) == 0) {
283 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
284 "%s: bad firmware length 0x%x",
285 __func__, ntohl(hp->firmware_length));
286 return (EINVAL);
287 }
288 wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
289 if (wrk == base + length) {
290 break;
291 }
292 if (wrk > base + length) {
293 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
294 "%s: out of bounds firmware length", __func__);
295 return (EINVAL);
296 }
297 hp = (void *)wrk;
298 }
299
300 /*
301 * Step 2- acquire scratch
302 */
303 (void) pmcs_acquire_scratch(pwp, B_TRUE);
304
305 /*
306 * Step 3- loop through firmware chunks and send each one
307 * down to be flashed.
308 */
309 hp = hdr;
310 wrk = (uint8_t *)hdr;
311 base = wrk;
312 for (;;) {
313 if (pmcs_flash_chunk(pwp, wrk)) {
314 pmcs_release_scratch(pwp);
315 return (EIO);
316 }
317 wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
318 if (wrk == base + length) {
319 break;
320 }
321 hp = (void *) wrk;
322 }
323 pmcs_release_scratch(pwp);
324 return (0);
325 }
326
327 static int
pmcs_flash_chunk(pmcs_hw_t * pwp,uint8_t * chunk)328 pmcs_flash_chunk(pmcs_hw_t *pwp, uint8_t *chunk)
329 {
330 pmcs_fw_hdr_t *hp;
331 pmcwork_t *pwrk;
332 uint32_t len, seg, off, result, amt, msg[PMCS_MSG_SIZE], *ptr;
333
334 hp = (void *)chunk;
335 len = sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length);
336
337 seg = off = 0;
338 while (off < len) {
339 amt = PMCS_SCRATCH_SIZE;
340 if (off + amt > len) {
341 amt = len - off;
342 }
343 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
344 "%s: segment %d offset %u length %u",
345 __func__, seg, off, amt);
346 (void) memcpy(pwp->scratch, &chunk[off], amt);
347 pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
348 if (pwrk == NULL) {
349 return (ENOMEM);
350 }
351 pwrk->arg = msg;
352 msg[0] = LE_32(PMCS_HIPRI(pwp,
353 PMCS_OQ_EVENTS, PMCIN_FW_FLASH_UPDATE));
354 msg[1] = LE_32(pwrk->htag);
355 msg[2] = LE_32(off);
356 msg[3] = LE_32(amt);
357 if (off == 0) {
358 msg[4] = LE_32(len);
359 } else {
360 msg[4] = 0;
361 }
362 msg[5] = 0;
363 msg[6] = 0;
364 msg[7] = 0;
365 msg[8] = 0;
366 msg[9] = 0;
367 msg[10] = 0;
368 msg[11] = 0;
369 msg[12] = LE_32(DWORD0(pwp->scratch_dma));
370 msg[13] = LE_32(DWORD1(pwp->scratch_dma));
371 msg[14] = LE_32(amt);
372 msg[15] = 0;
373 mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
374 ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
375 if (ptr == NULL) {
376 mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
377 pmcs_pwork(pwp, pwrk);
378 pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
379 pmcs_nomsg, __func__);
380 return (ENOMEM);
381 }
382 COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
383 (void) memset(msg, 0xaf, sizeof (msg));
384 pwrk->state = PMCS_WORK_STATE_ONCHIP;
385 INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
386 WAIT_FOR(pwrk, PMCS_FLASH_WAIT_TIME, result);
387 pmcs_pwork(pwp, pwrk);
388 if (result) {
389 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
390 pmcs_timeo, __func__);
391 return (EIO);
392 }
393 switch (LE_32(msg[2])) {
394 case FLASH_UPDATE_COMPLETE_PENDING_REBOOT:
395 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
396 "%s: segment %d complete pending reboot",
397 __func__, seg);
398 break;
399 case FLASH_UPDATE_IN_PROGRESS:
400 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
401 "%s: segment %d downloaded", __func__, seg);
402 break;
403 case FLASH_UPDATE_HDR_ERR:
404 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
405 "%s: segment %d header error", __func__, seg);
406 return (EIO);
407 case FLASH_UPDATE_OFFSET_ERR:
408 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
409 "%s: segment %d offset error", __func__, seg);
410 return (EIO);
411 case FLASH_UPDATE_UPDATE_CRC_ERR:
412 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
413 "%s: segment %d update crc error", __func__, seg);
414 return (EIO);
415 case FLASH_UPDATE_LENGTH_ERR:
416 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
417 "%s: segment %d length error", __func__, seg);
418 return (EIO);
419 case FLASH_UPDATE_HW_ERR:
420 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
421 "%s: segment %d hw error", __func__, seg);
422 return (EIO);
423 case FLASH_UPDATE_DNLD_NOT_SUPPORTED:
424 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
425 "%s: segment %d download not supported error",
426 __func__, seg);
427 return (EIO);
428 case FLASH_UPDATE_DISABLED:
429 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
430 "%s: segment %d update disabled error",
431 __func__, seg);
432 return (EIO);
433 default:
434 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
435 "%s: segment %d unknown error %x",
436 __func__, seg, msg[2]);
437 return (EIO);
438 }
439 off += amt;
440 seg++;
441 }
442 return (0);
443 }
444
445 /*
446 * pmcs_validate_vpd
447 *
448 * Input: softstate pointer and pointer to vpd data buffer
449 * Returns: B_TRUE if VPD data looks OK, B_FALSE otherwise
450 */
451 static boolean_t
pmcs_validate_vpd(pmcs_hw_t * pwp,uint8_t * data)452 pmcs_validate_vpd(pmcs_hw_t *pwp, uint8_t *data)
453 {
454 pmcs_vpd_header_t *vpd_header;
455 uint8_t *bufp, kv_len, *chksump, chksum = 0;
456 char tbuf[80];
457 char prop[24];
458 int idx, str_len;
459 uint16_t strid_length, chksum_len;
460 uint64_t wwid;
461 pmcs_vpd_kv_t *vkvp;
462
463 vpd_header = (pmcs_vpd_header_t *)data;
464
465 /*
466 * Make sure we understand the format of this data
467 */
468
469 /*
470 * Only VPD version 1 is VALID for Thebe-INT cards and
471 * Only VPD version 2 is valid for Thebe-EXT cards
472 */
473 if ((vpd_header->eeprom_version == PMCS_EEPROM_INT_VERSION &&
474 vpd_header->subsys_pid[0] == PMCS_EEPROM_INT_SSID_BYTE1 &&
475 vpd_header->subsys_pid[1] == PMCS_EEPROM_INT_SSID_BYTE2) ||
476 (vpd_header->eeprom_version == PMCS_EEPROM_EXT_VERSION &&
477 vpd_header->subsys_pid[0] == PMCS_EEPROM_EXT_SSID_BYTE1 &&
478 vpd_header->subsys_pid[1] == PMCS_EEPROM_EXT_SSID_BYTE2)) {
479 goto valid_version;
480 } else {
481 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
482 "%s: Detected Thebe card with SSID(%02x%02x)", __func__,
483 vpd_header->subsys_pid[0], vpd_header->subsys_pid[1]);
484 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
485 "%s: EEPROM(%d) unsupported; requires %d for INT(%02x%02x) "
486 " and %d for EXT(%02x%02x) cards.", __func__,
487 vpd_header->eeprom_version,
488 PMCS_EEPROM_INT_VERSION, PMCS_EEPROM_INT_SSID_BYTE1,
489 PMCS_EEPROM_INT_SSID_BYTE2, PMCS_EEPROM_EXT_VERSION,
490 PMCS_EEPROM_EXT_SSID_BYTE1, PMCS_EEPROM_EXT_SSID_BYTE2);
491 return (B_FALSE);
492 }
493
494 valid_version:
495 /*
496 * Do we have a valid SAS WWID?
497 */
498 if (((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4) != NAA_IEEE_REG) {
499 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
500 "%s: SAS WWN has invalid NAA (%d)", __func__,
501 ((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4));
502 return (B_FALSE);
503 }
504 wwid = pmcs_barray2wwn(vpd_header->hba_sas_wwid);
505 for (idx = 0; idx < PMCS_MAX_PORTS; idx++) {
506 pwp->sas_wwns[idx] = wwid + idx;
507 }
508
509 if (vpd_header->vpd_start_byte != PMCS_VPD_START) {
510 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
511 "%s: Didn't see VPD start byte", __func__);
512 return (B_FALSE);
513 }
514
515 /*
516 * We only checksum the VPD data between (and including) VPD Start byte
517 * and the checksum value byte. The length of this data for CRC is
518 * 15 less than the length indicated in vpd_length field of the header.
519 * 8 (SAS WWN) + 2 (subsystem ID) + 2 (subsystem vendor ID) +
520 * 1 (end tag) + 2 (hex byte CRC, different from this one) = 15 bytes
521 */
522 /*
523 * VPD length (little endian format) is represented as byte-array field
524 * & read the following way to avoid alignment issues (in SPARC)
525 */
526 chksum_len = ((vpd_header->vpd_length[1] << 8) |
527 (vpd_header->vpd_length[0])) - 15;
528 /* Validate VPD data checksum */
529 chksump = (uint8_t *)&vpd_header->vpd_start_byte;
530 ASSERT (*chksump == PMCS_VPD_START);
531 for (idx = 0; idx < chksum_len; idx++, chksump++) {
532 chksum += *chksump;
533 }
534 ASSERT (*chksump == PMCS_VPD_END);
535 if (chksum) {
536 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
537 "%s: VPD checksum failure", __func__);
538 return (B_FALSE);
539 }
540
541 /*
542 * Get length of string ID tag and read it.
543 */
544 bufp = (uint8_t *)&vpd_header->vpd_start_byte;
545 bufp += 3; /* Skip the start byte and length */
546 /*
547 * String ID tag length (little endian format) is represented as
548 * byte-array & read the following way to avoid alignment issues
549 * (in SPARC)
550 */
551 strid_length = (vpd_header->strid_length[1] << 8) |
552 (vpd_header->strid_length[0]);
553 if (strid_length > 79) {
554 strid_length = 79;
555 }
556 bcopy(bufp, tbuf, strid_length);
557 tbuf[strid_length] = 0;
558
559 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
560 "%s: Product Name: '%s'", __func__, tbuf);
561 pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_MODEL_NAME, tbuf);
562
563 /*
564 * Skip VPD-R tag and length of read-only tag, then start reading
565 * keyword/value pairs
566 */
567 bufp += strid_length; /* Skip to VPD-R tag */
568 bufp += 3; /* Skip VPD-R tag and length of VPD-R data */
569
570 vkvp = (pmcs_vpd_kv_t *)bufp;
571
572 while (vkvp->keyword[0] != PMCS_VPD_END) {
573 tbuf[0] = 0;
574 str_len = snprintf(tbuf, 80, "VPD: %c%c = <",
575 vkvp->keyword[0], vkvp->keyword[1]);
576
577 kv_len = vkvp->value_length;
578 for (idx = 0; idx < kv_len; idx++) {
579 tbuf[str_len + idx] = vkvp->value[idx];
580 prop[idx] = vkvp->value[idx];
581 }
582 prop[idx] = '\0';
583 str_len += kv_len;
584 tbuf[str_len] = '>';
585 tbuf[str_len + 1] = 0;
586 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s (Len: 0x%x)",
587 tbuf, kv_len);
588
589 /* Keyword is Manufacturer */
590 if ((vkvp->keyword[0] == 'M') && (vkvp->keyword[1] == 'N')) {
591 pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING,
592 PMCS_MANUFACTURER, prop);
593 }
594 /* Keyword is Serial Number */
595 if ((vkvp->keyword[0] == 'S') && (vkvp->keyword[1] == 'N')) {
596 pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING,
597 PMCS_SERIAL_NUMBER, prop);
598 }
599
600 vkvp = (pmcs_vpd_kv_t *)(bufp + 3 + kv_len);
601 bufp += kv_len + 3;
602 }
603
604 return (B_TRUE);
605 }
606
607 /*
608 * pmcs_get_nvmd
609 *
610 * This function will read the requested data from the non-volatile
611 * storage on the card. This could mean SEEPROM, VPD, or other areas
612 * as defined by the PM8001 programmer's manual.
613 *
614 * nvmd_type: The data type being requested
615 * nvmd: NVM device to access (IOP/AAP1)
616 * offset: Must be 4K alignment
617 * buf: Pointer to memory region for retrieved data
618 * size_left: Total available bytes left in buf
619 *
620 * Returns: non-negative on success, -1 on failure
621 */
622
623 /*ARGSUSED*/
624 int
pmcs_get_nvmd(pmcs_hw_t * pwp,pmcs_nvmd_type_t nvmd_type,uint8_t nvmd,uint32_t offset,char * buf,uint32_t size_left)625 pmcs_get_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t nvmd,
626 uint32_t offset, char *buf, uint32_t size_left)
627 {
628 pmcs_get_nvmd_cmd_t iomb;
629 pmcwork_t *workp;
630 uint8_t *chunkp;
631 uint32_t *ptr, ibq, *iombp;
632 uint32_t dlen;
633 uint16_t status;
634 uint8_t tdas_nvmd, ip, tda, tbn_tdps;
635 uint8_t doa[3];
636 int32_t result = -1, i = 0;
637
638 switch (nvmd_type) {
639 case PMCS_NVMD_VPD:
640 tdas_nvmd = PMCIN_NVMD_TDPS_1 | PMCIN_NVMD_TWI;
641 tda = PMCIN_TDA_PAGE(2);
642 tbn_tdps = PMCIN_NVMD_TBN(0) | PMCIN_NVMD_TDPS_8;
643 ip = PMCIN_NVMD_INDIRECT_PLD;
644 dlen = LE_32(PMCS_SEEPROM_PAGE_SIZE);
645 doa[0] = 0;
646 doa[1] = 0;
647 doa[2] = 0;
648 break;
649 case PMCS_NVMD_REG_DUMP:
650 tdas_nvmd = nvmd;
651 tda = 0;
652 tbn_tdps = 0;
653 ip = PMCIN_NVMD_INDIRECT_PLD;
654 dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE);
655 doa[0] = offset & 0xff;
656 doa[1] = (offset >> 8) & 0xff;
657 doa[2] = (offset >> 16) & 0xff;
658 break;
659 case PMCS_NVMD_EVENT_LOG:
660 tdas_nvmd = nvmd;
661 tda = 0;
662 tbn_tdps = 0;
663 ip = PMCIN_NVMD_INDIRECT_PLD;
664 dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE);
665 offset = offset + PMCS_NVMD_EVENT_LOG_OFFSET;
666 doa[0] = offset & 0xff;
667 doa[1] = (offset >> 8) & 0xff;
668 doa[2] = (offset >> 16) & 0xff;
669 break;
670 default:
671 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
672 "%s: Invalid nvmd type: %d", __func__, nvmd_type);
673 return (-1);
674 }
675
676 workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
677 if (workp == NULL) {
678 pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
679 "%s: Unable to get work struct", __func__);
680 return (-1);
681 }
682
683 ptr = &iomb.header;
684 bzero(ptr, sizeof (pmcs_get_nvmd_cmd_t));
685 *ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_GET_NVMD_DATA));
686 workp->arg = (void *)&iomb;
687 iomb.htag = LE_32(workp->htag);
688 iomb.ip = ip;
689 iomb.tbn_tdps = tbn_tdps;
690 iomb.tda = tda;
691 iomb.tdas_nvmd = tdas_nvmd;
692 iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr));
693 iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr));
694 iomb.ipdl = dlen;
695 iomb.doa[0] = doa[0];
696 iomb.doa[1] = doa[1];
697 iomb.doa[2] = doa[2];
698
699 /*
700 * ptr will now point to the inbound queue message
701 */
702 GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq);
703 if (ptr == NULL) {
704 pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
705 "!%s: Unable to get IQ entry", __func__);
706 pmcs_pwork(pwp, workp);
707 return (-1);
708 }
709
710 bzero(ptr, PMCS_MSG_SIZE << 2); /* PMCS_MSG_SIZE is in dwords */
711 iombp = (uint32_t *)&iomb;
712 COPY_MESSAGE(ptr, iombp, sizeof (pmcs_get_nvmd_cmd_t) >> 2);
713 workp->state = PMCS_WORK_STATE_ONCHIP;
714 INC_IQ_ENTRY(pwp, ibq);
715
716 WAIT_FOR(workp, 1000, result);
717 ptr = workp->arg;
718 if (result) {
719 pmcs_timed_out(pwp, workp->htag, __func__);
720 pmcs_pwork(pwp, workp);
721 return (-1);
722 }
723 status = LE_32(*(ptr + 3)) & 0xffff;
724 if (status != PMCS_NVMD_STAT_SUCCESS) {
725 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
726 "%s: Error, status = 0x%04x", __func__, status);
727 pmcs_pwork(pwp, workp);
728 return (-1);
729 }
730
731 pmcs_pwork(pwp, workp);
732
733 if (ddi_dma_sync(pwp->cip_handles, 0, 0,
734 DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS) {
735 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
736 "Condition check failed at %s():%d", __func__, __LINE__);
737 }
738 chunkp = (uint8_t *)pwp->flash_chunkp;
739
740 switch (nvmd) {
741 case PMCIN_NVMD_VPD:
742 if (pmcs_validate_vpd(pwp, chunkp)) {
743 result = 0;
744 } else {
745 result = -1;
746 }
747 break;
748 case PMCIN_NVMD_AAP1:
749 case PMCIN_NVMD_IOP:
750 ASSERT(buf);
751 i = 0;
752 if (nvmd_type == PMCS_NVMD_REG_DUMP) {
753 while ((i < PMCS_FLASH_CHUNK_SIZE) &&
754 (chunkp[i] != 0xff) && (chunkp[i] != '\0')) {
755 (void) snprintf(&buf[i], (size_left - i),
756 "%c", chunkp[i]);
757 i++;
758 }
759 } else if (nvmd_type == PMCS_NVMD_EVENT_LOG) {
760 i = pmcs_dump_binary(pwp, pwp->flash_chunkp, 0,
761 (PMCS_FLASH_CHUNK_SIZE >> 2), buf, size_left);
762 }
763 result = i;
764 break;
765 default:
766 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
767 "UNKNOWN NVMD DEVICE");
768 return (-1);
769 }
770
771 return (result);
772 }
773
774 /*
775 * pmcs_set_nvmd
776 *
777 * This function will write the requested data to non-volatile storage
778 * on the HBA. This could mean SEEPROM, VPD, or other areas as defined by
779 * the PM8001 programmer's manual.
780 *
781 * nvmd_type: The data type to be written
782 * buf: Pointer to memory region for data to write
783 * len: Length of the data buffer
784 *
785 * Returns: B_TRUE on success, B_FALSE on failure
786 */
787
788 boolean_t
pmcs_set_nvmd(pmcs_hw_t * pwp,pmcs_nvmd_type_t nvmd_type,uint8_t * buf,size_t len)789 pmcs_set_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t *buf,
790 size_t len)
791 {
792 pmcs_set_nvmd_cmd_t iomb;
793 pmcwork_t *workp;
794 uint32_t *ptr, ibq, *iombp;
795 uint32_t dlen;
796 uint16_t status;
797 uint8_t tdas_nvmd, ip;
798 int result;
799
800 switch (nvmd_type) {
801 case PMCS_NVMD_SPCBOOT:
802 tdas_nvmd = PMCIN_NVMD_SEEPROM;
803 ip = PMCIN_NVMD_INDIRECT_PLD;
804 ASSERT((len >= PMCS_SPCBOOT_MIN_SIZE) &&
805 (len <= PMCS_SPCBOOT_MAX_SIZE));
806 dlen = LE_32(len);
807 break;
808 default:
809 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
810 "%s: Invalid nvmd type: %d", __func__, nvmd_type);
811 return (B_FALSE);
812 }
813
814 pmcs_prt(pwp, PMCS_PRT_DEBUG_DEVEL, NULL, NULL,
815 "%s: Request for nvmd type: %d", __func__, nvmd_type);
816
817 workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
818 if (workp == NULL) {
819 pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
820 "%s: Unable to get work struct", __func__);
821 return (B_FALSE);
822 }
823
824 ptr = &iomb.header;
825 bzero(ptr, sizeof (pmcs_set_nvmd_cmd_t));
826 *ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_SET_NVMD_DATA));
827 workp->arg = (void *)&iomb;
828 iomb.htag = LE_32(workp->htag);
829 iomb.ip = ip;
830 iomb.tdas_nvmd = tdas_nvmd;
831 iomb.signature = LE_32(PMCS_SEEPROM_SIGNATURE);
832 iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr));
833 iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr));
834 iomb.ipdl = dlen;
835
836 pmcs_print_entry(pwp, PMCS_PRT_DEBUG_DEVEL,
837 "PMCIN_SET_NVMD_DATA iomb", (void *)&iomb);
838
839 bcopy(buf, pwp->flash_chunkp, len);
840 if (ddi_dma_sync(pwp->cip_handles, 0, 0,
841 DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {
842 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
843 "Condition check failed at %s():%d", __func__, __LINE__);
844 }
845
846 /*
847 * ptr will now point to the inbound queue message
848 */
849 GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq);
850 if (ptr == NULL) {
851 pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
852 "!%s: Unable to get IQ entry", __func__);
853 pmcs_pwork(pwp, workp);
854 return (B_FALSE);
855 }
856
857 bzero(ptr, PMCS_MSG_SIZE << 2); /* PMCS_MSG_SIZE is in dwords */
858 iombp = (uint32_t *)&iomb;
859 COPY_MESSAGE(ptr, iombp, sizeof (pmcs_set_nvmd_cmd_t) >> 2);
860 workp->state = PMCS_WORK_STATE_ONCHIP;
861 INC_IQ_ENTRY(pwp, ibq);
862
863 WAIT_FOR(workp, 2000, result);
864
865 if (result) {
866 pmcs_timed_out(pwp, workp->htag, __func__);
867 pmcs_pwork(pwp, workp);
868 return (B_FALSE);
869 }
870
871 pmcs_pwork(pwp, workp);
872
873 status = LE_32(*(ptr + 3)) & 0xffff;
874 if (status != PMCS_NVMD_STAT_SUCCESS) {
875 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
876 "%s: Error, status = 0x%04x", __func__, status);
877 return (B_FALSE);
878 }
879
880 return (B_TRUE);
881 }
882