gint/src/fs/fygue/fat/flash/cluster.c
2025-04-12 08:47:17 +02:00

331 lines
8.7 KiB
C

/*
** fygue/_flash/cluster - flash cluster handling
*/
#include <string.h>
#include <gint/defs/attributes.h>
#include <gint/defs/util.h>
#include <gint/hardware.h>
#include "flash.h"
//---
// Internals
//---
/* _bfile_cluster_metadata - special cluster metadata information */
struct _bfile_cluster_metadata
{
uint8_t bitmask[2];
uint32_t fcluster_version;
uint16_t lcluster_id;
uint16_t fcluster_crc;
uint8_t constant[24];
uint8_t signature[3];
uint8_t ecc_bitmask_position[3];
} GPACKED(1);
/* _fygue_flash_cluster_convert - ensure that the provided meta is valid
*
* notes
* - assume that fcluster is not NULL
* - try using P1 information first then, P2 if this greedy-read fail
* - to ensure that the provided bfile metadata is valid we will perform
* some test (also performed by Casio). First, ensure that the
* fcluster "kind" is valid (only 0x88, 0x22 or 0x11 are known and
* used) and then ensure that the "constant" part is also valid
*
* todo : ensure checksum
* todo : ensure ecc handling (?)
* todo : try P1 then P2
*
* return
* -1 if the provided `fcluster` is not NULL
* -1 if the bfile metadata cannot be validated
* 0 otherwise */
static int _fygue_flash_cluster_convert(
struct fygue_flash *flash,
struct fygue_flash_cluster *fcluster,
uintptr_t meta
) {
#if 0
static const uint8_t _constant1[] = {
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
};
static const uint8_t _constant2[] = {
0x11, 0x11, 0x11, 0x22,
0x22, 0x22, 0x44, 0x44,
0x44, 0x88, 0x88, 0x88,
0x77, 0x77, 0x77, 0xbb,
0xbb, 0xbb, 0xdd, 0xdd,
0xdd, 0xee, 0xee, 0xee,
};
#endif
struct _bfile_cluster_metadata *bfile_meta;
if (flash == NULL || fcluster == NULL)
return -1;
bfile_meta = (void*)(meta | 0xa0000000);
if (
bfile_meta->signature[0] != 0x5a ||
bfile_meta->signature[1] != 0x5a ||
bfile_meta->signature[2] != 0x5a
) {
return -2;
}
if (
bfile_meta->bitmask[0] != 0x88 &&
bfile_meta->bitmask[0] != 0x22 &&
bfile_meta->bitmask[0] != 0x11
) {
return -3;
}
if (bfile_meta->bitmask[1] != 0x0f)
return -4;
if (
bfile_meta->bitmask[0] != 0x11 &&
bfile_meta->lcluster_id != 0xffff
) {
return -5;
}
// on the fxcp400, have only some flash sector with the constants used
// in other device. Internally, on the fxcp400, Casio only perform
// a check an CRC that is performed on the whole cluster
// TODO: CRC check
#if 0
if (
memcmp(&(bfile_meta->constant), _constant1, 0x18) != 0 &&
memcmp(&(bfile_meta->constant), _constant2, 0x18) != 0
) {
return -6;
}
#endif
fcluster->lcluster_id = bfile_meta->lcluster_id;
fcluster->kind = bfile_meta->bitmask[0];
fcluster->version = bfile_meta->fcluster_version;
return 0;
}
/* _fygue_flash_cluster_meta(): special metadata cluster handling
*
* notes:
* metadata cluster are the last flash cluster of a flash sector it does
* not have Bfile information. Generate a fake flash cluster with a
* special custom kind to allow quick identification */
static int _fygue_flash_cluster_meta(
struct fygue_flash *flash,
struct fygue_flash_cluster *fcluster,
int fsector_id
) {
GAUTOTYPE geometry = &(flash->geometry);
memset(fcluster, 0x00, sizeof(struct fygue_flash_cluster));
fcluster->kind = FYGUE_FCLUSTER_KIND_METADATA;
fcluster->lcluster_id = 0xffff;
fcluster->fcluster_id = 0xffff;
fcluster->fcluster_id_bfile = 0xffff;
fcluster->version = 0xffffffff;
fcluster->meta_addr = 0xffffffff;
fcluster->data_addr = (
(geometry->phy_start | 0xa0000000) +
((fsector_id + 1) * geometry->fsector_size) -
(8 * 512)
);
return 0;
}
/* _fygue_flash_cluster_details(): special flash sector details handling
*
* notes:
* flash sector details is a particular flash cluster that contains
* critical versioning information needed to rebuild Casio's flash
* mapping.
* However, this particular flash cluster are not represented in the
* same way between machines. For example, the G35EII that use flash
* sector of 64ko merge the details sector inside the metadata cluster to
* keep some space which is not the case with the FXCP400 or CG50 that
* use 128ko flash sector and which the first cluster should be a details
* one type.
*
* for 64k cluster the 0xf3c0 offset can be calculated as:
* 0x10000 - (8*512) = 0xf000 | sector size minus cluster size
* 15 * 0x40 = 0x3c0 | skip all data cluster meta info */
static int _fygue_flash_cluster_details(
struct fygue_flash *flash,
struct fygue_flash_cluster *fcluster,
int fsector_id
) {
GAUTOTYPE geometry = &(flash->geometry);
volatile uint32_t *data32;
uintptr_t meta;
uintptr_t data;
memset(fcluster, 0x00, sizeof(struct fygue_flash_cluster));
if (geometry->fsector_size == FYGUE_FSECTOR_SIZE_64K) {
data = (
(geometry->phy_start | 0xa0000000) +
(fsector_id * 0x10000) +
(0xf3c0)
);
meta = data + 512;
} else {
data = (geometry->phy_start | 0xa0000000) + (fsector_id * 0x20000);
meta = data + 0x1f000;
}
if (_fygue_flash_cluster_convert(flash, fcluster, meta) != 0)
return -1;
if (fcluster->version != 0xffffffff)
return -2;
data32 = (void*)data;
if (
((data32[0] ^ data32[1]) != 0xffffffff) ||
((data32[2] ^ data32[3]) != 0xffffffff) ||
((data32[4] ^ data32[5]) != 0xffffffff)
) {
return -1;
}
fcluster->data_addr = data;
fcluster->meta_addr = meta;
fcluster->version = data32[0];
return 0;
}
/* _fygue_flash_cluster_data(): handle generic data flash cluster
*
* notes
* flash cluster data (0x11 or 0x88) are handled in the same way between
* all device */
static int _fygue_flash_cluster_data(
struct fygue_flash *flash,
struct fygue_flash_cluster *fcluster,
int fcluster_id,
int fsector_id,
int fcluster_off
) {
GAUTOTYPE geometry = &(flash->geometry);
uintptr_t meta;
uintptr_t data;
if (geometry->fsector_size == FYGUE_FSECTOR_SIZE_64K) {
data = (
(geometry->phy_start | 0xa0000000) +
(fsector_id * 0x10000) +
(fcluster_off * (8 * 512))
);
meta = (
(geometry->phy_start | 0xa0000000) +
(fsector_id * 0x10000) +
(0xf000) +
(fcluster_off * 0x40)
);
} else {
data = (
(geometry->phy_start | 0xa0000000) +
(fsector_id * 0x20000) +
(fcluster_off * (8 * 512))
);
meta = (
(geometry->phy_start | 0xa0000000) +
(fsector_id * 0x20000) +
(0x1f000) +
(fcluster_off * 0x40)
);
}
if (_fygue_flash_cluster_convert(flash, fcluster, meta) != 0)
return -1;
fcluster->data_addr = data;
fcluster->meta_addr = meta;
fcluster->fcluster_id = fcluster_id;
fcluster->fcluster_id_bfile = (
(fsector_id * (geometry->fcluster_per_fsector - 1)) +
(fcluster_off)
);
return 0;
}
//---
// Public
//---
/* fygue_flash_cluster_get() - get flash cluster information
*
* return
* -1 if the provided `fline` is not valid
* -2 if the provided `fline` have invalid Bfile metadata
* 0 if the meta information has been generated */
int fygue_flash_cluster_get(
struct fygue_flash *flash,
struct fygue_flash_cluster *fcluster,
int fcluster_id
) {
GAUTOTYPE geometry = &(flash->geometry);
int fsector_id;
int fcluster_off;
int nb_entry;
if (flash == NULL || fcluster == NULL)
return -1;
if (fcluster_id >= flash->geometry.fcluster_count)
return -1;
if (geometry->fsector_size == FYGUE_FSECTOR_SIZE_64K) {
nb_entry = 16;
fsector_id = fcluster_id / 16;
fcluster_off = fcluster_id % 16;
} else {
nb_entry = 32;
fsector_id = fcluster_id / 32;
fcluster_off = fcluster_id % 32;
}
if (fcluster_off == 0)
return _fygue_flash_cluster_details(flash, fcluster, fsector_id);
if (fcluster_off == (nb_entry - 1))
return _fygue_flash_cluster_meta(flash, fcluster, fsector_id);
return _fygue_flash_cluster_data(
flash,
fcluster,
fcluster_id,
fsector_id,
fcluster_off
);
}
/* fygue_flash_cluster_geometry() - get geometry information
*
* notes
* generate flash cluster geometry information. Note that we handle
* sector as a full 32 entry table, which differ from logical cluster ID
* that ignore the last entry which contain meta information
*
* returns
* -1 if the provided address is not valid
* 0 success */
int fygue_flash_cluster_geometry(
struct fygue_flash *flash,
uintptr_t addr,
uint16_t *fcluster_id,
uint16_t *fsector_id,
uint16_t *fcluster_off
) {
int nb_cluster;
if ((addr & 0x0fffffff) < flash->geometry.phy_start)
return -1;
nb_cluster = flash->geometry.fcluster_per_fsector;
addr = (addr & 0x0fffffff) - flash->geometry.phy_start;
if (fcluster_id != NULL) {
*fcluster_id = ((addr & 0xfffe0000) >> 17) * nb_cluster;
*fcluster_id += ((addr & 0x0001ffff) >> 12);
}
if (fsector_id != NULL)
*fsector_id = ((addr & 0xfffe0000) >> 17) * nb_cluster;
if (fcluster_off != NULL)
*fcluster_off = ((addr & 0x0001ffff) >> 12);
return 0;
}