欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > qemu(4) -- qemu-system-arm使用

qemu(4) -- qemu-system-arm使用

2025/5/2 17:46:57 来源:https://blog.csdn.net/qq_37858281/article/details/147267921  浏览:    关键词:qemu(4) -- qemu-system-arm使用

1. 前言

参考网上的资料,使用qemu中的vexpress_a9板子,跑一下Linux环境。

2. 源码

工作目录的结构如下。

$ tree uboot_linux_busybox/ -L 1
uboot_linux_busybox/ # 工作目录
├── build            # 编译输出目录
├── busybox-1.37.0   # busybox源码目录
├── linux-5.15.180   # linux源码目录
├── script           # 辅助脚本目录:根文件系统制作、qemu启动、编译
├── source           # 源码压缩包目录
└── u-boot-v2025.04-rc5 # u-boot源码目录

2.1 u-boot

可以到U-Boot官网下载对应的源码,我下载的是u-boot-2025.04-rc5.tar.gz,大约24MB。

2.2 linux

可以到The Linux Kernel Archives官网下载对应的源码,我下载的是linux-5.15.180.tar.xz,大约121MB。最新的是linux-6.14.4.tar.xz,qemu开启lcd时,6.14提示Guest has not initialized the display (yet),不清楚为什么,所以就选择使用5.15的版本了。

2.3 busybox

可以到BUSYBOX官网下载对应的源码,我下载的是busybox-1.37.0.tar.bz2,大约2.4MB。1.37.0版本有些bug,busybox-1.37.0/libbb/hash_md5_sha.c文件需要进行以下修改,其中13161318行是新增的,否者编译的时候报错。

1316 # if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
1317 || ctx->process_block == sha1_process_block64_shaNI
1318 # endif

3. 依赖

后期可能要经常修改和编译,就选择直接在wsl上进行编译开发不构建容器了,编译过程中发现还需要安装一些依赖包。

# gnutls-dev,编译u-boot时需要
# libmpc-dev,编译内核时需要
sudo apt install gnutls-dev libmpc-dev

4. 编译

  1. 编译脚本文件如下。
$ tree uboot_linux_busybox/script/vexpress_a9/
uboot_linux_busybox/script/vexpress_a9/ # 编译脚本所在目录
├── build.sh       # 编译全部
├── busybox.sh     # 编译busybox
├── linux.sh       # 编译linux
├── mmc.sh         # 制作mmc镜像
├── private        # 私有脚本,被其他脚本引用
│   ├── common.sh  # 定义环境变量和通用函数,不需要可执行权限
│   ├── net_up.sh  # qemu网卡加载脚本
│   └── rootfs.sh  # 创建根文件系统脚本
├── qemu.sh        # qemu启动脚本
└── uboot.sh       # u-boot编译脚本
  1. 编译输出的目录结构如下。
$ tree uboot_linux_busybox/build/vexpress_a9 -L 1
uboot_linux_busybox/build/vexpress_a9 # 编译输出目录
├── busybox  # busybox编译输出目录
├── images   # 存放qemu启动所需文件的目录
├── linux    # linux编译输出目录
├── rootfs   # 根文件系统目录
└── uboot    # u-boot编译输出目录
  1. 执行./build.sh即可进行全部的编译,也可以根据需要仅编译其中的一项。
# 进入脚本目录
$ cd uboot_linux_busybox/script/vexpress_a9/
# 开始编译,第一次编译时,u-boot和busybox会弹出menuconfig进行设置
$ ./build.sh

4.1 common.sh

