分析 u-boot 的第一阶段代码(cpu/arm920t/start.S)
发布时间:2020-12-15 18:48:32 所属栏目:百科 来源:网络整理
导读:uboot中start.S详解(zt) ? #include config.h#include version.h/* 这段代码的主要作用是初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境 */#if defined(CONFIG_OMAP1610)#
uboot中start.S详解(zt)
#include <config.h> #include <version.h> /* 这段代码的主要作用是初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境 */ #if defined(CONFIG_OMAP1610) #include <./configs/omap1510.h> #elif defined(CONFIG_OMAP730) #include <./configs/omap730.h> #endif /************************************************************************* * * Jump vector table as in table 3.1 in [1] * *************************************************************************/ .globl _start _start: /* 系统复位位置,各个异常向量对应的跳转代码 */ b reset /* 复位向量 */ 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 .balignl 16,0xdeadbeef /************************************************分析**************************************************** * 从中我们可以看出,ARM支持7种异常。问题是发生了异常后ARM是如何响应的呢? * 第一个复位异常很好理解,它放在0x0的位置,一上电就执行它,而且我们的程序总是从 * 复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。那么什么时候会执行 * 到后面几个异常处理函数呢?步骤是这样的: * * 当一个异常出现以后,ARM会自动执行以下几个步骤: * (1) 把下一条指令的地址放到连接寄存器LR(通常是R14),这样就能够在处理异常返回时从正确的位置继续执行。 * (2) 将相应的CPSR(当前程序状态寄存器)复制到SPSR(备份的程序状态寄存器)中。从异常退出的时候,就可以由SPSR来恢复CPSR。 * (3) 根据异常类型,强制设置CPSR的运行模式位。 * (4) PC(程序计数器)被强制成相关异常向量处理函数地址,从而跳转到相应的异常处理程序中。 * * 当异常处理完毕后,ARM会执行以下几步操作从异常返回: * (1) 将连接寄存器LR的值减去相应的偏移量后送到PC中 * (2) 将SPSR复制回CPSR中 * (3) 若在进入异常处理时设置了中断禁止位,要在此清除 * * ARM规定了异常向量的地址: * b reset ; 复位 0x0 * ldr pc,_undefined_instruction ; 未定义的指令异常 0x4 * ldr pc,_software_interrupt ; 软件中断异常 0x8 * ldr pc,_prefetch_abort ; 预取指令 0xc * ldr pc,_data_abort ; 数据 0x10 * ldr pc,_not_used ; 未使用 0x14 * ldr pc,_irq ; 慢速中断异常 0x18 * ldr pc,_fiq ; 快速中断异常 0x1c * 这样理解这段代码就非常简单了。碰到异常时,PC会被强制设置为对应的异常向量,从而跳转到 * 相应的处理程序,然后再返回到主程序继续执行。 * * .balignl 16,0xdeadbeef,将地址对齐到16的倍数,如果地址寄存器的值(PC)跳过4个字节才是16的倍数, * 则使用0xdeadbeef填充这4个字节,如果它跳过1、2、3个字节,则填充值不确定。如果地址寄存器的值(PC) * 是16的倍数,则无需移动。 ********************************************************************************************************/ /************************************************************************* * * 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 /*************************************************分析********************************************************** * 上面这段代码,主要保存一些全局变量,用于BOOT程序从FLASH拷贝到RAM,或者其它的使用。 * 还有一些变量的值是通过连接脚本得到的,比如TEXT_BASE位于/u-boot-1.1.6/board/xxx(开发板目录名称)/config.mk * 文件里。__bss_start、_end位于/u-boot-1.1.6/board/xxx(开发板目录名称)/u-boot.lds文件里,具体值是由编译器算出来的。 ***************************************************************************************************************/ /* the actual reset code*/ /* 系统的复位代码。系统一上电,就跳到这里运行 */ reset: /* * set the cpu to SVC32 mode */ mrs r0,cpsr /* 取得当前程序状态寄存器cpsr到r0 */ bic r0,r0,#0x1f /* 这里使用位清除指令,把中断全部清除,只置位模式控制位 为中断提供服务通常是 OS *设备驱动程序的责任,因此在 Boot Loader 的执行全过程中可以不必响应任何中断。 */ orr r0,#0xd3 /* 计算为超级保护模式 */ msr cpsr,r0 /* 设置cpsr为超级保护模式 */ /***********************************************分析************************************************************* * 设置cpu运行在SVC32模式。ARM共有7种模式: * 用户模式(usr): arm处理器正常的程序执行状态 * 快速中断模式(fiq): 用于高速数据传输或通道处理 * 外部中断模式(irq): 用于通用的中断处理 * 超级保护模式(svc): 操作系统使用的保护模式 * 数据访问终止模式(abt): 当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护 * 系统模式(sys): 运行具有特权的操作系统任务 * 未定义指令中止模式(und): 当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真 * * 通过设置ARM的CPSR寄存器,让CPU运行在操作系统保护模式,为后面进行其它操作作好准备了。 ****************************************************************************************************************/ /* * we do sys-critical inits only at reboot,* not when booting from ram! */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit /****************************************************************************** * BL为相对寻址,以程序计数器PC 的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址 * ARM 指令集中的4条跳转指令可以完成从当前指令向前或向后的32MB 的地址空间的跳转, * 用的是相对寻址,它们是:B、BL、BLX、BX *******************************************************************************/ #endif #ifndef CONFIG_SKIP_RELOCATE_UBOOT /* 重定位Boot代码到RAM内存,将Boot代码从FLASH移到RAM中 */ relocate: /* relocate U-Boot to RAM */ adr r0,_start /* r0 <- current position of code */ /************************************************************************** * 把_start的相对地址移到r0,相对寻址以程序计数器PC 的当前值为基地址, * 指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。 * 它是与位置无关的,主要看Boot在哪里运行,也就是PC指针在哪里 (假设_start偏移量为0), * 例如这段代码在 0x02000000 (FLASH起始地址)运行,即此时PC=0x02000000,那么 adr r0,_start 得到 r0 = 0x02000000; * 如果在地址 0x81008000(Boot在RAM中加载地址)运行,即此时PC=0x81008000,那么r0就是 0x81008000 了。 * * 此处要注意ldr与adr的区别,看下面的代码片段: * ldr r0,_start * adr r0,_start * ldr r0,=_start * nop * mov pc,lr * _start: * nop * 下面是反汇编的结果: * 0c008000 <_start-0x14>: * c008000: e59f000c ldr r0,[pc,#12] ; c008014 <_start> * c008004: e28f0008 add r0,pc,#8 ; 0x8 * c008008: e59f0008 ldr r0,#8] ; c008018 <_start+0x4> * c00800c: e1a00000 nop (mov r0,r0) * c008010: e1a0f00e mov pc,lr * * 0c008014 <_start>: * c008014: e1a00000 nop (mov r0,r0) * * 分析: * ldr r0,_start * 从内存地址 _start 的地方把值读入。执行这个后,r0 = 0xe1a00000 * * adr r0,_start * 取得 _start 的地址到 r0,但是请看反编译的结果,它是与位置无关的。其实取得的是相对的位置。例如这段代码在 0x0c008000 运行, * 那么 adr r0,_start 得到 r0 = 0x0c008014;如果在地址 0 运行,就是 0x00000014 了。即当前PC值加上_start的偏移量。 * * ldr r0,=_start * 这个取得标号 _start 的绝对地址。这个绝对地址是在 link 的时候确定的。看上去这只是一个指令,但是它要占用 2 个 32bit 的空间, * 一条是指令,另一条是 _start 的数据(因为在编译的时候不能确定 _start 的值,所以不能直接用 mov 指令来给 r0 赋一个 32bit 的常量, * 所以需要多出一个空间存放 _start 的真正数据,这个数据是在 link 的时候确定的,在这里就是 0x0c008014)。 * 因此可以看出,这个是绝对的寻址,不管这段代码在什么地方运行,它的结果都是 r0 = 0x0c008014 **************************************************************************/ ldr r1,_TEXT_BASE/* test if we run from flash or RAM */ /* 把_TEXT_BASE地址处的值TEXT_BASE,也就是BOOT在RAM中运行地址移到r1 */ cmp r0,r1 /* don't reloc during debug */ /* 比较两个地址是否相同,如果相同,就已经在RAM运行,否则就是FLASH中运行 */ beq stack_setup /* 如果是在FLASH中运行, 则把FLASH中的Boot代码移到RAM中,然后再运行 */ ldr r2,_armboot_start /* 把_armboot_start地址处的值也就是_start绝对地址(也即在内存中的地址,这个绝对 * 地址是在 link 的时候确定的,如0x81008000)移到r2 */ ldr r3,_bss_start /* 把_bss_start地址处的值也就是__bss_start绝对地址(也即在内存中的地址,这个绝对 * 地址是在 link 的时候确定的)移到r3 */ sub r2,r3,r2 /* r2 <- size of armboot */ /* 计算引导代码大小并存到r2 */ add r2,r2 /* r2 <- source end address */ /* 计算引导代码最后相对地址并存入r2 */ copy_loop: ldmia r0!,{r3-r10} /* copy from source address [r0] */ /* 从源地址[r0]读取32个字节到寄存器,并更新r0 */ stmia r1!,{r3-r10} /* copy to target address [r1] */ /* 拷贝寄存器r3-r10的32个字节值保存到 [r1]指明的地址,并更新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 r0,#CFG_MALLOC_LEN /* malloc area */ sub r0,#CFG_GBL_DATA_SIZE /* bdinfo */ #ifdef CONFIG_USE_IRQ sub r0,#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif sub sp,#12 /* leave 3 words for abort-stack */ /* 初始化内存中bss段中数据为0 */ clear_bss: ldr r0,_bss_start /* find start of bss segment*/ /* 把_bss_start地址处存储的绝对地址移到r0 */ ldr r1,_bss_end /* stop here */ /* 把_bss_end地址处存储的绝对地址移到r1 */ mov r2,#0x00000000 /* clear */ clbss_l: str r2,[r0] /* clear loop... STR 指令用于从源寄存器中r2将一个32 位的字数据传送到存储器中[r0]*/ add r0,#4 cmp r0,r1 ble clbss_l /* 小于或等于跳转 */ ldr pc,_start_armboot /*********************************************************** * 已经准备好了堆栈,就可跳到C写的代码里了,也就是 * 跳到内存中的/u-boot-1.1.6/board.c --> start_armboot中运行了 * 把_start_armboot地址处的值也就是start_armboot绝对地址值移到pc * 神啊!终于跳到C代码了。 ***********************************************************/ _start_armboot: .word start_armboot /************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * *************************************************************************/ /************************************************************************** * 1、关闭 MMU和CPU 内部指令/数据 (I/D)cache。 * 2、设置 CPU 的速度和时钟频率。 * 3 、RAM 初始化。 ****************************************************************************/ cpu_init_crit: /* flush v4 I/D caches*/ mov r0,#0 mcr p15,c7,0 /* flush v3/v4 cache */ /****************************************************************************************************** * MCR 指令用于将ARM 处理器寄存器中的数据传送到协处理器寄存器中,格式为: * MCR 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2。 * 其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的操作, * 源寄存器为ARM 处理器的寄存器,目的寄存器1 和目的寄存器2 均为协处理器的寄存器。 ******************************************************************************************************/ mcr p15,c8,0 /* flush v4 TLB */ / * disable MMU stuff and caches*/ mrc p15,c1,c0,0 bic r0,#0x00002300 /* clear bits 13,9:8 (--V- --RS) */ bic r0,#0x00000087 /* clear bits 7,2:0 (B--- -CAM) */ orr r0,#0x00000002 /* set bit 2 (A) Align */ orr r0,#0x00001000 /* set bit 12 (I) I-Cache */ mcr p15,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 */ /* 位于u-boot-1.1.6/board/xxx(开发板目录名称)/lowlevel_init.S */ mov lr,ip /* restore link */ mov pc,lr /* back to my caller */ /* 从cpu_init_crit子函数返回 */ /************************************************************************* * * 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 stmia sp,{r0 - r12} @ Save user registers (now in svc mode) r0-r12 ldr r2,_armboot_start sub r2,r2,#(CONFIG_STACKSIZE+CFG_MALLOC_LEN) sub r2,#(CFG_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,#S_FRAME_SIZE @ grab pointer to old stack add r5,#S_SP mov r1,lr stmia r5,{r0 - r3} @ save sp_SVC,lr_SVC,cpsr mov r0,sp @ save current stack into r0 (param register) .endm .macro irq_save_user_regs sub sp,{r0 - r12} @ Calling r0-r12 @ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good. add r8,#S_PC stmdb r8,{sp,lr}^ @ Calling SP,LR str lr,[r8,#0] @ Save calling PC mrs r6,spsr str r6,#4] @ Save CPSR str r0,#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,#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+CFG_MALLOC_LEN) sub r13,#(CFG_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 /* * exception handlers */ .align 5 /*‘.align 5’向后移动位置计数器直至32(2^5)的倍数(计数器的最低的5位为0)。如果地址已经是32倍数,则无需移动。*/ 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
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |