首页 > 其他分享 >U-Boot启动流程详解

U-Boot启动流程详解

时间:2025-01-15 21:28:34浏览次数:3  
标签:r0 arch 流程 Boot start init 详解 寄存器 CONFIG

一、第一部分

要分析 uboot的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入口。

打开u-boot.lds文件看到第三行,可以发现_start是代码的入口点。

ENTRY(_start)

_start在文件 arch/arm/lib/vectors.S中有定义,定义如下:

.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

_start后面就是中断向量表,从图中的.section ".vectors", "ax”可以得到,此代码存放在 .vectors段里面。 

u-boot.map是 uboot的映射文件,可以从此文件看到某个文件或者函数链接到了哪个地址,我们将两个文件对比起来看:vectors段的起始地址是 0X87800000,说明整个 uboot的起始地址就是0X87800000,__image_copy_start顾名思义为镜像拷贝的起始地址也为0X87800000。之后是各个文件的代码段链接到整个中断向量表地址之后,

 .text :
 {
  *(.__image_copy_start)
  *(.vectors)
  arch/arm/cpu/armv7/start.o (.text*)
  *(.text*)
 }
.text           0x0000000087800000    0x3cd64
 *(.__image_copy_start)
 .__image_copy_start
                0x0000000087800000        0x0 arch/arm/lib/built-in.o
                0x0000000087800000                __image_copy_start
 *(.vectors)
 .vectors       0x0000000087800000      0x300 arch/arm/lib/built-in.o
                0x0000000087800000                _start
                0x0000000087800020                _undefined_instruction
                0x0000000087800024                _software_interrupt
                0x0000000087800028                _prefetch_abort
                0x000000008780002c                _data_abort
                0x0000000087800030                _not_used
                0x0000000087800034                _irq
                0x0000000087800038                _fiq
                0x0000000087800040                IRQ_STACK_START_IN
 arch/arm/cpu/armv7/start.o(.text*)
 .text          0x0000000087800300       0xb0 arch/arm/cpu/armv7/start.o

在 u-boot.lds中有一些跟地址有关的“变量”需要我们注意一下,后面分析 u-boot源码的时候会用到,这些变量要最终编译完成才能确定的!!!比如我编译完成以后这些“变量”的值为:

变量数值描述
__image_copy_start0x87800000uboot拷贝的首地址
__image_copy_end0x8784f1a4uboot拷贝的结束地址
__rel_dyn_start0x8784f1a4.rel.dyn段起始地址
__rel_dyn_end0x8785794c.rel.dyn段结束地址
_image_binary_end0x8785794c镜像结束地址
__bss_start0x8784f1a4 .bss段起始地址
__bss_end0x8789a194.bss段结束地址
.image_copy_end
                0x000000008784f1a4        0x0
 *(.__image_copy_end)
 .__image_copy_end
                0x000000008784f1a4        0x0 arch/arm/lib/built-in.o

.rel_dyn_start  0x000000008784f1a4        0x0
 *(.__rel_dyn_start)
 .__rel_dyn_start
                0x000000008784f1a4        0x0 arch/arm/lib/built-in.o

.rel_dyn_end    0x000000008785794c        0x0
 *(.__rel_dyn_end)
 .__rel_dyn_end
                0x000000008785794c        0x0 arch/arm/lib/built-in.o

.end            0x000000008785794c        0x0
 *(.__end)
 .__end         0x000000008785794c        0x0 arch/arm/lib/built-in.o
                0x000000008785794c                _image_binary_end = .
                0x0000000087858000                . = ALIGN (0x1000)

.bss_start      0x000000008784f1a4        0x0
 *(.__bss_start)
 .__bss_start   0x000000008784f1a4        0x0 arch/arm/lib/built-in.o
                0x000000008784f1a4                __bss_start
                0x000000008784f1a4                __bss_base = .

.bss_end        0x000000008789a194        0x0
 *(.__bss_end)
 .__bss_end     0x000000008789a194        0x0 arch/arm/lib/built-in.o
                0x000000008789a194                __bss_end

现在开始分析_start,第一行就跳到reset函数执行,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
——————————————————————————————————————————————————————————
ENTRY(save_boot_params)
	b	save_boot_params_ret		@ back to my caller
——————————————————————————————————————————————————————————

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

 进入reset后,又跳到了save_boot_params函数,在save_boot_params函数中,又跳到了save_boot_params_ret中执行。进入此函数后,我们看到了对cpsr寄存器进行操作,我们知道该寄存器的bit0~bit4这5位是用来控制处理器的模式,在此函数中主要用到了两个模式:

M[4:0]模式
11010Hyp(hyp)
10011Supervisor(svc)

