前言

推荐在 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


  • 无标签
写评论...