=============
8051 CPU core
=============

There appears to be an 8051 CPU core which acts as an interface controller.
In this case, it implements a USB Mass Storage device.

The 8051 firmware is only 8 kB, and it appears to be a single generic firmware
image that's shared by the bootloader and all versions of application firmware
observed. All application firmwares have an identical image that's signed at
the end with "~I_blieve:)Faith". The bootloader contains an image that is
signed with random-looking hex bytes but which is otherwise identical.

Both the 8051 and ARM implement "main loops" which seem to consist entirely of
processing messages received over inter-processor communication pipes. So, it
would not appear on first inspection that either processor is really in the
driver's seat, but ultimately any code or data which differentiates the
functionality of the bootloader versus the normal firmware must live on the
ARM side of the channel. So this tells us that the tiny 8051 firmware image is
probably intended to be very generic. From the perspective of a firmware
engineer on the optical drive, this is probably a "black box" binary blob that
makes the USB controller work.

It seems like we can use this processor for whatever we like; the firmware
doesn't seem to be stored in flash, it appears to be in a memory mapped region
initialized by the ARM.

Of all the weird modified 8051 cores people use, this one seems to be quite
tame. In fact, it seems to be fairly light on features. No additional SFRs
beyond the standard ones that I can see, only 128 bytes of internal RAM. All
the I/O occurs via XDATA, which doesn't seem to have any RAM attached to it.

The main I/O idiom is to use one XDATA address as a hardware FIFO to read or
write bytes. This is pretty fast, especially if the 8051 runs at a high clock
rate. (seems likely)


Memory Regions
--------------

- Program Memory
  8 kB region

  Serves as the "Internal ROM" in 8051 architecture
  Loaded/mapped by the ARM core.

  An example of this region (not necessarily execute-in-place) is at 0xE000 in
  the bootloader flash. These images are easily recognized by a signature at
  offset +0x60, "MoaiEasterIslandThomasYoyo(^o^)/". It's unknown whether this
  string is checked or if it's just a greeting. The "Thomas" part appears again
  later, in what seems like a very low-level debug message.

- Internal RAM
  128 bytes, from 00 to 7F.

- XDATA
  (Partially mapped to ARM memory at 41f0000)

  4100 - 41ff     Shared MMIO

                  [4180]  Write IPC? FIFO
                  [4181]  On init, write 1C D4 here
                  [4185]  IPC? FIFO busy flag

  4b00 - 4cff     Shared MMIO - USB

                  [4b01]
                  [4b04]
                  [4b06]  Flags
                  [4b08]  Flags
                  [4b0b]
                  [4b0e]

                  [4b20]  Read 8-byte SETUP packet

                  [4b24]  Write to 13-byte USB command status buffer

                  [4b32]
                  [4b34]
                  [4b35]

                  [4b50]

                  [4b63]

                  [4b70]
                  [4b71]
                  [4b72]  Write trigger for USB reply at [4b24]
                  [4b73]

                  [4bf7]  Error response?

                  [4b84]
                  [4b85]
                  [4b86]
                  [4b87]
                  [4b88]

                  [4b94]
                  [4b95]
                  [4b98]
                  [4b9c]
                  [4b9d]
                  [4b9e]
                  [4b9f]

                  [4bc0]
                  [4bc1]
                  [4bc2]
                  [4bc3]
                  [4bc4]
                  [4bc5]
                  [4bc6]
                  [4bc7]
                  [4bc8]
                  [4bc9]
                  [4bca]

                  [4bd2]
                  [4bd3]
                  [4bd9]
                  [4bde]

                  [4bf4]

  4d00 - 4dff     Shared MMIO

                  [4d01]
                  [4d03]
                  [4d04]
                  [4d0c]

                  [4d50]  Write to firmware memory
                  [4d51]  Firmware memory read/write flags
                  [4d52]  Read from firmware memory

                  [4d70]  SCSI CDB

                  [4d90]  Firmware-defined opcode byte
                  [4d91]  Firmware-defined status byte

                  [4db2]  Reset control?
                            30 -> delay -> 20 -> 30 -> longer delay -> 20

                  [4da5]
                  [4dad]
                  [4db1]

                  [4dcc]  Processor reset flags

                  [4dd0]
                  [4dd1]
                  [4dd4]

  8000 - 81ff     Local RAM


