diff options
-rw-r--r-- | lib/libcam/Makefile | 2 | ||||
-rw-r--r-- | sbin/camcontrol/camcontrol.8 | 218 | ||||
-rw-r--r-- | sbin/camcontrol/camcontrol.c | 1332 | ||||
-rw-r--r-- | sys/cam/cam.c | 38 | ||||
-rw-r--r-- | sys/cam/cam.h | 7 | ||||
-rw-r--r-- | sys/cam/cam_ccb.h | 88 | ||||
-rw-r--r-- | sys/cam/cam_periph.c | 24 | ||||
-rw-r--r-- | sys/cam/cam_xpt.c | 1 | ||||
-rw-r--r-- | sys/cam/cam_xpt_internal.h | 4 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_all.c | 28 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_all.h | 163 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_pass.c | 8 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_xpt.c | 220 | ||||
-rw-r--r-- | sys/cam/scsi/smp_all.c | 620 | ||||
-rw-r--r-- | sys/cam/scsi/smp_all.h | 520 | ||||
-rw-r--r-- | sys/conf/files | 1 | ||||
-rw-r--r-- | sys/dev/mps/mps.c | 56 | ||||
-rw-r--r-- | sys/dev/mps/mps_pci.c | 1 | ||||
-rw-r--r-- | sys/dev/mps/mps_sas.c | 346 | ||||
-rw-r--r-- | sys/dev/mps/mps_user.c | 2 | ||||
-rw-r--r-- | sys/dev/mps/mpsvar.h | 16 | ||||
-rw-r--r-- | sys/sys/param.h | 2 |
22 files changed, 3623 insertions, 74 deletions
diff --git a/lib/libcam/Makefile b/lib/libcam/Makefile index 1f819d0..c5773ea 100644 --- a/lib/libcam/Makefile +++ b/lib/libcam/Makefile @@ -3,7 +3,7 @@ LIB= cam SHLIBDIR?= /lib SRCS= camlib.c scsi_cmdparse.c scsi_all.c scsi_da.c scsi_sa.c cam.c \ - ata_all.c + ata_all.c smp_all.c INCS= camlib.h DPADD= ${LIBSBUF} diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8 index b85f81d..fc0609a 100644 --- a/sbin/camcontrol/camcontrol.8 +++ b/sbin/camcontrol/camcontrol.8 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 1, 2010 +.Dd November 30, 2010 .Dt CAMCONTROL 8 .Os .Sh NAME @@ -131,6 +131,43 @@ .Op Fl r Ar fmt .Ek .Nm +.Ic smpcmd +.Op device id +.Op generic args +.Aq Fl r Ar len Ar fmt Op args +.Aq Fl R Ar len Ar fmt Op args +.Nm +.Ic smprg +.Op device id +.Op generic args +.Op Fl l +.Nm +.Ic smppc +.Op device id +.Op generic args +.Aq Fl p Ar phy +.Op Fl l +.Op Fl o Ar operation +.Op Fl d Ar name +.Op Fl m Ar rate +.Op Fl M Ar rate +.Op Fl T Ar pp_timeout +.Op Fl a Ar enable|disable +.Op Fl A Ar enable|disable +.Op Fl s Ar enable|disable +.Op Fl S Ar enable|disable +.Nm +.Ic smpphylist +.Op device id +.Op generic args +.Op Fl l +.Op Fl q +.Nm +.Ic smpmaninfo +.Op device id +.Op generic args +.Op Fl l +.Nm .Ic debug .Op Fl I .Op Fl P @@ -554,6 +591,177 @@ If the format is .Sq - , 11 result registers will be written to standard output in hex. .El +.It Ic smpcmd +Allows the user to send an arbitrary Serial +Management Protocol (SMP) command to a device. +The +.Ic smpcmd +function requires the +.Fl r +argument to specify the SMP request to be sent, and the +.Fl R +argument to specify the format of the SMP response. +The syntax for the SMP request and response arguments is documented in +.Xr cam_cdbparse 3 . +.Pp +Note that SAS adapters that support SMP passthrough (at least the currently +known adapters) do not accept CRC bytes from the user in the request and do +not pass CRC bytes back to the user in the response. +Therefore users should not include the CRC bytes in the length of the +request and not expect CRC bytes to be returned in the response. +.Bl -tag -width 17n +.It Fl r Ar len Ar fmt Op args +This specifies the size of the SMP request, without the CRC bytes, and the +SMP request format. If the format is +.Sq - , +.Ar len +bytes of data will be read from standard input and written as the SMP +request. +.It Fl R Ar len Ar fmt Op args +This specifies the size of the buffer allocated for the SMP response, and +the SMP response format. +If the format is +.Sq - , +.Ar len +bytes of data will be allocated for the response and the response will be +written to standard output. +.El +.It Ic smprg +Allows the user to send the Serial Management Protocol (SMP) Report General +command to a device. +.Nm +will display the data returned by the Report General command. +If the SMP target supports the long response format, the additional data +will be requested and displayed automatically. +.Bl -tag -width 8n +.It Fl l +Request the long response format only. +Not all SMP targets support the long response format. +This option causes +.Nm +to skip sending the initial report general request without the long bit set +and only issue a report general request with the long bit set. +.El +.It Ic smppc +Allows the user to issue the Serial Management Protocol (SMP) PHY Control +command to a device. +This function should be used with some caution, as it can render devices +inaccessible, and could potentially cause data corruption as well. +The +.Fl p +argument is required to specify the PHY to operate on. +.Bl -tag -width 17n +.It Fl p Ar phy +Specify the PHY to operate on. +This argument is required. +.It Fl l +Request the long request/response format. +Not all SMP targets support the long response format. +For the PHY Control command, this currently only affects whether the +request length is set to a value other than 0. +.It Fl o Ar operation +Specify a PHY control operation. +Only one +.Fl o +operation may be specified. +The operation may be specified numerically (in decimal, hexadecimal, or octal) +or one of the following operation names may be specified: +.Bl -tag -width 16n +.It nop +No operation. +It is not necessary to specify this argument. +.It linkreset +Send the LINK RESET command to the phy. +.It hardreset +Send the HARD RESET command to the phy. +.It disable +Send the DISABLE command to the phy. +Note that the LINK RESET or HARD RESET commands should re-enable the phy. +.It clearerrorlog +Send the CLEAR ERROR LOG command. +This clears the error log counters for the specified phy. +.It clearaffiliation +Send the CLEAR AFFILIATION command. +This clears the affiliation from the STP initiator port with the same SAS +address as the SMP initiator that requests the clear operation. +.It sataportsel +Send the TRANSMIT SATA PORT SELECTION SIGNAL command to the phy. +This will cause a SATA port selector to use the given phy as its active phy +and make the other phy inactive. +.It clearitnl +Send the CLEAR STP I_T NEXUS LOSS command to the PHY. +.It setdevname +Send the SET ATTACHED DEVICE NAME command to the PHY. +This requires the +.Fl d +argument to specify the device name. +.El +.It Fl d Ar name +Specify the attached device name. +This option is needed with the +.Fl o Ar setdevname +phy operation. +The name is a 64-bit number, and can be specified in decimal, hexadecimal +or octal format. +.It Fl m Ar rate +Set the minimum physical link rate for the phy. +This is a numeric argument. +Currently known link rates are: +.Bl -tag -width 5n +.It 0x0 +Do not change current value. +.It 0x8 +1.5 Gbps +.It 0x9 +3 Gbps +.It 0xa +6 Gbps +.El +.Pp +Other values may be specified for newer physical link rates. +.It Fl M Ar rate +Set the maximum physical link rate for the phy. +This is a numeric argument. +See the +.Fl m +argument description for known link rate arguments. +.It Fl T Ar pp_timeout +Set the partial pathway timeout value, in microseconds. +See the +.Tn ANSI +.Tn SAS +Protcol Layer (SPL) +specification for more information on this field. +.It Fl a Ar enable|disable +Enable or disable SATA slumber phy power conditions. +.It Fl A Ar enable|disable +Enable or disable SATA partial power conditions. +.It Fl s Ar enable|disable +Enable or disable SAS slumber phy power conditions. +.It Fl S Ar enable|disable +Enable or disable SAS partial phy power conditions. +.El +.It Ic smpphylist +List phys attached to a SAS expander, the address of the end device +attached to the phy, and the inquiry data for that device and peripheral +devices attached to that device. +The inquiry data and peripheral devices are displayed if available. +.Bl -tag -width 5n +.It Fl l +Turn on the long response format for the underlying SMP commands used for +this command. +.It Fl q +Only print out phys that are attached to a device in the CAM EDT (Existing +Device Table). +.El +.It Ic smpmaninfo +Send the SMP Report Manufacturer Information command to the device and +display the response. +.Bl -tag -width 5n +.It Fl l +Turn on the long response format for the underlying SMP commands used for +this command. +.El .It Ic debug Turn on CAM debugging printfs in the kernel. This requires options CAMDEBUG @@ -965,6 +1173,14 @@ camcontrol negotiate -n da -u 3 -R 20.000 -O 15 -a Negotiate a sync rate of 20MHz and an offset of 15 with da3. Then send a Test Unit Ready command to make the settings take effect. +.Pp +.Bd -literal -offset indent +camcontrol smpcmd ses0 -v -r 4 "40 0 00 0" -R 1020 "s9 i1" +.Ed +.Pp +Send the SMP REPORT GENERAL command to ses0, and display the number of PHYs +it contains. +Display SMP errors if the command fails. .Sh SEE ALSO .Xr cam 3 , .Xr cam_cdbparse 3 , diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index 2379a8e..88ae1ac 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -33,11 +33,14 @@ __FBSDID("$FreeBSD$"); #include <sys/stdint.h> #include <sys/types.h> #include <sys/endian.h> +#include <sys/sbuf.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <inttypes.h> +#include <limits.h> #include <fcntl.h> #include <ctype.h> #include <err.h> @@ -50,6 +53,7 @@ __FBSDID("$FreeBSD$"); #include <cam/scsi/scsi_da.h> #include <cam/scsi/scsi_pass.h> #include <cam/scsi/scsi_message.h> +#include <cam/scsi/smp_all.h> #include <cam/ata/ata_all.h> #include <camlib.h> #include "camcontrol.h" @@ -77,7 +81,12 @@ typedef enum { CAM_CMD_IDENTIFY = 0x00000013, CAM_CMD_IDLE = 0x00000014, CAM_CMD_STANDBY = 0x00000015, - CAM_CMD_SLEEP = 0x00000016 + CAM_CMD_SLEEP = 0x00000016, + CAM_CMD_SMP_CMD = 0x00000017, + CAM_CMD_SMP_RG = 0x00000018, + CAM_CMD_SMP_PC = 0x00000019, + CAM_CMD_SMP_PHYLIST = 0x0000001a, + CAM_CMD_SMP_MANINFO = 0x0000001b } cam_cmdmask; typedef enum { @@ -117,7 +126,7 @@ typedef enum { struct camcontrol_opts { const char *optname; - cam_cmdmask cmdnum; + uint32_t cmdnum; cam_argmask argnum; const char *subopt; }; @@ -126,6 +135,9 @@ struct camcontrol_opts { static const char scsicmd_opts[] = "a:c:dfi:o:r"; static const char readdefect_opts[] = "f:GP"; static const char negotiate_opts[] = "acD:M:O:qR:T:UW:"; +static const char smprg_opts[] = "l"; +static const char smppc_opts[] = "a:A:d:lm:M:o:p:s:S:T:"; +static const char smpphylist_opts[] = "lq"; #endif struct camcontrol_opts option_table[] = { @@ -145,6 +157,14 @@ struct camcontrol_opts option_table[] = { #ifndef MINIMALISTIC {"cmd", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts}, {"command", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts}, + {"smpcmd", CAM_CMD_SMP_CMD, CAM_ARG_NONE, "r:R:"}, + {"smprg", CAM_CMD_SMP_RG, CAM_ARG_NONE, smprg_opts}, + {"smpreportgeneral", CAM_CMD_SMP_RG, CAM_ARG_NONE, smprg_opts}, + {"smppc", CAM_CMD_SMP_PC, CAM_ARG_NONE, smppc_opts}, + {"smpphycontrol", CAM_CMD_SMP_PC, CAM_ARG_NONE, smppc_opts}, + {"smpplist", CAM_CMD_SMP_PHYLIST, CAM_ARG_NONE, smpphylist_opts}, + {"smpphylist", CAM_CMD_SMP_PHYLIST, CAM_ARG_NONE, smpphylist_opts}, + {"smpmaninfo", CAM_CMD_SMP_MANINFO, CAM_ARG_NONE, "l"}, {"defects", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts}, {"defectlist", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts}, #endif /* MINIMALISTIC */ @@ -173,11 +193,25 @@ typedef enum { CC_OR_FOUND } camcontrol_optret; +struct cam_devitem { + struct device_match_result dev_match; + int num_periphs; + struct periph_match_result *periph_matches; + struct scsi_vpd_device_id *device_id; + int device_id_len; + STAILQ_ENTRY(cam_devitem) links; +}; + +struct cam_devlist { + STAILQ_HEAD(, cam_devitem) dev_queue; + path_id_t path_id; +}; + cam_cmdmask cmdlist; cam_argmask arglist; - -camcontrol_optret getoption(char *arg, cam_cmdmask *cmdnum, cam_argmask *argnum, +camcontrol_optret getoption(struct camcontrol_opts *table, char *arg, + uint32_t *cmdnum, cam_argmask *argnum, const char **subopt); #ifndef MINIMALISTIC static int getdevlist(struct cam_device *device); @@ -206,6 +240,21 @@ static void modepage(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout); static int scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout); +static int smpcmd(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); +static int smpreportgeneral(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); +static int smpphycontrol(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); +static int smpmaninfo(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); +static int getdevid(struct cam_devitem *item); +static int buildbusdevlist(struct cam_devlist *devlist); +static void freebusdevlist(struct cam_devlist *devlist); +static struct cam_devitem *findsasdevice(struct cam_devlist *devlist, + uint64_t sasaddr); +static int smpphylist(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); static int tagcontrol(struct cam_device *device, int argc, char **argv, char *combinedopt); static void cts_print(struct cam_device *device, @@ -234,13 +283,13 @@ static int atapm(struct cam_device *device, int argc, char **argv, #endif camcontrol_optret -getoption(char *arg, cam_cmdmask *cmdnum, cam_argmask *argnum, - const char **subopt) +getoption(struct camcontrol_opts *table, char *arg, uint32_t *cmdnum, + cam_argmask *argnum, const char **subopt) { struct camcontrol_opts *opts; int num_matches = 0; - for (opts = option_table; (opts != NULL) && (opts->optname != NULL); + for (opts = table; (opts != NULL) && (opts->optname != NULL); opts++) { if (strncmp(opts->optname, arg, strlen(arg)) == 0) { *cmdnum = opts->cmdnum; @@ -2455,10 +2504,12 @@ scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt, if (((retval = cam_send_ccb(device, ccb)) < 0) || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + const char *warnstr = "error sending command"; + if (retval < 0) - warn("error sending command"); + warn(warnstr); else - warnx("error sending command"); + warnx(warnstr); if (arglist & CAM_ARG_VERBOSE) { cam_error_print(device, ccb, CAM_ESF_ALL, @@ -4274,6 +4325,1207 @@ bailout: } static int +smpcmd(struct cam_device *device, int argc, char **argv, char *combinedopt, + int retry_count, int timeout) +{ + int c, error; + union ccb *ccb; + uint8_t *smp_request = NULL, *smp_response = NULL; + int request_size = 0, response_size = 0; + int fd_request = 0, fd_response = 0; + char *datastr = NULL; + struct get_hook hook; + int retval; + int flags = 0; + + /* + * Note that at the moment we don't support sending SMP CCBs to + * devices that aren't probed by CAM. + */ + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + return (1); + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'R': + arglist |= CAM_ARG_CMD_IN; + response_size = strtol(optarg, NULL, 0); + if (response_size <= 0) { + warnx("invalid number of response bytes %d", + response_size); + error = 1; + goto smpcmd_bailout; + } + hook.argc = argc - optind; + hook.argv = argv + optind; + hook.got = 0; + optind++; + datastr = cget(&hook, NULL); + /* + * If the user supplied "-" instead of a format, he + * wants the data to be written to stdout. + */ + if ((datastr != NULL) + && (datastr[0] == '-')) + fd_response = 1; + + smp_response = (u_int8_t *)malloc(response_size); + if (smp_response == NULL) { + warn("can't malloc memory for SMP response"); + error = 1; + goto smpcmd_bailout; + } + break; + case 'r': + arglist |= CAM_ARG_CMD_OUT; + request_size = strtol(optarg, NULL, 0); + if (request_size <= 0) { + warnx("invalid number of request bytes %d", + request_size); + error = 1; + goto smpcmd_bailout; + } + hook.argc = argc - optind; + hook.argv = argv + optind; + hook.got = 0; + datastr = cget(&hook, NULL); + smp_request = (u_int8_t *)malloc(request_size); + if (smp_request == NULL) { + warn("can't malloc memory for SMP request"); + error = 1; + goto smpcmd_bailout; + } + bzero(smp_request, request_size); + /* + * If the user supplied "-" instead of a format, he + * wants the data to be read from stdin. + */ + if ((datastr != NULL) + && (datastr[0] == '-')) + fd_request = 1; + else + buff_encode_visit(smp_request, request_size, + datastr, + iget, &hook); + optind += hook.got; + break; + default: + break; + } + } + + /* + * If fd_data is set, and we're writing to the device, we need to + * read the data the user wants written from stdin. + */ + if ((fd_request == 1) && (arglist & CAM_ARG_CMD_OUT)) { + ssize_t amt_read; + int amt_to_read = request_size; + u_int8_t *buf_ptr = smp_request; + + for (amt_read = 0; amt_to_read > 0; + amt_read = read(STDIN_FILENO, buf_ptr, amt_to_read)) { + if (amt_read == -1) { + warn("error reading data from stdin"); + error = 1; + goto smpcmd_bailout; + } + amt_to_read -= amt_read; + buf_ptr += amt_read; + } + } + + if (((arglist & CAM_ARG_CMD_IN) == 0) + || ((arglist & CAM_ARG_CMD_OUT) == 0)) { + warnx("%s: need both the request (-r) and response (-R) " + "arguments", __func__); + error = 1; + goto smpcmd_bailout; + } + + flags |= CAM_DEV_QFRZDIS; + + cam_fill_smpio(&ccb->smpio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*flags*/ flags, + /*smp_request*/ smp_request, + /*smp_request_len*/ request_size, + /*smp_response*/ smp_response, + /*smp_response_len*/ response_size, + /*timeout*/ timeout ? timeout : 5000); + + ccb->smpio.flags = SMP_FLAG_NONE; + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + const char *warnstr = "error sending command"; + + if (retval < 0) + warn(warnstr); + else + warnx(warnstr); + + if (arglist & CAM_ARG_VERBOSE) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + } + } + + if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) + && (response_size > 0)) { + if (fd_response == 0) { + buff_decode_visit(smp_response, response_size, + datastr, arg_put, NULL); + fprintf(stdout, "\n"); + } else { + ssize_t amt_written; + int amt_to_write = response_size; + u_int8_t *buf_ptr = smp_response; + + for (amt_written = 0; (amt_to_write > 0) && + (amt_written = write(STDOUT_FILENO, buf_ptr, + amt_to_write)) > 0;){ + amt_to_write -= amt_written; + buf_ptr += amt_written; + } + if (amt_written == -1) { + warn("error writing data to stdout"); + error = 1; + goto smpcmd_bailout; + } else if ((amt_written == 0) + && (amt_to_write > 0)) { + warnx("only wrote %u bytes out of %u", + response_size - amt_to_write, + response_size); + } + } + } +smpcmd_bailout: + if (ccb != NULL) + cam_freeccb(ccb); + + if (smp_request != NULL) + free(smp_request); + + if (smp_response != NULL) + free(smp_response); + + return (error); +} + +static int +smpreportgeneral(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout) +{ + union ccb *ccb; + struct smp_report_general_request *request = NULL; + struct smp_report_general_response *response = NULL; + struct sbuf *sb = NULL; + int error = 0; + int c, long_response = 0; + int retval; + + /* + * Note that at the moment we don't support sending SMP CCBs to + * devices that aren't probed by CAM. + */ + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + return (1); + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'l': + long_response = 1; + break; + default: + break; + } + } + request = malloc(sizeof(*request)); + if (request == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*request)); + error = 1; + goto bailout; + } + + response = malloc(sizeof(*response)); + if (response == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*response)); + error = 1; + goto bailout; + } + +try_long: + smp_report_general(&ccb->smpio, + retry_count, + /*cbfcnp*/ NULL, + request, + /*request_len*/ sizeof(*request), + (uint8_t *)response, + /*response_len*/ sizeof(*response), + /*long_response*/ long_response, + timeout); + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + const char *warnstr = "error sending command"; + + if (retval < 0) + warn(warnstr); + else + warnx(warnstr); + + if (arglist & CAM_ARG_VERBOSE) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + } + error = 1; + goto bailout; + } + + /* + * If the device supports the long response bit, try again and see + * if we can get all of the data. + */ + if ((response->long_response & SMP_RG_LONG_RESPONSE) + && (long_response == 0)) { + ccb->ccb_h.status = CAM_REQ_INPROG; + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + long_response = 1; + goto try_long; + } + + /* + * XXX KDM detect and decode SMP errors here. + */ + sb = sbuf_new_auto(); + if (sb == NULL) { + warnx("%s: error allocating sbuf", __func__); + goto bailout; + } + + smp_report_general_sbuf(response, sizeof(*response), sb); + + sbuf_finish(sb); + + printf("%s", sbuf_data(sb)); + +bailout: + if (ccb != NULL) + cam_freeccb(ccb); + + if (request != NULL) + free(request); + + if (response != NULL) + free(response); + + if (sb != NULL) + sbuf_delete(sb); + + return (error); +} + +struct camcontrol_opts phy_ops[] = { + {"nop", SMP_PC_PHY_OP_NOP, CAM_ARG_NONE, NULL}, + {"linkreset", SMP_PC_PHY_OP_LINK_RESET, CAM_ARG_NONE, NULL}, + {"hardreset", SMP_PC_PHY_OP_HARD_RESET, CAM_ARG_NONE, NULL}, + {"disable", SMP_PC_PHY_OP_DISABLE, CAM_ARG_NONE, NULL}, + {"clearerrlog", SMP_PC_PHY_OP_CLEAR_ERR_LOG, CAM_ARG_NONE, NULL}, + {"clearaffiliation", SMP_PC_PHY_OP_CLEAR_AFFILIATON, CAM_ARG_NONE,NULL}, + {"sataportsel", SMP_PC_PHY_OP_TRANS_SATA_PSS, CAM_ARG_NONE, NULL}, + {"clearitnl", SMP_PC_PHY_OP_CLEAR_STP_ITN_LS, CAM_ARG_NONE, NULL}, + {"setdevname", SMP_PC_PHY_OP_SET_ATT_DEV_NAME, CAM_ARG_NONE, NULL}, + {NULL, 0, 0, NULL} +}; + +static int +smpphycontrol(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout) +{ + union ccb *ccb; + struct smp_phy_control_request *request = NULL; + struct smp_phy_control_response *response = NULL; + int long_response = 0; + int retval = 0; + int phy = -1; + uint32_t phy_operation = SMP_PC_PHY_OP_NOP; + int phy_op_set = 0; + uint64_t attached_dev_name = 0; + int dev_name_set = 0; + uint32_t min_plr = 0, max_plr = 0; + uint32_t pp_timeout_val = 0; + int slumber_partial = 0; + int set_pp_timeout_val = 0; + int c; + + /* + * Note that at the moment we don't support sending SMP CCBs to + * devices that aren't probed by CAM. + */ + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + return (1); + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'a': + case 'A': + case 's': + case 'S': { + int enable = -1; + + if (strcasecmp(optarg, "enable") == 0) + enable = 1; + else if (strcasecmp(optarg, "disable") == 0) + enable = 2; + else { + warnx("%s: Invalid argument %s", __func__, + optarg); + retval = 1; + goto bailout; + } + switch (c) { + case 's': + slumber_partial |= enable << + SMP_PC_SAS_SLUMBER_SHIFT; + break; + case 'S': + slumber_partial |= enable << + SMP_PC_SAS_PARTIAL_SHIFT; + break; + case 'a': + slumber_partial |= enable << + SMP_PC_SATA_SLUMBER_SHIFT; + break; + case 'A': + slumber_partial |= enable << + SMP_PC_SATA_PARTIAL_SHIFT; + break; + default: + warnx("%s: programmer error", __func__); + retval = 1; + goto bailout; + break; /*NOTREACHED*/ + } + break; + } + case 'd': + attached_dev_name = (uintmax_t)strtoumax(optarg, + NULL,0); + dev_name_set = 1; + break; + case 'l': + long_response = 1; + break; + case 'm': + /* + * We don't do extensive checking here, so this + * will continue to work when new speeds come out. + */ + min_plr = strtoul(optarg, NULL, 0); + if ((min_plr == 0) + || (min_plr > 0xf)) { + warnx("%s: invalid link rate %x", + __func__, min_plr); + retval = 1; + goto bailout; + } + break; + case 'M': + /* + * We don't do extensive checking here, so this + * will continue to work when new speeds come out. + */ + max_plr = strtoul(optarg, NULL, 0); + if ((max_plr == 0) + || (max_plr > 0xf)) { + warnx("%s: invalid link rate %x", + __func__, max_plr); + retval = 1; + goto bailout; + } + break; + case 'o': { + camcontrol_optret optreturn; + cam_argmask argnums; + const char *subopt; + + if (phy_op_set != 0) { + warnx("%s: only one phy operation argument " + "(-o) allowed", __func__); + retval = 1; + goto bailout; + } + + phy_op_set = 1; + + /* + * Allow the user to specify the phy operation + * numerically, as well as with a name. This will + * future-proof it a bit, so options that are added + * in future specs can be used. + */ + if (isdigit(optarg[0])) { + phy_operation = strtoul(optarg, NULL, 0); + if ((phy_operation == 0) + || (phy_operation > 0xff)) { + warnx("%s: invalid phy operation %#x", + __func__, phy_operation); + retval = 1; + goto bailout; + } + break; + } + optreturn = getoption(phy_ops, optarg, &phy_operation, + &argnums, &subopt); + + if (optreturn == CC_OR_AMBIGUOUS) { + warnx("%s: ambiguous option %s", __func__, + optarg); + usage(0); + retval = 1; + goto bailout; + } else if (optreturn == CC_OR_NOT_FOUND) { + warnx("%s: option %s not found", __func__, + optarg); + usage(0); + retval = 1; + goto bailout; + } + break; + } + case 'p': + phy = atoi(optarg); + break; + case 'T': + pp_timeout_val = strtoul(optarg, NULL, 0); + if (pp_timeout_val > 15) { + warnx("%s: invalid partial pathway timeout " + "value %u, need a value less than 16", + __func__, pp_timeout_val); + retval = 1; + goto bailout; + } + set_pp_timeout_val = 1; + break; + default: + break; + } + } + + if (phy == -1) { + warnx("%s: a PHY (-p phy) argument is required",__func__); + retval = 1; + goto bailout; + } + + if (((dev_name_set != 0) + && (phy_operation != SMP_PC_PHY_OP_SET_ATT_DEV_NAME)) + || ((phy_operation == SMP_PC_PHY_OP_SET_ATT_DEV_NAME) + && (dev_name_set == 0))) { + warnx("%s: -d name and -o setdevname arguments both " + "required to set device name", __func__); + retval = 1; + goto bailout; + } + + request = malloc(sizeof(*request)); + if (request == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*request)); + retval = 1; + goto bailout; + } + + response = malloc(sizeof(*response)); + if (response == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*request)); + retval = 1; + goto bailout; + } + + smp_phy_control(&ccb->smpio, + retry_count, + /*cbfcnp*/ NULL, + request, + sizeof(*request), + (uint8_t *)response, + sizeof(*response), + long_response, + /*expected_exp_change_count*/ 0, + phy, + phy_operation, + (set_pp_timeout_val != 0) ? 1 : 0, + attached_dev_name, + min_plr, + max_plr, + slumber_partial, + pp_timeout_val, + timeout); + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + const char *warnstr = "error sending command"; + + if (retval < 0) + warn(warnstr); + else + warnx(warnstr); + + if (arglist & CAM_ARG_VERBOSE) { + /* + * Use CAM_EPF_NORMAL so we only get one line of + * SMP command decoding. + */ + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_NORMAL, stderr); + } + retval = 1; + goto bailout; + } + + /* XXX KDM print out something here for success? */ +bailout: + if (ccb != NULL) + cam_freeccb(ccb); + + if (request != NULL) + free(request); + + if (response != NULL) + free(response); + + return (retval); +} + +static int +smpmaninfo(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout) +{ + union ccb *ccb; + struct smp_report_manuf_info_request request; + struct smp_report_manuf_info_response response; + struct sbuf *sb = NULL; + int long_response = 0; + int retval = 0; + int c; + + /* + * Note that at the moment we don't support sending SMP CCBs to + * devices that aren't probed by CAM. + */ + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + return (1); + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'l': + long_response = 1; + break; + default: + break; + } + } + bzero(&request, sizeof(request)); + bzero(&response, sizeof(response)); + + smp_report_manuf_info(&ccb->smpio, + retry_count, + /*cbfcnp*/ NULL, + &request, + sizeof(request), + (uint8_t *)&response, + sizeof(response), + long_response, + timeout); + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + const char *warnstr = "error sending command"; + + if (retval < 0) + warn(warnstr); + else + warnx(warnstr); + + if (arglist & CAM_ARG_VERBOSE) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + } + retval = 1; + goto bailout; + } + + sb = sbuf_new_auto(); + if (sb == NULL) { + warnx("%s: error allocating sbuf", __func__); + goto bailout; + } + + smp_report_manuf_info_sbuf(&response, sizeof(response), sb); + + sbuf_finish(sb); + + printf("%s", sbuf_data(sb)); + +bailout: + + if (ccb != NULL) + cam_freeccb(ccb); + + if (sb != NULL) + sbuf_delete(sb); + + return (retval); +} + +static int +getdevid(struct cam_devitem *item) +{ + int retval = 0; + union ccb *ccb = NULL; + + struct cam_device *dev; + + dev = cam_open_btl(item->dev_match.path_id, + item->dev_match.target_id, + item->dev_match.target_lun, O_RDWR, NULL); + + if (dev == NULL) { + warnx("%s", cam_errbuf); + retval = 1; + goto bailout; + } + + item->device_id_len = CAM_SCSI_DEVID_MAXLEN; + item->device_id = malloc(item->device_id_len); + if (item->device_id == NULL) { + warn("%s: unable to allocate %d bytes", __func__, + item->device_id_len); + retval = 1; + goto bailout; + } + + ccb = cam_getccb(dev); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + retval = 1; + goto bailout; + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + ccb->ccb_h.func_code = XPT_GDEV_ADVINFO; + ccb->ccb_h.flags = CAM_DIR_IN; + ccb->cgdai.flags = CGDAI_FLAG_PROTO; + ccb->cgdai.buftype = CGDAI_TYPE_SCSI_DEVID; + ccb->cgdai.bufsiz = item->device_id_len; + ccb->cgdai.buf = (uint8_t *)item->device_id; + + if (cam_send_ccb(dev, ccb) < 0) { + warn("%s: error sending XPT_GDEV_ADVINFO CCB", __func__); + retval = 1; + goto bailout; + } + + if (ccb->ccb_h.status != CAM_REQ_CMP) { + warnx("%s: CAM status %#x", __func__, ccb->ccb_h.status); + retval = 1; + goto bailout; + } + +bailout: + if (dev != NULL) + cam_close_device(dev); + + if (ccb != NULL) + cam_freeccb(ccb); + + return (retval); +} + +/* + * XXX KDM merge this code with getdevtree()? + */ +static int +buildbusdevlist(struct cam_devlist *devlist) +{ + union ccb ccb; + int bufsize, fd = -1; + struct dev_match_pattern *patterns; + struct cam_devitem *item = NULL; + int skip_device = 0; + int retval = 0; + + if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { + warn("couldn't open %s", XPT_DEVICE); + return(1); + } + + bzero(&ccb, sizeof(union ccb)); + + ccb.ccb_h.path_id = CAM_XPT_PATH_ID; + ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; + ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; + + ccb.ccb_h.func_code = XPT_DEV_MATCH; + bufsize = sizeof(struct dev_match_result) * 100; + ccb.cdm.match_buf_len = bufsize; + ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); + if (ccb.cdm.matches == NULL) { + warnx("can't malloc memory for matches"); + close(fd); + return(1); + } + ccb.cdm.num_matches = 0; + ccb.cdm.num_patterns = 2; + ccb.cdm.pattern_buf_len = sizeof(struct dev_match_pattern) * + ccb.cdm.num_patterns; + + patterns = (struct dev_match_pattern *)malloc(ccb.cdm.pattern_buf_len); + if (patterns == NULL) { + warnx("can't malloc memory for patterns"); + retval = 1; + goto bailout; + } + + ccb.cdm.patterns = patterns; + bzero(patterns, ccb.cdm.pattern_buf_len); + + patterns[0].type = DEV_MATCH_DEVICE; + patterns[0].pattern.device_pattern.flags = DEV_MATCH_PATH; + patterns[0].pattern.device_pattern.path_id = devlist->path_id; + patterns[1].type = DEV_MATCH_PERIPH; + patterns[1].pattern.periph_pattern.flags = PERIPH_MATCH_PATH; + patterns[1].pattern.periph_pattern.path_id = devlist->path_id; + + /* + * We do the ioctl multiple times if necessary, in case there are + * more than 100 nodes in the EDT. + */ + do { + unsigned int i; + + if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { + warn("error sending CAMIOCOMMAND ioctl"); + retval = 1; + goto bailout; + } + + if ((ccb.ccb_h.status != CAM_REQ_CMP) + || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) + && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { + warnx("got CAM error %#x, CDM error %d\n", + ccb.ccb_h.status, ccb.cdm.status); + retval = 1; + goto bailout; + } + + for (i = 0; i < ccb.cdm.num_matches; i++) { + switch (ccb.cdm.matches[i].type) { + case DEV_MATCH_DEVICE: { + struct device_match_result *dev_result; + + dev_result = + &ccb.cdm.matches[i].result.device_result; + + if ((dev_result->flags + & DEV_RESULT_UNCONFIGURED) + && ((arglist & CAM_ARG_VERBOSE) == 0)) { + skip_device = 1; + break; + } else + skip_device = 0; + + item = malloc(sizeof(*item)); + if (item == NULL) { + warn("%s: unable to allocate %zd bytes", + __func__, sizeof(*item)); + retval = 1; + goto bailout; + } + bzero(item, sizeof(*item)); + bcopy(dev_result, &item->dev_match, + sizeof(*dev_result)); + STAILQ_INSERT_TAIL(&devlist->dev_queue, item, + links); + + if (getdevid(item) != 0) { + retval = 1; + goto bailout; + } + break; + } + case DEV_MATCH_PERIPH: { + struct periph_match_result *periph_result; + + periph_result = + &ccb.cdm.matches[i].result.periph_result; + + if (skip_device != 0) + break; + item->num_periphs++; + item->periph_matches = realloc( + item->periph_matches, + item->num_periphs * + sizeof(struct periph_match_result)); + if (item->periph_matches == NULL) { + warn("%s: error allocating periph " + "list", __func__); + retval = 1; + goto bailout; + } + bcopy(periph_result, &item->periph_matches[ + item->num_periphs - 1], + sizeof(*periph_result)); + break; + } + default: + fprintf(stderr, "%s: unexpected match " + "type %d\n", __func__, + ccb.cdm.matches[i].type); + retval = 1; + goto bailout; + break; /*NOTREACHED*/ + } + } + } while ((ccb.ccb_h.status == CAM_REQ_CMP) + && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); +bailout: + + if (fd != -1) + close(fd); + + free(patterns); + + free(ccb.cdm.matches); + + if (retval != 0) + freebusdevlist(devlist); + + return (retval); +} + +static void +freebusdevlist(struct cam_devlist *devlist) +{ + struct cam_devitem *item, *item2; + + STAILQ_FOREACH_SAFE(item, &devlist->dev_queue, links, item2) { + STAILQ_REMOVE(&devlist->dev_queue, item, cam_devitem, + links); + free(item->device_id); + free(item->periph_matches); + free(item); + } +} + +static struct cam_devitem * +findsasdevice(struct cam_devlist *devlist, uint64_t sasaddr) +{ + struct cam_devitem *item; + + STAILQ_FOREACH(item, &devlist->dev_queue, links) { + uint8_t *item_addr; + + /* + * XXX KDM look for LUN IDs as well? + */ + item_addr = scsi_get_sas_addr(item->device_id, + item->device_id_len); + if (item_addr == NULL) + continue; + + if (scsi_8btou64(item_addr) == sasaddr) + return (item); + } + + return (NULL); +} + +static int +smpphylist(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout) +{ + struct smp_report_general_request *rgrequest = NULL; + struct smp_report_general_response *rgresponse = NULL; + struct smp_discover_request *disrequest = NULL; + struct smp_discover_response *disresponse = NULL; + struct cam_devlist devlist; + union ccb *ccb; + int long_response = 0; + int num_phys = 0; + int quiet = 0; + int retval; + int i, c; + + /* + * Note that at the moment we don't support sending SMP CCBs to + * devices that aren't probed by CAM. + */ + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + return (1); + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + + rgrequest = malloc(sizeof(*rgrequest)); + if (rgrequest == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*rgrequest)); + retval = 1; + goto bailout; + } + + rgresponse = malloc(sizeof(*rgresponse)); + if (rgresponse == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*rgresponse)); + retval = 1; + goto bailout; + } + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'l': + long_response = 1; + break; + case 'q': + quiet = 1; + break; + default: + break; + } + } + + smp_report_general(&ccb->smpio, + retry_count, + /*cbfcnp*/ NULL, + rgrequest, + /*request_len*/ sizeof(*rgrequest), + (uint8_t *)rgresponse, + /*response_len*/ sizeof(*rgresponse), + /*long_response*/ long_response, + timeout); + + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + const char *warnstr = "error sending command"; + + if (retval < 0) + warn(warnstr); + else + warnx(warnstr); + + if (arglist & CAM_ARG_VERBOSE) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + } + retval = 1; + goto bailout; + } + + num_phys = rgresponse->num_phys; + + if (num_phys == 0) { + if (quiet == 0) + fprintf(stdout, "%s: No Phys reported\n", __func__); + retval = 1; + goto bailout; + } + + STAILQ_INIT(&devlist.dev_queue); + devlist.path_id = device->path_id; + + retval = buildbusdevlist(&devlist); + if (retval != 0) + goto bailout; + + if (quiet == 0) { + fprintf(stdout, "%d PHYs:\n", num_phys); + fprintf(stdout, "PHY Attached SAS Address\n"); + } + + disrequest = malloc(sizeof(*disrequest)); + if (disrequest == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*disrequest)); + retval = 1; + goto bailout; + } + + disresponse = malloc(sizeof(*disresponse)); + if (disresponse == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*disresponse)); + retval = 1; + goto bailout; + } + + for (i = 0; i < num_phys; i++) { + struct cam_devitem *item; + struct device_match_result *dev_match; + char vendor[16], product[48], revision[16]; + char tmpstr[256]; + int j; + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + + ccb->ccb_h.status = CAM_REQ_INPROG; + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + smp_discover(&ccb->smpio, + retry_count, + /*cbfcnp*/ NULL, + disrequest, + sizeof(*disrequest), + (uint8_t *)disresponse, + sizeof(*disresponse), + long_response, + /*ignore_zone_group*/ 0, + /*phy*/ i, + timeout); + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) + && (disresponse->function_result != SMP_FR_PHY_VACANT))) { + const char *warnstr = "error sending command"; + + if (retval < 0) + warn(warnstr); + else + warnx(warnstr); + + if (arglist & CAM_ARG_VERBOSE) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + } + retval = 1; + goto bailout; + } + + if (disresponse->function_result == SMP_FR_PHY_VACANT) { + if (quiet == 0) + fprintf(stdout, "%3d <vacant>\n", i); + continue; + } + + item = findsasdevice(&devlist, + scsi_8btou64(disresponse->attached_sas_address)); + + if ((quiet == 0) + || (item != NULL)) { + fprintf(stdout, "%3d 0x%016jx", i, + (uintmax_t)scsi_8btou64( + disresponse->attached_sas_address)); + if (item == NULL) { + fprintf(stdout, "\n"); + continue; + } + } else if (quiet != 0) + continue; + + dev_match = &item->dev_match; + + if (dev_match->protocol == PROTO_SCSI) { + cam_strvis(vendor, dev_match->inq_data.vendor, + sizeof(dev_match->inq_data.vendor), + sizeof(vendor)); + cam_strvis(product, dev_match->inq_data.product, + sizeof(dev_match->inq_data.product), + sizeof(product)); + cam_strvis(revision, dev_match->inq_data.revision, + sizeof(dev_match->inq_data.revision), + sizeof(revision)); + sprintf(tmpstr, "<%s %s %s>", vendor, product, + revision); + } else if ((dev_match->protocol == PROTO_ATA) + || (dev_match->protocol == PROTO_SATAPM)) { + cam_strvis(product, dev_match->ident_data.model, + sizeof(dev_match->ident_data.model), + sizeof(product)); + cam_strvis(revision, dev_match->ident_data.revision, + sizeof(dev_match->ident_data.revision), + sizeof(revision)); + sprintf(tmpstr, "<%s %s>", product, revision); + } else { + sprintf(tmpstr, "<>"); + } + fprintf(stdout, " %-33s ", tmpstr); + + /* + * If we have 0 periphs, that's a bug... + */ + if (item->num_periphs == 0) { + fprintf(stdout, "\n"); + continue; + } + + fprintf(stdout, "("); + for (j = 0; j < item->num_periphs; j++) { + if (j > 0) + fprintf(stdout, ","); + + fprintf(stdout, "%s%d", + item->periph_matches[j].periph_name, + item->periph_matches[j].unit_number); + + } + fprintf(stdout, ")\n"); + } +bailout: + if (ccb != NULL) + cam_freeccb(ccb); + + free(rgrequest); + + free(rgresponse); + + free(disrequest); + + free(disresponse); + + freebusdevlist(&devlist); + + return (retval); +} + +static int atapm(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout) { @@ -4393,6 +5645,16 @@ usage(int verbose) " camcontrol cmd [dev_id][generic args]\n" " <-a cmd [args] | -c cmd [args]>\n" " [-d] [-f] [-i len fmt|-o len fmt [args]] [-r fmt]\n" +" camcontrol smpcmd [dev_id][generic args]\n" +" <-r len fmt [args]> <-R len fmt [args]>\n" +" camcontrol smprg [dev_id][generic args][-l]\n" +" camcontrol smppc [dev_id][generic args] <-p phy> [-l]\n" +" [-o operation][-d name][-m rate][-M rate]\n" +" [-T pp_timeout][-a enable|disable]\n" +" [-A enable|disable][-s enable|disable]\n" +" [-S enable|disable]\n" +" camcontrol smpphylist [dev_id][generic args][-l][-q]\n" +" camcontrol smpmaninfo [dev_id][generic args][-l]\n" " camcontrol debug [-I][-P][-T][-S][-X][-c]\n" " <all|bus[:target[:lun]]|off>\n" " camcontrol tags [dev_id][generic args] [-N tags] [-q] [-v]\n" @@ -4426,7 +5688,12 @@ usage(int verbose) "reset reset all busses, the given bus, or bus:target:lun\n" "defects read the defect list of the specified device\n" "modepage display or edit (-e) the given mode page\n" -"cmd send the given scsi command, may need -i or -o as well\n" +"cmd send the given SCSI command, may need -i or -o as well\n" +"smpcmd send the given SMP command, requires -o and -i\n" +"smprg send the SMP Report General command\n" +"smppc send the SMP PHY Control command, requires -p\n" +"smpphylist display phys attached to a SAS expander\n" +"smpmaninfo send the SMP Report Manufacturer Info command\n" "debug turn debugging on/off for a bus, target, or lun, or all devices\n" "tags report or set the number of transaction slots for a device\n" "negotiate report or set device negotiation parameters\n" @@ -4476,6 +5743,28 @@ usage(int verbose) "-c cdb [args] specify the SCSI CDB\n" "-i len fmt specify input data and input data format\n" "-o len fmt [args] specify output data and output data fmt\n" +"smpcmd arguments:\n" +"-r len fmt [args] specify the SMP command to be sent\n" +"-R len fmt [args] specify SMP response format\n" +"smprg arguments:\n" +"-l specify the long response format\n" +"smppc arguments:\n" +"-p phy specify the PHY to operate on\n" +"-l specify the long request/response format\n" +"-o operation specify the phy control operation\n" +"-d name set the attached device name\n" +"-m rate set the minimum physical link rate\n" +"-M rate set the maximum physical link rate\n" +"-T pp_timeout set the partial pathway timeout value\n" +"-a enable|disable enable or disable SATA slumber\n" +"-A enable|disable enable or disable SATA partial phy power\n" +"-s enable|disable enable or disable SAS slumber\n" +"-S enable|disable enable or disable SAS partial phy power\n" +"smpphylist arguments:\n" +"-l specify the long response format\n" +"-q only print phys with attached devices\n" +"smpmaninfo arguments:\n" +"-l specify the long response format\n" "debug arguments:\n" "-I CAM_DEBUG_INFO -- scsi commands, errors, data\n" "-T CAM_DEBUG_TRACE -- routine flow tracking\n" @@ -4537,7 +5826,7 @@ main(int argc, char **argv) /* * Get the base option. */ - optreturn = getoption(argv[1], &cmdlist, &arglist, &subopt); + optreturn = getoption(option_table,argv[1], &cmdlist, &arglist,&subopt); if (optreturn == CC_OR_AMBIGUOUS) { warnx("ambiguous option %s", argv[1]); @@ -4767,6 +6056,27 @@ main(int argc, char **argv) error = scsicmd(cam_dev, argc, argv, combinedopt, retry_count, timeout); break; + case CAM_CMD_SMP_CMD: + error = smpcmd(cam_dev, argc, argv, combinedopt, + retry_count, timeout); + break; + case CAM_CMD_SMP_RG: + error = smpreportgeneral(cam_dev, argc, argv, + combinedopt, retry_count, + timeout); + break; + case CAM_CMD_SMP_PC: + error = smpphycontrol(cam_dev, argc, argv, combinedopt, + retry_count, timeout); + break; + case CAM_CMD_SMP_PHYLIST: + error = smpphylist(cam_dev, argc, argv, combinedopt, + retry_count, timeout); + break; + case CAM_CMD_SMP_MANINFO: + error = smpmaninfo(cam_dev, argc, argv, combinedopt, + retry_count, timeout); + break; case CAM_CMD_DEBUG: error = camdebug(argc, argv, combinedopt); break; diff --git a/sys/cam/cam.c b/sys/cam/cam.c index 452c9c9..b3c9023 100644 --- a/sys/cam/cam.c +++ b/sys/cam/cam.c @@ -37,12 +37,14 @@ __FBSDID("$FreeBSD$"); #else /* _KERNEL */ #include <stdlib.h> #include <stdio.h> +#include <string.h> #include <camlib.h> #endif /* _KERNEL */ #include <cam/cam.h> #include <cam/cam_ccb.h> #include <cam/scsi/scsi_all.h> +#include <cam/scsi/smp_all.h> #include <sys/sbuf.h> #ifdef _KERNEL @@ -83,6 +85,8 @@ const struct cam_status_entry cam_status_table[] = { { CAM_REQ_TOO_BIG, "The request was too large for this host" }, { CAM_REQUEUE_REQ, "Unconditionally Re-queue Request", }, { CAM_ATA_STATUS_ERROR, "ATA Status Error" }, + { CAM_SCSI_IT_NEXUS_LOST,"Initiator/Target Nexus Lost" }, + { CAM_SMP_STATUS_ERROR, "SMP Status Error" }, { CAM_IDE, "Initiator Detected Error Message Received" }, { CAM_RESRC_UNAVAIL, "Resource Unavailable" }, { CAM_UNACKED_EVENT, "Unacknowledged Event by Host" }, @@ -263,6 +267,21 @@ cam_error_string(struct cam_device *device, union ccb *ccb, char *str, break; } break; + case XPT_SMP_IO: + switch (proto_flags & CAM_EPF_LEVEL_MASK) { + case CAM_EPF_NONE: + break; + case CAM_EPF_ALL: + proto_flags |= CAM_ESMF_PRINT_FULL_CMD; + /* FALLTHROUGH */ + case CAM_EPF_NORMAL: + case CAM_EPF_MINIMAL: + proto_flags |= CAM_ESMF_PRINT_STATUS; + /* FALLTHROUGH */ + default: + break; + } + break; default: break; } @@ -289,6 +308,12 @@ cam_error_string(struct cam_device *device, union ccb *ccb, char *str, #endif /* _KERNEL/!_KERNEL */ sbuf_printf(&sb, "\n"); break; + case XPT_SMP_IO: + smp_command_sbuf(&ccb->smpio, &sb, path_str, 79 - + strlen(path_str), (proto_flags & + CAM_ESMF_PRINT_FULL_CMD) ? 79 : 0); + sbuf_printf(&sb, "\n"); + break; default: break; } @@ -355,6 +380,19 @@ cam_error_string(struct cam_device *device, union ccb *ccb, char *str, #endif /* _KERNEL/!_KERNEL */ } break; + case XPT_SMP_IO: + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != + CAM_SMP_STATUS_ERROR) + break; + + if (proto_flags & CAM_ESF_PRINT_STATUS) { + sbuf_cat(&sb, path_str); + sbuf_printf(&sb, "SMP status: %s (%#x)\n", + smp_error_desc(ccb->smpio.smp_response[2]), + ccb->smpio.smp_response[2]); + } + /* There is no SMP equivalent to SCSI sense. */ + break; default: break; } diff --git a/sys/cam/cam.h b/sys/cam/cam.h index 2b3b98c..7f99a9c 100644 --- a/sys/cam/cam.h +++ b/sys/cam/cam.h @@ -147,6 +147,7 @@ typedef enum { */ CAM_ATA_STATUS_ERROR, /* ATA error, look at error code in CCB */ CAM_SCSI_IT_NEXUS_LOST, /* Initiator/Target Nexus lost. */ + CAM_SMP_STATUS_ERROR, /* SMP error, look at error code in CCB */ CAM_IDE = 0x33, /* Initiator Detected Error */ CAM_RESRC_UNAVAIL, /* Resource Unavailable */ CAM_UNACKED_EVENT, /* Unacknowledged Event by Host */ @@ -198,6 +199,12 @@ typedef enum { } cam_error_scsi_flags; typedef enum { + CAM_ESMF_PRINT_NONE = 0x00, + CAM_ESMF_PRINT_STATUS = 0x10, + CAM_ESMF_PRINT_FULL_CMD = 0x20, +} cam_error_smp_flags; + +typedef enum { CAM_EAF_PRINT_NONE = 0x00, CAM_EAF_PRINT_STATUS = 0x10, CAM_EAF_PRINT_RESULT = 0x20 diff --git a/sys/cam/cam_ccb.h b/sys/cam/cam_ccb.h index bbb5069..9c1e3ab 100644 --- a/sys/cam/cam_ccb.h +++ b/sys/cam/cam_ccb.h @@ -66,7 +66,7 @@ typedef enum { */ CAM_SCATTER_VALID = 0x00000010,/* Scatter/gather list is valid */ CAM_DIS_AUTOSENSE = 0x00000020,/* Disable autosense feature */ - CAM_DIR_RESV = 0x00000000,/* Data direction (00:reserved) */ + CAM_DIR_BOTH = 0x00000000,/* Data direction (00:IN/OUT) */ CAM_DIR_IN = 0x00000040,/* Data direction (01:DATA IN) */ CAM_DIR_OUT = 0x00000080,/* Data direction (10:DATA OUT) */ CAM_DIR_NONE = 0x000000C0,/* Data direction (11:no data) */ @@ -144,6 +144,8 @@ typedef enum { /* Device statistics (error counts, etc.) */ XPT_FREEZE_QUEUE = 0x0d, /* Freeze device queue */ + XPT_GDEV_ADVINFO = 0x0e, + /* Advanced device information */ /* SCSI Control Functions: 0x10->0x1F */ XPT_ABORT = 0x10, /* Abort the specified CCB */ @@ -185,6 +187,9 @@ typedef enum { * Set SIM specific knob values. */ + XPT_SMP_IO = 0x1b | XPT_FC_DEV_QUEUED, + /* Serial Management Protocol */ + XPT_SCAN_TGT = 0x1E | XPT_FC_QUEUED | XPT_FC_USER_CCB | XPT_FC_XPT_ONLY, /* Scan Target */ @@ -608,6 +613,32 @@ struct ccb_pathstats { struct timeval last_reset; /* Time of last bus reset/loop init */ }; +typedef enum { + SMP_FLAG_NONE = 0x00, + SMP_FLAG_REQ_SG = 0x01, + SMP_FLAG_RSP_SG = 0x02 +} ccb_smp_pass_flags; + +/* + * Serial Management Protocol CCB + * XXX Currently the semantics for this CCB are that it is executed either + * by the addressed device, or that device's parent (i.e. an expander for + * any device on an expander) if the addressed device doesn't support SMP. + * Later, once we have the ability to probe SMP-only devices and put them + * in CAM's topology, the CCB will only be executed by the addressed device + * if possible. + */ +struct ccb_smpio { + struct ccb_hdr ccb_h; + uint8_t *smp_request; + int smp_request_len; + uint16_t smp_request_sglist_cnt; + uint8_t *smp_response; + int smp_response_len; + uint16_t smp_response_sglist_cnt; + ccb_smp_pass_flags flags; +}; + typedef union { u_int8_t *sense_ptr; /* * Pointer to storage @@ -1054,6 +1085,26 @@ struct ccb_eng_exec { /* This structure must match SCSIIO size */ #define XPT_CCB_INVALID -1 /* for signaling a bad CCB to free */ /* + * CCB for getting advanced device information. This operates in a fashion + * similar to XPT_GDEV_TYPE. Specify the target in ccb_h, the buffer + * type requested, and provide a buffer size/buffer to write to. If the + * buffer is too small, the handler will set GDEVAI_FLAG_MORE. + */ +struct ccb_getdev_advinfo { + struct ccb_hdr ccb_h; + uint32_t flags; +#define CGDAI_FLAG_TRANSPORT 0x1 +#define CGDAI_FLAG_PROTO 0x2 + uint32_t buftype; /* IN: Type of data being requested */ + /* NB: buftype is interpreted on a per-transport basis */ +#define CGDAI_TYPE_SCSI_DEVID 1 + off_t bufsiz; /* IN: Size of external buffer */ +#define CAM_SCSI_DEVID_MAXLEN 65536 /* length in buffer is an uint16_t */ + off_t provsiz; /* OUT: Size required/used */ + uint8_t *buf; /* IN/OUT: Buffer for requested data */ +}; + +/* * Union of all CCB types for kernel space allocation. This union should * never be used for manipulating CCBs - its only use is for the allocation * and deallocation of raw CCB space and is the return type of xpt_ccb_alloc @@ -1087,9 +1138,11 @@ union ccb { struct ccb_notify_acknowledge cna2; struct ccb_eng_inq cei; struct ccb_eng_exec cee; + struct ccb_smpio smpio; struct ccb_rescan crcn; struct ccb_debug cdbg; struct ccb_ataio ataio; + struct ccb_getdev_advinfo cgdai; }; __BEGIN_DECLS @@ -1116,6 +1169,13 @@ cam_fill_ataio(struct ccb_ataio *ataio, u_int32_t retries, u_int32_t timeout); static __inline void +cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), uint32_t flags, + uint8_t *smp_request, int smp_request_len, + uint8_t *smp_response, int smp_response_len, + uint32_t timeout); + +static __inline void cam_fill_csio(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int32_t flags, u_int8_t tag_action, @@ -1172,6 +1232,32 @@ cam_fill_ataio(struct ccb_ataio *ataio, u_int32_t retries, ataio->tag_action = tag_action; } +static __inline void +cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), uint32_t flags, + uint8_t *smp_request, int smp_request_len, + uint8_t *smp_response, int smp_response_len, + uint32_t timeout) +{ +#ifdef _KERNEL + KASSERT((flags & CAM_DIR_MASK) == CAM_DIR_BOTH, + ("direction != CAM_DIR_BOTH")); + KASSERT((smp_request != NULL) && (smp_response != NULL), + ("need valid request and response buffers")); + KASSERT((smp_request_len != 0) && (smp_response_len != 0), + ("need non-zero request and response lengths")); +#endif /*_KERNEL*/ + smpio->ccb_h.func_code = XPT_SMP_IO; + smpio->ccb_h.flags = flags; + smpio->ccb_h.retry_count = retries; + smpio->ccb_h.cbfcnp = cbfcnp; + smpio->ccb_h.timeout = timeout; + smpio->smp_request = smp_request; + smpio->smp_request_len = smp_request_len; + smpio->smp_response = smp_response; + smpio->smp_response_len = smp_response_len; +} + void cam_calc_geometry(struct ccb_calc_geometry *ccg, int extended); __END_DECLS diff --git a/sys/cam/cam_periph.c b/sys/cam/cam_periph.c index 83bd075..14f3beb 100644 --- a/sys/cam/cam_periph.c +++ b/sys/cam/cam_periph.c @@ -648,6 +648,21 @@ cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; numbufs = 1; break; + case XPT_SMP_IO: + data_ptrs[0] = &ccb->smpio.smp_request; + lengths[0] = ccb->smpio.smp_request_len; + dirs[0] = CAM_DIR_OUT; + data_ptrs[1] = &ccb->smpio.smp_response; + lengths[1] = ccb->smpio.smp_response_len; + dirs[1] = CAM_DIR_IN; + numbufs = 2; + break; + case XPT_GDEV_ADVINFO: + data_ptrs[0] = (uint8_t **)&ccb->cgdai.buf; + lengths[0] = ccb->cgdai.bufsiz; + dirs[0] = CAM_DIR_IN; + numbufs = 1; + break; default: return(EINVAL); break; /* NOTREACHED */ @@ -787,6 +802,15 @@ cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) data_ptrs[0] = &ccb->ataio.data_ptr; numbufs = min(mapinfo->num_bufs_used, 1); break; + case XPT_SMP_IO: + numbufs = min(mapinfo->num_bufs_used, 2); + data_ptrs[0] = &ccb->smpio.smp_request; + data_ptrs[1] = &ccb->smpio.smp_response; + break; + case XPT_GDEV_ADVINFO: + numbufs = min(mapinfo->num_bufs_used, 1); + data_ptrs[0] = (uint8_t **)&ccb->cgdai.buf; + break; default: /* allow ourselves to be swapped once again */ PRELE(curproc); diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c index dc16e9f..a6298aa 100644 --- a/sys/cam/cam_xpt.c +++ b/sys/cam/cam_xpt.c @@ -2386,6 +2386,7 @@ xpt_action_default(union ccb *start_ccb) /* FALLTHROUGH */ case XPT_RESET_DEV: case XPT_ENG_EXEC: + case XPT_SMP_IO: { struct cam_path *path = start_ccb->ccb_h.path; int frozen; diff --git a/sys/cam/cam_xpt_internal.h b/sys/cam/cam_xpt_internal.h index aa6161c..f485e37 100644 --- a/sys/cam/cam_xpt_internal.h +++ b/sys/cam/cam_xpt_internal.h @@ -93,6 +93,10 @@ struct cam_ed { cam_xport transport; u_int transport_version; struct scsi_inquiry_data inq_data; + uint8_t *supported_vpds; + uint8_t supported_vpds_len; + uint32_t device_id_len; + uint8_t *device_id; struct ata_params ident_data; u_int8_t inq_flags; /* * Current settings for inquiry flags. diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c index cddac5c..7ededa1 100644 --- a/sys/cam/scsi/scsi_all.c +++ b/sys/cam/scsi/scsi_all.c @@ -3552,6 +3552,34 @@ scsi_calc_syncparam(u_int period) return (period/400); } +uint8_t * +scsi_get_sas_addr(struct scsi_vpd_device_id *id, uint32_t len) +{ + uint8_t *bufp, *buf_end; + struct scsi_vpd_id_descriptor *descr; + struct scsi_vpd_id_naa_basic *naa; + + bufp = buf_end = (uint8_t *)id; + bufp += SVPD_DEVICE_ID_HDR_LEN; + buf_end += len; + while (bufp < buf_end) { + descr = (struct scsi_vpd_id_descriptor *)bufp; + bufp += SVPD_DEVICE_ID_DESC_HDR_LEN; + /* Right now, we only care about SAS NAA IEEE Reg addrs */ + if (((descr->id_type & SVPD_ID_PIV) != 0) + && (descr->proto_codeset >> SVPD_ID_PROTO_SHIFT) == + SCSI_PROTO_SAS + && (descr->id_type & SVPD_ID_TYPE_MASK) == SVPD_ID_TYPE_NAA){ + naa = (struct scsi_vpd_id_naa_basic *)bufp; + if ((naa->naa >> 4) == SVPD_ID_NAA_IEEE_REG) + return bufp; + } + bufp += descr->length; + } + + return NULL; +} + void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h index cddf4f9..0a7a58f 100644 --- a/sys/cam/scsi/scsi_all.h +++ b/sys/cam/scsi/scsi_all.h @@ -796,13 +796,29 @@ struct scsi_vpd_supported_page_list { u_int8_t device; u_int8_t page_code; -#define SVPD_SUPPORTED_PAGE_LIST 0x00 +#define SVPD_SUPPORTED_PAGE_LIST 0x00 +#define SVPD_SUPPORTED_PAGES_HDR_LEN 4 u_int8_t reserved; u_int8_t length; /* number of VPD entries */ #define SVPD_SUPPORTED_PAGES_SIZE 251 u_int8_t list[SVPD_SUPPORTED_PAGES_SIZE]; }; +/* + * This structure is more suited to target operation, because the + * number of supported pages is left to the user to allocate. + */ +struct scsi_vpd_supported_pages +{ + u_int8_t device; + u_int8_t page_code; + u_int8_t reserved; +#define SVPD_SUPPORTED_PAGES 0x00 + u_int8_t length; + u_int8_t page_list[0]; +}; + + struct scsi_vpd_unit_serial_number { u_int8_t device; @@ -814,6 +830,148 @@ struct scsi_vpd_unit_serial_number u_int8_t serial_num[SVPD_SERIAL_NUM_SIZE]; }; +struct scsi_vpd_device_id +{ + u_int8_t device; + u_int8_t page_code; +#define SVPD_DEVICE_ID 0x83 +#define SVPD_DEVICE_ID_MAX_SIZE 0xffff +#define SVPD_DEVICE_ID_HDR_LEN 4 +#define SVPD_DEVICE_ID_DESC_HDR_LEN 4 + u_int8_t length[2]; + u_int8_t desc_list[0]; +}; + +struct scsi_vpd_id_descriptor +{ + u_int8_t proto_codeset; +#define SCSI_PROTO_FC 0x00 +#define SCSI_PROTO_SPI 0x01 +#define SCSI_PROTO_SSA 0x02 +#define SCSI_PROTO_1394 0x03 +#define SCSI_PROTO_RDMA 0x04 +#define SCSI_PROTO_iSCSI 0x05 +#define SCSI_PROTO_SAS 0x06 +#define SVPD_ID_PROTO_SHIFT 4 +#define SVPD_ID_CODESET_BINARY 0x01 +#define SVPD_ID_CODESET_ASCII 0x02 + u_int8_t id_type; +#define SVPD_ID_PIV 0x80 +#define SVPD_ID_ASSOC_LUN 0x00 +#define SVPD_ID_ASSOC_PORT 0x10 +#define SVPD_ID_ASSOC_TARGET 0x20 +#define SVPD_ID_TYPE_VENDOR 0x00 +#define SVPD_ID_TYPE_T10 0x01 +#define SVPD_ID_TYPE_EUI64 0x02 +#define SVPD_ID_TYPE_NAA 0x03 +#define SVPD_ID_TYPE_RELTARG 0x04 +#define SVPD_ID_TYPE_TPORTGRP 0x05 +#define SVPD_ID_TYPE_LUNGRP 0x06 +#define SVPD_ID_TYPE_MD5_LUN_ID 0x07 +#define SVPD_ID_TYPE_SCSI_NAME 0x08 +#define SVPD_ID_TYPE_MASK 0x0f + u_int8_t reserved; + u_int8_t length; + u_int8_t identifier[0]; +}; + +struct scsi_vpd_id_t10 +{ + u_int8_t vendor[8]; + u_int8_t vendor_spec_id[0]; +}; + +struct scsi_vpd_id_eui64 +{ + u_int8_t ieee_company_id[3]; + u_int8_t extension_id[5]; +}; + +struct scsi_vpd_id_naa_basic +{ + uint8_t naa; + /* big endian, packed: + uint8_t naa : 4; + uint8_t naa_desig : 4; + */ +#define SVPD_ID_NAA_IEEE_EXT 0x02 +#define SVPD_ID_NAA_LOCAL_REG 0x03 +#define SVPD_ID_NAA_IEEE_REG 0x05 +#define SVPD_ID_NAA_IEEE_REG_EXT 0x06 + uint8_t naa_data[0]; +}; + +struct scsi_vpd_id_naa_ieee_extended_id +{ + uint8_t naa; + uint8_t vendor_specific_id_a; + uint8_t ieee_company_id[3]; + uint8_t vendor_specific_id_b[4]; +}; + +struct scsi_vpd_id_naa_local_reg +{ + uint8_t naa; + uint8_t local_value[7]; +}; + +struct scsi_vpd_id_naa_ieee_reg +{ + uint8_t naa; + uint8_t reg_value[7]; + /* big endian, packed: + uint8_t naa_basic : 4; + uint8_t ieee_company_id_0 : 4; + uint8_t ieee_company_id_1[2]; + uint8_t ieee_company_id_2 : 4; + uint8_t vendor_specific_id_0 : 4; + uint8_t vendor_specific_id_1[4]; + */ +}; + +struct scsi_vpd_id_naa_ieee_reg_extended +{ + uint8_t naa; + uint8_t reg_value[15]; + /* big endian, packed: + uint8_t naa_basic : 4; + uint8_t ieee_company_id_0 : 4; + uint8_t ieee_company_id_1[2]; + uint8_t ieee_company_id_2 : 4; + uint8_t vendor_specific_id_0 : 4; + uint8_t vendor_specific_id_1[4]; + uint8_t vendor_specific_id_ext[8]; + */ +}; + +struct scsi_vpd_id_rel_trgt_port_id +{ + uint8_t obsolete[2]; + uint8_t rel_trgt_port_id[2]; +}; + +struct scsi_vpd_id_trgt_port_grp_id +{ + uint8_t reserved[2]; + uint8_t trgt_port_grp[2]; +}; + +struct scsi_vpd_id_lun_grp_id +{ + uint8_t reserved[2]; + uint8_t log_unit_grp[2]; +}; + +struct scsi_vpd_id_md5_lun_id +{ + uint8_t lun_id[16]; +}; + +struct scsi_vpd_id_scsi_name +{ + uint8_t name_string[256]; +}; + struct scsi_read_capacity { u_int8_t opcode; @@ -1164,7 +1322,8 @@ void scsi_print_inquiry(struct scsi_inquiry_data *inq_data); u_int scsi_calc_syncsrate(u_int period_factor); u_int scsi_calc_syncparam(u_int period); - +uint8_t * scsi_get_sas_addr(struct scsi_vpd_device_id *id, uint32_t len); + void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), diff --git a/sys/cam/scsi/scsi_pass.c b/sys/cam/scsi/scsi_pass.c index dddfc7b..00cf61f 100644 --- a/sys/cam/scsi/scsi_pass.c +++ b/sys/cam/scsi/scsi_pass.c @@ -524,8 +524,8 @@ passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb) * We only attempt to map the user memory into kernel space * if they haven't passed in a physical memory pointer, * and if there is actually an I/O operation to perform. - * Right now cam_periph_mapmem() only supports SCSI and device - * match CCBs. For the SCSI CCBs, we only pass the CCB in if + * cam_periph_mapmem() supports SCSI, ATA, SMP, ADVINFO and device + * match CCBs. For the SCSI and ATA CCBs, we only pass the CCB in if * there's actually data to map. cam_periph_mapmem() will do the * right thing, even if there isn't data to map, but since CCBs * without data are a reasonably common occurance (e.g. test unit @@ -535,7 +535,9 @@ passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb) && (((ccb->ccb_h.func_code == XPT_SCSI_IO || ccb->ccb_h.func_code == XPT_ATA_IO) && ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)) - || (ccb->ccb_h.func_code == XPT_DEV_MATCH))) { + || (ccb->ccb_h.func_code == XPT_DEV_MATCH) + || (ccb->ccb_h.func_code == XPT_SMP_IO) + || (ccb->ccb_h.func_code == XPT_GDEV_ADVINFO))) { bzero(&mapinfo, sizeof(mapinfo)); diff --git a/sys/cam/scsi/scsi_xpt.c b/sys/cam/scsi/scsi_xpt.c index 979e0a0..2a38128 100644 --- a/sys/cam/scsi/scsi_xpt.c +++ b/sys/cam/scsi/scsi_xpt.c @@ -68,7 +68,7 @@ struct scsi_quirk_entry { struct scsi_inquiry_pattern inq_pat; u_int8_t quirks; #define CAM_QUIRK_NOLUNS 0x01 -#define CAM_QUIRK_NOSERIAL 0x02 +#define CAM_QUIRK_NOVPDS 0x02 #define CAM_QUIRK_HILUNS 0x04 #define CAM_QUIRK_NOHILUNS 0x08 #define CAM_QUIRK_NORPTLUNS 0x10 @@ -134,8 +134,9 @@ typedef enum { PROBE_FULL_INQUIRY, PROBE_REPORT_LUNS, PROBE_MODE_SENSE, - PROBE_SERIAL_NUM_0, - PROBE_SERIAL_NUM_1, + PROBE_SUPPORTED_VPD_LIST, + PROBE_DEVICE_ID, + PROBE_SERIAL_NUM, PROBE_TUR_FOR_NEGOTIATION, PROBE_INQUIRY_BASIC_DV1, PROBE_INQUIRY_BASIC_DV2, @@ -149,8 +150,9 @@ static char *probe_action_text[] = { "PROBE_FULL_INQUIRY", "PROBE_REPORT_LUNS", "PROBE_MODE_SENSE", - "PROBE_SERIAL_NUM_0", - "PROBE_SERIAL_NUM_1", + "PROBE_SUPPORTED_VPD_LIST", + "PROBE_DEVICE_ID", + "PROBE_SERIAL_NUM", "PROBE_TUR_FOR_NEGOTIATION", "PROBE_INQUIRY_BASIC_DV1", "PROBE_INQUIRY_BASIC_DV2", @@ -463,7 +465,7 @@ static struct scsi_quirk_entry scsi_quirk_table[] = T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG", " TDC 3600", "U07:" }, - CAM_QUIRK_NOSERIAL, /*mintags*/0, /*maxtags*/0 + CAM_QUIRK_NOVPDS, /*mintags*/0, /*maxtags*/0 }, { /* @@ -696,6 +698,21 @@ probeschedule(struct cam_periph *periph) xpt_schedule(periph, CAM_PRIORITY_XPT); } +static int +device_has_vpd(struct cam_ed *device, uint8_t page_id) +{ + int i, num_pages; + struct scsi_vpd_supported_pages *vpds; + + vpds = (struct scsi_vpd_supported_pages *)device->supported_vpds; + num_pages = device->supported_vpds_len - SVPD_SUPPORTED_PAGES_HDR_LEN; + for (i = 0;i < num_pages;i++) + if (vpds->page_list[i] == page_id) + return 1; + + return 0; +} + static void probestart(struct cam_periph *periph, union ccb *start_ccb) { @@ -810,7 +827,8 @@ again: if (INQ_DATA_TQ_ENABLED(inq_buf)) PROBE_SET_ACTION(softc, PROBE_MODE_SENSE); else - PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0); + PROBE_SET_ACTION(softc, + PROBE_SUPPORTED_VPD_LIST); goto again; } scsi_report_luns(csio, 5, probedone, MSG_SIMPLE_Q_TAG, @@ -843,19 +861,20 @@ again: } xpt_print(periph->path, "Unable to mode sense control page - " "malloc failure\n"); - PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0); + PROBE_SET_ACTION(softc, PROBE_SUPPORTED_VPD_LIST); } /* FALLTHROUGH */ - case PROBE_SERIAL_NUM_0: + case PROBE_SUPPORTED_VPD_LIST: { - struct scsi_vpd_supported_page_list *vpd_list = NULL; + struct scsi_vpd_supported_page_list *vpd_list; struct cam_ed *device; + vpd_list = NULL; device = periph->path->device; - if ((SCSI_QUIRK(device)->quirks & CAM_QUIRK_NOSERIAL) == 0) { + + if ((SCSI_QUIRK(device)->quirks & CAM_QUIRK_NOVPDS) == 0) vpd_list = malloc(sizeof(*vpd_list), M_CAMXPT, M_NOWAIT | M_ZERO); - } if (vpd_list != NULL) { scsi_inquiry(csio, @@ -878,7 +897,39 @@ again: probedone(periph, start_ccb); return; } - case PROBE_SERIAL_NUM_1: + case PROBE_DEVICE_ID: + { + struct scsi_vpd_device_id *devid; + struct cam_ed *device; + + devid = NULL; + device = periph->path->device; + if (device_has_vpd(device, SVPD_DEVICE_ID)) + devid = malloc(SVPD_DEVICE_ID_MAX_SIZE, M_CAMXPT, + M_NOWAIT | M_ZERO); + + if (devid != NULL) { + scsi_inquiry(csio, + /*retries*/4, + probedone, + MSG_SIMPLE_Q_TAG, + (uint8_t *)devid, + SVPD_DEVICE_ID_MAX_SIZE, + /*evpd*/TRUE, + SVPD_DEVICE_ID, + SSD_MIN_SIZE, + /*timeout*/60 * 1000); + break; + } + /* + * We'll have to do without, let our probedone + * routine finish up for us. + */ + start_ccb->csio.data_ptr = NULL; + probedone(periph, start_ccb); + return; + } + case PROBE_SERIAL_NUM: { struct scsi_vpd_unit_serial_number *serial_buf; struct cam_ed* device; @@ -891,8 +942,10 @@ again: device->serial_num_len = 0; } - serial_buf = (struct scsi_vpd_unit_serial_number *) - malloc(sizeof(*serial_buf), M_CAMXPT, M_NOWAIT|M_ZERO); + if (device_has_vpd(device, SVPD_UNIT_SERIAL_NUMBER)) + serial_buf = (struct scsi_vpd_unit_serial_number *) + malloc(sizeof(*serial_buf), M_CAMXPT, + M_NOWAIT|M_ZERO); if (serial_buf != NULL) { scsi_inquiry(csio, @@ -1046,6 +1099,8 @@ proberequestbackoff(struct cam_periph *periph, struct cam_ed *device) return (1); } +#define CCB_COMPLETED_OK(ccb) (((ccb).status & CAM_STATUS_MASK) == CAM_REQ_CMP) + static void probedone(struct cam_periph *periph, union ccb *done_ccb) { @@ -1133,7 +1188,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb) PROBE_MODE_SENSE); else PROBE_SET_ACTION(softc, - PROBE_SERIAL_NUM_0); + PROBE_SUPPORTED_VPD_LIST); if (path->device->flags & CAM_DEV_UNCONFIGURED) { path->device->flags &= ~CAM_DEV_UNCONFIGURED; @@ -1290,7 +1345,8 @@ probedone(struct cam_periph *periph, union ccb *done_ccb) if (INQ_DATA_TQ_ENABLED(inq_buf)) PROBE_SET_ACTION(softc, PROBE_MODE_SENSE); else - PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0); + PROBE_SET_ACTION(softc, + PROBE_SUPPORTED_VPD_LIST); xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; @@ -1326,36 +1382,38 @@ probedone(struct cam_periph *periph, union ccb *done_ccb) } xpt_release_ccb(done_ccb); free(mode_hdr, M_CAMXPT); - PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0); + PROBE_SET_ACTION(softc, PROBE_SUPPORTED_VPD_LIST); xpt_schedule(periph, priority); return; } - case PROBE_SERIAL_NUM_0: + case PROBE_SUPPORTED_VPD_LIST: { struct ccb_scsiio *csio; struct scsi_vpd_supported_page_list *page_list; - int length, serialnum_supported, i; - serialnum_supported = 0; csio = &done_ccb->csio; page_list = (struct scsi_vpd_supported_page_list *)csio->data_ptr; + if (path->device->supported_vpds != NULL) { + free(path->device->supported_vpds, M_CAMXPT); + path->device->supported_vpds = NULL; + path->device->supported_vpds_len = 0; + } + if (page_list == NULL) { /* * Don't process the command as it was never sent */ - } else if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP - && (page_list->length > 0)) { - length = min(page_list->length, - SVPD_SUPPORTED_PAGES_SIZE); - for (i = 0; i < length; i++) { - if (page_list->list[i] == - SVPD_UNIT_SERIAL_NUMBER) { - serialnum_supported = 1; - break; - } - } + } else if (CCB_COMPLETED_OK(csio->ccb_h)) { + /* Got vpd list */ + path->device->supported_vpds_len = page_list->length + + SVPD_SUPPORTED_PAGES_HDR_LEN; + path->device->supported_vpds = (uint8_t *)page_list; + xpt_release_ccb(done_ccb); + PROBE_SET_ACTION(softc, PROBE_DEVICE_ID); + xpt_schedule(periph, priority); + return; } else if (cam_periph_error(done_ccb, 0, SF_RETRY_UA|SF_NO_PRINT, &softc->saved_ccb) == ERESTART) { @@ -1366,21 +1424,62 @@ probedone(struct cam_periph *periph, union ccb *done_ccb) /*run_queue*/TRUE); } - if (page_list != NULL) + if (page_list) free(page_list, M_CAMXPT); + /* No VPDs available, skip to device check. */ + csio->data_ptr = NULL; + goto probe_device_check; + } + case PROBE_DEVICE_ID: + { + struct scsi_vpd_device_id *devid; + struct ccb_scsiio *csio; + uint32_t length = 0; - if (serialnum_supported) { - xpt_release_ccb(done_ccb); - PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_1); - xpt_schedule(periph, priority); + csio = &done_ccb->csio; + devid = (struct scsi_vpd_device_id *)csio->data_ptr; + + /* Clean up from previous instance of this device */ + if (path->device->device_id != NULL) { + path->device->device_id_len = 0; + free(path->device->device_id, M_CAMXPT); + path->device->device_id = NULL; + } + + if (devid == NULL) { + /* Don't process the command as it was never sent */ + } else if (CCB_COMPLETED_OK(csio->ccb_h)) { + length = scsi_2btoul(devid->length); + if (length != 0) { + /* + * NB: device_id_len is actual response + * size, not buffer size. + */ + path->device->device_id_len = length + + SVPD_DEVICE_ID_HDR_LEN; + path->device->device_id = (uint8_t *)devid; + } + } else if (cam_periph_error(done_ccb, 0, + SF_RETRY_UA|SF_NO_PRINT, + &softc->saved_ccb) == ERESTART) { return; + } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { + /* Don't wedge the queue */ + xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, + /*run_queue*/TRUE); } - csio->data_ptr = NULL; - /* FALLTHROUGH */ + /* Free the device id space if we don't use it */ + if (devid && length == 0) + free(devid, M_CAMXPT); + xpt_release_ccb(done_ccb); + PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM); + xpt_schedule(periph, priority); + return; } - case PROBE_SERIAL_NUM_1: +probe_device_check: + case PROBE_SERIAL_NUM: { struct ccb_scsiio *csio; struct scsi_vpd_unit_serial_number *serial_buf; @@ -1395,13 +1494,6 @@ probedone(struct cam_periph *periph, union ccb *done_ccb) serial_buf = (struct scsi_vpd_unit_serial_number *)csio->data_ptr; - /* Clean up from previous instance of this device */ - if (path->device->serial_num != NULL) { - free(path->device->serial_num, M_CAMXPT); - path->device->serial_num = NULL; - path->device->serial_num_len = 0; - } - if (serial_buf == NULL) { /* * Don't process the command as it was never sent @@ -2227,6 +2319,10 @@ scsi_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id) device->queue_flags = 0; device->serial_num = NULL; device->serial_num_len = 0; + device->device_id = NULL; + device->device_id_len = 0; + device->supported_vpds = NULL; + device->supported_vpds_len = 0; /* * XXX should be limited by number of CCBs this bus can @@ -2337,6 +2433,31 @@ scsi_devise_transport(struct cam_path *path) } static void +scsi_getdev_advinfo(union ccb *start_ccb) +{ + struct cam_ed *device; + struct ccb_getdev_advinfo *cgdai; + off_t amt; + + device = start_ccb->ccb_h.path->device; + cgdai = &start_ccb->cgdai; + switch(cgdai->buftype) { + case CGDAI_TYPE_SCSI_DEVID: + cgdai->provsiz = device->device_id_len; + if (device->device_id_len == 0) + break; + amt = device->device_id_len; + if (cgdai->provsiz > cgdai->bufsiz) + amt = cgdai->bufsiz; + bcopy(device->device_id, cgdai->buf, amt); + break; + default: + break; + } + start_ccb->ccb_h.status = CAM_REQ_CMP; +} + +static void scsi_action(union ccb *start_ccb) { @@ -2365,6 +2486,11 @@ scsi_action(union ccb *start_ccb) (*(sim->sim_action))(sim, start_ccb); break; } + case XPT_GDEV_ADVINFO: + { + scsi_getdev_advinfo(start_ccb); + break; + } default: xpt_action_default(start_ccb); break; diff --git a/sys/cam/scsi/smp_all.c b/sys/cam/scsi/smp_all.c new file mode 100644 index 0000000..064f1d5 --- /dev/null +++ b/sys/cam/scsi/smp_all.c @@ -0,0 +1,620 @@ +/*- + * Copyright (c) 2010 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * $Id: //depot/users/kenm/FreeBSD-test/sys/cam/scsi/smp_all.c#4 $ + */ + +/* + * Serial Management Protocol helper functions. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/types.h> +#ifdef _KERNEL +#include <sys/systm.h> +#include <sys/libkern.h> +#include <sys/kernel.h> +#else /* _KERNEL */ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#endif /* _KERNEL */ + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_xpt.h> +#include <cam/scsi/smp_all.h> +#include <sys/sbuf.h> + +#ifndef _KERNEL +#include <camlib.h> +#endif + +static char *smp_yesno(int val); + +static char * +smp_yesno(int val) +{ + char *str; + + if (val) + str = "Yes"; + else + str = "No"; + + return (str); +} + +struct smp_error_table_entry { + uint8_t function_result; + const char *desc; +}; + +/* List current as of SPL Revision 7 */ +static struct smp_error_table_entry smp_error_table[] = { + {SMP_FR_ACCEPTED, "SMP Function Accepted"}, + {SMP_FR_UNKNOWN_FUNC, "Unknown SMP Function"}, + {SMP_FR_FUNCTION_FAILED, "SMP Function Failed"}, + {SMP_FR_INVALID_REQ_FRAME_LEN, "Invalid Request Frame Length"}, + {SMP_FR_INVALID_EXP_CHG_CNT, "Invalid Expander Change Count"}, + {SMP_FR_BUSY, "Busy"}, + {SMP_FR_INCOMPLETE_DESC_LIST, "Incomplete Descriptor List"}, + {SMP_FR_PHY_DOES_NOT_EXIST, "Phy Does Not Exist"}, + {SMP_FR_INDEX_DOES_NOT_EXIST, "Index Does Not Exist"}, + {SMP_FR_PHY_DOES_NOT_SUP_SATA, "Phy Does Not Support SATA"}, + {SMP_FR_UNKNOWN_PHY_OP, "Unknown Phy Operation"}, + {SMP_FR_UNKNOWN_PHY_TEST_FUNC, "Unknown Phy Test Function"}, + {SMP_FR_PHY_TEST_FUNC_INPROG, "Phy Test Function In Progress"}, + {SMP_FR_PHY_VACANT, "Phy Vacant"}, + {SMP_FR_UNKNOWN_PHY_EVENT_SRC, "Unknown Phy Event Source"}, + {SMP_FR_UNKNOWN_DESC_TYPE, "Unknown Descriptor Type"}, + {SMP_FR_UNKNOWN_PHY_FILTER, "Unknown Phy Filter"}, + {SMP_FR_AFFILIATION_VIOLATION, "Affiliation Violation"}, + {SMP_FR_SMP_ZONE_VIOLATION, "SMP Zone Violation"}, + {SMP_FR_NO_MGMT_ACCESS_RIGHTS, "No Management Access Rights"}, + {SMP_FR_UNKNOWN_ED_ZONING_VAL, "Unknown Enable Disable Zoning Value"}, + {SMP_FR_ZONE_LOCK_VIOLATION, "Zone Lock Violation"}, + {SMP_FR_NOT_ACTIVATED, "Not Activated"}, + {SMP_FR_ZG_OUT_OF_RANGE, "Zone Group Out of Range"}, + {SMP_FR_NO_PHYS_PRESENCE, "No Physical Presence"}, + {SMP_FR_SAVING_NOT_SUP, "Saving Not Supported"}, + {SMP_FR_SRC_ZONE_DNE, "Source Zone Group Does Not Exist"}, + {SMP_FR_DISABLED_PWD_NOT_SUP, "Disabled Password Not Supported"} +}; + +const char * +smp_error_desc(int function_result) +{ + int i; + + for (i = 0; i < (sizeof(smp_error_table)/sizeof(smp_error_table[0])); + i++){ + if (function_result == smp_error_table[i].function_result) + return (smp_error_table[i].desc); + } + return ("Reserved Function Result"); +} + +/* List current as of SPL Revision 7 */ +struct smp_cmd_table_entry { + uint8_t cmd_num; + const char *desc; +} smp_cmd_table[] = { + {SMP_FUNC_REPORT_GENERAL, "REPORT GENERAL"}, + {SMP_FUNC_REPORT_MANUF_INFO, "REPORT MANUFACTURER INFORMATION"}, + {SMP_FUNC_REPORT_SC_STATUS, "REPORT SELF-CONFIGURATION STATUS"}, + {SMP_FUNC_REPORT_ZONE_PERM_TBL, "REPORT ZONE PERMISSION TABLE"}, + {SMP_FUNC_REPORT_BROADCAST, "REPORT BROADCAST"}, + {SMP_FUNC_DISCOVER, "DISCOVER"}, + {SMP_FUNC_REPORT_PHY_ERR_LOG, "REPORT PHY ERROR LOG"}, + {SMP_FUNC_REPORT_PHY_SATA, "REPORT PHY SATA"}, + {SMP_FUNC_REPORT_ROUTE_INFO, "REPORT ROUTE INFORMATION"}, + {SMP_FUNC_REPORT_PHY_EVENT, "REPORT PHY EVENT"}, + {SMP_FUNC_DISCOVER_LIST, "DISCOVER LIST"}, + {SMP_FUNC_REPORT_PHY_EVENT_LIST, "REPORT PHY EVENT LIST"}, + {SMP_FUNC_REPORT_EXP_RTL, "REPORT EXPANDER ROUTE TABLE LIST"}, + {SMP_FUNC_CONFIG_GENERAL, "CONFIGURE GENERAL"}, + {SMP_FUNC_ENABLE_DISABLE_ZONING, "ENABLE DISABLE ZONING"}, + {SMP_FUNC_ZONED_BROADCAST, "ZONED BROADCAST"}, + {SMP_FUNC_ZONE_LOCK, "ZONE LOCK"}, + {SMP_FUNC_ZONE_ACTIVATE, "ZONE ACTIVATE"}, + {SMP_FUNC_ZONE_UNLOCK, "ZONE UNLOCK"}, + {SMP_FUNC_CONFIG_ZM_PWD, "CONFIGURE ZONE MANAGER PASSWORD"}, + {SMP_FUNC_CONFIG_ZONE_PHY_INFO, "CONFIGURE ZONE PHY INFORMATION"}, + {SMP_FUNC_CONFIG_ZONE_PERM_TBL, "CONFIGURE ZONE PERMISSION TABLE"}, + {SMP_FUNC_CONFIG_ROUTE_INFO, "CONFIGURE ROUTE INFORMATION"}, + {SMP_FUNC_PHY_CONTROL, "PHY CONTROL"}, + {SMP_FUNC_PHY_TEST_FUNC, "PHY TEST FUNCTION"}, + {SMP_FUNC_CONFIG_PHY_EVENT, "CONFIGURE PHY EVENT"} +}; + +const char * +smp_command_desc(uint8_t cmd_num) +{ + int i; + + for (i = 0; i < (sizeof(smp_cmd_table)/sizeof(smp_cmd_table[0])) && + smp_cmd_table[i].cmd_num <= cmd_num; i++) { + if (cmd_num == smp_cmd_table[i].cmd_num) + return (smp_cmd_table[i].desc); + } + + /* + * 0x40 to 0x7f and 0xc0 to 0xff are the vendor specific SMP + * command ranges. + */ + if (((cmd_num >= 0x40) && (cmd_num <= 0x7f)) + || (cmd_num >= 0xc0)) { + return ("Vendor Specific SMP Command"); + } else { + return ("Unknown SMP Command"); + } +} + +/* + * Decode a SMP request buffer into a string of hexadecimal numbers. + * + * smp_request: SMP request + * request_len: length of the SMP request buffer, may be reduced if the + * caller only wants part of the buffer printed + * sb: sbuf(9) buffer + * line_prefix: prefix for new lines, or an empty string ("") + * first_line_len: length left on first line + * line_len: total length of subsequent lines, 0 for no additional lines + * if there are no additional lines, first line will get ... + * at the end if there is additional data + */ +void +smp_command_decode(uint8_t *smp_request, int request_len, struct sbuf *sb, + char *line_prefix, int first_line_len, int line_len) +{ + int i, cur_len; + + for (i = 0, cur_len = first_line_len; i < request_len; i++) { + /* + * Each byte takes 3 characters. As soon as we go less + * than 6 (meaning we have at least 3 and at most 5 + * characters left), check to see whether the subsequent + * line length (line_len) is long enough to bother with. + * If the user set it to 0, or some other length that isn't + * enough to hold at least the prefix and one byte, put ... + * on the first line to indicate that there is more data + * and bail out. + */ + if ((cur_len < 6) + && (line_len < (strlen(line_prefix) + 3))) { + sbuf_printf(sb, "..."); + return; + } + if (cur_len < 3) { + sbuf_printf(sb, "\n%s", line_prefix); + cur_len = line_len - strlen(line_prefix); + } + sbuf_printf(sb, "%02x ", smp_request[i]); + cur_len = cur_len - 3; + } +} + +void +smp_command_sbuf(struct ccb_smpio *smpio, struct sbuf *sb, + char *line_prefix, int first_line_len, int line_len) +{ + sbuf_printf(sb, "%s. ", smp_command_desc(smpio->smp_request[1])); + + /* + * Acccount for the command description and the period and space + * after the command description. + */ + first_line_len -= strlen(smp_command_desc(smpio->smp_request[1])) + 2; + + smp_command_decode(smpio->smp_request, smpio->smp_request_len, sb, + line_prefix, first_line_len, line_len); +} + +/* + * Print SMP error output. For userland commands, we need the cam_device + * structure so we can get the path information from the CCB. + */ +#ifdef _KERNEL +void +smp_error_sbuf(struct ccb_smpio *smpio, struct sbuf *sb) +#else /* !_KERNEL*/ +void +smp_error_sbuf(struct cam_device *device, struct ccb_smpio *smpio, + struct sbuf *sb) +#endif /* _KERNEL/!_KERNEL */ +{ + char path_str[64]; + +#ifdef _KERNEL + xpt_path_string(smpio->ccb_h.path, path_str, sizeof(path_str)); +#else + cam_path_string(device, path_str, sizeof(path_str)); +#endif + smp_command_sbuf(smpio, sb, path_str, 80 - strlen(path_str), 80); + sbuf_printf(sb, "\n"); + + sbuf_cat(sb, path_str); + sbuf_printf(sb, "SMP Error: %s (0x%x)\n", + smp_error_desc(smpio->smp_response[2]), + smpio->smp_response[2]); +} + +/* + * Decode the SMP REPORT GENERAL response. The format is current as of SPL + * Revision 7, but the parsing should be backward compatible for older + * versions of the spec. + */ +void +smp_report_general_sbuf(struct smp_report_general_response *response, + int response_len, struct sbuf *sb) +{ + sbuf_printf(sb, "Report General\n"); + sbuf_printf(sb, "Response Length: %d words (%d bytes)\n", + response->response_len, + response->response_len * SMP_WORD_LEN); + sbuf_printf(sb, "Expander Change Count: %d\n", + scsi_2btoul(response->expander_change_count)); + sbuf_printf(sb, "Expander Route Indexes: %d\n", + scsi_2btoul(response->expander_route_indexes)); + sbuf_printf(sb, "Long Response: %s\n", + smp_yesno(response->long_response & + SMP_RG_LONG_RESPONSE)); + sbuf_printf(sb, "Number of Phys: %d\n", response->num_phys); + sbuf_printf(sb, "Table to Table Supported: %s\n", + smp_yesno(response->config_bits0 & + SMP_RG_TABLE_TO_TABLE_SUP)); + sbuf_printf(sb, "Zone Configuring: %s\n", + smp_yesno(response->config_bits0 & + SMP_RG_ZONE_CONFIGURING)); + sbuf_printf(sb, "Self Configuring: %s\n", + smp_yesno(response->config_bits0 & + SMP_RG_SELF_CONFIGURING)); + sbuf_printf(sb, "STP Continue AWT: %s\n", + smp_yesno(response->config_bits0 & + SMP_RG_STP_CONTINUE_AWT)); + sbuf_printf(sb, "Open Reject Retry Supported: %s\n", + smp_yesno(response->config_bits0 & + SMP_RG_OPEN_REJECT_RETRY_SUP)); + sbuf_printf(sb, "Configures Others: %s\n", + smp_yesno(response->config_bits0 & + SMP_RG_CONFIGURES_OTHERS)); + sbuf_printf(sb, "Configuring: %s\n", + smp_yesno(response->config_bits0 & + SMP_RG_CONFIGURING)); + sbuf_printf(sb, "Externally Configurable Route Table: %s\n", + smp_yesno(response->config_bits0 & + SMP_RG_CONFIGURING)); + sbuf_printf(sb, "Enclosure Logical Identifier: 0x%016jx\n", + (uintmax_t)scsi_8btou64(response->encl_logical_id)); + + /* + * If the response->response_len is 0, then we don't have the + * extended information. Also, if the user didn't allocate enough + * space for the full request, don't try to parse it. + */ + if ((response->response_len == 0) + || (response_len < (sizeof(struct smp_report_general_response) - + sizeof(response->crc)))) + return; + + sbuf_printf(sb, "STP Bus Inactivity Time Limit: %d\n", + scsi_2btoul(response->stp_bus_inact_time_limit)); + sbuf_printf(sb, "STP Maximum Connect Time Limit: %d\n", + scsi_2btoul(response->stp_max_conn_time_limit)); + sbuf_printf(sb, "STP SMP I_T Nexus Loss Time: %d\n", + scsi_2btoul(response->stp_smp_it_nexus_loss_time)); + + sbuf_printf(sb, "Number of Zone Groups: %d\n", + (response->config_bits1 & SMP_RG_NUM_ZONE_GROUPS_MASK) >> + SMP_RG_NUM_ZONE_GROUPS_SHIFT); + sbuf_printf(sb, "Zone Locked: %s\n", + smp_yesno(response->config_bits1 & SMP_RG_ZONE_LOCKED)); + sbuf_printf(sb, "Physical Presence Supported: %s\n", + smp_yesno(response->config_bits1 & SMP_RG_PP_SUPPORTED)); + sbuf_printf(sb, "Physical Presence Asserted: %s\n", + smp_yesno(response->config_bits1 & SMP_RG_PP_ASSERTED)); + sbuf_printf(sb, "Zoning Supported: %s\n", + smp_yesno(response->config_bits1 & + SMP_RG_ZONING_SUPPORTED)); + sbuf_printf(sb, "Zoning Enabled: %s\n", + smp_yesno(response->config_bits1 & SMP_RG_ZONING_ENABLED)); + + sbuf_printf(sb, "Saving: %s\n", + smp_yesno(response->config_bits2 & SMP_RG_SAVING)); + sbuf_printf(sb, "Saving Zone Manager Password Supported: %s\n", + smp_yesno(response->config_bits2 & + SMP_RG_SAVING_ZM_PWD_SUP)); + sbuf_printf(sb, "Saving Zone Phy Information Supported: %s\n", + smp_yesno(response->config_bits2 & + SMP_RG_SAVING_PHY_INFO_SUP)); + sbuf_printf(sb, "Saving Zone Permission Table Supported: %s\n", + smp_yesno(response->config_bits2 & + SMP_RG_SAVING_ZPERM_TAB_SUP)); + sbuf_printf(sb, "Saving Zoning Enabled Supported: %s\n", + smp_yesno(response->config_bits2 & + SMP_RG_SAVING_ZENABLED_SUP)); + + sbuf_printf(sb, "Maximum Number of Routed SAS Addresses: %d\n", + scsi_2btoul(response->max_num_routed_addrs)); + + sbuf_printf(sb, "Active Zone Manager SAS Address: 0x%016jx\n", + scsi_8btou64(response->active_zm_address)); + + sbuf_printf(sb, "Zone Inactivity Time Limit: %d\n", + scsi_2btoul(response->zone_lock_inact_time_limit)); + + sbuf_printf(sb, "First Enclosure Connector Element Index: %d\n", + response->first_encl_conn_el_index); + + sbuf_printf(sb, "Number of Enclosure Connector Element Indexes: %d\n", + response->num_encl_conn_el_indexes); + + sbuf_printf(sb, "Reduced Functionality: %s\n", + smp_yesno(response->reduced_functionality & + SMP_RG_REDUCED_FUNCTIONALITY)); + + sbuf_printf(sb, "Time to Reduced Functionality: %d\n", + response->time_to_reduced_func); + sbuf_printf(sb, "Initial Time to Reduced Functionality: %d\n", + response->initial_time_to_reduced_func); + sbuf_printf(sb, "Maximum Reduced Functionality Time: %d\n", + response->max_reduced_func_time); + + sbuf_printf(sb, "Last Self-Configuration Status Descriptor Index: %d\n", + scsi_2btoul(response->last_sc_stat_desc_index)); + + sbuf_printf(sb, "Maximum Number of Storated Self-Configuration " + "Status Descriptors: %d\n", + scsi_2btoul(response->max_sc_stat_descs)); + + sbuf_printf(sb, "Last Phy Event List Descriptor Index: %d\n", + scsi_2btoul(response->last_phy_evl_desc_index)); + + sbuf_printf(sb, "Maximum Number of Stored Phy Event List " + "Descriptors: %d\n", + scsi_2btoul(response->max_stored_pel_descs)); + + sbuf_printf(sb, "STP Reject to Open Limit: %d\n", + scsi_2btoul(response->stp_reject_to_open_limit)); +} + +/* + * Decode the SMP REPORT MANUFACTURER INFORMATION response. The format is + * current as of SPL Revision 7, but the parsing should be backward + * compatible for older versions of the spec. + */ +void +smp_report_manuf_info_sbuf(struct smp_report_manuf_info_response *response, + int response_len, struct sbuf *sb) +{ + char vendor[16], product[48], revision[16]; + char comp_vendor[16]; + + sbuf_printf(sb, "Report Manufacturer Information\n"); + sbuf_printf(sb, "Expander Change count: %d\n", + scsi_2btoul(response->expander_change_count)); + sbuf_printf(sb, "SAS 1.1 Format: %s\n", + smp_yesno(response->sas_11_format & SMP_RMI_SAS11_FORMAT)); + cam_strvis(vendor, response->vendor, sizeof(response->vendor), + sizeof(vendor)); + cam_strvis(product, response->product, sizeof(response->product), + sizeof(product)); + cam_strvis(revision, response->revision, sizeof(response->revision), + sizeof(revision)); + sbuf_printf(sb, "<%s %s %s>\n", vendor, product, revision); + + if ((response->sas_11_format & SMP_RMI_SAS11_FORMAT) == 0) { + uint8_t *curbyte; + int line_start, line_cursor; + + sbuf_printf(sb, "Vendor Specific Data:\n"); + + /* + * Print out the bytes roughly in the style of hd(1), but + * without the extra ASCII decoding. Hexadecimal line + * numbers on the left, and 16 bytes per line, with an + * extra space after the first 8 bytes. + * + * It would be nice if this sort of thing were available + * in a library routine. + */ + for (curbyte = (uint8_t *)&response->comp_vendor, line_start= 1, + line_cursor = 0; curbyte < (uint8_t *)&response->crc; + curbyte++, line_cursor++) { + if (line_start != 0) { + sbuf_printf(sb, "%08lx ", + (unsigned long)(curbyte - + (uint8_t *)response)); + line_start = 0; + line_cursor = 0; + } + sbuf_printf(sb, "%02x", *curbyte); + + if (line_cursor == 15) { + sbuf_printf(sb, "\n"); + line_start = 1; + } else + sbuf_printf(sb, " %s", (line_cursor == 7) ? + " " : ""); + } + if (line_cursor != 16) + sbuf_printf(sb, "\n"); + return; + } + + cam_strvis(comp_vendor, response->comp_vendor, + sizeof(response->comp_vendor), sizeof(comp_vendor)); + sbuf_printf(sb, "Component Vendor: %s\n", comp_vendor); + sbuf_printf(sb, "Component ID: %#x\n", scsi_2btoul(response->comp_id)); + sbuf_printf(sb, "Component Revision: %#x\n", response->comp_revision); + sbuf_printf(sb, "Vendor Specific: 0x%016jx\n", + (uintmax_t)scsi_8btou64(response->vendor_specific)); +} + +/* + * Compose a SMP REPORT GENERAL request and put it into a CCB. This is + * current as of SPL Revision 7. + */ +void +smp_report_general(struct ccb_smpio *smpio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + struct smp_report_general_request *request, int request_len, + uint8_t *response, int response_len, int long_response, + uint32_t timeout) +{ + cam_fill_smpio(smpio, + retries, + cbfcnp, + /*flags*/CAM_DIR_BOTH, + (uint8_t *)request, + request_len - SMP_CRC_LEN, + response, + response_len, + timeout); + + bzero(request, sizeof(*request)); + + request->frame_type = SMP_FRAME_TYPE_REQUEST; + request->function = SMP_FUNC_REPORT_GENERAL; + request->response_len = long_response ? SMP_RG_RESPONSE_LEN : 0; + request->request_len = 0; +} + +/* + * Compose a SMP DISCOVER request and put it into a CCB. This is current + * as of SPL Revision 7. + */ +void +smp_discover(struct ccb_smpio *smpio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + struct smp_discover_request *request, int request_len, + uint8_t *response, int response_len, int long_response, + int ignore_zone_group, int phy, uint32_t timeout) +{ + cam_fill_smpio(smpio, + retries, + cbfcnp, + /*flags*/CAM_DIR_BOTH, + (uint8_t *)request, + request_len - SMP_CRC_LEN, + response, + response_len, + timeout); + + bzero(request, sizeof(*request)); + request->frame_type = SMP_FRAME_TYPE_REQUEST; + request->function = SMP_FUNC_DISCOVER; + request->response_len = long_response ? SMP_DIS_RESPONSE_LEN : 0; + request->request_len = long_response ? SMP_DIS_REQUEST_LEN : 0; + if (ignore_zone_group != 0) + request->ignore_zone_group |= SMP_DIS_IGNORE_ZONE_GROUP; + request->phy = phy; +} + +/* + * Compose a SMP REPORT MANUFACTURER INFORMATION request and put it into a + * CCB. This is current as of SPL Revision 7. + */ +void +smp_report_manuf_info(struct ccb_smpio *smpio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + struct smp_report_manuf_info_request *request, + int request_len, uint8_t *response, int response_len, + int long_response, uint32_t timeout) +{ + cam_fill_smpio(smpio, + retries, + cbfcnp, + /*flags*/CAM_DIR_BOTH, + (uint8_t *)request, + request_len - SMP_CRC_LEN, + response, + response_len, + timeout); + + bzero(request, sizeof(*request)); + + request->frame_type = SMP_FRAME_TYPE_REQUEST; + request->function = SMP_FUNC_REPORT_MANUF_INFO; + request->response_len = long_response ? SMP_RMI_RESPONSE_LEN : 0; + request->request_len = long_response ? SMP_RMI_REQUEST_LEN : 0; +} + +/* + * Compose a SMP PHY CONTROL request and put it into a CCB. This is + * current as of SPL Revision 7. + */ +void +smp_phy_control(struct ccb_smpio *smpio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + struct smp_phy_control_request *request, int request_len, + uint8_t *response, int response_len, int long_response, + uint32_t expected_exp_change_count, int phy, int phy_op, + int update_pp_timeout_val, uint64_t attached_device_name, + int prog_min_prl, int prog_max_prl, int slumber_partial, + int pp_timeout_value, uint32_t timeout) +{ + cam_fill_smpio(smpio, + retries, + cbfcnp, + /*flags*/CAM_DIR_BOTH, + (uint8_t *)request, + request_len - SMP_CRC_LEN, + response, + response_len, + timeout); + + bzero(request, sizeof(*request)); + + request->frame_type = SMP_FRAME_TYPE_REQUEST; + request->function = SMP_FUNC_PHY_CONTROL; + request->response_len = long_response ? SMP_PC_RESPONSE_LEN : 0; + request->request_len = long_response ? SMP_PC_REQUEST_LEN : 0; + scsi_ulto2b(expected_exp_change_count, request->expected_exp_chg_cnt); + request->phy = phy; + request->phy_operation = phy_op; + + if (update_pp_timeout_val != 0) + request->update_pp_timeout |= SMP_PC_UPDATE_PP_TIMEOUT; + + scsi_u64to8b(attached_device_name, request->attached_device_name); + request->prog_min_phys_link_rate = (prog_min_prl << + SMP_PC_PROG_MIN_PL_RATE_SHIFT) & SMP_PC_PROG_MIN_PL_RATE_MASK; + request->prog_max_phys_link_rate = (prog_max_prl << + SMP_PC_PROG_MAX_PL_RATE_SHIFT) & SMP_PC_PROG_MAX_PL_RATE_MASK; + request->config_bits0 = slumber_partial; + request->pp_timeout_value = pp_timeout_value; +} + diff --git a/sys/cam/scsi/smp_all.h b/sys/cam/scsi/smp_all.h new file mode 100644 index 0000000..99e05de --- /dev/null +++ b/sys/cam/scsi/smp_all.h @@ -0,0 +1,520 @@ +/*- + * Copyright (c) 2010 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * $Id: //depot/users/kenm/FreeBSD-test/sys/cam/scsi/smp_all.h#4 $ + * $FreeBSD$ + */ + +/* + * Serial Management Protocol definitions. + */ + +#ifndef _SCSI_SMP_ALL_H +#define _SCSI_SMP_ALL_H 1 + +#define SMP_FRAME_TYPE_REQUEST 0x40 +#define SMP_FRAME_TYPE_RESPONSE 0x41 +#define SMP_WORD_LEN 4 +#define SMP_CRC_LEN 4 + +/* + * SMP Functions (current as of SPL Revision 7) + */ +/* 0x00 to 0x7f: SMP input functions */ +/* 0x00 to 0x0f: General SMP input functions */ +#define SMP_FUNC_REPORT_GENERAL 0x00 +#define SMP_FUNC_REPORT_MANUF_INFO 0x01 +#define SMP_FUNC_REPORT_SC_STATUS 0x03 +#define SMP_FUNC_REPORT_ZONE_PERM_TBL 0x04 +#define SMP_FUNC_REPORT_ZONE_MAN_PWD 0x05 +#define SMP_FUNC_REPORT_BROADCAST 0x06 + +/* 0x10 to 0x1f: Phy-based SMP input functions */ +#define SMP_FUNC_DISCOVER 0x10 +#define SMP_FUNC_REPORT_PHY_ERR_LOG 0x11 +#define SMP_FUNC_REPORT_PHY_SATA 0x12 +#define SMP_FUNC_REPORT_ROUTE_INFO 0x13 +#define SMP_FUNC_REPORT_PHY_EVENT 0x14 + +/* 0x20 to 0x2f: Descriptor list-based SMP input functions */ +#define SMP_FUNC_DISCOVER_LIST 0x20 +#define SMP_FUNC_REPORT_PHY_EVENT_LIST 0x21 +#define SMP_FUNC_REPORT_EXP_RTL 0x22 + +/* 0x30 to 0x3f: Reserved for SMP input functions */ +/* 0x40 to 0x7f: Vendor specific */ + +/* 0x80 to 0xff: SMP output functions */ +/* 0x80 to 0x8f: General SMP output functions */ +#define SMP_FUNC_CONFIG_GENERAL 0x80 +#define SMP_FUNC_ENABLE_DISABLE_ZONING 0x81 +#define SMP_FUNC_ZONED_BROADCAST 0x85 +#define SMP_FUNC_ZONE_LOCK 0x86 +#define SMP_FUNC_ZONE_ACTIVATE 0x87 +#define SMP_FUNC_ZONE_UNLOCK 0x88 +#define SMP_FUNC_CONFIG_ZM_PWD 0x89 +#define SMP_FUNC_CONFIG_ZONE_PHY_INFO 0x8a +#define SMP_FUNC_CONFIG_ZONE_PERM_TBL 0x8b + +/* 0x90 to 0x9f: Phy-based SMP output functions */ +#define SMP_FUNC_CONFIG_ROUTE_INFO 0x90 +#define SMP_FUNC_PHY_CONTROL 0x91 +#define SMP_FUNC_PHY_TEST_FUNC 0x92 +#define SMP_FUNC_CONFIG_PHY_EVENT 0x93 + +/* 0xa0 to 0xbf: Reserved for SMP output functions */ +/* 0xc0 to 0xff: Vendor specific */ + +/* + * Function Results (current as of SPL Revision 7) + */ +#define SMP_FR_ACCEPTED 0x00 +#define SMP_FR_UNKNOWN_FUNC 0x01 +#define SMP_FR_FUNCTION_FAILED 0x02 +#define SMP_FR_INVALID_REQ_FRAME_LEN 0x03 +#define SMP_FR_INVALID_EXP_CHG_CNT 0x04 +#define SMP_FR_BUSY 0x05 +#define SMP_FR_INCOMPLETE_DESC_LIST 0x06 +#define SMP_FR_PHY_DOES_NOT_EXIST 0x10 +#define SMP_FR_INDEX_DOES_NOT_EXIST 0x11 +#define SMP_FR_PHY_DOES_NOT_SUP_SATA 0x12 +#define SMP_FR_UNKNOWN_PHY_OP 0x13 +#define SMP_FR_UNKNOWN_PHY_TEST_FUNC 0x14 +#define SMP_FR_PHY_TEST_FUNC_INPROG 0x15 +#define SMP_FR_PHY_VACANT 0x16 +#define SMP_FR_UNKNOWN_PHY_EVENT_SRC 0x17 +#define SMP_FR_UNKNOWN_DESC_TYPE 0x18 +#define SMP_FR_UNKNOWN_PHY_FILTER 0x19 +#define SMP_FR_AFFILIATION_VIOLATION 0x1a +#define SMP_FR_SMP_ZONE_VIOLATION 0x20 +#define SMP_FR_NO_MGMT_ACCESS_RIGHTS 0x21 +#define SMP_FR_UNKNOWN_ED_ZONING_VAL 0x22 +#define SMP_FR_ZONE_LOCK_VIOLATION 0x23 +#define SMP_FR_NOT_ACTIVATED 0x24 +#define SMP_FR_ZG_OUT_OF_RANGE 0x25 +#define SMP_FR_NO_PHYS_PRESENCE 0x26 +#define SMP_FR_SAVING_NOT_SUP 0x27 +#define SMP_FR_SRC_ZONE_DNE 0x28 +#define SMP_FR_DISABLED_PWD_NOT_SUP 0x29 + +/* + * REPORT GENERAL request and response, current as of SPL Revision 7. + */ +struct smp_report_general_request +{ + uint8_t frame_type; + uint8_t function; + uint8_t response_len; + uint8_t request_len; + uint8_t crc[4]; +}; + +struct smp_report_general_response +{ + uint8_t frame_type; + uint8_t function; + uint8_t function_result; + uint8_t response_len; +#define SMP_RG_RESPONSE_LEN 0x11 + uint8_t expander_change_count[2]; + uint8_t expander_route_indexes[2]; + uint8_t long_response; +#define SMP_RG_LONG_RESPONSE 0x80 + uint8_t num_phys; + uint8_t config_bits0; +#define SMP_RG_TABLE_TO_TABLE_SUP 0x80 +#define SMP_RG_ZONE_CONFIGURING 0x40 +#define SMP_RG_SELF_CONFIGURING 0x20 +#define SMP_RG_STP_CONTINUE_AWT 0x10 +#define SMP_RG_OPEN_REJECT_RETRY_SUP 0x08 +#define SMP_RG_CONFIGURES_OTHERS 0x04 +#define SMP_RG_CONFIGURING 0x02 +#define SMP_RG_EXT_CONFIG_ROUTE_TABLE 0x01 + uint8_t reserved0; + uint8_t encl_logical_id[8]; + uint8_t reserved1[8]; + uint8_t reserved2[2]; + uint8_t stp_bus_inact_time_limit[2]; + uint8_t stp_max_conn_time_limit[2]; + uint8_t stp_smp_it_nexus_loss_time[2]; + uint8_t config_bits1; +#define SMP_RG_NUM_ZONE_GROUPS_MASK 0xc0 +#define SMP_RG_NUM_ZONE_GROUPS_SHIFT 6 +#define SMP_RG_ZONE_LOCKED 0x10 +#define SMP_RG_PP_SUPPORTED 0x08 +#define SMP_RG_PP_ASSERTED 0x04 +#define SMP_RG_ZONING_SUPPORTED 0x02 +#define SMP_RG_ZONING_ENABLED 0x01 + uint8_t config_bits2; +#define SMP_RG_SAVING 0x10 +#define SMP_RG_SAVING_ZM_PWD_SUP 0x08 +#define SMP_RG_SAVING_PHY_INFO_SUP 0x04 +#define SMP_RG_SAVING_ZPERM_TAB_SUP 0x02 +#define SMP_RG_SAVING_ZENABLED_SUP 0x01 + uint8_t max_num_routed_addrs[2]; + uint8_t active_zm_address[8]; + uint8_t zone_lock_inact_time_limit[2]; + uint8_t reserved3[2]; + uint8_t reserved4; + uint8_t first_encl_conn_el_index; + uint8_t num_encl_conn_el_indexes; + uint8_t reserved5; + uint8_t reduced_functionality; +#define SMP_RG_REDUCED_FUNCTIONALITY 0x80 + uint8_t time_to_reduced_func; + uint8_t initial_time_to_reduced_func; + uint8_t max_reduced_func_time; + uint8_t last_sc_stat_desc_index[2]; + uint8_t max_sc_stat_descs[2]; + uint8_t last_phy_evl_desc_index[2]; + uint8_t max_stored_pel_descs[2]; + uint8_t stp_reject_to_open_limit[2]; + uint8_t reserved6[2]; + uint8_t crc[4]; +}; + +/* + * REPORT MANUFACTURER INFORMATION request and response, current as of SPL + * Revision 7. + */ +struct smp_report_manuf_info_request +{ + uint8_t frame_type; + uint8_t function; + uint8_t response_len; + uint8_t request_len; +#define SMP_RMI_REQUEST_LEN 0x00 + uint8_t crc[4]; +}; + +struct smp_report_manuf_info_response +{ + uint8_t frame_type; + uint8_t function; + uint8_t function_result; + uint8_t response_len; +#define SMP_RMI_RESPONSE_LEN 0x0e + uint8_t expander_change_count[2]; + uint8_t reserved0[2]; + uint8_t sas_11_format; +#define SMP_RMI_SAS11_FORMAT 0x01 + uint8_t reserved1[3]; + uint8_t vendor[8]; + uint8_t product[16]; + uint8_t revision[4]; + uint8_t comp_vendor[8]; + uint8_t comp_id[2]; + uint8_t comp_revision; + uint8_t reserved2; + uint8_t vendor_specific[8]; + uint8_t crc[4]; +}; + +/* + * DISCOVER request and response, current as of SPL Revision 7. + */ +struct smp_discover_request +{ + uint8_t frame_type; + uint8_t function; + uint8_t response_len; + uint8_t request_len; +#define SMP_DIS_REQUEST_LEN 0x02 + uint8_t reserved0[4]; + uint8_t ignore_zone_group; +#define SMP_DIS_IGNORE_ZONE_GROUP 0x01 + uint8_t phy; + uint8_t reserved1[2]; + uint8_t crc[4]; +}; + +struct smp_discover_response +{ + uint8_t frame_type; + uint8_t function; + uint8_t function_result; + uint8_t response_len; +#define SMP_DIS_RESPONSE_LEN 0x20 + uint8_t expander_change_count[2]; + uint8_t reserved0[3]; + uint8_t phy; + uint8_t reserved1[2]; + uint8_t attached_device; +#define SMP_DIS_AD_TYPE_MASK 0x70 +#define SMP_DIS_AD_TYPE_NONE 0x00 +#define SMP_DIS_AD_TYPE_SAS_SATA 0x10 +#define SMP_DIS_AD_TYPE_EXP 0x20 +#define SMP_DIS_AD_TYPE_EXP_OLD 0x30 +#define SMP_DIS_ATTACH_REASON_MASK 0x0f + uint8_t neg_logical_link_rate; +#define SMP_DIS_LR_MASK 0x0f +#define SMP_DIS_LR_DISABLED 0x01 +#define SMP_DIS_LR_PHY_RES_PROB 0x02 +#define SMP_DIS_LR_SPINUP_HOLD 0x03 +#define SMP_DIS_LR_PORT_SEL 0x04 +#define SMP_DIS_LR_RESET_IN_PROG 0x05 +#define SMP_DIS_LR_UNSUP_PHY_ATTACHED 0x06 +#define SMP_DIS_LR_G1_15GBPS 0x08 +#define SMP_DIS_LR_G2_30GBPS 0x09 +#define SMP_DIS_LR_G3_60GBPS 0x0a + uint8_t config_bits0; +#define SMP_DIS_ATTACHED_SSP_INIT 0x08 +#define SMP_DIS_ATTACHED_STP_INIT 0x04 +#define SMP_DIS_ATTACHED_SMP_INIT 0x02 +#define SMP_DIS_ATTACHED_SATA_HOST 0x01 + uint8_t config_bits1; +#define SMP_DIS_ATTACHED_SATA_PORTSEL 0x80 +#define SMP_DIS_STP_BUFFER_TOO_SMALL 0x10 +#define SMP_DIS_ATTACHED_SSP_TARG 0x08 +#define SMP_DIS_ATTACHED_STP_TARG 0x04 +#define SMP_DIS_ATTACHED_SMP_TARG 0x02 +#define SMP_DIS_ATTACHED_SATA_DEV 0x01 + uint8_t sas_address[8]; + uint8_t attached_sas_address[8]; + uint8_t attached_phy_id; + uint8_t config_bits2; +#define SMP_DIS_ATT_SLUMB_CAP 0x10 +#define SMP_DIS_ATT_PAR_CAP 0x08 +#define SMP_DIS_ATT_IN_ZPSDS_PER 0x04 +#define SMP_DIS_ATT_REQ_IN_ZPSDS 0x02 +#define SMP_DIS_ATT_BREAK_RPL_CAP 0x01 + uint8_t reserved2[6]; + uint8_t link_rate0; +#define SMP_DIS_PROG_MIN_LR_MASK 0xf0 +#define SMP_DIS_PROG_MIN_LR_SHIFT 4 +#define SMP_DIS_HARD_MIN_LR_MASK 0x0f + uint8_t link_rate1; +#define SMP_DIS_PROG_MAX_LR_MAX 0xf0 +#define SMP_DIS_PROG_MAX_LR_SHIFT 4 +#define SMP_DIS_HARD_MAX_LR_MASK 0x0f + uint8_t phy_change_count; + uint8_t pp_timeout; +#define SMP_DIS_VIRTUAL_PHY 0x80 +#define SMP_DIS_PP_TIMEOUT_MASK 0x0f + uint8_t routing_attr; + uint8_t conn_type; + uint8_t conn_el_index; + uint8_t conn_phys_link; + uint8_t config_bits3; +#define SMP_DIS_PHY_POW_COND_MASK 0xc0 +#define SMP_DIS_PHY_POW_COND_SHIFT 6 +#define SMP_DIS_SAS_SLUMB_CAP 0x08 +#define SMP_DIS_SAS_PART_CAP 0x04 +#define SMP_DIS_SATA_SLUMB_CAP 0x02 +#define SMP_DIS_SATA_PART_CAP 0x01 + uint8_t config_bits4; +#define SMP_DIS_SAS_SLUMB_ENB 0x08 +#define SMP_DIS_SAS_PART_ENB 0x04 +#define SMP_DIS_SATA_SLUMB_ENB 0x02 +#define SMP_DIS_SATA_PART_ENB 0x01 + uint8_t vendor_spec[2]; + uint8_t attached_dev_name[8]; + uint8_t config_bits5; +#define SMP_DIS_REQ_IN_ZPSDS_CHG 0x40 +#define SMP_DIS_IN_ZPSDS_PER 0x20 +#define SMP_DIS_REQ_IN_ZPSDS 0x10 +#define SMP_DIS_ZG_PER 0x04 +#define SMP_DIS_IN_ZPSDS 0x02 +#define SMP_DIS_ZONING_ENB 0x01 + uint8_t reserved3[2]; + uint8_t zone_group; + uint8_t self_config_status; + uint8_t self_config_levels_comp; + uint8_t reserved4[2]; + uint8_t self_config_sas_addr[8]; + uint8_t prog_phy_cap[4]; + uint8_t current_phy_cap[4]; + uint8_t attached_phy_cap[4]; + uint8_t reserved5[6]; + uint8_t neg_phys_link_rate; +#define SMP_DIS_REASON_MASK 0xf0 +#define SMP_DIS_REASON_SHIFT 4 +#define SMP_DIS_PHYS_LR_MASK 0x0f + uint8_t config_bits6; +#define SMP_DIS_OPTICAL_MODE_ENB 0x04 +#define SMP_DIS_NEG_SSC 0x02 +#define SMP_DIS_HW_MUX_SUP 0x01 + uint8_t config_bits7; +#define SMP_DIS_DEF_IN_ZPSDS_PER 0x20 +#define SMP_DIS_DEF_REQ_IN_ZPSDS 0x10 +#define SMP_DIS_DEF_ZG_PER 0x04 +#define SMP_DIS_DEF_ZONING_ENB 0x01 + uint8_t reserved6; + uint8_t reserved7; + uint8_t default_zone_group; + uint8_t config_bits8; +#define SMP_DIS_SAVED_IN_ZPSDS_PER 0x20 +#define SMP_DIS_SAVED_REQ_IN_SPSDS 0x10 +#define SMP_DIS_SAVED_ZG_PER 0x04 +#define SMP_DIS_SAVED_ZONING_ENB 0x01 + uint8_t reserved8; + uint8_t reserved9; + uint8_t saved_zone_group; + uint8_t config_bits9; +#define SMP_DIS_SHADOW_IN_ZPSDS_PER 0x20 +#define SMP_DIS_SHADOW_IN_REQ_IN_ZPSDS 0x10 +#define SMP_DIS_SHADOW_ZG_PER 0x04 + uint8_t reserved10; + uint8_t reserved11; + uint8_t shadow_zone_group; + uint8_t device_slot_num; + uint8_t device_slot_group_num; + uint8_t device_slot_group_out_conn[6]; + uint8_t stp_buffer_size[2]; + uint8_t reserved12; + uint8_t reserved13; + uint8_t crc[4]; +}; + +/* + * PHY CONTROL request and response. Current as of SPL Revision 7. + */ +struct smp_phy_control_request +{ + uint8_t frame_type; + uint8_t function; + uint8_t response_len; +#define SMP_PC_RESPONSE_LEN 0x00 + uint8_t request_len; +#define SMP_PC_REQUEST_LEN 0x09 + uint8_t expected_exp_chg_cnt[2]; + uint8_t reserved0[3]; + uint8_t phy; + uint8_t phy_operation; +#define SMP_PC_PHY_OP_NOP 0x00 +#define SMP_PC_PHY_OP_LINK_RESET 0x01 +#define SMP_PC_PHY_OP_HARD_RESET 0x02 +#define SMP_PC_PHY_OP_DISABLE 0x03 +#define SMP_PC_PHY_OP_CLEAR_ERR_LOG 0x05 +#define SMP_PC_PHY_OP_CLEAR_AFFILIATON 0x06 +#define SMP_PC_PHY_OP_TRANS_SATA_PSS 0x07 +#define SMP_PC_PHY_OP_CLEAR_STP_ITN_LS 0x08 +#define SMP_PC_PHY_OP_SET_ATT_DEV_NAME 0x09 + uint8_t update_pp_timeout; +#define SMP_PC_UPDATE_PP_TIMEOUT 0x01 + uint8_t reserved1[12]; + uint8_t attached_device_name[8]; + uint8_t prog_min_phys_link_rate; +#define SMP_PC_PROG_MIN_PL_RATE_MASK 0xf0 +#define SMP_PC_PROG_MIN_PL_RATE_SHIFT 4 + uint8_t prog_max_phys_link_rate; +#define SMP_PC_PROG_MAX_PL_RATE_MASK 0xf0 +#define SMP_PC_PROG_MAX_PL_RATE_SHIFT 4 + uint8_t config_bits0; +#define SMP_PC_SP_NC 0x00 +#define SMP_PC_SP_DISABLE 0x02 +#define SMP_PC_SP_ENABLE 0x01 +#define SMP_PC_SAS_SLUMBER_NC 0x00 +#define SMP_PC_SAS_SLUMBER_DISABLE 0x80 +#define SMP_PC_SAS_SLUMBER_ENABLE 0x40 +#define SMP_PC_SAS_SLUMBER_MASK 0xc0 +#define SMP_PC_SAS_SLUMBER_SHIFT 6 +#define SMP_PC_SAS_PARTIAL_NC 0x00 +#define SMP_PC_SAS_PARTIAL_DISABLE 0x20 +#define SMP_PC_SAS_PARTIAL_ENABLE 0x10 +#define SMP_PC_SAS_PARTIAL_MASK 0x30 +#define SMP_PC_SAS_PARTIAL_SHIFT 4 +#define SMP_PC_SATA_SLUMBER_NC 0x00 +#define SMP_PC_SATA_SLUMBER_DISABLE 0x08 +#define SMP_PC_SATA_SLUMBER_ENABLE 0x04 +#define SMP_PC_SATA_SLUMBER_MASK 0x0c +#define SMP_PC_SATA_SLUMBER_SHIFT 2 +#define SMP_PC_SATA_PARTIAL_NC 0x00 +#define SMP_PC_SATA_PARTIAL_DISABLE 0x02 +#define SMP_PC_SATA_PARTIAL_ENABLE 0x01 +#define SMP_PC_SATA_PARTIAL_MASK 0x03 +#define SMP_PC_SATA_PARTIAL_SHIFT 0 + uint8_t reserved2; + uint8_t pp_timeout_value; +#define SMP_PC_PP_TIMEOUT_MASK 0x0f + uint8_t reserved3[3]; + uint8_t crc[4]; +}; + +struct smp_phy_control_response +{ + uint8_t frame_type; + uint8_t function; + uint8_t function_result; + uint8_t response_len; +#define SMP_PC_RESPONSE_LEN 0x00 + uint8_t crc[4]; +}; + +__BEGIN_DECLS + +const char *smp_error_desc(int function_result); +const char *smp_command_desc(uint8_t cmd_num); +void smp_command_decode(uint8_t *smp_request, int request_len, struct sbuf *sb, + char *line_prefix, int first_line_len, int line_len); +void smp_command_sbuf(struct ccb_smpio *smpio, struct sbuf *sb, + char *line_prefix, int first_line_len, int line_len); + +#ifdef _KERNEL +void smp_error_sbuf(struct ccb_smpio *smpio, struct sbuf *sb); +#else /* !_KERNEL*/ +void smp_error_sbuf(struct cam_device *device, struct ccb_smpio *smpio, + struct sbuf *sb); +#endif /* _KERNEL/!_KERNEL */ + +void smp_report_general_sbuf(struct smp_report_general_response *response, + int response_len, struct sbuf *sb); + +void smp_report_manuf_info_sbuf(struct smp_report_manuf_info_response *response, + int response_len, struct sbuf *sb); + +void smp_report_general(struct ccb_smpio *smpio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + struct smp_report_general_request *request, + int request_len, uint8_t *response, int response_len, + int long_response, uint32_t timeout); + +void smp_discover(struct ccb_smpio *smpio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + struct smp_discover_request *request, int request_len, + uint8_t *response, int response_len, int long_response, + int ignore_zone_group, int phy, uint32_t timeout); + +void smp_report_manuf_info(struct ccb_smpio *smpio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + struct smp_report_manuf_info_request *request, + int request_len, uint8_t *response, int response_len, + int long_response, uint32_t timeout); + +void smp_phy_control(struct ccb_smpio *smpio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + struct smp_phy_control_request *request, int request_len, + uint8_t *response, int response_len, int long_response, + uint32_t expected_exp_change_count, int phy, int phy_op, + int update_pp_timeout_val, uint64_t attached_device_name, + int prog_min_prl, int prog_max_prl, int slumber_partial, + int pp_timeout_value, uint32_t timeout); +__END_DECLS + +#endif /*_SCSI_SMP_ALL_H*/ diff --git a/sys/conf/files b/sys/conf/files index b880c53..f85df8d 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -140,6 +140,7 @@ cam/scsi/scsi_ses.c optional ses cam/scsi/scsi_sg.c optional sg cam/scsi/scsi_targ_bh.c optional targbh cam/scsi/scsi_target.c optional targ +cam/scsi/smp_all.c optional scbus contrib/altq/altq/altq_cbq.c optional altq \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/altq/altq/altq_cdnr.c optional altq diff --git a/sys/dev/mps/mps.c b/sys/dev/mps/mps.c index 404b12e..1fb37e2 100644 --- a/sys/dev/mps/mps.c +++ b/sys/dev/mps/mps.c @@ -1569,17 +1569,53 @@ mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) sc = cm->cm_sc; /* - * Set up DMA direction flags. Note no support for - * bi-directional transactions. + * In this case, just print out a warning and let the chip tell the + * user they did the wrong thing. + */ + if ((cm->cm_max_segs != 0) && (nsegs > cm->cm_max_segs)) { + mps_printf(sc, "%s: warning: busdma returned %d segments, " + "more than the %d allowed\n", __func__, nsegs, + cm->cm_max_segs); + } + + /* + * Set up DMA direction flags. Note that we don't support + * bi-directional transfers, with the exception of SMP passthrough. */ sflags = 0; - if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) { + if (cm->cm_flags & MPS_CM_FLAGS_SMP_PASS) { + /* + * We have to add a special case for SMP passthrough, there + * is no easy way to generically handle it. The first + * S/G element is used for the command (therefore the + * direction bit needs to be set). The second one is used + * for the reply. We'll leave it to the caller to make + * sure we only have two buffers. + */ + /* + * Even though the busdma man page says it doesn't make + * sense to have both direction flags, it does in this case. + * We have one s/g element being accessed in each direction. + */ + dir = BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD; + + /* + * Set the direction flag on the first buffer in the SMP + * passthrough request. We'll clear it for the second one. + */ + sflags |= MPI2_SGE_FLAGS_DIRECTION | + MPI2_SGE_FLAGS_END_OF_BUFFER; + } else if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) { sflags |= MPI2_SGE_FLAGS_DIRECTION; dir = BUS_DMASYNC_PREWRITE; } else dir = BUS_DMASYNC_PREREAD; for (i = 0; i < nsegs; i++) { + if ((cm->cm_flags & MPS_CM_FLAGS_SMP_PASS) + && (i != 0)) { + sflags &= ~MPI2_SGE_FLAGS_DIRECTION; + } error = mps_add_dmaseg(cm, segs[i].ds_addr, segs[i].ds_len, sflags, nsegs - i); if (error != 0) { @@ -1595,6 +1631,13 @@ mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) return; } +static void +mps_data_cb2(void *arg, bus_dma_segment_t *segs, int nsegs, bus_size_t mapsize, + int error) +{ + mps_data_cb(arg, segs, nsegs, error); +} + /* * Note that the only error path here is from bus_dmamap_load(), which can * return EINPROGRESS if it is waiting for resources. @@ -1605,7 +1648,10 @@ mps_map_command(struct mps_softc *sc, struct mps_command *cm) MPI2_SGE_SIMPLE32 *sge; int error = 0; - if ((cm->cm_data != NULL) && (cm->cm_length != 0)) { + if (cm->cm_flags & MPS_CM_FLAGS_USE_UIO) { + error = bus_dmamap_load_uio(sc->buffer_dmat, cm->cm_dmamap, + &cm->cm_uio, mps_data_cb2, cm, 0); + } else if ((cm->cm_data != NULL) && (cm->cm_length != 0)) { error = bus_dmamap_load(sc->buffer_dmat, cm->cm_dmamap, cm->cm_data, cm->cm_length, mps_data_cb, cm, 0); } else { @@ -1619,7 +1665,7 @@ mps_map_command(struct mps_softc *sc, struct mps_command *cm) MPI2_SGE_FLAGS_SHIFT; sge->Address = 0; } - mps_enqueue_request(sc, cm); + mps_enqueue_request(sc, cm); } return (error); diff --git a/sys/dev/mps/mps_pci.c b/sys/dev/mps/mps_pci.c index 2b33ba8..2c57286 100644 --- a/sys/dev/mps/mps_pci.c +++ b/sys/dev/mps/mps_pci.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include <sys/conf.h> #include <sys/malloc.h> #include <sys/sysctl.h> +#include <sys/uio.h> #include <machine/bus.h> #include <machine/resource.h> diff --git a/sys/dev/mps/mps_sas.c b/sys/dev/mps/mps_sas.c index 5883e49..0e7ce03 100644 --- a/sys/dev/mps/mps_sas.c +++ b/sys/dev/mps/mps_sas.c @@ -41,6 +41,8 @@ __FBSDID("$FreeBSD$"); #include <sys/malloc.h> #include <sys/uio.h> #include <sys/sysctl.h> +#include <sys/sglist.h> +#include <sys/endian.h> #include <machine/bus.h> #include <machine/resource.h> @@ -55,6 +57,9 @@ __FBSDID("$FreeBSD$"); #include <cam/cam_periph.h> #include <cam/scsi/scsi_all.h> #include <cam/scsi/scsi_message.h> +#if __FreeBSD_version >= 900026 +#include <cam/scsi/smp_all.h> +#endif #include <dev/mps/mpi/mpi2_type.h> #include <dev/mps/mpi/mpi2.h> @@ -69,9 +74,11 @@ struct mpssas_target { uint16_t handle; uint8_t linkrate; uint64_t devname; + uint64_t sasaddr; uint32_t devinfo; uint16_t encl_handle; uint16_t encl_slot; + uint16_t parent_handle; int flags; #define MPSSAS_TARGET_INABORT (1 << 0) #define MPSSAS_TARGET_INRESET (1 << 1) @@ -144,6 +151,12 @@ static int mpssas_complete_tm_request(struct mps_softc *sc, struct mps_command *cm, int free_cm); static void mpssas_action_scsiio(struct mpssas_softc *, union ccb *); static void mpssas_scsiio_complete(struct mps_softc *, struct mps_command *); +#if __FreeBSD_version >= 900026 +static void mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm); +static void mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb, + uint64_t sasaddr); +static void mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb); +#endif /* __FreeBSD_version >= 900026 */ static void mpssas_resetdev(struct mpssas_softc *, struct mps_command *); static void mpssas_action_resetdev(struct mpssas_softc *, union ccb *); static void mpssas_resetdev_complete(struct mps_softc *, struct mps_command *); @@ -312,6 +325,8 @@ mpssas_probe_device_complete(struct mps_softc *sc, probe->target.devinfo = buf->DeviceInfo; probe->target.encl_handle = buf->EnclosureHandle; probe->target.encl_slot = buf->Slot; + probe->target.sasaddr = mps_to_u64(&buf->SASAddress); + probe->target.parent_handle = buf->ParentDevHandle; if (buf->DeviceInfo & MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH) { params->page_address = @@ -916,6 +931,11 @@ mpssas_action(struct cam_sim *sim, union ccb *ccb) case XPT_SCSI_IO: mpssas_action_scsiio(sassc, ccb); return; +#if __FreeBSD_version >= 900026 + case XPT_SMP_IO: + mpssas_action_smpio(sassc, ccb); + return; +#endif /* __FreeBSD_version >= 900026 */ default: ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; break; @@ -1361,6 +1381,9 @@ mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb) bcopy(csio->cdb_io.cdb_bytes, &req->CDB.CDB32[0],csio->cdb_len); req->IoFlags = csio->cdb_len; + /* + * XXX need to handle S/G lists and physical addresses here. + */ cm->cm_data = csio->data_ptr; cm->cm_length = csio->dxfer_len; cm->cm_sge = &req->SGL; @@ -1525,6 +1548,329 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm) xpt_done(ccb); } +#if __FreeBSD_version >= 900026 +static void +mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm) +{ + MPI2_SMP_PASSTHROUGH_REPLY *rpl; + MPI2_SMP_PASSTHROUGH_REQUEST *req; + uint64_t sasaddr; + union ccb *ccb; + + ccb = cm->cm_complete_data; + rpl = (MPI2_SMP_PASSTHROUGH_REPLY *)cm->cm_reply; + if (rpl == NULL) { + mps_dprint(sc, MPS_INFO, "%s: NULL cm_reply!\n", __func__); + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + goto bailout; + } + + req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req; + sasaddr = le32toh(req->SASAddress.Low); + sasaddr |= ((uint64_t)(le32toh(req->SASAddress.High))) << 32; + + if ((rpl->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS || + rpl->SASStatus != MPI2_SASSTATUS_SUCCESS) { + mps_dprint(sc, MPS_INFO, "%s: IOCStatus %04x SASStatus %02x\n", + __func__, rpl->IOCStatus, rpl->SASStatus); + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + goto bailout; + } + + mps_dprint(sc, MPS_INFO, "%s: SMP request to SAS address " + "%#jx completed successfully\n", __func__, + (uintmax_t)sasaddr); + + if (ccb->smpio.smp_response[2] == SMP_FR_ACCEPTED) + ccb->ccb_h.status = CAM_REQ_CMP; + else + ccb->ccb_h.status = CAM_SMP_STATUS_ERROR; + +bailout: + /* + * We sync in both directions because we had DMAs in the S/G list + * in both directions. + */ + bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); + mps_free_command(sc, cm); + xpt_done(ccb); +} + +static void +mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb, uint64_t sasaddr) +{ + struct mps_command *cm; + uint8_t *request, *response; + MPI2_SMP_PASSTHROUGH_REQUEST *req; + struct mps_softc *sc; + struct sglist *sg; + int error; + + sc = sassc->sc; + sg = NULL; + error = 0; + + /* + * XXX We don't yet support physical addresses here. + */ + if (ccb->ccb_h.flags & (CAM_DATA_PHYS|CAM_SG_LIST_PHYS)) { + mps_printf(sc, "%s: physical addresses not supported\n", + __func__); + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + return; + } + + /* + * If the user wants to send an S/G list, check to make sure they + * have single buffers. + */ + if (ccb->ccb_h.flags & CAM_SCATTER_VALID) { + /* + * The chip does not support more than one buffer for the + * request or response. + */ + if ((ccb->smpio.smp_request_sglist_cnt > 1) + || (ccb->smpio.smp_response_sglist_cnt > 1)) { + mps_printf(sc, "%s: multiple request or response " + "buffer segments not supported for SMP\n", + __func__); + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + return; + } + + /* + * The CAM_SCATTER_VALID flag was originally implemented + * for the XPT_SCSI_IO CCB, which only has one data pointer. + * We have two. So, just take that flag to mean that we + * might have S/G lists, and look at the S/G segment count + * to figure out whether that is the case for each individual + * buffer. + */ + if (ccb->smpio.smp_request_sglist_cnt != 0) { + bus_dma_segment_t *req_sg; + + req_sg = (bus_dma_segment_t *)ccb->smpio.smp_request; + request = (uint8_t *)req_sg[0].ds_addr; + } else + request = ccb->smpio.smp_request; + + if (ccb->smpio.smp_response_sglist_cnt != 0) { + bus_dma_segment_t *rsp_sg; + + rsp_sg = (bus_dma_segment_t *)ccb->smpio.smp_response; + response = (uint8_t *)rsp_sg[0].ds_addr; + } else + response = ccb->smpio.smp_response; + } else { + request = ccb->smpio.smp_request; + response = ccb->smpio.smp_response; + } + + cm = mps_alloc_command(sc); + if (cm == NULL) { + mps_printf(sc, "%s: cannot allocate command\n", __func__); + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(ccb); + return; + } + + req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req; + bzero(req, sizeof(*req)); + req->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; + + /* Allow the chip to use any route to this SAS address. */ + req->PhysicalPort = 0xff; + + req->RequestDataLength = ccb->smpio.smp_request_len; + req->SGLFlags = + MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE | MPI2_SGLFLAGS_SGL_TYPE_MPI; + + mps_dprint(sc, MPS_INFO, "%s: sending SMP request to SAS " + "address %#jx\n", __func__, (uintmax_t)sasaddr); + + mpi_init_sge(cm, req, &req->SGL); + + /* + * Set up a uio to pass into mps_map_command(). This allows us to + * do one map command, and one busdma call in there. + */ + cm->cm_uio.uio_iov = cm->cm_iovec; + cm->cm_uio.uio_iovcnt = 2; + cm->cm_uio.uio_segflg = UIO_SYSSPACE; + + /* + * The read/write flag isn't used by busdma, but set it just in + * case. This isn't exactly accurate, either, since we're going in + * both directions. + */ + cm->cm_uio.uio_rw = UIO_WRITE; + + cm->cm_iovec[0].iov_base = request; + cm->cm_iovec[0].iov_len = req->RequestDataLength; + cm->cm_iovec[1].iov_base = response; + cm->cm_iovec[1].iov_len = ccb->smpio.smp_response_len; + + cm->cm_uio.uio_resid = cm->cm_iovec[0].iov_len + + cm->cm_iovec[1].iov_len; + + /* + * Trigger a warning message in mps_data_cb() for the user if we + * wind up exceeding two S/G segments. The chip expects one + * segment for the request and another for the response. + */ + cm->cm_max_segs = 2; + + cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; + cm->cm_complete = mpssas_smpio_complete; + cm->cm_complete_data = ccb; + + /* + * Tell the mapping code that we're using a uio, and that this is + * an SMP passthrough request. There is a little special-case + * logic there (in mps_data_cb()) to handle the bidirectional + * transfer. + */ + cm->cm_flags |= MPS_CM_FLAGS_USE_UIO | MPS_CM_FLAGS_SMP_PASS | + MPS_CM_FLAGS_DATAIN | MPS_CM_FLAGS_DATAOUT; + + /* The chip data format is little endian. */ + req->SASAddress.High = htole32(sasaddr >> 32); + req->SASAddress.Low = htole32(sasaddr); + + /* + * XXX Note that we don't have a timeout/abort mechanism here. + * From the manual, it looks like task management requests only + * work for SCSI IO and SATA passthrough requests. We may need to + * have a mechanism to retry requests in the event of a chip reset + * at least. Hopefully the chip will insure that any errors short + * of that are relayed back to the driver. + */ + error = mps_map_command(sc, cm); + if ((error != 0) && (error != EINPROGRESS)) { + mps_printf(sc, "%s: error %d returned from mps_map_command()\n", + __func__, error); + goto bailout_error; + } + + return; + +bailout_error: + mps_free_command(sc, cm); + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(ccb); + return; + +} + +static void +mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb) +{ + struct mps_softc *sc; + struct mpssas_target *targ; + uint64_t sasaddr = 0; + + sc = sassc->sc; + + /* + * Make sure the target exists. + */ + targ = &sassc->targets[ccb->ccb_h.target_id]; + if (targ->handle == 0x0) { + mps_printf(sc, "%s: target %d does not exist!\n", __func__, + ccb->ccb_h.target_id); + ccb->ccb_h.status = CAM_SEL_TIMEOUT; + xpt_done(ccb); + return; + } + + /* + * If this device has an embedded SMP target, we'll talk to it + * directly. + * figure out what the expander's address is. + */ + if ((targ->devinfo & MPI2_SAS_DEVICE_INFO_SMP_TARGET) != 0) + sasaddr = targ->sasaddr; + + /* + * If we don't have a SAS address for the expander yet, try + * grabbing it from the page 0x83 information cached in the + * transport layer for this target. LSI expanders report the + * expander SAS address as the port-associated SAS address in + * Inquiry VPD page 0x83. Maxim expanders don't report it in page + * 0x83. + * + * XXX KDM disable this for now, but leave it commented out so that + * it is obvious that this is another possible way to get the SAS + * address. + * + * The parent handle method below is a little more reliable, and + * the other benefit is that it works for devices other than SES + * devices. So you can send a SMP request to a da(4) device and it + * will get routed to the expander that device is attached to. + * (Assuming the da(4) device doesn't contain an SMP target...) + */ +#if 0 + if (sasaddr == 0) + sasaddr = xpt_path_sas_addr(ccb->ccb_h.path); +#endif + + /* + * If we still don't have a SAS address for the expander, look for + * the parent device of this device, which is probably the expander. + */ + if (sasaddr == 0) { + struct mpssas_target *parent_target; + + if (targ->parent_handle == 0x0) { + mps_printf(sc, "%s: handle %d does not have a valid " + "parent handle!\n", __func__, targ->handle); + ccb->ccb_h.status = CAM_REQ_INVALID; + goto bailout; + } + parent_target = mpssas_find_target(sassc, 0, + targ->parent_handle); + + if (parent_target == NULL) { + mps_printf(sc, "%s: handle %d does not have a valid " + "parent target!\n", __func__, targ->handle); + ccb->ccb_h.status = CAM_REQ_INVALID; + goto bailout; + } + + if ((parent_target->devinfo & + MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0) { + mps_printf(sc, "%s: handle %d parent %d does not " + "have an SMP target!\n", __func__, + targ->handle, parent_target->handle); + ccb->ccb_h.status = CAM_REQ_INVALID; + goto bailout; + + } + + sasaddr = parent_target->sasaddr; + } + + if (sasaddr == 0) { + mps_printf(sc, "%s: unable to find SAS address for handle %d\n", + __func__, targ->handle); + ccb->ccb_h.status = CAM_REQ_INVALID; + goto bailout; + } + mpssas_send_smpcmd(sassc, ccb, sasaddr); + + return; + +bailout: + xpt_done(ccb); + +} + +#endif /* __FreeBSD_version >= 900026 */ + static void mpssas_action_resetdev(struct mpssas_softc *sassc, union ccb *ccb) { diff --git a/sys/dev/mps/mps_user.c b/sys/dev/mps/mps_user.c index ede4c02..7ca90c1 100644 --- a/sys/dev/mps/mps_user.c +++ b/sys/dev/mps/mps_user.c @@ -322,7 +322,7 @@ mps_user_write_cfg_page(struct mps_softc *sc, return (0); } -static void +void mpi_init_sge(struct mps_command *cm, void *req, void *sge) { int off, space; diff --git a/sys/dev/mps/mpsvar.h b/sys/dev/mps/mpsvar.h index db91030..f578347 100644 --- a/sys/dev/mps/mpsvar.h +++ b/sys/dev/mps/mpsvar.h @@ -60,11 +60,19 @@ struct mps_chain { uint32_t chain_busaddr; }; +/* + * This needs to be at least 2 to support SMP passthrough. + */ +#define MPS_IOVEC_COUNT 2 + struct mps_command { TAILQ_ENTRY(mps_command) cm_link; struct mps_softc *cm_sc; void *cm_data; u_int cm_length; + struct uio cm_uio; + struct iovec cm_iovec[MPS_IOVEC_COUNT]; + u_int cm_max_segs; u_int cm_sglsize; MPI2_SGE_IO_UNION *cm_sge; uint8_t *cm_req; @@ -82,6 +90,8 @@ struct mps_command { #define MPS_CM_FLAGS_DATAIN (1 << 4) #define MPS_CM_FLAGS_WAKEUP (1 << 5) #define MPS_CM_FLAGS_ACTIVE (1 << 6) +#define MPS_CM_FLAGS_USE_UIO (1 << 7) +#define MPS_CM_FLAGS_SMP_PASS (1 << 8) u_int cm_state; #define MPS_CM_STATE_FREE 0 #define MPS_CM_STATE_BUSY 1 @@ -238,12 +248,15 @@ mps_free_command(struct mps_softc *sc, struct mps_command *cm) { struct mps_chain *chain, *chain_temp; - if (cm->cm_reply != NULL) + if (cm->cm_reply != NULL) { mps_free_reply(sc, cm->cm_reply_data); + cm->cm_reply = NULL; + } cm->cm_flags = 0; cm->cm_complete = NULL; cm->cm_complete_data = NULL; cm->cm_targ = 0; + cm->cm_max_segs = 0; cm->cm_state = MPS_CM_STATE_FREE; TAILQ_FOREACH_SAFE(chain, &cm->cm_chain_list, chain_link, chain_temp) { TAILQ_REMOVE(&cm->cm_chain_list, chain, chain_link); @@ -368,6 +381,7 @@ int mps_map_command(struct mps_softc *sc, struct mps_command *cm); int mps_read_config_page(struct mps_softc *, struct mps_config_params *); int mps_write_config_page(struct mps_softc *, struct mps_config_params *); void mps_memaddr_cb(void *, bus_dma_segment_t *, int , int ); +void mpi_init_sge(struct mps_command *cm, void *req, void *sge); int mps_attach_user(struct mps_softc *); void mps_detach_user(struct mps_softc *); diff --git a/sys/sys/param.h b/sys/sys/param.h index ea90308..257d272 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 900025 /* Master, propagated to newvers */ +#define __FreeBSD_version 900026 /* Master, propagated to newvers */ #ifdef _KERNEL #define P_OSREL_SIGSEGV 700004 |