1 /*
2  *      Copyright (C) 1994-1996 Bas Laarhoven,
3  *                (C) 1996-1997 Claus Heine.
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2, or (at your option)
8  any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; see the file COPYING.  If not, write to
17  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 
19  *
20  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.c,v $
21  * $Revision: 1.3 $
22  * $Date: 1997/10/05 19:15:15 $
23  *
24  *      This file contains the bad-sector map handling code for
25  *      the QIC-117 floppy tape driver for Linux.
26  *      QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented.
27  */
28 
29 #include <linux/string.h>
30 
31 #include <linux/ftape.h>
32 #include "../lowlevel/ftape-tracing.h"
33 #include "../lowlevel/ftape-bsm.h"
34 #include "../lowlevel/ftape-ctl.h"
35 #include "../lowlevel/ftape-rw.h"
36 
37 /*      Global vars.
38  */
39 
40 /*      Local vars.
41  */
42 static __u8 *bad_sector_map;
43 static SectorCount *bsm_hash_ptr;
44 
45 typedef enum {
46 	forward, backward
47 } mode_type;
48 
49 #if 0
50 /*  fix_tape converts a normal QIC-80 tape into a 'wide' tape.
51  *  For testing purposes only !
52  */
53 void fix_tape(__u8 * buffer, ft_format_type new_code)
54 {
55 	static __u8 list[BAD_SECTOR_MAP_SIZE];
56 	SectorMap *src_ptr = (SectorMap *) list;
57 	__u8 *dst_ptr = bad_sector_map;
58 	SectorMap map;
59 	unsigned int sector = 1;
60 	int i;
61 
62 	if (format_code != fmt_var && format_code != fmt_big) {
63 		memcpy(list, bad_sector_map, sizeof(list));
64 		memset(bad_sector_map, 0, sizeof(bad_sector_map));
65 		while ((__u8 *) src_ptr - list < sizeof(list)) {
66 			map = *src_ptr++;
67 			if (map == EMPTY_SEGMENT) {
68 				*(SectorMap *) dst_ptr = 0x800000 + sector;
69 				dst_ptr += 3;
70 				sector += SECTORS_PER_SEGMENT;
71 			} else {
72 				for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
73 					if (map & 1) {
74 						*(SewctorMap *) dst_ptr = sector;
75 						dst_ptr += 3;
76 					}
77 					map >>= 1;
78 					++sector;
79 				}
80 			}
81 		}
82 	}
83 	bad_sector_map_changed = 1;
84 	*(buffer + 4) = new_code;	/* put new format code */
85 	if (format_code != fmt_var && new_code == fmt_big) {
86 		PUT4(buffer, FT_6_HSEG_1,   (__u32)GET2(buffer, 6));
87 		PUT4(buffer, FT_6_HSEG_2,   (__u32)GET2(buffer, 8));
88 		PUT4(buffer, FT_6_FRST_SEG, (__u32)GET2(buffer, 10));
89 		PUT4(buffer, FT_6_LAST_SEG, (__u32)GET2(buffer, 12));
90 		memset(buffer+6, '\0', 8);
91 	}
92 	format_code = new_code;
93 }
94 
95 #endif
96 
97 /*   given buffer that contains a header segment, find the end of
98  *   of the bsm list
99  */
ftape_find_end_of_bsm_list(__u8 * address)100 __u8 * ftape_find_end_of_bsm_list(__u8 * address)
101 {
102 	__u8 *ptr   = address + FT_HEADER_END; /* start of bsm list */
103 	__u8 *limit = address + FT_SEGMENT_SIZE;
104 	while (ptr + 2 < limit) {
105 		if (ptr[0] || ptr[1] || ptr[2]) {
106 			ptr += 3;
107 		} else {
108 			return ptr;
109 		}
110 	}
111 	return NULL;
112 }
113 
put_sector(SectorCount * ptr,unsigned int sector)114 static inline void put_sector(SectorCount *ptr, unsigned int sector)
115 {
116 	ptr->bytes[0] = sector & 0xff;
117 	sector >>= 8;
118 	ptr->bytes[1] = sector & 0xff;
119 	sector >>= 8;
120 	ptr->bytes[2] = sector & 0xff;
121 }
122 
get_sector(SectorCount * ptr)123 static inline unsigned int get_sector(SectorCount *ptr)
124 {
125 #if 1
126 	unsigned int sector;
127 
128 	sector  = ptr->bytes[0];
129 	sector += ptr->bytes[1] <<  8;
130 	sector += ptr->bytes[2] << 16;
131 
132 	return sector;
133 #else
134 	/*  GET4 gets the next four bytes in Intel little endian order
135 	 *  and converts them to host byte order and handles unaligned
136 	 *  access.
137 	 */
138 	return (GET4(ptr, 0) & 0x00ffffff); /* back to host byte order */
139 #endif
140 }
141 
bsm_debug_fake(void)142 static void bsm_debug_fake(void)
143 {
144 	/* for testing of bad sector handling at end of tape
145 	 */
146 #if 0
147 	ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 3,
148 				   0x000003e0;
149 	ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 2,
150 				   0xff3fffff;
151 	ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 1,
152 				   0xffffe000;
153 #endif
154 	/*  Enable to test bad sector handling
155 	 */
156 #if 0
157 	ftape_put_bad_sector_entry(30, 0xfffffffe)
158 	ftape_put_bad_sector_entry(32, 0x7fffffff);
159 	ftape_put_bad_sector_entry(34, 0xfffeffff);
160 	ftape_put_bad_sector_entry(36, 0x55555555);
161 	ftape_put_bad_sector_entry(38, 0xffffffff);
162 	ftape_put_bad_sector_entry(50, 0xffff0000);
163 	ftape_put_bad_sector_entry(51, 0xffffffff);
164 	ftape_put_bad_sector_entry(52, 0xffffffff);
165 	ftape_put_bad_sector_entry(53, 0x0000ffff);
166 #endif
167 	/*  Enable when testing multiple volume tar dumps.
168 	 */
169 #if 0
170 	{
171 		int i;
172 
173 		for (i = ft_first_data_segment;
174 		     i <= ft_last_data_segment - 7; ++i) {
175 			ftape_put_bad_sector_entry(i, EMPTY_SEGMENT);
176 		}
177 	}
178 #endif
179 	/*  Enable when testing bit positions in *_error_map
180 	 */
181 #if 0
182 	{
183 		int i;
184 
185 		for (i = first_data_segment; i <= last_data_segment; ++i) {
186 			ftape_put_bad_sector_entry(i,
187 					   ftape_get_bad_sector_entry(i)
188 					   | 0x00ff00ff);
189 		}
190 	}
191 #endif
192 }
193 
print_bad_sector_map(void)194 static void print_bad_sector_map(void)
195 {
196 	unsigned int good_sectors;
197 	unsigned int total_bad = 0;
198 	int i;
199 	TRACE_FUN(ft_t_flow);
200 
201 	if (ft_format_code == fmt_big ||
202 	    ft_format_code == fmt_var ||
203 	    ft_format_code == fmt_1100ft) {
204 		SectorCount *ptr = (SectorCount *)bad_sector_map;
205 		unsigned int sector;
206 		__u16 *ptr16;
207 
208 		while((sector = get_sector(ptr++)) != 0) {
209 			if ((ft_format_code == fmt_big ||
210 			     ft_format_code == fmt_var) &&
211 			    sector & 0x800000) {
212 				total_bad += FT_SECTORS_PER_SEGMENT - 3;
213 				TRACE(ft_t_noise, "bad segment at sector: %6d",
214 				      sector & 0x7fffff);
215 			} else {
216 				++total_bad;
217 				TRACE(ft_t_noise, "bad sector: %6d", sector);
218 			}
219 		}
220 		/*  Display old ftape's end-of-file marks
221 		 */
222 		ptr16 = (__u16*)ptr;
223 		while ((sector = get_unaligned(ptr16++)) != 0) {
224 			TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
225 			      sector, get_unaligned(ptr16++));
226 		}
227 	} else { /* fixed size format */
228 		for (i = ft_first_data_segment;
229 		     i < (int)(ft_segments_per_track * ft_tracks_per_tape); ++i) {
230 			SectorMap map = ((SectorMap *) bad_sector_map)[i];
231 
232 			if (map) {
233 				TRACE(ft_t_noise,
234 				      "bsm for segment %4d: 0x%08x", i, (unsigned int)map);
235 				total_bad += ((map == EMPTY_SEGMENT)
236 					       ? FT_SECTORS_PER_SEGMENT - 3
237 					       : count_ones(map));
238 			}
239 		}
240 	}
241 	good_sectors =
242 		((ft_segments_per_track * ft_tracks_per_tape - ft_first_data_segment)
243 		 * (FT_SECTORS_PER_SEGMENT - 3)) - total_bad;
244 	TRACE(ft_t_info, "%d Kb usable on this tape", good_sectors);
245 	if (total_bad == 0) {
246 		TRACE(ft_t_info,
247 		      "WARNING: this tape has no bad blocks registered !");
248 	} else {
249 		TRACE(ft_t_info, "%d bad sectors", total_bad);
250 	}
251 	TRACE_EXIT;
252 }
253 
254 
ftape_extract_bad_sector_map(__u8 * buffer)255 void ftape_extract_bad_sector_map(__u8 * buffer)
256 {
257 	TRACE_FUN(ft_t_any);
258 
259 	/*  Fill the bad sector map with the contents of buffer.
260 	 */
261 	if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
262 		/* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed
263 		 * sector log but use this area to extend the bad sector map.
264 		 */
265 		bad_sector_map = &buffer[FT_HEADER_END];
266 	} else {
267 		/* non-wide QIC-80 tapes have a failed sector log area that
268 		 * mustn't be included in the bad sector map.
269 		 */
270 		bad_sector_map = &buffer[FT_FSL + FT_FSL_SIZE];
271 	}
272 	if (ft_format_code == fmt_1100ft ||
273 	    ft_format_code == fmt_var    ||
274 	    ft_format_code == fmt_big) {
275 		bsm_hash_ptr = (SectorCount *)bad_sector_map;
276 	} else {
277 		bsm_hash_ptr = NULL;
278 	}
279 	bsm_debug_fake();
280 	if (TRACE_LEVEL >= ft_t_info) {
281 		print_bad_sector_map();
282 	}
283 	TRACE_EXIT;
284 }
285 
cvt2map(unsigned int sector)286 static inline SectorMap cvt2map(unsigned int sector)
287 {
288 	return 1 << (((sector & 0x7fffff) - 1) % FT_SECTORS_PER_SEGMENT);
289 }
290 
cvt2segment(unsigned int sector)291 static inline int cvt2segment(unsigned int sector)
292 {
293 	return ((sector & 0x7fffff) - 1) / FT_SECTORS_PER_SEGMENT;
294 }
295 
forward_seek_entry(int segment_id,SectorCount ** ptr,SectorMap * map)296 static int forward_seek_entry(int segment_id,
297 			      SectorCount **ptr,
298 			      SectorMap *map)
299 {
300 	unsigned int sector;
301 	int segment;
302 
303 	do {
304 		sector = get_sector((*ptr)++);
305 		segment = cvt2segment(sector);
306 	} while (sector != 0 && segment < segment_id);
307 	(*ptr) --; /* point to first sector >= segment_id */
308 	/*  Get all sectors in segment_id
309 	 */
310 	if (sector == 0 || segment != segment_id) {
311 		*map = 0;
312 		return 0;
313 	} else if ((sector & 0x800000) &&
314 		   (ft_format_code == fmt_var || ft_format_code == fmt_big)) {
315 		*map = EMPTY_SEGMENT;
316 		return FT_SECTORS_PER_SEGMENT;
317 	} else {
318 		int count = 1;
319 		SectorCount *tmp_ptr = (*ptr) + 1;
320 
321 		*map = cvt2map(sector);
322 		while ((sector = get_sector(tmp_ptr++)) != 0 &&
323 		       (segment = cvt2segment(sector)) == segment_id) {
324 			*map |= cvt2map(sector);
325 			++count;
326 		}
327 		return count;
328 	}
329 }
330 
backwards_seek_entry(int segment_id,SectorCount ** ptr,SectorMap * map)331 static int backwards_seek_entry(int segment_id,
332 				SectorCount **ptr,
333 				SectorMap *map)
334 {
335 	unsigned int sector;
336 	int segment; /* max unsigned int */
337 
338 	if (*ptr <= (SectorCount *)bad_sector_map) {
339 		*map = 0;
340 		return 0;
341 	}
342 	do {
343 		sector  = get_sector(--(*ptr));
344 		segment = cvt2segment(sector);
345 	} while (*ptr > (SectorCount *)bad_sector_map && segment > segment_id);
346 	if (segment > segment_id) { /*  at start of list, no entry found */
347 		*map = 0;
348 		return 0;
349 	} else if (segment < segment_id) {
350 		/*  before smaller entry, adjust for overshoot */
351 		(*ptr) ++;
352 		*map = 0;
353 		return 0;
354 	} else if ((sector & 0x800000) &&
355 		   (ft_format_code == fmt_big || ft_format_code == fmt_var)) {
356 		*map = EMPTY_SEGMENT;
357 		return FT_SECTORS_PER_SEGMENT;
358 	} else { /*  get all sectors in segment_id */
359 		int count = 1;
360 
361 		*map = cvt2map(sector);
362 		while(*ptr > (SectorCount *)bad_sector_map) {
363 			sector = get_sector(--(*ptr));
364 			segment = cvt2segment(sector);
365 			if (segment != segment_id) {
366 				break;
367 			}
368 			*map |= cvt2map(sector);
369 			++count;
370 		}
371 		if (segment < segment_id) {
372 			(*ptr) ++;
373 		}
374 		return count;
375 	}
376 }
377 
ftape_put_bad_sector_entry(int segment_id,SectorMap new_map)378 void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map)
379 {
380 	SectorCount *ptr = (SectorCount *)bad_sector_map;
381 	int count;
382 	int new_count;
383 	SectorMap map;
384 	TRACE_FUN(ft_t_any);
385 
386 	if (ft_format_code == fmt_1100ft ||
387 	    ft_format_code == fmt_var ||
388 	    ft_format_code == fmt_big) {
389 		count = forward_seek_entry(segment_id, &ptr, &map);
390 		new_count = count_ones(new_map);
391 		/* If format code == 4 put empty segment instead of 32
392 		 * bad sectors.
393 		 */
394 		if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
395 			if (new_count == FT_SECTORS_PER_SEGMENT) {
396 				new_count = 1;
397 			}
398 			if (count == FT_SECTORS_PER_SEGMENT) {
399 				count = 1;
400 			}
401 		}
402 		if (count != new_count) {
403 			/* insert (or delete if < 0) new_count - count
404 			 * entries.  Move trailing part of list
405 			 * including terminating 0.
406 			 */
407 			SectorCount *hi_ptr = ptr;
408 
409 			do {
410 			} while (get_sector(hi_ptr++) != 0);
411 			/*  Note: ptr is of type byte *, and each bad sector
412 			 *  consumes 3 bytes.
413 			 */
414 			memmove(ptr + new_count, ptr + count,
415 				(size_t)(hi_ptr - (ptr + count))*sizeof(SectorCount));
416 		}
417 		TRACE(ft_t_noise, "putting map 0x%08x at %p, segment %d",
418 		      (unsigned int)new_map, ptr, segment_id);
419 		if (new_count == 1 && new_map == EMPTY_SEGMENT) {
420 			put_sector(ptr++, (0x800001 +
421 					  segment_id *
422 					  FT_SECTORS_PER_SEGMENT));
423 		} else {
424 			int i = 0;
425 
426 			while (new_map) {
427 				if (new_map & 1) {
428 					put_sector(ptr++,
429 						   1 + segment_id *
430 						   FT_SECTORS_PER_SEGMENT + i);
431 				}
432 				++i;
433 				new_map >>= 1;
434 			}
435 		}
436 	} else {
437 		((SectorMap *) bad_sector_map)[segment_id] = new_map;
438 	}
439 	TRACE_EXIT;
440 }
441 
ftape_get_bad_sector_entry(int segment_id)442 SectorMap ftape_get_bad_sector_entry(int segment_id)
443 {
444 	if (ft_used_header_segment == -1) {
445 		/*  When reading header segment we'll need a blank map.
446 		 */
447 		return 0;
448 	} else if (bsm_hash_ptr != NULL) {
449 		/*  Invariants:
450 		 *    map - mask value returned on last call.
451 		 *    bsm_hash_ptr - points to first sector greater or equal to
452 		 *          first sector in last_referenced segment.
453 		 *    last_referenced - segment id used in the last call,
454 		 *                      sector and map belong to this id.
455 		 *  This code is designed for sequential access and retries.
456 		 *  For true random access it may have to be redesigned.
457 		 */
458 		static int last_reference = -1;
459 		static SectorMap map;
460 
461 		if (segment_id > last_reference) {
462 			/*  Skip all sectors before segment_id
463 			 */
464 			forward_seek_entry(segment_id, &bsm_hash_ptr, &map);
465 		} else if (segment_id < last_reference) {
466 			/* Skip backwards until begin of buffer or
467 			 * first sector in segment_id
468 			 */
469 			backwards_seek_entry(segment_id, &bsm_hash_ptr, &map);
470 		}		/* segment_id == last_reference : keep map */
471 		last_reference = segment_id;
472 		return map;
473 	} else {
474 		return ((SectorMap *) bad_sector_map)[segment_id];
475 	}
476 }
477 
478 /*  This is simply here to prevent us from overwriting other kernel
479  *  data. Writes will result in NULL Pointer dereference.
480  */
ftape_init_bsm(void)481 void ftape_init_bsm(void)
482 {
483 	bad_sector_map = NULL;
484 	bsm_hash_ptr   = NULL;
485 }
486