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
ata_rdwr(int dir,int fd,diskaddr_t blk64,int secnt,caddr_t bufaddr,int flags,int * xfercntp)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
ata_ck_format()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
get_alts_slice()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
put_alts_slice()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
ata_convert_list(struct defect_list * list,int list_format)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
ata_ex_cur(struct defect_list * list)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
ata_repair(diskaddr_t bn,int flag)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
ata_wr_cur(struct defect_list * list)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