mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-10 08:03:04 -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
|
|
@ -2857,6 +2857,58 @@ static bool view_ram_info(void)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && (CONFIG_NAND == NAND_RK27XX) \
|
||||||
|
&& !(CONFIG_STORAGE & STORAGE_NAND)
|
||||||
|
#include "ftl-probe-rk27xx.h"
|
||||||
|
|
||||||
|
/* The target does not know its NAND's FTL scheme yet: show what the
|
||||||
|
* finder sees, for the user to report */
|
||||||
|
static bool dbg_ftl_scheme(void)
|
||||||
|
{
|
||||||
|
struct ftl_probe p;
|
||||||
|
struct simplelist_info info;
|
||||||
|
|
||||||
|
ftl_probe(&p);
|
||||||
|
simplelist_info_init(&info, "FTL scheme", 0, NULL);
|
||||||
|
simplelist_reset_lines();
|
||||||
|
simplelist_addline("Scheme: %s", ftl_probe_scheme_name(p.scheme));
|
||||||
|
if (p.flash_error)
|
||||||
|
{
|
||||||
|
simplelist_addline("No usable NAND (%d)", p.flash_error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
simplelist_addline("Chip: %lu blocks", (unsigned long)p.blocks);
|
||||||
|
simplelist_addline("%u planes, %u/%u sec", p.planes, p.sec_per_page,
|
||||||
|
p.sec_per_block);
|
||||||
|
if (p.idb_boot_blocks)
|
||||||
|
{
|
||||||
|
simplelist_addline("IDB: %u blk %u+%u MB", p.idb_boot_blocks,
|
||||||
|
p.idb_sys_mb, p.idb_data_mb);
|
||||||
|
if (p.idb_rk27)
|
||||||
|
simplelist_addline("IDB: RK27, ECC t=%u", p.idb_ecc_t);
|
||||||
|
else
|
||||||
|
simplelist_addline("IDB: early layout");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
simplelist_addline("IDB: none");
|
||||||
|
simplelist_addline("Scanned %u blocks, t=%u", p.scanned, p.scan_ecc_t);
|
||||||
|
simplelist_addline("Erased %u, bad ECC %u", p.erased, p.unreadable);
|
||||||
|
simplelist_addline("A: %u logs, %u used", p.a_logs, p.a_blocks);
|
||||||
|
simplelist_addline("B: %u tables at %u", p.b_tables,
|
||||||
|
p.b_tables ? p.first_b_table : 0);
|
||||||
|
simplelist_addline("B: %u cache, %u data", p.b_cache, p.b_data);
|
||||||
|
if (p.b_other)
|
||||||
|
simplelist_addline("B: %u other, %04x", p.b_other, p.first_b_other);
|
||||||
|
if (p.other)
|
||||||
|
simplelist_addline("Other: %u, %u %02x%02x%02x", p.other,
|
||||||
|
p.first_other, p.first_other_meta[0],
|
||||||
|
p.first_other_meta[1], p.first_other_meta[2]);
|
||||||
|
}
|
||||||
|
return simplelist_show_list(&info);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/****** The menu *********/
|
/****** The menu *********/
|
||||||
static const struct {
|
static const struct {
|
||||||
unsigned char *desc; /* string or ID */
|
unsigned char *desc; /* string or ID */
|
||||||
|
|
@ -2909,6 +2961,10 @@ static const struct {
|
||||||
#if ((CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(SONY_NWZ_LINUX) || defined(HIBY_LINUX) || defined(FIIO_M3K_LINUX)) && !defined(SIMULATOR)
|
#if ((CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(SONY_NWZ_LINUX) || defined(HIBY_LINUX) || defined(FIIO_M3K_LINUX)) && !defined(SIMULATOR)
|
||||||
{ "View HW info", dbg_hw_info },
|
{ "View HW info", dbg_hw_info },
|
||||||
#endif
|
#endif
|
||||||
|
#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && (CONFIG_NAND == NAND_RK27XX) \
|
||||||
|
&& !(CONFIG_STORAGE & STORAGE_NAND)
|
||||||
|
{ "View FTL scheme", dbg_ftl_scheme },
|
||||||
|
#endif
|
||||||
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
||||||
{ "View partitions", dbg_partitions },
|
{ "View partitions", dbg_partitions },
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1714,6 +1714,11 @@ target/arm/rk27xx/ftl-rk27xx.c
|
||||||
target/arm/rk27xx/ftl-scheme-a.c
|
target/arm/rk27xx/ftl-scheme-a.c
|
||||||
target/arm/rk27xx/flash-rk27xx.c
|
target/arm/rk27xx/flash-rk27xx.c
|
||||||
target/arm/rk27xx/nand-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/usb-rk27xx.c
|
||||||
target/arm/rk27xx/lcdif-rk27xx.c
|
target/arm/rk27xx/lcdif-rk27xx.c
|
||||||
target/arm/rk27xx/rkw-loader.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