Subversion Repositories freemyipod

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 theseven 1
//
2
//
3
//    Copyright 2010 TheSeven
4
//
5
//
6
//    This file is part of emBIOS.
7
//
8
//    emBIOS is free software: you can redistribute it and/or
9
//    modify it under the terms of the GNU General Public License as
10
//    published by the Free Software Foundation, either version 2 of the
11
//    License, or (at your option) any later version.
12
//
13
//    emBIOS is distributed in the hope that it will be useful,
14
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
15
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
//    See the GNU General Public License for more details.
17
//
18
//    You should have received a copy of the GNU General Public License along
19
//    with emBIOS.  If not, see <http://www.gnu.org/licenses/>.
20
//
21
//
22
 
23
 
24
 
25
#include "global.h"
26
#include "nand.h"
54 theseven 27
#include "ftl.h"
2 theseven 28
#include "util.h"
54 theseven 29
#include "thread.h"
30
#include "panic.h"
31
#include "debug.h"
124 theseven 32
#include "console.h"
33
#include "progressbar.h"
34
#include "lcdconsole.h"
2 theseven 35
 
36
 
37
 
54 theseven 38
#define FTL_COPYBUF_SIZE 4
39
#define FTL_WRITESPARE_SIZE 32
2 theseven 40
//#define FTL_FORCEMOUNT
41
 
42
 
43
 
44
#ifdef FTL_FORCEMOUNT
45
#ifndef FTL_READONLY
46
#define FTL_READONLY
47
#endif
48
#endif
49
 
50
 
51
#ifdef FTL_READONLY
54 theseven 52
uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer)
2 theseven 53
{
54
    (void)sector;
55
    (void)count;
56
    (void)buffer;
57
    return -1;
58
}
54 theseven 59
uint32_t ftl_sync(void)
2 theseven 60
{
61
    return 0;
62
}
63
#endif
64
 
65
 
66
 
67
/* Keeps the state of a scattered page block.
68
   This structure is used in memory only, not on flash,
69
   but it equals the one the OFW uses. */
70
struct ftl_log_type
71
{
72
 
73
    /* The ftl_cxt.nextblockusn at the time the block was allocated,
74
       needed in order to be able to remove the oldest ones first. */
75
    uint32_t usn;
76
 
77
    /* The vBlock number at which the scattered pages are stored */
78
    uint16_t scatteredvblock;
79
 
80
    /* the lBlock number for which those pages are */
81
    uint16_t logicalvblock;
82
 
83
    /* Pointer to ftl_offsets, contains the mapping which lPage is
84
       currently stored at which scattered vPage. */
85
    uint16_t* pageoffsets;
86
 
87
    /* Pages used in the vBlock, i.e. next page number to be written */
88
    uint16_t pagesused;
89
 
90
    /* Pages that are still up to date in this block, i.e. need to be
91
       moved when this vBlock is deallocated. */
92
    uint16_t pagescurrent;
93
 
94
    /* A flag whether all pages are still sequential in this block.
95
       Initialized to 1 on allocation, zeroed as soon as anything is
96
       written out of sequence, so that the block will need copying
97
       when committing to get the pages back into the right order.
98
       This is used to half the number of block erases needed when
99
       writing huge amounts of sequential data. */
100
    uint32_t issequential;
101
 
102
} __attribute__((packed));
103
 
104
 
105
/* Keeps the state of the FTL, both on flash and in memory */
106
struct ftl_cxt_type
107
{
108
 
109
    /* Update sequence number of the FTL context, decremented
110
       every time a new revision of FTL meta data is written. */
111
    uint32_t usn;
112
 
113
    /* Update sequence number for user data blocks. Incremented
114
       every time a portion of user pages is written, so that
115
       a consistency check can determine which copy of a user
116
       page is the most recent one. */
117
    uint32_t nextblockusn;
118
 
119
    /* Count of currently free pages in the block pool */
120
    uint16_t freecount;
121
 
54 theseven 122
    /* Index to the first free hyperblock in the blockpool ring buffer */
2 theseven 123
    uint16_t nextfreeidx;
124
 
125
    /* This is a counter that is used to better distribute block
126
       wear. It is incremented on every block erase, and if it
127
       gets too high (300 on writes, 20 on sync), the most and
54 theseven 128
       least worn hyperblock will be swapped (causing an additional
2 theseven 129
       block write) and the counter will be decreased by 20. */
130
    uint16_t swapcounter;
131
 
54 theseven 132
    /* Ring buffer of currently free hyperblocks. nextfreeidx is the
2 theseven 133
       index to freecount free ones, the other ones are currently
54 theseven 134
       allocated for scattered page hyperblocks. */
2 theseven 135
    uint16_t blockpool[0x14];
136
 
137
    /* Alignment to 32 bits */
138
    uint16_t field_36;
139
 
140
    /* vPages where the block map is stored */
141
    uint32_t ftl_map_pages[8];
142
 
143
    /* Probably additional map page number space for bigger chips */
144
    uint8_t field_58[0x28];
145
 
146
    /* vPages where the erase counters are stored */
147
    uint32_t ftl_erasectr_pages[8];
148
 
149
    /* Seems to be padding */
150
    uint8_t field_A0[0x70];
151
 
152
    /* Pointer to ftl_map used by Whimory, not used by us */
153
    uint32_t ftl_map_ptr;
154
 
155
    /* Pointer to ftl_erasectr used by Whimory, not used by us */
156
    uint32_t ftl_erasectr_ptr;
157
 
158
    /* Pointer to ftl_log used by Whimory, not used by us */
159
    uint32_t ftl_log_ptr;
160
 
161
    /* Flag used to indicate that some erase counter pages should be committed
54 theseven 162
       because they were changed more than 100 times since the last commit. */
2 theseven 163
    uint32_t erasedirty;
164
 
165
    /* Seems to be unused */
166
    uint16_t field_120;
167
 
168
    /* vBlocks used to store the FTL context, map, and erase
169
       counter pages. This is also a ring buffer, and the oldest
170
       page gets swapped with the least used page from the block
171
       pool ring buffer when a new one is allocated. */
172
    uint16_t ftlctrlblocks[3];
173
 
174
    /* The last used vPage number from ftlctrlblocks */
175
    uint32_t ftlctrlpage;
176
 
177
    /* Set on context sync, reset on write, so obviously never
178
       zero in the context written to the flash */
179
    uint32_t clean_flag;
180
 
181
    /* Seems to be unused, but gets loaded from flash by Whimory. */
182
    uint8_t field_130[0x15C];
183
 
184
} __attribute__((packed));
185
 
186
 
187
/* Keeps the state of the bank's VFL, both on flash and in memory.
188
   There is one of these per bank. */
189
struct ftl_vfl_cxt_type
190
{
191
 
192
    /* Cross-bank update sequence number, incremented on every VFL
193
       context commit on any bank. */
194
    uint32_t usn;
195
 
196
    /* See ftl_cxt.ftlctrlblocks. This is stored to the VFL contexts
197
       in order to be able to find the most recent FTL context copy
198
       when mounting the FTL. The VFL context number this will be
199
       written to on an FTL context commit is chosen semi-randomly. */
200
    uint16_t ftlctrlblocks[3];
201
 
202
    /* Alignment to 32 bits */
203
    uint8_t field_A[2];
204
 
205
    /* Decrementing update counter for VFL context commits per bank */
206
    uint32_t updatecount;
207
 
208
    /* Number of the currently active VFL context block, it's an index
209
       into vflcxtblocks. */
210
    uint16_t activecxtblock;
211
 
54 theseven 212
    /* Number of the first free page in the active VFL context block */
2 theseven 213
    uint16_t nextcxtpage;
214
 
215
    /* Seems to be unused */
216
    uint8_t field_14[4];
217
 
218
    /* Incremented every time a block erase error leads to a remap,
219
       but doesn't seem to be read anywhere. */
220
    uint16_t field_18;
221
 
222
    /* Number of spare blocks used */
223
    uint16_t spareused;
224
 
225
    /* pBlock number of the first spare block */
226
    uint16_t firstspare;
227
 
228
    /* Total number of spare blocks */
229
    uint16_t sparecount;
230
 
231
    /* Block remap table. Contains the vBlock number the n-th spare
232
       block is used as a replacement for. 0 = unused, 0xFFFF = bad. */
233
    uint16_t remaptable[0x334];
234
 
235
    /* Bad block table. Each bit represents 8 blocks. 1 = OK, 0 = Bad.
236
       If the entry is zero, you should look at the remap table to see
237
       if the block is remapped, and if yes, where the replacement is. */
238
    uint8_t bbt[0x11A];
239
 
240
    /* pBlock numbers used to store the VFL context. This is a ring
241
       buffer. On a VFL context write, always 8 pages are written,
242
       and it passes if at least 4 of them can be read back. */
243
    uint16_t vflcxtblocks[4];
244
 
245
    /* Blocks scheduled for remapping are stored at the end of the
246
       remap table. This is the first index used for them. */
247
    uint16_t scheduledstart;
248
 
249
    /* Probably padding */
250
    uint8_t field_7AC[0x4C];
251
 
252
    /* First checksum (addition) */
253
    uint32_t checksum1;
254
 
255
    /* Second checksum (XOR), there is a bug in whimory regarding this. */
256
    uint32_t checksum2;
257
 
258
} __attribute__((packed));
259
 
260
 
261
/* Layout of the spare bytes of each page on the flash */
262
union ftl_spare_data_type
263
{
264
 
265
    /* The layout used for actual user data (types 0x40 and 0x41) */
266
    struct ftl_spare_data_user_type
267
    {
268
 
269
        /* The lPage, i.e. Sector, number */
270
        uint32_t lpn;
271
 
272
        /* The update sequence number of that page,
273
           copied from ftl_cxt.nextblockusn on write */
274
        uint32_t usn;
275
 
276
        /* Seems to be unused */
277
        uint8_t field_8;
278
 
279
        /* Type field, 0x40 (data page) or 0x41 (last data page of block) */
280
        uint8_t type;
281
 
282
        /* ECC mark, usually 0xFF. If an error occurred while reading the
283
           page during a copying operation earlier, this will be 0x55. */
284
        uint8_t eccmark;
285
 
286
        /* Seems to be unused */
287
        uint8_t field_B;
288
 
289
        /* ECC data for the user data */
290
        uint8_t dataecc[0x28];
291
 
292
        /* ECC data for the first 0xC bytes above */
293
        uint8_t spareecc[0xC];
294
 
295
    } __attribute__((packed)) user;
296
 
297
    /* The layout used for meta data (other types) */
298
    struct ftl_spare_data_meta_type
299
    {
300
 
301
        /* ftl_cxt.usn for FTL stuff, ftl_vfl_cxt.updatecount for VFL stuff */
302
        uint32_t usn;
303
 
304
        /* Index of the thing inside the page,
305
           for example number / index of the map or erase counter page */
306
        uint16_t idx;
307
 
308
        /* Seems to be unused */
309
        uint8_t field_6;
310
 
311
        /* Seems to be unused */
312
        uint8_t field_7;
313
 
314
        /* Seems to be unused */
315
        uint8_t field_8;
316
 
317
       /* Type field:
318
            0x43: FTL context page
319
            0x44: Block map page
320
            0x46: Erase counter page
321
            0x47: "FTL is currently mounted", i.e. unclean shutdown, mark
322
            0x80: VFL context page */
323
        uint8_t type;
324
 
325
        /* ECC mark, usually 0xFF. If an error occurred while reading the
326
           page during a copying operation earlier, this will be 0x55. */
327
        uint8_t eccmark;
328
 
329
        /* Seems to be unused */
330
        uint8_t field_B;
331
 
332
        /* ECC data for the user data */
333
        uint8_t dataecc[0x28];
334
 
335
        /* ECC data for the first 0xC bytes above */
336
        uint8_t spareecc[0xC];
337
 
338
    } __attribute__((packed)) meta;
339
 
340
};
341
 
342
 
343
/* Keeps track of troublesome blocks, only in memory, lost on unmount. */
344
struct ftl_trouble_type
345
{
346
 
347
    /* vBlock number of the block giving trouble */
348
    uint16_t block;
349
 
350
    /* Bank of the block giving trouble */
351
    uint8_t bank;
352
 
353
    /* Error counter, incremented by 3 on error, decremented by 1 on erase,
354
       remaping will be done when it reaches 6. */
355
    uint8_t errors;
356
 
357
} __attribute__((packed));
358
 
359
 
360
 
361
/* Pointer to an info structure regarding the flash type used */
362
const struct nand_device_info_type* ftl_nand_type;
363
 
364
/* Number of banks we detected a chip on */
365
uint32_t ftl_banks;
366
 
367
/* Block map, used vor pBlock to vBlock mapping */
124 theseven 368
static uint16_t ftl_map[0x2000] CACHEALIGN_ATTR;
2 theseven 369
 
370
/* VFL context for each bank */
66 theseven 371
static struct ftl_vfl_cxt_type ftl_vfl_cxt[4];
2 theseven 372
 
373
/* FTL context */
124 theseven 374
static struct ftl_cxt_type ftl_cxt CACHEALIGN_ATTR;
2 theseven 375
 
376
/* Temporary data buffers for internal use by the FTL */
66 theseven 377
static uint8_t ftl_buffer[0x800] CACHEALIGN_ATTR;
2 theseven 378
 
379
/* Temporary spare byte buffer for internal use by the FTL */
66 theseven 380
static union ftl_spare_data_type ftl_sparebuffer[FTL_WRITESPARE_SIZE] CACHEALIGN_ATTR;
2 theseven 381
 
382
 
383
#ifndef FTL_READONLY
384
 
385
/* Lowlevel BBT for each bank */
66 theseven 386
static uint8_t ftl_bbt[4][0x410];
2 theseven 387
 
54 theseven 388
/* Erase counters for the vBlocks */
124 theseven 389
static uint16_t ftl_erasectr[0x2000] CACHEALIGN_ATTR;
2 theseven 390
 
391
/* Used by ftl_log */
66 theseven 392
static uint16_t ftl_offsets[0x11][0x200];
2 theseven 393
 
394
/* Structs keeping record of scattered page blocks */
66 theseven 395
static struct ftl_log_type ftl_log[0x11];
2 theseven 396
 
397
/* Global cross-bank update sequence number of the VFL context */
66 theseven 398
static uint32_t ftl_vfl_usn;
2 theseven 399
 
400
/* Keeps track (temporarily) of troublesome blocks */
66 theseven 401
static struct ftl_trouble_type ftl_troublelog[5];
2 theseven 402
 
403
/* Counts erase counter page changes, after 100 of them the affected
404
   page will be committed to the flash. */
66 theseven 405
static uint8_t ftl_erasectr_dirt[8];
2 theseven 406
 
407
/* Buffer needed for copying pages around while moving or committing blocks.
408
   This can't be shared with ftl_buffer, because this one could be overwritten
409
   during the copying operation in order to e.g. commit a CXT. */
66 theseven 410
static uint8_t ftl_copybuffer[FTL_COPYBUF_SIZE][0x800] CACHEALIGN_ATTR;
411
static union ftl_spare_data_type ftl_copyspare[FTL_COPYBUF_SIZE] CACHEALIGN_ATTR;
2 theseven 412
 
413
/* Needed to store the old scattered page offsets in order to be able to roll
414
   back if something fails while compacting a scattered page block. */
66 theseven 415
static uint16_t ftl_offsets_backup[0x200] CACHEALIGN_ATTR;
2 theseven 416
 
124 theseven 417
/* Buffers needed for FTL recovery */
418
static uint32_t blk_usn[0x2000] INITBSS_ATTR;
419
static uint8_t blk_type[0x2000] INITBSS_ATTR;
420
static uint32_t erasectr_usn[8] INITBSS_ATTR;
421
static uint32_t pageusn[0x200] INITBSS_ATTR;
422
static uint8_t pagedata[0x200][0x800] INITBSS_ATTR CACHEALIGN_ATTR;
423
 
424
/* State information needed for FTL recovery */
425
static uint32_t meta_usn INITBSS_ATTR;
426
static uint32_t user_usn INITBSS_ATTR;
427
static uint32_t allocmode INITBSS_ATTR;
428
static uint32_t firstfree INITBSS_ATTR;
429
 
