macOS Environment Setup

Setting up a QEMU + Linux 5.18 lab environment on macOS

macOS
한국어
← Back to DPAS Lab Guide

0. Prerequisite: Check Mac Architecture

uname -m

Apple Silicon Notes

Apple Silicon uses the ARM architecture, so x86_64 VMs cannot run with HVF acceleration.

MethodSpeedNotes
qemu-system-x86_64 (software emulation)Very slowNot recommended
UTM (x86_64 emulation)SlowGUI convenience
qemu-system-aarch64 (native ARM HVF)FastUsed in this guide
Recommendation On Apple Silicon, we use the ARM64 Ubuntu 22.04 + ARM kernel 5.18 combination. NVMe polling concepts (poll_queues, io_poll sysfs, fio hipri) work identically on ARM.

Step 1: Install QEMU

Install Homebrew (if not installed)

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Install QEMU

brew install qemu

Verify Installation

# Intel Mac
qemu-system-x86_64 --version

# Apple Silicon
qemu-system-aarch64 --version

Step 2: Download Ubuntu 22.04 ISO

Intel Mac

# Ubuntu 22.04.4 LTS (x86_64)
curl -L -o ubuntu-22.04-server-amd64.iso \
  https://releases.ubuntu.com/22.04/ubuntu-22.04.5-live-server-amd64.iso

Apple Silicon

# Ubuntu 22.04.4 LTS (ARM64)
curl -L -o ubuntu-22.04-server-arm64.iso \
  https://cdimage.ubuntu.com/releases/22.04/release/ubuntu-22.04.5-live-server-arm64.iso

Step 3: Create Disk Images

Why Separate Disks? During the lab, we repeatedly rmmod/insmod the NVMe driver. If the OS resides on the NVMe device, removing the driver will freeze the system. Therefore, the OS is placed on a virtio disk (/dev/vda) and the test disk on the NVMe device (/dev/nvme0n1).
mkdir -p ~/qemu-lab && cd ~/qemu-lab

# OS disk (for Ubuntu installation, connected via virtio)
qemu-img create -f qcow2 ubuntu.qcow2 30G

# NVMe disk (for lab exercises, raw format — raw is required for O_DIRECT support)
qemu-img create -f raw nvme_disk.raw 16G

Step 4: Install Ubuntu

Intel Mac

cd ~/qemu-lab
qemu-system-x86_64 \
  -m 4G -smp 4 \
  -accel hvf \
  -drive file=ubuntu.qcow2,if=virtio,format=qcow2 \
  -cdrom ubuntu-22.04-server-amd64.iso \
  -boot d \
  -net nic -net user,hostfwd=tcp::2222-:22 \
  -nographic

Apple Silicon

# Check UEFI firmware path
ls $(brew --prefix)/share/qemu/edk2-aarch64-code.fd

cd ~/qemu-lab
qemu-system-aarch64 \
  -M virt \
  -cpu host \
  -m 4G -smp 4 \
  -accel hvf \
  -bios $(brew --prefix)/share/qemu/edk2-aarch64-code.fd \
  -drive file=ubuntu.qcow2,if=virtio,format=qcow2 \
  -cdrom ubuntu-22.04-server-arm64.iso \
  -boot d \
  -net nic -net user,hostfwd=tcp::2222-:22 \
  -nographic
Notes for Ubuntu Server Installation
  • Language/keyboard/network: use defaults
  • openssh-server: make sure to select it
  • Storage: When configuring LVM, allocate the entire disk (at least 20GB is needed for kernel builds). The default only allocates half the disk, so after installation run sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv && sudo resize2fs /dev/ubuntu-vg/ubuntu-lv to expand, or allocate the full size during installation via "Custom storage layout"
  • Remove the ISO after reboot

Step 5: Boot VM after Installation (with NVMe)

After installation is complete, boot without -cdrom and add the NVMe disk.

Intel Mac

cd ~/qemu-lab
qemu-system-x86_64 \
  -m 4G -smp 4 \
  -accel hvf \
  -drive file=ubuntu.qcow2,if=virtio,format=qcow2 \
  -drive file=nvme_disk.raw,id=nvm,if=none,format=raw \
  -device nvme,id=nvme0,serial=deadbeef \
  -device nvme-ns,drive=nvm,bus=nvme0,nsid=1,logical_block_size=512,physical_block_size=512 \
  -net nic -net user,hostfwd=tcp::2222-:22 \
  -nographic

Apple Silicon

cd ~/qemu-lab
qemu-system-aarch64 \
  -M virt \
  -cpu host \
  -m 4G -smp 4 \
  -accel hvf \
  -bios $(brew --prefix)/share/qemu/edk2-aarch64-code.fd \
  -drive file=ubuntu.qcow2,if=virtio,format=qcow2 \
  -drive file=nvme_disk.raw,id=nvm,if=none,format=raw \
  -device nvme,id=nvme0,serial=deadbeef \
  -device nvme-ns,drive=nvm,bus=nvme0,nsid=1,logical_block_size=512,physical_block_size=512 \
  -net nic -net user,hostfwd=tcp::2222-:22 \
  -nographic

Once the VM boots, connect via SSH:

ssh -p 2222 <username>@localhost