Inter-Processor Communication
-----------------------------

The 8051 and ARM processors communicate via a memory mapped FIFO and/or shared
memory regions.

 Firmware region 0010 - 009c
    This is copied to RAM (just above the stack, at 2000D80) early during boot
    Overlaps with the 8051 interrupt vector table.
    This seems to be related to the "Shared Block" described in bootloader.txt


Shared Block
------------

[BOOT] 2000d80 - 2000e0b
[TS01] 2000cec -

In [60]: rdw 2000c00
02000c00  00000200 0000000a 00000000 0a0a0000 00000002 00002100 00000000 00000000
02000c20  435aa000 c6d42700 00000000 00000000 00044000 00044000 0000413a 00004193
02000c40  008d008d 00000000 00920043 00000000 00000000 00000000 00000000 00000000
02000c60  00000000 00000000 ff200000 000000ff 00000000 00000000 00000000 00000000
02000c80  00000000 00000000 00000000 00000000 00000000 00000250 00110000 00000000
02000ca0  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000cc0  00000000 0000aa67 00400100 00000000 00000000 00000000 00000000 10000000
02000ce0  01000083 00000100 00000000 003e0000 01c08000 00347600 02000eec 00000000
02000d00  00000001 ff000001 00000009 0000001e 00000000 00000000 ffffff00 00000001
02000d20  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000d40  00000000 00000000 00000001 00000000 00000000 00000000 00000000 00000000
02000d60  00000000 00000000 ff0a2e7c 00030000 ffff0a00 00000000 ffff0000 00050e5f
02000d80  00050096 00001000 00000001 01f5b96d 01f58b5e 00600000 00000465 00000000
02000da0  ffffff00 00000000 00000601 ffffff00 00000000 ffffff00 00000000 00000000
02000dc0  0000ff00 ffff0000 00000000 00000000 ffffff00 0035ef80 00000000 00000000
02000de0  ffffff00 00000000 00000000 00000000 00000000 ffffff00 00000000 00000000
02000e00  00050096 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000e20  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000e40  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000e60  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000e80  00000000 00000000 028fff04 0bb813c0 00000527 00000000 00000000 00000000
02000ea0  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000ec0  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000ee0  00000011 00000070 ac0c0080 636f6cac 02000ea0 0000001c 00000070 00000000
02000f00  00000000 00000000 00000000 00000000 00000000 000c0000 00000000 00000000
02000f20  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000f40  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
02000f60  00000000 00000000 00000000 00000000 c0c45ee1 53202394 aeee98ba 487e0043
02000f80  8401c20c cd2f0bbf c7159453 d809252b d19e4090 88caab35 e817504c 76b67caa
02000fa0  936a69bf db04180c 76fbfeb3 7848fb80 a9b383a8 06a0930c 983173ee 3e3fe540
02000fc0  bb0c8a12 6e2a7c9c 4e3b433a 369fb227 9cc40895 aee1d615 1228bb9c e4d3461a
02000fe0  34950556 bd40dbec 51dac434 003cff48 b4006c69 81063908 0ef862f9 87f3a0d9

  [*] Most of these notes are still from static analysis prior to cmshell, and
      the numbers are really suspicious. The above dump is much more recent.

Seems to be shared by the 8051 and ARM processors.

Been abbreviating this as ref[index].

   b [ 0]
   b [ 1]
   b [ 2]   = 0
   b [ 3]  Oneshot bool flag, if set send SCSI 5 24 0 "Invalid field" error and clear flag.
             These fields must be to reduce code size. They're redundant with the other
             SCSI error reporting fields below, but for commonly used errors it's less code
             repetition to set this bit instead of a full SCSI error code.
   b [ 4]
   b [ 5]
   b [ 6]  On command completion, set to 1 by ARM SCSI code
   b [ 7]   = 0
   b [ 8]  Oneshot bool error flag, SCSI 0 29 6
   b [ 9]
  hf [ a]  Transfer length

