转自:http://blog.csdn.net/lizuobin2/article/details/52054293

最近打算移植一个比较新的 uboot 到开发板,回想起来上一次移植 uboot1.1.6 已经差不多是一年前了,手头保留了一些当时移植分析时的笔记,但是没有归纳梳理,在移植新版 uboot 之前,再来回味一下经典。本文重点在于分析 uboot 启动流程以及 uboot 自身的细节,比如栈空间的划分、如何设置 tag 、如何添加一个自定义命令等。但是不涉及基本的硬件驱动的分析,比如内存初始化、时钟初始化、mmu 、nandflash
等等这些细节不是我们的重点。

一、链接脚本

uboot1.1.6 的链接脚本 u-boot.lds 位于 u-boot-1.1.6\board\smdk2410 目录下:

[cpp] view
plain
 copy

  1. ENTRY(_start)
  2. SECTIONS
  3. {
  4. . = 0x00000000;
  5. . = ALIGN(4);
  6. .text      :
  7. {
  8. cpu/arm920t/start.o   (.text)
  9. *(.text)
  10. }
  11. . = ALIGN(4);
  12. .rodata : { *(.rodata) }
  13. . = ALIGN(4);
  14. .data : { *(.data) }
  15. . = ALIGN(4);
  16. .got : { *(.got) }
  17. . = .;
  18. __u_boot_cmd_start = .;
  19. .u_boot_cmd : { *(.u_boot_cmd) }
  20. __u_boot_cmd_end = .;
  21. . = ALIGN(4);
  22. __bss_start = .;
  23. .bss : { *(.bss) }
  24. _end = .;
  25. }

链接地址为 0 ?显然不应该,实际编译的时候执行的大概是这样一条语句:

arm-Linux-ld -Bstatic
-T u-boot.lds -Ttext 0x33F80000 start.o ...

0x33F80000 在 board/smdk2410/config.mk 中定义,为 TEXT_BASE = 0x33F80000 (链接地址)

整个 uboot 的入口 _start 包含在 cpu/arm920t/start.S 中

二、第一阶段

    uboot 的第一阶段主要工作是作基本的初始化工作,例如关看门狗、初始化时钟、初始化 sdram 以及代码重定位,为第二阶段做准备。这里的代码都是没有经过移植的源代码~!



  1、设置异常向量

[cpp] view
plain
 copy

  1. .globl _start
  2. _start: b       reset
  3. ldr pc, _undefined_instruction
  4. ldr pc, _software_interrupt
  5. ldr pc, _prefetch_abort
  6. ldr pc, _data_abort
  7. ldr pc, _not_used
  8. ldr pc, _irq
  9. ldr pc, _fiq
  10. _undefined_instruction: .word undefined_instruction
  11. _software_interrupt:    .word software_interrupt
  12. _prefetch_abort:    .word prefetch_abort
  13. _data_abort:        .word data_abort
  14. _not_used:      .word not_used
  15. _irq:           .word irq
  16. _fiq:           .word fiq
  17. .balignl 16,0xdeadbeef

第一条 b reset ,因为刚开始运行时代码都是在片内 sram 里,我们在 sram 里调来跳去的话就需要用位置无关码,那么 b 就是最佳选择,因为它是相对跳转。

    ldr    pc, _undefined_instruction

_undefined_instruction:.word undefined_instruction

感觉真是在卖弄,两条指令连起来的结果就是,CPU 会跳转到 undefined_instruction 链接地址处去执行(sdram里)。

那么其实,一条 ldr pc,=undefined_instruction 就够了,它是位置有关码,绝对跳转。

或许,uboot 的作者别有用意我没看透,不知道这是不是个伏笔。在u-boot2015里,就只有一个 reset 一个异常入口了。

2、进入管理模式

[cpp] view
plain
 copy

  1. reset:
  2. /*
  3. * set the cpu to SVC32 mode
  4. */
  5. mrs r0,cpsr
  6. bic r0,r0,#0x1f
  7. orr r0,r0,#0xd3
  8. msr cpsr,r0

ARM每种工作模式除R0~R15共16个寄存器外,还有第17个寄存器CPSR,叫做 当前程序状态寄存器,CPSR中一些位被用于标识各种状态,一些位被用于标识当前出于什么工作模式。

有时候我们会碰到 CPSR_C ,它其实就是 CPSR 的低 8 位而已。

