学习目标:
uboot启动流程分析(1)
学习内容:
学习时间:
2022-07-22
学习产出:
uboot函数的入口是arch/arm/lib/vectors.S中的_start函数,部分源码如下:
_start:
#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
.word CONFIG_SYS_DV_NOR_BOOT_CFG
#endif
b 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
此函数设置中断向量表,跳转到reset函数,此函数位于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
在reset函数中又跳转到了save_boot_params_ret函数中,此函数也在本文件中,函数如下:
ENTRY(save_boot_params)
b save_boot_params_ret @ back to my caller
此函数中又跳转到了save_boot_params_ret函数,此函数也位于本文件中,函数源码如下:
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
and r1, r0, #0x1f @ mask mode bits
teq r1, #0x1a @ test for HYP mode
bicne r0, r0, #0x1f @ clear all mode bits
orrne r0, r0, #0x13 @ set SVC mode
orr r0, r0, #0xc0 @ disable FIQ and IRQ
msr cpsr,r0
/*
* 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
bic r0, #CR_V @ V = 0
mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register
/* Set vector address in CP15 VBAR register */
ldr r0, =_start
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
bl cpu_init_crit
#endif
bl _main
首先设置cpsr寄存的值,使处理器进入FIQ和IRQ中断,然后设置处理器进入SVC32模式。接下来读取CP15中c1寄存器的值到r0寄存器中,也就是读取SCTLR寄存器的值,清楚V位,使能中断向量表重定位,将r0的值设置为_start函数的地址,写入到cp15的c12寄存器,这样就完成了重新定位中断向量表的地址为0x87800000。
继续向下执行,cpu_init_cp15函数,在本文件内有定义,此函数源码为:
ENTRY(cpu_init_cp15)
/*
* Invalidate L1 I/D
*/
mov r0, #0 @ set up for MCR
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array
mcr p15, 0, r0, c7, c10, 4 @ DSB
mcr p15, 0, r0, c7, c5, 4 @ ISB
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
#ifdef CONFIG_SYS_ICACHE_OFF
bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache
#else
orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache
#endif
mcr p15, 0, r0, c1, c0, 0
#ifdef CONFIG_ARM_ERRATA_716044
mrc p15, 0, r0, c1, c0, 0 @ read system control register
orr r0, r0, #1 << 11 @ set bit #11
mcr p15, 0, r0, c1, c0, 0 @ write system control register
#endif
#if (defined(CONFIG_ARM_ERRATA_742230) || defined(CONFIG_ARM_ERRATA_794072))
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 4 @ set bit #4
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_743622
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 6 @ set bit #6
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_751472
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 11 @ set bit #11
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_761320
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 21 @ set bit #21
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_845369
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 22 @ set bit #22
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
mov r5, lr @ Store my Caller
mrc p15, 0, r1, c0, c0, 0 @ r1 has Read Main ID Register (MIDR)
mov r3, r1, lsr #20 @ get variant field
and r3, r3, #0xf @ r3 has CPU variant
and r4, r1, #0xf @ r4 has CPU revision
mov r2, r3, lsl #4 @ shift variant field for combined value
orr r2, r4, r2 @ r2 has combined CPU variant + revision
#ifdef CONFIG_ARM_ERRATA_798870
cmp r2, #0x30 @ Applies to lower than R3p0
bge skip_errata_798870 @ skip if not affected rev
cmp r2, #0x20 @ Applies to including and above R2p0
blt skip_errata_798870 @ skip if not affected rev
mrc p15, 1, r0, c15, c0, 0 @ read l2 aux ctrl reg
orr r0, r0, #1 << 7 @ Enable hazard-detect timeout
push {r1-r5} @ Save the cpu info registers
bl v7_arch_cp15_set_l2aux_ctrl
isb @ Recommended ISB after l2actlr update
pop {r1-r5} @ Restore the cpu info - fall through
skip_errata_798870:
#endif
#ifdef CONFIG_ARM_ERRATA_801819
cmp r2, #0x24 @ Applies to lt including R2p4
bgt skip_errata_801819 @ skip if not affected rev
cmp r2, #0x20 @ Applies to including and above R2p0
blt skip_errata_801819 @ skip if not affected rev
mrc p15, 0, r0, c0, c0, 6 @ pick up REVIDR reg
and r0, r0, #1 << 3 @ check REVIDR[3]
cmp r0, #1 << 3
beq skip_errata_801819 @ skip erratum if REVIDR[3] is set
mrc p15, 0, r0, c1, c0, 1 @ read auxilary control register
orr r0, r0, #3 << 27 @ Disables streaming. All write-allocate
@ lines allocate in the L1 or L2 cache.
orr r0, r0, #3 << 25 @ Disables streaming. All write-allocate
@ lines allocate in the L1 cache.
push {r1-r5} @ Save the cpu info registers
bl v7_arch_cp15_set_acr
pop {r1-r5} @ Restore the cpu info - fall through
skip_errata_801819:
#endif
#ifdef CONFIG_ARM_ERRATA_454179
cmp r2, #0x21 @ Only on < r2p1
bge skip_errata_454179
mrc p15, 0, r0, c1, c0, 1 @ Read ACR
orr r0, r0, #(0x3 << 6) @ Set DBSM(BIT7) and IBE(BIT6) bits
push {r1-r5} @ Save the cpu info registers
bl v7_arch_cp15_set_acr
pop {r1-r5} @ Restore the cpu info - fall through
skip_errata_454179:
#endif
#ifdef CONFIG_ARM_ERRATA_430973
cmp r2, #0x21 @ Only on < r2p1
bge skip_errata_430973
mrc p15, 0, r0, c1, c0, 1 @ Read ACR
orr r0, r0, #(0x1 << 6) @ Set IBE bit
push {r1-r5} @ Save the cpu info registers
bl v7_arch_cp15_set_acr
pop {r1-r5} @ Restore the cpu info - fall through
skip_errata_430973:
#endif
#ifdef CONFIG_ARM_ERRATA_621766
cmp r2, #0x21 @ Only on < r2p1
bge skip_errata_621766
mrc p15, 0, r0, c1, c0, 1 @ Read ACR
orr r0, r0, #(0x1 << 5) @ Set L1NEON bit
push {r1-r5} @ Save the cpu info registers
bl v7_arch_cp15_set_acr
pop {r1-r5} @ Restore the cpu info - fall through
skip_errata_621766:
#endif
mov pc, r5 @ back to my caller
ENDPROC(cpu_init_cp15)
此函数主要用来设置cp15寄存器相关的,如关闭MMU。
继续执行cpu_init_crit函数,此函数也在本文件中定义,函数如下:
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
ENDPROC(cpu_init_crit)
此函数中跳转到lowlevel_init中。
lowlevel_init函数位于 arch/arm/cpu/armv7/lowlevel_init.S中,内容如下:
ENTRY(lowlevel_init)
/*
* Setup a temporary stack. Global data is not available yet.
*/
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
#ifdef CONFIG_SPL_DM
mov r9, #0
#else
/*
* Set up global data for boards that still need it. This will be
* removed soon.
*/
#ifdef CONFIG_SPL_BUILD
ldr r9, =gdata
#else
sub sp, sp, #GD_SIZE
bic sp, sp, #7
mov r9, sp
#endif
#endif
/*
* Save the old lr(passed in ip) and the current lr to stack
*/
push {ip, lr}
/*
* Call the very early init function. This should do only the
* absolute bare minimum to get started. It should not:
*
* - set up DRAM
* - use global_data
* - clear BSS
* - try to start a console
*
* For boards with SPL this should be empty since SPL can do all of
* this init in the SPL board_init_f() function which is called
* immediately after this.
*/
bl s_init
pop {ip, pc}
ENDPROC(lowlevel_init)
首先设置sp的值为CONFIG_SYS_INIT_SP_ADDR,此定义位于include/configs/mx6ullevk.h文件中,定义如下:
#define CONFIG_SYS_INIT_RAM_ADDR IRAM_BASE_ADDR
#define CONFIG_SYS_INIT_RAM_SIZE IRAM_SIZE
#define CONFIG_SYS_INIT_SP_OFFSET \
(CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)
#define CONFIG_SYS_INIT_SP_ADDR \
(CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)
要知道CONFIG_SYS_INIT_SP_ADDR 的值需要知道CONFIG_SYS_INIT_RAM_ADDR 和CONFIG_SYS_INIT_SP_OFFSET的值。而要知道这两个的值,需要知道IRAM_BASE_ADDR和IRAM_SIZE的值,这两个值在文件arch/arm/include/asm/arch-mx6/imx-regs.h中有如下定义,其实就是 IMX6UL/IM6ULL 内部 ocram 的首地址和大小:
#define IRAM_BASE_ADDR 0x00900000
#define IRAM_SIZE 0x00020000
此外还需要知道GENERATED_GBL_DATA_SIZE的值,此值在文件在文件include/generated/generic-asm-offsets.h中有定义:
#define GENERATED_GBL_DATA_SIZE 256 /* (sizeof(struct global_data) + 15) & ~15 @ */
#define GENERATED_BD_INFO_SIZE 80 /* (sizeof(struct bd_info) + 15) & ~15 @ */
#define GD_SIZE 248 /* sizeof(struct global_data) @ */
#define GD_BD 0 /* offsetof(struct global_data, bd) @ */
#define GD_MALLOC_BASE 192 /* offsetof(struct global_data, malloc_base) @ */
#define GD_RELOCADDR 48 /* offsetof(struct global_data, relocaddr) @ */
#define GD_RELOC_OFF 68 /* offsetof(struct global_data, reloc_off) @ */
#define GD_START_ADDR_SP 64 /* offsetof(struct global_data, start_addr_sp) @ */
综上所述,
CONFIG_SYS_INIT_SP_ADDR = CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET =
CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE =
IRAM_BASE_ADDR + IRAM_SIZE - GENERATED_GBL_DATA_SIZE =
0x00900000 + 0x00020000 - 256 = 0X0091FF00
CONFIG_SYS_INIT_SP_OFFSET = 0x00020000 – 256 = 0x1FF00。
CONFIG_SYS_INIT_SP_ADDR = 0x00900000 + 0X1FF00 = 0X0091FF00,
如下图所示

