release
dev builds
extras
themes manual
wiki
device status forums
mailing lists
IRC bugs
patches
dev guide



Wiki > Main > SansaAMSFirmware (compare)

Difference: SansaAMSFirmware (r43 vs. r42)

The Rockbox Sansa AMS Firmware

Sansa AMS Porting Status

Firmware recovery procedures have been found on the e200, but no other AMS device yet, however some buttons have been enabled allowing edited firmware to be carefully loaded.

As raised on the forums, it could probably be possible to create a development firmware replacing the Firmware block (block 1) in a first pass, thus allowing development without completely understanding library and resource blocks. To be confirmed.

Overview

All AMS devices use the same SoC as their main processing unit: the AS3525.

The AMS firmware file is divided in a bunch of blocks. The M200v2 v4.1.08A firmware has been used for analysis, but it seems that the file format is (almost) the same for other models.

In the M200 firmware, there seem to be 32 blocks. The following table shows the firmware file blocks details for that specific firmware version:

Block IndexAddressDescription
0 0x000000 Firmware header
1 0x000400 Main firmware
2 to 18 0x400 + (Index * 0x1E000) Library blocks
19 0x21AC00 Empty library block (filled with 0xFF)
20 to 26 Unregular, read below Resource blocks
27 0x4D5400 Empty resource block (filled with DEADBEEF)
28 0x4D5600 Unknown data block
29 0x4D5800 Unknown data block 2 (only contains 0xDF0 at 0x4D5800)
30 0x4D5A00 Unicode certificate data
31 0x4D6800 Empty ending block (filled with DEADBEEF)

Blocks Description

This section describes the actual firmware file blocks.

Every block has fixed length, but the effective code or data it contains can be smaller than this. There are 2 different padding types used in the firmware file: 0xFF paddings and 0xDEADBEEF paddings. Every byte following the effective block length is padded as 0xFF up to a 0x200 size boundary. Starting from that boundary up to the complete block size, 0xDEADBEEF padding is used.

Firmware Header (Block 0)

The firmware header is present at the very beginning of the firmware file. Numbers are represented in little endian. There are only 2 structures in this header, the first one being at 0x00000000, and the second one at 0x00000200. The second one is a mirror on the first one with a single exception, the FWHeaderIndex field is 1 while it was 0 in the first copy. The FirmwareHeader structure is as follow:

Structure OffsetSizeNameValueDescription
0x00 4 FirmwareHeaderIndex 0 for the first copy, 1 for the second FirmwareHeader structure index
0x04 4 FirmwareChecksum Sum of all dwords present in block 1 effective section Firmware checksum
0x08 4 FirmwareBlockSizeMultiplier 0xE4, 0xE8, 0xEA, 0xEC, 0xED, or 0xF0 Number that, multiplied by 0x200, gives the size of the main firmware block
0x0C 4 FirmwareSize Effective size of the firmware block Effective size of the firmware block
0x10 4 Unknown1 3 Still unknown
0x14 1 Unknown2 Variable Still unknown
0x15 1 ModelId 0x22: Clip, 0x23: C200, 0x24: E200, 0x1e: Fuze, 0x25: M200 Sansa model identifier
0x16 2 Zero 0 Unknown, but seems to always be 0
0x18 4 FourthyHex 0x40 Unknown, but seems to always be 0x40
0x1C 4 One 1 Unknown, but seems to always be 1
0x3C 4 FiveThousandHex 0x5000 Unknown, but present in the E200 firmwares only. Seems to always be 0x5000

Every byte not included in the above structure is 0xFF.

Firmware (Block 1)

The firmware block is the block that is loaded at address 0x00000000 in ROM/FLASH/whatever non-volatile memory in the device. The actual code hasn't been thoroughly analyzed, but we can deduce that it contains all the code needed for the device operation, except for specific libraries contained in library blocks.

It seems that the Firmware block actually contains the usual ARM vector table right off at the block's beginning (0x400 offset from beginning of firmware file). In the M200 firmware, only the reset, IRQ and FIQ seems to be handled though. The reserved vector is simply a NOP, while all the (four) others are simply infinite loops, so if they ever happen, the device is frozen. The following table shows the vector table of the specific analyzed firmware file:

