_start函数分析(1)
1、_start函数
_start函数在vectors.S中,部分代码如下,先执行了reset函数,下面的是中断向量表。
_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
2、_start->reset->save_boot_params_ret函数
reset->save_boot_params->save_boot_params_ret,最终转化成分析save_boot_params_ret函数
reset:
/* Allow the board to save important registers */
b save_boot_params
ENTRY(save_boot_params)
b save_boot_params_ret
save_boot_params_ret函数主要做了设置处理器处于SVC模式,禁止FIQ和IRQ,然后重定位了中断向量表,然后执行cpu_init_cp15,cpu_init_crit,_main函数。
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
2.1 CPSR寄存器和CP15协处理器
CPSR和CP15的介绍具体见:
https://blog.csdn.net/u010681589/article/details/121024196
CP15这边主要用来设置向量表重定位的,,bit13 为 V 位,此位是向量表控制位,当为 0 的时候向量表基地址为 0X00000000,软件可以重定位向量表。为 1 的时候向量表基地址为 0XFFFF0000,软件不能重定位向量表。最终将向量表的首地址定位到uboot的首地址。
2.2 save_boot_params_ret-> cpu_init_cp15函数
以下为cpu_init_cp15函数代码,删除了一些没有用到的宏定义代码,主要关闭cache, MMU, TLBs。
cache:处理器内部的一小块高速SRAM内存,用于缓存普通内存,容量相对较大。
TLBs:在硬件上和cache一样,是处理器内部的一小块高速SRAM内存,用于缓存,与cache不同的是,它专门缓存存放在内存中的页表,容量相对较小。
MMU:mmu可以实现虚拟内存和内存保护等功能,完成对内存的操作和管理。
为什么在启动的时候需要先关闭他们?
在板子启动的时候是没有对mmu进行初始化的,而且这个时候也用不到mmu,为了避免他们影响启动时的初始化,所以需要先关闭mmu、cache和TLB。
/*************************************************************************
*
* cpu_init_cp15
*
* Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless
* CONFIG_SYS_ICACHE_OFF is defined.
*
*************************************************************************/
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
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
mov pc, r5 @ back to my caller
ENDPROC(cpu_init_cp15)
2.3 save_boot_params_ret->cpu_init_crit函数
cpu_init_crit会调用lowlevel_init 进行具体的初始化,比如时钟,IO,内存等。
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)
2.3.1 lowlevel_init函数
lowlevel_init函数代码如下。先设置了堆栈指针的首地址,经过计算
CONFIG_SYS_INIT_SP_ADDR = CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE =0x00900000 + 0x00020000 - 256 = 0X0091FF00;
此时sp = 0X0091FF00;0X00900000-0X0091FFFF为128KB,最后的256B用来保存gdata。
gdata是一个全局变量,并存在r9寄存器中,此时内部RAM分配情况如下图所示。最后执行s_init函数
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)
2.3.1.1 s_init函数
s_init函数的部分代码如下
这个是为IMX6Q处理器准备的函数,6ULL直接返回
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;
2.3 save_boot_params_ret->main
这个函数主要初始化C语言运行,并调用board_init_f进行一些初始化。