xref: /illumos-gate/usr/src/cmd/format/ctlr_ata.c (revision 27954b0d964ffcb749cf19296906e7fecdf3da1b)
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 2009 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 * cur_blksz;
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 	char *bufaddr;
217 	int status;
218 
219 	bufaddr = (char *)zalloc(4 * cur_blksz);
220 	status = ata_rdwr(DIR_READ, cur_file, (diskaddr_t)1, 4,
221 	    (caddr_t)bufaddr, 0, NULL);
222 
223 	free(bufaddr);
224 
225 	return (!status);
226 }
227 
228 
229 #if defined(i386)
230 
231 static int
232 get_alts_slice()
233 {
234 
235 	int	i;
236 	int	alts_slice = -1;
237 
238 	if (cur_parts == NULL) {
239 		(void) fprintf(stderr, "No current partition list\n");
240 		return (-1);
241 	}
242 
243 	for (i = 0; i < V_NUMPAR && alts_slice == -1; i++) {
244 		if (cur_parts->vtoc.v_part[i].p_tag == V_ALTSCTR) {
245 			alts_slice = i;
246 			dpart = &cur_parts->vtoc.v_part[i];
247 		}
248 	}
249 
250 	if (alts_slice == -1) {
251 		(void) fprintf(stderr, "NO Alt slice\n");
252 		return (-1);
253 	}
254 	if (!solaris_offset)
255 		if (copy_solaris_part(&cur_disk->fdisk_part))
256 			return (-1);
257 
258 	altsec_offset = dpart->p_start + solaris_offset;
259 
260 	return (SUCCESS);
261 }
262 
263 
264 static int
265 put_alts_slice()
266 {
267 	int	status;
268 
269 	status = wr_altsctr();
270 	if (status) {
271 		return (status);
272 	}
273 
274 	if (ioctl(cur_file, DKIOCADDBAD, NULL) == -1) {
275 		(void) fprintf(stderr, "Warning: DKIOCADDBAD ioctl failed\n");
276 		sync();
277 		return (-1);
278 	}
279 	sync();
280 	return (0);
281 }
282 
283 static int
284 ata_convert_list(struct defect_list *list, int list_format)
285 {
286 
287 	int	i;
288 	struct  defect_entry    *new_defect;
289 
290 	switch (list_format) {
291 
292 	case BFI_FORMAT:
293 		if (ap->ap_tblp->alts_ent_used) {
294 			new_defect = calloc(ap->ap_tblp->alts_ent_used,
295 			    sizeof (struct defect_entry));
296 			if (new_defect == NULL) {
297 				err_print(
298 				    "ata_convert_list: calloc failed\n");
299 				fullabort();
300 			}
301 			list->header.count = ap->ap_tblp->alts_ent_used;
302 			list->header.magicno = (uint_t)DEFECT_MAGIC;
303 			list->list = new_defect;
304 			for (i = 0; i < ap->ap_tblp->alts_ent_used;
305 			    i++, new_defect++) {
306 				new_defect->cyl =
307 				    bn2c((ap->ap_entp)[i].bad_start);
308 				new_defect->head =
309 				    bn2h((ap->ap_entp)[i].bad_start);
310 				new_defect->bfi = UNKNOWN;
311 				new_defect->sect =
312 				    bn2s((ap->ap_entp)[i].bad_start);
313 				new_defect->nbits = UNKNOWN;
314 			}
315 
316 
317 		} else {
318 
319 			list->header.count = 0;
320 			list->header.magicno = (uint_t)DEFECT_MAGIC;
321 			new_defect = calloc(1,
322 			    sizeof (struct defect_entry));
323 			if (new_defect == NULL) {
324 				err_print(
325 				    "ata_convert_list: calloc failed\n");
326 				fullabort();
327 			}
328 			list->list = new_defect;
329 		}
330 		break;
331 
332 	default:
333 		err_print("ata_convert_list: can't deal with it\n");
334 		exit(0);
335 	}
336 	(void) checkdefsum(list, CK_MAKESUM);
337 	return (0);
338 }
339 
340 
341 /*
342  * NB - there used to be a ata_ex_man() which was identical to
343  * ata_ex_cur; since it's really not a "manufacturer's list",
344  * it's gone; if we ever want that exact functionality back,
345  * we can add ata_ex_cur() to the ctlr_ops above.  Otherwise,
346  * if this is ever modified to support formatting of IDE drives,
347  * we should probably add something that issues the
348  * drive Read Defect list rather than getting the s9 info
349  * as ata_ex_cur() does.
350  */
351 
352 static int
353 ata_ex_cur(struct defect_list *list)
354 {
355 	int	status;
356 
357 	status = get_alts_slice();
358 	if (status)
359 		return (status);
360 	status = read_altsctr(dpart);
361 	if (status) {
362 		return (status);
363 	}
364 	(void) ata_convert_list(list, BFI_FORMAT);
365 	return (status);
366 }
367 
368 int
369 ata_repair(diskaddr_t bn, int flag)
370 {
371 
372 	int	status;
373 	struct	badsec_lst	*blc_p;
374 	struct	badsec_lst	*blc_p_nxt;
375 
376 #ifdef lint
377 	flag++;
378 #endif
379 
380 	(void) get_alts_slice();
381 	if (!gbadsl_chain) {
382 		blc_p = (struct badsec_lst *)calloc(1, BADSLSZ);
383 		if (!blc_p) {
384 			(void) fprintf(stderr,
385 		"Unable to allocate memory for additional bad sectors\n");
386 			return (-1);
387 		}
388 		gbadsl_chain = blc_p;
389 	}
390 	for (blc_p = gbadsl_chain; blc_p->bl_nxt; )
391 		blc_p = blc_p->bl_nxt;
392 
393 	if (blc_p->bl_cnt == MAXBLENT) {
394 		blc_p->bl_nxt = (struct badsec_lst *)calloc(1, BADSLSZ);
395 		if (!blc_p->bl_nxt) {
396 			(void) fprintf(stderr,
397 		"Unable to allocate memory for additional bad sectors\n");
398 			return (-1);
399 		}
400 		blc_p = blc_p->bl_nxt;
401 	}
402 	blc_p->bl_sec[blc_p->bl_cnt++] = (uint_t)bn;
403 	gbadsl_chain_cnt++;
404 
405 	(void) updatebadsec(dpart, 0);
406 	status = put_alts_slice();
407 
408 	/* clear out the bad sector list chains that were generated */
409 
410 	if (badsl_chain) {
411 		if (badsl_chain->bl_nxt == NULL) {
412 			free(badsl_chain);
413 		} else {
414 			for (blc_p = badsl_chain; blc_p; ) {
415 				blc_p_nxt = blc_p->bl_nxt;
416 				free(blc_p);
417 				blc_p = blc_p_nxt;
418 			}
419 		}
420 		badsl_chain = NULL;
421 		badsl_chain_cnt = 0;
422 	}
423 
424 	if (gbadsl_chain) {
425 		if (gbadsl_chain->bl_nxt == NULL) {
426 			free(gbadsl_chain);
427 		} else {
428 			for (blc_p = gbadsl_chain; blc_p; ) {
429 				blc_p_nxt = blc_p->bl_nxt;
430 				free(blc_p);
431 				blc_p = blc_p_nxt;
432 			}
433 		}
434 		gbadsl_chain = NULL;
435 		gbadsl_chain_cnt = 0;
436 	}
437 
438 	return (status);
439 
440 }
441 
442 int
443 ata_wr_cur(struct defect_list *list)
444 {
445 	int	status;
446 	int	sec_count;
447 	int	x;
448 	struct	badsec_lst	*blc_p;
449 	struct	badsec_lst	*blc_p_nxt;
450 	struct	defect_entry	*dlist;
451 
452 	if (list->header.magicno != (uint_t)DEFECT_MAGIC)
453 		return (-1);
454 
455 	sec_count = list->header.count;
456 	dlist = list->list;
457 
458 	(void) get_alts_slice();
459 	for (x = 0; x < sec_count; x++) {
460 
461 		/* test for unsupported list format */
462 		if ((dlist->bfi != UNKNOWN) || (dlist->nbits != UNKNOWN)) {
463 			(void) fprintf(stderr,
464 			    "BFI unsuported format for bad sectors\n");
465 			return (-1);
466 		}
467 
468 		if (!gbadsl_chain) {
469 			blc_p = (struct badsec_lst *)calloc(1, BADSLSZ);
470 			if (!blc_p) {
471 				(void) fprintf(stderr,
472 		"Unable to allocate memory for additional bad sectors\n");
473 				return (-1);
474 			}
475 			gbadsl_chain = blc_p;
476 		}
477 
478 		for (blc_p = gbadsl_chain; blc_p->bl_nxt; )
479 			blc_p = blc_p->bl_nxt;
480 
481 		if (blc_p->bl_cnt == MAXBLENT) {
482 			blc_p->bl_nxt = (struct badsec_lst *)calloc(1, BADSLSZ);
483 			if (!blc_p->bl_nxt) {
484 				(void) fprintf(stderr,
485 		"Unable to allocate memory for additional bad sectors\n");
486 				return (-1);
487 			}
488 			blc_p = blc_p->bl_nxt;
489 		}
490 		blc_p->bl_sec[blc_p->bl_cnt++] =
491 		    (uint_t)chs2bn(dlist->cyl, dlist->head, dlist->sect);
492 		gbadsl_chain_cnt++;
493 		dlist++;
494 	}
495 
496 
497 	(void) updatebadsec(dpart, 0);
498 	status = put_alts_slice();
499 
500 	/* clear out the bad sector list chains that were generated */
501 
502 	if (badsl_chain) {
503 		if (badsl_chain->bl_nxt == NULL) {
504 			free(badsl_chain);
505 		} else {
506 			for (blc_p = badsl_chain; blc_p; ) {
507 				blc_p_nxt = blc_p->bl_nxt;
508 				free(blc_p);
509 				blc_p = blc_p_nxt;
510 			}
511 		}
512 		badsl_chain = NULL;
513 		badsl_chain_cnt = 0;
514 	}
515 
516 	if (gbadsl_chain) {
517 		if (gbadsl_chain->bl_nxt == NULL) {
518 			free(gbadsl_chain);
519 		} else {
520 			for (blc_p = gbadsl_chain; blc_p; ) {
521 				blc_p_nxt = blc_p->bl_nxt;
522 				free(blc_p);
523 				blc_p = blc_p_nxt;
524 			}
525 		}
526 		gbadsl_chain = NULL;
527 		gbadsl_chain_cnt = 0;
528 	}
529 
530 	return (status);
531 }
532 
533 #endif	/*  defined(i386)  */
534