rk27xx: add the Scheme B flash translation layer

Scheme B is the self-describing NAND format of the HiFiMAN HM-601 and
similar players: every block carries its logical number and a version
in its metadata, so the mapping is rebuilt by a scan at mount, and
small writes go through a 16-page RAM cache journalled to flash.

ftl-scheme-b.c is a reimplementation from reverse engineering. The
format and the behaviour were worked out by analysing the machine code
of the HM-601's NAND bootloader and of a compiled Rockchip FTL object
from the rk2808 platform, which handles the same format, and checked
against dumps of the media; no source code was used. Where the two
binaries differ the HM-601 is followed: 16-bit versions compared
across wrap, plain 0xF200/0xF100 tags, a copy that stamps one header
on every page. The number of open exchange blocks is configurable - 8
on the HM-601, whose mount recovers no more.

Checked by running the compiled object under qemu over a NAND
simulator, side by side with this code, on a 4 GiB HM-601 dump: the
same state after mount, identical reads of all 3958 logical blocks,
and flash programs and erases identical one for one - over 600 random
writes on each of three seeds and at every power-cut point of three
sweeps, 1435 points - with 0 wrong sectors.

Not built yet: no target selects it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Icb935ea78d716c1e51f1453fc3ab3f47f3467027
This commit is contained in:
Marcin Bukat 2026-10-01 09:28:08 +02:00
parent efde59472e
commit 16492de569
2 changed files with 1867 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,87 @@
/***************************************************************************
* __________ __ ___.
* 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 "Scheme B" flash translation layer of rk27xx devices - the
* self-describing on-flash format the original firmware of the HiFiMAN
* HM-601 and similar players uses, read and written compatibly so that the
* original firmware keeps working on the same media. A reimplementation
* from reverse engineering; see ftl-scheme-b.c for how it works. */
#ifndef __FTL_SCHEME_B_H__
#define __FTL_SCHEME_B_H__
#include <stdbool.h>
#include <stdint.h>
struct ftl_b_config
{
/* The first block the FTL owns; the boot area is everything before it.
* Not recorded in the FTL's own structures. */
uint16_t first_block;
/* Exchange blocks open at once. The original firmware's own count must
* not be exceeded: its mount recovers no more than that many, and loses
* the data of the rest. 8 on the HM-601. */
uint8_t exch_blocks;
/* Never write, not even the repairs a mount normally makes. */
bool read_only;
};
/* Why a mount failed */
enum ftl_b_error
{
FTL_B_OK = 0,
FTL_B_ERR_GEOMETRY, /* the chip is not one this FTL can map */
FTL_B_ERR_CONFIG, /* an unusable configuration */
FTL_B_ERR_NO_TABLE, /* no bad-block table: not formatted */
FTL_B_ERR_TABLE, /* the bad-block table is damaged */
FTL_B_ERR_VERSION, /* a format generation not supported */
FTL_B_ERR_TOO_BIG, /* more logical blocks than fit in RAM */
};
struct ftl_b_status
{
enum ftl_b_error error;
bool writable;
uint16_t format_version; /* as recorded in the bad-block table */
uint16_t logical_blocks;
uint16_t bad_blocks;
uint16_t free_blocks; /* erased or erasable, queued for use */
uint8_t open_exch; /* exchange blocks in progress */
uint8_t cached_pages; /* pages held in the write cache */
};
/* Mount the media. Returns FTL_B_OK or the reason it failed. */
enum ftl_b_error ftl_b_mount(const struct ftl_b_config *config);
void ftl_b_get_status(struct ftl_b_status *status);
/* Sectors in the logical space; 0 when not mounted */
uint32_t ftl_b_capacity(void);
/* Return 0, or non-zero if the request was refused or a sector could not be
* read reliably. Sectors are 512 bytes. */
int ftl_b_read(uint32_t sector, void *buf, uint32_t count);
int ftl_b_write(uint32_t sector, const void *buf, uint32_t count);
/* Every write is on the flash when ftl_b_write() returns - the write cache
* is journalled as it changes - so there is nothing to write out. Kept for
* the storage layer's interface. */
void ftl_b_sync(void);
#endif /* __FTL_SCHEME_B_H__ */