FreeCalypso > hg > freecalypso-reveng
comparison mpffs/Description @ 28:c9f7a4afccc9
Mokopir-FFS: verbal description finished
| author | Michael Spacefalcon <msokolov@ivan.Harhan.ORG> |
|---|---|
| date | Sun, 30 Jun 2013 04:15:00 +0000 |
| parents | 343b6b2f178b |
| children | 86a494a5f2b0 |
comparison
equal
deleted
inserted
replaced
| 27:343b6b2f178b | 28:c9f7a4afccc9 |
|---|---|
| 58 and the exact binary byte content of all files contained therein. | 58 and the exact binary byte content of all files contained therein. |
| 59 | 59 |
| 60 However, the knowledge possessed by the present hacker (and conveyed in this | 60 However, the knowledge possessed by the present hacker (and conveyed in this |
| 61 document and the accompanying source code) is NOT sufficient for constructing a | 61 document and the accompanying source code) is NOT sufficient for constructing a |
| 62 valid Mokopir-FFS image "in vitro" given a tree of directories and files, or | 62 valid Mokopir-FFS image "in vitro" given a tree of directories and files, or |
| 63 for making modifications to the file or directory content on an existing image | 63 for making modifications to the file or directory content of an existing image |
| 64 and producing a content-modified image that is also valid; valid as in suitable | 64 and producing a content-modified image that is also valid; valid as in suitable |
| 65 for the original proprietary firmware to make its normal read and write | 65 for the original proprietary firmware to make its normal read and write |
| 66 operations without noticing anything amiss. | 66 operations without noticing anything amiss. |
| 67 | 67 |
| 68 Constructing "de novo" Mokopir-FFS images or modifying existing images in such | 68 Constructing "de novo" Mokopir-FFS images or modifying existing images in such |
| 69 a way that they remain 100% valid for all read and write operations of the | 69 a way that they remain 100% valid for all read and write operations of the |
| 70 original proprietary firmware would, at the very minimum, require an | 70 original proprietary firmware would, at the very minimum, require an |
| 71 understanding of the meaning of *all* fields on the on-media FFS format. Some | 71 understanding of the meaning of *all* fields of the on-media FFS format. Some |
| 72 of these fields are still left as "non-understood" for now though: a read-only | 72 of these fields are still left as "non-understood" for now though: a read-only |
| 73 implementation can get away with simply ignoring them, but a writer/generator | 73 implementation can get away with simply ignoring them, but a writer/generator |
| 74 would have to put *something* in those fields. | 74 would have to put *something* in those fields. |
| 75 | 75 |
| 76 As you read the "read-only" description of the Mokopir-FFS on-media format in | 76 As you read the "read-only" description of the Mokopir-FFS on-media format in |
| 228 name of the directory itself, padded with FFs to a 16-byte boundary. For | 228 name of the directory itself, padded with FFs to a 16-byte boundary. For |
| 229 example, an FFS directory named /gsm would be represented by an object | 229 example, an FFS directory named /gsm would be represented by an object |
| 230 consisting of two flash writes: a 16-byte entry in the active index block, with | 230 consisting of two flash writes: a 16-byte entry in the active index block, with |
| 231 the object type byte set to F2, and a corresponding 16-byte chunk in one of the | 231 the object type byte set to F2, and a corresponding 16-byte chunk in one of the |
| 232 data sectors, with the 16 bytes containing "gsm", a terminating NUL byte, and | 232 data sectors, with the 16 bytes containing "gsm", a terminating NUL byte, and |
| 233 12 FF bytes to pad up to 16. In the case of files, this name may be following | 233 12 FF bytes to pad up to 16. In the case of files, this name may be followed |
| 234 by the first chunk of file data content, as explained further down. | 234 by the first chunk of file data content, as explained further down. |
| 235 | 235 |
| 236 In order to parse the FFS directory tree (whether the objective is to dump the | 236 In order to parse the FFS directory tree (whether the objective is to dump the |
| 237 whole thing recursively or to find a specific file given a pathname), one needs | 237 whole thing recursively or to find a specific file given a pathname), one needs |
| 238 to first (well, after finding the active AB block) find the root directory node. | 238 to first (well, after finding the active AB block) find the root directory node. |
| 276 chunks to a new flash sector, invalidating the old copies - turning the latter | 276 chunks to a new flash sector, invalidating the old copies - turning the latter |
| 277 into deleted objects. The root node will be among them. Then at some point | 277 into deleted objects. The root node will be among them. Then at some point |
| 278 the active index block is going to fill up too, and will need to be rewritten | 278 the active index block is going to fill up too, and will need to be rewritten |
| 279 into a new sector - at which point the previously-deleted index entries are | 279 into a new sector - at which point the previously-deleted index entries are |
| 280 omitted and the root node becomes #1 again...] | 280 omitted and the root node becomes #1 again...] |
| 281 | |
| 282 Tree structure | |
| 283 | |
| 284 Once the root node has been found, the descendant and sibling pointers are used | |
| 285 to traverse the tree structure. For each directory object, including the root | |
| 286 node, the descendant pointer points to the first child object of this directory: | |
| 287 the first file or subdirectory contained therein. (Descendant and sibling | |
| 288 pointers take the form of index numbers in the active index block. A "nil" | |
| 289 pointer is indicated by all 1s (FFFF) - the usual all-0s NULL pointer convention | |
| 290 couldn't be used because it's flash, where the blank state is all 1s.) If the | |
| 291 descendant pointer of a directory object is nil, that means an empty directory. | |
| 292 The sibling pointer of each file or directory points to its next sibling, i.e., | |
| 293 the next member of the same parent directory. The sibling pointer of the root | |
| 294 node is nil. | |
| 295 | |
| 296 Data content of files | |
| 297 | |
| 298 Objects of type F1 are the head chunks of files. Each file has a head chunk, | |
| 299 and may or may not have continuation chunks. More precisely, the head chunk | |
| 300 may contain only the name (or viewed alternatively, 0 bytes of data), or it may | |
| 301 contain a nonzero number of payload bytes; orthogonally to this variability, | |
| 302 there may or may not be continuation chunk(s) present. | |
| 303 | |
| 304 Continuation chunks | |
| 305 | |
| 306 The descendant pointer of each file head object (the object of type F1, the one | |
| 307 reached by traversing the directory tree) indicates whether or not there are | |
| 308 any continuation chunks present. If this descendant pointer is nil, there are | |
| 309 no continuation chunks; otherwise it points to the first continuation chunk | |
| 310 object. File continuation objects have type F4, don't have any siblings (the | |
| 311 sibling pointer is nil), and the descendant pointer of each continuation object | |
| 312 points to the next continuation object, if there is one - nil otherwise. | |
| 313 | |
| 314 Payload data delineation | |
| 315 | |
| 316 Each chunk, whether head or continuation, always has a length that is a nonzero | |
| 317 multiple of 16 bytes. The length of the chunk here means the amount of flash | |
| 318 space it occupies in its data sector - which is NOT equal to the payload data | |
| 319 length. | |
| 320 | |
| 321 The head chunk of each file begins with the filename, terminated by a NUL byte. | |
| 322 If there are any payload data bytes present in this head chunk (I'll explain | |
| 323 momentarily how you would tell), the byte immediately after the NUL that | |
| 324 terminates the filename is the first byte of the payload. In the case of a | |
| 325 continuation chunk, there is no filename and the first byte of the chunk is the | |
| 326 first byte of that chunk's portion of the user data payload. | |
| 327 | |
| 328 Each data-containing chunk (head or continuation) has the following termination | |
| 329 after the last byte of that chunk's payload data: one byte of 00, followed by | |
| 330 however many bytes are needed ([0,15] range) of FFs to pad to a 16-byte | |
| 331 boundary. A file head chunk that has no payload data has the same format as a | |
| 332 directory name chunk: filename followed by its terminating NUL followed by | |
| 333 [0,15] bytes of FFs to pad to the next 16-byte boundary. | |
| 334 | |
| 335 When working with a head chunk, find the beginning of possible payload data (1 | |
| 336 byte after the filename terminating NUL) and find the end per the standard | |
| 337 termination logic: scanning from the end of the chunk, skip FFs until 00 is | |
| 338 found (encountering anything else is an error). If the head chunk has no data, | |
| 339 the effective data length (end_pointer - start_pointer) will be 0 or -1. (The | |
| 340 latter possibility is the most likely, as there will normally be a "shared" 00 | |
| 341 byte, serving as both the filename terminator and the 00 before the padding | |
| 342 FF bytes.) | |
| 343 | |
| 344 ------------------------------------------------------------------------------- | |
| 345 | |
| 346 That's all I can think of right now. If anything is unclear, see the | |
| 347 accompanying source code for the listing/extraction utilities: with the general | |
| 348 explanation given by this document, it should be clear what my code does and | |
| 349 why. And if a given piece of knowledge is found neither in this document nor | |
| 350 in my source code, then I don't know it myself either, and my read-only | |
| 351 Mokopir-FFS implementation makes do without it. | |
| 352 | |
| 353 All knowledge contained herein has been recovered by reverse engineering. | |
| 354 Believe it or not, I have figured it out by staring at the hex dump of FFS | |
| 355 sectors, reasoning about how one could possibly implement an FFS given the | |
| 356 requirement of dynamic writability and the physical constraints of flash memory, | |
| 357 and writing listing/extraction test code iteratively until I got something that | |
| 358 appears to correctly parse all FFS images available to me - the result is the | |
| 359 code in this package. | |
| 360 | |
| 361 I never got as far as attempting to locate the FFS implementation routines | |
| 362 within the proprietary firmware binary code images, and I most certainly don't | |
| 363 have anything from TI that would help in this case. (The TSM30 code doesn't | |
| 364 seem to be of any use as its FFS appears to be totally different, and I haven't | |
| 365 looked at the FFS code in the more recently found LoCosto code leak because I | |
| 366 assumed from the documentation in the latter that the FFS implemented there is | |
| 367 different as well.) | |
| 368 | |
| 369 Michael Spacefalcon | |
| 370 SE 52 Mes 11 |
