U-Boot学习(5):系统初始化之start.S文件源码分析

本文主要分析Cortex - A核中U-Boot系统寄存器的初始化。介绍了上电代码执行流程、save_boot_params和位置无关码,阐述了LPAE特性及代码分析,还涉及特权模式切换、关闭中断、设置异常向量表地址,以及CPU初始化等内容,最后程序跳转执行后续操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前我有分析过基于Cortex-M单片机中BootLoader的严谨实现详解,那在Cortex-A核中的U-Boot做了什么事呢?比如在M核中向量表的地址就直接在一个系统寄存器中设置,而在A核中向量表在CP15协处理器中进行设置,所以需要触发一个异常,在特权模式下进行设置。上一节中分析了U-Boot上电后进行初始化系统的start.S文件中的代码重定位,我们就接着分析后面的系统寄存器的初始化。

1 上电代码执行流程

在上一节u-boot.lds链接脚本分析中,我们知道程序的最开始是vector.S,我们看一下开头的代码:
在这里插入图片描述
也就是程序一开始,我们就跳转到reset标号处执行,这个标号在start.S文件中定义:
在这里插入图片描述
完整代码如下:

reset:
	/* Allow the board to save important registers */
	b	save_boot_params
save_boot_params_ret:
#ifdef CONFIG_POSITION_INDEPENDENT
	/*
	 * Fix .rela.dyn relocations. This allows U-Boot to loaded to and
	 * executed at a different address than it was linked at.
	 */
pie_fixup:
	adr	r0, reset	/* r0 <- Runtime value of reset label */
	ldr	r1, =reset	/* r1 <- Linked value of reset label */
	subs	r4, r0, r1	/* r4 <- Runtime-vs-link offset */
	beq	pie_fixup_done

	adr	r0, pie_fixup
	ldr	r1, _rel_dyn_start_ofs
	add	r2, r0, r1	/* r2 <- Runtime &__rel_dyn_start */
	ldr	r1, _rel_dyn_end_ofs
	add	r3, r0, r1	/* r3 <- Runtime &__rel_dyn_end */

