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