嵌入式之uboot源码分析-启动第一阶段学习笔记

本文详细分析了UBoot启动第一阶段的start.S执行步骤,包括头文件包含、16字节头部、异常向量表构建、设置CPU模式、初始化DDR、设置栈、调用lowlevel_init函数、虚拟地址映射等内容。重点讨论了lowlevel_init.S中对复位状态检查、DDR初始化、时钟初始化、串口初始化的操作。文章深入浅出地解释了代码中关键步骤的作用和原理。
摘要由CSDN通过智能技术生成

注: 以下的内容来自朱老师物联网大讲堂uboot部分课件

Uboot启动第一阶段start.S执行步骤

1.头文件包含 <config.h>(x210的各种宏定义) <version.h>(u-boot的版本信息) <asm/proc/domain.h>(用于存储用户、管理员各自数据的,类似于商场分为顾客、商贩) <regs.h>
x210寄存器的相关宏定义
2.16K的校验头,SD卡启动/Nand启动等整个镜像开头需要16字节的校验头。
3.代码开始,先reset进入SVC管理模式
4.构建异常向量表
5.读取链接地址和物理地址
6.Cpu初始化(关、设置、打开l2cache)
7.刷新L1 cache的icache和dcache,关MMU
8.识别启动方式(x210有多种启动方式)
9.第一次设置栈(因为要进行函数调用,需要保存返回地址在LR)
10.调用函数lowlevel_init(设置时钟和DDR,并使用串口打印debug字符“OK”)
11.开发板供电锁存
12.第二次设置栈(因为现在DDR已经可以使用了,我们就将栈迁移至DDR)
13.判断是否需要重定位
14.重定位,(热启动,即:睡眠唤醒等,已经重定位过,就不需要重定位)
15.虚拟地址映射(目的提高效率)
16.第三次设置栈(栈放在比较合适(安全,紧凑而不浪费内存)的地方)
17.清理bss
18.调用start_armboot,进入uboot的第二阶段

具体步骤及对应代码分析

1. start.S引入

根据之前的学习经验我们知道,程序的运行肯定都要有一个入口,uboot的程序运行也是一样的,我们在裸机的部分知道了程序执行开始必须执行一段汇编代码进行相关的设定后(关看门狗、设置SVC栈、开/关icache、重定位等),才能调用C语言程序,所以这里我们要先找到汇编代码的入口。

1.1 u-boot.lds中找到start.S入口

(1)在C语言中整个项目的入口就是main函数(这是C语言规定的),所以譬如说一个有10000个.c文件的项目,第一个要分析的文件就是包含了main函数的那个文件。
(2)在uboot中因为有汇编阶段参与,因此不能直接找main.c。整个程序的入口取决于链接脚本中ENTRY声明的地方。ENTRY(_start)因此_start符号所在的文件就是整个程序的起始文件,_start所在处的代码就是整个程序的起始代码。

1.2 利用SI软件中找到文件

(1)利用SI工具搜索到一共7个_start,然后分析搜索出来的7处,发现有2个是api_example,2个是onenand相关的,都不是我们要找的。剩下3个都在uboot/cpu/s5pc11x/start.S文件中。
(2)然后进入start.S文件中,发现57行中就是_start标号的定义处,于是乎我们就找到了整个uboot的入口代码,就是第57行。

1.3 SI中找文件技巧

(1)以上,找到了start.S文件,下面我们就从start.S文件开始分析uboot第一阶段。
(2)在SI中,如果我们知道我们要找的文件的名字,但是我们又不知道他在哪个目录下,我们要怎样找到并打开这个文件?方法是在SI中先打开右边的工程项目管理栏目,然后点击最左边那个(这个是以文件为单位来浏览的),然后在上面输入栏中输入要找的文件的名字。我们在输入的时候,SI在不断帮我们进行匹配,即使你不记得文件的全名只是大概记得名字,也能帮助你找到你要找的文件(更多的使用需要自己去实践)。

至此我们就找到了start.S文件也就是程序入口,下面将进行解析

1.4 start.S解析

1.4.1 _start之前的头文件包含(28~33)
#include <config.h>
#include <version.h>
#if defined(CONFIG_ENABLE_MMU)
#include <asm/proc/domain.h>
#endif
#include <regs.h>
#include <config.h>