2 theseven 430
#endif
431
 
432
 
54 theseven 433
static struct mutex ftl_mtx;
210 theseven 434
bool ftl_initialized = false;
2 theseven 435
 
436
 
437
 
438
/* Finds a device info page for the specified bank and returns its number.
439
   Used to check if one is present, and to read the lowlevel BBT. */
124 theseven 440
static uint32_t ftl_find_devinfo(uint32_t bank) INITCODE_ATTR;
66 theseven 441
static uint32_t ftl_find_devinfo(uint32_t bank)
2 theseven 442
{
443
    /* Scan the last 10% of the flash for device info pages */
54 theseven 444
    uint32_t lowestBlock = ftl_nand_type->blocks
445
                         - (ftl_nand_type->blocks / 10);
2 theseven 446
    uint32_t block, page, pagenum;
54 theseven 447
    for (block = ftl_nand_type->blocks - 1; block >= lowestBlock; block--)
2 theseven 448
    {
54 theseven 449
        page = ftl_nand_type->pagesperblock - 8;
450
        for (; page < ftl_nand_type->pagesperblock; page++)
2 theseven 451
        {
54 theseven 452
            pagenum = block * ftl_nand_type->pagesperblock + page;
2 theseven 453
            if ((nand_read_page(bank, pagenum, ftl_buffer,
54 theseven 454
                                &ftl_sparebuffer[0], 1, 0) & 0x11F) != 0)
2 theseven 455
                continue;
456
            if (memcmp(ftl_buffer, "DEVICEINFOSIGN\0", 0x10) == 0)
457
                return pagenum;
458
        }
459
    }
460
    return 0;
461
}
462
 
463
 
464
/* Checks if all banks have proper device info pages */
124 theseven 465
static uint32_t ftl_has_devinfo(void) INITCODE_ATTR;
66 theseven 466
static uint32_t ftl_has_devinfo(void)
2 theseven 467
{
468
    uint32_t i;
469
    for (i = 0; i < ftl_banks; i++) if (ftl_find_devinfo(i) == 0) return 0;
470
    return 1;
471
}
472
 
473
 
474
/* Loads the lowlevel BBT for a bank to the specified buffer.
475
   This is based on some cryptic disassembly and not fully understood yet. */
124 theseven 476
static uint32_t ftl_load_bbt(uint32_t bank, uint8_t* bbt) INITCODE_ATTR;
66 theseven 477
static uint32_t ftl_load_bbt(uint32_t bank, uint8_t* bbt)
2 theseven 478
{
479
    uint32_t i, j;
480
    uint32_t pagebase, page = ftl_find_devinfo(bank), page2;
481
    uint32_t unk1, unk2, unk3;
482
    if (page == 0) return 1;
54 theseven 483
    pagebase = page & ~(ftl_nand_type->pagesperblock - 1);
2 theseven 484
    if ((nand_read_page(bank, page, ftl_buffer,
166 theseven 485
                        NULL, 1, 0) & 0x11F) != 0) return 1;
2 theseven 486
    if (memcmp(&ftl_buffer[0x18], "BBT", 4) != 0) return 1;
487
    unk1 = ((uint16_t*)ftl_buffer)[0x10];
488
    unk2 = ((uint16_t*)ftl_buffer)[0x11];
54 theseven 489
    unk3 = ((uint16_t*)ftl_buffer)[((uint32_t*)ftl_buffer)[4] * 6 + 10]
490
         + ((uint16_t*)ftl_buffer)[((uint32_t*)ftl_buffer)[4] * 6 + 11];
2 theseven 491
    for (i = 0; i < unk1; i++)
492
    {
493
        for (j = 0; ; j++)
494
        {
495
            page2 = unk2 + i + unk3 * j;
54 theseven 496
            if (page2 >= (uint32_t)(ftl_nand_type->pagesperblock - 8))
2 theseven 497
                break;
498
            if ((nand_read_page(bank, pagebase + page2, ftl_buffer,
166 theseven 499
                                NULL, 1, 0) & 0x11F) == 0)
2 theseven 500
            {
501
                memcpy(bbt, ftl_buffer, 0x410);
502
                return 0;
503
            }
504
        }
505
    }
506
    return 1;
507
}
508
 
509
 
510
/* Calculates the checksums for the VFL context page of the specified bank */
66 theseven 511
static void ftl_vfl_calculate_checksum(uint32_t bank,
512
                                       uint32_t* checksum1, uint32_t* checksum2)
2 theseven 513
{
514
    uint32_t i;
515
    *checksum1 = 0xAABBCCDD;
516
    *checksum2 = 0xAABBCCDD;
517
    for (i = 0; i < 0x1FE; i++)
518
    {
519
        *checksum1 += ((uint32_t*)(&ftl_vfl_cxt[bank]))[i];
520
        *checksum2 ^= ((uint32_t*)(&ftl_vfl_cxt[bank]))[i];
521
    }
522
}
523
 
524
 
525
/* Checks if the checksums of the VFL context
526
   of the specified bank are correct */
66 theseven 527
static uint32_t ftl_vfl_verify_checksum(uint32_t bank)
2 theseven 528
{
529
    uint32_t checksum1, checksum2;
530
    ftl_vfl_calculate_checksum(bank, &checksum1, &checksum2);
531
    if (checksum1 == ftl_vfl_cxt[bank].checksum1) return 0;
532
    /* The following line is pretty obviously a bug in Whimory,
533
       but we do it the same way for compatibility. */
534
    if (checksum2 != ftl_vfl_cxt[bank].checksum2) return 0;
54 theseven 535
    DEBUGF("FTL: Bad VFL CXT checksum on bank %d!", bank);
2 theseven 536
    return 1;
537
}
538
 
539
 
540
#ifndef FTL_READONLY
541
/* Updates the checksums of the VFL context of the specified bank */
66 theseven 542
static void ftl_vfl_update_checksum(uint32_t bank)
2 theseven 543
{
544
    ftl_vfl_calculate_checksum(bank, &ftl_vfl_cxt[bank].checksum1,
545
                               &ftl_vfl_cxt[bank].checksum2);
546
}
547
#endif
548
 
549
 
550
#ifndef FTL_READONLY
551
/* Writes 8 copies of the VFL context of the specified bank to flash,
552
   and succeeds if at least 4 can be read back properly. */
66 theseven 553
static uint32_t ftl_vfl_store_cxt(uint32_t bank)
2 theseven 554
{
555
    uint32_t i;
556
    ftl_vfl_cxt[bank].updatecount--;
557
    ftl_vfl_cxt[bank].usn = ++ftl_vfl_usn;
558
    ftl_vfl_cxt[bank].nextcxtpage += 8;
559
    ftl_vfl_update_checksum(bank);
54 theseven 560
    memset(&ftl_sparebuffer[0], 0xFF, 0x40);
561
    ftl_sparebuffer[0].meta.usn = ftl_vfl_cxt[bank].updatecount;
562
    ftl_sparebuffer[0].meta.field_8 = 0;
563
    ftl_sparebuffer[0].meta.type = 0x80;
2 theseven 564
    for (i = 1; i <= 8; i++)
565
    {
566
        uint32_t index = ftl_vfl_cxt[bank].activecxtblock;
567
        uint32_t block = ftl_vfl_cxt[bank].vflcxtblocks[index];
54 theseven 568
        uint32_t page = block * ftl_nand_type->pagesperblock;
2 theseven 569
        page += ftl_vfl_cxt[bank].nextcxtpage - i;
54 theseven 570
        nand_write_page(bank, page, &ftl_vfl_cxt[bank], &ftl_sparebuffer[0], 1);
2 theseven 571
    }
572
    uint32_t good = 0;
54 theseven 573
    for (i = 1; i <= 8; i++)
2 theseven 574
    {
575
        uint32_t index = ftl_vfl_cxt[bank].activecxtblock;
576
        uint32_t block = ftl_vfl_cxt[bank].vflcxtblocks[index];
54 theseven 577
        uint32_t page = block * ftl_nand_type->pagesperblock;
2 theseven 578
        page += ftl_vfl_cxt[bank].nextcxtpage - i;
579
        if ((nand_read_page(bank, page, ftl_buffer,
54 theseven 580
                            &ftl_sparebuffer[0], 1, 0) & 0x11F) != 0)
2 theseven 581
            continue;
582
        if (memcmp(ftl_buffer, &ftl_vfl_cxt[bank], 0x7AC) != 0)
583
            continue;
54 theseven 584
        if (ftl_sparebuffer[0].meta.usn != ftl_vfl_cxt[bank].updatecount)
2 theseven 585
            continue;
54 theseven 586
        if (ftl_sparebuffer[0].meta.field_8 == 0
587
         && ftl_sparebuffer[0].meta.type == 0x80) good++;
2 theseven 588
    }
589
    return good > 3 ? 0 : 1;
590
}
591
#endif
592
 
593
 
594
#ifndef FTL_READONLY
595
/* Commits the VFL context of the specified bank to flash,
596
   retries until it works or all available pages have been tried */
66 theseven 597
static uint32_t ftl_vfl_commit_cxt(uint32_t bank)
2 theseven 598
{
61 theseven 599
    DEBUGF("FTL: VFL: Committing context on bank %d", bank);
54 theseven 600
    if (ftl_vfl_cxt[bank].nextcxtpage + 8 <= ftl_nand_type->pagesperblock)
2 theseven 601
        if (ftl_vfl_store_cxt(bank) == 0) return 0;
602
    uint32_t current = ftl_vfl_cxt[bank].activecxtblock;
603
    uint32_t i = current, j;
604
    while (1)
605
    {
606
        i = (i + 1) & 3;
607
        if (i == current) break;
608
        if (ftl_vfl_cxt[bank].vflcxtblocks[i] == 0xFFFF) continue;
609
        for (j = 0; j < 4; j++)
610
            if (nand_block_erase(bank, ftl_vfl_cxt[bank].vflcxtblocks[i]
54 theseven 611
                                     * ftl_nand_type->pagesperblock) == 0)
2 theseven 612
                break;
613
        if (j == 4) continue;
614
        ftl_vfl_cxt[bank].activecxtblock = i;
615
        ftl_vfl_cxt[bank].nextcxtpage = 0;
616
        if (ftl_vfl_store_cxt(bank) == 0) return 0;
617
    }
249 theseven 618
    ftl_initialized = false;
619
    panicf(PANIC_KILLTHREAD, "VFL: Failed to commit VFL CXT!");
2 theseven 620
    return 1;
621
}
622
#endif
623
 
624
 
625
/* Returns a pointer to the most recently updated VFL context,
626
   used to find out the current FTL context vBlock numbers
627
   (planetbeing's "maxthing") */
124 theseven 628
static struct ftl_vfl_cxt_type* ftl_vfl_get_newest_cxt(void) INITCODE_ATTR;
66 theseven 629
static struct ftl_vfl_cxt_type* ftl_vfl_get_newest_cxt(void)
2 theseven 630
{
631
    uint32_t i, maxusn;
166 theseven 632
    struct ftl_vfl_cxt_type* cxt = NULL;
2 theseven 633
    maxusn = 0;
634
    for (i = 0; i < ftl_banks; i++)
635
        if (ftl_vfl_cxt[i].usn >= maxusn)
636
        {
637
            cxt = &ftl_vfl_cxt[i];
638
            maxusn = ftl_vfl_cxt[i].usn;
639
        }
640
    return cxt;
641
}
642
 
643
 
644
/* Checks if the specified pBlock is marked bad in the supplied lowlevel BBT.
645
   Only used while mounting the VFL. */
124 theseven 646
static uint32_t ftl_is_good_block(uint8_t* bbt, uint32_t block) INITCODE_ATTR;
66 theseven 647
static uint32_t ftl_is_good_block(uint8_t* bbt, uint32_t block)
2 theseven 648
{
649
    if ((bbt[block >> 3] & (1 << (block & 7))) == 0) return 0;
650
    else return 1;
651
}
652
 
653
 
654
/* Checks if the specified vBlock could be remapped */
66 theseven 655
static uint32_t ftl_vfl_is_good_block(uint32_t bank, uint32_t block)
2 theseven 656
{
657
    uint8_t bbtentry = ftl_vfl_cxt[bank].bbt[block >> 6];
658
    if ((bbtentry & (1 << ((7 - (block >> 3)) & 7))) == 0) return 0;
659
    else return 1;
660
}
661
 
662
 
663
#ifndef FTL_READONLY
664
/* Sets or unsets the bad bit of the specified vBlock
665
   in the specified bank's VFL context */
66 theseven 666
static void ftl_vfl_set_good_block(uint32_t bank, uint32_t block, uint32_t isgood)
2 theseven 667
{
668
    uint8_t bit = (1 << ((7 - (block >> 3)) & 7));
669
    if (isgood == 1) ftl_vfl_cxt[bank].bbt[block >> 6] |= bit;
670
    else ftl_vfl_cxt[bank].bbt[block >> 6] &= ~bit;
671
}
672
#endif
673
 
674
 
675
/* Tries to read a VFL context from the specified bank, pBlock and page */
66 theseven 676
static uint32_t ftl_vfl_read_page(uint32_t bank, uint32_t block,
677
                                  uint32_t startpage, void* databuffer,
124 theseven 678
                                  union ftl_spare_data_type* sparebuffer) INITCODE_ATTR;
679
static uint32_t ftl_vfl_read_page(uint32_t bank, uint32_t block,
680
                                  uint32_t startpage, void* databuffer,
66 theseven 681
                                  union ftl_spare_data_type* sparebuffer)
2 theseven 682
{
683
    uint32_t i;
684
    for (i = 0; i < 8; i++)
685
    {
54 theseven 686
        uint32_t page = block * ftl_nand_type->pagesperblock
2 theseven 687
                      + startpage + i;
688
        if ((nand_read_page(bank, page, databuffer,
689
                            sparebuffer, 1, 1) & 0x11F) == 0)
54 theseven 690
            if (sparebuffer->meta.field_8 == 0
691
             && sparebuffer->meta.type == 0x80)
2 theseven 692
                return 0;
693
    }
694
    return 1;
695
}
696
 
697
 
698
/* Translates a bank and vBlock to a pBlock, following remaps */
66 theseven 699
static uint32_t ftl_vfl_get_physical_block(uint32_t bank, uint32_t block)
2 theseven 700
{
701
    if (ftl_vfl_is_good_block(bank, block) == 1) return block;
702
 
703
    uint32_t spareindex;
704
    uint32_t spareused = ftl_vfl_cxt[bank].spareused;
705
    for (spareindex = 0; spareindex < spareused; spareindex++)
706
        if (ftl_vfl_cxt[bank].remaptable[spareindex] == block)
201 theseven 707
        {
61 theseven 708
            DEBUGF("FTL: VFL: Following remapped block: %d => %d",
54 theseven 709
                   block, ftl_vfl_cxt[bank].firstspare + spareindex);
2 theseven 710
            return ftl_vfl_cxt[bank].firstspare + spareindex;
201 theseven 711
        }
2 theseven 712
    return block;
713
}
714
 
715
 
716
#ifndef FTL_READONLY
717
/* Checks if remapping is scheduled for the specified bank and vBlock */
66 theseven 718
static uint32_t ftl_vfl_check_remap_scheduled(uint32_t bank, uint32_t block)
2 theseven 719
{
720
    uint32_t i;
721
    for (i = 0x333; i > 0 && i > ftl_vfl_cxt[bank].scheduledstart; i--)
722
        if (ftl_vfl_cxt[bank].remaptable[i] == block) return 1;
723
    return 0;
724
}
725
#endif
726
 
727
 
728
#ifndef FTL_READONLY
729
/* Schedules remapping for the specified bank and vBlock */
66 theseven 730
static void ftl_vfl_schedule_block_for_remap(uint32_t bank, uint32_t block)
2 theseven 731
{
732
    if (ftl_vfl_check_remap_scheduled(bank, block) == 1) return;
249 theseven 733
    ftl_initialized = false;
734
    panicf(PANIC_KILLTHREAD, "FTL: Scheduling bank %u block %u for remap!",
201 theseven 735
           (unsigned)bank, (unsigned)block);
2 theseven 736
    if (ftl_vfl_cxt[bank].scheduledstart == ftl_vfl_cxt[bank].spareused)
737
        return;
738
    ftl_vfl_cxt[bank].remaptable[--ftl_vfl_cxt[bank].scheduledstart] = block;
739
    ftl_vfl_commit_cxt(bank);
740
}
741
#endif
742
 
