Uboot启动流程(一)

Uboot启动流程简要概括

Uboot可以看作是一个比较复杂的综合裸机程序,学习之余对其启动流程进行一个简要的概括,理清自己的思路。
详细内容参考STM32MP1嵌入式Linux驱动开发指南

Uboot程序入口 _start

根据链接脚本 u-boot.lds找到代码入口:_start
该函数定义在:arch/arm/lib/vectors.S
开始是中断向量表,然后跳转到reset函数

reset函数概要

定义在arch/arm/cpu/armv7/start.S
分别调用函数:

  1. save_boot_params
    该函数中又调用了save_boot_params_ret函数,设置处理器进入SVC模式,关闭 FIQ 和 IRQ这两个中断,向量表重定位。
  2. cpu_init_cp15
    用来设置CP15 相关的内容,比如关闭MMU
  3. cpu_init_crit
    其中只是调用了lowlevel_init函数。
  4. _main
    下面详解

_main函数

该函数定义在文件 arch/arm/lib/crt0.S 中
首先设置sp指针指向0XC0100000
调用board_init_f_alloc_reserve预留出早期malloc区和gd内存区
初始化(清零)gd指针
调用board_init_f , 该函数定义在common/board_f.c中,主要完成外设初始化和uboot重定位
调用relocate_code函数进行uboot重定位
调用relocate_vectors对中断向量表重定位
调用函数 c_runtime_cpu_setup, board_init_f并没有初始化所有的外设,还需要做一些后续工作,这 些后续工作就是由函数 board_init_r 来完成的

board_init_f函数概要

函数定义在文件 common/board_f.c
该函数中重点:通过函数 initcall_run_list 来运行初始化序列 init_sequence_f里面的一些 列函数,init_sequence_f 里面包含了一系列的初始化函数

/* 初始化序列函数数组 */
static init_fnc_t init_sequence_f[] = {
    setup_mon_len,    /* 设置gd的mon_len成员,表示uboot代码的长度 */
    initf_malloc,    /* 初始化gd中与malloc相关的成员 */
    initf_console_record,
    /* 初始化arm架构相关的东西 */
    arch_cpu_init,        /* basic arch cpu dependent setup */
    initf_dm,    /* 初始化驱动模型相关 */
    arch_cpu_init_dm,
    mark_bootstage,        /* need timer, go after init dm */
#if defined(CONFIG_BOARD_EARLY_INIT_F)
    board_early_init_f,    /* 与板子的早期外设初始化,imx6ul用来初始化串口 */
#endif

#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || \
        defined(CONFIG_BLACKFIN) || defined(CONFIG_NDS32) || \
        defined(CONFIG_SPARC)
    timer_init,    /* initialize timer */ /* 初始化Cortex-A7中的定时器 */
#endif

#if defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K)
    get_clocks,    /* 获取时钟值 */
#endif

    /* 和环境变量有关,设置gd的env_addr成员 */
    env_init, /* initialize environment */ 
    init_baud_rate,  /* initialze baudrate settings */ /* 初始化串口波特率 */
    serial_init, /* serial communications setup */ /* 初始化串口 */
    console_init_f,   /* stage 1 init of console */ /* 设置console标志位 */
    display_options, /* say that we are here */ /* 显示uboot版本和编译时间字符串 */
    display_text_info,    /* show debugging info if required */
    /* 显示cpu的相关信息 */
    print_cpuinfo,        /* display cpu info (and speed) */ 

#if defined(CONFIG_DISPLAY_BOARDINFO)
    show_board_info,    /* 显示board的相关信息 */
#endif

    INIT_FUNC_WATCHDOG_INIT
    INIT_FUNC_WATCHDOG_RESET

#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
    init_func_i2c,    /* 初始化i2c接口(实际初始化的是SPD_BUS) */
#endif

#if defined(CONFIG_HARD_SPI)
    init_func_spi,    /* i.mx6ul并没有初始化SPI接口 */
#endif
    announce_dram_init,    /* 输出"DRAM:"字符串 */
#if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || \
        defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32)
    dram_init,    /* configure available RAM banks */ /* 获取DDR的大小 */
#endif
    /*
     * Now that we have DRAM mapped and working, we can
     * relocate the code and continue running from DRAM.
     *
     * Reserve memory at end of RAM for (top down in that order):
     *  - area that won't get touched by U-Boot and Linux (optional)
     *  - kernel log buffer
     *  - protected RAM
     *  - LCD framebuffer
     *  - monitor code
     *  - board info struct
     */
    setup_dest_addr,/* 设置目的地址(gd->ram_size,gd->ram_top,gd->relocaddr) */
    reserve_round_4k,    /* 对gd->relocaddr做4K对齐 */

#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \
        defined(CONFIG_ARM)
    reserve_mmu,    /* 留出mmu的TLB表位置 */
#endif

    reserve_trace,    /* 留出ddr调试追踪的内存 */
#if !defined(CONFIG_BLACKFIN)
    reserve_uboot,    /* 留出重定位uboot占用的位置 */
#endif
#ifndef CONFIG_SPL_BUILD
    reserve_malloc,    /* 留出malloc的内存位置和ENV的内存大小 */
    reserve_board,    /* 留出bd所占用的内存大小(80字节) */
