This is a step-by-step guide in order to enable KVM on ODROID-XU4. This guide is only available in u-boot odroidxu4-v2017.05 and Linux kernel 4.9.x and 4.14.11 or higher versions.

The first step is to rebuild the kernel. KVM is needed the arch timer instead of MCT (Multi-Core Timer) which is the default timer of ODROID-XU4 (by exynos5422-odroidxu4-kvm.dtb). And there are the virtualization related configurations in odroidxu4_kvm_defconfig file.

For 4.9.50

$ sudo apt update
$ sudo apt install git
$ git clone --depth 1 https://github.com/hardkernel/linux -b odroidxu4-4.9.y
$ cd linux
$ make odroidxu4_kvm_defconfig
$ make -j8
$ sudo make modules_install
$ sudo cp arch/arm/boot/zImage /media/boot/zImage_kvm
$ sudo cp arch/arm/boot/dts/exynos5422-odroidxu4-kvm.dtb /media/boot/

For 4.14.11

$ sudo apt update
$ sudo apt install git
$ git clone --depth 1 https://github.com/hardkernel/linux -b odroidxu4-4.14.y
$ cd linux
$ make odroidxu4_kvm_defconfig
$ make -j8
$ sudo make modules_install
$ sudo cp arch/arm/boot/zImage /media/boot/zImage_kvm
$ sudo cp arch/arm/boot/dts/exynos5422-odroidxu4-kvm.dtb /media/boot/
  • If you are working on minimal image and/or have a trouble with booting with the newly built zImage and dtb file, try to update ramdisk referring to the below.
$ sudo cp .config /boot/config-`make kernelrelease`
$ sudo update-initramfs -c -k `make kernelrelease`
$ sudo mkimage -A arm -O linux -T ramdisk -C none -a 0 -e 0 -n uInitrd -d /boot/initrd.img-`make kernelrelease` /boot/uInitrd-`make kernelrelease`
$ sudo cp /boot/uInitrd-`make kernelrelease` /media/boot/uInitrd
$ sync

Modify the boot.ini file. (zImage → zImage_kvm, exynos5422-odrooidxu4.dtb → exynos5422-odrooidxu4-kvm.dtb)

  • /media/boot/boot.ini
(......)
# Load kernel, initrd and dtb in that sequence
fatload mmc 0:1 0x40008000 zImage_kvm
(......)
if test "${board_name}" = "xu4"; then fatload mmc 0:1 0x44000000 exynos5422-odrooidxu4-kvm.dtb; setenv fdtloaded "true"; fi
(......)

Reboot the ODROID-XU4.
Check whether KVM is enabled or not after the booting process is finished.

$ dmesg | grep HYP
[    0.096589] CPU: All CPU(s) started in HYP mode.
[    0.777814] kvm [1]: HYP VA range: c0000000:ffffffff
$ dmesg | grep kvm
[    0.777771] kvm [1]: 8-bit VMID
[    0.777793] kvm [1]: IDMAP page: 40201000
[    0.777814] kvm [1]: HYP VA range: c0000000:ffffffff
[    0.778642] kvm [1]: Hyp mode initialized successfully
[    0.778713] kvm [1]: vgic-v2@10484000
[    0.779091] kvm [1]: vgic interrupt IRQ16
[    0.779127] kvm [1]: virtual timer IRQ60
$ cat /proc/interrupts | grep arch_timer
 58:          0          0          0          0          0          0          0          0     GIC-0  29 Level     arch_timer
 59:          0       1857       1412       1345      16986       6933       5162       3145     GIC-0  30 Level     arch_timer

Example: Ubuntu Minimal 16.04.3 Running using QEMU and KVM/ARM

To follow this section, the KVM is already enabled. And 4GB or more storage space is required for this work.

In this section, we run the Ubuntu Minimal 16.04.3 on the virtual machine using QEMU and KVM/ARM.

Install qemu-system-arm which is to virtualize the arm machine and required packages.

$ sudo apt update
$ sudo apt install qemu-system-arm kpartx 

Prepare guest OS kernel and dtb images. It is needed to set clock frequency for timer in dts file (Add 'clock-frequency = <100000000>;' line in timer node).

$ wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.13.tar.xz
$ tar Jxvf linux-4.13.tar.xz
$ cd linux-4.13
$ nano arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
  • A part of arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
        timer {
                compatible = "arm,armv7-timer";
                interrupts = <1 13 0xf08>,
                             <1 14 0xf08>,
                             <1 11 0xf08>,
                             <1 10 0xf08>;
                clock-frequency = <100000000>;
        };

Build and copy zImage and dtb images to the working directory.

$ make vexpress_defconfig
$ make menuconfig
Enable the block layer  --->
    [*]   Support for large (2TB+) block devices and files
$ make zImage dtbs -j8
$ cp arch/arm/boot/zImage ../
$ cp arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dtb ../
$ cd ..

Prepare Ubuntu minimal root filesystem image. Download Ubuntu minimal 16.04.3 image and generate the root filesystem image from the image.

$ wget https://odroid.in/ubuntu_16.04lts/ubuntu-16.04.3-4.9-minimal-odroid-xu4-20170824.img.xz
$ unxz ubuntu-16.04.3-4.9-minimal-odroid-xu4-20170824.img.xz
$ sudo kpartx -a ubuntu-16.04.3-4.9-minimal-odroid-xu4-20170824.img
$ sudo dd if=/dev/mapper/loop0p2 of=ubuntu-minimal-16.04.3.img
$ sudo kpartx -d ubuntu-16.04.3-4.9-minimal-odroid-xu4-20170824.img

Modify the root filesystem for the guest environment (Remove odroid specific file and configuration).

$ mkdir rootfs
$ sudo mount ubuntu-minimal-16.04.3.img rootfs
$ cd rootfs
$ sudo rm ./first_boot
$ sudo rm ./etc/fstab
$ sudo touch ./etc/fstab
$ cd ..
$ sudo umount rootfs

Run qemu (Host: Ubuntu Mate 16.04.3 / 4.9.50 kernel, Guest: Ubuntu Minimal 16.04.3 / 4.13 kernel)

$ qemu-system-arm -M vexpress-a15 -smp 2 -cpu host \
-enable-kvm -m 512 -kernel zImage -dtb vexpress-v2p-ca15-tc1.dtb \
-device virtio-blk-device,drive=virtio-blk \
-drive file=ubuntu-minimal-16.04.3.img,id=virtio-blk,if=none \
-netdev user,id=user -device virtio-net-device,netdev=user \
-append "console=tty1 root=/dev/vda rw rootwait fsck.repair=yes"

)
Host OS runs the LTS Kernel 4.9.50 while Guest OS runs the upstream Kernel 4.13.


Host OS runs the LTS Kernel 4.14.12 while Guest OS runs the upstream Kernel 4.15 RC6.
The Guest OS booted with linux-4.15-rc6 for vexpress-a15 config file.


Host OS runs the LTS Kernel 4.14.47 while Guest OS runs the upstream Kernel 4.17.
The Guest OS booted with linux-4.17 for vexpress-a15 config file.