xref: /illumos-gate/usr/src/cmd/format/defect.c (revision 65908c77)
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 routines that manipulate the defect list.
28  */
29 #include "global.h"
30 #include <sys/types.h>
31 #include <sys/param.h>
32 
33 #if defined(sparc)
34 #include <sys/hdio.h>
35 #endif		/* defined(sparc) */
36 
37 #include <sys/buf.h>
38 #include <sys/ioctl.h>
39 #include <sys/uio.h>
40 #include <sys/fcntl.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <memory.h>
44 
45 #if defined(sparc)
46 #include <sys/dkbad.h>
47 #endif		/* defined(sparc) */
48 
49 #include "misc.h"
50 #include "param.h"
51 
52 
53 #if defined(sparc)
54 /*
55  * This structure is the bad block table for the current disk if
56  * the disk uses bad-144 defect mapping.
57  */
58 struct	dkbad badmap;
59 #endif		/* defined(sparc) */
60 
61 /*
62  * This routine reads the defect list off the disk.  It also reads in the
63  * bad block table if the disk is a BAD144 type.  The defect list is
64  * located on the first 2 tracks of the 2nd alternate cylinder of all
65  * disks.  The bad block map is located on the first 5 even sectors of
66  * the last track of the last cylinder.
67  */
68 void
read_list(struct defect_list * list)69 read_list(struct defect_list *list)
70 {
71 	int	size, head;
72 
73 #if defined(sparc)
74 	int sec, status;
75 	struct	bt_bad *bt;
76 #endif	/* defined(sparc) */
77 
78 	assert(!EMBEDDED_SCSI);
79 
80 	/*
81 	 * This flags has been introduced only for Sparc ATA IDE.
82 	 * This indicates that no list manipulation is done in this controller
83 	 * and hence return without any other checking.
84 	 */
85 	if (cur_ctype->ctype_flags & CF_NOWLIST) {
86 		return;
87 	}
88 
89 	/*
90 	 * Panther's working list is maintained by the controller
91 	 */
92 	if (cur_ctype->ctype_flags & CF_WLIST) {
93 		if (*cur_ops->op_ex_cur != NULL &&
94 		    ((*cur_ops->op_ex_cur)(list)) == 0) {
95 			if (list->header.magicno != DEFECT_MAGIC) {
96 				fmt_print("Defect list BAD\n");
97 			} else {
98 				fmt_print("Controller working list found\n");
99 			}
100 			return;
101 		}
102 
103 		if (*cur_ops->op_ex_man != NULL &&
104 		    ((*cur_ops->op_ex_man)(list)) == 0) {
105 			if (list->header.magicno != DEFECT_MAGIC) {
106 				fmt_print("Defect list BAD\n");
107 			} else {
108 				fmt_print("MANUFACTURER's list found\n");
109 			}
110 			return;
111 		}
112 		fmt_print("No defect list found\n");
113 		return;
114 	}
115 
116 	/*
117 	 * Loop for each copy of the defect list until we get a good one.
118 	 */
119 	for (head = 0; head < LISTCOUNT; head++) {
120 		/*
121 		 * Try to read the list header.
122 		 */
123 		if ((*cur_ops->op_rdwr)(DIR_READ, cur_file,
124 		    (diskaddr_t)chs2bn(ncyl + 1, head, 0), 1,
125 		    (char *)&list->header, NULL), F_NORMAL)
126 			continue;
127 		/*
128 		 * If the magic number is wrong, this copy is corrupt.
129 		 */
130 		if (list->header.magicno != DEFECT_MAGIC)
131 			continue;
132 		/*
133 		 * Allocate space for the rest of the list.
134 		 */
135 		size = deflist_size(cur_blksz, list->header.count);
136 		list->list = (struct defect_entry *)zalloc(size * cur_blksz);
137 		/*
138 		 * Try to read in the rest of the list. If there is an
139 		 * error, or the checksum is wrong, this copy is corrupt.
140 		 */
141 		if ((*cur_ops->op_rdwr)(DIR_READ, cur_file,
142 		    (diskaddr_t)chs2bn(ncyl + 1, head, 1), size,
143 		    (char *)list->list, F_NORMAL, NULL) ||
144 		    checkdefsum(list, CK_CHECKSUM)) {
145 			/*
146 			 * Destroy the list and go on.
147 			 */
148 			kill_deflist(list);
149 			continue;
150 		}
151 		/*
152 		 * Got a good copy, stop searching.
153 		 */
154 		break;
155 	}
156 #if defined(sparc)
157 	if (!(cur_ctlr->ctlr_flags & DKI_BAD144))
158 		return;
159 	/*
160 	 * The disk uses BAD144, read in the bad-block table.
161 	 */
162 	for (sec = 0; ((sec < BAD_LISTCNT * 2) && (sec < nsect)); sec += 2) {
163 		status = (*cur_ops->op_rdwr)(DIR_READ, cur_file,
164 		    (diskaddr_t)chs2bn(ncyl + acyl - 1, nhead - 1, sec), 1,
165 		    &badmap, F_NORMAL, NULL);
166 		if (status)
167 			continue;
168 		/*
169 		 * Do a sanity check on the list read in.  If it passes,
170 		 * stop searching.
171 		 */
172 		if (badmap.bt_mbz != 0)
173 			continue;
174 		for (bt = badmap.bt_bad; bt - badmap.bt_bad < NDKBAD; bt++) {
175 			if (bt->bt_cyl < 0)
176 				break;
177 			if (bt->bt_trksec < 0)
178 				continue;
179 			head = bt->bt_trksec >> 8;
180 			if ((bt->bt_cyl >= pcyl) || (head >= nhead) ||
181 			    ((bt->bt_trksec & 0xff) >= sectors(head))) {
182 				status = -1;
183 				break;
184 			}
185 		}
186 		if (status)
187 			continue;
188 		return;
189 	}
190 	/*
191 	 * If we couldn't find the bad block table, initialize it to
192 	 * zero entries.
193 	 */
194 	for (bt = badmap.bt_bad; bt - badmap.bt_bad < NDKBAD; bt++)
195 		bt->bt_cyl = bt->bt_trksec = -1;
196 	badmap.bt_mbz = badmap.bt_csn = badmap.bt_flag = 0;
197 #endif		/* defined(sparc) */
198 }
199 
200 /*
201  * This routine either checks or calculates the checksum for a defect
202  * list, depending on the mode parameter. In check mode, it returns
203  * whether or not the checksum is correct.
204  */
205 int
checkdefsum(struct defect_list * list,int mode)206 checkdefsum(struct defect_list *list, int mode)
207 {
208 	register int *lp, i, sum = 0;
209 
210 	/*
211 	 * Perform the rolling xor to get what the checksum should be.
212 	 */
213 	lp = (int *)list->list;
214 	for (i = 0; i < (list->header.count *
215 	    sizeof (struct defect_entry) / sizeof (int)); i++)
216 		sum ^= *(lp + i);
217 	/*
218 	 * If in check mode, return whether header checksum was correct.
219 	 */
220 	if (mode == CK_CHECKSUM)
221 		return (sum != list->header.cksum);
222 	/*
223 	 * If in create mode, set the header checksum.
224 	 */
225 	else {
226 		list->header.cksum = sum;
227 		return (0);
228 	}
229 }
230 
231 /*
232  * This routine prints a single defect to stdout in a readable format.
233  */
234 void
pr_defect(struct defect_entry * def,int num)235 pr_defect(struct defect_entry *def, int num)
236 {
237 
238 	/*
239 	 * Make defect numbering look 1 relative.
240 	 */
241 	++num;
242 	/*
243 	 * Print out common values.
244 	 */
245 	fmt_print("%4d%8d%7d", num, def->cyl, def->head);
246 	/*
247 	 * The rest of the values may be unknown. If they are, just
248 	 * print blanks instead.  Also, only print length only if bfi is
249 	 * known, and assume that a known bfi implies an unknown sect.
250 	 */
251 	if (def->bfi != UNKNOWN) {
252 		fmt_print("%8d", def->bfi);
253 		if (def->nbits != UNKNOWN)
254 			fmt_print("%8d", def->nbits);
255 	} else {
256 		fmt_print("                ");
257 		fmt_print("%8d", def->sect);
258 		fmt_print("%8llu", chs2bn(def->cyl, def->head, def->sect));
259 	}
260 	fmt_print("\n");
261 }
262 
263 /*
264  * This routine calculates where in a defect list a given defect should
265  * be sorted. It returns the index that the defect should become.  The
266  * algorithm used sorts all bfi based defects by cylinder/head/bfi, and
267  * adds all logical sector defects to the end of the list.  This is
268  * necessary because the ordering of logical sector defects is significant
269  * when sector slipping is employed.
270  */
271 int
sort_defect(struct defect_entry * def,struct defect_list * list)272 sort_defect(struct defect_entry *def, struct defect_list *list)
273 {
274 	struct	defect_entry *ptr;
275 
276 	/*
277 	 * If it's a logical sector defect, return the entry at the end
278 	 * of the list.
279 	 */
280 	if (def->bfi == UNKNOWN)
281 		return (list->header.count);
282 	/*
283 	 * It's a bfi defect.  Loop through the defect list.
284 	 */
285 	for (ptr = list->list; ptr - list->list < list->header.count; ptr++) {
286 		/*
287 		 * If we get to a logical sector defect, put this defect
288 		 * right before it.
289 		 */
290 		if (ptr->bfi == UNKNOWN)
291 			goto found;
292 		/*
293 		 * If we get to a defect that is past this one in
294 		 * cylinder/head/bfi, put this defect right before it.
295 		 */
296 		if (def->cyl < ptr->cyl)
297 			goto found;
298 		if (def->cyl != ptr->cyl)
299 			continue;
300 		if (def->head < ptr->head)
301 			goto found;
302 		if (def->head != ptr->head)
303 			continue;
304 		if (def->bfi < ptr->bfi)
305 			goto found;
306 	}
307 found:
308 	/*
309 	 * Return the index to put the defect at.
310 	 */
311 	return (ptr - list->list);
312 }
313 
314 /*
315  * This routine writes the defect list on the back on the disk.  It also
316  * writes the bad block table to disk if bad-144 mapping applies to the
317  * current disk.
318  */
319 void
write_deflist(struct defect_list * list)320 write_deflist(struct defect_list *list)
321 {
322 	int	size, head, status;
323 
324 #if defined(sparc)
325 	int sec;
326 	caddr_t	bad_ptr = (caddr_t)&badmap;
327 #endif			/* defined(sparc) */
328 
329 	assert(!EMBEDDED_SCSI);
330 
331 	/*
332 	 * Sparc ATA IDE.
333 	 * This indicates that no list manipulation is done in this controller
334 	 * and hence return without any other checking.
335 	 */
336 	if (cur_ctype->ctype_flags & CF_NOWLIST) {
337 		return;
338 	}
339 
340 	/*
341 	 * Panther's working list is maintained by the controller
342 	 */
343 	if (cur_ctype->ctype_flags & CF_WLIST) {
344 		(*cur_ops->op_wr_cur)(list);
345 		return;
346 	}
347 
348 	/*
349 	 * If the list is null, there is nothing to write.
350 	 */
351 	if (list->list != NULL) {
352 		/*
353 		 * calculate how many sectors the defect list will occupy.
354 		 */
355 		size = deflist_size(cur_blksz, list->header.count);
356 		/*
357 		 * Loop for each copy of the list to be written.  Write
358 		 * out the header of the list followed by the data.
359 		 */
360 		for (head = 0; head < LISTCOUNT; head++) {
361 			status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file,
362 			    (diskaddr_t)chs2bn(ncyl + 1, head, 0), 1,
363 			    (char *)&list->header, F_NORMAL, NULL);
364 			if (status) {
365 				err_print(
366 "Warning: error saving defect list.\n");
367 				continue;
368 			}
369 			status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file,
370 			    (diskaddr_t)chs2bn(ncyl + 1, head, 1), size,
371 			    (char *)list->list, F_NORMAL, NULL);
372 			if (status)
373 				err_print(
374 "Warning: error saving defect list.\n");
375 		}
376 	}
377 	if (!(cur_ctlr->ctlr_flags & DKI_BAD144))
378 		return;
379 #if defined(sparc)
380 	/*
381 	 * Current disk uses bad-144 mapping.  Loop for each copy of the
382 	 * bad block table to be written and write it out.
383 	 */
384 	for (sec = 0; ((sec < BAD_LISTCNT * 2) && (sec < nsect)); sec += 2) {
385 		status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file,
386 		    (diskaddr_t)chs2bn(ncyl + acyl - 1, nhead - 1, sec), 1,
387 		    &badmap, F_NORMAL, NULL);
388 		if (status) {
389 			err_print(
390 "Warning: error saving bad block map table.\n");
391 			continue;
392 		}
393 	}
394 	/*
395 	 * Execute an ioctl to tell unix about the new bad block table.
396 	 */
397 	if (ioctl(cur_file, HDKIOCSBAD, &bad_ptr))
398 		err_print(
399 "Warning: error telling SunOS bad block map table.\n");
400 #endif		/* defined(sparc) */
401 }
402 
403 /*
404  * This routine adds a logical sector to the given defect list.
405  */
406 void
add_ldef(diskaddr_t blkno,struct defect_list * list)407 add_ldef(diskaddr_t blkno, struct defect_list *list)
408 {
409 	struct	defect_entry def;
410 	int	index;
411 
412 
413 	/*
414 	 * Calculate the fields for the defect struct.
415 	 */
416 	def.cyl = bn2c(blkno);
417 	def.head = bn2h(blkno);
418 	def.sect = bn2s(blkno);
419 	/*
420 	 * Initialize the unknown fields.
421 	 */
422 	def.bfi = def.nbits = UNKNOWN;
423 	/*
424 	 * Calculate the index into the list that the defect belongs at.
425 	 */
426 	index = sort_defect(&def, list);
427 	/*
428 	 * Add the defect to the list.
429 	 */
430 	add_def(&def, list, index);
431 }
432 
433 /*
434  * This routine adds the given defect struct to the defect list at
435  * a precalculated index.
436  */
437 void
add_def(struct defect_entry * def,struct defect_list * list,int index)438 add_def(struct defect_entry *def, struct defect_list *list, int index)
439 {
440 	int	count, i;
441 
442 	/*
443 	 * If adding this defect makes the list overflow into another
444 	 * sector, allocate the necessary space.
445 	 */
446 	count = list->header.count;
447 	if (deflist_size(cur_blksz, count + 1) > deflist_size(cur_blksz, count))
448 		list->list = (struct defect_entry *)rezalloc((void *)list->list,
449 		    deflist_size(cur_blksz, count + 1) * cur_blksz);
450 	/*
451 	 * Slip all the defects after this one down one slot in the list.
452 	 */
453 	for (i = count; i > index; i--)
454 		*(list->list + i) = *(list->list + i - 1);
455 	/*
456 	 * Fill in the created hole with this defect.
457 	 */
458 	*(list->list + i) = *def;
459 	/*
460 	 * Increment the count and calculate a new checksum.
461 	 */
462 	list->header.count++;
463 	(void) checkdefsum(list, CK_MAKESUM);
464 }
465 
466 /*
467  * This routine sets the given defect list back to null.
468  */
469 void
kill_deflist(struct defect_list * list)470 kill_deflist(struct defect_list *list)
471 {
472 
473 	/*
474 	 * If it's already null, we're done.
475 	 */
476 	if (list->list == NULL)
477 		return;
478 	/*
479 	 * Free the malloc'd space it's using.
480 	 */
481 	destroy_data((char *)list->list);
482 	/*
483 	 * Mark it as null, and clear any flags.
484 	 */
485 	list->list = NULL;
486 	list->flags = 0;
487 }
488 
489 /*
490  * This routine returns the defect list size
491  * according to the sector size.
492  */
493 int
deflist_size(int secsz,int sz)494 deflist_size(int secsz, int sz)
495 {
496 	int rval;
497 
498 	if (secsz == 0) {
499 		secsz = SECSIZE;
500 	}
501 
502 	rval = sz ? ((sz * sizeof (struct defect_entry) +
503 	    secsz - 1) / secsz) : 1;
504 
505 	return (rval);
506 }
507