1 /*
2 * (c) 2000 Kurt Garloff
3 * heavily based on Douglas Gilbert's sg_rbuf program.
4 * (c) 1999-2022 Douglas Gilbert
5 *
6 * Program to test the SCSI host adapter by issuing
7 * write and read operations on a device's buffer
8 * and calculating checksums.
9 * NOTE: If you can not reserve the buffer of the device
10 * for this purpose (SG_GET_RESERVED_SIZE), you risk
11 * serious data corruption, if the device is accessed by
12 * somebody else in the meantime.
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2, or (at your option)
17 * any later version.
18 *
19 * SPDX-License-Identifier: GPL-2.0-or-later
20 *
21 * $Id: sg_test_rwbuf.c,v 1.1 2000/03/02 13:50:03 garloff Exp $
22 *
23 * 2003/11/11 switch sg3_utils version to use SG_IO ioctl [dpg]
24 * 2004/06/08 remove SG_GET_VERSION_NUM check [dpg]
25 */
26
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include <stdbool.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <getopt.h>
36 #include <sys/ioctl.h>
37 #include <sys/types.h>
38 #include <time.h>
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #include "sg_lib.h"
45 #include "sg_io_linux.h"
46 #include "sg_unaligned.h"
47 #include "sg_pr2serr.h"
48
49
50 static const char * version_str = "1.21 20220118";
51
52 #define BPI (signed)(sizeof(int))
53
54 #define RB_MODE_DESC 3
55 #define RWB_MODE_DATA 2
56 #define RB_DESC_LEN 4
57
58 /* The microcode in a SCSI device is _not_ modified by doing a WRITE BUFFER
59 * with mode set to "data" (0x2) as done by this utility. Therefore this
60 * utility is safe in that respect. [Mode values 0x4, 0x5, 0x6 and 0x7 are
61 * the dangerous ones :-)]
62 */
63
64 #define ME "sg_test_rwbuf: "
65
66 static int base = 0x12345678;
67 static int buf_capacity = 0;
68 static int buf_granul = 255;
69 static uint8_t *cmpbuf = NULL;
70 static uint8_t *free_cmpbuf = NULL;
71
72
73 /* Options */
74 static int size = -1;
75 static bool do_quick = false;
76 static int addwrite = 0;
77 static int addread = 0;
78 static int verbose = 0;
79
80 static struct option long_options[] = {
81 {"help", no_argument, 0, 'h'},
82 {"quick", no_argument, 0, 'q'},
83 {"addrd", required_argument, 0, 'r'},
84 {"size", required_argument, 0, 's'},
85 {"times", required_argument, 0, 't'},
86 {"verbose", no_argument, 0, 'v'},
87 {"version", no_argument, 0, 'V'},
88 {"addwr", required_argument, 0, 'w'},
89 {0, 0, 0, 0},
90 };
91
92 static int
find_out_about_buffer(int sg_fd)93 find_out_about_buffer(int sg_fd)
94 {
95 uint8_t rb_cdb[] = {READ_BUFFER, 0, 0, 0, 0, 0, 0, 0, 0, 0};
96 uint8_t rbBuff[RB_DESC_LEN];
97 uint8_t sense_buffer[32];
98 struct sg_io_hdr io_hdr;
99 int res;
100
101 rb_cdb[1] = RB_MODE_DESC;
102 rb_cdb[8] = RB_DESC_LEN;
103 memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
104 io_hdr.interface_id = 'S';
105 io_hdr.cmd_len = sizeof(rb_cdb);
106 io_hdr.mx_sb_len = sizeof(sense_buffer);
107 io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
108 io_hdr.dxfer_len = RB_DESC_LEN;
109 io_hdr.dxferp = rbBuff;
110 io_hdr.cmdp = rb_cdb;
111 io_hdr.sbp = sense_buffer;
112 io_hdr.timeout = 60000; /* 60000 millisecs == 60 seconds */
113
114 if (verbose) {
115 char b[128];
116
117 pr2serr(" read buffer [mode desc] cdb: %s\n",
118 sg_get_command_str(rb_cdb, (int)sizeof(rb_cdb), false,
119 sizeof(b), b));
120 }
121 if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
122 perror(ME "SG_IO READ BUFFER descriptor error");
123 return -1;
124 }
125 /* now for the error processing */
126 res = sg_err_category3(&io_hdr);
127 switch (res) {
128 case SG_LIB_CAT_RECOVERED:
129 sg_chk_n_print3("READ BUFFER descriptor, continuing",
130 &io_hdr, true);
131 #if defined(__GNUC__)
132 #if (__GNUC__ >= 7)
133 __attribute__((fallthrough));
134 /* FALL THROUGH */
135 #endif
136 #endif
137 case SG_LIB_CAT_CLEAN:
138 break;
139 default: /* won't bother decoding other categories */
140 sg_chk_n_print3("READ BUFFER descriptor error", &io_hdr,
141 true);
142 return res;
143 }
144
145 buf_capacity = sg_get_unaligned_be24(rbBuff + 1);
146 buf_granul = (uint8_t)rbBuff[0];
147 #if 0
148 printf("READ BUFFER reports: %02x %02x %02x %02x\n",
149 rbBuff[0], rbBuff[1], rbBuff[2], rbBuff[3]);
150 #endif
151 if (verbose)
152 printf("READ BUFFER reports: buffer capacity=%d, offset "
153 "boundary=%d\n", buf_capacity, buf_granul);
154 return 0;
155 }
156
157 static int
mymemcmp(uint8_t * bf1,uint8_t * bf2,int len)158 mymemcmp (uint8_t *bf1, uint8_t *bf2, int len)
159 {
160 int df;
161 for (df = 0; df < len; df++)
162 if (bf1[df] != bf2[df]) return df;
163 return 0;
164 }
165
166 /* return 0 if good, else 2222 */
167 static int
do_checksum(int * buf,int len,bool quiet)168 do_checksum(int *buf, int len, bool quiet)
169 {
170 int sum = base;
171 int i; int rln = len;
172 for (i = 0; i < len/BPI; i++)
173 sum += buf[i];
174 while (rln%BPI) sum += ((char*)buf)[--rln];
175 if (sum != 0x12345678) {
176 if (!quiet) printf ("sg_test_rwbuf: Checksum error (sz=%i):"
177 " %08x\n", len, sum);
178 if (cmpbuf && !quiet) {
179 int diff = mymemcmp (cmpbuf, (uint8_t*)buf,
180 len);
181 printf ("Differ at pos %i/%i:\n", diff, len);
182 for (i = 0; i < 24 && i+diff < len; i++)
183 printf (" %02x", cmpbuf[i+diff]);
184 printf ("\n");
185 for (i = 0; i < 24 && i+diff < len; i++)
186 printf (" %02x",
187 ((uint8_t*)buf)[i+diff]);
188 printf ("\n");
189 }
190 return 2222;
191 }
192 else {
193 if (verbose > 1)
194 printf("Checksum value: 0x%x\n", sum);
195 return 0;
196 }
197 }
198
do_fill_buffer(int * buf,int len)199 void do_fill_buffer (int *buf, int len)
200 {
201 int sum;
202 int i; int rln = len;
203
204 srand(time(0));
205 retry:
206 if (len >= BPI)
207 base = 0x12345678 + rand(); /* don't need strong crypto */
208 else
209 base = 0x12345678 + (char)rand();
210 sum = base;
211 for (i = 0; i < len/BPI - 1; i++)
212 {
213 /* we rely on rand() giving full range of int */
214 buf[i] = rand();
215 sum += buf[i];
216 }
217 while (rln%BPI)
218 {
219 ((char*)buf)[--rln] = rand();
220 sum += ((char*)buf)[rln];
221 }
222 if (len >= BPI) buf[len/BPI - 1] = 0x12345678 - sum;
223 else ((char*)buf)[0] = 0x12345678 + ((char*)buf)[0] - sum;
224 if (do_checksum(buf, len, true)) {
225 if (len < BPI) goto retry;
226 printf ("sg_test_rwbuf: Memory corruption?\n");
227 exit (1);
228 }
229 if (cmpbuf) memcpy (cmpbuf, (char*)buf, len);
230 }
231
232
read_buffer(int sg_fd,unsigned ssize)233 int read_buffer (int sg_fd, unsigned ssize)
234 {
235 int res;
236 uint8_t rb_cdb[] = {READ_BUFFER, 0, 0, 0, 0, 0, 0, 0, 0, 0};
237 int bufSize = ssize + addread;
238 uint8_t * free_rbBuff = NULL;
239 uint8_t * rbBuff = (uint8_t *)sg_memalign(bufSize, 0, &free_rbBuff,
240 false);
241 uint8_t sense_buffer[32];
242 struct sg_io_hdr io_hdr;
243
244 if (NULL == rbBuff)
245 return -1;
246 rb_cdb[1] = RWB_MODE_DATA;
247 sg_put_unaligned_be24((uint32_t)bufSize, rb_cdb + 6);
248 memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
249 io_hdr.interface_id = 'S';
250 io_hdr.cmd_len = sizeof(rb_cdb);
251 io_hdr.mx_sb_len = sizeof(sense_buffer);
252 io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
253 io_hdr.dxfer_len = bufSize;
254 io_hdr.dxferp = rbBuff;
255 io_hdr.cmdp = rb_cdb;
256 io_hdr.sbp = sense_buffer;
257 io_hdr.pack_id = 2;
258 io_hdr.timeout = 60000; /* 60000 millisecs == 60 seconds */
259 if (verbose) {
260 char b[128];
261
262 pr2serr(" read buffer [mode data] cdb: %s\n",
263 sg_get_command_str(rb_cdb, (int)sizeof(rb_cdb), false,
264 sizeof(b), b));
265 }
266
267 if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
268 perror(ME "SG_IO READ BUFFER data error");
269 free(rbBuff);
270 return -1;
271 }
272 /* now for the error processing */
273 res = sg_err_category3(&io_hdr);
274 switch (res) {
275 case SG_LIB_CAT_RECOVERED:
276 sg_chk_n_print3("READ BUFFER data, continuing", &io_hdr, true);
277 #if defined(__GNUC__)
278 #if (__GNUC__ >= 7)
279 __attribute__((fallthrough));
280 /* FALL THROUGH */
281 #endif
282 #endif
283 case SG_LIB_CAT_CLEAN:
284 break;
285 default: /* won't bother decoding other categories */
286 sg_chk_n_print3("READ BUFFER data error", &io_hdr, true);
287 free(rbBuff);
288 return res;
289 }
290
291 res = do_checksum((int*)rbBuff, ssize, false);
292 if (free_rbBuff)
293 free(free_rbBuff);
294 return res;
295 }
296
write_buffer(int sg_fd,unsigned ssize)297 int write_buffer (int sg_fd, unsigned ssize)
298 {
299 uint8_t wb_cdb[] = {WRITE_BUFFER, 0, 0, 0, 0, 0, 0, 0, 0, 0};
300 int bufSize = ssize + addwrite;
301 uint8_t * free_wbBuff = NULL;
302 uint8_t * wbBuff = (uint8_t *)sg_memalign(bufSize, 0, &free_wbBuff,
303 false);
304 uint8_t sense_buffer[32];
305 struct sg_io_hdr io_hdr;
306 int res;
307
308 if (NULL == wbBuff)
309 return -1;
310 memset(wbBuff, 0, bufSize);
311 do_fill_buffer ((int*)wbBuff, ssize);
312 wb_cdb[1] = RWB_MODE_DATA;
313 sg_put_unaligned_be24((uint32_t)bufSize, wb_cdb + 6);
314 memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
315 io_hdr.interface_id = 'S';
316 io_hdr.cmd_len = sizeof(wb_cdb);
317 io_hdr.mx_sb_len = sizeof(sense_buffer);
318 io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
319 io_hdr.dxfer_len = bufSize;
320 io_hdr.dxferp = wbBuff;
321 io_hdr.cmdp = wb_cdb;
322 io_hdr.sbp = sense_buffer;
323 io_hdr.pack_id = 1;
324 io_hdr.timeout = 60000; /* 60000 millisecs == 60 seconds */
325 if (verbose) {
326 char b[128];
327
328 pr2serr(" write buffer [mode data] cdb: %s\n",
329 sg_get_command_str(wb_cdb, (int)sizeof(wb_cdb), false,
330 sizeof(b), b));
331 }
332
333 if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
334 perror(ME "SG_IO WRITE BUFFER data error");
335 free(wbBuff);
336 return -1;
337 }
338 /* now for the error processing */
339 res = sg_err_category3(&io_hdr);
340 switch (res) {
341 case SG_LIB_CAT_RECOVERED:
342 sg_chk_n_print3("WRITE BUFFER data, continuing", &io_hdr, true);
343 #if defined(__GNUC__)
344 #if (__GNUC__ >= 7)
345 __attribute__((fallthrough));
346 /* FALL THROUGH */
347 #endif
348 #endif
349 case SG_LIB_CAT_CLEAN:
350 break;
351 default: /* won't bother decoding other categories */
352 sg_chk_n_print3("WRITE BUFFER data error", &io_hdr, true);
353 free(wbBuff);
354 return res;
355 }
356 if (free_wbBuff)
357 free(free_wbBuff);
358 return res;
359 }
360
usage()361 void usage ()
362 {
363 printf ("Usage: sg_test_rwbuf [--addrd=AR] [--addwr=AW] [--help] "
364 "[--quick]\n");
365 printf (" --size=SZ [--times=NUM] [--verbose] "
366 "[--version]\n"
367 " DEVICE\n"
368 " or\n"
369 " sg_test_rwbuf DEVICE SZ [AW] [AR]\n");
370 printf (" where:\n"
371 " --addrd=AR|-r extra bytes to fetch during READ "
372 "BUFFER\n"
373 " --addwr=AW|-w extra bytes to send to WRITE BUFFER\n"
374 " --help|-l output this usage message then exit\n"
375 " --quick|-q output read buffer size then exit\n"
376 " --size=SZ|-s size of buffer (in bytes) to write "
377 "then read back\n"
378 " --times=NUM|-t number of times to run test "
379 "(default 1)\n"
380 " --verbose|-v increase verbosity of output\n"
381 " --version|-V output version then exit\n");
382 printf ("\nWARNING: If you access the device at the same time, e.g. "
383 "because it's a\n");
384 printf (" mounted hard disk, the device's buffer may be used by the "
385 "device itself\n");
386 printf (" for other data at the same time, and overwriting it may or "
387 "may not\n");
388 printf (" cause data corruption!\n");
389 printf ("(c) Douglas Gilbert, Kurt Garloff, 2000-2007, GNU GPL\n");
390 }
391
392
main(int argc,char * argv[])393 int main (int argc, char * argv[])
394 {
395 bool verbose_given = false;
396 bool version_given = false;
397 int sg_fd, res;
398 const char * device_name = NULL;
399 int times = 1;
400 int ret = 0;
401 int k = 0;
402 int err;
403
404 while (1) {
405 int option_index = 0;
406 int c;
407
408 c = getopt_long(argc, argv, "hqr:s:t:w:vV",
409 long_options, &option_index);
410 if (c == -1)
411 break;
412
413 switch (c) {
414 case 'h':
415 usage();
416 return 0;
417 case 'q':
418 do_quick = true;
419 break;
420 case 'r':
421 addread = sg_get_num(optarg);
422 if (-1 == addread) {
423 pr2serr("bad argument to '--addrd'\n");
424 return SG_LIB_SYNTAX_ERROR;
425 }
426 break;
427 case 's':
428 size = sg_get_num(optarg);
429 if (-1 == size) {
430 pr2serr("bad argument to '--size'\n");
431 return SG_LIB_SYNTAX_ERROR;
432 }
433 break;
434 case 't':
435 times = sg_get_num(optarg);
436 if (-1 == times) {
437 pr2serr("bad argument to '--times'\n");
438 return SG_LIB_SYNTAX_ERROR;
439 }
440 break;
441 case 'v':
442 verbose_given = true;
443 verbose++;
444 break;
445 case 'V':
446 version_given = true;
447 break;
448 case 'w':
449 addwrite = sg_get_num(optarg);
450 if (-1 == addwrite) {
451 pr2serr("bad argument to '--addwr'\n");
452 return SG_LIB_SYNTAX_ERROR;
453 }
454 break;
455 default:
456 usage();
457 return SG_LIB_SYNTAX_ERROR;
458 }
459 }
460 if (optind < argc) {
461 if (NULL == device_name) {
462 device_name = argv[optind];
463 ++optind;
464 }
465 }
466 if (optind < argc) {
467 if (-1 == size) {
468 size = sg_get_num(argv[optind]);
469 if (-1 == size) {
470 pr2serr("bad <sz>\n");
471 usage();
472 return SG_LIB_SYNTAX_ERROR;
473 }
474 if (++optind < argc) {
475 addwrite = sg_get_num(argv[optind]);
476 if (-1 == addwrite) {
477 pr2serr("bad [addwr]\n");
478 usage();
479 return SG_LIB_SYNTAX_ERROR;
480 }
481 if (++optind < argc) {
482 addread = sg_get_num(argv[optind]);
483 if (-1 == addread) {
484 pr2serr("bad [addrd]\n");
485 usage();
486 return SG_LIB_SYNTAX_ERROR;
487 }
488 }
489 }
490
491 }
492 if (optind < argc) {
493 for (; optind < argc; ++optind)
494 pr2serr("Unexpected extra argument" ": %s\n",
495 argv[optind]);
496 usage();
497 return SG_LIB_SYNTAX_ERROR;
498 }
499 }
500
501 #ifdef DEBUG
502 pr2serr("In DEBUG mode, ");
503 if (verbose_given && version_given) {
504 pr2serr("but override: '-vV' given, zero verbose and "
505 "continue\n");
506 verbose_given = false;
507 version_given = false;
508 verbose = 0;
509 } else if (! verbose_given) {
510 pr2serr("set '-vv'\n");
511 verbose = 2;
512 } else
513 pr2serr("keep verbose=%d\n", verbose);
514 #else
515 if (verbose_given && version_given)
516 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
517 #endif
518 if (version_given) {
519 pr2serr(ME "version: %s\n", version_str);
520 return 0;
521 }
522
523 if (NULL == device_name) {
524 pr2serr("no device name given\n");
525 usage();
526 return SG_LIB_SYNTAX_ERROR;
527 }
528 if ((size <= 0) && (! do_quick)) {
529 pr2serr("must give '--size' or '--quick' options or <sz> "
530 "argument\n");
531 usage();
532 return SG_LIB_SYNTAX_ERROR;
533 }
534
535 sg_fd = open(device_name, O_RDWR | O_NONBLOCK);
536 if (sg_fd < 0) {
537 err = errno;
538 perror("sg_test_rwbuf: open error");
539 return sg_convert_errno(err);
540 }
541 ret = find_out_about_buffer(sg_fd);
542 if (ret)
543 goto err_out;
544 if (do_quick) {
545 printf ("READ BUFFER read descriptor reports a buffer "
546 "of %d bytes [%d KiB]\n", buf_capacity,
547 buf_capacity / 1024);
548 goto err_out;
549 }
550 if (size > buf_capacity) {
551 pr2serr (ME "sz=%i > buf_capacity=%i\n", size, buf_capacity);
552 ret = SG_LIB_CAT_OTHER;
553 goto err_out;
554 }
555
556 cmpbuf = (uint8_t *)sg_memalign(size, 0, &free_cmpbuf, false);
557 for (k = 0; k < times; ++k) {
558 ret = write_buffer (sg_fd, size);
559 if (ret) {
560 goto err_out;
561 }
562 ret = read_buffer (sg_fd, size);
563 if (ret) {
564 if (2222 == ret)
565 ret = SG_LIB_CAT_MALFORMED;
566 goto err_out;
567 }
568 }
569
570 err_out:
571 if (free_cmpbuf)
572 free(free_cmpbuf);
573 res = close(sg_fd);
574 if (res < 0) {
575 perror(ME "close error");
576 if (0 == ret)
577 ret = sg_convert_errno(errno);
578 }
579 if ((0 == ret) && (! do_quick))
580 printf ("Success\n");
581 else if (times > 1)
582 printf ("Failed after %d successful cycles\n", k);
583 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
584 }
585