具体的代码分析过程为:读取寄存器 cpsr中的值,并保存到 r0寄存器中。判断 r1寄存器的值是否等于 0X1A(0b11010),也就是判断当前处理器模式是否处于 Hyp模式。如果 r1和 0X1A不相等也就是CPU不处于Hyp模式的话就将r0寄存器的bit0~4进行清零,其实就是清除模式位,且将 r0的寄存器的值与 0x13进行或运算,也就是设置处理器进入SVC模式。 r0寄存器的值再与 0xC0进行或运算,那么 r0寄存器此时的值就是 0xD3。cpsr寄存器的 bit7和bit6分别控制IRQ和 FIQ这两个中断的开关,设置为1就关闭了 FIQ和 IRQ。将 r0寄存器写回到 cpsr寄存器中。完成设置 CPU处于 SVC模式,并且关闭FIQ和 IRQ这两个中断。

继续往下看:

 * 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

我们没有定义CONFIG_OMAP44XX和CONFIG_SPL_BUILD条件成立,继续执行。

首先了解一下MCR和MRC指令的格式,格式如下:

MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>

MRC的指令格式和 MCR一样,只不过在 MRC指令中Rt就是目标寄存器,也就是从CP15指定寄存器读出来的数据会保存在 Rt中。而 CRn就是源寄存器,也就是要读取的写处理器寄存器。 

接下来的两张图是正点原子关于CRn分别为c1和c12寄存器的表示所指代的寄存器 

CR_V在 arch/arm/include/asm/system.h中有如下所示定义:

#define CR_V (1 << 13) /* Vectors relocated to 0xffff0000 */

第一个MRC指令就是将寄存器 SCTLR写到R0中,接着位清除指令目的就是清除SCTLR寄存器中的 bit13,此位是向量表控制位,当为0的时候向量表基地址为0X00000000,软件可以重定位向量表。为1的时候向量表基地址为 0XFFFF0000,软件不能重定位向量表。这里将该位清零,目的就是为了接下来的向量表重定位,将 r0寄存器的值重新写入到寄存器 SCTLR中。

接着设置 r0寄存器的值为 _start,_start就是整个 uboot的入口地址,其值为 0X87800000相当于 uboot的起始地址,因此 0x87800000也是向量表的起始地址。行将 r0寄存器的值 (向量表值 )写入到 CP15的c12寄存器中,也就是VBAR寄存器 ,对向量表进行重定位。

接着分析:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
	bl	cpu_init_cp15
	bl	cpu_init_crit
#endif

    bl	_main


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)

函数 cpu_init_cp15都是一些和 CP15有关的内容,我们不用关心。cpu_init_crit则调用了lowlevel_init,接着我们来重点分析一下lowlevel_init和_main函数。

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

首先设置sp指向 CONFIG_SYS_INIT_SP_ADDR,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)

IRAM_BASE_ADDR和 IRAM_SIZE在文件arch/arm/include/asm/arch-mx6/imx-regs.h中有定义, 在 .config中定义了 CONFIG_MX6UL,所以条件不成立,因此 IRAM_SIZE=0X20000=128KB。

#define IRAM_BASE_ADDR			0x00900000

#if !(defined(CONFIG_MX6SX) || defined(CONFIG_MX6UL) || \
	defined(CONFIG_MX6SLL) || defined(CONFIG_MX6SL))
#define IRAM_SIZE                    0x00040000
#else
#define IRAM_SIZE                    0x00020000
#endif

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 188 /* offsetof(struct global_data, malloc_base)	@ */
#define GD_RELOCADDR 44 /* offsetof(struct global_data, relocaddr)	@ */
#define GD_RELOC_OFF 64 /* offsetof(struct global_data, reloc_off)	@ */
#define GD_START_ADDR_SP 60 /* offsetof(struct global_data, start_addr_sp)	@ */

 综上所述:

CONFIG_SYS_INIT_SP_OFFSET = 0x00020000 - 256 = 0x1FF00。
CONFIG_SYS_INIT_SP_ADDR = 0x00900000 + 0X1FF00 = 0X0091FF00

可以看到,一个变量的赋值就疯狂套娃,所以大家最好跟着文档一起找一遍(虽然过程很烦)。

先8字节对齐,接着sp指针减去GD_SIZE,GD_SIZE 同样在generic-asm-offsets.h 中定义了,大小为248字节。接着对sp做8字节对齐,此时sp 的地址为0X0091FF00-248=0X0091FE08。附上正点的图。

接着将sp地址保存在r9寄存器中。将ip和lr压栈并调用函数s_init,函数调用完毕后将入栈的ip和lr进行出栈,并将lr赋给pc。

对于I.MX6UL/I.MX6ULL 来说,s_init就是个空函数。从s_init函数退出以后进入函数lowlevel_init,
但是lowlevel_init 函数也执行完成了,返回到了函数cpu_init_crit,函数cpu_init_crit也执行完成了,最终返回到save_boot_params_ret,准备调用_main函数。篇幅原因,这个放在下一篇文章分析(其实是要学晕了)。