AddressFunctionHandler summary
0x400 Reset Main code at 0x4B8
0x404 Undefined instruction Infinite loop
0x408 Software Interrupt Infinite loop
0x40C Prefetch Abort Infinite loop
0x410 Data Abort Infinite loop
0x414 Reserved NOP, effectively calling the IRQ handler
0x418 Interrupt Request Interrupt handler at 0x8468
0x41C Fast Interrupt Request Interrupt handler at 0x8468

In short, this is a "standard" image file for a processor to execute.

Libraries (Block 2 to 18)

The following table describes the library blocks header (offsets are relative to each block base address):

OffsetValue(s)Description
0x00 LibraryName Offset to the library name
0x04 LibraryBaseAddress Base address of the library
0x08 LibraryEndAddress Seems to be the library end address, but seems to include extra space(?)
0x0C LibraryBlockSize Size of effective data in this block
0x10 Unknown1 Still unknown. Needed space for variables?
0x14 ExportCount Number of publicly exported functions
0x18 Exports An array of ExportCount addresses which points to library functions

The 17 library blocks from the M200 firmware are tagged with the following "library names":

Block IndexLibrary Name
2 usb_functio
3 mp3_decoder
4 otg_functio
5 wav_codec
6 acp_decoder
7 aud_decoder
8 drm10_nonpk
9 drm10_pkcpt
10 drm10__Init
11 wma_decoder
12 wma_decInit
13 wma_decPlay
14 drm10_InitT
15 drm10_host_
16 wmaPDDRMini
17 wmaPDDRMper
18 sd_reload__

Resources (Block 20 to 26)

These blocks are still mostly misunderstood, but they seems to contain various resources (localized strings, etc.). Resource blocks size is understood to be the next 0x200 bytes boundary after the block effective size. For example, a resource block with an effective size of 0x1234 would end 0x1400 bytes after the block beginning.

The resource blocks have the following header:

OffsetValue(s)Description
0x00 ResourceBlockSize Size of effective data in this block
0x04 ResourceId The more plausible signification is a resource ID number
0x08 "HEADER" The "HEADER" ascii string

Whole-file checksum

In addition to the checksums in the Firmware Header, the last four bytes of the firmware file contains a whole-file checksum. This checksum is not present in the M200 firmware.

This is simply calculated as a 32-bit sum of all the 32-bit words in the firmware file, excluding the last four bytes.

Firmware Comprehension (M200 V2 series, 4.1.8a specific revision)

Form what we understand, the firmware file is directly loaded into ROM/FLASH/whatever and is booted directly by the uC. There is, however, one little thing. The firmware file as download by the sansa updater contains a firmware header which seems to be discarded when loaded into the non-volatile memory. So in fact, every addresses in there are relocated by 0x400 bytes. It also seems that only the first block (up to 0x1CC00) is loaded into non-volatile memory at first, then other blocks are loaded by the firmware at will.

All addresses referred in here are "true" firmware addresses, that is with the 0x400 bytes header removed. The first thing you should do to analyze the firmware is to discard the first 0x400 bytes and everything after 0x1C6AC (which is after the effective firmware instructions and data).

GPIOs Usage

This is, in the firmware, all known GPIOs usage:

GPIOUsage
A0 Keypad pattern scanner input
A1 Keypad pattern scanner input
A2 Keypad pattern scanner input
A3 Keypad pattern scanner input
A4 Keypad pattern scanner output
A5 Keypad pattern scanner output
A6 Keypad pattern scanner output
A7 Keypad pattern scanner output
B0 DBOP (LCD display)
B1 DBOP (LCD display)
B2 DBOP (LCD display)
B3 DBOP (LCD display)
B4 Output in some LCD display routines
B6 Output in the routine at 0x00006B38
C0 DBOP (LCD display)
C1 DBOP (LCD display), but also used as an input in 0x00006B38 before being used in LCD display routines
C2 DBOP (LCD display)
C3 DBOP (LCD display)
C4 DBOP (LCD display)
C5 DBOP (LCD display)
C6 DBOP (LCD display)
C7 DBOP (LCD display)
D1 Output in some LCD display routines
D3 Output in some LCD display routines

