1 /*
2  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3  *
4  * Copyright (C) 2002-2011 Aleph One Ltd.
5  *   for Toby Churchill Ltd and Brightstar Engineering
6  *
7  * Created by Charles Manning <charles@aleph1.co.uk>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  */
13 
14 #include "yaffs_guts.h"
15 #include "yaffs_tagscompat.h"
16 #include "yaffs_ecc.h"
17 #include "yaffs_getblockinfo.h"
18 #include "yaffs_trace.h"
19 
20 static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
21 
22 
23 /********** Tags ECC calculations  *********/
24 
yaffs_calc_ecc(const u8 * data,struct yaffs_spare * spare)25 void yaffs_calc_ecc(const u8 *data, struct yaffs_spare *spare)
26 {
27 	yaffs_ecc_calc(data, spare->ecc1);
28 	yaffs_ecc_calc(&data[256], spare->ecc2);
29 }
30 
yaffs_calc_tags_ecc(struct yaffs_tags * tags)31 void yaffs_calc_tags_ecc(struct yaffs_tags *tags)
32 {
33 	/* Calculate an ecc */
34 	unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
35 	unsigned i, j;
36 	unsigned ecc = 0;
37 	unsigned bit = 0;
38 
39 	tags->ecc = 0;
40 
41 	for (i = 0; i < 8; i++) {
42 		for (j = 1; j & 0xff; j <<= 1) {
43 			bit++;
44 			if (b[i] & j)
45 				ecc ^= bit;
46 		}
47 	}
48 	tags->ecc = ecc;
49 }
50 
yaffs_check_tags_ecc(struct yaffs_tags * tags)51 int yaffs_check_tags_ecc(struct yaffs_tags *tags)
52 {
53 	unsigned ecc = tags->ecc;
54 
55 	yaffs_calc_tags_ecc(tags);
56 
57 	ecc ^= tags->ecc;
58 
59 	if (ecc && ecc <= 64) {
60 		/* TODO: Handle the failure better. Retire? */
61 		unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
62 
63 		ecc--;
64 
65 		b[ecc / 8] ^= (1 << (ecc & 7));
66 
67 		/* Now recvalc the ecc */
68 		yaffs_calc_tags_ecc(tags);
69 
70 		return 1;	/* recovered error */
71 	} else if (ecc) {
72 		/* Wierd ecc failure value */
73 		/* TODO Need to do somethiong here */
74 		return -1;	/* unrecovered error */
75 	}
76 	return 0;
77 }
78 
79 /********** Tags **********/
80 
yaffs_load_tags_to_spare(struct yaffs_spare * spare_ptr,struct yaffs_tags * tags_ptr)81 static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
82 				     struct yaffs_tags *tags_ptr)
83 {
84 	union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
85 
86 	yaffs_calc_tags_ecc(tags_ptr);
87 
88 	spare_ptr->tb0 = tu->as_bytes[0];
89 	spare_ptr->tb1 = tu->as_bytes[1];
90 	spare_ptr->tb2 = tu->as_bytes[2];
91 	spare_ptr->tb3 = tu->as_bytes[3];
92 	spare_ptr->tb4 = tu->as_bytes[4];
93 	spare_ptr->tb5 = tu->as_bytes[5];
94 	spare_ptr->tb6 = tu->as_bytes[6];
95 	spare_ptr->tb7 = tu->as_bytes[7];
96 }
97 
yaffs_get_tags_from_spare(struct yaffs_dev * dev,struct yaffs_spare * spare_ptr,struct yaffs_tags * tags_ptr)98 static void yaffs_get_tags_from_spare(struct yaffs_dev *dev,
99 				      struct yaffs_spare *spare_ptr,
100 				      struct yaffs_tags *tags_ptr)
101 {
102 	union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
103 	int result;
104 
105 	tu->as_bytes[0] = spare_ptr->tb0;
106 	tu->as_bytes[1] = spare_ptr->tb1;
107 	tu->as_bytes[2] = spare_ptr->tb2;
108 	tu->as_bytes[3] = spare_ptr->tb3;
109 	tu->as_bytes[4] = spare_ptr->tb4;
110 	tu->as_bytes[5] = spare_ptr->tb5;
111 	tu->as_bytes[6] = spare_ptr->tb6;
112 	tu->as_bytes[7] = spare_ptr->tb7;
113 
114 	result = yaffs_check_tags_ecc(tags_ptr);
115 	if (result > 0)
116 		dev->n_tags_ecc_fixed++;
117 	else if (result < 0)
118 		dev->n_tags_ecc_unfixed++;
119 }
120 
yaffs_spare_init(struct yaffs_spare * spare)121 static void yaffs_spare_init(struct yaffs_spare *spare)
122 {
123 	memset(spare, 0xff, sizeof(struct yaffs_spare));
124 }
125 
yaffs_wr_nand(struct yaffs_dev * dev,int nand_chunk,const u8 * data,struct yaffs_spare * spare)126 static int yaffs_wr_nand(struct yaffs_dev *dev,
127 			 int nand_chunk, const u8 *data,
128 			 struct yaffs_spare *spare)
129 {
130 	if (nand_chunk < dev->param.start_block * dev->param.chunks_per_block) {
131 		yaffs_trace(YAFFS_TRACE_ERROR,
132 			"**>> yaffs chunk %d is not valid",
133 			nand_chunk);
134 		return YAFFS_FAIL;
135 	}
136 
137 	return dev->param.write_chunk_fn(dev, nand_chunk, data, spare);
138 }
139 
yaffs_rd_chunk_nand(struct yaffs_dev * dev,int nand_chunk,u8 * data,struct yaffs_spare * spare,enum yaffs_ecc_result * ecc_result,int correct_errors)140 static int yaffs_rd_chunk_nand(struct yaffs_dev *dev,
141 			       int nand_chunk,
142 			       u8 *data,
143 			       struct yaffs_spare *spare,
144 			       enum yaffs_ecc_result *ecc_result,
145 			       int correct_errors)
146 {
147 	int ret_val;
148 	struct yaffs_spare local_spare;
149 
150 	if (!spare) {
151 		/* If we don't have a real spare, then we use a local one. */
152 		/* Need this for the calculation of the ecc */
153 		spare = &local_spare;
154 	}
155 
156 	if (!dev->param.use_nand_ecc) {
157 		ret_val =
158 		    dev->param.read_chunk_fn(dev, nand_chunk, data, spare);
159 		if (data && correct_errors) {
160 			/* Do ECC correction */
161 			/* Todo handle any errors */
162 			int ecc_result1, ecc_result2;
163 			u8 calc_ecc[3];
164 
165 			yaffs_ecc_calc(data, calc_ecc);
166 			ecc_result1 =
167 			    yaffs_ecc_correct(data, spare->ecc1, calc_ecc);
168 			yaffs_ecc_calc(&data[256], calc_ecc);
169 			ecc_result2 =
170 			    yaffs_ecc_correct(&data[256], spare->ecc2,
171 					      calc_ecc);
172 
173 			if (ecc_result1 > 0) {
174 				yaffs_trace(YAFFS_TRACE_ERROR,
175 					"**>>yaffs ecc error fix performed on chunk %d:0",
176 					nand_chunk);
177 				dev->n_ecc_fixed++;
178 			} else if (ecc_result1 < 0) {
179 				yaffs_trace(YAFFS_TRACE_ERROR,
180 					"**>>yaffs ecc error unfixed on chunk %d:0",
181 					nand_chunk);
182 				dev->n_ecc_unfixed++;
183 			}
184 
185 			if (ecc_result2 > 0) {
186 				yaffs_trace(YAFFS_TRACE_ERROR,
187 					"**>>yaffs ecc error fix performed on chunk %d:1",
188 					nand_chunk);
189 				dev->n_ecc_fixed++;
190 			} else if (ecc_result2 < 0) {
191 				yaffs_trace(YAFFS_TRACE_ERROR,
192 					"**>>yaffs ecc error unfixed on chunk %d:1",
193 					nand_chunk);
194 				dev->n_ecc_unfixed++;
195 			}
196 
197 			if (ecc_result1 || ecc_result2) {
198 				/* We had a data problem on this page */
199 				yaffs_handle_rd_data_error(dev, nand_chunk);
200 			}
201 
202 			if (ecc_result1 < 0 || ecc_result2 < 0)
203 				*ecc_result = YAFFS_ECC_RESULT_UNFIXED;
204 			else if (ecc_result1 > 0 || ecc_result2 > 0)
205 				*ecc_result = YAFFS_ECC_RESULT_FIXED;
206 			else
207 				*ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
208 		}
209 	} else {
210 		/* Must allocate enough memory for spare+2*sizeof(int) */
211 		/* for ecc results from device. */
212 		struct yaffs_nand_spare nspare;
213 
214 		memset(&nspare, 0, sizeof(nspare));
215 
216 		ret_val = dev->param.read_chunk_fn(dev, nand_chunk, data,
217 						   (struct yaffs_spare *)
218 						   &nspare);
219 		memcpy(spare, &nspare, sizeof(struct yaffs_spare));
220 		if (data && correct_errors) {
221 			if (nspare.eccres1 > 0) {
222 				yaffs_trace(YAFFS_TRACE_ERROR,
223 					"**>>mtd ecc error fix performed on chunk %d:0",
224 					nand_chunk);
225 			} else if (nspare.eccres1 < 0) {
226 				yaffs_trace(YAFFS_TRACE_ERROR,
227 					"**>>mtd ecc error unfixed on chunk %d:0",
228 					nand_chunk);
229 			}
230 
231 			if (nspare.eccres2 > 0) {
232 				yaffs_trace(YAFFS_TRACE_ERROR,
233 					"**>>mtd ecc error fix performed on chunk %d:1",
234 					nand_chunk);
235 			} else if (nspare.eccres2 < 0) {
236 				yaffs_trace(YAFFS_TRACE_ERROR,
237 					"**>>mtd ecc error unfixed on chunk %d:1",
238 					nand_chunk);
239 			}
240 
241 			if (nspare.eccres1 || nspare.eccres2) {
242 				/* We had a data problem on this page */
243 				yaffs_handle_rd_data_error(dev, nand_chunk);
244 			}
245 
246 			if (nspare.eccres1 < 0 || nspare.eccres2 < 0)
247 				*ecc_result = YAFFS_ECC_RESULT_UNFIXED;
248 			else if (nspare.eccres1 > 0 || nspare.eccres2 > 0)
249 				*ecc_result = YAFFS_ECC_RESULT_FIXED;
250 			else
251 				*ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
252 
253 		}
254 	}
255 	return ret_val;
256 }
257 
258 /*
259  * Functions for robustisizing
260  */
261 
yaffs_handle_rd_data_error(struct yaffs_dev * dev,int nand_chunk)262 static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk)
263 {
264 	int flash_block = nand_chunk / dev->param.chunks_per_block;
265 
266 	/* Mark the block for retirement */
267 	yaffs_get_block_info(dev, flash_block + dev->block_offset)->
268 		needs_retiring = 1;
269 	yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
270 		"**>>Block %d marked for retirement",
271 		flash_block);
272 
273 	/* TODO:
274 	 * Just do a garbage collection on the affected block
275 	 * then retire the block
276 	 * NB recursion
277 	 */
278 }
279 
yaffs_tags_compat_wr(struct yaffs_dev * dev,int nand_chunk,const u8 * data,const struct yaffs_ext_tags * ext_tags)280 int yaffs_tags_compat_wr(struct yaffs_dev *dev,
281 			 int nand_chunk,
282 			 const u8 *data, const struct yaffs_ext_tags *ext_tags)
283 {
284 	struct yaffs_spare spare;
285 	struct yaffs_tags tags;
286 
287 	yaffs_spare_init(&spare);
288 
289 	if (ext_tags->is_deleted)
290 		spare.page_status = 0;
291 	else {
292 		tags.obj_id = ext_tags->obj_id;
293 		tags.chunk_id = ext_tags->chunk_id;
294 
295 		tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1);
296 
297 		if (dev->data_bytes_per_chunk >= 1024)
298 			tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3;
299 		else
300 			tags.n_bytes_msb = 3;
301 
302 		tags.serial_number = ext_tags->serial_number;
303 
304 		if (!dev->param.use_nand_ecc && data)
305 			yaffs_calc_ecc(data, &spare);
306 
307 		yaffs_load_tags_to_spare(&spare, &tags);
308 	}
309 	return yaffs_wr_nand(dev, nand_chunk, data, &spare);
310 }
311 
yaffs_tags_compat_rd(struct yaffs_dev * dev,int nand_chunk,u8 * data,struct yaffs_ext_tags * ext_tags)312 int yaffs_tags_compat_rd(struct yaffs_dev *dev,
313 			 int nand_chunk,
314 			 u8 *data, struct yaffs_ext_tags *ext_tags)
315 {
316 	struct yaffs_spare spare;
317 	struct yaffs_tags tags;
318 	enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN;
319 	static struct yaffs_spare spare_ff;
320 	static int init;
321 	int deleted;
322 
323 	if (!init) {
324 		memset(&spare_ff, 0xff, sizeof(spare_ff));
325 		init = 1;
326 	}
327 
328 	if (!yaffs_rd_chunk_nand(dev, nand_chunk,
329 					data, &spare, &ecc_result, 1))
330 		return YAFFS_FAIL;
331 
332 	/* ext_tags may be NULL */
333 	if (!ext_tags)
334 		return YAFFS_OK;
335 
336 	deleted = (hweight8(spare.page_status) < 7) ? 1 : 0;
337 
338 	ext_tags->is_deleted = deleted;
339 	ext_tags->ecc_result = ecc_result;
340 	ext_tags->block_bad = 0;	/* We're reading it */
341 	/* therefore it is not a bad block */
342 	ext_tags->chunk_used =
343 		memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0;
344 
345 	if (ext_tags->chunk_used) {
346 		yaffs_get_tags_from_spare(dev, &spare, &tags);
347 		ext_tags->obj_id = tags.obj_id;
348 		ext_tags->chunk_id = tags.chunk_id;
349 		ext_tags->n_bytes = tags.n_bytes_lsb;
350 
351 		if (dev->data_bytes_per_chunk >= 1024)
352 			ext_tags->n_bytes |=
353 				(((unsigned)tags.n_bytes_msb) << 10);
354 
355 		ext_tags->serial_number = tags.serial_number;
356 	}
357 
358 	return YAFFS_OK;
359 }
360 
yaffs_tags_compat_mark_bad(struct yaffs_dev * dev,int flash_block)361 int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block)
362 {
363 	struct yaffs_spare spare;
364 
365 	memset(&spare, 0xff, sizeof(struct yaffs_spare));
366 
367 	spare.block_status = 'Y';
368 
369 	yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL,
370 		      &spare);
371 	yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1,
372 		      NULL, &spare);
373 
374 	return YAFFS_OK;
375 }
376 
yaffs_tags_compat_query_block(struct yaffs_dev * dev,int block_no,enum yaffs_block_state * state,u32 * seq_number)377 int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
378 				  int block_no,
379 				  enum yaffs_block_state *state,
380 				  u32 *seq_number)
381 {
382 	struct yaffs_spare spare0, spare1;
383 	static struct yaffs_spare spare_ff;
384 	static int init;
385 	enum yaffs_ecc_result dummy;
386 
387 	if (!init) {
388 		memset(&spare_ff, 0xff, sizeof(spare_ff));
389 		init = 1;
390 	}
391 
392 	*seq_number = 0;
393 
394 	yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, NULL,
395 			    &spare0, &dummy, 1);
396 	yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1,
397 			    NULL, &spare1, &dummy, 1);
398 
399 	if (hweight8(spare0.block_status & spare1.block_status) < 7)
400 		*state = YAFFS_BLOCK_STATE_DEAD;
401 	else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0)
402 		*state = YAFFS_BLOCK_STATE_EMPTY;
403 	else
404 		*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
405 
406 	return YAFFS_OK;
407 }
408