1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
这两天在分析uboot,昨天分析完了makefile之后,今天分析了第一阶段的源码,故而分享之。新手望各位指教
开始先插一下,uboot移植的思路: 1首先来确定UBOOT的每一步动作,以及这个动作所使用的文件,函数等等 2再来确定哪一个动作需要修改的 3一般CPU都是ARM920T所以主要修改的是板级函数,在board文件夹下 下来进行分析:分析的前提是假设所有的配置都是打开的。 首先,看start.S .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 知道上电后执行的是 b reset 找到rest如下: reset: /* *set the cpu to SVC32 mode */ mrs r0,cpsr bic r0,r0,#0x1f orr r0,r0,#0xd3 msr cpsr,r0 /*turn off the watchdog */ #if defined(CONFIG_S3C2400) #define pWTCON 0x15300000 #define INTMSK 0x14400008 /* Interupt-Controller base addresses */ #define CLKDIVN 0x14800014 /* clock divisor register */ #elifdefined(CONFIG_S3C2410) #define pWTCON 0x53000000 #define INTMSK 0x4A000008 /* Interupt-Controller base addresses */ #define INTSUBMSK 0x4A00001C #define CLKDIVN 0x4C000014 /* clock divisor register */ #endif #ifdefined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) 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] # ifdefined(CONFIG_S3C2410) ldr r1,=0x3ff ldr r0,=INTSUBMSK str r1,[r0] #endif /* FCLK:HCLK:PCLK = 1:2:4 */ /* default FCLK is 120 MHz ! */ ldr r0,=CLKDIVN mov r1,#3 str r1,[r0] #endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */ /* *we do sys-critical inits only at reboot, *not when booting from ram! */ #ifndefCONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit #endif 很清楚地看到,首先上电后执行的是: 1. 设为SVC MODE 2. 关闭watchdog 3. 屏蔽IRQ中断 4. 设置时钟 5. 跳转到cpu_init_crit 好了,我们来找到cpu_init_crit在cpu/arm920t/start.S中。 看一看代码如下: #ifndefCONFIG_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 /* * before relocating, we have to setup RAM timing * because memory timing is board-dependend,you will * find a lowlevel_init.S in your boarddirectory. */ mov ip,lr bl lowlevel_init mov lr,ip mov pc,lr #endif /*CONFIG_SKIP_LOWLEVEL_INIT */ 代码的注释十分详细,不用看代码都可以知道,这个函数主要做的事情有 : 1 flush v4 I/D caches 2 disable MMU stuff and caches 3这句话很重要: /* *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,原因是because memory timing i***oard-dependend,,所以我们要根据自己的板子来设置好RAM,以进行代码的重定位。 4 bl lowlevel_init 跳转到lowlevel_init,根据这句you will find alowlevel_init.S in your board directory.,我们就到/board去找找看。 果然找到了 代码有点长,所以节选来看。 #include #include 首先它包含了两个头文件,第一个是我们的配置文件主要是选择打开的配置,是我们手动创建生成的。 第二 个是版本信息。没什么值得分析的 接下来定义了一个 _TEXT_BASE: .word TEXT_BASE 这个TEXT_BASE其实才是镜像文件的真正其实地址。我们来看,连接器脚本,look一下, SECTIONS { . = 0x00000000; . = ALIGN(4); .text : { cpu/arm920t/start.o (.text) *(.text) } . = ALIGN(4); .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 : { *(.bss) } _end = .; } 看起来好像是0地址开始的,可是......我们来看看map文件吧,look一下 Linker script andmemory map 0x00000000 . = 0x0 0x00000000 . = ALIGN (0x4) .text 0x33f80000 0x12844 cpu/arm920t/start.o(.text) .text 0x33f80000 0x400cpu/arm920t/start.o 0x33f80048 _bss_start 0x33f8004c _bss_end 0x33f80044 _armboot_start 0x33f80000 _start 看吧,地址是从0x33f80000开始的,为什么会这样呢? 其实奥妙就在makefile 当中. 我们看看实际上makefile 是怎么链接的 这一句cd $(LNDIR) &&$(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) 而在mkconfig脚本生成的config.mk文件中可以看到: LDFLAGS += -Bstatic-T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS) 吓尿了有木有!事实上连接起始地址是TEXT_BASE这个变量在board/smdk2410中定义是: TEXT_BASE =0x33F80000 为什么要这么做?因为我们的代码最终要拷贝到RAM 内以提高速度,而SMDK2410这块板子的RAM起始地址是0x30000000,所以0x3ff80000是RAM的地址代码就被链接到了RAM里面啦。 路人甲发问:为毛不是从0x30000000开始? 我:……不要在意这些细节……. 如果非要弄明白这个问题,那就请看start.S的代码: stack_setup: ldr r0,_TEXT_BASE /* upper 128 KiB:relocated uboot */ sub r0,r0, #CFG_MALLOC_LEN /* mallocarea */ sub r0,r0, #CFG_GBL_DATA_SIZE /* bdinfo */ #ifdefCONFIG_USE_IRQ sub r0,r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif sub sp,r0, #12 /* leave 3 words forabort-stack */ 很明白了,RAM起始部分的地址是留给栈和MALLOC,全局参数,以及ABORT异常的。 路人乙:那为毛链接到RAM里面的代码在NOR上跑起来了 我:卧槽!你有种就再说一遍! 咳咳咳,咱们不能再开叉了,不然就跑题了,如果想要明白这个问题的,请谷歌“位置无关码”“GUN程序加载数据方式” 好了花了那么多口舌去讲一个TEXT_BASE可见其很重要,为毛重要,是因为移植的时候我们必须要注意到这一点,不然写程序的时候没有意识到链接地址是TEXT_BASE开始的,那么我们自己添加的代码就会作废了。 继续分析 Lowlevel,刚刚分析到第四步,现在是第五步啦 5第五步就是初始化RAM啦 lowlevel_init: /* memory control configuration */ /* make r0 relative the current locationso that it */ /* reads SMRDATA out of FLASH rather thanmemory ! */ ldr r0, =SMRDATA ldr r1,_TEXT_BASE sub r0,r0, r1 ldr r1,=BWSCON /* Bus Width Status Controller */ add r2, r0, #13*4 0: ldr r3, [r0], #4 str r3, [r1], #4 cmp r2, r0 bne 0b 其中SMRDATA是RAM相关寄存器设置值的存放起始地址,一共十三个。因为这时候代码所在的位置是0起始的,而实际上连接的地址是0x33f80000,所以直接去访问数据肯定是错误的,为什么?因为程序在连接的时候已经确定了所有的符号地址,比如说你去访问SMRDATA这个数组,因为连接时地址已经确定,所以访问的地址应该是RAM中的地址,但是现在我们的镜像是在NOR里的,访问自然理所当然会出错。故而我们应该找到SMRDATA这个数组在NOR的位置,于是:sub r0,r0,r1.。然后做基址寻址,ok!大功告成。 6最后一步,事情都完成了,要返回咯 /* everything is fine now */ mov pc,lr 嗯?这个代码注释挺有喜感的,德国人都这么幽默吗? 返回哪里?我的天,当然是哪里调用的lowlevel_init就返回哪里呀。返回cpu_init_crit。 返回后我们看到这么一段代码 mov ip, lr bl lowlevel_init mov lr,ip mov pc,lr 嗯,从cpu_init_crit返回。返回到rest中。 看看接下来会发生什么吧。 好了现在我们回到rest的bl cpu_init_crit处接着执行。 回顾一下我们已经完成的工作: 1设为SVC MODE 2关闭watchdog 3屏蔽IRQ中断 4设置时钟 5清缓冲 6关闭MMU 7初始化RAM 既然更舒适温馨的房子----RAM已经准备好了,那么接下来就搬到RAM里面去住吧。 代码很简单,就是平常我们写的copy函数: relocate: /* relocate U-Boot to RAM */ adr r0,_start /* r0 <- current positionof code */ ldr r1,_TEXT_BASE /* test if we run fromflash or RAM */ cmp r0, r1 /* don'treloc during debug */ beq stack_setup ldr r2,_armboot_start ldr r3,_bss_start sub r2,r3, r2 /* r2 <- size ofarmboot */ add r2,r0, r2 /* r2 <- source endaddress */ copy_loop: ldmia r0!,{r3-r10} /* copy from sourceaddress [r0] */ stmia r1!,{r3-r10} /* copy to target address [r1] */ cmp r0,r2 /* until source endaddreee [r2] */ ble copy_loop 首先取出来当前代码所在的位置adr r0,_start 然后判断一下代码是不是在RAM里面,cmp r0,r1如果在RAM里面就直接跳到下一步,如果不在就copy_loop吧。 接下来,设置好栈,然后清BSS段,接着跳转到第二阶段(RAM内),开始新的旅程了。 跳转的代码如下: ldr pc,_start_armboot _start_armboot: .word start_armboot 这个_start_armboot就是第二阶段的入口,在/lib_arm/board.c内。 ———第一阶段分析结束 上大人孔乙己 评分 |
|
相关推荐
6 个讨论
|
|
谢谢分享!很详细,很细致 |
|
|
|
|
|
只有小组成员才能发言,加入小组>>
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 01:32 , Processed in 0.915873 second(s), Total 74, Slave 56 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号