2、u-boot-2016 - _start、reset、_main

i.MX6Q - u-boot_2016

飞凌嵌入式开发板 OKMX6Q_C 1G-DDR, 8G-EMMC版本。

u-boot.map

line 901: Address of section .text set to 0x17800000

这是u-boot的链接实际物理地址,也就是最开始U-boot被加载到这里运行。

line 910 - line 920:

 *(.vectors)
 .vectors       0x0000000017800000      0x2e8 arch/arm/lib/built-in.o
                0x0000000017800000                _start
                0x0000000017800020                _undefined_instruction
                0x0000000017800024                _software_interrupt
                0x0000000017800028                _prefetch_abort
                0x000000001780002c                _data_abort
                0x0000000017800030                _not_used
                0x0000000017800034                _irq
                0x0000000017800038                _fiq
                0x0000000017800040                IRQ_STACK_START_IN
这里是一段函数指针,也叫异常向量表。源码在 arch/arm/lib/vectors.S
 (关于中断向量表的描述以及顺序关系需要参考ARM的参考手册)。
1、_start :
#include <config.h>

/*
 *************************************************************************
 *
 * Symbol _start is referenced elsewhere, so make it global
 *
 *************************************************************************
 */

.globl _start

/*
 *************************************************************************
 *
 * Vectors have their own section so linker script can map them easily
 *
 *************************************************************************
 */

	.section ".vectors", "ax"

/*
 *************************************************************************
 *
 * Exception vectors as described in ARM reference manuals
 *
 * Uses indirect branch to allow reaching handlers anywhere in memory.
 *
 *************************************************************************
 */

_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

/*
 *************************************************************************
 *
 * Indirect vectors table
 *
 * Symbols referenced here must be defined somewhere else
 *
 *************************************************************************
 */

	.globl	_undefined_instruction
	.globl	_software_interrupt
	.globl	_prefetch_abort
	.globl	_data_abort
	.globl	_not_used
	.globl	_irq
	.globl	_fiq

_undefined_instruction:	.word undefined_instruction
_software_interrupt:	.word software_interrupt
_prefetch_abort:	.word prefetch_abort
_data_abort:		.word data_abort
_not_used:		.word not_used
_irq:			.word irq
_fiq:			.word fiq

	.balignl 16,0xdeadbeef

/*
 *************************************************************************
 *
 * Interrupt handling
 *
 *************************************************************************
 */

/* SPL interrupt handling: just hang */

#ifdef CONFIG_SPL_BUILD

	.align	5
undefined_instruction:
software_interrupt:
prefetch_abort:
data_abort:
not_used:
irq:
fiq:

1:
	bl	1b			/* hang and never return */

#else	/* !CONFIG_SPL_BUILD */

/* IRQ stack memory (calculated at run-time) + 8 bytes */
.globl IRQ_STACK_START_IN
IRQ_STACK_START_IN:
	.word	0x0badc0de

#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
	.word	0x0badc0de

/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
	.word 0x0badc0de

#endif /* CONFIG_USE_IRQ */

@
@ IRQ stack frame.
@
#define S_FRAME_SIZE	72

#define S_OLD_R0	68
#define S_PSR		64
#define S_PC		60
#define S_LR		56
#define S_SP		52

#define S_IP		48
#define S_FP		44
#define S_R10		40
#define S_R9		36
#define S_R8		32
#define S_R7		28
#define S_R6		24
#define S_R5		20
#define S_R4		16
#define S_R3		12
#define S_R2		8
#define S_R1		4
#define S_R0		0

#define MODE_SVC 0x13
#define I_BIT	 0x80

