u-boot 启动代码start.S详解(for mini2440)
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, cpsrbic r0, r0, #0x1forr r0, r0, #0xd3msr cpsr, r0@ bl coloured_LED_init
@ bl red_LED_on@针对AT91RM9200进行特殊处理
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)/** relocate exception table*/ldr r0, =_startldr r1, =0x0mov r2, #16
copyex:subs r2, r2, #1ldr r3, [r0], #4str r3, [r1], #4bne 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, =pWTCONmov r1, #0x0str r1, [r0]@关中断/** mask all IRQs by setting all bits in the INTMR - default*/mov r1, #0xffffffffldr r0, =INTMSKstr r1, [r0]
@关次中断
# if defined(CONFIG_S3C2410)ldr r1, =0x7ffldr r0, =INTSUBMSKstr r1, [r0]
# endif#if defined(CONFIG_S3C2440)ldr r1, =0x7fff ldr r0, =INTSUBMSKstr r1, [r0]
#endif#if defined(CONFIG_S3C2440)
/*配置FCLK:HCLK:PCLK的分频比*//* FCLK:HCLK:PCLK = 1:4:8 */ldr r0, =CLKDIVNmov r1, #5str 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, =CLKDIVNmov r1, #3str 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_INITbl cpu_init_crit /*协处理器配置(mmu、cache、sdram等初始化配置)*/
#endif/***************** CHECK_CODE_POSITION ******************************************/
/*
bl、b、adr指令的地址域是基于PC的相对偏移寻址,相当于PC+offset3 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_BASEldr 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 Clearstr r2, [r1, #oNFSTAT]ldr r2, [r1, #oNFSTAT]/*NAND Flash NFCMMD寄存器的配置*/mov r2, #0xff @ RESET commandstrb r2, [r1, #oNFCMD]
//下面几句是一个延时程序用来等待几秒,等到NAND 准备好
/*加入延迟*/ mov r3, #0 @ wait
nand1: add r3, r3, #0x1cmp r3, #0xablt nand1/*等到NAND 准备好*/
nand2:ldr r2, [r1, #oNFSTAT] @ wait readytst 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 RAMldr 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:@ verifymov r0, #0ldr r1, =TEXT_BASEmov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes //要比较多少个字节数go_next:ldr r3, [r0], #4ldr r4, [r1], #4teq r3, r4bne notmatch /*比较失败(拷贝失败),进入死循环*/subs r2, r2, #4beq 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 NANDmov r1, #NAND_CTL_BASEldr r2, =0xf830 @ initial valuestr r2, [r1, #oNFCONF]ldr r2, [r1, #oNFCONF]bic r2, r2, #0x800 @ enable chipstr r2, [r1, #oNFCONF]mov r2, #0xff @ RESET commandstrb r2, [r1, #oNFCMD]mov r3, #0 @ wait
nand1:add r3, r3, #0x1cmp r3, #0xablt nand1nand2:ldr r2, [r1, #oNFSTAT] @ wait readytst r2, #0x1beq nand2ldr r2, [r1, #oNFCONF]orr r2, r2, #0x800 @ disable chipstr r2, [r1, #oNFCONF]@ get read to call C functions (for nand_read())ldr sp, DW_STACK_START @ setup stack pointermov fp, #0 @ no previous frame, so fp=0@ copy U-Boot to RAMldr r0, =TEXT_BASEmov r1, #0x0mov r2, #LENGTH_UBOOTbl nand_read_lltst r0, #0x0beq ok_nand_readbad_nand_read:
loop2:b loop2 @ infinite loopok_nand_read:@ verifymov r0, #0ldr r1, =TEXT_BASEmov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:ldr r3, [r0], #4ldr r4, [r1], #4teq r3, r4bne notmatchsubs r2, r2, #4beq stack_setupbne go_nextnotmatch:
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_startldr r3, _bss_startsub 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_IRQsub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endifsub 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, #4cmp r0, r1ble clbss_lldr 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_BASEadd r1, r1, #oGPIO_Bldr r2, =0x295551str r2, [r1, #oGPIO_CON]mov r2, #0xffstr r2, [r1, #oGPIO_UP]ldr r2, =0x1c1str 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, #0mcr 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) Alignorr r0, r0, #0x00001000 @ set bit 12 (I) I-Cachemcr 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_regssub 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,r3add 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_regssub 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_PCstmdb r7, {sp, lr}^ @ Calling SP, LRstr lr, [r7, #0] @ Save calling PCmrs r6, spsrstr r6, [r7, #4] @ Save CPSRstr r0, [r7, #8] @ Save OLD_R0mov r0, sp.endm.macro irq_restore_user_regsldmia sp, {r0 - lr}^ @ Calling r0 - lrmov r0, r0ldr lr, [sp, #S_PC] @ Get PCadd sp, sp, #S_FRAME_SIZE/* return & move spsr_svc into cpsr */subs pc, lr, #4.endm
@保存lr,spsr.macro get_bad_stackldr r13, _armboot_start @ setup our mode stacksub 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, spsrstr lr, [r13, #4]@CPU切换回到SVC模式(发生中断模式被改变了)mov r13, #MODE_SVC @ prepare SVC-Mode@ msr spsr_c, r13msr spsr, r13mov lr, pcmovs pc, lr @返回.endm.macro get_irq_stack @ setup IRQ stackldr sp, IRQ_STACK_START.endm.macro get_fiq_stack @ setup FIQ stackldr 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_stackbad_save_user_regsbl do_software_interrupt.align 5
prefetch_abort:get_bad_stackbad_save_user_regsbl do_prefetch_abort.align 5
data_abort:get_bad_stackbad_save_user_regsbl do_data_abort.align 5
not_used:get_bad_stackbad_save_user_regsbl do_not_used#ifdef CONFIG_USE_IRQ.align 5
irq:
//Apollo +
/*get_irq_stackirq_save_user_regsbl do_irqirq_restore_user_regs
*//* use IRQ for USB and DMA */sub lr, lr, #4 @ the return addressldr sp, IRQ_STACK_START @ the stack for irqstmdb sp!, { r0-r12,lr } @ save registersldr lr, =int_return @ set the return addrldr 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_regsbl do_fiqirq_restore_user_regs#else.align 5
irq:get_bad_stackbad_save_user_regsbl do_irq.align 5
fiq:get_bad_stackbad_save_user_regsbl do_fiq#endif
u-boot 启动代码start.S详解(for mini2440)相关推荐
- u-boot启动代码start.S详解360
(1)定义入口.由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROM(Flash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本 ...
- [PXE] Linux(centos6)中PXE 服务器搭建,PXE安装、启动及PXE理论详解
本篇blog主要讲述了[PXE] linux(centos)PXE无盘服务器搭建,安装,启动及pxe协议详解 , Kickstart (PXE+DHCP+TFTP+HTTP). PXE环境概述 作为中 ...
- Linux_arm_启动_c语言部分详解,[原创]Linux arm 启动 c语言部分详解第四讲
Linux arm启动c语言部分详解第四讲(from setup_per_cpu_areas();) Written by leeming 上面的setup_arch花了我们大量的篇幅,现在我们要继续 ...
- Spring Boot 2.0 的配置详解(图文教程)
本文来自作者 泥瓦匠 @ bysocket.com 在 GitChat 上分享 「Spring Boot 2.0 的配置详解(图文教程)」 编辑 | 哈比 Spring Boot 配置,包括自动配置和 ...
- 怎么打开python shell_Python之使用adb shell命令启动应用的方法详解
一直有一个心愿希望可以用Python做安卓自动化功能测试,在一步步摸索中,之前是用monkeyrunner,但是发现对于控件ID的使用非常具有局限性,尤其是ID的内容不便于区分 具有重复性时,后面又发 ...
- python调用adb shell命令_Python之使用adb shell命令启动应用的方法详解
Python之使用adb shell命令启动应用的方法详解 一直有一个心愿希望可以用Python做安卓自动化功能测试,在一步步摸索中,之前是用monkeyrunner,但是发现对于控件ID的使用非常具 ...
- Tomcat 项目代码上线步骤详解
Tomcat 项目代码上线步骤详解 1.上线内容(JSP代码,图片,包文件(jar|war|ear)) 2.上线内容来源 a.开发人员提供(邮件形式).tar.gz 压缩包(包文件开发负责编译 编译命 ...
- spring boot(四):thymeleaf使用详解
spring boot(四):thymeleaf使用详解 在上篇文章springboot(二):web综合开发中简单介绍了一下thymeleaf,这篇文章将更加全面详细的介绍thymeleaf的使用. ...
- Spring Boot 使用 Druid 连接池详解
Spring Boot 使用 Druid 连接池详解 Alibaba Druid 是一个 JDBC 组件库,包含数据库连接池.SQL Parser 等组件,被大量业务和技术产品使用或集成,经历过严苛的 ...
最新文章
- 深度学习之反向传播算法
- 什么时候用activity什么时候用fragment
- Mac安装PhoneGap3
- 【Linux学习】Linux系统管理2—作业调度
- 6700设置上网教程
- nyoj-976-Youth的最大化(贪心+二分)
- Django 如何实现 如下 联表 JOIN 查询?
- 1.3.3 错题整理(组成原理)
- 【LeetCode】LeetCode之跳跃游戏——动态规划+贪心算法
- 校园导游java版,校园导游系统Word版
- android mqtt详解_Android mqtt入门 Android studio(转)
- 怎样对流媒体进行压力测试_四合一气体检测仪怎样进行气体测试?
- 未名 - markdown
- Struts2 面试题
- Qt_屏幕保护程序、进程监听、数据库读取、屏幕保护
- 小学计算机课教师教学笔记,小学信息技术教师教育随笔
- unity暂停和继续按钮
- 什么是link标签?
- k8s学习-网络策略NetworkPolicy(概念、模版、创建、删除等)
- 查看自己本地IP地址方法