嵌入式linux开发uboot启动过程源码分析(一)

【自取】最近整理的,有需要可以领取学习:

一、 uboot启动流程简介

与大多数BootLoader一样,uboot的启动过程分为BL1和BL2两个阶段。BL1阶段通常是开发板的配置等设备初始化代码,需要依赖依赖于SoC体系结构,通常用汇编语言来实现;BL2阶段主要是对外部设备如网卡、Flash等的初始化以及uboot命令集等的自身实现,通常用C语言来实现。

uboot的BL1阶段代码通常放在start.s文件中,用汇编语言实现,其主要代码功能如下:

(1) 指定uboot的入口。在链接脚本uboot.lds中指定uboot的入口为start.S中的_start。

(2)设置异常向量(exception vector)

(3)关闭IRQ、FIQ,设置SVC模式

(4)关闭L1 cache、设置L2 cache、关闭MMU

(5)根据OM引脚确定启动方式

(6)在SoC内部SRAM中设置栈

(7)lowlevel_init(主要初始化系统时钟、SDRAM初始化、串口初始化等)

(8)设置开发板供电锁存

(9)设置SDRAM中的栈

(10)将uboot从SD卡拷贝到SDRAM中

(11)设置并开启MMU

(12)通过对SDRAM整体使用规划,在SDRAM中合适的地方设置栈

(13)清除bss段,远跳转到start_armboot执行,BL1阶段执行完

start_armboot函数位于lib_arm/board.c中,是C语言开始的函数,也是BL2阶段代码中C语言的主函数,同时还是整个u-boot(armboot)的主函数,BL2阶段的主要功能如下:

(1)规划uboot的内存使用

(2)遍历调用函数指针数组init_sequence中的初始化函数

(3)初始化uboot的堆管理器mem_malloc_init

(4)初始化SMDKV210开发板的SD/MMC控制器mmc_initialize

(5)环境变量重定位env_relocate

(6)将环境变量中网卡地址赋值给全局变量的开发板变量

(7)开发板硬件设备的初始化devices_init

(8)跳转表jumptable_init

(9)控制台初始化console_init_r

(10)网卡芯片初始化eth_initialize

(11)uboot进入主循环main_loop

二、uboot程序入口分析

u-boot.lds文件是uboot工程的链接脚本文件,位于board\samsung\smdkc110目录下,对于工程项目编译后期的链接阶段非常重要,决定了uboot程序的组装。

u-boot.lds链接文件中的ENTRY(_start)指定了uboot程序的入口地址为_start。

在SourceInsight建立uboot工程,利用索引功能查找_start,在搜索结果中找到与三星smdkv210开发板相关的代码,最终锁定cpu\s5pc11x\start.S文件,定位到文件中的_start标识符。

三、start.S文件分析

start.S有四个头文件:

include

config.h头文件在配置开发板时由mkconfig脚本创建的头文件,头文件内容即包含开发板的头文件:#include

include

version.h头文件的内容为包含自动生成的版本头文件,头文件内容为:#include “version_autogenerated.h”,version_autogenerated.h头文件定义了版本宏,宏定义为:#define U_BOOT_VERSION “U-Boot 1.3.4″。版本宏的值就是Makefile中定义的版本信息。

include

domain.h头文件在定义了CONFIG_ENABLE_MMU宏时有效,为链接文件,实际指向的文件为include/asm-arm/proc-armv/domain.h。

include

regs.h头文件为链接文件,指向s5pc110.h头文件,s5pc110.h文件内部使用宏定义了有关SoC内部寄存器的大量信息。

if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)

.word 0x2000

.word 0x0

.word 0x0

.word 0x0

endif

定义uboot程序开头的16字节校验头信息填充空间,头校验信息块内的值需要在后面写入。

.globl _start

_start:

b reset

ldrpc, _undefined_instruction

ldrpc, _software_interrupt

ldrpc, _prefetch_abort

ldrpc, _data_abort

ldrpc, _not_used

ldrpc, _irq

ldrpc, _fiq

uboot程序的入口点实际是定义了异常向量表,异常向量表由SoC硬件实现,因此uboot在开机上电复位时需要跳转到reset执行。