I:1-禁止irq中断 0-允许irq中断

F:1-禁止fiq中断 1-允许fiq中断

T:1-Thumb 0-arm 指令集

M0-M4 : 工作模式

说了这么多,前边两条指令,先将 cpsr 低 5位 清零,然后或上 1101 0011B

禁止了 irq 和 fiq 中断,工作在 arm 指令集,管理模式。

3、关看门狗

[cpp] view
plain
 copy

  1. #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
  2. ldr     r0, =pWTCON
  3. mov     r1, #0x0
  4. str     r1, [r0]

4、屏蔽中断

[cpp] view
plain
 copy

  1. /*
  2. * mask all IRQs by setting all bits in the INTMR - default
  3. */
  4. mov r1, #0xffffffff
  5. ldr r0, =INTMSK
  6. str r1, [r0]
  7. # if defined(CONFIG_S3C2410)
  8. ldr r1, =0x3ff
  9. ldr r0, =INTSUBMSK
  10. str r1, [r0]
  11. # endif

前边通过 cpsr 禁止 irq 和 fiq 使 cpu 不接受来自中断控制器的中断请求,而这里通过中断屏蔽使中断发生时,中断控制寄存器自身就不会上报给 cpu 双保险。

  5、设置时钟

[cpp] view
plain
 copy

  1. /* FCLK:HCLK:PCLK = 1:2:4 */
  2. /* default FCLK is 120 MHz ! */
  3. ldr r0, =CLKDIVN
  4. mov r1, #3
  5. str r1, [r0]

6、关 I/D cache 关 TLB

[cpp] view
plain
 copy

  1. /*
  2. * flush v4 I/D caches
  3. */
  4. mov r0, #0
  5. mcr p15, 0, r0, c7, c7, 0   /* flush v3/v4 cache */
  6. mcr p15, 0, r0, c8, c7, 0   /* flush v4 TLB */


协处理器 p15 在2410的数据手册附录有介绍

或者参考:http://blog.sina.com.cn/s/blog_858820890102v1gc.html

7、关 mmu 

[cpp] view
plain
 copy

  1. /*
  2. * disable MMU stuff and caches
  3. */
  4. mrc p15, 0, r0, c1, c0, 0
  5. bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
  6. bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
  7. orr r0, r0, #0x00000002 @ set bit 2 (A) Align
  8. orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
  9. mcr p15, 0, r0, c1, c0, 0

这里主要涉及 P15 的 C1寄存器,用到的各位:

8、初始化 sdram 控制器

[cpp] view
plain
 copy

  1. .globl lowlevel_init
  2. lowlevel_init:
  3. /* memory control configuration */
  4. /* make r0 relative the current location so that it */
  5. /* reads SMRDATA out of FLASH rather than memory ! */
  6. ldr     r0, =SMRDATA
  7. ldr r1, _TEXT_BASE
  8. sub r0, r0, r1
  9. ldr r1, =BWSCON /* Bus Width Status Controller */
  10. add     r2, r0, #13*4
  11. 0:
  12. ldr     r3, [r0], #4
  13. str     r3, [r1], #4
  14. cmp     r2, r0
  15. bne     0b
  16. /* everything is fine now */
  17. mov pc, lr
  18. .ltorg
  19. /* the literal pools origin */
  20. SMRDATA:
  21. .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
  22. .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
  23. .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
  24. .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
  25. .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
  26. .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
  27. .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
  28. .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
  29. .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
  30. .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
  31. .word 0x32
  32. .word 0x30
  33. .word 0x30

写裸机代码的入门操作,初始化 sdram 寄存器。

9、代码重定位

[cpp] view
plain
 copy

  1. relocate:               /* relocate U-Boot to RAM       */
  2. adr r0, _start      /* r0 <- current position of code   */
  3. ldr r1, _TEXT_BASE      /* test if we run from flash or RAM */
  4. cmp     r0, r1                  /* don't reloc during debug         */
  5. beq     stack_setup
  6. ldr r2, _armboot_start
  7. ldr r3, _bss_start
  8. sub r2, r3, r2      /* r2 <- size of armboot            */
  9. add r2, r0, r2      /* r2 <- source end address         */
  10. copy_loop:
  11. ldmia   r0!, {r3-r10}       /* copy from source address [r0]    */
  12. stmia   r1!, {r3-r10}       /* copy to   target address [r1]    */
  13. cmp r0, r2          /* until source end addreee [r2]    */
  14. ble copy_loop