开个小番外,我们看到这里很多地方用到了字节对齐。需要字节对齐的根本原因在于CPU访问数据的效率问题。假设上面整型变量的地址不是自然对齐,原本我们只需要一次数据读写就可以完成操作,但现在因为没有数据对齐就需要多次用不同变量类型读取。

bic	sp, sp, #7

我们发现文中有这样一行代码,它能做8字节对齐的原因是它将该值低三位清零,其实就是将该值对8的余数进行了抹去。

标签:r0,arch,流程,Boot,start,init,详解,寄存器,CONFIG
From: https://blog.csdn.net/qq_64033704/article/details/145166083

相关文章

  • 基于Springboot的爱心公益网站设计与实现
       博主介绍:java高级开发,从事互联网行业多年,熟悉各种主流语言,精通java、python、php、爬虫、web开发,已经做了多年的设计程序开发,开发过上千套设计程序,没有什么华丽的语言,只有实实在在的写点程序。......
  • 基于spring boot宠物领养系统的设计与实现 宠物领养系统(源码+文档)
    目录一.研究目的二.需求分析三.数据库设计 四.系统页面展示五.免费源码获取方式一.研究目的本课题是根据用户的需要以及网络的优势建立的一个宠物领养系统,来满足用宠物领养的需求。本宠物领养系统应用JSP技术,Java语言,MYSQL数据库存储数据,基于B/S结构开发。在网站的整......
  • V-By-One 详解
    文章目录V-BY-ONE概述HTPDN,LOCKN信号V-BY-ONEHSLink系统图V-BY-ONE传输速率计算总比特率计算每通道编码比特率计算V-BY-ONE收发器功能划分V-BY-ONETX模块分析V-BY-ONEfsm模块V-BY-ONETX状态机V-BY-ONERX状态机链路启动流程链路失败流程V-BY-ONECDRtra......
  • springboot基于Vue框架的学生交流互助平台
    文章目录详细视频演示项目介绍技术介绍功能介绍核心代码系统效果图详细视频演示文章底部名片,获取项目的完整演示视频,免费解答技术疑问项目介绍  SpringBoot基于Vue框架的学生交流互助平台是一个功能丰富、操作简便、安全可靠的综合性系统。它为学生提供了一个......
  • springboot基于图像识别与分类的中国蛇类识别系统
    文章目录详细视频演示项目介绍技术介绍功能介绍核心代码系统效果图详细视频演示文章底部名片,获取项目的完整演示视频,免费解答技术疑问项目介绍  随着人工智能技术的不断发展,SpringBoot基于图像识别与分类的中国蛇类识别系统将在未来得到更广泛的应用和发展。它......
  • springboot基于司机信用评价的货运管理系统
    文章目录详细视频演示项目介绍技术介绍功能介绍核心代码系统效果图详细视频演示文章底部名片,获取项目的完整演示视频,免费解答技术疑问项目介绍  在当今快速发展的经济环境中,物流管理已成为企业运营的关键环节之一。高效的物流管理系统不仅能够提高货物运输的效......
  • 计算机毕业设计Springboot“小时光”儿童摄影管理系统 基于Spring Boot的“童影时光”
    计算机毕业设计Springboot“小时光”儿童摄影管理系统644iz033(配套有源码程序mysql数据库论文)本套源码可以先看具体功能演示视频领取,文末有联xi可分享随着社会的发展和人们生活水平的提高,儿童摄影逐渐成为一种热门的服务项目。传统的儿童摄影管理方式存在着诸多问题,如预......
  • 计算机毕业设计Springboot“小圈子”校园互助平台 基于Spring Boot的校园互助社区平台
    计算机毕业设计Springboot“小圈子”校园互助平台lc2rg3ad(配套有源码程序mysql数据库论文)本套源码可以先看具体功能演示视频领取,文末有联xi可分享随着互联网技术的飞速发展,校园内的互助需求也日益增长。传统的互助方式往往效率低下且不够便捷,因此,开发一个高效、便捷的校......
  • springboot 项目配置https
    当你的前端网页添加了https后,那么由于…前端调用后端的接口,同样的也需要配置https。下面以宝塔为例,如何实现,请看下面讲解:1.准备好SSL证书application.yml源文件:spring:redis:host:60.204.232.18port:6379database:3cloud:nacos:......
  • 计算机毕业设计Springboot“绿环”垃圾分类回收管理系统 SpringBoot驱动的“绿意”垃
    计算机毕业设计Springboot“绿环”垃圾分类回收管理系统w0nyol05(配套有源码程序mysql数据库论文)本套源码可以先看具体功能演示视频领取,文末有联xi可分享随着城市化进程的加速,垃圾处理问题日益凸显,成为制约城市可持续发展的关键因素之一。为了有效提升垃圾回收效率,促进资......