xref: /illumos-gate/usr/src/cmd/fwflash/plugins/transport/common/sd.c (revision aedf2b3bb56b025fcaf87b49ec6c8aeea07f16d7)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * sd / ssd (SCSI Direct-attached Device) specific functions.
28  */
29 #include <libnvpair.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <sys/types.h>
34 #include <sys/sysmacros.h>
35 #include <sys/queue.h>
36 #include <fcntl.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <scsi/libscsi.h>
40 #include <libintl.h> /* for gettext(3c) */
41 #include <fwflash/fwflash.h>
42 
43 typedef struct sam4_statdesc {
44 	int status;
45 	char *message;
46 } sam4_statdesc_t;
47 
48 static sam4_statdesc_t sam4_status[] = {
49 	{ SAM4_STATUS_GOOD, "Status: GOOD (success)" },
50 	{ SAM4_STATUS_CHECK_CONDITION, "Status: CHECK CONDITION" },
51 	{ SAM4_STATUS_CONDITION_MET, "Status: CONDITION MET" },
52 	{ SAM4_STATUS_BUSY, "Status: Device is BUSY" },
53 	{ SAM4_STATUS_RESERVATION_CONFLICT, "Status: Device is RESERVED" },
54 	{ SAM4_STATUS_TASK_SET_FULL,
55 	    "Status: TASK SET FULL (insufficient resources in command queue" },
56 	{ SAM4_STATUS_TASK_ABORTED, "Status: TASK ABORTED" },
57 	{ NULL, NULL }
58 };
59 
60 #define	NSAM4_STATUS	\
61 	(sizeof (sam4_status) / sizeof (sam4_status[0]))
62 
63 #define	FW_SD_FREE_DEVPATH(devpath)	{	\
64 		di_devfs_path_free((devpath));	\
65 	}
66 #define	FW_SD_FREE_DEVICELIST(thisdev, devpath) {	\
67 		free((thisdev));	\
68 		FW_SD_FREE_DEVPATH((devpath))	\
69 	}
70 #define	FW_SD_FREE_DRV_NAME(thisdev, devpath) {	\
71 		free((thisdev)->drvname);	\
72 		FW_SD_FREE_DEVICELIST((thisdev), (devpath))	\
73 	}
74 #define	FW_SD_FREE_CLS_NAME(thisdev, devpath) {	\
75 		free((thisdev)->classname);	\
76 		FW_SD_FREE_DRV_NAME((thisdev), (devpath))	\
77 	}
78 #define	FW_SD_FREE_ACC_NAME(thisdev, devpath) {	\
79 		free((thisdev)->access_devname);	\
80 		FW_SD_FREE_CLS_NAME(thisdev, devpath)	\
81 	}
82 #define	FW_SD_FREE_ADDR(thisdev, devpath) {	\
83 		free((thisdev)->addresses[0]);	\
84 		FW_SD_FREE_ACC_NAME(thisdev, devpath)	\
85 	}
86 #define	FW_SD_FREE_IDENT(thisdev, devpath) {	\
87 		free((thisdev)->ident);	\
88 		FW_SD_FREE_ADDR((thisdev), (devpath))	\
89 	}
90 #define	FW_SD_FREE_IDENT_VID(thisdev, devpath) {	\
91 		free((thisdev)->ident->vid);	\
92 		FW_SD_FREE_IDENT((thisdev), (devpath))	\
93 	}
94 #define	FW_SD_FREE_IDENT_PID(thisdev, devpath) {	\
95 		free((thisdev)->ident->pid);	\
96 		FW_SD_FREE_IDENT_VID((thisdev), (devpath))	\
97 	}
98 #define	FW_SD_FREE_IDENT_ALL(thisdev, devpath) {	\
99 		free((thisdev)->ident->revid);	\
100 		FW_SD_FREE_IDENT_PID((thisdev), (devpath))	\
101 	}
102 
103 int errno;
104 char drivername[] = "sd\0";
105 int plugin_version = FWPLUGIN_VERSION_2;
106 
107 static char *devprefix = "/devices";
108 extern di_node_t rootnode;
109 extern struct fw_plugin *self;
110 extern struct vrfyplugin *verifier;
111 extern int fwflash_debug;
112 
113 /* required functions for this plugin */
114 int fw_readfw(struct devicelist *device, char *filename);
115 int fw_writefw(struct devicelist *device);
116 int fw_identify(int start);
117 int fw_devinfo(struct devicelist *thisdev);
118 void fw_cleanup(struct devicelist *thisdev);
119 
120 /* helper functions */
121 static char *find_link(di_node_t bnode, char *acc_devname);
122 static int link_cb(di_devlink_t devlink, void *arg);
123 static int sd_idtfy_custmz(struct devicelist *device, char *sp);
124 
125 /*
126  * We don't currently support reading firmware from a disk. If we do eventually
127  * support it, we would use the scsi READ BUFFER command to do so.
128  */
129 int
130 fw_readfw(struct devicelist *flashdev, char *filename)
131 {
132 
133 	logmsg(MSG_INFO,
134 	    "%s: not writing firmware for device %s to file %s\n",
135 	    flashdev->drvname, flashdev->access_devname, filename);
136 	logmsg(MSG_ERROR,
137 	    gettext("\n\nReading of firmware images from %s-attached "
138 	    "devices is not supported\n\n"),
139 	    flashdev->drvname);
140 
141 	return (FWFLASH_SUCCESS);
142 }
143 
144 int
145 fw_writefw(struct devicelist *flashdev)
146 {
147 	int rv;
148 	int i = 0;
149 	libscsi_hdl_t	*handle;
150 	libscsi_target_t *target;
151 	libscsi_action_t *action;
152 	libscsi_errno_t serr;
153 	spc3_write_buffer_cdb_t *wb_cdb;
154 	sam4_status_t samstatus;
155 
156 	if ((verifier == NULL) || (verifier->imgsize == 0) ||
157 	    (verifier->fwimage == NULL)) {
158 		/* should _NOT_ happen */
159 		logmsg(MSG_ERROR,
160 		    gettext("%s: Firmware image has not been verified\n"),
161 		    flashdev->drvname);
162 		return (FWFLASH_FAILURE);
163 	}
164 
165 	if ((handle = libscsi_init(LIBSCSI_VERSION, &serr)) == NULL) {
166 		logmsg(MSG_ERROR, gettext("%s: failed to initialize libscsi\n"),
167 		    flashdev->drvname);
168 		return (FWFLASH_FAILURE);
169 	}
170 
171 	if ((target = libscsi_open(handle, NULL, flashdev->access_devname))
172 	    == NULL) {
173 		logmsg(MSG_ERROR,
174 		    gettext("%s: unable to open device %s\n"),
175 		    flashdev->drvname, flashdev->access_devname);
176 		libscsi_fini(handle);
177 		return (FWFLASH_FAILURE);
178 	}
179 
180 	action = libscsi_action_alloc(handle, SPC3_CMD_WRITE_BUFFER,
181 	    LIBSCSI_AF_WRITE|LIBSCSI_AF_RQSENSE,
182 	    (void *)verifier->fwimage, (size_t)verifier->imgsize);
183 
184 	wb_cdb = (spc3_write_buffer_cdb_t *)libscsi_action_get_cdb(action);
185 
186 	wb_cdb->wbc_mode = SPC3_WB_MODE_DL_UCODE_SAVE;
187 	wb_cdb->wbc_bufferid = verifier->flashbuf;
188 
189 	wb_cdb->wbc_buffer_offset[0] = 0;
190 	wb_cdb->wbc_buffer_offset[1] = 0;
191 	wb_cdb->wbc_buffer_offset[2] = 0;
192 
193 	wb_cdb->wbc_parameter_list_len[0] =
194 	    (verifier->imgsize & 0xff0000) >> 16;
195 	wb_cdb->wbc_parameter_list_len[1] = (verifier->imgsize & 0xff00) >> 8;
196 	wb_cdb->wbc_parameter_list_len[2] = (verifier->imgsize & 0xff);
197 
198 	rv = libscsi_exec(action, target);
199 	samstatus = libscsi_action_get_status(action);
200 
201 	logmsg(MSG_INFO, "\nscsi_writebuffer: ret 0x%0x, samstatus 0x%0x\n",
202 	    rv, samstatus);
203 
204 	libscsi_action_free(action);
205 	libscsi_close(handle, target);
206 	libscsi_fini(handle);
207 
208 	if (rv != FWFLASH_SUCCESS)
209 		return (FWFLASH_FAILURE);
210 
211 	for (i = 0; i < NSAM4_STATUS; i++) {
212 		if (sam4_status[i].status == samstatus) {
213 			logmsg(MSG_ERROR, gettext("RETURN STATUS: %s\n"),
214 			    (sam4_status[i].message));
215 			break;
216 		}
217 	}
218 	if (i == NSAM4_STATUS)
219 		logmsg(MSG_ERROR, gettext("Status UNKNOWN\n"));
220 
221 	if (samstatus == SAM4_STATUS_GOOD) {
222 		logmsg(MSG_ERROR, gettext("Note: For flash based disks "
223 		    "(SSD, etc). You may need power off the system to wait a "
224 		    "few minutes for supercap to fully discharge, then power "
225 		    "on the system again to activate the new firmware\n"));
226 		return (FWFLASH_SUCCESS);
227 	}
228 	return (FWFLASH_FAILURE);
229 }
230 
231 /*
232  * The fw_identify() function walks the device
233  * tree trying to find devices which this plugin
234  * can work with.
235  *
236  * The parameter "start" gives us the starting index number
237  * to give the device when we add it to the fw_devices list.
238  *
239  * firstdev is allocated by us and we add space as needed
240  *
241  * When we store the desired information, inquiry-serial-no
242  * goes in thisdev->addresses[1], and client-guid goes in
243  * thisdev->addresses[2].
244  */
245 int
246 fw_identify(int start)
247 {
248 	int idx = start;
249 	int fw_sata_disk = 0;
250 	int *exists;
251 	di_node_t thisnode;
252 	struct devicelist *newdev = NULL;
253 	char *devpath = NULL;
254 	char *driver = NULL;
255 	char *sp_temp;
256 	char *sp_temp_cut;
257 
258 	/* We need to inquiry information manually by sending probe command */
259 	libscsi_hdl_t *handle;
260 	libscsi_target_t *target;
261 	libscsi_errno_t serr;
262 
263 	/* Just in case we've got an FC-attached device on sparc */
264 	if (strcmp(self->drvname, "ssd") == 0) {
265 		driver = self->drvname;
266 	} else
267 		driver = drivername;
268 
269 	thisnode = di_drv_first_node(driver, rootnode);
270 
271 	if (thisnode == DI_NODE_NIL) {
272 		logmsg(MSG_INFO, "No %s nodes in this system\n", driver);
273 		return (FWFLASH_FAILURE);
274 	}
275 
276 	if ((handle = libscsi_init(LIBSCSI_VERSION, &serr)) == NULL) {
277 		logmsg(MSG_ERROR, gettext("%s: failed to initialize "
278 		    "libscsi\n"), newdev->drvname);
279 		return (FWFLASH_FAILURE);
280 	}
281 
282 	/* we've found one, at least */
283 	for (; thisnode != DI_NODE_NIL; thisnode = di_drv_next_node(thisnode)) {
284 		/* Need to free by di_devfs_path_free */
285 		if ((devpath = di_devfs_path(thisnode)) == NULL) {
286 			logmsg(MSG_INFO, "unable to get device path for "
287 			    "current node with errno %d\n", errno);
288 			continue;
289 		}
290 		/*
291 		 * We check if this is removable device, in which case
292 		 * we really aren't interested, so exit stage left
293 		 */
294 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, thisnode,
295 		    "removable-media", &exists) > -1) {
296 			logmsg(MSG_INFO,
297 			    "%s: not interested in removable media device\n"
298 			    "%s\n", driver, devpath);
299 			FW_SD_FREE_DEVPATH(devpath)
300 			continue;
301 		}
302 
303 		if ((newdev = calloc(1, sizeof (struct devicelist)))
304 		    == NULL) {
305 			logmsg(MSG_ERROR,
306 			    gettext("%s: identification function unable "
307 			    "to allocate space for device entry\n"),
308 			    driver);
309 			libscsi_fini(handle);
310 			FW_SD_FREE_DEVPATH(devpath)
311 			return (FWFLASH_FAILURE);
312 		}
313 
314 		if ((newdev->drvname = calloc(1, strlen(driver) + 1))
315 		    == NULL) {
316 			logmsg(MSG_ERROR,
317 			    gettext("%s: Unable to allocate space to store a "
318 			    "driver name\n"), driver);
319 			libscsi_fini(handle);
320 			FW_SD_FREE_DEVICELIST(newdev, devpath)
321 			return (FWFLASH_FAILURE);
322 		}
323 		(void) strlcpy(newdev->drvname, driver, strlen(driver) + 1);
324 
325 		if ((newdev->classname = calloc(1, strlen(driver) + 1))
326 		    == NULL) {
327 			logmsg(MSG_ERROR,
328 			    gettext("%s: Unable to allocate space for a class "
329 			    "name\n"), drivername);
330 			libscsi_fini(handle);
331 			FW_SD_FREE_DRV_NAME(newdev, devpath)
332 			return (FWFLASH_FAILURE);
333 		}
334 		(void) strlcpy(newdev->classname, driver, strlen(driver) + 1);
335 
336 		/* Get the access name for current node */
337 		if ((newdev->access_devname = calloc(1, MAXPATHLEN)) == NULL) {
338 			logmsg(MSG_ERROR,
339 			    gettext("%s: Unable to allocate space for a devfs "
340 			    "name\n"), driver);
341 			libscsi_fini(handle);
342 			FW_SD_FREE_CLS_NAME(newdev, devpath)
343 			return (FWFLASH_FAILURE);
344 		}
345 
346 		/* The slice number may be 2 or 0, we will try 2 first */
347 		(void) snprintf(newdev->access_devname, MAXPATHLEN,
348 		    "%s%s:c,raw", devprefix, devpath);
349 		if ((target = libscsi_open(handle, NULL,
350 		    newdev->access_devname)) == NULL) {
351 			/* try 0 for EFI label */
352 			(void) snprintf(newdev->access_devname, MAXPATHLEN,
353 			    "%s%s:a,raw", devprefix, devpath);
354 			if ((target = libscsi_open(handle, NULL,
355 			    newdev->access_devname)) == NULL) {
356 				logmsg(MSG_INFO,
357 				    "%s: unable to open device %s\n",
358 				    newdev->drvname, newdev->access_devname);
359 				FW_SD_FREE_ACC_NAME(newdev, devpath)
360 				continue;
361 			}
362 		}
363 
364 		/* and the /dev/rdsk/ name */
365 		if ((newdev->addresses[0] = find_link(thisnode,
366 		    newdev->access_devname)) == NULL) {
367 			libscsi_fini(handle);
368 			FW_SD_FREE_ACC_NAME(newdev, devpath)
369 			return (FWFLASH_FAILURE);
370 		}
371 
372 		/*
373 		 * Only alloc as much as we truly need, and DON'T forget
374 		 * that libdevinfo manages the memory!
375 		 */
376 		if ((newdev->ident = calloc(1, sizeof (struct vpr))) == NULL) {
377 			logmsg(MSG_ERROR,
378 			    gettext("%s: Unable to allocate space for SCSI "
379 			    "INQUIRY data\n"), driver);
380 			libscsi_fini(handle);
381 			FW_SD_FREE_ADDR(newdev, devpath)
382 			return (FWFLASH_FAILURE);
383 		}
384 
385 		/* We don't use new->ident->encap_ident currently */
386 
387 		/* Retrive information by using libscsi */
388 		/* Vendor ID */
389 		sp_temp = (char *)libscsi_vendor(target);
390 		if (strncmp(sp_temp, "ATA", 3) == 0) {
391 			/* We need to do customize the output for SATA disks */
392 			fw_sata_disk = 1;
393 		} else {
394 			fw_sata_disk = 0;
395 			if ((newdev->ident->vid =
396 			    calloc(1, strlen(sp_temp) + 1)) == NULL ||
397 			    sp_temp == NULL) {
398 				if (!sp_temp) {
399 					logmsg(MSG_ERROR, gettext("%s: unable "
400 					    "to get vendor id of %s\n"),
401 					    newdev->drvname,
402 					    newdev->access_devname);
403 				} else {
404 					logmsg(MSG_ERROR, gettext("Memory "
405 					    "allocation failure\n"));
406 				}
407 
408 				libscsi_close(handle, target);
409 				libscsi_fini(handle);
410 				FW_SD_FREE_IDENT(newdev, devpath)
411 				return (FWFLASH_FAILURE);
412 			}
413 			strlcpy(newdev->ident->vid, sp_temp,
414 			    strlen(sp_temp) + 1);
415 		}
416 
417 		/* Product ID */
418 		sp_temp = (char *)libscsi_product(target);
419 		if (fw_sata_disk) {
420 			sp_temp_cut = strchr(sp_temp, ' ');
421 			if (!sp_temp_cut) {
422 				/*
423 				 * There is no SPACE character in the PID field
424 				 * Customize strings for special SATA disks
425 				 */
426 				if (sd_idtfy_custmz(newdev, sp_temp)
427 				    != FWFLASH_SUCCESS) {
428 					libscsi_close(handle, target);
429 					libscsi_fini(handle);
430 					FW_SD_FREE_IDENT(newdev, devpath)
431 					return (FWFLASH_FAILURE);
432 				}
433 			} else {
434 				/* The first string is vendor id */
435 				if ((newdev->ident->vid = calloc(1,
436 				    (sp_temp_cut - sp_temp + 1))) == NULL) {
437 					logmsg(MSG_ERROR, gettext("%s: unable "
438 					    "to get sata vendor id of %s\n"),
439 					    newdev->drvname,
440 					    newdev->access_devname);
441 
442 					libscsi_close(handle, target);
443 					libscsi_fini(handle);
444 					FW_SD_FREE_IDENT(newdev, devpath)
445 					return (FWFLASH_FAILURE);
446 				}
447 				strlcpy(newdev->ident->vid, sp_temp,
448 				    sp_temp_cut - sp_temp + 1);
449 
450 				/* The second string is product id */
451 				if ((newdev->ident->pid =
452 				    calloc(1, strlen(sp_temp) -
453 				    strlen(newdev->ident->vid))) == NULL) {
454 					logmsg(MSG_ERROR, gettext("%s: unable "
455 					    "to get sata product id of %s\n"),
456 					    newdev->drvname,
457 					    newdev->access_devname);
458 
459 					libscsi_close(handle, target);
460 					libscsi_fini(handle);
461 					FW_SD_FREE_IDENT_VID(newdev, devpath)
462 					return (FWFLASH_FAILURE);
463 				}
464 				strlcpy(newdev->ident->pid, sp_temp_cut + 1,
465 				    strlen(sp_temp) -
466 				    strlen(newdev->ident->vid));
467 			}
468 		} else {
469 			if ((newdev->ident->pid =
470 			    calloc(1, strlen(sp_temp) + 1)) == NULL ||
471 			    sp_temp == NULL) {
472 				logmsg(MSG_ERROR, gettext("%s: unable to get "
473 				    "product id of %s\n"), newdev->drvname,
474 				    newdev->access_devname);
475 				FW_SD_FREE_IDENT_VID(newdev, devpath)
476 				libscsi_close(handle, target);
477 				libscsi_fini(handle);
478 				return (FWFLASH_FAILURE);
479 			}
480 			strlcpy(newdev->ident->pid, sp_temp,
481 			    strlen(sp_temp) + 1);
482 		}
483 
484 		/* Revision ID */
485 		sp_temp = (char *)libscsi_revision(target);
486 		if ((newdev->ident->revid = calloc(1, strlen(sp_temp) + 1))
487 		    == NULL || sp_temp == NULL) {
488 			logmsg(MSG_ERROR, gettext("%s: unable to get revision "
489 			    "id of %s\n"), newdev->drvname,
490 			    newdev->access_devname);
491 			libscsi_close(handle, target);
492 			libscsi_fini(handle);
493 			FW_SD_FREE_IDENT_PID(newdev, devpath)
494 			return (FWFLASH_FAILURE);
495 		}
496 		strlcpy(newdev->ident->revid, sp_temp, strlen(sp_temp) + 1);
497 
498 		/* Finish using libscsi */
499 		libscsi_close(handle, target);
500 
501 		if (di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode,
502 		    "inquiry-serial-no", &newdev->addresses[1]) < 0) {
503 			logmsg(MSG_INFO,
504 			    "%s: no inquiry-serial-no property for %s\n",
505 			    driver, newdev->access_devname);
506 			logmsg(MSG_INFO, "The errno is %d\n", errno);
507 		}
508 
509 		if ((di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode,
510 		    "client-guid", &newdev->addresses[2])) < 0) {
511 			logmsg(MSG_INFO,
512 			    "%s: no client-guid property "
513 			    "for device %s\n",
514 			    driver, newdev->access_devname);
515 			/* try fallback */
516 			if ((di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode,
517 			    "guid", &newdev->addresses[2])) < 0) {
518 				logmsg(MSG_INFO,
519 				    "%s: no guid property for device %s\n",
520 				    driver, newdev->access_devname);
521 			}
522 		} else {
523 			logmsg(MSG_INFO,
524 			    "client-guid property: %s\n",
525 			    newdev->addresses[2]);
526 		}
527 
528 		newdev->index = idx;
529 		++idx;
530 		newdev->plugin = self;
531 
532 		TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev);
533 		FW_SD_FREE_DEVPATH(devpath)
534 	}
535 	libscsi_fini(handle);
536 
537 	/* Check if sd targets presented are all unflashable. */
538 	if (idx == start)
539 		return (FWFLASH_FAILURE);
540 
541 	if (fwflash_debug != 0) {
542 		struct devicelist *tempdev;
543 
544 		TAILQ_FOREACH(tempdev, fw_devices, nextdev) {
545 			logmsg(MSG_INFO, "%s:fw_identify:\n",
546 			    driver);
547 			logmsg(MSG_INFO,
548 			    "\ttempdev @ 0x%lx\n"
549 			    "\t\taccess_devname: %s\n"
550 			    "\t\tdrvname: %s\tclassname: %s\n"
551 			    "\t\tident->vid:   %s\n"
552 			    "\t\tident->pid:   %s\n"
553 			    "\t\tident->revid: %s\n"
554 			    "\t\tindex:	%d\n"
555 			    "\t\taddress[0]:   %s\n"
556 			    "\t\taddress[1]:   %s\n"
557 			    "\t\taddress[2]:   %s\n"
558 			    "\t\tplugin @ 0x%lx\n\n",
559 			    &tempdev,
560 			    tempdev->access_devname,
561 			    tempdev->drvname, newdev->classname,
562 			    tempdev->ident->vid,
563 			    tempdev->ident->pid,
564 			    tempdev->ident->revid,
565 			    tempdev->index,
566 			    tempdev->addresses[0],
567 			    (tempdev->addresses[1] ? tempdev->addresses[1] :
568 			    "(not supported)"),
569 			    (tempdev->addresses[2] ? tempdev->addresses[2] :
570 			    "(not supported)"),
571 			    &tempdev->plugin);
572 		}
573 	}
574 	return (FWFLASH_SUCCESS);
575 }
576 
577 int
578 fw_devinfo(struct devicelist *thisdev)
579 {
580 	fprintf(stdout, gettext("Device[%d]\t\t\t%s\n"
581 	    "  Class [%s]\t\t\t%s\n"),
582 	    thisdev->index, thisdev->access_devname,
583 	    thisdev->classname, thisdev->addresses[0]);
584 
585 	fprintf(stdout,
586 	    gettext(
587 	    "\tVendor\t\t\t: %s\n"
588 	    "\tProduct\t\t\t: %s\n"
589 	    "\tFirmware revision\t: %-s\n"
590 	    "\tInquiry Serial Number   : %-s\n"
591 	    "\tGUID\t\t\t: %s\n"),
592 	    thisdev->ident->vid,
593 	    thisdev->ident->pid,
594 	    thisdev->ident->revid,
595 	    (thisdev->addresses[1] ? thisdev->addresses[1] :
596 	    "(not supported)"),
597 	    (thisdev->addresses[2] ? thisdev->addresses[2] :
598 	    "(not supported)"));
599 
600 	fprintf(stdout, "\n\n");
601 
602 	return (FWFLASH_SUCCESS);
603 }
604 
605 void
606 fw_cleanup(struct devicelist *thisdev)
607 {
608 	/*
609 	 * Function to clean up all the memory allocated
610 	 * by this plugin, for thisdev.
611 	 */
612 	free(thisdev->access_devname);
613 	free(thisdev->drvname);
614 	free(thisdev->classname);
615 
616 	/*
617 	 * Note that we DO NOT free addresses[1,2] because _IF_
618 	 * these elements are valid, they are managed by libdevinfo
619 	 * and we didn't allocate any space for them.
620 	 */
621 	free(thisdev->addresses[0]);
622 
623 	/* what this points to is freed in common code */
624 	thisdev->plugin = NULL;
625 
626 	free(thisdev->ident->vid);
627 	free(thisdev->ident->pid);
628 	free(thisdev->ident->revid);
629 
630 	thisdev->ident = NULL;
631 }
632 
633 /*
634  * Helper functions
635  */
636 static int
637 link_cb(di_devlink_t devlink, void *arg)
638 {
639 	const char *result;
640 
641 	result = di_devlink_path(devlink);
642 	if (result == NULL) {
643 		arg = (void *)"(null)";
644 	} else {
645 		(void) strlcpy(arg, result, strlen(result) + 1);
646 	}
647 
648 	logmsg(MSG_INFO, "\nlink_cb::linkdata->resultstr = %s\n",
649 	    ((result != NULL) ? result : "(null)"));
650 
651 	return (DI_WALK_CONTINUE);
652 }
653 
654 static char *
655 find_link(di_node_t bnode, char *acc_devname)
656 {
657 	di_minor_t devminor = DI_MINOR_NIL;
658 	di_devlink_handle_t hdl;
659 	char *cbresult = NULL;
660 	char linkname[] = "^rdsk/\0";
661 
662 	if (bnode == DI_NODE_NIL) {
663 		logmsg(MSG_ERROR,
664 		    gettext("find_link must be called with non-null "
665 		    "di_node_t\n"));
666 		return (NULL);
667 	}
668 
669 	if ((cbresult = calloc(1, MAXPATHLEN)) == NULL) {
670 		logmsg(MSG_ERROR, gettext("unable to allocate space for dev "
671 		    "link\n"));
672 		return (NULL);
673 	}
674 
675 	devminor = di_minor_next(bnode, devminor);
676 	errno = 0;
677 	hdl = di_devlink_init(di_devfs_minor_path(devminor), DI_MAKE_LINK);
678 	if (hdl == NULL) {
679 		if (errno == EPERM || errno == EACCES) {
680 			logmsg(MSG_ERROR,
681 			    gettext("%s: You must be super-user to use this "
682 			    "plugin.\n"), drivername);
683 		} else {
684 			logmsg(MSG_ERROR,
685 			    gettext("unable to take devlink snapshot: %s\n"),
686 			    strerror(errno));
687 		}
688 		free(cbresult);
689 		return (NULL);
690 	}
691 
692 	errno = 0;
693 	if (di_devlink_walk(hdl, linkname, acc_devname + strlen(devprefix),
694 	    DI_PRIMARY_LINK, (void *)cbresult, link_cb) < 0) {
695 		logmsg(MSG_ERROR,
696 		    gettext("Unable to walk devlink snapshot for %s: %s\n"),
697 		    acc_devname, strerror(errno));
698 		free(cbresult);
699 		return (NULL);
700 	}
701 
702 	if (di_devlink_fini(&hdl) < 0) {
703 		logmsg(MSG_ERROR,
704 		    gettext("Unable to close devlink snapshot: %s\n"),
705 		    strerror(errno));
706 	}
707 
708 	logmsg(MSG_INFO, "cbresult: %s\n", cbresult);
709 	return (cbresult);
710 }
711 
712 static int
713 sd_idtfy_custmz(struct devicelist *device, char *sp)
714 {
715 	/* vid customization */
716 	if (strncmp(sp, "ST", 2) == 0) {
717 		/* Customize retail Seagate disks */
718 		if ((device->ident->vid = strdup("SEAGATE")) == NULL) {
719 			return (FWFLASH_FAILURE);
720 		}
721 	} else if (strncmp(sp, "SSD", 3) == 0) {
722 		/* Customize retail INTEL disks */
723 		if ((device->ident->vid = strdup("INTEL")) == NULL) {
724 			return (FWFLASH_FAILURE);
725 		}
726 	} else {
727 		/* disks to do in the furture, fill 'ATA' first */
728 		if ((device->ident->vid = strdup("ATA")) == NULL) {
729 			return (FWFLASH_FAILURE);
730 		}
731 	}
732 
733 	/* pid customization */
734 	if ((device->ident->pid = calloc(1, strlen(sp) + 1)) == NULL) {
735 		logmsg(MSG_ERROR, gettext("Unable to allocate space for "
736 		    "product id\n"));
737 		free(device->ident->vid);
738 		return (FWFLASH_FAILURE);
739 	}
740 	strlcpy(device->ident->pid, sp, strlen(sp) + 1);
741 
742 	return (FWFLASH_SUCCESS);
743 }
744