(1)#include <config.h>。config.h是在include目录下的,这个文件不是源码中本身存在的文件,而是配置过程中自动生成的文件。(详见mkconfig脚本)。这个文件的内容其实是包含了一个头文件:#include <configs/x210_sd.h>".

(2)经过分析后,发现start.S中包含的第一个头文件就是:include/configs/x210_sd.h,这个文件是整个uboot移植时的配置文件。这里面是好多宏。因此这个头文件包含将include/configs/x210_sd.h文件和start.S文件关联了起来。因此之后在分析start.S文件时,主要要考虑的就是x210_sd.h文件。

#include <version.h>

#include <version.h>。include/version.h中包含了include/version_autogenerated.h,这个头文件就是配置过程中自动生成的。里面就一行内容:

#define U_BOOT_VERSION "U-Boot 1.3.4"

这里面定义的宏U_BOOT_VERSION的值是一个字符串,字符串中的版本号信息来自于Makefile中的配置值。这个宏在程序中会被调用,在uboot启动过程中会串口打印出uboot的版本号,那个版本号信息就是从这来的。

#include <asm/proc/domain.h>

(1)#include <asm/proc/domain.h>。asm目录不是uboot中的原生目录,uboot中本来是没有这个目录的。asm目录是配置时创建的一个符号链接,实际指向的是就是asm-arm(详解上一章节分析mkconfig脚本时).
(2)经过分析后发现,实际文件是:include/asm-arm/proc-armv/domain.h
(3)从这里可以看出之前配置时创建的符号链接的作用,如果没有这些符号链接则编译时根本通不过,因为找不到头文件。(所以uboot不能在windows的共享文件夹下配置编译,因为windows中没有符号链接)

思考:为什么start.S不直接包含asm-arm/proc-armv/domain.h,而要用asm/proc/domain.h。
:这样的设计主要是为了可移植性。因为如果直接包含,则start
.S文件和CPU架构(和硬件)有关了,可移植性就差了。譬如我要把uboot移植到mips架构下,则start.S源代码中所有的头文件包含全部要修改。我们用了符号链接之后,则start.S中源代码不用改,只需要在具体的硬件移植时配置不同,创建的符号链接指向的不同,则可以具有可移植性。

#include <regs.h>

regs.h 里面是一些寄存器的宏定义

1.4.2 启动代码的16字节头部(49~54)
#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
	.word 0x2000
	.word 0x0
	.word 0x0
	.word 0x0
#endif

.word 0x2000就是在当前位置放一个word型的值,这个值就是0x2000,占4个字节。

(1)裸机中讲过,在SD卡启动/Nand启动等整个镜像开头需要16字节的校验头。(mkv210image.c中就是为了计算这个校验头)。我们以前做裸机程序时根本没考虑这16字节校验头,
因为:

  1. 如果我们是usb启动直接下载的方式启动的则不需要16字节校验头(irom application note);
  2. 如果是SD卡启动mkv210image.c中会给原镜像前加16字节的校验头。

(2)uboot这里start.S中在开头位置放了16字节的填充占位,这个占位的16字节只是保证正式的image的头部确实有16字节,但是这16字节的内容是不对的,还是需要后面去计算校验和然后重新填充的。

1.4.3 异常向量表的构建(57~85)
_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

_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
_pad:
	.word 0x12345678 /* now 16*4=64 */
.global _end_vect
_end_vect:

	.balignl 16,0xdeadbeef

(1)异常向量表是硬件决定的,软件只是参照硬件的设计来实现它。
(2)异常向量表中每种异常都应该被处理,否则真遇到了这种异常就跑飞了。但是我们在uboot中并未非常细致的处理各种异常。
(3)复位异常处的代码是:b reset,因此在CPU复位后真正去执行的有效代码是reset处的代码,因此reset符号处才是真正的有意义的代码开始的地方。

有点意思的deadbeef

(1).balignl 16,0xdeadbeef. 这一句指令是让当前地址对齐排布,如果当前地址不对齐则自动向后走地址直到对齐,并且向后走的那些内存要用0xdeadbeef来填充。
(2)0xdeadbeef这是一个十六进制的数字,这个数字很有意思,组成这个数字的十六进制数全是abcdef之中的字母,而且这8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值