uboot概述
U-Boot,全称 Universal Boot Loader,是遵循GPL条款的从FADSROM、8xxROM、PPCBOOT逐步发展演化而来的 开放源码项目。
U-boot,是一个主要用于嵌入式系统的引导加载程序,可以支持多种不同的计算机系统结构,其主要作用为:==引导系统的启动!==目前,U-Boot不仅支持Linux系统的引导,还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS, android等多种嵌入式操作系统。
uboot主要特性及功能
- 开放:开放的源代码
- 多平台:支持多种嵌入式操作系统,如Linux、NetBSD、android等:
- 生态:有丰富的设备驱动源码,如以太网、SDRAM、LCD等,同时也具有丰富的开发文档。:
uboot下载地址
uboot目录结构
uboot的编译
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean make ARCH=arm CORSS_COMPILE=arm-linux-gnueabihf- colibri-imx6ull_defconfig make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
ARCH=arm:arm架构
CROSS_COMPILE:使用的交叉编译器
如果编译出错,your compile older 6.0,可以参考【1】
colibri-imx6ull_defconfig:指定一个config文件,作为相关版型的配置信息
V=1:这个选项能显示出编译过程中的详细信息,即是verbose编译模式
-j8:多核并行编译,可以提高编译速度,受硬件限制
uboot存放位置
嵌入式系统,一般使用Flash来作为启动设备,Flash上存储着U-boot、环境变量、内核映像、文件系统等。U-boot存放于Flash的起始地址,所在扇区由Soc规定。
uboot的作用
- uboot本质上是一个裸机程序(不是操作系统),一旦uboot开始SoC就会单纯运行uboot(意思就是uboot运行的时候别的程序是不可能同时运行的),一旦uboot结束运行则无法再回到uboot(所以uboot启动了内核后uboot自己本身就死了,要想再次看到 uboot界面只能重启系统。
重启并不是复活了刚才的uboot,重启只是uboot的另一生)- uboot的入口就是开机自动启动,uboot的唯一出口就是启动内核 。
uboot还可以执行很多别的任务(譬如烧录系统),但是其他任务执行完后都可以回到uboot的命令行继续执行uboot命令,而启动内核命令一旦执行就回不来了.
uboot的工作模式
- 启动加载模式
启动加载模式是Bootloader的正常工作模式,嵌入式产品发布时,Bootloader必须工作在这种模式下,Bootloader将 [嵌入式操作系统](file:///h)从FLASH中加载到SDRAM中运行,整个过程是自动的- 下载模式
下载模式就是Bootloader通过某些通信手段将 内核映像或 根文件系统映像等从PC机中下载到 目标板的FLASH中。
用户可以利用Bootloader提供的一些命令接口来完成自己想要的操作。
开发人员可以使用各种命令,通过串口连接或网络连接等通信手段从主机(Host)下载文件(比如内核映像、文件系统映像),将它们直接放在内存运行或是烧入Flash类固态存储设备中。
板子与主机间传输文件时,可以使用串口的xmodem/ymodem/zmodem协议,还可以使用网络通过tftp、nfs协议来传输,以及USB下载等方法。
一般来说,嵌入式开发人员采用下载模式进行开发嵌入式系统。
通常采用交叉网线将PC与目标开发板连接,通过TFTP服务器下载内核,用NFS服务器挂载文件系统。
uboot思路分析
1)设置CPU;
2)关闭Watchdog;
3)设置Clock;
4)屏蔽所有的Interrupt;
5)初始化SDRAM;
6)初始化nandflash(如果u-boot代码在nandflash上);
7)重定位BootLoader的代码到SDRAM;
8)设置栈(C语言函数调用需要先设置栈,栈要在SDRAM初始化之后设置);
9)清BSS段;
10)进入BootLoader的Step-2阶段启动内核;
Step-2:
11)设置BootLoader要传递给内核的参数;
12)从Nor 或 Nand flash 上复制内核代码到SDRAM的0x30007FC0的地方(这个地址不是固定的,可以任意指定,只要不破坏u-boot使用的内存空间,u-boot会根据uImage的Header中的Load Addr判断内核是否装载到Load Addr的地址处,Load Addr在u-boot中默认为0x30007FC0,如果不在,u-boot会移动内核代码到改地址处);
13)跳转到uImage的Header中Entry Point指定的入口地址处开始执行内核代码(内核执行后,u-boot不在执行);
uboot常用命令
printenv/print //查看环境变量 setenv //修改/删除 环境变量 saveenv //保存环境变量 boot //按照环境变量bootcmd启动内核 reset //重新启动 /* nand 命令 */ nand info //查看 nand 信息. nand read addr offset count //读命令. nand erase //擦除整片 NAND. nand erase offset count //擦除指定 NAND 指定的区域. nand write addr offset count//将内存的数据写到 NAND 指定区. //addr 是内存基地址,offset 是 NAND 的偏移地址,count 是写入字节数。 /* emmc 命令 */ mmc info //打印 mmc的信息. mmc list // 查看 mmc 设备. mmc read addr blk#cnt// 从 eMMC 读 cnt 个块数据到内存 addr 处. mmc write addr blk# cnt//把内存 addr 处的 cnt 个块数据写到 eMMC. mmc erase blk# cnt//擦除 blk#开始的 cnt 个数据块. //addr 为内存地址,blk#是 mmc 的块号,cnt是设备块的个数,块的单位是 512 字节。 /* 网络相关命令 */ setenv ethaddr 73:70:F4:E6:36:EA //设置以太网物理地址 setenv ipaddr 192.168.1.27 //设置ARM本地IP地址 setenv ipaddr 192.168.1.14 //设置tftp服务器的 IP 地址 setenv serverip 192.168.1.1 //设置网关 setenv netmask 255.255.255.0 //设置子网掩码 tftp addr file //tftp 下载命令,使用 tftp 命令从 tftp服务器端下载镜像到内存, addr 为内存地址,file 为服务器端的文件. bootm //引导 uImage 镜像启动 bootz //从内存启动 Linux 镜像和设备 ping //测试与主机的网络连接 loadb //通过串口 Kermit 协议下载二进制数据到内存指定位置 loady loady 通过串口线下载文件到内存 go //跳转到内存地址处,运行程序 env default //重置环境变量 run //可以执行环境变量中的命令,如输入“run bootcmd”启动内核 ls <interface> [<dev[:part]> [directory]] //uboot 下查看文件系统结构(使用的 emmc 版本) ls mmc 1:1 //查看分区 1: lsmmc 1:2 //查看分区 2 文件系统目录 ls mmc 1:2 etc //查看目录包含信息:
uboot 环境变量
- baudrate
串口控制台的波特率- bootargs
传递给 linux 内核的自启动参数,主要用来告诉内核启动信息,分区信息和 根文件系统所在的分区,console=ttymxc0 代表串口节点 ttymxc0 实现内核和 PC 的交互。
波特率是 115200, root=ubi0:rootfs 文件系统的名字叫做 rootfs,分区依次是 uboot,uboot 环境变量,开机 logo,内核,设备 树,文件系统。
bootargs=console=ttymxc0,115200 \ root=ubi0:rootfsrootfstype=ubifs ubi.mtd=5 mtdparts=gpmi-nand:4m(u-boot),2m(-boot->> env),4m(logo),10m(kernel),2m(dtb),-(rootfs) rootwait rw
- bootcmd
是系统自启动参数,在启动参数由一些 uboot 命令组成,在倒计时结束后 uboot 会依次执 行设置好的命令.
run default_bootcmd #表示运行default_bootcmd
- bootdelay
uboot 启动后倒计时的秒数- tftp服务参数
serverip
- 服务器ip地址gatewayip
- 网关IP ipaddr
- 本地ip地址netmask
- 以太网接口的子网掩码- autoload
配置自动下载- ethaddr
以太网网卡的物理地址- bootfile
缺省的下载文件
uboot的作用
(1)uboot主要作用是用来启动操作系统内核。
(2)uboot还要负责部署整个计算机系统。
(3)uboot中还要驱动一些外设比如Flash。lcd,触摸屏等。
(4)uboot还得提供一个命令行界面供人来操作。
uboot 主Makefile清理规则
**make clean:**删除大多数生成的文件,留出足够的空间来构建外部模块
**make mrproper:**删除当前配置以及所有生成的文件
**make distclean:**删除编辑器备份文件,修补剩余文件等
######################################################################### ### # Cleaning is done on three levels. # make clean Delete most generated files # Leave enough to build external modules # make mrproper Delete the current configuration, and all generated files # make distclean Remove editor backup files, patch leftover files and the like # Directories & files removed with 'make clean' CLEAN_DIRS += $(MODVERDIR) \ $(foreach d, spl tpl, $(patsubst %,$d/%, \ $(filter-out include, $(shell ls -1 $d 2>/dev/null)))) CLEAN_FILES += include/bmp_logo.h include/bmp_logo_data.h tools/version.h \ boot* u-boot* MLO* SPL System.map fit-dtb.blob* \ u-boot-ivt.img.log u-boot-dtb.imx.log SPL.log u-boot.imx.log \ lpc32xx-* bl31.c bl31.elf bl31_*.bin image.map tispl.bin* \ idbloader.img flash.bin flash.log defconfig # Directories & files removed with 'make mrproper' MRPROPER_DIRS += include/config include/generated spl tpl \ .tmp_objdiff doc/output MRPROPER_FILES += .config .config.old include/autoconf.mk* include/config.h \ ctags etags tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \ drivers/video/fonts/*.S # clean - Delete most, but leave enough to build external modules # clean: rm-dirs := $(CLEAN_DIRS) clean: rm-files := $(CLEAN_FILES) clean-dirs := $(foreach f,$(u-boot-alldirs),$(if $(wildcard $(srctree)/$f/Makefile),$f)) clean-dirs := $(addprefix _clean_, $(clean-dirs)) PHONY += $(clean-dirs) clean archclean $(clean-dirs): $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) clean: $(clean-dirs) $(call cmd,rmdirs) $(call cmd,rmfiles) @find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \ \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ -o -name '*.ko.*' -o -name '*.su' -o -name '*.pyc' \ -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \ -o -name '*.lex.c' -o -name '*.tab.[ch]' \ -o -name '*.asn1.[ch]' \ -o -name '*.symtypes' -o -name 'modules.order' \ -o -name modules.builtin -o -name '.tmp_*.o.*' \ -o -name 'dsdt.aml' -o -name 'dsdt.asl.tmp' -o -name 'dsdt.c' \ -o -name '*.efi' -o -name '*.gcno' -o -name '*.so' \) \ -type f -print | xargs rm -f # mrproper - Delete all generated files, including .config # mrproper: rm-dirs := $(wildcard $(MRPROPER_DIRS)) mrproper: rm-files := $(wildcard $(MRPROPER_FILES)) mrproper-dirs := $(addprefix _mrproper_,scripts) PHONY += $(mrproper-dirs) mrproper archmrproper $(mrproper-dirs): $(Q)$(MAKE) $(clean)=$(patsubst _mrproper_%,%,$@) mrproper: clean $(mrproper-dirs) $(call cmd,rmdirs) $(call cmd,rmfiles) @rm -f arch/*/include/asm/arch # distclean # PHONY += distclean distclean: mrproper @find $(srctree) $(RCS_FIND_IGNORE) \ \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ -o -name '.*.rej' -o -name '*%' -o -name 'core' \ -o -name '*.pyc' \) \ -type f -print | xargs rm -f @rm -f boards.cfg CHANGELOG
uboot关键启动函数
vectors.S(arch/arm/lib/vectors.S) 设置异常中断向量表
save_boot_params_retarch/arm/lib/vectors.S) 保证 CPU 在 SVC 模式,并禁止 FIQ 和
IRQ 中断 cpu_init_cp15(arch/arm/lib/vectors.S) 关闭 cache,MMU,TLB等,这些都是虚拟内
存转化相关功能,uboot 阶段使用的是物理内存
lowlevel_init(arch/arm/cpu/armv7/lowlevel_init.S)
设置栈顶指针,CONFIG_SYS_INIT_SP_ADDR 加载 Uboot 第二阶段(C 语言阶段) 代码代码到
RAM,设置堆栈,为调用 C 函数进行进一步初始化.
board_init_f_alloc_reserve(common/init/board_init.c ) 留出早期的 malloc
内存区域和 gd 内存区域
_main(arch/arm/lib/crt0.S) 调用 board_init_f函数初始化外设,初始化gd结构体-> board_init_r 函数善后
run_main_loop->main_loop(common/board_r.c->common/main.c)
读秒进入uboot命令模式 cli_loop (common/cli.c) uboot 的命令行处理函数
do_bootz(cmd/bootz.c) bootz命令执行函数 do_bootm_states(common/bootm.c)
处理uboot启动状态 bootm_os_get_boot_func(common/bootm_os.c)
通过boot_os_fn查找系统启动函数 boot_selected_os (common/bootm_os.c) ->
do_bootm_linux(arch/arm/lib/bootm.c) 启动 Linux 内核,调用 系统启动函数 boot_fn
,启动Linux 系统对应的启动函数 boot_prep_linux (arch/arm/lib/bootm.c)
主要用于处理环境变量bootargs(保存着传递给 Linux kernel 的参数)
uboot的“生命周期”
(1)uboot的生命周期就是指:uboot什么时候开始运行,什么时候结束运行。
(2)uboot本质上是一个裸机程序(不是操作系统),
一旦uboot开始SoC就会单纯运行uboot(意思就是uboot运行的时候别的程序是不可能同时运行的),
一旦uboot结束运行则无法再回到uboot(所以uboot启动了内核后uboot自己本身就死了,要想再次看到uboot界面只能重启系统。
重启并不是复活了刚才的uboot,重启只是uboot的另一生。
(3)uboot的入口和出口。
uboot的入口就是开机自动启动,uboot的唯一出口就是启动内核。
uboot还可以执行很多别的任务(譬如烧录系统),但是其他任务执行完后都可以回到uboot的命令行继续执行uboot命令,
而启动内核命令一旦执行就回不来了。
总结:一切都是为了启动内核。
嵌入式linux系统uboot启动过程
(1)典型嵌入式系统的部署:
uboot程序部署在Flash(能作为启动设备的Flash)上、OS部署在FLash(嵌入式系统中用Flash代替了硬盘)上、内存在掉电时无作用,CPU在掉电时不工作。
(2)启动过程:
嵌入式系统上电后先执行uboot、然后uboot负责初始化DDR,初始化Flash,然后将OS从Flash中读取到DDR中,然后启动OS(OS启动后uboot就无用了)总结:
嵌入式系统和PC机的启动过程几乎没有两样,只是BIOS成了uboot,硬盘成了Flash.
Uboot启动第一阶段:
主脉络:部分硬件初始化——>加载完整uboot到RAM——>跳转到第二阶段入口开始执行
①、中断向量表
②、reset
③、cpu_init_cp15(CP15是协处理器)
④、cpu_init_crit
⑤、lowlevel_init
⑥、call_board_init_f
⑦、relocate_code
⑧、call_board_init_r
整个过程最重要的两个文件:
start.S,汇编语言文件,涉及到特定硬件设备的读写寄存器操作以及特定体系结构的汇编语言;
lowlevel_init.S,设计的操作和文件名吻合,即完成底层的初始化。uboot流程第二阶段:
①、Main_loop经过简化后如下所示,看见,这是个很清晰很简单的流程,检测是否有按键按下,没有则倒计时,有则开始处理命令相关的内容。
②、流程转到run_command,经简化可得:这部分是对函数的进一步封装,这里其实是有两个流程的,一个是有关哈希查找命令的,另一个就是调用builtin_run_command.
③、流程转到r builtin_run_command,经简化可得:这里所做的各种为完整解析命令,并调用函数去进一步执行。
④、流程转到cmd_process,经简化可得:得到完整的命令和参数,执行命令。
至此,uboot的使命便完成了,将舞台交给linux。