u-boot存储器映射图:
start.S 代码分析
#include <common.h>
#include <config.h>
/*#include <config.h>头文件中声明的以下三个头文件,加一个CONFIG_BOARDDIR声明
#define CONFIG_BOARDDIR board/tekkamanninja/mini2440
#include <config_defaults.h>
#include <configs/mini2440.h>
#include <asm/config.h>
*/
.globl _start /*定义一个编译器使用的全局变量*/ //不占内存
_start: b start_code /*复位向量,上电后执行的第一条指令(这里要用b相对跳转指令,因为编译时的链接地址不为0,而是0x3*******)*///占4字节内存
ldr pc, _undefined_instruction /*未定义指令异常,0x04(属于绝对跳转,跳转到undefined_instruction标签地址值处)*/ //占4字节内存
ldr pc, _software_interrupt /*软中断异常,0x08*/ //占4字节内存
ldr pc, _prefetch_abort /*内存操作异常,0x0c*/ //占4字节内存
ldr pc, _data_abort /*数据异常,0x10*/ //占4字节内存
ldr pc, _not_used /*未使用,0x14*/ //占4字节内存
ldr pc, _irq /*慢速中断异常,0x18*/ //占4字节内存
ldr pc, _fiq /*快速中断异常,0x1c*/ //占4字节内存
_undefined_instruction: .word undefined_instruction /*取得undefined_instruction标签地址值(word型),_undefined_instruction引用了该地址*///占4字节内存
_software_interrupt: .word software_interrupt //占4字节内存
_prefetch_abort: .word prefetch_abort //占4字节内存
_data_abort: .word data_abort //占4字节内存
_not_used: .word not_used //占4字节内存
_irq: .word irq //占4字节内存
_fiq: .word fiq //占4字节内存
/*
用途:后会用这个地址的内容来判断是否是从norflash启动(软件自动识别启动方式,自动选择代码拷贝方案,norflash和nandflash的拷贝方式不同)
15*4=60 前面占用了60字节地址,所以.balignl伪指令占用4个字节地址。就能使16字节对齐了
*/
.balignl 16,0xdeadbeef //找到第一次出现的以16为整数倍的地址,占4字节内存就能使16字节对齐了//占4字节内存
/*此处地址为@0x00000016 */
/*
*注意:0xdeadbeef是什么意思?
*类似这样的值很多,像0xabababab,它们的作用就是为内存做标记,插在那里,就表示从这个位置往后的一段有特殊作用的内存。
*/
/*
*************************************************************************
*
* Startup Code (called from the ARM reset exception vector)
*
* do important init only if we don't start from memory!
* relocate armboot to ram
* setup stack
* jump to second stage
*
*************************************************************************
*/
@这里的 TEXT_BASE = 0x33F80000 ,_start= 0x33F80000 (都是代码段的启始链接地址,代码运行时的地址)//反汇编查看uboot.elf可以看到
_TEXT_BASE:
.word TEXT_BASE /*把TEXT_BASE值保存在当前地址,TEXT_BASE是你设置的代码段的启始链接地址*/ //占4字节内存
.globl _armboot_start /*暴露全局,编译时_armboot_start可以被其他文件引用*/ //不占内存
_armboot_start:
.word _start /*把_start值保存在当前地址,_start是你设置的代码段的启始链接地址*/ //占4字节内存
/*
* These are defined in the board-specific linker script.
*/
@__bss_start定义在和开发板相关的u-boot.lds中,_bss_start保存的是__bss_start标号所在的地址。
.globl _bss_start
_bss_start:
.word __bss_start /*把__bss_start值保存在当前地址,__bss_start是你设置的bbs段的启始地址*/ //占4字节内存(去反汇编得到地址为0x33fb86c4,由编译器计算得到)
.globl _bss_end
_bss_end:
.word _end /*把_end值保存在当前地址,_end是你设置的bbs段的结束地址*/ //占4字节内存 (去反汇编得到地址为0x33ffb49c,由编译器计算得到)
@中断的堆栈设置
#ifdef CONFIG_USE_IRQ /*是否声明了要使用中断功能(mini2440配置文件在include/configs/mini2440.h)*/
/* 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 start code
*/
start_code:
/*
* set the cpu to SVC32 mode
* 设置管理员( SVC)模式
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr, r0
@ bl coloured_LED_init
@ bl red_LED_on
@针对AT91RM9200进行特殊处理
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
/*
* relocate exception table
*/
ldr r0, =_start
ldr r1, =0x0
mov r2, #16
copyex:
subs r2, r2, #1
ldr r3, [r0], #4
str r3, [r1], #4
bne copyex
#endif
@针对CONFIG_S3C24X0类别的CPU进行配置
#ifdef CONFIG_S3C24X0
/* turn off the watchdog */
@S3C24X0相关寄存器的定义
# if defined(CONFIG_S3C2400) /*s3c2400没有次中断寄存器*/
# define pWTCON 0x15300000 /*pWTCON定义为看门狗控制寄存器的地址*/
# define INTMSK 0x14400008 /* INTMSK定义为主中断屏蔽寄存器的地址 */
# define CLKDIVN 0x14800014 /* CLKDIVN定义为时钟分频控制寄存器的地址 */
#else
# define pWTCON 0x53000000 /*pWTCON定义为看门狗控制寄存器的地址*/
# define INTMSK 0x4A000008 /* INTMSK定义为主中断屏蔽寄存器的地址 */
# define INTSUBMSK 0x4A00001C /*INTSUBMSK定义为次中断屏蔽寄存器的地址*/
# define CLKDIVN 0x4C000014 /* CLKDIVN定义为时钟分频控制寄存器的地址 */
# endif
@至此寄存器地址设置完毕
/*PLL配置相关参数定义*/
#define CLK_CTL_BASE 0x4C000000 /*PLL 锁定时间计数寄存器 tekkaman */
@输出频率为405MHZ的配置
#define MDIV_405 0x7f << 12 /*主分频器控制 tekkaman */
#define PSDIV_405 0x21 /*预分频后分频器控制 tekkaman */
@输出频率为200MHZ的配置
#define MDIV_200 0xa1 << 12 /*主分频器控制 tekkaman */
#define PSDIV_200 0x31 /*预分频后分频器控制 tekkaman */
@关闭看门狗
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
@关中断
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
@关次中断
# if defined(CONFIG_S3C2410)
ldr r1, =0x7ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
#if defined(CONFIG_S3C2440)
ldr r1, =0x7fff
ldr r0, =INTSUBMSK
str r1, [r0]
#endif
#if defined(CONFIG_S3C2440)
/*配置FCLK:HCLK:PCLK的分频比*/
/* FCLK:HCLK:PCLK = 1:4:8 */
ldr r0, =CLKDIVN
mov r1, #5
str r1, [r0]
/*CP15协处理器配置,CP15的C1寄存器各种控制位配置(mmu的协处理器)*/
mrc p15, 0, r1, c1, c0, 0 /*将CP15的寄存器C1的值读到r0中*/
orr r1, r1, #0xc0000000 /*使能MMU或者PU,使能地址对齐检查*/
mcr p15, 0, r1, c1, c0, 0 /*将r0的值写到CP15的寄存器C1中 tekkaman*/
/*配置倍频,pll配置,405MHZ*/
mov r1, #CLK_CTL_BASE /*PLL 锁定时间计数寄存器 tekkaman */
mov r2, #MDIV_405
add r2, r2, #PSDIV_405
str r2, [r1, #0x04] /* MPLLCON tekkaman */
#else
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
/*CP15协处理器配置,CP15的C1寄存器各种控制位配置(具体查看手册:《ARM920TTechnical Reference Manual》 搜索:“ Register 1”)
手册上有这样一段话:
如果 HDIVN 不为 0,CPU 总线模式应该使用以下指令使其从快总线模式改变为异步总线模式(S3C2440
不支持同步总线模式)。
MMU_SetAsyncBusMode
MRC p15, 0, r0, c1, c0, 0
ORR r0, r0, #R1_nF:OR:R1_iA
MCR p15, 0, r0, c1, c0, 0
如果 HDIVN 不为 0 并且 CPU 总线模式为快总线模式,CPU 运行在 HCLK。可以用此特性在不影响 HCLK 和
PCLK 的情况下改变 CPU 频率为一半或更多。
*/
mrc p15, 0, r1, c1, c0, 0 /*将CP15的寄存器C1的值读到r0中*/
orr r1, r1, #0xc0000000 /*其中[31:30]bit是与“FastBus mode/Asynchronous”相关的。如果要切换到“Asynchronous”模式,则“iA=1;nF=1”,所以“#R1_nF:OR:R1_iA”就表示“0xc0000000”。*/
mcr p15, 0, r1, c1, c0, 0 /*将r0的值写到CP15的寄存器C1中 tekkaman*/
/*配置倍频,pll配置,405MHZ*/
mov r1, #CLK_CTL_BASE /* tekkaman*/
mov r2, #MDIV_200
add r2, r2, #PSDIV_200
str r2, [r1, #0x04]
#endif
#endif /* CONFIG_S3C24X0 */
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
/*选择是否初始化CPU */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit /*协处理器配置(mmu、cache、sdram等初始化配置)*/
#endif
/***************** CHECK_CODE_POSITION ******************************************/
/*
bl、b、adr指令的地址域是基于PC的相对偏移寻址,相当于PC+offset
3 FLASH运行
在FLASH运行时候,标号_start存储于0x00000000地址处,而相应的adr r0, _start命令存储于地址0x00000010处。当执行adr r0, _adr命令时,PC等于其存储地址0x00000010,因此r0 = PC + offset = 0x00000010 - 0x10 = 0x00000000。
4 RAM运行
假设已经通过重定位将U-Boot的代码复制到SDRAM 0x33f80000开始的位置,此时_start存储于0x33f80000地址处,adr r0, _ad命令存储于0x33f80010地址处。当在RAM中执行该命令时,PC等于其存储地址0x33f80010,偏移地址offset不变,因此r0 = PC + offset = 0x33f80010 - 0x10 = 0x33f80000。
*/
adr r0, _start /* r0 <- current position of code 读取_start在内存中的地址(通过相对偏移量来计算:pc-(pc与_start的地址差),pc值的两种可能在前面已经做说明)*/
ldr r1, _TEXT_BASE /* test if we run from flash or RAM 起始链接地址*/
cmp r0, r1 /* don't reloc during debug 是否已经把代码拷贝到ram中运行 */
beq stack_setup /*在ram中运行,则直接开始初始化堆栈*/
/***************** CHECK_CODE_POSITION ******************************************
不是NANDFLASH,就意味着是NORFLASH,那么就把所有代码拷贝到SDRAM,即跳到relocate
如果是NANDFLASH的话,那么就是NANDFLASH的初始化,然后也把所有代码拷贝到SDRAM
原理:
norflash启动内存会同时映射到0地址和40000000地址,
因为前面往0x0000003C地址写了0xdeadbeef值,所以通过清空0x4000003C地址,
再判断0x0000003C地址是否也被清空,就可以知道是否是norflash启动。
***************** CHECK_BOOT_FLASH ******************************************/
/*清零0x4000003C地址的内容*/
ldr r1, =( (4<<28)|(3<<4)|(3<<2) ) /* address of Internal SRAM 0x4000003C */
mov r0, #0 /* r0 = 0 */
str r0, [r1]
/*读取0x0000003C地址中的内容*/
mov r1, #0x3c /* address of men 0x0000003C*/
ldr r0, [r1]
cmp r0, #0 /*0x0000003C地址中的数据是否等于0 (norflash启动时Z标志被置1,norflash启动时Z标志置零)*/
bne relocate /*Z标识位为0则,表示是norflash启动,跳转到relocate*/
/*-----------------------------------------------------------------------------------------------------------------------------
/*nandflash代码拷贝*/
判断结果是nandflash启动则往下执行,
把前面被清0的0x4000003C地址的内容(0xdeadbeef)写回去(借来用完了,给人家还回去)*/
/* recovery */
ldr r0, =(0xdeadbeef)
ldr r1, =( (4<<28)|(3<<4)|(3<<2) )
str r0, [r1]
/***************** CHECK_BOOT_FLASH ******************************************
从nand启动代码,定义u-boot在Nand flash中存放的长度为#define LENGTH_UBOOT 0x100000,
可以方便修改u-boot因为裁剪和增添大小的改变而占的长度。 首先检测代码位置,如果在内存中,
就设置堆栈,然后跳到c语言执行,否则就进行初始化nandflash,然后搬运代码
***************** NAND_BOOT *************************************************/
#define LENGTH_UBOOT 0x60000 /*uboot大小*/
#define NAND_CTL_BASE 0x4E000000 /*NAND Flash 相关寄存器起始地址 */
#ifdef CONFIG_S3C2440
/* Offset */
#define oNFCONF 0x00 /*NFCONF寄存器地址偏移量(NAND Flash配置寄存器)*/
#define oNFCONT 0x04 /*NFCONT寄存器地址偏移量(NAND Flash控制寄存器) */
#define oNFCMD 0x08 /*NFCMMD寄存器地址偏移量(NAND Flash命令寄存器) */
#define oNFSTAT 0x20 /*NFSTAT寄存器地址偏移量(NAND Flash状态寄存器) */
@ reset NAND
/*NAND Flash NFCONF寄存器的配置*/
mov r1, #NAND_CTL_BASE
ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )
str r2, [r1, #oNFCONF] /*把r2值写入NFCONF寄存器*/
ldr r2, [r1, #oNFCONF] /*从NFCONF寄存器读值到r2*/
/*NAND Flash NFCONT寄存器的配置*/
ldr r2, =( (1<<4)|(0<<1)|(1<<0) ) @ Active low CE Control
str r2, [r1, #oNFCONT]
ldr r2, [r1, #oNFCONT]
/*NAND Flash NFSTAT寄存器的配置*/
ldr r2, =(0x6) @ RnB Clear
str r2, [r1, #oNFSTAT]
ldr r2, [r1, #oNFSTAT]
/*NAND Flash NFCMMD寄存器的配置*/
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
//下面几句是一个延时程序用来等待几秒,等到NAND 准备好
/*加入延迟*/
mov r3, #0 @ wait
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
/*等到NAND 准备好*/
nand2:
ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x4 //检查NFSTAT寄存器,是否准备好了
beq nand2 //没有准备好则继续等待
ldr r2, [r1, #oNFCONT]
orr r2, r2, #0x2 @ Flash Memory Chip Disable//禁止掉NAND FLASH等到要使用FLASH时再使能
str r2, [r1, #oNFCONT]
/*调用c函数去读取
sp寄存器在任意时刻会保存我们栈顶的地址.
fp寄存器属于通用寄存器,在某些时刻我们利用它保存栈底的地址!
*/
@ get read to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer //每次需要从汇编调到C函数时 都需要设置好堆栈
mov fp, #0 @ no previous frame, so fp=0 fp是R14
/*
之前都是一些初始化,下面才开始利用C函数来真正开始拷贝.
从nandflash拷贝u-boot到ram*/
@ copy U-Boot to RAM
ldr r0, =TEXT_BASE /*传递第一个参数*/
mov r1, #0x0 /*传递第二个参数*/
mov r2, #LENGTH_UBOOT /*传递第三个参数*/
bl nand_read_ll /*调用c函数,函数会去获取上面三个参数*/
/*TST 类似于 CMP,不产生放置到目的寄存器中的结果。而是在给出的两个操作数上进行操作并把结果反映到状态标志上。
使用 TST 来检查是否设置了特定的位。操作数 1 是要测试的数据字而操作数 2 是一个位掩码。经过测试后,如果匹配则设置 Zero 标志,
否则清除它。象 CMP 那样,你不需要指定 S 后缀。 */
tst r0, #0x0 /*nand_read_ll函数返回值是否为0(第0位为0则Z标志被置1)*/
beq ok_nand_read /*读nandflash结束*/
/*拷贝到nandflash失败*/
bad_nand_read:
loop2:
b loop2 @ infinite loop
/*拷贝到nandflash成功*/
ok_nand_read:
@ verify
mov r0, #0
ldr r1, =TEXT_BASE
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes //要比较多少个字节数
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch /*比较失败(拷贝失败),进入死循环*/
subs r2, r2, #4
beq stack_setup /*比较完成,跳转到stack_setup执行栈初始化*/
bne go_next
notmatch:
loop3:
b loop3 @ infinite loop
#endif
/*nandflash代码拷贝结束*/
/*-----------------------------------------------------------------------------------------------------------------------------
#ifdef CONFIG_S3C2410
/* Offset */
#define oNFCONF 0x00
#define oNFCMD 0x04
#define oNFSTAT 0x10
@ reset NAND
mov r1, #NAND_CTL_BASE
ldr r2, =0xf830 @ initial value
str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
bic r2, r2, #0x800 @ enable chip
str r2, [r1, #oNFCONF]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
mov r3, #0 @ wait
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
nand2:
ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x1
beq nand2
ldr r2, [r1, #oNFCONF]
orr r2, r2, #0x800 @ disable chip
str r2, [r1, #oNFCONF]
@ get read to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
@ copy U-Boot to RAM
ldr r0, =TEXT_BASE
mov r1, #0x0
mov r2, #LENGTH_UBOOT
bl nand_read_ll
tst r0, #0x0
beq ok_nand_read
bad_nand_read:
loop2:
b loop2 @ infinite loop
ok_nand_read:
@ verify
mov r0, #0
ldr r1, =TEXT_BASE
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch
subs r2, r2, #4
beq stack_setup
bne go_next
notmatch:
loop3:
b loop3 @ infinite loop
#endif
/***************** NAND_BOOT *************************************************/
/***************** NOR_BOOT *************************************************/
/**norflash启动代码复制 */
relocate: /* relocate U-Boot to RAM */
/*********** CHECK_FOR_MAGIC_NUMBER***************/
ldr r1, =(0xdeadbeef) /**0x0000003c地址重新赋值0xdeadbeef */
cmp r0, r1 /** */
bne loop3 /**进入死循环 */
/*********** CHECK_FOR_MAGIC_NUMBER***************/
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
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
/***************** NOR_BOOT *************************************************/
/* 初始化堆栈 */
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
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
sub sp, r0, #12 /* leave 3 words for abort-stack */
/*得到最终sp的值*/
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
ldr pc, _start_armboot
/**********************************************************************/
* 已经准备好了堆栈,就可跳到C写的代码里了,也就是
* 跳到内存中的/u-boot-1.1.4/board.c --> start_armboot中运行了
* 把_start_armboot地址处的值也就是start_armboot绝对地址值移到pc
* 于是跳到C代码。
start_armboot是U-Boot执行的第一个C语言函数,完成如下工作:
1. 初始化MMU
2.初始化外部端口
3. 中断处理程序表初始化
4. 串口初始化
5. 其它部分初始化(可选)
6. 主程序循环
/*********************************************************************/
#if defined(CONFIG_MINI2440_LED)
#define GPIO_CTL_BASE 0x56000000
#define oGPIO_B 0x10
#define oGPIO_CON 0x0
/* R/W, Configures the pins of the port */
#define oGPIO_DAT 0x4
#define oGPIO_UP 0x8
/* R/W, Pull-up disable register */
mov r1, #GPIO_CTL_BASE
add r1, r1, #oGPIO_B
ldr r2, =0x295551
str r2, [r1, #oGPIO_CON]
mov r2, #0xff
str r2, [r1, #oGPIO_UP]
ldr r2, =0x1c1
str r2, [r1, #oGPIO_DAT]
#endif
_start_armboot: .word start_armboot
#define STACK_BASE 0x33f00000
#define STACK_SIZE 0x10000
.align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
/*
* flush v4 I/D caches
* 初始化CACHES
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
/*使I/D cache失效: 协处理寄存器操作,将r0中的数据写入到协处理器p15的c7中,c7对应cp15的cache控制寄存器*/
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*使TLB操作寄存器失效:将r0数据送到cp15的c8、c7中。C8对应TLB操作寄存器*/
/*
* disable MMU stuff and caches
* 关闭MMU和CACHES
*/
mrc p15, 0, r0, c1, c0, 0 //将c1、c0的值写入到r0中
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 //将设置好的r0值写入到协处理器p15的c1、c0中
@对协处理器的操作请查看《ARM技术手册(ARM920T Technical Reference Manual)》的协处理器部分。
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a lowlevel_init.S in your board directory.
*/
@初始化RAM时钟,因为内存是跟开发板密切相关的,所以这部分在/开发板目录/lowlevel_init.S中实现
mov ip, lr /*ip是R12的别名,这里是保存当前lr,不然lr的值会被下面的bl lowlevel_init跳转给覆盖掉*/
bl lowlevel_init /*在/board/samsung/smdk2410/lowlevel_init.S中,对SDRAM内存时钟相关的设置*/
mov lr, ip /*还原lr*/
mov pc, lr /*函数返回*/
/*
* 对系统总线的初始化,初始化了连接存储器的位宽、速度、刷新率等重要参数
*/
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
/*
*************************************************************************
*
* 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
*/
@bad_save_user_regs 内联(注意:.macro表示编译时插入到调用位置,非调用)代码段,用来保存程序“出错”时用户态的寄存器的值
.macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE @在栈中预留 S_FRAME_SIZE字节(72)的空间,用来保存当前(72/4=18个)寄存器的值
@之所以先保存r0-r12 ,后面会被改变
stmia sp, {r0 - r12} @ Calling r0-r12 @保存r0 - r12到栈中 (stmia递增存储,存储完后sp重新指向原先栈地址)
ldr r2, _armboot_start
sub r2, r2, #(CONFIG_STACKSIZE)
sub r2, r2, #(CONFIG_SYS_MALLOC_LEN)
/* set base 2 words into abort stack */
sub r2, r2, #(CONFIG_SYS_GBL_DATA_SIZE+8) @CFG_GBL_DATA_SIZE是一个全局变量的大小,8:两个字,用来存放发生这些异常时的pc、cpsr寄存器
ldmia r2, {r2 - r3} @ get pc, cpsr @把发生异常时,使用get_bad_stack保存的pc、cpsr寄存器加载到r2,r3
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC @ r0=原来的SP值,就是sp_SVC,即被中断时的SP值
add r5, sp, #S_SP @ S_SP等于52,表示“原来SP + 52”的地方,是保存sp寄存器
mov r1, lr @ r1=lr 被中断时的lr值
@保存sp_SVC, lr_SVC, pc, cpsr 到栈中
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp @r0=sp
.endm
@irq_save_user_regs代码段,用来保存程序“出错”时用户态的寄存器的值
.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE @在栈中预留 S_FRAME_SIZE字节(72)的空间,用来保存当前(72/4=18个)寄存器的值
stmia sp, {r0 - r12} @ Calling r0-r12 @保存r0 - r12到栈中 (stmia递增存储,存储完后sp重新指向原先栈地址)
add r7, sp, #S_PC
stmdb r7, {sp, lr}^ @ Calling SP, LR
str lr, [r7, #0] @ Save calling PC
mrs r6, spsr
str r6, [r7, #4] @ Save CPSR
str r0, [r7, #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
/* return & move spsr_svc into cpsr */
subs pc, lr, #4
.endm
@保存lr,spsr
.macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE)
sub r13, r13, #(CONFIG_SYS_MALLOC_LEN)
/* reserve a couple spots in abort stack */
sub r13, r13, #(CONFIG_SYS_GBL_DATA_SIZE+8) @CFG_GBL_DATA_SIZE是一个全局变量的大小,8:两个字,用来存放发生这些异常时的pc、cpsr寄存器
@把lr寄存器保存到r13这个地址中 (当前r13指向 _armboot_start-CONFIG_STACKSIZE-CONFIG_SYS_MALLOC_LEN-CONFIG_SYS_GBL_DATA_SIZE-8这个地址)
str lr, [r13] @ save caller lr / spsr
@读spsr寄存器,并且把它保存到 _armboot_start-CONFIG_STACKSIZE-CONFIG_SYS_MALLOC_LEN-CONFIG_SYS_GBL_DATA_SIZE-4这个地址
mrs lr, spsr
str lr, [r13, #4]
@CPU切换回到SVC模式(发生中断模式被改变了)
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
mov lr, pc
movs pc, lr @返回
.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
/*
* exception handlers
*/
.align 5
undefined_instruction:
get_bad_stack @保存寄存器pc和cpsr到内存中
bad_save_user_regs @分配栈空间,保存r0-r12寄存器到栈中,读取前面保存的pc和cpsr,并保存到栈中
bl do_undefined_instruction @调用c函数,打印前面保存在栈中的18个寄存器的值
.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:
//Apollo +
/*
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
*/
/* use IRQ for USB and DMA */
sub lr, lr, #4 @ the return address
ldr sp, IRQ_STACK_START @ the stack for irq
stmdb sp!, { r0-r12,lr } @ save registers
ldr lr, =int_return @ set the return addr
ldr pc, =IRQ_Handle @ call the isr
int_return:
ldmia sp!, { r0-r12,pc }^ @ return from interrupt
//Apollo -
.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