ARM启动过程之Uboot两个阶段具体工作(以S5PV210为例)

1,S5PV210的启动过程

(1)iROM:iROM(价格最贵)在0地址处,与CPU总线式连接,上电就会执行,和norflash类似。210启动时会首先执行内部的固件代码,三星公司出厂前内置的代码段,称为BL0。BL0部分包括对SD卡、Nandflash等SOC内部硬件进行初始化并判断系统启动方式,找到外部用来启动系统的存储器,并将存储器中的启动代码(Bootloader)拷贝到SRAM中。

注释:不论是arm的何种处理器,其都是从0x00地址处开始执行程序。IROM上电能直接执行是因为不需要进行初始化,总线式访问,而SD卡之类的需要通过时序实现,需要初始化才能使用。BL0空间有限,所以做最重要的事,进行必要的初始化,不然无法使用SD卡等设备。

(2)Boot device:用来存放启动代码、内核、文件系统的外设存储器,例如Nandflash、SD卡、SPIflash等(价格低、时序访问,需要初始化)。iROM部分的固件代码会自动判断从哪里获取启动代码,一般看不到固件代码但是芯片手册会告诉我们固件代码所支持的外设存储器,只需要将启动代码放在存储器中,固件代码会将他拷贝到SOC内部的SRAM中。

(3)SRAM:三星210芯片有96KB(价格贵、总线式访问),固件代码将Uboot中的前16KB拷贝到SRAM中运行,这16KB代码叫BL1,BL1中要完成关看门狗、设置栈、开iCache、初始化DDR、从SD卡复制BL2到DDR中特定位置,跳转执行BL2。如果BL2小于80KB则被拷贝到SRAM中运行,而实际Uboot至少大于280KB,所以BL1汇编代码还要负责将(BL1+BL2)拷贝到DDR中运行(利用ldr pc, =main这种方式以远跳转从SRAM中运行的BL1跳转到DDR中运行的BL2)。

注释:三星210SRAM只有96KB,而其他厂商可能有300KB,不分BL1与BL2,一次读取完Uboot,不经过第4步;或者读取一部分,省略第3步,然后直接拷贝整个Uboot到DDR中运行,最后引导内核启动,uboot结束并死掉。

(4)SDRAM:外部存储器DDR,也就是运行内存,内核与应用程序申请内存资源的地方。需要初始化才能使用。

 

1.start.S引入,u-boot.lds中找到start.S入口
(1)在C语言中整个项目的入口就是main函数(这是C语言规定的),所以譬如说一个有10000个.c文件的项目,第一个要分析的文件就是包含了main函数的那个文件。
(2)在uboot中因为有汇编阶段参与,因此不能直接找main.c。整个程序的入口取决于链接脚本中ENTRY声明的地方。ENTRY(_start)因此_start符号所在的文件就是整个程序的起始文件,_start所在处的代码就是整个程序的起始代码。

 

uboot的start.S第一阶段做了哪些主要工作:

1、异常向量表的构建
(1)异常向量表是硬件决定的,软件只是参照硬件的设计来实现它。
(2)异常向量表中每种异常都应该被处理,否则真遇到了这种异常就跑飞了。但是我们在uboot中并未非常细致的处理各种异常。
(3)复位异常处的代码是:b reset,因此在CPU复位后真正去执行的有效代码是reset处的代码,因此reset符号处才是真正的有意义的代码开始的地方。
2、设置SRAM中的栈并调用lowlevel_init
(1)284-286行第一次设置栈。这次设置栈是在SRAM中设置的,因为当前整个代码还在SRAM中运行,此时DDR还未被初始化还不能用。栈地址0xd0036000是自己指定的,指定的原则就是这块空间只给栈用,不会被别人占用。
(2)在调用函数前初始化栈,主要原因是在被调用的函数内还有再次调用函数,而BL只会将返回地址存储到LR中,但是我们只有一个LR,所以在第二层调用函数前要先将LR入栈,否则函数返回时第一层的返回地址就丢了。
3、再次设置栈(DDR中的栈)linux中是满减栈
(1)之前在调用lowlevel_init程序前设置过1次栈(start.S 284-287行),那时候因为DDR尚未初始化,因此程序执行都是在SRAM中,所以在SRAM中分配了一部分内存作为栈。本次因为DDR已经被初始化了,因此要把栈挪移到DDR中,所以要重新设置栈,这是第二次(start.S 297-299行);这里实际设置的栈的地址是33E00000,刚好在uboot的代码段的下面紧挨着。

