1d65b419eSXinChen /*
2d65b419eSXinChen  * CDDL HEADER START
3d65b419eSXinChen  *
4d65b419eSXinChen  * The contents of this file are subject to the terms of the
5d65b419eSXinChen  * Common Development and Distribution License (the "License").
6d65b419eSXinChen  * You may not use this file except in compliance with the License.
7d65b419eSXinChen  *
8d65b419eSXinChen  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d65b419eSXinChen  * or http://www.opensolaris.org/os/licensing.
10d65b419eSXinChen  * See the License for the specific language governing permissions
11d65b419eSXinChen  * and limitations under the License.
12d65b419eSXinChen  *
13d65b419eSXinChen  * When distributing Covered Code, include this CDDL HEADER in each
14d65b419eSXinChen  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d65b419eSXinChen  * If applicable, add the following below this CDDL HEADER, with the
16d65b419eSXinChen  * fields enclosed by brackets "[]" replaced with your own identifying
17d65b419eSXinChen  * information: Portions Copyright [yyyy] [name of copyright owner]
18d65b419eSXinChen  *
19d65b419eSXinChen  * CDDL HEADER END
20d65b419eSXinChen  */
21*0b4d6575SRobert Mustacchi 
22d65b419eSXinChen /*
23d65b419eSXinChen  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24d65b419eSXinChen  * Use is subject to license terms.
25*0b4d6575SRobert Mustacchi  *
26*0b4d6575SRobert Mustacchi  * Copyright 2016 Joyent, Inc.
27d65b419eSXinChen  */
28d65b419eSXinChen 
29d65b419eSXinChen /*
30d65b419eSXinChen  * sd / ssd (SCSI Direct-attached Device) specific functions.
31d65b419eSXinChen  */
32*0b4d6575SRobert Mustacchi 
33d65b419eSXinChen #include <libnvpair.h>
34d65b419eSXinChen #include <stdio.h>
35d65b419eSXinChen #include <stdlib.h>
36d65b419eSXinChen #include <unistd.h>
37d65b419eSXinChen #include <sys/types.h>
38d65b419eSXinChen #include <sys/sysmacros.h>
39d65b419eSXinChen #include <sys/queue.h>
40*0b4d6575SRobert Mustacchi #include <sys/stat.h>
41d65b419eSXinChen #include <fcntl.h>
42d65b419eSXinChen #include <string.h>
43d65b419eSXinChen #include <errno.h>
44d65b419eSXinChen #include <scsi/libscsi.h>
45*0b4d6575SRobert Mustacchi #include <sys/scsi/scsi_types.h>
46d65b419eSXinChen #include <libintl.h> /* for gettext(3c) */
47d65b419eSXinChen #include <fwflash/fwflash.h>
48*0b4d6575SRobert Mustacchi #include <sys/debug.h>
49*0b4d6575SRobert Mustacchi #include <umem.h>
50d65b419eSXinChen 
51d65b419eSXinChen typedef struct sam4_statdesc {
52*0b4d6575SRobert Mustacchi 	int sam_status;
53*0b4d6575SRobert Mustacchi 	char *sam_message;
54d65b419eSXinChen } sam4_statdesc_t;
55d65b419eSXinChen 
56d65b419eSXinChen static sam4_statdesc_t sam4_status[] = {
57d65b419eSXinChen 	{ SAM4_STATUS_GOOD, "Status: GOOD (success)" },
58d65b419eSXinChen 	{ SAM4_STATUS_CHECK_CONDITION, "Status: CHECK CONDITION" },
59d65b419eSXinChen 	{ SAM4_STATUS_CONDITION_MET, "Status: CONDITION MET" },
60d65b419eSXinChen 	{ SAM4_STATUS_BUSY, "Status: Device is BUSY" },
61d65b419eSXinChen 	{ SAM4_STATUS_RESERVATION_CONFLICT, "Status: Device is RESERVED" },
62d65b419eSXinChen 	{ SAM4_STATUS_TASK_SET_FULL,
63d65b419eSXinChen 	    "Status: TASK SET FULL (insufficient resources in command queue" },
64*0b4d6575SRobert Mustacchi 	{ SAM4_STATUS_TASK_ABORTED, "Status: TASK ABORTED" }
65d65b419eSXinChen };
66d65b419eSXinChen 
67*0b4d6575SRobert Mustacchi #define	NSAM4_STATUS	(sizeof (sam4_status) / sizeof (sam4_status[0]))
68d65b419eSXinChen 
69d65b419eSXinChen #define	FW_SD_FREE_DEVPATH(devpath)	{	\
70d65b419eSXinChen 		di_devfs_path_free((devpath));	\
71d65b419eSXinChen 	}
72d65b419eSXinChen #define	FW_SD_FREE_DEVICELIST(thisdev, devpath) {	\
73d65b419eSXinChen 		free((thisdev));	\
74d65b419eSXinChen 		FW_SD_FREE_DEVPATH((devpath))	\
75d65b419eSXinChen 	}
76d65b419eSXinChen #define	FW_SD_FREE_DRV_NAME(thisdev, devpath) {	\
77d65b419eSXinChen 		free((thisdev)->drvname);	\
784b3dc1e2SXinChen 		FW_SD_FREE_DEVICELIST((thisdev), (devpath))	\
79d65b419eSXinChen 	}
80d65b419eSXinChen #define	FW_SD_FREE_CLS_NAME(thisdev, devpath) {	\
81d65b419eSXinChen 		free((thisdev)->classname);	\
82d65b419eSXinChen 		FW_SD_FREE_DRV_NAME((thisdev), (devpath))	\
83d65b419eSXinChen 	}
844b3dc1e2SXinChen #define	FW_SD_FREE_ACC_NAME(thisdev, devpath) {	\
854b3dc1e2SXinChen 		free((thisdev)->access_devname);	\
864b3dc1e2SXinChen 		FW_SD_FREE_CLS_NAME(thisdev, devpath)	\
874b3dc1e2SXinChen 	}
884b3dc1e2SXinChen #define	FW_SD_FREE_ADDR(thisdev, devpath) {	\
894b3dc1e2SXinChen 		free((thisdev)->addresses[0]);	\
904b3dc1e2SXinChen 		FW_SD_FREE_ACC_NAME(thisdev, devpath)	\
914b3dc1e2SXinChen 	}
92d65b419eSXinChen #define	FW_SD_FREE_IDENT(thisdev, devpath) {	\
93d65b419eSXinChen 		free((thisdev)->ident);	\
944b3dc1e2SXinChen 		FW_SD_FREE_ADDR((thisdev), (devpath))	\
95d65b419eSXinChen 	}
96d65b419eSXinChen #define	FW_SD_FREE_IDENT_VID(thisdev, devpath) {	\
97d65b419eSXinChen 		free((thisdev)->ident->vid);	\
98d65b419eSXinChen 		FW_SD_FREE_IDENT((thisdev), (devpath))	\
99d65b419eSXinChen 	}
100d65b419eSXinChen #define	FW_SD_FREE_IDENT_PID(thisdev, devpath) {	\
101d65b419eSXinChen 		free((thisdev)->ident->pid);	\
102d65b419eSXinChen 		FW_SD_FREE_IDENT_VID((thisdev), (devpath))	\
103d65b419eSXinChen 	}
104d65b419eSXinChen #define	FW_SD_FREE_IDENT_ALL(thisdev, devpath) {	\
105d65b419eSXinChen 		free((thisdev)->ident->revid);	\
106d65b419eSXinChen 		FW_SD_FREE_IDENT_PID((thisdev), (devpath))	\
107d65b419eSXinChen 	}
108d65b419eSXinChen 
109*0b4d6575SRobert Mustacchi /*
110*0b4d6575SRobert Mustacchi  * This is our default partial write size when we encounter a situation where we
111*0b4d6575SRobert Mustacchi  * need to upgrade disks whose firmware image cannot be done in a single write.
112*0b4d6575SRobert Mustacchi  * While in theory we should just use the maximum transfer size and make sure
113*0b4d6575SRobert Mustacchi  * it's aligned, that's proven to be problematic for some Seagate disks. Hence
114*0b4d6575SRobert Mustacchi  * we just make sure that if partial writes are required that this value fits in
115*0b4d6575SRobert Mustacchi  * the required alignment and in the actual maximum transfer size.
116*0b4d6575SRobert Mustacchi  */
117*0b4d6575SRobert Mustacchi #define	FW_SD_PARTIAL_WRITE_SIZE	(64 * 1024)
118*0b4d6575SRobert Mustacchi 
119*0b4d6575SRobert Mustacchi /*
120*0b4d6575SRobert Mustacchi  * Declarations required for fwflash
121*0b4d6575SRobert Mustacchi  */
122d65b419eSXinChen char drivername[] = "sd\0";
123d65b419eSXinChen int plugin_version = FWPLUGIN_VERSION_2;
124d65b419eSXinChen 
125*0b4d6575SRobert Mustacchi /*
126*0b4d6575SRobert Mustacchi  * Data provided by fwflash
127*0b4d6575SRobert Mustacchi  */
128d65b419eSXinChen extern di_node_t rootnode;
129d65b419eSXinChen extern struct fw_plugin *self;
130d65b419eSXinChen extern struct vrfyplugin *verifier;
131d65b419eSXinChen extern int fwflash_debug;
132d65b419eSXinChen 
133*0b4d6575SRobert Mustacchi static char *sdfw_devprefix = "/devices";
134d65b419eSXinChen 
135*0b4d6575SRobert Mustacchi static char *sdfw_find_link(di_node_t bnode, char *acc_devname);
136*0b4d6575SRobert Mustacchi static int sdfw_link_cb(di_devlink_t devlink, void *arg);
137*0b4d6575SRobert Mustacchi static int sdfw_idtfy_custmz(struct devicelist *device, char *sp);
138d65b419eSXinChen 
139d65b419eSXinChen /*
140d65b419eSXinChen  * We don't currently support reading firmware from a disk. If we do eventually
141d65b419eSXinChen  * support it, we would use the scsi READ BUFFER command to do so.
142d65b419eSXinChen  */
143d65b419eSXinChen int
fw_readfw(struct devicelist * flashdev,char * filename)144d65b419eSXinChen fw_readfw(struct devicelist *flashdev, char *filename)
145d65b419eSXinChen {
146d65b419eSXinChen 
147d65b419eSXinChen 	logmsg(MSG_INFO,
148d65b419eSXinChen 	    "%s: not writing firmware for device %s to file %s\n",
149d65b419eSXinChen 	    flashdev->drvname, flashdev->access_devname, filename);
150d65b419eSXinChen 	logmsg(MSG_ERROR,
151d65b419eSXinChen 	    gettext("\n\nReading of firmware images from %s-attached "
152d65b419eSXinChen 	    "devices is not supported\n\n"),
153d65b419eSXinChen 	    flashdev->drvname);
154d65b419eSXinChen 
155d65b419eSXinChen 	return (FWFLASH_SUCCESS);
156d65b419eSXinChen }
157d65b419eSXinChen 
158*0b4d6575SRobert Mustacchi 
159*0b4d6575SRobert Mustacchi static int
sdfw_read_descriptor(struct devicelist * flashdev,libscsi_hdl_t * hdl,libscsi_target_t * targ,uint8_t * align)160*0b4d6575SRobert Mustacchi sdfw_read_descriptor(struct devicelist *flashdev, libscsi_hdl_t *hdl,
161*0b4d6575SRobert Mustacchi     libscsi_target_t *targ, uint8_t *align)
162*0b4d6575SRobert Mustacchi {
163*0b4d6575SRobert Mustacchi 	spc3_read_buffer_cdb_t *rb_cdb;
164*0b4d6575SRobert Mustacchi 	size_t nwritten;
165*0b4d6575SRobert Mustacchi 	libscsi_action_t *action = NULL;
166*0b4d6575SRobert Mustacchi 	uint8_t descbuf[4];
167*0b4d6575SRobert Mustacchi 	sam4_status_t samstatus;
168*0b4d6575SRobert Mustacchi 
169*0b4d6575SRobert Mustacchi 	VERIFY3P(hdl, !=, NULL);
170*0b4d6575SRobert Mustacchi 	VERIFY3P(targ, !=, NULL);
171*0b4d6575SRobert Mustacchi 	VERIFY3P(align, !=, NULL);
172*0b4d6575SRobert Mustacchi 
173*0b4d6575SRobert Mustacchi 	if ((action = libscsi_action_alloc(hdl, SPC3_CMD_READ_BUFFER,
174*0b4d6575SRobert Mustacchi 	    LIBSCSI_AF_READ, descbuf, sizeof (descbuf))) == NULL) {
175*0b4d6575SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to alloc scsi action: "
176*0b4d6575SRobert Mustacchi 		    "%s\n"),
177*0b4d6575SRobert Mustacchi 		    flashdev->drvname, libscsi_errmsg(hdl));
178*0b4d6575SRobert Mustacchi 		return (FWFLASH_FAILURE);
179*0b4d6575SRobert Mustacchi 	}
180*0b4d6575SRobert Mustacchi 
181*0b4d6575SRobert Mustacchi 	rb_cdb = (spc3_read_buffer_cdb_t *)libscsi_action_get_cdb(action);
182*0b4d6575SRobert Mustacchi 
183*0b4d6575SRobert Mustacchi 	rb_cdb->rbc_mode = SPC3_RB_MODE_DESCRIPTOR;
184*0b4d6575SRobert Mustacchi 
185*0b4d6575SRobert Mustacchi 	/*
186*0b4d6575SRobert Mustacchi 	 * Microcode upgrade usually only uses the first buffer ID which is
187*0b4d6575SRobert Mustacchi 	 * sequentially indexed from zero. Strictly speaking these are all
188*0b4d6575SRobert Mustacchi 	 * vendor defined, but so far most vendors we've seen use index zero
189*0b4d6575SRobert Mustacchi 	 * for this.
190*0b4d6575SRobert Mustacchi 	 */
191*0b4d6575SRobert Mustacchi 	rb_cdb->rbc_bufferid = 0;
192*0b4d6575SRobert Mustacchi 
193*0b4d6575SRobert Mustacchi 	rb_cdb->rbc_allocation_len[0] = 0;
194*0b4d6575SRobert Mustacchi 	rb_cdb->rbc_allocation_len[1] = 0;
195*0b4d6575SRobert Mustacchi 	rb_cdb->rbc_allocation_len[2] = sizeof (descbuf);
196*0b4d6575SRobert Mustacchi 
197*0b4d6575SRobert Mustacchi 	if (libscsi_exec(action, targ) != 0) {
198*0b4d6575SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to execute SCSI buffer "
199*0b4d6575SRobert Mustacchi 		    "data read: %s\n"),
200*0b4d6575SRobert Mustacchi 		    flashdev->drvname, libscsi_errmsg(hdl));
201*0b4d6575SRobert Mustacchi 		libscsi_action_free(action);
202*0b4d6575SRobert Mustacchi 		return (FWFLASH_FAILURE);
203*0b4d6575SRobert Mustacchi 	}
204*0b4d6575SRobert Mustacchi 
205*0b4d6575SRobert Mustacchi 	if ((samstatus = libscsi_action_get_status(action)) !=
206*0b4d6575SRobert Mustacchi 	    SAM4_STATUS_GOOD) {
207*0b4d6575SRobert Mustacchi 		int i;
208*0b4d6575SRobert Mustacchi 		for (i = 0; i < NSAM4_STATUS; i++) {
209*0b4d6575SRobert Mustacchi 			if (samstatus == sam4_status[i].sam_status) {
210*0b4d6575SRobert Mustacchi 				logmsg(MSG_ERROR, gettext("%s: SCSI buffer "
211*0b4d6575SRobert Mustacchi 				    "data read failed: %s\n"),
212*0b4d6575SRobert Mustacchi 				    flashdev->drvname,
213*0b4d6575SRobert Mustacchi 				    sam4_status[i].sam_message);
214*0b4d6575SRobert Mustacchi 				libscsi_action_free(action);
215*0b4d6575SRobert Mustacchi 				return (FWFLASH_FAILURE);
216*0b4d6575SRobert Mustacchi 			}
217*0b4d6575SRobert Mustacchi 		}
218*0b4d6575SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: SCSI buffer data read failed: "
219*0b4d6575SRobert Mustacchi 		    "unknown error: %d\n"), flashdev->drvname, samstatus);
220*0b4d6575SRobert Mustacchi 		libscsi_action_free(action);
221*0b4d6575SRobert Mustacchi 		return (FWFLASH_FAILURE);
222*0b4d6575SRobert Mustacchi 	}
223*0b4d6575SRobert Mustacchi 
224*0b4d6575SRobert Mustacchi 	if (libscsi_action_get_buffer(action, NULL, NULL, &nwritten) != 0) {
225*0b4d6575SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to get actual data "
226*0b4d6575SRobert Mustacchi 		    "size: %s\n"),
227*0b4d6575SRobert Mustacchi 		    flashdev->drvname, libscsi_errmsg(hdl));
228*0b4d6575SRobert Mustacchi 		libscsi_action_free(action);
229*0b4d6575SRobert Mustacchi 		return (FWFLASH_FAILURE);
230*0b4d6575SRobert Mustacchi 	}
231*0b4d6575SRobert Mustacchi 	libscsi_action_free(action);
232*0b4d6575SRobert Mustacchi 
233*0b4d6575SRobert Mustacchi 	if (nwritten != sizeof (descbuf)) {
234*0b4d6575SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: received a short read from the "
235*0b4d6575SRobert Mustacchi 		    "SCSI READ BUFFER command, expected %u bytes, read %zu\n"),
236*0b4d6575SRobert Mustacchi 		    flashdev->drvname, sizeof (descbuf), nwritten);
237*0b4d6575SRobert Mustacchi 		return (FWFLASH_FAILURE);
238*0b4d6575SRobert Mustacchi 	}
239*0b4d6575SRobert Mustacchi 
240*0b4d6575SRobert Mustacchi 	if (descbuf[0] == 0 && descbuf[1] == 0 && descbuf[2] == 0 &&
241*0b4d6575SRobert Mustacchi 	    descbuf[3] == 0) {
242*0b4d6575SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: devices %s does not support "
243*0b4d6575SRobert Mustacchi 		    "firmware upgrade\n"), verifier->vendor,
244*0b4d6575SRobert Mustacchi 		    flashdev->access_devname);
245*0b4d6575SRobert Mustacchi 		return (FWFLASH_FAILURE);
246*0b4d6575SRobert Mustacchi 	}
247*0b4d6575SRobert Mustacchi 
248*0b4d6575SRobert Mustacchi 	*align = descbuf[0];
249*0b4d6575SRobert Mustacchi 
250*0b4d6575SRobert Mustacchi 	return (FWFLASH_SUCCESS);
251*0b4d6575SRobert Mustacchi }
252*0b4d6575SRobert Mustacchi 
253*0b4d6575SRobert Mustacchi static int
sdfw_write(struct devicelist * flashdev,libscsi_hdl_t * handle,libscsi_target_t * target,size_t len,size_t off,void * buf)254*0b4d6575SRobert Mustacchi sdfw_write(struct devicelist *flashdev, libscsi_hdl_t *handle,
255*0b4d6575SRobert Mustacchi     libscsi_target_t *target, size_t len, size_t off, void *buf)
256*0b4d6575SRobert Mustacchi {
257*0b4d6575SRobert Mustacchi 	sam4_status_t samstatus;
258*0b4d6575SRobert Mustacchi 	libscsi_action_t *action = NULL;
259*0b4d6575SRobert Mustacchi 	spc3_write_buffer_cdb_t *wb_cdb;
260*0b4d6575SRobert Mustacchi 
261*0b4d6575SRobert Mustacchi 	logmsg(MSG_INFO, "%s: writing %u bytes of image %s at offset %zu from "
262*0b4d6575SRobert Mustacchi 	    "address %p\n", flashdev->drvname, len, verifier->imgfile, off,
263*0b4d6575SRobert Mustacchi 	    buf);
264*0b4d6575SRobert Mustacchi 	logmsg(MSG_INFO, "%s: writing to buffer id %u\n",
265*0b4d6575SRobert Mustacchi 	    flashdev->drvname, verifier->flashbuf);
266*0b4d6575SRobert Mustacchi 
267*0b4d6575SRobert Mustacchi 	VERIFY3P(flashdev, !=, NULL);
268*0b4d6575SRobert Mustacchi 	VERIFY3P(handle, !=, NULL);
269*0b4d6575SRobert Mustacchi 	VERIFY3P(target, !=, NULL);
270*0b4d6575SRobert Mustacchi 	VERIFY3P(buf, !=, NULL);
271*0b4d6575SRobert Mustacchi 	VERIFY3U(len, >, 0);
272*0b4d6575SRobert Mustacchi 	VERIFY3U(off + len, <=, verifier->imgsize);
273*0b4d6575SRobert Mustacchi 
274*0b4d6575SRobert Mustacchi 	action = libscsi_action_alloc(handle, SPC3_CMD_WRITE_BUFFER,
275*0b4d6575SRobert Mustacchi 	    LIBSCSI_AF_WRITE | LIBSCSI_AF_RQSENSE | LIBSCSI_AF_ISOLATE, buf,
276*0b4d6575SRobert Mustacchi 	    len);
277*0b4d6575SRobert Mustacchi 	if (action == NULL) {
278*0b4d6575SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to alloc scsi action: "
279*0b4d6575SRobert Mustacchi 		    "%s\n"), flashdev->drvname, libscsi_errmsg(handle));
280*0b4d6575SRobert Mustacchi 		goto err;
281*0b4d6575SRobert Mustacchi 	}
282*0b4d6575SRobert Mustacchi 
283*0b4d6575SRobert Mustacchi 	wb_cdb = (spc3_write_buffer_cdb_t *)libscsi_action_get_cdb(action);
284*0b4d6575SRobert Mustacchi 
285*0b4d6575SRobert Mustacchi 	wb_cdb->wbc_mode = SPC3_WB_MODE_DL_UCODE_OFFS_SAVE;
286*0b4d6575SRobert Mustacchi 
287*0b4d6575SRobert Mustacchi 	wb_cdb->wbc_buffer_offset[0] = (off >> 16) & 0xff;
288*0b4d6575SRobert Mustacchi 	wb_cdb->wbc_buffer_offset[1] = (off >> 8) & 0xff;
289*0b4d6575SRobert Mustacchi 	wb_cdb->wbc_buffer_offset[2] = off & 0xff;
290*0b4d6575SRobert Mustacchi 
291*0b4d6575SRobert Mustacchi 	wb_cdb->wbc_bufferid = verifier->flashbuf;
292*0b4d6575SRobert Mustacchi 
293*0b4d6575SRobert Mustacchi 	wb_cdb->wbc_parameter_list_len[0] = (len >> 16) & 0xff;
294*0b4d6575SRobert Mustacchi 	wb_cdb->wbc_parameter_list_len[1] = (len >> 8) & 0xff;
295*0b4d6575SRobert Mustacchi 	wb_cdb->wbc_parameter_list_len[2] = len & 0xff;
296*0b4d6575SRobert Mustacchi 
297*0b4d6575SRobert Mustacchi 	logmsg(MSG_INFO, "%s: spc3_write_buffer_cdb_t opcode: %u\n",
298*0b4d6575SRobert Mustacchi 	    flashdev->drvname, wb_cdb->wbc_opcode);
299*0b4d6575SRobert Mustacchi 
300*0b4d6575SRobert Mustacchi 	if (libscsi_exec(action, target) != 0) {
301*0b4d6575SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to execute SCSI WRITE "
302*0b4d6575SRobert Mustacchi 		    "BUFFER: %s\n"),
303*0b4d6575SRobert Mustacchi 		    flashdev->drvname, libscsi_errmsg(handle));
304*0b4d6575SRobert Mustacchi 		goto err;
305*0b4d6575SRobert Mustacchi 	}
306*0b4d6575SRobert Mustacchi 
307*0b4d6575SRobert Mustacchi 	if ((samstatus = libscsi_action_get_status(action)) ==
308*0b4d6575SRobert Mustacchi 	    SAM4_STATUS_CHECK_CONDITION) {
309*0b4d6575SRobert Mustacchi 		uint64_t asc = 0, ascq = 0, key = 0;
310*0b4d6575SRobert Mustacchi 		const char *code, *keystr;
311*0b4d6575SRobert Mustacchi 
312*0b4d6575SRobert Mustacchi 		if (libscsi_action_parse_sense(action, &key, &asc, &ascq,
313*0b4d6575SRobert Mustacchi 		    NULL) != 0) {
314*0b4d6575SRobert Mustacchi 			logmsg(MSG_ERROR, gettext("%s: failed to write "
315*0b4d6575SRobert Mustacchi 			    "firmware. Received CHECK_CONDITION that cannot be "
316*0b4d6575SRobert Mustacchi 			    "parsed.\n"),
317*0b4d6575SRobert Mustacchi 			    flashdev->drvname);
318*0b4d6575SRobert Mustacchi 			goto err;
319*0b4d6575SRobert Mustacchi 		}
320*0b4d6575SRobert Mustacchi 
321*0b4d6575SRobert Mustacchi 		code = libscsi_sense_code_name(asc, ascq);
322*0b4d6575SRobert Mustacchi 		keystr = libscsi_sense_key_name(key);
323*0b4d6575SRobert Mustacchi 
324*0b4d6575SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to write firmware: "
325*0b4d6575SRobert Mustacchi 		    "received sense key %" PRIu64 " (%s) additional sense code "
326*0b4d6575SRobert Mustacchi 		    "0x%" PRIx64 "/0x%" PRIx64 " (%s)\n"), flashdev->drvname,
327*0b4d6575SRobert Mustacchi 		    key, keystr != NULL ? keystr : "<unknown>",
328*0b4d6575SRobert Mustacchi 		    asc, ascq, code != NULL ? code : "<unknown>");
329*0b4d6575SRobert Mustacchi 		goto err;
330*0b4d6575SRobert Mustacchi 	} else if (samstatus != SAM4_STATUS_GOOD) {
331*0b4d6575SRobert Mustacchi 		int i;
332*0b4d6575SRobert Mustacchi 
333*0b4d6575SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: SCSI buffer data write failed:"),
334*0b4d6575SRobert Mustacchi 		    flashdev->drvname);
335*0b4d6575SRobert Mustacchi 		for (i = 0; i < NSAM4_STATUS; i++) {
336*0b4d6575SRobert Mustacchi 			if (samstatus == sam4_status[i].sam_status) {
337*0b4d6575SRobert Mustacchi 				logmsg(MSG_ERROR, gettext("%s\n"),
338*0b4d6575SRobert Mustacchi 				    sam4_status[i].sam_message);
339*0b4d6575SRobert Mustacchi 				goto err;
340*0b4d6575SRobert Mustacchi 			}
341*0b4d6575SRobert Mustacchi 		}
342*0b4d6575SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("unknown error: %d\n"), samstatus);
343*0b4d6575SRobert Mustacchi 		goto err;
344*0b4d6575SRobert Mustacchi 	} else {
345*0b4d6575SRobert Mustacchi 		logmsg(MSG_INFO, "%s: received STATUS GOOD\n",
346*0b4d6575SRobert Mustacchi 		    flashdev->drvname);
347*0b4d6575SRobert Mustacchi 	}
348*0b4d6575SRobert Mustacchi 
349*0b4d6575SRobert Mustacchi 	libscsi_action_free(action);
350*0b4d6575SRobert Mustacchi 	return (FWFLASH_SUCCESS);
351*0b4d6575SRobert Mustacchi 
352*0b4d6575SRobert Mustacchi err:
353*0b4d6575SRobert Mustacchi 	if (action != NULL)
354*0b4d6575SRobert Mustacchi 		libscsi_action_free(action);
355*0b4d6575SRobert Mustacchi 	return (FWFLASH_FAILURE);
356*0b4d6575SRobert Mustacchi }
357*0b4d6575SRobert Mustacchi 
358d65b419eSXinChen int
fw_writefw(struct devicelist * flashdev)359d65b419eSXinChen fw_writefw(struct devicelist *flashdev)
360d65b419eSXinChen {
361d65b419eSXinChen 	libscsi_hdl_t	*handle;
362d65b419eSXinChen 	libscsi_target_t *target;
363d65b419eSXinChen 	libscsi_errno_t serr;
364*0b4d6575SRobert Mustacchi 	size_t maxxfer, nwrite;
365*0b4d6575SRobert Mustacchi 	uint8_t align;
366*0b4d6575SRobert Mustacchi 	int ret = FWFLASH_FAILURE;
367d65b419eSXinChen 
368d65b419eSXinChen 	if ((verifier == NULL) || (verifier->imgsize == 0) ||
369d65b419eSXinChen 	    (verifier->fwimage == NULL)) {
370d65b419eSXinChen 		/* should _NOT_ happen */
371d65b419eSXinChen 		logmsg(MSG_ERROR,
372d65b419eSXinChen 		    gettext("%s: Firmware image has not been verified\n"),
373d65b419eSXinChen 		    flashdev->drvname);
374d65b419eSXinChen 		return (FWFLASH_FAILURE);
375d65b419eSXinChen 	}
376d65b419eSXinChen 
377d65b419eSXinChen 	if ((handle = libscsi_init(LIBSCSI_VERSION, &serr)) == NULL) {
378d65b419eSXinChen 		logmsg(MSG_ERROR, gettext("%s: failed to initialize libscsi\n"),
379d65b419eSXinChen 		    flashdev->drvname);
380d65b419eSXinChen 		return (FWFLASH_FAILURE);
381d65b419eSXinChen 	}
382d65b419eSXinChen 
383*0b4d6575SRobert Mustacchi 	if ((target = libscsi_open(handle, NULL, flashdev->access_devname)) ==
384*0b4d6575SRobert Mustacchi 	    NULL) {
385d65b419eSXinChen 		logmsg(MSG_ERROR,
386d65b419eSXinChen 		    gettext("%s: unable to open device %s\n"),
387d65b419eSXinChen 		    flashdev->drvname, flashdev->access_devname);
388d65b419eSXinChen 		libscsi_fini(handle);
389d65b419eSXinChen 		return (FWFLASH_FAILURE);
390d65b419eSXinChen 	}
391d65b419eSXinChen 
392*0b4d6575SRobert Mustacchi 	if (libscsi_max_transfer(target, &maxxfer) != 0) {
393*0b4d6575SRobert Mustacchi 		logmsg(MSG_ERROR, gettext("%s: failed to determine device "
394*0b4d6575SRobert Mustacchi 		    "maximum transfer size: %s\n"), flashdev->drvname,
395*0b4d6575SRobert Mustacchi 		    libscsi_errmsg(handle));
396*0b4d6575SRobert Mustacchi 		goto err;
397*0b4d6575SRobert Mustacchi 	}
398d65b419eSXinChen 
399*0b4d6575SRobert Mustacchi 	if (sdfw_read_descriptor(flashdev, handle, target, &align) !=
400*0b4d6575SRobert Mustacchi 	    FWFLASH_SUCCESS) {
401*0b4d6575SRobert Mustacchi 		goto err;
402*0b4d6575SRobert Mustacchi 	}
403d65b419eSXinChen 
404*0b4d6575SRobert Mustacchi 	/*
405*0b4d6575SRobert Mustacchi 	 * If the maximum transfer size is less than the maximum image size then
406*0b4d6575SRobert Mustacchi 	 * we have to do some additional work. We need to read the descriptor
407*0b4d6575SRobert Mustacchi 	 * via a READ BUFFER command and make sure that we support the required
408*0b4d6575SRobert Mustacchi 	 * offset alignment. Note that an alignment of 0xff indicates that the
409*0b4d6575SRobert Mustacchi 	 * device does not support partial writes and must receive the firmware
410*0b4d6575SRobert Mustacchi 	 * in a single WRITE BUFFER.  Otherwise a value in align represents a
411*0b4d6575SRobert Mustacchi 	 * required offset alignment of 2^off. From there, we make sure that
412*0b4d6575SRobert Mustacchi 	 * this works for our partial write size and that our partial write size
413*0b4d6575SRobert Mustacchi 	 * fits in the maximum transfer size.
414*0b4d6575SRobert Mustacchi 	 */
415*0b4d6575SRobert Mustacchi 	if (maxxfer < verifier->imgsize) {
416*0b4d6575SRobert Mustacchi 		logmsg(MSG_INFO, "%s: Maximum transfer is %zu, required "
417*0b4d6575SRobert Mustacchi 		    "alignment is 2^%u\n", flashdev->drvname, maxxfer, align);
418*0b4d6575SRobert Mustacchi 		if (FW_SD_PARTIAL_WRITE_SIZE > maxxfer) {
419*0b4d6575SRobert Mustacchi 			logmsg(MSG_ERROR, gettext("%s: cannot write firmware "
420*0b4d6575SRobert Mustacchi 			    "image: HBA enforces a maximum transfer size of "
421*0b4d6575SRobert Mustacchi 			    "%zu bytes, but the default partial transfer size "
422*0b4d6575SRobert Mustacchi 			    "is %u bytes\n"), flashdev->drvname, maxxfer,
423*0b4d6575SRobert Mustacchi 			    FW_SD_PARTIAL_WRITE_SIZE);
424*0b4d6575SRobert Mustacchi 			goto err;
425*0b4d6575SRobert Mustacchi 		}
426*0b4d6575SRobert Mustacchi 		maxxfer = FW_SD_PARTIAL_WRITE_SIZE;
427d65b419eSXinChen 
428*0b4d6575SRobert Mustacchi 		if (ffsll(maxxfer) < align || align == 0xff) {
429*0b4d6575SRobert Mustacchi 			logmsg(MSG_ERROR, gettext("%s: cannot write firmware "
430*0b4d6575SRobert Mustacchi 			    "image: device requires partial writes aligned "
431*0b4d6575SRobert Mustacchi 			    "to an unsupported value\n"), flashdev->drvname);
432*0b4d6575SRobert Mustacchi 			goto err;
433*0b4d6575SRobert Mustacchi 		}
434d65b419eSXinChen 
435*0b4d6575SRobert Mustacchi 		logmsg(MSG_INFO, "%s: final transfer block size is %zu\n",
436*0b4d6575SRobert Mustacchi 		    flashdev->drvname, maxxfer);
437*0b4d6575SRobert Mustacchi 	}
438d65b419eSXinChen 
439*0b4d6575SRobert Mustacchi 	logmsg(MSG_INFO, "%s: Writing out %u bytes to %s\n", flashdev->drvname,
440*0b4d6575SRobert Mustacchi 	    verifier->imgsize, flashdev->access_devname);
441*0b4d6575SRobert Mustacchi 	nwrite = 0;
442*0b4d6575SRobert Mustacchi 	for (;;) {
443*0b4d6575SRobert Mustacchi 		uintptr_t buf;
444*0b4d6575SRobert Mustacchi 		size_t towrite = MIN(maxxfer, verifier->imgsize - nwrite);
445d65b419eSXinChen 
446*0b4d6575SRobert Mustacchi 		if (towrite == 0)
447*0b4d6575SRobert Mustacchi 			break;
448d65b419eSXinChen 
449*0b4d6575SRobert Mustacchi 		buf = (uintptr_t)verifier->fwimage;
450*0b4d6575SRobert Mustacchi 		buf += nwrite;
451d65b419eSXinChen 
452*0b4d6575SRobert Mustacchi 		if (sdfw_write(flashdev, handle, target, towrite, nwrite,
453*0b4d6575SRobert Mustacchi 		    (void *)buf) != FWFLASH_SUCCESS) {
454*0b4d6575SRobert Mustacchi 			logmsg(MSG_ERROR, gettext("%s: failed to write to %s "
455*0b4d6575SRobert Mustacchi 			    "successfully: %s\n"), flashdev->drvname,
456*0b4d6575SRobert Mustacchi 			    flashdev->access_devname, libscsi_errmsg(handle));
457*0b4d6575SRobert Mustacchi 			goto err;
458d65b419eSXinChen 		}
459d65b419eSXinChen 
460*0b4d6575SRobert Mustacchi 		nwrite += towrite;
461d65b419eSXinChen 	}
462*0b4d6575SRobert Mustacchi 
463*0b4d6575SRobert Mustacchi 	logmsg(MSG_ERROR, gettext("Note: For flash based disks "
464*0b4d6575SRobert Mustacchi 	    "(SSD, etc). You may need power off the system to wait a "
465*0b4d6575SRobert Mustacchi 	    "few minutes for supercap to fully discharge, then power "
466*0b4d6575SRobert Mustacchi 	    "on the system again to activate the new firmware\n"));
467*0b4d6575SRobert Mustacchi 	ret = FWFLASH_SUCCESS;
468*0b4d6575SRobert Mustacchi 
469*0b4d6575SRobert Mustacchi err:
470*0b4d6575SRobert Mustacchi 	if (target != NULL)
471*0b4d6575SRobert Mustacchi 		libscsi_close(handle, target);
472*0b4d6575SRobert Mustacchi 	if (handle != NULL)
473*0b4d6575SRobert Mustacchi 		libscsi_fini(handle);
474*0b4d6575SRobert Mustacchi 
475*0b4d6575SRobert Mustacchi 	return (ret);
476d65b419eSXinChen }
477d65b419eSXinChen 
478d65b419eSXinChen /*
479d65b419eSXinChen  * The fw_identify() function walks the device
480d65b419eSXinChen  * tree trying to find devices which this plugin
481d65b419eSXinChen  * can work with.
482d65b419eSXinChen  *
483d65b419eSXinChen  * The parameter "start" gives us the starting index number
484d65b419eSXinChen  * to give the device when we add it to the fw_devices list.
485d65b419eSXinChen  *
486d65b419eSXinChen  * firstdev is allocated by us and we add space as needed
487d65b419eSXinChen  *
488d65b419eSXinChen  * When we store the desired information, inquiry-serial-no
489d65b419eSXinChen  * goes in thisdev->addresses[1], and client-guid goes in
490d65b419eSXinChen  * thisdev->addresses[2].
491d65b419eSXinChen  */
492d65b419eSXinChen int
fw_identify(int start)493d65b419eSXinChen fw_identify(int start)
494d65b419eSXinChen {
495d65b419eSXinChen 	int idx = start;
496d65b419eSXinChen 	int fw_sata_disk = 0;
497d65b419eSXinChen 	int *exists;
498d65b419eSXinChen 	di_node_t thisnode;
499d65b419eSXinChen 	struct devicelist *newdev = NULL;
500d65b419eSXinChen 	char *devpath = NULL;
501d65b419eSXinChen 	char *driver = NULL;
502d65b419eSXinChen 	char *sp_temp;
503d65b419eSXinChen 	char *sp_temp_cut;
504d65b419eSXinChen 
505d65b419eSXinChen 	/* We need to inquiry information manually by sending probe command */
506d65b419eSXinChen 	libscsi_hdl_t *handle;
507d65b419eSXinChen 	libscsi_target_t *target;
508d65b419eSXinChen 	libscsi_errno_t serr;
509d65b419eSXinChen 
510d65b419eSXinChen 	/* Just in case we've got an FC-attached device on sparc */
511d65b419eSXinChen 	if (strcmp(self->drvname, "ssd") == 0) {
512d65b419eSXinChen 		driver = self->drvname;
513d65b419eSXinChen 	} else
514d65b419eSXinChen 		driver = drivername;
515d65b419eSXinChen 
516d65b419eSXinChen 	thisnode = di_drv_first_node(driver, rootnode);
517d65b419eSXinChen 
518d65b419eSXinChen 	if (thisnode == DI_NODE_NIL) {
519d65b419eSXinChen 		logmsg(MSG_INFO, "No %s nodes in this system\n", driver);
520d65b419eSXinChen 		return (FWFLASH_FAILURE);
521d65b419eSXinChen 	}
522d65b419eSXinChen 
523d65b419eSXinChen 	if ((handle = libscsi_init(LIBSCSI_VERSION, &serr)) == NULL) {
524d65b419eSXinChen 		logmsg(MSG_ERROR, gettext("%s: failed to initialize "
525d65b419eSXinChen 		    "libscsi\n"), newdev->drvname);
526d65b419eSXinChen 		return (FWFLASH_FAILURE);
527d65b419eSXinChen 	}
528d65b419eSXinChen 
529d65b419eSXinChen 	/* we've found one, at least */
530d65b419eSXinChen 	for (; thisnode != DI_NODE_NIL; thisnode = di_drv_next_node(thisnode)) {
5314b3dc1e2SXinChen 		/* Need to free by di_devfs_path_free */
5324b3dc1e2SXinChen 		if ((devpath = di_devfs_path(thisnode)) == NULL) {
5334b3dc1e2SXinChen 			logmsg(MSG_INFO, "unable to get device path for "
5344b3dc1e2SXinChen 			    "current node with errno %d\n", errno);
5354b3dc1e2SXinChen 			continue;
5360bc9814fSXinChen 		}
537d65b419eSXinChen 		/*
538d65b419eSXinChen 		 * We check if this is removable device, in which case
539d65b419eSXinChen 		 * we really aren't interested, so exit stage left
540d65b419eSXinChen 		 */
541d65b419eSXinChen 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, thisnode,
542d65b419eSXinChen 		    "removable-media", &exists) > -1) {
543d65b419eSXinChen 			logmsg(MSG_INFO,
544d65b419eSXinChen 			    "%s: not interested in removable media device\n"
545d65b419eSXinChen 			    "%s\n", driver, devpath);
5464b3dc1e2SXinChen 			FW_SD_FREE_DEVPATH(devpath)
547d65b419eSXinChen 			continue;
548d65b419eSXinChen 		}
549d65b419eSXinChen 
550*0b4d6575SRobert Mustacchi 		if ((newdev = calloc(1, sizeof (struct devicelist))) ==
551*0b4d6575SRobert Mustacchi 		    NULL) {
552d65b419eSXinChen 			logmsg(MSG_ERROR,
553d65b419eSXinChen 			    gettext("%s: identification function unable "
554d65b419eSXinChen 			    "to allocate space for device entry\n"),
555d65b419eSXinChen 			    driver);
556d65b419eSXinChen 			libscsi_fini(handle);
557d65b419eSXinChen 			FW_SD_FREE_DEVPATH(devpath)
558d65b419eSXinChen 			return (FWFLASH_FAILURE);
559d65b419eSXinChen 		}
560d65b419eSXinChen 
561*0b4d6575SRobert Mustacchi 		if ((newdev->drvname = calloc(1, strlen(driver) + 1)) ==
562*0b4d6575SRobert Mustacchi 		    NULL) {
563d65b419eSXinChen 			logmsg(MSG_ERROR,
564d65b419eSXinChen 			    gettext("%s: Unable to allocate space to store a "
565d65b419eSXinChen 			    "driver name\n"), driver);
566d65b419eSXinChen 			libscsi_fini(handle);
5674b3dc1e2SXinChen 			FW_SD_FREE_DEVICELIST(newdev, devpath)
568d65b419eSXinChen 			return (FWFLASH_FAILURE);
569d65b419eSXinChen 		}
570d65b419eSXinChen 		(void) strlcpy(newdev->drvname, driver, strlen(driver) + 1);
571d65b419eSXinChen 
572*0b4d6575SRobert Mustacchi 		if ((newdev->classname = calloc(1, strlen(driver) + 1)) ==
573*0b4d6575SRobert Mustacchi 		    NULL) {
574d65b419eSXinChen 			logmsg(MSG_ERROR,
575d65b419eSXinChen 			    gettext("%s: Unable to allocate space for a class "
576d65b419eSXinChen 			    "name\n"), drivername);
577d65b419eSXinChen 			libscsi_fini(handle);
578d65b419eSXinChen 			FW_SD_FREE_DRV_NAME(newdev, devpath)
579d65b419eSXinChen 			return (FWFLASH_FAILURE);
580d65b419eSXinChen 		}
581d65b419eSXinChen 		(void) strlcpy(newdev->classname, driver, strlen(driver) + 1);
582d65b419eSXinChen 
5834b3dc1e2SXinChen 		/* Get the access name for current node */
5844b3dc1e2SXinChen 		if ((newdev->access_devname = calloc(1, MAXPATHLEN)) == NULL) {
5854b3dc1e2SXinChen 			logmsg(MSG_ERROR,
5864b3dc1e2SXinChen 			    gettext("%s: Unable to allocate space for a devfs "
5874b3dc1e2SXinChen 			    "name\n"), driver);
5884b3dc1e2SXinChen 			libscsi_fini(handle);
5894b3dc1e2SXinChen 			FW_SD_FREE_CLS_NAME(newdev, devpath)
5904b3dc1e2SXinChen 			return (FWFLASH_FAILURE);
5914b3dc1e2SXinChen 		}
5924b3dc1e2SXinChen 
5934b3dc1e2SXinChen 		/* The slice number may be 2 or 0, we will try 2 first */
5944b3dc1e2SXinChen 		(void) snprintf(newdev->access_devname, MAXPATHLEN,
595*0b4d6575SRobert Mustacchi 		    "%s%s:c,raw", sdfw_devprefix, devpath);
5964b3dc1e2SXinChen 		if ((target = libscsi_open(handle, NULL,
5974b3dc1e2SXinChen 		    newdev->access_devname)) == NULL) {
5984b3dc1e2SXinChen 			/* try 0 for EFI label */
5994b3dc1e2SXinChen 			(void) snprintf(newdev->access_devname, MAXPATHLEN,
600*0b4d6575SRobert Mustacchi 			    "%s%s:a,raw", sdfw_devprefix, devpath);
6014b3dc1e2SXinChen 			if ((target = libscsi_open(handle, NULL,
6024b3dc1e2SXinChen 			    newdev->access_devname)) == NULL) {
6034b3dc1e2SXinChen 				logmsg(MSG_INFO,
6044b3dc1e2SXinChen 				    "%s: unable to open device %s\n",
6054b3dc1e2SXinChen 				    newdev->drvname, newdev->access_devname);
6064b3dc1e2SXinChen 				FW_SD_FREE_ACC_NAME(newdev, devpath)
6074b3dc1e2SXinChen 				continue;
6084b3dc1e2SXinChen 			}
6094b3dc1e2SXinChen 		}
6104b3dc1e2SXinChen 
6114b3dc1e2SXinChen 		/* and the /dev/rdsk/ name */
612*0b4d6575SRobert Mustacchi 		if ((newdev->addresses[0] = sdfw_find_link(thisnode,
6134b3dc1e2SXinChen 		    newdev->access_devname)) == NULL) {
6144b3dc1e2SXinChen 			libscsi_fini(handle);
6154b3dc1e2SXinChen 			FW_SD_FREE_ACC_NAME(newdev, devpath)
6164b3dc1e2SXinChen 			return (FWFLASH_FAILURE);
6174b3dc1e2SXinChen 		}
6184b3dc1e2SXinChen 
619d65b419eSXinChen 		/*
620d65b419eSXinChen 		 * Only alloc as much as we truly need, and DON'T forget
621d65b419eSXinChen 		 * that libdevinfo manages the memory!
622d65b419eSXinChen 		 */
623d65b419eSXinChen 		if ((newdev->ident = calloc(1, sizeof (struct vpr))) == NULL) {
624d65b419eSXinChen 			logmsg(MSG_ERROR,
625d65b419eSXinChen 			    gettext("%s: Unable to allocate space for SCSI "
626d65b419eSXinChen 			    "INQUIRY data\n"), driver);
627d65b419eSXinChen 			libscsi_fini(handle);
6284b3dc1e2SXinChen 			FW_SD_FREE_ADDR(newdev, devpath)
629d65b419eSXinChen 			return (FWFLASH_FAILURE);
630d65b419eSXinChen 		}
631d65b419eSXinChen 
632d65b419eSXinChen 		/* We don't use new->ident->encap_ident currently */
633d65b419eSXinChen 
634d65b419eSXinChen 		/* Retrive information by using libscsi */
635d65b419eSXinChen 		/* Vendor ID */
636d65b419eSXinChen 		sp_temp = (char *)libscsi_vendor(target);
637d65b419eSXinChen 		if (strncmp(sp_temp, "ATA", 3) == 0) {
638d65b419eSXinChen 			/* We need to do customize the output for SATA disks */
639d65b419eSXinChen 			fw_sata_disk = 1;
640d65b419eSXinChen 		} else {
641d65b419eSXinChen 			fw_sata_disk = 0;
642d65b419eSXinChen 			if ((newdev->ident->vid =
643d65b419eSXinChen 			    calloc(1, strlen(sp_temp) + 1)) == NULL ||
644d65b419eSXinChen 			    sp_temp == NULL) {
645d65b419eSXinChen 				if (!sp_temp) {
646d65b419eSXinChen 					logmsg(MSG_ERROR, gettext("%s: unable "
647d65b419eSXinChen 					    "to get vendor id of %s\n"),
648d65b419eSXinChen 					    newdev->drvname,
649d65b419eSXinChen 					    newdev->access_devname);
650d65b419eSXinChen 				} else {
651d65b419eSXinChen 					logmsg(MSG_ERROR, gettext("Memory "
652d65b419eSXinChen 					    "allocation failure\n"));
653d65b419eSXinChen 				}
654d65b419eSXinChen 
655d65b419eSXinChen 				libscsi_close(handle, target);
656d65b419eSXinChen 				libscsi_fini(handle);
657d65b419eSXinChen 				FW_SD_FREE_IDENT(newdev, devpath)
658d65b419eSXinChen 				return (FWFLASH_FAILURE);
659d65b419eSXinChen 			}
660d65b419eSXinChen 			strlcpy(newdev->ident->vid, sp_temp,
661d65b419eSXinChen 			    strlen(sp_temp) + 1);
662d65b419eSXinChen 		}
663d65b419eSXinChen 
664d65b419eSXinChen 		/* Product ID */
665d65b419eSXinChen 		sp_temp = (char *)libscsi_product(target);
666d65b419eSXinChen 		if (fw_sata_disk) {
667d65b419eSXinChen 			sp_temp_cut = strchr(sp_temp, ' ');
668d65b419eSXinChen 			if (!sp_temp_cut) {
6694b3dc1e2SXinChen 				/*
6704b3dc1e2SXinChen 				 * There is no SPACE character in the PID field
6714b3dc1e2SXinChen 				 * Customize strings for special SATA disks
6724b3dc1e2SXinChen 				 */
673*0b4d6575SRobert Mustacchi 				if (sdfw_idtfy_custmz(newdev, sp_temp)
674d65b419eSXinChen 				    != FWFLASH_SUCCESS) {
675d65b419eSXinChen 					libscsi_close(handle, target);
676d65b419eSXinChen 					libscsi_fini(handle);
677d65b419eSXinChen 					FW_SD_FREE_IDENT(newdev, devpath)
678d65b419eSXinChen 					return (FWFLASH_FAILURE);
679d65b419eSXinChen 				}
680d65b419eSXinChen 			} else {
681d65b419eSXinChen 				/* The first string is vendor id */
682d65b419eSXinChen 				if ((newdev->ident->vid = calloc(1,
683d65b419eSXinChen 				    (sp_temp_cut - sp_temp + 1))) == NULL) {
684d65b419eSXinChen 					logmsg(MSG_ERROR, gettext("%s: unable "
685d65b419eSXinChen 					    "to get sata vendor id of %s\n"),
686d65b419eSXinChen 					    newdev->drvname,
687d65b419eSXinChen 					    newdev->access_devname);
688d65b419eSXinChen 
689d65b419eSXinChen 					libscsi_close(handle, target);
690d65b419eSXinChen 					libscsi_fini(handle);
691d65b419eSXinChen 					FW_SD_FREE_IDENT(newdev, devpath)
692d65b419eSXinChen 					return (FWFLASH_FAILURE);
693d65b419eSXinChen 				}
694d65b419eSXinChen 				strlcpy(newdev->ident->vid, sp_temp,
695d65b419eSXinChen 				    sp_temp_cut - sp_temp + 1);
696d65b419eSXinChen 
697d65b419eSXinChen 				/* The second string is product id */
698d65b419eSXinChen 				if ((newdev->ident->pid =
699d65b419eSXinChen 				    calloc(1, strlen(sp_temp) -
700d65b419eSXinChen 				    strlen(newdev->ident->vid))) == NULL) {
701d65b419eSXinChen 					logmsg(MSG_ERROR, gettext("%s: unable "
702d65b419eSXinChen 					    "to get sata product id of %s\n"),
703d65b419eSXinChen 					    newdev->drvname,
704d65b419eSXinChen 					    newdev->access_devname);
705d65b419eSXinChen 
706d65b419eSXinChen 					libscsi_close(handle, target);
707