/* * cciss.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2007 Sergey Svishchev * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include "config.h" #if defined(linux) || defined(__linux__) # include # ifdef HAVE_LINUX_COMPILER_H # include # endif # if defined(HAVE_LINUX_CCISS_IOCTL_H) # include # define _HAVE_CCISS # endif # include # ifndef be32toh # define be32toh __be32_to_cpu # endif #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) # include # include CISS_LOCATION # define _HAVE_CCISS #endif #ifdef _HAVE_CCISS #include "cciss.h" #include "scsicmds.h" #include "utility.h" const char * cciss_cpp_cvsid = "$Id: cciss.cpp 4977 2019-11-22 19:57:04Z chrfranke $" CCISS_H_CVSID; typedef struct _ReportLUNdata_struct { uint32_t LUNListLength; /* always big-endian */ uint32_t reserved; uint8_t LUN[CISS_MAX_LUN][8]; } ReportLunData_struct; /* Structure/defines of Report Physical LUNS of drive */ #ifndef CISS_MAX_LUN #define CISS_MAX_LUN 16 #endif #define CISS_MAX_PHYS_LUN 1024 #define CISS_REPORT_PHYS 0xc3 #define LSCSI_DRIVER_SENSE 0x8 /* alternate CHECK CONDITION indication */ #define SEND_IOCTL_RESP_SENSE_LEN 16 /* ioctl limitation */ static int cciss_getlun(int device, int target, unsigned char *physlun, int report); static int cciss_sendpassthru(unsigned int cmdtype, unsigned char *CDB, unsigned int CDBlen, char *buff, unsigned int size, unsigned int LunID, unsigned char *scsi3addr, int fd); /* This is an interface that uses the cciss passthrough to talk to the SMART controller on the HP system. The cciss driver provides a way to send SCSI cmds through the CCISS passthrough. */ int cciss_io_interface(int device, int target, struct scsi_cmnd_io * iop, int report) { switch (iop->dxfer_dir) { case DXFER_NONE: case DXFER_FROM_DEVICE: break; default: return -ENOTSUP; // TODO: Support DXFER_TO_DEVICE } unsigned char phylun[8] = {0}; int status = cciss_getlun(device, target, phylun, report); if (report > 0) pout(" cciss_getlun(%d, %d) = 0x%x; scsi3addr: %02x %02x %02x %02x %02x %02x %02x %02x\n", device, target, status, phylun[0], phylun[1], phylun[2], phylun[3], phylun[4], phylun[5], phylun[6], phylun[7]); if (status) { return -ENXIO; /* give up, assume no device there */ } unsigned char sensebuf[SEND_IOCTL_RESP_SENSE_LEN]; unsigned char * pBuf = (iop->dxferp ? iop->dxferp : sensebuf); unsigned iBufLen = (iop->dxferp ? iop->dxfer_len : sizeof(sensebuf)); status = cciss_sendpassthru( 2, iop->cmnd, iop->cmnd_len, (char*) pBuf, iBufLen, 1, phylun, device); if (0 == status) { if (report > 0) pout(" status=0\n"); if (DXFER_FROM_DEVICE == iop->dxfer_dir) { if (report > 1) { int trunc = (iop->dxfer_len > 256) ? 1 : 0; pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, (trunc ? " [only first 256 bytes shown]" : "")); dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); } } return 0; } iop->scsi_status = status & 0x7e; /* bits 0 and 7 used to be for vendors */ if (LSCSI_DRIVER_SENSE == ((status >> 24) & 0xf)) iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; unsigned len = (SEND_IOCTL_RESP_SENSE_LEN < iop->max_sense_len) ? SEND_IOCTL_RESP_SENSE_LEN : iop->max_sense_len; if (len > iBufLen) len = iBufLen; if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && iop->sensep && (len > 0)) { memcpy(iop->sensep, pBuf, len); iop->resp_sense_len = len; if (report > 1) { pout(" >>> Sense buffer, len=%d:\n", (int)len); dStrHex((const uint8_t *)pBuf, len , 1); } } if (report) { if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) { pout(" status=%x: sense_key=%x asc=%x ascq=%x\n", status & 0xff, pBuf[2] & 0xf, pBuf[12], pBuf[13]); } else pout(" status=0x%x\n", status); } if (iop->scsi_status > 0) return 0; else { if (report > 0) pout(" ioctl status=0x%x but scsi status=0, fail with ENXIO\n", status); return -ENXIO; /* give up, assume no device there */ } } static int cciss_sendpassthru(unsigned int cmdtype, unsigned char *CDB, unsigned int CDBlen, char *buff, unsigned int size, unsigned int LunID, unsigned char *scsi3addr, int fd) { int err ; IOCTL_Command_struct iocommand; memset(&iocommand, 0, sizeof(iocommand)); if (cmdtype == 0) { // To controller; nothing to do } else if (cmdtype == 1) { iocommand.LUN_info.LogDev.VolId = LunID; iocommand.LUN_info.LogDev.Mode = 1; } else if (cmdtype == 2) { memcpy(&iocommand.LUN_info.LunAddrBytes,scsi3addr,8); iocommand.LUN_info.LogDev.Mode = 0; } else { pout("cciss_sendpassthru: bad cmdtype\n"); return 1; } memcpy(&iocommand.Request.CDB[0], CDB, CDBlen); iocommand.Request.CDBLen = CDBlen; iocommand.Request.Type.Type = TYPE_CMD; iocommand.Request.Type.Attribute = ATTR_SIMPLE; iocommand.Request.Type.Direction = XFER_READ; // TODO: OK for DXFER_NONE ? iocommand.Request.Timeout = 0; iocommand.buf_size = size; iocommand.buf = (unsigned char *)buff; if ((err = ioctl(fd, CCISS_PASSTHRU, &iocommand))) { pout("CCISS ioctl error %d (fd %d CDBLen %u buf_size %u)\n", fd, err, CDBlen, size); } return err; } static int cciss_getlun(int device, int target, unsigned char *physlun, int report) { unsigned char CDB[16]= {0}; ReportLunData_struct *luns; int reportlunsize = sizeof(*luns) + CISS_MAX_PHYS_LUN * 8; int ret; luns = (ReportLunData_struct *)malloc(reportlunsize); memset(luns, 0, reportlunsize); /* Get Physical LUN Info (for physical device) */ CDB[0] = CISS_REPORT_PHYS; CDB[6] = (reportlunsize >> 24) & 0xFF; /* MSB */ CDB[7] = (reportlunsize >> 16) & 0xFF; CDB[8] = (reportlunsize >> 8) & 0xFF; CDB[9] = reportlunsize & 0xFF; if ((ret = cciss_sendpassthru(0, CDB, 12, (char *)luns, reportlunsize, 0, NULL, device))) { free(luns); return ret; } if (report > 1) { unsigned int i,j; unsigned char *stuff = (unsigned char *)luns; pout("\n===== [%s] DATA START (BASE-16) =====\n", "LUN DATA"); for (i=0; i<(sizeof(_ReportLUNdata_struct)+15)/16; i++){ pout("%03d-%03d: ", 16*i, 16*(i+1)-1); for (j=0; j<15; j++) pout("%02x ",*stuff++); pout("%02x\n",*stuff++); } pout("===== [%s] DATA END (%u Bytes) =====\n\n", "LUN DATA", (unsigned)sizeof(_ReportLUNdata_struct)); } if (target >= 0 && target < (int) be32toh(luns->LUNListLength) / 8) { memcpy(physlun, luns->LUN[target], 8); free(luns); return 0; } free(luns); return 1; } #endif