word [ c]  Pointer to SCSI command data block (early bootloader main loop)

word [10]  Address (ARM)
            1e2000
            1ffff8
         >= 200000   Causes exit from bootloader into firmware

word [14]  Residual byte count
          <  10000   Different bootloader paths

word [18]  Address (8051)
             Stored (low 24 bits) in [4032010]

word [1c]  Total byte count

word [20]  ARM address of mapped 8051 memory
           Aligned on a 16MB boundary (low 24 bits are zero?)

             +0000   Might be RAM? Related to signature checking?
             +1000   Start of firmware RAM

           Calculated based on address from [4030f5c], but the specifics may
           depend on hardware version.

word [24]  Index of what I'll call the "working packet" inside the 8051 mapped area, abbreviated wp[]

word [28]  SCSI command response
word [2c]
word [30]
word [3c]
word [40]  Current ARM address inside from wp[]
word [68]

Another shared block, used much less. Been calling this zr[index] (zero
reference) since I saw a reference associated with a zero'ed (bss) segment in
the 8051 MCU setup. Whereas the regular shared block is referenced to 0xE010
in the 8051 image, zr is referenced to 0xE09C. It's right after the other
shared block:

2000e0c - 2000ee7

   b [31]  Checked early on in INQUIRY?
   b [3d]
   b [5c]  SCSI command data block, starting with opcode byte
   b [bc]  Internal error code byte, translated to scsi


Main Loop
---------

A typical main loop on this processor handles inter-processor calls coming in from the ARM.

One-time setup:
    [4B94] = 0
    [4D01] = 3
    [4D04] = 46 'F'
    [4B98] = 0
    [4d92] = FE
    [4b95] = 6
    [4b73] = 94
    [4d84] = 0

Each loop:
    Ready? [4d91] = 1
    Sync? Read FIFO [4d90], repeat until byte is nonzero
    Read opcode byte, opcode = [4d90]
    Opcode switch

Opcodes:

    02   Takes some parameters, some data block parsing?
    04   Send SCSI status, "USBS" packets
    05   USB block copy, optional "USBS" response
    06     Variant of 08
    07     Variant of 08
    08   Copy USB header from IO memory to RAM ...
    09     Variant of 08
    0b     Variant of 0d
    0a     Variant of 0d
    0c     Variant of 0d
    0d   Implements an entire transfer of some sort, with setup, loop, and status
    0e   Read 8 byte struct from IO, simple parsing loop
    0f   Very simple, 8-byte tag/length update?
    16     Variant of 04
    17     Variant of 05
    18   Wrapper for other opcodes: 04 09 07 22 1e
    1e   Complicated; involves timer, 32-bit length comparisons, two looping transfers
    1f     Variant of 08
    20     Variant of 08
    21   SCSI completion: some small USB transfers, optionally fall through to 04
    22     Variant of 08
    23   SCSI completion: another small USB thing, falls through to 04
    24     Variant of 23
    25     Variant of 23
    27   USB things, may fall through to 0e
    28     Variant of 27 with different size calculation
    29     Variant of 0d
    3b   Store 2 bytes: Could be system mode or version info. Same address was cleared by a weird table-driven startup loop.
    3c   Hardware reset?  [4db2]=30 delay [4db2]=20 [4db2]=30 delay [4db2]=20
    3f   Low-level protocol thing, part of main loop. Sends IPC 83 'A' 'N' [4d91]. Get write/read count?

IPCs from ARM often come packed as a 32-bit opcode and up to 12 bytes of params.
On the ARM bootloader, this comes in via ipc_message at 46F2.

(Possible that these aren't actually IPCs at all, but they're debug logging?)