743
 
744
#ifndef FTL_READONLY
745
/* Removes the specified bank and vBlock combination
746
   from the remap scheduled list */
66 theseven 747
static void ftl_vfl_mark_remap_done(uint32_t bank, uint32_t block)
2 theseven 748
{
749
    uint32_t i;
750
    uint32_t start = ftl_vfl_cxt[bank].scheduledstart;
751
    uint32_t lastscheduled = ftl_vfl_cxt[bank].remaptable[start];
752
    for (i = 0x333; i > 0 && i > start; i--)
753
        if (ftl_vfl_cxt[bank].remaptable[i] == block)
754
        {
755
            if (i != start && i != 0x333)
756
                ftl_vfl_cxt[bank].remaptable[i] = lastscheduled;
757
            ftl_vfl_cxt[bank].scheduledstart++;
758
            return;
759
        }
760
}
761
#endif
762
 
763
 
764
#ifndef FTL_READONLY
765
/* Logs that there is trouble for the specified vBlock on the specified bank.
766
   The vBlock will be scheduled for remap
767
   if there is too much trouble with it. */
66 theseven 768
static void ftl_vfl_log_trouble(uint32_t bank, uint32_t vblock)
2 theseven 769
{
770
    uint32_t i;
771
    for (i = 0; i < 5; i++)
772
        if (ftl_troublelog[i].block == vblock
773
         && ftl_troublelog[i].bank == bank)
774
        {
775
            ftl_troublelog[i].errors += 3;
776
            if (ftl_troublelog[i].errors > 5)
777
            {
778
                ftl_vfl_schedule_block_for_remap(bank, vblock);
779
                ftl_troublelog[i].block = 0xFFFF;
780
            }
781
            return;
782
        }
783
    for (i = 0; i < 5; i++)
784
        if (ftl_troublelog[i].block == 0xFFFF)
785
        {
786
            ftl_troublelog[i].block = vblock;
787
            ftl_troublelog[i].bank = bank;
788
            ftl_troublelog[i].errors = 3;
789
            return;
790
        }
791
}
792
#endif
793
 
794
 
795
#ifndef FTL_READONLY
796
/* Logs a successful erase for the specified vBlock on the specified bank */
66 theseven 797
static void ftl_vfl_log_success(uint32_t bank, uint32_t vblock)
2 theseven 798
{
799
    uint32_t i;
800
    for (i = 0; i < 5; i++)
801
        if (ftl_troublelog[i].block == vblock
802
         && ftl_troublelog[i].bank == bank)
803
        {
804
            if (--ftl_troublelog[i].errors == 0)
805
                ftl_troublelog[i].block = 0xFFFF;
806
            return;
807
        }
808
}
809
#endif
810
 
811
 
812
#ifndef FTL_READONLY
813
/* Tries to remap the specified vBlock on the specified bank,
814
   not caring about data in there.
815
   If it worked, it will return the new pBlock number,
816
   if not (no more spare blocks available), it will return zero. */
66 theseven 817
static uint32_t ftl_vfl_remap_block(uint32_t bank, uint32_t block)
2 theseven 818
{
819
    uint32_t i;
820
    uint32_t newblock = 0, newidx;
249 theseven 821
    ftl_initialized = false;
822
    panicf(PANIC_KILLTHREAD, "FTL: Remapping bank %u block %u!",
201 theseven 823
           (unsigned)bank, (unsigned)block);
54 theseven 824
    if (bank >= ftl_banks || block >= ftl_nand_type->blocks) return 0;
2 theseven 825
    for (i = 0; i < ftl_vfl_cxt[bank].sparecount; i++)
826
        if (ftl_vfl_cxt[bank].remaptable[i] == 0)
827
        {
828
            newblock = ftl_vfl_cxt[bank].firstspare + i;
829
            newidx = i;
830
            break;
831
        }
832
    if (newblock == 0) return 0;
833
    for (i = 0; i < 9; i++)
834
        if (nand_block_erase(bank,
54 theseven 835
                             newblock * ftl_nand_type->pagesperblock) == 0)
2 theseven 836
            break;
837
    for (i = 0; i < newidx; i++)
838
        if (ftl_vfl_cxt[bank].remaptable[i] == block)
839
            ftl_vfl_cxt[bank].remaptable[i] = 0xFFFF;
840
    ftl_vfl_cxt[bank].remaptable[newidx] = block;
841
    ftl_vfl_cxt[bank].spareused++;
842
    ftl_vfl_set_good_block(bank, block, 0);
843
    return newblock;
844
}
845
#endif
846
 
847
 
54 theseven 848
/* Reads the specified vPage, dealing with all kinds of trouble */
66 theseven 849
static uint32_t ftl_vfl_read(uint32_t vpage, void* buffer, void* sparebuffer,
850
                             uint32_t checkempty, uint32_t remaponfail)
2 theseven 851
{
54 theseven 852
#ifdef VFL_TRACE
61 theseven 853
    DEBUGF("FTL: VFL: Reading page %d", vpage);
54 theseven 854
#endif
855
 
856
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
857
    uint32_t syshyperblocks = ftl_nand_type->blocks
858
                            - ftl_nand_type->userblocks - 0x17;
2 theseven 859
    uint32_t abspage = vpage + ppb * syshyperblocks;
54 theseven 860
    if (abspage >= ftl_nand_type->blocks * ppb || abspage < ppb)
2 theseven 861
    {
58 theseven 862
        DEBUGF("FTL: Trying to read out-of-bounds vPage %u", (unsigned)vpage);
2 theseven 863
        return 4;
864
    }
865
 
866
    uint32_t bank = abspage % ftl_banks;
54 theseven 867
    uint32_t block = abspage / (ftl_nand_type->pagesperblock * ftl_banks);
868
    uint32_t page = (abspage / ftl_banks) % ftl_nand_type->pagesperblock;
2 theseven 869
    uint32_t physblock = ftl_vfl_get_physical_block(bank, block);
54 theseven 870
    uint32_t physpage = physblock * ftl_nand_type->pagesperblock + page;
2 theseven 871
 
872
    uint32_t ret = nand_read_page(bank, physpage, buffer,
873
                                  sparebuffer, 1, checkempty);
874
 
875
    if ((ret & 0x11D) != 0 && (ret & 2) == 0)
876
    {
877
        nand_reset(bank);
878
        ret = nand_read_page(bank, physpage, buffer,
879
                             sparebuffer, 1, checkempty);
880
#ifdef FTL_READONLY
881
        (void)remaponfail;
882
#else
883
        if (remaponfail == 1 &&(ret & 0x11D) != 0 && (ret & 2) == 0)
884
        {
61 theseven 885
            DEBUGF("FTL: VFL: Scheduling vBlock %d for remapping!", block);
2 theseven 886
            ftl_vfl_schedule_block_for_remap(bank, block);
887
        }
888
#endif
889
        return ret;
890
    }
891
 
892
    return ret;
893
}
894
 
895
 
54 theseven 896
/* Multi-bank version of ftl_vfl_read, will read ftl_banks pages in parallel */
66 theseven 897
static uint32_t ftl_vfl_read_fast(uint32_t vpage, void* buffer, void* sparebuffer,
898
                                  uint32_t checkempty, uint32_t remaponfail)
54 theseven 899
{
900
#ifdef VFL_TRACE
61 theseven 901
    DEBUGF("FTL: VFL: Fast reading page %d on all banks", vpage);
54 theseven 902
#endif
903
 
904
    uint32_t i, rc = 0;
905
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
906
    uint32_t syshyperblocks = ftl_nand_type->blocks
907
                            - ftl_nand_type->userblocks - 0x17;
908
    uint32_t abspage = vpage + ppb * syshyperblocks;
909
    if (abspage + ftl_banks - 1 >= ftl_nand_type->blocks * ppb || abspage < ppb)
201 theseven 910
    {
54 theseven 911
        DEBUGF("FTL: Trying to read out-of-bounds vPage %u", (unsigned)vpage);
912
        return 4;
201 theseven 913
    }
54 theseven 914
 
915
    uint32_t bank = abspage % ftl_banks;
916
    uint32_t block = abspage / (ftl_nand_type->pagesperblock * ftl_banks);
917
    uint32_t page = (abspage / ftl_banks) % ftl_nand_type->pagesperblock;
918
    uint32_t remapped = 0;
919
    for (i = 0; i < ftl_banks; i++)
920
        if (ftl_vfl_get_physical_block(i, block) != block)
921
            remapped = 1;
922
    if (bank || remapped)
923
    {
924
        for (i = 0; i < ftl_banks; i++)
925
        {
166 theseven 926
            void* databuf = NULL;
927
            void* sparebuf = NULL;
54 theseven 928
            if (buffer) databuf = (void*)((uint32_t)buffer + 0x800 * i);
929
            if (sparebuffer) sparebuf = (void*)((uint32_t)sparebuffer + 0x40 * i);
930
            uint32_t ret = ftl_vfl_read(vpage + i, databuf, sparebuf, checkempty, remaponfail);
931
            if (ret & 1) rc |= 1 << (i << 2);
932
            if (ret & 2) rc |= 2 << (i << 2);
933
            if (ret & 0x10) rc |= 4 << (i << 2);
934
            if (ret & 0x100) rc |= 8 << (i << 2);
935
        }
936
        return rc;
937
    }
938
    uint32_t physpage = block * ftl_nand_type->pagesperblock + page;
939
 
940
    rc = nand_read_page_fast(physpage, buffer, sparebuffer, 1, checkempty);
941
    if (!(rc & 0xdddd)) return rc;
942
 
943
    for (i = 0; i < ftl_banks; i++)
944
    {
945
        if ((rc >> (i << 2)) & 0x2) continue;
946
        if ((rc >> (i << 2)) & 0xd)
947
        {
948
            rc &= ~(0xf << (i << 2));
949
            nand_reset(i);
950
            uint32_t ret = nand_read_page(i, physpage,
951
                                          (void*)((uint32_t)buffer + 0x800 * i),
952
                                          (void*)((uint32_t)sparebuffer + 0x40 * i),
953
                                          1, checkempty);
954
#ifdef FTL_READONLY
955
            (void)remaponfail;
956
#else
957
            if (remaponfail == 1 && (ret & 0x11D) != 0 && (ret & 2) == 0)
958
                ftl_vfl_schedule_block_for_remap(i, block);
959
#endif
960
            if (ret & 1) rc |= 1 << (i << 2);
961
            if (ret & 2) rc |= 2 << (i << 2);
962
            if (ret & 0x10) rc |= 4 << (i << 2);
963
            if (ret & 0x100) rc |= 8 << (i << 2);
964
        }
965
    }
966
 
967
    return rc;
968
}
969
 
970
 
2 theseven 971
#ifndef FTL_READONLY
972
/* Writes the specified vPage, dealing with all kinds of trouble */
66 theseven 973
static uint32_t ftl_vfl_write(uint32_t vpage, uint32_t count,
974
                              void* buffer, void* sparebuffer)
2 theseven 975
{
54 theseven 976
    uint32_t i, j;
977
#ifdef VFL_TRACE
61 theseven 978
    DEBUGF("FTL: VFL: Writing page %d", vpage);
54 theseven 979
#endif
980
 
981
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
982
    uint32_t syshyperblocks = ftl_nand_type->blocks
983
                            - ftl_nand_type->userblocks - 0x17;
2 theseven 984
    uint32_t abspage = vpage + ppb * syshyperblocks;
54 theseven 985
    if (abspage + count > ftl_nand_type->blocks * ppb || abspage < ppb)
201 theseven 986
    {
54 theseven 987
        DEBUGF("FTL: Trying to write out-of-bounds vPage %u",
988
               (unsigned)vpage);
989
        return 4;
201 theseven 990
    }
54 theseven 991
 
992
    uint32_t bank[5];
993
    uint32_t block[5];
994
    uint32_t physpage[5];
995
 
996
    for (i = 0; i < count; i++, abspage++)
2 theseven 997
    {
54 theseven 998
        for (j = ftl_banks; j > 0; j--)
999
        {
1000
            bank[j] = bank[j - 1];
1001
            block[j] = block[j - 1];
1002
            physpage[j] = physpage[j - 1];
1003
        }
1004
        bank[0] = abspage % ftl_banks;
1005
        block[0] = abspage / (ftl_nand_type->pagesperblock * ftl_banks);
1006
        uint32_t page = (abspage / ftl_banks) % ftl_nand_type->pagesperblock;
1007
        uint32_t physblock = ftl_vfl_get_physical_block(bank[0], block[0]);
1008
        physpage[0] = physblock * ftl_nand_type->pagesperblock + page;
1009
 
1010
        if (i >= ftl_banks)
1011
            if (nand_write_page_collect(bank[ftl_banks]))
1012
                if (nand_read_page(bank[ftl_banks], physpage[ftl_banks],
1013
                                   ftl_buffer, &ftl_sparebuffer[0], 1, 1) & 0x11F)
1014
                {
249 theseven 1015
                    ftl_initialized = false;
1016
                    panicf(PANIC_KILLTHREAD, "FTL: write error (2) on vPage %u, bank %u, pPage %u",
54 theseven 1017
                           (unsigned)(vpage + i - ftl_banks),
1018
                           (unsigned)bank[ftl_banks],
1019
                           (unsigned)physpage[ftl_banks]);
1020
                    ftl_vfl_log_trouble(bank[ftl_banks], block[ftl_banks]);
1021
                }
1022
        if (nand_write_page_start(bank[0], physpage[0],
1023
                                  (void*)((uint32_t)buffer + 0x800 * i),
1024
                                  (void*)((uint32_t)sparebuffer + 0x40 * i), 1))
1025
            if (nand_read_page(bank[0], physpage[0], ftl_buffer,
1026
                               &ftl_sparebuffer[0], 1, 1) & 0x11F)
1027
            {
249 theseven 1028
                ftl_initialized = false;
1029
                panicf(PANIC_KILLTHREAD, "FTL: write error (1) on vPage %u, bank %u, pPage %u",
54 theseven 1030
                       (unsigned)(vpage + i), (unsigned)bank[0], (unsigned)physpage[0]);
1031
                ftl_vfl_log_trouble(bank[0], block[0]);
1032
            }
2 theseven 1033
    }
1034
 
54 theseven 1035
    for (i = count < ftl_banks ? count : ftl_banks; i > 0; i--)
1036
        if (nand_write_page_collect(bank[i - 1]))
1037
            if (nand_read_page(bank[i - 1], physpage[i - 1],
1038
                               ftl_buffer, &ftl_sparebuffer[0], 1, 1) & 0x11F)
1039
            {
249 theseven 1040
                ftl_initialized = false;
1041
                panicf(PANIC_KILLTHREAD, "FTL: write error (2) on vPage %u, bank %u, pPage %u",
54 theseven 1042
                       (unsigned)(vpage + count - i),
1043
                       (unsigned)bank[i - 1], (unsigned)physpage[i - 1]);
1044
                ftl_vfl_log_trouble(bank[i - 1], block[i - 1]);
1045
            }
2 theseven 1046
 
54 theseven 1047
    return 0;
2 theseven 1048
}
1049
#endif
1050
 
1051
 
