1/* make_bad_sector.c v3.03 by Mark Lord */
2#include <stdio.h>
3#include <stdlib.h>
4#include <unistd.h>
5#include <fcntl.h>
6#include <errno.h>
7#include <string.h>
8#include <sys/ioctl.h>
9#include <sys/stat.h>
10#include <sys/types.h>
11#include <linux/fs.h>
12#include <scsi/scsi.h>
13#include <scsi/sg.h>
14
15#include "hdparm.h"
16#include "sgio.h"
17
18#define READ	0
19#define WRITE	1
20
21int verbose = 0;	// used by sgio.c
22
23#if 0
24static void init_hdio_taskfile (struct hdio_taskfile *r, __u8 ata_op, int rw, int force_lba48,
25				__u64 lba, unsigned int nsect, int data_bytes)
26{
27	const __u64 lba28_mask = 0x0fffffff;
28
29	memset(r, 0, sizeof(struct hdio_taskfile) + data_bytes);
30	r->xfer_method	= TASKFILE_XFER_METHOD_PIO_OUT;
31	if (!data_bytes) {
32		r->cmd_req = TASKFILE_CMD_REQ_NODATA;
33	} else {
34		r->cmd_req = rw ? TASKFILE_CMD_REQ_OUT : TASKFILE_CMD_REQ_IN;
35		if (rw)
36			r->out_bytes = data_bytes;
37		else
38			r->in_bytes  = data_bytes;
39	}
40	r->lob.command           = ata_op;
41	r->out_flags.lob.b.command = 1;
42	r->out_flags.lob.b.dev     = 1;
43	r->out_flags.lob.b.lbal    = 1;
44	r->out_flags.lob.b.lbam    = 1;
45	r->out_flags.lob.b.lbah    = 1;
46	r->out_flags.lob.b.nsect   = 1;
47
48	r->lob.nsect = nsect;
49	r->lob.lbal  = lba;
50	r->lob.lbam  = lba >>  8;
51	r->lob.lbah  = lba >> 16;
52	r->lob.dev   = ATA_USING_LBA;
53
54	if ((lba & ~lba28_mask) == 0 && nsect <= 256 && !force_lba48) {
55		r->lob.dev |= lba >> 24;
56	} else {
57		r->hob.nsect = nsect >>  8;
58		r->hob.lbal = lba    >> 24;
59		r->hob.lbam = lba    >> 32;
60		r->hob.lbah = lba    >> 40;
61		r->out_flags.hob.b.nsect = 1;
62		r->out_flags.hob.b.lbal  = 1;
63		r->out_flags.hob.b.lbam  = 1;
64		r->out_flags.hob.b.lbah  = 1;
65	}
66}
67#endif
68
69int main (int argc, char *argv[])
70{
71	/*
72	 * Note: the extra bytes are transfered ONE-PER_WORD after the sector data,
73	 * so for 4 extra bytes, we must transfer 4 extra WORDs.
74	 */
75	const char *devpath, *myname = argv[0];;
76	int rc = 0, fd, do_rewrite = 0, do_readback = 0;
77	__u8 ata_op;
78	__u64 lba;
79	unsigned char bad_pattern = 0x00;
80	struct hdio_taskfile *r;
81	const int ten_seconds = 10;
82
83	r = malloc(sizeof(struct hdio_taskfile) + 520);
84	if (!r) {
85		perror("malloc()");
86		exit(1);
87	}
88
89	while (argc-- > 1 && **++argv == '-') {
90		if (!strcmp("--readback", argv[0])) {
91			do_readback = 1;
92		} else if (!strcmp("--rewrite", argv[0])) {
93			do_rewrite = 1;
94		} else {
95			fprintf(stderr, "%s: unknown flag: %s\n", myname, argv[0]);
96			exit(1);
97		}
98	}
99	if (argc != 2) {
100		fprintf(stderr, "%s: bad/missing parms: expected [--rewrite|--readback] <devpath> <lba>\n", myname);
101		exit(1);
102	}
103	devpath = argv[0];
104	lba = strtol(argv[1], NULL, 0);
105
106	fd = open(devpath, O_RDWR);
107	if (fd == -1) {
108		perror(devpath);
109		exit(1);
110	}
111
112	// Try and ensure that the system doesn't have our sector in cache:
113	(void) ioctl(fd, BLKFLSBUF, NULL);
114
115	if (do_rewrite) {
116		fprintf(stderr, "%s: overwriting LBA=%llu (this should succeed!)\n",
117			 devpath, (unsigned long long)lba);
118		ata_op = (lba >> 28) ? ATA_OP_WRITE_PIO_EXT : ATA_OP_WRITE_PIO;
119		init_hdio_taskfile(r, ata_op, WRITE, 0, lba, 1, 512);
120		rc = do_taskfile_cmd(fd, r, ten_seconds);
121		fprintf(stderr, "%s: %s\n", devpath, rc ? "error" : "success");
122	}
123	if (rc == 0 || do_readback) {
124		fprintf(stderr, "%s: readback test LBA=%llu%s\n",
125			 devpath, (unsigned long long)lba,
126			 do_rewrite ? " (this should succeed!)" : "");
127		ata_op = (lba >> 28) ? ATA_OP_READ_PIO_EXT : ATA_OP_READ_PIO;
128		init_hdio_taskfile(r, ata_op, READ, 0, lba, 1, 512);
129		rc = do_taskfile_cmd(fd, r, ten_seconds);
130		fprintf(stderr, "%s: %s\n", devpath, rc ? "error" : "success");
131	}
132	if (do_rewrite || do_readback)
133		exit(rc);
134
135	do {
136		int i;
137
138		init_hdio_taskfile(r, ATA_OP_WRITE_LONG_ONCE, WRITE, 0, lba, 1, 520);
139		--bad_pattern;
140		// Corrupt and rewrite the sector:
141		for (i = 0; i < 520; ++i)
142			r->data[i] = bad_pattern;
143		fprintf(stderr, "%s: writing LBA=%llu\n", devpath, (unsigned long long)lba);
144		rc = do_taskfile_cmd(fd, r, ten_seconds);
145		if (rc) {
146			fprintf(stderr, "%s: WRITE_LONG failed\n", devpath);
147		} else {
148			fprintf(stderr, "%s: readback test LBA=%llu (this should fail!)\n",
149				 devpath, (unsigned long long)lba);
150			//init_hdio_taskfile(r, ATA_OP_READ_VERIFY_ONCE, READ, 0, lba, 1, 0);
151			init_hdio_taskfile(r, ATA_OP_READ_PIO_EXT, READ, 1, lba, 1, 512);
152			rc = do_taskfile_cmd(fd, r, ten_seconds);
153			if (rc)
154				fprintf(stderr, "%s: readback failed\n", devpath);
155		}
156	} while (rc == 0);
157	exit (0);
158}
159