adr 位置无关码,获取_start实际当前位于的地方,_TEXT_BASE 为 0x33f80000 ,这里判断的是代码是否直接运行在 sdram 里了,如果是就不需要重定位了。

拷贝范围:_start 至 _bss_start 前,拷贝到 0x33f80000 处。

33f80048 <_bss_start>:

      33f80048:    33fb064c

0x33fb064c - 0x33f80000 > 192K ,什么意思呢?整个 uboot 除了 bss 段 > 4k,如果是 nandflash 启动的话需要从 nandflash 里读取 uboot 到内核,而这里是直接从 0 地址开始读,并读取 > 193k 的东西,显然 uboot 运行在 Norflash 才可以。默认
uboot 不支持 nandflash 启动。

10、设置栈

[cpp] view
plain
 copy

  1. stack_setup:
  2. ldr r0, _TEXT_BASE      /* upper 128 KiB: relocated uboot   */
  3. sub r0, r0, #CFG_MALLOC_LEN /* malloc area                      */
  4. sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */
  5. #ifdef CONFIG_USE_IRQ
  6. sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
  7. #endif
  8. sub sp, r0, #12     /* leave 3 words for abort-stack    */
  9. clear_bss:
  10. ldr r0, _bss_start      /* find start of bss segment        */
  11. ldr r1, _bss_end        /* stop here                        */
  12. mov     r2, #0x00000000     /* clear                            */
  13. clbss_l:str r2, [r0]        /* clear loop...                    */
  14. add r0, r0, #4
  15. cmp r0, r1
  16. ble clbss_l

TEXT_BASE = 0x33F80000 其它的宏在 Smdk2410.h (include\configs):

[cpp] view
plain
 copy

  1. #define CFG_MALLOC_LEN      (CFG_ENV_SIZE + 128*1024)
  2. #define CFG_ENV_SIZE        0x10000 /* Total Size of Environment Sector */
  3. #define CFG_GBL_DATA_SIZE   128
  4. #define CONFIG_STACKSIZE_IRQ    (4*1024)    /* IRQ stack */
  5. #define CONFIG_STACKSIZE_FIQ    (4*1024)    /* FIQ stack */
[cpp] view
plain
 copy

  1. 0x34000000:
  2. (512K)              存放 uboot
  3. 0x33F80000:  <span style="white-space:pre">       </span>TEXT_BASE
  4. (64K+128K == 192K)  <span style="white-space:pre">        </span>mallo区
  5. 0x33F50000:
  6. (128bytes)          global data区,后边会提到主要放的gd、bd全局结构体
  7. 0x33F4FF80:
  8. (4*1024*2)          IRQ+FIQ的栈
  9. 0x33F4DF80:
  10. (12byte)            abort-stack,栈溢出
  11. 0x33F4DF74:         sp

11、清 BSS 段

[cpp] view
plain
 copy

  1. clear_bss:
  2. ldr r0, _bss_start      /* find start of bss segment        */
  3. ldr r1, _bss_end        /* stop here                        */
  4. mov     r2, #0x00000000     /* clear                            */
  5. clbss_l:str r2, [r0]        /* clear loop...                    */
  6. add r0, r0, #4
  7. cmp r0, r1
  8. ble clbss_l

三、第二阶段

[cpp] view
plain
 copy

  1. ldr pc, _start_armboot
  2. start_armboot:  .word start_armboot

跳转到 sdram 里的 start_armboot 函数执行。