/*
 * use bad_save_user_regs for abort/prefetch/undef/swi ...
 * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
 */

	.macro	bad_save_user_regs
	@ carve out a frame on current user stack
	sub	sp, sp, #S_FRAME_SIZE
	stmia	sp, {r0 - r12}	@ Save user registers (now in svc mode) r0-r12
	ldr	r2, IRQ_STACK_START_IN
	@ get values for "aborted" pc and cpsr (into parm regs)
	ldmia	r2, {r2 - r3}
	add	r0, sp, #S_FRAME_SIZE		@ grab pointer to old stack
	add	r5, sp, #S_SP
	mov	r1, lr
	stmia	r5, {r0 - r3}	@ save sp_SVC, lr_SVC, pc, cpsr
	mov	r0, sp		@ save current stack into r0 (param register)
	.endm

	.macro	irq_save_user_regs
	sub	sp, sp, #S_FRAME_SIZE
	stmia	sp, {r0 - r12}			@ Calling r0-r12
	@ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good.
	add	r8, sp, #S_PC
	stmdb	r8, {sp, lr}^		@ Calling SP, LR
	str	lr, [r8, #0]		@ Save calling PC
	mrs	r6, spsr
	str	r6, [r8, #4]		@ Save CPSR
	str	r0, [r8, #8]		@ Save OLD_R0
	mov	r0, sp
	.endm

	.macro	irq_restore_user_regs
	ldmia	sp, {r0 - lr}^			@ Calling r0 - lr
	mov	r0, r0
	ldr	lr, [sp, #S_PC]			@ Get PC
	add	sp, sp, #S_FRAME_SIZE
	subs	pc, lr, #4		@ return & move spsr_svc into cpsr
	.endm

	.macro get_bad_stack
	ldr	r13, IRQ_STACK_START_IN		@ setup our mode stack

	str	lr, [r13]	@ save caller lr in position 0 of saved stack
	mrs	lr, spsr	@ get the spsr
	str	lr, [r13, #4]	@ save spsr in position 1 of saved stack
	mov	r13, #MODE_SVC	@ prepare SVC-Mode
	@ msr	spsr_c, r13
	msr	spsr, r13	@ switch modes, make sure moves will execute
	mov	lr, pc		@ capture return pc
	movs	pc, lr		@ jump to next instruction & switch modes.
	.endm

	.macro get_irq_stack			@ setup IRQ stack
	ldr	sp, IRQ_STACK_START
	.endm

	.macro get_fiq_stack			@ setup FIQ stack
	ldr	sp, FIQ_STACK_START
	.endm

/*
 * exception handlers
 */

	.align  5
undefined_instruction:
	get_bad_stack
	bad_save_user_regs
	bl	do_undefined_instruction

	.align	5
software_interrupt:
	get_bad_stack
	bad_save_user_regs
	bl	do_software_interrupt

	.align	5
prefetch_abort:
	get_bad_stack
	bad_save_user_regs
	bl	do_prefetch_abort

	.align	5
data_abort:
	get_bad_stack
	bad_save_user_regs
	bl	do_data_abort

	.align	5
not_used:
	get_bad_stack
	bad_save_user_regs
	bl	do_not_used

#ifdef CONFIG_USE_IRQ

	.align	5
irq:
	get_irq_stack
	irq_save_user_regs
	bl	do_irq
	irq_restore_user_regs

	.align	5
fiq:
	get_fiq_stack
	/* someone ought to write a more effiction fiq_save_user_regs */
	irq_save_user_regs
	bl	do_fiq
	irq_restore_user_regs

#else

	.align	5
irq:
	get_bad_stack
	bad_save_user_regs
	bl	do_irq

	.align	5
fiq:
	get_bad_stack
	bad_save_user_regs
	bl	do_fiq

#endif /* CONFIG_USE_IRQ */

#endif	/* CONFIG_SPL_BUILD */

预处理后如下:
_start:

	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

	.globl	_undefined_instruction
	.globl	_software_interrupt
	.globl	_prefetch_abort
	.globl	_data_abort
	.globl	_not_used
	.globl	_irq
	.globl	_fiq

_undefined_instruction:	.word undefined_instruction
_software_interrupt:	.word software_interrupt
_prefetch_abort:	.word prefetch_abort
_data_abort:		.word data_abort
_not_used:		.word not_used
_irq:			.word irq
_fiq:			.word fiq

	.balignl 16,0xdeadbeef

globl IRQ_STACK_START_IN
IRQ_STACK_START_IN:
	.word	0x0badc0de

@
@ IRQ stack frame.
@
#define S_FRAME_SIZE	72

#define S_OLD_R0	68
#define S_PSR		64
#define S_PC		60
#define S_LR		56
#define S_SP		52

#define S_IP		48
#define S_FP		44
#define S_R10		40
#define S_R9		36
#define S_R8		32
#define S_R7		28
#define S_R6		24
#define S_R5		20
#define S_R4		16
#define S_R3		12
#define S_R2		8
#define S_R1		4
#define S_R0		0

#define MODE_SVC 0x13
#define I_BIT	 0x80

/*
 * use bad_save_user_regs for abort/prefetch/undef/swi ...
 * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
 */

	.macro	bad_save_user_regs
	@ carve out a frame on current user stack
	sub	sp, sp, #S_FRAME_SIZE
	stmia	sp, {r0 - r12}	@ Save user registers (now in svc mode) r0-r12
	ldr	r2, IRQ_STACK_START_IN
	@ get values for "aborted" pc and cpsr (into parm regs)
	ldmia	r2, {r2 - r3}
	add	r0, sp, #S_FRAME_SIZE		@ grab pointer to old stack
	add	r5, sp, #S_SP
	mov	r1, lr
	stmia	r5, {r0 - r3}	@ save sp_SVC, lr_SVC, pc, cpsr
	mov	r0, sp		@ save current stack into r0 (param register)
	.endm

	.macro	irq_save_user_regs
	sub	sp, sp, #S_FRAME_SIZE
	stmia	sp, {r0 - r12}			@ Calling r0-r12
	@ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good.
	add	r8, sp, #S_PC
	stmdb	r8, {sp, lr}^		@ Calling SP, LR
	str	lr, [r8, #0]		@ Save calling PC
	mrs	r6, spsr
	str	r6, [r8, #4]		@ Save CPSR
	str	r0, [r8, #8]		@ Save OLD_R0
	mov	r0, sp
	.endm

	.macro	irq_restore_user_regs
	ldmia	sp, {r0 - lr}^			@ Calling r0 - lr
	mov	r0, r0
	ldr	lr, [sp, #S_PC]			@ Get PC
	add	sp, sp, #S_FRAME_SIZE
	subs	pc, lr, #4		@ return & move spsr_svc into cpsr
	.endm

	.macro get_bad_stack
	ldr	r13, IRQ_STACK_START_IN		@ setup our mode stack

	str	lr, [r13]	@ save caller lr in position 0 of saved stack
	mrs	lr, spsr	@ get the spsr
	str	lr, [r13, #4]	@ save spsr in position 1 of saved stack
	mov	r13, #MODE_SVC	@ prepare SVC-Mode
	@ msr	spsr_c, r13
	msr	spsr, r13	@ switch modes, make sure moves will execute
	mov	lr, pc		@ capture return pc
	movs	pc, lr		@ jump to next instruction & switch modes.
	.endm

	.macro get_irq_stack			@ setup IRQ stack
	ldr	sp, IRQ_STACK_START
	.endm

	.macro get_fiq_stack			@ setup FIQ stack
	ldr	sp, FIQ_STACK_START
	.endm

/*
 * exception handlers
 */
	.align  5
undefined_instruction:
	get_bad_stack
	bad_save_user_regs
	bl	do_undefined_instruction

	.align	5
software_interrupt:
	get_bad_stack
	bad_save_user_regs
	bl	do_software_interrupt

	.align	5
prefetch_abort:
	get_bad_stack
	bad_save_user_regs
	bl	do_prefetch_abort

	.align	5
data_abort:
	get_bad_stack
	bad_save_user_regs
	bl	do_data_abort

	.align	5
not_used:
	get_bad_stack
	bad_save_user_regs
	bl	do_not_used

	.align	5
irq:
	get_bad_stack
	bad_save_user_regs
	bl	do_irq

	.align	5
fiq:
	get_bad_stack
	bad_save_user_regs
	bl	do_fiq

这一部分都链接到了vectors段,从u-boot.lds脚本可以看出 vectors 段放在最首位,然后在_start上面有一段
.section ".vectors", "ax" 表示下面代码编译后链接到vectors段内。
开始几行是异常向量指针,指向下面定义的标号,这里汇编写的不过多深究。
系统正常启动,一上电执行的是reset异常。在源码中 _start 标号后的第一条指令是 b reset 。这是汇编语言,直接跳转到reset函数。

2、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
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

/*------------------------------------------------------------------------------*/

ENTRY(c_runtime_cpu_setup)
/*
 * If I-cache is enabled invalidate it
 */
#ifndef CONFIG_SYS_ICACHE_OFF
	mcr	p15, 0, r0, c7, c5, 0	@ invalidate icache
	mcr     p15, 0, r0, c7, c10, 4	@ DSB
	mcr     p15, 0, r0, c7, c5, 4	@ ISB
#endif

	bx	lr

ENDPROC(c_runtime_cpu_setup)

/*************************************************************************
 *
 * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)
 *	__attribute__((weak));
 *
 * Stack pointer is not yet initialized at this moment
 * Don't save anything to stack even if compiled with -O0
 *
 *************************************************************************/
ENTRY(save_boot_params)
	b	save_boot_params_ret		@ back to my caller
ENDPROC(save_boot_params)
	.weak	save_boot_params

/*************************************************************************
 *
 * 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

#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)

#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
ENDPROC(cpu_init_crit)
#endif
代码较长,去掉未定义宏和不相关代码后如下:
	.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 在本文件内下方。
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
	// 这一段关掉中断,把cpu设置成 SVC32模式

/*
 * Setup vector:
 * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
 * Continue to use ROM code vector only in OMAP4 spl)
 */
	/* 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
	// 这里通过协处理器 p15 设置向量表,向量表的基地址就是 _start
	
	/* the mask ROM code should have PLL and others stable */
	bl	cpu_init_cp15
	// 跳转到 cpu_init_cp15 ,执行完毕跳转回来
	bl	cpu_init_crit
	// 跳转到 cpu_init_crit,执行完毕跳转回来

	bl	_main

/*------------------------------------------------------------------------------*/

ENTRY(c_runtime_cpu_setup)
/*
 * If I-cache is enabled invalidate it
 */
	mcr	p15, 0, r0, c7, c5, 0	@ invalidate icache
	mcr     p15, 0, r0, c7, c10, 4	@ DSB
	mcr     p15, 0, r0, c7, c5, 4	@ ISB

	bx	lr

ENDPROC(c_runtime_cpu_setup)

/*************************************************************************
 *
 * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)
 *	__attribute__((weak));
 *
 * Stack pointer is not yet initialized at this moment
 * Don't save anything to stack even if compiled with -O0
 *
 *************************************************************************/
ENTRY(save_boot_params)
	b	save_boot_params_ret		@ back to my caller
	// 这里直接跳转到 sava_boot_parans_ret 处。
	// sava_boot_parans_ret 标号在 b save_boot_params 下面一行,相当于什么都没做跳转回去了。
ENDPROC(save_boot_params)
	.weak	save_boot_params
	// .weak 表示这个 save_boot_params 是弱链接,如果其他地方也定义了 save_boot_params 标号,则链接另外的标号。

/*************************************************************************
 *
 * 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
	// 通过p15协处理器关掉 一级缓存的 I-Cache 和 D-Cache
	
	/*
	 * 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

	orr	r0, r0, #0x00001000	@ set bit 12 (I) I-cache

	mcr	p15, 0, r0, c1, c0, 0
	// 通过p15协处理器关掉 MMU 和 二级缓存的 I-Cache

	// 下面的操作根据开始的宏定义看是修正ARM的一些bug
	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

	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

	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

	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

	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

	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)

/*************************************************************************
 *
 * 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
ENDPROC(cpu_init_crit)
  • 2.1、b save_boot_params @ 跳转到 save_boot_params 函数。

      	ENTRY(save_boot_params)
    		b	save_boot_params_ret		@ back to my caller
    		// 直接跳转到 save_boot_params_ret
    	ENDPROC(save_boot_params)
    	.weak	save_boot_params
    

    start.S文件中定义的 save_boot_params 函数未执行任何动作就跳转到了 save_boot_params_ret 标识处,.weak save_boot_params说明该函数为弱链接,如果其他地方定义了该函数则不链接此处的函数。
    save_boot_params_ret 标号在 b save_boot_params 下方。

  • 2.2、关中断,设置SVC32模式

    /*
     * 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
    

    这里其实就是操作cpsr寄存器, cpsr寄存器为ARM内核状态寄存器(Current Program Status Register),需要参考ARM手册。

  • 2.3、设置向量表

    /* 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
    

    这里是在操作 p15寄存器,实际上cp15并不是一个寄存器,CP15是一个协处理器,包含一组寄存器。

  • 2.4、bl 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
    
    	orr	r0, r0, #0x00001000	@ set bit 12 (I) I-cache
    
    	mcr	p15, 0, r0, c1, c0, 0
    
    	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
    
    	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
    
    	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
    
    	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
    
    	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
    
    	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)
    

    这里通过设置协处理器cp15内相关的寄存器以关掉DCache和ICache,禁止mmu等。 根据上面的一下宏定义看,还有一些代码是为了修正ARM内核的bug。

  • 2.5、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)
    

    简单设置栈指针寄存器,调用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);
    }
    

    这里主要设置系统时钟。

3、_main

_main 函数是在 arch/arm/lib/crt0.S 中。 crt是c runtime,所以这里应该主要设置c语言环境调用c函数, _main函数内容如下:

ENTRY(_main)

/*
 * Set up initial C runtime environment and call board_init_f(0).
 */

#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
	ldr	sp, =(CONFIG_SPL_STACK)
#else
	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
#if defined(CONFIG_CPU_V7M)	/* v7M forbids using SP as BIC destination */
	mov	r3, sp
	bic	r3, r3, #7
	mov	sp, r3
#else
	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */
#endif
	mov	r0, sp
	bl	board_init_f_alloc_reserve
	mov	sp, r0
	/* set up gd here, outside any C code */
	mov	r9, r0
	bl	board_init_f_init_reserve

	mov	r0, #0
	bl	board_init_f

#if ! defined(CONFIG_SPL_BUILD)

/*
 * Set up intermediate environment (new sp and gd) and call
 * relocate_code(addr_moni). Trick here is that we'll return
 * 'here' but relocated.
 */

	ldr	sp, [r9, #GD_START_ADDR_SP]	/* sp = gd->start_addr_sp */
#if defined(CONFIG_CPU_V7M)	/* v7M forbids using SP as BIC destination */
	mov	r3, sp
	bic	r3, r3, #7
	mov	sp, r3
#else
	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */
#endif
	ldr	r9, [r9, #GD_BD]		/* r9 = gd->bd */
	sub	r9, r9, #GD_SIZE		/* new GD is below bd */

	adr	lr, here
	ldr	r0, [r9, #GD_RELOC_OFF]		/* r0 = gd->reloc_off */
	add	lr, lr, r0
#if defined(CONFIG_CPU_V7M)
	orr	lr, #1				/* As required by Thumb-only */
#endif
	ldr	r0, [r9, #GD_RELOCADDR]		/* r0 = gd->relocaddr */
	b	relocate_code
here:
/*
 * now relocate vectors
 */

	bl	relocate_vectors

/* Set up final (full) environment */

	bl	c_runtime_cpu_setup	/* we still call old routine here */
#endif
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)
# ifdef CONFIG_SPL_BUILD
	/* Use a DRAM stack for the rest of SPL, if requested */
	bl	spl_relocate_stack_gd
	cmp	r0, #0
	movne	sp, r0
	movne	r9, r0
# endif
	ldr	r0, =__bss_start	/* this is auto-relocated! */

#ifdef CONFIG_USE_ARCH_MEMSET
	ldr	r3, =__bss_end		/* this is auto-relocated! */
	mov	r1, #0x00000000		/* prepare zero to clear BSS */

	subs	r2, r3, r0		/* r2 = memset len */
	bl	memset
#else
	ldr	r1, =__bss_end		/* this is auto-relocated! */
	mov	r2, #0x00000000		/* prepare zero to clear BSS */

clbss_l:cmp	r0, r1			/* while not at end of BSS */
#if defined(CONFIG_CPU_V7M)
	itt	lo
#endif
	strlo	r2, [r0]		/* clear 32-bit BSS word */
	addlo	r0, r0, #4		/* move to next */
	blo	clbss_l
#endif

#if ! defined(CONFIG_SPL_BUILD)
	bl coloured_LED_init
	bl red_led_on
#endif
	/* call board_init_r(gd_t *id, ulong dest_addr) */
	mov     r0, r9                  /* gd_t */
	ldr	r1, [r9, #GD_RELOCADDR]	/* dest_addr */
	/* call board_init_r */
#if defined(CONFIG_SYS_THUMB_BUILD)
	ldr	lr, =board_init_r	/* this is auto-relocated! */
	bx	lr
#else
	ldr	pc, =board_init_r	/* this is auto-relocated! */
#endif
	/* we should not return here. */
#endif

ENDPROC(_main)
去掉未定义宏和无关代码后如下:
ENTRY(_main)
/*
 * Set up initial C runtime environment and call board_init_f(0).
 */
	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)

	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */

	mov	r0, sp
	bl	board_init_f_alloc_reserve
	mov	sp, r0
	/* set up gd here, outside any C code */
	mov	r9, r0
	bl	board_init_f_init_reserve

	mov	r0, #0
	bl	board_init_f

/*
 * Set up intermediate environment (new sp and gd) and call
 * relocate_code(addr_moni). Trick here is that we'll return
 * 'here' but relocated.
 */

	ldr	sp, [r9, #GD_START_ADDR_SP]	/* sp = gd->start_addr_sp */

	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */

	ldr	r9, [r9, #GD_BD]		/* r9 = gd->bd */
	sub	r9, r9, #GD_SIZE		/* new GD is below bd */

	adr	lr, here
	ldr	r0, [r9, #GD_RELOC_OFF]		/* r0 = gd->reloc_off */
	add	lr, lr, r0

	ldr	r0, [r9, #GD_RELOCADDR]		/* r0 = gd->relocaddr */
	b	relocate_code
here:
/*
 * now relocate vectors
 */

	bl	relocate_vectors

/* Set up final (full) environment */

	bl	c_runtime_cpu_setup	/* we still call old routine here */

	ldr	r0, =__bss_start	/* this is auto-relocated! */

	ldr	r1, =__bss_end		/* this is auto-relocated! */
	mov	r2, #0x00000000		/* prepare zero to clear BSS */

clbss_l:cmp	r0, r1			/* while not at end of BSS */
	strlo	r2, [r0]		/* clear 32-bit BSS word */
	addlo	r0, r0, #4		/* move to next */
	blo	clbss_l

	bl coloured_LED_init
	bl red_led_on

	/* call board_init_r(gd_t *id, ulong dest_addr) */
	mov     r0, r9                  /* gd_t */
	ldr	r1, [r9, #GD_RELOCADDR]	/* dest_addr */
	/* call board_init_r */
	ldr	pc, =board_init_r	/* this is auto-relocated! */

	/* we should not return here. */
ENDPROC(_main)
  • 3.1、设置栈指针

    ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)
    bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */
    mov	r0, sp
    

    设置站指针,并且要8字节对齐,然后把栈顶地址存放在r0寄存器中。
    CONFIG_SYS_INIT_SP_ADDR 定义在 include/configs/mx6sabre_common.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)
    
    // arch/arm/include/asm/arch-mx6/imx-regs.h
    #define IRAM_BASE_ADDR 			0x00900000
    #define IRAM_SIZE				0x00040000
    
    // include/generated/generic-asm-offsets.h
    #define GENERATED_GBL_DATA_SIZE  256
    

    所以这里 sp = 0x00900000 + 0x00040000 - 256
    IRAM 既是imx6q上的OCRAM, 所以这里的栈是放在片上SRAM。

  • 3.2、board_init_f_alloc_reserve

    // common/init/board_init.c
    ulong board_init_f_alloc_reserve(ulong top)
    {
    /* Reserve early malloc arena */
    #if defined(CONFIG_SYS_MALLOC_F)
      	top -= CONFIG_SYS_MALLOC_F_LEN;
    #endif
    	/* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
    	top = rounddown(top - sizeof(struct global_data), 16);
    
    	return top;
    }
    

    这里定义了 CONFIG_SYS_MALLOC_F 为 1, CONFIG_SYS_MALLOC_F_LEN 为 0x400。
    该函数的参数top,为栈顶地址。在调用该函数之前,栈指针被放入到r0寄存器中。
    这里函数用栈顶指针减去一个堆的大小CONFIG_SYS_MALLOC_F_LEN,和 一个 16字节对齐后的global_data 结构体大小。

    mov	sp, r0
    /* set up gd here, outside any C code */
    mov	r9, r0
    

    减去后的指针为新的栈顶指针。并且新的栈顶指针也是global_data的基地址(存放到r9寄存器中)。

  • 3.3、board_init_f_init_reserve

    void board_init_f_init_reserve(ulong base)
    {
    	struct global_data *gd_ptr;
    #ifndef _USE_MEMCPY
    	int *ptr;
    #endif
    
    	/*
    	* clear GD entirely and set it up.
    	* Use gd_ptr, as gd may not be properly set yet.
    	*/
    
    	gd_ptr = (struct global_data *)base;
    	/* zero the area */
    #ifdef _USE_MEMCPY
    	memset(gd_ptr, '\0', sizeof(*gd));
    #else
    	for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); )
    		*ptr++ = 0;
    #endif
    	/* set GD unless architecture did it already */
    #if !defined(CONFIG_ARM)
    	arch_setup_gd(gd_ptr);
    #endif
    	/* next alloc will be higher by one GD plus 16-byte alignment */
    	base += roundup(sizeof(struct global_data), 16);
    
    	/*
    	* record early malloc arena start.
    	* Use gd as it is now properly set for all architectures.
    	*/
    
    #if defined(CONFIG_SYS_MALLOC_F)
    	/* go down one 'early malloc arena' */
    	gd->malloc_base = base;
    	/* next alloc will be higher by one 'early malloc arena' size */
    	base += CONFIG_SYS_MALLOC_F_LEN;
    #endif
    }
    

    该函数的参数 base 为r0寄存器的值,也是global_data的基地址。该函数的作用是:

    • 1、对global_data结构体初始化,如果定义了_USE_MEMCPY就用memset,否则用for循环赋0值。
    • 2、对global_data结构体的 malloc_base 赋值,这个既是堆的基地址,也是global_data结构体16字节对齐后的末地址。
    • 3、这里可以看出,堆在global_data结构体的高地址方向, 栈在global_data结构体的低地址方向。
  • 3.4、board_init_f,关键硬件初始化和代码重定位的准备。

    // common/board_f.c
    DECLARE_GLOBAL_DATA_PTR		
    void board_init_f(ulong boot_flags)
    {
    #ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA
    	/*
    	* For some archtectures, global data is initialized and used before
    	* calling this function. The data should be preserved. For others,
    	* CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack
    	* here to host global data until relocation.
    	*/
    	gd_t data;
    
    	gd = &data;
    
    	/*
    	* Clear global data before it is accessed at debug print
    	* in initcall_run_list. Otherwise the debug print probably
    	* get the wrong vaule of gd->have_console.
    	*/
    	zero_global_data();
    #endif
    
    	gd->flags = boot_flags;
    	gd->have_console = 0;
    
    	if (initcall_run_list(init_sequence_f))
    		hang();
    
    #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
    		!defined(CONFIG_EFI_APP)
    	/* NOTREACHED - jump_to_copy() does not return */
    	hang();
    #endif
    }
    

    这里未定义CONFIG_SYS_GENERIC_GLOBAL_DATA

    • 1、该函数首先对global_data结构体的 flags和have_console赋值。
      同样boot_flags 是r0寄存器的值,在调用board_init_f函数前执行了mov r0, #0。所以 boot_flags 为0。
      have_console 赋值0,说明此时串口未初始化,不能打印输出。
    • 2、init_sequence_f 是一个函数指针数组,initcall_run_list 的作用就是执行这些函数指针。
  • 3.5、代码重定位前的最后准备。

    /*
    * Set up intermediate environment (new sp and gd) and call
    * relocate_code(addr_moni). Trick here is that we'll return
    * 'here' but relocated.
    */
    // 在 board_init_f 函数中设置好的新的栈地址赋值给栈寄存器。
    ldr	sp, [r9, #GD_START_ADDR_SP]	/* sp = gd->start_addr_sp */
    // 栈指针做8字节对齐
    bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */
    // 新的 bd_t 结构体基地址赋值给 r9 寄存器。
    ldr	r9, [r9, #GD_BD]		/* r9 = gd->bd */
    // bd_t 的基地址也是新的 global_data 结构体的末地址,
    // 这里r9寄存器减去global_data 结构体的大小,既把 global_data 基地址赋值给r9寄存器
    sub	r9, r9, #GD_SIZE		/* new GD is below bd */
    // 把 here指向的地址赋值给链接寄存器 lr
    adr	lr, here
    // 把重定位代码的偏移量赋值给 r0寄存器。
    ldr	r0, [r9, #GD_RELOC_OFF]		/* r0 = gd->reloc_off */
    // 链接寄存器加上重定位代码的偏移量,也就是链接寄存器加上重定位偏移量,也就是指向重定位后的here
    add	lr, lr, r0
    // 把重定位代码的首地址赋值给r0寄存器
    ldr	r0, [r9, #GD_RELOCADDR]		/* r0 = gd->relocaddr */
    
  • 3.6、relocate_code

    // arch/arm/lib/relocate.S
    /*
    * void relocate_code(addr_moni)
    *
    * This function relocates the monitor code.
    *
    * NOTE:
    * To prevent the code below from containing references with an R_ARM_ABS32
    * relocation record type, we never refer to linker-defined symbols directly.
    * Instead, we declare literals which contain their relative location with
    * respect to relocate_code, and at run time, add relocate_code back to them.
    */
    
    ENTRY(relocate_code)
    	ldr	r1, =__image_copy_start	/* r1 <- SRC &__image_copy_start */
    	// 把原来的u-boot代码的首地址赋值到 r1 寄存器。
    	subs	r4, r0, r1		/* r4 <- relocation offset */
    	// 在上面的操作中 r0 寄存器存储着重定位后的 u-boot 代码的首地址
    	// subs 把 r0 和 r1 相减,把结果放到r4寄存器中。
    	// 如果r0 == r1,则不进行重定位。
    	beq	relocate_done		/* skip relocation */
    	ldr	r2, =__image_copy_end	/* r2 <- SRC &__image_copy_end */
    	// 这里把原来的 u-boot 代码的末尾地址赋值给 r2 寄存器。
    copy_loop:
    	ldmia	r1!, {r10-r11}		/* copy from source address [r1]    */
    	// 这里的操作是把 r1寄存器指向的地址空间内容写入到 r10, r11寄存器中,并且r1寄存器指向的地址依次累加(+4)。
    	stmia	r0!, {r10-r11}		/* copy to   target address [r0]    */
    	// 这里的操作跟上面相反,把 r10,r11寄存器中的内容依次写入到 r0 寄存器指向的地址空间去, 并且r0寄存器指向的地址依次累加(+4)。
    	cmp	r1, r2			/* until source end address [r2]    */
    	// 比较 r1 和 r2寄存器指向的地址是否相等
    	// 相等则拷贝结束,不等则继续下一次拷贝。
    	blo	copy_loop
    
    	/*
     	* fix .rel.dyn relocations
     	*/
     	// __rel_dyn_start	是u-boot中的重定位代码段的起始地址
     	// __rel_dyn_end	是u-boot中的重定位代码段的结尾地址
    	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) */
    	// 把r2寄存器指向的地址空间内容依次写到r0,r1寄存器中,也就是从 __rel_dyn_start	开始,把数据写入r0寄存器4字节,r1寄存器4字节。
    	// 每拷贝4字节,r2寄存器自动加4
    	and	r1, r1, #0xff
    	// r1寄存器的值与上0xff,在写回r1寄存器。只要低8位数据,其他位不要。
    	cmp	r1, #23			/* relative fixup? */
    	// 比较r1寄存器的值是否等于23。
    	bne	fixnext
    	// 如果 r1寄存器的不等于23,则跳转到fixnext。
    	// 如果相等则往下执行
    
    	/* relative fix: increase location by offset */
    	add	r0, r0, r4
    	// r0 寄存器的值是地址,这个地址是重定位前的地址,加上 r4寄存器的值就是重定位后的地址。
    	// r4 寄存器中存储着 relocation offset
    	// r0 寄存器的值加上重定位偏移量的和,再写回r0寄存器。
    
    	ldr	r1, [r0]
    	// 把r0存储的地址指向的数据加载到r1寄存器中,
    	add	r1, r1, r4
    	// 把 r1 寄存器的值加上重定位的偏移量,和写入到r1寄存器中。
    	str	r1, [r0]
    	// 再把r1寄存器的值写入 r0 寄存器存储的地址指向的数据
    fixnext:
    	cmp	r2, r3
    	// 比较 r2 和 r3 寄存器的值,
    	// r2 == r3说明地址修复完毕,不等则跳转到fixloop
    	blo	fixloop
    
    relocate_done:
    	// 修复完毕,返回到链接寄存器指向的地址。此时的链接寄存器指向的是重定位后的 here 标号。
    #ifdef __XSCALE__
    	/*
    	 * On xscale, icache must be invalidated and write buffers drained,
     	* even with cache disabled - 4.2.7 of xscale core developer's manual
     	*/
    	mcr	p15, 0, r0, c7, c7, 0	/* invalidate icache */
    	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
    #endif
    
    	/* ARMv4- don't know bx lr but the assembler fails to see that */
    
    #ifdef __ARM_ARCH_4__
    	mov	pc, lr
    #else
    	bx	lr
    	// 跳转到 lr 链接寄存器指向的地址
    #endif
    
    ENDPROC(relocate_code)
    
  • 3.7、relocate_vectors

    ENTRY(relocate_vectors)
    // 未定义 CONFIG_CPU_V7M
    #ifdef CONFIG_CPU_V7M
    	/*
     	* On ARMv7-M we only have to write the new vector address
    	 * to VTOR register.
     	*/
    	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd->relocaddr */
    	ldr	r1, =V7M_SCB_BASE
    	str	r0, [r1, V7M_SCB_VTOR]
    #else
    // 定义了 CONFIG_HAS_VBAR
    #ifdef CONFIG_HAS_VBAR
    	/*
    	 * If the ARM processor has the security extensions,
    	 * use VBAR to relocate the exception vectors.
     	*/
     	// 重新设置异常向量表的位置
    	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd->relocaddr */
    	mcr     p15, 0, r0, c12, c0, 0  /* Set VBAR */
    #else
    	/*
     	* Copy the relocated exception vectors to the
    	 * correct address
     	* CP15 c1 V bit gives us the location of the vectors:
     	* 0x00000000 or 0xFFFF0000.
     	*/
    	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd->relocaddr */
    	mrc	p15, 0, r2, c1, c0, 0	/* V bit (bit[13]) in CP15 c1 */
    	ands	r2, r2, #(1 << 13)
    	ldreq	r1, =0x00000000		/* If V=0 */
    	ldrne	r1, =0xFFFF0000		/* If V=1 */
    	ldmia	r0!, {r2-r8,r10}
    	stmia	r1!, {r2-r8,r10}
    	ldmia	r0!, {r2-r8,r10}
    	stmia	r1!, {r2-r8,r10}
    #endif
    #endif
    	// 跳转到链接寄存器指向的地址
    	// 跳转到relocate_vectors时 用的bl
    	bx	lr
    
    ENDPROC(relocate_vectors)
    
  • 3.8、c_runtime_cpu_setup

    // arch/arm/cpu/armv7/start.S
    ENTRY(c_runtime_cpu_setup)
    /*
     * If I-cache is enabled invalidate it
     */
    #ifndef CONFIG_SYS_ICACHE_OFF
    	mcr	p15, 0, r0, c7, c5, 0	@ invalidate icache
    	mcr     p15, 0, r0, c7, c10, 4	@ DSB
    	mcr     p15, 0, r0, c7, c5, 4	@ ISB
    #endif
    
    	bx	lr
    
    ENDPROC(c_runtime_cpu_setup)
    

    这里只是把I-cache关掉。然后跳转回链接地址。

  • 3.9、bss段初始化

    	ldr	r0, =__bss_start	/* this is auto-relocated! */
    
    	ldr	r1, =__bss_end		/* this is auto-relocated! */
    	mov	r2, #0x00000000		/* prepare zero to clear BSS */
    
    clbss_l:cmp	r0, r1			/* while not at end of BSS */
    	strlo	r2, [r0]		/* clear 32-bit BSS word */
    	addlo	r0, r0, #4		/* move to next */
    	blo	clbss_l
    

    __bss_start 和 __bss_end 定义在 u-boot.lds 中,这里是编译好之后 bss 段的开始地址和bss段的结束地址。
    r0寄存器保存bss段开始地址,r1寄存器保存bss段的结束地址,r2寄存器赋0值。
    然后进入循环,比较r0和r1寄存器的值。如果不等,对r0寄存器指向的内存赋值0。然后r0寄存器的值加4,一直循环到bss段末。
    疑问:
    注释说 __bss_start 和 __bss_end 是自动重定位的,所以这里应该是重定位后的地址?什么原理呢?

  • 3.10、coloured_LED_init

    // common/board_f.c
    __weak void coloured_LED_init(void) {}
    
  • 3.11、red_led_on

    // common/board_f.c
    __weak void red_led_on(void) {}
    

    3.10、3.11两个函数都是虚函数,为保留功能。
    3.10是初始化led,3.11是打开led。硬件指示灯,需要硬件支持,软件再实现。

  • 3.12、board_init_r

_main函数的功能:
  • 1、设置初始环境,为调用 board_init_f做准备。 包括保留一段内存,存放 global data 结构体和设置栈。这里所在内存都是上电可用的内存,不需要初始化,一般是SRAM。不过在i.MX 6q平台 ddr由bootrom初始化,所以这里的内存是DDR。
  • 2、调用 board_init_f,该函数的作用一般是初始化外置RAM,初始化串口打印。
  • 3、设置栈指针,重定位代码,初始化完整的c语言运行环境。
  • 4、调用 board_init_r 函数。
arch/arm/lib/vectors.S
arch/arm/cpu/armv7/start.S
arch/arm/lib/crt0.S
_start
reset
_main
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值