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