changeset 21:d41c555d7f1d

beginning to explore MysteryFFS
author Michael Spacefalcon <msokolov@ivan.Harhan.ORG>
date Sat, 18 May 2013 08:22:15 +0000
parents a52e76c12e6b
children 00ad22936ca5
files .hgignore mysteryffs/Makefile mysteryffs/README mysteryffs/scan1.c
diffstat 4 files changed, 224 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- 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$
--- /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
--- /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.
--- /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 <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <endian.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+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("<invptr>");
+	}
+	dptr <<= 4;
+	if (dptr >= total_img_size)
+		goto inv;
+	data = image + dptr;
+	if (is_namestr_ok(data))
+		return(data);
+	else
+		return("<notname>");
+}
+
+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);
+}