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