Uboot,全称Universal Boot Loader,是一个遵循GPL条款的开源项目。它主要用于嵌入式系统,作为一个引导加载程序,支持多种不同的计算机系统结构和嵌入式操作系统。以下是对Uboot的详细解释:
uboot属于bootloader的一种,还有armboot、vivi、redboot等
uboot本质就是一个裸板程序
uboot是德国的denx开发小组维护,官网:http://www.denx.de/wiki/U-Boot/WebHome
uboot依赖硬件信息,所以根据硬件差异修改官方uboot源码适配自己的板子
一、主要作用
Uboot的主要作用是引导系统的启动。它可以从多种设备(如SD卡、NAND闪存、USB设备、网络等)加载操作系统内核,并将其传递给处理器执行。一旦Uboot启动并加载了操作系统内核,它自己的任务就完成了,此时系统控制权将移交给操作系统。
二、支持的系统与架构
Uboot不仅支持Linux系统的引导,还支持NetBSD、VxWorks、QNX、RTEMS、ARTOS、LynxOS、Android等多种嵌入式操作系统。同时,它还支持多种处理器架构,如ARM、x86、PowerPC、MIPS、FPGA、DSP、STM32等。
三、工作模式
Uboot通常包含两种不同的操作模式:下载模式和启动模式。
- 下载模式:在此模式下,Uboot可以通过串口、网络等通信手段从主机下载文件(如内核映像、根文件系统映像等),然后控制启动流程。这在嵌入式系统的开发和调试过程中非常有用。
- 启动模式:这是Uboot的正常工作模式。在嵌入式产品发布时,Uboot必须工作在这种模式下。它会从目标机上的某个固态存储设备(如Flash)上将操作系统自动加载到RAM中运行。
四、功能特点
1、硬件初始化:在系统启动时,Uboot负责初始化处理器、内存、存储设备、网络接口等硬件资源。
例如:mmc read 0x48000000 0x800 0x3000 //将linux内核从emmc中加载到内存中
硬件是按需初始化,不需要的可以暂时不初始化
2、环境变量:Uboot使用环境变量来存储启动参数和配置信息。这些变量可以在运行时进行修改,从而改变启动行为。
3、网络启动支持:Uboot支持TFTP、NFS等网络协议,可以通过网络引导操作系统。
4、命令行界面:Uboot提供一个命令行界面,允许用户手动执行各种操作,如内存读写、设备检测、文件传输等。
5、可移植性:由于其模块化设计,Uboot可以很容易地移植到不同的硬件平台上。开发者可以根据具体的硬件需求进行裁剪和定制。
6、设备树支持:Uboot可以解析设备树文件(Device Tree Blob,DTB),这使得系统在引导时能够根据硬件配置自动调整。
五、uboot的功能
1、硬件初始化
U-Boot在启动过程中必须初始化的硬件主要包括以下几类:
- CPU:
- U-Boot需要对CPU进行初始化,以确保其能够正确执行后续的代码。这通常包括设置CPU的工作模式、时钟频率、缓存配置等。
- 内存:
- 内存初始化是U-Boot启动过程中的重要环节。U-Boot需要配置内存控制器,以确保系统能够正确地访问内存。这包括设置内存的基地址、大小、访问时序等参数。
- 外设:
- 外设初始化涉及各种外部设备的配置和启动。这些设备可能包括存储设备(如NAND闪存、SD卡)、网络接口(如以太网控制器)、串口控制器等。U-Boot需要确保这些设备能够正常工作,以便在后续的操作中能够正确地访问和使用它们。
- 其他硬件:
- 除了上述硬件外,U-Boot还可能需要对其他硬件进行初始化,如中断控制器、时钟源、电源管理等。这些硬件的初始化对于系统的稳定性和性能至关重要。
需要注意的是,U-Boot的硬件初始化过程可能因具体的硬件平台和操作系统需求而有所不同。在某些情况下,U-Boot可能需要更详细的硬件初始化步骤,以支持特定的硬件功能或操作系统特性。
总的来说,U-Boot的硬件初始化过程是一个复杂而关键的任务,它确保了系统在启动过程中能够正确地配置和启动所有必要的硬件组件。通过这一过程,U-Boot为操作系统的加载和运行提供了一个稳定而可靠的环境。
2、加载linux内核
1、准备工作
- 编译Linux内核:根据具体的嵌入式设备和需求,编译Linux内核。编译完成后,会得到一个zImage或uImage格式的内核镜像文件。
- 传输内核镜像:将编译得到的Linux内核镜像文件通过网络、串口、USB等方式传输到嵌入式设备上。
2、设置U-Boot环境变量
在设备上启动U-Boot后,需要设置U-Boot的环境变量,以指定加载内核的方式。通常,需要设置以下几个环境变量:
- 内核镜像文件的路径:通过设置这个环境变量,可以告诉U-Boot如何找到并加载Linux内核镜像文件。
- 其他相关环境变量:如
bootcmd
和bootargs
等。bootcmd
包含了U-Boot进入主循环后要执行的命令,通常用于指定加载内核的具体操作。bootargs
则设置了要传递给内核的信息,主要用来告诉内核分区信息和根文件系统所在的分区。
3、加载Linux内核
设置完成环境变量后,可以通过命令启动U-Boot,加载Linux内核。以下是加载过程的主要步骤:
- U-Boot启动并监听:U-Boot启动后,会监听用户输入。如果在设定的等待时间内没有接收到用户输入,U-Boot会调用
getenv("bootcmd")
函数获取bootcmd
环境变量的值并执行。 - 执行
bootcmd
中的命令:bootcmd
环境变量中的命令通常包含了加载内核所需的一系列操作。例如,它可能会指定从某个存储设备中读取内核镜像文件到内存中,并设置相应的启动参数。 - 引导Linux内核:在加载内核镜像文件和设置启动参数后,U-Boot会使用
booti
或类似的命令来引导Linux内核。这个过程中,U-Boot会调用相应的内核引导函数,并将控制权交给Linux内核。
4、Linux内核启动
一旦U-Boot成功引导Linux内核,Linux内核就会开始启动。它会初始化各种硬件资源,加载设备驱动程序,并启动用户空间的第一个进程。至此,嵌入式系统就可以正常运行Linux操作系统了。
总的来说,U-Boot加载Linux内核的过程是一个复杂而精细的操作。它涉及多个步骤和多个组件的协同工作,包括U-Boot本身的初始化、环境变量的设置、内核镜像文件的加载以及引导命令的执行等。通过这个过程,U-Boot能够将控制权平稳地交给Linux内核,从而实现操作系统的启动和运行。
3、挂接根文件系统rootfs
U-Boot挂载rootfs(根文件系统)的过程通常涉及几个关键步骤,这取决于具体的硬件平台和配置。以下是一个概括性的描述:
1、准备rootfs
- 编译或获取rootfs:根据目标硬件平台和需求,编译或获取合适的rootfs镜像。这可以是一个压缩包(如tar.gz格式),或者是一个已经格式化好的文件系统镜像。
- 传输rootfs:将rootfs镜像传输到目标设备上。这可以通过网络(如NFS、TFTP)、串口、USB或其他存储介质(如SD卡、eMMC)等方式完成。
2、设置U-Boot环境变量
在U-Boot启动后,需要设置相应的环境变量来指定rootfs的位置和挂载方式。这些环境变量可能包括:
bootargs
:这个环境变量包含了要传递给Linux内核的启动参数。其中,root=
参数指定了rootfs的位置和类型(如NFS、RAMDISK、块设备等),nfsroot=
参数(如果使用NFS挂载)指定了NFS服务器的IP地址和共享目录。- 其他相关环境变量:如
bootcmd
(用于自动启动时的命令序列)等。
3、挂载rootfs
在U-Boot中,挂载rootfs通常不是由U-Boot本身直接完成的,而是由Linux内核在启动过程中完成的。U-Boot的任务是提供正确的启动参数给Linux内核,让内核知道如何找到并挂载rootfs。
- 内核启动并解析
bootargs
:当Linux内核启动时,它会解析bootargs
环境变量中的参数。这些参数告诉内核rootfs的位置、类型以及挂载选项等。 - 内核挂载rootfs:根据
bootargs
中的信息,Linux内核会尝试挂载rootfs。如果使用NFS挂载,内核会通过网络请求NFS服务器上的文件系统;如果使用块设备或RAMDISK,内核则会直接访问这些设备上的文件系统。 - 启动用户空间进程:一旦rootfs挂载成功,Linux内核就会启动用户空间的第一个进程(通常是init进程或systemd等),从而进入正常的系统运行状态。
4、注意事项
- NFS挂载:如果使用NFS方式挂载rootfs,需要确保NFS服务器已经正确配置并启动,同时网络连接正常。此外,还需要在
bootargs
中正确设置nfsroot=
和root=
参数。 - RAMDISK挂载:如果使用RAMDISK方式挂载rootfs,通常需要将RAMDISK镜像加载到内存中,并在
bootargs
中指定其位置和大小。然后,内核会在启动时将其挂载为rootfs。 - 块设备挂载:如果使用块设备(如SD卡、eMMC等)挂载rootfs,需要确保这些设备已经正确分区和格式化,并在
bootargs
中指定根分区的设备号或UUID等信息。
综上所述,U-Boot本身并不直接挂载rootfs,而是通过设置正确的启动参数来告诉Linux内核如何找到并挂载rootfs。挂载过程由Linux内核在启动过程中完成。
五、常用命令
Uboot提供了一系列常用命令,用于执行各种任务。以下是一些常用的Uboot命令:
- printenv/print:显示当前的环境变量。
- setenv:设置或修改环境变量。
- saveenv:保存当前环境变量到非易失性存储器(如Flash)。
- boot:按照环境变量bootcmd启动内核。
- reset:重新启动系统。
- nand 命令系列:用于操作NAND闪存设备,如查看NAND信息、读写NAND数据等。
- mmc 命令系列:用于操作MMC/SD卡等存储设备,如查看MMC信息、读写MMC数据等。
- 网络相关命令:如setenv ethaddr设置以太网物理地址、setenv ipaddr设置本地IP地址、tftp下载命令等。
六、uboot源码操作
==获取交叉编译器并且安装交叉编译器==
建议:从芯片厂家获取
切记:交叉编译器的版本要和uboot源码版本要匹配
==获取uboot源码==
切记:从芯片厂家获取
注意:此源码仅仅支持芯片厂家的参考板,不一定能够在自己设计的开发板上运行起来,所以将来势必要根据硬件差异修改uboot源码
拿到源码时,先不要根据硬件差异修改官方的uboot源码,先对uboot源码做编译验证:
uboot源码操作三步骤:
make distclean //获取最干净的uboot源码,只做一次,清除生成的所有文件
make abc_config //配置uboot源码,配置成能够运行在abc这个参考板上,只做一次,abc 参考板的名称
make //编译,只要修改了uboot源码,必须重新编译
ls
ubootpak.bin //便是三星独有的 u-boot.bin
案例:
交叉编译X6818开发板官方的uboot源码
上位机执行:
sudo chown tarena /opt -R
sudo chgrp tarena /opt -R
cp uboot.tar.bz2 /opt/
cd /opt/
tar -xvf uboot.tar.bz2
cd /opt/uboot //进入uboot源码根目录
//三步骤
make distclean
make x6818_config
make -j2/-j4/-j8 //采用2核或者4核或者8核同时编译
ls
ubootpak.bin //编译生成的uboot可执行文件
==uboot源码操作==
修改uboot源码中跟硬件信息相关的头文件
此头文件定义了大量的外设硬件信息,只需根据硬件差异修改对应的宏即可;
此头文件位于:uboot源码/include/configs/abc.h(abc就是参考板的名称)
案例:利用sourceinsight创建uboot源码工程,然后阅读x6818开发板对应的头文件;
位于:/opt/uboot/include/configs/x6818.h
下面操作x6818.h头文件
uboot的内存格局
/*设置系统(x6818)代码段基地址0x43C00000
x6818地址范围:0x40000000 ~ 0x7FFFFFFF(1GB)*/
#define CONFIG_SYS_TEXT_BASE 0x43C00000
-------------------------------------------------------------------------------------------------------------------------
/*设置x6818初始化栈指针指向代码段基地址
注意:ARM处理器 栈区地址是往下走 满减栈的意思是先降地址,再放数据*/
#define CONFIG_SYS_INIT_SP_ADDR CONFIG_SYS_TEXT_BASE
/*设置堆区起始地址*/
#define CONFIG_MEM_MALLOC_START 0x44000000
/*堆区的大小 32M,
分配大小一般比你需要的大小要大一些,防止你其他地方也需要分配堆区*/
#define CONFIG_MEM_MALLOC_LENGTH 32*1024*1024
-------------------------------------------------------------------------------------------------------------------------
/*Config_LCD 显存起始地址*/
#define CONFIG_FB_ADDR 0x46000000
#define CONFIG_BMP_ADDR 0x47000000
-------------------------------------------------------------------------------------------------------------------------
/* Download OFFSET
指定uboot默认内存下载地址*/
#define CONFIG_MEM_LOAD_ADDR 0x48000000
-------------------------------------------------------------------------------------------------------------------------
/*指定内存的起始地址和内存的大小*/
#define CONFIG_SYS_SDRAM_BASE CFG_MEM_PHY_SYSTEM_BASE
#define CONFIG_SYS_SDRAM_SIZE CFG_MEM_PHY_SYSTEM_SIZE
== ==
#define CFG_MEM_PHY_SYSTEM_BASE 0x40000000
#define CFG_MEM_PHY_SYSTEM_SIZE 0x40000000 /* 1GB */
//#define CFG_MEM_PHY_SYSTEM_SIZE 0x80000000 /* 2GB */
-------------------------------------------------------------------------------------------------------------------------
#define CONFIG_SYS_MALLOC_END (CONFIG_MEM_MALLOC_START + CONFIG_MEM_MALLOC_LENGTH)
/* board_init_f, more than 2M(0x8000) for ubifs */
#define CONFIG_SYS_MALLOC_LEN (CONFIG_MEM_MALLOC_LENGTH - 0x8000)
/* kernel load address */
#define CONFIG_SYS_LOAD_ADDR CONFIG_MEM_LOAD_ADDR
/*内存测试 :可以找一块内存区,往那一块内存区里面写入数据,看是否读出来是你写入的数据,写入的和读出的数据一致便成功*/
/* memtest works on */
#define CONFIG_SYS_MEMTEST_START CONFIG_SYS_MALLOC_END
#define CONFIG_SYS_MEMTEST_END (CONFIG_SYS_SDRAM_BASE + CONFIG_SYS_SDRAM_SIZE)
七、重要性
在嵌入式系统中,Uboot通常是启动过程中的第一个软件组件。它从硬件复位状态开始,将系统带到一个可以启动操作系统的状态。因此,Uboot的稳定性和可靠性对整个系统的正常运行至关重要。
综上所述,Uboot是一个功能强大且高度灵活的引导加载程序,在嵌入式系统的开发和生产中发挥着重要作用。