1052
/* Mounts the VFL on all banks */
124 theseven 1053
static uint32_t ftl_vfl_open(void) INITCODE_ATTR;
1054
static uint32_t ftl_vfl_open()
2 theseven 1055
{
1056
    uint32_t i, j, k;
1057
    uint32_t minusn, vflcxtidx, last;
1058
    struct ftl_vfl_cxt_type* cxt;
1059
    uint16_t vflcxtblock[4];
1060
#ifndef FTL_READONLY
1061
    ftl_vfl_usn = 0;
1062
#else
1063
    /* Temporary BBT buffer if we're readonly,
1064
       as we won't need it again after mounting */
1065
    uint8_t bbt[0x410];
1066
#endif
1067
 
54 theseven 1068
    uint32_t syshyperblocks = ftl_nand_type->blocks
1069
                            - ftl_nand_type->userblocks - 0x18;
2 theseven 1070
 
1071
    for (i = 0; i < ftl_banks; i++)
1072
#ifndef FTL_READONLY
1073
        if (ftl_load_bbt(i, ftl_bbt[i]) == 0)
1074
#else
1075
        if (ftl_load_bbt(i, bbt) == 0)
1076
#endif
1077
        {
1078
            for (j = 1; j <= syshyperblocks; j++)
1079
#ifndef FTL_READONLY
1080
                if (ftl_is_good_block(ftl_bbt[i], j) != 0)
1081
#else
1082
                if (ftl_is_good_block(bbt, j) != 0)
1083
#endif
1084
                    if (ftl_vfl_read_page(i, j, 0, ftl_buffer,
54 theseven 1085
                                          &ftl_sparebuffer[0]) == 0)
2 theseven 1086
                    {
1087
                        struct ftl_vfl_cxt_type* cxt;
1088
                        cxt = (struct ftl_vfl_cxt_type*)ftl_buffer;
54 theseven 1089
                        memcpy(vflcxtblock, &cxt->vflcxtblocks, 8);
2 theseven 1090
                        minusn = 0xFFFFFFFF;
1091
                        vflcxtidx = 4;
1092
                        for (k = 0; k < 4; k++)
1093
                            if (vflcxtblock[k] != 0xFFFF)
1094
                                if (ftl_vfl_read_page(i, vflcxtblock[k], 0,
1095
                                                      ftl_buffer,
54 theseven 1096
                                                      &ftl_sparebuffer[0]) == 0)
1097
                                    if (ftl_sparebuffer[0].meta.usn > 0
1098
                                     && ftl_sparebuffer[0].meta.usn <= minusn)
2 theseven 1099
                                    {
54 theseven 1100
                                        minusn = ftl_sparebuffer[0].meta.usn;
2 theseven 1101
                                        vflcxtidx = k;
1102
                                    }
54 theseven 1103
                        if (vflcxtidx == 4)
201 theseven 1104
                        {
54 theseven 1105
                            DEBUGF("FTL: No VFL CXT block found on bank %u!",
1106
                                   (unsigned)i);
201 theseven 1107
                            return 1;
1108
                        }
2 theseven 1109
                        last = 0;
54 theseven 1110
                        uint32_t max = ftl_nand_type->pagesperblock;
2 theseven 1111
                        for (k = 8; k < max; k += 8)
1112
                        {
1113
                            if (ftl_vfl_read_page(i, vflcxtblock[vflcxtidx],
1114
                                                  k, ftl_buffer,
54 theseven 1115
                                                  &ftl_sparebuffer[0]) != 0)
2 theseven 1116
                                break;
1117
                            last = k;
1118
                        }
1119
                        if (ftl_vfl_read_page(i, vflcxtblock[vflcxtidx],
1120
                                              last, ftl_buffer,
54 theseven 1121
                                              &ftl_sparebuffer[0]) != 0)
249 theseven 1122
                        {
1123
                            ftl_initialized = false;
1124
                            panicf(PANIC_KILLTHREAD, "FTL: Re-reading VFL CXT block "
1125
                                                     "on bank %u failed!?", (unsigned)i);
54 theseven 1126
                            //return 1;
249 theseven 1127
                        }
2 theseven 1128
                        memcpy(&ftl_vfl_cxt[i], ftl_buffer, 0x800);
1129
                        if (ftl_vfl_verify_checksum(i) != 0) return 1;
1130
#ifndef FTL_READONLY
1131
                        if (ftl_vfl_usn < ftl_vfl_cxt[i].usn)
1132
                            ftl_vfl_usn = ftl_vfl_cxt[i].usn;
1133
#endif
1134
                        break;
1135
                    }
1136
        }
1137
        else
201 theseven 1138
        {
54 theseven 1139
            DEBUGF("FTL: Couldn't load bank %u lowlevel BBT!", (unsigned)i);
201 theseven 1140
            return 1;
1141
        }
2 theseven 1142
    cxt = ftl_vfl_get_newest_cxt();
1143
    for (i = 0; i < ftl_banks; i++)
54 theseven 1144
        memcpy(ftl_vfl_cxt[i].ftlctrlblocks, cxt->ftlctrlblocks, 6);
2 theseven 1145
    return 0;
1146
}
1147
 
1148
 
1149
/* Mounts the actual FTL */
124 theseven 1150
static uint32_t ftl_open(void) INITCODE_ATTR;
1151
static uint32_t ftl_open()
2 theseven 1152
{
1153
    uint32_t i;
1154
    uint32_t ret;
54 theseven 1155
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
2 theseven 1156
    struct ftl_vfl_cxt_type* cxt = ftl_vfl_get_newest_cxt();
1157
 
1158
    uint32_t ftlcxtblock = 0xffffffff;
54 theseven 1159
    uint32_t minusn = 0xffffffff;
2 theseven 1160
    for (i = 0; i < 3; i++)
1161
    {
54 theseven 1162
        ret = ftl_vfl_read(ppb * cxt->ftlctrlblocks[i],
1163
                           ftl_buffer, &ftl_sparebuffer[0], 1, 0);
2 theseven 1164
        if ((ret &= 0x11F) != 0) continue;
54 theseven 1165
        if (ftl_sparebuffer[0].meta.type - 0x43 > 4) continue;
1166
        if (ftlcxtblock != 0xffffffff && ftl_sparebuffer[0].meta.usn >= minusn)
2 theseven 1167
            continue;
54 theseven 1168
        minusn = ftl_sparebuffer[0].meta.usn;
1169
        ftlcxtblock = cxt->ftlctrlblocks[i];
2 theseven 1170
    }
1171
 
54 theseven 1172
    if (ftlcxtblock == 0xffffffff)
1173
    {
1174
        DEBUGF("FTL: Couldn't find readable FTL CXT block!");
1175
        return 1;
1176
    }
2 theseven 1177
 
61 theseven 1178
    DEBUGF("FTL: Found FTL context block: vBlock %d", ftlcxtblock);
2 theseven 1179
    uint32_t ftlcxtfound = 0;
54 theseven 1180
    for (i = ftl_nand_type->pagesperblock * ftl_banks - 1; i > 0; i--)
2 theseven 1181
    {
1182
        ret = ftl_vfl_read(ppb * ftlcxtblock + i,
54 theseven 1183
                           ftl_buffer, &ftl_sparebuffer[0], 1, 0);
2 theseven 1184
        if ((ret & 0x11F) != 0) continue;
54 theseven 1185
        else if (ftl_sparebuffer[0].meta.type == 0x43)
2 theseven 1186
        {
1187
            memcpy(&ftl_cxt, ftl_buffer, 0x28C);
1188
            ftlcxtfound = 1;
1189
            break;
1190
        }
1191
        else
1192
        {
54 theseven 1193
            /* This will trip if there was an unclean unmount before. */
61 theseven 1194
            DEBUGF("FTL: Unclean shutdown before!");
54 theseven 1195
#ifdef FTL_FORCEMOUNT
61 theseven 1196
            DEBUGF("FTL: Forcing mount nevertheless...");
54 theseven 1197
#else
2 theseven 1198
            break;
1199
#endif
1200
        }
1201
    }
1202
 
54 theseven 1203
    if (ftlcxtfound == 0)
201 theseven 1204
    {
54 theseven 1205
        DEBUGF("FTL: Couldn't find FTL CXT page!");
201 theseven 1206
        return 1;
1207
    }
2 theseven 1208
 
61 theseven 1209
    DEBUGF("FTL: Successfully read FTL context block");
54 theseven 1210
    uint32_t pagestoread = ftl_nand_type->userblocks >> 10;
1211
    if ((ftl_nand_type->userblocks & 0x1FF) != 0) pagestoread++;
2 theseven 1212
 
1213
    for (i = 0; i < pagestoread; i++)
1214
    {
1215
        if ((ftl_vfl_read(ftl_cxt.ftl_map_pages[i],
54 theseven 1216
                          ftl_buffer, &ftl_sparebuffer[0], 1, 1) & 0x11F) != 0)
201 theseven 1217
        {
54 theseven 1218
            DEBUGF("FTL: Failed to read block map page %u", (unsigned)i);
2 theseven 1219
            return 1;
201 theseven 1220
        }
2 theseven 1221
 
1222
        uint32_t toread = 2048;
54 theseven 1223
        if (toread > (ftl_nand_type->userblocks << 1) - (i << 11))
1224
            toread = (ftl_nand_type->userblocks << 1) - (i << 11);
2 theseven 1225
 
1226
        memcpy(&ftl_map[i << 10], ftl_buffer, toread);
1227
    }
1228
 
1229
#ifndef FTL_READONLY
54 theseven 1230
    pagestoread = (ftl_nand_type->userblocks + 23) >> 10;
1231
    if (((ftl_nand_type->userblocks + 23) & 0x1FF) != 0) pagestoread++;
2 theseven 1232
 
1233
    for (i = 0; i < pagestoread; i++)
1234
    {
1235
        if ((ftl_vfl_read(ftl_cxt.ftl_erasectr_pages[i],
54 theseven 1236
                          ftl_buffer, &ftl_sparebuffer[0], 1, 1) & 0x11F) != 0)
201 theseven 1237
        {
54 theseven 1238
            DEBUGF("FTL: Failed to read erase counter page %u", (unsigned)i);
2 theseven 1239
            return 1;
201 theseven 1240
        }
2 theseven 1241
 
1242
        uint32_t toread = 2048;
54 theseven 1243
        if (toread > ((ftl_nand_type->userblocks + 23) << 1) - (i << 11))
1244
            toread = ((ftl_nand_type->userblocks + 23) << 1) - (i << 11);
2 theseven 1245
 
1246
        memcpy(&ftl_erasectr[i << 10], ftl_buffer, toread);
1247
    }
1248
 
1249
    for (i = 0; i < 0x11; i++)
1250
    {
1251
        ftl_log[i].scatteredvblock = 0xFFFF;
1252
        ftl_log[i].logicalvblock = 0xFFFF;
1253
        ftl_log[i].pageoffsets = ftl_offsets[i];
1254
    }
1255
 
1256
    memset(ftl_troublelog, 0xFF, 20);
1257
    memset(ftl_erasectr_dirt, 0, 8);
1258
#endif
1259
 
54 theseven 1260
#ifdef FTL_DEBUG
1261
    uint32_t j, k;
1262
    for (i = 0; i < ftl_banks; i++)
1263
    {
1264
        uint32_t badblocks = 0;
1265
#ifndef FTL_READONLY
124 theseven 1266
        for (j = 0; j < ftl_nand_type->blocks >> 3; j++)
54 theseven 1267
        {
1268
            uint8_t bbtentry = ftl_bbt[i][j];
1269
            for (k = 0; k < 8; k++) if ((bbtentry & (1 << k)) == 0) badblocks++;
1270
        }
61 theseven 1271
        DEBUGF("FTL: BBT for bank %d: %d bad blocks", i, badblocks);
54 theseven 1272
        badblocks = 0;
1273
#endif
1274
        for (j = 0; j < ftl_vfl_cxt[i].sparecount; j++)
1275
            if (ftl_vfl_cxt[i].remaptable[j] == 0xFFFF) badblocks++;
61 theseven 1276
        DEBUGF("FTL: VFL: Bank %d: %d of %d spare blocks are bad",
54 theseven 1277
               i, badblocks, ftl_vfl_cxt[i].sparecount);
61 theseven 1278
        DEBUGF("FTL: VFL: Bank %d: %d blocks remapped",
54 theseven 1279
               i, ftl_vfl_cxt[i].spareused);
61 theseven 1280
        DEBUGF("FTL: VFL: Bank %d: %d blocks scheduled for remapping",
54 theseven 1281
               i, 0x334 - ftl_vfl_cxt[i].scheduledstart);
1282
    }
1283
#ifndef FTL_READONLY
1284
    uint32_t min = 0xFFFFFFFF, max = 0, total = 0;
124 theseven 1285
    for (i = 0; i < ftl_nand_type->userblocks + 23; i++)
54 theseven 1286
    {
1287
        if (ftl_erasectr[i] > max) max = ftl_erasectr[i];
1288
        if (ftl_erasectr[i] < min) min = ftl_erasectr[i];
1289
        total += ftl_erasectr[i];
1290
    }
61 theseven 1291
    DEBUGF("FTL: Erase counters: Minimum: %d, maximum %d, average: %d, total: %d",
124 theseven 1292
           min, max, total / (ftl_nand_type->userblocks + 23), total);
54 theseven 1293
#endif
1294
#endif
1295
 
2 theseven 1296
    return 0;
1297
}
1298
 
1299
 
1300
#ifndef FTL_READONLY
1301
/* Returns a pointer to the ftl_log entry for the specified vBlock,
1302
   or null, if there is none */
66 theseven 1303
static struct ftl_log_type* ftl_get_log_entry(uint32_t block)
2 theseven 1304
{
1305
    uint32_t i;
1306
    for (i = 0; i < 0x11; i++)
1307
    {
1308
        if (ftl_log[i].scatteredvblock == 0xFFFF) continue;
1309
        if (ftl_log[i].logicalvblock == block) return &ftl_log[i];
1310
    }
166 theseven 1311
    return NULL;
2 theseven 1312
}
1313
#endif
1314
 
1315
/* Exposed function: Read highlevel sectors */
54 theseven 1316
uint32_t ftl_read(uint32_t sector, uint32_t count, void* buffer)
2 theseven 1317
{
54 theseven 1318
    uint32_t i, j;
1319
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
2 theseven 1320
    uint32_t error = 0;
1321
 
155 theseven 1322
    if (!ftl_initialized) return -1;
128 theseven 1323
 
54 theseven 1324
#ifdef FTL_TRACE
61 theseven 1325
    DEBUGF("FTL: Reading %d sectors starting at %d", count, sector);
54 theseven 1326
#endif
1327
 
201 theseven 1328
    if ((uint32_t)buffer & (CACHEALIGN_SIZE - 1))
1329
        panicf(PANIC_KILLTHREAD,
1330
               "ftl_read: Misaligned data buffer at %08X (sector %lu, count %lu)",
1331
               (unsigned int)buffer, sector, count);
68 theseven 1332
 
54 theseven 1333
    if (sector + count > ftl_nand_type->userblocks * ppb)
1334
    {
61 theseven 1335
        DEBUGF("FTL: Sector %d is out of range!", sector + count - 1);
155 theseven 1336
        return -2;
54 theseven 1337
    }
2 theseven 1338
    if (count == 0) return 0;
1339
 
54 theseven 1340
    mutex_lock(&ftl_mtx, TIMEOUT_BLOCK);
249 theseven 1341
    if (!ftl_initialized)
1342
    {
1343
        mutex_unlock(&ftl_mtx);
1344
        return -1;
1345
    }
54 theseven 1346
 
2 theseven 1347
    for (i = 0; i < count; i++)
1348
    {
1349
        uint32_t block = (sector + i) / ppb;
1350
        uint32_t page = (sector + i) % ppb;
1351
 
1352
        uint32_t abspage = ftl_map[block] * ppb + page;
1353
#ifndef FTL_READONLY
1354
        struct ftl_log_type* logentry = ftl_get_log_entry(block);
166 theseven 1355
        if (logentry != NULL)
2 theseven 1356
        {
54 theseven 1357
#ifdef FTL_TRACE
201 theseven 1358
            DEBUGF("FTL: Block %d has a log entry", block);
54 theseven 1359
#endif
1360
            if (logentry->scatteredvblock != 0xFFFF
1361
             && logentry->pageoffsets[page] != 0xFFFF)
2 theseven 1362
            {
54 theseven 1363
#ifdef FTL_TRACE
201 theseven 1364
                DEBUGF("FTL: Found page %d at block %d, page %d", page,
1365
                      (*logentry).scatteredvblock, (*logentry).pageoffsets[page]);
54 theseven 1366
#endif
1367
                abspage = logentry->scatteredvblock * ppb
1368
                        + logentry->pageoffsets[page];
2 theseven 1369
            }
1370
        }
1371
#endif
1372
 
54 theseven 1373
#ifndef FTL_READONLY
1374
        if (count >= i + ftl_banks && !(page & (ftl_banks - 1))
166 theseven 1375
         && logentry == NULL)
54 theseven 1376
#else
1377
        if (count >= i + ftl_banks && !(page & (ftl_banks - 1)))
1378
#endif
2 theseven 1379
        {
54 theseven 1380
            uint32_t ret = ftl_vfl_read_fast(abspage, &((uint8_t*)buffer)[i << 11],
1381
                                             &ftl_sparebuffer[0], 1, 1);
1382
            for (j = 0; j < ftl_banks; j++)
1383
                if (ret & (2 << (j << 2)))
1384
                    memset(&((uint8_t*)buffer)[(i + j) << 11], 0, 0x800);
1385
                else if ((ret & (0xd << (j << 2))) || ftl_sparebuffer[j].user.eccmark != 0xFF)
1386
                {
201 theseven 1387
                    DEBUGF("FTL: Error while reading sector %d!", (sector + i));
155 theseven 1388
                    error = -3;
54 theseven 1389
                    memset(&((uint8_t*)buffer)[(i + j) << 11], 0, 0x800);
1390
                }
1391
            i += ftl_banks - 1;
2 theseven 1392
        }
54 theseven 1393
        else
1394
        {
1395
            uint32_t ret = ftl_vfl_read(abspage, &((uint8_t*)buffer)[i << 11],
1396
                                        &ftl_sparebuffer[0], 1, 1);
1397
            if (ret & 2) memset(&((uint8_t*)buffer)[i << 11], 0, 0x800);
1398
            else if ((ret & 0x11D) != 0 || ftl_sparebuffer[0].user.eccmark != 0xFF)
1399
            {
201 theseven 1400
                DEBUGF("FTL: Error while reading sector %d!", (sector + i));
155 theseven 1401
                error = -4;
54 theseven 1402
                memset(&((uint8_t*)buffer)[i << 11], 0, 0x800);
1403
            }
1404
        }
2 theseven 1405
    }
54 theseven 1406
 
1407
    mutex_unlock(&ftl_mtx);
1408
 
2 theseven 1409
    return error;
1410
}
1411
 