Interrupt Handlers

Reset Handler

The reset handler is located at 0x000000B8 in the firmware. It is quite simple and does the following:

digraph g{ node [fontsize = 10, shape = rectangle, height = 0, width = 0 ];

N0 [ label = "Disable I & D caches, & MMU" ]; { rank = same; C1 [ shape = note, label = "Variables area" ]; N1 [ label = "Clear 0x1C6AC to 0x24700" ]; } { rank = same; C2 [ shape = note, label = "Stack area" ]; N2 [ label = "Clear 0x49C60 to 0x4A060" ]; } N3 [ label = "Select IRQ level & set SP to 0x49CE0" ]; N4 [ label = "Select SVC level & set SP to 0x4A060" ]; { rank = same; C5 [ shape = note, label = "0x0000019C" ] ; N5 [ label = "Call main()", url="#void_main_void_" ]; } N6 [ label = "Infinite loop" ];

N0 -> N1 -> N2 -> N3 -> N4 -> N5 -> N6 -> N6; C1 -> N1 [ arrowhead = none, style = dashed ]; C2 -> N2 [ arrowhead = none, style = dashed ]; C5 -> N5 [ arrowhead = none, style = dashed ] ; }

IRQ Handler

The IRQ handler, in the M200 firmware, is called for both the IRQ and FIQ interrupts. It is located at 0x00008068 and does the following:

digraph g{ node [fontsize = 10, shape = rectangle, height = 0, width = 0 ];

N0 [ label = "Push R0-R3, R12, LR & SPSR" ]; N1 [ label = "Switch to SVC level" ]; N2 [ label = "Push LR" ]; { rank = same; C3 [ shape = note, label = "Not analyzed" ]; N3 [ label = "Call 0x8204" ]; } { rank = same; C4 [shape = note, label = "0x0000613C" ]; N4 [ label = "Call svc_handler" ]; } { rank = same; C5 [ shape = note, label = "Not analyzed" ]; N5 [ label = "Call 0x8210" ]; } N6 [ label = "Pop LR" ]; N7 [ label = "Switch back to IRQ level" ]; N8 [ label = "Pop R0-R3, R12, LR & SPSR" ];

N0 -> N1 -> N2 -> N3 -> N4 -> N5 -> N6 -> N7 -> N8; C3 -> N3 [ arrowhead = none, style = dashed ]; C4 -> N4 [ arrowhead = none, style = dashed ]; C5 -> N5 [ arrowhead = none, style = dashed ]; }

Functions

void main(void)

The main function (which is located at 0x0000019C) is as follow:

