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