zoukankan      html  css  js  c++  java
  • u-boot v2018.01 启动流程分析 简单版(转)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/weixin_39655765/article/details/80058644

        make smdkc100_defconfig
        以被默认支持的smdkc100单板为背景分析u-boot v2018.01

        参考图1可知uboot code链接顺序:

                               图1 u-boot.lds

    一、sections.c (archarmlib)

    第24行:

    char __image_copy_start[0] __attribute__((section(".__image_copy_start")));

        1

        不占内存空间,可在u-boot镜像开始位置生成标签__image_copy_start。

    二、vectors.S (archarmlib)

                        图2 .vectors段头部
    _start:建立异常向量表。

          某些SOC要求Bootloader头部有hook数据用来指导BL0(固化在iROM)将Nand flash中的BootLoader加载到iRAM中。此时需定义CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK,及定义asm/arch/boot0.h 文件,参考archarmincludeasmarch-bcm281xxoot0.h

    三、start.S (archarmcpuarmv7)

    reset:
      1. 设置CPSR:CPU为SVC模式,禁止IRQ/FIQ;
      2. 通过SCTLR和VBAR设置异常向量表的地址到_start;
      3. cpu_init_cp15: 失效TLB、L1 icache、BP数组;关MMU、dcache,开icache和分支预测;将CPU的
       variant + revision存于R2;
      4. cpu_init_crit: 调用lowlevel_init(boardsamsungsmdkc100lowlevel_init.S):
         ① 关闭看门狗;
         ② 设置SRAM;
         ③ 禁止所有中断线,并设为IRQ及清除标志位;
         ④ 初始化uart的引脚;
         ⑤ 初始化tzpc为关闭;
      5. 跳到_main。
    四、crt0.S (archarmlib)

    _main:
      1. 设置sp为0x2f000000;
      2. 调用board_init_f_alloc_reserve (top=0x2f000000)(commoninitoard_init.c):返回top =
       (0x2f000000 - 1KB(malloc) - sizeof(struct global_data) ) & ~0xF ;
      3. r9(gd) = sp = top;
      4. 调用board_init_f_init_reserve (base=top):struct global_data清0,gd->malloc_base设在
       struct global_data之上;如下图所示为建立C语言运行环境的内存分布:

                        图3 C运行环境建立

      5. 调用board_init_f (boot_flags=0)(commonoard_f.c):gd->flags = 0,gd->have_console = 0,执行
       init_sequence_f[]中的函数:
         (1) setup_mon_len():设gd->mon_len为__bss_end-_start;
         (2) fdtdec_setup():gd->fdt_blob设在_end(CONFIG_OF_SEPARATE=> u-boot.bin = uboot+dtb),或用
           default_environment的"fdtcontroladdr"覆盖其值;检查设备树的header;
         (3) initf_malloc():设gd->malloc_limit为(1KB),gd->malloc_ptr = 0;
         (4) log_init(),initf_bootstage(), initf_console_record():空;
         (5) arch_cpu_init():读PRO_ID寄存器内容解析出CPU id到全局变量s5p_cpu_id;
         (6) mach_cpu_init():空;
         (7) initf_dm():初始化dm资源,绑定dm驱动到gd中,扫描设备树中dm设备内容;
         (8) arch_cpu_init_dm():空;
         (9) timer_init():初始化定时器4和gd->arch中的定时器成员;
         (10) env_init():通过默认env_driver初始化env或者gd->env_addr = (ulong)&default_environment[0];,
           gd->env_valid = ENV_VALID;
         (11) init_baud_rate():gd->baudrate设为env中"baudrate"的值;
         (12) serial_init()(driversserialserial-uclass.c):在设备树中找"stdout-path"的节点,用节点找
           UCLASS_SERIAL类设备probe起来,gd->cur_serial_dev = dev;,gd->flags |= GD_FLG_SERIAL_READY;
         (13) console_init_f():gd->have_console = 1,用CONFIG_SILENT_CONSOLE可让控制台“沉默”;
         (14) display_options():打印u-boot版本信息;
         (15) display_text_info():开debug时,打印u-boot code的内存地址;
         (16) print_cpuinfo()(archarmcpuarmv7s5p-commoncpu_info.c):打印设备树"cpu-model"标签的data,
           或字符串S5P和s5p_cpu_id变量值;打印CPU主频;
         (17) show_board_info():打印设备树"model"的data和单板名;
         (18) announce_dram_init(),dram_init():初始化gd->ram_size为通过写读SDRAM校验后得到的实际大小;

            Now that we have DRAM mapped and working, we can relocate the code and continue running from DRAM.

         (19) setup_dest_addr():gd->ram_top,gd->relocaddr设为SDRAM末尾:
           CONFIG_SYS_SDRAM_BASE + gd->ram_size;
         (20) reserve_round_4k():gd->relocaddr调整为4KB对齐;
         (21) reserve_mmu():gd->arch.tlb_size设为16KB,SDRAM为TLB预留空间,设置gd->arch.tlb_addr;
         (22) reserve_video():依赖CONFIG_LCD(未定义),为显存预留内存,初始化gd->fb_base;
         (23) reserve_trace():依赖CONFIG_TRACE(未定义),初始化gd->trace_buff;
         (24) reserve_uboot():预留gd->mon_len个字节给u-boot code,地址存于gd->relocaddr;
         (25) reserve_malloc():预留malloc和env区;
         (26) reserve_board():预留struct bd_info的空间并清零,地址存于gd->bd;
         (27) setup_machine():依赖CONFIG_MACH_TYPE(未定义),设置gd->bd->bi_arch_number;
         (28) reserve_global_data():预留struct global_data的空间,地址存于gd->new_gd;
         (29) reserve_fdt():预留存放设备树的内存,设置gd->fdt_size和gd->new_fdt;
         (30) reserve_bootstage():依赖CONFIG_BOOTSTAGE(未定义),预留存放struct bootstage_data的内存,设置
           gd->new_bootstage;
         (31) reserve_arch():空;
         (32) reserve_stacks():设置gd->irq_sp(需16B对齐),预留为4个word的地址记到gd->start_addr_sp;

            函数(19)到(32)进行的内存划分结果如图4所示:

                        图4 重定位前内存划分
         (33) dram_init_banksize():初始化gd->bd->bi_dram;
         (34) show_dram_config():打印DRAM的大小;
         (35) display_new_sp():打印gd->start_addr_sp的值;
         (36) reloc_fdt():将gd->fdt_blob地址的设备树重定位到gd->new_fdt地址上,更新gd->fdt_blob;
         (37) reloc_bootstage():依赖CONFIG_BOOTSTAGE(未定义),重定位gd->bootstage内容到
           gd->new_bootstage,更新gd->bootstage;
         (38) setup_reloc():初始化gd->reloc_off为重定位目标地址与链接地址之差,重定位gd_t内容到
           gd->new_gd;
      6. 执行sp = gd->start_addr_sp,r9(gd) = gd->new_gd,记录重定位代码后的here地址到lr,执行
       relocate_code (gd->relocaddr)(archarmlib elocate.S),如图5所示:
         ① 将地址__image_copy_start至__image_copy_end的u-boot code 重定位到地址gd->relocaddr;
         ② 通过.rel.dyn段确定u-boot code中所有符号索引的内存地址,用重定位偏移校正符号索引的值[1];

                        图5 动态重定位

          跳转到重定位后的u-boot code执行以下代码:

    here:
      7. 调用relocate_vectors()设置VBAR重定位异常向量表地址到gd->relocaddr;c_runtime_cpu_setup()
       (archarmcpuarmv7start.S)失效icache内容,数据同步内存屏障(DSB),指令同步内存屏障(ISB);执行
       memset(__bss_start,__bss_end,__bss_end-__bss_start)清零BSS段;
      8. coloured_LED_init(),red_led_on(),空;
      9. 执行board_init_r (gd, gd->relocaddr);正式进入bootloader第二阶段。
    五、board_init_r(gd, gd->relocaddr) (common/board_r.c)

      1. gd->flags &= ~GD_FLG_LOG_READY;:指示log系统未初始化;
      2. 调用init_sequence_r[]中函数,打印函数指针链接地址和重定位地址(需开DEBUG):
        (1) initr_trace():依赖CONFIG_TRACE(未定义),trace system函数未实现;
        (2) initr_reloc():gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT;标志重定位完成;
        (3) initr_caches():调用arch/arm/mach-s5pc1xx/cache.c函数,开dcache (undef CONFIG_SYS_DCACHE_OFF);
        (4) initr_reloc_global_data():重定位全局变量:monitor_flash_len,gd->fdt_blob(CONFIG_OF_EMBED),
         EFI的扩展固件(CONFIG_EFI_LOADER);
        (5) initr_barrier():空;
        (6) initr_malloc():初始化malloc功能和清零malloc区;
        (7) log_init():依赖CONFIG_LOG(未定义),初始化log驱动;
        (8) initr_bootstage():设进度为BOOTSTAGE_ID_START_UBOOT_R,并记到bootstage(依赖CONFIG_BOOTSTAGE
         -未定义),show_boot_progress()(未实现)提示进度,枚举bootstage_id罗列了进度id;
        (9) initr_console_record():依赖CONFIG_CONSOLE_RECORD(未定义),给console record功能分配内存;
        (10) bootstage_relocate():依赖CONFIG_BOOTSTAGE(未定义),重定位gd->bootstage的内容;
        (11) initr_of_live():依赖CONFIG_OF_LIVE(未定义),用gd->fdt_blob在堆上建立设备树;
        (12) initr_dm():依赖CONFIG_DM,初始化驱动模型,绑定所有设备(使用U_BOOT_DEVICE或设备树中声明)和
         驱动(U_BOOT_DRIVER声明)并probe;
        (13) board_init():smc9115连到SOC接口和对应SROMC的初始化,保存机器ID到gd->bd->bi_arch_number,
         设置gd->bd->bi_boot_params保存引导操作系统的启动参数;
        (14) efi_memory_init():依赖CONFIG_EFI_LOADER,初始化EFI功能及分配内存;
        (15) stdio_init_tables():初始化标准输入输出设备链表;
        (16) initr_serial():调用drivers/serial/serial-uclass.c(依赖CONFIG_DM_SERIAL),在设备树alias节点
         获得属性"stdout-path"或"console",从而得到作为标准输入输出的设备节点,生成UCLASS_SERIAL类的
         udevice来匹配兼容的驱动及probe;该串行设备记录到gd->cur_serial_dev;标志GD_FLG_SERIAL_READY;;
        (17) initr_announce():打印u-boot重定位后起始地址(需开DEBUG);
        (18) power_init_board():空;
        (19) initr_nand():依赖CONFIG_CMD_NAND,调用nand硬件驱动之board_nand_init()填充nand_chip的成员,
         架构层通过nand驱动扫描外部nand设备,从而完善MTD原始设备mtd_info并注册到MTD子系统;打印nand
         的容量;
        (20) initr_env():通用env层(env/env.c)调用env硬件驱动层(若定义CONFIG_ENV_IS_IN_NAND则在env/nand.c),
         加载nand中CONFIG_ENV_OFFSET开始的env数据到栈中,检查crc成功则将其(失败则使用default_environment
         )复制到堆中,内存地址记录进env_htab,标志置位GD_FLG_ENV_READY或GD_FLG_ENV_DEFAULT;插入或设置环
         境变量fdtcontroladdr为gd->fdt_blob;
        (21) initr_secondary_cpu():空;
        (22) stdio_add_devices():调用drv_xxx_init()(需开CONFIG_XXX),如drv_lcd_init()(需定义CONFIG_LCD),
         drv_system_init()则关于串口;驱动通用层填充stdio_dev并注册添加到标准输入输出链表上,在硬件驱动
         层做硬件初始化;
        (23) initr_jumptable():为函数跳转表(struct jt_funcs定义)分配内存并记录内存地址到gd->jt;
        (24) console_init_r():定义CONFIG_SILENT_CONSOLE和环境变量"silent"可标志GD_FLG_SILENT,在标准输入输
         出设备表(stdio_add_devices()生成,common/console.c)将首个标志为DEV_FLAGS_INPUT或DEV_FLAGS_OUTPUT作
         为控制台io设备,设置环境变量”stdxxx”为设备名,标志GD_FLG_DEVINIT;
        (25) interrupt_init(),initr_enable_interrupts:关于irq栈的设置;
        (26) initr_ethaddr():设置gd->bd->bi_enetaddr为环境变量"ethaddr"的值;
        (27) initr_net():通过网络设备驱动通用层(net/eth_legacy.c)调用硬件驱动层smc911x_initialize()初始化网
         路设备,检测环境变量"ethaddr"值有效性,为空则生成随机MAC地址(需开CONFIG_NET_RANDOM_ETHADDR),
         网络设备名记录到环境变量"ethact";
        (28) run_main_loop():初始化hush解析器(CONFIG_HUSH_PARSER),用环境变量"bootdelay"或设备树节点config
         的属性bootdelay作为启动延迟时间,通过hush解析控制台输入的内容打断倒计时并进入命令行;倒计时期间
         控制台无输入则执行环境变量或设备树/config节点的bootcmd,最后执行命令bootm 0x30007FC0;
    六、bootm 0x30007FC0 (cmd/bootm.c)

      1. do_bootm(...)执行该命令,作命令的解析;
      2. do_bootm_states(...),如下内容:
      3. bootm_start():环境变量verify决定后续是否对kernel镜像进行校验和检查,lmb(logical memory blocks)相关内
       容的初始化;
      4. bootm_find_os():
       (1) boot_get_kernel():获取kernel镜像格式为IMAGE_FORMAT_LEGACY,验证镜像hcrc,打印镜像的名字、类型、数
        据大小、加载地址和入口地址,验证dcrc(依赖env的verify),判断arch是否支持;
       (2) 解析镜像的结果填充images.os的成员,kernel入口地址记到images.ep,镜像头部地址记到images.os.start;
      5. bootm_find_other():
       (1) boot_get_ramdisk():解析ramdisk镜像,bootm第三个参数为其地址(如bootm xxx yyy,yyy为对应地址);
       (2) boot_get_fdt():获取和解析设备树镜像内容,设备树镜像的起始地址需在bootm命令第四个参数指明,如
        bootm xxx yyy zzz,zzz为对应地址;
      6. bootm_load_os():解压os数据或移动到images->os.load地址,所以kernel应有Load Address=Entry Point;
      7. boot_ramdisk_high():重新定位并初始化ramdisk,需定义CONFIG_SYS_BOOT_RAMDISK_HIGH;
      8. bootm_os_get_boot_func(images->os.os)根据os类型获得启动函数boot_fn = do_bootm_linux;
      9. do_bootm_linux(BOOTM_STATE_OS_PREP, argc, argv, images): boot_prep_linux():若未指定传递给kernel的设
        备树地址,则建立各种tag到地址gd->bd->bi_boot_params;
      10. boot_selected_os():通过函数指针boot_fn调用do_bootm_linux(BOOTM_STATE_OS_GO, ...),进而调用
        boot_jump_linux(images, BOOTM_STATE_OS_GO):
       (1) 通过gd->bd->bi_arch_number或者环境变量machid获得机器码;
       (2) announce_and_cleanup():打印提示开始启动内核,注销驱动模型下设备驱动;调用cleanup_before_linux():
        关L1/2 D-cache和MMU,冲刷掉dcache内数据;关I-cache,失效I-cache内条目,失效整个分支预测器阵列;
        执行数据和指令内存屏障,确保前面的操作完成;
       (3) kernel_entry(0, machid, r2):参数r2传递启动参数(tag或设备树)的内存地址,正式跳转到kernel。
    参考文献

      [1] fireaxe. PIC(与位置无关代码)在u-boot上的实现[EB/OL]. ChinaUnix,2014
    ————————————————
    版权声明:本文为CSDN博主「Lucifer_Zhu」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/weixin_39655765/article/details/80058644

  • 相关阅读:
    阿诺尔德给5至15岁孩子出的数学题
    上手机器学习,从搞懂这十大经典算法开始
    海报模板
    测度论--长度是怎样炼成的[zz]
    柯西不是你
    搭建Web部署环境
    搭建jdk环境
    Win10远程桌面 出现 身份验证错误,要求的函数不受支持,这可能是由于CredSSP加密Oracle修正 解决方法
    Web开发技术选型之Java与PHP
    从java到web前端再到php,一路走来的小总结
  • 原文地址:https://www.cnblogs.com/idyllcheung/p/11880937.html
Copyright © 2011-2022 走看看