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