rockbox/utils/hwstub/stub/jz4760b/crt0.S
Amaury Pouly cc2389b7a6 hwstub: add jz4760b stub
The stub is quite versatile: it can be loaded using bootrom or another other
means (like factory boot on Fiio X1). It relocates itself to TCSM0 and provides
basic functionality (it does not recover from failed read/writes at the moment).

Change-Id: Ib646a4b43fba9358d6f93f0f73a5c2e9bcd775a7
2017-01-24 15:22:27 +01:00

98 lines
3.2 KiB
ArmAsm

#include "mips.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 the 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
/* jump to C code */
la t0, main
jr t0
move a0, k0