summaryrefslogtreecommitdiff
path: root/arch/cris/arch-v10/boot/rescue/head.S
blob: 8689ea972c4642fe4b4274be8736ca2ecb030b60 (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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/* $Id: head.S,v 1.6 2003/04/09 08:12:43 pkj Exp $
 * 
 * Rescue code, made to reside at the beginning of the
 * flash-memory. when it starts, it checks a partition
 * table at the first sector after the rescue sector.
 * the partition table was generated by the product builder
 * script and contains offsets, lengths, types and checksums
 * for each partition that this code should check.
 *
 * If any of the checksums fail, we assume the flash is so
 * corrupt that we cant use it to boot into the ftp flash
 * loader, and instead we initialize the serial port to
 * receive a flash-loader and new flash image. we dont include
 * any flash code here, but just accept a certain amount of
 * bytes from the serial port and jump into it. the downloaded
 * code is put in the cache.
 *
 * The partitiontable is designed so that it is transparent to
 * code execution - it has a relative branch opcode in the
 * beginning that jumps over it. each entry contains extra
 * data so we can add stuff later.
 *
 * Partition table format:
 *
 *     Code transparency:
 * 
 *     2 bytes    [opcode 'nop']
 *     2 bytes    [opcode 'di']
 *     4 bytes    [opcode 'ba <offset>', 8-bit or 16-bit version]
 *     2 bytes    [opcode 'nop', delay slot]
 *
 *     Table validation (at +10):	
 * 
 *     2 bytes    [magic/version word for partitiontable - 0xef, 0xbe]
 *     2 bytes    [length of all entries plus the end marker]
 *     4 bytes    [checksum for the partitiontable itself]
 *
 *     Entries, each with the following format, last has offset -1:	
 *    
 *        4 bytes    [offset in bytes, from start of flash]
 *        4 bytes    [length in bytes of partition]
 *        4 bytes    [checksum, simple longword sum]
 *        2 bytes    [partition type]
 *        2 bytes    [flags, only bit 0 used, ro/rw = 1/0]
 *        16 bytes   [reserved for future use]
 *
 *     End marker
 *
 *        4 bytes    [-1]
 * 
 *	 10 bytes    [0, padding]
 * 
 * Bit 0 in flags signifies RW or RO. The rescue code only bothers
 * to check the checksum for RO partitions, since the others will
 * change their data without updating the checksums. A 1 in bit 0
 * means RO, 0 means RW. That way, it is possible to set a partition
 * in RO mode initially, and later mark it as RW, since you can always
 * write 0's to the flash.
 *
 * During the wait for serial input, the status LED will flash so the
 * user knows something went wrong.
 * 
 * Copyright (C) 1999, 2000, 2001, 2002, 2003 Axis Communications AB
 */

#include <linux/config.h>
#define ASSEMBLER_MACROS_ONLY
#include <asm/arch/sv_addr_ag.h>

	;; The partitiontable is looked for at the first sector after the boot
	;; sector. Sector size is 65536 bytes in all flashes we use.
		
#define PTABLE_START CONFIG_ETRAX_PTABLE_SECTOR
#define PTABLE_MAGIC 0xbeef

	;; The normal Etrax100 on-chip boot ROM does serial boot at 0x380000f0.
	;; That is not where we put our downloaded serial boot-code. The length is
	;; enough for downloading code that loads the rest of itself (after
	;; having setup the DRAM etc). It is the same length as the on-chip
	;; ROM loads, so the same host loader can be used to load a rescued
	;; product as well as one booted through the Etrax serial boot code.
		
#define CODE_START 0x40000000
#define CODE_LENGTH 784

#ifdef CONFIG_ETRAX_RESCUE_SER0
#define SERXOFF R_SERIAL0_XOFF
#define SERBAUD R_SERIAL0_BAUD
#define SERRECC R_SERIAL0_REC_CTRL
#define SERRDAT R_SERIAL0_REC_DATA
#define SERSTAT R_SERIAL0_STATUS
#endif
#ifdef CONFIG_ETRAX_RESCUE_SER1
#define SERXOFF R_SERIAL1_XOFF
#define SERBAUD R_SERIAL1_BAUD
#define SERRECC R_SERIAL1_REC_CTRL
#define SERRDAT R_SERIAL1_REC_DATA
#define SERSTAT R_SERIAL1_STATUS
#endif
#ifdef CONFIG_ETRAX_RESCUE_SER2
#define SERXOFF R_SERIAL2_XOFF
#define SERBAUD R_SERIAL2_BAUD
#define SERRECC R_SERIAL2_REC_CTRL
#define SERRDAT R_SERIAL2_REC_DATA
#define SERSTAT R_SERIAL2_STATUS
#endif	
#ifdef CONFIG_ETRAX_RESCUE_SER3
#define SERXOFF R_SERIAL3_XOFF
#define SERBAUD R_SERIAL3_BAUD
#define SERRECC R_SERIAL3_REC_CTRL
#define SERRDAT R_SERIAL3_REC_DATA
#define SERSTAT R_SERIAL3_STATUS
#endif

#define NOP_DI 0xf025050f
#define RAM_INIT_MAGIC 0x56902387

	.text
	
	;; This is the entry point of the rescue code
	;; 0x80000000 if loaded in flash (as it should be)
	;; since etrax actually starts at address 2 when booting from flash, we
	;; put a nop (2 bytes) here first so we dont accidentally skip the di
	
	nop	
	di

	jump	in_cache	; enter cached area instead
in_cache:	

	;; first put a jump test to give a possibility of upgrading the rescue code
	;; without erasing/reflashing the sector. we put a longword of -1 here and if
	;; it is not -1, we jump using the value as jump target. since we can always
	;; change 1's to 0's without erasing the sector, it is possible to add new
	;; code after this and altering the jumptarget in an upgrade.

jtcd:	move.d	[jumptarget], $r0
	cmp.d	0xffffffff, $r0
	beq	no_newjump
	nop
	
	jump	[$r0]

jumptarget:	
	.dword	0xffffffff	; can be overwritten later to insert new code
	
no_newjump:
#ifdef CONFIG_ETRAX_ETHERNET		
	;; Start MII clock to make sure it is running when tranceiver is reset
	move.d 0x3, $r0    ; enable = on, phy = mii_clk
	move.d $r0, [R_NETWORK_GEN_CONFIG]
#endif
	
	;; We need to setup the bus registers before we start using the DRAM
#include "../../lib/dram_init.S"

	;; we now should go through the checksum-table and check the listed
	;; partitions for errors.
	
	move.d	PTABLE_START, $r3
	move.d	[$r3], $r0
	cmp.d	NOP_DI, $r0	; make sure the nop/di is there...
	bne	do_rescue
	nop
	
	;; skip the code transparency block (10 bytes).

	addq	10, $r3
	
	;; check for correct magic
	
	move.w	[$r3+], $r0
	cmp.w	PTABLE_MAGIC, $r0
	bne	do_rescue	; didn't recognize - trig rescue
	nop

	;; check for correct ptable checksum

	movu.w	[$r3+], $r2	; ptable length
	move.d	$r2, $r8	; save for later, length of total ptable
	addq	28, $r8		; account for the rest
	move.d	[$r3+], $r4	; ptable checksum
	move.d	$r3, $r1
	jsr	checksum	; r1 source, r2 length, returns in r0

	cmp.d	$r0, $r4
	bne	do_rescue	; didn't match - trig rescue
	nop
	
	;; ptable is ok. validate each entry.

	moveq	-1, $r7
	
ploop:	move.d	[$r3+], $r1	; partition offset (from ptable start)
	bne	notfirst	; check if it is the partition containing ptable
	nop			; yes..
	move.d	$r8, $r1	; for its checksum check, skip the ptable
	move.d	[$r3+], $r2	; partition length
	sub.d	$r8, $r2	; minus the ptable length
	ba	bosse
	nop
notfirst:	
	cmp.d	-1, $r1		; the end of the ptable ?
	beq	flash_ok	;   if so, the flash is validated
	move.d	[$r3+], $r2	; partition length
bosse:	move.d	[$r3+], $r5	; checksum
	move.d	[$r3+], $r4	; type and flags
	addq	16, $r3		; skip the reserved bytes
	btstq	16, $r4		; check ro flag
	bpl	ploop		;   rw partition, skip validation
	nop
	btstq	17, $r4		; check bootable flag
	bpl	1f
	nop
	move.d	$r1, $r7	; remember boot partition offset
1:	

	add.d	PTABLE_START, $r1
	
	jsr	checksum	; checksum the partition
	
	cmp.d	$r0, $r5
	beq	ploop		; checksums matched, go to next entry
	nop

	;; otherwise fall through to the rescue code.
	
do_rescue:
	;; setup port PA and PB default initial directions and data
	;; (so we can flash LEDs, and so that DTR and others are set)
	
	move.b	CONFIG_ETRAX_DEF_R_PORT_PA_DIR, $r0
	move.b	$r0, [R_PORT_PA_DIR]
	move.b	CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r0
	move.b	$r0, [R_PORT_PA_DATA]
	
	move.b	CONFIG_ETRAX_DEF_R_PORT_PB_DIR, $r0
	move.b	$r0, [R_PORT_PB_DIR]
	move.b	CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r0
	move.b	$r0, [R_PORT_PB_DATA]

	;; setup the serial port at 115200 baud
	
	moveq	0, $r0
	move.d	$r0, [SERXOFF] 

	move.b	0x99, $r0
	move.b	$r0, [SERBAUD]		; 115.2kbaud for both transmit and receive

	move.b	0x40, $r0		; rec enable
	move.b	$r0, [SERRECC] 

	moveq	0, $r1		; "timer" to clock out a LED red flash
	move.d	CODE_START, $r3	; destination counter
	movu.w	CODE_LENGTH, $r4; length
	
wait_ser:
	addq	1, $r1
#ifndef CONFIG_ETRAX_NO_LEDS
#ifdef CONFIG_ETRAX_PA_LEDS
	move.b	CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r2
#endif
#ifdef CONFIG_ETRAX_PB_LEDS
	move.b	CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r2
#endif
	move.d	(1 << CONFIG_ETRAX_LED1R) | (1 << CONFIG_ETRAX_LED2R), $r0
	btstq	16, $r1
	bpl	1f
	nop
	or.d	$r0, $r2	; set bit
	ba	2f
	nop
1:	not	$r0		; clear bit
	and.d	$r0, $r2
2:	
#ifdef CONFIG_ETRAX_PA_LEDS
	move.b	$r2, [R_PORT_PA_DATA]	
#endif	
#ifdef CONFIG_ETRAX_PB_LEDS
	move.b	$r2, [R_PORT_PB_DATA]	
#endif
#ifdef CONFIG_ETRAX_90000000_LEDS
	move.b	$r2, [0x90000000]
#endif
#endif
	
	;; check if we got something on the serial port
	
	move.b	[SERSTAT], $r0
	btstq	0, $r0		; data_avail
	bpl	wait_ser
	nop

	;; got something - copy the byte and loop

	move.b	[SERRDAT], $r0
	move.b	$r0, [$r3+]
	
	subq	1, $r4		; decrease length
	bne	wait_ser
	nop

	;; jump into downloaded code

	move.d	RAM_INIT_MAGIC, $r8	; Tell next product that DRAM is initialized
	jump	CODE_START

flash_ok:
	;; check r7, which contains either -1 or the partition to boot from

	cmp.d	-1, $r7
	bne	1f
	nop
	move.d	PTABLE_START, $r7; otherwise use the ptable start
1:
	move.d	RAM_INIT_MAGIC, $r8	; Tell next product that DRAM is initialized
	jump	$r7		; boot!


	;; Helper subroutines

	;; Will checksum by simple addition
	;; r1 - source
	;; r2 - length in bytes
	;; result will be in r0
checksum:
	moveq	0, $r0
1:	addu.b	[$r1+], $r0
	subq	1, $r2
	bne	1b
	nop
	ret
	nop