#endif
    setup_machine,    
    reserve_global_data,    /* 留出gd_t结构的内存大小(248字节) */
    reserve_fdt,    /* 留出设备树的内存大小 */
    reserve_arch,    /* 空函数 */
    reserve_stacks,    /* 留出栈空间(16字节)并做16字节对齐 */
    setup_dram_config,    /* 设置DRAM的信息 */
    show_dram_config,    /* 显示DRAM的位置 */
    display_new_sp,    /* 显示新的sp位置 */
    INIT_FUNC_WATCHDOG_RESET
    reloc_fdt,    /* 重定位fdt(没有用) */
    setup_reloc,    /* 设置gd结构体的一些其他成员 */
    NULL,
};

代码段参考链接:https://www.cnblogs.com/Cqlismy/p/12006287.html

最终内存分配图如下:
最终内存分配图

relocate_code 函数概要

relocate_code 函数是用于代码拷贝的,此函数定义在文件 arch/arm/lib/relocate.S 中

ENTRY(relocate_code)
    ldr    r1, =__image_copy_start /* r1 <- SRC &__image_copy_start */ //r1保存源image开始地址 __image_copy_start=0XC0100000.
    //r0 = gd->relocaddr,r4 = r0-r1 = 0XF5C46000 - 0XC0100000 = 0X35B46000, r4保存的是偏移量
    subs    r4, r0, r1 /* r4 <- relocation offset */ //r4 = gd->reloc_off,保存偏移地址
    beq    relocate_done    /* skip relocation */ //如果r0和r1相等,则不需要uboot重定位
    ldr    r2, =__image_copy_end  /* r2 <- SRC &__image_copy_end */  //r2保存源image结束地址, __image_copy_end = 0XC01ACD98.

copy_loop:
    ldmia    r1!, {r10-r11}  /* copy from source address [r1] */ //拷贝uboot代码到r10和r1, 拷贝结束后更新r1.
    stmia    r0!, {r10-r11}  /* copy to  target address [r0] */ //将uboot代码写到目的地址, 结束后更新r0.
    cmp    r1, r2     /* until source end address [r2] */ //判断uboot是否拷贝完成
    blo    copy_loop  //循环拷贝

    /*
     * fix .rel.dyn relocations //修复(重定位).rel.dyn段
     */
    ldr    r2, =__rel_dyn_start    /* r2 <- SRC &__rel_dyn_start */
    ldr    r3, =__rel_dyn_end    /* r3 <- SRC &__rel_dyn_end */
fixloop:
    ldmia    r2!, {r0-r1}        /* (r0,r1) <- (SRC location,fixup) */
    and    r1, r1, #0xff
    cmp    r1, #23            /* relative fixup? */
    bne    fixnext

    /* relative fix: increase location by offset */
    add    r0, r0, r4
    ldr    r1, [r0]
    add    r1, r1, r4
    str    r1, [r0]
fixnext:
    cmp    r2, r3
    blo    fixloop

relocate_done:

ENDPROC(relocate_code)

参考链接:https://www.cnblogs.com/Cqlismy/p/12152400.html

编译链接 uboot 的时候会使用到 “-pie”选项,使用“-pie”选项以后会生成一个.rel.dyn 段,uboot 就是靠这个.rel.dyn 来解决重定位问题。

relocate_vectors 函数概要

函数 relocate_vectors 用于重定位向量表,此函数定义在文件 relocate.S 中

board_init_r 函数

board_init_r 函数定义在文件 common/board_r.c 中
board_init_f 并没有初始化所有的外设,还需要做一些后续工作,这 些后续工作就是由函数 board_init_r 来完成的
程序中调用 initcall_run_list 函数来执行初始化序列 init_sequence_r

init_fnc_t init_sequence_r[] = {
    initr_trace, /* 初始化buffer跟踪相关 */
    initr_reloc, /* 标记重定位完成 */
    
#ifdef CONFIG_ARM
    initr_caches, /* 使能caches */
#endif

    initr_reloc_global_data, /* 初始化gd的一些成员变量 */
    initr_barrier,
    initr_malloc,  /* 初始化malloc区域 */
    initr_console_record, /* 初始化控制台相关内容 */
    bootstage_relocate, /* 启动状态重定位 */
    initr_bootstage, /* 初始化bootstage */
    
#if defined(CONFIG_ARM) || defined(CONFIG_NDS32)
    board_init,    /* Setup chipselects */
#endif

    stdio_init_tables, /* stdio相关初始化 */
    initr_serial,  /* serial接口初始化 */
    initr_announce,
    INIT_FUNC_WATCHDOG_RESET
    power_init_board,
    
#ifndef CONFIG_SYS_NO_FLASH
    initr_flash,
#endif

    INIT_FUNC_WATCHDOG_RESET

#ifdef CONFIG_CMD_NAND
    initr_nand,  /* 初始化nand flash */
#endif
 #ifdef CONFIG_GENERIC_MMC	   initr_mmc, #endif
    initr_env,  /* 环境变量初始化 */
    INIT_FUNC_WATCHDOG_RESET
    initr_secondary_cpu,
    stdio_add_devices,
    initr_jumptable,

    console_init_r,        /* fully init console as a device */
    interrupt_init,
    
#if defined(CONFIG_ARM) || defined(CONFIG_AVR32)
    initr_enable_interrupts,
#endif

#ifdef CONFIG_CMD_NET
    initr_ethaddr,  /* 获取mac地址 */
#endif

#ifdef CONFIG_BOARD_LATE_INIT
    board_late_init,  /* 板子后期外设初始化 */
#endif

#ifdef CONFIG_CMD_NET
    INIT_FUNC_WATCHDOG_RESET
    initr_net,  /* 初始化板子的网络设备 */
#endif

    run_main_loop,
};

初始化完成后进入run_main_loop函数,该函数为处理命令的主循环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值