前言
推荐在 Linux 系统上安装 qemu。可以登录www.qemu.org/download/ 下载最新的qemu版本。根据需要选择源码构建安装或者直接使用各个Linux发行版仓库中的版本。如无特殊需要,可以直接使用Linux发行版自带的qemu,安装简单方便。
代码块 |
---|
|
qemu-system-aarch64 --version |
qemu 自定义内核
下载并编译内核
代码块 |
---|
|
(1)以下载6.7.6版本的Linux kernel代码为例,其他版本可自行修改
$ wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.7.6.tar.xz
(2)解压代码压缩包
$ tar -Jxf linux-6.7.6.tar.xz
(3)安装编译工具和依赖库
$ sudo apt install gcc-aarch64-linux-gnu
$ sudo apt install libncurses5-dev build-essential git bison flex libssl-dev
检查安装情况
$aarch64-linux-gnu-gcc -v
Using built-in specs.
COLLECT_GCC=aarch64-linux-gnu-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc-cross/aarch64-linux-gnu/11/lto-wrapper
Target: aarch64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 11.4.0-1ubuntu1~22.04' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libquadmath --disable-libquadmath-support --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --without-target-system-zlib --enable-multiarch --enable-fix-cortex-a53-843419 --disable-werror --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=aarch64-linux-gnu --program-prefix=aarch64-linux-gnu- --includedir=/usr/aarch64-linux-gnu/include --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
(4)指定环境变量
$ export ARCH=arm64
$ export CROSS_COMPILE=aarch64-linux-gnu-
(5) 配置内核:这里采用ARM公司提供的Versatile Express开发平台模拟
cd 到解压出来的内核代码根目录下
$ cp arch/arm/configs/vexpress_defconfig .config
$ make menuconfig
添加hotplug的支持
Device Drivers
-> Generic Driver Options
-> Support for uevent helper
(/sbin/hotplug) path to uevent helper
配置内核的虚拟地址空间范围
Kernel Features --->
Page size(4KB) --->
Virtual address space size(48-bit)--->
增加debug_fs的支持
Kernel hacking --->
Generic Kernel Debugging Instruments --->
[*] Debug Filesystem
(6)编译内核,编译产物为arch/arm64/boot/Image。
$ make all
视自己的机器配置情况,为了提升编译速度,可以使用make all -j4 或者make all -j8 |
制作根文件系统 rootfs
代码块 |
---|
|
(1)下载busybox
$ wget http://busybox.net/downloads/
$ tar jxvf busybox-1.36.1.tar.bz2
(2) busybox编译配置打开静态链接选项
$ make menuconfig
Settings --->
[*] Build static binary (no shared libs)
(3) 编译busybox,执行成功会在代码根目录生成_install目录
cd 到busybox代码根目录
$ make && make install
(4) 在rootfs里添加/etc /lib 和/dev目录
$ cd _install
$ mkdir etc lib dev
$ ls
bin dev etc lib linuxrc sbin usr
// 在etc目录下创建如下文件
$ cat profile
#!/bin/sh
export HOSTNAME=virt-machine
export USER=root
export HOME=/home
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
$ cat 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
debugfs /sys/kernel/debug debugfs defaults 0 0
kmod_mount /mnt 9p trans=virtio 0 0
$ cat inittab
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
// 指明挂载的文件系统
$ mkdir -p etc/init.d
$ cat etc/init.d/rcS
mkdir -p /sys
mkdir -p /tmp
mkdir -p /proc
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 +x rcS
// 添加设备文件
$ cd dev
$ sudo mknod console c 5 1
$ sudo mknod null c 1 3
// 拷贝lib库到lib目录,方便qemu虚拟机启动后,动态编译的程序可以运行
$ cd linb && cp /usr/aarch64-linux-gnu/lib/*.so* -a .
$ ls
ld-linux-aarch64.so.1 libatomic.so.1 libgcc_s.so.1 liblsan.so.0 libnss_compat.so.2 libresolv.so libtsan.so.0
libBrokenLocale.so libatomic.so.1.2.0 libgomp.so.1 liblsan.so.0.0.0 libnss_dns.so.2 libresolv.so.2 libtsan.so.0.0.0
libBrokenLocale.so.1 libc.so libgomp.so.1.0.0 libm.so libnss_files.so.2 librt.so.1 libubsan.so.1
libanl.so libc.so.6 libhwasan.so.0 libm.so.6 libnss_hesiod.so libstdc++.so.6 libubsan.so.1.0.0
libanl.so.1 libc_malloc_debug.so libhwasan.so.0.0.0 libmemusage.so libnss_hesiod.so.2 libstdc++.so.6.0.30 libutil.so.1
libasan.so.6 libc_malloc_debug.so.0 libitm.so.1 libnsl.so.1 libpcprofile.so libthread_db.so
libasan.so.6.0.0 libdl.so.2 libitm.so.1.0.0 libnss_compat.so libpthread.so.0 libthread_db.so.1 |
制作虚拟硬盘
代码块 |
---|
|
cd 到内核源码根目录下,执行下面的命令
dd if=/dev/zero of=rootfs_ext4.img bs=1M count=1024
mkfs.ext4 rootfs_ext4.img
mkdir -p tmpfs
sudo mount -t ext4 rootfs_ext4.img tmpfs/ -o loop
sudo cp ~/busybox-1.36.1/_install/ ./rootfs -a
sudo chown -R root:root rootfs
sudo cp -af rootfs/* tmpfs/
sudo umount tmpfs
chmod 777 rootfs_ext4.img |
qemu 启动
代码块 |
---|
|
// 创建qemu和主机的共享目录,该目录下的文件可以同时被qemu虚拟机中的Linux和主机上的Linux访问修改
mkdir kmodules
// 启动qemu虚拟机
qemu-system-aarch64 -machine virt -cpu cortex-a57 \
-m 1024 \
-smp 4 \
-kernel arch/arm64/boot/Image \
--append "noinitrd root=/dev/vda rw console=ttyAMA0 loglevel=8" \
-nographic \
-drive if=none,file=rootfs_ext4.img,id=hd0 \
-device virtio-blk-device,drive=hd0 \
--fsdev local,id=kmod_dev,path=$PWD/kmodules,security_model=none \
-device virtio-9p-device,fsdev=kmod_dev,mount_tag=kmod_mount |
qemu-system-aarch64: 这是 QEMU 的命令行工具,用于启动模拟 ARM64 架构的虚拟机。
-machine virt: 这个选项指定要模拟的虚拟机的类型,即通用的虚拟机。
-cpu cortex-a57: 指定虚拟机使用的 CPU 模型,这里选择了 Cortex-A57。
-m 1024: 设置虚拟机的内存大小为 1GB。
-smp 4: 指定虚拟机的 CPU 核心数量为 4 个。
-kernel arch/arm64/boot/Image: 指定用作虚拟机启动镜像的内核文件。
--append "noinitrd root=/dev/vda rw console=ttyAMA0 loglevel=8": 这个参数指定了启动内核时要传递给内核的命令行参数。具体来说:
- noinitrd: 表示不使用 initramfs(即不使用 RAM 文件系统)。
- root=/dev/vda: 指定根文件系统的设备为/dev/vda。
- rw: 将根文件系统挂载为读写模式。
- console=ttyAMA0: 将控制台输出重定向到串行端口 ttyAMA0。
- loglevel=8: 设置内核日志级别为 8,以便显示更多的调试信息。
-nographic: 这个选项告诉 QEMU 不要显示图形窗口,而是将所有输出发送到控制台。
-drive if=none,file=rootfs_ext4.img,id=hd0: 这个选项定义了一个虚拟硬盘驱动器。具体来说:
- if=none: 指定不使用任何接口类型,因为后面会使用virtio-blk-device设备来连接这个虚拟硬盘。
- file=rootfs_ext4.img: 指定虚拟硬盘的镜像文件为rootfs_ext4.img。
- id=hd0: 为这个虚拟硬盘指定一个唯一的标识符。
-device virtio-blk-device,drive=hd0: 这个选项定义了一个 virtio 块设备,连接到之前定义的虚拟硬盘。drive=hd0指定连接到名为hd0的虚拟硬盘。
--fsdev local,id=kmod_dev,path=$PWD/kmodules,security_model=none: 这个参数定义了一个本地文件系统设备,用于将主机上的文件系统挂载到虚拟机中。具体来说:
- local: 指定这是一个本地文件系统。
- id=kmod_dev: 为这个设备指定一个唯一的标识符。
- path=$PWD/kmodules: 指定要挂载到虚拟机中的文件系统路径。这里假设在当前工作目录下有一个名为kmodules的文件夹。
- security_model=none: 禁用安全模型,允许对文件系统进行完全的读写访问。
-device virtio-9p-device,fsdev=kmod_dev,mount_tag=kmod_mount: 这个选项定义了一个 virtio-9p 设备,用于将之前定义的本地文件系统挂载到虚拟机中。具体来说:
- fsdev=kmod_dev: 指定要挂载的文件系统设备。
- mount_tag=kmod_mount: 指定挂载的标签名称,在虚拟机中将使用这个标签来识别挂载的文件系统。
qemu 自定义 debian
获取基础文件
代码块 |
---|
|
wget -O installer-initrd.gz http://ftp.debian.org/debian/dists/bullseye/main/installer-arm64/current/images/netboot/debian-installer/arm64/initrd.gz
wget -O installer-linux http://ftp.debian.org/debian/dists/bullseye/main/installer-arm64/current/images/netboot/debian-installer/arm64/linux |
创建磁盘
代码块 |
---|
|
qemu-img create -f qcow2 disk.qcow2 20G |
安装 debian
代码块 |
---|
|
qemu-system-aarch64 -smp 2 -M virt -cpu cortex-a57 -m 1G \
-initrd installer-initrd.gz -kernel installer-linux \
-append "root=/dev/ram" \
-device virtio-scsi-device \
-blockdev qcow2,node-name=hd0,file.driver=file,file.filename=disk.qcow2 \
-device scsi-hd,drive=hd0 \
-netdev user,id=unet -device virtio-net-device,netdev=unet \
-nographic -no-reboot |
TBD: Replace with libguestfs. You need to extract the initrd.img and vmlinuz files from the qcow2 disk file. The easiest way to do this is to use nbd in a Linux environment, e.g. a Virtual Machine that has access to the qcow2 disk file:
代码块 |
---|
|
sudo apt install nbd-client qemu qemu-utils
sudo modprobe nbd max_part=8
sudo qemu-nbd --connect=/dev/nbd0 disk.qcow2
sudo mount /dev/nbd0p1 /media/qcow
sudo cp initrd.img-5.10.0-17-arm64 vmlinuz-5.10.0-17-arm64 ../qemu/.
sync
sudo umount /dev/nbd0p1
sudo nbd-client -d /dev/nbd0 |
然后
代码块 |
---|
|
qemu-system-aarch64 -smp 2 -M virt -cpu cortex-a57 -m 1G \
-initrd initrd.img-5.10.0-17-arm64 \
-kernel vmlinuz-5.10.0-17-arm64 \
-append "root=/dev/sda2 console=ttyAMA0" \
-device virtio-scsi-device \
-blockdev qcow2,node-name=hd0,file.driver=file,file.filename=disk.qcow2 \
-device scsi-hd,drive=hd0 \
-netdev user,id=unet -device virtio-net-device,netdev=unet \
-nographic |
调整资源配置
代码块 |
---|
|
qemu-system-aarch64 -smp 8 -M virt -cpu cortex-a57 -m 4G \
-initrd initrd.img-5.10.0-17-arm64 -kernel vmlinuz-5.10.0-17-arm64 \
-append "root=/dev/sda2" \
-device virtio-scsi-device \
-device scsi-hd,drive=hd0 \
-blockdev qcow2,node-name=hd0,file.driver=file,file.filename=disk.qcow2 \
-netdev user,id=net0,hostfwd=tcp::5555-:22 -device virtio-net-device,netdev=net0 \
-nographic |
远程登录
代码块 |
---|
|
ssh-copy-id -i ~/.ssh/diozero matt@localhost:5555 |
远程复制 scp -P 5555 some_file matt@localhost:/home/matt/some_file
设置sudo
代码块 |
---|
|
ssh -p 5555 matt@localhost
su - -c "apt -y install sudo"
su - -c "usermod -aG sudo matt"
exit |
再次登录并安装基础工具
代码块 |
---|
|
sudo sh -c 'echo "%sudo ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/01_sudo-nopassword'
sudo apt update && sudo apt -y full-upgrade
sudo apt -y install curl gcc make unzip zip vim git build-essential libz-dev zlib1g-dev
sudo systemctl set-default multi-user.target |
配置 Java & GraalVM
代码块 |
---|
|
sudo apt -y install openjdk-17-jdk-headless maven
wget -O - https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.2.0/graalvm-ce-java17-linux-aarch64-22.2.0.tar.gz | tar zxf -
./graalvm-ce-java17-22.2.0/bin/gu install native-image |