/* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_sync_cache_10(int sg_fd, int sync_nv, int immed, int group, unsigned int lba, unsigned int count, int noisy, int verbose) { static const char * const cdb_name_s = "synchronize cache(10)"; int res, ret, k, sense_cat; unsigned char sc_cdb[SYNCHRONIZE_CACHE_CMDLEN] = {SYNCHRONIZE_CACHE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (sync_nv) sc_cdb[1] |= 4; if (immed) sc_cdb[1] |= 2; sg_put_unaligned_be32((uint32_t)lba, sc_cdb + 2); sc_cdb[6] = group & 0x1f; if (count > 0xffff) { pr2ws("count too big\n"); return -1; } sg_put_unaligned_be16((int16_t)count, sc_cdb + 7); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SYNCHRONIZE_CACHE_CMDLEN; ++k) pr2ws("%02x ", sc_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, sc_cdb, sizeof(sc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, 0, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
static int ll_sync_cache_16(int sg_fd, int sync_nv, int immed, int group, uint64_t lba, unsigned int num_lb, int to_secs, int noisy, int verbose) { int res, ret, k, sense_cat; unsigned char sc_cdb[SYNCHRONIZE_CACHE16_CMDLEN] = { SYNCHRONIZE_CACHE16_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (sync_nv) sc_cdb[1] |= 4; /* obsolete in sbc3r35d */ if (immed) sc_cdb[1] |= 2; sg_put_unaligned_be64(lba, sc_cdb + 2); sc_cdb[14] = group & 0x1f; sg_put_unaligned_be32((uint32_t)num_lb, sc_cdb + 10); if (verbose) { pr2serr(" synchronize cache(16) cdb: "); for (k = 0; k < SYNCHRONIZE_CACHE16_CMDLEN; ++k) pr2serr("%02x ", sc_cdb[k]); pr2serr("\n"); } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("synchronize cache(16): out of memory\n"); return -1; } set_scsi_pt_cdb(ptvp, sc_cdb, sizeof(sc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, to_secs, verbose); ret = sg_cmds_process_resp(ptvp, "synchronize cache(16)", res, 0, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI REPORT TIMESTAMP command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ static int sg_ll_rep_timestamp(int sg_fd, void * resp, int mx_resp_len, int * residp, int noisy, int verbose) { int k, ret, res, sense_cat; unsigned char rtCmdBlk[REP_TIMESTAMP_CMDLEN] = {SG_MAINTENANCE_IN, REP_TIMESTAMP_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; sg_put_unaligned_be32((uint32_t)mx_resp_len, rtCmdBlk + 6); if (verbose) { pr2serr(" Report timestamp cdb: "); for (k = 0; k < REP_TIMESTAMP_CMDLEN; ++k) pr2serr("%02x ", rtCmdBlk[k]); pr2serr("\n"); } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("%s: out of memory\n", __func__); return -1; } set_scsi_pt_cdb(ptvp, rtCmdBlk, sizeof(rtCmdBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, "report timestamp", res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; k = get_scsi_pt_resid(ptvp); if (residp) *residp = k; if ((verbose > 2) && ((mx_resp_len - k) > 0)) { pr2serr("Parameter data returned:\n"); dStrHexErr((const char *)resp, mx_resp_len - k, ((verbose > 3) ? -1 : 1)); } destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes the SET TIMESTAMP command. Return of 0 -> success, various * SG_LIB_CAT_* positive values or -1 -> other errors */ static int sg_ll_set_timestamp(int sg_fd, void * paramp, int param_len, int noisy, int verbose) { int k, ret, res, sense_cat; unsigned char stCmdBlk[SET_TIMESTAMP_CMDLEN] = {SG_MAINTENANCE_OUT, SET_TIMESTAMP_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; sg_put_unaligned_be32(param_len, stCmdBlk + 6); if (verbose) { pr2serr(" Set timestamp cdb: "); for (k = 0; k < SET_TIMESTAMP_CMDLEN; ++k) pr2serr("%02x ", stCmdBlk[k]); pr2serr("\n"); if ((verbose > 1) && paramp && param_len) { pr2serr(" set timestamp parameter list:\n"); dStrHexErr((const char *)paramp, param_len, -1); } } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("%s: out of memory\n", __func__); return -1; } set_scsi_pt_cdb(ptvp, stCmdBlk, sizeof(stCmdBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, "set timestamp", res, 0, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_readcap_16(int sg_fd, int pmi, uint64_t llba, void * resp, int mx_resp_len, int noisy, int verbose) { static const char * const cdb_name_s = "read capacity(16)"; int k, ret, res, sense_cat; unsigned char rc_cdb[SERVICE_ACTION_IN_16_CMDLEN] = {SERVICE_ACTION_IN_16_CMD, READ_CAPACITY_16_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (pmi) { /* lbs only valid when pmi set */ rc_cdb[14] |= 1; sg_put_unaligned_be64(llba, rc_cdb + 2); } /* Allocation length, no guidance in SBC-2 rev 15b */ sg_put_unaligned_be32((uint32_t)mx_resp_len, rc_cdb + 10); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k) pr2ws("%02x ", rc_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, rc_cdb, sizeof(rc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI READ CAPACITY (10) command. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_readcap_10(int sg_fd, bool pmi, unsigned int lba, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const cdb_s = "read capacity(10)"; int k, ret, res, sense_cat; uint8_t rc_cdb[READ_CAPACITY_10_CMDLEN] = {READ_CAPACITY_10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (pmi) { /* lbs only valid when pmi set */ rc_cdb[8] |= 1; sg_put_unaligned_be32((uint32_t)lba, rc_cdb + 2); } if (verbose) { pr2ws(" %s cdb: ", cdb_s); for (k = 0; k < READ_CAPACITY_10_CMDLEN; ++k) pr2ws("%02x ", rc_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; set_scsi_pt_cdb(ptvp, rc_cdb, sizeof(rc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
static int sg_start_io(Rq_elem * rep) { sg_io_hdr_t * hp = &rep->io_hdr; int res; rep->qstate = rep->wr ? QS_OUT_STARTED : QS_IN_STARTED; memset(rep->cmd, 0, sizeof(rep->cmd)); rep->cmd[0] = rep->wr ? SGP_WRITE10 : SGP_READ10; sg_put_unaligned_be32((uint32_t)rep->blk, rep->cmd + 2); sg_put_unaligned_be16((uint16_t)rep->num_blks, rep->cmd + 7); memset(hp, 0, sizeof(sg_io_hdr_t)); hp->interface_id = 'S'; hp->cmd_len = sizeof(rep->cmd); hp->cmdp = rep->cmd; hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV; hp->dxfer_len = rep->bs * rep->num_blks; hp->dxferp = rep->buffp; hp->mx_sb_len = sizeof(rep->sb); hp->sbp = rep->sb; hp->timeout = DEF_TIMEOUT; hp->usr_ptr = rep; hp->pack_id = rep->blk; if (rep->dio) hp->flags |= SG_FLAG_DIRECT_IO; if (rep->debug > 8) { fprintf(stderr, "sg_start_io: SCSI %s, blk=%d num_blks=%d\n", rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks); sg_print_command(hp->cmdp); fprintf(stderr, " len=%d, dxfrp=%p, cmd_len=%d\n", hp->dxfer_len, hp->dxferp, hp->cmd_len); } while (((res = write(rep->wr ? rep->outfd : rep->infd, hp, sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno)) ; if (res < 0) { if (ENOMEM == errno) return 1; return res; } return 0; }
/* Invokes a SCSI WRITE AND VERIFY (16) command (SBC). Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ static int sg_ll_write_verify16(int sg_fd, int wrprotect, bool dpo, int bytchk, uint64_t llba, int num_lb, int group, uint8_t *dop, int do_len, int timeout, bool noisy, int verbose) { int ret; uint8_t wv_cdb[WRITE_VERIFY16_CMDLEN]; memset(wv_cdb, 0, sizeof(wv_cdb)); wv_cdb[0] = WRITE_VERIFY16_CMD; wv_cdb[1] = ((wrprotect & WRPROTECT_MASK) << WRPROTECT_SHIFT); if (dpo) wv_cdb[1] |= 0x10; if (bytchk) wv_cdb[1] |= ((bytchk & 0x3) << 1); sg_put_unaligned_be64(llba, wv_cdb + 2); sg_put_unaligned_be32((uint32_t)num_lb, wv_cdb + 10); wv_cdb[14] = group & 0x1f; ret = run_scsi_transaction(sg_fd, wv_cdb, sizeof(wv_cdb), dop, do_len, timeout, noisy, verbose); return ret; }
static int sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks, int64_t start_block, int write_true, int fua, int dpo) { int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88}; int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a}; int sz_ind; memset(cdbp, 0, cdb_sz); if (dpo) cdbp[1] |= 0x10; if (fua) cdbp[1] |= 0x8; switch (cdb_sz) { case 6: sz_ind = 0; cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : rd_opcode[sz_ind]); sg_put_unaligned_be24(0x1fffff & start_block, cdbp + 1); cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks; if (blocks > 256) { pr2serr(ME "for 6 byte commands, maximum number of blocks is " "256\n"); return 1; } if ((start_block + blocks - 1) & (~0x1fffff)) { pr2serr(ME "for 6 byte commands, can't address blocks beyond " "%d\n", 0x1fffff); return 1; } if (dpo || fua) { pr2serr(ME "for 6 byte commands, neither dpo nor fua bits " "supported\n"); return 1; } break; case 10: sz_ind = 1; cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : rd_opcode[sz_ind]); sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2); sg_put_unaligned_be16((uint16_t)blocks, cdbp + 7); if (blocks & (~0xffff)) { pr2serr(ME "for 10 byte commands, maximum number of blocks is " "%d\n", 0xffff); return 1; } break; case 12: sz_ind = 2; cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : rd_opcode[sz_ind]); sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2); sg_put_unaligned_be32((uint32_t)blocks, cdbp + 6); break; case 16: sz_ind = 3; cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : rd_opcode[sz_ind]); sg_put_unaligned_be64((uint64_t)start_block, cdbp + 2); sg_put_unaligned_be32((uint32_t)blocks, cdbp + 10); break; default: pr2serr(ME "expected cdb size of 6, 10, 12, or 16 but got %d\n", cdb_sz); return 1; } return 0; }
static int do_write_same(int sg_fd, const struct opts_t * op, const void * dataoutp, int * act_cdb_lenp) { int k, ret, res, sense_cat, cdb_len; uint64_t llba; uint8_t ws_cdb[WRITE_SAME32_LEN]; uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; cdb_len = op->pref_cdb_size; if (WRITE_SAME10_LEN == cdb_len) { llba = op->lba + op->numblocks; if ((op->numblocks > 0xffff) || (llba > UINT32_MAX) || op->ndob || (op->unmap && (! op->want_ws10))) { cdb_len = WRITE_SAME16_LEN; if (op->verbose) { const char * cp = "use WRITE SAME(16) instead of 10 byte " "cdb"; if (op->numblocks > 0xffff) pr2serr("%s since blocks exceed 65535\n", cp); else if (llba > UINT32_MAX) pr2serr("%s since LBA may exceed 32 bits\n", cp); else pr2serr("%s due to ndob or unmap settings\n", cp); } } } if (act_cdb_lenp) *act_cdb_lenp = cdb_len; memset(ws_cdb, 0, sizeof(ws_cdb)); switch (cdb_len) { case WRITE_SAME10_LEN: ws_cdb[0] = WRITE_SAME10_OP; ws_cdb[1] = ((op->wrprotect & 0x7) << 5); /* ANCHOR + UNMAP not allowed for WRITE_SAME10 in sbc3r24+r25 but * a proposal has been made to allow it. Anticipate approval. */ if (op->anchor) ws_cdb[1] |= 0x10; if (op->unmap) ws_cdb[1] |= 0x8; if (op->pbdata) ws_cdb[1] |= 0x4; if (op->lbdata) ws_cdb[1] |= 0x2; sg_put_unaligned_be32((uint32_t)op->lba, ws_cdb + 2); ws_cdb[6] = (op->grpnum & 0x1f); sg_put_unaligned_be16((uint16_t)op->numblocks, ws_cdb + 7); break; case WRITE_SAME16_LEN: ws_cdb[0] = WRITE_SAME16_OP; ws_cdb[1] = ((op->wrprotect & 0x7) << 5); if (op->anchor) ws_cdb[1] |= 0x10; if (op->unmap) ws_cdb[1] |= 0x8; if (op->pbdata) ws_cdb[1] |= 0x4; if (op->lbdata) ws_cdb[1] |= 0x2; if (op->ndob) ws_cdb[1] |= 0x1; sg_put_unaligned_be64(op->lba, ws_cdb + 2); sg_put_unaligned_be32((uint32_t)op->numblocks, ws_cdb + 10); ws_cdb[14] = (op->grpnum & 0x1f); break; case WRITE_SAME32_LEN: ws_cdb[0] = VARIABLE_LEN_OP; ws_cdb[6] = (op->grpnum & 0x1f); ws_cdb[7] = WRITE_SAME32_ADD; sg_put_unaligned_be16((uint16_t)WRITE_SAME32_SA, ws_cdb + 8); ws_cdb[10] = ((op->wrprotect & 0x7) << 5); if (op->anchor) ws_cdb[10] |= 0x10; if (op->unmap) ws_cdb[10] |= 0x8; if (op->pbdata) ws_cdb[10] |= 0x4; if (op->lbdata) ws_cdb[10] |= 0x2; if (op->ndob) ws_cdb[10] |= 0x1; sg_put_unaligned_be64(op->lba, ws_cdb + 12); sg_put_unaligned_be32((uint32_t)op->numblocks, ws_cdb + 28); break; default: pr2serr("do_write_same: bad cdb length %d\n", cdb_len); return -1; } if (op->verbose > 1) { pr2serr(" Write same(%d) cdb: ", cdb_len); for (k = 0; k < cdb_len; ++k) pr2serr("%02x ", ws_cdb[k]); pr2serr("\n Data-out buffer length=%d\n", op->xfer_len); } if ((op->verbose > 3) && (op->xfer_len > 0)) { pr2serr(" Data-out buffer contents:\n"); hex2stderr((const uint8_t *)dataoutp, op->xfer_len, 1); } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("Write same(%d): out of memory\n", cdb_len); return -1; } set_scsi_pt_cdb(ptvp, ws_cdb, cdb_len); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (uint8_t *)dataoutp, op->xfer_len); res = do_scsi_pt(ptvp, sg_fd, op->timeout, op->verbose); ret = sg_cmds_process_resp(ptvp, "Write same", res, SG_NO_DATA_IN, sense_b, true /*noisy */, op->verbose, &sense_cat); if (-1 == ret) get_scsi_pt_os_err(ptvp); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_MEDIUM_HARD: { bool valid; int slen; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); valid = sg_get_sense_info_fld(sense_b, slen, &ull); if (valid) pr2serr("Medium or hardware error starting at lba=%" PRIu64 " [0x%" PRIx64 "]\n", ull, ull); } ret = sense_cat; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
static int send_then_receive(int sg_fd, uint32_t gen_code, int off_off, const uint8_t * dmp, int dmp_len, struct dout_buff_t * wp, uint8_t * dip, int din_len, bool last, const struct opts_t * op) { bool send_data = false; int do_len, rem, res, rsp_len, k, n, num, mc_status, resid, act_len, verb; int ret = 0; uint32_t rec_gen_code; const uint8_t * bp; const char * cp; verb = (op->verbose > 1) ? op->verbose - 1 : 0; switch (op->mc_mode) { case MODE_DNLD_MC_OFFS: case MODE_DNLD_MC_OFFS_SAVE: case MODE_DNLD_MC_OFFS_DEFER: send_data = true; do_len = 24 + dmp_len; rem = do_len % 4; if (rem) do_len += (4 - rem); break; case MODE_ACTIVATE_MC: case MODE_ABORT_MC: do_len = 24; break; default: pr2serr("%s: unexpected mc_mode=0x%x\n", __func__, op->mc_mode); return SG_LIB_SYNTAX_ERROR; } if (do_len > wp->dout_len) { if (wp->doutp) free(wp->doutp); wp->doutp = sg_memalign(do_len, 0, &wp->free_doutp, op->verbose > 3); if (! wp->doutp) { pr2serr("%s: unable to alloc %d bytes\n", __func__, do_len); return SG_LIB_CAT_OTHER; } wp->dout_len = do_len; } else memset(wp->doutp, 0, do_len); wp->doutp[0] = DPC_DOWNLOAD_MICROCODE; wp->doutp[1] = op->mc_subenc; sg_put_unaligned_be16(do_len - 4, wp->doutp + 2); sg_put_unaligned_be32(gen_code, wp->doutp + 4); wp->doutp[8] = op->mc_mode; wp->doutp[11] = op->mc_id; if (send_data) sg_put_unaligned_be32(op->mc_offset + off_off, wp->doutp + 12); sg_put_unaligned_be32(op->mc_tlen, wp->doutp + 16); sg_put_unaligned_be32(dmp_len, wp->doutp + 20); if (send_data && (dmp_len > 0)) memcpy(wp->doutp + 24, dmp, dmp_len); if ((op->verbose > 2) || (op->dry_run && op->verbose)) { pr2serr("send diag: sub-enc id=%u exp_gen=%u download_mc_code=%u " "buff_id=%u\n", op->mc_subenc, gen_code, op->mc_mode, op->mc_id); pr2serr(" buff_off=%u image_len=%u this_mc_data_len=%u " "dout_len=%u\n", op->mc_offset + off_off, op->mc_tlen, dmp_len, do_len); } /* select long duration timeout (7200 seconds) */ if (op->dry_run) { if (op->mc_subenc < 4) { int s = op->mc_offset + off_off + dmp_len; n = 8 + (op->mc_subenc * 16); dummy_rd_resp[n + 11] = op->mc_id; sg_put_unaligned_be32(((send_data && (! last)) ? s : 0), dummy_rd_resp + n + 12); if (MODE_ABORT_MC == op->mc_mode) dummy_rd_resp[n + 2] = 0x80; else if (MODE_ACTIVATE_MC == op->mc_mode) dummy_rd_resp[n + 2] = 0x0; /* done */ else dummy_rd_resp[n + 2] = (s >= op->mc_tlen) ? 0x13 : 0x1; } res = 0; } else res = sg_ll_send_diag(sg_fd, 0 /* st_code */, true /* pf */, false /* st */, false /* devofl */, false /* unitofl */, 1 /* long_duration */, wp->doutp, do_len, true /* noisy */, verb); if (op->mc_non) { /* If non-standard, only call RDR after failed SD */ if (0 == res) return 0; /* If RDR error after SD error, prefer reporting SD error */ ret = res; } else { switch (op->mc_mode) { case MODE_DNLD_MC_OFFS: case MODE_DNLD_MC_OFFS_SAVE: if (res) return res; else if (last) { if (op->ealsd) return 0; /* RDR after last may hit a device reset */ } break; case MODE_DNLD_MC_OFFS_DEFER: if (res) return res; break; case MODE_ACTIVATE_MC: case MODE_ABORT_MC: if (0 == res) { if (op->ealsd) return 0; /* RDR after this may hit a device reset */ } /* SD has failed, so do a RDR but return SD's error */ ret = res; break; default: pr2serr("%s: mc_mode=0x%x\n", __func__, op->mc_mode); return SG_LIB_SYNTAX_ERROR; } } if (op->dry_run) { n = sizeof(dummy_rd_resp); n = (n < din_len) ? n : din_len; memcpy(dip, dummy_rd_resp, n); resid = din_len - n; res = 0; } else res = sg_ll_receive_diag_v2(sg_fd, true /* pcv */, DPC_DOWNLOAD_MICROCODE, dip, din_len, 0 /* default timeout */, &resid, true, verb); if (res) return ret ? ret : res; rsp_len = sg_get_unaligned_be16(dip + 2) + 4; act_len = din_len - resid; if (rsp_len > din_len) { pr2serr("<<< warning response buffer too small [%d but need " "%d]>>>\n", din_len, rsp_len); rsp_len = din_len; } if (rsp_len > act_len) { pr2serr("<<< warning response too short [actually got %d but need " "%d]>>>\n", act_len, rsp_len); rsp_len = act_len; } if (rsp_len < 8) { pr2serr("Download microcode status dpage too short [%d]\n", rsp_len); return ret ? ret : SG_LIB_CAT_OTHER; } rec_gen_code = sg_get_unaligned_be32(dip + 4); if ((op->verbose > 2) || (op->dry_run && op->verbose)) { n = 8 + (op->mc_subenc * 16); pr2serr("rec diag: rsp_len=%d, num_sub-enc=%u rec_gen_code=%u " "exp_buff_off=%u\n", rsp_len, dip[1], sg_get_unaligned_be32(dip + 4), sg_get_unaligned_be32(dip + n + 12)); } if (rec_gen_code != gen_code) pr2serr("gen_code changed from %" PRIu32 " to %" PRIu32 ", continuing but may fail\n", gen_code, rec_gen_code); num = (rsp_len - 8) / 16; if ((rsp_len - 8) % 16) pr2serr("Found %d Download microcode status descriptors, but there " "is residual\n", num); bp = dip + 8; for (k = 0; k < num; ++k, bp += 16) { if ((unsigned int)op->mc_subenc == (unsigned int)bp[1]) { mc_status = bp[2]; cp = get_mc_status_str(mc_status); if ((mc_status >= 0x80) || op->verbose) pr2serr("mc offset=%u: status: %s [0x%x, additional=0x%x]\n", sg_get_unaligned_be32(bp + 12), cp, mc_status, bp[3]); if (op->verbose > 1) pr2serr(" subenc_id=%d, expected_buffer_id=%d, " "expected_offset=0x%" PRIx32 "\n", bp[1], bp[11], sg_get_unaligned_be32(bp + 12)); if (mc_status >= 0x80) ret = ret ? ret : SG_LIB_CAT_OTHER; } } return ret; }
static int send_then_receive(int sg_fd, uint32_t gen_code, int off_off, const unsigned char * dmp, int dmp_len, struct dout_buff_t * wp, unsigned char * dip, int last, const struct opts_t * op) { int do_len, rem, res, rsp_len, k, num, mc_status, verb; int send_data = 0; int ret = 0; uint32_t rec_gen_code; const unsigned char * ucp; const char * cp; verb = (op->verbose > 1) ? op->verbose - 1 : 0; switch (op->mc_mode) { case MODE_DNLD_MC_OFFS: case MODE_DNLD_MC_OFFS_SAVE: case MODE_DNLD_MC_OFFS_DEFER: send_data = 1; do_len = 24 + dmp_len; rem = do_len % 4; if (rem) do_len += (4 - rem); break; case MODE_ACTIVATE_MC: do_len = 24; break; default: pr2serr("send_then_receive: unexpected mc_mode=0x%x\n", op->mc_mode); return SG_LIB_SYNTAX_ERROR; } if (do_len > wp->dout_len) { if (wp->doutp) free(wp->doutp); wp->doutp = (unsigned char *)malloc(do_len); if (! wp->doutp) { pr2serr("send_then_receive: unable to malloc %d bytes\n", do_len); return SG_LIB_CAT_OTHER; } wp->dout_len = do_len; } memset(wp->doutp, 0, do_len); wp->doutp[0] = DPC_DOWNLOAD_MICROCODE; wp->doutp[1] = op->mc_subenc; sg_put_unaligned_be16(do_len - 4, wp->doutp + 2); sg_put_unaligned_be32(gen_code, wp->doutp + 4); wp->doutp[8] = op->mc_mode; wp->doutp[11] = op->mc_id; if (send_data) sg_put_unaligned_be32(op->mc_offset + off_off, wp->doutp + 12); sg_put_unaligned_be32(op->mc_tlen, wp->doutp + 16); sg_put_unaligned_be32(dmp_len, wp->doutp + 20); if (send_data && (dmp_len > 0)) memcpy(wp->doutp + 24, dmp, dmp_len); /* select long duration timeout (7200 seconds) */ res = sg_ll_send_diag(sg_fd, 0 /* sf_code */, 1 /* pf */, 0 /* sf */, 0 /* devofl */, 0 /* unitofl */, 1 /* long_duration */, wp->doutp, do_len, 1 /* noisy */, verb); if (op->mc_non) { /* If non-standard, only call RDR after failed SD */ if (0 == res) return 0; /* If RDR error after SD error, prefer reporting SD error */ ret = res; } else { switch (op->mc_mode) { case MODE_DNLD_MC_OFFS: case MODE_DNLD_MC_OFFS_SAVE: if (res) return res; else if (last) return 0; /* RDR after last may hit a device reset */ break; case MODE_DNLD_MC_OFFS_DEFER: if (res) return res; break; case MODE_ACTIVATE_MC: if (0 == res) return 0; /* RDR after ACTIVATE_MC may hit a device reset */ /* SD has failed, so do a RDR but return SD's error */ ret = res; break; default: pr2serr("send_then_receive: mc_mode=0x%x\n", op->mc_mode); return SG_LIB_SYNTAX_ERROR; } } res = sg_ll_receive_diag(sg_fd, 1 /* pcv */, DPC_DOWNLOAD_MICROCODE, dip, DEF_DI_LEN, 1, verb); if (res) return ret ? ret : res; rsp_len = sg_get_unaligned_be16(dip + 2) + 4; if (rsp_len > DEF_DI_LEN) { pr2serr("<<< warning response buffer too small [%d but need " "%d]>>>\n", DEF_DI_LEN, rsp_len); rsp_len = DEF_DI_LEN; } if (rsp_len < 8) { pr2serr("Download microcode status dpage too short\n"); return ret ? ret : SG_LIB_CAT_OTHER; } rec_gen_code = sg_get_unaligned_be32(dip + 4); if (rec_gen_code != gen_code) pr2serr("gen_code changed from %" PRIu32 " to %" PRIu32 ", continuing but may fail\n", gen_code, rec_gen_code); num = (rsp_len - 8) / 16; if ((rsp_len - 8) % 16) pr2serr("Found %d Download microcode status descriptors, but there " "is residual\n", num); ucp = dip + 8; for (k = 0; k < num; ++k, ucp += 16) { if ((unsigned int)op->mc_subenc == (unsigned int)ucp[1]) { mc_status = ucp[2]; cp = get_mc_status_str(mc_status); if ((mc_status >= 0x80) || op->verbose) pr2serr("mc offset=%d: status: %s [0x%x, additional=0x%x]\n", off_off, cp, mc_status, ucp[3]); if (op->verbose > 1) pr2serr(" subenc_id=%d, expected_buffer_id=%d, " "expected_offset=0x%" PRIx32 "\n", ucp[1], ucp[11], sg_get_unaligned_be32(ucp + 12)); if (mc_status >= 0x80) ret = ret ? ret : SG_LIB_CAT_OTHER; } } return ret; }
/* Invokes a SCSI READ ATTRIBUTE command (SPC+SMC). Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ static int sg_ll_read_attr(int sg_fd, void * resp, int * residp, const struct opts_t * op) { int k, ret, res, sense_cat; int noisy = 1; unsigned char ra_cdb[SG_READ_ATTRIBUTE_CMDLEN] = {SG_READ_ATTRIBUTE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; ra_cdb[1] = 0x1f & op->sa; if (op->ea) sg_put_unaligned_be16(op->ea, ra_cdb + 2); if (op->lvn) ra_cdb[5] = 0xff & op->lvn; if (op->pn) ra_cdb[7] = 0xff & op->pn; if (op->fai) sg_put_unaligned_be16(op->fai, ra_cdb + 8); sg_put_unaligned_be32((uint32_t)op->maxlen, ra_cdb + 10); if (op->cache) ra_cdb[14] |= 0x1; if (op->verbose) { pr2serr(" Read attribute cdb: "); for (k = 0; k < SG_READ_ATTRIBUTE_CMDLEN; ++k) pr2serr("%02x ", ra_cdb[k]); pr2serr("\n"); } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("%s: out of memory\n", __func__); return -1; } set_scsi_pt_cdb(ptvp, ra_cdb, sizeof(ra_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, op->maxlen); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, op->verbose); ret = sg_cmds_process_resp(ptvp, "read attribute", res, op->maxlen, sense_b, noisy, op->verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; if (residp) *residp = get_scsi_pt_resid(ptvp); destruct_scsi_pt_obj(ptvp); return ret; }