digraph g{ node [fontsize = 10, shape = rectangle, height = 0, width = 0 ];

N0 [ label = "Push R4 and LR" ]; { rank = same; C1 [ shape = note, label = "0x00000130" ]; N1 [ label = "Call ResetPeripherals()" ]; } { rank = same; C2 [ shape = note, label = "Not analyzed yet" ]; N2 [ label = "Call 0x0000013C(1, 0)" ]; } { rank = same; C3 [ shape = note, label = "var: 0x0001F44C, fct: 0x00000148, param0: priority" ]; N3 [ label = "void *TaskCtrlBlkFilesystem = CreateTaskFilesystem(100)" ]; } { rank = same; C4 [ shape = note, label = "var: 0x0001F450, fct: 0x00000154, param0: priority" ]; N4 [ label = "void *TaskCtrlBlkDecoder = CreateTaskDecoder(40)" ]; } { rank = same; C5 [ shape = note, label = "var: 0x0001F454, fct: 0x00000160, param0: priority" ]; N5 [ label = "void *TaskCtrlBlkEncoder = CreateTaskEncoder(30)" ]; } { rank = same; C6 [ shape = note, label = "var: 0x0001F458, fct: 0x0000016C, param0: priority" ]; N6 [ label = "void *TaskCtrlBlkRecorder = CreateTaskRecorder(25)" ]; } { rank = same; C7 [ shape = note, label = "var: 0x0001F45C, fct: 0x00000178, param0: priority" ]; N7 [ label = "void *TaskCtrlBlkFCA = CreateTaskFCA(20)" ]; } { rank = same; C8 [ shape = note, label = "var: 0x0001F460, fct: 0x00000184, param0: priority" ]; N8 [ label = "void *TaskCtrlBlkControl = CreateTaskControl(15)" ]; } { rank = same; C9 [ shape = note, label = "var: 0x0001F464, fct: 0x00000190, param0: priority" ]; N9 [ label = "void *TaskCtrlBlkUI = CreateTaskUI(10)" ]; } N10 [ label = "Pop R4 and LR" ]; { rank = same; C11 [ shape = note, label = "Not analyzed yet" ]; N11 [ label = "Branch to 0x00000190" ]; }

N0 -> N1 -> N2 -> N3 -> N4 -> N5 -> N6 -> N7 -> N8 -> N9 -> N10 -> N11; C1 -> N1 [ arrowhead = none, style = dashed ]; C2 -> N2 [ arrowhead = none, style = dashed ]; C3 -> N3 [ arrowhead = none, style = dashed ]; C4 -> N4 [ arrowhead = none, style = dashed ]; C5 -> N5 [ arrowhead = none, style = dashed ]; C6 -> N6 [ arrowhead = none, style = dashed ]; C7 -> N7 [ arrowhead = none, style = dashed ]; C8 -> N8 [ arrowhead = none, style = dashed ]; C9 -> N9 [ arrowhead = none, style = dashed ]; C11 -> N11 [ arrowhead = none, style = dashed ]; }

int strncmp(char *s1, char *s2, int n)

There is function at 0x0011668 that compares two strings up to a specific number of characters. Its signature looks like it is an implementation of the well-known strncmp function. It is frequently used to compare a string with predefined "library names" strings, and as expected, n in those cases is 11 (or 0xB) as is the length of those "library names". An usage example can be seen at 0x000078E4.

File type recognition

There is a very interesting function at 0x00010A08 which seems to recognize the audio file type and load the appropriate decoder for it.

[graph to be done]

Library block loading

At 0x0000782C, there is a function (that is called from the file type recognition function) that seems to dynamically load a library block in memory at a specified address, with specified length, etc. At 0x00007852, there is a comparison with 18 (or 0x12) which is also one more than the number of library blocks in the firmware file. Coincidence? Probably not...

At 0x00007882, there is a function call to 0x00007440, which seems to be a sub-function of the loading function. It is interesting to note that in this sub-function, there is a call to 0x00007364 which directly exposes a parameter value of 0x1E000, which is the library blocks size. In this same sub-function (0x00007440), there is calls at 0x00007498 and 0x000074C4 to a function at 0x00011878 which is evidently a block copy routine with the following signature: void block_copy(void *dest, void *src, int size).

For the records, there also seems to be some sort of critical region or something like that in this function, which is protected with the use of a particular variable stored 0x1F840. This is to be verified though.

[graph to be done]

Firmware naming conventions

The name of the firmware file affects the functionality of the device, for example flashing the Sansa Clip with a firmware renamed from m300f.bin to m300t.bin enables extra functionality like more languages and a diagnosis mode.

More information is available here in the sansa AMS thread. See also this thread on the anythingbutipod forums.

References and Links

http://www.arm.com/documentation/ARMProcessor_Cores Documentation on ARM processor cores
http://daniel.haxx.se/sansa/ams.html Bagder's information page on the Sansa AMS models
http://forums.rockbox.org/index.php?topic=14064 Rockbox forum topic for Sansa v2 porting discussions

r45 - 29 Nov 2010 - 19:45:20 - BjornStenberg

Revision r43 - 23 Nov 2009 - 22:29 - PhilipBarton
Revision r42 - 23 Nov 2009 - 22:24 - DominikWenger
Copyright by the contributing authors.