xref: /illumos-gate/usr/src/cmd/format/ctlr_ata.c (revision f63f7506be0210195779706f51c58646e568cc40)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains the routines for the IDE drive interface
31  */
32 #include "global.h"
33 
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/ioctl.h>
37 #include <sys/uio.h>
38 #include <sys/fcntl.h>
39 #include <memory.h>
40 #include <malloc.h>
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sys/byteorder.h>
45 #include <errno.h>
46 #if defined(i386)
47 #include <sys/dktp/altsctr.h>
48 #endif
49 #include <sys/dktp/dadkio.h>
50 
51 
52 #include "startup.h"
53 #include "misc.h"
54 #include "ctlr_ata.h"
55 #include "analyze.h"
56 #include "param.h"
57 #include "io.h"
58 #include "badsec.h"
59 
60 #include "menu_fdisk.h"
61 
62 int	wr_altsctr();
63 int	read_altsctr();
64 int	updatebadsec();
65 
66 #ifdef  __STDC__
67 static int	ata_ck_format(void);
68 #ifdef i386
69 static int	ata_ex_cur(struct defect_list *);
70 static int	ata_wr_cur(struct defect_list *);
71 static int	ata_repair(int, int);
72 #endif /* i386 */
73 #else /* __STDC__ */
74 static int	ata_ck_format();
75 #ifdef i386
76 static int	ata_ex_cur();
77 static int	ata_wr_cur();
78 static int	ata_repair();
79 #endif /* i386 */
80 #endif
81 
82 struct  ctlr_ops ataops = {
83 #if defined(sparc)
84 	ata_rdwr,
85 	ata_ck_format,
86 	0,
87 	0,
88 	0,
89 	0,
90 	0,
91 	0,
92 #else
93 	ata_rdwr,
94 	ata_ck_format,
95 	0,
96 	0,
97 	ata_ex_cur,
98 	ata_repair,
99 	0,
100 	ata_wr_cur,
101 #endif	/* defined(sparc) */
102 };
103 
104 struct  ctlr_ops pcmcia_ataops = {
105 	ata_rdwr,
106 	ata_ck_format,
107 	0,
108 	0,
109 	0,
110 	0,
111 	0,
112 	0,
113 };
114 
115 
116 #if defined(i386)
117 static struct	partition	*dpart = NULL;
118 #endif	/* defined(i386) */
119 extern	struct	badsec_lst	*badsl_chain;
120 extern	int	badsl_chain_cnt;
121 extern	struct	badsec_lst	*gbadsl_chain;
122 extern	int	gbadsl_chain_cnt;
123 extern	struct	alts_mempart	*ap;
124 
125 static char *dadkrawioerrs[] = {
126 	"cmd was successful",		/* DADKIO_STAT_NO_ERROR */
127 	"device not ready",		/* DADKIO_STAT_NOT_READY */
128 	"error on medium blkno: %d",	/* DADKIO_STAT_MEDIUM_ERROR */
129 	"other hardware error",		/* DADKIO_STAT_HARDWARE_ERROR */
130 	"illegal request",		/* DADKIO_STAT_ILLEGAL_REQUEST */
131 	"illegal block address: %d",	/* DADKIO_STAT_ILLEGAL_ADDRESS */
132 	"device write-protected",	/* DADKIO_STAT_WRITE_PROTECTED	*/
133 	"no response from device",	/* DADKIO_STAT_TIMED_OUT */
134 	"parity error in data",		/* DADKIO_STAT_PARITY */
135 	"error on bus",			/* DADKIO_STAT_BUS_ERROR */
136 	"data recovered via ECC",	/* DADKIO_STAT_SOFT_ERROR */
137 	"no resources for cmd",		/* DADKIO_STAT_NO_RESOURCES */
138 	"device is not formatted",	/* DADKIO_STAT_NOT_FORMATTED */
139 	"device is reserved",		/* DADKIO_STAT_RESERVED */
140 	"feature not supported",	/* DADKIO_STAT_NOT_SUPPORTED */
141 	};
142 
143 /*ARGSUSED6*/
144 #if	defined(i386)
145 int
146 ata_rdwr(int dir, int fd, diskaddr_t blk64, int secnt, caddr_t bufaddr,
147 		int flags, int *xfercntp)
148 #else	/* defined(i386) */
149 static int
150 ata_rdwr(int dir, int fd, diskaddr_t blk64, int secnt, caddr_t bufaddr,
151 		int flags, int *xfercntp)
152 #endif	/* defined(i386) */
153 {
154 	int	tmpsec;
155 	struct dadkio_rwcmd	dadkio_rwcmd;
156 	daddr_t	blkno;
157 
158 	blkno = (daddr_t)blk64;
159 	bzero((caddr_t)&dadkio_rwcmd, sizeof (struct dadkio_rwcmd));
160 
161 	tmpsec = secnt * 512;
162 
163 	/* Doing raw read */
164 	dadkio_rwcmd.cmd = (dir == DIR_READ) ? DADKIO_RWCMD_READ :
165 					DADKIO_RWCMD_WRITE;
166 	dadkio_rwcmd.blkaddr = blkno;
167 	dadkio_rwcmd.buflen  = tmpsec;
168 	dadkio_rwcmd.flags   = flags;
169 	dadkio_rwcmd.bufaddr = bufaddr;
170 
171 	media_error = 0;
172 	if (cur_ctype->ctype_ctype == DKC_PCMCIA_ATA) {
173 		/*
174 		 * PCATA requires to use "p0" when calling
175 		 *	DIOCTL_RWCMD ioctl() to read/write the label
176 		 */
177 		(void) close(fd);
178 		(void) open_cur_file(FD_USE_P0_PATH);
179 		fd = cur_file;
180 	}
181 
182 	if (ioctl(fd, DIOCTL_RWCMD, &dadkio_rwcmd) == -1) {
183 		err_print("DIOCTL_RWCMD: %s\n", strerror(errno));
184 		return (1);
185 	}
186 
187 	if (cur_ctype->ctype_ctype == DKC_PCMCIA_ATA) {
188 		/* Restore cur_file with cur_disk->disk_path */
189 		(void) open_cur_file(FD_USE_CUR_DISK_PATH);
190 	}
191 
192 	switch (dadkio_rwcmd.status.status) {
193 	case  DADKIO_STAT_NOT_READY:
194 			disk_error = DISK_STAT_NOTREADY;
195 			break;
196 	case  DADKIO_STAT_RESERVED:
197 			disk_error = DISK_STAT_RESERVED;
198 			break;
199 	case  DADKIO_STAT_WRITE_PROTECTED:
200 			disk_error = DISK_STAT_DATA_PROTECT;
201 			break;
202 	case DADKIO_STAT_MEDIUM_ERROR:
203 			media_error = 1;
204 			break;
205 	}
206 
207 	if (dadkio_rwcmd.status.status) {
208 		if ((flags & F_SILENT) == 0)
209 			err_print(dadkrawioerrs[dadkio_rwcmd.status.status],
210 				dadkio_rwcmd.status.failed_blk);
211 		return (1);
212 	}
213 	return (0);
214 }
215 
216 int
217 ata_ck_format()
218 {
219 	unsigned char bufaddr[2048];
220 	int status;
221 
222 	status = ata_rdwr(DIR_READ, cur_file, 1, 4, (caddr_t)bufaddr, 0, NULL);
223 
224 	return (!status);
225 }
226 
227 
228 #if defined(i386)
229 
230 static int
231 get_alts_slice()
232 {
233 
234 	int	i;
235 	int	alts_slice = -1;
236 
237 	if (cur_parts == NULL) {
238 		(void) fprintf(stderr, "No current partition list\n");
239 		return (-1);
240 	}
241 
242 	for (i = 0; i < V_NUMPAR && alts_slice == -1; i++) {
243 		if (cur_parts->vtoc.v_part[i].p_tag == V_ALTSCTR) {
244 			alts_slice = i;
245 			dpart = (struct partition *)&cur_parts->vtoc.v_part[i];
246 		}
247 	}
248 
249 	if (alts_slice == -1) {
250 		(void) fprintf(stderr, "NO Alt slice\n");
251 		return (-1);
252 	}
253 	if (!solaris_offset)
254 		if (copy_solaris_part(&cur_disk->fdisk_part))
255 			return (-1);
256 
257 	altsec_offset = dpart->p_start + solaris_offset;
258 
259 	return (SUCCESS);
260 }
261 
262 
263 static int
264 put_alts_slice()
265 {
266 	int	status;
267 
268 	status = wr_altsctr();
269 	if (status) {
270 		return (status);
271 	}
272 
273 	if (ioctl(cur_file, DKIOCADDBAD, NULL) == -1) {
274 		(void) fprintf(stderr, "Warning: DKIOCADDBAD ioctl failed\n");
275 		sync();
276 		return (-1);
277 	}
278 	sync();
279 	return (0);
280 }
281 
282 static int
283 ata_convert_list(struct defect_list *list, int list_format)
284 {
285 
286 	int	i;
287 	struct  defect_entry    *new_defect;
288 
289 	switch (list_format) {
290 
291 	case BFI_FORMAT:
292 		if (ap->ap_tblp->alts_ent_used) {
293 			new_defect = calloc(ap->ap_tblp->alts_ent_used,
294 			    sizeof (struct defect_entry));
295 			if (new_defect == NULL) {
296 				err_print(
297 				    "ata_convert_list: calloc failed\n");
298 				fullabort();
299 			}
300 			list->header.count = ap->ap_tblp->alts_ent_used;
301 			list->header.magicno = (uint_t)DEFECT_MAGIC;
302 			list->list = new_defect;
303 			for (i = 0; i < ap->ap_tblp->alts_ent_used;
304 					    i++, new_defect++) {
305 				new_defect->cyl =
306 					    bn2c((ap->ap_entp)[i].bad_start);
307 				new_defect->head =
308 					    bn2h((ap->ap_entp)[i].bad_start);
309 				new_defect->bfi = UNKNOWN;
310 				new_defect->sect =
311 					    bn2s((ap->ap_entp)[i].bad_start);
312 				new_defect->nbits = UNKNOWN;
313 			}
314 
315 
316 		} else {
317 
318 			list->header.count = 0;
319 			list->header.magicno = (uint_t)DEFECT_MAGIC;
320 			new_defect = calloc(1,
321 			    sizeof (struct defect_entry));
322 			if (new_defect == NULL) {
323 				err_print(
324 				    "ata_convert_list: calloc failed\n");
325 				fullabort();
326 			}
327 			list->list = new_defect;
328 		}
329 		break;
330 
331 	default:
332 		err_print("ata_convert_list: can't deal with it\n");
333 		exit(0);
334 	}
335 	(void) checkdefsum(list, CK_MAKESUM);
336 	return (0);
337 }
338 
339 
340 /*
341  * NB - there used to be a ata_ex_man() which was identical to
342  * ata_ex_cur; since it's really not a "manufacturer's list",
343  * it's gone; if we ever want that exact functionality back,
344  * we can add ata_ex_cur() to the ctlr_ops above.  Otherwise,
345  * if this is ever modified to support formatting of IDE drives,
346  * we should probably add something that issues the
347  * drive Read Defect list rather than getting the s9 info
348  * as ata_ex_cur() does.
349  */
350 
351 static int
352 ata_ex_cur(struct defect_list *list)
353 {
354 	int	status;
355 
356 	status = get_alts_slice();
357 	if (status)
358 		return (status);
359 	status = read_altsctr(dpart);
360 	if (status) {
361 		return (status);
362 	}
363 	(void) ata_convert_list(list, BFI_FORMAT);
364 	return (status);
365 }
366 
367 int
368 ata_repair(int bn, int flag)
369 {
370 
371 	int	status;
372 	struct	badsec_lst	*blc_p;
373 	struct	badsec_lst	*blc_p_nxt;
374 
375 #ifdef lint
376 	flag++;
377 #endif
378 
379 	(void) get_alts_slice();
380 	if (!gbadsl_chain) {
381 		blc_p = (struct badsec_lst *)calloc(1, BADSLSZ);
382 		if (!blc_p) {
383 			(void) fprintf(stderr,
384 		"Unable to allocate memory for additional bad sectors\n");
385 			return (-1);
386 		}
387 		gbadsl_chain = blc_p;
388 	}
389 	for (blc_p = gbadsl_chain; blc_p->bl_nxt; )
390 		blc_p = blc_p->bl_nxt;
391 
392 	if (blc_p->bl_cnt == MAXBLENT) {
393 		blc_p->bl_nxt = (struct badsec_lst *)calloc(1, BADSLSZ);
394 		if (!blc_p->bl_nxt) {
395 			(void) fprintf(stderr,
396 		"Unable to allocate memory for additional bad sectors\n");
397 			return (-1);
398 		}
399 		blc_p = blc_p->bl_nxt;
400 	}
401 	blc_p->bl_sec[blc_p->bl_cnt++] = bn;
402 	gbadsl_chain_cnt++;
403 
404 	(void) updatebadsec(dpart, 0);
405 	status = put_alts_slice();
406 
407 	/* clear out the bad sector list chains that were generated */
408 
409 	if (badsl_chain) {
410 		if (badsl_chain->bl_nxt == NULL) {
411 			free(badsl_chain);
412 		} else {
413 			for (blc_p = badsl_chain; blc_p; ) {
414 				blc_p_nxt = blc_p->bl_nxt;
415 				free(blc_p);
416 				blc_p = blc_p_nxt;
417 			}
418 		}
419 		badsl_chain = NULL;
420 		badsl_chain_cnt = 0;
421 	}
422 
423 	if (gbadsl_chain) {
424 		if (gbadsl_chain->bl_nxt == NULL) {
425 			free(gbadsl_chain);
426 		} else {
427 			for (blc_p = gbadsl_chain; blc_p; ) {
428 				blc_p_nxt = blc_p->bl_nxt;
429 				free(blc_p);
430 				blc_p = blc_p_nxt;
431 			}
432 		}
433 		gbadsl_chain = NULL;
434 		gbadsl_chain_cnt = 0;
435 	}
436 
437 	return (status);
438 
439 }
440 
441 int
442 ata_wr_cur(struct defect_list *list)
443 {
444 	int	status;
445 	int	sec_count;
446 	int	x;
447 	struct	badsec_lst	*blc_p;
448 	struct	badsec_lst	*blc_p_nxt;
449 	struct	defect_entry	*dlist;
450 
451 	if (list->header.magicno != (uint_t)DEFECT_MAGIC)
452 		return (-1);
453 
454 	sec_count = list->header.count;
455 	dlist = list->list;
456 
457 	(void) get_alts_slice();
458 	for (x = 0; x < sec_count; x++) {
459 
460 		/* test for unsupported list format */
461 		if ((dlist->bfi != UNKNOWN) || (dlist->nbits != UNKNOWN)) {
462 			(void) fprintf(stderr,
463 				"BFI unsuported format for bad sectors\n");
464 			return (-1);
465 		}
466 
467 		if (!gbadsl_chain) {
468 			blc_p = (struct badsec_lst *)calloc(1, BADSLSZ);
469 			if (!blc_p) {
470 				(void) fprintf(stderr,
471 		"Unable to allocate memory for additional bad sectors\n");
472 				return (-1);
473 			}
474 			gbadsl_chain = blc_p;
475 		}
476 
477 		for (blc_p = gbadsl_chain; blc_p->bl_nxt; )
478 			blc_p = blc_p->bl_nxt;
479 
480 		if (blc_p->bl_cnt == MAXBLENT) {
481 			blc_p->bl_nxt = (struct badsec_lst *)calloc(1, BADSLSZ);
482 			if (!blc_p->bl_nxt) {
483 				(void) fprintf(stderr,
484 		"Unable to allocate memory for additional bad sectors\n");
485 				return (-1);
486 			}
487 			blc_p = blc_p->bl_nxt;
488 		}
489 		blc_p->bl_sec[blc_p->bl_cnt++] =
490 			    chs2bn(dlist->cyl, dlist->head, dlist->sect);
491 		gbadsl_chain_cnt++;
492 		dlist++;
493 	}
494 
495 
496 	(void) updatebadsec(dpart, 0);
497 	status = put_alts_slice();
498 
499 	/* clear out the bad sector list chains that were generated */
500 
501 	if (badsl_chain) {
502 		if (badsl_chain->bl_nxt == NULL) {
503 			free(badsl_chain);
504 		} else {
505 			for (blc_p = badsl_chain; blc_p; ) {
506 				blc_p_nxt = blc_p->bl_nxt;
507 				free(blc_p);
508 				blc_p = blc_p_nxt;
509 			}
510 		}
511 		badsl_chain = NULL;
512 		badsl_chain_cnt = 0;
513 	}
514 
515 	if (gbadsl_chain) {
516 		if (gbadsl_chain->bl_nxt == NULL) {
517 			free(gbadsl_chain);
518 		} else {
519 			for (blc_p = gbadsl_chain; blc_p; ) {
520 				blc_p_nxt = blc_p->bl_nxt;
521 				free(blc_p);
522 				blc_p = blc_p_nxt;
523 			}
524 		}
525 		gbadsl_chain = NULL;
526 		gbadsl_chain_cnt = 0;
527 	}
528 
529 	return (status);
530 }
531 
532 #endif	/*  defined(i386)  */
533