| /* |
| * Copyright (c) 2014-2015 Douglas Gilbert. |
| * All rights reserved. |
| * Use of this source code is governed by a BSD-style |
| * license that can be found in the BSD_LICENSE file. |
| */ |
| |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <ctype.h> |
| #include <string.h> |
| #include <getopt.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #define __STDC_FORMAT_MACROS 1 |
| #include <inttypes.h> |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| #include "sg_lib.h" |
| #include "sg_cmds_basic.h" |
| #include "sg_cmds_extra.h" |
| #ifdef SG_LIB_WIN32 |
| #ifdef SG_LIB_WIN32_DIRECT |
| #include "sg_pt.h" /* needed for scsi_pt_win32_direct() */ |
| #endif |
| #endif |
| #include "sg_unaligned.h" |
| #include "sg_pr2serr.h" |
| |
| /* |
| * This utility issues the SCSI SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC |
| * RESULTS commands in order to send microcode to the given SES device. |
| */ |
| |
| static const char * version_str = "1.03 20151219"; /* ses3r07 */ |
| |
| #define ME "sg_ses_microcode: " |
| #define MAX_XFER_LEN (128 * 1024 * 1024) |
| #define DEF_XFER_LEN (8 * 1024 * 1024) |
| #define DEF_DI_LEN (8 * 1024) |
| #define EBUFF_SZ 256 |
| |
| #define DPC_DOWNLOAD_MICROCODE 0xe |
| |
| struct opts_t { |
| int bpw; |
| int bpw_then_activate; |
| int mc_id; |
| int mc_len; |
| int mc_len_given; |
| int mc_mode; |
| int mc_non; |
| int mc_offset; |
| int mc_skip; |
| int mc_subenc; |
| int mc_tlen; |
| int verbose; |
| }; |
| |
| static struct option long_options[] = { |
| {"bpw", required_argument, 0, 'b'}, |
| {"help", no_argument, 0, 'h'}, |
| {"id", required_argument, 0, 'i'}, |
| {"in", required_argument, 0, 'I'}, |
| {"length", required_argument, 0, 'l'}, |
| {"mode", required_argument, 0, 'm'}, |
| {"non", no_argument, 0, 'N'}, |
| {"offset", required_argument, 0, 'o'}, |
| {"skip", required_argument, 0, 's'}, |
| {"subenc", required_argument, 0, 'S'}, |
| {"tlength", required_argument, 0, 't'}, |
| {"verbose", no_argument, 0, 'v'}, |
| {"version", no_argument, 0, 'V'}, |
| {0, 0, 0, 0}, |
| }; |
| |
| |
| static void |
| usage() |
| { |
| pr2serr("Usage: " |
| "sg_ses_microcode [--bpw=CS] [--help] [--id=ID] [--in=FILE]\n" |
| " [--length=LEN] [--mode=MO] " |
| "[--non]\n" |
| " [--offset=OFF] [--skip=SKIP] " |
| "[--subenc=SEID]\n" |
| " [--tlength=TLEN] [--verbose] " |
| "[--version]\n" |
| " DEVICE\n" |
| " where:\n" |
| " --bpw=CS|-b CS CS is chunk size: bytes per send " |
| "diagnostic\n" |
| " command (def: 0 -> as many as " |
| "possible)\n" |
| " --help|-h print out usage message then exit\n" |
| " --id=ID|-i ID buffer identifier (0 (default) to " |
| "255)\n" |
| " --in=FILE|-I FILE read from FILE ('-I -' read " |
| "from stdin)\n" |
| " --length=LEN|-l LEN length in bytes to send; may be " |
| "deduced from\n" |
| " FILE\n" |
| " --mode=MO|-m MO download microcode mode, MO is " |
| "number or\n" |
| " acronym (def: 0 -> 'dmc_status')\n" |
| " --non|-N non-standard: bypass all receive " |
| "diagnostic\n" |
| " results commands except after check " |
| "condition\n" |
| " --offset=OFF|-o OFF buffer offset (unit: bytes, def: " |
| "0);\n" |
| " ignored if --bpw=CS given\n" |
| " --skip=SKIP|-s SKIP bytes in file FILE to skip before " |
| "reading\n" |
| " --subenc=SEID|-S SEID subenclosure identifier (def: 0 " |
| "(primary))\n" |
| " --tlength=TLEN|-t TLEN total length of firmware in " |
| "bytes\n" |
| " (def: 0). Only needed if " |
| "TLEN>LEN\n" |
| " --verbose|-v increase verbosity\n" |
| " --version|-V print version string and exit\n\n" |
| "Does one or more SCSI SEND DIAGNOSTIC followed by RECEIVE " |
| "DIAGNOSTIC\nRESULTS command sequences in order to download " |
| "microcode. Use '-m xxx'\nto list available modes. With only " |
| "DEVICE given, the Download Microcode\nStatus dpage is output.\n" |
| ); |
| } |
| |
| #define MODE_DNLD_STATUS 0 |
| #define MODE_DNLD_MC_OFFS 6 |
| #define MODE_DNLD_MC_OFFS_SAVE 7 |
| #define MODE_DNLD_MC_OFFS_DEFER 0x0E |
| #define MODE_ACTIVATE_MC 0x0F |
| |
| struct mode_s { |
| const char *mode_string; |
| int mode; |
| const char *comment; |
| }; |
| |
| static struct mode_s mode_arr[] = { |
| {"dmc_status", MODE_DNLD_STATUS, "report status of microcode " |
| "download"}, |
| {"dmc_offs", MODE_DNLD_MC_OFFS, "download microcode with offsets " |
| "and activate"}, |
| {"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with " |
| "offsets, save and\n\t\t\t\tactivate"}, |
| {"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode " |
| "with offsets, save and\n\t\t\t\tdefer activation"}, |
| {"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"}, |
| {NULL, 0, NULL}, |
| }; |
| |
| |
| static void |
| print_modes(void) |
| { |
| const struct mode_s * mp; |
| |
| pr2serr("The modes parameter argument can be numeric (hex or decimal)\n" |
| "or symbolic:\n"); |
| for (mp = mode_arr; mp->mode_string; ++mp) { |
| pr2serr(" %2d (0x%02x) %-18s%s\n", mp->mode, mp->mode, |
| mp->mode_string, mp->comment); |
| } |
| pr2serr("\nAdditionally '--bpw=<val>,act' does a activate deferred " |
| "microcode after a\nsuccessful multipart dmc_offs_defer mode " |
| "download.\n"); |
| } |
| |
| struct diag_page_code { |
| int page_code; |
| const char * desc; |
| }; |
| |
| /* An array of Download microcode status field values and descriptions */ |
| static struct diag_page_code mc_status_arr[] = { |
| {0x0, "No download microcode operation in progress"}, |
| {0x1, "Download in progress, awaiting more"}, |
| {0x2, "Download complete, updating storage"}, |
| {0x3, "Updating storage with deferred microcode"}, |
| {0x10, "Complete, no error, starting now"}, |
| {0x11, "Complete, no error, start after hard reset or power cycle"}, |
| {0x12, "Complete, no error, start after power cycle"}, |
| {0x13, "Complete, no error, start after activate_mc, hard reset or " |
| "power cycle"}, |
| {0x80, "Error, discarded, see additional status"}, |
| {0x81, "Error, discarded, image error"}, |
| {0x82, "Timeout, discarded"}, |
| {0x83, "Internal error, need new microcode before reset"}, |
| {0x84, "Internal error, need new microcode, reset safe"}, |
| {0x85, "Unexpected activate_mc received"}, |
| {0x1000, NULL}, |
| }; |
| |
| static const char * |
| get_mc_status_str(unsigned char status_val) |
| { |
| const struct diag_page_code * mcsp; |
| |
| for (mcsp = mc_status_arr; mcsp->desc; ++mcsp) { |
| if (status_val == mcsp->page_code) |
| return mcsp->desc; |
| } |
| return ""; |
| } |
| |
| /* DPC_DOWNLOAD_MICROCODE [0xe] */ |
| static void |
| ses_download_code_sdg(const unsigned char * resp, int resp_len, |
| uint32_t gen_code) |
| { |
| int k, num_subs, num; |
| const unsigned char * ucp; |
| const char * cp; |
| |
| printf("Download microcode status diagnostic page:\n"); |
| if (resp_len < 8) |
| goto truncated; |
| num_subs = resp[1]; /* primary is additional one) */ |
| num = (resp_len - 8) / 16; |
| if ((resp_len - 8) % 16) |
| pr2serr("Found %d Download microcode status descriptors, but there " |
| "is residual\n", num); |
| printf(" number of secondary subenclosures: %d\n", num_subs); |
| printf(" generation code: 0x%" PRIx32 "\n", gen_code); |
| ucp = resp + 8; |
| for (k = 0; k < num; ++k, ucp += 16) { |
| cp = (0 == ucp[1]) ? " [primary]" : ""; |
| printf(" subenclosure identifier: %d%s\n", ucp[1], cp); |
| cp = get_mc_status_str(ucp[2]); |
| if (strlen(cp) > 0) { |
| printf(" download microcode status: %s [0x%x]\n", cp, ucp[2]); |
| printf(" download microcode additional status: 0x%x\n", |
| ucp[3]); |
| } else |
| printf(" download microcode status: 0x%x [additional " |
| "status: 0x%x]\n", ucp[2], ucp[3]); |
| printf(" download microcode maximum size: %" PRIu32 " bytes\n", |
| sg_get_unaligned_be32(ucp + 4)); |
| printf(" download microcode expected buffer id: 0x%x\n", ucp[11]); |
| printf(" download microcode expected buffer id offset: %" PRIu32 |
| "\n", sg_get_unaligned_be32(ucp + 12)); |
| } |
| return; |
| truncated: |
| pr2serr(" <<<download status: response too short>>>\n"); |
| return; |
| } |
| |
| struct dout_buff_t { |
| unsigned char * doutp; |
| int dout_len; |
| }; |
| |
| 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; |
| } |
| |
| |
| int |
| main(int argc, char * argv[]) |
| { |
| int sg_fd, res, c, len, k, n, got_stdin, is_reg, rsp_len, verb, last; |
| int infd = -1; |
| int do_help = 0; |
| const char * device_name = NULL; |
| const char * file_name = NULL; |
| unsigned char * dmp = NULL; |
| unsigned char * dip = NULL; |
| char * cp; |
| char ebuff[EBUFF_SZ]; |
| struct stat a_stat; |
| struct dout_buff_t dout; |
| struct opts_t opts; |
| struct opts_t * op; |
| const struct mode_s * mp; |
| uint32_t gen_code = 0; |
| int ret = 0; |
| |
| op = &opts; |
| memset(op, 0, sizeof(opts)); |
| memset(&dout, 0, sizeof(dout)); |
| while (1) { |
| int option_index = 0; |
| |
| c = getopt_long(argc, argv, "b:hi:I:l:m:No:s:S:t:vV", long_options, |
| &option_index); |
| if (c == -1) |
| break; |
| |
| switch (c) { |
| case 'b': |
| op->bpw = sg_get_num(optarg); |
| if (op->bpw < 0) { |
| pr2serr("argument to '--bpw' should be in a positive " |
| "number\n"); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| if ((cp = strchr(optarg, ','))) { |
| if (0 == strncmp("act", cp + 1, 3)) |
| ++op->bpw_then_activate; |
| } |
| break; |
| case 'h': |
| case '?': |
| ++do_help; |
| break; |
| case 'i': |
| op->mc_id = sg_get_num(optarg); |
| if ((op->mc_id < 0) || (op->mc_id > 255)) { |
| pr2serr("argument to '--id' should be in the range 0 to " |
| "255\n"); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| break; |
| case 'I': |
| file_name = optarg; |
| break; |
| case 'l': |
| op->mc_len = sg_get_num(optarg); |
| if (op->mc_len < 0) { |
| pr2serr("bad argument to '--length'\n"); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| op->mc_len_given = 1; |
| break; |
| case 'm': |
| if (isdigit(*optarg)) { |
| op->mc_mode = sg_get_num(optarg); |
| if ((op->mc_mode < 0) || (op->mc_mode > 255)) { |
| pr2serr("argument to '--mode' should be in the range 0 " |
| "to 255\n"); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| } else { |
| len = strlen(optarg); |
| for (mp = mode_arr; mp->mode_string; ++mp) { |
| if (0 == strncmp(mp->mode_string, optarg, len)) { |
| op->mc_mode = mp->mode; |
| break; |
| } |
| } |
| if (! mp->mode_string) { |
| print_modes(); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| } |
| break; |
| case 'N': |
| ++op->mc_non; |
| break; |
| case 'o': |
| op->mc_offset = sg_get_num(optarg); |
| if (op->mc_offset < 0) { |
| pr2serr("bad argument to '--offset'\n"); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| if (0 != (op->mc_offset % 4)) { |
| pr2serr("'--offset' value needs to be a multiple of 4\n"); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| break; |
| case 's': |
| op->mc_skip = sg_get_num(optarg); |
| if (op->mc_skip < 0) { |
| pr2serr("bad argument to '--skip'\n"); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| break; |
| case 'S': |
| op->mc_subenc = sg_get_num(optarg); |
| if ((op->mc_subenc < 0) || (op->mc_subenc > 255)) { |
| pr2serr("expected argument to '--subenc' to be 0 to 255\n"); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| break; |
| case 't': |
| op->mc_tlen = sg_get_num(optarg); |
| if (op->mc_tlen < 0) { |
| pr2serr("bad argument to '--tlength'\n"); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| break; |
| case 'v': |
| ++op->verbose; |
| break; |
| case 'V': |
| pr2serr(ME "version: %s\n", version_str); |
| return 0; |
| default: |
| pr2serr("unrecognised option code 0x%x ??\n", c); |
| usage(); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| } |
| if (do_help) { |
| if (do_help > 1) { |
| usage(); |
| pr2serr("\n"); |
| print_modes(); |
| } else |
| usage(); |
| return 0; |
| } |
| if (optind < argc) { |
| if (NULL == device_name) { |
| device_name = argv[optind]; |
| ++optind; |
| } |
| if (optind < argc) { |
| for (; optind < argc; ++optind) |
| pr2serr("Unexpected extra argument: %s\n", argv[optind]); |
| usage(); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| } |
| |
| if (NULL == device_name) { |
| pr2serr("missing device name!\n"); |
| usage(); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| |
| if ((op->mc_len > 0) && (op->bpw > op->mc_len)) { |
| pr2serr("trim chunk size (CS) to be the same as LEN\n"); |
| op->bpw = op->mc_len; |
| } |
| |
| #ifdef SG_LIB_WIN32 |
| #ifdef SG_LIB_WIN32_DIRECT |
| if (op->verbose > 4) |
| pr2serr("Initial win32 SPT interface state: %s\n", |
| scsi_pt_win32_spt_state() ? "direct" : "indirect"); |
| scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */); |
| #endif |
| #endif |
| |
| sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, op->verbose); |
| if (sg_fd < 0) { |
| pr2serr(ME "open error: %s: %s\n", device_name, |
| safe_strerror(-sg_fd)); |
| return SG_LIB_FILE_ERROR; |
| } |
| |
| if (file_name && ((MODE_DNLD_STATUS == op->mc_mode) || |
| (MODE_ACTIVATE_MC == op->mc_mode))) |
| pr2serr("ignoring --in=FILE option\n"); |
| else if (file_name) { |
| got_stdin = (0 == strcmp(file_name, "-")) ? 1 : 0; |
| if (got_stdin) |
| infd = STDIN_FILENO; |
| else { |
| if ((infd = open(file_name, O_RDONLY)) < 0) { |
| snprintf(ebuff, EBUFF_SZ, |
| ME "could not open %s for reading", file_name); |
| perror(ebuff); |
| ret = SG_LIB_FILE_ERROR; |
| goto fini; |
| } else if (sg_set_binary_mode(infd) < 0) |
| perror("sg_set_binary_mode"); |
| } |
| if ((0 == fstat(infd, &a_stat)) && S_ISREG(a_stat.st_mode)) { |
| is_reg = 1; |
| if (0 == op->mc_len) { |
| if (op->mc_skip >= a_stat.st_size) { |
| pr2serr("skip exceeds file size of %d bytes\n", |
| (int)a_stat.st_size); |
| ret = SG_LIB_FILE_ERROR; |
| goto fini; |
| } |
| op->mc_len = (int)(a_stat.st_size) - op->mc_skip; |
| } |
| } else { |
| is_reg = 0; |
| if (0 == op->mc_len) |
| op->mc_len = DEF_XFER_LEN; |
| } |
| if (op->mc_len > MAX_XFER_LEN) { |
| pr2serr("file size or requested length (%d) exceeds " |
| "MAX_XFER_LEN of %d bytes\n", op->mc_len, |
| MAX_XFER_LEN); |
| ret = SG_LIB_FILE_ERROR; |
| goto fini; |
| } |
| if (NULL == (dmp = (unsigned char *)malloc(op->mc_len))) { |
| pr2serr(ME "out of memory (to hold microcode)\n"); |
| ret = SG_LIB_CAT_OTHER; |
| goto fini; |
| } |
| /* Don't remember why this is preset to 0xff, from write_buffer */ |
| memset(dmp, 0xff, op->mc_len); |
| if (op->mc_skip > 0) { |
| if (! is_reg) { |
| if (got_stdin) |
| pr2serr("Can't skip on stdin\n"); |
| else |
| pr2serr(ME "not a 'regular' file so can't apply skip\n"); |
| ret = SG_LIB_FILE_ERROR; |
| goto fini; |
| } |
| if (lseek(infd, op->mc_skip, SEEK_SET) < 0) { |
| snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to " |
| "required position on %s", file_name); |
| perror(ebuff); |
| ret = SG_LIB_FILE_ERROR; |
| goto fini; |
| } |
| } |
| res = read(infd, dmp, op->mc_len); |
| if (res < 0) { |
| snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s", |
| file_name); |
| perror(ebuff); |
| ret = SG_LIB_FILE_ERROR; |
| goto fini; |
| } |
| if (res < op->mc_len) { |
| if (op->mc_len_given) { |
| pr2serr("tried to read %d bytes from %s, got %d bytes\n", |
| op->mc_len, file_name, res); |
| pr2serr("pad with 0xff bytes and continue\n"); |
| } else { |
| if (op->verbose) { |
| pr2serr("tried to read %d bytes from %s, got %d " |
| "bytes\n", op->mc_len, file_name, res); |
| pr2serr("will send %d bytes", res); |
| if ((op->bpw > 0) && (op->bpw < op->mc_len)) |
| pr2serr(", %d bytes per WRITE BUFFER command\n", |
| op->bpw); |
| else |
| pr2serr("\n"); |
| } |
| op->mc_len = res; |
| } |
| } |
| if (! got_stdin) |
| close(infd); |
| infd = -1; |
| } else if (! ((MODE_DNLD_STATUS == op->mc_mode) || |
| (MODE_ACTIVATE_MC == op->mc_mode))) { |
| pr2serr("need --in=FILE option with given mode\n"); |
| ret = SG_LIB_SYNTAX_ERROR; |
| goto fini; |
| } |
| if (op->mc_tlen < op->mc_len) |
| op->mc_tlen = op->mc_len; |
| if (op->mc_non && (MODE_DNLD_STATUS == op->mc_mode)) { |
| pr2serr("Do nothing because '--non' given so fetching the Download " |
| "microcode status\ndpage might be dangerous\n"); |
| goto fini; |
| } |
| |
| if (NULL == (dip = (unsigned char *)malloc(DEF_DI_LEN))) { |
| pr2serr(ME "out of memory (data-in buffer)\n"); |
| ret = SG_LIB_CAT_OTHER; |
| goto fini; |
| } |
| memset(dip, 0, DEF_DI_LEN); |
| verb = (op->verbose > 1) ? op->verbose - 1 : 0; |
| /* Fetch Download microcode status dpage for generation code ++ */ |
| res = sg_ll_receive_diag(sg_fd, 1 /* pcv */, DPC_DOWNLOAD_MICROCODE, dip, |
| DEF_DI_LEN, 1, verb); |
| if (0 == 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"); |
| ret = SG_LIB_CAT_OTHER; |
| goto fini; |
| } |
| } else { |
| ret = res; |
| goto fini; |
| } |
| gen_code = sg_get_unaligned_be32(dip + 4); |
| |
| if (MODE_DNLD_STATUS == op->mc_mode) { |
| ses_download_code_sdg(dip, rsp_len, gen_code); |
| goto fini; |
| } else if (MODE_ACTIVATE_MC == op->mc_mode) { |
| res = send_then_receive(sg_fd, gen_code, 0, NULL, 0, &dout, dip, 1, |
| op); |
| ret = res; |
| goto fini; |
| } |
| |
| res = 0; |
| if (op->bpw > 0) { |
| for (k = 0, last = 0; k < op->mc_len; k += n) { |
| n = op->mc_len - k; |
| if (n > op->bpw) |
| n = op->bpw; |
| else |
| last = 1; |
| if (op->verbose) |
| pr2serr("bpw loop: mode=0x%x, id=%d, off_off=%d, len=%d, " |
| "last=%d\n", op->mc_mode, op->mc_id, k, n, last); |
| res = send_then_receive(sg_fd, gen_code, k, dmp + k, n, &dout, |
| dip, last, op); |
| if (res) |
| break; |
| } |
| if (op->bpw_then_activate && (0 == res)) { |
| op->mc_mode = MODE_ACTIVATE_MC; |
| if (op->verbose) |
| pr2serr("sending Activate deferred microcode [0xf]\n"); |
| res = send_then_receive(sg_fd, gen_code, 0, NULL, 0, &dout, |
| dip, 1, op); |
| } |
| } else { |
| if (op->verbose) |
| pr2serr("single: mode=0x%x, id=%d, offset=%d, len=%d\n", |
| op->mc_mode, op->mc_id, op->mc_offset, op->mc_len); |
| res = send_then_receive(sg_fd, gen_code, 0, dmp, op->mc_len, &dout, |
| dip, 1, op); |
| } |
| if (res) |
| ret = res; |
| |
| fini: |
| if ((infd >= 0) && (! got_stdin)) |
| close(infd); |
| if (dmp) |
| free(dmp); |
| if (dout.doutp) |
| free(dout.doutp); |
| res = sg_cmds_close_device(sg_fd); |
| if (res < 0) { |
| pr2serr("close error: %s\n", safe_strerror(-res)); |
| if (0 == ret) |
| return SG_LIB_FILE_ERROR; |
| } |
| if (ret && (0 == op->verbose)) { |
| if (SG_LIB_CAT_INVALID_OP == ret) |
| pr2serr("%sRECEIVE DIAGNOSTIC RESULTS command not supported\n", |
| ((MODE_DNLD_STATUS == op->mc_mode) ? |
| "" : "SEND DIAGNOSTIC or ")); |
| else if (ret > 0) |
| pr2serr("Failed, exit status %d\n", ret); |
| else if (ret < 0) |
| pr2serr("Some error occurred\n"); |
| } |
| return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; |
| } |