From c3cea8f4a94dd57d5aeeac27914ee9533e853c93 Mon Sep 17 00:00:00 2001 From: itycodes Date: Tue, 9 Jul 2024 08:40:39 +0200 Subject: [PATCH] Initial commit The tool is incomplete as of now, and does not compile yet. We need the right scsi command contents. A lot of the code was copied from smartmontools. Only megaraid support is currently present. --- main.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 main.c diff --git a/main.c b/main.c new file mode 100644 index 0000000..9aa3749 --- /dev/null +++ b/main.c @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MAX_IOCTL_SGE 16 + +#define MEGASAS_MAGIC 'M' +#define MEGASAS_IOC_FIRMWARE _IOWR(MEGASAS_MAGIC, 1, struct megasas_iocpacket) + +#define MFI_FRAME_DIR_NONE 0x0000 +#define MFI_FRAME_DIR_WRITE 0x0008 +#define MFI_FRAME_DIR_READ 0x0010 +#define MFI_FRAME_DIR_BOTH 0x0018 + +#define MFI_CMD_PD_SCSI_IO 0x04 + +#define SAT_ATA_PASSTHROUGH_12 0xa1 +#define SAT_ATA_PASSTHROUGH_16 0x85 + +#define ATA_SMART_CMD 0xb0 +#define ATA_SMART_WRITE_LOG_SECTOR 0xd6 + +struct megasas_sge32 { + uint32_t phys_addr; + uint32_t length; +} __attribute__ ((packed)); + +union megasas_sgl { + struct megasas_sge32 sge32[1]; +} __attribute__ ((packed)); + +struct megasas_pthru_frame { + + uint8_t cmd; /*00h */ + uint8_t sense_len; /*01h */ + uint8_t cmd_status; /*02h */ + uint8_t scsi_status; /*03h */ + + uint8_t target_id; /*04h */ + uint8_t lun; /*05h */ + uint8_t cdb_len; /*06h */ + uint8_t sge_count; /*07h */ + + uint32_t context; /*08h */ + uint32_t pad_0; /*0Ch */ + + uint16_t flags; /*10h */ + uint16_t timeout; /*12h */ + uint32_t data_xfer_len; /*14h */ + + uint32_t sense_buf_phys_addr_lo; /*18h */ + uint32_t sense_buf_phys_addr_hi; /*1Ch */ + + uint8_t cdb[16]; /*20h */ + union megasas_sgl sgl; /*30h */ + +} __attribute__ ((packed)); + +struct megasas_iocpacket { + uint16_t host_no; + uint16_t __pad1; + uint32_t sgl_off; + uint32_t sge_count; + uint32_t sense_off; + uint32_t sense_len; + union { + uint8_t raw[128]; + struct megasas_pthru_frame pthru; + } frame; + + struct iovec sgl[MAX_IOCTL_SGE]; +} __attribute__ ((packed)); + +#define DXFER_NONE 0 +#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) */ + size_t cmnd_len; /* [in]: number of bytes in SCSI command */ + int dxfer_dir; /* [in]: DXFER_NONE, DXFER_FROM_DEVICE, or + DXFER_TO_DEVICE */ + uint8_t * dxferp; /* [in]: ptr to outgoing or incoming data buffer */ + size_t dxfer_len; /* [in]: bytes to be transferred to/from dxferp */ + uint8_t * sensep; /* [in]: ptr to sense buffer, filled when + CHECK CONDITION status occurs */ + size_t max_sense_len; /* [in]: max number of bytes to write to sensep */ + unsigned timeout; /* [in]: seconds, 0-> default timeout (60 seconds?) */ + size_t resp_sense_len; /* [out]: sense buffer length written */ + uint8_t scsi_status; /* [out]: 0->ok, 2->CHECK CONDITION, etc ... */ + int resid; /* [out]: Number of bytes requested to be transferred + less actual number transferred (0 if not + supported) */ +}; + +bool prepare_dev(char* dev_name, uint32_t* out_m_hba) { + // dev_name should be in /dev/bus/%u + if (sscanf(dev_name, "/dev/bus/%u", out_m_hba) == 0) { + fprintf(stderr, "prepare_dev: bad dev_name\n"); + return false; + } + return true; +} + + +bool megasas_cmd(unsigned int m_hba, int m_fd, unsigned int m_disknum, + int cdbLen, void* cdb, int dataLen, void* data, + int /*senseLen*/, void * /*sense*/, int /*report*/, + int dxfer_dir) { + struct megasas_pthru_frame *pthru; + struct megasas_iocpacket uio; + + memset(&uio, 0, sizeof(uio)); + pthru = &uio.frame.pthru; + pthru->cmd = MFI_CMD_PD_SCSI_IO; + pthru->cmd_status = 0xFF; + pthru->scsi_status = 0x0; + pthru->target_id = m_disknum; + pthru->lun = 0; + pthru->cdb_len = cdbLen; + pthru->timeout = 0; + switch (dxfer_dir) { + case DXFER_NONE: + pthru->flags = MFI_FRAME_DIR_NONE; + break; + case DXFER_FROM_DEVICE: + pthru->flags = MFI_FRAME_DIR_READ; + break; + case DXFER_TO_DEVICE: + pthru->flags = MFI_FRAME_DIR_WRITE; + break; + default: + fprintf(stderr, "megasas_cmd: bad dxfer_dir\n"); + return false; + } + + if (dataLen > 0) { + pthru->sge_count = 1; + pthru->data_xfer_len = dataLen; + pthru->sgl.sge32[0].phys_addr = (intptr_t)data; + pthru->sgl.sge32[0].length = (uint32_t)dataLen; + } + memcpy(pthru->cdb, cdb, cdbLen); + + uio.host_no = m_hba; + if (dataLen > 0) { + printf("1322 branch taken\n"); + uio.sge_count = 1; + uio.sgl_off = offsetof(struct megasas_pthru_frame, sgl); + uio.sgl[0].iov_base = data; + uio.sgl[0].iov_len = dataLen; + } + + errno = 0; + int rc = ioctl(m_fd, MEGASAS_IOC_FIRMWARE, &uio); +} + + +bool scsi_pass_through(unsigned int m_hba, int m_fd, unsigned int m_disknum, struct scsi_cmnd_io *iop) +{ + // Controller rejects Test Unit Ready + if (iop->cmnd[0] == 0x00) + return true; + + if (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 || iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16) { + // Controller does not return ATA output registers in SAT sense data + if (iop->cmnd[2] & (1 << 5)) { // chk_cond + fprintf(stderr, "scsi_pass_through: ATA return descriptor not supported by controller firmware\n"); + return false; + } + } + // SMART WRITE LOG SECTOR causing media errors + if ((iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16 // SAT16 WRITE LOG + && iop->cmnd[14] == ATA_SMART_CMD && iop->cmnd[3]==0 && iop->cmnd[4] == ATA_SMART_WRITE_LOG_SECTOR) || + (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 // SAT12 WRITE LOG + && iop->cmnd[9] == ATA_SMART_CMD && iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR)) + { + fprintf(stderr, ": SMART WRITE LOG SECTOR may cause problems\n"); + return false; + } + + return megasas_cmd(m_hba, m_fd, m_disknum, iop->cmnd_len, iop->cmnd, + iop->dxfer_len, iop->dxferp, + iop->max_sense_len, iop->sensep, 0, iop->dxfer_dir); +} + +int main(int argc, char* argv[]) { + int m_fd = open("/dev/megaraid_sas_ioctl_node", O_RDWR); + char* dev_name = "/dev/bus/0"; + uint32_t m_hba = 0; + prepare_dev(dev_name, &m_hba); + uint32_t m_disknum = 0; // megaraid,0 + struct scsi_cmnd_io iop = { + // TODO fill this up + }; + scsi_pass_through(m_hba, m_fd, m_disknum, &iop); +}