1
0
Fork 0
mirror of https://github.com/CTCaer/hekate.git synced 2024-11-09 12:01:45 +00:00

Add simple exception handling

This adds support for exception handling.
It should provide simple and fast reporting of crucial info and full restoration without powering off.
This commit is contained in:
CTCaer 2020-04-27 09:56:19 +03:00
parent 9a5cfdff4c
commit 0462f3b252
8 changed files with 574 additions and 9 deletions

View file

@ -0,0 +1,233 @@
/*
* Copyright (c) 2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Armv7tdmi Status register.
*
* bit0: Mode 0.
* bit1: Mode 1.
* bit2: Mode 2.
* bit3: Mode 3.
* bit4: Mode 4.
* bit5: Thumb state.
* bit6: FIQ disable.
* bit7: IRQ disable.
* bit8-27: Reserved.
* bit28: Overflow condition.
* bit29: Carry/Borrow/Extend condition.
* bit30: Zero condition.
* bit31: Negative/Less than condition.
*
* M[4:0] | Mode | Visible Thumb-state registers | Visible ARM-state registers
* 10000 | USER | r0r7, SP, LR, PC, CPSR | r0r14, PC, CPSR
* 10001 | FIQ | r0r7, SP_fiq, LR_fiq, PC, CPSR, SPSR_fiq | r0r7, r8_fiqr14_fiq, PC, CPSR, SPSR_fiq
* 10010 | IRQ | r0r7, SP_irq, LR_irq, PC, CPSR, SPSR_irq | r0r12, r13_irq, r14_irq, PC, CPSR, SPSR_irq
* 10011 | SVC | r0r7, SP_svc, LR_svc, PC, CPSR, SPSR_svc | r0r12, r13_svc, r14_svc, PC, CPSR, SPSR_svc
* 10111 | ABRT | r0r7, SP_abt, LR_abt, PC, CPSR, SPSR_abt | r0r12, r13_abt, r14_abt, PC, CPSR, SPSR_abt
* 11011 | UNDF | r0r7, SP_und, LR_und, PC, CPSR, SPSR_und | r0r12, r13_und, r14_und, PC, CPSR, SPSR_und
* 11111 | SYS | r0r7, SP, LR, PC, CPSR | r0r14, PC, CPSR
*/
#define EXCP_EN_ADDR 0x4003FFFC
#define EXCP_TYPE_ADDR 0x4003FFF8
#define EXCP_LR_ADDR 0x4003FFF4
#define EXCP_VEC_BASE 0x6000F000
#define EVP_COP_RESET_VECTOR 0x200
#define EVP_COP_UNDEF_VECTOR 0x204
#define EVP_COP_SWI_VECTOR 0x208
#define EVP_COP_PREFETCH_ABORT_VECTOR 0x20C
#define EVP_COP_DATA_ABORT_VECTOR 0x210
#define EVP_COP_RSVD_VECTOR 0x214
#define EVP_COP_IRQ_VECTOR 0x218
#define EVP_COP_FIQ_VECTOR 0x21C
#define MODE_USR 0x10
#define MODE_FIQ 0x11
#define MODE_IRQ 0x12
#define MODE_SVC 0x13
#define MODE_ABT 0x17
#define MODE_UDF 0x1B
#define MODE_SYS 0x1F
#define MODE_MASK 0x1F
#define FIQ 0x40
#define IRQ 0x80
.section .text._irq_setup
.arm
.extern ipl_main
.type ipl_main, %function
.extern svc_handler
.type svc_handler, %function
.extern irq_handler
.type irq_handler, %function
.extern fiq_setup
.type fiq_setup, %function
.extern fiq_handler
.type fiq_handler, %function
.globl _irq_setup
.type _irq_setup, %function
_irq_setup:
MRS R0, CPSR
BIC R0, R0, #MODE_MASK /* Clear mode bits */
ORR R0, R0, #(MODE_SVC | IRQ | FIQ) /* SUPERVISOR mode, IRQ/FIQ disabled */
MSR CPSR, R0
/* Setup IRQ stack pointer */
MSR CPSR, #(MODE_IRQ | IRQ | FIQ) /* IRQ mode, IRQ/FIQ disabled */
LDR SP, =0x40040000
/* Setup SYS stack pointer */
MSR CPSR, #(MODE_SYS | IRQ | FIQ) /* SYSTEM mode, IRQ/FIQ disabled */
LDR SP, =0x4003FF00 /* Will be changed later to DRAM */
MOV LR, PC
BL setup_vectors
/*BL fiq_setup*/
/* Enable interrupts */
BL irq_enable_cpu_irq_exceptions
B ipl_main
B .
_reset:
LDR R0, =EXCP_EN_ADDR
LDR R1, =0x30505645 /* EVP0 */
STR R1, [R0] /* EVP0 in EXCP_EN_ADDR */
LDR R0, =EXCP_LR_ADDR
MOV R1, LR
STR R1, [R0] /* Save LR in EXCP_LR_ADDR */
LDR R0, =__bss_start
EOR R1, R1, R1
LDR R2, =__bss_end
SUB R2, R2, R0
BL memset
B _irq_setup
_reset_handler:
LDR R0, =EXCP_TYPE_ADDR
LDR R1, =0x545352 /* RST */
STR R1, [R0] /* RST in EXCP_TYPE_ADDR */
B _reset
_undefined_handler:
LDR R0, =EXCP_TYPE_ADDR
LDR R1, =0x464455 /* UDF */
STR R1, [R0] /* UDF in EXCP_TYPE_ADDR */
B _reset
_prefetch_abort_handler:
LDR R0, =EXCP_TYPE_ADDR
LDR R1, =0x54424150 /* PABT */
STR R1, [R0] /* PABT in EXCP_TYPE_ADDR */
B _reset
_data_abort_handler:
LDR R0, =EXCP_TYPE_ADDR
LDR R1, =0x54424144 /* DABT */
STR R1, [R0] /* DABT in EXCP_TYPE_ADDR */
B _reset
.globl irq_enable_cpu_irq_exceptions
.type irq_enable_cpu_irq_exceptions, %function
irq_enable_cpu_irq_exceptions:
MRS R12, CPSR
BIC R12, R12, #(IRQ | FIQ) /* IRQ/FIQ enabled */
MSR CPSR, R12
BX LR
.globl irq_disable_cpu_irq_exceptions
.type irq_disable_cpu_irq_exceptions, %function
irq_disable_cpu_irq_exceptions:
MRS R12, CPSR
ORR R12, R12, #(IRQ | FIQ) /* IRQ/FIQ disabled */
MSR CPSR, R12
BX LR
_irq_handler:
MOV R13, R0 /* Save R0 in R13_IRQ */
SUB R0, LR, #4 /* Put return address in R0_SYS */
MOV LR, R1 /* Save R1 in R14_IRQ (LR) */
MRS R1, SPSR /* Put the SPSR in R1_SYS */
MSR CPSR_c, #(MODE_SYS | IRQ) /* SYSTEM mode, IRQ disabled */
STMFD SP!, {R0, R1} /* SPSR and PC */
STMFD SP!, {R2-R3, R12, LR} /* AAPCS-clobbered registers */
MOV R0, SP /* Make SP_SYS visible to IRQ mode */
SUB SP, SP, #8 /* Make room for stacking R0 and R1 */
MSR CPSR_c, #(MODE_IRQ | IRQ) /* IRQ mode, IRQ disabled */
STMFD R0!, {R13, R14} /* Finish saving the context (R0, R1) */
MSR CPSR_c, #(MODE_SYS | IRQ) /* SYSTEM mode, IRQ disabled */
LDR R12, =irq_handler
MOV LR, PC /* Copy the return address to link register */
BX R12 /* Call the C IRQ handler (ARM/THUMB) */
MSR CPSR_c, #(MODE_SYS | IRQ | FIQ) /* SYSTEM mode, IRQ/FIQ disabled */
MOV R0, SP /* Make SP_SYS visible to IRQ mode */
ADD SP, SP, #32 /* Fake unstacking 8 registers from SP_SYS */
MSR CPSR_c, #(MODE_IRQ | IRQ | FIQ) /* IRQ mode, IRQ/FIQ disabled */
MOV SP, R0 /* Copy SP_SYS to SP_IRQ */
LDR R0, [SP, #28] /* Load the saved SPSR from the stack */
MSR SPSR_cxsf, R0 /* Copy it into SPSR_IRQ */
LDMFD SP, {R0-R3, R12, LR}^ /* Unstack all saved USER/SYSTEM registers */
NOP /* Cant access barked registers immediately */
LDR LR, [SP, #24] /* Load return address from the SYS stack */
MOVS PC, LR /* Return restoring CPSR from SPSR */
_fiq_handler:
BL fiq_handler
setup_vectors:
/* Setup vectors */
LDR R0, =EXCP_VEC_BASE
LDR R1, =_reset_handler
STR R1, [R0, #EVP_COP_RESET_VECTOR]
LDR R1, =_undefined_handler
STR R1, [R0, #EVP_COP_UNDEF_VECTOR]
LDR R1, =_reset_handler
STR R1, [R0, #EVP_COP_SWI_VECTOR]
LDR R1, =_prefetch_abort_handler
STR R1, [R0, #EVP_COP_PREFETCH_ABORT_VECTOR]
LDR R1, =_data_abort_handler
STR R1, [R0, #EVP_COP_DATA_ABORT_VECTOR]
LDR R1, =_reset_handler
STR R1, [R0, #EVP_COP_RSVD_VECTOR]
LDR R1, =_irq_handler
STR R1, [R0, #EVP_COP_IRQ_VECTOR]
LDR R1, =_fiq_handler
STR R1, [R0, #EVP_COP_FIQ_VECTOR]
BX LR

View file

@ -7,6 +7,7 @@ SECTIONS {
*(.text._start);
*(._boot_cfg);
*(._ipl_version);
*(.text._irq_setup);
*(.text*);
}
.data : {

View file

@ -1182,8 +1182,24 @@ static void _patched_rcm_protection()
sdmmc_storage_end(&storage);
}
#define EXCP_EN_ADDR 0x4003FFFC
#define EXCP_MAGIC 0x30505645 // EVP0
#define EXCP_TYPE_ADDR 0x4003FFF8
#define EXCP_TYPE_RESET 0x545352 // RST
#define EXCP_TYPE_UNDEF 0x464455 // UDF
#define EXCP_TYPE_PABRT 0x54424150 // PABT
#define EXCP_TYPE_DABRT 0x54424144 // DABT
#define EXCP_LR_ADDR 0x4003FFF4
static void _show_errors()
{
u32 *excp_enabled = (u32 *)EXCP_EN_ADDR;
u32 *excp_type = (u32 *)EXCP_TYPE_ADDR;
u32 *excp_lr = (u32 *)EXCP_LR_ADDR;
if (*excp_enabled == EXCP_MAGIC)
h_cfg.errors |= ERR_EXCEPT_ENB;
if (h_cfg.errors)
{
gfx_clear_grey(0x1B);
@ -1195,7 +1211,35 @@ static void _show_errors()
if (h_cfg.errors & ERR_SYSOLD_MTC)
WPRINTF("Missing or old Minerva library!\n");
WPRINTF("\nUpdate your bootloader folder!\n\n");
if (h_cfg.errors & ~ERR_EXCEPT_ENB)
{
WPRINTF("\nUpdate your bootloader folder!\n\n");
}
if (h_cfg.errors & ERR_EXCEPT_ENB)
{
WPRINTFARGS("An exception happened (LR %08X):\n", *excp_lr);
switch (*excp_type)
{
case EXCP_TYPE_RESET:
WPRINTF("Reset");
break;
case EXCP_TYPE_UNDEF:
WPRINTF("Undefined instruction");
break;
case EXCP_TYPE_PABRT:
WPRINTF("Prefetch abort");
break;
case EXCP_TYPE_DABRT:
WPRINTF("Data abort");
break;
}
WPRINTF("\n");
// Clear the exception.
*excp_enabled = 0;
}
WPRINTF("Press any key...");
msleep(2000);

View file

@ -23,8 +23,8 @@
.extern memset
.type memset, %function
.extern ipl_main
.type ipl_main, %function
.extern _irq_setup
.type _irq_setup, %function
.globl _start
.type _start, %function
@ -67,7 +67,7 @@ _real_start:
LDR R2, =__bss_end
SUB R2, R2, R0
BL memset
BL ipl_main
BL _irq_setup
B .
.globl pivot_stack

View file

@ -0,0 +1,233 @@
/*
* Copyright (c) 2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Armv7tdmi Status register.
*
* bit0: Mode 0.
* bit1: Mode 1.
* bit2: Mode 2.
* bit3: Mode 3.
* bit4: Mode 4.
* bit5: Thumb state.
* bit6: FIQ disable.
* bit7: IRQ disable.
* bit8-27: Reserved.
* bit28: Overflow condition.
* bit29: Carry/Borrow/Extend condition.
* bit30: Zero condition.
* bit31: Negative/Less than condition.
*
* M[4:0] | Mode | Visible Thumb-state registers | Visible ARM-state registers
* 10000 | USER | r0r7, SP, LR, PC, CPSR | r0r14, PC, CPSR
* 10001 | FIQ | r0r7, SP_fiq, LR_fiq, PC, CPSR, SPSR_fiq | r0r7, r8_fiqr14_fiq, PC, CPSR, SPSR_fiq
* 10010 | IRQ | r0r7, SP_irq, LR_irq, PC, CPSR, SPSR_irq | r0r12, r13_irq, r14_irq, PC, CPSR, SPSR_irq
* 10011 | SVC | r0r7, SP_svc, LR_svc, PC, CPSR, SPSR_svc | r0r12, r13_svc, r14_svc, PC, CPSR, SPSR_svc
* 10111 | ABRT | r0r7, SP_abt, LR_abt, PC, CPSR, SPSR_abt | r0r12, r13_abt, r14_abt, PC, CPSR, SPSR_abt
* 11011 | UNDF | r0r7, SP_und, LR_und, PC, CPSR, SPSR_und | r0r12, r13_und, r14_und, PC, CPSR, SPSR_und
* 11111 | SYS | r0r7, SP, LR, PC, CPSR | r0r14, PC, CPSR
*/
#define EXCP_EN_ADDR 0x4003FFFC
#define EXCP_TYPE_ADDR 0x4003FFF8
#define EXCP_LR_ADDR 0x4003FFF4
#define EXCP_VEC_BASE 0x6000F000
#define EVP_COP_RESET_VECTOR 0x200
#define EVP_COP_UNDEF_VECTOR 0x204
#define EVP_COP_SWI_VECTOR 0x208
#define EVP_COP_PREFETCH_ABORT_VECTOR 0x20C
#define EVP_COP_DATA_ABORT_VECTOR 0x210
#define EVP_COP_RSVD_VECTOR 0x214
#define EVP_COP_IRQ_VECTOR 0x218
#define EVP_COP_FIQ_VECTOR 0x21C
#define MODE_USR 0x10
#define MODE_FIQ 0x11
#define MODE_IRQ 0x12
#define MODE_SVC 0x13
#define MODE_ABT 0x17
#define MODE_UDF 0x1B
#define MODE_SYS 0x1F
#define MODE_MASK 0x1F
#define FIQ 0x40
#define IRQ 0x80
.section .text._irq_setup
.arm
.extern ipl_main
.type ipl_main, %function
.extern svc_handler
.type svc_handler, %function
.extern irq_handler
.type irq_handler, %function
.extern fiq_setup
.type fiq_setup, %function
.extern fiq_handler
.type fiq_handler, %function
.globl _irq_setup
.type _irq_setup, %function
_irq_setup:
MRS R0, CPSR
BIC R0, R0, #MODE_MASK /* Clear mode bits */
ORR R0, R0, #(MODE_SVC | IRQ | FIQ) /* SUPERVISOR mode, IRQ/FIQ disabled */
MSR CPSR, R0
/* Setup IRQ stack pointer */
MSR CPSR, #(MODE_IRQ | IRQ | FIQ) /* IRQ mode, IRQ/FIQ disabled */
LDR SP, =0x40040000
/* Setup SYS stack pointer */
MSR CPSR, #(MODE_SYS | IRQ | FIQ) /* SYSTEM mode, IRQ/FIQ disabled */
LDR SP, =0x4003FF00 /* Will be changed later to DRAM */
MOV LR, PC
BL setup_vectors
/*BL fiq_setup*/
/* Enable interrupts */
BL irq_enable_cpu_irq_exceptions
B ipl_main
B .
_reset:
LDR R0, =EXCP_EN_ADDR
LDR R1, =0x30505645 /* EVP0 */
STR R1, [R0] /* EVP0 in EXCP_EN_ADDR */
LDR R0, =EXCP_LR_ADDR
MOV R1, LR
STR R1, [R0] /* Save LR in EXCP_LR_ADDR */
LDR R0, =__bss_start
EOR R1, R1, R1
LDR R2, =__bss_end
SUB R2, R2, R0
BL memset
B _irq_setup
_reset_handler:
LDR R0, =EXCP_TYPE_ADDR
LDR R1, =0x545352 /* RST */
STR R1, [R0] /* RST in EXCP_TYPE_ADDR */
B _reset
_undefined_handler:
LDR R0, =EXCP_TYPE_ADDR
LDR R1, =0x464455 /* UDF */
STR R1, [R0] /* UDF in EXCP_TYPE_ADDR */
B _reset
_prefetch_abort_handler:
LDR R0, =EXCP_TYPE_ADDR
LDR R1, =0x54424150 /* PABT */
STR R1, [R0] /* PABT in EXCP_TYPE_ADDR */
B _reset
_data_abort_handler:
LDR R0, =EXCP_TYPE_ADDR
LDR R1, =0x54424144 /* DABT */
STR R1, [R0] /* DABT in EXCP_TYPE_ADDR */
B _reset
.globl irq_enable_cpu_irq_exceptions
.type irq_enable_cpu_irq_exceptions, %function
irq_enable_cpu_irq_exceptions:
MRS R12, CPSR
BIC R12, R12, #(IRQ | FIQ) /* IRQ/FIQ enabled */
MSR CPSR, R12
BX LR
.globl irq_disable_cpu_irq_exceptions
.type irq_disable_cpu_irq_exceptions, %function
irq_disable_cpu_irq_exceptions:
MRS R12, CPSR
ORR R12, R12, #(IRQ | FIQ) /* IRQ/FIQ disabled */
MSR CPSR, R12
BX LR
_irq_handler:
MOV R13, R0 /* Save R0 in R13_IRQ */
SUB R0, LR, #4 /* Put return address in R0_SYS */
MOV LR, R1 /* Save R1 in R14_IRQ (LR) */
MRS R1, SPSR /* Put the SPSR in R1_SYS */
MSR CPSR_c, #(MODE_SYS | IRQ) /* SYSTEM mode, IRQ disabled */
STMFD SP!, {R0, R1} /* SPSR and PC */
STMFD SP!, {R2-R3, R12, LR} /* AAPCS-clobbered registers */
MOV R0, SP /* Make SP_SYS visible to IRQ mode */
SUB SP, SP, #8 /* Make room for stacking R0 and R1 */
MSR CPSR_c, #(MODE_IRQ | IRQ) /* IRQ mode, IRQ disabled */
STMFD R0!, {R13, R14} /* Finish saving the context (R0, R1) */
MSR CPSR_c, #(MODE_SYS | IRQ) /* SYSTEM mode, IRQ disabled */
LDR R12, =irq_handler
MOV LR, PC /* Copy the return address to link register */
BX R12 /* Call the C IRQ handler (ARM/THUMB) */
MSR CPSR_c, #(MODE_SYS | IRQ | FIQ) /* SYSTEM mode, IRQ/FIQ disabled */
MOV R0, SP /* Make SP_SYS visible to IRQ mode */
ADD SP, SP, #32 /* Fake unstacking 8 registers from SP_SYS */
MSR CPSR_c, #(MODE_IRQ | IRQ | FIQ) /* IRQ mode, IRQ/FIQ disabled */
MOV SP, R0 /* Copy SP_SYS to SP_IRQ */
LDR R0, [SP, #28] /* Load the saved SPSR from the stack */
MSR SPSR_cxsf, R0 /* Copy it into SPSR_IRQ */
LDMFD SP, {R0-R3, R12, LR}^ /* Unstack all saved USER/SYSTEM registers */
NOP /* Cant access barked registers immediately */
LDR LR, [SP, #24] /* Load return address from the SYS stack */
MOVS PC, LR /* Return restoring CPSR from SPSR */
_fiq_handler:
BL fiq_handler
setup_vectors:
/* Setup vectors */
LDR R0, =EXCP_VEC_BASE
LDR R1, =_reset_handler
STR R1, [R0, #EVP_COP_RESET_VECTOR]
LDR R1, =_undefined_handler
STR R1, [R0, #EVP_COP_UNDEF_VECTOR]
LDR R1, =_reset_handler
STR R1, [R0, #EVP_COP_SWI_VECTOR]
LDR R1, =_prefetch_abort_handler
STR R1, [R0, #EVP_COP_PREFETCH_ABORT_VECTOR]
LDR R1, =_data_abort_handler
STR R1, [R0, #EVP_COP_DATA_ABORT_VECTOR]
LDR R1, =_reset_handler
STR R1, [R0, #EVP_COP_RSVD_VECTOR]
LDR R1, =_irq_handler
STR R1, [R0, #EVP_COP_IRQ_VECTOR]
LDR R1, =_fiq_handler
STR R1, [R0, #EVP_COP_FIQ_VECTOR]
BX LR

View file

@ -6,6 +6,7 @@ SECTIONS {
.text : {
*(.text._start);
*(._ipl_version);
*(.text._irq_setup);
*(.text*);
}
.data : {

View file

@ -367,6 +367,59 @@ void load_saved_configuration()
}
}
#define EXCP_EN_ADDR 0x4003FFFC
#define EXCP_MAGIC 0x30505645 // EVP0
#define EXCP_TYPE_ADDR 0x4003FFF8
#define EXCP_TYPE_RESET 0x545352 // RST
#define EXCP_TYPE_UNDEF 0x464455 // UDF
#define EXCP_TYPE_PABRT 0x54424150 // PABT
#define EXCP_TYPE_DABRT 0x54424144 // DABT
#define EXCP_LR_ADDR 0x4003FFF4
static void _show_errors()
{
u32 *excp_enabled = (u32 *)EXCP_EN_ADDR;
u32 *excp_type = (u32 *)EXCP_TYPE_ADDR;
u32 *excp_lr = (u32 *)EXCP_LR_ADDR;
if (*excp_enabled == EXCP_MAGIC)
{
gfx_clear_grey(0);
gfx_con_setpos(0, 0);
display_backlight_brightness(100, 1000);
WPRINTFARGS("An exception happened (LR %08X):\n", *excp_lr);
switch (*excp_type)
{
case EXCP_TYPE_RESET:
WPRINTF("Reset");
break;
case EXCP_TYPE_UNDEF:
WPRINTF("Undefined instruction");
break;
case EXCP_TYPE_PABRT:
WPRINTF("Prefetch abort");
break;
case EXCP_TYPE_DABRT:
WPRINTF("Data abort");
break;
}
WPRINTF("\n");
// Clear the exception.
*excp_lr = 0;
*excp_type = 0;
*excp_enabled = 0;
WPRINTF("Press any key...");
msleep(2000);
btn_wait();
reload_nyx();
}
}
void nyx_init_load_res()
{
bpmp_mmu_enable();

View file

@ -23,8 +23,8 @@
.extern memset
.type memset, %function
.extern ipl_main
.type ipl_main, %function
.extern _irq_setup
.type _irq_setup, %function
.globl _start
.type _start, %function
@ -36,7 +36,7 @@ _start:
/* If we are not in the right location already, copy a relocator to upper IRAM. */
ADR R2, _reloc_ipl
LDR R3, =0x4003FFE0
LDR R3, =0x4003FF00
MOV R4, #(_real_start - _reloc_ipl)
_copy_loop:
LDMIA R2!, {R5}
@ -48,7 +48,7 @@ _copy_loop:
LDR R2, =__ipl_end
SUB R2, R2, R1
LDR R3, =_real_start
LDR R4, =0x4003FFE0
LDR R4, =0x4003FF00
BX R4
_reloc_ipl:
@ -67,7 +67,7 @@ _real_start:
LDR R2, =__bss_end
SUB R2, R2, R0
BL memset
BL ipl_main
BL _irq_setup
B .
.globl pivot_stack