xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_nvram.c (revision 2dd5848fa9da42f374782814f362e0afda124ecd)
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