=================
MT1939 Bootloader
=================

Notation
--------

All numbers are hexadecimal.

Memory accesses are denoted as Width[Address], where Width is B, H, or (blank)
for Byte, Halfword, or Word respectively.

C-style operators "|=", "&=", etc. are shorthand for read-modify-write
operations which require a load and store.


SCSI Commands
-------------

Handled by the bootloader:

  12 Inquiry
  1B Start / Stop Unit
  46 Get Configuration
  4A Get Event Status Notification
  5A Mode Sense (10)
  3C Read Buffer
  3B Write Buffer
  F1 Undocumented
  FF Undocumented

Command FF has its opcode followed by a 16-bit subcommand. Unrecognized
commands are ignored silently without an error response. This is the command
used by the official firmware updater app from TSST.

  FF 00 01 00 00 xx    Firmware update (x=0,ff,2)
  FF 00 04 00 00 0x    Set [4002058] bit0 to 'x' (bootloader "app select")
  FF 00 05             Return packet '00 0x' with bit0 of [4002058]
  FF 00 FF             Firmware version info, bootloader returns:
                            00 06 01 01 54 53 20 00

The F1 command seems to be another vendor specific undocumented thing. This
one has a big switch tree of subcommands, including one that seems to invoke
app firmware unconditionally?

Read Buffer (3C) and Write Buffer (3B) are part of the spec for firmware
updates, but TSSC clearly went their own route. Looks like these might be
fully custom, or they might be some vestiges of official spec support that
they decided to drop. Whatever the reason, these turn out to be super useful.

  3c 02 00 aa aa aa ll ll ll      Read 0xllllll bytes from address 0xaaaaaa.
                                  This is a new address space, possibly a bus
                                  address for the USB controller. The
                                  implementation of this command passes the
                                  address directly to the same DMA controller
                                  that usually receives the address of an
                                  allocated DRAM buffer.

                                  This address space has access to DRAM, but
                                  it's unclear whether there's a linear
                                  mapping. For now, I've noticed this one
                                  correspondence which seems to be the second
                                  (larger) DSP firmware image.

  [EXAMPLE]  Correspondence between I/O address space and firmware in DRAM

              %sc 1000 3c 2 0  0 10 0  0 10 0
              %rd 1ff2022 1000

  3c 06 00 aa aa aa ll ll ll      Read from ARM memory. Since the address is
                                  only 24 bits, this can't reach RAM or MMIO.
                                  It can read from flash, including the weird
                                  mapped regions like the one at 0x000.

  [EXAMPLE]  These backdoor shell commands return identical data:

              %sc 100 3c 6 0  2 ab cd  0 1 0
              %rd 2abcd 100

      (*) Bonus! It's possible to use this command to "see" through the data
          cache, to see what values the firmware has been using recently. Just
          after erasing the flash, you can use this command to dump memory.
          Anything still in the cache will show up against the background of ff

  3b 01 00            Much simpler

  3b 02 00            Seem to be synonyms for the same complicated command?
  3b 06 00              Involves a bootloader flash self-test


Memory Map
----------

00000000 - 00000fff      Encrypted page?
                         May be transparently decrypted on exec?
                         - ARM code issues function calls into this page
                         - Looks like part of a syscall interface

00001000 - 000013ff      ARM startup code
                         - Low level bootloader
                
00001400 - 00002fff      ARM application code
                         - Includes main loop, SCSI command handlers
                         - Might be usermode
                         - Probably compiled using a higher level SDK

00003000 - 0000dfff      ARM library code
                         - Bulk of code is here
                         - Invoked by startup code

0000e000 - 0000ffff      8051 firmware image
                         - Self contained, maps to 8051 code memory
                         - Unknown how this is loaded
                         - Implements USB Storage protocol
                         - Interprocessor communication pipe between ARM and 8051

00010000 - 001e1fff      Loadable firmware image (1863 kB)

001e2000 - 001fffff      Bootloader information region (120 kB)
                         - Not checksummed
                         - Lots of reserved blank space
                         - Interesting data table at 1f2000
                         - Might be keys at 1f2080
                         - Zero-padded 256-byte blob at 1fe000
                              Opcodes or other structured data,
                              some aligned 16-bit values too
                         - Signature block at 1ff000, matches 16 bytes at 10ff0
                         - Infonub at 1fffd0 (product/vendor/version, weak checksum)


Initialization
--------------

First known step:

- ARM processor starts, PC=0 T=0
  Mappings are already set up such that addresses 000:3ff map to 1000:13ff in flash.

