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