Uboot 详解
来源:互联网 发布:淘宝大尺度买家秀18p 编辑:程序博客网 时间:2024/04/29 09:38
收录 Uboot 详解
(2013-05-04 21:16:12)it
分类:linux我们知道,bootloader是系统上电后最初加载运行的代码。它提供了处理器上电复位后最开始需要执行的初始化代码。
一个嵌入式的存储设备通过通常包括四个分区:
第一分区:存放的当然是u-boot
第二个分区:存放着u-boot要传给系统内核的参数
第三个分区:是系统内核(kernel)
第四个分区:则是根文件系统
如下图所示:
-------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------
u-boot是一种普遍用于嵌入式系统中的Bootloader。
Bootloader介绍
Bootloader是进行嵌入式开发必然会接触的一个概念,它是嵌入式学院<<ahref="http://www.embedu.org/courses/course1.htm"target="_blank">嵌入式工程师职业培训班>二期课程中嵌入式linux系统开发方面的重要内容。本篇文章主要讲解Bootloader的基本概念以及内部原理,这部分内容的掌握将对嵌入式linux系统开发的学习非常有帮助!
Bootloader的定义:Bootloader是在操作系统运行之前执行的一小段程序,通过这一小段程序,我们可以初始化硬件设备、建立内存空间的映射表,从而建立适当的系统软硬件环境,为最终调用操作系统内核做好准备。意思就是说如果我们要想让一个操作系统在我们的板子上运转起来,我们就必须首先对我们的板子进行一些基本配置和初始化,然后才可以将操作系统引导进来运行。具体在Bootloader中完成了哪些操作我们会在后面分析到,这里我们先来回忆一下PC的体系结构:PC机中的引导加载程序是由BIOS和位于硬盘MBR中的OSBoot Loader(比如LILO和GRUB等)一起组成的,BIOS在完成硬件检测和资源分配后,将硬盘MBR中的BootLoader读到系统的RAM中,然后将控制权交给OS Boot Loader。BootLoader的主要运行任务就是将内核映象从硬盘上读到RAM中,然后跳转到内核的入口点去运行,即开始启动操作系统。在嵌入式系统中,通常并没有像BIOS那样的固件程序(注:有的嵌入式cpu也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成。比如在一个基于ARM7TDMIcore的嵌入式系统中,系统在上电或复位时通常都从地址0x00000000处开始执行,而在这个地址处安排的通常就是系统的BootLoader程序。(先想一下,通用PC和嵌入式系统为何会在此处存在如此的差异呢?)
Bootloader是基于特定硬件平台来实现的,因此几乎不可能为所有的嵌入式系统建立一个通用的Bootloader,不同的处理器架构都有不同的Bootloader,Bootloader不但依赖于cpu的体系结构,还依赖于嵌入式系统板级设备的配置。对于2块不同的板子而言,即使他们使用的是相同的处理器,要想让运行在一块板子上的Bootloader程序也能运行在另一块板子上,一般也需要修改Bootloader的源程序。
Bootloader的启动方式
Bootloader的启动方式主要有网络启动方式、磁盘启动方式和Flash启动方式。
1、网络启动方式
图1
如图1所示,里面主机和目标板,他们中间通过网络来连接,首先目标板的DHCP/BIOS通过BOOTP服务来为Bootloader分配IP地址,配置网络参数,这样才能支持网络传输功能。我们使用的u-boot可以直接设置网络参数,因此这里就不用使用DHCP的方式动态分配IP了。接下来目标板的Bootloader通过TFTP服务将内核映像下载到目标板上,然后通过网络文件系统来建立主机与目标板之间的文件通信过程,之后的系统更新通常也是使用BootLoader的这种工作模式。工作于这种模式下的Boot Loader通常都会向它的终端用户提供一个简单的命令行接口。
2、磁盘启动方式
这种方式主要是用在台式机和服务器上的,这些计算机都使用BIOS引导,并且使用磁盘作为存储介质,这里面两个重要的用来启动linux的有LILO和GRUB,这里就不再具体说明了。
3、Flash启动方式
这是我们最常用的方式。Flash有NOR Flash和NAND Flash两种。NORFlash可以支持随机访问,所以代码可以直接在Flash上执行,Bootloader一般是存储在Flash芯片上的。另外Flash上还存储着参数、内核映像和文件系统。这种启动方式与网络启动方式之间的不同之处就在于,在网络启动方式中,内核映像和文件系统首先是放在主机上的,然后经过网络传输下载进目标板的,而这种启动方式中内核映像和文件系统则直接是放在Flash中的,这两点在我们u-boot的使用过程中都用到了。
U-boot的定义
U-boot,全称Universal BootLoader,是由DENX小组的开发的遵循GPL条款的开放源码项目,它的主要功能是完成硬件设备初始化、操作系统代码搬运,并提供一个控制台及一个指令集在操作系统运行前操控硬件设备。U-boot之所以这么通用,原因是他具有很多特点:开放源代码、支持多种嵌入式操作系统内核、支持多种处理器系列、较高的稳定性、高度灵活的功能设置、丰富的设备驱动源码以及较为丰富的开发调试文档与强大的网络技术支持。另外u-boot对操作系统和产品研发提供了灵活丰富的支持,主要表现在:可以引导压缩或非压缩系统内核,可以灵活设置/传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,支持多种文件系统,支持多种目标板环境参数存储介质,采用CRC32校验,可校验内核及镜像文件是否完好,提供多种控制台接口,使用户可以在不需要ICE的情况下通过串口/以太网/USB等接口下载数据并烧录到存储设备中去(这个功能在实际的产品中是很实用的,尤其是在软件现场升级的时候),以及提供丰富的设备驱动等。
u-boot源代码的目录结构
1、board中存放于开发板相关的配置文件,每一个开发板都以子文件夹的形式出现。
2、Commom文件夹实现u-boot行下支持的命令,每一个命令对应一个文件。
3、cpu中存放特定cpu架构相关的目录,每一款cpu架构都对应了一个子目录。
4、Doc是文档目录,有u-boot非常完善的文档。
5、Drivers中是u-boot支持的各种设备的驱动程序。
6、Fs是支持的文件系统,其中最常用的是JFFS2文件系统。
7、Include文件夹是u-boot使用的头文件,还有各种硬件平台支持的汇编文件,系统配置文件和文件系统支持的文件。
8、Net是与网络协议相关的代码,bootp协议、TFTP协议、NFS文件系统得实现。
9、Tooles是生成U-boot的工具。
对u-boot的目录有了一些了解后,分析启动代码的过程就方便多了,其中比较重要的目录就是/board、/cpu、/drivers和/include目录,如果想实现u-boot在一个平台上的移植,就要对这些目录进行深入的分析。
-------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------
(一)编译地址:
(二)运行地址:是指程序指令真正运行的地址,是由用户指定的,用户将运行地址烧录到哪里,哪里就是运行的地址。
这段话表示,用户告诉编译器编译地址的起始地址
-------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------
(二)下载(Downloading)模式:在这种模式下,目标机上的 Boot Loader将通过串口连接或网络连接等通信手段从主机(Host)下载文件,比如:下载内核映像和根文件系统映像等。从主机下载的文件通常首先被Boot Loader保存到目标机的RAM 中,然后再被 BootLoader写到目标机上的FLASH类固态存储设备中。Boot Loader的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新也会使用 Boot Loader的这种工作模式。工作于这种模式下的 Boot Loader通常都会向它的终端用户提供一个简单的命令行接口。这种工作模式通常在第一次安装内核与跟文件系统时使用。或者在系统更新时使用。进行嵌入式系统调试时一般也让bootloader工作在这一模式下。
-------------------------------------------------------------------------------------------------------------------------------------------
第一、大概总结性得的分析
因为我们用的是ARM7TDMI的cpu架构,在复位后从地址0x00000000取它的第一条指令,所以我们将Flash映射到这个地址上,
这样在系统加电后,cpu将首先执行u-boot程序。u-boot的启动过程是多阶段实现的,分了两个阶段。
依赖于cpu体系结构的代码(如设备初始化代码等)通常都放在stage1中,而且通常都是用汇编语言来实现,以达到短小精悍的目的。
而stage2则通常是用C语言来实现的,这样可以实现复杂的功能,而且代码具有更好的可读性和可移植性。
下面我们先详细分析下stage1中的代码,如图2所示:
图2
基本内存分布图:
图3
本篇文章将分析重点放在了前面的start.s上,是因为这部分无论在移植还是在调试过程中都是最容易出问题的地方,要解决问题就需要程序员对代码进行修改,所以在这里简单介绍了一下start.s的基本流程,希望能对大家有所帮助
-------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------
第二、代码分析
2.2 阶段 1 介绍
uboot 的 stage1 代码通常放在 start.s 文件中,它用汇编语言写成,其主要代码部分如下:
2.2.1 定义入口
由于一个可执行的 Image 必须有一个入口点,并且只能有一个全局入口,通常这个入口放在 ROM(Flash)的 0x0
地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成。
1. board/crane2410/uboot.lds:
2. uboot 代码区(TEXT_BASE = 0x33F80000)定义在board/crane2410/config.mk
U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下:
Ø
Ø
Ø
Ø
Ø
Ø
Ø
Ø
Ø
详细分析
图 2.1 U-Boot启动第一阶段流程
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm","elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000; //起始地址
. = ALIGN(4); //4字节对齐
.text : //test指代码段,上面3行标识是不占用任何空间的
{
cpu/arm920t/start.o (.text) //这里把start.o放在第一位就表示把start.s编
译时放到最开始,这就是为什么把uboot烧到起始地址上它肯定运行的是start.s
*(.text)
}
. = ALIGN(4); //前面的 “.” 代表当前值,是计算一个当前的值,是计算上
面占用的整个空间,再加一个单元就表示它现在的位置
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .; //bss表示归零段
.bss : { *(.bss) }
_end = .;
}
1.
(1)设置异常向量
globl_startglobal
_start:
_undefined_instruction:
_software_interrupt:
_prefetch_abort:
_data_abort:
_not_used:
_irq:
_fiq:
word伪操作用于分配一段字内存单元(分配的单元都是字对齐的),并用伪操作中的expr初始化
.
是由.word undefined_instruction来指定的,pc就代表你运行代码的地址,她就实现了CPU要做一次跳转时的工作。
表 2.1ARM异常向量表
描述
0x00000000
复位
0x00000004
未定义指令
0x00000008
软件中断
0x0000000c
预存指令
0x00000010
数据操作
0x00000014
未使用
0x00000018
IRQ
0x0000001c
FIQ
(2)CPU进入SVC模式
start_code:
(3)设置控制寄存器地址
# ifdefined(CONFIG_S3C2400)
#
#
#
#else
#
#
#
#
# endif
(4)关闭看门狗
为什么要关看门狗?
关狗---详细的原因:
(5)屏蔽中断
# if defined(CONFIG_S3C2440)
屏蔽所有中断,为什么要关中断?
中断处理中ldrpc是将代码的编译地址放在了指针上,而这段时间还没有搬移代码,所以编译地址上面没有这个代码,如果进行跳转就会跳转到空指针上面
(6)设置MPLLCON,UPLLCON, CLKDIVN
# if defined(CONFIG_S3C2440)
#define MPLLCON
#define UPLLCON
# else
#endif
表 2.2 S3C2440的CLKDIVN寄存器格式
HDIVN
[2:1]
00
PDIVN
[0]
0: PCLK =HCLK/1
0
表 2.3 推荐PLL值
12.0000MHz
48.00 MHz
56(0x38)
2
2
12.0000MHz
405.00 MHz
127(0x7f)
2
1
默认频率为
设置时钟分频,为什么要设置时钟?
起始可以不设,系统能不能跑起来和频率没有任何关系,频率的设置是要让外围的设备能承受所设置的频率,如果频率过高则会导致cpu操作外围设备失败
说白了:设置频率,就为了CPU能去操作外围设备
(7)关闭MMU,cache
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
#endif
320
321
322
324
325
326
327
328
329
332
333
334
335
336
337
338
339
344
345
346
347
348
349
350
表 2.3CP15的c1寄存器格式(部分)
.
.
V
I
.
.
R
S
B
.
.
.
.
C
A
M
V :
I :
R、S : 用来与页表中的描述符一起确定内存的访问权限
B :
C :
A :
M :
为什么要关闭catch和MMU呢?catch和MMU是做什么用的?
概述:
一,关catch
二:关MMU
详细分析---
(8)初始化RAM控制寄存器
45
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
问题二:Nor Flash和NandFlash本质区别:
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(9)复制U-Boot第二阶段代码到RAM
nand_boot:
stack_setup:
int bBootFrmNORFlash(void)
{
}
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
nand_boot:
str r2, [r1, #oNFSTAT]
tst
beq stack_setup
bad_nand_read:
loop2: bloop2
.align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
#define NAND_CTL_BASE
#define STACK_BASE
#define STACK_SIZE
#define oNFCONF
#define oNFCONT
#define oNFADDR
#define oNFDATA
#define oNFCMD
#define oNFSTAT
#define oNFECC
表 2.4 NFCONF的Bit3、Bit2与NAND Flash的关系
0
256 B/page
512 B/page
1
1024 B/page
2048 B/page
int nand_read_ll(unsigned char *buf, unsigned long start_addr,int size)
{
//根据NFCONF寄存器的Bit3来区分2种NAND Flash
}
(10)设置堆栈
stack_setup:
#ifdef CONFIG_USE_IRQ
#endif
图2.2 U-Boot内存使用情况
(11)清除BSS段
clear_bss:
clbss_l:str
(12)跳转到第二阶段代码入口
_start_armboot:
图 2.3 U-Boot第二阶段执行流程
(1)gd_t结构体
typedef
} gd_t;
#defineDECLARE_GLOBAL_DATA_PTR
gd = TEXT_BASE -CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)
(2)bd_t结构体
typedef struct bd_info {
} bd_t;
这两个类型变量记录了刚启动时的信息,并将要记录作为引导内核和文件系统的参数,如bootargs等等,并且将来还会在启动内核时,由uboot交由kernel时会有所用。
(3)init_sequence数组
typedef int (init_fnc_t) (void);
init_fnc_t *init_sequence[] = {
};
gd->bd->bi_arch_number = MACH_TYPE_MINI2440;
gd->bd->bi_boot_params =0x30000100;
int dram_init (void)
{
}
mini2440使用2片32MB的SDRAM组成了64MB的内存,接在存储控制器的BANK6,地址空间是0x30000000~0x34000000。
在include/configs/mini2440.h中PHYS_SDRAM_1和PHYS_SDRAM_1_SIZE分别被定义为0x30000000和0x04000000(64M)。
void start_armboot (void)
{
#ifndef CONFIG_SYS_NO_FLASH
#endif
#if defined(CONFIG_CMD_NAND)
#endif
#ifdef CONFIG_USB_DEVICE
#endif
#if defined(CONFIG_CMD_NET)
#endif
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
#endif
… …
#endif
}
struct tag_header {
};
struct tag {
};
59
60
61
62
63
64
65
66
67
68
73
86
87
88
89
90
91
92
93
100
101
102
103
104
105
113
114
115
116
117
126
127
128
129
130
131
132
static void setup_start_tag (bd_t *bd)
{
}
static void setup_memory_tags (bd_t *bd)
{
}
static void setup_end_tag (bd_t *bd)
{
}
(1)
Ø
Ø
Ø
(2)
Ø
Ø
(3)
int cleanup_before_linux (void)
{
}
do_bootm_linux中:
64
… …
73
… …
128
(1)
(2)
_BOOT_CMD(
);
U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)
name:命令名,非字符串,但在U_BOOT_CMD中用“#”符号转化为字符串
maxargs:命令的最大参数个数
rep:是否自动重复(按Enter键是否会重复执行)
cmd:该命令对应的响应函数
usage:简短的使用说明(字符串)
help:较详细的使用说明(字符串)
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs,rep, cmd, usage, help}
struct cmd_tbl_s {
#ifdef
#endif
#ifdef CONFIG_AUTO_COMPLETE
#endif
};
typedef struct cmd_tbl_s
#define Struct_Section
cmd_tbl_t __u_boot_cmd_menu __attribute__ ((unused,section(".u_boot_cmd"))) = {menu, 3, 0, do_menu, "menu - display a menu,to select the items to do something\n", " - display a menu, toselect the items to do something"}
(3)
int do_menu (cmd_tbl_t *cmdtp, int flag, int argc, char*argv[])
{
}
(4)
COBJS-$(CONFIG_BOOT_MENU) += cmd_menu.o
#define CONFIG_BOOT_MENU 1
(5)menu命令执行的过程
- Uboot详解~~~
- uboot详解
- Uboot详解~~~
- uboot详解
- uboot详解
- uboot详解
- uboot详解
- uboot详解
- Uboot 详解
- uboot 详解
- Uboot 详解
- Uboot详解
- uboot.lds解读------详解
- 【UBOOT】:mkimage使用详解
- uboot makefile详解
- UBoot移植详解
- uboot copy_from_nand代码详解
- uboot copy_from_nand代码详解
- 关于java判断sql是否执行成功
- 显示父div范围
- Bootstrap3学习笔记---2
- Nickname
- 获取手机所有安装的app(转的)
- Uboot 详解
- linux脚本编程---vim的使用(1)
- com.atomikos.icatch.SysException: Error in init(): Log already in use
- hdu 4687 带花树求一般图最大匹配(模板)
- 二分图匹配(KM算法)n^4
- 数字签名与数字证书
- 关于cocos2dx坐标及几个重要函数说明
- <java基础>零起点学Android(五)之应用窗口
- java多线程学习资料网站集合