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