Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame^] | 1 | /* $Id: head.S,v 1.6 2003/04/09 08:12:43 pkj Exp $ |
| 2 | * |
| 3 | * Rescue code, made to reside at the beginning of the |
| 4 | * flash-memory. when it starts, it checks a partition |
| 5 | * table at the first sector after the rescue sector. |
| 6 | * the partition table was generated by the product builder |
| 7 | * script and contains offsets, lengths, types and checksums |
| 8 | * for each partition that this code should check. |
| 9 | * |
| 10 | * If any of the checksums fail, we assume the flash is so |
| 11 | * corrupt that we cant use it to boot into the ftp flash |
| 12 | * loader, and instead we initialize the serial port to |
| 13 | * receive a flash-loader and new flash image. we dont include |
| 14 | * any flash code here, but just accept a certain amount of |
| 15 | * bytes from the serial port and jump into it. the downloaded |
| 16 | * code is put in the cache. |
| 17 | * |
| 18 | * The partitiontable is designed so that it is transparent to |
| 19 | * code execution - it has a relative branch opcode in the |
| 20 | * beginning that jumps over it. each entry contains extra |
| 21 | * data so we can add stuff later. |
| 22 | * |
| 23 | * Partition table format: |
| 24 | * |
| 25 | * Code transparency: |
| 26 | * |
| 27 | * 2 bytes [opcode 'nop'] |
| 28 | * 2 bytes [opcode 'di'] |
| 29 | * 4 bytes [opcode 'ba <offset>', 8-bit or 16-bit version] |
| 30 | * 2 bytes [opcode 'nop', delay slot] |
| 31 | * |
| 32 | * Table validation (at +10): |
| 33 | * |
| 34 | * 2 bytes [magic/version word for partitiontable - 0xef, 0xbe] |
| 35 | * 2 bytes [length of all entries plus the end marker] |
| 36 | * 4 bytes [checksum for the partitiontable itself] |
| 37 | * |
| 38 | * Entries, each with the following format, last has offset -1: |
| 39 | * |
| 40 | * 4 bytes [offset in bytes, from start of flash] |
| 41 | * 4 bytes [length in bytes of partition] |
| 42 | * 4 bytes [checksum, simple longword sum] |
| 43 | * 2 bytes [partition type] |
| 44 | * 2 bytes [flags, only bit 0 used, ro/rw = 1/0] |
| 45 | * 16 bytes [reserved for future use] |
| 46 | * |
| 47 | * End marker |
| 48 | * |
| 49 | * 4 bytes [-1] |
| 50 | * |
| 51 | * 10 bytes [0, padding] |
| 52 | * |
| 53 | * Bit 0 in flags signifies RW or RO. The rescue code only bothers |
| 54 | * to check the checksum for RO partitions, since the others will |
| 55 | * change their data without updating the checksums. A 1 in bit 0 |
| 56 | * means RO, 0 means RW. That way, it is possible to set a partition |
| 57 | * in RO mode initially, and later mark it as RW, since you can always |
| 58 | * write 0's to the flash. |
| 59 | * |
| 60 | * During the wait for serial input, the status LED will flash so the |
| 61 | * user knows something went wrong. |
| 62 | * |
| 63 | * Copyright (C) 1999, 2000, 2001, 2002, 2003 Axis Communications AB |
| 64 | */ |
| 65 | |
| 66 | #include <linux/config.h> |
| 67 | #define ASSEMBLER_MACROS_ONLY |
| 68 | #include <asm/arch/sv_addr_ag.h> |
| 69 | |
| 70 | ;; The partitiontable is looked for at the first sector after the boot |
| 71 | ;; sector. Sector size is 65536 bytes in all flashes we use. |
| 72 | |
| 73 | #define PTABLE_START CONFIG_ETRAX_PTABLE_SECTOR |
| 74 | #define PTABLE_MAGIC 0xbeef |
| 75 | |
| 76 | ;; The normal Etrax100 on-chip boot ROM does serial boot at 0x380000f0. |
| 77 | ;; That is not where we put our downloaded serial boot-code. The length is |
| 78 | ;; enough for downloading code that loads the rest of itself (after |
| 79 | ;; having setup the DRAM etc). It is the same length as the on-chip |
| 80 | ;; ROM loads, so the same host loader can be used to load a rescued |
| 81 | ;; product as well as one booted through the Etrax serial boot code. |
| 82 | |
| 83 | #define CODE_START 0x40000000 |
| 84 | #define CODE_LENGTH 784 |
| 85 | |
| 86 | #ifdef CONFIG_ETRAX_RESCUE_SER0 |
| 87 | #define SERXOFF R_SERIAL0_XOFF |
| 88 | #define SERBAUD R_SERIAL0_BAUD |
| 89 | #define SERRECC R_SERIAL0_REC_CTRL |
| 90 | #define SERRDAT R_SERIAL0_REC_DATA |
| 91 | #define SERSTAT R_SERIAL0_STATUS |
| 92 | #endif |
| 93 | #ifdef CONFIG_ETRAX_RESCUE_SER1 |
| 94 | #define SERXOFF R_SERIAL1_XOFF |
| 95 | #define SERBAUD R_SERIAL1_BAUD |
| 96 | #define SERRECC R_SERIAL1_REC_CTRL |
| 97 | #define SERRDAT R_SERIAL1_REC_DATA |
| 98 | #define SERSTAT R_SERIAL1_STATUS |
| 99 | #endif |
| 100 | #ifdef CONFIG_ETRAX_RESCUE_SER2 |
| 101 | #define SERXOFF R_SERIAL2_XOFF |
| 102 | #define SERBAUD R_SERIAL2_BAUD |
| 103 | #define SERRECC R_SERIAL2_REC_CTRL |
| 104 | #define SERRDAT R_SERIAL2_REC_DATA |
| 105 | #define SERSTAT R_SERIAL2_STATUS |
| 106 | #endif |
| 107 | #ifdef CONFIG_ETRAX_RESCUE_SER3 |
| 108 | #define SERXOFF R_SERIAL3_XOFF |
| 109 | #define SERBAUD R_SERIAL3_BAUD |
| 110 | #define SERRECC R_SERIAL3_REC_CTRL |
| 111 | #define SERRDAT R_SERIAL3_REC_DATA |
| 112 | #define SERSTAT R_SERIAL3_STATUS |
| 113 | #endif |
| 114 | |
| 115 | #define NOP_DI 0xf025050f |
| 116 | #define RAM_INIT_MAGIC 0x56902387 |
| 117 | |
| 118 | .text |
| 119 | |
| 120 | ;; This is the entry point of the rescue code |
| 121 | ;; 0x80000000 if loaded in flash (as it should be) |
| 122 | ;; since etrax actually starts at address 2 when booting from flash, we |
| 123 | ;; put a nop (2 bytes) here first so we dont accidentally skip the di |
| 124 | |
| 125 | nop |
| 126 | di |
| 127 | |
| 128 | jump in_cache ; enter cached area instead |
| 129 | in_cache: |
| 130 | |
| 131 | ;; first put a jump test to give a possibility of upgrading the rescue code |
| 132 | ;; without erasing/reflashing the sector. we put a longword of -1 here and if |
| 133 | ;; it is not -1, we jump using the value as jump target. since we can always |
| 134 | ;; change 1's to 0's without erasing the sector, it is possible to add new |
| 135 | ;; code after this and altering the jumptarget in an upgrade. |
| 136 | |
| 137 | jtcd: move.d [jumptarget], $r0 |
| 138 | cmp.d 0xffffffff, $r0 |
| 139 | beq no_newjump |
| 140 | nop |
| 141 | |
| 142 | jump [$r0] |
| 143 | |
| 144 | jumptarget: |
| 145 | .dword 0xffffffff ; can be overwritten later to insert new code |
| 146 | |
| 147 | no_newjump: |
| 148 | #ifdef CONFIG_ETRAX_ETHERNET |
| 149 | ;; Start MII clock to make sure it is running when tranceiver is reset |
| 150 | move.d 0x3, $r0 ; enable = on, phy = mii_clk |
| 151 | move.d $r0, [R_NETWORK_GEN_CONFIG] |
| 152 | #endif |
| 153 | |
| 154 | ;; We need to setup the bus registers before we start using the DRAM |
| 155 | #include "../../lib/dram_init.S" |
| 156 | |
| 157 | ;; we now should go through the checksum-table and check the listed |
| 158 | ;; partitions for errors. |
| 159 | |
| 160 | move.d PTABLE_START, $r3 |
| 161 | move.d [$r3], $r0 |
| 162 | cmp.d NOP_DI, $r0 ; make sure the nop/di is there... |
| 163 | bne do_rescue |
| 164 | nop |
| 165 | |
| 166 | ;; skip the code transparency block (10 bytes). |
| 167 | |
| 168 | addq 10, $r3 |
| 169 | |
| 170 | ;; check for correct magic |
| 171 | |
| 172 | move.w [$r3+], $r0 |
| 173 | cmp.w PTABLE_MAGIC, $r0 |
| 174 | bne do_rescue ; didn't recognize - trig rescue |
| 175 | nop |
| 176 | |
| 177 | ;; check for correct ptable checksum |
| 178 | |
| 179 | movu.w [$r3+], $r2 ; ptable length |
| 180 | move.d $r2, $r8 ; save for later, length of total ptable |
| 181 | addq 28, $r8 ; account for the rest |
| 182 | move.d [$r3+], $r4 ; ptable checksum |
| 183 | move.d $r3, $r1 |
| 184 | jsr checksum ; r1 source, r2 length, returns in r0 |
| 185 | |
| 186 | cmp.d $r0, $r4 |
| 187 | bne do_rescue ; didn't match - trig rescue |
| 188 | nop |
| 189 | |
| 190 | ;; ptable is ok. validate each entry. |
| 191 | |
| 192 | moveq -1, $r7 |
| 193 | |
| 194 | ploop: move.d [$r3+], $r1 ; partition offset (from ptable start) |
| 195 | bne notfirst ; check if it is the partition containing ptable |
| 196 | nop ; yes.. |
| 197 | move.d $r8, $r1 ; for its checksum check, skip the ptable |
| 198 | move.d [$r3+], $r2 ; partition length |
| 199 | sub.d $r8, $r2 ; minus the ptable length |
| 200 | ba bosse |
| 201 | nop |
| 202 | notfirst: |
| 203 | cmp.d -1, $r1 ; the end of the ptable ? |
| 204 | beq flash_ok ; if so, the flash is validated |
| 205 | move.d [$r3+], $r2 ; partition length |
| 206 | bosse: move.d [$r3+], $r5 ; checksum |
| 207 | move.d [$r3+], $r4 ; type and flags |
| 208 | addq 16, $r3 ; skip the reserved bytes |
| 209 | btstq 16, $r4 ; check ro flag |
| 210 | bpl ploop ; rw partition, skip validation |
| 211 | nop |
| 212 | btstq 17, $r4 ; check bootable flag |
| 213 | bpl 1f |
| 214 | nop |
| 215 | move.d $r1, $r7 ; remember boot partition offset |
| 216 | 1: |
| 217 | |
| 218 | add.d PTABLE_START, $r1 |
| 219 | |
| 220 | jsr checksum ; checksum the partition |
| 221 | |
| 222 | cmp.d $r0, $r5 |
| 223 | beq ploop ; checksums matched, go to next entry |
| 224 | nop |
| 225 | |
| 226 | ;; otherwise fall through to the rescue code. |
| 227 | |
| 228 | do_rescue: |
| 229 | ;; setup port PA and PB default initial directions and data |
| 230 | ;; (so we can flash LEDs, and so that DTR and others are set) |
| 231 | |
| 232 | move.b CONFIG_ETRAX_DEF_R_PORT_PA_DIR, $r0 |
| 233 | move.b $r0, [R_PORT_PA_DIR] |
| 234 | move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r0 |
| 235 | move.b $r0, [R_PORT_PA_DATA] |
| 236 | |
| 237 | move.b CONFIG_ETRAX_DEF_R_PORT_PB_DIR, $r0 |
| 238 | move.b $r0, [R_PORT_PB_DIR] |
| 239 | move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r0 |
| 240 | move.b $r0, [R_PORT_PB_DATA] |
| 241 | |
| 242 | ;; setup the serial port at 115200 baud |
| 243 | |
| 244 | moveq 0, $r0 |
| 245 | move.d $r0, [SERXOFF] |
| 246 | |
| 247 | move.b 0x99, $r0 |
| 248 | move.b $r0, [SERBAUD] ; 115.2kbaud for both transmit and receive |
| 249 | |
| 250 | move.b 0x40, $r0 ; rec enable |
| 251 | move.b $r0, [SERRECC] |
| 252 | |
| 253 | moveq 0, $r1 ; "timer" to clock out a LED red flash |
| 254 | move.d CODE_START, $r3 ; destination counter |
| 255 | movu.w CODE_LENGTH, $r4; length |
| 256 | |
| 257 | wait_ser: |
| 258 | addq 1, $r1 |
| 259 | #ifndef CONFIG_ETRAX_NO_LEDS |
| 260 | #ifdef CONFIG_ETRAX_PA_LEDS |
| 261 | move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r2 |
| 262 | #endif |
| 263 | #ifdef CONFIG_ETRAX_PB_LEDS |
| 264 | move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r2 |
| 265 | #endif |
| 266 | move.d (1 << CONFIG_ETRAX_LED1R) | (1 << CONFIG_ETRAX_LED2R), $r0 |
| 267 | btstq 16, $r1 |
| 268 | bpl 1f |
| 269 | nop |
| 270 | or.d $r0, $r2 ; set bit |
| 271 | ba 2f |
| 272 | nop |
| 273 | 1: not $r0 ; clear bit |
| 274 | and.d $r0, $r2 |
| 275 | 2: |
| 276 | #ifdef CONFIG_ETRAX_PA_LEDS |
| 277 | move.b $r2, [R_PORT_PA_DATA] |
| 278 | #endif |
| 279 | #ifdef CONFIG_ETRAX_PB_LEDS |
| 280 | move.b $r2, [R_PORT_PB_DATA] |
| 281 | #endif |
| 282 | #ifdef CONFIG_ETRAX_90000000_LEDS |
| 283 | move.b $r2, [0x90000000] |
| 284 | #endif |
| 285 | #endif |
| 286 | |
| 287 | ;; check if we got something on the serial port |
| 288 | |
| 289 | move.b [SERSTAT], $r0 |
| 290 | btstq 0, $r0 ; data_avail |
| 291 | bpl wait_ser |
| 292 | nop |
| 293 | |
| 294 | ;; got something - copy the byte and loop |
| 295 | |
| 296 | move.b [SERRDAT], $r0 |
| 297 | move.b $r0, [$r3+] |
| 298 | |
| 299 | subq 1, $r4 ; decrease length |
| 300 | bne wait_ser |
| 301 | nop |
| 302 | |
| 303 | ;; jump into downloaded code |
| 304 | |
| 305 | move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is initialized |
| 306 | jump CODE_START |
| 307 | |
| 308 | flash_ok: |
| 309 | ;; check r7, which contains either -1 or the partition to boot from |
| 310 | |
| 311 | cmp.d -1, $r7 |
| 312 | bne 1f |
| 313 | nop |
| 314 | move.d PTABLE_START, $r7; otherwise use the ptable start |
| 315 | 1: |
| 316 | move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is initialized |
| 317 | jump $r7 ; boot! |
| 318 | |
| 319 | |
| 320 | ;; Helper subroutines |
| 321 | |
| 322 | ;; Will checksum by simple addition |
| 323 | ;; r1 - source |
| 324 | ;; r2 - length in bytes |
| 325 | ;; result will be in r0 |
| 326 | checksum: |
| 327 | moveq 0, $r0 |
| 328 | 1: addu.b [$r1+], $r0 |
| 329 | subq 1, $r2 |
| 330 | bne 1b |
| 331 | nop |
| 332 | ret |
| 333 | nop |