u-boot启动过程分析

备注:本文中使用的u-boot版本是2010.3

u-boot启动过程分为两个阶段:汇编语言阶段和C语言启动阶段,这两个阶段分别实现做了些,下面说说我的理解,有不对的地方欢迎指出纠正:

第一阶段:初始化SOC相关的配置,例如:系统时钟、MMU、看门狗等;并为C语言提供运行环境
第二阶段:初始化板级相关的外设,例如:网卡、DDR有效地址等;并初始化环境变量,加载kernel
1、第一阶段:汇编语言阶段

‘ENTRY(_start)’中的符号标签‘_start’为程序的入口,在‘_start’标签处定义的异常向量表的跳转地址:

.globl _start
_start: b   reset                    //复位异常 --> 进入管理模式:复位电平有效时触发
    ldr pc, _undefined_instruction  //未定义指令异常 --> 未定义指令模式:CPU无法识别的指令时触发
    ldr pc, _software_interrupt    //软中断异常 -->管理模式:SWI指令触发
    ldr pc, _prefetch_abort       //预取指令异常 -->中止模式:获取指令不存在时触发
    ldr pc, _data_abort          //数据异常 --> 中止模式:获取数据不存在时触发
    ldr pc, _not_used           //保留位
    ldr pc, _irq               //中断异常 -->IRQ模式
    ldr pc, _fiq              //快速中断异常 -->FIQ模式
1.1进入管理模式

当CPU上电时,复位电平有效,CPU当做是复位异常处理,因此在此异常向量表中将运行一下指令,使得进入管理模式(supervisor):

b   reset 

reset:
    /*
     * set the cpu to SVC32 mode and IRQ & FIQ disable
     */
    mrs r0, cpsr
    bic r0, r0, #0x1f
    orr r0, r0, #0xd3
    msr cpsr,r0
1.2 关闭数据Cache 和 MMU
为什么要关闭数据Cache呢,这是因为在设备刚上电的时候,内存的初始化会比较慢,当CPU初始化完成后,
内存此时还没做好准备,如果此时对内存进行数据读取,会导致数据预取异常。
为什么要关闭MMU?在设备刚上电时,MMU并为初始化;其次,在会汇编语言阶段一般是通过操作寄存器的实际物理地址
进行配置。因此为了避免MMU的干扰,把其给屏蔽。
1.3 lowlevel_init

跳转至符号标签‘lowlevel_init’,关闭看门狗、对系统时钟、SDRAM等进行初始:

bl system_clock_init        //系统时钟初始化
bl mem_ctrl_asm_init_ddr3   //初始化ddr3
bl uart_asm_init            //初始化串口

/* 初始化串口会先后打印‘O’、‘K’ 两个字符 */
/* Print 'O' */
ldr r1, =0x4f4f4f4f
str r1, [r0, #UTXH_OFFSET]  

/* Print 'K' */
ldr r0, =ELFIN_UART_CONSOLE_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET]
1.4 重定位

重定位:将u-boot冲内部的SRAM搬到SDRAM(DDR3)中运行,如果u-boot引进运行在SDRAM中时,则不需要再进行重定位。配置虚拟地址映射表,启动MMU

    mmu_table:
    ……
    .set __base,0x400                 //物理地址
    // 512MB for SDRAM with cacheable
    .rept 0x600 - 0x400              //虚拟地址范围
    FL_SECTION_ENTRY __base,3,0,1,1
    .set __base,__base+1
    .endr
    ……
1.5 初始化C语言运行环境

初始化栈stack_setup:为C语言提供运行环境
清除bss段:bss段用于存放初始化值为0的全局变量和未初始化的全局变量;并将未初始化的全局变量初始化为0,这也是为什么在C语言中为什么一个未初始化全局变量其值为0的原因。
跳转到C语言运行阶段:start_armboot

stack_setup:
    ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)

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
    ldr pc, _start_armboot //将pc指针指向start_armboot函数的地址

_start_armboot:
    .word start_armboot
2、第二阶段:C语言阶段

C语言的启动阶段的启动函数:start_armboot,在该函数中中实现了板子相关的初始化配置
重要结构:init_sequence指针数组,包含了板级初始化的相关函数,以下列出了函数的调用关系和功能:

start_armboot:
    init_sequence指针数组
        board_init,板级相关的初始化(board/samsung/smdkc210),例如网卡驱动
        interrupt_init,中断相关的初始化
        env_init,环境变量相关的初始化
        init_baudrate,波特率初始化
        serial_init,串口初始化
        console_init_f,控制台第一阶段初始化
        display_banner,打印uboot相关的信息
        print_cpuinfo,打印CPU相关的信息
        checkboard,打印开发板相关的信息
        dram_init,配置可用内存大小
        display_dram_config,显示内存大小函数:
    env_relocate (),初始化环境变量
    enable_interrupts,启动中断
    main_loop:根据启动延时进行判断,是进入启动kernel阶段,还是进行u-boot命令解析控制台
2.1 命令解析、并将参数传递给kernel

main_loop函数:通过调用run_command函数,执行命令解析

void main_loop (void)
{
    ……
    if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
            ……
            run_command (s, 0);
            ……
        }
    ……
    for (;;) {
        ……
    }

}

下面列出了解析名后的函数调用关系:

run_command-->(cmdtp->cmd) (cmdtp, flag, argc, argv):‘s = getenv ("bootcmd")’-->do_bootm函数
    do_bootm
        do_bootm_linux<-->boot_fn(0, argc, argv, &images)(typedef 类型重命名)
        do_bootm_linux:
            theKernel = (void (*)(int, int, uint))images->ep;//指向kernel的起始地址
            theKernel(0,machid,bd->bi_boot_params);//将3个参数传分别写入寄存器r0,r1,r2传递给kernel
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值