SoC上电复位后运行的第一段代码就是reset。主要包括以下几部分:

A、关闭IRQ、FIQ,并将处理器模式设置为SVC模式

B、CPU关键寄存器的初始化cpu_init_crit:

关闭L2 cache

初始化L2 cache

开启L2 cache

关闭L1 cache

关闭MMU

读取OM启动引脚信息

确定从启动设备SD卡启动

设置SRAM中的栈为调用lowlevel_init做准备(lowlevel_init内部有嵌套调用)

调用lowlevel_init(主要初始化系统时钟、SDRAM初始化、串口初始化等)

设置开发板供电锁存

设置SDRAM中的栈

判断当前代码是否运行在SDRAM中,如果当前代码运行在SDRAM中,则跳过代码重定位。

判断启动方式,选择SD卡启动设备,跳转到mmcsd_boot

SD卡启动的准备工作,从SD卡拷贝uboot到SDRAM:movi_bl2_copy

C、设置MMU,开启MMU

D、通过对SDRAM整体使用规划,在SDRAM中合适的地方设置栈

E、清除bss段,远跳转到start_armboot执行,BL1阶段执行完

lowlevel_init位于\board\samsung\smdkc110\lowlevel_init.S中,主要功能如下:

A、检查复位状态,判断启动的方式

根据复位状态选择复位启动模式,当处于低功率状态时,复位启动可以跳过多个后续步骤。

[En]

The mode of reset startup is selected according to the reset state, and the reset startup can skip a number of subsequent steps when it is in a low power state.

B、IO状态恢复

C、关闭看门狗

D、外部SRAM的GPIO初始化、外部SROM初始化

E、开发板供电锁存设置

F、判断当前代码是否运行在SDRAM,如果当前代码运行在SDRAM,说明目前从低功耗状态复位,可以跳过系统时钟初始化、串口初始化、SDRAM初始化等

G、初始化系统时钟:system_clock_init

H、初始化SDRAM内存:mem_ctrl_asm_init

I、初始化串口,打印出’O’:uart_asm_init

J、初始化trustzone:tzpc_init

K、初始化nand或onenand

L、检查复位状态

M、关闭ABB

N、串口打印出’K’

说明:”OK”是打印出的调试信息,如果打印出’O’则说明在串口初始化uart_asm_init前的所有代码是正确的。如果打印出”OK”则说明在开发板板级初始化lowlevel_init前的所有代码是正常工作的。

system_clock_init、uart_asm_init、tzpc_init、nand_asm_init都位于lowlevel_init.S文件内,mem_ctrl_asm_init位于cpu\s5pc11x\s5pc110\cpu_init.S文件中。

四、board.c文件分析

uboot在执行完BL1阶段后远跳转到start_armboot函数执行BL2,start_armboot函数位于lib_arm\board.c中。

typedef int (init_fnc_t) (void);函数类型

init_fnc_t **init_fnc_ptr;//二级函数指针

define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (“r8”)

DECLARE_GLOBAL_DATA_PTR定义了一个存储在寄存器r8中的指向gd_t类型全局变量的指针gd。

全局变量结构体的定义:

typedefstructglobal_data {

bd_t*bd;//boardinfo结构体信息,存放和开发板有关的信息

unsigned longflags;//标志位

unsigned longbaudrate;//串口通信波特率

unsigned longhave_console;//控制台/ serial_init() was called /

unsigned longreloc_off;//重定位偏移量/ Relocation Offset /

unsigned longenv_addr;//环境变量结构体的地址/ Address of Environment struct /

unsigned longenv_valid;//环境变量使用标志/ Checksum of Environment valid? /

unsigned longfb_base;//fb基地址/ base address of frame buffer /

ifdef CONFIG_VFD

unsigned charvfd_type;/// display type /

endif

voidjt;//跳转表/ jump table /

} gd_t;

开发板信息结构体变量的定义:

typedef struct bd_info {

intbi_baudrate;//硬件串口波特率/ serial console baudrate /

unsigned longbi_ip_addr;//开发板IP地址/ IP Address /

unsigned charbi_enetaddr[6];//开发板网卡地址 / Ethernet adress /

struct environment_s *bi_env;//环境变量指针

ulong bi_arch_number;//机器码/ unique id for this board /

ulong bi_boot_params;//uboot启动参数/ where this board expects params /

struct/ RAM configuration /

ulong start;

ulong size;

}bi_dram[CONFIG_NR_DRAM_BANKS];//内存插条信息

