rockbox/utils/hwstub/stub/jz4760b/crt0.S
Amaury Pouly 9bb6050d40 hwstub: rewrite exception catching
Since we can catch exceptions like data aborts on read/write, it takes very
little to also catch exceptions in calls. When extending this with the catching
of illegal instructions, the call instruction now becomes much more robust and
also for address and instruction probing. Since we can catch several types of
exception, rename set_data_abort_jmp to set_exception_jmp. At the same time,
simplify the logic in read/write request handlers. Also fix a bug in ARM
jump code: it was using
  stmia r1, {..., pc}
as if pc would get current pc + 8 but this is actually implementation defined
on older ARMs (typically pc + 12) and deprecated on newer ARMs, so rewrite the
code avoid that. The set_exception_jmp() function now also reports the exception
type.

Change-Id: Icd0dd52d2456b361b27c4776be09c3d13528ed93
2017-01-24 15:34:19 +01:00

167 lines
5.2 KiB
ArmAsm

#include "mips.h"
#include "system.h"
.extern main
.global start
.set mips32
.set noreorder
.set noat
.section .init.text,"ax",%progbits
/* WARNING
* We have no idea where the stubs starts running, there basically are three cases:
* - tcsm0: the stub is already at the right place, nothing do to
* - ram: sdram/ddram is active and we just need to move the stub
* - cache: the bootrom put us in cache-as-ram, we need to be careful
* Note that that those are initially quite different because:
* - tcsm0 is uncached
* - ram is almost always cached when we are running from it
* - cache-as-ram is cached but the cache is the only copy of our code and the
* icache was prefilled from dcache by the bootrom using some mips magic
*
* This means we have to be very careful with the cache because if we flush the
* icache in the cache-as-cache case, we cannot refill it, and worse, we cannot
* commit the dcache either because the ram might not even be initialised. Thus
* the only safe option in all cases is to copy the stub to an *uncached* location
* so that we don't have to commit the dcache and the icache can safely read from
* it.
*/
start:
bltzal zero, load_addr /* ra = PC + 8, branch not taken */
nop
load_addr:
addiu v0, ra, -8 /* calc real load address
account for branch delay slot */
move k0, v0 /* store starting location to give it to main */
la t0, relocstart /* relocate code if needed */
la t1, relocend
beq t0, v0, clear_bss /* no relocation needed */
nop
reloc_loop:
lw s0, 0(v0) /* v0 = src */
lw s1, 4(v0)
lw s2, 8(v0)
lw s3, 12(v0)
sw s0, 0(t0) /* t0 = dst */
sw s1, 4(t0)
sw s2, 8(t0)
sw s3, 12(t0)
/* Tricky part: as explained earlier, tcsm0 is uncached so no need to commit
* the dcache but we want to invalidate the icache ONLY AT THIS LOCATION.
* Indeed, if we invalidate the entire icache in the cache-as-ram case, we
* will miserably crash */
cache ICHitInv, 0(t0) /* invalidate virtual address in icache */
addiu t0, t0, 16 /* inc dst addr */
slt t2, t0, t1
bnez t2, reloc_loop
addiu v0, v0, 16 /* inc src addr */
/* jump to tcsm0 */
la t0, tcsm0_entry
jr t0
sync
tcsm0_entry:
/* now that we are running from tcsm0, which is uncached, we can finally
* properly invalidate all caches just to be sure */
mtc0 zero, C0_TagLo
mtc0 zero, C0_DataLo
la t0, 0x80000000 /* an idx op should use an unmappable address */
ori t1, t0, 0x4000 /* 16kB cache */
cache_inv_loop:
cache ICIndexStTag, 0(t0) /* index store icache tag */
cache DCIndexStTag, 0(t0) /* index store dcache tag */
bne t0, t1, cache_inv_loop
addiu t0, 0x20 /* 32 bytes per line */
clear_bss:
la t0, bssbegin
la t1, bssend
beq t0, t1, stack_setup
nop
clear_bss_loop:
sw zero, 0(t0)
bne t0, t1, clear_bss_loop
addiu t0, 4
stack_setup:
la sp, oc_stackend
/* the tcsm0 is usually accessed by its weird 0xf4000000 address but this
* address is not in the range available for EBASE (because EBASE[31:30]
* is hardwired to 0b10). Fortunately, the TCSM0 can be accessed by its
* physical address (0x132b0000) if we ungate the AHB1 */
la t0, 0xb0000028 /* CPM_CLKGATE1 */
lw t1, 0(t0)
li t2, 0xffffff7e /* AHB1 */
and t1, t2 /* clear AHB1 */
sw t1, 0(t0)
/* keep interrupts disabled, use normal exception vectors (to use EBASE) */
li t0, 0
mtc0 t0, C0_STATUS
/* set EBASE */
la t0, irqbase
mtc0 t0, C0_EBASE
/* jump to C code */
la t0, main
jr t0
move a0, k0
die_blink:
/* die blinking */
la a0, 0xb0010400
li a1, 2
sw a1, 0x48(a0) /* clear function (gpio or interrupt) */
sw a1, 0x58(a0) /* clear select (gpio) */
sw a1, 0x64(a0) /* set direction (out) */
sw a1, 0x34(a0) /* set pull (disable) */
/* turn backlight on and off */
la a0, 0xb0010414
li a1, 2
.blink_loop:
sw a1, (a0)
la v0, 10000000
.wait:
bnez v0, .wait
subu v0, 1
sw a1, 4(a0)
la v0, 10000000
.wait2:
bnez v0, .wait2
subu v0, 1
j .blink_loop
nop
/* restore_exception_jmp restores the context and returns from exception, it takes
* as argument the type of exception */
.extern restore_exception_jmp
.global tlb_refill_handler
.section .exception.tlb_refill,"ax",%progbits
tlb_refill_handler:
li a0, EXCEPTION_ADDR
la k0, restore_exception_jmp
jr k0
nop
.global cache_error_handler
.section .exception.cache_error,"ax",%progbits
cache_error_handler:
li a0, EXCEPTION_ADDR
la k0, restore_exception_jmp
jr k0
nop
.global general_exception_handler
.section .exception.general_exception,"ax",%progbits
general_exception_handler:
li a0, EXCEPTION_UNSP
la k0, restore_exception_jmp
jr k0
nop