#!/bin/bash# 编译环境所需的全局环境变量
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-# 项目名称,设置为当前目录的名称
PRJNAME=$(basename $(pwd))
# 获取主机的cpu核心数,后面命令行中用于设置make的线程数
JOBS=$(nproc)
# 工作目录路径,当前目录是script/${PRJNAME}/
WORKPATH=../../
# 根目录路径,相对当前目录
ROOTFSPATH=${WORKPATH}build/${PRJNAME}/rootfs/
# 镜像路径,相对当前目录
IMAGESPATH=${WORKPATH}build/${PRJNAME}/images/
# 编译输出路径,相对于源码目录
OUTPATH=../build/${PRJNAME}/# 先比较两个文件是否一致,如果不同则拷贝
func_cp()
{# $1: src file# $2: dst filediff -q "$1" "$2" > /dev/null 2> /dev/null || \cp -f "$1" "$2"
}# 创建编译输出目录
if [ ! -e ${WORKPATH}build/${PRJNAME} ]; thenmkdir -p ${WORKPATH}build/${PRJNAME}cd ${WORKPATH}build/${PRJNAME}mkdir -p busybox  images  linux  rootfs  ubootcd ${OLDPWD}# 执行脚本初始化文件系统的内容./private/rootfs.sh ${ROOTFSPATH}
fi

4.2 rootfs.sh

#!/bin/bash# 进入根目录
cd $1# 创建顶层目录
mkdir -p boot dev etc/init.d mnt proc root tmp var# 创建字符设备
sudo mknod -m 666 dev/tty1    c 4 1
sudo mknod -m 666 dev/tty2    c 4 2
sudo mknod -m 666 dev/tty3    c 4 3
sudo mknod -m 666 dev/tty4    c 4 4
sudo mknod -m 666 dev/console c 5 1
sudo mknod -m 666 dev/null    c 1 3# 创建etc目录下文件
cat << EOF > etc/fstab
proc    /proc           proc    defaults        0       0
none    /dev/pts        devpts  mode=0622       0       0
mdev    /dev            ramfs   defaults        0       0
sysfs   /sys            sysfs   defaults        0       0
tmpfs   /dev/shm        tmpfs   defaults        0       0
tmpfs   /dev            tmpfs   defaults        0       0
tmpfs   /mnt            tmpfs   defaults        0       0
var     /dev            tmpfs   defaults        0       0
ramfs   /dev            ramfs   defaults        0       0
EOFcat << EOF > etc/hosts
127.0.0.1 localhost vexpress
::1       ip6-localhost ip6-loopback
fe00::0   ip6-localnet
EOFcat << EOF > etc/profile
export PS1='[\u@\h \W]\$ '
EOFcat << EOF > etc/passwd
root:CkE1d99EEQf9U:0:0:root:/root:/bin/sh
EOFcat << EOF > etc/inittab
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
EOFcat << EOF > etc/group
root:x:0:root
EOFcat << EOF > etc/init.d/rcS
#!/bin/sh
PATH=/bin:/sbin:/usr/bin:/usr/sbin
export LD_LIBRARY_PATH=/lib:/usr/lib
/bin/mount -n -t ramfs ramfs /var
/bin/mount -n -t ramfs ramfs /tmp
/bin/mount -n -t sysfs none /sys
/bin/mount -n -t ramfs none /dev
/bin/mkdir /var/tmp
/bin/mkdir /var/modules
/bin/mkdir /var/run
/bin/mkdir /var/log
/bin/mkdir -p /dev/pts
/bin/mkdir -p /dev/shm
/sbin/mdev -s
/bin/mount -a/sbin/ifconfig lo up
/sbin/ifconfig eth0 192.168.101.201 netmask 255.255.255.0 broadcast 192.168.101.1 up
/usr/sbin/telnetd
/bin/hostname vexpressecho "-----------------------------------"
echo "*****welcome to vexpress board*****"
echo "-----------------------------------"
EOF# 设置rcS文件可执行权限
chmod +x etc/init.d/rcS

4.3 uboot.sh