ifdef CONFIG_HAS_ETH1

/ second onboard ethernet port /

unsigned char bi_enet1addr[6];//第二块网卡的地址

endif

} bd_t;

2、uboot的内存规划

SDRAM_BASE被MMU映射在0xC0000000,CFG_UBOOT_BASE是0xC3E00000

在BL1段运行时,uboot镜像被拷贝到CFG_UBOOT_BASE开始的地址处。

gd的地址:

gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE – CFG_MALLOC_LEN – CFG_STACK_SIZE – sizeof(gd_t);

bd的地址:

gd->bd = (bd_t)((char)gd – sizeof(bd_t));

start_armboot函数的主要功能如下:

(1)、遍历调用函数指针数组init_sequence中的初始化函数

依次遍历调用函数指针数组init_sequence中的函数,如果有函数执行出错,则执行hang函数,打印出”### ERROR ### Please RESET the board ###”,进入死循环。

(2)、初始化uboot的堆管理器mem_malloc_init

(3)、初始化SMDKV210的SD/MMC控制器mmc_initialize

(4)、环境变量重定位env_relocate

(5)、将环境变量中网卡地址赋值给全局变量的开发板变量

(6)、开发板硬件设备的初始化devices_init

(7)、跳转表jumptable_init

(8)、控制台初始化console_init_r

(9)、网卡芯片初始化eth_initialize

(10)、uboot进入主循环main_loop