1412
 
1413
#ifndef FTL_READONLY
1414
/* Performs a vBlock erase, dealing with hardware,
1415
   remapping and all kinds of trouble */
66 theseven 1416
static uint32_t ftl_erase_block_internal(uint32_t block)
2 theseven 1417
{
1418
    uint32_t i, j;
54 theseven 1419
    block = block + ftl_nand_type->blocks
1420
          - ftl_nand_type->userblocks - 0x17;
1421
    if (block == 0 || block >= ftl_nand_type->blocks) return 1;
2 theseven 1422
    for (i = 0; i < ftl_banks; i++)
1423
    {
1424
        if (ftl_vfl_check_remap_scheduled(i, block) == 1)
1425
        {
1426
            ftl_vfl_remap_block(i, block);
1427
            ftl_vfl_mark_remap_done(i, block);
1428
        }
1429
        ftl_vfl_log_success(i, block);
1430
        uint32_t pblock = ftl_vfl_get_physical_block(i, block);
1431
        uint32_t rc;
1432
        for (j = 0; j < 3; j++)
1433
        {
54 theseven 1434
            rc = nand_block_erase(i, pblock * ftl_nand_type->pagesperblock);
2 theseven 1435
            if (rc == 0) break;
1436
        }
1437
        if (rc != 0)
1438
        {
249 theseven 1439
            ftl_initialized = false;
1440
            panicf(PANIC_KILLTHREAD, "FTL: Block erase failed on bank %u block %u",
54 theseven 1441
                   (unsigned)i, (unsigned)block);
2 theseven 1442
            if (pblock != block)
1443
            {
1444
                uint32_t spareindex = pblock - ftl_vfl_cxt[i].firstspare;
1445
                ftl_vfl_cxt[i].remaptable[spareindex] = 0xFFFF;
1446
            }
1447
            ftl_vfl_cxt[i].field_18++;
1448
            if (ftl_vfl_remap_block(i, block) == 0) return 1;
1449
            if (ftl_vfl_commit_cxt(i) != 0) return 1;
1450
            memset(&ftl_sparebuffer, 0, 0x40);
1451
            nand_write_page(i, pblock, &ftl_vfl_cxt[0], &ftl_sparebuffer, 1);
1452
        }
1453
    }
1454
    return 0;
1455
}
1456
#endif
1457
 
1458
 
1459
#ifndef FTL_READONLY
1460
/* Highlevel vBlock erase, that increments the erase counter for the block */
66 theseven 1461
static uint32_t ftl_erase_block(uint32_t block)
2 theseven 1462
{
1463
    ftl_erasectr[block]++;
1464
    if (ftl_erasectr_dirt[block >> 10] == 100) ftl_cxt.erasedirty = 1;
1465
    else ftl_erasectr_dirt[block >> 10]++;
1466
    return ftl_erase_block_internal(block);
1467
}
1468
#endif
1469
 
1470
 
1471
#ifndef FTL_READONLY
1472
/* Allocates a block from the pool,
1473
   returning its vBlock number, or 0xFFFFFFFF on error */
66 theseven 1474
static uint32_t ftl_allocate_pool_block(void)
2 theseven 1475
{
1476
    uint32_t i;
1477
    uint32_t erasectr = 0xFFFFFFFF, bestidx = 0xFFFFFFFF, block;
1478
    for (i = 0; i < ftl_cxt.freecount; i++)
1479
    {
1480
        uint32_t idx = ftl_cxt.nextfreeidx + i;
1481
        if (idx >= 0x14) idx -= 0x14;
1482
        if (!ftl_cxt.blockpool[idx]) continue;
1483
        if (ftl_erasectr[ftl_cxt.blockpool[idx]] < erasectr)
1484
        {
1485
            erasectr = ftl_erasectr[ftl_cxt.blockpool[idx]];
1486
            bestidx = idx;
1487
        }
1488
    }
249 theseven 1489
    if (bestidx == 0xFFFFFFFF0)
1490
    {
1491
        ftl_initialized = false;
1492
        panicf(PANIC_KILLTHREAD, "FTL: Out of pool blocks!");
1493
    }
2 theseven 1494
    block = ftl_cxt.blockpool[bestidx];
1495
    if (bestidx != ftl_cxt.nextfreeidx)
1496
    {
1497
        ftl_cxt.blockpool[bestidx] = ftl_cxt.blockpool[ftl_cxt.nextfreeidx];
1498
        ftl_cxt.blockpool[ftl_cxt.nextfreeidx] = block;
1499
    }
54 theseven 1500
    if (block > (uint32_t)ftl_nand_type->userblocks + 0x17)
249 theseven 1501
    {
1502
        ftl_initialized = false;
1503
        panicf(PANIC_KILLTHREAD, "FTL: Bad block number in pool: %u", (unsigned)block);
1504
    }
2 theseven 1505
    if (ftl_erase_block(block) != 0) return 0xFFFFFFFF;
1506
    if (++ftl_cxt.nextfreeidx == 0x14) ftl_cxt.nextfreeidx = 0;
1507
    ftl_cxt.freecount--;
1508
    return block;
1509
}
1510
#endif
1511
 
1512
 
1513
#ifndef FTL_READONLY
1514
/* Releases a vBlock back into the pool */
66 theseven 1515
static void ftl_release_pool_block(uint32_t block)
2 theseven 1516
{
54 theseven 1517
    if (block >= (uint32_t)ftl_nand_type->userblocks + 0x17)
249 theseven 1518
    {
1519
        ftl_initialized = false;
1520
        panicf(PANIC_KILLTHREAD, "FTL: Tried to release block %u", (unsigned)block);
1521
    }
2 theseven 1522
    uint32_t idx = ftl_cxt.nextfreeidx + ftl_cxt.freecount++;
1523
    if (idx >= 0x14) idx -= 0x14;
1524
    ftl_cxt.blockpool[idx] = block;
1525
}
1526
#endif
1527
 
1528
 
1529
#ifndef FTL_READONLY
1530
/* Commits the location of the FTL context blocks
1531
   to a semi-randomly chosen VFL context */
66 theseven 1532
static uint32_t ftl_store_ctrl_block_list(void)
2 theseven 1533
{
1534
    uint32_t i;
1535
    for (i = 0; i < ftl_banks; i++)
1536
        memcpy(ftl_vfl_cxt[i].ftlctrlblocks, ftl_cxt.ftlctrlblocks, 6);
1537
    return ftl_vfl_commit_cxt(ftl_vfl_usn % ftl_banks);
1538
}
1539
#endif
1540
 
1541
 
1542
#ifndef FTL_READONLY
1543
/* Saves the n-th erase counter page to the flash,
1544
   because it is too dirty or needs to be moved. */
66 theseven 1545
static uint32_t ftl_save_erasectr_page(uint32_t index)
2 theseven 1546
{
54 theseven 1547
    memset(&ftl_sparebuffer[0], 0xFF, 0x40);
1548
    ftl_sparebuffer[0].meta.usn = ftl_cxt.usn;
1549
    ftl_sparebuffer[0].meta.idx = index;
1550
    ftl_sparebuffer[0].meta.type = 0x46;
1551
    if (ftl_vfl_write(ftl_cxt.ftlctrlpage, 1, &ftl_erasectr[index << 10],
1552
                      &ftl_sparebuffer[0]) != 0)
2 theseven 1553
        return 1;
1554
    if ((ftl_vfl_read(ftl_cxt.ftlctrlpage, ftl_buffer,
54 theseven 1555
                      &ftl_sparebuffer[0], 1, 1) & 0x11F) != 0)
2 theseven 1556
        return 1;
1557
    if (memcmp(ftl_buffer, &ftl_erasectr[index << 10], 0x800) != 0) return 1;
54 theseven 1558
    if (ftl_sparebuffer[0].meta.type != 0x46) return 1;
1559
    if (ftl_sparebuffer[0].meta.idx != index) return 1;
1560
    if (ftl_sparebuffer[0].meta.usn != ftl_cxt.usn) return 1;
2 theseven 1561
    ftl_cxt.ftl_erasectr_pages[index] = ftl_cxt.ftlctrlpage;
1562
    ftl_erasectr_dirt[index] = 0;
1563
    return 0;
1564
}
1565
#endif
1566
 
1567
 
1568
#ifndef FTL_READONLY
1569
/* Increments ftl_cxt.ftlctrlpage to the next available FTL context page,
1570
   allocating a new context block if neccessary. */
66 theseven 1571
static uint32_t ftl_next_ctrl_pool_page(void)
2 theseven 1572
{
1573
    uint32_t i;
54 theseven 1574
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
2 theseven 1575
    if (++ftl_cxt.ftlctrlpage % ppb != 0) return 0;
1576
    for (i = 0; i < 3; i++)
1577
        if ((ftl_cxt.ftlctrlblocks[i] + 1) * ppb == ftl_cxt.ftlctrlpage)
1578
            break;
1579
    i = (i + 1) % 3;
1580
    uint32_t oldblock = ftl_cxt.ftlctrlblocks[i];
1581
    uint32_t newblock = ftl_allocate_pool_block();
1582
    if (newblock == 0xFFFFFFFF) return 1;
1583
    ftl_cxt.ftlctrlblocks[i] = newblock;
1584
    ftl_cxt.ftlctrlpage = newblock * ppb;
61 theseven 1585
    DEBUGF("Starting new FTL control block at %d", ftl_cxt.ftlctrlpage);
54 theseven 1586
    uint32_t pagestoread = (ftl_nand_type->userblocks + 23) >> 10;
1587
    if (((ftl_nand_type->userblocks + 23) & 0x1FF) != 0) pagestoread++;
2 theseven 1588
    for (i = 0; i < pagestoread; i++)
1589
        if (oldblock * ppb <= ftl_cxt.ftl_erasectr_pages[i]
1590
         && (oldblock + 1) * ppb > ftl_cxt.ftl_erasectr_pages[i])
1591
         {
1592
            ftl_cxt.usn--;
1593
            if (ftl_save_erasectr_page(i) != 0)
1594
            {
1595
                ftl_cxt.ftlctrlblocks[i] = oldblock;
1596
                ftl_cxt.ftlctrlpage = oldblock * (ppb + 1) - 1;
1597
                ftl_release_pool_block(newblock);
1598
                return 1;
1599
            }
1600
            ftl_cxt.ftlctrlpage++;
1601
         }
1602
    ftl_release_pool_block(oldblock);
1603
    return ftl_store_ctrl_block_list();
1604
}
1605
#endif
1606
 
1607
 
1608
#ifndef FTL_READONLY
1609
/* Copies a vPage from one location to another */
66 theseven 1610
static uint32_t ftl_copy_page(uint32_t source, uint32_t destination,
1611
                              uint32_t lpn, uint32_t type)
2 theseven 1612
{
54 theseven 1613
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
1614
    uint32_t rc = ftl_vfl_read(source, ftl_copybuffer[0],
1615
                               &ftl_copyspare[0], 1, 1) & 0x11F;
1616
    memset(&ftl_copyspare[0], 0xFF, 0x40);
1617
    ftl_copyspare[0].user.lpn = lpn;
1618
    ftl_copyspare[0].user.usn = ++ftl_cxt.nextblockusn;
1619
    ftl_copyspare[0].user.type = 0x40;
1620
    if ((rc & 2) != 0) memset(ftl_copybuffer[0], 0, 0x800);
1621
    else if (rc != 0) ftl_copyspare[0].user.eccmark = 0x55;
2 theseven 1622
    if (type == 1 && destination % ppb == ppb - 1)
54 theseven 1623
        ftl_copyspare[0].user.type = 0x41;
1624
    return ftl_vfl_write(destination, 1, ftl_copybuffer[0], &ftl_copyspare[0]);
2 theseven 1625
}
1626
#endif
1627
 
1628
 
1629
#ifndef FTL_READONLY
1630
/* Copies a pBlock to a vBlock */
66 theseven 1631
static uint32_t ftl_copy_block(uint32_t source, uint32_t destination)
2 theseven 1632
{
54 theseven 1633
    uint32_t i, j;
1634
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
2 theseven 1635
    uint32_t error = 0;
1636
    ftl_cxt.nextblockusn++;
54 theseven 1637
    for (i = 0; i < ppb; i += FTL_COPYBUF_SIZE)
2 theseven 1638
    {
54 theseven 1639
        uint32_t rc = ftl_read(source * ppb + i,
1640
                               FTL_COPYBUF_SIZE, ftl_copybuffer[0]);
1641
        memset(&ftl_copyspare[0], 0xFF, 0x40 * FTL_COPYBUF_SIZE);
1642
        for (j = 0; j < FTL_COPYBUF_SIZE; j++)
2 theseven 1643
        {
54 theseven 1644
            ftl_copyspare[j].user.lpn = source * ppb + i + j;
1645
            ftl_copyspare[j].user.usn = ftl_cxt.nextblockusn;
1646
            ftl_copyspare[j].user.type = 0x40;
1647
            if (rc)
1648
            {
1649
                if (ftl_read(source * ppb + i + j, 1, ftl_copybuffer[j]))
1650
                    ftl_copyspare[j].user.eccmark = 0x55;
1651
            }
1652
            if (i + j == ppb - 1) ftl_copyspare[j].user.type = 0x41;
1653
        }
1654
        if (ftl_vfl_write(destination * ppb + i, FTL_COPYBUF_SIZE,
1655
                          ftl_copybuffer[0], &ftl_copyspare[0]))
1656
        {
2 theseven 1657
            error = 1;
1658
            break;
1659
        }
1660
    }
1661
    if (error != 0)
1662
    {
1663
        ftl_erase_block(destination);
1664
        return 1;
1665
    }
1666
    return 0;
1667
}
1668
#endif
1669
 
1670
 
