总结uboot的重要概念,不知道的看过来

        本篇内容不讲解下uboot的源码,只总结面试中uboot被高频问到的重要知识点。嵌入式新人看了可以对uboot有个深刻了解应付面试没问题,老手看了可以复习查漏补缺。

1、PC机的启动过程

PC上电后先执行BIOS程序(实际上PC的BIOS就是NorFlash),BIOS程序负责初始化DDR内存,负责初始化硬盘,然后从硬盘上将OS镜像读取到DDR中,然后跳转到DDR中去执行OS直到启动(OS启动后BIOS就无用了)

2、嵌入式linux系统启动过程

 嵌入式系统上电后先执行uboot、然后uboot负责初始化DDR,初始化Flash,然后将OS从Flash中读取到DDR中,然后启动OS(OS启动后uboot就无用了)

3、uboot是什么,uboot的作用

uboot 属于bootloader的一种,uboot是个庞大的单线程裸机程序,有两个作用一个是初始化硬件内存,flash等,第二个是引导内核,从flash中读出内核,放到内存中,Uboot去启动内核

总结:嵌入式linux系统和PC机的启动过程几乎没有两样,只是BIOS成了uboot,硬盘成了Flash。

总结:uboot到底是干嘛的?

(1)uboot主要作用是用来启动操作系统内核。

(2)uboot还要负责部署整个计算机系统。

(3)uboot中还要初始化一些外设比如ddr, Flash, lcd,触摸屏等。

(4)uboot还得提供一个命令行界面供人来操作。

4、uboot的启动阶段(不同平台不一样)

我所接触过三个平台

MTK: boot rom -> preloader -> lk (可以理解是uboot) -> kernel

RK: bootrom -> spl(miniloader) -> uboot->trust (optee) -> kernel

NXP: bootrom -> bl2 -> ATF ->uboot->kernel

5、Uboot支持多种启动方式

项目中知道的有 SPI Flash/eMMC/SD/Hard Disk/U-Disk/net

启动方式的不同存放不同阶段的固件也就不同,以RK平台为例

a ) SPI Flash启动的话,(SPL and U-Boot and trust only) in SPI flash, 其他阶段像kernel 或者 rootfs阶段的固件就只能在其他地方存放了

b ) eMMC启动的话,所有启动阶段的固件都可以存放在eMMC里

c ) SD卡启动的话,所有启动阶段的固件都可以存放在SD卡里

d )Hard Disk启动的话,除了SPL and U-Boot and trust 不能存放在Hard Disk里,其他阶段kernel,rootfs的固件是都可以存放在Hard Disk

e ) U-Disk启动的话,除了SPL and U-Boot and trust 不能存放在U-Disk里,其他阶段kernel,rootfs的固件是都可以存放在U-Disk

f ) net/tftp 除了SPL and U-Boot and trust 不能通过网络进行下载,其他阶段kernel,rootfs的固件是都可以通过net/tftp通过网络进行下载

6、掌握uboot使用的2个关键点:命令和环境变量

(1)uboot启动后大部分时间和工作都是在shell下完成的(譬如uboot要部署系统要在shell下输命令、要设置环境变量也得在命令行下,要启动内核也要在命令行底下敲命令)。

(2)命令就是uboot的shell中可以识别的各种命令。uboot中有几十个命令,其中有一些常用另一些不常用(我们还可以自己给uboot添加命令)。

(3)uboot的环境变量和操作系统的环境变量工作原理和方式几乎完全相同。uboot在设计时借助了操作系统的设计理念(命令行工作方式借鉴了linux终端命令行,环境变量借鉴了操作系统的环境变量,uboot的驱动管理几乎完全照抄了linux的驱动框架)。

环境变量值得注意的有两个:bootcmd和bootargs。

bootcmd:自动启动时执行的命令

uboot上电启动后会自动倒数bootdelay秒,如果没有人按下回车打断启动,则uboot会自动执行bootcmd启动命令来启动内核。

例如:bootcmd=boot_logo;nand read 1000000 3c0000 300000;bootm 1000000

意思是启动u-boot后,执行boot_logo显示logo信息,然后从nand flash中读内核映像到内存,然后启动内核。

bootargs:传递给内核的启动参数

这个参数也比较重要,如果没有设置对,内核很有可能启动不起来,报Not init found之类的日志。还有之前说的Uboot支持多种启动方式也是通过这个bootargs来区分到底是什么方式启动内核的。

下面介绍一下bootargs常用参数,bootargs的种类非常的多,而且随着kernel的发展会出现一些新的参数,使得设置会更加灵活多样。

A. root

用来指定rootfs的位置, 常见的情况有:

root=/dev/ram rw
root=/dev/ram0 rw

请注意上面的这两种设置情况是通用的。

root=/dev/mtdx rw
root=/dev/mtdblockx rw
root=/dev/mtdblock/x rw

上面的这几个在一定情况下是通用的,当然这要看你当前的系统是否支持,不过mtd是字符设备,而mtdblock是块设备,有时候你的挨个的试到底当前的系统支持上面那种情况下,不过root=/dev/mtdblockx rw比较通用。

