Uboot启动流程分析(一)
1、前言
Linux系统的启动需要一个bootloader程序,该bootloader程序会先初始化DDR等外设,然后将Linux内核从Flash中拷贝到DDR中,最后启动Linux内核,uboot的全称为Universal Boot Loader,Linux系统中常用的bootloader就是uboot,接下来,将会进行简单的uboot启动流程分析,uboot的源码为uboot-imx-rel_imx_4.15_2.1.0。
2、uboot入口
在分析之前,需要对整个uboot工程进行编译,生成一些分析时需要用到的文件,例如链接文件uboot.lds和uboot映射文件uboot.map,通过链接文件,可以找到uboot的入口,找到uboot启动后运行的第一行代码。
在uboot源码根目录下找到链接文件uboot.lds,如下:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start) //当前入口_start
SECTIONS
{
. = 0x00000000;
. = ALIGN();
.text :
{
*(.__image_copy_start) //入口
*(.vectors) //中断向量表
arch/arm/cpu/armv7/start.o (.text*) //arch/arm/cpu/armv7/start.S代码段
*(.text*)
}
. = ALIGN();
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN();
.data : {
*(.data*)
}
. = ALIGN();
. = .;
. = ALIGN();
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*)));
}
. = ALIGN();
.image_copy_end :
{
*(.__image_copy_end)
}
.rel_dyn_start :
{
*(.__rel_dyn_start)
}
.rel.dyn : {
*(.rel*)
}
.rel_dyn_end :
{
*(.__rel_dyn_end)
}
.end :
{
*(.__end)
}
_image_binary_end = .;
. = ALIGN();
.mmutable : {
*(.mmutable)
}
.bss_start __rel_dyn_start (OVERLAY) : {
KEEP(*(.__bss_start));
__bss_base = .;
}
.bss __bss_base (OVERLAY) : {
*(.bss*)
. = ALIGN();
__bss_limit = .;
}
.bss_end __bss_limit (OVERLAY) : {
KEEP(*(.__bss_end));
}
.dynsym _image_binary_end : { *(.dynsym) }
.dynbss : { *(.dynbss) }
.dynstr : { *(.dynstr*) }
.dynamic : { *(.dynamic*) }
.plt : { *(.plt*) }
.interp : { *(.interp*) }
.gnu.hash : { *(.gnu.hash) }
.gnu : { *(.gnu*) }
.ARM.exidx : { *(.ARM.exidx*) }
.gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
}
在上面的链接文件中,可以确定uboot的入口为_start,该定义在arch/arm/lib/vectors.S文件中:
_start: #ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
.word CONFIG_SYS_DV_NOR_BOOT_CFG
#endif b reset //中断向量表,跳转到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后,运行b reset语句,跳转到reset中运行,b reset后面跟着中断向量表,reset的定义,对于不同的CPU架构不一样,对于NXP的i.mx6ul芯片,该定义在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
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 //读取cpsr寄存器的值到r0寄存器中(cpsr:bit0~bit4保存处理器工作模式)
and r1, r0, #0x1f @ mask mode bits //r0的值与0x1f相与,结果保存到r1寄存器
teq r1, #0x1a @ test for HYP mode //判断当前处理器模式是否是HYP模式
bicne r0, r0, #0x1f @ clear all mode bits //如果CPU不处于HYP模式,则清除bit0~bit4
orrne r0, r0, #0x13 @ set SVC mode //设置为SVC模式
orr r0, r0, #0xc0 @ disable FIQ and IRQ //禁止FIQ和IRQ
msr cpsr,r0 //将当前r0寄存器的值回写到cpsr寄存器 /*
* 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, , r0, c1, c0, @ Read CP15 SCTLR Register //读取SCTLR寄存器
bic r0, #CR_V @ V = //设置V = 0
mcr p15, , r0, c1, c0, @ Write CP15 SCTLR Register /* Set vector address in CP15 VBAR register */
ldr r0, =_start //设置vector地址到CP15 VBAR寄存器
mcr p15, , r0, c12, c0, @Set VBAR
#endif /* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15 //跳转到cpu_init_cp15
bl cpu_init_crit //跳转到cpu_init_crit
#endif bl _main //跳转到_main
在上面的代码中主要是对arm架构处理器的运行模式进行设置,对一些寄存器进行赋值操作,对于cpu_init_crit函数的实现如下:
#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 //跳到lowlevel_init,设置pll、mux和memory
ENDPROC(cpu_init_crit)
#endif
在cpu_init_crit函数中,跳到了lowlevel_init函数中运行,接下来,将详细分析一下lowlevel_init和_main函数。
3、lowlevel_init函数
在uboot源码中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 //设置sp指针指向CONFIG_SYS_INIT_SP_ADDR(i.mx6ul内部RAM)
bic sp, sp, # /* 8-byte alignment for ABI compliance */ //sp指针8字节对齐处理
#ifdef CONFIG_SPL_DM
mov r9, #
#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 //sp = sp - 248
bic sp, sp, #
mov r9, sp //将sp指针保存到r9寄存器
#endif
#endif
/*
* Save the old lr(passed in ip) and the current lr to stack
*/
push {ip, lr} //将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 //跳转到s_init(对与im6ul啥也没干,直接返回)
pop {ip, pc} //将ip和pc指针出栈
ENDPROC(lowlevel_init)
函数进来后,首先设置了sp指针的值为CONFIG_SYS_INIT_SP_ADDR,该值为一个宏定义,对于i.mx6ul芯片定义在文件include/configs/mx6ul_14x14_evk.h,如下:
#define CONFIG_SYS_INIT_RAM_ADDR IRAM_BASE_ADDR //0x00900000(OCRAM的基地址)
#define CONFIG_SYS_INIT_RAM_SIZE IRAM_SIZE //0x00020000(OCRAM的大小,容量为128KB) #define CONFIG_SYS_INIT_SP_OFFSET \
(CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE) //0x00020000 - 256 = 0x1FF00
#define CONFIG_SYS_INIT_SP_ADDR \
(CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET) //0x00900000 + 0x1FF00 = 0x0091FF00
OCRAM的基地址和大小可以在imx6ul的数据手册中的系统内存映射表中查到:
对于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) @ */
因此,当前的sp指针指向如下所示:
继续返回到lowlevel_init.S文件中分析,sp指针减去GD_SIZE的大小,并sp指针进行8字节对齐,在上面可以知道GD_SIZE的大小为248,因此,此时的OCRAM分配如下所示:
接下来,则是将sp指针的值保存到了r9寄存器,然后跳转到s_init函数里面执行,s_init函数的定义在文件arch/arm/cpu/armv7/mx6/soc.c文件中,定义如下:
void s_init(void)
{
...
...
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;
...
...
}
该函数,会判断CPU的类型,如果是imx6ul的话,函数将直接返回,s_init函数返回后,回到low_level_init函数,此时,lowlevel_init函数也执行完了,继续回到cpu_init_crit函数,函数执行完成,最终返回到save_boot_params_ret,继续往下运行bl _main这句代码。
4、小结
找到了uboot的入口后,并对save_boot_params_ret函数简单分析后,总结一下其调用流程,如下所示:
save_boot_params_ret
|
cpu_init_crit
| |
| lowlevel_init
| |
| s_init
|
_main
Uboot启动流程分析(一)的更多相关文章
- u-boot启动流程分析(2)_板级(board)部分
转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...
- imx6 uboot启动流程分析
参考http://blog.csdn.net/skyflying2012/article/details/25804209 这里以imx6平台为例,分析uboot启动流程对于任何程序,入口函数是在链接 ...
- Uboot启动流程分析(三)
1.前言 在前面的文章Uboot启动流程分析(二)中,链接如下: https://www.cnblogs.com/Cqlismy/p/12002764.html 已经对_main函数的整个大体调用流程 ...
- Uboot启动流程分析(二)
1.前言 在前面的文章Uboot启动流程分析(一)中,链接如下: https://www.cnblogs.com/Cqlismy/p/12000889.html 已经简单地分析了low_level_i ...
- 嵌入式Linux驱动学习之路(五)u-boot启动流程分析
这里说的u-boot启动流程,值得是从上电开机执行u-boot,到u-boot,到u-boot加载操作系统的过程.这一过程可以分为两个过程,各个阶段的功能如下. 第一阶段的功能: 硬件设备初始化. 加 ...
- u-boot启动流程分析(1)_平台相关部分
转自:http://www.wowotech.net/u-boot/boot_flow_1.html 1. 前言 本文将结合u-boot的“board—>machine—>arch—> ...
- Uboot启动流程分析(转载)
最近一段时间一直在做uboot移植相关的工作,需要将uboot-2016-7移植到单位设计的ARMv7的处理器上.正好元旦放假三天闲来无事,有段完整的时间来整理下最近的工作成果.之前在学习uboot时 ...
- am335x uboot启动流程分析
基本指令含义 .globl _start .globl指示告诉汇编器,_start这个符号要被链接器用到,所以要在目标文件的符号表中标记它是一个全局符号 b,bl b是不带返回的跳转 bl带返回的跳 ...
- U-BOOT启动流程分析--start.s(二)
一.概述 u-boot的启动流程: 从文件层面上看主要流程是在两个文件中:cpu/arm920t/start.s,lib_arm/board.c, 先来分析start.s 在flash中执行的引 ...
随机推荐
- Python的互斥锁与信号量
并发与锁 a. 多个线程共享数据的时候,如果数据不进行保护,那么可能出现数据不一致现象,使用锁,信号量.条件锁 b. c.互斥锁1. 互斥锁,是使用一把锁把代码保护起来,以牺牲性能换取代码的安全性,那 ...
- ASP.NET Core 如何用 Cookie 来做身份验证
前言 本示例完全是基于 ASP.NET Core 3.0.本文核心是要理解 Claim, ClaimsIdentity, ClaimsPrincipal,读者如果有疑问,可以参考文章 理解ASP.NE ...
- [算法]LeetCode 152:乘积最大子序列
题目描述: 给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数). 示例 1: 输入: [2,3,-2,4]输出: 6解释: 子数组 [2,3] 有最大乘积 6.示 ...
- asp.net core 新建area使用asp-action,asp-controller不管用
解决方法: 在新建的Area目录下,这里使用Admin,Admin/Views下新建_ViewImports.cshtml和_ViewStart.cshtml两个视图文件,复制项目自动生成的到对应的新 ...
- JS基础语法---阶段复习+作业练习+接下来知识点heads up
调试:调试代码---高级程序员都是从调试开始的 调试: 写代码---打开浏览器--F12(开发人员工具)--->Sources---双击文件,在某一行代码前面点击一下(出现的东西就是断点) 一元 ...
- 腾讯云游戏服务平台CMatrix品牌全新升级为GameMatrix
近日,隶属腾讯互娱公共研发运营体系(下文称CROS)下的云游戏服务平台CMatrix宣布进行品牌升级,启用全新商标Tencent GameMatrix,将原先代表云服务的“C”替换成游戏的英文单词“G ...
- 浅谈Vue下的components模板
浅谈Vue下的components模板在我们越来越深入Vue时,我们会发现我们对HTML代码的工程量会越来越少,今天我们来谈谈Vue下的 components模板的 初步使用方法与 应用 我们先来简单 ...
- [b0029] python 归纳 (十四)_队列Queue实现生产者消费者
# -*- coding: UTF-8 -*- """ 多线程的生产者,消费者 使用队列Queue """ import Queue imp ...
- 渗透测试学习 十三、 SQLmap使用详解
SQLmap介绍 sqlmap是一个由python语言编写的开源的渗透测试工具,它主要是检测SQL注入漏洞,是一款功能强大的SQL漏洞检测利用工具. 他可以检测的数据库有:access.msSQL.M ...
- fiddler---Fiddler接口测试
前面介绍了Fiddler一些简单的使用功能,Fiddler不仅可以抓包也可以做接口工具使用,在没有接口文档的时候我们也可以通过Fiddler查看接口具体有哪些内容 Fiddler发送请求 在Fiddl ...