mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-09 23:53:28 -04:00
rk27xx: add a finder for the NAND's FTL scheme
Many rk27xx targets have NAND whose format nobody has examined. The
finder reads ID block 1 and the first page of the first 512 blocks and
says which FTL formatted them: Scheme A by its remap-log blocks,
Scheme B by its bad-block table and data headers, another Scheme B
generation by other 0xFxxx tags. The later ID block layout ('RK27' at
0x0a) records the FTL area's BCH strength at 0x1ed - 8 on the HM-601,
14 on the Archos Vision 28 - and the scan reads in that mode.
It is read-only, shown in the debug menu as "View FTL scheme", and
built for targets whose NAND is not storage - none yet.
Run on dumps of an HM-601 it reports Scheme B, a Samsung YP-CP3
Scheme A, and an Archos Vision 28 the other Scheme B generation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I0528f9d2a6996089b6488a77b43942f6fd1f2c16
This commit is contained in:
parent
16492de569
commit
e83b7d6dc4
4 changed files with 363 additions and 0 deletions
|
|
@ -1714,6 +1714,11 @@ target/arm/rk27xx/ftl-rk27xx.c
|
|||
target/arm/rk27xx/ftl-scheme-a.c
|
||||
target/arm/rk27xx/flash-rk27xx.c
|
||||
target/arm/rk27xx/nand-rk27xx.c
|
||||
#if (CONFIG_NAND == NAND_RK27XX) && !(CONFIG_STORAGE & STORAGE_NAND) \
|
||||
&& !defined(BOOTLOADER)
|
||||
/* no FTL scheme configured: the finder (debug menu) */
|
||||
target/arm/rk27xx/ftl-probe-rk27xx.c
|
||||
#endif
|
||||
target/arm/rk27xx/usb-rk27xx.c
|
||||
target/arm/rk27xx/lcdif-rk27xx.c
|
||||
target/arm/rk27xx/rkw-loader.c
|
||||
|
|
|
|||
219
firmware/target/arm/rk27xx/ftl-probe-rk27xx.c
Normal file
219
firmware/target/arm/rk27xx/ftl-probe-rk27xx.c
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
*
|
||||
* Copyright (C) 2026 by Marcin Bukat
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/* The FTL scheme finder - see ftl-probe-rk27xx.h.
|
||||
*
|
||||
* The two formats tell themselves apart in the metadata of a block's first
|
||||
* sector (flash-rk27xx.h): Scheme A marks its remap-log blocks with type
|
||||
* 0x52 in byte 2 (ftl-scheme-a.c), Scheme B keeps a 0xfxxx tag in bytes 0-1
|
||||
* - 0xf000 for its bad-block table near the start of the chip, 0xf100 and
|
||||
* 0xf200 for its cache and data blocks (ftl-scheme-b.c). Every zone of
|
||||
* Scheme A has two log blocks, the first zone within its first 256 blocks;
|
||||
* Scheme B's table lies within 50 blocks of the boot area. So the first
|
||||
* SCAN_BLOCKS blocks decide.
|
||||
*
|
||||
* They are read in the BCH mode the FTL's area uses, which need not be the
|
||||
* boot area's t=8: the Archos Vision writes its FTL area with t=14. ID
|
||||
* block 1 of the later generation - 'RK27' at 0x0a - records it at 0x1ed
|
||||
* (8 on the HM-601, 14 on the Archos); the earlier generation's ID block
|
||||
* has no such field, and its devices are t=8 throughout. */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "nand-target.h"
|
||||
#include "flash-rk27xx.h"
|
||||
#include "ftl-probe-rk27xx.h"
|
||||
|
||||
#define SCAN_BLOCKS 512
|
||||
|
||||
/* ID blocks: every IDB_STRIDE-th raw sector of the boot area, metadata type
|
||||
* IDB_TYPE; ID block 1 is the sector after (ftl-rk27xx.c) */
|
||||
#define IDB_STRIDE 512
|
||||
#define IDB_POSITIONS 50
|
||||
#define IDB_TYPE 0x69
|
||||
#define IDB1_MAGIC 0x0a /* 'RK27' in the later generation's ... */
|
||||
#define IDB1_ECC_T 0x1ed /* ... which records the BCH t here */
|
||||
#define DEFAULT_ECC_T 8
|
||||
|
||||
#define A_LOG_TYPE 0x52
|
||||
#define B_TAG_TABLE 0xf000
|
||||
#define B_TAG_CACHE 0xf100
|
||||
#define B_TAG_DATA 0xf200
|
||||
|
||||
static void probe_idb(struct ftl_probe *p)
|
||||
{
|
||||
uint8_t data[FLASH_SECTOR_SIZE], meta[FLASH_META_SIZE];
|
||||
uint32_t pos;
|
||||
bool found = false;
|
||||
|
||||
for (pos = 0; pos < IDB_POSITIONS && !found; pos++)
|
||||
{
|
||||
uint32_t raw = pos * IDB_STRIDE;
|
||||
|
||||
if (flash_read_raw(raw, data, meta) == 0 && meta[2] == IDB_TYPE &&
|
||||
flash_read_raw(raw + 1, data, meta) == 0)
|
||||
{
|
||||
p->idb_boot_blocks = (uint16_t)(data[0] | data[1] << 8);
|
||||
p->idb_sys_mb = (uint16_t)(data[2] | data[3] << 8);
|
||||
p->idb_data_mb = (uint16_t)(data[4] | data[5] << 8);
|
||||
p->idb_rk27 = memcmp(data + IDB1_MAGIC, "RK27", 4) == 0;
|
||||
if (p->idb_rk27)
|
||||
{
|
||||
p->idb_ecc_t = data[IDB1_ECC_T];
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void probe_block(struct ftl_probe *p, uint32_t blk)
|
||||
{
|
||||
uint8_t meta[FLASH_META_SIZE];
|
||||
uint16_t tag;
|
||||
|
||||
if (flash_read(blk * p->sec_per_block, NULL, meta, 1) != 0)
|
||||
{
|
||||
p->unreadable++;
|
||||
}
|
||||
else
|
||||
{
|
||||
tag = (uint16_t)(meta[0] | meta[1] << 8);
|
||||
|
||||
/* tag 0xffff: erased, or a block only its byte 2 marks - an ID
|
||||
* block, type 0x69 */
|
||||
if (tag == 0xffff)
|
||||
{
|
||||
p->erased++;
|
||||
}
|
||||
else if (meta[0] == 0xff && meta[1] == 0x00 && meta[2] == A_LOG_TYPE)
|
||||
{
|
||||
p->a_logs++;
|
||||
}
|
||||
else if (meta[1] == 0x00)
|
||||
{
|
||||
/* Scheme A's flash layer zeroes byte 1 of every page */
|
||||
p->a_blocks++;
|
||||
}
|
||||
else if (tag == B_TAG_TABLE)
|
||||
{
|
||||
if (p->b_tables++ == 0)
|
||||
{
|
||||
p->first_b_table = (uint16_t)blk;
|
||||
}
|
||||
}
|
||||
else if ((tag & 0xff00) == B_TAG_CACHE)
|
||||
{
|
||||
p->b_cache++;
|
||||
}
|
||||
else if ((tag & 0xff00) == B_TAG_DATA)
|
||||
{
|
||||
p->b_data++;
|
||||
}
|
||||
else if ((tag & 0xf000) == 0xf000)
|
||||
{
|
||||
if (p->b_other++ == 0)
|
||||
{
|
||||
p->first_b_other = tag;
|
||||
}
|
||||
}
|
||||
else if (p->other++ == 0)
|
||||
{
|
||||
p->first_other = (uint16_t)blk;
|
||||
memcpy(p->first_other_meta, meta, sizeof(p->first_other_meta));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ftl_probe(struct ftl_probe *p)
|
||||
{
|
||||
const struct flash_geometry *geo;
|
||||
uint32_t blk;
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->first_b_table = 0xffff;
|
||||
|
||||
flash_init();
|
||||
p->flash_error = flash_layer_init();
|
||||
|
||||
if (p->flash_error == 0)
|
||||
{
|
||||
geo = flash_get_geometry();
|
||||
p->planes = geo->planes;
|
||||
p->sec_per_page = geo->sec_per_page;
|
||||
p->sec_per_block = geo->sec_per_block;
|
||||
p->blocks = geo->total_blocks;
|
||||
|
||||
probe_idb(p);
|
||||
|
||||
/* a value the controller has no mode for is reported, and t=8 used */
|
||||
p->scan_ecc_t = DEFAULT_ECC_T;
|
||||
if (p->idb_rk27 && flash_set_ecc(p->idb_ecc_t) == 0)
|
||||
{
|
||||
p->scan_ecc_t = p->idb_ecc_t;
|
||||
}
|
||||
|
||||
for (blk = 0; blk < SCAN_BLOCKS && blk < p->blocks; blk++)
|
||||
{
|
||||
probe_block(p, blk);
|
||||
}
|
||||
p->scanned = (uint16_t)blk;
|
||||
flash_set_ecc(DEFAULT_ECC_T);
|
||||
|
||||
if (p->b_tables > 0 && p->b_data > 0)
|
||||
{
|
||||
p->scheme = p->b_other > 0 ? FTL_PROBE_SCHEME_B_OTHER
|
||||
: FTL_PROBE_SCHEME_B;
|
||||
}
|
||||
else if (p->a_logs >= 2 && p->b_tables == 0)
|
||||
{
|
||||
p->scheme = FTL_PROBE_SCHEME_A;
|
||||
}
|
||||
else if (p->unreadable > p->scanned / 2)
|
||||
{
|
||||
p->scheme = FTL_PROBE_UNREADABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *ftl_probe_scheme_name(enum ftl_probe_scheme scheme)
|
||||
{
|
||||
const char *name = "unknown";
|
||||
|
||||
switch (scheme)
|
||||
{
|
||||
case FTL_PROBE_SCHEME_A:
|
||||
name = "A";
|
||||
break;
|
||||
case FTL_PROBE_SCHEME_B:
|
||||
name = "B";
|
||||
break;
|
||||
case FTL_PROBE_UNREADABLE:
|
||||
name = "unknown, ECC fails";
|
||||
break;
|
||||
case FTL_PROBE_SCHEME_B_OTHER:
|
||||
name = "B, other generation";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
83
firmware/target/arm/rk27xx/ftl-probe-rk27xx.h
Normal file
83
firmware/target/arm/rk27xx/ftl-probe-rk27xx.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
*
|
||||
* Copyright (C) 2026 by Marcin Bukat
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/* Which flash translation layer formatted an rk27xx target's NAND - for
|
||||
* targets whose config does not define CONFIG_RK27XX_FTL yet. Read-only:
|
||||
* it looks at the first page of the first blocks and nothing else. */
|
||||
|
||||
#ifndef __FTL_PROBE_RK27XX_H__
|
||||
#define __FTL_PROBE_RK27XX_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum ftl_probe_scheme
|
||||
{
|
||||
FTL_PROBE_UNKNOWN = 0,
|
||||
FTL_PROBE_SCHEME_A, /* remap-log blocks: ftl-scheme-a.c */
|
||||
FTL_PROBE_SCHEME_B, /* bad-block table and data headers:
|
||||
* ftl-scheme-b.c */
|
||||
FTL_PROBE_SCHEME_B_OTHER, /* Scheme B tags of another generation */
|
||||
FTL_PROBE_UNREADABLE, /* most blocks fail ECC in the mode used */
|
||||
};
|
||||
|
||||
struct ftl_probe
|
||||
{
|
||||
enum ftl_probe_scheme scheme;
|
||||
int flash_error; /* flash_layer_init() result; 0 = chip found */
|
||||
|
||||
/* the chip */
|
||||
uint8_t planes;
|
||||
uint8_t sec_per_page; /* both planes */
|
||||
uint16_t sec_per_block;
|
||||
uint32_t blocks;
|
||||
|
||||
/* ID block 1, 0 if none found */
|
||||
uint16_t idb_boot_blocks; /* raw blocks */
|
||||
uint16_t idb_sys_mb;
|
||||
uint16_t idb_data_mb;
|
||||
bool idb_rk27; /* the later layout, with 'RK27' at 0x0a ... */
|
||||
uint8_t idb_ecc_t; /* ... and the FTL area's BCH t at 0x1ed */
|
||||
|
||||
uint8_t scan_ecc_t; /* the BCH t the blocks were read with */
|
||||
|
||||
/* what page 0 of the blocks scanned holds */
|
||||
uint16_t scanned;
|
||||
uint16_t unreadable;
|
||||
uint16_t erased;
|
||||
uint16_t a_logs; /* Scheme A remap-log blocks */
|
||||
uint16_t a_blocks; /* other blocks Scheme A has programmed */
|
||||
uint16_t b_tables; /* Scheme B: 0xf000 bad-block table */
|
||||
uint16_t b_cache; /* 0xf1xx */
|
||||
uint16_t b_data; /* 0xf2xx */
|
||||
uint16_t b_other; /* other 0xfxxx tags */
|
||||
uint16_t other; /* none of these */
|
||||
uint16_t first_b_table; /* block of the first 0xf000; 0xffff = none */
|
||||
uint16_t first_b_other; /* the first other 0xfxxx tag; 0 = none */
|
||||
uint16_t first_other; /* first block of none of these ... */
|
||||
uint8_t first_other_meta[3]; /* ... and its metadata */
|
||||
};
|
||||
|
||||
/* Run the probe; takes a fraction of a second */
|
||||
void ftl_probe(struct ftl_probe *p);
|
||||
|
||||
const char *ftl_probe_scheme_name(enum ftl_probe_scheme scheme);
|
||||
|
||||
#endif /* __FTL_PROBE_RK27XX_H__ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue