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