学前洗脑 ==>>> Uboot也是人写出来的,就是复杂点,第一遍学不会很正常
文章目录
- 一、Uboot简介
-
- 1、Uboot的作用
- 2、Uboot的获取
- [3、Uboot 命令](#3、Uboot 命令)
- 二、Uboot分析
-
- 1、工程目录分析
- 2、顶层Makefile文件分析(后期补)
- [3、Uboot启动流程 (后期补)](#3、Uboot启动流程 (后期补))
-
- [1、链接脚本 u-boot.lds](#1、链接脚本 u-boot.lds)
- 4、Uboot启动Linux内核过程(后期补)
- 三、Uboot移植过程
- 四、Uboot的图形化界面配置
- 杂项
-
- 1、为啥Ubuntu下有些服务不能用systemctl管理
- [2、tar 命令可实现的压缩格式和常用参数](#2、tar 命令可实现的压缩格式和常用参数)
- 3、交叉编译器命名规则
- 4、Linux下存储的查看
- 5、Uboot输出的编译时间和主机时间不一致
- [6、Freescale(飞思卡尔)和 恩智浦(NXP)](#6、Freescale(飞思卡尔)和 恩智浦(NXP))
- 7、WIN下的U盘修复
- 8、stm32启动流程和Uboot启动流程的思考(待补充)
一、Uboot简介
1、Uboot的作用
Uboot 的全称是Universal Boot Loader ,Uboot 是一个遵循 GPL 协议的开源软件,uboot是一
个裸机代码,可以看作是一个裸机综合例程。Linux系统由 Uboot + Linux内核 + 文件系统组成。Uboot的作用就是在板卡上电后初始化部分外设,如EMMC、RAM、网口等、将Linux内核从存储位置(EMMC、SD卡、ftp服务器等)加载到RAM中,然后将控制权交到内核,开始运行Linux。
如果熟悉STM32等单片机,就可以将Uboot看作 app bootloader,就是开机后实现了应用跳转功能,只不过Uboot跳转到了Linux内核。
2、Uboot的获取
1、 首先可以从Uboot官方网站获取,虽然会对不同厂家的芯片有所支持,但是不常用,主要是芯片厂家为自己的芯片做适配时使用,如 NXP、TI等。
2、芯片厂家的Uboot,这是芯片厂商对自己的芯片做过适配/优化的Uboot版本,对具体芯片的支持肯定比Uboot官方做的好。
3、板卡厂商,如正电原子、野火等,围绕具体芯片挂在外设,实现具体功能的电路板。在芯片厂家的Uboot的基础上修修改改,让Uboot可以实现对应功能,如:使用 IMX8 系列的板卡,使用了 a型号的EMMC。在芯片厂家的Uboot上修改,使得Uboot上电后可以初始化该EMMC。当然EMMC或网卡等都有不同的选择,所以板卡厂商就要写或移植不同的驱动到Uboot。
一般的嵌入式Linux应用软件人员拿到的就是板卡厂商的Uboot,直接烧录运行做应用层开发即可。
而Linux驱动开发人员可能使用芯片厂家的Uboot,添加驱动。
3、Uboot 命令
比较多,用的时候先help列出所有命令,然后用啥查啥,不用记。
二、Uboot分析
1、工程目录分析
只列出需要关注的
| 路径 | 内容 |
|---|---|
| arch | 和具体架构相关,比如芯片、CPU架构。芯片厂商有关 |
| board | 和具体板卡相关,板卡厂商有关 |
| configs | Uboot配置相关,在编译时的配置参数就是该目录下的文件 xx_defconfig |
| .xxx_cmd 文件 | 这是编译时产生的文件,里面定义了一些命令由于辅助生成最终目标 |
| Makefile | 顶层Makefile文件 |
| u-boot.xxx | 编译生成的各种格式的Uboot目标、链接脚本、配置文件 |
| .config | make 根据板卡配置文件生成的uboot配置文件,包含选项开关定义和变量定义 |
| README | uboot工程介绍、编译介绍等 |
.u-boot.cmd 将.o 连接为 elf 格式文件 ,件.u-boot-nodtb.bin.cmd 将 elf转为bin,件.u-boot.bin.cmd将最终bin文件复制一份并命名为 u-boot.bin,件.u-boot.imx.cmd在u-boot.bin中添加头部信息,生成u-boot.imx
2、顶层Makefile文件分析(后期补)
3、Uboot启动流程 (后期补)
- 上电,BOOT 引脚配置为 SD 卡启动。
- CPU 直接执行片内 BootROM(出厂固化),初始化基础时钟与 uSDHC 控制器。
- BootROM 从 SD 卡固定偏移读取 IVT/DCD,校验并加载 SPL(U-Boot 第一阶段)到片内 SRAM 执行。
- SPL 初始化 DRAM、时钟、SD/eMMC 控制器,将 U-Boot 主体(u-boot.bin)从 SD 卡加载到 DRAM。
- SPL 跳转到 DRAM 中 U-Boot 的入口 _start,U-Boot 重新初始化硬件、设置栈与向量表。
- 此后系统完全运行在 DRAM,SRAM 中的 SPL 不再使用。
1、链接脚本 u-boot.lds
这个东西在stm32的单片机中也有使用,主要定义各种段存储位置而且Uboot的启动也可以参照stm32的启动流程,大概都是
1、复位
2、然后跳转到 start。 一般为汇编,做一些初始化设置(如时钟、终端向量表、CPU模式,PC指针等)
3、跳转到Linux内核启动函数(单片机是跳转到了_main函数最终跳转到了main函数)
4、Uboot启动Linux内核过程(后期补)
三、Uboot移植过程
1、获取芯片厂家uboot
找到自己的板卡参考芯片原厂的evk板卡的配置文件,先编译一版,烧录到SD卡,看从SD卡是否能够启动。应为如果参考芯片厂家板卡,那么最起码调试串口、SD卡等的驱动是一致的。
2、确定自己板卡和芯片厂家板卡的不同
确定不同,如网口、屏幕等
3、添加自己的板卡文件
- 添加配置文件 /uboot/configs/xxx_defconfig (将参考板卡的配置文件先复制一份,在此基础上修改),文件内容如下:
bash
CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_alientek_emmc/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"
CONFIG_ARM=y
CONFIG_ARCH_MX6=y
CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y
CONFIG_CMD_GPIO=y
/*解释
第一行:告诉uboot 芯片启动配置文件的路径(board目录下的具体系列的具体板卡的文件夹下),并声明了一个宏名称MX6ULL_EVK_EMMC_REWORK,用来在代码中根据该宏切换编译代码段,实现指定功能 ,这一行需要修改
第二行:指定是arm架构
第三行:指定芯片系列为 MX6
第四行:这是和自己的板卡相关的一个宏,应该是指定板卡为CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC,定义为y,应该是在其他文件中读取变量值,拼接目标变量用的这一行要改为自己的
第五行:在Uboot中开启gpio操作功能
注意:uboot文件中定义为y的一般是选项开关,决定某些文件要不要编译,要不要链接到最终目标,如uboot顶层Makefile中的 obj-y
*/
- 添加板卡头文件 /uboot/include/configs/xxx.h(复制参考板卡的),修改防止重编译宏为自己的。里面基本上是一些宏开关,用来裁剪uboot功能和配置板卡功能:如 DRAM大小、malloc 内存池大小、使能那些串口、串口地址、emmc接口、sd接口、NAND接口、uboot启动时是否输出信息等。
即:uboot初始化那些设备、实现那些功能、内核地址、文件系统地址、堆栈大小、输出那些信息等都在这里使能。 - 添加板级文件夹
- 修改板级文件夹下的文件
4、修改自己的驱动
主要是在板卡文件夹下的c和include/configs 下的h和board对应板卡的c文件中做修改。
5、bootcmd
板卡的.h中定义 CONFIG_EXTRA_ENV_SETTINGS,在此宏定义中是环境变量的默认值,是利用类似脚本语言,判断情况定义变量的不同值。CONFIG_EXTRA_ENV_SETTINGS中保存的就是脚本的定义
c
#define CONFIG_EXTRA_ENV_SETTINGS \
CONFIG_MFG_ENV_SETTINGS \
"script=boot.scr\0" \
"image=zImage\0" \
"console=ttymxc0\0" \
"fdt_high=0xffffffff\0" \
"initrd_high=0xffffffff\0" \
"fdt_file=undefined\0" \
"fdt_addr=0x83000000\0" \
"boot_fdt=try\0" \
"ip_dyn=yes\0" \
"panel=TFT43AB\0" \
"mmcdev="__stringify(CONFIG_SYS_MMC_ENV_DEV)"\0" \
"mmcpart=" __stringify(CONFIG_SYS_MMC_IMG_LOAD_PART) "\0" \
"mmcroot=" CONFIG_MMCROOT " rootwait rw\0" \
.....
bootcmd中保存的是uboot默认命令,用于启动Linux内核

uboot在第一次运行时会使用默认值,如果在uboot中做了修改,uboot就会使用修改后的值。
6、bootargs
保存uboot传去linux内核的参数,比如传入调试串口号、波特率、根文件挂载路径等。

7、uboot移植的总结
一般来说,板卡厂商制作的板卡是参考了芯片厂商的评估板。和评估板使用相同的外设emmc、sd卡、网口等。就算板卡厂商使用的外设(如网口PHY芯片)不同,但是接口是一样的。所以添加自己的板卡的配置一般就是将芯片厂商对应的配置文件都复制一份,然后在这些文件的基础上添加自己的配置。我觉得一般来说可能就是网口或串口需要修改,这涉及到uboot的输出和输入。emmc和sd卡的接口一都是特定的接口,而且通信协议也是统一的,所以不需要修改。调试串口可能从片上串口1变为片上串口2,网口可能会更换phy芯片,这些 需要修改,修改时一般在板卡对应的c和h文件中 修改,其他地方不做改动。而我们的uboot的启动地址肯定和芯片厂家的评估板是一致的。等到uboot启动,并且启动了linux内核,uboot的工作已经结束了,不需要我们做其他的工作了。接下来的工作围绕linux内核展开。这才是我们工作的主要领域。所以uboot没必要深究,它可以通过存储将内核启动就行了。当然,如果要做一款芯片的适配,还是要了解uboot的。但是这一般是芯片厂家的工作。
四、Uboot的图形化界面配置
使用Kconfig实现图形化界面配置。这是基于ncurses库的终端字符GUI界面。最后生成.coonfig文件,以便make使用。我们以配置文件xx_defconfig为参数时,最后也是生成了.cconfig文件,二者的结果没有区别。
具体的后面载了解。
杂项
1、为啥Ubuntu下有些服务不能用systemctl管理
systemctl 是新一代系统(systemd)的命令 ,只管理以下目录下的**.service** 文件:
/usr/lib/systemd/system/
/etc/systemd/system/
/etc/init.d/ 是 Linux 传统 SysVinit 初始化脚本目录,作用非常明确:
1、存放服务启动 / 停止 / 重启的脚本(如 vsftpd、nginx、mysql 老版本)
2、是老一代 Linux 服务管理方式
如果想使用systemctl下的自动管理就要在指定目录添加.service文件,现在的Linux下的开机自启程序一般就是这样实现的。
.service 的实现参考 Qt与Linux-->>>Linux中Qt开机自启问题
2、tar 命令可实现的压缩格式和常用参数
| 格式 | 压缩参数 | 常见后缀 | 特点 |
|---|---|---|---|
| gzip | -z | .tar.gz / .tgz | 速度最快,最通用 |
| bzip2 | -j | .tar.bz2 | 压缩比更高,比 gz 小一点 |
| xz | -J | .tar.xz | 压缩率最高(最小),最慢 |
| tar 打包 | 无 | .tar | 只打包,不压缩 |
| 参数 | 作用 |
|---|---|
| -c | 创建压缩包(create) |
| -x | 解压(extract) |
| -v | 显示详细过程(verbose) |
| -f | 指定文件(必须放最后) |
| -t | 查看包内内容(不解压) |
| -C | 指定解压到目标目录 |
| -z | 处理 gz 格式 |
| -j | 处理 bz2 格式 |
| -J | 处理 xz 格式 |
| -p | 保留文件权限 |
3、交叉编译器命名规则
公式
| arch | [-vendor] | [-os] | -eabi / gnueabi / musleabi |
|---|---|---|---|
| 架构 | [厂商] | 系统 | 运行库/二进制接口 |
完整后缀含义对照表
| 完整编译器名称 | 架构 | 系统 | 库 | 浮点 | 用途 |
|---|---|---|---|---|---|
| arm-linux-gnueabihf | ARM 32 位 | Linux | glibc | 硬件浮点 | 嵌入式 Linux 开发(最主流) |
| arm-linux-gnueabi | ARM 32 位 | Linux | glibc | 软件浮点 | 老款 ARM 芯片 |
| aarch64-linux-gnu | ARM64 | Linux | glibc | 硬件浮点 | 64 位 ARM Linux(RK3568/3588) |
| arm-none-eabi | ARM 32 位 | 裸机 | newlib | 硬件浮点 | STM32 单片机、裸机开发 |
| riscv64-linux-gnu | RISC-V 64 | Linux | glibc | 硬件浮点 | RISC-V Linux 开发 |
主流交叉编译器
| 后缀关键字 | 含义 | 适用场景 |
|---|---|---|
| none | 无操作系统 | 裸机、STM32、RTOS |
| linux | Linux 系统 | 嵌入式 Linux、树莓派、IMX6ULL、STM32MP1 |
| eabi | 嵌入式二进制接口 | 统一函数调用规则 |
| gnueabi | glibc + EABI | Linux 应用程序(标准 C 库) |
| musleabi | musl libc + EABI | 轻量级 Linux、小体积系统 |
| hf | hard float | 硬件浮点运算(性能高) |
| 无 hf | soft float | 软件模拟浮点(老芯片) |
EABI = Embedded Application Binary Interface,EABI 就是嵌入式系统的 "统一沟通标准",
让程序、库、系统之间能稳定、高效、兼容地运行。
4、Linux下存储的查看
1、查看识别的存储
javascript
lsblk 或 sudo fdisk -l
fdisk -l 会打印 设备文件名和大小,只有挂载了才会输出
| 设备 | 启动 | 起点 | 末尾 | 扇区 | 大小 | Id | 类型 |
|---|---|---|---|---|---|---|---|
| /dev/sdb1 | 8192 | 15603711 | 15595520 | 7.4G | b | W95 FAT32 |
lsblk是识别了但没有挂载也会输出
2、查看是否挂载
lsblk 会输出挂载目录
3、查看挂载的目录和类型
bash
df -T
举例:
插入了SD卡,先lsblk 查看是否识别,找到和你存储卡差不多大小的那个,SD一般为FAT32类型。
5、Uboot输出的编译时间和主机时间不一致
GCC 交叉编译器内置时间被固定死了。
DATE → 编译日期(由 GCC 编译器提供)
TIME → 编译时间。
它们不读取系统时间它们读取 GCC 编译器自己内部的时间,gcc-linaro-4.9.4-2017.01这种超老 Linaro编译器有一个经典 BUG:编译器内部时间被硬编码固定到 2025 年,无论你主机现在是 2026、2027、2030, 它永远输出 2025 年。这是 老 Linaro 4.9 编译器的通病。
6、Freescale(飞思卡尔)和 恩智浦(NXP)
2015 年 12 月,被恩智浦(NXP) 以118 亿美元收购,成为 NXP 的一部分,飞思卡尔作为独立公司不复存在。i.MX 系列是Freescale基于 ARM 的应用处理器,广泛用于嵌入式与消费电子。
所以在Uboot中,IMX的板卡相关的文件在**/home/wangju/linux/uboot/board/freescale** 目录下。
7、WIN下的U盘修复
diskpart
list disk # 列出所有磁盘,看容量找U盘(如 disk 1)
select disk X # 选中U盘(务必核对容量!)
attributes disk clear readonly # 清除写保护
clean # 清空分区表(数据全清)
create partition primary # 新建主分区
format fs=fat32 quick # 快速格式化为FAT32(大容量用 exFAT)
assign # 分配盘符
exit # 退出
8、stm32启动流程和Uboot启动流程的思考(待补充)
上电后运行的第一行代码到进入应用的流程(单片机是进入main ,uboot是进入Linux内核)