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);
707d65b419eSXinChen 					libscsi_fini(handle);
708d65b419eSXinChen 					FW_SD_FREE_IDENT_VID(newdev, devpath)
709d65b419eSXinChen 					return (FWFLASH_FAILURE);
710d65b419eSXinChen 				}
711d65b419eSXinChen 				strlcpy(newdev->ident->pid, sp_temp_cut + 1,
712d65b419eSXinChen 				    strlen(sp_temp) -
713d65b419eSXinChen 				    strlen(newdev->ident->vid));
714d65b419eSXinChen 			}
715d65b419eSXinChen 		} else {
716d65b419eSXinChen 			if ((newdev->ident->pid =
717d65b419eSXinChen 			    calloc(1, strlen(sp_temp) + 1)) == NULL ||
718d65b419eSXinChen 			    sp_temp == NULL) {
719d65b419eSXinChen 				logmsg(MSG_ERROR, gettext("%s: unable to get "
720d65b419eSXinChen 				    "product id of %s\n"), newdev->drvname,
721d65b419eSXinChen 				    newdev->access_devname);
722d65b419eSXinChen 				FW_SD_FREE_IDENT_VID(newdev, devpath)
723d65b419eSXinChen 				libscsi_close(handle, target);
724d65b419eSXinChen 				libscsi_fini(handle);
725d65b419eSXinChen 				return (FWFLASH_FAILURE);
726d65b419eSXinChen 			}
727d65b419eSXinChen 			strlcpy(newdev->ident->pid, sp_temp,
728d65b419eSXinChen 			    strlen(sp_temp) + 1);
729d65b419eSXinChen 		}
730d65b419eSXinChen 
731d65b419eSXinChen 		/* Revision ID */
732d65b419eSXinChen 		sp_temp = (char *)libscsi_revision(target);
733*0b4d6575SRobert Mustacchi 		if ((newdev->ident->revid = calloc(1, strlen(sp_temp) + 1)) ==
734*0b4d6575SRobert Mustacchi 		    NULL || sp_temp == NULL) {
735d65b419eSXinChen 			logmsg(MSG_ERROR, gettext("%s: unable to get revision "
736d65b419eSXinChen 			    "id of %s\n"), newdev->drvname,
737d65b419eSXinChen 			    newdev->access_devname);
738d65b419eSXinChen 			libscsi_close(handle, target);
739d65b419eSXinChen 			libscsi_fini(handle);
740d65b419eSXinChen 			FW_SD_FREE_IDENT_PID(newdev, devpath)
741d65b419eSXinChen 			return (FWFLASH_FAILURE);
742d65b419eSXinChen 		}
743d65b419eSXinChen 		strlcpy(newdev->ident->revid, sp_temp, strlen(sp_temp) + 1);
744d65b419eSXinChen 
745d65b419eSXinChen 		/* Finish using libscsi */
746d65b419eSXinChen 		libscsi_close(handle, target);
747d65b419eSXinChen 
748d65b419eSXinChen 		if (di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode,
749d65b419eSXinChen 		    "inquiry-serial-no", &newdev->addresses[1]) < 0) {
750d65b419eSXinChen 			logmsg(MSG_INFO,
751d65b419eSXinChen 			    "%s: no inquiry-serial-no property for %s\n",
752d65b419eSXinChen 			    driver, newdev->access_devname);
753d65b419eSXinChen 			logmsg(MSG_INFO, "The errno is %d\n", errno);
754d65b419eSXinChen 		}
755d65b419eSXinChen 
756d65b419eSXinChen 		if ((di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode,
757d65b419eSXinChen 		    "client-guid", &newdev->addresses[2])) < 0) {
758d65b419eSXinChen 			logmsg(MSG_INFO,
759d65b419eSXinChen 			    "%s: no client-guid property "
760d65b419eSXinChen 			    "for device %s\n",
761d65b419eSXinChen 			    driver, newdev->access_devname);
762d65b419eSXinChen 			/* try fallback */
763d65b419eSXinChen 			if ((di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode,
764d65b419eSXinChen 			    "guid", &newdev->addresses[2])) < 0) {
765d65b419eSXinChen 				logmsg(MSG_INFO,
766d65b419eSXinChen 				    "%s: no guid property for device %s\n",
767d65b419eSXinChen 				    driver, newdev->access_devname);
768d65b419eSXinChen 			}
769d65b419eSXinChen 		} else {
770d65b419eSXinChen 			logmsg(MSG_INFO,
771d65b419eSXinChen 			    "client-guid property: %s\n",
772d65b419eSXinChen 			    newdev->addresses[2]);
773d65b419eSXinChen 		}
774d65b419eSXinChen 
775d65b419eSXinChen 		newdev->index = idx;
776d65b419eSXinChen 		++idx;
777d65b419eSXinChen 		newdev->plugin = self;
778d65b419eSXinChen 
779d65b419eSXinChen 		TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev);
7804b3dc1e2SXinChen 		FW_SD_FREE_DEVPATH(devpath)
781d65b419eSXinChen 	}
782d65b419eSXinChen 	libscsi_fini(handle);
783d65b419eSXinChen 
784d65b419eSXinChen 	/* Check if sd targets presented are all unflashable. */
785d65b419eSXinChen 	if (idx == start)
786d65b419eSXinChen 		return (FWFLASH_FAILURE);
787d65b419eSXinChen 
788d65b419eSXinChen 	if (fwflash_debug != 0) {
789d65b419eSXinChen 		struct devicelist *tempdev;
790d65b419eSXinChen 
791d65b419eSXinChen 		TAILQ_FOREACH(tempdev, fw_devices, nextdev) {
792d65b419eSXinChen 			logmsg(MSG_INFO, "%s:fw_identify:\n",
793d65b419eSXinChen 			    driver);
794d65b419eSXinChen 			logmsg(MSG_INFO,
795d65b419eSXinChen 			    "\ttempdev @ 0x%lx\n"
796d65b419eSXinChen 			    "\t\taccess_devname: %s\n"
797d65b419eSXinChen 			    "\t\tdrvname: %s\tclassname: %s\n"
798d65b419eSXinChen 			    "\t\tident->vid:   %s\n"
799d65b419eSXinChen 			    "\t\tident->pid:   %s\n"
800d65b419eSXinChen 			    "\t\tident->revid: %s\n"
801d65b419eSXinChen 			    "\t\tindex:	%d\n"
802d65b419eSXinChen 			    "\t\taddress[0]:   %s\n"
803d65b419eSXinChen 			    "\t\taddress[1]:   %s\n"
804d65b419eSXinChen 			    "\t\taddress[2]:   %s\n"
805d65b419eSXinChen 			    "\t\tplugin @ 0x%lx\n\n",
806d65b419eSXinChen 			    &tempdev,
807d65b419eSXinChen 			    tempdev->access_devname,
808d65b419eSXinChen 			    tempdev->drvname, newdev->classname,
809d65b419eSXinChen 			    tempdev->ident->vid,
810d65b419eSXinChen 			    tempdev->ident->pid,
811d65b419eSXinChen 			    tempdev->ident->revid,
812d65b419eSXinChen 			    tempdev->index,
813d65b419eSXinChen 			    tempdev->addresses[0],
814d65b419eSXinChen 			    (tempdev->addresses[1] ? tempdev->addresses[1] :
815d65b419eSXinChen 			    "(not supported)"),
816d65b419eSXinChen 			    (tempdev->addresses[2] ? tempdev->addresses[2] :
817d65b419eSXinChen 			    "(not supported)"),
818d65b419eSXinChen 			    &tempdev->plugin);
819d65b419eSXinChen 		}
820d65b419eSXinChen 	}
821d65b419eSXinChen 	return (FWFLASH_SUCCESS);
822d65b419eSXinChen }
823d65b419eSXinChen 
824d65b419eSXinChen int
fw_devinfo(struct devicelist * thisdev)825d65b419eSXinChen fw_devinfo(struct devicelist *thisdev)
826d65b419eSXinChen {
827d65b419eSXinChen 	fprintf(stdout, gettext("Device[%d]\t\t\t%s\n"
828d65b419eSXinChen 	    "  Class [%s]\t\t\t%s\n"),
829d65b419eSXinChen 	    thisdev->index, thisdev->access_devname,
830d65b419eSXinChen 	    thisdev->classname, thisdev->addresses[0]);
831d65b419eSXinChen 
832d65b419eSXinChen 	fprintf(stdout,
833d65b419eSXinChen 	    gettext(
834d65b419eSXinChen 	    "\tVendor\t\t\t: %s\n"
835d65b419eSXinChen 	    "\tProduct\t\t\t: %s\n"
836d65b419eSXinChen 	    "\tFirmware revision\t: %-s\n"
837d65b419eSXinChen 	    "\tInquiry Serial Number   : %-s\n"
838d65b419eSXinChen 	    "\tGUID\t\t\t: %s\n"),
839d65b419eSXinChen 	    thisdev->ident->vid,
840d65b419eSXinChen 	    thisdev->ident->pid,
841d65b419eSXinChen 	    thisdev->ident->revid,
842d65b419eSXinChen 	    (thisdev->addresses[1] ? thisdev->addresses[1] :
843d65b419eSXinChen 	    "(not supported)"),
844d65b419eSXinChen 	    (thisdev->addresses[2] ? thisdev->addresses[2] :
845d65b419eSXinChen 	    "(not supported)"));
846d65b419eSXinChen 
847d65b419eSXinChen 	fprintf(stdout, "\n\n");
848d65b419eSXinChen 
849d65b419eSXinChen 	return (FWFLASH_SUCCESS);
850d65b419eSXinChen }
851d65b419eSXinChen 
852d65b419eSXinChen void
fw_cleanup(struct devicelist * thisdev)853d65b419eSXinChen fw_cleanup(struct devicelist *thisdev)
854d65b419eSXinChen {
855d65b419eSXinChen 	/*
856d65b419eSXinChen 	 * Function to clean up all the memory allocated
857d65b419eSXinChen 	 * by this plugin, for thisdev.
858d65b419eSXinChen 	 */
859d65b419eSXinChen 	free(thisdev->access_devname);
860d65b419eSXinChen 	free(thisdev->drvname);
861d65b419eSXinChen 	free(thisdev->classname);
862d65b419eSXinChen 
863d65b419eSXinChen 	/*
864d65b419eSXinChen 	 * Note that we DO NOT free addresses[1,2] because _IF_
865d65b419eSXinChen 	 * these elements are valid, they are managed by libdevinfo
866d65b419eSXinChen 	 * and we didn't allocate any space for them.
867d65b419eSXinChen 	 */
868d65b419eSXinChen 	free(thisdev->addresses[0]);
869d65b419eSXinChen 
870d65b419eSXinChen 	/* what this points to is freed in common code */
871d65b419eSXinChen 	thisdev->plugin = NULL;
872d65b419eSXinChen 
873d65b419eSXinChen 	free(thisdev->ident->vid);
874d65b419eSXinChen 	free(thisdev->ident->pid);
875d65b419eSXinChen 	free(thisdev->ident->revid);
876d65b419eSXinChen 
877d65b419eSXinChen 	thisdev->ident = NULL;
878d65b419eSXinChen }
879d65b419eSXinChen 
880d65b419eSXinChen /*
881d65b419eSXinChen  * Helper functions
882d65b419eSXinChen  */
883d65b419eSXinChen static int
sdfw_link_cb(di_devlink_t devlink,void * arg)884*0b4d6575SRobert Mustacchi sdfw_link_cb(di_devlink_t devlink, void *arg)
885d65b419eSXinChen {
886d65b419eSXinChen 	const char *result;
887d65b419eSXinChen 
888d65b419eSXinChen 	result = di_devlink_path(devlink);
889d65b419eSXinChen 	if (result == NULL) {
890d65b419eSXinChen 		arg = (void *)"(null)";
891d65b419eSXinChen 	} else {
892d65b419eSXinChen 		(void) strlcpy(arg, result, strlen(result) + 1);
893d65b419eSXinChen 	}
894d65b419eSXinChen 
895*0b4d6575SRobert Mustacchi 	logmsg(MSG_INFO, "\nsdfw_link_cb::linkdata->resultstr = %s\n",
896d65b419eSXinChen 	    ((result != NULL) ? result : "(null)"));
897d65b419eSXinChen 
898d65b419eSXinChen 	return (DI_WALK_CONTINUE);
899d65b419eSXinChen }
900d65b419eSXinChen 
901d65b419eSXinChen static char *
sdfw_find_link(di_node_t bnode,char * acc_devname)902*0b4d6575SRobert Mustacchi sdfw_find_link(di_node_t bnode, char *acc_devname)
903d65b419eSXinChen {
904d65b419eSXinChen 	di_minor_t devminor = DI_MINOR_NIL;
9054b3dc1e2SXinChen 	di_devlink_handle_t hdl;
906d65b419eSXinChen 	char *cbresult = NULL;
907d65b419eSXinChen 	char linkname[] = "^rdsk/\0";
908d65b419eSXinChen 
909d65b419eSXinChen 	if (bnode == DI_NODE_NIL) {
910d65b419eSXinChen 		logmsg(MSG_ERROR,
911*0b4d6575SRobert Mustacchi 		    gettext("sdfw_find_link must be called with non-null "
912d65b419eSXinChen 		    "di_node_t\n"));
913d65b419eSXinChen 		return (NULL);
914d65b419eSXinChen 	}
915d65b419eSXinChen 
9164b3dc1e2SXinChen 	if ((cbresult = calloc(1, MAXPATHLEN)) == NULL) {
917d65b419eSXinChen 		logmsg(MSG_ERROR, gettext("unable to allocate space for dev "
918d65b419eSXinChen 		    "link\n"));
919d65b419eSXinChen 		return (NULL);
920d65b419eSXinChen 	}
921d65b419eSXinChen 
922d65b419eSXinChen 	devminor = di_minor_next(bnode, devminor);
923d65b419eSXinChen 	errno = 0;
924d65b419eSXinChen 	hdl = di_devlink_init(di_devfs_minor_path(devminor), DI_MAKE_LINK);
925d65b419eSXinChen 	if (hdl == NULL) {
926d65b419eSXinChen 		if (errno == EPERM || errno == EACCES) {
927d65b419eSXinChen 			logmsg(MSG_ERROR,
928d65b419eSXinChen 			    gettext("%s: You must be super-user to use this "
929d65b419eSXinChen 			    "plugin.\n"), drivername);
930d65b419eSXinChen 		} else {
931d65b419eSXinChen 			logmsg(MSG_ERROR,
932d65b419eSXinChen 			    gettext("unable to take devlink snapshot: %s\n"),
933d65b419eSXinChen 			    strerror(errno));
934d65b419eSXinChen 		}
9354b3dc1e2SXinChen 		free(cbresult);
936d65b419eSXinChen 		return (NULL);
937d65b419eSXinChen 	}
938d65b419eSXinChen 
939d65b419eSXinChen 	errno = 0;
940*0b4d6575SRobert Mustacchi 	if (di_devlink_walk(hdl, linkname, acc_devname + strlen(sdfw_devprefix),
941*0b4d6575SRobert Mustacchi 	    DI_PRIMARY_LINK, (void *)cbresult, sdfw_link_cb) < 0) {
942d65b419eSXinChen 		logmsg(MSG_ERROR,
943d65b419eSXinChen 		    gettext("Unable to walk devlink snapshot for %s: %s\n"),
9444b3dc1e2SXinChen 		    acc_devname, strerror(errno));
9454b3dc1e2SXinChen 		free(cbresult);
946d65b419eSXinChen 		return (NULL);
947d65b419eSXinChen 	}
948d65b419eSXinChen 
949d65b419eSXinChen 	if (di_devlink_fini(&hdl) < 0) {
950d65b419eSXinChen 		logmsg(MSG_ERROR,
951d65b419eSXinChen 		    gettext("Unable to close devlink snapshot: %s\n"),
952d65b419eSXinChen 		    strerror(errno));
953d65b419eSXinChen 	}
954d65b419eSXinChen 
955d65b419eSXinChen 	logmsg(MSG_INFO, "cbresult: %s\n", cbresult);
956d65b419eSXinChen 	return (cbresult);
957d65b419eSXinChen }
958d65b419eSXinChen 
959d65b419eSXinChen static int
sdfw_idtfy_custmz(struct devicelist * device,char * sp)960*0b4d6575SRobert Mustacchi sdfw_idtfy_custmz(struct devicelist *device, char *sp)
961d65b419eSXinChen {
962d65b419eSXinChen 	/* vid customization */
963d65b419eSXinChen 	if (strncmp(sp, "ST", 2) == 0) {
964d65b419eSXinChen 		/* Customize retail Seagate disks */
965d65b419eSXinChen 		if ((device->ident->vid = strdup("SEAGATE")) == NULL) {
966d65b419eSXinChen 			return (FWFLASH_FAILURE);
967d65b419eSXinChen 		}
968d65b419eSXinChen 	} else if (strncmp(sp, "SSD", 3) == 0) {
969d65b419eSXinChen 		/* Customize retail INTEL disks */
970d65b419eSXinChen 		if ((device->ident->vid = strdup("INTEL")) == NULL) {
971d65b419eSXinChen 			return (FWFLASH_FAILURE);
972d65b419eSXinChen 		}
973d65b419eSXinChen 	} else {
974*0b4d6575SRobert Mustacchi 		/* disks to do in the future, fill 'ATA' first */
975d65b419eSXinChen 		if ((device->ident->vid = strdup("ATA")) == NULL) {
976d65b419eSXinChen 			return (FWFLASH_FAILURE);
977d65b419eSXinChen 		}
978d65b419eSXinChen 	}
979d65b419eSXinChen 
980d65b419eSXinChen 	/* pid customization */
981d65b419eSXinChen 	if ((device->ident->pid = calloc(1, strlen(sp) + 1)) == NULL) {
982d65b419eSXinChen 		logmsg(MSG_ERROR, gettext("Unable to allocate space for "
983d65b419eSXinChen 		    "product id\n"));
984d65b419eSXinChen 		free(device->ident->vid);
985d65b419eSXinChen 		return (FWFLASH_FAILURE);
986d65b419eSXinChen 	}
987d65b419eSXinChen 	strlcpy(device->ident->pid, sp, strlen(sp) + 1);
988d65b419eSXinChen 
989d65b419eSXinChen 	return (FWFLASH_SUCCESS);
990d65b419eSXinChen }
991