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