# HG changeset patch # User Michael Spacefalcon # Date 1368865335 0 # Node ID d41c555d7f1d2c22e53a8966770ef5e353edefd9 # Parent a52e76c12e6b4e7d9a33ad9b6119a61744f8db39 beginning to explore MysteryFFS diff -r a52e76c12e6b -r d41c555d7f1d .hgignore --- a/.hgignore Thu Apr 25 06:56:17 2013 +0000 +++ b/.hgignore Sat May 18 08:22:15 2013 +0000 @@ -1,1 +1,2 @@ -mokosrec2bin +re:^mokosrec2bin$ +re:^mysteryffs/scan1$ diff -r a52e76c12e6b -r d41c555d7f1d mysteryffs/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mysteryffs/Makefile Sat May 18 08:22:15 2013 +0000 @@ -0,0 +1,13 @@ +CC= gcc +CFLAGS= -O2 +PROGS= scan1 + +all: ${PROGS} + +${PROGS}: + ${CC} ${CFLAGS} -o $@ $@.c + +scan1: scan1.c + +clean: + rm -f ${PROGS} *.o *errs *.out diff -r a52e76c12e6b -r d41c555d7f1d mysteryffs/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mysteryffs/README Sat May 18 08:22:15 2013 +0000 @@ -0,0 +1,35 @@ +MysteryFFS is my (Michael Spacefalcon's) arbitrarily-chosen nickname for the +flash file system found in at least two Calypso/Riviera-based GSM phone or +modem SW designs: Pirelli DP-L10 and Closedmoko GTA0x. + +The FFS implementation which I have nicknamed MysteryFFS is identifiable by the +following magic at the beginning of every flash erase unit used by this FFS: + +00000000: 46 66 73 23 10 02 FF FF AB FF FF FF FF FF FF FF Ffs#............ + +The byte at offset 8 into the erase unit (AB in the illustration above) +has been observed to be one of 3 possibilities: + +AB: this erase unit appears to be the active index block +BD: regular data block +BF: last block of the flash "partition" used for the FFS + +On the Pirelli this MysteryFFS takes up the first 4.5 MiB (18 erase units +of 256 KiB each) of the 2nd flash chip select (nCS3). On the Closedmoko +it lives in the second half of the last megabyte of the 4 MiB flash chip, +taking up 7 erase units of 64 KiB each, i.e., spanning absolute flash chip +offsets from 0x380000 through 0x3EFFFF. + +(The 4 MiB NOR flash chip used by Closedmoko has an independent R/W bank + division between the first 3 MiB and the last 1 MiB. The first 3 MiB are used + to hold the field-flashable closed firmware images distributed as *.m0 files; + the independent last megabyte holds the FFS, and thus the FW could be + implemented to do FFS writes while running from flash in the main bank. + Less than half of that last megabyte appears to be used for the FFS though; + the rest appears to be unused - blank flash observed.) + +I have nicknamed this flash file system MysteryFFS because I haven't been able +to identify it as any known FFS design. The FFS implemented in the liberated +TSM30 code appears to be different, hence that source is not of much help. +Therefore, I am reverse-engineering this MysteryFFS in order to extract the +file system content from my Pirelli phones and from my GTA02. diff -r a52e76c12e6b -r d41c555d7f1d mysteryffs/scan1.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mysteryffs/scan1.c Sat May 18 08:22:15 2013 +0000 @@ -0,0 +1,174 @@ +/* + * This program is the first MysteryFFS analysis tool written. + * Here I'm trying to understand the meaning of various fields + * in the index block records. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +u8 blank_line[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +u8 mysteryffs_hdr[6] = {'F', 'f', 's', '#', 0x10, 0x02}; + +struct index_entry { + u16 len; + u8 unknown1; + u8 type; + u16 nptr1; + u16 nptr2; + u32 dataptr; + u16 unknown2; + u16 version; +}; + +char *imgfile; +u32 eraseblk_size; +int total_blocks; +u32 total_img_size; +u8 *image, *indexblk; + +read_img_file() +{ + int fd; + struct stat st; + + fd = open(imgfile, O_RDONLY); + if (fd < 0) { + perror(imgfile); + exit(1); + } + fstat(fd, &st); + if (!S_ISREG(st.st_mode)) { + fprintf(stderr, "%s is not a regular file\n", imgfile); + exit(1); + } + if (st.st_size < total_img_size) { + fprintf(stderr, "%s has fewer than 0x%x bytes\n", imgfile, + total_img_size); + exit(1); + } + image = malloc(total_img_size); + if (!image) { + perror("malloc"); + exit(1); + } + read(fd, image, total_img_size); + close(fd); +} + +find_index_block() +{ + int i; + u8 *ptr; + + for (ptr = image, i = 0; i < total_blocks; i++, ptr += eraseblk_size) { + if (bcmp(ptr, mysteryffs_hdr, 6)) + continue; + if (ptr[8] != 0xAB) + continue; + indexblk = ptr; + return(0); + } + fprintf(stderr, "could not find a MysteryFFS index block in %s\n", + imgfile); + exit(1); +} + +is_namestr_ok(s) + char *s; +{ + int cnt; + + for (cnt = 0; *s; s++, cnt++) { + if (cnt >= 32) + return(0); + if (!isprint(*s)) + return(0); + } + if (cnt) + return(1); + else + return(0); +} + +char * +dataptr_to_name(dptr) + u32 dptr; +{ + u8 *data; + + if (dptr > 0x0FFFFFFF) { +inv: return(""); + } + dptr <<= 4; + if (dptr >= total_img_size) + goto inv; + data = image + dptr; + if (is_namestr_ok(data)) + return(data); + else + return(""); +} + +dump_entry(rawptr, entrynum) + u8 *rawptr; +{ + struct index_entry ent; + + bcopy(rawptr, &ent, 0x10); + ent.len = le16toh(ent.len); + ent.nptr1 = le16toh(ent.nptr1); + ent.nptr2 = le16toh(ent.nptr2); + ent.dataptr = le32toh(ent.dataptr); + ent.unknown2 = le16toh(ent.unknown2); + ent.version = le16toh(ent.version); + printf("%x %s: len=%x %02X %02X nptr1=%x nptr2=%x %04X %04X\n", + entrynum, dataptr_to_name(ent.dataptr), + ent.len, ent.unknown1, ent.type, + ent.nptr1, ent.nptr2, + ent.unknown2, ent.version); +} + +dump_index() +{ + u32 offset; + u8 *entry; + + for (offset = 0x10; offset < eraseblk_size; offset += 0x10) { + entry = indexblk + offset; + if (!bcmp(entry, blank_line, 16)) + continue; + dump_entry(entry, offset >> 4); + } +} + +main(argc, argv) + char **argv; +{ + if (argc != 4) { + fprintf(stderr, "usage: %s imgfile blksize nblocks\n", argv[0]); + exit(1); + } + imgfile = argv[1]; + eraseblk_size = strtoul(argv[2], 0, 0); + total_blocks = strtoul(argv[3], 0, 0); + total_img_size = eraseblk_size * total_blocks; + read_img_file(); + find_index_block(); + dump_index(); + exit(0); +}