ARM的启动方式和bootloader解析(下)
来源:互联网 发布:天天聚财网网络借贷 编辑:程序博客网 时间:2024/05/21 09:29
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 */
- ARM的启动方式和bootloader解析(下)
- ARM的启动方式和bootloader解析(上)
- Bootloader 设计蓝图 和 ARM 的启动流程
- ARM下启动linux条件-bootloader
- ARM学习笔记012之mini2440上电的启动和初始化程序分析(即Bootloader分析)
- ARM体系架构下的linux启动之一,从bootloader到linux内核
- Bootloader的分区和启动
- arm-linux内核start_kernel之前启动分析(1)-接过bootloader的衣钵
- arm-linux内核start_kernel之前启动分析(1)-接过bootloader的衣钵
- 基于ARM的嵌入式系统Bootloader启动流程分析(S3C44B0X)
- 基于ARM的嵌入式系统Bootloader启动流程分析----- 转!!
- 基于ARM的嵌入式系统Bootloader启动流程分析
- 启动代码和Bootloader的区别
- Bootloader的结构和启动过程
- 启动代码和Bootloader
- 启动代码和Bootloader
- Bootloader介绍和启动
- arm-linux移植手记(二)bootloader移植(下)
- mysql优化的20条建议
- Linux fedora 19 x86_64 双显卡 关闭独显
- C#委托的介绍(delegate、Action、Func、predicate)
- poj 1010 STAMPS
- 01-访问修饰符-封装
- ARM的启动方式和bootloader解析(下)
- 天梯 1212 最大公约数
- O(n)时间的排序
- sift+bag_of_words+LDA实现图片搜索(二)
- 数独-解
- [leetcode刷题系列]Swap Nodes in Pairs
- 【30分钟学习】二种简单实用的方法实现多语言解决方案(源码在附件)
- Linux无锁编程
- 九度OJ 1035:找出直系亲属(二叉树)