uboot/fs/yaffs2/yaffs_tagscompat.c
<<
>>
Prefs
   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
  25void 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
  31void 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
  51int 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
  81static 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
  98static 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
 121static void yaffs_spare_init(struct yaffs_spare *spare)
 122{
 123        memset(spare, 0xff, sizeof(struct yaffs_spare));
 124}
 125
 126static 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
 140static 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
 262static 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
 280int 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
 312int 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
 361int 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
 377int 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