安装系统:在基于ARM处理器的开发板上安装Linux系统
1.基础
1.1 嵌入式系统分层
1.2 移植的目的
不同架构的处理器指令集不兼容,即便是相同的处理器架构,板卡不同驱动代码也不兼容
Linux是一个通用的内核并不是为某一个特定的处理器架构或板卡设计的,所以从官方获取Linux源码后我们要先经过相应的配置使其与我们当前的硬件平台相匹配后才能进行编译和安装
1.3 移植的过程
2.开发板启动过程
2.1 开发板启动流程
- 开发板上电后首先运行SOC内部iROM中固化的代码(BL0),这段代码先对基本的软硬件环境(时钟等...)进行初始化,然后再检测拨码开关位置获取启动方式,然后再将对应存储器中的uboot搬移到内存,然后跳转到uboot运行。
- uboot开始运行后首先对开发板上的软硬件环境做进一步初始化,然后将linux内核、设备树(dtb)、根文件系统(rootfs)从外部存储器(或网络)搬移到内存,然后跳转到linux运行。
- linux开始运行后先对系统环境做初始化,当系统启动完成后,Linux再从内存中(或网络)挂载根文件系统。
2.2 系统移植步骤
- uboot移植
- linux内核移植(包含设备树)
- 根文件系统移植
3.交叉开发环境搭建
网络环境、tftp、nfs
3.1 ubuntu网络环境配置
3.2 tftp服务器环境
3.3 nfs服务器环境
4.uboot概述及SD卡制作
4.1Bootloader
4.2常见Bootloader
Bootloader | Monitor | 描述 | X86 | ARM | PowerPC |
LILO | 否 | Linux磁盘引导程序 | 是 | 否 | 否 |
GRUB | 否 | GNU的LILO替代程序 | 是 | 否 | 否 |
Loading | 否 | 从DOS引导linux | 是 | 否 | 否 |
ROLO | 否 | 从ROM引导linux而不需要BOIS | 是 | 否 | 否 |
Etherboot | 否 | 通过以太网卡启动linux系统的固件 | 是 | 否 | 否 |
LinuxBOIS | 否 | 完全替代BUIS的linux引导程序 | 是 | 否 | 否 |
BLOB | 是 | LART等硬件平台的引导程序 | 否 | 是 | 否 |
U-boot | 是 | 通用引导程序 | 是 | 是 | 是 |
RedBoot | 是 | 基于eCos的引导程序 | 是 | 是 | 是 |
Vivi | 是 | Mizi公司针对于三星的arm cpu设计的引导程序 |
4.3SD卡启动盘制作
5.uboot应用
5.1启动命令
5.1 uboot模式
自启动模式:
uboot启动后若没有用户介入,倒计时结束后会自动执行。自启动环境变量(bootcmd)中设置的命令(一般作加载和启动内核)
交互模式:
倒计时结束之前按下任意按键uboot会进入交互模式,交互模式下用户可输入uboot命令
5.2 uboot帮助命令
help 查看uboot支持的所有命令
help 命令 查看当前命令的使用方法
5.3 uboot环境变量命令
printenv 打印uboot中所有的环境变量
setenv 设置指定的环境变量(保存在RAM中)
setenv 环境变量 环境变量的值
saveenv 保存所有环境变量到EMMC中
5.4 uboot常用环境变量
ipaddr uboot的IP地址
serverip 服务器的IP地址(即ubuntu的IP)
bootdelay 进入自启动模式之前倒计时的秒数
5.5 uboot网络传输命令
loadb 通过Kermit协议下载文件到指定的内存地址
loadb 地址
tftp 通过tftp协议下载文件到指定的内存地址
tftp 地址 文件名
注:使用tftp之前要配置好网络及tftp服务器
5.6 uboot存储器访问命令
mmc read
将EMMC中指定扇区中的内容读取到内存中指定的地址
mmc read <addr> <blk#> <cnt>
addr: 内存地址
blk#: EMMC中的扇区编号
cnt: 读取的扇区的个数
mmc write
将内存中指定地址中的内容写入到EMMC中指定的扇区
mmc write <addr> <blk#> <cnt>
5.7 uboot自启动环境变量
bootcmd
自启动的环境变量
该环境变量可以设置成一到多个uboot命令的集合(若有多个使用\;分割)
自启动模式下uboot就会按照bootcmd中命令的顺序逐条执行
eg:
setenv bootcmd tftp 40008000 interface.bin\;go 40008000
saveenv
5.8 uboot内核启动命令
bootm
启动指定内存地址上的Linux内核并为内核传递参数
bootm kernel-addr ramdisk-addr dtb-addr
注:
kernel-addr: 内核的下载地址
ramdisk-addr: 根文件系统的下载地址
dtb-addr: 设备树的下载地址
若不使用相应的地址,对应的位置写“-”
eg:
bootm 0x41000000 - 0x42000000
5.9 uboot自启动参数环境变量
bootargs
eg:
setenv bootargs root=/dev/nfs nfsroot=xxx.xxx.xxx.xxx:/opt/4412/rootfs
rw console=ttySAC2,115200 init=/linuxrc ip=***.***.***.***
注:
root 根文件系统类型(nfs)
nfsroot 网络文件系统路径(xxx.xxx.xxx.xxx:/opt/4412/rootfs)
rw 操作网络文件系统的权限(rw)
console 控制台(使用串口2,波特率115200)
init init进程的位置(/linuxrc)
ip linux启动后自身的IP(***.***.***.***)
6.linux内核安装及交叉编译
6.1 tftp加载kernel、rootfs
6.2 emmc加载kernel、rootfs
6.3 tftp加载kernel、xxx.dtb,nfs挂载rootfs
6.4 emmc加载uboot
文件:
uboot : uboot.bin
kernel : uImage
device tree : xxx.dtb
rootfs : rootfs.img
重启tftp、nfs服务器
sudo service tftpd-hpa restart
sudo service nfs-kernel-server restart
6.1 tftp加载kernel、xxx.dtb、rootfs
kernel、rootfs放到tftp工作目录
uboot加护模式下设置uboot启动参数:
setenv ipaddr 192.168.10.200
setenv serverip 192.168.10.100
setenv bootcmd tftp 0x41000000 uImage\;tftp 0x42000000 xxx.dtb\;tftp 0x43000000 rootfs.img\;bootm 0x41000000 0x43000000 0x42000000
setenv bootargs root=/dev/nfs nfsroot=192.168.10.100:/home/linux/fs4412/tftpboot rw console=ttySAC2,115200 init=/linuxrc ip=192.168.10.200
saveenv
6.2 emmc加载kernel、xxx.dtb、rootfs.img
下载内核镜像到内存中:
tftp 0x41000000 uImage
将内核镜像写入emmc指定扇区
mmc write 0 0x41000000 0x800 0x2000
下载设备树到内存中:
tftp 0x41000000 xxx.dtb
将内核镜像写入emmc指定扇区
mmc write 0 0x41000000 0x2800 0x800
下载根文件系统镜像到内存中:
tftp 0x41000000 rootfs.img
将内核镜像写入emmc指定扇区
mmc write 0 0x41000000 0x3000 0x2000
修改uboot启动参数:
setenv bootcmd mmc read 0 0x41000000 0x800 0x2000\;mmc read 0 0x42000000 0x2800 0x800\;mmc read 0 0x43000000 0x3000 0x2000\;bootm 0x41000000 0x43000000 0x42000000
saveenv
saveenv失败时可以尝试这个格式:
setenv bootcmd 'mmc read 0 0x41000000 0x800 0x2000\;mmc read 0 0x42000000 0x2800 0x800\;mmc read 0 0x43000000 0x3000 0x2000\;bootm 0x41000000 0x43000000 0x42000000'
6.3 tftp加载内核、设备树,nfs挂载rootfs
开发阶段:根文件系统中经常添加驱动、应用程序,反复将内核、设备树、根文件系统安装到emmc中比较繁琐,一般采用tftp加载内核、设备树,nfs挂载rootfs。产品定型以后再安装到emmc中
文件存储位置:
uboot烧录到SD卡中,uImage、xxx.dtb放到tftp工作目录,rootfs放到nfs工作目录
uboot启动参数:
setenv bootcmd tftp 0x41000000 uImage\;tftp 0x42000000 xxx.dtb\;bootm 0x41000000-0x42000000
bootargs=root=/dev/nfs nfsroot=192.168.10.100:/home/linux/fs4412/source/rootfs rw console=ttySAC2,115200 init=/linuxrc ip=192.168.10.200
saveenv保存
6.4 uboot安装到emmc
uboot交互模式下,下载并安装uboot
下载:
tftp 0x41000000 uboot.bin
安装:
(将内核镜像安装到emmc指定扇区,往emmc的引导分区里面刷写)
emmc open 0
mmc write 0 0x41000000 0x0 0x800
emmc close 0
调整拨码开关位emmc启动
重新配置uboot环境变量
7.交叉编译工具链
可执行文件格式、交叉编译工具链安装
bsp板级开发支持包中获取交叉编译工具链
7.1 交叉编译
7.2 编译原理
- 机器码(二进制)是处理器能直接识别的语言,不同的机器码代表不同的运算指令,处理器能够识别哪些机器码是由处理器的硬件设计所决定的,不同的处理器机器码不同,所以机器码不可移植
- 汇编语言是机器码的符号化,即汇编就是用一个符号来代替一条机器码,所以不同的处理器汇编也不一样,即汇编语言也不可移植
- C语言在编译时我们可以使用不同的编译器将C源码编译成不同架构处理器的汇编,所以C语言可以移植
7.3 编译过程
7.4 ELF、BIN文件格式
ELF文件格式:
ELF格式是Linux平台上应用最广泛的二进制工业标准之一
ELF格式的文件内包含了很多个段不同的段存储了不同的信息;因为ELF格式的文件要通过Linux系统的加载和管理才能运行,所以除了最基本的代码段和数据段之外,其中还存储了很多其它的信息,如符号表、调试信息等
ELF文件相关命令:
file
file + 文件名 查看文件的详细信息
readelf
readelf -h + 文件名 列出elf文件的头部信息
readelf -a + 文件名 列出elf文件的所有信息
BIN文件格式:
BIN文件一般是直接运行在CPU之上的可执行文件,文件内只包含了CPU能够直接识别和运行的指令和数据,不包含其它系统相关的信息
7.5 交叉编译工具链常用工具
size
列出目标文件每一段的大小以及总体的大小
size + 文件名
nm
列出目标文件中的符号表(标示符)
nm + 文件名
strip
丢弃目标文件中的符号
strip + 文件名
objdump
从目标文件中显示信息
eg:
objdump -d + 文件名 将目标文件反汇编(机器码->汇编)
objcopy
对目标文件进行复制和转换
eg:
objcopy --gap-fill=0xff -o binary a.out a.bin
将目标文件转换为bin格式
注:对于嵌入式开发,这个命令很重要
代码段、初始化变量、未初始化变量、总大小、十六进制大小
7.6 安装交叉编译工具链
1、打开家目录文件下的配置文件.bashrc,
2、在最后一行添加如下内容,将交叉编译工具链的安装路径添加到了全局环境变量中
3、重启配置文件使配置生效
4、检查是否安装成功
vi ~/.bashrc
export PATH=$PATH:~/fs4412/toolchain/gcc-4.6.4/bin/
source ~/.bashrc
arm-none-linux-gnueabi-gcc -v
8.uboot原码配置编译
8.1 uboot原码结构
uboot特点:
代码结构清晰
支持丰富的处理器与开发板,易于移植
支持丰富的用户命令
支持丰富的网络协议
支持丰富的文件系统
支持丰富的设备驱动
更新活跃、用户较多、资料丰富
开放源代码
较高的稳定性
不具有通用性(不同的处理器、开发板uboot不可通用)
uboot源码结构:
平台相关代码:
即与CPU架构或开发板硬件相关的源码,硬件的改动对应的代码也需要进行修改
arch:与CPU架构相关的源代码
board:与开发板相关的源代码,包含各种官方评估板对应的源码
平台无关代码
api: 应用接口
common: uboot命令源码
disk: 对磁盘设备的支持
drivers: 设备驱动源码
fs: 对文件系统的支持
include: 头文件
lib: 库
net: 对网络协议的支持
post: 上电自检程序
... ...
配置文件、帮助文档、示例程序、工具等:
README: 说明文档
doc: 帮助文档
Makefile: 编译管理
CREDITS: 开发者
COPYING: 版权
examples: 示例程序
tools: 工具
... ...
8.2 uboot的配置与编译
uboot配置:
1.指定当前使用的硬件平台
make <board_name>_config
注1:<board_name>为当前使用的开发板的名字
注2:执行该命令的前提是uboot源码支持该开发板
注3:该命令必须在uboot源码的顶层目录下执行
2.指定编译uboot源码使用的编译器
在uboot源码顶层目录下的Makefile中指定(CROSS_COMPILE变量)
uboot编译:
1.编译uboot
make
注1:该命令必须在uboot源码的顶层目录下执行
注2:该命令执行后在uboot源码顶层目录下生成u-boot.bin
2.清除编译过程中生成的中间文件
make clean删除生成的所有.o文件
make distclean删除编译生成的所有文件,包含最终可执行文件u-boot
注1:该命令必须在uboot源码的顶层目录下执行
9.linux内核移植
9.1 linux内核特点、源码结构
linux内核特点:
代码结构清晰、模块化设计
支持丰富的硬件平台
较高的稳定性
轻量化及较强的裁剪性
开放源代码
更新活跃、用户较多、资料丰富
支持丰富的网络协议
... ...
linux内核源码结构:
平台相关代码
arch: 与CPU架构相关的源代码
平台无关代码
block:磁盘设备的支持 crypto:加密相关
drivers:设备驱动 firmware:固件
fs:文件系统 include:头文件
init:内核初始化 ipc:进程间通信
kernel:内核核心调度机制等 lib:库
mm:内存管理 net:网络协议
scripts:工具、脚本等 security:安全
usr:打包与压缩 virt:虚拟
帮助文档、示例程序、工具等
COPYING: 版权
CREDITS: 内核贡献者
README: 说明文档
Documentation: 帮助文档
Makefile: 编译管理
samples: 示例
tools: 工具
... ...
9.2 linux内核源码配置
linux内核源码配置:
指定处理器架构及编译工具:
在Linux内核源码顶层目录下的Makefile中指定(ARCH、CROSS_COMPILE)
导入当前处理器的默认配置:
make <soc_name>_defconfig
注1:soc_name为当前使用的处理器的名字
注2:内核源码的arch/arm/configs下对各个厂商的soc都有一个默认配置文件
执行该命令后就会将对应的配置文件中的信息导入到源码顶层目录下的.config
文件中CONFIG_xxx=y表示内核选中了该功能,内核编译时就会将该功能对应的
代码编译,内核的体积也会增大。#CONFIG_xxx is not set表示内核没有选中
该功能,内核编译时该功能对应的代码不会被编译,内核的体积也会减小。
修改配置:
默认配置只能保证内核拥有最基本的功能,我们需要根据自己的实际需求对内核做进一步的配置
方法1:
直接修改.config文件(不推荐)
方法2:
make menuconfig
make menuconfig:
[ ] 有两种状态
输入Y,显示“*”,内核中该功能被选中,相关代码会被编译进内核
输入N,显示“ ”,内核中该功能不被选中,相关代码不会被编译进内核
< > 有三种状态
输入Y,显示“*”,内核中该功能被选中,相关代码会被编译进内核
输入N,显示“ ”,内核中该功能不被选中,相关代码不会被编译进内核
输入M,显示“M”,内核中该功能被选为模块(被编译为独立的模块)
注:使用make menuconfig配置的本质还是修改.config文件
9.3 内核源码编译
内核编译:
(以下命令均在内核源码的顶层目录下执行)
make uImage
编译内核(编译选为“*”的选项到内核)
make modules
编译内核模块(编译选为“M”的选项为独立模块)
make dtbs
编译设备树(将设备树源文件dts编译为二进制文件dtb)
make clean
删除编译过程中产生的中间文件
9.4 设备树
设备树是一种描述硬件信息的数据结构,Linux内核运行时可以通过设备树将硬件信息直接传递给Linux内核,而不再需要在Linux内核中包含大量的冗余编码
设备树文件
dts 设备树源文件
dtsi 类似于头文件,包含一些公共的信息,可被其它设备树文件引用
dtb 编译后的设备树文件
设备树语法
设备树的语法为树状结构,由一系列的节点和属性组成,根节点下包含子节点
子节点下还可以包含子节点,节点内部包含了对应设备的属性
Linux内核驱动移植
1.在make menuconfig界面中选中要安装的驱动
2.在设备树中添加/修改相应的设备信息
3.重新编译内核/设备树
10.linux内核调试及rootfs移植
根文件系统
根文件系统是内核启动后挂载的第一个文件系统系统引导程序会在根文件系统挂载后从中把一些基本的初始化脚本和服务等加载到内存中去运行
根文件系统内容
bin shell命令(elf格式)(通过busybox编译生成)
dev 设备文件(内核启动后会将设备信息写入该目录)
etc 内核配置文件
lib 共享库(elf格式)(从交叉编译工具链中获取)
linuxrc 内核运行的第一个应用程序(通过busybox编译生成)
mnt 挂载目录(非必要)
proc 进程相关文件(内核启动后会将进程信息写入该目录)
root 超级用户家目录(非必要)
sbin 系统管理shell命令(elf格式)(通过busybox编译生成)
sys 驱动相关文件(内核启动后会将驱动信息写入该目录)
usr shell命令(elf格式)(通过busybox编译生成)
BusyBox
BusyBox将很多常用的工具集成到一个很小的可执行文件中,为普通用户提供大多数常用的命令,BusyBox实现的命令都是精简版的,很多扩展都不支持。BusyBox被称为Linux工具里的瑞士军刀