diff --git a/smartmontools/ChangeLog b/smartmontools/ChangeLog index 975defb7d8997720bcf0227083c344b856cc8e8f..de3ddfe5d78487f63951ffcaba30a92f4c7bf2f7 100644 --- a/smartmontools/ChangeLog +++ b/smartmontools/ChangeLog @@ -1,5 +1,10 @@ $Id$ +2022-05-28 Douglas Gilbert <dgilbert@interlog.com> + + [SCSI]: more work for calling REPORT SUPPORTED OPERATION + CODES [RSOC] command. + 2022-05-27 Douglas Gilbert <dgilbert@interlog.com> [SCSI]: prepare for calling REPORT SUPPORTED OPERATION diff --git a/smartmontools/dev_interface.h b/smartmontools/dev_interface.h index 41129918e2103690af802f3b057c999afadaadde..6657f0f175617e14501da92e39a750a1fac8a85d 100644 --- a/smartmontools/dev_interface.h +++ b/smartmontools/dev_interface.h @@ -575,6 +575,12 @@ protected: struct scsi_cmnd_io; +struct scsi_rsoc_elem { + uint8_t cdb0; + uint8_t sa_valid; + uint16_t sa; +}; + /// SCSI device access class scsi_device : virtual public /*extends*/ smart_device @@ -596,6 +602,10 @@ public: bool use_rcap16() const { return rcap16_first; } + void set_spc4_or_higher() { spc4_or_above = true; } + + bool is_spc4_or_higher() const { return spc4_or_above; } + protected: /// Hide/unhide SCSI interface. void hide_scsi(bool hide = true) @@ -604,11 +614,15 @@ protected: /// Default constructor, registers device as SCSI. scsi_device() : smart_device(never_called), - rcap16_first(false) + rcap16_first(false), + spc4_or_above(false) { hide_scsi(false); } private: bool rcap16_first; + bool spc4_or_above; + /* rsoc: report supported operation codes (command) */ + std::vector<scsi_rsoc_elem> rsoc_list; }; diff --git a/smartmontools/os_freebsd.cpp b/smartmontools/os_freebsd.cpp index 5d0c8b68a7edf9d738530e6208580c90850203d2..9e07ba4717ae52102ce579e379f6a2b7c5e49bab 100644 --- a/smartmontools/os_freebsd.cpp +++ b/smartmontools/os_freebsd.cpp @@ -889,7 +889,7 @@ bool freebsd_megaraid_device::scsi_pass_through(scsi_cmnd_io *iop) char buff[256]; const int sz = (int)sizeof(buff); - np = scsi_get_opcode_name(ucp[0], false, 0); + np = scsi_get_opcode_name(ucp); j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>"); for (k = 0; k < (int)iop->cmnd_len; ++k) j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); @@ -1253,7 +1253,7 @@ bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) const unsigned char * ucp = iop->cmnd; const char * np; - np = scsi_get_opcode_name(ucp[0], false, 0); + np = scsi_get_opcode_name(ucp); pout(" [%s: ", np ? np : "<unknown opcode>"); for (k = 0; k < iop->cmnd_len; ++k) pout("%02x ", ucp[k]); diff --git a/smartmontools/os_linux.cpp b/smartmontools/os_linux.cpp index b750521fb58fc012a7497d43fda29e8bb3e4650d..b0b95e51d09983c66f889cd1dfa72354cf13e1fc 100644 --- a/smartmontools/os_linux.cpp +++ b/smartmontools/os_linux.cpp @@ -542,7 +542,7 @@ static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report, const int sz = (int)sizeof(buff); pout(">>>> do_scsi_cmnd_io: sg_io_ifc=%d\n", (int)sg_io_ifc); - np = scsi_get_opcode_name(ucp[0], false, 0); + np = scsi_get_opcode_name(ucp); j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>"); for (k = 0; k < (int)iop->cmnd_len; ++k) j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); @@ -921,7 +921,7 @@ bool linux_aacraid_device::scsi_pass_through(scsi_cmnd_io *iop) char buff[256]; const int sz = (int)sizeof(buff); - np = scsi_get_opcode_name(ucp[0], false, 0); + np = scsi_get_opcode_name(ucp); j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>"); for (k = 0; k < (int)iop->cmnd_len; ++k) j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); @@ -1226,7 +1226,7 @@ bool linux_megaraid_device::scsi_pass_through(scsi_cmnd_io *iop) char buff[256]; const int sz = (int)sizeof(buff); - np = scsi_get_opcode_name(ucp[0], false, 0); + np = scsi_get_opcode_name(ucp); j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>"); for (k = 0; k < (int)iop->cmnd_len; ++k) j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); diff --git a/smartmontools/os_netbsd.cpp b/smartmontools/os_netbsd.cpp index 66c3a4795bbeed8f7e6c475242af9571f7ff842f..612a384be657d96125526ffb53490ec55d789981 100644 --- a/smartmontools/os_netbsd.cpp +++ b/smartmontools/os_netbsd.cpp @@ -357,7 +357,7 @@ bool netbsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) const unsigned char * ucp = iop->cmnd; const char * np; - np = scsi_get_opcode_name(ucp[0], false, 0); + np = scsi_get_opcode_name(ucp); pout(" [%s: ", np ? np : "<unknown opcode>"); for (k = 0; k < iop->cmnd_len; ++k) pout("%02x ", ucp[k]); diff --git a/smartmontools/os_openbsd.cpp b/smartmontools/os_openbsd.cpp index d2ffe9c33a4dfaa9edb8453a14b6cff2c053e510..df010cc5a3c8640e6a493c8d31fe91c613f7d8f9 100644 --- a/smartmontools/os_openbsd.cpp +++ b/smartmontools/os_openbsd.cpp @@ -244,7 +244,7 @@ bool openbsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) const unsigned char * ucp = iop->cmnd; const char * np; - np = scsi_get_opcode_name(ucp[0], false, 0); + np = scsi_get_opcode_name(ucp); pout(" [%s: ", np ? np : "<unknown opcode>"); for (k = 0; k < iop->cmnd_len; ++k) pout("%02x ", ucp[k]); diff --git a/smartmontools/os_solaris.cpp b/smartmontools/os_solaris.cpp index 00c9645575f51a28eb7a41d61597bd545ecc4079..f8b4b022c0b110d1fc5e22ff151d720a27c20e8d 100644 --- a/smartmontools/os_solaris.cpp +++ b/smartmontools/os_solaris.cpp @@ -240,7 +240,7 @@ int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) const unsigned char * ucp = iop->cmnd; const char * np; - np = scsi_get_opcode_name(ucp[0], false, 0); + np = scsi_get_opcode_name(ucp); pout(" [%s: ", np ? np : "<unknown opcode>"); for (k = 0; k < (int)iop->cmnd_len; ++k) pout("%02x ", ucp[k]); diff --git a/smartmontools/os_win32.cpp b/smartmontools/os_win32.cpp index 0af305b1cfe5ee7971942c0149ee9df87e2efd6e..58905280ba31a0f80cbd52b2e3988f513562d8f2 100644 --- a/smartmontools/os_win32.cpp +++ b/smartmontools/os_win32.cpp @@ -2830,7 +2830,7 @@ bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop) char buff[256]; const int sz = (int)sizeof(buff); - np = scsi_get_opcode_name(ucp[0], false, 0); + np = scsi_get_opcode_name(ucp); j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>"); for (k = 0; k < (int)iop->cmnd_len; ++k) j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); @@ -2959,7 +2959,7 @@ static long scsi_pass_through_direct(HANDLE fd, UCHAR targetid, struct scsi_cmnd char buff[256]; const int sz = (int)sizeof(buff); - np = scsi_get_opcode_name(ucp[0], false, 0); + np = scsi_get_opcode_name(ucp); j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>"); for (k = 0; k < (int)iop->cmnd_len; ++k) j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); @@ -3396,7 +3396,7 @@ bool win_aacraid_device::scsi_pass_through(struct scsi_cmnd_io *iop) const char * np; char buff[256]; const int sz = (int)sizeof(buff); - np = scsi_get_opcode_name(ucp[0], false, 0); + np = scsi_get_opcode_name(ucp); j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>"); for (k = 0; k < (int)iop->cmnd_len; ++k) j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); diff --git a/smartmontools/scsicmds.cpp b/smartmontools/scsicmds.cpp index c094a90aa2d865ae6b51131a357978b18fcae8fb..58a3e86527befc617f16b72375428b786eb010fe 100644 --- a/smartmontools/scsicmds.cpp +++ b/smartmontools/scsicmds.cpp @@ -197,6 +197,36 @@ is_scsi_cdb(const uint8_t * cdbp, int clen) return false; } +enum scsi_sa_t { + scsi_sa_none = 0, + scsi_sa_b1b4n5, /* for cdb byte 1, bit 4, number 5 bits */ + scsi_sa_b8b7n16, +}; + +struct scsi_sa_var_map { + uint8_t cdb0; + enum scsi_sa_t sa_var; +}; + +static struct scsi_sa_var_map sa_var_a[] = { + {0x3b, scsi_sa_b1b4n5}, /* Write buffer modes_s */ + {0x3c, scsi_sa_b1b4n5}, /* Read buffer(10) modes_s */ + {0x48, scsi_sa_b1b4n5}, /* Sanitize sa_s */ + {0x5e, scsi_sa_b1b4n5}, /* Persistent reserve in sa_s */ + {0x5f, scsi_sa_b1b4n5}, /* Persistent reserve out sa_s */ + {0x7f, scsi_sa_b8b7n16}, /* Variable length commands */ + {0x83, scsi_sa_b1b4n5}, /* Extended copy out/cmd sa_s */ + {0x84, scsi_sa_b1b4n5}, /* Extended copy in sa_s */ + {0x8c, scsi_sa_b1b4n5}, /* Read attribute sa_s */ + {0x9b, scsi_sa_b1b4n5}, /* Read buffer(16) modes_s */ + {0x9e, scsi_sa_b1b4n5}, /* Service action in (16) */ + {0x9f, scsi_sa_b1b4n5}, /* Service action out (16) */ + {0xa3, scsi_sa_b1b4n5}, /* Maintenance in */ + {0xa4, scsi_sa_b1b4n5}, /* Maintenance out */ + {0xa9, scsi_sa_b1b4n5}, /* Service action out (12) */ + {0xab, scsi_sa_b1b4n5}, /* Service action in (12) */ +}; + struct scsi_opcode_name { uint8_t opcode; bool sa_valid; @@ -238,14 +268,43 @@ static const char * vendor_specific = "<vendor specific>"; /* Need to expand to take service action into account. For commands * of interest the service action is in the 2nd command byte */ const char * -scsi_get_opcode_name(uint8_t opcode, bool sa_valid, uint16_t sa) +scsi_get_opcode_name(const uint8_t * cdbp) { + uint8_t opcode = cdbp[0]; + uint8_t cdb0; + enum scsi_sa_t sa_var = scsi_sa_none; + bool sa_valid = false; + uint16_t sa = 0; + int k; + static const int sa_var_len = sizeof(sa_var_a) / + sizeof(sa_var_a[0]); static const int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]); if (opcode >= 0xc0) return vendor_specific; - for (int k = 0; k < len; ++k) { + for (k = 0; k < sa_var_len; ++k) { + cdb0 = sa_var_a[k].cdb0; + if (opcode == cdb0) { + sa_var = sa_var_a[k].sa_var; + break; + } + if (opcode < cdb0) + break; + } + switch (sa_var) { + case scsi_sa_none: + break; + case scsi_sa_b1b4n5: + sa_valid = true; + sa = cdbp[1] & 0x1f; + break; + case scsi_sa_b8b7n16: + sa_valid = true; + sa = sg_get_unaligned_be16(cdbp + 8); + break; + } + for (k = 0; k < len; ++k) { struct scsi_opcode_name * onp = &opcode_name_arr[k]; if (opcode == onp->opcode) { @@ -255,7 +314,7 @@ scsi_get_opcode_name(uint8_t opcode, bool sa_valid, uint16_t sa) if (sa == onp->sa) return onp->name; } - /* should see sa_valid and ! onp->sa_valid (or vice versa) */ + /* should not see sa_valid and ! onp->sa_valid (or vice versa) */ } else if (opcode < onp->opcode) return NULL; } @@ -865,11 +924,14 @@ scsiStdInquiry(scsi_device * device, uint8_t *pBuf, int bufLen) { struct scsi_sense_disect sinfo; struct scsi_cmnd_io io_hdr = {}; + int res; uint8_t cdb[6] = {}; uint8_t sense[32]; if ((bufLen < 0) || (bufLen > 1023)) return -EINVAL; + if (bufLen >= 36) /* normal case */ + memset(pBuf, 0, 36); io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = bufLen; io_hdr.dxferp = pBuf; @@ -882,8 +944,22 @@ scsiStdInquiry(scsi_device * device, uint8_t *pBuf, int bufLen) io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; if (! scsi_pass_through_yield_sense(device, &io_hdr, sinfo)) - return -device->get_errno(); - return scsiSimpleSenseFilter(&sinfo); + return -device->get_errno(); + res = scsiSimpleSenseFilter(&sinfo); + if ((SIMPLE_NO_ERROR == res) && (! device->is_spc4_or_higher())) { + if (((bufLen - io_hdr.resid) >= 36) && + (pBuf[2] >= 6) && /* VERSION field >= SPC-4 */ + ((pBuf[3] & 0xf) == 2)) { /* RESPONSE DATA field == 2 */ + uint8_t pdt = pBuf[0] & 0x1f; + + if ((SCSI_PT_DIRECT_ACCESS == pdt) || + (SCSI_PT_HOST_MANAGED == pdt) || + (SCSI_PT_SEQUENTIAL_ACCESS == pdt) || + (SCSI_PT_MEDIUM_CHANGER == pdt)) + device->set_spc4_or_higher(); + } + } + return res; } /* INQUIRY to fetch Vital Page Data. Returns 0 if ok, 1 if NOT READY @@ -1323,6 +1399,39 @@ scsiReadCapacity16(scsi_device * device, uint8_t *pBuf, int bufLen) return scsiSimpleSenseFilter(&sinfo); } +/* REPORT SUPPORTED OPERATION CODES [RSOC] command. If SIMPLE_NO_ERROR is + * returned then the response length is written to rspLen. */ +int +scsiRSOCcmd(scsi_device * device, uint8_t *pBuf, int bufLen, int & rspLen) +{ + struct scsi_cmnd_io io_hdr = {}; + struct scsi_sense_disect sinfo; + int res; + uint8_t cdb[12] = {}; + uint8_t sense[32]; + + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = MAINTENANCE_IN_12; + cdb[1] = MI_REP_SUP_OPCODES; + /* RCTD=0 (no timeout descriptors); REPORTING_OPTION=0 (all commands) */ + /* those settings imply response should contain 8 bytes per command */ + sg_put_unaligned_be32(bufLen, cdb + 6); + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + if (!scsi_pass_through_yield_sense(device, &io_hdr, sinfo)) + return -device->get_errno(); + res = scsiSimpleSenseFilter(&sinfo); + if (SIMPLE_NO_ERROR == res) + rspLen = bufLen - io_hdr.resid; + return res; +} + /* Return number of bytes of storage in 'device' or 0 if error. If * successful and lb_sizep is not NULL then the logical block size in bytes * is written to the location pointed to by lb_sizep. If the 'Logical Blocks diff --git a/smartmontools/scsicmds.h b/smartmontools/scsicmds.h index bcb03087326f1d47d4b2276f2eeeecd52249d357..95bad0e0bb3d6b3ac5f0eb45d245fc1ba814ac7c 100644 --- a/smartmontools/scsicmds.h +++ b/smartmontools/scsicmds.h @@ -106,6 +106,9 @@ #define DXFER_FROM_DEVICE 1 #define DXFER_TO_DEVICE 2 + +/* scsi_rsoc_elem and scsi_device is defined in dev_interface.h */ + struct scsi_cmnd_io { uint8_t * cmnd; /* [in]: ptr to SCSI command block (cdb) */ @@ -369,8 +372,6 @@ Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */ #define SCSI_TIMEOUT_SELF_TEST (5 * 60 * 60) /* allow max 5 hours for */ /* extended foreground self test */ - - #define LOGPAGEHDRSIZE 4 class scsi_device; @@ -479,6 +480,8 @@ int scsiReadCapacity10(scsi_device * device, unsigned int * last_lbp, int scsiReadCapacity16(scsi_device * device, uint8_t *pBuf, int bufLen); +int scsiRSOCcmd(scsi_device * device, uint8_t *pBuf, int bufLen, int & rspLen); + /* SMART specific commands */ int scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage, uint8_t *asc, uint8_t *ascq, uint8_t *currenttemp, @@ -523,7 +526,7 @@ int scsiSmartSelfTestAbort(scsi_device * device); const char * scsiTapeAlertsTapeDevice(unsigned short code); const char * scsiTapeAlertsChangerDevice(unsigned short code); -const char * scsi_get_opcode_name(uint8_t opcode, bool sa_valid, uint16_t sa); +const char * scsi_get_opcode_name(const uint8_t * cdbp); void scsi_format_id_string(char * out, const uint8_t * in, int n); void dStrHex(const uint8_t * up, int len, int no_ascii);