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