完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
3个回答
|
|
start.s文件是U-Boot启动最先执行的代码,对start.s 文件的正确理解是整个U-Boot源码理解的开端和基础。要理解start.S 文件,需要具备一些预备知识,下面先介绍这些预备知识。
一、start.s预备知识 1.start.s中的汇编指令 1.1 .globl 汇编程序中以.开头的名称并不是指令的助记符,不会被翻译成机器指令,而是给汇编器一些特殊指示,称为汇编指示(Assembler Directive)或伪操作(Pseudo-operation),由于它不是真正的指令所以加个“伪”字。.globl伪指令的用法是: .globl xxx .globl xxx的作用是将xxx符号声明为外部程序可访问的标签,也就是说xxx符号可以被其他.c .s文件使用。 1.2 .word .word伪指令的用法是: .word xxx .word xxx的作用就是在当前位置放一个word型的值,这个值就是xxx。举例来说: _rWTCON: .word 0x15300000 上面两句的意思就是在当前地址,即_rWTCON处放一个值0x15300000。 1.3 .balignl .balignl是.balign的变体,.balign的意思是,在以当前地址开始,地址计数器必须是以第一个参数为整数倍的地址为结尾,并且在前面填入一个字节长度的信息,信息内容为第二个参数。举例来说: .balign 8, 0xde 上面这条语句的意思是,在以当前地址开始,在地址为8的倍数的位置的前面填入一个字节内容为0xde的内容。如果当前地址正好是8的倍数,则没有东西被写入到内存。 那么以此类推,.balignw则表示第二个参数填入的内容长度为一个字的长度,即16位,所以一般有这样的形式出现: .balignw 4,0x368d 以此类推,.balignl这个指令用来填写一个双字,即内容长度为4个字节。举例来说: .balignl 16,0xdeadbeef 上面这条语句的意思是,在以当前地址开始,在地址为16的倍数的位置的前面填入4个字节长度的内容,填入的内容为0xdeadbeef。 1.4 .align .align伪指令的用法是: .align n .align n 它的含义就是使得下面的代码按一定规则对齐。.align n 指令的对齐值有两种方案:n 或 2^n 。各种平台最初的汇编器一般都不是gas ,采取方案1 或2 的都很多,gas 的目标是取代原来的汇编器,必然要保持和原来汇编器的兼容,因此在gas 中如何解释.align 指令会显得有些混乱,原因在于保持兼容。arm-linux 是按照 2^n 的方案对齐的,需要说明的是这个对齐和ld-script 里的对齐不同,不是一会事。下面的英文就不同平台的对齐进行了说明:版本2.11.92.0.12 的gas 的info(Mandrake 8.2 上的) 这样说: The way the required alignment is specified varies from system to system. For the a29k, hppa, m68k, m88k, w65, sparc, and Hitachi SH, and i386 using ELF format, the first expression is the alignment request in bytes. For example .align 8 advances the location counter until it is a multiple of 8. If the location counter is already a multiple of 8, no change is needed. For other systems, including the i386 using a.out format, and the arm and strongarm, it is the number of low-order zero bits the location counter must have after advancement. For example `.align 3' advances the location counter until it a multiple of 8. If the location counter is already a multiple of 8, no change is needed. 从这段文字来看,ARM 的 .align 5就是 2 的 5次方对齐,也就是 4 字节对齐,通过反汇编也可以看出对齐方式。 1.5 .macro ... .endm .macro ... .endm伪指令的作用相当于c语言的#define,即宏定义。 1.6 b和bl b指令是ARM分支指令,实现程序的跳转。其语法是: b{ 地址label是以一个有符号的相对于pc的偏移量保存在指令中,且必须被限制在分支指令的约32MB范围内。 大多数汇编语言通过使用地址标号来隐藏分支指令编码的细节。地址标号放在一行的开始处,汇编器会记录该行指令的地址,用于计算跳转的偏移量。 bl指令和b指令类似,区别在于bl指令是带返回的跳转,它会把bl后面的的第一条指令地址赋值给lr寄存器。 1.7 ldr和str ldr用于把存储器里的一个32位数据传入至一个处理器寄存器。其语法是: ldr reg, address 其功能是把存储器地址address处的一个32位数据传入至处理器寄存器reg。 str用于把一个32位数据存储至指定地址。其语法是: str reg, address 其功能是把reg的值存储至address地址处。 1.8 mrs和msr mrs指令用于把状态寄存器(cpsr或spsr)的值传送到通用寄存器。指令语法: mrs rd, cpsr|spsr msr指令用于把通用寄存器的值传送到状态寄存器(cpsr或spsr)。指令语法: mrs cpsr|spsr, rm 1.9 bic bic指令用于实现逻辑位清除。指令语法: bic rd, rn, n 其功能相当于rd = rn & (~n)。 1.10 orr orr指令用于实现32位逻辑或。指令语法: bic rd, rn, n 其功能相当于rd = rn | n。 1.11 mrc和mcr mrc和mcr是协处理器传送指令。mrc指令把协处理器寄存器的值传送至通用寄存器。mcr指令把通用寄存器的值传送至协处理器寄存器。指令语法: mrc|mcr cp, opcode1, rd, cn, cm, {, opcode2} 在协处理器指令语法中,cp代表协处理器编号,为p0~p15。opcode描述要在协处理器中执行的操作。cn、cm描述在协处理器中的寄存器(cn一般是主寄存器,cm是辅寄存器)。协处理器的操作和寄存器依赖于具体使用的协处理器。协处理器15(cp15)是为系统控制预留的,如内存管理、写缓冲控制、cache控制及寄存器识别等。 1.12 move move指令把一个32位数送到一个寄存器。指令语法: move rd, n n是32为数,rd是寄存器,指令结果相当于rd = n。 1.13 adr adr是一条伪指令,它把一个相对地址写入寄存器中,它将会使用一个包括pc相对地址的表达式进行编码。指令语法: adr rd, label rd是寄存器,label是地址标号。adr指令以程序计数器pc的基值与label偏移量相加得到的和赋给rd。比如start.s的第149行: adr r0, _start 该语句的意思是,如果这段代码在 0x02000000 (FLASH起始地址)运行,即此时pc基值=0x02000000,_start偏移量等于0,那么adr r0, _start 得到 r0 = 0x02000000。如果这段代码在地址 0x81008000(Boot在RAM中加载地址)运行,即此时pc基值=0x81008000,_start偏移量等于0,那么r0就是 0x81008000 了。 1.14 ldm和stm指令 ldm指令从目标地址装载多个数据至多个寄存器。stm指令把多个寄存器的值存储至目标地址。指令语法: ldm|stm rd{!}, regs ldm指令从寄存器rd里的地址装载多个数据至多个寄存器regs(比如r3-r10,共8个寄存器)。 ldm和stm指令有多种不同的寻址模式,分别是ia(执行后增加)、ib(执行前增加)、da(执行后减少)、db(执行前减少)。比如: ldmia r0!, {r3-r10} stmia r1!, {r3-r10} 第一条指令表示从r0地址装载完4个字节数据至r3后,地址自动增加4,然后从新地址装载4个字节数据至r4后,以此类推。 第2条指令表示把r3的值存储至r1地址后,地址自动增加4,然后把r4的值存储至新地址,以此类推。 1.15 cmp cmp指令是比较指令,指令语法: cmp rn, n cmp指令根据rn减n的值设置cpsr的z标志位。如果rn等于n,则cpsr的z标志位为1。如果rn不等于n,则cpsr的z标志位为0。 在设置cpsr的z标志位后,其他指令可通过条件执行来改变程序的执行流程。比如ble指令,如果cpsr的z标志位为1,则程序跳转。 1.16 add和sub add指令是32位加法指令,指令语法: add rd, rn, n 其相当于rd = rn + n。 sub指令是32位减法指令,指令语法: sub rd, rn, n 其相当于rd = rn - n。 2. u-boot.lds 对于u-boot.lds文件,它定义了整个程序编译之后的链接过程,决定了一个可执行程序的各个段的存储位置。 先看一下GNU官方网站上对.lds文件形式的完整描述: SECTIONS { ... secname start BLOCK(align) (NOLOAD) : AT ( ldadr ) { contents } >region :phdr =fill ... } secname和 contents是必须的,其他的都是可选的。下面介绍几个常用的: 1)secname secname表示段名 2)contents 决定哪些内容放在本段,可以是整个目标文件,也可以是目标文件中的某段(bss段、data段、rodata段、text段)。下面简单介绍下 bss段、data段、rodata段、text段: bss段 (bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。bss是英文Block Started by Symbol的简称。BSS段属于静态内存分配。该段用于存储未初始化的全局变量或者是默认初始化为0的全局变量,它不占用程序文件的大小,但是占用程序运行时的内存空间。 data段用于存储初始化的全局变量,初始化为0的全局变量出于编译优化的策略还是被保存在BSS段。 rodata段也叫常量区,用于存放常量数据。 text段是用于存放程序代码的,编译时确定只读。更进一步讲是存放处理器的机器指令,当各个源文件单独编译之后生成目标文件,经连接器链接各个目标文件并解决各个源文件之间函数的引用,与此同时,还得将所有目标文件中的.text段合在一起,但不是简单的将它们“堆”在一起就完事,还需要处理各个段之间的函数引用问题。 3)start 本段链接(运行)的地址,如果没有使用AT(ldadr),本段存储的地址也是start。GNU网站上说start可以用任意一种描述地址的符号来描述。 4)AT(ldadr) 定义本段存储(加载)的地址。 下面看下freescale的mx28系列的u-boot.lds文件: OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { . = 0x00000000; . = ALIGN(4); .text : { cpu/arm926ejs/start.o (.text) *(.text) } .rodata : { *(.rodata) } . = ALIGN(4); .data : { *(.data) } . = ALIGN(4); .got : { *(.got) } . = .; __u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .; . = ALIGN(4); __bss_start = .; .bss (NOLOAD) : { *(.bss) } _end = .; } 第1行:指定输出可执行文件是elf格式,32位ARM指令,小端。 第2行:指定输出可执行文件的平台为ARM。 第3行:指定输出可执行文件的起始代码段为_start。 第6行:指定整个u-boot程序从地址0x00000000开始链接。 第7行:指定代码以4字节对齐。 第8-12行:表示该段的名字为.text,第10行指定start.s文件中的text段位于地址0x00000000,u-boot程序的其他text段位于其后面。 第13行:表示该段的名字为.rodata,指定u-boot程序的rodata段位于text段后面。 第15行:表示该段的名字为.data,指定u-boot程序的data段位于rodata段后面。 第17行:表示该段的名字为.got,指定u-boot程序的got段位于data段后面。got段是uboot自定义的一个段, 非标准段。 第20行:表示把当前地址值赋给全局变量__u_boot_cmd_start(__u_boot_cmd_start 定义在其他文件里) 。 第21行:表示该段的名字为.u_boot_cmd ,指定u-boot程序的uboot命令位于got段后面。 第22行:表示把当前地址值赋给全局变量__u_boot_cmd_end(__u_boot_cmd_end定义在其他文件里) 。 第25行:表示把当前地址(即bss段的开始位置)赋给全局变量__bss_start (__bss_start 在start.s中被使用) 。 第26行:表示该段的名字为.bss,指定u-boot程序的bss段位于.u_boot_cmd段后面。 第27行:表示把当前地址(即bss段的结束位置)赋给全局变量_end (_end在start.s中被使用) 。 从mx28系列的u-boot.lds文件可以知道生成的u-boot映像的地址分布如下图所示: |
|
|
|
二、start.s源码分析
***出cpu/arm926ejs/start.s的源码: /* * armboot - Startup Code for ARM926EJS CPU-core * * Copyright (c) 2003 Texas Instruments * * ----- Adapted for OMAP1610 OMAP730 from ARM925t code ------ * * Copyright (c) 2001 Marius Gröger * Copyright (c) 2002 Alex Züpke * Copyright (c) 2002 Gary Jennejohn * Copyright (c) 2003 Richard Woodruff * Copyright (c) 2003 Kshitij * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include #include #include #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 /* ************************************************************************* * * 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 */ .globl reset reset: /* * set the cpu to SVC32 mode */ 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 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 */ 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 bl coloured_LED_init bl red_LED_on ldr pc, _start_armboot _start_armboot: .word start_armboot /* ************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * ************************************************************************* */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT cpu_init_crit: /* * flush v4 I/D caches */ 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 */ 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 */ /* ************************************************************************* * * 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+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 /* * exception handlers */ .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 |
|
|
|
第53行:.globl指示告诉汇编器,_start这个符号要被链接器用到,所以要在目标文件的符号表中标记它是一个全局符号。_start就像C程序的main函数一样特殊,是GNU汇编器默认的整个程序的入口,链接器在链接时会查找目标文件中的_start符号代表的地址,把它设置为整个程序的入口地址,所以每个汇编程序都要提供一个_start符号并且用.globl声明。如果一个符号没有用.globl声明,就表示这个符号不会被链接器用到。
第54行:定义了一个名为_start的地址标号,这个地址标号代表的地址为第55行指令(b reset)的地址。 第55-62行:arm体系结构的向量表。当一个异常或中断发生时,处理器会把pc设置为一个特定的存储器地址。这个地址放在一个被称为向量表的特定的地址范围内。向量表的入口是一些跳转指令,跳转到专门处理某个异常或中断的子程序(复位、未定义指令、软件中断、预取指中止、数据中止、保留、中断请求、快速中断请求)。 第55行:是一条相对跳转指令,表示跳转到reset(复位异常处理程序)处。 第56行:表示把_undefined_instruction地址处的值装入pc。根据前面.word介绍可以知道_undefined_instruction地址处存储的值是undefined_instruction地址标号代表的地址。所以第56行实现了程序跳转至undefined_instruction(未定义指令异常处理程序)的功能。 第57行:表示程序跳转至software_interrupt(软件中断异常处理程序)。 第58行:表示程序跳转至prefetch_abort(预取指中止异常处理程序)。 第59行:表示程序跳转至data_abort(数据中止异常处理程序)。 第60行:表示程序跳转至not_used(保留异常处理程序)。 第61行:表示程序跳转至irq(irq中断处理程序)。 第62行:表示程序跳转至fiq(fiq中断处理程序)。 第64-65行:定义了名为_undefined_instruction的地址标号,在这个地址标号处存放了值undefined_instruction,即未定义指令异常处理程序首地址。 第66-67行:定义了名为_software_interrupt的地址标号,在这个地址标号处存放了值software_interrupt,即软件中断异常处理程序首地址。 第68-69行:定义了名为_prefetch_abort的地址标号,在这个地址标号处存放了值prefetch_abort,即预取指中止异常处理程序首地址。 第70-71行:定义了名为_data_abort的地址标号,在这个地址标号处存放了值data_abort,即数据中止异常处理程序首地址。 第72-73行:定义了名为_not_used的地址标号,在这个地址标号处存放了值not_used,即保留异常处理程序首地址。 第74-75行:定义了名为_irq的地址标号,在这个地址标号处存放了值irq,即irq中断处理程序首地址。 第76-77行:定义了名为_fiq的地址标号,在这个地址标号处存放了值fiq,即fiq中断处理程序首地址。 第79行:实现了在_start偏移60-63字节地址处(54行-77行的地址范围是_start至_start+59)存放一个值为0xdeadbeef数据的目的。0xdeadbeef的作用大概就是为内存做标记,有点儿像个小旗子,插在那里,表示从这个位置往后,就是干什么的内存,这个位置往前,禁止访问。 第95行:定义了名为_TEXT_BASE的地址标号,在这个地址标号处存放了值TEXT_BASE。TEXT_BASE定义在board目录下对应开发板的config.mk文件中。TEXT_BASE指定了生成u-boot映像的链接地址,即最终的运行地址(RAM),该地址分布情况可以在u-boot.map文件中查看。特别说明:虽然u-boot.lds文件也定义了u-boot映像的地址分布,但是因为顶层的config.mk中的-Ttext $(TEXT_BASE)命令(LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS))的使用,链接器把UBOOT从地址TEXT_BASE开始连接,至于链接脚本u-boot.lds,其主要作用是用来指明各个*.o文件的顺序,如入口地址标号(_start)等,以及使两个地址标号得到当前的地址,比如: __u_boot_cmd_start = .; *.u_boot_cmd段的起始地址 .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .; *.u_boot_cmd段的结束地址 以供C程序使用。 __u_boot_cmd_start和__u_boot_cmd_end可以作为全局的一个常数使用。 第98行:把_armboot_start符号声明为全局符号。因为_armboot_start要被lib_arm/board.c中的start_armboot函数用到。 第99-100行:定义了名为_armboot_start的地址标号,在这个地址标号处存放了值_start,即u-boot映像的基地址。 第105行:把_bss_start符号声明为全局符号。因为_bss_start要被lib_arm/board.c中的start_armboot函数用到。 第106-107行:定义了名为_bss_start的地址标号,在这个地址标号处存放了值__bss_start,__bss_start定义在u-boot.lds中。 第109行:把_bss_end符号声明为全局符号。因为_bss_end要被lib_arm/board.c中的start_armboot函数用到。 第110-111行:定义了名为_bss_end的地址标号,在这个地址标号处存放了值_end,_end定义在u-boot.lds中。 第115行:把IRQ_STACK_START符号声明为全局符号。因为IRQ_STACK_START要被lib_arm/interrupts.c中interrupt_init函数使用。interrupt_init函数计算IRQ_STACK_START值和FIQ_STACK_START值。IRQ_STACK_START表示IRQ中断使用的栈的起始地址,FIR_STACK_START表示FIR中断使用的栈的起始地址。 第116-117行:定义了名为IRQ_STACK_START的地址标号,在这个地址标号处存放了值0x0badc0de。0x0badc0de的作用是内存标记。 第120行:把FIR_STACK_START符号声明为全局符号。因为FIR_STACK_START要被lib_arm/interrupts.c中interrupt_init函数使用。interrupt_init函数计算IRQ_STACK_START值和FIQ_STACK_START值。IRQ_STACK_START表示IRQ中断使用的栈的起始地址,FIR_STACK_START表示FIR中断使用的栈的起始地址。 第121-122行:定义了名为FIQ_STACK_START的地址标号,在这个地址标号处存放了值0x0badc0de。0x0badc0de的作用是内存标记。 第129-192行:复位异常处理程序。 第129行:把reset符号声明为全局符号。 第130行:定义了名为reset的地址标号。 第134-137行:通过设置cpsr寄存器的bit0-bit7(值为二进制11010011)来禁止IRQ和FIQ中断,切换为ARM状态,切换为SVC模式。 第144行:程序跳转至cpu_init_crit。cpu_init_crit位于第205行至第231行。cpu_init_crit主要是做一些处理器关键且必要的初始化工作,比如清除并关闭caches,关闭MMU,调用lowlevel_init函数初始化其他外设(如晶振、存储器控制器等等)。lowlevel_init函数位于board目录下对应开发板的lowlever_init.S文件中或cpu目录下对应处理器的lowlever_init.S文件中。每种处理器或者开发板的lowlevel_init函数要完成的的功能可能会有很大不一样。下面分析下cpu_init_crit: 第210-212行:清除I-Cache和D-Cache。具体要参看对应cpu的协处理器指令。 第217-222行:关闭MMU和Cache。具体要参看对应cpu的协处理器指令。由于在进入操作系统之前,都不需要且不允许虚拟内存,所以必须关闭MMU。 第227行:保存lr至ip寄存器。由于第228行使用了bl指令,该bl指令会把144行的bl指令保存下来的lr的值覆盖掉,所以,这里需要先保存lr的值,以便程序能正确返回。第229行和230行实现了cpu_init_crit的返回。 第228行:程序跳转至lowlevel_init函数。lowlevel_init函数位于board目录下对应开发板的lowlever_init.S文件中或cpu目录下对应处理器的lowlever_init.S文件中。每种处理器或者开发板的lowlevel_init函数要完成的的功能可能会有很大不一样。lowlevel_init函数初始化其他外设(如晶振、存储器控制器等等)。 接下来回到程序执行流程,分析第148-164行。 第148-164行的功能是把u-boot映像从非易失性存储器(比如:NAND Flash、NOR Flash、SD卡等)复制到RAM(比如:SDRAM)。如果u-boot映像本身就存储在RAM中(调试阶段),则不进行拷贝。在这个操作完成之前,u-boot的代码都是在存储u-boot映像的存储器里运行的,比如在NAND Flash、NOR Flash、SD卡等中。这个操作完成之后,将在RAM(比如:SDRAM)中运行start_armboot函数和异常及中断处理的代码。也就是说,除了start_armboot函数和异常及中断处理的代码在RAM中运行外,start.s中的其他代码都在存储u-boot映像的存储器里运行的。为什么是这样?这个主要是由跳转指令b和bl导致的。下面会有具体的分析。 第148行:定义了名为relocate的地址标号。 第149-151行:这三行通过判断u-boot当前运行位置(Flash或者RAM)来决定是否需要把u-boot映像拷贝至RAM。由前面adr指令介绍可知,第149行获得了u-boot当前运行地址的基地址(上电时u-boot的运行地址)并存入r0。第150行获得了u-boot映像的链接基地址,即最终的运行地址的基地址(RAM)并存入r1。如果上电时u-boot运行在Flash中,则r0不等于r1,此时需要把u-boot映像拷贝至RAM。如果上电时u-boot运行在RAM中(调试阶段),则r0等于r1,此时不需要把u-boot映像拷贝至RAM,程序直接跳转至stack_setup,进行堆栈设置。 第154-157行:这4行的作用是计算u-boot映像的结束地址,给copy_loop用。由u-boot.lds文件知道_armboot_start至_bss_start地址范围是u-boot映像的大小。 第159-163行:这4行实现了把u-boot映像拷贝至RAM。第160行从r0中的u-boot映像基地址连续读取8个4字节数据至寄存器r3-r10,然后第161行把寄存器r3-r10的值连续存入r1中的u-boot最终运行地址中(RAM)。第162行判断r0是否到达u-boot映像的结束地址r2,如果未到达u-boot映像的结束地址,则继续拷贝。 第167-174行:这8行的作用是分配堆区(malloc函数使用的区域)、全局数据区、IRQ中断使用的栈区、FIQ中断使用的栈区和用户栈区。用户栈区的设置是通过设置寄存器sp来实现的。根据这8行代码可以知道U-Boot内存使用情况了,如下图所示: 第176-184行:把bss区清零。 第186行:程序跳转至coloured_LED_init。coloured_LED_init函数位于board目录下对应开发板的led.c文件,作用是初始化开发板的指示灯。 第187行:程序跳转至red_LED_on。red_LED_on函数位于board目录下对应开发板的led.c文件,作用是点亮开发板的红色指示灯。 第189-192行:程序跳转至start_armboot。start_armboot函数位于lib_arm目录下的board.c文件中。start_armboot函数是u-boot启动的第2阶段,相当于main函数。 接下来分析向量表的另外6个异常或中断处理程序(未定义指令、软件中断、预取指中止、数据中止、保留、中断请求、快速中断请求),其位于第233-399行。 未定义指令、软件中断、预取指中止、数据中止、保留异常处理程序的行为基本上都一样:先保存异常发生时所有寄存器(r0-r10、fp、ip、sp、lr、pc、cpsr)的值,然后以易以调试的方式把它们的值打印出来,最后复位cpu。 中断请求、快速中断请求中断处理程序的行为基本一样:先保存中断发生时所有寄存器(r0-r10、fp、ip、sp、lr、pc、cpsr)的值,然后跳转至实际的中断处理程序。最后恢复寄存器值。 宏get_bad_stack的作用是保存异常发生时lr、spsr寄存器值至用户栈区。宏bad_save_user_regs的作用是保存异常发生时其他寄存器值至用户栈区。 do_xxx_instruction函数把异常发生时所有寄存器的值,以易以调试的方式把它们的值打印出来,最后复位cpu。do_xxx_instruction函数在lib_arm目录下的interrupts.c文件中。 宏irq_save_user_regs的作用是保存irq或fiq中断发生时所有寄存器(r0-r10、fp、ip、sp、lr、pc、cpsr)的值至用户栈区。 宏irq_restore_user_regs的作用是恢复irq或fiq中断的寄存器值。 do_irq和do_fiq是实际的irq和fiq中断处理程序。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1780 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1621 浏览 1 评论
1081 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
728 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1679 浏览 2 评论
1938浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
731浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
570浏览 3评论
596浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
557浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-24 06:34 , Processed in 1.227405 second(s), Total 80, Slave 64 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号