summaryrefslogtreecommitdiff
path: root/posts/2014-09-18-wavedrum-firmware-disassembler.skr
blob: 0a679cbf74f69374a41dec20a79ee09ce2c22667 (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
(post
 :title "Disassembling the Wavedrum firmware"
 :date (string->date* "2014-09-18 00:00")
 :tags '("DIY" "electronics" "music" "hacking" "wavedrum" "SHARC")

 (p [The Wavedrum’s processing unit is an ADSP-21375 by Analog Devices,
     a member of the SHARC processor family.  The firmware of the
     Wavedrum, which is stored on a micro SD-card and, more
     importantly, on the flash memory chip is loaded into the processor
     during the boot process.])
 
 (p [,(ref "2013-12-09-wavedrum-connectors.html" "Last time") I
     confirmed by tracing the connections of the two boot pins that the
     processor is wired to boot from EPROM/Flash (,(code [BOOT_CFG1-0 =
     10])).  I’m too lazy to try to verify that the firmware on the
     micro SD-card is identical to the contents of the flash memory, so
     I’ll just assume that this is the case.])

 ;; TODO: link to hardware reference is dead
 (p [When the processor is initially powered on, the internal memory is
     in an undefined state.  According to the ,(ref
     "http://www.analog.com/static/imported-files/processor_manuals/ADSP-21367_hwr_rev2-1.pdf"
     "ADSP hardware reference") (section 17-7 ,(em [Processor
     Booting])), the processor core is put into ,(code [IDLE]) mode
     while loading the boot kernel from an external source (in our case
     this is the flash memory) into the internal memory at address
     0x90000 (,(code [IVT_START_ADDR])), and finally executes the boot
     kernel code when a peripheral interrupt signals that the kernel
     has been loaded.  Then the application code is loaded from the
     memory and finally the application’s Interrupt Vector Table (IVT)
     is loaded.  The boot kernel is 256 48-bit words long, as is the
     IVT.  The application code is of arbitrary length.])
 
 
 (h2 [Word packing])
 
 (p [The boot kernel is exactly 256 48-bit words long (shorter kernels
     are padded with NOPs), but the words are not transferred as 48-bit
     words.  The boot kernel is transferred from the external memory as
     a stream of 384 “packed” 32-bit words (see table 17-6 in the
     hardware reference).  To understand packing we have to know that
     conceptionally the internal memory consists of many rows of four
     columns, each of which is 16 bit wide.  Accessing 48-bit words is
     often referred to as 3-column access in the reference, whereas
     accessing 32-bit words is called 2-column access.  All of the
     SHARC instructions are 48 bit wide.  This means that any 32-bit
     wide package never contains the full instruction.  Dependent on
     the order of the packages, the 48-bit words can be reconstructed
     from a stream of 32-bit words.])
 
 (p [Here’s the original explanation in the hardware reference on page
     17-27:])
 
 (blockquote (p [During the boot process, word packing (for example 8
                 to 32-bit) is performed over the SPI. In other words,
                 the kernel is not loaded directly with 256 x 48-bit
                 words, instead it is loaded with 384 x 32-bit ‘packed
                 words’ (2-column access). The same physical memory for
                 instruction boot is loaded via DMA in normal word (NW)
                 2 column. However, after booting the same physical
                 memory region is fetched by the sequencer in NW
                 3-column. For example the loader kernel itself has a
                 NW 2 columns count of 256 x 3/2 = 384 words but the
                 kernel is executed with 256 instruction fetches.]))
 
 (p [When booting from external memory the bus width is set to 8 bits.
     According to the ,(ref
     "http://www.analog.com/static/imported-files/processor_manuals/ADSP-21367_hwr_rev2-1.pdf"
     "ADSP hardware reference") (section “External Port Booting”), the
     processor reads a stream of 8-bit words from the data port and
     packs four of them into 32-bit words (LSB first) before storing
     them into the internal memory.])
 
 (p [Figure 17-5 in the hardware reference visualises this process
     rather nicely.  It shows how streams of different word sizes end
     up being stored in the internal memory.])
 
 (wide-img "2014/wavedrum-word-packing.png" "word packing")
 
 (p [Once I understood that words are transferred from the memory in
     8-bit chuncks with the LSB first, writing a disassembler that
     would translate the binary boot kernel into assembly code was no
     longer very challenging.])
 
 
 (h2 [A free disassembler for ADSP-213xx])
 
 (p [I implemented a simple tool in Haskell that takes a binary
     firmware and spits out assembly code for the ADSP.  At the moment
     it only supports a limited set of instructions, just enough so
     that I could translate the boot kernel.  The remainder of the
     instructions will be implemented later as I happen to encounter
     them in the application code.])
 
 (p [It currently only parses the first 256 words of the firmware,
     i.e. only the boot kernel.  Before I can parse the remainder I
     need to figure out the memory layout.])
 
 (p [The code is freely available under the ,(ref
     "https://www.gnu.org/licenses/#GPL" "GPL") and can be ,(ref
     "https://git.elephly.net/wavedrum/sharc-disassembler"
     "downloaded here").  See the included instructions for assistance
     in compiling and using the disassembler.])
 
 
 (h2 [Next steps])
 
 (p [My work isn’t over yet.  Next I’ll focus on the following tasks:])
 
 (ul
  (li [figure out how the boot kernel loads the application code into
       the processor])
  (li [disassemble (parts of) the application code])
  (li [look out for snippets that involve loading code from the
       micro-SD card to the flash memory, because that’s our ticket to
       upgrade the firmware easily]))
 
 (p [If you want to help me, I’d be happy if you could send me a copy
     of the Global Edition’s firmware.  To do this you only need to
     take out the micro SD-card and create a disk image of it.  (On
     GNU/Linux this can easily be done with ,(code [dd]) on the command
     line; see ,(ref
     "/posts/2013-08-11-hacking-the-wavedrum.html"
     "my first post on hacking the Wavedrum") for more detailed
     instructions.)])
 
 (p [I would also be very grateful for code reviews and patches.
     Translating the various specifications into executable code wasn’t
     always easy and I’m sure there are bugs in the code here and
     there.])
 
 (p [Read ,(ref "/tags/wavedrum.html" "more posts
     about the Wavedrum here").]))