ARM的启动方式和bootloader解析(下)

Linux一路的“鸟语花香”

3.ARM的启动方式和bootloader解析(下)

作者:(vianowu)                               

本期关键词:    NAND flash启动方式  Romboot  uboot   bootstrap

本期扩展关键词:NOR flash启动方式  emmc  cp15  dataflash

平台: AT91SAM9x25

问题四:我们用RomBoot引导什么程序?那个程序做什么?

我们烧写的程序就是bootloader。但是有的bootloader为了更好的区分引导程序功能,又将bootloader分为一级bootloader(又叫bootstrap)和二级bootloaderU-boot)。第一阶段的bootstrap主要完成的功能是硬件初始化,加载U-bootRAM,设置堆栈,跳到第二段代码入口。第二阶段的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-bootSDRAM在地址

           /* 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之后返回给robootstrap就根据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都设为

    */

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解析(下))