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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * ses (SCSI Generic Device) specific functions.
30 #include <libnvpair.h>
34 #include <sys/types.h>
35 #include <sys/sysmacros.h>
36 #include <sys/queue.h>
39 #include <scsi/libscsi.h>
40 #include <scsi/libses.h>
41 #include <libintl.h> /* for gettext(3c) */
42 #include <fwflash/fwflash.h>
48 #define SASADDRLEN 0x10
51 #define STATBUFLEN 0xfe
52 #define INQBUFLEN 0x80
55 #define UCODE_CHECK_STATUS 0
56 #define UCODE_CHECK_SUPPORTED 1
58 typedef struct ucode_statdesc
{
65 static ucode_statdesc_t ucode_statdesc_table
[] = {
66 { SES2_DLUCODE_S_NOP
, "none", B_FALSE
, B_FALSE
},
67 { SES2_DLUCODE_S_INPROGRESS
, "in progress", B_TRUE
, B_FALSE
},
68 { SES2_DLUCODE_S_SAVING
, "saved", B_TRUE
, B_FALSE
},
69 { SES2_DLUCODE_S_COMPLETE_NOW
, "completed (available)", B_FALSE
,
71 { SES2_DLUCODE_S_COMPLETE_AT_RESET
,
72 "completed (need reset or power on)", B_FALSE
, B_FALSE
},
73 { SES2_DLUCODE_S_COMPLETE_AT_POWERON
, "completed (need power on)",
75 { SES2_DLUCODE_S_PAGE_ERR
, "page error (offset %d)",
77 { SES2_DLUCODE_S_IMAGE_ERR
, "invalid image",
79 { SES2_DLUCODE_S_TIMEOUT
, "download timeout",
81 { SES2_DLUCODE_S_INTERNAL_NEEDIMAGE
,
82 "internal error (NEED NEW IMAGE BEFORE RESET)",
84 { SES2_DLUCODE_S_INTERNAL_SAFE
,
85 "internal error (reset to revert to backup)",
89 #define NUCODE_STATUS \
90 (sizeof (ucode_statdesc_table) / sizeof (ucode_statdesc_table[0]))
92 typedef struct ucode_status
{
99 typedef struct ucode_wait
{
100 uint64_t uw_prevstatus
;
101 boolean_t uw_pending
;
102 ses_node_t
*uw_oldnp
;
106 typedef struct sam4_statdesc
{
112 static sam4_statdesc_t sam4_status
[] = {
113 { SAM4_STATUS_GOOD
, "Status: GOOD (success)" },
114 { SAM4_STATUS_CHECK_CONDITION
, "Status: CHECK CONDITION" },
115 { SAM4_STATUS_CONDITION_MET
, "Status: CONDITION MET" },
116 { SAM4_STATUS_BUSY
, "Status: Device is BUSY" },
117 { SAM4_STATUS_RESERVATION_CONFLICT
, "Status: Device is RESERVED" },
118 { SAM4_STATUS_TASK_SET_FULL
,
119 "Status: TASK SET FULL (insufficient resources in command queue" },
120 { SAM4_STATUS_TASK_ABORTED
, "Status: TASK ABORTED" },
124 #define NSAM4_STATUS \
125 (sizeof (sam4_status) / sizeof (sam4_status[0]))
129 char drivername
[] = "ses\0";
130 static char *devprefix
= "/devices";
131 static char *sessuffix
= ":0";
132 static char *sgensuffix
= ":ses";
135 static ses_target_t
*ses_target
;
137 extern di_node_t rootnode
;
139 extern struct fw_plugin
*self
;
140 extern struct vrfyplugin
*verifier
;
141 extern int fwflash_debug
;
144 /* required functions for this plugin */
145 int fw_readfw(struct devicelist
*device
, char *filename
);
146 int fw_writefw(struct devicelist
*device
);
147 int fw_identify(int start
);
148 int fw_devinfo(struct devicelist
*thisdev
);
151 /* helper functions */
152 static int print_updated_status(ses_node_t
*np
, void *arg
);
153 static int get_status(nvlist_t
*props
, ucode_status_t
*sp
);
154 static int sendimg(ses_node_t
*np
, void *data
);
155 static int scsi_writebuf();
158 * We don't currently support reading firmware from a SAS
159 * expander. If we do eventually support it, we would use
160 * the scsi READ BUFFER command to do so.
163 fw_readfw(struct devicelist
*flashdev
, char *filename
)
167 "%s: not writing firmware for device %s to file %s\n",
168 flashdev
->drvname
, flashdev
->access_devname
, filename
);
170 gettext("\n\nReading of firmware images from %s-attached "
171 "devices is not supported\n\n"),
174 return (FWFLASH_SUCCESS
);
179 * If we're invoking fw_writefw, then flashdev is a valid,
180 * flashable device supporting the SES2 Download Microcode Diagnostic
181 * Control page (0x0e).
183 * If verifier is null, then we haven't been called following a firmware
184 * image verification load operation.
186 * *THIS* function uses scsi SEND DIAGNOSTIC/download microcode to
187 * achieve the task... if you chase down to the bottom of libses you
191 fw_writefw(struct devicelist
*flashdev
)
193 int rv
= FWFLASH_FAILURE
;
195 ses_snap_t
*snapshot
;
196 ses_node_t
*targetnode
;
198 if ((verifier
== NULL
) || (verifier
->imgsize
== 0) ||
199 (verifier
->fwimage
== NULL
)) {
200 /* should _not_ happen */
202 gettext("%s: Firmware image has not "
205 return (FWFLASH_FAILURE
);
208 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0 ||
209 nvlist_add_uint64(nvl
, SES_CTL_PROP_UCODE_MODE
,
210 SES_DLUCODE_M_WITH_OFFS
) != 0) {
212 gettext("%s: Unable to allocate "
213 "space for device prop list\n"),
215 return (FWFLASH_FAILURE
);
218 fprintf(stdout
, "\n"); /* get a fresh line for progress updates */
220 if (nvlist_add_uint64(nvl
, SES_CTL_PROP_UCODE_BUFID
,
221 verifier
->flashbuf
) != 0) {
223 gettext("%s: Unable to add buffer id "
224 "property, hence unable to flash device\n"),
229 if (nvlist_add_byte_array(nvl
, SES_CTL_PROP_UCODE_DATA
,
230 (uint8_t *)verifier
->fwimage
, verifier
->imgsize
) != 0) {
232 "%s: Out of memory for property addition\n",
238 ses_open(LIBSES_VERSION
, flashdev
->access_devname
)) == NULL
) {
240 gettext("%s: Unable to open flashable device %s\n"),
241 flashdev
->drvname
, flashdev
->access_devname
);
245 snapshot
= ses_snap_hold(ses_target
);
247 if ((targetnode
= ses_snap_primary_enclosure(snapshot
)) == NULL
) {
249 gettext("%s: Unable to locate primary enclosure for "
251 flashdev
->access_devname
);
253 rv
= sendimg(targetnode
, nvl
);
254 if (rv
== FWFLASH_SUCCESS
) {
256 gettext("%s: Done. New image will be active "
257 "after the system is rebooted.\n\n"),
261 "%s: unable to flash image %s to device %s\n\n",
262 flashdev
->drvname
, verifier
->imgfile
,
263 flashdev
->access_devname
);
267 ses_snap_rele(snapshot
);
268 ses_close(ses_target
);
277 * The fw_identify() function walks the device
278 * tree trying to find devices which this plugin
281 * The parameter "start" gives us the starting index number
282 * to give the device when we add it to the fw_devices list.
284 * firstdev is allocated by us and we add space as needed
287 fw_identify(int start
)
290 int rv
= FWFLASH_FAILURE
;
292 struct devicelist
*newdev
;
297 size_t devlength
= 0;
299 ses_snap_t
*snapshot
;
300 ses_node_t
*rootnodep
, *nodep
;
303 if (strcmp(self
->drvname
, "sgen") == 0) {
304 devsuffix
= sgensuffix
;
305 driver
= self
->drvname
;
307 devsuffix
= sessuffix
;
311 thisnode
= di_drv_first_node(driver
, rootnode
);
313 if (thisnode
== DI_NODE_NIL
) {
314 logmsg(MSG_INFO
, gettext("No %s nodes in this system\n"),
316 return (FWFLASH_FAILURE
);
319 if ((devpath
= calloc(1, MAXPATHLEN
+ 1)) == NULL
) {
321 gettext("%s: Unable to allocate space "
322 "for a device node\n"),
324 return (FWFLASH_FAILURE
);
327 /* we've found one, at least */
329 for (; thisnode
!= DI_NODE_NIL
; thisnode
= di_drv_next_node(thisnode
)) {
331 devpath
= di_devfs_path(thisnode
);
333 if ((newdev
= calloc(1, sizeof (struct devicelist
)))
336 gettext("%s: identification function unable "
337 "to allocate space for device entry\n"),
340 return (FWFLASH_FAILURE
);
343 /* calloc enough for /devices + devpath + devsuffix + '\0' */
344 devlength
= strlen(devpath
) + strlen(devprefix
) +
345 strlen(devsuffix
) + 2;
347 if ((newdev
->access_devname
= calloc(1, devlength
)) == NULL
) {
349 gettext("%s: Unable to allocate "
350 "space for a devfs name\n"),
354 return (FWFLASH_FAILURE
);
356 snprintf(newdev
->access_devname
, devlength
,
357 "%s%s%s", devprefix
, devpath
, devsuffix
);
359 if ((newdev
->drvname
= calloc(1, strlen(driver
) + 1))
362 gettext("%s: Unable to allocate "
363 "space to store a driver name\n"),
365 free(newdev
->access_devname
);
368 return (FWFLASH_FAILURE
);
370 (void) strlcpy(newdev
->drvname
, driver
,
373 if ((newdev
->classname
= calloc(1, strlen(driver
) + 1))
376 gettext("%s: Unable to malloc "
377 "space for a class name\n"),
379 free(newdev
->access_devname
);
380 free(newdev
->drvname
);
383 return (FWFLASH_FAILURE
);
385 (void) strlcpy(newdev
->classname
, driver
,
389 * Only alloc as much as we truly need, and DON'T forget
390 * that libnvpair manages the memory for property lookups!
391 * The same goes for libdevinfo properties.
393 * Also note that we're allocating here before we try to
394 * ses_open() the target, because if we can't allocate
395 * sufficient space then we might as well go home.
397 newdev
->ident
= calloc(1, VIDLEN
+ PIDLEN
+ REVLEN
+ 3);
398 if (newdev
->ident
== NULL
) {
400 gettext("%s: Unable to malloc space for"
401 "SCSI INQUIRY data\n"), driver
);
402 free(newdev
->classname
);
403 free(newdev
->drvname
);
404 free(newdev
->access_devname
);
407 return (FWFLASH_FAILURE
);
411 ses_open(LIBSES_VERSION
, newdev
->access_devname
))
414 gettext("%s: Unable to open device %s\n"),
415 driver
, newdev
->access_devname
);
417 free(newdev
->classname
);
418 free(newdev
->access_devname
);
419 free(newdev
->drvname
);
424 snapshot
= ses_snap_hold(ses_target
);
425 rootnodep
= ses_root_node(snapshot
);
428 * If the node has no properties, or the INQUIRY properties
429 * don't exist, this device does not comply with SES2 so we
432 if ((props
= ses_node_props(rootnodep
)) == NULL
) {
434 ses_snap_rele(snapshot
);
435 ses_close(ses_target
);
436 free(newdev
->classname
);
437 free(newdev
->access_devname
);
438 free(newdev
->drvname
);
444 if ((nvlist_lookup_string(props
, SCSI_PROP_VENDOR
,
445 &newdev
->ident
->vid
) != 0) ||
446 (nvlist_lookup_string(props
, SCSI_PROP_PRODUCT
,
447 &newdev
->ident
->pid
) != 0) ||
448 (nvlist_lookup_string(props
, SCSI_PROP_REVISION
,
449 &newdev
->ident
->revid
) != 0)) {
451 ses_snap_rele(snapshot
);
452 ses_close(ses_target
);
453 free(newdev
->classname
);
454 free(newdev
->access_devname
);
455 free(newdev
->drvname
);
461 nodep
= ses_snap_primary_enclosure(snapshot
);
463 if ((props
= ses_node_props(nodep
)) == NULL
) {
465 ses_snap_rele(snapshot
);
466 ses_close(ses_target
);
467 free(newdev
->classname
);
468 free(newdev
->access_devname
);
469 free(newdev
->drvname
);
476 "\nvid: %s\npid: %s\nrevid: %s\n",
479 newdev
->ident
->revid
);
481 if (nvlist_lookup_string(props
, LIBSES_EN_PROP_CSN
,
482 &newdev
->addresses
[0]) == 0) {
484 "Chassis Serial Number: %s\n",
485 newdev
->addresses
[0]);
488 "%s: no chassis-serial-number property "
490 driver
, newdev
->access_devname
);
493 rv
= di_prop_lookup_strings(DDI_DEV_T_ANY
,
494 thisnode
, "target-port", &newdev
->addresses
[1]);
497 "%s: no target-port property "
499 driver
, newdev
->access_devname
);
502 "target-port property: %s\n",
503 newdev
->addresses
[1]);
508 newdev
->plugin
= self
;
510 ses_snap_rele(snapshot
);
511 TAILQ_INSERT_TAIL(fw_devices
, newdev
, nextdev
);
515 if (fwflash_debug
!= 0) {
516 struct devicelist
*tempdev
;
518 TAILQ_FOREACH(tempdev
, fw_devices
, nextdev
) {
519 logmsg(MSG_INFO
, "%s:fw_identify:\n",
522 "\ttempdev @ 0x%lx\n"
523 "\t\taccess_devname: %s\n"
524 "\t\tdrvname: %s\tclassname: %s\n"
525 "\t\tident->vid: %s\n"
526 "\t\tident->pid: %s\n"
527 "\t\tident->revid: %s\n"
529 "\t\taddress[0]: %s\n"
530 "\t\taddress[1]: %s\n"
531 "\t\tplugin @ 0x%lx\n\n",
533 tempdev
->access_devname
,
534 tempdev
->drvname
, newdev
->classname
,
537 tempdev
->ident
->revid
,
539 (tempdev
->addresses
[0] ? tempdev
->addresses
[0] :
541 (tempdev
->addresses
[1] ? tempdev
->addresses
[1] :
547 return (FWFLASH_SUCCESS
);
553 fw_devinfo(struct devicelist
*thisdev
)
556 fprintf(stdout
, gettext("Device[%d] %s\n Class [%s]\n"),
557 thisdev
->index
, thisdev
->access_devname
, thisdev
->classname
);
560 gettext("\tVendor : %s\n"
562 "\tFirmware revision : %s\n"
563 "\tChassis Serial Number : %s\n"
564 "\tTarget-port identifier : %s\n"),
567 thisdev
->ident
->revid
,
568 (thisdev
->addresses
[0] ? thisdev
->addresses
[0] :
570 (thisdev
->addresses
[1] ? thisdev
->addresses
[1] :
573 fprintf(stdout
, "\n\n");
575 return (FWFLASH_SUCCESS
);
584 get_status(nvlist_t
*props
, ucode_status_t
*sp
)
587 uint64_t status
, astatus
;
589 if (nvlist_lookup_uint64(props
, SES_EN_PROP_UCODE
, &status
) != 0) {
590 sp
->us_status
= -1ULL;
591 (void) snprintf(sp
->us_desc
, sizeof (sp
->us_desc
),
593 return (FWFLASH_FAILURE
);
596 if (nvlist_lookup_uint64(props
, SES_EN_PROP_UCODE_A
,
599 gettext("\nError: Unable to retrieve current status\n"));
600 return (FWFLASH_FAILURE
);
603 for (i
= 0; i
< NUCODE_STATUS
; i
++) {
604 if (ucode_statdesc_table
[i
].us_value
== status
)
608 sp
->us_status
= status
;
610 if (i
== NUCODE_STATUS
) {
611 (void) snprintf(sp
->us_desc
, sizeof (sp
->us_desc
),
612 "unknown (0x%02x)", (int)status
);
613 sp
->us_iserr
= sp
->us_pending
= B_TRUE
;
614 return (FWFLASH_FAILURE
);
617 (void) snprintf(sp
->us_desc
, sizeof (sp
->us_desc
),
618 ucode_statdesc_table
[i
].us_desc
, (int)astatus
);
619 sp
->us_iserr
= ucode_statdesc_table
[i
].us_iserr
;
620 sp
->us_pending
= ucode_statdesc_table
[i
].us_pending
;
623 return (FWFLASH_SUCCESS
);
628 print_updated_status(ses_node_t
*np
, void *arg
)
630 ucode_wait_t
*uwp
= arg
;
632 ucode_status_t status
;
635 if ((props
= ses_node_props(np
)) == NULL
) {
636 return (FWFLASH_FAILURE
);
639 if (get_status(props
, &status
) != FWFLASH_SUCCESS
)
640 return (FWFLASH_FAILURE
);
642 if (status
.us_status
!= uwp
->uw_prevstatus
)
643 (void) printf("%30s: %s\n", "status", status
.us_desc
);
645 uwp
->uw_prevstatus
= status
.us_status
;
646 uwp
->uw_pending
= status
.us_pending
;
648 if (status
.us_iserr
) {
650 "libses: status.us_iserr: 0x%0x\n",
652 return (FWFLASH_FAILURE
);
654 return (FWFLASH_SUCCESS
);
659 sendimg(ses_node_t
*np
, void *data
)
662 nvlist_t
*arg
= data
;
663 char *vendor
, *product
, *revision
, *csn
;
667 ucode_status_t statdesc
;
673 /* If we've been called without data, eject */
674 if (nvlist_lookup_byte_array(arg
, SES_CTL_PROP_UCODE_DATA
,
675 &imagedata
, &len
) != 0) {
676 return (FWFLASH_FAILURE
);
679 props
= ses_node_props(np
);
680 if ((props
== NULL
) ||
681 (nvlist_lookup_string(props
, SES_EN_PROP_VID
, &vendor
) != 0) ||
682 (nvlist_lookup_string(props
, SES_EN_PROP_PID
, &product
) != 0) ||
683 (nvlist_lookup_string(props
, SES_EN_PROP_REV
, &revision
) != 0) ||
684 (nvlist_lookup_string(props
, LIBSES_EN_PROP_CSN
, &csn
) != 0)) {
685 return (FWFLASH_FAILURE
);
688 (void) printf("%30s: %s\n", "vendor", vendor
);
689 (void) printf("%30s: %s\n", "product", product
);
690 (void) printf("%30s: %s\n", "revision", revision
);
691 (void) printf("%30s: %s\n", "serial", csn
);
693 ret
= get_status(props
, &statdesc
);
694 (void) printf("%30s: %s\n", "current status", statdesc
.us_desc
);
695 if (ret
!= FWFLASH_SUCCESS
) {
696 return (FWFLASH_FAILURE
);
699 (void) snprintf(buf
, sizeof (buf
), "downloading %u bytes", len
);
700 (void) printf("\n%30s: ", buf
);
703 * If the bufferid isn't 2, then the verifier has already
704 * OK'd the image that the user has provided.
706 * At present the non-"standard" images need to be flashed
707 * using the scsi WRITE BUFFER command
709 if (verifier
->flashbuf
!= 2)
710 return (scsi_writebuf());
713 if (ses_node_ctl(np
, SES_CTL_OP_DL_UCODE
, arg
) != FWFLASH_SUCCESS
) {
714 (void) printf("failed!\n");
715 (void) printf("%s\n", ses_errmsg());
716 return (FWFLASH_FAILURE
);
718 (void) printf("ok\n");
721 wait
.uw_prevstatus
= -1ULL;
724 if ((newsnap
= ses_snap_new(ses_target
)) == NULL
) {
726 "failed to update SES snapshot: %s",
728 return (FWFLASH_FAILURE
);
731 print_updated_status(ses_snap_primary_enclosure(newsnap
),
733 ses_snap_rele(newsnap
);
735 return (FWFLASH_SUCCESS
);
743 libscsi_action_t
*action
;
744 spc3_write_buffer_cdb_t
*wb_cdb
;
745 libscsi_hdl_t
*handle
;
746 libscsi_target_t
*target
;
747 sam4_status_t samstatus
;
750 target
= ses_scsi_target(ses_target
);
751 handle
= libscsi_get_handle(target
);
752 action
= libscsi_action_alloc(handle
, SPC3_CMD_WRITE_BUFFER
,
753 LIBSCSI_AF_WRITE
|LIBSCSI_AF_RQSENSE
,
754 (void *)verifier
->fwimage
, (size_t)verifier
->imgsize
);
756 wb_cdb
= (spc3_write_buffer_cdb_t
*)libscsi_action_get_cdb(action
);
758 wb_cdb
->wbc_mode
= SPC3_WB_MODE_DATA
;
759 wb_cdb
->wbc_bufferid
= verifier
->flashbuf
;
761 wb_cdb
->wbc_buffer_offset
[0] = 0;
762 wb_cdb
->wbc_buffer_offset
[1] = 0;
763 wb_cdb
->wbc_buffer_offset
[2] = 0;
765 wb_cdb
->wbc_parameter_list_len
[0] =
766 (verifier
->imgsize
& 0xff0000) >> 16;
767 wb_cdb
->wbc_parameter_list_len
[1] = (verifier
->imgsize
& 0xff00) >> 8;
768 wb_cdb
->wbc_parameter_list_len
[2] = (verifier
->imgsize
& 0xff);
770 ret
= libscsi_exec(action
, target
);
771 samstatus
= libscsi_action_get_status(action
);
774 "\nscsi_writebuffer: ret 0x%0x, samstatus 0x%0x\n",
777 if ((ret
!= FWFLASH_SUCCESS
) || samstatus
!= SAM4_STATUS_GOOD
) {
778 libscsi_action_free(action
);
779 return (FWFLASH_FAILURE
);
781 (void) printf("ok\n");
784 for (i
= 0; i
< NSAM4_STATUS
; i
++) {
785 if (sam4_status
[i
].status
== samstatus
) {
786 (void) printf("%s\n", (sam4_status
[i
].message
));
791 if (i
== NSAM4_STATUS
)
792 (void) printf("Status: UNKNOWN\n");
794 if (samstatus
== SAM4_STATUS_GOOD
) {
795 return (FWFLASH_SUCCESS
);
798 return (FWFLASH_FAILURE
);