- Boot code 1000

  - SoC main reset
    
    [4001000] |= 10
    [4001000] &= 10

  - Set up RAM segments
    First one is 6 kB, second one is 2 MB.

    [4020f20] = 02000000
    [4020f24] = 020019ff
    [4020f04] |= 0x800

    [4030f04] = 2

    [4031004] = 8
    [4031000] = 40000800

    Addresses calculated based on masking off 1ffff/2

    [40300f4] = 1ffff
    [4030f20] = 1e00000
    [4030f24] = 1ffffff

    [4030f40] = 2000000
    [4030f44] = 20019ff

    [4030f04] = 8802

  - Something else!

    [4011f04] |= 0xB00

  - Stack to 2000d80
  - CPSR to D3
  - Branch to next bootloader section, "MT1939 Boot Code" at 3010.

- Boot code 3010

   ...

  SVC 0x123456, R0=18 R1=20026
    R1 could plausibly be an entry-related address

  (Sadly this SVC leads into the encrypted area...)


- Hard to nail down exactly which checks go into running or not running the image,
  since it's a huge foresty state machine. Here's a running list of things that
  seem to matter:

   10ff0   128-bit key (big endian)
   10400   Table of 128-bit memory segment hashes
  1ff000   Signature matches 10ff0
  1ffff8   16-bit value aaaa
  1ffffe   16-bit checksum over range 10000:1e0000

- Finally, if the image is good, we install it by copying 0x1000 bytes from 0x10000
  to 400000, which seems like either an uncached mirror of RAM, or a special memory
  region where we'd keep the application IVT. This is the same length as the encrypted
  region, so it's possible there's a kind of specialized memory in use for that page.


App-select Register
-------------------

The word register at [4002058] seems to be heavily involved in the process of
deciding what path the bootloader takes; whether to rn the app, and a few
other things. So I called it "appselect".

Just prior to the main loop:

  [4002058] |= 40
  (Set bit 6)

In the main loop:

  If bit6 is 0, act as if the checksum test failed.

  If bit6 is 1, enable an increment by 1f000 and transfer of 1000 bytes on the
  way out (setting ref[10] to 200000).

Just prior to exit (after ref[10] >= 200000)

  [4002058] |= 80
  (Set bit 7)


Magic numbers
-------------

a1b9d358 13201959


Vector Table
------------

The area from 10000 - 102ff is set aside for interrupt vectors.

Differences between TS00 and TS01 seem to only be related to code addresses
changing; no validity checks in this section as far as I can tell.


Signature
---------

Bootloader reads two 16-byte regions:

 01ff000: d7a3 e98e 5a3a 2573 357d 324b 0ccb 1f53  ....Z:%s5}2K...S
 0010ff0: d7a3 e98e 5a3a 2573 357d 324b 0ccb 1f53  ....Z:%s5}2K...S

From near the beginning and end of the application firmware image.
All 16-byte signatures were identical in the images seen so far.

It looks like they're intended to be identical, but I haven't yet
determined exactly how they're checked. Seems like it involves the
8051 processor as well.

The one at 1ff000 is read just after the infonub checksum is validated.


 xxxxxxxx - 001e2000    blocksize 1000
  


Infonub
-------

There's a 48-byte region at the end of flash (001fffd0 - 001fffff) that I've
been calling the "infonub". It's a tiny nub of versioning and integrity data
that the bootloader checks before invoking the application firmware.

1fff90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
1fffa0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
1fffb0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
1fffc0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
1fffd0: 54 53 53 54 63 6f 72 70 42 44 44 56 44 57 20 53 TSSTcorpBDDVDW S
1fffe0: 45 2d 35 30 36 43 42 20 54 53 30 31 3e 22 04 00 E-506CB TS01>"..
1ffff0: 54 53 20 ff ff ff ff ff aa aa ff ff ff ff 59 18 TS ...........Y.

Most of this is clear vendor, product, and revision info.
A lot of it is boring.

 - This version info appears to be only for image identification. It isn't
   checksummed, and changing it doesn't result in any difference in INQURIY
   results from the device.

The last two bytes are a really dumb 16-bit checksum:

def get_stored_checksum(fw):
    return (ord(fw[0x1ffffe]) << 8) | ord(fw[0x1fffff])

def calculate_checksum(fw):
    s = 0
    for i in range(0x10000, 0x1e2000):
        s = 0xffff & (s + ord(fw[i]))
    return s

def file_info(name):
    fw = open(name, 'rb').read()
    print '%s stored:%04x calc:%04x' % (
        name, get_stored_checksum(fw), calculate_checksum(fw))

>>> file_info('SE-506CB_TS00.bin')
SE-506CB_TS00.bin stored:06e8 calc:06e8

