1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
27/*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30/*
31 * Copyrighted as an unpublished work.
32 * (c) Copyright INTERACTIVE Systems Corporation 1986, 1988, 1990
33 * All rights reserved.
34 */
35
36#include <stdio.h>
37#include <fcntl.h>
38#include <memory.h>
39#include <sys/types.h>
40#include <sys/param.h>
41#include <sys/stat.h>
42#include <sys/mkdev.h>
43#include <sys/vtoc.h>
44#include <sys/dkio.h>
45#include <errno.h>
46#include <stdlib.h>
47#include <strings.h>
48#include <unistd.h>
49#include <stropts.h>
50#include <sys/scsi/generic/commands.h>
51#include <sys/scsi/impl/commands.h>
52#include <sys/scsi/impl/uscsi.h>
53#include "badsec.h"
54
55char    *devname;		/* name of device */
56int	devfd;			/* device file descriptor */
57struct	dk_geom		dkg;	/* geometry */
58struct  extvtoc	vtoc;		/* table of contents */
59char	*progname;
60
61extern	struct	badsec_lst *badsl_chain;
62extern	int	badsl_chain_cnt;
63extern	struct	badsec_lst *gbadsl_chain;
64extern	int	gbadsl_chain_cnt;
65extern	int	print_altsec(struct extpartition *);
66extern	int	updatebadsec(struct extpartition *, int);
67extern	void	wr_altsctr(void);
68
69int		alts_fd;
70
71static void giveusage(void);
72static void rd_gbad(FILE *badsecfd);
73static void add_gbad(int badsec_entry);
74static int try_hw_remap(void);
75static int hardware_remap(blkaddr_t);
76
77int
78main(int argc, char *argv[])
79{
80	extern int	optind;
81	extern char	*optarg;
82
83	static char	options[] = "Ipa:f:";
84	char		numbuf[100];
85	char		*nxtarg;
86	char		*alts_name;
87	minor_t 	minor_val;
88	struct stat 	statbuf;
89	struct extpartition	*part = NULL;
90	int		alts_slice = -1;
91	int		l;
92	int		p;
93	int		init_flag = 0;
94	int		print_flag = 0;
95	int 		c;
96	int 		i;
97	FILE		*badsecfd = NULL;
98
99	progname = argv[0];
100	while ((c = getopt(argc, argv, options)) != EOF) {
101		switch (c) {
102		case 'I':
103			init_flag = 1;
104			break;
105		case 'p':
106			print_flag = 1;
107			break;
108		case 'a':
109			nxtarg = optarg;
110			for (; *nxtarg != '\0'; )
111				add_gbad(strtol(nxtarg, &nxtarg, 0));
112			break;
113		case 'f':
114			if ((badsecfd = fopen(optarg, "r")) == NULL) {
115				(void) fprintf(stderr,
116				    "%s: unable to open %s file\n",
117				    progname, optarg);
118				exit(1);
119			}
120			break;
121		default:
122			giveusage();
123			exit(2);
124		}
125	}
126
127		/* get the last argument -- device stanza */
128	if (argc != optind+1) {
129		(void) fprintf(stderr, "Missing disk device name\n");
130		giveusage();
131		exit(3);
132	}
133	devname = argv[optind];
134
135	if (stat(devname, &statbuf)) {
136		(void) fprintf(stderr, "%s: invalid device %s, stat failed\n",
137		    progname, devname);
138		giveusage();
139		exit(4);
140	}
141	if ((statbuf.st_mode & S_IFMT) != S_IFCHR) {
142		(void) fprintf(stderr, "%s: device %s is not character"
143		    " special\n", progname, devname);
144		giveusage();
145		exit(5);
146	}
147	minor_val = minor(statbuf.st_rdev);
148	/*
149	 * NEED A DEFINE FOR THE PHYSICAL BIT (0x10)
150	 */
151	if ((minor_val & 0x10) == 0) {
152		(void) fprintf(stderr, "%s: device %s is not a physical"
153		    " slice\n", progname, devname);
154		giveusage();
155		exit(6);
156	}
157	if ((minor_val % V_NUMPAR) != 0) {
158		(void) fprintf(stderr, "%s: device %s is not a slice 0"
159		    " device\n", progname, devname);
160		giveusage();
161		exit(7);
162	}
163	if ((devfd = open(devname, O_RDWR)) == -1) {
164		(void) fprintf(stderr, "%s: open of %s failed\n",
165		    progname, devname);
166		perror("");
167		exit(8);
168	}
169	if ((ioctl(devfd, DKIOCGGEOM, &dkg)) == -1) {
170		(void) fprintf(stderr, "%s: unable to get disk geometry.\n",
171		    progname);
172		perror("");
173		exit(9);
174	}
175
176	if (ioctl(devfd, DKIOCGEXTVTOC, &vtoc) == -1) {
177		(void) fprintf(stderr, "%s: could not get VTOC.\n", progname);
178		giveusage();
179		exit(14);
180	}
181
182	if ((vtoc.v_sanity != VTOC_SANE) || (vtoc.v_version != V_VERSION)) {
183		(void) fprintf(stderr, "%s: invalid VTOC found.\n", progname);
184		giveusage();
185		exit(15);
186	}
187	if (badsecfd)
188		rd_gbad(badsecfd);
189
190#ifdef ADDBAD_DEBUG
191{
192	struct badsec_lst *blc_p;
193	printf("\n main: Total bad sectors found= %d\n", gbadsl_chain_cnt);
194	for (blc_p = gbadsl_chain; blc_p; blc_p = blc_p->bl_nxt) {
195		for (i = 0; i < blc_p->bl_cnt; i++)
196			printf(" badsec=%d ", blc_p->bl_sec[i]);
197	}
198	printf("\n");
199}
200#endif
201#ifdef PPP
202	/*
203	 * If init_flag is set, run to completion.
204	 */
205	if (gbadsl_chain_cnt == 0 && init_flag == 0)
206		/*
207		 * No defects and not initializing
208		 */
209		exit(0);
210#endif
211	if (gbadsl_chain_cnt != 0)
212	{
213		if (try_hw_remap() == SUCCESS)
214			exit(0);
215	}
216	/*
217	 * get ALTS slice
218	 */
219	for (i = 0; i < V_NUMPAR && alts_slice == -1; i++)
220	{
221		if (vtoc.v_part[i].p_tag == V_ALTSCTR)
222		{
223			alts_slice = i;
224			part = &vtoc.v_part[i];
225		}
226	}
227	if (alts_slice == -1)
228	{
229		(void) fprintf(stderr, "%s: No alternates slice.\n", progname);
230		exit(16);
231	}
232	l = strlen(devname);
233	(void) sprintf(numbuf, "%d", alts_slice);
234	p = strlen(numbuf);
235	alts_name = (char *)malloc(l + p);
236	(void) strcpy(alts_name, devname);
237	alts_name[l - 2] = 's';
238	(void) strcpy(&alts_name[l - 1], numbuf);
239	alts_name[l + p - 1] = '\0';
240	if ((alts_fd = open(alts_name, O_RDWR)) == -1) {
241		(void) fprintf(stderr, "%s: open of %s failed\n",
242		    progname, alts_name);
243		perror("");
244		exit(9);
245	}
246	if (print_flag)
247	{
248		(void) print_altsec(part);
249		exit(0);
250	}
251	(void) updatebadsec(part, init_flag);
252	wr_altsctr();
253
254	if (ioctl(devfd, DKIOCADDBAD, NULL) == -1) {
255		(void) fprintf(stderr,  "Warning: DKIOCADDBAD io control"
256		    " failed. System must be re-booted\n");
257		(void) fprintf(stderr, "for alternate sectors to be usable.\n");
258		exit(17);
259	}
260	sync();
261
262	(void) fclose(badsecfd);
263	(void) close(alts_fd);
264	(void) close(devfd);
265	return (0);
266}
267
268/*
269 * Giveusage ()
270 * Give a (not so) concise message on how to use this program.
271 */
272static void
273giveusage(void)
274{
275	(void) fprintf(stderr, "%s [-p] [-a sector] [-f filename]"
276	    " raw-device\n", progname);
277	(void) fprintf(stderr, "	p - Print existing bad block map\n");
278	(void) fprintf(stderr, "	a - Add the given sectors to the"
279	    " bad block list\n");
280	(void) fprintf(stderr, "	f - Add the sectors from <filename>"
281	    " to the bad block list\n");
282	if (devfd)
283		(void) close(devfd);
284}
285
286
287/*
288 *	read in the additional growing bad sectors
289 */
290static void
291rd_gbad(FILE *badsecfd)
292{
293	int	badsec_entry;
294	int	status;
295
296	status = fscanf(badsecfd, "%d", &badsec_entry);
297	while (status != EOF) {
298		add_gbad(badsec_entry);
299		status = fscanf(badsecfd, "%d", &badsec_entry);
300	}
301}
302
303static void
304add_gbad(int badsec_entry)
305{
306	struct badsec_lst *blc_p;
307
308	if (!gbadsl_chain) {
309		blc_p = (struct badsec_lst *)malloc(BADSLSZ);
310		if (!blc_p) {
311			(void) fprintf(stderr, "Unable to allocate memory"
312			    " for additional bad sectors\n");
313			exit(18);
314		}
315		gbadsl_chain = blc_p;
316		blc_p->bl_cnt = 0;
317		blc_p->bl_nxt = 0;
318	}
319	for (blc_p = gbadsl_chain; blc_p->bl_nxt; )
320		blc_p = blc_p->bl_nxt;
321
322	if (blc_p->bl_cnt == MAXBLENT) {
323		blc_p->bl_nxt = (struct badsec_lst *)malloc(BADSLSZ);
324		if (!blc_p->bl_nxt) {
325			(void) fprintf(stderr, "Unable to allocate memory"
326			    " for additional bad sectors\n");
327			exit(19);
328		}
329		blc_p = blc_p->bl_nxt;
330		blc_p->bl_cnt = 0;
331		blc_p->bl_nxt = 0;
332	}
333	blc_p->bl_sec[blc_p->bl_cnt++] = badsec_entry;
334	gbadsl_chain_cnt++;
335}
336
337/*
338 * Map a block using hardware (SCSI) techniques.
339 */
340/*ARGSUSED*/
341static int
342hardware_remap(bn)
343blkaddr_t	bn;
344{
345	uint_t		byte_swap_32(uint_t);
346	ushort_t	byte_swap_16(ushort_t);
347
348	struct uscsi_cmd		ucmd;
349	union scsi_cdb			cdb;
350	struct scsi_reassign_blk	defect_list;
351
352	/*
353	 * Build and execute the uscsi ioctl
354	 */
355	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
356	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
357	(void) memset((char *)&defect_list, 0,
358		sizeof (struct scsi_reassign_blk));
359	cdb.scc_cmd = SCMD_REASSIGN_BLOCK;
360	ucmd.uscsi_cdb = (caddr_t)&cdb;
361	ucmd.uscsi_cdblen = CDB_GROUP0;
362	ucmd.uscsi_bufaddr = (caddr_t)&defect_list;
363	ucmd.uscsi_buflen = sizeof (struct scsi_reassign_blk);
364	defect_list.length = byte_swap_16(sizeof (defect_list.defect));
365	defect_list.defect = byte_swap_32(bn);
366	/*
367	 * Set function flags for driver.
368	 */
369	ucmd.uscsi_flags = USCSI_ISOLATE | USCSI_DIAGNOSE | USCSI_SILENT;
370	ucmd.uscsi_timeout = 30;		/* 30 seconds */
371
372	/*
373	 * Execute the ioctl
374	 */
375	if (ioctl(devfd, USCSICMD, &ucmd) == -1)
376	{
377		if (errno != ENOTTY)
378		{
379			perror("SCSI hardware re-assign failed");
380			/*
381			 * It looks like a failure but by returning success
382			 * the upper layer will not try to do
383			 * software remapping.
384			 */
385			return (SUCCESS);
386		}
387		return (FAILURE);
388	}
389	return (SUCCESS);
390}
391
392uint_t
393byte_swap_32(uint_t nav)
394{
395	uint_t	rc;
396	rc = ((nav & 0xff000000) >> 24) | ((nav & 0x00ff0000) >> 8) |
397	    ((nav & 0x0000ff00) << 8)  | ((nav & 0x000000ff) << 24);
398	return (rc);
399}
400
401ushort_t
402byte_swap_16(ushort_t niv)
403{
404	ushort_t	rc;
405	rc = (ushort_t)((int)(niv & 0xff00) >> 8) | ((niv & 0x00ff) << 8);
406	return (rc);
407}
408
409static int
410try_hw_remap()
411{
412	struct badsec_lst *blc_p;
413	int	i;
414
415	for (blc_p = gbadsl_chain; blc_p != 0; blc_p = blc_p->bl_nxt) {
416		for (i = 0; i < blc_p->bl_cnt; i++)
417		if (hardware_remap(blc_p->bl_sec[i]) == FAILURE)
418			return (FAILURE);
419	}
420	return (SUCCESS);
421}
422