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