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