void start_armboot (void)
{
//全局数据变量指针gd占用r8。
DECLARE_GLOBAL_DATA_PTR;
/ 给全局数据变量gd安排空间/
gd = (gd_t)(_armboot_start – CFG_MALLOC_LEN – sizeof(gd_t));
memset ((void
)gd, 0, sizeof (gd_t));
/ 给板子数据变量gd->bd安排空间/
gd->bd = (bd_t)((char)gd – sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
monitor_flash_len = _bss_start – _armboot_start;//u-boot长度。
/ 顺序执行init_sequence数组中的初始化函数 /
for (init_fnc_ptr = init_sequence; init_fnc_ptr; ++init_fnc_ptr) {
if ((
init_fnc_ptr)() != 0) {
hang ();
}
}
/ 初始化堆空间 /
mem_malloc_init (_armboot_start – CFG_MALLOC_LEN);
/ 重新定位环境变量, /
env_relocate ();
/ 从环境变量中获取IP地址 /
gd->bd->bi_ip_addr = getenv_IPaddr (“ipaddr”);
/ 以太网接口MAC 地址 /
devices_init (); / 设备初始化 /
jumptable_init (); //跳转表初始化
console_init_r (); / 完整地初始化控制台设备 /
enable_interrupts (); / 使能中断处理 /
/ 通过环境变量初始化 /
if ((s = getenv (“loadaddr”)) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
/ main_loop()循环不断执行 /
for (;;) {
main_loop (); / 主循环函数处理执行用户命令 — common/main.c /
}
}

函数指针数组init_sequence:

init_fnc_t *init_sequence[] = {

cpu_init,//CPU架构的初始化,为空cpu\s5pc11x\cpu.c

board_init,//开发板初始化board\samsung\smdkc110\smdkc110.c

interrupt_init,//定时器timer4初始化cpu\s5pc11x\interrupts.c

env_init,//环境变量初始化common\env_movi.c

init_baudrate,//波特率设置lib_arm\board.c

serial_init,//延时函数C,没有再次初始化串口cpu\s5pc11x\serial.c

console_init_f,//控制台第一阶段初始化,控制台未初始化好common\console.c

display_banner,//用串口发送uboot版本信息lib_arm\board.c

if defined(CONFIG_DISPLAY_CPUINFO)

print_cpuinfo,//串口打印系统时钟信息cpu\s5pc11x\s5pc110\speed.c

endif

if defined(CONFIG_DISPLAY_BOARDINFO)

checkboard,//打印开发板信Board:SMDKV210

//board\samsung\smdkc110\smdkc110.c

endif

if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)

init_func_i2c,//SMDKV210未定义I2C,函数为空lib_arm\board.c

endif

dram_init,//初始化gd->bd->bi_dram,开发板的SDRAM配置信息

board\samsung\smdkc110\smdkc110.c

display_dram_config,//串口打印出DRAM的大小信息,DRAM:xxxMB

lib_arm\board.c

NULL,

board_init函数:

dm9000_pre_init();//网卡初始化,GPIO和端口设置

gd->bd->bi_arch_number = MACH_TYPE;//开发板的机器码,uboot的机器码和linux的机器码之间必须适配

gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);//uboot给内核的传参地址

display_banner函数:

打印uboot版本信息:uboot-1.3.4

print_cpuinfo函数:

打印CPU时钟系统的时钟信息

checkboard函数:

打印出开发板信息Board: SMDKV210

display_dram_config函数:

打印出DRAM的大小信息,DRAM:xxxMB

打印出的信息可以作为调试使用,依次遍历调用函数指针数组init_sequence中的函数,如果有函数执行出错,则执行hang函数,打印出”### ERROR ### Please RESET the board ###”,进入死循环。

Original: https://www.cnblogs.com/cyyljw/p/10998066.html
Author: 怀想天空2013
Title: 嵌入式linux开发uboot启动过程源码分析(一)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/10220/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • jenkins自动构建项目源码git pull时出现冲突的终极解决方案(git远程覆盖本地分支)

    jenkins线上构建项目时,有时候到拉取远程代码步骤时出现冲突比较烦人,也不方便登陆服务器中去解决冲突,这时候需要的解决方案是使用远程代码完全覆盖掉本地代码。做法如下: git …

    Linux 2022年8月30日
    0280
  • linux下postgres的安装

    在浏览器中访问https://www.enterprisedb.com/download-postgresql-binaries 然后选择适合自己的版本,我选择的是linux64位…

    Linux 2022年8月26日
    0390
  • 在自己的项目中使用PCL

    在自己的项目中使用PCL项目设置:1、创建cpp文件,如pcd_write.cpp,文件内容如下例: #include find_package(PCL 1.3 REQUIRED …

    Linux 2022年9月10日
    0190
  • 【Redis】单线程理解及可能影响性能的操作

    一. Redis的单线程 二. 可能影响单线程性能的操作 1. bigKey操作 2. 复杂命令 3. 大量key集中过期 4. 淘汰策略 5. 主从全量同步生成RDB 6. AO…

    Linux 2022年9月14日
    0180
  • Linux:用户&用户组操作

    用户:查看用户:whoami、who am i、who mom likes创建用户:adduser dgw切换登录用户:su -l dgw退出当前用户: exit删除用户:delu…

    Linux 2022年8月24日
    0240
  • 应用 Valgrind 发现 Linux 程序的内存问题

    Valgrind 概述 Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类…

    Linux 2022年8月24日
    0240
  • 采用QSharedMemory实现多进程间的通信 Linux | window

    直接上代码:简单的很 1 ///////////////////////////////////////////////////////////////////// 2 /// f…

    Linux 2022年8月26日
    0390
  • Shell 函数带中横线问题排查

    Shell 中编写的函数,如果函数名中带了中横线,在使用 /bin/sh 执行时会报错。 ➜ subprocess git:(master) ✗ cat kubectl.sh _k…

    Linux 2022年9月14日
    0210
  • Linux:fuser命令详情

    fuser命令详情 fuser:由文件找出占用该文件的程序 # 示例: fuser -v /root/log/a.log #显示进程信息 fuser -v 80/tcp #查看那些…

    Linux 2022年8月24日
    0290
  • Redis 配置文件

    http://blog.csdn.net/tonysz126/article/details/8280696/ 2.1 Redis配置文件 为了对Redis的系统实现有一个直接的认…

    Linux 2022年9月14日
    0200
  • 安装Redis

    1、下载redis 2、解压缩、安装 安装完之后,可以执行以下make test,执行make test之前需要先安装tcl 3、配置 redis服务后台启动 找到: 修改为: 4…

    Linux 2022年9月14日
    0290
  • Linux中添加新硬盘后对硬盘的分区以及挂载

    转自:https://www.linuxidc.com/Linux/2018-06/152958.htm 我将使用VM来进行模拟 先使用df看下我的电脑硬盘信息: df -h 可以…

    2022年8月20日
    0410
  • redis的另一个分支 keydb

    今天无意间发现了redis还有一个分支keydb 是多线程的,貌似在机器内核多的情况下效果比redis效果好 执行这些命令 在这之前先把redis关掉 sudo systemctl…

    Linux 2022年9月14日
    0110
  • linux 编译运行c文件

    在ubuntu安装gcc 编辑 test.c /* Not stdio.h */ #include <unistd.h> void main() { char str[…

    Linux 2022年8月20日
    0300
  • .gitignore文件的使用方法(学习总结版)—— .gitignore 文件的配合用法

    本文紧接前文: ============================================= 本文主要讨论前文中所说的一个操作,即:.gitignore 文件一般在创…

    Linux 2022年8月30日
    0340
  • Linux-031-常用端口相关命令

    查看监听的端口 netstat -lnpt 检查端口被哪个进程占用 netstat -lnpt |grep 3306 查看进程的详细信息 ps 6832 查看防火墙所有开放的端口 …

    Linux 2022年8月26日
    0430
  • linux 命令之top

    top命令是显示当前系统正在执行的进程相关信息。包含进程ID、内存占用率等;top命令格式例如以下: top [OPTIONS] OPTIONS&#xFF1A; -<…

    Linux 2022年8月11日
    0630
  • linux下pppoe连接管理

    一、安装pppoe组件 二、配置pppoe 图形界面配置pppoe,在terminal里输入 然后就会弹出图形界面让你配置网络,只要新建一个DSL连接即可。填好用户名密码,服务不用…

    Linux 2022年8月20日
    0520
  • linux:永久打开core文件功能

    在Linux下程序不寻常退出时,内核会在当前工作目录下生成一个core文件(是一个内存映像,同时加上调试信息)。使用gdb来查看core文件,可以指示出导致程序出错的代码所在文件和…

    2022年8月11日
    0420
  • linux 常用命令

    Linux返回上一级目录的命令 cd .. 返回上一级目录 cd ../.. 返回上两级目录 cd或cd ~ 返回home目录 cd – 目录名 返回指定目录 Linu…

    Linux 2022年8月24日
    0260
  • firewalld dbus接口使用指南

    Overview firewalld,一个基于动态区的iptables/nftables守护程序,自2009年左右开始开发,CentOS7基于 firewalld-0.6.3 , …

    Linux 2022年9月10日
    0170
  • Linux中断子系统(三)-softirq和tasklet 【转】

    转自:https://www.cnblogs.com/LoyenWang/p/13124803.html#:~:text=Linux%E5%86%85%E6%A0%B8%E4%BC…

    Linux 2022年8月24日
    0330
  • 十四、linux内核裁剪

    一、下载解压linux内核源码 我这里下载的是5.4.191版本(仅做实验),为了和我ubuntu虚拟机的交叉编译环境匹配。 二、清理临时文件 三、配置内核 得到系统源文件下的co…

    Linux 2022年8月24日
    0340
  • 如何在linux中运行sql文件

    1.在linux中进入sql命令行 mysql -u root -p 输入密码 2.假设home下面有a.sql文件 先得use databasename,要不会报错 &#8220…

    Linux 2022年8月13日
    0400
  • 在Linux下的文件IO操作

    系统调用 系统调用: 操作系统提供给用户程序调用的一组”特殊”接口,用户程序可以通过这组”特殊”接口来获得操作系统内核提供的服务 为…

    Linux 2022年9月10日
    01780
  • 【Git】git pull和git pull –rebase的使用

    git pull = git fetch + git merge git pull –rebase = git fetch + git rebase 现在来看看git …

    Linux 2022年8月30日
    0390

发表回复

登录后才能评论
免费咨询
免费咨询
扫码关注
扫码关注
联系站长

站长Johngo!

大数据和算法重度研究者!

持续产出大数据、算法、LeetCode干货,以及业界好资源!

2022012703491714

微信来撩,免费咨询:xiaozhu_tec

分享本页
返回顶部