• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2014-2021 Douglas Gilbert.
3  * All rights reserved.
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the BSD_LICENSE file.
6  *
7  * SPDX-License-Identifier: BSD-2-Clause
8  */
9 
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdarg.h>
15 #include <stdbool.h>
16 #include <stdint.h>
17 #include <ctype.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <getopt.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #define __STDC_FORMAT_MACROS 1
24 #include <inttypes.h>
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 
30 #include "sg_lib.h"
31 #include "sg_lib_data.h"
32 #include "sg_cmds_basic.h"
33 #include "sg_cmds_extra.h"
34 #include "sg_unaligned.h"
35 #include "sg_pr2serr.h"
36 
37 #ifdef SG_LIB_WIN32
38 #ifdef SG_LIB_WIN32_DIRECT
39 #include "sg_pt.h"      /* needed for scsi_pt_win32_direct() */
40 #endif
41 #endif
42 
43 /*
44  * This utility issues the SCSI SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC
45  * RESULTS commands in order to send microcode to the given SES device.
46  */
47 
48 static const char * version_str = "1.19 20210610";    /* ses4r02 */
49 
50 #define ME "sg_ses_microcode: "
51 #define MAX_XFER_LEN (128 * 1024 * 1024)
52 #define DEF_XFER_LEN (8 * 1024 * 1024)
53 #define DEF_DIN_LEN (8 * 1024)
54 #define EBUFF_SZ 256
55 
56 #define DPC_DOWNLOAD_MICROCODE 0xe
57 
58 struct opts_t {
59     bool dry_run;
60     bool ealsd;
61     bool mc_non;
62     bool bpw_then_activate;
63     bool mc_len_given;
64     int bpw;            /* bytes per write, chunk size */
65     int mc_id;
66     int mc_len;         /* --length=LEN */
67     int mc_mode;
68     int mc_offset;      /* Buffer offset in SCSI commands */
69     int mc_skip;        /* on FILE */
70     int mc_subenc;
71     int mc_tlen;        /* --tlength=TLEN */
72     int verbose;
73 };
74 
75 static struct option long_options[] = {
76     {"bpw", required_argument, 0, 'b'},
77     {"dry-run", no_argument, 0, 'd'},
78     {"dry_run", no_argument, 0, 'd'},
79     {"ealsd", no_argument, 0, 'e'},
80     {"help", no_argument, 0, 'h'},
81     {"id", required_argument, 0, 'i'},
82     {"in", required_argument, 0, 'I'},
83     {"length", required_argument, 0, 'l'},
84     {"mode", required_argument, 0, 'm'},
85     {"non", no_argument, 0, 'N'},
86     {"offset", required_argument, 0, 'o'},
87     {"skip", required_argument, 0, 's'},
88     {"subenc", required_argument, 0, 'S'},
89     {"tlength", required_argument, 0, 't'},
90     {"verbose", no_argument, 0, 'v'},
91     {"version", no_argument, 0, 'V'},
92     {0, 0, 0, 0},
93 };
94 
95 #define MODE_DNLD_STATUS        0
96 #define MODE_DNLD_MC_OFFS       6
97 #define MODE_DNLD_MC_OFFS_SAVE  7
98 #define MODE_DNLD_MC_OFFS_DEFER 0x0E
99 #define MODE_ACTIVATE_MC        0x0F
100 #define MODE_ABORT_MC           0xFF    /* actually reserved; any reserved
101                                          * value aborts a microcode download
102                                          * in progress */
103 
104 struct mode_s {
105         const char *mode_string;
106         int   mode;
107         const char *comment;
108 };
109 
110 static struct mode_s mode_arr[] = {
111     {"dmc_status", MODE_DNLD_STATUS, "report status of microcode "
112      "download"},
113     {"dmc_offs", MODE_DNLD_MC_OFFS, "download microcode with offsets "
114      "and activate"},
115     {"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with "
116      "offsets, save and\n\t\t\t\tactivate"},
117     {"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode "
118      "with offsets, save and\n\t\t\t\tdefer activation"},
119     {"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"},
120     {"dmc_abort", MODE_ABORT_MC, "abort download microcode in progress"},
121     {NULL, 0, NULL},
122 };
123 
124 /* An array of Download microcode status field values and descriptions.
125  * This table is a subset of one in sg_read_buffer for the read microcode
126  * status page. */
127 static struct sg_lib_simple_value_name_t mc_status_arr[] = {
128     {0x0, "No download microcode operation in progress"},
129     {0x1, "Download in progress, awaiting more"},
130     {0x2, "Download complete, updating storage"},
131     {0x3, "Updating storage with deferred microcode"},
132     {0x10, "Complete, no error, starting now"},
133     {0x11, "Complete, no error, start after hard reset or power cycle"},
134     {0x12, "Complete, no error, start after power cycle"},
135     {0x13, "Complete, no error, start after activate_mc, hard reset or "
136            "power cycle"},
137     {0x80, "Error, discarded, see additional status"},
138     {0x81, "Error, discarded, image error"},
139     {0x82, "Timeout, discarded"},
140     {0x83, "Internal error, need new microcode before reset"},
141     {0x84, "Internal error, need new microcode, reset safe"},
142     {0x85, "Unexpected activate_mc received"},
143     {0x1000, NULL},
144 };
145 
146 struct dout_buff_t {
147     uint8_t * doutp;
148     uint8_t * free_doutp;
149     int dout_len;
150 };
151 
152 /* This dummy response is used when --dry-run skips the RECEIVE DIAGNOSTICS
153  * RESULTS command. Say maximum download MC size is 4 MB. Set generation
154  * code to 0 . */
155 uint8_t dummy_rd_resp[] = {
156     0xe,  3,  0, 68,  0, 0, 0, 0,
157     0,  0,  0,  0,  0x0, 0x40, 0x0, 0x0,  0, 0, 0,  0,  0x0, 0x0, 0x0, 0x0,
158     0,  1,  0,  0,  0x0, 0x40, 0x0, 0x0,  0, 0, 0,  0,  0x0, 0x0, 0x0, 0x0,
159     0,  2,  0,  0,  0x0, 0x40, 0x0, 0x0,  0, 0, 0,  0,  0x0, 0x0, 0x0, 0x0,
160     0,  3,  0,  0,  0x0, 0x40, 0x0, 0x0,  0, 0, 0,  0,  0x0, 0x0, 0x0, 0x0,
161 };
162 
163 
164 static void
usage()165 usage()
166 {
167     pr2serr("Usage: "
168             "sg_ses_microcode [--bpw=CS] [--dry-run] [--ealsd] [--help] "
169             "[--id=ID]\n"
170             "                        [--in=FILE] [--length=LEN] [--mode=MO] "
171             "[--non]\n"
172             "                        [--offset=OFF] [--skip=SKIP] "
173             "[--subenc=SEID]\n"
174             "                        [--tlength=TLEN] [--verbose] "
175             "[--version]\n"
176             "                        DEVICE\n"
177             "  where:\n"
178             "    --bpw=CS|-b CS         CS is chunk size: bytes per send "
179             "diagnostic\n"
180             "                           command (def: 0 -> as many as "
181             "possible)\n"
182             "                           can append ',act' to do activate "
183             "after last\n"
184             "    --dry-run|-d           skip SCSI commands, do everything "
185             "else\n"
186             "    --ealsd|-e             exit after last Send Diagnostic "
187             "command\n"
188             "    --help|-h              print out usage message then exit\n"
189             "    --id=ID|-i ID          buffer identifier (0 (default) to "
190             "255)\n"
191             "    --in=FILE|-I FILE      read from FILE ('-I -' read "
192             "from stdin)\n"
193             "    --length=LEN|-l LEN    length in bytes to send (def: "
194             "deduced from\n"
195             "                           FILE taking SKIP into account)\n"
196             "    --mode=MO|-m MO        download microcode mode, MO is "
197             "number or\n"
198             "                           acronym (def: 0 -> 'dmc_status')\n"
199             "    --non|-N               non-standard: bypass all receive "
200             "diagnostic\n"
201             "                           results commands except after check "
202             "condition\n"
203             "    --offset=OFF|-o OFF    buffer offset (unit: bytes, def: "
204             "0);\n"
205             "                           ignored if --bpw=CS given\n"
206             "    --skip=SKIP|-s SKIP    bytes in file FILE to skip before "
207             "reading\n"
208             "    --subenc=SEID|-S SEID     subenclosure identifier (def: 0 "
209             "(primary))\n"
210             "    --tlength=TLEN|-t TLEN    total length of firmware in "
211             "bytes\n"
212             "                              (def: 0). Only needed if "
213             "TLEN>LEN\n"
214             "    --verbose|-v           increase verbosity\n"
215             "    --version|-V           print version string and exit\n\n"
216             "Does one or more SCSI SEND DIAGNOSTIC followed by RECEIVE "
217             "DIAGNOSTIC\nRESULTS command sequences in order to download "
218             "microcode. Use '-m xxx'\nto list available modes. With only "
219             "DEVICE given, the Download Microcode\nStatus dpage is output.\n"
220           );
221 }
222 
223 static void
print_modes(void)224 print_modes(void)
225 {
226     const struct mode_s * mp;
227 
228     pr2serr("The modes parameter argument can be numeric (hex or decimal)\n"
229             "or symbolic:\n");
230     for (mp = mode_arr; mp->mode_string; ++mp) {
231         pr2serr(" %3d [0x%02x]  %-18s%s\n", mp->mode, mp->mode,
232                 mp->mode_string, mp->comment);
233     }
234     pr2serr("\nAdditionally '--bpw=<val>,act' does a activate deferred "
235             "microcode after a\nsuccessful multipart dmc_offs_defer mode "
236             "download.\n");
237 }
238 
239 static const char *
get_mc_status_str(uint8_t status_val)240 get_mc_status_str(uint8_t status_val)
241 {
242     const struct sg_lib_simple_value_name_t * mcsp;
243 
244     for (mcsp = mc_status_arr; mcsp->name; ++mcsp) {
245         if (status_val == mcsp->value)
246             return mcsp->name;
247     }
248     return "";
249 }
250 
251 /* display DPC_DOWNLOAD_MICROCODE status dpage [0xe] */
252 static void
show_download_mc_sdg(const uint8_t * resp,int resp_len,uint32_t gen_code)253 show_download_mc_sdg(const uint8_t * resp, int resp_len,
254                      uint32_t gen_code)
255 {
256     int k, num_subs, num;
257     const uint8_t * bp;
258     const char * cp;
259 
260     printf("Download microcode status diagnostic page:\n");
261     if (resp_len < 8)
262         goto truncated;
263     num_subs = resp[1];  /* primary is additional one) */
264     num = (resp_len - 8) / 16;
265     if ((resp_len - 8) % 16)
266         pr2serr("Found %d Download microcode status descriptors, but there "
267                 "is residual\n", num);
268     printf("  number of secondary subenclosures: %d\n", num_subs);
269     printf("  generation code: 0x%" PRIx32 "\n", gen_code);
270     bp = resp + 8;
271     for (k = 0; k < num; ++k, bp += 16) {
272         cp = (0 == bp[1]) ? " [primary]" : "";
273         printf("   subenclosure identifier: %d%s\n", bp[1], cp);
274         cp = get_mc_status_str(bp[2]);
275         if (strlen(cp) > 0) {
276             printf("     download microcode status: %s [0x%x]\n", cp, bp[2]);
277             printf("     download microcode additional status: 0x%x\n",
278                    bp[3]);
279         } else
280             printf("     download microcode status: 0x%x [additional "
281                    "status: 0x%x]\n", bp[2], bp[3]);
282         printf("     download microcode maximum size: %" PRIu32 " bytes\n",
283                sg_get_unaligned_be32(bp + 4));
284         printf("     download microcode expected buffer id: 0x%x\n", bp[11]);
285         printf("     download microcode expected buffer id offset: %" PRIu32
286                "\n", sg_get_unaligned_be32(bp + 12));
287     }
288     return;
289 truncated:
290     pr2serr("    <<<download status: response too short>>>\n");
291     return;
292 }
293 
294 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)295 send_then_receive(int sg_fd, uint32_t gen_code, int off_off,
296                   const uint8_t * dmp, int dmp_len,
297                   struct dout_buff_t * wp, uint8_t * dip,
298                   int din_len, bool last, const struct opts_t * op)
299 {
300     bool send_data = false;
301     int do_len, rem, res, rsp_len, k, n, num, mc_status, resid, act_len, verb;
302     int ret = 0;
303     uint32_t rec_gen_code;
304     const uint8_t * bp;
305     const char * cp;
306 
307     verb = (op->verbose > 1) ? op->verbose - 1 : 0;
308     switch (op->mc_mode) {
309     case MODE_DNLD_MC_OFFS:
310     case MODE_DNLD_MC_OFFS_SAVE:
311     case MODE_DNLD_MC_OFFS_DEFER:
312         send_data = true;
313         do_len = 24 + dmp_len;
314         rem = do_len % 4;
315         if (rem)
316             do_len += (4 - rem);
317         break;
318     case MODE_ACTIVATE_MC:
319     case MODE_ABORT_MC:
320         do_len = 24;
321         break;
322     default:
323         pr2serr("%s: unexpected mc_mode=0x%x\n", __func__, op->mc_mode);
324         return SG_LIB_SYNTAX_ERROR;
325     }
326     if (do_len > wp->dout_len) {
327         if (wp->doutp)
328             free(wp->doutp);
329         wp->doutp = sg_memalign(do_len, 0, &wp->free_doutp, op->verbose > 3);
330         if (! wp->doutp) {
331             pr2serr("%s: unable to alloc %d bytes\n", __func__, do_len);
332             return SG_LIB_CAT_OTHER;
333         }
334         wp->dout_len = do_len;
335     } else
336         memset(wp->doutp, 0, do_len);
337     wp->doutp[0] = DPC_DOWNLOAD_MICROCODE;
338     wp->doutp[1] = op->mc_subenc;
339     sg_put_unaligned_be16(do_len - 4, wp->doutp + 2);
340     sg_put_unaligned_be32(gen_code, wp->doutp + 4);
341     wp->doutp[8] = op->mc_mode;
342     wp->doutp[11] = op->mc_id;
343     if (send_data)
344         sg_put_unaligned_be32(op->mc_offset + off_off, wp->doutp + 12);
345     sg_put_unaligned_be32(op->mc_tlen, wp->doutp + 16);
346     sg_put_unaligned_be32(dmp_len, wp->doutp + 20);
347     if (send_data && (dmp_len > 0))
348         memcpy(wp->doutp + 24, dmp, dmp_len);
349     if ((op->verbose > 2) || (op->dry_run && op->verbose)) {
350         pr2serr("send diag: sub-enc id=%u exp_gen=%u download_mc_code=%u "
351                 "buff_id=%u\n", op->mc_subenc, gen_code, op->mc_mode,
352                 op->mc_id);
353         pr2serr("    buff_off=%u image_len=%u this_mc_data_len=%u "
354                 "dout_len=%u\n", op->mc_offset + off_off, op->mc_tlen,
355                 dmp_len, do_len);
356     }
357     /* select long duration timeout (7200 seconds) */
358     if (op->dry_run) {
359         if (op->mc_subenc < 4) {
360             int s = op->mc_offset + off_off + dmp_len;
361 
362             n = 8 + (op->mc_subenc * 16);
363             dummy_rd_resp[n + 11] = op->mc_id;
364             sg_put_unaligned_be32(((send_data && (! last)) ? s : 0),
365                                   dummy_rd_resp + n + 12);
366             if (MODE_ABORT_MC == op->mc_mode)
367                 dummy_rd_resp[n + 2] = 0x80;
368             else if (MODE_ACTIVATE_MC == op->mc_mode)
369                 dummy_rd_resp[n + 2] = 0x0;     /* done */
370             else
371                 dummy_rd_resp[n + 2] = (s >= op->mc_tlen) ? 0x13 : 0x1;
372         }
373         res = 0;
374     } else
375         res = sg_ll_send_diag(sg_fd, 0 /* st_code */, true /* pf */,
376                               false /* st */, false /* devofl */,
377                               false /* unitofl */, 1 /* long_duration */,
378                               wp->doutp, do_len, true /* noisy */, verb);
379     if (op->mc_non) {
380         /* If non-standard, only call RDR after failed SD */
381         if (0 == res)
382             return 0;
383         /* If RDR error after SD error, prefer reporting SD error */
384         ret = res;
385     } else {
386         switch (op->mc_mode) {
387         case MODE_DNLD_MC_OFFS:
388         case MODE_DNLD_MC_OFFS_SAVE:
389             if (res)
390                 return res;
391             else if (last) {
392                 if (op->ealsd)
393                     return 0;   /* RDR after last may hit a device reset */
394             }
395             break;
396         case MODE_DNLD_MC_OFFS_DEFER:
397             if (res)
398                 return res;
399             break;
400         case MODE_ACTIVATE_MC:
401         case MODE_ABORT_MC:
402             if (0 == res) {
403                 if (op->ealsd)
404                     return 0;   /* RDR after this may hit a device reset */
405             }
406             /* SD has failed, so do a RDR but return SD's error */
407             ret = res;
408             break;
409         default:
410             pr2serr("%s: mc_mode=0x%x\n", __func__, op->mc_mode);
411             return SG_LIB_SYNTAX_ERROR;
412         }
413     }
414 
415     if (op->dry_run) {
416         n = sizeof(dummy_rd_resp);
417         n = (n < din_len) ? n : din_len;
418         memcpy(dip, dummy_rd_resp, n);
419         resid = din_len - n;
420         res = 0;
421     } else
422         res = sg_ll_receive_diag_v2(sg_fd, true /* pcv */,
423                                     DPC_DOWNLOAD_MICROCODE, dip, din_len,
424                                     0 /* default timeout */, &resid, true,
425                                     verb);
426     if (res)
427         return ret ? ret : res;
428     rsp_len = sg_get_unaligned_be16(dip + 2) + 4;
429     act_len = din_len - resid;
430     if (rsp_len > din_len) {
431         pr2serr("<<< warning response buffer too small [%d but need "
432                 "%d]>>>\n", din_len, rsp_len);
433         rsp_len = din_len;
434     }
435     if (rsp_len > act_len) {
436         pr2serr("<<< warning response too short [actually got %d but need "
437                 "%d]>>>\n", act_len, rsp_len);
438         rsp_len = act_len;
439     }
440     if (rsp_len < 8) {
441         pr2serr("Download microcode status dpage too short [%d]\n", rsp_len);
442         return ret ? ret : SG_LIB_CAT_OTHER;
443     }
444     rec_gen_code = sg_get_unaligned_be32(dip + 4);
445     if ((op->verbose > 2) || (op->dry_run && op->verbose)) {
446         n = 8 + (op->mc_subenc * 16);
447         pr2serr("rec diag: rsp_len=%d, num_sub-enc=%u rec_gen_code=%u "
448                 "exp_buff_off=%u\n", rsp_len, dip[1],
449                 sg_get_unaligned_be32(dip + 4),
450                 sg_get_unaligned_be32(dip + n + 12));
451     }
452     if (rec_gen_code != gen_code)
453         pr2serr("gen_code changed from %" PRIu32 " to %" PRIu32
454                 ", continuing but may fail\n", gen_code, rec_gen_code);
455     num = (rsp_len - 8) / 16;
456     if ((rsp_len - 8) % 16)
457         pr2serr("Found %d Download microcode status descriptors, but there "
458                 "is residual\n", num);
459     bp = dip + 8;
460     for (k = 0; k < num; ++k, bp += 16) {
461         if ((unsigned int)op->mc_subenc == (unsigned int)bp[1]) {
462             mc_status = bp[2];
463             cp = get_mc_status_str(mc_status);
464             if ((mc_status >= 0x80) || op->verbose)
465                 pr2serr("mc offset=%u: status: %s [0x%x, additional=0x%x]\n",
466                         sg_get_unaligned_be32(bp + 12), cp, mc_status, bp[3]);
467             if (op->verbose > 1)
468                 pr2serr("  subenc_id=%d, expected_buffer_id=%d, "
469                         "expected_offset=0x%" PRIx32 "\n", bp[1], bp[11],
470                         sg_get_unaligned_be32(bp + 12));
471             if (mc_status >= 0x80)
472                 ret = ret ? ret : SG_LIB_CAT_OTHER;
473         }
474     }
475     return ret;
476 }
477 
478 
479 int
main(int argc,char * argv[])480 main(int argc, char * argv[])
481 {
482     bool last, got_stdin, is_reg;
483     bool want_file = false;
484     bool verbose_given = false;
485     bool version_given = false;
486     int res, c, len, k, n, rsp_len, resid, act_len, din_len, verb;
487     int sg_fd = -1;
488     int infd = -1;
489     int do_help = 0;
490     int ret = 0;
491     uint32_t gen_code = 0;
492     const char * device_name = NULL;
493     const char * file_name = NULL;
494     uint8_t * dmp = NULL;
495     uint8_t * dip = NULL;
496     uint8_t * free_dip = NULL;
497     char * cp;
498     char ebuff[EBUFF_SZ];
499     struct stat a_stat;
500     struct dout_buff_t dout;
501     struct opts_t opts;
502     struct opts_t * op;
503     const struct mode_s * mp;
504 
505     op = &opts;
506     memset(op, 0, sizeof(opts));
507     memset(&dout, 0, sizeof(dout));
508     din_len = DEF_DIN_LEN;
509     while (1) {
510         int option_index = 0;
511 
512         c = getopt_long(argc, argv, "b:dehi:I:l:m:No:s:S:t:vV", long_options,
513                         &option_index);
514         if (c == -1)
515             break;
516 
517         switch (c) {
518         case 'b':
519             op->bpw = sg_get_num(optarg);
520             if (op->bpw < 0) {
521                 pr2serr("argument to '--bpw' should be in a positive "
522                         "number\n");
523                 return SG_LIB_SYNTAX_ERROR;
524             }
525             if ((cp = strchr(optarg, ','))) {
526                 if (0 == strncmp("act", cp + 1, 3))
527                     op->bpw_then_activate = true;
528             }
529             break;
530         case 'd':
531             op->dry_run = true;
532             break;
533         case 'e':
534             op->ealsd = true;
535             break;
536         case 'h':
537         case '?':
538             ++do_help;
539             break;
540         case 'i':
541             op->mc_id = sg_get_num_nomult(optarg);
542             if ((op->mc_id < 0) || (op->mc_id > 255)) {
543                 pr2serr("argument to '--id' should be in the range 0 to "
544                         "255\n");
545                 return SG_LIB_SYNTAX_ERROR;
546             }
547             break;
548         case 'I':
549             file_name = optarg;
550             break;
551         case 'l':
552             op->mc_len = sg_get_num(optarg);
553             if (op->mc_len < 0) {
554                 pr2serr("bad argument to '--length'\n");
555                 return SG_LIB_SYNTAX_ERROR;
556              }
557              op->mc_len_given = true;
558              break;
559         case 'm':
560             if (isdigit((uint8_t)*optarg)) {
561                 op->mc_mode = sg_get_num_nomult(optarg);
562                 if ((op->mc_mode < 0) || (op->mc_mode > 255)) {
563                     pr2serr("argument to '--mode' should be in the range 0 "
564                             "to 255\n");
565                     return SG_LIB_SYNTAX_ERROR;
566                 }
567             } else {
568                 len = strlen(optarg);
569                 for (mp = mode_arr; mp->mode_string; ++mp) {
570                     if (0 == strncmp(mp->mode_string, optarg, len)) {
571                         op->mc_mode = mp->mode;
572                         break;
573                     }
574                 }
575                 if (! mp->mode_string) {
576                     print_modes();
577                     return SG_LIB_SYNTAX_ERROR;
578                 }
579             }
580             break;
581         case 'N':
582             op->mc_non = true;
583             break;
584         case 'o':
585            op->mc_offset = sg_get_num(optarg);
586            if (op->mc_offset < 0) {
587                 pr2serr("bad argument to '--offset'\n");
588                 return SG_LIB_SYNTAX_ERROR;
589             }
590             if (0 != (op->mc_offset % 4)) {
591                 pr2serr("'--offset' value needs to be a multiple of 4\n");
592                 return SG_LIB_SYNTAX_ERROR;
593             }
594             break;
595         case 's':
596            op->mc_skip = sg_get_num(optarg);
597            if (op->mc_skip < 0) {
598                 pr2serr("bad argument to '--skip'\n");
599                 return SG_LIB_SYNTAX_ERROR;
600             }
601             break;
602         case 'S':
603            op->mc_subenc = sg_get_num_nomult(optarg);
604            if ((op->mc_subenc < 0) || (op->mc_subenc > 255)) {
605                 pr2serr("expected argument to '--subenc' to be 0 to 255\n");
606                 return SG_LIB_SYNTAX_ERROR;
607             }
608             break;
609         case 't':
610            op->mc_tlen = sg_get_num(optarg);
611            if (op->mc_tlen < 0) {
612                 pr2serr("bad argument to '--tlength'\n");
613                 return SG_LIB_SYNTAX_ERROR;
614             }
615             break;
616         case 'v':
617             verbose_given = true;
618             ++op->verbose;
619             break;
620         case 'V':
621             version_given = true;
622             break;
623         default:
624             pr2serr("unrecognised option code 0x%x ??\n", c);
625             usage();
626             return SG_LIB_SYNTAX_ERROR;
627         }
628     }
629     if (do_help) {
630         if (do_help > 1) {
631             usage();
632             pr2serr("\n");
633             print_modes();
634         } else
635             usage();
636         return 0;
637     }
638     if (optind < argc) {
639         if (NULL == device_name) {
640             device_name = argv[optind];
641             ++optind;
642         }
643         if (optind < argc) {
644             for (; optind < argc; ++optind)
645                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
646             usage();
647             return SG_LIB_SYNTAX_ERROR;
648         }
649     }
650 
651 #ifdef DEBUG
652     pr2serr("In DEBUG mode, ");
653     if (verbose_given && version_given) {
654         pr2serr("but override: '-vV' given, zero verbose and continue\n");
655         verbose_given = false;
656         version_given = false;
657         op->verbose = 0;
658     } else if (! verbose_given) {
659         pr2serr("set '-vv'\n");
660         op->verbose = 2;
661     } else
662         pr2serr("keep verbose=%d\n", op->verbose);
663 #else
664     if (verbose_given && version_given)
665         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
666 #endif
667     if (version_given) {
668         pr2serr(ME "version: %s\n", version_str);
669         return 0;
670     }
671 
672     if (NULL == device_name) {
673         pr2serr("missing device name!\n\n");
674         usage();
675         return SG_LIB_SYNTAX_ERROR;
676     }
677     switch (op->mc_mode) {
678     case MODE_DNLD_MC_OFFS:
679     case MODE_DNLD_MC_OFFS_SAVE:
680     case MODE_DNLD_MC_OFFS_DEFER:
681         want_file = true;
682         break;
683     case MODE_DNLD_STATUS:
684     case MODE_ACTIVATE_MC:
685     case MODE_ABORT_MC:
686         want_file = false;
687         break;
688     default:
689         pr2serr("%s: mc_mode=0x%x, continue for now\n", __func__,
690                 op->mc_mode);
691         break;
692     }
693 
694     if ((op->mc_len > 0) && (op->bpw > op->mc_len)) {
695         pr2serr("trim chunk size (CS) to be the same as LEN\n");
696         op->bpw = op->mc_len;
697     }
698     if ((op->mc_offset > 0) && (op->bpw > 0)) {
699         op->mc_offset = 0;
700         pr2serr("WARNING: --offset= ignored (set back to 0) when --bpw= "
701                 "argument given (and > 0)\n");
702     }
703 
704 #ifdef SG_LIB_WIN32
705 #ifdef SG_LIB_WIN32_DIRECT
706     if (op->verbose > 4)
707         pr2serr("Initial win32 SPT interface state: %s\n",
708                 scsi_pt_win32_spt_state() ? "direct" : "indirect");
709     scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
710 #endif
711 #endif
712 
713     sg_fd = sg_cmds_open_device(device_name, false /* rw */, op->verbose);
714     if (sg_fd < 0) {
715         if (op->verbose)
716             pr2serr(ME "open error: %s: %s\n", device_name,
717                     safe_strerror(-sg_fd));
718         ret = sg_convert_errno(-sg_fd);
719         goto fini;
720     }
721 
722     if (file_name && (! want_file))
723         pr2serr("ignoring --in=FILE option\n");
724     else if (file_name) {
725         got_stdin = (0 == strcmp(file_name, "-"));
726         if (got_stdin)
727             infd = STDIN_FILENO;
728         else {
729             if ((infd = open(file_name, O_RDONLY)) < 0) {
730                 ret = sg_convert_errno(errno);
731                 snprintf(ebuff, EBUFF_SZ,
732                          ME "could not open %s for reading", file_name);
733                 perror(ebuff);
734                 goto fini;
735             } else if (sg_set_binary_mode(infd) < 0)
736                 perror("sg_set_binary_mode");
737         }
738         if ((0 == fstat(infd, &a_stat)) && S_ISREG(a_stat.st_mode)) {
739             is_reg = true;
740             if (0 == op->mc_len) {
741                 if (op->mc_skip >= a_stat.st_size) {
742                     pr2serr("skip exceeds file size of %d bytes\n",
743                             (int)a_stat.st_size);
744                     ret = SG_LIB_FILE_ERROR;
745                     goto fini;
746                 }
747                 op->mc_len = (int)(a_stat.st_size) - op->mc_skip;
748             }
749         } else {
750             is_reg = false;
751             if (0 == op->mc_len)
752                 op->mc_len = DEF_XFER_LEN;
753         }
754         if (op->mc_len > MAX_XFER_LEN) {
755             pr2serr("file size or requested length (%d) exceeds "
756                     "MAX_XFER_LEN of %d bytes\n", op->mc_len,
757                     MAX_XFER_LEN);
758             ret = SG_LIB_FILE_ERROR;
759             goto fini;
760         }
761         if (NULL == (dmp = (uint8_t *)malloc(op->mc_len))) {
762             pr2serr(ME "out of memory to hold microcode read from FILE\n");
763             ret = SG_LIB_CAT_OTHER;
764             goto fini;
765         }
766         /* Don't remember why this is preset to 0xff, from write_buffer */
767         memset(dmp, 0xff, op->mc_len);
768         if (op->mc_skip > 0) {
769             if (! is_reg) {
770                 if (got_stdin)
771                     pr2serr("Can't skip on stdin\n");
772                 else
773                     pr2serr(ME "not a 'regular' file so can't apply skip\n");
774                 ret = SG_LIB_FILE_ERROR;
775                 goto fini;
776             }
777             if (lseek(infd, op->mc_skip, SEEK_SET) < 0) {
778                 ret = sg_convert_errno(errno);
779                 snprintf(ebuff,  EBUFF_SZ, ME "couldn't skip to "
780                          "required position on %s", file_name);
781                 perror(ebuff);
782                 goto fini;
783             }
784         }
785         res = read(infd, dmp, op->mc_len);
786         if (res < 0) {
787             ret = sg_convert_errno(errno);
788             snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
789                      file_name);
790             perror(ebuff);
791             goto fini;
792         }
793         if (res < op->mc_len) {
794             if (op->mc_len_given) {
795                 pr2serr("tried to read %d bytes from %s, got %d bytes\n",
796                         op->mc_len, file_name, res);
797                 pr2serr("pad with 0xff bytes and continue\n");
798             } else {
799                 if (op->verbose) {
800                     pr2serr("tried to read %d bytes from %s, got %d "
801                             "bytes\n", op->mc_len, file_name, res);
802                     pr2serr("will send %d bytes", res);
803                     if ((op->bpw > 0) && (op->bpw < op->mc_len))
804                         pr2serr(", %d bytes per WRITE BUFFER command\n",
805                                 op->bpw);
806                     else
807                         pr2serr("\n");
808                 }
809                 op->mc_len = res;
810             }
811         }
812         if (! got_stdin)
813             close(infd);
814         infd = -1;
815     } else if (want_file) {
816         pr2serr("need --in=FILE option with given mode\n");
817         ret = SG_LIB_CONTRADICT;
818         goto fini;
819     }
820     if (op->mc_tlen < op->mc_len)
821         op->mc_tlen = op->mc_len;
822     if (op->mc_non && (MODE_DNLD_STATUS == op->mc_mode)) {
823         pr2serr("Do nothing because '--non' given so fetching the Download "
824                 "microcode status\ndpage might be dangerous\n");
825         goto fini;
826     }
827 
828     dip = sg_memalign(din_len, 0, &free_dip, op->verbose > 3);
829     if (NULL == dip) {
830         pr2serr(ME "out of memory (data-in buffer)\n");
831         ret = SG_LIB_CAT_OTHER;
832         goto fini;
833     }
834     verb = (op->verbose > 1) ? op->verbose - 1 : 0;
835     /* Fetch Download microcode status dpage for generation code ++ */
836     if (op->dry_run) {
837         n = sizeof(dummy_rd_resp);
838         n = (n < din_len) ? n : din_len;
839         memcpy(dip, dummy_rd_resp, n);
840         resid = din_len - n;
841         res = 0;
842     } else
843         res = sg_ll_receive_diag_v2(sg_fd, true /* pcv */,
844                                     DPC_DOWNLOAD_MICROCODE, dip, din_len,
845                                     0 /*default timeout */, &resid, true,
846                                     verb);
847     if (0 == res) {
848         rsp_len = sg_get_unaligned_be16(dip + 2) + 4;
849         act_len = din_len - resid;
850         if (rsp_len > din_len) {
851             pr2serr("<<< warning response buffer too small [%d but need "
852                     "%d]>>>\n", din_len, rsp_len);
853             rsp_len = din_len;
854         }
855         if (rsp_len > act_len) {
856             pr2serr("<<< warning response too short [actually got %d but "
857                     "need %d]>>>\n", act_len, rsp_len);
858             rsp_len = act_len;
859         }
860         if (rsp_len < 8) {
861             pr2serr("Download microcode status dpage too short\n");
862             ret = SG_LIB_CAT_OTHER;
863             goto fini;
864         }
865         if ((op->verbose > 2) || (op->dry_run && op->verbose))
866             pr2serr("rec diag(ini): rsp_len=%d, num_sub-enc=%u "
867                     "rec_gen_code=%u\n", rsp_len, dip[1],
868                     sg_get_unaligned_be32(dip + 4));
869     } else {
870         ret = res;
871         goto fini;
872     }
873     gen_code = sg_get_unaligned_be32(dip + 4);
874 
875     if (MODE_DNLD_STATUS == op->mc_mode) {
876         show_download_mc_sdg(dip, rsp_len, gen_code);
877         goto fini;
878     } else if (! want_file) {   /* ACTIVATE and ABORT */
879         res = send_then_receive(sg_fd, gen_code, 0, NULL, 0, &dout, dip,
880                                 din_len, true, op);
881         ret = res;
882         goto fini;
883     }
884 
885     res = 0;
886     if (op->bpw > 0) {
887         for (k = 0, last = false; k < op->mc_len; k += n) {
888             n = op->mc_len - k;
889             if (n > op->bpw)
890                 n = op->bpw;
891             else
892                 last = true;
893             if (op->verbose)
894                 pr2serr("bpw loop: mode=0x%x, id=%d, off_off=%d, len=%d, "
895                         "last=%d\n", op->mc_mode, op->mc_id, k, n, last);
896             res = send_then_receive(sg_fd, gen_code, k, dmp + k, n, &dout,
897                                     dip, din_len, last, op);
898             if (res)
899                 break;
900         }
901         if (op->bpw_then_activate && (0 == res)) {
902             op->mc_mode = MODE_ACTIVATE_MC;
903             if (op->verbose)
904                 pr2serr("sending Activate deferred microcode [0xf]\n");
905             res = send_then_receive(sg_fd, gen_code, 0, NULL, 0, &dout,
906                                     dip, din_len, true, op);
907         }
908     } else {
909         if (op->verbose)
910             pr2serr("single: mode=0x%x, id=%d, offset=%d, len=%d\n",
911                     op->mc_mode, op->mc_id, op->mc_offset, op->mc_len);
912         res = send_then_receive(sg_fd, gen_code, 0, dmp, op->mc_len, &dout,
913                                 dip, din_len, true, op);
914     }
915     if (res)
916         ret = res;
917 
918 fini:
919     if ((infd >= 0) && (! got_stdin))
920         close(infd);
921     if (dmp)
922         free(dmp);
923     if (dout.free_doutp)
924         free(dout.free_doutp);
925     if (free_dip)
926         free(free_dip);
927     if (sg_fd >= 0) {
928         res = sg_cmds_close_device(sg_fd);
929         if (res < 0) {
930             pr2serr("close error: %s\n", safe_strerror(-res));
931             if (0 == ret)
932                 ret = sg_convert_errno(-res);
933         }
934     }
935     if (0 == op->verbose) {
936         if (! sg_if_can2stderr("sg_ses_microcode failed: ", ret))
937             pr2serr("Some error occurred, try again with '-v' "
938                     "or '-vv' for more information\n");
939     }
940     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
941 }
942