1、前言
Linux系统的启动需要一个bootloader程序,该bootloader程序会先初始化DDR等外设,然后将Linux内核从flash中拷贝到DDR中,最后启动Linux内核,uboot的全称为Universal Boot Loader,Linux系统中常用的bootloader就是uboot,接下来,将会进行简单的uboot启动流程分析,uboot的源码为uboot-imx-rel_imx_4.15_2.1.0。
2、uboot入口
在分析之前,需要对整个uboot工程进行编译,生成一些分析时需要用到的文件,例如链接文件uboot.lds和uboot映射文件uboot.map,通过链接文件,可以找到uboot的入口,找到uboot启动后运行的第一行代码。
在uboot源码根目录下找到链接文件uboot.lds,如下:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) //当前入口_start SECTIONS { . = 0x00000000; . = ALIGN(4); .text : { *(.__image_copy_start) //入口 *(.vectors) //中断向量表 arch/arm/cpu/armv7/start.o (.text*) //arch/arm/cpu/armv7/start.S代码段 *(.text*) } . = ALIGN(4); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } . = ALIGN(4); .data : { *(.data*) } . = ALIGN(4); . = .; . = ALIGN(4); .u_boot_list : { KEEP(*(SORT(.u_boot_list*))); } . = ALIGN(4); .image_copy_end : { *(.__image_copy_end) } .rel_dyn_start : { *(.__rel_dyn_start) } .rel.dyn : { *(.rel*) } .rel_dyn_end : { *(.__rel_dyn_end) } .end : { *(.__end) } _image_binary_end = .; . = ALIGN(4096); .mmutable : { *(.mmutable) } .bss_start __rel_dyn_start (OVERLAY) : { KEEP(*(.__bss_start)); __bss_base = .; } .bss __bss_base (OVERLAY) : { *(.bss*) . = ALIGN(4); __bss_limit = .; } .bss_end __bss_limit (OVERLAY) : { KEEP(*(.__bss_end)); } .dynsym _image_binary_end : { *(.dynsym) } .dynbss : { *(.dynbss) } .dynstr : { *(.dynstr*) } .dynamic : { *(.dynamic*) } .plt : { *(.plt*) } .interp : { *(.interp*) } .gnu.hash : { *(.gnu.hash) } .gnu : { *(.gnu*) } .ARM.exidx : { *(.ARM.exidx*) } .gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) } }
在上面的链接文件中,可以确定uboot的入口为_start,该定义在arch/arm/lib/vectors.S文件中:
_start: #ifdef CONFIG_SYS_DV_NOR_BOOT_CFG .word CONFIG_SYS_DV_NOR_BOOT_CFG #endif b reset //中断向量表,跳转到reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq
进入到_start后,运行b reset代码,跳转到reset中运行,b reset后面跟照中断向量表,reset的定义,对于不通的架构不一样,对于NXP的i.mx6ul芯片,该定义在arch/arm/cpu/armv7/start.S文件中:
.globl reset .globl save_boot_params_ret reset: /* Allow the board to save important registers */ b save_boot_params //跳到save_boot_params save_boot_params_ret: /* * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode, * except if in HYP mode already */ mrs r0, cpsr //读取cpsr寄存器的值到r0寄存器中(cps:bit0~bit4保存处理器工作模式) and r1, r0, #0x1f @ mask mode bits //r0的值与0x1f相与,结果保存到r1寄存器 teq r1, #0x1a @ test for HYP mode //判断当前处理器模式是否是HYP模式 bicne r0, r0, #0x1f @ clear all mode bits //如果CPU不处于HYP模式,则清除bit0~bit4 orrne r0, r0, #0x13 @ set SVC mode //设置为SVC模式 orr r0, r0, #0xc0 @ disable FIQ and IRQ //禁止FIQ和IRQ msr cpsr,r0 //将当前r0寄存器的值回写到cpsr寄存器 /* * Setup vector: * (OMAP4 spl TEXT_BASE is not 32 byte aligned. * Continue to use ROM code vector only in OMAP4 spl) */ #if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD)) /* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */ mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR Register //读取SCTLR寄存器 bic r0, #CR_V @ V = 0 //设置V = 0 mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register /* Set vector address in CP15 VBAR register */ ldr r0, =_start //设置vector地址到CP15 VBAR寄存器 mcr p15, 0, r0, c12, c0, 0 @Set VBAR #endif /* the mask ROM code should have PLL and others stable */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_cp15 //跳转到cpu_init_cp15 bl cpu_init_crit //跳转到cpu_init_crit #endif bl _main //跳转到_main
在上面的代码中主要是对arm处理器的运行模式进行设置,对一些寄存器进行赋值操作,对于cpu_init_crit函数的实现如下:
#ifndef CONFIG_SKIP_LOWLEVEL_INIT /************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * *************************************************************************/ ENTRY(cpu_init_crit) /* * Jump to board specific initialization... * The Mask ROM will have already initialized * basic memory. Go here to bump up clock rate and handle * wake up conditions. */ b lowlevel_init @ go setup pll,mux,memory //跳到lowlevel_init,设置pll、mux和memory ENDPROC(cpu_init_crit) #endif
在cpu_init_crit函数中,跳到了lowlevel_init函数中运行,接下来,详细分析一下lowlevel_init和_main函数。
扫描二维码关注公众号,回复:
8101952 查看本文章
3、lowlevel_init函数
4、_main函数