>>> file_info('SE-506CB_TS01.bin')
SE-506CB_TS01.bin stored:5918 calc:5918

*** Verified that this is not the ONLY checksum in use. A trivially hacked
    firmware will not boot even with this checksum fixed.


Image Validity
--------------

How does the bootloader decide that an application firmware image (everything
from 10000 to 1fffff) is valid before executing it? If this validity check
fails, the bootloader will present its own tiny USB storage interface that
only supports firmware uploading.

- The bootloader region from 0 through 10000 is explicitly NOT verified. It
  seems to be completely ignored during upload. Possibly it was included in
  the flashing procedure to support initial installation of the loader in th
  factory using the same binary image?

- There's a 16-bit checksum at 1ffffe, covering memory from 10000 up to 1e2000.

- Experimentally found an area that isn't validated

  1e2000 - 1f2000  (64 kB).  This might be reserved for runtime storage,
                             hence the nice round size and lack of checksum.
                             It contains all FF in the shipping firmware images.

  1f2000 - 1fa07f  (0x8080)  The beginning contains an important-looking table
                             that I don't want to change until I know what I'm
                             doing, but the end of this region is zero-padded.
                             Changes here still allow the image to boot.

  1fa080 -                   Changes here also seem fine. This whole region that
                             isn't CRC-protected might not be validated, but lots
                             of it is scary data tables that will probably brick
                             the device if it does boot the modified image.

    1ff300 - 1ff307          Verified okay


So far I haven't had luck in updating the checksum at 1ffffe after changing
the image, BUT it seems possible to modify unused space (such as at 1083c) to
adjust the checksum of the image to match the stored one.

Area at 10400 is a good candidate for additional image checksumming. Possibly
this is checked by the bootloader; possibly it's checked by the firmware
itself after it starts up (and the cheesy 16-bit checksum might be the only
check used by the bootloader itself).

  ROM:00010400 dword_10400     DCD 1                   ; DATA XREF: ROM:0016838A
  ROM:00010400                                         ; sub_16841C+8
  ROM:00010404                 DCD 0x11000
  ROM:00010408                 DCD 0x16FFF
  ROM:0001040C                 DCD 0x3477DBC5
  ROM:00010410                 DCD 0xD5C4AB44
  ROM:00010414                 DCD 0x2A69E0AC
  ROM:00010418                 DCD 0x7ABD7E09
  ROM:0001041C                 DCD 1
  ROM:00010420                 DCD 0x17000
  ROM:00010424                 DCD 0x1817FF
  ROM:00010428                 DCD 0x114CF615
  ROM:0001042C                 DCD 0x217A4416
  ROM:00010430                 DCD 0x9EAC335F
  ROM:00010434                 DCD 0x3138D3CC
                                     FFFFFFFF ....

This seems to indicate two logically separate areas within the firmware image:
possibly a second-stage loader, then the higher level application. The
boundary at 17000 does correspond with a logical boundary in the allocation of
the firmware image, and 1817ff is the very end of the 8051 firmware image
(right before the two maybe-DSP-image blobs). Very likely that these are
checksums! The length would be right for MD5, but it doesn't seem that simple.

Looks like the checking happens in this first image. So 11000:16fff is a kind
of second stage loader that does these checks.

Should be able to test this theory by looking at regions that are covered by
the checksum16 but not by these validations. The checksum covers 10000-1e2000.
The tests above made changes in the area between 10000:11000 successfully with
this technique.

 - Understanding this check will also tell us how the firmware can exit back
   to the bootloader safely!

These values appear to be calculated using a 128-bit block cypher implemented
in hardware. Keys are loaded by the bootloader from the firmware header. TS00
and TS01 both use the same key:

  0010ff0: d7a3 e98e 5a3a 2573 357d 324b 0ccb 1f53  ....Z:%s5}2K...S

This is also loaded into address 0x0000 in the ref[20] 8051 mapping area. This
is plausibly just a lazy place to put the key during loading, so I'm not yet
sure if the 8051 firmware actually needs it, or if that MCU even has a
compatible crypto core.

... much more research follows, and it seems like the MCU has an 128-bit block
... cypher core, and there are several different parts of the boot process
... that use it. It also seems to have a way of transparently decrypting code
... from flash or remapping writable regions over the encrypted functions so
... that unencrypted versions can be substituted.

The most straightforward implementation of the signature verification seems to
be in the implementation of SCSI command 3B "Write Buffer" with undocumented
functionality that seems to include a different unused form of firmware
updater.

The crypto portions of this operation are contained nicely within a critical
section, so it's easy to see what needs to stay together.

Beginning the critical section:

  


~MeS`14
