1. 流程
在SD卡内烧写uboot,然后设置开发板,通过tftp把uimage和exynos.dtb下载到开发板,通过nfs挂载根文件系统。即可以在开发板上先启动uboot,然后在模拟linux环境。或者可以自由制作uboot和rootfs根文件系统。
2. 概念
2.1 设备树
Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离。在设备树出现以前,所有关于设备的具体信息都要写在驱动里,一旦外围设备变化,驱动代码就要重写。引入了设备树之后,驱动代码只负责处理驱动的逻辑,而关于设备的具体信息存放到设备树文件中,这样,如果只是硬件接口信息的变化而没有驱动逻辑的变化,驱动开发者只需要修改设备树文件信息,不需要改写驱动代码。比如在ARM Linux内,一个.dts(device tree source)文件对应一个ARM的machine,一般放置在内核的"arch/arm/boot/dts/“目录内,比如exynos4412参考板的板级设备树文件就是"arch/arm/boot/dts/exynos4412-origen.dts”。这个文件可以通过$make dtbs命令编译成二进制的.dtb文件供内核驱动使用。
2.2 根文件系统
根文件系统(root filesystem)是运行Linux系统所必须的各种工具软件、库文件、脚本、配置文件的一种特殊的文件目录结构。Linux内核要启动后要挂载一个文件系统才能运行。
2.3 文件说明
1.uImage : 编译好的Linux内核文件,它是专门给u-boot提供的,u-boot是用来引导linux的一个系统。
2.exynos4412-itop-elite.dtb:编译好的设备树文件,设备树中描述了硬件信息,如LED信息,网卡信息DM9606等等。
3.rootfs.tar.gz : 这是文件系统,Linux内核要运行必须挂载一个文件系统。
3. 交叉编译链
3.1 作用
ubuntu为x86环境,交叉编译链的作用是在x86的环境下模拟通过arm编译。
3.2 在linux下配置
- gcc-arm-8.3.tar.gz 放到 /opt目录下
sudo mv gcc-arm-8.3.tar.gz /opt
- 解压
sudo tar zxvf gcc-arm-8.3.tar.gz
- 解压后在 /opt 目录下会出现
gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf
- 编辑环境变量加载文件
sudo vim /etc/profile
#在文件尾部添加下面两行
export ARM_PATH=/opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin
export PATH=${PATH}:${ARM_PATH}
- 使 /etc/profile 生效: 在控制终端运行 source /etc/profile
- 测试交叉编译工具链有没有安装成功
在控制终端 输入 arm-linux-gnueabihf-gcc -v
7.若以上方法无法找到arm-linux-gnueabihf-gcc命令(可选)
vim ~/.bashrc .bashrc文件是在你的用户根目录下,即在/home/<用户名>目录下
在.bashrc末尾添加
export ARM_PATH=/opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin
export PATH=${PATH}:${ARM_PATH}
4. tftp
4.1 作用
(在这里的作用)把uimage和exynos.dtb下载到开发板
4.2 安装过程
1.安装tftp服务器及客户端
sudo apt-get install tftpd-hpa tftp-hpa
2.在 /home/[用户名]/路径下创建一个文件夹: tftpboot
mkdir ~/tftpboot
3.修改tftp的配置文件
sudo vi /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/home/[用户名]/tftpboot"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="-c -s -l"
4.重启tftp服务器
sudo systemctl restart tftpd-hpa.service
5.启动成功
6.上传/下载文件测试
<1>上传文件到tftp服务器
在任意一个目录新建一个文件a.txt,并向其中写入任意内容,然后执行下面的命令
tftp 127.0.0.1
tftp> put a.txt
tftp配置文件 sudo vi /etc/default/tftpd-hpa 中的OPTIONS里要加上-c,才能新建文件
修改 /home/[用户名]/tftpboot 文件夹权限为777
<2>开发板测试tftp服务器功能
从开发板上传文件到tftp服务器目录 :
tftp 192.168.101.242 -p -l new_file.txt
从tftp服务器目录下载文件到开发板 :
tftp 192.168.101.242 -g -r a.txt
测试从tftp服务器下载文件:
tftp 127.0.0.1
tftp> get a.txt
将uImage 和 exynos4412-itop-elite.dtb放在tftp服务器的配置目录下,如tftpboot目录下。
5. nfs
5.1 作用
在linux运行起来后,挂载根文件系统。linux内核在运行时支持通过网络挂载另一网络地址上的文件系统。将pc机上的目录共享给开发板使用,提高开发效率。
5.2 安装过程
1.在/home/[用户名]/目录下创建一个文件夹nfs,将根文件系统的压缩包rootfs.tar.gz拷贝至/home/[用户名]/nfs目录下,然后解压。解压后生成一个目录rootfs,然后修改rootfs权限。
mkdir ~/nfs
cp rootfs.tar.gz ~/nfs
cd nfs
tar -zxvf rootfs.tar.gz
chmod 777 rootfs
2.重新安装nfs服务器
sudo apt-get install nfs-kernel-server
3.配置NFS服务器
sudo vim /etc/exports
在最末尾添加:
/home/[用户名]/nfs/rootfs *(rw,sync,no_subtree_check,no_root_squash)
选项可以参考 man 5 exports (/etc/exports注释中提示的)
4.重启NFS服务器
sudo service nfs-kernel-server restart
5.查看状态
$ sudo systemctl status nfs-kernel-server.service
6.进行挂载测试
sudo mkdir /mnt/root_nfs
sudo mount -t nfs localhost:/home/[用户名]/nfs/rootfs /mnt/root_nfs
ls -l /mnt/root_nfs
7.卸载对rootfs的挂载(可选)
sudo umount /mnt/root_nfs
6. 配置开发板
1.设置开发板的IP地址,开发板通过USB转串口连接到电脑,通过SecureCRT打开串口,输入以下命令:
setenv ipaddr 192.168.xx.xx
2.设置服务器的IP地址,就是虚拟机的地址
setenv serverip 192.168.xx.xx
3.设置网关ip
setenv gatewayip 192.168.101.1
4.设置网络掩码
setenv netmask 255.255.255.0
5.设置开发板启动linux启动的环境变量,设置从nfs挂载根文件系统
setenv bootargs root=/dev/nfs nfsroot=192.168.107.10:/home/[用户名]/nfs/rootfs,proto=tcp,nfsvers=3 rw console=ttySAC2,115200n8 init=/linuxrc ip=192.168.107.100:192.168.107.10:192.168.107.1:255.255.255.0::eth0:o
//bootargs 中的ip参数解释
//bootargs参数由linux内核运行到最后挂载根文件系统时使用
root=/dev/nfs //使用的网络文件系统
nfsroot=192.168.x.x //为虚拟机服务器的ip
nfsroot=192.168.107.10:/home/wyl/nfs/rootfs //其中/home/wyl/nfs/rootfs 是Ubuntu上rootfs的真实路径
ip=192.168.x.x:192.168.x.x:192.168.x.1 //第一个ip为开发的ip,第二个是虚拟机的ip, 第三个是网关
6.下载kernel到内存
tftpboot 40008000 uImage
tftpboot 42000000 exynos4412-itop-elite.dtb
7.手动启动
bootm 40008000 - 42000000
8.如果上面的测试通过的话,可以把2,3中的命令写到bootcmd环境变量中,以后重启就是自动执行
setenv bootcmd tftpboot 40008000 uImage\;tftpboot 42000000 exynos4412-itop-elite.dtb\;bootm 40008000 - 42000000
1. 切换到 u-boot 运行环境下,设置以下环境变量
setenv bootcmd tftpboot 40008000 uImage\;tftpboot 42000000 exynos4412-itop-elite.dtb\;bootm 40008000 - 42000000
再用 saveenv 命令保存u-boot下的环境变量,重启开发板,u-boot倒计时结束 后会自动启动linux
9.保存
saveenv
10.若开发板挂载根文件系统太慢,则可以在Ubuntu上 用ping命令,去ping开发板的IP地址。
7. linux下的uboot镜像烧写到SD卡中
7.1 生成uboot二进制文件,二进制文件就是裸机程序。
运行 uboot源码目录(uboot_itop4412_2017.11.tar.gz 或 jason412_uboot-2017.11)mkboot子目录下的脚本程序 build.sh,在uboot/mkboot目录下输入 ./build.sh 进行 uboot源码的编译,编译完成后,会生成两个文件,一个是itop4412-spl.bin,一个是u-boot.bin。
uboot的源码目录是指:jason412_uboot-2017.11目录
itop4412-spl.bin是在 uboot/spl目录下
u-boot.bin是在 uboot目录下
脚本程序build.sh会将itop4412-spl.bin文件和u-boot.bin拷贝到 uboot/mkboot目录下。
7.2 合成最终的uboot
通过命令 cat E4412_N.bl1.bin itop4412-spl.bin env.bin u-boot.bin > u-boot-iTOP-4412.bin
E4412_N.bl1.bin:三星提供的二进制文件。
itop4412-spl.bin :u-boot编译生成的。
env.bin :保存的环境变量。
u-boot.bin:编译uboot生成的。
通过命令cat E4412_N.bl1.bin itop4412-spl.bin env.bin u-boot.bin > u-boot-iTOP-4412.bin
把三个二进制文件合成为一个最终的 u-boot-iTOP-4412.bin 。
7.3 通过dd命令把u-boot-iTOP-4412.bin烧写到SD卡中
sudo dd iflag=dsync oflag=dsync if=u-boot-iTOP-4412.bin of=/dev/sdb seek=1
解析:
sudo是超级用户权限
dd 是linux 的命令;
dsync 读写数据采用同步IO;所谓同步就是写入或读取磁盘会花费时间,同步就是等待读/写结束,阻塞式等待。
iflag=dsync 使用iflag来控制读取数据时的行为特征。
oflag=dsync 使用oflag来控制写入数据时的行为特征。
if: 输入的文件
of:输出到哪个设备文件(磁盘文件),of=/dev/sdb 其中的 /dev/sdb就代表SD卡
因此意思是:使用超级用户权限把u-boot-iTOP-4412.bin写入到磁盘sdb上,跳过该设备的第一个块(block)(每个block的大小为512B)
块(block):举例说明,内存是以字节为单位来访问的,块设备,如磁盘(flash,SD卡,emmc)是按照块大小为单位来访问的。块的大小可以理解 为内存中的一个存储单元。
磁盘设备的第一个块,存储有磁盘的一些重要信息,一定不能用dd命令去写。
8. 制作linux根文件系统
8.1 busybox
BusyBox 是一个轻量级的开源工具箱,官网地址是:http://www.busybox.net/,其中包含了许多标准的 Unix 工具,例如 sh、ls、cp、sed、awk、grep 等,同时它也支持大多数关键的系统功能,例如自启动、进程管理、启动脚本等等。
8.2 需要配置的软件环境
- 安装arm交叉编译工具链 8.3.0
- 在ubuntu中安装libncurses5-dev
sudo apt install libncurses5-dev
8.3 配置busybox选项并编译
- 解压buxybox压缩包
tar -jxvf busybox-1.36.1.tar.bz2 //解压buxybox压缩包
cd busybox-1.36.1 //会在当前目录下解压为一个文件夹,进入
- 配置编译选项
make menuconfig ARCH=arm //弹出图形化界面
一般配置三个选项:
a)编译器前缀
Settings ---->
(arm-linux-gnueabihf-) Cross compiler prefix //添加交叉编译链arm-linux-gnueabihf-
b)编译静态库
Settings ---->
[*] Build static binary (no shared libs)
c)安装目录
Settings ---->
[*] ( ./_install) Destination path for 'make install' //./install为rootfs安装的目录路径
d)保存后退出
- 编译
make ARCH=arm CROSS_COMPILE=arm-linux-guneabihf-
在 busybox-1.36.1目录下会生成一个busybox可执行文件。运行file busybox,如下:
$ file busybox 确认编译生成的是 ARM 平台的(显示为ELF 32-bit LSB executable, ARM)
- 安装
make install
- 做了什么
至此通过busybox编译安装了根文件目录_install,即rootfs,现在要做的是将根文件目录放到nfs下
8.4 制作根文件系统
- 进入busybox install后的安装目录并创建所需目录
cd _install
mkdir dev etc mnt proc var tmp sys root lib
- 将工具链中的库拷贝到_install/lib 目录下
cd lib
#从编译工具链中拷贝运行库到刚制作的根据文件系统的lib下
sudo cp -Parf /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/arm-linux-gnueabihf/lib/* ./
sudo cp -Parf /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib/* ./
- 删除静态库和共享库(动态库)文件中的符号表以减小体积
#在 lib 目录下,删除掉静态库
rm *.a
#裁剪掉调试信息
arm-linux-gnueabihf-strip ./*
- 查看lib目录大小
du -mh lib/
应该是7.0MB的大小左右
- 添加系统启动文件
cp -rf ~/nfs/rootfs/etc .
拷入成熟的参考配置,将linux内核能成功挂载的rootfs目录下的etc文件夹拷贝到新制作的rootfs目录下面。
a) 在etc下添加文件inittab,文件内容如下:
#this is run first except when booting in single-user mode.
::sysinit:/etc/init.d/rcS
# /bin/sh invocations on selected ttys
# Start an "askfirst" shell on the console (whatever that may be)
::respawn:-/bin/sh
# Stuff to do when restarting the init process
::restart:/sbin/init
# Stuff to do before rebooting
::ctrlaltdel:/sbin/reboot
b) 在etc目录下创建init.d子目录,并在etc/init.d目录下添加rcS文件:
#!/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
runlevel=S
prevlevel=N
umask 022
export PATH runlevel prevlevel
mount -a
echo /sbin/mdev>/proc/sys/kernel/hotplug
mdev -s
mount -o remount,rw /
c) 更改rcS的权限
chmod a+x _install/etc/init.d/rcS
d) 修改以可读可写方式挂载文件系统Readonly file system
vim rootfs/etc/init.d/rcS
e) 在文件末尾添加以下内容
mount -o remount,rw /
f) 在etc下添加文件fstab,文件内容如下
#device mount-point type options dump fsck order
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
- 支持挂载proc, sysfs, tmpfs文件的内核配置
这里我们挂载的文件系统有三个proc、sysfs和tmpfs。在内核中proc和sysfs默认都支持,而tmpfs是没有支持的,我们需要添加tmpfs的支持。
进入linux-4.14.12内核源码根目录,修改Linux内核配置并重新编译:
$ make menuconfig
File systems --->
Pseudo filesystems --->
[*] Tmpfs virtual memory file system support (former shm fs)
[*] Tmpfs POSIX Access Control Lists
在Linux内核源码根目录下,重新编译内核
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LOADADDR=0x40008000 uImage
- 在rootfs目录下的etc目录下添加profile文件,文件内容为
#!/bin/sh
export HOSTNAME=i4412
export USER=root
export HOME=root
export PS1="[$USER@$HOSTNAME \W]\# "
PATH=/bin:/sbin:/usr/bin:/usr/sbin
LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
export PATH LD_LIBRARY_PATH
重要:新制作的文件系统尺寸若超出8M,删除不需要的库文件
9. 创建设备文件
在根文件系统的根目录执行以下命令
#在根文件系统的dev目录一创建一个console设备文件
sudo mknod dev/console c 5 1
#在根文件系统的dev目录一创建一个null设备文件
sudo mknod dev/null c 1 3
- 测试:看新的根文件系统能否正常挂载到开发板
#把原有的根文件系统备份一下
linux@linux:~/busybox-1.22.1 $ mv ~/nfs/rootfs /nfs/rootfs.ok
把新的根文件系统拷贝过去(改名rootfs)
linux@linux:~/busybox-1.22.1$ sudo cp -arf _install ~/nfs/rootfs
- 挂载成功