1671
#ifndef FTL_READONLY
1672
/* Clears ftl_log.issequential, if something violating that is written. */
66 theseven 1673
static void ftl_check_still_sequential(struct ftl_log_type* entry, uint32_t page)
2 theseven 1674
{
54 theseven 1675
    if (entry->pagesused != entry->pagescurrent
1676
     || entry->pageoffsets[page] != page)
1677
        entry->issequential = 0;
2 theseven 1678
}
1679
#endif
1680
 
1681
 
1682
#ifndef FTL_READONLY
1683
/* Copies all pages that are currently used from the scattered page block in
1684
   use by the supplied ftl_log entry to a newly-allocated one, and releases
1685
   the old one.
1686
   In other words: It kicks the pages containing old garbage out of it to make
1687
   space again. This is usually done when a scattered page block is being
1688
   removed because it is full, but less than half of the pages in there are
1689
   still in use and rest is just filled with old crap. */
66 theseven 1690
static uint32_t ftl_compact_scattered(struct ftl_log_type* entry)
2 theseven 1691
{
1692
    uint32_t i, j;
54 theseven 1693
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
2 theseven 1694
    uint32_t error;
1695
    struct ftl_log_type backup;
54 theseven 1696
    if (entry->pagescurrent == 0)
2 theseven 1697
    {
54 theseven 1698
        ftl_release_pool_block(entry->scatteredvblock);
1699
        entry->scatteredvblock = 0xFFFF;
2 theseven 1700
        return 0;
1701
    }
1702
    backup = *entry;
54 theseven 1703
    memcpy(ftl_offsets_backup, entry->pageoffsets, 0x400);
2 theseven 1704
    for (i = 0; i < 4; i++)
1705
    {
1706
        uint32_t block = ftl_allocate_pool_block();
1707
        if (block == 0xFFFFFFFF) return 1;
54 theseven 1708
        entry->pagesused = 0;
1709
        entry->pagescurrent = 0;
1710
        entry->issequential = 1;
1711
        entry->scatteredvblock = block;
2 theseven 1712
        error = 0;
1713
        for (j = 0; j < ppb; j++)
54 theseven 1714
            if (entry->pageoffsets[j] != 0xFFFF)
2 theseven 1715
            {
54 theseven 1716
                uint32_t lpn = entry->logicalvblock * ppb + j;
1717
                uint32_t newpage = block * ppb + entry->pagesused;
2 theseven 1718
                uint32_t oldpage = backup.scatteredvblock * ppb
54 theseven 1719
                                 + entry->pageoffsets[j];
2 theseven 1720
                if (ftl_copy_page(oldpage, newpage, lpn,
54 theseven 1721
                                  entry->issequential) != 0)
2 theseven 1722
                {
1723
                    error = 1;
1724
                    break;
1725
                }
54 theseven 1726
                entry->pageoffsets[j] = entry->pagesused++;
1727
                entry->pagescurrent++;
2 theseven 1728
                ftl_check_still_sequential(entry, j);
1729
            }
54 theseven 1730
        if (backup.pagescurrent != entry->pagescurrent) error = 1;
2 theseven 1731
        if (error == 0)
1732
        {
1733
            ftl_release_pool_block(backup.scatteredvblock);
1734
            break;
1735
        }
1736
        *entry = backup;
54 theseven 1737
        memcpy(entry->pageoffsets, ftl_offsets_backup, 0x400);
2 theseven 1738
    }
1739
    return error;
1740
}
1741
#endif
1742
 
1743
 
1744
#ifndef FTL_READONLY
1745
/* Commits an ftl_log entry to proper blocks, no matter what's in there. */
66 theseven 1746
static uint32_t ftl_commit_scattered(struct ftl_log_type* entry)
2 theseven 1747
{
1748
    uint32_t i;
1749
    uint32_t error;
1750
    uint32_t block;
1751
    for (i = 0; i < 4; i++)
1752
    {
1753
        block = ftl_allocate_pool_block();
1754
        if (block == 0xFFFFFFFF) return 1;
54 theseven 1755
        error = ftl_copy_block(entry->logicalvblock, block);
2 theseven 1756
        if (error == 0) break;
1757
        ftl_release_pool_block(block);
1758
    }
1759
    if (error != 0) return 1;
54 theseven 1760
    ftl_release_pool_block(entry->scatteredvblock);
1761
    entry->scatteredvblock = 0xFFFF;
1762
    ftl_release_pool_block(ftl_map[entry->logicalvblock]);
1763
    ftl_map[entry->logicalvblock] = block;
2 theseven 1764
    return 0;
1765
}
1766
#endif
1767
 
1768
 
1769
#ifndef FTL_READONLY
1770
/* Fills the rest of a scattered page block that was actually written
1771
   sequentially until now, in order to be able to save a block erase by
1772
   committing it without needing to copy it again.
1773
   If this fails for whichever reason, it will be committed the usual way. */
66 theseven 1774
static uint32_t ftl_commit_sequential(struct ftl_log_type* entry)
2 theseven 1775
{
54 theseven 1776
    uint32_t i;
1777
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
2 theseven 1778
 
54 theseven 1779
    if (entry->issequential != 1
1780
     || entry->pagescurrent != entry->pagesused)
2 theseven 1781
        return 1;
1782
 
54 theseven 1783
    for (; entry->pagesused < ppb; )
2 theseven 1784
    {
54 theseven 1785
        uint32_t lpn = entry->logicalvblock * ppb + entry->pagesused;
1786
        uint32_t newpage = entry->scatteredvblock * ppb
1787
                         + entry->pagesused;
1788
        uint32_t count = FTL_COPYBUF_SIZE < ppb - entry->pagesused
1789
                       ? FTL_COPYBUF_SIZE : ppb - entry->pagesused;
1790
        for (i = 0; i < count; i++)
1791
            if (entry->pageoffsets[entry->pagesused + i] != 0xFFFF)
1792
                return ftl_commit_scattered(entry);
1793
        uint32_t rc = ftl_read(lpn, count, ftl_copybuffer[0]);
1794
        memset(&ftl_copyspare[0], 0xFF, 0x40 * FTL_COPYBUF_SIZE);
1795
        for (i = 0; i < count; i++)
1796
        {
1797
            ftl_copyspare[i].user.lpn = lpn + i;
1798
            ftl_copyspare[i].user.usn = ++ftl_cxt.nextblockusn;
1799
            ftl_copyspare[i].user.type = 0x40;
1800
            if (rc) ftl_copyspare[i].user.eccmark = 0x55;
1801
            if (entry->pagesused + i == ppb - 1)
1802
                ftl_copyspare[i].user.type = 0x41;
1803
        }
1804
        if (ftl_vfl_write(newpage, count, ftl_copybuffer[0], &ftl_copyspare[0]))
2 theseven 1805
            return ftl_commit_scattered(entry);
54 theseven 1806
        entry->pagesused += count;
2 theseven 1807
    }
54 theseven 1808
    ftl_release_pool_block(ftl_map[entry->logicalvblock]);
1809
    ftl_map[entry->logicalvblock] = entry->scatteredvblock;
1810
    entry->scatteredvblock = 0xFFFF;
2 theseven 1811
    return 0;
1812
}
1813
#endif
1814
 
1815
 
1816
#ifndef FTL_READONLY
1817
/* If a log entry is supplied, its scattered page block will be removed in
1818
   whatever way seems most appropriate. Else, the oldest scattered page block
1819
   will be freed by committing it. */
66 theseven 1820
static uint32_t ftl_remove_scattered_block(struct ftl_log_type* entry)
2 theseven 1821
{
1822
    uint32_t i;
54 theseven 1823
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
2 theseven 1824
    uint32_t age = 0xFFFFFFFF, used = 0;
166 theseven 1825
    if (entry == NULL)
2 theseven 1826
    {
1827
        for (i = 0; i < 0x11; i++)
1828
        {
1829
            if (ftl_log[i].scatteredvblock == 0xFFFF) continue;
1830
            if (ftl_log[i].pagesused == 0 || ftl_log[i].pagescurrent == 0)
1831
                return 1;
1832
            if (ftl_log[i].usn < age
1833
             || (ftl_log[i].usn == age && ftl_log[i].pagescurrent > used))
1834
            {
1835
                age = ftl_log[i].usn;
1836
                used = ftl_log[i].pagescurrent;
1837
                entry = &ftl_log[i];
1838
            }
1839
        }
166 theseven 1840
        if (entry == NULL) return 1;
2 theseven 1841
    }
54 theseven 1842
    else if (entry->pagescurrent < ppb / 2)
2 theseven 1843
    {
1844
        ftl_cxt.swapcounter++;
1845
        return ftl_compact_scattered(entry);
1846
    }
1847
    ftl_cxt.swapcounter++;
54 theseven 1848
    if (entry->issequential == 1) return ftl_commit_sequential(entry);
2 theseven 1849
    else return ftl_commit_scattered(entry);
1850
}
1851
#endif
1852
 
1853
 
1854
#ifndef FTL_READONLY
1855
/* Initialize a log entry to the values for an empty scattered page block */
66 theseven 1856
static void ftl_init_log_entry(struct ftl_log_type* entry)
2 theseven 1857
{
54 theseven 1858
    entry->issequential = 1;
1859
    entry->pagescurrent = 0;
1860
    entry->pagesused = 0;
1861
    memset(entry->pageoffsets, 0xFF, 0x400);
2 theseven 1862
}
1863
#endif
1864
 
1865
 
1866
#ifndef FTL_READONLY
1867
/* Allocates a log entry for the specified vBlock,
1868
   first making space, if neccessary. */
66 theseven 1869
static struct ftl_log_type* ftl_allocate_log_entry(uint32_t block)
2 theseven 1870
{
1871
    uint32_t i;
1872
    struct ftl_log_type* entry = ftl_get_log_entry(block);
166 theseven 1873
    if (entry != NULL)
155 theseven 1874
    {
1875
        entry->usn = ftl_cxt.nextblockusn - 1;
1876
        return entry;
1877
    }
2 theseven 1878
 
1879
    for (i = 0; i < 0x11; i++)
1880
    {
1881
        if (ftl_log[i].scatteredvblock == 0xFFFF) continue;
1882
        if (ftl_log[i].pagesused == 0)
1883
        {
1884
            entry = &ftl_log[i];
1885
            break;
1886
        }
1887
    }
1888
 
166 theseven 1889
    if (entry == NULL)
2 theseven 1890
    {
249 theseven 1891
        if (ftl_cxt.freecount < 3)
1892
        {
1893
            ftl_initialized = false;
1894
            panicf(PANIC_KILLTHREAD, "FTL: Detected a pool block leak!");
1895
        }
54 theseven 1896
        else if (ftl_cxt.freecount == 3)
166 theseven 1897
            if (ftl_remove_scattered_block(NULL) != 0)
1898
                return NULL;
2 theseven 1899
        entry = ftl_log;
54 theseven 1900
        while (entry->scatteredvblock != 0xFFFF) entry = &entry[1];
1901
        entry->scatteredvblock = ftl_allocate_pool_block();
1902
        if (entry->scatteredvblock == 0xFFFF)
166 theseven 1903
            return NULL;
2 theseven 1904
    }
1905
 
1906
    ftl_init_log_entry(entry);
54 theseven 1907
    entry->logicalvblock = block;
1908
    entry->usn = ftl_cxt.nextblockusn - 1;
2 theseven 1909
 
1910
    return entry;
1911
}
1912
#endif
1913
 
1914
 
1915
#ifndef FTL_READONLY
1916
/* Commits the FTL block map, erase counters, and context to flash */
66 theseven 1917
static uint32_t ftl_commit_cxt(void)
2 theseven 1918
{
1919
    uint32_t i;
54 theseven 1920
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
1921
    uint32_t mappages = (ftl_nand_type->userblocks + 0x3ff) >> 10;
1922
    uint32_t ctrpages = (ftl_nand_type->userblocks + 23 + 0x3ff) >> 10;
2 theseven 1923
    uint32_t endpage = ftl_cxt.ftlctrlpage + mappages + ctrpages + 1;
61 theseven 1924
    DEBUGF("FTL: Committing context");
54 theseven 1925
    if (endpage >= (ftl_cxt.ftlctrlpage / ppb + 1) * ppb)
2 theseven 1926
        ftl_cxt.ftlctrlpage |= ppb - 1;
1927
    for (i = 0; i < ctrpages; i++)
1928
    {
1929
        if (ftl_next_ctrl_pool_page() != 0) return 1;
1930
        if (ftl_save_erasectr_page(i) != 0) return 1;
1931
    }
1932
    for (i = 0; i < mappages; i++)
1933
    {
1934
        if (ftl_next_ctrl_pool_page() != 0) return 1;
54 theseven 1935
        memset(&ftl_sparebuffer[0], 0xFF, 0x40);
1936
        ftl_sparebuffer[0].meta.usn = ftl_cxt.usn;
1937
        ftl_sparebuffer[0].meta.idx = i;
1938
        ftl_sparebuffer[0].meta.type = 0x44;
1939
        if (ftl_vfl_write(ftl_cxt.ftlctrlpage, 1, &ftl_map[i << 10],
1940
                          &ftl_sparebuffer[0]) != 0)
2 theseven 1941
            return 1;
1942
        ftl_cxt.ftl_map_pages[i] = ftl_cxt.ftlctrlpage;
1943
    }
1944
    if (ftl_next_ctrl_pool_page() != 0) return 1;
1945
    ftl_cxt.clean_flag = 1;
54 theseven 1946
    memset(&ftl_sparebuffer[0], 0xFF, 0x40);
1947
    ftl_sparebuffer[0].meta.usn = ftl_cxt.usn;
1948
    ftl_sparebuffer[0].meta.type = 0x43;
1949
    if (ftl_vfl_write(ftl_cxt.ftlctrlpage, 1, &ftl_cxt, &ftl_sparebuffer[0]) != 0)
2 theseven 1950
        return 1;
61 theseven 1951
    DEBUGF("FTL: Wrote context to page %d", ftl_cxt.ftlctrlpage);
2 theseven 1952
    return 0;
1953
}
1954
#endif
1955
 
1956
 
1957
#ifndef FTL_READONLY
1958
/* Swaps the most and least worn block on the flash,
1959
   to better distribute wear. It will refuse to do anything
1960
   if the wear spread is lower than 5 erases. */
66 theseven 1961
static uint32_t ftl_swap_blocks(void)
2 theseven 1962
{
1963
    uint32_t i;
1964
    uint32_t min = 0xFFFFFFFF, max = 0, maxidx = 0x14;
1965
    uint32_t minidx = 0, minvb = 0, maxvb = 0;
1966
    for (i = 0; i < ftl_cxt.freecount; i++)
1967
    {
1968
        uint32_t idx = ftl_cxt.nextfreeidx + i;
1969
        if (idx >= 0x14) idx -= 0x14;
1970
        if (ftl_erasectr[ftl_cxt.blockpool[idx]] > max)
1971
        {
1972
            maxidx = idx;
1973
            maxvb = ftl_cxt.blockpool[idx];
1974
            max = ftl_erasectr[maxidx];
1975
        }
1976
    }
1977
    if (maxidx == 0x14) return 0;
54 theseven 1978
    for (i = 0; i < ftl_nand_type->userblocks; i++)
2 theseven 1979
    {
1980
        if (ftl_erasectr[ftl_map[i]] > max) max = ftl_erasectr[ftl_map[i]];
166 theseven 1981
        if (ftl_get_log_entry(i) != NULL) continue;
2 theseven 1982
        if (ftl_erasectr[ftl_map[i]] < min)
1983
        {
1984
            minidx = i;
1985
            minvb = ftl_map[i];
1986
            min = ftl_erasectr[minidx];
1987
        }
1988
    }
1989
    if (max - min < 5) return 0;
1990
    if (minvb == maxvb) return 0;
1991
    if (ftl_erase_block(maxvb) != 0) return 1;
1992
    if (ftl_copy_block(minidx, maxvb) != 0) return 1;
1993
    ftl_cxt.blockpool[maxidx] = minvb;
1994
    ftl_map[minidx] = maxvb;
1995
    return 0;
1996
}
1997
#endif
1998
 
1999
 