root=/dev/nfs

在文件系统为基于nfs的文件系统的时候使用。当然指定root=/dev/nfs之后,还需要指定nfsroot=serverip:nfs_dir,即指明文件系统存在那个主机的那个目录下面。

B console

console=tty使用虚拟串口终端设备.

console=ttyS[,options]使用特定的串口,options可以是这样的形式bbbbpnx,这里bbbb是指串口的波特率,p是奇偶位(从来没有看过使用过),n是指的bits。

console=ttySAC[,options]同上面。

console=ttyS0,115200 

上面是使用特定的串口

C. mem

mem=xxM

指定内核使用内存的大小,不是必须的。

比如物理内存是1024M, mem=1000M,那么最后保留的24M不被内核使用。这种方法适用于没有DTS的Linux版本,kernle的参数通过cmdline传递过去。

对于有DTS的Linux版本,kernel的参数是通过设备树传入的,因此需要修改设备树的reserved-memory来保留预留内存,所以目前这个方法用的比较多

D. ramdisk_size

ramdisk=xxxxx不推荐
ramdisk_size=xxxxx推荐

上面这两个都可以告诉ramdisk驱动,创建的ramdisk的size,默认情况下是4m(s390默认8M),你可以查看Documentation/ramdisk.txt找到相关的描述,不过ramdisk=xxxxx在新版的内核都已经没有提了,不推荐使用。

E. initrd, noinitrd

当你没有使用ramdisk启动系统的时候,你需要使用noinitrd这个参数,但是如果使用了的话,就需要指定initrd=r_addr,size, r_addr表示initrd在内存中的位置,size表示initrd的大小。

用bootz和booti (kernel_addr_r)(initrd) (fdt_addr_r)命令启动内核时,就要指定initrd内存地址,不然一般用-表示不用initrd内存地址

F. init

init指定的是内核启起来后,进入系统中运行的第一个脚本,一般init=/linuxr。/linuxrc指的是/目录下面的linuxrc脚本,一般是一个连接罢了

G. mtdparts

mtdparts=fc000000.nor_flash:1920k(linux),128k(fdt),20M(ramdisk),4M(jffs2),38272k(user),256k(env),384k(uboot)

要想这个参数起作用,内核中的mtd驱动必须要支持,即内核配置时需要选上Device Drivers ---> Memory Technology Device (MTD) support ---> Command line partition table parsing

mtdparts的格式如下:

mtdparts=[;
:= :[,]
:= [@offset][][ro]
:= unique id used in mapping driver/device
:= standard linux memsize OR "-" to denote all remaining space
:= (NAME)


因此你在使用的时候需要按照下面的格式来设置:
mtdparts=mtd-id:@(),@()
这里面有几个必须要注意的:
a. mtd-id 必须要跟你当前平台的flash的mtd-id一致,不然整个mtdparts会失效
b. size在设置的时候可以为实际的size(xxM,xxk,xx),也可以为'-'这表示剩余的所有空间。
举例:
假设flash 的mtd-id是sa1100,那么你可以使用下面的方式来设置:
mtdparts=sa1100:- → 只有一个分区
mtdparts=sa1100:256k(ARMboot)ro,-(root) → 有两个分区
可以查看drivers/mtd/cmdlinepart.c中的注释找到相关描述。

说完常见的几种bootargs,那么我们来讨论平常我经常使用的几种组合:
1). 假设文件系统是ramdisk,且直接就在内存中,bootargs的设置应该如下:

setenv bootargs ‘initrd=0x32000000,0xa00000 root=/dev/ram0 console=ttySAC0 mem=64M init=/linuxrc’

2). 假设文件系统是ramdisk,且在flash中,bootargs的设置应该如下:

setenv bootargs ‘mem=32M console=ttyS0,115200 root=/dev/ram rw init=/linuxrc’

注意这种情况下你应该要在bootm命令中指定ramdisk在flash中的地址,如bootm kernel_addr ramdisk_addr (fdt_addr)

3). 假设文件系统是jffs2类型的,且在flash中,bootargs的设置应该如下

setenv bootargs ‘mem=32M console=ttyS0,115200 noinitrd root=/dev/mtdblock2 rw rootfstype=jffs2 init=/linuxrc’

4).假设文件系统是基于nfs的,bootargs的设置应该如下

setenv bootargs ‘noinitrd mem=64M console=ttySAC0 root=/dev/nfs nfsroot=192.168.0.3:/nfs ip=192.168.0.5:192.168.0.3:192.168.0.3:255.255.255.0::eth0:off’
或者
setenv bootargs ‘noinitrd mem=64M console=ttySAC0 root=/dev/nfs nfsroot=192.168.0.3:/nfs ip=192.168.0.5’

下面是指rootfs在EMMC第三分区里启动

setenv bootargs ‘console=ttyS0,115200 earlycon=uart8250,mmio,0x21c0500 root=/dev/mmcblk0p3 rw rootwait‘’

今天是本次分享的uboot干货,如果你也对底层感兴趣,那就点赞关注我

你可能感兴趣的:(linux)