#!/bin/bash# 加载环境变量和通用函数
. private/common.sh# 进入u-boot源码目录
cd ${WORKPATH}u-boot-v2025.04-rc5# $1不为空则执行相应的命令,执行后退出
if [ ! -z "$1" ]; thenmake O=${OUTPATH}uboot $1exit $?
fi# “O=”指定编译输出目录,使用vexpress_ca9x4板子的配置,如果不存在.config则执行
if [ ! -e ${OUTPATH}uboot/.config ]; thenmake O=${OUTPATH}uboot vexpress_ca9x4_defconfig# 编辑Boot options --->,配置启动命令,注意新配置的命令中引号需要使用斜杠转义# (run distro_bootcmd; run bootflash) bootcmd value =》ext4load mmc 0 0x60003000 boot/uImage;ext4load mmc 0 0x60500000 boot/vexpress-v2p-ca9.dtb;setenv bootargs \"root=/dev/mmcblk0 rw console=ttyAMA0\";bootm 0x60003000 - 0x60500000make O=${OUTPATH}uboot menuconfig
fi# about 1min
make O=${OUTPATH}uboot -j${JOBS}# 将编译出的mkimage程序拷贝到PATH目录中,后面linux制作uImage时会用到它
# ~/bins已经被我添加到PATH环境变量中了
# 如果~/bins/mkimage不存在就执行,也即第一次编译时会执行
[ -e ~/bins/mkimage ] || cp ${OUTPATH}/uboot/tools/mkimage ~/bins
# 将u-boot的镜像拷贝到images目录,之后qemu启动时使用
func_cp ${OUTPATH}/uboot/u-boot ${OUTPATH}images/u-boot

4.4 linux.sh

#!/bin/bash# 加载环境变量和通用函数
. private/common.sh# 进入linux源码目录
cd ${WORKPATH}linux-5.15.180# $1不为空则执行相应的命令,执行后退出
if [ ! -z "$1" ]; thenmake O=${OUTPATH}linux $1exit $?
fi# 使用vexpress的配置,如果不存在.config则执行
if [ ! -e ${OUTPATH}linux/.config ]; thenmake O=${OUTPATH}linux vexpress_defconfig
fi# about 6min,编译kernel镜像,zImage表示压缩镜像
make O=${OUTPATH}linux zImage -j${JOBS}
# 编译设备树
make O=${OUTPATH}linux dtbs
# 编译模块文件
make O=${OUTPATH}linux modules
# 使用u-boot-2025.04-rc5编译出的mkimage程序制作uImage,LOADADDR指定uImage中的头信息
make O=${OUTPATH}linux uImage LOADADDR=0x60003000# 将内核镜像zImage拷贝到images目录
func_cp ${OUTPATH}linux/arch/arm/boot/zImage ${OUTPATH}images/zImage
# 将内核镜像uImage拷贝到boot目录,之后通过u-boot加载内核时,从boot目录读取该文件
func_cp ${OUTPATH}linux/arch/arm/boot/uImage ${OUTPATH}rootfs/boot/uImage
# 将设备树拷贝到images和boot目录,前者用于直接启动,后者用于u-boot启动
func_cp ${OUTPATH}linux/arch/arm/boot/dts/vexpress-v2p-ca9.dtb ${OUTPATH}images/vexpress-v2p-ca9.dtb
func_cp ${OUTPATH}linux/arch/arm/boot/dts/vexpress-v2p-ca9.dtb ${OUTPATH}rootfs/boot/vexpress-v2p-ca9.dtb

4.5 busybox.sh

#!/bin/bash# 加载环境变量和通用函数
. private/common.sh# 进入busybox源码目录
cd ${WORKPATH}busybox-1.37.0# $1不为空则执行相应的命令,执行后退出
if [ ! -z "$1" ]; thenmake O=${OUTPATH}busybox $1exit $?
fi# 使用默认的配置,如果不存在.config则执行
if [ ! -e ${OUTPATH}busybox/.config ];thenmake O=${OUTPATH}busybox defconfig# 执行menconfig修改一些配置项,修改的内容都在“Settings --->”下面# [*] Build static binary (no shared libs),表示使用静态链接,不使用动态库# (./_install)Destination path for 'make install' => ../rootfs_${PRJNAME},表示修改安装目录,此处的目录是相对于编译目录而言的# [*] vi-style line editing commands (New),启用vi命令make O=${OUTPATH}busybox menuconfig
fi# about 1min,此处install会先编译后安装
make O=${OUTPATH}busybox install -j${JOBS}

4.6 mmc.sh