2000
#ifndef FTL_READONLY
2001
/* Exposed function: Write highlevel sectors */
54 theseven 2002
uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer)
2 theseven 2003
{
54 theseven 2004
    uint32_t i, j, k;
2005
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
2 theseven 2006
 
155 theseven 2007
    if (!ftl_initialized) return -1;
128 theseven 2008
 
54 theseven 2009
#ifdef FTL_TRACE
61 theseven 2010
    DEBUGF("FTL: Writing %d sectors starting at %d", count, sector);
54 theseven 2011
#endif
2012
 
201 theseven 2013
    if ((uint32_t)buffer & (CACHEALIGN_SIZE - 1))
2014
        panicf(PANIC_KILLTHREAD,
2015
               "ftl_write: Misaligned data buffer at %08X (sector %lu, count %lu)",
2016
               (unsigned int)buffer, sector, count);
68 theseven 2017
 
54 theseven 2018
    if (sector + count > ftl_nand_type->userblocks * ppb)
2019
    {
61 theseven 2020
        DEBUGF("FTL: Sector %d is out of range!", sector + count - 1);
155 theseven 2021
        return -2;
54 theseven 2022
    }
2 theseven 2023
    if (count == 0) return 0;
2024
 
54 theseven 2025
    mutex_lock(&ftl_mtx, TIMEOUT_BLOCK);
249 theseven 2026
    if (!ftl_initialized)
2027
    {
2028
        mutex_unlock(&ftl_mtx);
2029
        return -1;
2030
    }
54 theseven 2031
 
2 theseven 2032
    if (ftl_cxt.clean_flag == 1)
2033
    {
2034
        for (i = 0; i < 3; i++)
2035
        {
201 theseven 2036
            DEBUGF("FTL: Marking dirty, try %d", i);
54 theseven 2037
            if (ftl_next_ctrl_pool_page() != 0)
2038
            {
2039
                mutex_unlock(&ftl_mtx);
155 theseven 2040
                return -3;
54 theseven 2041
            }
2 theseven 2042
            memset(ftl_buffer, 0xFF, 0x800);
54 theseven 2043
            memset(&ftl_sparebuffer[0], 0xFF, 0x40);
2044
            ftl_sparebuffer[0].meta.usn = ftl_cxt.usn;
2045
            ftl_sparebuffer[0].meta.type = 0x47;
2046
            if (ftl_vfl_write(ftl_cxt.ftlctrlpage, 1, ftl_buffer,
2047
                              &ftl_sparebuffer[0]) == 0)
2 theseven 2048
                break;
2049
        }
54 theseven 2050
        if (i == 3)
2051
        {
2052
            mutex_unlock(&ftl_mtx);
155 theseven 2053
            return -4;
54 theseven 2054
        }
201 theseven 2055
        DEBUGF("FTL: Wrote dirty mark to %d", ftl_cxt.ftlctrlpage);
2 theseven 2056
        ftl_cxt.clean_flag = 0;
2057
    }
2058
 
2059
    for (i = 0; i < count; )
2060
    {
2061
        uint32_t block = (sector + i) / ppb;
2062
        uint32_t page = (sector + i) % ppb;
2063
 
2064
        struct ftl_log_type* logentry = ftl_allocate_log_entry(block);
166 theseven 2065
        if (logentry == NULL)
54 theseven 2066
        {
2067
            mutex_unlock(&ftl_mtx);
155 theseven 2068
            return -5;
54 theseven 2069
        }
2 theseven 2070
        if (page == 0 && count - i >= ppb)
2071
        {
54 theseven 2072
#ifdef FTL_TRACE
201 theseven 2073
            DEBUGF("FTL: Going to write a full hyperblock in one shot");
54 theseven 2074
#endif
2075
            uint32_t vblock = logentry->scatteredvblock;
2076
            logentry->scatteredvblock = 0xFFFF;
2077
            if (logentry->pagesused != 0)
2 theseven 2078
            {
54 theseven 2079
#ifdef FTL_TRACE
201 theseven 2080
                DEBUGF("FTL: Scattered block had some pages already used, committing");
54 theseven 2081
#endif
2 theseven 2082
                ftl_release_pool_block(vblock);
2083
                vblock = ftl_allocate_pool_block();
54 theseven 2084
                if (vblock == 0xFFFFFFFF)
2085
                {
2086
                    mutex_unlock(&ftl_mtx);
155 theseven 2087
                    return -6;
54 theseven 2088
                }
2 theseven 2089
            }
2090
            ftl_cxt.nextblockusn++;
54 theseven 2091
            for (j = 0; j < ppb; j += FTL_WRITESPARE_SIZE)
2 theseven 2092
            {
54 theseven 2093
                memset(&ftl_sparebuffer[0], 0xFF, 0x40 * FTL_WRITESPARE_SIZE);
2094
                for (k = 0; k < FTL_WRITESPARE_SIZE; k++)
2095
                {
2096
                    ftl_sparebuffer[k].user.lpn = sector + i + j + k;
2097
                    ftl_sparebuffer[k].user.usn = ftl_cxt.nextblockusn;
2098
                    ftl_sparebuffer[k].user.type = 0x40;
2099
                    if (j == ppb - 1) ftl_sparebuffer[k].user.type = 0x41;
2100
                }
2101
                uint32_t rc = ftl_vfl_write(vblock * ppb + j, FTL_WRITESPARE_SIZE,
2102
                                            &((uint8_t*)buffer)[(i + j) << 11],
2103
                                            &ftl_sparebuffer[0]);
2104
                if (rc)
2105
                    for (k = 0; k < ftl_banks; k++)
2106
                        if (rc & (1 << k))
2107
                        {
2108
                            while (ftl_vfl_write(vblock * ppb + j + k, 1,
2109
                                                 &((uint8_t*)buffer)[(i + j + k) << 11],
2110
                                                 &ftl_sparebuffer[k]));
2111
                        }
2 theseven 2112
            }
2113
            ftl_release_pool_block(ftl_map[block]);
2114
            ftl_map[block] = vblock;
2115
            i += ppb;
2116
        }
2117
        else
2118
        {
54 theseven 2119
            if (logentry->pagesused == ppb)
2 theseven 2120
            {
54 theseven 2121
#ifdef FTL_TRACE
201 theseven 2122
                DEBUGF("FTL: Scattered block is full, committing");
54 theseven 2123
#endif
2 theseven 2124
                ftl_remove_scattered_block(logentry);
2125
                logentry = ftl_allocate_log_entry(block);
166 theseven 2126
                if (logentry == NULL)
54 theseven 2127
                {
2128
                    mutex_unlock(&ftl_mtx);
155 theseven 2129
                    return -7;
54 theseven 2130
                }
2 theseven 2131
            }
54 theseven 2132
            uint32_t cnt = FTL_WRITESPARE_SIZE;
2133
            if (cnt > count - i) cnt = count - i;
2134
            if (cnt > ppb - logentry->pagesused) cnt = ppb - logentry->pagesused;
2135
            if (cnt > ppb - page) cnt = ppb - page;
2136
            memset(&ftl_sparebuffer[0], 0xFF, 0x40 * cnt);
2137
            for (j = 0; j < cnt; j++)
2 theseven 2138
            {
54 theseven 2139
                ftl_sparebuffer[j].user.lpn = sector + i + j;
2140
                ftl_sparebuffer[j].user.usn = ++ftl_cxt.nextblockusn;
2141
                ftl_sparebuffer[j].user.type = 0x40;
2142
                if (logentry->pagesused + j == ppb - 1 && logentry->issequential)
2143
                    ftl_sparebuffer[j].user.type = 0x41;
2 theseven 2144
            }
54 theseven 2145
            uint32_t abspage = logentry->scatteredvblock * ppb
2146
                             + logentry->pagesused;
2147
            logentry->pagesused += cnt;
2148
            if (ftl_vfl_write(abspage, cnt, &((uint8_t*)buffer)[i << 11],
2149
                              &ftl_sparebuffer[0]) == 0)
2150
            {
2151
                for (j = 0; j < cnt; j++)
2152
                {
2153
                    if (logentry->pageoffsets[page + j] == 0xFFFF)
2154
                        logentry->pagescurrent++;
2155
                    logentry->pageoffsets[page + j] = logentry->pagesused - cnt + j;
2156
                    if (logentry->pagesused - cnt + j + 1 != logentry->pagescurrent
2157
                     || logentry->pageoffsets[page + j] != page + j)
2158
                        logentry->issequential = 0;
2159
                }
2160
                i += cnt;
2161
            }
249 theseven 2162
            else
2163
            {
2164
                ftl_initialized = false;
2165
                panicf(PANIC_KILLTHREAD, "FTL: Write error: %u %u %u!",
2166
                       (unsigned)sector, (unsigned)count, (unsigned)i);
2167
            }
2 theseven 2168
        }
54 theseven 2169
        if (logentry->pagesused == ppb) ftl_remove_scattered_block(logentry);
2 theseven 2170
    }
2171
    if (ftl_cxt.swapcounter >= 300)
2172
    {
2173
        ftl_cxt.swapcounter -= 20;
2174
        for (i = 0; i < 4; i++) if (ftl_swap_blocks() == 0) break;
2175
    }
2176
    if (ftl_cxt.erasedirty == 1)
2177
    {
2178
        ftl_cxt.erasedirty = 0;
2179
        for (i = 0; i < 8; i++)
2180
            if (ftl_erasectr_dirt[i] >= 100)
2181
            {
2182
                ftl_next_ctrl_pool_page();
2183
                ftl_save_erasectr_page(i);
2184
            }
2185
    }
54 theseven 2186
    mutex_unlock(&ftl_mtx);
2 theseven 2187
    return 0;
2188
}
2189
#endif
2190
 
2191
 
2192
#ifndef FTL_READONLY
54 theseven 2193
/* Exposed function: Performes a sync / unmount,
2194
   i.e. commits all scattered page blocks,
2195
   distributes wear, and commits the FTL context. */
2196
uint32_t ftl_sync(void)
2 theseven 2197
{
2198
    uint32_t i;
2199
    uint32_t rc = 0;
54 theseven 2200
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
128 theseven 2201
 
206 theseven 2202
    if (!ftl_initialized) return 0;
128 theseven 2203
 
2 theseven 2204
    if (ftl_cxt.clean_flag == 1) return 0;
2205
 
66 theseven 2206
    mutex_lock(&ftl_mtx, TIMEOUT_BLOCK);
249 theseven 2207
    if (!ftl_initialized)
2208
    {
2209
        mutex_unlock(&ftl_mtx);
2210
        return -1;
2211
    }
201 theseven 2212
 
54 theseven 2213
#ifdef FTL_TRACE
61 theseven 2214
    DEBUGF("FTL: Syncing");
54 theseven 2215
#endif
2216
 
2 theseven 2217
    if (ftl_cxt.swapcounter >= 20)
2218
        for (i = 0; i < 4; i++)
2219
            if (ftl_swap_blocks() == 0)
2220
            {
2221
                ftl_cxt.swapcounter -= 20;
2222
                break;
2223
            }
2224
    for (i = 0; i < 0x11; i++)
2225
    {
2226
        if (ftl_log[i].scatteredvblock == 0xFFFF) continue;
2227
        ftl_cxt.nextblockusn++;
2228
        if (ftl_log[i].issequential == 1)
2229
            rc |= ftl_commit_sequential(&ftl_log[i]);
2230
        else rc |= ftl_commit_scattered(&ftl_log[i]);
2231
    }
54 theseven 2232
    if (rc != 0)
2233
    {
2234
        mutex_unlock(&ftl_mtx);
155 theseven 2235
        return -1;
54 theseven 2236
    }
2237
    for (i = 0; i < 5; i++)
2238
        if (ftl_commit_cxt() == 0)
2239
        {
2240
            mutex_unlock(&ftl_mtx);
2241
            return 0;
2242
        }
2243
        else ftl_cxt.ftlctrlpage |= ppb - 1;
2244
    mutex_unlock(&ftl_mtx);
155 theseven 2245
    return -2;
2 theseven 2246
}
2247
#endif
2248
 
2249
 
124 theseven 2250
/* Block allocator for FTL recovery */
2251
static uint32_t ftl_alloc_block() INITCODE_ATTR;
2252
static uint32_t ftl_alloc_block()
2253
{
2254
    while (1)
2255
    {
2256
        for (; firstfree < ftl_nand_type->userblocks + 0x17; firstfree++)
2257
            if (!blk_type[firstfree]) break;
2258
            else if (allocmode && blk_type[firstfree] != 1)
2259
            {
2260
                if (ftl_erase_block(firstfree))
2261
                {
2262
                    cprintf(CONSOLE_BOOT, "Couldn't erase vBlock %d (pool alloc)!\n", firstfree);
2263
                    return 1;
2264
                }
2265
                break;
2266
            }
2267
        if (firstfree < ftl_nand_type->userblocks + 0x17)
2268
        {
2269
            blk_type[firstfree] = 1;
2270
            return firstfree++;
2271
        }
2272
        if (!allocmode)
2273
        {
2274
            allocmode = 1;
2275
            firstfree = 0;
2276
        }
2277
        else
2278
        {
2279
            cputs(CONSOLE_BOOT, "Out of empty blocks!\n");
2280
            return 1;
2281
        }
2282
    }
2283
}
2284
 
2285
 
