xref: /aosp_15_r20/external/sg3_utils/src/sg_test_rwbuf.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
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