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 20static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); 21 22 23/********** Tags ECC calculations *********/ 24 25 26void 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 46int 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 76static 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 93static 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 116static void yaffs_spare_init(struct yaffs_spare *spare) 117{ 118 memset(spare, 0xff, sizeof(struct yaffs_spare)); 119} 120 121static 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 132static 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 219static 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 237static 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 271static 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 320static 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 336static 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 369void 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} 382