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