本文档主要介绍linux+arm的开发环境搭建, 采用 虚拟机+qemu+linux+nfs的模式, 主要步骤分为:
https://wowothink.com/f9af0016/
- 虚拟机+ubuntu系统安装
- uboot的下载和编译
- linux内核源码的下载和编译
- busybox的下载和制作,通过nfs挂载
- 在qemu运行制作的完整系统
虚拟机和ubuntu系统
这一步可以在网上找到很多资料, 这里跳过
uboot的下载和编译
源码下载地址: https://ftp.denx.de/pub/u-boot/
- 编译前, 需安装交叉编译工具
$ sudo apt-get install gcc-arm-linux-gnueabi
$ arm-linux-gnueabi-gcc -v
- 修改uboot的Makefile文件
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
- 配置开发板并编译
$ make vexpress_ca9x4_defconfig
$ make –j4
- 运行
$ qemu-system-arm -M vexpress-a9 –kernel u-boot – nographic m 512M
linux内核源码的下载和编译
linux 源码地址: www.kernel.org
选择一个版本下载, 完成后上传到linux环境, 并解压
$ tar xvf linux-5.10.4.tar.xz
$ cd linux-5.10.4/
- 修改linux的Makefile文件
$ vi Makefile // 在文件中添加如下两行
-- <371> ARCH=arm
-- <372> CROSS_COMPILE=arm-linux-gnueabi-
$ make vexpress_defconfig // 配置board config
-- 如遇错误, 需执行
-- sudo apt-get install bison
-- sudo apt-get install flex
$ make menuconfig // 配置内核
-- 如遇错误, 需执行 sudo apt-get install libncurses5-dev
我们需要内核支持ramdisk驱动,所以需要选中如下配置:
General setup --->
----> [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
Device Drivers --->
[*] Block devices --->
<*> RAM block device support
(65536) Default RAM disk size (kbytes
$ make zImage // 编译内核,成功后的内核位于:arch/x86_64/boot/bzImage
-- 如遇错误, 需执行
-- sudo apt-get install libelf-dev
-- sudo apt-get install libssl-dev
$ make modules
$ make dtbs
说明: vmlinuz / zImage / uImage
- uboot 编译后生成了一个 ELF 格式的可执行程序 u-boot,对应的烧录程序是 u-boot.bin,它是由 u-boot 使用 arm-linux-objcopy 得到(主要目的是去掉一些无用的)
- Linux kernel 经过编译后也会生成一个 ELF 格式的可执行程序,叫 vmlinuz 或者 vmlinux,这个就是原始的未经任何处理加工的原版内核 ELF 文件,嵌入式部署烧录的一般不是这个 vmlinuz,而是使用 objcpy 工具去制作成烧录镜像格式的 Image,制作镜像主要目的是缩减大小,节俭磁盘
- 原则上 Image 就可以直接被烧录到 Flash,但实际上并不是这么简单,实际上 Linux 的作者们觉得 Image 太大了,对其进一步压缩,并且在压缩后的前一段部分,附加了一部分解压缩代码,构成了一个压缩可是的镜像就是 zImage(因为当年 Image 大小刚刚比一张软盘大,(软盘有两种,1.2M 和 1.4M,Image 比 1.4M 大一点),为了节省一张软盘的钱,于是乎,设计了这种压缩 Image 成 zImage 的技术)
- uboot 为了启动 Linux 内核,还发明了一种内核格式叫 uImage。uImage 是由 zImage 加工得到的,uboot 中有一个工具,可以将 zImage 加工生产 uImage。注意:uImage 不管 Linux 内核的事,Linux 内核只管生成 zImage 即可,然后 uboot 中的 mkimage 工具再去由 zImage 加工生成 uImage 来给 uboot 启动。这个加工的过程是在 zImage 前面加上 64 字节的 uImage 的头信息即可
- 原则上 uboot 启动时应该给它 uImage 格式的内核镜像,但是实际上uboot也可以支持 zImage,是否支持就看 x210_sd.h 中是否定义了 LINUX_ZIMAG_MAGIC 这个宏。所以有些 uboot 支持 zImage 启动,有些则不支持。但是所有的 uboot肯定都支持 uImage 启动
busybox的下载和制作,通过nfs挂载
- 文件系统的下载和编译
源码地址: https://busybox.net/downloads/
选择一个版本下载, 完成后上传到linux环境, 并解压
$ tar xvf busybox-1.30.0.tar.bz2
$ make menuconfig //配置buysbox源码
-- 把busybox配置为静态编译,这样busybox在运行的时候就不需要额外的动态链接库了
Busybox Settings --->
Build Options --->
[*] Build BusyBox as a static binary (no shared libs)
$ export ARCH=arm
$ export CROSS_COMPILE=arm-linux-gnueabi-
$ make && make install
-- 编译完成后的busybox在源码根目录下的_instll目录下
补充文件和目录
# mkdir etc dev mnt
# mkdir -p etc/init.d/
# vim etc/fstab
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
none /tmp ramfs defaults 0 0
sysfs /sys sysfs defaults 0 0
mdev /dev ramfs defaults 0 0
# vim etc/init.d/rcS
mkdir -p /proc
mkdir -p /tmp
mkdir -p /sys
mkdir -p /mnt
/bin/mount -a
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
# chmod 755 etc/init.d/rcS
# vim etc/inittab
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::cttlaltdel:/bin/umount -a -r
# chmod 755 etc/inittab
# cd dev
# sudo mknod console c 5 1
# sudo mknod null c 1 3
# sudo mknod tty1 c 4 1
这样一个最小的、完整的可以被内核启动的文件系统就齐活啦
- 制作根文件系统镜像文件
1.先制作一个空的镜像文件
2.然后把此镜像文件格式化为ext3格式
3.然后把此镜像文件挂载,并把根文件系统复制到挂载目录
4.卸载该镜像文件
5.打成gzip包
新建如下脚本,并执行
#!/bin/bash
dd if=/dev/zero of=./rootfs.ext3 bs=1M count=32
mkfs.ext3 rootfs.ext3
mkdir fs
mount -o loop rootfs.ext3 ./fs
cp -rf ./busybox-1.33.0/_install/* ./fs
umount ./fs
gzip --best -c rootfs.ext3 > rootfs.img.gz
rm -rf fs
rm -rf rootfs.ext3
uboot通过sd卡加载uImage
1) 生成一个空的SD卡镜像
$ dd if=/dev/zero of=uboot.disk bs=1M count=256 //256M 的空间
2) 创建两个分区 <分别存放kernel和dtbs rootfs>
$ fdisk uboot.disk
-- m //查看fdisk的命令信息
-- n //新建分区
-- p //选择主分区
-- +50M // 分配一个50M 的分区存放kernel
-- n //剩下的空间, 分配给另一个分区, 存放rootfs
-- 直接默认设置
-- w 保存分区信息
$ fdisk -l uboot.disk 查看分区信息
3) 格式化两个分区, 并挂载
mount -o loop 默认的将文件和 /dev/loop0 挂载起来了, 但并不能适用于所有的场景。对于创建一个硬盘文件,然后对该文件进行分区,接着挂载其中一个子分区,
这时就不能用 -o loop 这种方法了. 因此必须如下做:
// 该SD卡是通过文件虚拟的块设备, 所以需要losetup命令, 先关联loop设备
$ sudo losetup /dev/loop1 uboot.disk
$ sudo fdisk -l // 此时可以查看到loop设备
$ sudo partprobe // 将分区表更改通知操作系统
$ sudo mkfs.ext4 /dev/loop1p1 //格式化分区
$ sudo mkfs.ext4 /dev/loop1p2
// 挂载分区
$ mkdir kernel_disk rootfs_disk
$ sudo mount -t ext4 /dev/loop1p1 kernel_disk
$ sudo mount -t ext4 /dev/loop1p2 rootfs_disk
4) 拷贝对应文件到分区, 并取消挂载
// 将zImage和dtb拷贝到 kernel_disk
$ sudo cp linux-5.10.4/arch/arm/boot/zImage kernel_disk/
$ sudo cp linux-5.10.4/arch/arm/boot/dts/vexpress-v2p-ca9.dtb kernel_disk/
// 拷贝rootfs 镜像
$ sudo cp -r busybox-1.33.0/_install/* rootfs_disk/
-- rootfs.img.gz 镜像文件不能会直接用于uboot引导, 需通过mkimage命令 给头部加上64字节信息, 才能使用
-- 这里直接将整个目录拷贝到sd卡, 方便操作
// 取消挂载
$ sudo umount kernel_disk/ rootfs_disk
$ sudo losetup -d /dev/loop1
5) 启动uboot
在倒计时结束之前打断它,不要让其进入自主模式
$ qemu-system-arm -M vexpress-a9 -m 1024M -smp 1 -nographic -kernel u-boot-2021.01/u-boot -sd ./uboot.disk
==> reset 结束 uboot
查看 SD卡的情况,可以用下面的命令查看(默认SD卡就是出于可用状态)
=> mmc info
Device: MMC
Manufacturer ID: aa
OEM: 5859
Name: QEMU!
Bus Speed: 6250000
Mode: MMC legacy
Rd Block Len: 512
SD version 1.0
High Capacity: No
Capacity: 256 MiB
Bus Width: 1-bit
Erase Group Size: 512 Bytes
=> part list mmc 0
Partition Map for MMC device 0 -- Partition Type: DOS
Part Start Sector Num Sectors UUID Type
1 2048 102400 316f1aca-01 83 Boot
2 104448 419840 316f1aca-02 83
=> ls mmc 0:1 // 或者 ext4ls mmc 0:1
<DIR> 1024 .
<DIR> 1024 ..
<DIR> 12288 lost+found
4840192 zImage
14163 vexpress-v2p-ca9.dtb
=> ls mmc 0:2
<DIR> 1024 .
<DIR> 1024 ..
<DIR> 12288 lost+found
...
加载kernel、设备树、设置bootargs、引导内核
=> load mmc 0:1 0x60008000 zImage
3961960 bytes read in 11446 ms (337.9 KiB/s)
=> load mmc 0:1 0x61000000 vexpress-v2p-ca9.dtb
14692 bytes read in 35 ms (409.2 KiB/s)
=> setenv bootargs 'root=/dev/mmcblk0p2 rootwait rw init=/linuxrc console=ttyAMA0 rootfstype=ext4 ignore_loglevel'
=> bootz 0x60008000 - 0x61000000
bootargs命令组合:
启动结果:
6) 固化启动命令
在include/configs/vexpress_common.h中添加CONFIG_BOOTCOMMAND
#define CONFIG_BOOTCOMMAND "load mmc 0:1 0x60008000 zImage ;load mmc 0:1 0x61000000 vexpress-v2p-ca9.dtb; "\
"setenv bootargs \"root=/dev/mmcblk0p2 rootwait rw init=/linuxrc console=ttyAMA0\"; bootz 0x60008000 - 0x61000000"
添加后, 需要重新编译
$ make -j4
完成后, 就可以直接启动了
uboot通过tftp加载uImage、nfs挂载根文件系统
1) 安装TFTP服务、NFS服务
$ sudo apt-get install tftp-hpa tftpd-hpa
$ cat /etc/default/tftpd-hpa
# /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="xxx" // 修改为tftp的下载目录, 用于存放下载的文件, 比如: uImage / dtbs
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure"
-------------------------
$ sudo service tftpd-hpa restart // 修改配置文件后, 需重启服务
$ sudo apt-get install nfs-kernel-server nfs-common
$ cat /etc/exports
# /etc/exports: the access control list for filesystems which may be exported
...
xxx *(rw,sync,no_root_squash,no_subtree_check)
// 添加nfs的下载目录
// 参数说明: *(允许任何客户端连接) rw(读写权限) sync(内存和磁盘上的内容保持同步)
// no_root_squash(不限制root权限) no_subtree_check(即使输出目录是一个子目录,NFS服务器也不检查其父目录的权限)
-------------------------
$ cat /etc/default/nfs-kernel-server
# Number of servers to start up
RPCNFSDCOUNT=8
...
RPCNFSDOPTS="--nfs-version 2,3,4 --debug --syslog"
// ubuntu默认支持协议2, 需添加协议3和协议4的支持
$ sudo service nfs-kernel-server restart
2) 配置网络 qemu <–> linux
新建如下文件
if test -z $1 ; then
echo need a argument: down/up
exit
fi
if [ "up" = $1 ] ; then
brctl addbr br0 # 新建一个网桥, 名称为br0
ifconfig enp0s3 down
brctl addif br0 enp0s3 # 添加网络设备enp0s3到网桥br0
brctl stp br0 off # 关闭生成树协议
ifconfig br0 10.0.2.3 netmask 255.255.255.0 promisc up
ifconfig enp0s3 10.0.2.5 netmask 255.255.255.0 promisc up
route add default gw 10.0.2.2
# 使用命令tunctl添加虚拟网卡tap
tunctl -t tap0 -u alivs
ifconfig tap0 10.0.2.4 netmask 255.255.255.0 promisc up
brctl addif br0 tap0
else
ifconfig tap0 down
brctl delif br0 tap0
ifconfig enp0s3 down
brctl delif br0 enp0s3
ifconfig br0 down
brctl delbr br0
ifconfig enp0s3 10.0.2.15 netmask 255.255.255.0
route add default gw 10.0.2.2
fi
通过enp0s3 网卡 和 qemu 的tap0口建立网桥
$ ./tupap.sh up //完成网络配置
网卡关系说明:
3)编译uImage
uImage 专门用于uboot引导的内核文件
$ make menuconfig // 支持 NFS
File systems ---> Network File Systems
<*> NFS client support
<*> NFS client support for NFS version 2
<*> NFS client support for NFS version 3
[*] NFS client support for the NFSv3 ACL protocol extension
<*> NFS client support for NFS version 4
[*] Provide swap over NFS support
[*] NFS client support for NFSv4.1
[*] NFS client support for NFSv4.2
(kernel.org) NFSv4.1 Implementation ID Domain
[*] NFSv4.1 client support for migration
[*] Root file system on NFS
$ sudo apt-get install mkimage uboot-tools
$ make LOADADDR=0x60003000 uImage -j8
4) 拷贝相关文件到 tftp目录
$ sudo cp uImage /var/lib/tftpboot
$ sudo cp vexpress-v2p-ca9.dtb /var/lib/tftpboot
$ sudo cp busybox-1.33.0/_install/* -ra nfs_home/
5)设置uboot启动命令
在include/configs/vexpress_common.h中修改CONFIG_BOOTCOMMAND
#define CONFIG_BOOTCOMMAND "tftp 0x60003000 uImage;tftp 0x60500000 vexpress-v2p-ca9.dtb; "\
"setenv bootargs 'root=/dev/nfs rw "\
"nfsroot=10.0.2.3:/home/alivs/kernel/nfs_home init=/linuxrc "\
"ip=10.0.2.100 console=ttyAMA0'; "\
"bootm 0x60003000 - 0x60500000;"
/* 配置开发板、主机IP地址 */
#define CONFIG_IPADDR 10.0.2.100
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_SERVERIP 10.0.2.3
-------------------
$ make -j4 // 修改后, 需重新编译
6)NFS启动
$ qemu-system-arm -M vexpress-a9 -m 512 -nographic -net nic -net tap,ifname=tap0,script=no -kernel u-boot-2021.01/u-boot