在 QEMU x86_64 上运行 Hvisor
一、环境准备
硬件
- 具有 Intel CPU 的计算机
- 支持 VT-x,并已在 BIOS 启用
软件
- 使用 Ubuntu 等 Linux 操作系统,以下示例基于 WSL2 Ubuntu 24.04 LTS
二、安装 gcc 编译器
sudo apt update
sudo apt install gcc
三、安装 QEMU
推荐自行编译 QEMU,便于日后修改 QEMU 源码进行调试。此处以 QEMU v9.2.3 为例,也可以安装更新的版本。
# 安装依赖
sudo apt install git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev \
ninja-build python3-venv bzip2 make
# 获取 QEMU 源码
wget https://download.qemu.org/qemu-9.2.3.tar.xz
# 解压
tar xvJf qemu-9.2.3.tar.xz
# 进入源码目录
cd qemu-9.2.3/
# 生成配置并编译
./configure --enable-kvm --enable-slirp --target-list=x86_64-softmmu
# 编译 qemu
make -j$(nproc)
编辑 ~/.bashrc
文件,在末尾加入:
export PATH="/path/to/qemu-9.2.3/build:$PATH"
随后在终端执行 source ~/.bashrc
更新环境变量,或者重启一个新终端。使用 qemu-system-x86_64 --version
确认当前 QEMU 版本,若为 9.2.3 则安装成功。
四、编译 Linux Kernel
以 Linux v5.19 为例。
# 安装依赖
sudo apt install flex bison libelf-dev libssl-dev
# 下载 linux 5.19 源码
git clone https://github.com/torvalds/linux -b v5.19 --depth=1
cd linux
git checkout v5.19
# 生成默认的编译配置
make ARCH=x86_64 defconfig
# 启用 X2APIC 及其依赖项
./scripts/config --enable CONFIG_X86_X2APIC
./scripts/config --enable CONFIG_ACRN_GUEST
# 启用 RAM 块设备支持
./scripts/config --enable CONFIG_BLK_DEV_RAM
# 启用 IPV6、BRIDGE 和 TUN,以支持在 root linux 中创建网桥和 tap 设备
./scripts/config --enable CONFIG_IPV6
./scripts/config --enable CONFIG_BRIDGE
./scripts/config --enable CONFIG_TUN
# 启用 VIRTIO MMIO,以支持 non-root linux 使用 virtio 驱动
./scripts/config --enable CONFIG_VIRTIO_MMIO
./scripts/config --enable CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES
# 关闭编译期间将警告视为报错
./scripts/config --disable CONFIG_WERROR
# 编译,遇到选项一直 Enter 即可
make ARCH=x86_64 -j$(nproc)
五、基于 Ubuntu 22.04 构建根文件系统
# 下载 Ubuntu 镜像
wget http://cdimage.ubuntu.com/ubuntu-base/releases/22.04/release/ubuntu-base-22.04.5-base-amd64.tar.gz
# 创建 rootfs,用于挂载 rootfs1.img
mkdir -p rootfs
# 创建一个 2G 大小的 ubuntu.img,可以修改 count 修改 img 大小
dd if=/dev/zero of=rootfs1.img bs=1M count=2048 oflag=direct
# 格式化为 ext4 文件系统
mkfs.ext4 rootfs1.img
# 挂载 rootfs1.img
sudo mount -t ext4 rootfs1.img rootfs/
# 将 ubuntu.tar.gz 的内容解压到 rootfs
sudo tar -xzf ubuntu-base-22.04.5-base-amd64.tar.gz -C rootfs/
# 让 rootfs 绑定和获取物理机的一些信息和硬件
sudo cp /etc/resolv.conf rootfs/etc/resolv.conf
sudo mount -t proc /proc rootfs/proc
sudo mount -t sysfs /sys rootfs/sys
sudo mount -o bind /dev rootfs/dev
sudo mount -o bind /dev/pts rootfs/dev/pts
# 将文件系统切换到 rootfs
sudo chroot rootfs
# 在 rootfs 中安装必要的软件包
apt update
apt install git sudo vim bash-completion \
kmod net-tools iputils-ping resolvconf ntpdate screen \
pciutils iproute2 isc-dhcp-client systemd bridge-utils
# 创建进入根文件系统时执行的 init 脚本,赋予执行权限
touch init
chmod 777 init
# 修改 init 脚本,具体内容如下:
# ======================= init =======================
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mkdir -p /dev/pts
mount -t devpts none /dev/pts
echo
echo "Hello Zone 0!"
echo "This boot took $(cut -d' ' -f1 /proc/uptime) seconds"
echo
script /dev/null -c "hostname zone0 && su"
# ====================================================
# 退出 rootfs
exit
# 卸载 rootfs
sudo umount rootfs/proc
sudo umount rootfs/sys
sudo umount rootfs/dev/pts
sudo umount rootfs/dev
sudo umount rootfs
六、Rust 环境配置
请参考 Rust 语言圣经。
七、编译并运行 Hvisor,进入 zone0 Linux
# 下载到本地
git clone https://github.com/syswonder/hvisor.git
cd ./hvisor
# 安装依赖
sudo apt install grub-common xorriso grub-efi-amd64 mtools ovmf
# 新建 kernel 文件夹,用于放置 Linux kernel
mkdir -p ./platform/x86_64/qemu/image/kernel
# 将第四步编译的 Linux kernel 复制到 kernel 文件夹
LINUX_PATH="<路径>/linux"
cp "${LINUX_PATH}/arch/x86/boot/setup.bin" ./platform/x86_64/qemu/image/kernel/
cp "${LINUX_PATH}/arch/x86/boot/vmlinux.bin" ./platform/x86_64/qemu/image/kernel/
# 新建 virtdisk 文件夹,用于放置根文件系统
mkdir -p ./platform/x86_64/qemu/image/virtdisk
# 将第五步制作的根文件系统 rootfs1.img 复制到 virtdisk 文件夹
ROOTFS1_PATH="<路径>/rootfs1.img"
cp "${ROOTFS1_PATH}" ./platform/x86_64/qemu/image/virtdisk/
# 运行 Hvisor
make ARCH=x86_64 BOARD=qemu run
请注意
若执行 `make BOARD=qemu run` 遇到如下报错:
Could not access KVM kernel module: Permission denied
qemu-system-x86_64: -accel kvm: failed to initialize kvm: Permission denied
执行 sudo usermod -a -G kvm 你的用户名
加入 kvm 的用户组,重启终端后再次尝试。
八、使用 hvisor-tool 运行 zone1 Linux
完成最新版本的 hvisor-tool 的编译,具体请参考 hvisor-tool 的 README。
git clone https://github.com/syswonder/hvisor-tool.git
cd hvisor-tool
# KDIR 必须设为第四步编译的 Linux kernel 根目录
make all ARCH=x86_64 LOG=LOG_WARN KDIR=linux根目录
编译完成后,需要将 hvisor-tool 的可执行文件 tools/hvisor
和内核模块 driver/hvisor.ko
复制到 zone0 的根文件系统中的指定位置,例如 /
,将 zone1 的根文件系统、内核镜像以及配置文件放在同一目录。具体的文件名需要与 hvisor-tool 配置文件(来自 hvisor-tool 的 examples/qemu-x86_64/virtio_cfg.json
和 examples/qemu-x86_64/zone1_linux.json
)的内容保持一致。
LINUX_PATH="<路径>/linux"
HVISOR_PATH="<路径>/hvisor"
HVISOR_TOOL_PATH="<路径>/hvisor-tool"
ROOTFS2_PATH="<路径>/rootfs2.img"
# 回到创建根文件系统时的目录,挂载
sudo mount -t ext4 rootfs1.img rootfs/
# 复制 hvisor-tool 的 driver/hvisor.ko 和 tools/hvisor
sudo cp "${HVISOR_TOOL_PATH}/driver/hvisor.ko" rootfs/hvisor.ko
sudo cp "${HVISOR_TOOL_PATH}/tools/hvisor" rootfs/hvisor
# 复制 zone1 的配置文件到 root 路径下
sudo cp "${HVISOR_TOOL_PATH}/examples/qemu-x86_64/virtio_cfg.json" \
rootfs/virtio_cfg.json
sudo cp "${HVISOR_TOOL_PATH}/examples/qemu-x86_64/zone1_linux.json" \
rootfs/zone1_linux.json
# 复制 zone1 的根文件系统和内核镜像,文件系统的制作见下文,
# 内核镜像可以直接沿用第四步所得
sudo cp "${ROOTFS2_PATH}" \
rootfs/rootfs2.img
sudo cp "${LINUX_PATH}/arch/x86/boot/setup.bin" \
rootfs/setup.bin
sudo cp "${LINUX_PATH}/arch/x86/boot/vmlinux.bin" \
rootfs/vmlinux.bin
# 复制内核跳板,需要 Hvisor 已经编译过一次
sudo cp "${HVISOR_PATH}/platform/x86_64/qemu/image/bootloader/out/boot.bin" \
rootfs/boot.bin
# 修改 init,新增内容如下:
# ======================= init =======================
echo "This boot took $(cut -d' ' -f1 /proc/uptime) seconds"
echo
...
ifconfig eth0 up
dhclient eth0
brctl addbr br0
brctl addif br0 eth0
ifconfig eth0 0
dhclient br0
ip tuntap add dev tap0 mode tap
brctl addif br0 tap0
ip link set dev tap0 up
insmod hvisor.ko
...
script /dev/null -c "hostname zone0 && su"
# ====================================================
# 卸载
sudo umount rootfs
# 需要将新的 rootfs1.img 移动至 Hvisor 中
ROOTFS1_PATH="<路径>/rootfs1.img"
cp "${ROOTFS1_PATH}" ./platform/x86_64/qemu/image/virtdisk/
zone1 根文件系统的制作可以参考第五步,不用安装额外的依赖包。但需要适当缩减容量(例如 1G),使之能够放入 zone0 的根文件系统中。zone1 的 init 脚本内容如下:
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mkdir -p /dev/pts
mount -t devpts none /dev/pts
echo
echo "Hello Zone 1!"
echo "This boot took $(cut -d' ' -f1 /proc/uptime) seconds"
echo
ip link set eth0 up
dhclient eth0
script /dev/null -c "hostname zone1 && su"
回到 Hvisor 的根目录,执行下述指令,即可进入 zone1 Linux
# 运行 Hvisor,进行 zone0
make ARCH=x86_64 BOARD=qemu run
# 启动 hvisor-tool virtio 设备
nohup ./hvisor virtio start virtio_cfg.json &
# 启动 zone1
./hvisor zone start ./zone1_linux.json
# 切换到 zone1 的终端,xxx 一般为 1
screen /dev/pts/xxx