4、判断当前地址以决定是否重定位
(1)再次用相同的代码判断运行地址是在SRAM中还是DDR中,不过本次判断的目的不同(上次判断是为了决定是否要执行初始化时钟和DDR的代码)这次判断是为了决定是否进行uboot的relocate。
(2)冷启动时当前情况是uboot的前一部分(16kb或者8kb)开机自动从SD卡加载到SRAM中正在运行,uboot的第二部分(其实第二部分是整个uboot)还躺在SD卡的某个扇区开头的N个扇区中。此时uboot的第一阶段已经即将结束了(第一阶段该做的事基本做完了),结束之前要把第二部分加载到DDR中链接地址处(33e00000),这个加载过程就叫重定位。
(3)真正的重定位是通过调用movi_bl2_copy函数完成的,在uboot/cpu/s5pc11x/movi.c中。是一个C语言的函数
(4)copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT,CFG_PHY_UBOOT_BASE, 0);
分析参数:2表示通道2;MOVI_BL2_POS是uboot的第二部分在SD卡中的开始扇区,这个扇区数字必须和烧录uboot时烧录的位置相同;MOVI_BL2_BLKCNT是uboot的长度占用的扇区数;CFG_PHY_UBOOT_BASE是重定位时将uboot的第二部分复制到DDR中的起始地址(33E00000)

5、建立虚拟地址映射转换表TTB

(1)MMU就是memory management unit,内存管理单元。MMU实际上是SOC中一个硬件单元,它的主要功能就是实现虚拟地址到物理地址的映射。

(2)TTB就是translation table base,转换表基地址。首先要明白什么是TT(translation table转换表),TTB其实就是转换表的基地址。
(3)转换表是建立一套虚拟地址映射的关键。转换表分2部分,表索引和表项。表索引对应虚拟地址,表项对应物理地址。一对表索引和表项构成一个转换表单元,能够对一个内存块进行虚拟地址转换。(映射中基本规定中规定了内存映射和管理是以块为单位的,至于块有多大,要看你的MMU的支持和你自己的选择。在ARM中支持3种块大小,细表1KB、粗表4KB、段1MB)。真正的转换表就是由若干个转换表单元构成的,每个单元负责1个内存块,总体的转换表负责整个内存空间(0-4G)的映射。
(4)整个建立虚拟地址映射的主要工作就是建立这张转换表
(5)转换表放置在内存中的,放置时要求起始地址在内存中要xx位对齐(多少兆字节对齐)。转换表不需要软件去干涉使用,而是将基地址TTB设置到cp15的c2寄存器中,然后MMU工作时会自动去查转换表。

6、ldr    pc, _start_armboot
(1)start_armboot是uboot/lib_arm/board.c中,这是一个C语言实现的函数。这个函数就是uboot的第二阶段。这句代码的作用就是将uboot第二阶段执行的函数的地址传给pc,实际上就是使用一个远跳转直接跳转到DDR中的第二阶段开始地址处。
(2)远跳转的含义就是这句话加载的地址和当前运行地址无关,而和链接地址有关。因此这个远跳转可以实现从SRAM中的第一阶段跳转到DDR中的第二阶段。
(3)这里这个远跳转就是uboot第一阶段和第二阶段的分界线。

uboot的start.S第二阶段做了哪些主要工作:

(1)第二阶段主要是对开发板级别的硬件、软件数据结构进行初始化。
(2)
    init_sequence
        cpu_init    空的
        board_init    网卡、机器码、内存传参地址
            dm9000_pre_init            网卡
            gd->bd->bi_arch_number    机器码
            gd->bd->bi_boot_params    内存传参地址
        interrupt_init    定时器
        env_init
        init_baudrate    gd数据结构中波特率
        serial_init        空的
        console_init_f    空的
        display_banner    打印启动信息
        print_cpuinfo    打印CPU时钟设置信息
        checkboard        检验开发板名字
        dram_init        gd数据结构中DDR信息
        display_dram_config    打印DDR配置信息表
    mem_malloc_init        初始化uboot自己维护的堆管理器的内存
    mmc_initialize        inand/SD卡的SoC控制器和卡的初始化
    env_relocate        环境变量重定位
    gd->bd->bi_ip_addr    gd数据结构赋值
    gd->bd->bi_enetaddr    gd数据结构赋值
    devices_init        空的
    jumptable_init        不用关注的
    console_init_r        真正的控制台初始化
    enable_interrupts    空的
    loadaddr、bootfile     环境变量读出初始化全局变量
    board_late_init        空的
    eth_initialize        空的
    x210_preboot_init    LCD初始化和显示logo
    check_menu_update_from_sd    检查自动更新
    main_loop            主循环

启动过程特征总结
(1)第一阶段为汇编阶段、第二阶段为C阶段
(2)第一阶段在SRAM中、第二阶段在DRAM中
(3)第一阶段注重SoC内部、第二阶段注重SoC外部Board内部

最后通过uboot引导并加载内核,通过bootm命令在指定地址启动内核,uboot在加载内核到指定地址时,可以通过内核源码链接脚本或Makefile知道。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章