#!/bin/bash# 加载环境变量和通用函数
. private/common.sh# 进入镜像目录
cd ${IMAGESPATH}if [ ! -e mmc0.img ]; then# 创建一个32MB的文件rootfs.ext4,使用0填充dd if=/dev/zero of=mmc0.img bs=1M count=32# 将mmc0.img文件格式化成ext4文件系统mkfs.ext4 mmc0.img# 创建一个临时目录作为挂载点mkdir temp
fi# 将文件mmc0.img挂载到temp/目录上,mount会自动识别文件系统格式,需要管理员权限,-o loop表示挂载的是一个文件虚拟的块设备
sudo mount mmc0.img temp/ -o loop
# 将根目中的文件拷贝到temp/目录中
sudo cp ../rootfs/* temp/ -r
# 取消挂载,此时mmc0.img就是一个根文件系统镜像了,其文件系统格式为ext4,且拥有../rootfs/目录下的所有文件
sudo umount temp

4.7 build.sh

#!/bin/bash# 编译uboot
./uboot.sh
# 编译linux
./linux.sh
# 编译busybox
./busybox.sh
# 构建mmc镜像
./mmc.sh

5. 启动

编辑qemu.sh脚本内容如下。

#!/bin/bash# 加载环境变量和通用函数
. private/common.sh# $1指定启动方式
# linux-gui -- 直接启动linux,使能qemu的gui
# uboot ------ 启动u-boot,不使用gui
# 其他 ------- 直接启动linux,不使用gui
case $1 inlinux-gui)# 因为创建了网络,所以需要管理员权限,-E表示使用当前用户的环境变量sudo -E qemu-system-arm \-M vexpress-a9 \-m 512M \-sd ${IMAGESPATH}mmc0.img \-nic tap,ifname=tap0,script=private/net_up.sh \-kernel ${IMAGESPATH}zImage \-dtb ${IMAGESPATH}vexpress-v2p-ca9.dtb \-append "root=/dev/mmcblk0 rw console=tty0";;uboot)sudo -E qemu-system-arm \-M vexpress-a9 \-m 512M \-sd ${IMAGESPATH}mmc0.img \-nic tap,ifname=tap0,script=private/net_up.sh \-nographic \-kernel ${IMAGESPATH}u-boot;;*)sudo -E qemu-system-arm \-M vexpress-a9 \-m 512M \-sd ${IMAGESPATH}mmc0.img \-nic tap,ifname=tap0,script=private/net_up.sh \-nographic \-kernel ${IMAGESPATH}zImage \-dtb ${IMAGESPATH}vexpress-v2p-ca9.dtb \-append "root=/dev/mmcblk0 rw console=ttyAMA0";;
esac

5.1 无gui

# 进入脚本目录
$ cd uboot_linux_busybox/script/vexpress_a9/
# 无窗口启动,启动日志会直接输出到控制台,先按下CTRL+a再按下x可以退出qemu
$ ./qemu.sh
....(此处省略许多日志,日志中ALSA等信息是可声卡有关的错误,暂时可以先不理会)
-----------------------------------
*****welcome to vexpress board*****
-----------------------------------Please press Enter to activate this console.(此处按下回车即可登录到控制台)
# 进入linux控制台
[root@vexpress ~]$ ls
bin         etc         mnt         sbin        var
boot        linuxrc     proc        tmp
dev         lost+found  root        usr

5.2 有gui

# 进入脚本目录
$ cd uboot_linux_busybox/script/vexpress_a9/
# 有窗口启动,可以看到qemu弹出了gui窗口,等待一段时间会在窗口上出现小企鹅,启动日志会输出到gui窗口
$ ./qemu.sh linux-gui

有窗口启动时,鼠标单击窗口即可进行输入交互,我在两个电脑上运行过,有个电脑gui窗口交互有些问题,输入的命令总是收的不全,另一台电脑正常。鼠标点进窗口后就不能点击qemu窗口的菜单栏了,根据窗口标题提示,需要先按下Ctrl+Alt+g,表示将鼠标的焦点退出控制台,然后才能点击窗口的菜单栏,通过菜单栏的【Machine】》【Quit】可以退出qemu。
有窗口的启动


上一篇:qemu(3) – qemu-arm使用
下一篇:qemu(4) – qemu-system-arm使用
目录:全部文章合集

参考

QEMU教程
U-Boot 文档
Arm Versatile Express boards (vexpress-a9, vexpress-a15)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词