Step 6: Inside the Guest — Install Kernel 5.18

Proceed while connected to the guest VM via SSH.

Method A: Ubuntu Mainline PPA (recommended, fast)

# Install tools
sudo apt update
sudo apt install -y wget curl

Intel Mac (x86_64) Guest

KVER=5.18.0-051800
mkdir -p /tmp/kernel
wget -P /tmp/kernel \
  https://kernel.ubuntu.com/mainline/v5.18/amd64/linux-image-unsigned-${KVER}-generic_${KVER}.202205222030_amd64.deb \
  https://kernel.ubuntu.com/mainline/v5.18/amd64/linux-modules-${KVER}-generic_${KVER}.202205222030_amd64.deb \
  https://kernel.ubuntu.com/mainline/v5.18/amd64/linux-headers-${KVER}-generic_${KVER}.202205222030_amd64.deb \
  https://kernel.ubuntu.com/mainline/v5.18/amd64/linux-headers-${KVER}_${KVER}.202205222030_all.deb

sudo dpkg -i /tmp/kernel/*.deb
sudo reboot

Apple Silicon (arm64) Guest

KVER=5.18.0-051800
mkdir -p /tmp/kernel
wget -P /tmp/kernel \
  https://kernel.ubuntu.com/mainline/v5.18/arm64/linux-image-unsigned-${KVER}-generic_${KVER}.202205222030_arm64.deb \
  https://kernel.ubuntu.com/mainline/v5.18/arm64/linux-modules-${KVER}-generic_${KVER}.202205222030_arm64.deb \
  https://kernel.ubuntu.com/mainline/v5.18/arm64/linux-headers-${KVER}-generic_${KVER}.202205222030_arm64.deb \
  https://kernel.ubuntu.com/mainline/v5.18/amd64/linux-headers-${KVER}_${KVER}.202205222030_all.deb

sudo dpkg -i /tmp/kernel/*.deb
sudo reboot

After reboot, verify the kernel version:

uname -r
# 5.18.0-051800-generic

Method B: Source Compilation (when custom kernel 5.18.0-rc6-dpas-fast26 is needed)

Note Use this when a separately provided kernel source or config file is available
# Build dependencies
sudo apt install -y build-essential libncurses-dev bison flex libssl-dev \
  libelf-dev bc pahole dwarves zstd

# Download source (based on rc6)
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.18-rc6.tar.xz
tar xf linux-5.18-rc6.tar.xz
cd linux-5.18-rc6

# Start from current config (minimal changes)
cp /boot/config-$(uname -r) .config
make olddefconfig

# (Optional) Set localversion
echo "-dpas-fast26" > localversion

# Compile (adjust -j to match core count)
make -j$(nproc) bindeb-pkg

# Install
sudo dpkg -i ../linux-image-*.deb ../linux-headers-*.deb
sudo reboot

Step 7: Verify NVMe Polling

After reboot, reconnect via SSH:

ssh -p 2222 <username>@localhost
# Check kernel version
uname -r

# Check NVMe device
lsblk | grep nvme

# Enable poll_queues (reload nvme module)
sudo rmmod nvme
sudo modprobe nvme poll_queues=2

# Verify polling is enabled
cat /sys/block/nvme0n1/queue/io_poll        # 1
cat /sys/block/nvme0n1/queue/io_poll_delay  # -1 (adaptive)
# Install fio and test polling
sudo apt install -y fio

sudo fio --name=poll_test --filename=/dev/nvme0n1 \
  --ioengine=io_uring --hipri=1 \
  --rw=randread --bs=4k --direct=1 \
  --iodepth=1 --numjobs=1 --runtime=10

Convenience Scripts

Save startup scripts for repeated VM launches.

Intel Mac: ~/qemu-lab/start-vm.sh

cat > ~/qemu-lab/start-vm.sh << 'EOF'
#!/bin/bash
cd ~/qemu-lab
qemu-system-x86_64 \
  -m 4G -smp 4 \
  -accel hvf \
  -drive file=ubuntu.qcow2,if=virtio,format=qcow2 \
  -drive file=nvme_disk.raw,id=nvm,if=none,format=raw \
  -device nvme,id=nvme0,serial=deadbeef \
  -device nvme-ns,drive=nvm,bus=nvme0,nsid=1,logical_block_size=512,physical_block_size=512 \
  -net nic -net user,hostfwd=tcp::2222-:22 \
  -nographic
EOF
chmod +x ~/qemu-lab/start-vm.sh

Connection Alias

# Add to ~/.zshrc or ~/.bashrc
alias vm-start="~/qemu-lab/start-vm.sh"
alias vm-ssh="ssh -p 2222 <username>@localhost"

Troubleshooting

SymptomCauseSolution
Could not access KVM kernel moduleUsing -enable-kvm on macOSChange to -accel hvf
hvf: Error initializing HVFAttempting to run x86_64 VM on Apple SiliconSwitch to ARM64 image
nvme0n1 device not foundpoll_queues option missingCheck QEMU launch command
io_poll value is 0Kernel 5.19+ or CONFIG not setVerify kernel 5.18 installation
Cannot connect via SSHVM is still bootingWait 30 seconds and retry
edk2-aarch64-code.fd not foundQEMU brew version issuebrew reinstall qemu