pie_fix_loop:
	ldr	r0, [r2]	/* r0 <- Link location */
	ldr	r1, [r2, #4]	/* r1 <- fixup */
	cmp	r1, #23		/* relative fixup? */
	bne	pie_skip_reloc

	/* relative fix: increase location by offset */
	add	r0, r4
	ldr	r1, [r0]
	add	r1, r4
	str	r1, [r0]
	str	r0, [r2]
	add	r2, #8
pie_skip_reloc:
	cmp	r2, r3
	blo	pie_fix_loop
pie_fixup_done:
#endif

#ifdef CONFIG_ARMV7_LPAE
/*
 * check for Hypervisor support
 */
	mrc	p15, 0, r0, c0, c1, 1		@ read ID_PFR1
	and	r0, r0, #CPUID_ARM_VIRT_MASK	@ mask virtualization bits
	cmp	r0, #(1 << CPUID_ARM_VIRT_SHIFT)
	beq	switch_to_hypervisor
switch_to_hypervisor_ret:
#endif
	/*
	 * 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

#ifdef CONFIG_HAS_VBAR
	/* Set vector address in CP15 VBAR register */
	ldr	r0, =_start
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR
#endif
#endif

	/* the mask ROM code should have PLL and others stable */
#if !CONFIG_IS_ENABLED(SKIP_LOWLEVEL_INIT)
#ifdef CONFIG_CPU_V7A
	bl	cpu_init_cp15
#endif
#if !CONFIG_IS_ENABLED(SKIP_LOWLEVEL_INIT_ONLY)
	bl	cpu_init_crit
#endif
#endif

	bl	_main

这篇文章就来分析一下这一部分的代码。

2 save_boot_params和位置无关码

一开始就使用b直接跳转到标号save_boot_params了,这里使用的是b而不是bl也就是不会返回了。但我们发现save_boot_params就在当前文件中定义:
在这里插入图片描述
这边又跳到save_boot_params_ret,即正好是reset的下一行代码。所以这个应该是当我们需要保存一些boot参数的时候,可以通过save_boot_params函数实现。

接下来如果定义了CONFIG_POSITION_INDEPENDENT宏定义,就会执行代码重定位的操作。
在这里插入图片描述
实际上,这种代码重定位一般用在比较老的芯片中,比如S3C2440,系统上电后会从Flash中读取4KB的代码到内置的大小为4KB的SRAM中。但我们的U-Boot的完整代码不止4KB,那我们就需要在这4KB的代码中实现将剩余的代码手动拷贝到更大的SDRAM中。在拷贝完后便出现了变量地址不一致、位置无关代码等问题。

我目前使用的是I.MX6ULL,在上电后ROM BootLoader就帮我们完整地拷贝到RAM中了,所以我们在U-Boot目录下的.config文件中就没有定义这个宏定义。另外,这种实现已经过时了,在后面的U-Boot源码中有其它重定位的方式,所以这部分代码就不分析了。

3 LPAE(Large Physical Address Extensions)

如果使能了CONFIG_ARMV7_LPAE的话,将执行相关的代码。

3.1 ARMv7的LPAE特性

LPAE(Large Physical Address Extension,长物理地址扩展)是ARMv7-A的一个特性,他可以将32位虚拟内存地址转换为40位物理内存地址,将可访问的物理地址范围从4GB扩展到1TB。

当运行虚拟机时,虚拟化扩展提供了额外的第二阶段地址转换。该翻译的第一阶段生成中间物理地址(Intermediate Physical Address,IPA),而第二阶段则生成物理地址。这个转换过程的第二阶段由Hypervisor控制,TLB条目还可以具有与ASID(Address Space ID)一样的虚拟机ID。同样,可以禁用第二阶段的MMU,并实现从IPA到PA的平面映射。如下图所示:

在这里插入图片描述

3.2 代码分析

在这里插入图片描述

读协处理器的语句为:

MRC{cond} p15, <opcode_1>, <rd>, <CRn>, <CRm>, <opcode_2>

mrc p15, 0, r0, c0, c1, 1对应手册中的:
在这里插入图片描述
也就是把ID_PFR1读到r0寄存器中。

#define CPUID_ARM_VIRT_SHIFT		12
#define CPUID_ARM_VIRT_MASK		(0xF << CPUID_ARM_VIRT_SHIFT)

后面的and与操作和cmp比较操作实际上就是判断ID_PFR1的bit12是否为1,若为1则表示支持虚拟化。
在这里插入图片描述
如果支持虚拟化,则后面的代码beq switch_to_hypervisor就会跳转到switch_to_hypervisor标签处,切换到Hypervisor模式以支持长地址。

  • 实际上U-Boot没有帮我们实现switch_to_hypervisor,这需要我们自己实现切换到Hypervisor特权等级。然后跳转回下面的标号switch_to_hypervisor_ret

4 特权模式切换、关闭中断

继续往下看代码,从注释可以看出,这段代码将禁用FIQIRQ,然后设置CPU为SVC特权模式。
在这里插入图片描述
前两行读出CPSR寄存器的低5位并保存在r1中,来看一下这5位的定义:
在这里插入图片描述
接着我们用teq指令判断模式位是否为0x1a,即Hypervisor模式。
在这里插入图片描述

继续往下看代码:

  • bicne r0, r0, #0x1f: 如果前面的测试结果不相等,则将r0中低5位的模式位清空

    • bicneBIt Clear if Not Equal
  • orrne r0, r0, #0x13: 如果前面的测试结果不相等,则将r0的模式位设置为SVC模式

    • orrneOR if Not Equal
  • orr r0, r0, #0xc0: 使用按位或指令,将r0的IRQ和FIQ位设置为1,即禁用IRQ和FIQ

在这里插入图片描述

  • msr cpsr, r0: 将r0的值写回CPSR寄存器,完成模式切换和中断禁用。

5 设置异常向量表的地址

如果未定义宏CONFIG_OMAP44XX且未定义宏CONFIG_SPL_BUILD,则执行下面的代码块,主要是设置处理器的异常向量地址,其中包含一些与处理器控制和向量基地址相关的操作。
在这里插入图片描述

  • CONFIG_OMAP44XX:当前使用的是OMAP4系列芯片。即该芯片不支持这些配置
  • CONFIG_SPL_BUILD:SPL(Secondary Program Loader)是用于启动U-Boot的第一阶段程序,负责完成一些基本的硬件初始化、加载主引导加载器、设置环境变量等任务。也就是说,如果有SPL了,在SPL中已经帮我们完成了这些代码了,就不用再执行了。
    • 具体参考U-Boot目录下的doc/README.SPL文档

现在来看一下代码,前三行就是读CP15中的SCTLR寄存器中的值,然后将其的CR_V位清零,即bit13位:

#define CR_V (1 << 13)

我们来看一下SCTLR的第13位:
在这里插入图片描述
从手册描述可以知道,将该位设置为0,即将异常向量表放置在低地址,这样处理器在发生异常时会跳转到低地址处执行相应的异常处理程序,还可以通过重新映射来改变异常向量表的实际物理地址。

接下来我们将_start标号的地址赋给r0,从下图可以看到_start就是我们程序向量表的地址,从上一节u-boot.lds链接脚本分析分析可知,这个地址就是程序的链接地址。
在这里插入图片描述
然后将这个地址设置给向量表寄存器,在Cortex-A7中,向量表寄存器即CP15的c15寄存器中的CBAR寄存器。

6 CPU初始化

下面的代码在SKIP_LOWLEVEL_INIT关闭时执行,如果定义了CONFIG_CPU_V7A则执行cpu_init_cp15,如果没有使能SKIP_LOWLEVEL_INIT_ONLY则执行cpu_init_crit。在我使用的U-Boot配置中,这里的所有情况都满足,所以会执行这两个函数。
在这里插入图片描述

6.1 cpu_init_cp15

这段代码很长,主要是对CP15所有必要的寄存器做一个初始化。

ENTRY(cpu_init_cp15)

#if CONFIG_IS_ENABLED(ARMV7_SET_CORTEX_SMPEN)
	/*
	 * The Arm Cortex-A7 TRM says this bit must be enabled before
	 * "any cache or TLB maintenance operations are performed".
	 */
	mrc	p15, 0, r0, c1, c0, 1	@ read auxilary control register
	orr	r0, r0, #1 << 6		@ set SMP bit to enable coherency
	mcr	p15, 0, r0, c1, c0, 1	@ write auxilary control register
#endif

	/*
	 * 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
#if CONFIG_IS_ENABLED(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

/* Early stack for ERRATA that needs into call C code */
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
	ldr	r0, =(CONFIG_SPL_STACK)
#else
	ldr	r0, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
	bic	r0, r0, #7	/* 8-byte alignment for ABI compliance */
	mov	sp, r0

#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_CORTEX_A15_CVE_2017_5715
	mrc	p15, 0, r0, c1, c0, 1	@ read auxilary control register
	orr	r0, r0, #1 << 0		@ Enable invalidates of BTB
	push	{r1-r5}			@ Save the cpu info registers
	bl	v7_arch_cp15_set_acr
	pop	{r1-r5}			@ Restore the cpu info - fall through
#endif

#ifdef CONFIG_ARM_ERRATA_454179
	mrc	p15, 0, r0, c1, c0, 1	@ Read ACR

	cmp	r2, #0x21		@ Only on < r2p1
	orrlt	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
#endif

#if defined(CONFIG_ARM_ERRATA_430973) || defined (CONFIG_ARM_CORTEX_A8_CVE_2017_5715)
	mrc	p15, 0, r0, c1, c0, 1	@ Read ACR

#ifdef CONFIG_ARM_CORTEX_A8_CVE_2017_5715
	orr	r0, r0, #(0x1 << 6)	@ Set IBE bit always to enable OS WA
#else
	cmp	r2, #0x21		@ Only on < r2p1
	orrlt	r0, r0, #(0x1 << 6)	@ Set IBE bit
#endif
	push	{r1-r5}			@ Save the cpu info registers
	bl	v7_arch_cp15_set_acr
	pop	{r1-r5}			@ Restore the cpu info - fall through
#endif

#ifdef CONFIG_ARM_ERRATA_621766
	mrc	p15, 0, r0, c1, c0, 1	@ Read ACR

	cmp	r2, #0x21		@ Only on < r2p1
	orrlt	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
#endif

#ifdef CONFIG_ARM_ERRATA_725233
	mrc	p15, 1, r0, c9, c0, 2	@ Read L2ACR

	cmp	r2, #0x21		@ Only on < r2p1 (Cortex A8)
	orrlt	r0, r0, #(0x1 << 27)	@ L2 PLD data forwarding disable

	push	{r1-r5}			@ Save the cpu info registers
	bl	v7_arch_cp15_set_l2aux_ctrl
	pop	{r1-r5}			@ Restore the cpu info - fall through
#endif

#ifdef CONFIG_ARM_ERRATA_852421
	mrc	p15, 0, r0, c15, c0, 1	@ read diagnostic register
	orr	r0, r0, #1 << 24	@ set bit #24
	mcr	p15, 0, r0, c15, c0, 1	@ write diagnostic register
#endif

#ifdef CONFIG_ARM_ERRATA_852423
	mrc	p15, 0, r0, c15, c0, 1	@ read diagnostic register
	orr	r0, r0, #1 << 12	@ set bit #12
	mcr	p15, 0, r0, c15, c0, 1	@ write diagnostic register
#endif

	mov	pc, r5			@ back to my caller
ENDPROC(cpu_init_cp15)

这里就不把每个CP15寄存器的描述都截图上来了。简单总结一下
这段代码主要完成了一些与 ARM Cortex-A 系列处理器相关的低级别初始化工作,包括设置协处理器寄存器、清除缓存、禁用 MMU(内存管理单元)等。下面是对代码的主要功能进行的总结:

  1. 检查并设置SMP位:
    • 通过读取和修改辅助控制寄存器(auxilary control register)的第6位,设置SMP位(Symmetric Multi-Processing),以启用协同性。
  2. 清除L1 I/D Cache和TLBs:
    • 使用协处理器指令 mcr,对 L1 I/D 缓存进行清除。
    • 对TLBs进行清除,确保缓存和TLBs不包含任何旧的数据。
  3. 禁用MMU和缓存
    • 通过读取和修改系统控制寄存器,清除与MMU相关的位(位13,即–V-)、清除缓存相关的位(位0~2),同时设置一些其他位,以禁用MMU和缓存。
  4. 处理 ARM 处理器的错误
    • 根据 ARM 处理器的具体型号和可能的错误,进行相应的错误处理。
    • 这涉及到一系列的条件判断和配置,包括设置协处理器寄存器、修改控制寄存器等。
  5. 设置堆栈指针SP
    • 为了处理一些Errata(硬件或软件设计中发现的错误、缺陷或问题的列表)需要调用C代码的情况,设置堆栈指针 sp
  6. 执行ARM处理器的Errata补丁
    • 根据处理器的具体型号和可能的Errata,执行相应的Errata补丁。
    • 这包括设置协处理器寄存器、修改控制寄存器等,以修复或绕过处理器的缺陷。

6.2 cpu_init_crit

代码如下:

ENTRY(cpu_init_crit)
	b	lowlevel_init		@ go setup pll,mux,memory
ENDPROC(cpu_init_crit)

实际上就是调用lowlevel_init函数(在/arch/arm/cpu/armv7/lowlevel_init.S中),为了方便阅读,我把不满足的宏定义分支删掉了:

WEAK(lowlevel_init)
	ldr	sp, =CONFIG_SYS_INIT_SP_ADDR
	bic	sp, sp, #7

	sub	sp, sp, #GD_SIZE
	bic	sp, sp, #7
	mov	r9, sp

	push	{ip, lr}
	bl	s_init
	pop	{ip, pc}
ENDPROC(lowlevel_init)

相关的宏定义如下:

#define GD_SIZE 224
#define IRAM_BASE_ADDR			0x00900000
#define IRAM_SIZE               0x00020000
#define GENERATED_GBL_DATA_SIZE 240
#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算出来就是:0x00900000+0x00020000-240=0x91FF10,即

#define CONFIG_SYS_INIT_SP_ADDR    0x91FF10
  • 0x00900000是芯片内置RAM的地址,大小为0x20000=128KB。

现在再回来分析一下前面的汇编代码,这段代码比较简单,主要就是设置一下堆栈指针,然后调用s_init
在这里插入图片描述
此时SP的值为0x91FF10-224=0x91FE30,如果栈是向下增长的话,也就是说,最前面留了240B作为GENERATED_GBL_DATA_SIZE,又留了224B作为GD_SIZE,至于这有什么用途,这里就不详细分析了,总之就是为这两个数据结构保留了栈空间。

6.2.1 s_init

这个函数就对应到厂商的芯片初始化代码中了。由于硬件限制,在MX6Q或其它的芯片中我们需要对所有PFD进行门控/取消门控,以确保PFD正常工作,否则,在复位后,PFD可能不会输出时钟。由于我这里使用的是I.MX6ULL,所以这个函数会直接返回,什么都不用执行。

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 defined(CONFIG_ANDROID_SUPPORT)
        /* Enable RTC */
        writel(0x21, 0x020cc038);
#endif
	if (is_mx6sx() || is_mx6ul() || is_mx6ull() || is_mx6sll())
		return;
	......
}

7 总结

最后程序就跳转到_main(在arch/arm/lib/crt0.S中)函数执行。我们知道U-Boot最后就是执行一个命令行程序,然后可以跳转到内核执行。这正是在_main中要完成的操作。下一篇文章我们继续分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tilblackout

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值