1e0a1a1b195d583316b8e414e9642e80c2e779cb
[yaffs2.git] / yaffs_tagscompat.c
1 /*
2  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3  *
4  * Copyright (C) 2002-2007 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
19 static void yaffs_HandleReadDataError(yaffs_Device *dev, int chunkInNAND);
20 #ifdef NOTYET
21 static void yaffs_CheckWrittenBlock(yaffs_Device *dev, int chunkInNAND);
22 static void yaffs_HandleWriteChunkOk(yaffs_Device *dev, int chunkInNAND,
23                                      const __u8 *data,
24                                      const yaffs_Spare *spare);
25 static void yaffs_HandleUpdateChunk(yaffs_Device *dev, int chunkInNAND,
26                                     const yaffs_Spare *spare);
27 static void yaffs_HandleWriteChunkError(yaffs_Device *dev, int chunkInNAND);
28 #endif
29
30 static const char yaffs_countBitsTable[256] = {
31         0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
32         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
33         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
34         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
35         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
36         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
37         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
38         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
39         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
40         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
41         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
42         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
43         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
44         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
45         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
46         4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
47 };
48
49 int yaffs_CountBits(__u8 x)
50 {
51         int retVal;
52         retVal = yaffs_countBitsTable[x];
53         return retVal;
54 }
55
56 /********** Tags ECC calculations  *********/
57
58 void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)
59 {
60         yaffs_ECCCalculate(data, spare->ecc1);
61         yaffs_ECCCalculate(&data[256], spare->ecc2);
62 }
63
64 void yaffs_CalcTagsECC(yaffs_Tags *tags)
65 {
66         /* Calculate an ecc */
67
68         unsigned char *b = ((yaffs_TagsUnion *) tags)->asBytes;
69         unsigned i, j;
70         unsigned ecc = 0;
71         unsigned bit = 0;
72
73         tags->ecc = 0;
74
75         for (i = 0; i < 8; i++) {
76                 for (j = 1; j & 0xff; j <<= 1) {
77                         bit++;
78                         if (b[i] & j)
79                                 ecc ^= bit;
80                 }
81         }
82
83         tags->ecc = ecc;
84
85 }
86
87 int yaffs_CheckECCOnTags(yaffs_Tags *tags)
88 {
89         unsigned ecc = tags->ecc;
90
91         yaffs_CalcTagsECC(tags);
92
93         ecc ^= tags->ecc;
94
95         if (ecc && ecc <= 64) {
96                 /* TODO: Handle the failure better. Retire? */
97                 unsigned char *b = ((yaffs_TagsUnion *) tags)->asBytes;
98
99                 ecc--;
100
101                 b[ecc / 8] ^= (1 << (ecc & 7));
102
103                 /* Now recvalc the ecc */
104                 yaffs_CalcTagsECC(tags);
105
106                 return 1;       /* recovered error */
107         } else if (ecc) {
108                 /* Wierd ecc failure value */
109                 /* TODO Need to do somethiong here */
110                 return -1;      /* unrecovered error */
111         }
112
113         return 0;
114 }
115
116 /********** Tags **********/
117
118 static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr,
119                                 yaffs_Tags *tagsPtr)
120 {
121         yaffs_TagsUnion *tu = (yaffs_TagsUnion *) tagsPtr;
122
123         yaffs_CalcTagsECC(tagsPtr);
124
125         sparePtr->tagByte0 = tu->asBytes[0];
126         sparePtr->tagByte1 = tu->asBytes[1];
127         sparePtr->tagByte2 = tu->asBytes[2];
128         sparePtr->tagByte3 = tu->asBytes[3];
129         sparePtr->tagByte4 = tu->asBytes[4];
130         sparePtr->tagByte5 = tu->asBytes[5];
131         sparePtr->tagByte6 = tu->asBytes[6];
132         sparePtr->tagByte7 = tu->asBytes[7];
133 }
134
135 static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,
136                                 yaffs_Tags *tagsPtr)
137 {
138         yaffs_TagsUnion *tu = (yaffs_TagsUnion *) tagsPtr;
139         int result;
140
141         tu->asBytes[0] = sparePtr->tagByte0;
142         tu->asBytes[1] = sparePtr->tagByte1;
143         tu->asBytes[2] = sparePtr->tagByte2;
144         tu->asBytes[3] = sparePtr->tagByte3;
145         tu->asBytes[4] = sparePtr->tagByte4;
146         tu->asBytes[5] = sparePtr->tagByte5;
147         tu->asBytes[6] = sparePtr->tagByte6;
148         tu->asBytes[7] = sparePtr->tagByte7;
149
150         result = yaffs_CheckECCOnTags(tagsPtr);
151         if (result > 0)
152                 dev->tagsEccFixed++;
153         else if (result < 0)
154                 dev->tagsEccUnfixed++;
155 }
156
157 static void yaffs_SpareInitialise(yaffs_Spare *spare)
158 {
159         memset(spare, 0xFF, sizeof(yaffs_Spare));
160 }
161
162 static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,
163                                 int chunkInNAND, const __u8 *data,
164                                 yaffs_Spare *spare)
165 {
166         if (chunkInNAND < dev->startBlock * dev->nChunksPerBlock) {
167                 T(YAFFS_TRACE_ERROR,
168                   (TSTR("**>> yaffs chunk %d is not valid" TENDSTR),
169                    chunkInNAND));
170                 return YAFFS_FAIL;
171         }
172
173         dev->nPageWrites++;
174         return dev->writeChunkToNAND(dev, chunkInNAND, data, spare);
175 }
176
177 static int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,
178                                    int chunkInNAND,
179                                    __u8 *data,
180                                    yaffs_Spare *spare,
181                                    yaffs_ECCResult *eccResult,
182                                    int doErrorCorrection)
183 {
184         int retVal;
185         yaffs_Spare localSpare;
186
187         dev->nPageReads++;
188
189         if (!spare && data) {
190                 /* If we don't have a real spare, then we use a local one. */
191                 /* Need this for the calculation of the ecc */
192                 spare = &localSpare;
193         }
194
195         if (!dev->useNANDECC) {
196                 retVal = dev->readChunkFromNAND(dev, chunkInNAND, data, spare);
197                 if (data && doErrorCorrection) {
198                         /* Do ECC correction */
199                         /* Todo handle any errors */
200                         int eccResult1, eccResult2;
201                         __u8 calcEcc[3];
202
203                         yaffs_ECCCalculate(data, calcEcc);
204                         eccResult1 =
205                             yaffs_ECCCorrect(data, spare->ecc1, calcEcc);
206                         yaffs_ECCCalculate(&data[256], calcEcc);
207                         eccResult2 =
208                             yaffs_ECCCorrect(&data[256], spare->ecc2, calcEcc);
209
210                         if (eccResult1 > 0) {
211                                 T(YAFFS_TRACE_ERROR,
212                                   (TSTR
213                                    ("**>>yaffs ecc error fix performed on chunk %d:0"
214                                     TENDSTR), chunkInNAND));
215                                 dev->eccFixed++;
216                         } else if (eccResult1 < 0) {
217                                 T(YAFFS_TRACE_ERROR,
218                                   (TSTR
219                                    ("**>>yaffs ecc error unfixed on chunk %d:0"
220                                     TENDSTR), chunkInNAND));
221                                 dev->eccUnfixed++;
222                         }
223
224                         if (eccResult2 > 0) {
225                                 T(YAFFS_TRACE_ERROR,
226                                   (TSTR
227                                    ("**>>yaffs ecc error fix performed on chunk %d:1"
228                                     TENDSTR), chunkInNAND));
229                                 dev->eccFixed++;
230                         } else if (eccResult2 < 0) {
231                                 T(YAFFS_TRACE_ERROR,
232                                   (TSTR
233                                    ("**>>yaffs ecc error unfixed on chunk %d:1"
234                                     TENDSTR), chunkInNAND));
235                                 dev->eccUnfixed++;
236                         }
237
238                         if (eccResult1 || eccResult2) {
239                                 /* We had a data problem on this page */
240                                 yaffs_HandleReadDataError(dev, chunkInNAND);
241                         }
242
243                         if (eccResult1 < 0 || eccResult2 < 0)
244                                 *eccResult = YAFFS_ECC_RESULT_UNFIXED;
245                         else if (eccResult1 > 0 || eccResult2 > 0)
246                                 *eccResult = YAFFS_ECC_RESULT_FIXED;
247                         else
248                                 *eccResult = YAFFS_ECC_RESULT_NO_ERROR;
249                 }
250         } else {
251                 /* Must allocate enough memory for spare+2*sizeof(int) */
252                 /* for ecc results from device. */
253                 struct yaffs_NANDSpare nspare;
254
255                 memset(&nspare, 0, sizeof(nspare));
256
257                 retVal = dev->readChunkFromNAND(dev, chunkInNAND, data,
258                                         (yaffs_Spare *) &nspare);
259                 memcpy(spare, &nspare, sizeof(yaffs_Spare));
260                 if (data && doErrorCorrection) {
261                         if (nspare.eccres1 > 0) {
262                                 T(YAFFS_TRACE_ERROR,
263                                   (TSTR
264                                    ("**>>mtd ecc error fix performed on chunk %d:0"
265                                     TENDSTR), chunkInNAND));
266                         } else if (nspare.eccres1 < 0) {
267                                 T(YAFFS_TRACE_ERROR,
268                                   (TSTR
269                                    ("**>>mtd ecc error unfixed on chunk %d:0"
270                                     TENDSTR), chunkInNAND));
271                         }
272
273                         if (nspare.eccres2 > 0) {
274                                 T(YAFFS_TRACE_ERROR,
275                                   (TSTR
276                                    ("**>>mtd ecc error fix performed on chunk %d:1"
277                                     TENDSTR), chunkInNAND));
278                         } else if (nspare.eccres2 < 0) {
279                                 T(YAFFS_TRACE_ERROR,
280                                   (TSTR
281                                    ("**>>mtd ecc error unfixed on chunk %d:1"
282                                     TENDSTR), chunkInNAND));
283                         }
284
285                         if (nspare.eccres1 || nspare.eccres2) {
286                                 /* We had a data problem on this page */
287                                 yaffs_HandleReadDataError(dev, chunkInNAND);
288                         }
289
290                         if (nspare.eccres1 < 0 || nspare.eccres2 < 0)
291                                 *eccResult = YAFFS_ECC_RESULT_UNFIXED;
292                         else if (nspare.eccres1 > 0 || nspare.eccres2 > 0)
293                                 *eccResult = YAFFS_ECC_RESULT_FIXED;
294                         else
295                                 *eccResult = YAFFS_ECC_RESULT_NO_ERROR;
296
297                 }
298         }
299         return retVal;
300 }
301
302 #ifdef NOTYET
303 static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,
304                                   int chunkInNAND)
305 {
306         static int init;
307         static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];
308         static __u8 data[YAFFS_BYTES_PER_CHUNK];
309         /* Might as well always allocate the larger size for */
310         /* dev->useNANDECC == true; */
311         static __u8 spare[sizeof(struct yaffs_NANDSpare)];
312
313         dev->readChunkFromNAND(dev, chunkInNAND, data, (yaffs_Spare *) spare);
314
315         if (!init) {
316                 memset(cmpbuf, 0xff, YAFFS_BYTES_PER_CHUNK);
317                 init = 1;
318         }
319
320         if (memcmp(cmpbuf, data, YAFFS_BYTES_PER_CHUNK))
321                 return YAFFS_FAIL;
322         if (memcmp(cmpbuf, spare, 16))
323                 return YAFFS_FAIL;
324
325         return YAFFS_OK;
326
327 }
328 #endif
329
330 /*
331  * Functions for robustisizing
332  */
333
334 static void yaffs_HandleReadDataError(yaffs_Device *dev, int chunkInNAND)
335 {
336         int blockInNAND = chunkInNAND / dev->nChunksPerBlock;
337
338         /* Mark the block for retirement */
339         yaffs_GetBlockInfo(dev, blockInNAND + dev->blockOffset)->needsRetiring = 1;
340         T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
341           (TSTR("**>>Block %d marked for retirement" TENDSTR), blockInNAND));
342
343         /* TODO:
344          * Just do a garbage collection on the affected block
345          * then retire the block
346          * NB recursion
347          */
348 }
349
350 #ifdef NOTYET
351 static void yaffs_CheckWrittenBlock(yaffs_Device *dev, int chunkInNAND)
352 {
353 }
354
355 static void yaffs_HandleWriteChunkOk(yaffs_Device *dev, int chunkInNAND,
356                                      const __u8 *data,
357                                      const yaffs_Spare *spare)
358 {
359 }
360
361 static void yaffs_HandleUpdateChunk(yaffs_Device *dev, int chunkInNAND,
362                                     const yaffs_Spare *spare)
363 {
364 }
365
366 static void yaffs_HandleWriteChunkError(yaffs_Device *dev, int chunkInNAND)
367 {
368         int blockInNAND = chunkInNAND / dev->nChunksPerBlock;
369
370         /* Mark the block for retirement */
371         yaffs_GetBlockInfo(dev, blockInNAND)->needsRetiring = 1;
372         /* Delete the chunk */
373         yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__);
374 }
375
376 static int yaffs_VerifyCompare(const __u8 *d0, const __u8 *d1,
377                                const yaffs_Spare *s0, const yaffs_Spare *s1)
378 {
379
380         if (memcmp(d0, d1, YAFFS_BYTES_PER_CHUNK) != 0 ||
381             s0->tagByte0 != s1->tagByte0 ||
382             s0->tagByte1 != s1->tagByte1 ||
383             s0->tagByte2 != s1->tagByte2 ||
384             s0->tagByte3 != s1->tagByte3 ||
385             s0->tagByte4 != s1->tagByte4 ||
386             s0->tagByte5 != s1->tagByte5 ||
387             s0->tagByte6 != s1->tagByte6 ||
388             s0->tagByte7 != s1->tagByte7 ||
389             s0->ecc1[0] != s1->ecc1[0] ||
390             s0->ecc1[1] != s1->ecc1[1] ||
391             s0->ecc1[2] != s1->ecc1[2] ||
392             s0->ecc2[0] != s1->ecc2[0] ||
393             s0->ecc2[1] != s1->ecc2[1] || s0->ecc2[2] != s1->ecc2[2]) {
394                 return 0;
395         }
396
397         return 1;
398 }
399 #endif                          /* NOTYET */
400
401 int yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(yaffs_Device *dev,
402                                                 int chunkInNAND,
403                                                 const __u8 *data,
404                                                 const yaffs_ExtendedTags *eTags)
405 {
406         yaffs_Spare spare;
407         yaffs_Tags tags;
408
409         yaffs_SpareInitialise(&spare);
410
411         if (eTags->chunkDeleted) {
412                 spare.pageStatus = 0;
413         } else {
414                 tags.objectId = eTags->objectId;
415                 tags.chunkId = eTags->chunkId;
416
417                 tags.byteCountLSB = eTags->byteCount & 0x3ff;
418
419                 if (dev->nDataBytesPerChunk >= 1024) {
420                         tags.byteCountMSB = (eTags->byteCount >> 10) & 3;
421                 } else {
422                         tags.byteCountMSB = 3;
423                 }
424
425
426                 tags.serialNumber = eTags->serialNumber;
427
428                 if (!dev->useNANDECC && data) {
429                         yaffs_CalcECC(data, &spare);
430                 }
431                 yaffs_LoadTagsIntoSpare(&spare, &tags);
432
433         }
434
435         return yaffs_WriteChunkToNAND(dev, chunkInNAND, data, &spare);
436 }
437
438 int yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(yaffs_Device *dev,
439                                                      int chunkInNAND,
440                                                      __u8 *data,
441                                                      yaffs_ExtendedTags *eTags)
442 {
443
444         yaffs_Spare spare;
445         yaffs_Tags tags;
446         yaffs_ECCResult eccResult = YAFFS_ECC_RESULT_UNKNOWN;
447
448         static yaffs_Spare spareFF;
449         static int init;
450
451         if (!init) {
452                 memset(&spareFF, 0xFF, sizeof(spareFF));
453                 init = 1;
454         }
455
456         if (yaffs_ReadChunkFromNAND
457             (dev, chunkInNAND, data, &spare, &eccResult, 1)) {
458                 /* eTags may be NULL */
459                 if (eTags) {
460
461                         int deleted =
462                             (yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0;
463
464                         eTags->chunkDeleted = deleted;
465                         eTags->eccResult = eccResult;
466                         eTags->blockBad = 0;    /* We're reading it */
467                         /* therefore it is not a bad block */
468                         eTags->chunkUsed =
469                             (memcmp(&spareFF, &spare, sizeof(spareFF)) !=
470                              0) ? 1 : 0;
471
472                         if (eTags->chunkUsed) {
473                                 yaffs_GetTagsFromSpare(dev, &spare, &tags);
474
475                                 eTags->objectId = tags.objectId;
476                                 eTags->chunkId = tags.chunkId;
477                                 eTags->byteCount = tags.byteCountLSB;
478
479                                 if (dev->nDataBytesPerChunk >= 1024)
480                                         eTags->byteCount |= (((unsigned) tags.byteCountMSB) << 10);
481
482                                 eTags->serialNumber = tags.serialNumber;
483                         }
484                 }
485
486                 return YAFFS_OK;
487         } else {
488                 return YAFFS_FAIL;
489         }
490 }
491
492 int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev,
493                                             int blockInNAND)
494 {
495
496         yaffs_Spare spare;
497
498         memset(&spare, 0xff, sizeof(yaffs_Spare));
499
500         spare.blockStatus = 'Y';
501
502         yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock, NULL,
503                                &spare);
504         yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock + 1,
505                                NULL, &spare);
506
507         return YAFFS_OK;
508
509 }
510
511 int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev,
512                                           int blockNo,
513                                           yaffs_BlockState *state,
514                                           __u32 *sequenceNumber)
515 {
516
517         yaffs_Spare spare0, spare1;
518         static yaffs_Spare spareFF;
519         static int init;
520         yaffs_ECCResult dummy;
521
522         if (!init) {
523                 memset(&spareFF, 0xFF, sizeof(spareFF));
524                 init = 1;
525         }
526
527         *sequenceNumber = 0;
528
529         yaffs_ReadChunkFromNAND(dev, blockNo * dev->nChunksPerBlock, NULL,
530                                 &spare0, &dummy, 1);
531         yaffs_ReadChunkFromNAND(dev, blockNo * dev->nChunksPerBlock + 1, NULL,
532                                 &spare1, &dummy, 1);
533
534         if (yaffs_CountBits(spare0.blockStatus & spare1.blockStatus) < 7)
535                 *state = YAFFS_BLOCK_STATE_DEAD;
536         else if (memcmp(&spareFF, &spare0, sizeof(spareFF)) == 0)
537                 *state = YAFFS_BLOCK_STATE_EMPTY;
538         else
539                 *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
540
541         return YAFFS_OK;
542 }