uboot专题:uboot1.1.6 第一阶段

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

    

一、链接脚本

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

  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、设置异常向量

  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、进入管理模式

  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中一些位被用于标识各种状态,一些位被用于标识当前出于什么工作模式。

    uboot专题:uboot1.1.6 第一阶段_第1张图片

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

uboot专题:uboot1.1.6 第一阶段_第2张图片

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

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

    T:1-Thumb 0-arm 指令集

    M0-M4 : 工作模式

uboot专题:uboot1.1.6 第一阶段_第3张图片

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

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

  3、关看门狗

  1. #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
  2. ldr r0, =pWTCON
  3. mov r1, # 0x0
  4. str r1, [r0]
  4、屏蔽中断
  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、设置时钟
  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
  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 

  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寄存器,用到的各位:

uboot专题:uboot1.1.6 第一阶段_第4张图片

  8、初始化 sdram 控制器

  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、代码重定位

  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、设置栈

  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):
  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 */
  1. 0x34000000:
  2. ( 512K) 存放 uboot
  3. 0x33F80000"white-space:pre"> TEXT_BASE
  4. ( 64K+ 128K == 192K) "white-space:pre"> 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

uboot专题:uboot1.1.6 第一阶段_第5张图片
  11、清 BSS 段
  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


三、第二阶段

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

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


https://blog.csdn.net/lizuobin2/article/details/52054293#comments



图解Uboot-引导流程

 

Uboot引导——第一阶段

参考:

https://blog.csdn.net/ce123_zhouwei/article/details/7304909

图解U-Boot:第一阶段源码分析

U-boot第一阶段的详细源码分析可以参看这篇博客

https://blog.csdn.net/czg13548930186/article/details/53484475

uboot源码分析1-启动第一阶段

https://blog.csdn.net/column/mycolumn.html

博客专栏地址

 

上电

|

初始化/cpu/arm920/start.S

       主要工作:

1、  设置arm进入SVC模式

2、  关闭看门狗,屏蔽所有中断,设置时钟分频

3、  进入cpu_init_crit

a)        关闭数据和指令cache,禁用mmu

b)        进入lowlevel_init初始化存储控制器

4、  判断是否在norflash中,如果是,复制uboot到sdram中

5、  如果不是或者拷贝完毕,建立堆栈stack_steup

6、  Bss段清零,clear bss

|

Start_armboot /lib_arm/board.c

第一阶段执行完毕,执行第二阶段c语言代码

U-Boot第一阶段的启动流程。这个阶段主要是初始化硬件设备,为加载U-Boot的第二阶段代码准备RAM空间最后跳转到lib_arm/board.c中start_armboot函数,

这是第二阶段的入口点。

 

 

Uboot引导——第二阶段

https://blog.csdn.net/qixi_feng/article/details/9005392

图解U-Boot:第二阶段源码分析

Uboot第二阶段的详细源码分析可以参考这篇博客

https://blog.csdn.net/czg13548930186/article/details/76339222

Start_armboot /lib_arm/board.c

1、  初始化gd和bd

2、  Init_sequence

3、  初始化flash

4、  Env_relocate,将环境参数读入内存中的指定位置

5、  初始化网络设备

6、  Device_init设备初始化

7、  Jumptable_init,跳转表初始化,用来登记函数调用地址

8、  Console_init_r,后期控制台初始化

9、  Enable_interrupt,使能中断处理

10、              Cs8900_get_eneaddr

11、              Main_loop

详细的流程和细节要参考第二篇文档和uboot源码来仔细分析了。


你可能感兴趣的:(uboot)