一、概述
linux启动的过程概述




1:什么是uboot,uboot的作用
uboot以tag结构体的方式给内核传参,cmdline,bootargs,以及uboot如何启动内核
uboot(universal boot)是通用的启动代码,支持多种架构的CPU,并且是开源的。uboot是高度定制的,大致分为Soc级资源管理和板级资源管理。不同的CPU或者同款CPU不同的开发板,uboot都是不同的,要根据硬件电路进行移植。
bootLoader是嵌入式设备中用来引导内核启动的一段代码。内核启动是需要一定条件的,当设备上电后会首先运行BootLoader,BootLoader会初始化必要的硬件,比如DDR、Flash、串口等,相关初始化完成后就会去启动内核。我的理解,BootLoader是一个概念并不是具体的代码,只要满足在设备启动初期启动内核功能的代码都可以叫做BootLoader。
uboot和bootLoader的关系
uboot是BootLoader的一个具象化的表现,uboot是BootLoader,但BootLoader不仅仅指uboot,比如bios也是属于BootLoader。但是在我们日常的嵌入式开发中,经常把BootLoader和uboot混在一起,很多时候BootLoader和uboot都指的是uboot,因为在嵌入式开发中,使用的BootLoader基本都是uboot。
cmdline是uboot引导内核启动时传递给内核的,作用是指导内核启动。内核启动阶段会去解析cmdline,并根据cmdline去指导内核启动。
用来引导启动操作系统内核,pc机从bios(norflash)的程序开始执行,负责初始化DDR内存,初始化硬盘,从硬盘加载操作系统镜像到内存执行,跳转到内存启动内核,启动系统。
1)负责部署整个计算机系统
2)uboot操作驱动
3)uboot提供一个命令行界面供人操作
setenv bootdelay 1
setenv baudrate 115200
setenv use_mdio 0
setenv ethaddr 00:00:23:34:45:66
setenv bootfile "uImage"
setenv filesize BD0000
setenv fileaddr 82000000
setenv gatewayip 192.168.0.1
setenv netmask 255.255.255.0
setenv ipaddr 192.168.0.142
setenv serverip 192.168.0.47
setenv bootargs 'mem=128M console=ttyAMA0,115200 root=/dev/mtdblock2 rootfstype=jffs2 rw mtdparts=hi_sfc:1M(boot),4M(kernel),24M(rootfs)'
setenv bootcmd 'sf probe 0;sf read 0x82000000 0x100000 0x400000;bootm 0x82000000'
saveenv
2:uboot解决的问题
1)自身可以开机直接启动,就要根据具体的soc启动设计来设计uboot(如flash启动。sd卡启动,nandflash启动)
uboot的启动第一阶段star.s文件中具体更改了根据不同硬件的代码
2)能够引导操作系统内核启动并给内核传参,内核启动后就动特定位置取传递的参数,根据参数用来指导内核启动过程
3)uboot能被借助完成整个系统在flash上的烧录下载工作,使用uboot将镜像烧录到inand中
4)uboot能够驱动串口提供界面操作。供启动过程,初始化部分硬件,
5)uboot的生命周期,uboot用来烧录镜像时,完成后回到uboot的命令行继续执行uboot的命令。而启动内核后就结束,把cpu
交还给内核
3:uboot的工作方式
1)uboot本身是一个裸机程序,和xx.bin一样,只是大于16kb。uboot有很多文件组成,配置编译之后
生成uboot.bin,这个镜像文件就可以启动内核了,uboot运行时,从flash被加载到内存中执行,cpu执行代码
2)uboot启动后在shell下完成输入命令,部署系统,
3)在运行时通过读取环境变量指导程序的运行
4:uboot常用命令
1)SD卡/inand操作指令 movi
help movi指令可以查看到movi的所有使用方法,如moi read 和movi write。
movi read {
uboot | kernel} {
addr}
如movi read u-boot 0x30000000
把inand中的uboot分区读到DDR的起始地址0x3000000处,uboot中命令行都是当做十六进制,
2)nandflash操作指令 nand
3)内存操作指令 mm ,mv, md
DDR下载时,注意ddr是没有分区的,防止越界,uboot不像操作系统,可以管理内存,所以下载时为了防止覆盖数据,将下载到
0x23E00000
如:md(memery display)命令,help md 查看具体用法。
mm(memeroy modify)
mw(memeory write)
4)启动内核指令 bootm
bootm命令启动时给内核传参
5)环境变量
环境变量有2分,一份是在flash中,当启动uboot时,复制到DDR中,然后执行时,是用ddr中的环境变量,当修改后
saveenv后,保存更新到flash中,然后reset重启,set delay 后面什么都不加,可以删除delay这个变量
网络设置:ipaddr(开发板本地ip地址), serverip(是开发板通过tftp命令去tftp服务器下载的地址)
gatewayip(是开发板本地的网关地址)
netmask()
ethaddr是开发板的本地网卡MAC地址
2)自动运行命令 bootcmd(会自动执行后面的命令)
uboot启动后开机自动倒数,没有打断则自动进入启动内核,
print命令后,可以看到bootcmd= ‘movi read kernal 0x30000000; bootm 30008000’ 读内核到DDR内存0x30000000处,然后从这里开始
启动内核,
如果修改为set bootcmd = printenv。saveenv后 ,则会倒数后打印环境变量。
3)bootargs 命令 给内核传参
在uboot的变量设置中将bootargs设置好,bootm时可以传入事先设置好的参数
bootargs=console=ttySAC2,115200(控制台2,波特率为115200)
root=/dev/mmcblk0p2 rw 根文件系统在SD卡端口0,设备(inand)第2分区,是可读可写的
init=/linuxrc linux的进程1(init进程)的路劲
rootfstype=ext3 根文件系统的类型是ext3
6:新建,更改,删除一个环境变量
1)新建,set var value
2) 更改 set var varlue
3) 删除 set var
(修改后需保存)
7:uboot对flash和ddr的管理
1)对flash的分区:
各分区要设置好在系统移植前
uboot在flash开头,然后是kernel或rootfs,环境变量,然后是自由分区,在下载时,要注意地址,防止溢出到其他区,
也要注意太大浪费空间,根据文件的大小,设置好地址,
uboot(一般小于512个字节) kernel(一般为3mb)
2)对DDR的分区:
uboot阶段的DDR的分区,ddr掉电是消失的,因此ddr分区部署是每次系统运行时进行的,
内存的分区主要是在内核启动起来之前,启动后内核的内存管理模块会接管内存空间
注意内存分区管理是设置具体模块干什么的,以防相互践踏导致出错
uboot源码文件解读:
api: 与硬件无关的功能函数的API,
board:里面的文件代表可移植的每一种开发板。所以可移植性好,支持多种开发板,在进行配置阶段,
会确定使用具体board下的哪个文件,现在在board下放置芯片生产厂商的信息。然后后里面放置不同开发板的文件,
所以x210的开发板放置了在board下board/samsung/x210,在配置阶段要注意是否在这个目录下,不然编译失败
注意:uboot的配置阶段,就是根目录下的mkconfig脚本和Makefile的配置部分,确定具体文件的路径,然后编译时找到相应的文件,如果
board下不同配置则会移植失败
conmon: 普通的通用的文件,这个文件下放的一些与具体硬件无关的普通代码。譬如控制台实现。cr校验。cmd开头的,实现uboot的命令系统的 。一类是emv开头的。实现环境变量的
cpu: 这个目录是与soc相关的,代码放的是与soc相关的初始化和控制代码,如中断,串口,包括起始代码start.s,里面的文件移植在同一芯片时几乎不要修改,里面放了很多不同处理器的与硬件相关的代码,
disk: 磁盘与关
doc: 文档目录,关于uboot的文档
drivers: 驱动,从linux内核抠出来的设备驱动
fs: filesystem,从linux中移植来的文件系统,管理flash等资源
include: 头文件,把所有的头文件全部集中存放在include目录下
lib开头的文件: )(lib.arm 与arm架构的库文件和lib.generic通用库文件)
libfdt: 设备树,进行启动传参,进行硬件相关信息描述
net: 网络相关代码,如uboot中的tftp,nfs,ping
onenand:
sd_fusing: 烧录uboot镜像到sd卡的代码
tool : 里面是一些工具类代码
uboot启动内核的大致步骤
(1)首先uboot要通过读取SD卡/flash等外存 或者 通过tftp、nfs等网络下载方式,将内核加载到内存的链接地址处;
(2)解析加载的内核的头信息,区分出当前启动方式是zImage、uImage还是设备树方式,构建出描述该内核的image_header_t结构体;
(3)将上一步得到的image_header_t结构体和内核所在地址传入do_bootm_linux函数,启动Linux内核;
(4)boot->bootm->do_bootm_linux->内核启动,uboot结束
内核有三种状态,加载内核镜像到DDR的链接地址处:
(1)静态:没有启动内核时,内核以镜像的方式存放在外存中;
(2)动态:内核在DDR中运行时;
(3)静态->动态:把内核从外存加载到DDR,并启动内核;
内核的重定位
内核从外存加载到DDR的过程就叫做重定位,必须加载DDR的特定地址(链接地址),因为启动的时候就是去链接地址启动。内核的重定位是uboot完成的,根据启动方式的不同,uboot可能从flash等外存去读取内核,也可能通过tftp、nfs等网络下载方式读取内核,但不管何种读取内核的方式,最终内核都是被加载到链接地址。链接地址在编译脚本、环境变量bootcmd、配置文件的CONFIG_BOOTCOMMAND宏定义可以查到。
第一阶段的初始化工作
1):构建异常向量表
2);设置cpu为svc模式
3):关看门狗
4):开发板供电锁存
5):时钟初始化
6):DDR初始化
7);串口2初始化并输出过程打印ok
8):重定位
9):建立映射表并开启MMU
10):跳转到uboot的第二阶段
1:linux内核经过编译后生产一个elf格式的可执行文件,叫vmlinux(78Mb),这个是原始的为处理的elf文件,使用objcopy工具去制作成烧录镜像格式像uboot.bin这种形式的叫做Image,(进行压缩,去掉无用的东西到7.5Mb大小)
原则上是可以吧Image烧录到flash进行启动,但还是太大,进行了压缩且在文件前段加了加压缩代码,得到的文件格式叫zImage
2:uboot为了启动linux内核,还发明了一种内核格式uImage,由zImage在uboot的工具mkimage加工而成uimage,在zimage的
前面加上64字节的头信息,在uboot中有一个宏在x210_sd.h中定义了linux_zimage_magic,也可以支持zimage启动。
在uboot的根目录下的tools下,有一个mkimage.h 和mkimage.c(有空去百度查看(mkimage.c的源代码),编译时会生成mkimage工具。cp mkimage /usr/local/bin
把工具复制到用户目录下,再去kernel/arch/arm/boot下执行make uImage,编译内核,得到uimage,
下载uimage到开发板,bootm 0x3008000启动内核,
3:在编译软件中输入 do_bootm可以查看到这个函数,在uboot/conmon文件下,zimage启动的校验
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
#ifdef CONFIG_ZIMAGE_BOOT //这里定义了一个zimage宏,来选选择编译zimage格式的uboot
#define LINUX_ZIMAGE_MAGIC 0x016f2818 //这个数表明是zimage格式的镜像,判断时,比较与这个宏的值是否相等
/* find out kernel image address */
if (argc < 2) {
//如果bootm命令不带参数,则默认地址启动
addr = load_addr;
debug ("* kernel: default image load address = 0x%08lx\n",
load_addr);
} else {
//如果bootm 0x30008000 ,则从指定的地址启动
addr = simple_strtoul(argv[1], NULL, 16);
debug ("* kernel: cmdline image address = 0x%08lx\n", img_addr);
}
if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {
//从指定的地址往后的37个字节的地址的数与zimage的
标志进行比较,如果相等,则打印是 ziamge格式镜像
printf("Boot with zImage\n");
addr = virt_to_phys(addr);
hdr = (image_header_t *)addr;
hdr->ih_os = IH_OS_LINUX;
一直到397行,after_header_check函数都是在进行镜像的头校验,do_bootm函数的核心是去分辨传进来的image是什么类型,
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address */
uint32_t ih_ep; /* Entry Point Address */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */ //从这里开始的37个字节的4个字节与
zimage宏进行比较
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
4:uimage 启动的校验,如果不是zimage启动,则继续判断是否为uimage启动。227开始到401行也是校验
5:设备树方式内核启动,FIR方式,
总结:
1) #ifdefine CONFIG_ZIMAGE_BOOT zimage启动
2) #if defined(CONFIG_FIT) 设备树启动
3)#if defined (legacy ) uimag 启动
6:校验完之后,找到407行的 do_bootm_linux定义的这个函数。在uboot/lib_arm/bootm.c中文件。
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],bootm_headers_t *images)
{
ulong initrd_start, initrd_end;
ulong ep = 0;
bd_t *bd = gd->bd;
char *s;
int machid

&spm=1001.2101.3001.5002&articleId=142514593&d=1&t=3&u=7362bf88c87b44dbaf2edc68fccccc7e)
906

被折叠的 条评论
为什么被折叠?