回味经典——uboot1.1.6 之 第一阶段的更多相关文章

  1. 回味经典——uboot1.1.6 之 第二阶段 第三阶段

    转自:http://blog.csdn.net/lizuobin2/article/details/52061530 上篇文章说到,再清 BSS 段之后,CPU 跳转到 sdram 里的 start_ ...

  2. u-boot-1.1.6第2阶段入口函数start_armboot分析

    学习目标: 1.分析u-boot-1.1.6第2阶段入口函数void start_armboot (void),熟悉该函数所实现的功能 2.为后面能够掌握u-boot-1.1.6如何启动内核过程打下基 ...

  3. PHP四个阶段目标以及第一阶段学习内容

    PHP课程体系主要分为四个阶段,第一阶段讲的是网页HTML和数据库MySQL,第一阶段要学会网页制作的基础知识,熟用各种基本标签,对数据库进行操作,各项考核都能够达标,拿出出众的项目展示. 在第二个阶 ...

  4. Bete冲刺第一阶段

    Bete冲刺第一阶段 今日工作: github团队协作流程 web:调整dao层设计,增加新的dao组件 客户端:之前遗留的界面跳转的BUG 目前所遇问题: 第一,COCOAPODS的安装上还是有点问 ...

  5. java - 第一阶段总结

    java - 第一阶段总结 递归 递归:能不用就不用,因为效率极低 package over; //递归 public class Fi { public static void main(Strin ...

  6. 自我总结(六)---(学习j2ee+j2ee第一阶段项目)

    自我完善的过程就是在不断的自我总结不断的改进. 学习了Struts2 Spring Hibernate. 十天前结束了这个课程.也考试了.这次考试老师说机试考的还不错.其实就是一个简单的用户登录,进行 ...

  7. JAVA EE 第一阶段考试

    在第一阶段中我们学习了Spring Struts2 Hibernate.虽然在外面的公司中,公司项目的框架中都不在使用Struts2了.他好像出现了不可修复的bug.但是在学校,依然还是要学习这个.在 ...

  8. Java第一阶段总结

    学习java已经一个多月的时间了,第一阶段总算完成了. 这中间遇到很多问题,通过问同学问学长,收获了很多,但也知道自己和其他同学相差很远.他们java第一阶段只用了不到一个月的时间,而我拖了很长时间, ...

  9. [原创].NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇)

    原文:[原创].NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇) .NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇) 前言:接着上篇来. 系列文章链接: [ ...

随机推荐

  1. idea2018注册方法

    下面是具体的破解激活步骤: 1. 下载破解补丁文件,路径为:http://idea.lanyus.com/jar/JetbrainsCrack-2.7-release-str.jar 2.将补丁放在安 ...

  2. viewState详解

    作者:Infinities Loop 概述 ViewState是一个被误解很深的动物了.我希望通过此文章来澄清人们对 ViewState的一些错误认识.为了达到这个目的,我决定从头到尾详细的描述一下整 ...

  3. Retimer、Redriver(Level Shifter)

    重定时器Retimer和驱动器Redriver9(Level Shifter) 在高速串行通道的信号传输中,需要使用Redriver 和Retimer来保证信号传输的质量. Redriver,可以重新 ...

  4. Numpy数组计算

    NumPy是高性能科学计算和数据分析的基础包.它是pandas等其他各种工具的基础. NumPy的主要功能 ndarray,一个多维数组结构,高效且节省空间 无需循环对整组数据进行快速运算的数学函数 ...

  5. Linux驱动经典面试题目

    1.  linux驱动分类 2.  信号量与自旋锁 3.  platform总线设备及总线设备怎样编写 4.  kmalloc和vmalloc的差别 5.  module_init的级别 6.  加入 ...

  6. 宜信开源微服务任务调度平台(SIA-TASK)

    背景 无论是互联网应用或者企业级应用,都充斥着大量的批处理任务.常常需要一些任务调度系统帮助开发者解决问题.随着微服务化架构的逐步演进,单体架构逐渐演变为分布式.微服务架构.在此的背景下,很多原先的任 ...

  7. python中if __name__ == '__main__': 的解析(转载)

    当你打开一个.py文件时,经常会在代码的最下面看到if __name__ == '__main__':,现在就来介 绍一下它的作用. 模块是对象,并且所有的模块都有一个内置属性 __name__.一个 ...

  8. android studio 程序真机执行中文显示乱码

    代码里中文显示正常,真机执行后中文显示乱码,解决的方法: build.gradle中加入一句 android { compileOptions.encoding = "GBK" }

  9. SpringInAction4笔记——复习

    由于目前只做后端的业务代码的开发,所以根据自己掌握的熟悉程度,只需要复习几个模块即可 重点看的是核心容器(IOC),redis,缓存,消息(主要是rabbitmq),事务,springboot,单元测 ...

  10. Android笔记之平移View

    方法有多种,只讲一种 使用View.setLeft和View.setRight 对于wrap_content的View,要横向平移,setRight是必要的,否则View的宽度会被改变(right应设 ...