2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
4 * Copyright (C) 2002-2010 Aleph One Ltd.
5 * for Toby Churchill Ltd and Brightstar Engineering
7 * Created by Charles Manning <charles@aleph1.co.uk>
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.
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"
20 static void yaffs_handle_rd_data_error(yaffs_dev_t
*dev
, int nand_chunk
);
22 static void yaffs_check_written_block(yaffs_dev_t
*dev
, int nand_chunk
);
23 static void yaffs_handle_chunk_wr_ok(yaffs_dev_t
*dev
, int nand_chunk
,
25 const yaffs_spare
*spare
);
26 static void yaffs_handle_chunk_update(yaffs_dev_t
*dev
, int nand_chunk
,
27 const yaffs_spare
*spare
);
28 static void yaffs_handle_chunk_wr_error(yaffs_dev_t
*dev
, int nand_chunk
);
31 static const char yaffs_count_bits_table
[256] = {
32 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
33 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
34 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
35 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
36 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
37 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
38 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
39 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
40 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
41 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
42 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
43 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
44 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
45 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
46 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
47 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
50 int yaffs_count_bits(__u8 x
)
53 retVal
= yaffs_count_bits_table
[x
];
57 /********** Tags ECC calculations *********/
59 void yaffs_calc_ecc(const __u8
*data
, yaffs_spare
*spare
)
61 yaffs_ecc_cacl(data
, spare
->ecc1
);
62 yaffs_ecc_cacl(&data
[256], spare
->ecc2
);
65 void yaffs_calc_tags_ecc(yaffs_tags_t
*tags
)
67 /* Calculate an ecc */
69 unsigned char *b
= ((yaffs_tags_union_t
*) tags
)->as_bytes
;
76 for (i
= 0; i
< 8; i
++) {
77 for (j
= 1; j
& 0xff; j
<<= 1) {
88 int yaffs_check_tags_ecc(yaffs_tags_t
*tags
)
90 unsigned ecc
= tags
->ecc
;
92 yaffs_calc_tags_ecc(tags
);
96 if (ecc
&& ecc
<= 64) {
97 /* TODO: Handle the failure better. Retire? */
98 unsigned char *b
= ((yaffs_tags_union_t
*) tags
)->as_bytes
;
102 b
[ecc
/ 8] ^= (1 << (ecc
& 7));
104 /* Now recvalc the ecc */
105 yaffs_calc_tags_ecc(tags
);
107 return 1; /* recovered error */
109 /* Wierd ecc failure value */
110 /* TODO Need to do somethiong here */
111 return -1; /* unrecovered error */
117 /********** Tags **********/
119 static void yaffs_load_tags_to_spare(yaffs_spare
*sparePtr
,
120 yaffs_tags_t
*tagsPtr
)
122 yaffs_tags_union_t
*tu
= (yaffs_tags_union_t
*) tagsPtr
;
124 yaffs_calc_tags_ecc(tagsPtr
);
126 sparePtr
->tb0
= tu
->as_bytes
[0];
127 sparePtr
->tb1
= tu
->as_bytes
[1];
128 sparePtr
->tb2
= tu
->as_bytes
[2];
129 sparePtr
->tb3
= tu
->as_bytes
[3];
130 sparePtr
->tb4
= tu
->as_bytes
[4];
131 sparePtr
->tb5
= tu
->as_bytes
[5];
132 sparePtr
->tb6
= tu
->as_bytes
[6];
133 sparePtr
->tb7
= tu
->as_bytes
[7];
136 static void yaffs_get_tags_from_spare(yaffs_dev_t
*dev
, yaffs_spare
*sparePtr
,
137 yaffs_tags_t
*tagsPtr
)
139 yaffs_tags_union_t
*tu
= (yaffs_tags_union_t
*) tagsPtr
;
142 tu
->as_bytes
[0] = sparePtr
->tb0
;
143 tu
->as_bytes
[1] = sparePtr
->tb1
;
144 tu
->as_bytes
[2] = sparePtr
->tb2
;
145 tu
->as_bytes
[3] = sparePtr
->tb3
;
146 tu
->as_bytes
[4] = sparePtr
->tb4
;
147 tu
->as_bytes
[5] = sparePtr
->tb5
;
148 tu
->as_bytes
[6] = sparePtr
->tb6
;
149 tu
->as_bytes
[7] = sparePtr
->tb7
;
151 result
= yaffs_check_tags_ecc(tagsPtr
);
153 dev
->n_tags_ecc_fixed
++;
155 dev
->n_tags_ecc_unfixed
++;
158 static void yaffs_spare_init(yaffs_spare
*spare
)
160 memset(spare
, 0xFF, sizeof(yaffs_spare
));
163 static int yaffs_wr_nand(struct yaffs_dev_s
*dev
,
164 int nand_chunk
, const __u8
*data
,
167 if (nand_chunk
< dev
->param
.start_block
* dev
->param
.chunks_per_block
) {
169 (TSTR("**>> yaffs chunk %d is not valid" TENDSTR
),
174 return dev
->param
.write_chunk_fn(dev
, nand_chunk
, data
, spare
);
177 static int yaffs_rd_chunk_nand(struct yaffs_dev_s
*dev
,
181 yaffs_ecc_result
*ecc_result
,
182 int doErrorCorrection
)
185 yaffs_spare localSpare
;
187 if (!spare
&& data
) {
188 /* If we don't have a real spare, then we use a local one. */
189 /* Need this for the calculation of the ecc */
193 if (!dev
->param
.use_nand_ecc
) {
194 retVal
= dev
->param
.read_chunk_fn(dev
, nand_chunk
, data
, spare
);
195 if (data
&& doErrorCorrection
) {
196 /* Do ECC correction */
197 /* Todo handle any errors */
198 int ecc_result1
, ecc_result2
;
201 yaffs_ecc_cacl(data
, calcEcc
);
203 yaffs_ecc_correct(data
, spare
->ecc1
, calcEcc
);
204 yaffs_ecc_cacl(&data
[256], calcEcc
);
206 yaffs_ecc_correct(&data
[256], spare
->ecc2
, calcEcc
);
208 if (ecc_result1
> 0) {
211 ("**>>yaffs ecc error fix performed on chunk %d:0"
212 TENDSTR
), nand_chunk
));
214 } else if (ecc_result1
< 0) {
217 ("**>>yaffs ecc error unfixed on chunk %d:0"
218 TENDSTR
), nand_chunk
));
219 dev
->n_ecc_unfixed
++;
222 if (ecc_result2
> 0) {
225 ("**>>yaffs ecc error fix performed on chunk %d:1"
226 TENDSTR
), nand_chunk
));
228 } else if (ecc_result2
< 0) {
231 ("**>>yaffs ecc error unfixed on chunk %d:1"
232 TENDSTR
), nand_chunk
));
233 dev
->n_ecc_unfixed
++;
236 if (ecc_result1
|| ecc_result2
) {
237 /* We had a data problem on this page */
238 yaffs_handle_rd_data_error(dev
, nand_chunk
);
241 if (ecc_result1
< 0 || ecc_result2
< 0)
242 *ecc_result
= YAFFS_ECC_RESULT_UNFIXED
;
243 else if (ecc_result1
> 0 || ecc_result2
> 0)
244 *ecc_result
= YAFFS_ECC_RESULT_FIXED
;
246 *ecc_result
= YAFFS_ECC_RESULT_NO_ERROR
;
249 /* Must allocate enough memory for spare+2*sizeof(int) */
250 /* for ecc results from device. */
251 struct yaffs_nand_spare nspare
;
253 memset(&nspare
, 0, sizeof(nspare
));
255 retVal
= dev
->param
.read_chunk_fn(dev
, nand_chunk
, data
,
256 (yaffs_spare
*) &nspare
);
257 memcpy(spare
, &nspare
, sizeof(yaffs_spare
));
258 if (data
&& doErrorCorrection
) {
259 if (nspare
.eccres1
> 0) {
262 ("**>>mtd ecc error fix performed on chunk %d:0"
263 TENDSTR
), nand_chunk
));
264 } else if (nspare
.eccres1
< 0) {
267 ("**>>mtd ecc error unfixed on chunk %d:0"
268 TENDSTR
), nand_chunk
));
271 if (nspare
.eccres2
> 0) {
274 ("**>>mtd ecc error fix performed on chunk %d:1"
275 TENDSTR
), nand_chunk
));
276 } else if (nspare
.eccres2
< 0) {
279 ("**>>mtd ecc error unfixed on chunk %d:1"
280 TENDSTR
), nand_chunk
));
283 if (nspare
.eccres1
|| nspare
.eccres2
) {
284 /* We had a data problem on this page */
285 yaffs_handle_rd_data_error(dev
, nand_chunk
);
288 if (nspare
.eccres1
< 0 || nspare
.eccres2
< 0)
289 *ecc_result
= YAFFS_ECC_RESULT_UNFIXED
;
290 else if (nspare
.eccres1
> 0 || nspare
.eccres2
> 0)
291 *ecc_result
= YAFFS_ECC_RESULT_FIXED
;
293 *ecc_result
= YAFFS_ECC_RESULT_NO_ERROR
;
301 static int yaffs_check_chunk_erased(struct yaffs_dev_s
*dev
,
305 static __u8 cmpbuf
[YAFFS_BYTES_PER_CHUNK
];
306 static __u8 data
[YAFFS_BYTES_PER_CHUNK
];
307 /* Might as well always allocate the larger size for */
308 /* dev->param.use_nand_ecc == true; */
309 static __u8 spare
[sizeof(struct yaffs_nand_spare
)];
311 dev
->param
.read_chunk_fn(dev
, nand_chunk
, data
, (yaffs_spare
*) spare
);
314 memset(cmpbuf
, 0xff, YAFFS_BYTES_PER_CHUNK
);
318 if (memcmp(cmpbuf
, data
, YAFFS_BYTES_PER_CHUNK
))
320 if (memcmp(cmpbuf
, spare
, 16))
329 * Functions for robustisizing
332 static void yaffs_handle_rd_data_error(yaffs_dev_t
*dev
, int nand_chunk
)
334 int flash_block
= nand_chunk
/ dev
->param
.chunks_per_block
;
336 /* Mark the block for retirement */
337 yaffs_get_block_info(dev
, flash_block
+ dev
->block_offset
)->needs_retiring
= 1;
338 T(YAFFS_TRACE_ERROR
| YAFFS_TRACE_BAD_BLOCKS
,
339 (TSTR("**>>Block %d marked for retirement" TENDSTR
), flash_block
));
342 * Just do a garbage collection on the affected block
343 * then retire the block
349 static void yaffs_check_written_block(yaffs_dev_t
*dev
, int nand_chunk
)
353 static void yaffs_handle_chunk_wr_ok(yaffs_dev_t
*dev
, int nand_chunk
,
355 const yaffs_spare
*spare
)
359 static void yaffs_handle_chunk_update(yaffs_dev_t
*dev
, int nand_chunk
,
360 const yaffs_spare
*spare
)
364 static void yaffs_handle_chunk_wr_error(yaffs_dev_t
*dev
, int nand_chunk
)
366 int flash_block
= nand_chunk
/ dev
->param
.chunks_per_block
;
368 /* Mark the block for retirement */
369 yaffs_get_block_info(dev
, flash_block
)->needs_retiring
= 1;
370 /* Delete the chunk */
371 yaffs_chunk_del(dev
, nand_chunk
, 1, __LINE__
);
374 static int yaffs_verify_cmp(const __u8
*d0
, const __u8
*d1
,
375 const yaffs_spare
*s0
, const yaffs_spare
*s1
)
378 if (memcmp(d0
, d1
, YAFFS_BYTES_PER_CHUNK
) != 0 ||
379 s0
->tb0
!= s1
->tb0
||
380 s0
->tb1
!= s1
->tb1
||
381 s0
->tb2
!= s1
->tb2
||
382 s0
->tb3
!= s1
->tb3
||
383 s0
->tb4
!= s1
->tb4
||
384 s0
->tb5
!= s1
->tb5
||
385 s0
->tb6
!= s1
->tb6
||
386 s0
->tb7
!= s1
->tb7
||
387 s0
->ecc1
[0] != s1
->ecc1
[0] ||
388 s0
->ecc1
[1] != s1
->ecc1
[1] ||
389 s0
->ecc1
[2] != s1
->ecc1
[2] ||
390 s0
->ecc2
[0] != s1
->ecc2
[0] ||
391 s0
->ecc2
[1] != s1
->ecc2
[1] || s0
->ecc2
[2] != s1
->ecc2
[2]) {
399 int yaffs_tags_compat_wr(yaffs_dev_t
*dev
,
402 const yaffs_ext_tags
*eTags
)
407 yaffs_spare_init(&spare
);
409 if (eTags
->is_deleted
)
410 spare
.page_status
= 0;
412 tags
.obj_id
= eTags
->obj_id
;
413 tags
.chunk_id
= eTags
->chunk_id
;
415 tags
.n_bytes_lsb
= eTags
->n_bytes
& 0x3ff;
417 if (dev
->data_bytes_per_chunk
>= 1024)
418 tags
.n_bytes_msb
= (eTags
->n_bytes
>> 10) & 3;
420 tags
.n_bytes_msb
= 3;
423 tags
.serial_number
= eTags
->serial_number
;
425 if (!dev
->param
.use_nand_ecc
&& data
)
426 yaffs_calc_ecc(data
, &spare
);
428 yaffs_load_tags_to_spare(&spare
, &tags
);
432 return yaffs_wr_nand(dev
, nand_chunk
, data
, &spare
);
435 int yaffs_tags_compat_rd(yaffs_dev_t
*dev
,
438 yaffs_ext_tags
*eTags
)
443 yaffs_ecc_result ecc_result
= YAFFS_ECC_RESULT_UNKNOWN
;
445 static yaffs_spare spareFF
;
449 memset(&spareFF
, 0xFF, sizeof(spareFF
));
453 if (yaffs_rd_chunk_nand
454 (dev
, nand_chunk
, data
, &spare
, &ecc_result
, 1)) {
455 /* eTags may be NULL */
459 (yaffs_count_bits(spare
.page_status
) < 7) ? 1 : 0;
461 eTags
->is_deleted
= deleted
;
462 eTags
->ecc_result
= ecc_result
;
463 eTags
->block_bad
= 0; /* We're reading it */
464 /* therefore it is not a bad block */
466 (memcmp(&spareFF
, &spare
, sizeof(spareFF
)) !=
469 if (eTags
->chunk_used
) {
470 yaffs_get_tags_from_spare(dev
, &spare
, &tags
);
472 eTags
->obj_id
= tags
.obj_id
;
473 eTags
->chunk_id
= tags
.chunk_id
;
474 eTags
->n_bytes
= tags
.n_bytes_lsb
;
476 if (dev
->data_bytes_per_chunk
>= 1024)
477 eTags
->n_bytes
|= (((unsigned) tags
.n_bytes_msb
) << 10);
479 eTags
->serial_number
= tags
.serial_number
;
489 int yaffs_tags_compat_mark_bad(struct yaffs_dev_s
*dev
,
495 memset(&spare
, 0xff, sizeof(yaffs_spare
));
497 spare
.block_status
= 'Y';
499 yaffs_wr_nand(dev
, flash_block
* dev
->param
.chunks_per_block
, NULL
,
501 yaffs_wr_nand(dev
, flash_block
* dev
->param
.chunks_per_block
+ 1,
508 int yaffs_tags_compat_query_block(struct yaffs_dev_s
*dev
,
510 yaffs_block_state_t
*state
,
514 yaffs_spare spare0
, spare1
;
515 static yaffs_spare spareFF
;
517 yaffs_ecc_result dummy
;
520 memset(&spareFF
, 0xFF, sizeof(spareFF
));
526 yaffs_rd_chunk_nand(dev
, block_no
* dev
->param
.chunks_per_block
, NULL
,
528 yaffs_rd_chunk_nand(dev
, block_no
* dev
->param
.chunks_per_block
+ 1, NULL
,
531 if (yaffs_count_bits(spare0
.block_status
& spare1
.block_status
) < 7)
532 *state
= YAFFS_BLOCK_STATE_DEAD
;
533 else if (memcmp(&spareFF
, &spare0
, sizeof(spareFF
)) == 0)
534 *state
= YAFFS_BLOCK_STATE_EMPTY
;
536 *state
= YAFFS_BLOCK_STATE_NEEDS_SCANNING
;