Codes:

    49 'I'

    49a100aa  - Three startup messages, depending on HW type?
    49a200aa
    49a300aa
    49a400aa  - Another group of three
    49a500aa
    49a600aa
    49a700aa  - Also in init

    49ab00aa

    49ad00aa
    49ae04ea
    49af04ea
    49b000aa
    49b100aa
    49b200aa

    49b70302
    49b800aa

    49ca09f2
    49cb00aa
    49cc00aa
    49cd00aa
    49ce00aa
    49cf00aa

    49d100aa
    49d200aa
    49d300aa

    49d500aa

    49d708fa
    49d808fa
    49d908fa
    49df0302
    49e00302

    49e506da
    49e600aa

    49e800aa

    4a 'J'

    4a9800aa
    4a9e00aa
    4aa000aa

    4ac800aa
    4ac9012a
    4aca04ea
    4acb04ea
    4acc04ea
    4acd012a
    4ace012a
    4acf00aa
    4ad0012a
    4ad104ea
    4ad204ea
    4ad304ea
    4ad4012a
    4ad5012a
    4ad6012a

    02

    02110400  (byte)ref[c], (uint24be)ref[28]

R0=code R1=arg1 R2=arg2 R3=arg3

Code is a packed structure {
   uint8   typeof_args
   uint8   sizeof_args
   uint16  opcode
}

typeof_args:
   packed list, 2 bits each, MSB to LSB
   00=byte 01=half 10=skip 11=word
   examples:
     aa=void 2a=byte ea=word
     fa=word,word f2=word,word,byte

Uses zr[c] as command buffer:

   initiator byte (arg size + e3)
   opcode high  (49, 4a, 02)
   opcode low
   argument types byte
   arguments (variable length)
   complement of initiator byte (for synchronization?)

Opcode groupings

    Theory: The top byte encodes transfer type or direction info.
    "__" row lists which ops are implemented in 8051 firmware.


    02  11 ---

    49                                a1 a2 a3 a4 a5 a6 a7          ab    ad ae af b0 b1 b2             b7 b8                                                       ca cb cc cd ce cf    d1 d2 d3    d5    d7 d8 d9 df e0             e5 e6    e8
    4a     98                9e    a0                                                                                                                         c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6

    __     02    04 05 06 07 08 09 0a 0b 0c 0d 0e 0f                      16 17 18                1e 1f 20 21 22 23 24 25    27 28 29                                                    3b 3c 3f


Sample messages sent by the 8051, via FIFO at [4180].
(Not sure if these are IPC or not... could be SPI or some other on-chip bus.)

Procedure:
 Loop reading [4185] until bit0 is zero
 Write (four?) bytes to [4180]
 Write 1 to [4185]

Bytes seen:

 83 "232"   <-- During boot. Related to moai string?
 83 "Tho"
 83 "mas"
 83 "Spt"
 83 "1" 08 00
 83 "2" 07 ff
 83 "3" f8 00
 83 "4" 06 00
 83 "spl"

 83 46 47 [4d93]
 83 [4d79] [4d80] [4d81]
 40 91 ea (byte) [4d0c] [4d0c]
 83 [4d16] [4d15] [4d14]
 40 91 ea 9c [4b94] [4b98]
 40 91 ea cb 70 [4d94]


USB Descriptors
---------------

To focus on one case study where more of the unknowns are known...

USB descriptors were found in RAM on the ARM side of the fence. During init:

  IPC/log code 49b100aa

  USB init
    - two different versions based on "appselect" [4002058] register
    - includes descriptor copy and many other steps
    - descriptor copy function is the same in both cases

  Descriptor copy
    Uses the "working packet" wp[], at ref[24] (8051) via the ref[20] ARM mapping base.

    - Copy 8 bytes from boot USB descriptor table header to wp[18f0] on 8051 side:
      0 1 1 0 0 0 0 0
    - Copy device descriptor to wp[18f8]:
      12 1 0 2 0 0 0 40 8d e 56 19 0 0 1 2 3 1
    - Copy configuration descriptor, 0x20 bytes to wp[190a]
    - Copy string descriptors starting at wp[192a]
    - Copy device qualifier descriptor to wp[1a2a]
      A 6 0 2 0 0 0 40 1 0

  IPC/log code 49b200aa


~MeS`14