2286
static uint32_t ftl_repair(void) INITCODE_ATTR;
2287
static uint32_t ftl_repair()
2288
{
2289
    uint32_t i, j, k;
2290
#ifdef HAVE_LCD
2291
    struct progressbar_state progressbar;
2292
#endif
2293
 
2294
    cputs(CONSOLE_BOOT, "Scanning flash...\n");
2295
#ifdef HAVE_LCD
2296
    lcdconsole_progressbar(&progressbar, 0, ftl_nand_type->userblocks + 0x17);
2297
#endif
2298
    uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks;
223 theseven 2299
    memset(&ftl_cxt, 0x00, sizeof(ftl_cxt));
2300
    memset(ftl_map, 0xff, sizeof(ftl_map));
2301
    memset(blk_usn, 0x00, sizeof(blk_usn));
2302
    memset(blk_type, 0x00, sizeof(blk_type));
2303
    memset(ftl_erasectr, 0x00, sizeof(ftl_erasectr));
2304
    memset(erasectr_usn, 0xff, sizeof(erasectr_usn));
124 theseven 2305
    user_usn = 0;
2306
    meta_usn = 0xffffffff;
2307
    for (i = 0; i < ftl_nand_type->userblocks + 0x17; i++)
2308
    {
2309
        uint32_t ret = ftl_vfl_read((i + 1) * ppb - 1, 0, &ftl_sparebuffer[0], 1, 0);
2310
        if ((ret & 0x11F) == 0 && ftl_sparebuffer[0].meta.type == 0x41)
2311
        {
2312
            uint32_t lbn = ftl_sparebuffer[0].user.lpn / ppb;
2313
            if (ftl_sparebuffer[0].user.usn > user_usn)
2314
                user_usn = ftl_sparebuffer[0].user.usn;
2315
            if (ftl_sparebuffer[0].user.usn > blk_usn[lbn])
2316
            {
2317
                if (ftl_map[lbn] != 0xffff)
2318
                    blk_type[ftl_map[lbn]] = 5;
2319
                blk_usn[lbn] = ftl_sparebuffer[0].user.usn;
2320
                ftl_map[lbn] = i;
2321
                blk_type[i] = 1;
2322
            }
2323
            else blk_type[i] = 5;
2324
        }
2325
        else
2326
            for (j = 0; j < ppb; j++)
2327
            {
227 theseven 2328
                ret = ftl_vfl_read(i * ppb + j, ftl_buffer, &ftl_sparebuffer[0], 1, 0);
124 theseven 2329
                if (ret & 2) break;
2330
                if (ret & 0x11F)
2331
                {
2332
                    blk_type[i] = 4;
2333
                    continue;
2334
                }
2335
                if (ftl_sparebuffer[0].meta.type == 0x40)
2336
                {
2337
                    blk_type[i] = 2;
2338
                    break;
2339
                }
2340
                else if (ftl_sparebuffer[0].meta.type - 0x43 <= 4)
2341
                {
2342
                    blk_type[i] = 3;
2343
                    if (ftl_sparebuffer[0].meta.type == 0x46)
2344
                    {
2345
                        uint32_t idx = ftl_sparebuffer[0].meta.idx;
2346
                        if (ftl_sparebuffer[0].meta.usn < meta_usn)
2347
                            meta_usn = ftl_sparebuffer[0].meta.usn;
2348
                        if (ftl_sparebuffer[0].meta.usn < erasectr_usn[idx])
2349
                        {
2350
                            erasectr_usn[idx] = ftl_sparebuffer[0].meta.usn;
2351
                            ret = ftl_vfl_read(i * ppb + j, &ftl_erasectr[idx << 10],
2352
                                               &ftl_sparebuffer[0], 1, 0);
2353
                            if (ret & 0x11f) memset(&ftl_erasectr[idx << 10], 0, 0x800);
2354
                        }
2355
                    }
2356
                }
2357
                else
2358
                {
2359
                    cprintf(CONSOLE_BOOT, "Invalid block type %02X while reading vPage %d\n",
2360
                            ftl_sparebuffer[0].meta.type, i * ppb + j);
227 theseven 2361
                    for (i = 0; i < 64; i += 8)
2362
                        cprintf(CONSOLE_BOOT, "%02X %02X %02X %02X  %02X %02X %02X %02X\n",
2363
                                ((uint8_t*)(ftl_sparebuffer))[i], ((uint8_t*)(ftl_sparebuffer))[i + 1], ((uint8_t*)(ftl_sparebuffer))[i + 2], ((uint8_t*)(ftl_sparebuffer))[i + 3],
2364
                                ((uint8_t*)(ftl_sparebuffer))[i + 4], ((uint8_t*)(ftl_sparebuffer))[i + 5], ((uint8_t*)(ftl_sparebuffer))[i + 6], ((uint8_t*)(ftl_sparebuffer))[i + 7]);
2365
                    for (i = 0; i < 2048; i += 16)
2366
                        cprintf(CONSOLE_BOOT, "%02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X\n",
2367
                                ftl_buffer[i], ftl_buffer[i + 1], ftl_buffer[i + 2], ftl_buffer[i + 3], ftl_buffer[i + 4], ftl_buffer[i + 5], ftl_buffer[i + 6], ftl_buffer[i + 7],
2368
                                ftl_buffer[i + 8], ftl_buffer[i + 9], ftl_buffer[i + 10], ftl_buffer[i + 11], ftl_buffer[i + 12], ftl_buffer[i + 13], ftl_buffer[i + 14], ftl_buffer[i + 15]);
2369
                    cprintf(CONSOLE_BOOT, "Remaps:");
2370
                    for (j = 0; j < ftl_banks; j++)
2371
                    {
2372
                        cprintf(CONSOLE_BOOT, "\nBank %d", j);
2373
                        for (i = 0; i < ftl_vfl_cxt[j].spareused; i++)
2374
                        {
2375
                            cprintf(CONSOLE_BOOT, ": %d => %d ",
2376
                                    ftl_vfl_cxt[j].remaptable[i], ftl_vfl_cxt[j].firstspare + i);
2377
                            if (ftl_vfl_is_good_block(j, i) != 1) cprintf(CONSOLE_BOOT, "(NOT IN BBT) ");
2378
                        }
2379
                    }
2380
                    cprintf(CONSOLE_BOOT, "\n");
124 theseven 2381
                    return 1;
2382
                }
2383
            }
2384
#ifdef HAVE_LCD
2385
        progressbar_setpos(&progressbar, i + 1, false);
2386
#endif
2387
    }
2388
 
2389
    uint32_t sum = 0;
2390
    uint32_t count = 0;
2391
    for (i = 0; i < 0x2000; i++)
2392
        if (ftl_erasectr[i])
2393
        {
2394
            sum += ftl_erasectr[i];
2395
            count++;
2396
        }
213 theseven 2397
    uint32_t average = 0;
2398
    if (count) average = sum / count;
124 theseven 2399
    for (i = 0; i < 0x2000; i++)
2400
        if (!ftl_erasectr[i])
2401
            ftl_erasectr[i] = average;
2402
 
2403
    cputs(CONSOLE_BOOT, "Committing scattered pages...\n");
2404
    count = 0;
2405
    for (i = 0; i < ftl_nand_type->userblocks + 0x17; i++)
2406
        if (blk_type[i] == 2) count++;
2407
    uint32_t block;
2408
    uint32_t dirty;
2409
    if (count)
2410
    {
2411
#ifdef HAVE_LCD
134 theseven 2412
        lcdconsole_progressbar(&progressbar, 0, count * ppb * 2);
124 theseven 2413
#endif
2414
        count = 0;
2415
        for (i = 0; i < ftl_nand_type->userblocks + 0x17; i++)
2416
            if (blk_type[i] == 2)
2417
            {
2418
                block = 0xffff;
134 theseven 2419
                for (j = 0; j < ppb; j++)
124 theseven 2420
                {
134 theseven 2421
#ifdef HAVE_LCD
2422
                    progressbar_setpos(&progressbar, count * ppb * 2 + j, false);
2423
#endif
124 theseven 2424
                    uint32_t ret = ftl_vfl_read(i * ppb + j, ftl_buffer,
2425
                                                &ftl_sparebuffer[0], 1, 0);
2426
                    if (ret & 0x11F) continue;
2427
                    if (ftl_sparebuffer[0].user.type != 0x40)
2428
                    {
2429
                        cprintf(CONSOLE_BOOT, "Invalid block type %02X while reading "
2430
                                              "vPage %d (scattered page)!\n",
2431
                                ftl_sparebuffer[0].meta.type, i * ppb + j);
2432
                        return 1;
2433
                    }
2434
                    if (block == 0xffff)
2435
                    {
2436
                        block = ftl_sparebuffer[0].user.lpn / ppb;
2437
                        memset(pageusn, 0x00, 0x800);
2438
                        memset(pagedata, 0x00, 0x100000);
2439
                        if (ftl_map[block] != 0xffff)
2440
                            for (k = 0; k < ppb; k++)
2441
                            {
2442
                                uint32_t ret = ftl_vfl_read(ftl_map[block] * ppb + k, pagedata[k],
2443
                                                            &ftl_copyspare[0], 1, 0);
2444
                                if (ret & 0x11F) continue;
2445
                                if (ftl_copyspare[0].user.type != 0x40
2446
                                 && ftl_copyspare[0].user.type != 0x41)
2447
                                {
2448
                                    cprintf(CONSOLE_BOOT, "Invalid block type %02X while reading "
2449
                                                          "vPage %d (scattered page orig)!\n",
2450
                                            ftl_sparebuffer[0].meta.type,
2451
                                            ftl_map[block] * ppb + k);
2452
                                    return 1;
2453
                                }
2454
                                if (block != ftl_copyspare[0].user.lpn / ppb)
2455
                                {
2456
                                    cprintf(CONSOLE_BOOT, "Foreign page in scattered page orig "
2457
                                                          "block (vPage %d, LPN %d)!\n",
2458
                                            ftl_map[block] * ppb + k,
2459
                                            ftl_sparebuffer[0].user.usn);
2460
                                    return 1;
2461
                                }
2462
                                pageusn[k] = ftl_copyspare[0].user.usn;
2463
                            }
2464
                        dirty = 0;
2465
                    }
2466
                    if (block != ftl_sparebuffer[0].user.lpn / ppb)
2467
                    {
2468
                        cprintf(CONSOLE_BOOT, "Foreign page in scattered page block "
2469
                                              "block (vPage %d, LPN %d)!\n",
2470
                                i * ppb + j, ftl_sparebuffer[0].user.lpn);
2471
                        return 1;
2472
                    }
2473
                    uint32_t idx = ftl_sparebuffer[0].user.lpn % ppb;
2474
                    if (ftl_sparebuffer[0].user.usn > user_usn)
2475
                        user_usn = ftl_sparebuffer[0].user.usn;
2476
                    if (ftl_sparebuffer[0].user.usn > pageusn[idx])
2477
                    {
2478
                        pageusn[idx] = ftl_sparebuffer[0].user.usn;
2479
                        memcpy(pagedata[idx], ftl_buffer, 0x800);
2480
                        dirty = 1;
2481
                    }
2482
                }
2483
                if (dirty)
2484
                {
2485
                    if (ftl_erase_block(i))
2486
                    {
2487
                        cprintf(CONSOLE_BOOT, "Couldn't erase vBlock %d "
2488
                                              "(scattered page commit)!\n", i);
2489
                        return 1;
2490
                    }
2491
                    for (j = 0; j < ppb; j++)
2492
                    {
134 theseven 2493
#ifdef HAVE_LCD
2494
                        progressbar_setpos(&progressbar, count * ppb * 2 + ppb + j, false);
2495
#endif
124 theseven 2496
                        memset(&ftl_sparebuffer[0], 0xFF, 0x40);
2497
                        ftl_sparebuffer[0].user.lpn = block * ppb + j;
2498
                        ftl_sparebuffer[0].user.usn = pageusn[j];
2499
                        ftl_sparebuffer[0].user.type = 0x40;
2500
                        if (j == ppb - 1) ftl_sparebuffer[0].user.type = 0x41;
2501
                        if (ftl_vfl_write(i * ppb + j, 1, pagedata[j], &ftl_sparebuffer[0]))
2502
                        {
2503
                            cprintf(CONSOLE_BOOT, "Couldn't write vPage %d "
2504
                                                  "(scattered page commit)!\n", i * ppb + j);
2505
                            return 1;
2506
                        }
2507
                    }
2508
                    if (ftl_map[block] != 0xffff) blk_type[ftl_map[block]] = 5;
2509
                    blk_type[i] = 1;
2510
                    ftl_map[block] = i;
2511
                }
2512
                else blk_type[i] = 5;
2513
#ifdef HAVE_LCD
134 theseven 2514
                progressbar_setpos(&progressbar, ++count * ppb * 2, false);
124 theseven 2515
#endif
2516
            }
2517
    }
2518
 
2519
    cputs(CONSOLE_BOOT, "Fixing block map...\n");
2520
    allocmode = 0;
2521
    firstfree = 0;
2522
    for (i = 0; i < 3; i++) ftl_cxt.ftlctrlblocks[i] = ftl_alloc_block();
2523
    for (i = 0; i < 20; i++) ftl_cxt.blockpool[i] = ftl_alloc_block();
2524
    for (i = 0; i < ftl_nand_type->userblocks; i++)
2525
        if (ftl_map[i] == 0xffff)
2526
            ftl_map[i] = ftl_alloc_block();
2527
    ftl_cxt.usn = meta_usn - 1;
2528
    ftl_cxt.nextblockusn = user_usn + 1;
2529
    ftl_cxt.freecount = 20;
2530
    ftl_cxt.clean_flag = 1;
2531
 
2532
    cputs(CONSOLE_BOOT, "Committing FTL context...\n");
2533
    uint32_t blockmappages = ftl_nand_type->userblocks >> 10;
2534
    if ((ftl_nand_type->userblocks & 0x1FF) != 0) blockmappages++;
2535
    uint32_t erasectrpages = (ftl_nand_type->userblocks + 23) >> 10;
2536
    if (((ftl_nand_type->userblocks + 23) & 0x1FF) != 0) erasectrpages++;
2537
    uint32_t page = ftl_cxt.ftlctrlblocks[0] * ppb;
2538
    for (i = 0; i < erasectrpages; i++)
2539
    {
2540
        memset(&ftl_sparebuffer, 0xFF, 0x40);
2541
        ftl_sparebuffer[0].meta.usn = ftl_cxt.usn;
2542
        ftl_sparebuffer[0].meta.idx = i;
2543
        ftl_sparebuffer[0].meta.type = 0x46;
2544
        if (ftl_vfl_write(page, 1, &ftl_erasectr[i << 10], &ftl_sparebuffer[0]))
2545
        {
2546
            cprintf(CONSOLE_BOOT, "Couldn't write vPage %d (save erase counters)!\n", page);
2547
            return 1;
2548
        }
2549
        ftl_cxt.ftl_erasectr_pages[i] = page++;
2550
    }
2551
    for (i = 0; i < blockmappages; i++)
2552
    {
2553
        memset(&ftl_sparebuffer[0], 0xFF, 0x40);
2554
        ftl_sparebuffer[0].meta.usn = ftl_cxt.usn;
2555
        ftl_sparebuffer[0].meta.idx = i;
2556
        ftl_sparebuffer[0].meta.type = 0x44;
2557
        if (ftl_vfl_write(page, 1, &ftl_map[i << 10], &ftl_sparebuffer[0]))
2558
        {
2559
            cprintf(CONSOLE_BOOT, "Couldn't write vPage %d (save block map)!\n", page);
2560
            return 1;
2561
        }
2562
        ftl_cxt.ftl_map_pages[i] = page++;
2563
    }
2564
    ftl_cxt.ftlctrlpage = page;
2565
    memset(&ftl_sparebuffer[0], 0xFF, 0x40);
2566
    ftl_sparebuffer[0].meta.usn = ftl_cxt.usn;
2567
    ftl_sparebuffer[0].meta.type = 0x43;
2568
    if (ftl_vfl_write(page, 1, &ftl_cxt, &ftl_sparebuffer[0]))
2569
    {
2570
        cprintf(CONSOLE_BOOT, "Couldn't write vPage %d (save FTL context)!\n", page);
2571
        return 1;
2572
    }
2573
    ftl_store_ctrl_block_list();
134 theseven 2574
    return 0;
124 theseven 2575
}
2576
 
2577
 
2 theseven 2578
/* Initializes and mounts the FTL.
2579
   As long as nothing was written, you won't need to unmount it.
54 theseven 2580
   Before shutting down after writing something, call ftl_sync(),
124 theseven 2581
   which will return immediately if everything was already clean. */
54 theseven 2582
uint32_t ftl_init(void)
2 theseven 2583
{
210 theseven 2584
    if (ftl_initialized) return 0;
54 theseven 2585
    mutex_init(&ftl_mtx);
2 theseven 2586
    uint32_t i;
61 theseven 2587
    int rc;
223 theseven 2588
    if ((rc = nand_device_init()) != 0)
249 theseven 2589
        panicf(PANIC_KILLTHREAD, "FTL: Lowlevel NAND driver init failed: %d", rc);
2 theseven 2590
    ftl_banks = 0;
2591
    for (i = 0; i < 4; i++)
2592
        if (nand_get_device_type(i) != 0) ftl_banks = i + 1;
2593
    ftl_nand_type = nand_get_device_type(0);
54 theseven 2594
 
223 theseven 2595
    if (!ftl_has_devinfo())
54 theseven 2596
    {
201 theseven 2597
           DEBUGF("FTL: No DEVICEINFO found!");
155 theseven 2598
        return -1;
54 theseven 2599
    }
2 theseven 2600
    if (ftl_vfl_open() == 0)
201 theseven 2601
    {
2 theseven 2602
        if (ftl_open() == 0)
128 theseven 2603
        {
2604
            ftl_initialized = true;
2 theseven 2605
            return 0;
128 theseven 2606
        }
249 theseven 2607
        cputs(CONSOLE_BOOT, "The FTL seems to be unclean. Forcing check.\n");
201 theseven 2608
        if (ftl_repair() != 0)
211 theseven 2609
            cputs(CONSOLE_BOOT, "FTL recovery failed. Use disk mode to recover.\n");
201 theseven 2610
        else
2611
        {
211 theseven 2612
            cputs(CONSOLE_BOOT, "FTL recovery finished. Trying to mount again...\n");
201 theseven 2613
            if (ftl_open() == 0)
128 theseven 2614
            {
211 theseven 2615
                cputs(CONSOLE_BOOT, "Mount succeeded.\n");
128 theseven 2616
                ftl_initialized = true;
2617
                return 0;
2618
            }
211 theseven 2619
            cputs(CONSOLE_BOOT, "Mounting FTL failed again, use disk mode to recover.\n");
201 theseven 2620
        }
2621
    }
2 theseven 2622
 
54 theseven 2623
    DEBUGF("FTL: Initialization failed!");
2624
 
223 theseven 2625
    return -2;
2 theseven 2626
}