Linux一路的“鸟语花香”
3.ARM的启动方式和bootloader解析(下)
作者:(vianowu)
本期关键词: NAND flash启动方式 Romboot uboot bootstrap
本期扩展关键词:NOR flash启动方式 emmc cp15 dataflash
平台: AT91SAM9x25
问题四:我们用RomBoot引导什么程序?那个程序做什么?
我们烧写的程序就是bootloader。但是有的bootloader为了更好的区分引导程序功能,又将bootloader分为一级bootloader(又叫bootstrap)和二级bootloader(U-boot)。第一阶段的bootstrap主要完成的功能是硬件初始化,加载U-boot到RAM,设置堆栈,跳到第二段代码入口。第二阶段的U-boot主要完成的功能是初始化要用的硬件设备,内存映射,从Flash读取内核映像和根文件系统,设置内核启动参数,调用内核。
我们用的第一阶段的启动代码bootstrap(支持nandflash启动的)入口是crt0_gnu.s。以下开始分析crt0_gnu.s的实现功能。
堆栈 -》时钟 -》代码数据段 -》初级硬件初始化
.section start .text #include "include/part.h" /* Application startup entry point */ //程序入口 .globl reset .align 4 reset: /* Exception vectors (should be a branch to be detected as a valid code by the rom */ //这就是前面提到的28个字节(7个向量,要不然Romboot就认为你的程序不合法啦) _exception_vectors: b reset_vector /* reset */ b undef_vector /* Undefined Instruction */ b swi_vector /* Software Interrupt */ b pabt_vector /* Prefetch Abort */ b dabt_vector /* Data Abort */ .word _edata /* Size of the image for SAM-BA */ b irq_vector /* IRQ : read the AIC */ b fiq_vector /* FIQ */ undef_vector: b undef_vector //这里其实是要我们命名的函数入口 swi_vector: b swi_vector pabt_vector: b pabt_vector dabt_vector: b dabt_vector rsvd_vector: b rsvd_vector irq_vector: b irq_vector fiq_vector: b fiq_vector reset_vector: //这里就是所有工作的开始啦 /* Init the stack */ _init_stack: ldr sp,=TOP_OF_MEMORY #ifdef CONFIG_FLASH //我们是NAND flash 启动,所以不看这段NOR Flash启动代码,以后蓝色的部分都代表不用看 /* * When running from NOR, we must relocate to SRAM prior to resetting * the clocks and SMC timings. */ _relocate_to_sram: #if 0 /* relocation is slow, disable the watchdog or it will trigger */ ldrr1, =0xFFFFFD44 movr2, #0x00008000 strr2, [r1] #endif movr1, #0 ldrr3, =_stext ldrr4, =_edata 1: cmp r3, r4 ldrcc r2, [r1], #4 strcc r2, [r3], #4 bcc 1b ldrpc, =_setup_clocks #endif /* CONFIG_FLASH */ //以下是时钟初始化 ldr r4, = lowlevel_clock_init mov lr, pc //保存PC指针 bx r4 //跳转至lowlevel_clock_init()函数的入口地址 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //程序此时跳转到pmc.c中, void lowlevel_clock_init() { #if defined(CONFIG_AT91SAM9X5EK)//我们在配置bootloader的时候会选择这个宏 unsigned long tmp; tmp = read_pmc(PMC_MCKR); // readl(offset + AT91C_BASE_PMC); /#define AT91C_BASE_PMC (0xFFFFFC00) 电源管理控制时钟使能寄存器 //返回的是 PMC_MCKR + AT91C_BASE_PMC 的地址的值;PMC_MCKR = 8; //结果是0xFFFFFC08 ,即返回电源管理控制时钟状态寄存器 的 值。
tmp &= ~AT91C_PMC_CSS; //#define AT91C_PMC_CSS (0x3 << 0) //准备使能时钟 DDRCK,SMDCK,UHP,UDP,PCK1,PCK0 tmp |= AT91C_PMC_CSS_MAIN_CLK;//准备PCK时钟使能
write_pmc(PMC_MCKR, tmp);// writel(offset + AT91C_BASE_PMC); //0xFFFF FC30 即PMC mask clock register,使能所有时钟。 while (!(read_pmc(PMC_SR) & AT91C_PMC_MCKRDY)) ;//读取主时钟状态寄存器,如果状态还不是ready,那就等待时钟稳定 if (!(read_pmc(PMC_SR) & AT91C_PMC_MOSCXTS)) { //如果XTAL时钟不稳定 /* * Enable 12MHz Main Oscillator //0xFFFF FC20 主振荡寄存器 */ write_pmc(PMC_MOR, (0x37 << 16) | AT91C_CKGR_MOSCXTEN | (0x40 << 8) | AT91C_CKGR_MOSCSEL | AT91C_CKGR_MOSCRCEN); //0000 0001 0011 0111 0100 0000 0000 1001 /* * Wait until 12MHz Main Oscillator is stable */ while (!(read_pmc(PMC_SR) & AT91C_PMC_MOSCXTS)) ; //等待XTAL时钟稳定 } #else if (!(read_pmc(PMC_SR) & AT91C_PMC_MOSCS)) { /* * Enable 12MHz Main Oscillator */ write_pmc(PMC_MOR, AT91C_CKGR_MOSCEN | (0x40 << 8)); /* * Wait until 12MHz Main Oscillator is stable */ while (!(read_pmc(PMC_SR) & AT91C_PMC_MOSCS)) ; } /* * After stablization, switch to 12MHz Main Oscillator */ if ((read_pmc(PMC_MCKR) & AT91C_PMC_CSS) == AT91C_PMC_CSS_SLOW_CLK) { unsigned long tmp; tmp = read_pmc(PMC_MCKR); tmp &= ~AT91C_PMC_CSS; tmp |= AT91C_PMC_CSS_MAIN_CLK; write_pmc(PMC_MCKR, tmp); while (!(read_pmc(PMC_SR) & AT91C_PMC_MCKRDY)) ; tmp &= ~AT91C_PMC_PRES; tmp |= AT91C_PMC_PRES_CLK; write_pmc(PMC_MCKR, tmp); while (!(read_pmc(PMC_SR) & AT91C_PMC_MCKRDY)) ; } #endif return; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #if 0 _setup_clocks: /* Test if main oscillator is enabled */ ldr r0,=AT91C_PMC_SR ldr r1, [r0] ldr r2,=AT91C_PMC_MOSCS ands r1, r1, r2 bne _switch_to_mosc /* Enable the main oscillator */ _enable_mosc: ldr r0,=AT91C_PMC_MOR mov r1, #(0x40 << 8) ldr r2,=AT91C_CKGR_MOSCEN orr r1, r1, r2 strr1, [r0] ldr r0,=AT91C_PMC_SR 1: ldr r1, [r0] ldr r2,=AT91C_PMC_MOSCS ands r1, r1, r2 beq 1b /* Test if MCK == SLOW CLOCK */ _switch_to_mosc: ldr r0,=AT91C_PMC_MCKR ldr r1,=AT91C_PMC_CSS ldr r2, [r0] and r2, r2, r1 movr1, #0 cmp r1, r2 /* No => Do nothing */ bne_init_data /* Yes => Switch to the main oscillator */ ldr r1,=AT91C_PMC_CSS_MAIN_CLK ldr r2,=AT91C_PMC_PRES_CLK orrr1, r1, r2 str r1, [r0] ldr r0,=AT91C_PMC_SR 1: ldr r1, [r0] ldrr2,=AT91C_PMC_MCKRDY ands r1, r1, r2 beq 1b #endif /* Copy the data section in RAM at .data link address */ //这段好好看,看懂了对汇编、底层理解就好多了。哈哈。 _init_data: ldr r2, =_lp_data // r2存储了lp_data这个地址 ldmia r2, {r1, r3, r4} // r1=[r2];r3=[r2+4];r4=[r2+8]; // r1存储了lp_data 这个地址所存的值(edummy) // r3 存储了lp_data 这个地址后四个字节地址所存的值(sdata) // r4 存储了lp_data 这个地址后八个字节地址所存的值(edata) 1: cmp r3, r4 ldrcc r2, [r1], #4 //r2=[r1];r1=r1+4; strcc r2, [r3], #4 //r2的数据给[r3].[r3]=[r3]+4; bcc 1b // r3 小于r4 就跳至1 // --------- _edummy --------- sdata // | link | | RAM | // --------- -----------edata /* Initialize the bss segment */ 清空bss段 _init_bss: adr r2, _lp_bss ldmia r2, {r3, r4} mov r2, #0 1: cmp r3, r4 strcc r2, [r3], #4 bcc 1b /* Branch on C code Main function (with interworking) */ _branch_main: ldr r4, = main mov lr, pc bx r4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 关于Main.c中函数实现会在该段末尾进行注解 int main(void) { #ifdef CONFIG_HW_INIT hw_init(); #endif #ifdef CONFIG_USER_HW_INIT user_hw_init(); #endif #if defined(CONFIG_AT91SAM9X5EK) extern void load_1wire_info(); load_1wire_info(); #endif dbg_log(1, "Downloading image...\n\r"); #if defined(CONFIG_LOAD_LINUX) LoadLinux(); #elif defined(CONFIG_LOAD_NK) || defined(CONFIG_LOAD_EBOOT) LoadWince(); #else /* Booting stand-alone application, e.g. U-Boot */ #if defined (CONFIG_DATAFLASH) load_df(AT91C_SPI_PCS_DATAFLASH, IMG_ADDRESS, IMG_SIZE, JUMP_ADDR); #elif defined(CONFIG_NANDFLASH) read_nandflash((unsigned char *)JUMP_ADDR, (unsigned long)IMG_ADDRESS, (int)IMG_SIZE); #elif defined(CONFIG_SDCARD) load_SDCard((void *)JUMP_ADDR); #else #error "No booting media specified!" #endif #endif dbg_log(1, "Done!\n\r"); #ifdef WINCE #ifdef CONFIG_LOAD_NK Jump(JUMP_ADDR + 0x1000); #else Jump(JUMP_ADDR); #endif #else /* !WINCE */ #ifdef CONFIG_LOAD_NK return (JUMP_ADDR + 0x1000); #else Wait(5000); return JUMP_ADDR; #endif #endif } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Main 返回一个跳转地址到r0中,该地址是U-boot在SDRAM在地址 /* Branch to the application at the end of the bootstrap init */ _go: ldr r1, =MACH_TYPE mov lr, pc bx r0 /*#ifdef CONFIG_THUMB*/ //THUMB指令集 是待会儿要讲到的初级硬件初始化跳转的对应地方 .globl set_cp15 set_cp15: mcr p15, 0, r0, c1, c0, 0 bx lr .globl get_cp15 get_cp15: mrc p15, 0, r0, c1, c0, 0 bx lr .global disable_irq disable_irq: mrs r0, cpsr orr r0, r0, #0xc0 msr cpsr_c, r0 bx lr .global get_cpsr get_cpsr: mrs r0, cpsr bx lr .global set_cpsr set_cpsr: msr cpsr_c, r0 bx lr .global disable_icache disable_icache: mrc p15, 0, r0, c1, c0, 0 mvn r1, #(1 << 12) and r0, r0, r1 mcr p15, 0, r0, c1, c0, 0 bx lr .global disable_dcache disable_dcache: mrc p15, 0, r0, c1, c0, 0 mvn r1, #(1 << 2) and r0, r0, r1 mcr p15, 0, r0, c1, c0, 0 bx lr .global flush_idcache flush_idcache: mov r0, #0 mcr p15, 0, r0, c7, c7, 0 bx lr /*#endif*/ .align _lp_data: .word _edummy //源地址 .word _sdata //目的地址起始地址 .word _edata //目的地址结束地址 _lp_bss: .word _sbss .word _ebss |
以上就是bootstrap完成的功能了,main()函数中主要完成的是硬件初始化和加载内核。以下是硬件初始化部分分析。
配置IO-》关闭看门狗-》主时钟12MHz-》IO
/*----------------------------------------------------------------------------*/ /* \fn hw_init */ /* \brief This function performs very low level HW initialization */ /* This function is invoked as soon as possible during the c_startup */ /* The bss segment must be initialized */ /*----------------------------------------------------------------------------*/ void hw_init(void) { unsigned int cp15; /* * Configure PIOs */ const struct pio_desc hw_pio[] = { #ifdef CONFIG_DEBUG //配置DEBUG口 {"RXD", AT91C_PIN_PA(9), 0, PIO_DEFAULT, PIO_PERIPH_A}, {"TXD", AT91C_PIN_PA(10), 0, PIO_DEFAULT, PIO_PERIPH_A}, #endif {(char *)0, 0, 0, PIO_DEFAULT, PIO_PERIPH_A}, }; /* * Disable watchdog //0xFFFFFE44 16位为1即为关闭看门狗 */ writel(AT91C_WDTC_WDDIS, AT91C_BASE_WDTC + WDTC_WDMR); /* * At this stage the main oscillator is supposed to be enabled * * PCK = MCK = MOSC */ writel(0x00, AT91C_BASE_PMC + PMC_PLLICPR); /* * Configure PLLA = MOSC * (PLL_MULA + 1) / PLL_DIVA */ pmc_cfg_plla(PLLA_SETTINGS, PLL_LOCK_TIMEOUT); /* * PCK = PLLA/2 = 3 * MCK */ pmc_cfg_mck(BOARD_PRESCALER_MAIN_CLOCK, PLL_LOCK_TIMEOUT); /* * Switch MCK on PLLA output */ pmc_cfg_mck(BOARD_PRESCALER_PLLA, PLL_LOCK_TIMEOUT); /* * Enable External Reset */ writel(AT91C_RSTC_KEY_UNLOCK || AT91C_RSTC_URSTEN, AT91C_BASE_RSTC + RSTC_RMR); /* * Configure CP15 */ cp15 = get_cp15(); cp15 |= I_CACHE; set_cp15(cp15); #ifdef CONFIG_SCLK sclk_enable(); #endif /* * Configure the PIO controller */ writel((1 << AT91C_ID_PIOA_B), (PMC_PCER + AT91C_BASE_PMC)); pio_setup(hw_pio); /* * Enable Debug messages on the DBGU */ #ifdef CONFIG_DEBUG dbgu_init(BAUDRATE(MASTER_CLOCK, 115200)); dbgu_print("Start AT91Bootstrap...\n\r"); #endif #ifdef CONFIG_DDR2 /* * Configure DDRAM Controller */ dbg_log(1, "Init DDR... "); ddramc_hw_init(); dbg_log(1, "Done!\n\r"); #endif /* CONFIG_DDR2 */ } #endif /* CONFIG_HW_INIT */ |
问题五:bootstrap引导的又是什么呢?没错,就是U-boot了。
前面已经提到U-boot的起始地址由main函数加载linux之后返回给ro,bootstrap就根据ro直接跳转到U-boot了。
U-boot的第一阶段代码在U-boot/u-boot-linux/arch/cpu/arm926ejs中的start.S。
.globl _start _start: b reset #ifdef CONFIG_PRELOADER /* No exception handlers in preloader */ ldrpc, _hang ldrpc, _hang ldrpc, _hang ldrpc, _hang ldrpc, _hang ldrpc, _hang ldrpc, _hang _hang: .worddo_hang /* pad to 64 byte boundary */ .word0x12345678 .word0x12345678 .word0x12345678 .word0x12345678 .word0x12345678 .word0x12345678 .word0x12345678 #else ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq _undefined_instruction: .word undefined_instruction _software_interrupt: .word software_interrupt _prefetch_abort: .word prefetch_abort _data_abort: .word data_abort _not_used: .word not_used _irq: .word irq _fiq: .word fiq #endif /* CONFIG_PRELOADER */ .balignl 16,0xdeadbeef //表示接下来的代码要16字节对齐,不满足时用0xdeadbeef填充,此字节无特殊含义,倒是其英文含义为死牛肉。 /* ************************************************************************* * * Startup Code (reset vector) * * do important init only if we don't start from memory! * setup Memory and board specific bits prior to relocation. * relocate armboot to ram * setup stack * ************************************************************************* */ _TEXT_BASE: .word TEXT_BASE .globl _armboot_start _armboot_start: .word _start /* * These are defined in the board-specific linker script. */ .globl _bss_start _bss_start: .word __bss_start .globl _bss_end _bss_end: .word _end #ifdef CONFIG_USE_IRQ /* IRQ stack memory (calculated at run-time) */ .globl IRQ_STACK_START IRQ_STACK_START: .word 0x0badc0de /* IRQ stack memory (calculated at run-time) */ .globl FIQ_STACK_START FIQ_STACK_START: .word 0x0badc0de #endif /* * the actual reset code */ reset: /* * set the cpu to SVC32 mode 同时关IRQ/FRQ中断 具体看cpsr的寄存器功能就知道了 P33 */ mrs r0,cpsr bic r0,r0,#0x1f orr r0,r0,#0xd3 msr cpsr,r0 /* * we do sys-critical inits only at reboot, * not when booting from ram! */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit #endif #ifndef CONFIG_SKIP_RELOCATE_UBOOT relocate: /* relocate U-Boot to RAM */ adr r0, _start /* r0 <- current position of code */ ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ cmp r0, r1 /* don't reloc during debug */ beq stack_setup ldr r2, _armboot_start ldr r3, _bss_start sub r2, r3, r2 /* r2 <- size of armboot */ add r2, r0, r2 /* r2 <- source end address */ copy_loop: ldmia r0!, {r3-r10} /* copy from source address [r0] */ stmia r1!, {r3-r10} /* copy to target address [r1] */ cmp r0, r2 /* until source end addreee [r2] */ ble copy_loop #endif /* CONFIG_SKIP_RELOCATE_UBOOT */ /* Set up the stack */ stack_setup: ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ sub sp, r0, #128 /* leave 32 words for abort-stack */ #ifndef CONFIG_PRELOADER sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */ sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */ #ifdef CONFIG_USE_IRQ sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif #endif /* CONFIG_PRELOADER */ sub sp, r0, #12 /* leave 3 words for abort-stack */ bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ clear_bss: ldr r0, _bss_start /* find start of bss segment */ ldr r1, _bss_end /* stop here */ mov r2, #0x00000000 /* clear */ #ifndef CONFIG_PRELOADER clbss_l:str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 ble clbss_l bl coloured_LED_init bl red_LED_on #endif /* CONFIG_PRELOADER */ ldr pc, _start_armboot _start_armboot: #ifdef CONFIG_NAND_SPL .word nand_boot #else .word start_armboot #endif /* CONFIG_NAND_SPL */ /* ************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * ************************************************************************* */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT cpu_init_crit: /* * flush v4 I/D caches 把C7 C8都设为0 */ mov r0, #0 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ /* * disable MMU stuff and caches 关闭IcacheDcache */ mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002300 /* clear bits 13, 9:8 (--V- --RS) */ bic r0, r0, #0x00000087 /* clear bits 7, 2:0 (B--- -CAM) */ orr r0, r0, #0x00000002 /* set bit 2 (A) Align */ orr r0, r0, #0x00001000 /* set bit 12 (I) I-Cache */ mcr p15, 0, r0, c1, c0, 0 /* * Go setup Memory and board specific bits prior to relocation. */ mov ip, lr /* perserve link reg across call */ bl lowlevel_init /* go setup pll,mux,memory */ mov lr, ip /* restore link */ mov pc, lr /* back to my caller */ #endif /* CONFIG_SKIP_LOWLEVEL_INIT */ #ifndef CONFIG_PRELOADER /* ************************************************************************* * * Interrupt handling * ************************************************************************* */ @ @ IRQ stack frame. @ #define S_FRAME_SIZE 72 #define S_OLD_R0 68 #define S_PSR 64 #define S_PC 60 #define S_LR 56 #define S_SP 52 #define S_IP 48 #define S_FP 44 #define S_R10 40 #define S_R9 36 #define S_R8 32 #define S_R7 28 #define S_R6 24 #define S_R5 20 #define S_R4 16 #define S_R3 12 #define S_R2 8 #define S_R1 4 #define S_R0 0 #define MODE_SVC 0x13 #define I_BIT 0x80 /* * use bad_save_user_regs for abort/prefetch/undef/swi ... * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling */ .macro bad_save_user_regs //相当于一个无参数的宏 @ carve out a frame on current user stack //保存现场 sub sp, sp, #S_FRAME_SIZE //sp = sp - 72 stmia sp, {r0 - r12} @ Save user registers (now in svc mode) r0-r12 //将r0~r12的内容 13*4字节保存到堆栈中 ldr r2, _armboot_start sub r2, r2, #(CONFIG_STACKSIZE+CONFIG_SYS_MALLOC_LEN) sub r2, r2, #(CONFIG_SYS_GBL_DATA_SIZE+8) @ set base 2 words into abort stack @ get values for "aborted" pc and cpsr (into parm regs) ldmia r2, {r2 - r3} add r0, sp, #S_FRAME_SIZE @ grab pointer to old stack add r5, sp, #S_SP mov r1, lr stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr mov r0, sp @ save current stack into r0 (param register) .endm .macro irq_save_user_regs sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ Calling r0-r12 @ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good. add r8, sp, #S_PC stmdb r8, {sp, lr}^ @ Calling SP, LR str lr, [r8, #0] @ Save calling PC mrs r6, spsr str r6, [r8, #4] @ Save CPSR str r0, [r8, #8] @ Save OLD_R0 mov r0, sp .endm .macro irq_restore_user_regs ldmia sp, {r0 - lr}^ @ Calling r0 - lr mov r0, r0 ldr lr, [sp, #S_PC] @ Get PC add sp, sp, #S_FRAME_SIZE subs pc, lr, #4 @ return & move spsr_svc into cpsr .endm .macro get_bad_stack ldr r13, _armboot_start @ setup our mode stack sub r13, r13, #(CONFIG_STACKSIZE+CONFIG_SYS_MALLOC_LEN) sub r13, r13, #(CONFIG_SYS_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack str lr, [r13] @ save caller lr in position 0 of saved stack mrs lr, spsr @ get the spsr str lr, [r13, #4] @ save spsr in position 1 of saved stack mov r13, #MODE_SVC @ prepare SVC-Mode @ msr spsr_c, r13 msr spsr, r13 @ switch modes, make sure moves will execute mov lr, pc @ capture return pc movs pc, lr @ jump to next instruction & switch modes. .endm .macro get_irq_stack @ setup IRQ stack ldr sp, IRQ_STACK_START .endm .macro get_fiq_stack @ setup FIQ stack ldr sp, FIQ_STACK_START .endm #endif /* CONFIG_PRELOADER */ /* * exception handlers */ #ifdef CONFIG_PRELOADER .align 5 do_hang: ldr sp, _TEXT_BASE /* switch to abort stack */ 1: bl 1b /* hang and never return */ #else /* !CONFIG_PRELOADER */ .align 5 undefined_instruction: get_bad_stack bad_save_user_regs bl do_undefined_instruction .align 5 software_interrupt: get_bad_stack bad_save_user_regs bl do_software_interrupt .align 5 prefetch_abort: get_bad_stack bad_save_user_regs bl do_prefetch_abort .align 5 data_abort: get_bad_stack bad_save_user_regs bl do_data_abort .align 5 not_used: get_bad_stack bad_save_user_regs bl do_not_used #ifdef CONFIG_USE_IRQ .align 5 irq: get_irq_stack irq_save_user_regs bl do_irq irq_restore_user_regs .align 5 fiq: get_fiq_stack /* someone ought to write a more effiction fiq_save_user_regs */ irq_save_user_regs bl do_fiq irq_restore_user_regs #else .align 5 irq: get_bad_stack bad_save_user_regs bl do_irq .align 5 fiq: get_bad_stack bad_save_user_regs bl do_fiq #endif #endif /* CONFIG_PRELOADER */ |