此时 sp 指向 0X91FF00,这属于 IMX6UL/IMX6ULL 的内部 ram。
继续回到lowlevel_init函数中,对sp进行8字节对齐,然后sp 指针减去 GD_SIZE, GD_SIZE 同样在 generic-asm-offsets.h 中定了,大小为248,对 sp 做 8 字节对齐,此时 sp 的地址为 0X0091FF00-248=0X0091FE08,此时 sp 位置如图所示:

接下来将 sp 地址保存在 r9 寄存器中,将 ip 和 lr 压栈,调用函数 s_init,执行完后将入栈的 ip 和 lr 进行出栈,并将 lr 赋给 pc。
s_init函数在文件arch/arm/cpu/armv7/mx6/soc.c中定义,如下所示:
void s_init(void)
{
struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR;
struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
u32 mask480;
u32 mask528;
u32 reg, periph1, periph2;
if (is_cpu_type(MXC_CPU_MX6SX) || is_cpu_type(MXC_CPU_MX6UL) ||
is_cpu_type(MXC_CPU_MX6ULL) || is_cpu_type(MXC_CPU_MX6SLL))
return;
/* Due to hardware limitation, on MX6Q we need to gate/ungate all PFDs
* to make sure PFD is working right, otherwise, PFDs may
* not output clock after reset, MX6DL and MX6SL have added 396M pfd
* workaround in ROM code, as bus clock need it
*/
mask480 = ANATOP_PFD_CLKGATE_MASK(0) |
ANATOP_PFD_CLKGATE_MASK(1) |
ANATOP_PFD_CLKGATE_MASK(2) |
ANATOP_PFD_CLKGATE_MASK(3);
mask528 = ANATOP_PFD_CLKGATE_MASK(1) |
ANATOP_PFD_CLKGATE_MASK(3);
reg = readl(&ccm->cbcmr);
periph2 = ((reg & MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_MASK)
>> MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_OFFSET);
periph1 = ((reg & MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK)
>> MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_OFFSET);
/* Checking if PLL2 PFD0 or PLL2 PFD2 is using for periph clock */
if ((periph2 != 0x2) && (periph1 != 0x2))
mask528 |= ANATOP_PFD_CLKGATE_MASK(0);
if ((periph2 != 0x1) && (periph1 != 0x1) &&
(periph2 != 0x3) && (periph1 != 0x3))
mask528 |= ANATOP_PFD_CLKGATE_MASK(2);
writel(mask480, &anatop->pfd_480_set);
writel(mask528, &anatop->pfd_528_set);
writel(mask480, &anatop->pfd_480_clr);
writel(mask528, &anatop->pfd_528_clr);
}
会判断当前 CPU 类型,如果 CPU 为 MX6SX、 MX6UL、 MX6ULL 或 MX6SLL中 的 任 意 一 种 , 那 么 就 会 直 接 返 回 , 相 当 于 s_init 函 数 什 么 都 没 做 。 所 以 对 于
I.MX6UL/I.MX6ULL 来说, s_init 就是个空函数。从 s_init 函数退出以后进入函数 lowlevel_init,
但是 lowlevel_init 函数也执行完成了,返回到了函数 cpu_init_crit,函数 cpu_init_crit 也执行完
成了,最终返回到 save_boot_params_ret,函数调用路径如图 所示:

接下来要执行的是 save_boot_params_ret 中的_main 函数。
本文详细分析了U-Boot的启动流程,从_arch/arm/lib/vectors.S_中的_start函数开始,经过中断向量表设置、处理器模式配置、内存初始化,最后跳转到_reset函数。在_reset函数中,执行了_save_boot_params_和_save_boot_params_ret_函数,对CPU寄存器进行了操作,包括关闭中断、设置中断向量表和CPU模式。接着,调用_cpu_init_cp15_函数初始化CP15寄存器,然后通过_lowlevel_init_进行更底层的初始化,包括设置堆栈和调用_s_init_函数。整个过程展示了U-Boot如何逐步准备硬件环境以执行_main_函数。

1895

被折叠的 条评论
为什么被折叠?



