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