Emulating ARM64 on Linux

Aug 28, 2015 [ #operatingsystems #arm #ubuntu ]

Contents

Intro

Over the last few years I’ve championed the ultra-low-power and high-density of a 64-bit ARM platform in the datacenter. The promise of thousands of cores all sipping power and taking up less room than the equivalent Intel architecture to me is both exciting and pragmatic as a large systems IT engineer.

Combining with the simplicity of a microservices architecture with the advantages of immutable infrastructure running across a sea-of-containers and you have an extremely agile development and operations platform.

Working for a semiconductor company specializing in ARM Architecture unfortuantely does not guarentee me access to this exciting new tech (yet), but through the wonders of emulation bringing up a development environment is just a few commands away.

The following gist contains the steps on standing up a emulated ARM64 virtual machine either with Ubuntu or Debian operating system. This should be enough for anyone wanting to tinker with an ARM64 before getting their hands on real hardware.

The biggest downside of this approach is while you can assign as much memory to QEMU as the host system has available, it is bound to only one CPU, which combined with the emulation environment leads to extremely slow performancen on CPU intensive tasks.

# Setting up a Ubuntu 14.04 or Debian 8 (jessie) arm64 VM

This is mainly a notes dump and should be used for reference. This guide assumes:

* Ubuntu 14.04 (or Debian 8) hypervisor/host with bridge networking
* Knowledge of qemu
* Knowledge of debootstrap

Limitations of the qemu-system-aarch64 emulator on x86 include only being able to emulate one CPU and no KVM support.

#### Install required packages

    sudo apt-get install debootstrap qemu-utils qemu

#### Install build deps for qemu (apt-src must be enabled in sources.list)

    sudo apt-get build-dep qemu

#### Download the latest qemu source and build for a target of aarch64

    git clone git://git.qemu.org/qemu.git qemu.git
    cd qemu.git
    ./configure --target-list=aarch64-softmmu --enable-fdt --enable-vhost-net --enable-kvm
    make -j4
    sudo make install

#### Binary installs to

    /usr/local/bin/qemu-system-aarch64

#### Create a 60GB qcow2 image that the VM will use as /

    qemu-img create -f qcow2 /srv/chroots/trusty.qcow2 60G

#### Mount qcow as nbd device so we can use deboostrap on it and as a chroot later

    modprobe -av nbd
    qemu-nbd -c /dev/nbd0 /srv/chroots/trusty.qcow2

#### Create partition on ndb0 and set the type to linux. Optionally you can also setup a swap partition.

    fdisk /dev/nbd0

#### Create an ext4 filesystem on new partition

    mkfs.ext4 /dev/nbd0p1

#### Mount partition on /mnt

    mount -t ext4 /dev/nbd0p1 /mnt

#### Run first-stage debootstrap for arm64

    debootstrap --arch=arm64 --keyring=/usr/share/keyrings/ubuntu-archive-keyring.gpg --verbose --foreign trusty /mnt/

#### Copy arm64 qemu binary into chroot

    cp /usr/bin/qemu-aarch64-static /mnt/usr/bin/

#### Go into chroot

    chroot /mnt/ /bin/bash

#### Run second stage of debootstrap while in chroot

    /debootstrap/debootstrap --second-stage

#### Add trusty arm64 ports to sources.list to install kernel in chroot

    deb http://ports.ubuntu.com/ubuntu-ports/ trusty main universe multiverse restricted
    deb http://ports.ubuntu.com/ubuntu-ports/ trusty-updates main universe multiverse restricted
    deb http://ports.ubuntu.com/ubuntu-ports/ trusty-security main universe multiverse restricted
    deb http://ports.ubuntu.com/ubuntu-ports/ trusty-proposed main universe multiverse restricted

    deb-src http://ports.ubuntu.com/ubuntu-ports/ trusty main universe multiverse restricted
    deb-src http://ports.ubuntu.com/ubuntu-ports/ trusty-updates main universe multiverse restricted
    deb-src http://ports.ubuntu.com/ubuntu-ports/ trusty-security main universe multiverse restricted
    deb-src http://ports.ubuntu.com/ubuntu-ports/ trusty-proposed main universe multiverse restricted

#### If installing Debian Jessie, the steps are similar

    qemu-img create -f qcow2 /srv/chroots/jessie.qcow2 60G
    modprobe -av nbd
    qemu-nbd -c /dev/nbd0 /srv/chroots/jessie.qcow2
    mkfs.ext4 /dev/nbd0p1
    mount -t ext4 /dev/nbd0p1 /mnt
    apt-get install debian-archive-keyring
    apt-key add /usr/share/keyrings/debian-archive-keyring.gpg
    mkdir -p /mnt/usr/bin
    cp /usr/bin/qemu-aarch64-static /mnt/usr/bin/
    debootstrap  --arch=arm64 --keyring /usr/share/keyrings/debian-archive-keyring.gpg  --exclude=debfoster jessie /mnt/ http://ftp.debian.org/debian
    chroot /mnt/ /bin/bash

#### Add jessie arm64 ports to sources.list to install kernel in chroot

    deb http://ftp.debian.org/debian/ jessie main contrib non-free
    deb http://ftp.debian.org/debian/ jessie-updates main contrib non-free

    deb-src http://ftp.debian.org/debian/ jessie main contrib non-free
    deb-src http://ftp.debian.org/debian/ jessie-updates main contrib non-free

#### Continue here for both Ubuntu and Debian
#### update apt sources

    apt-get update

#### Install kernel and headers
  - Ubuntu

        apt-get install linux-generic linux-headers-generic

  - Debian

        apt-get install linux-image-arm64 linux-headers-arm64

#### Exit chroot

#### Copy linux kernel and initrd from chroot to /srv/chroots/ on hypervisor

    cp /mnt/boot/vmlinux* /srv/chroots/
    cp /mnt/boot/initrd* /srv/chroots/

#### Umount chroot

    umount /mnt

#### Start up the VM in ro mode first without an initrd so we can get to a rescue shell to finish configuration

    /usr/local/bin/qemu-system-aarch64 -cpu cortex-a57 -machine type=virt -nographic -smp 1 -m 8192 -kernel /srv/chroots/vmlinuz-3.13.0-34-generic -drive file=/srv/chroots/trusty.qcow2,if=none,id=blk -device virtio-blk-device,drive=blk -device virtio-net-device,netdev=net0,mac=00:00:00:00:00:00 -netdev tap,id=net0 --append "root=/dev/vda1 ro console=ttyAMA0 --"

#### The boot will halt with an error about not being about to mount filesystems, choose continue to drop to a rescue shell and 
    remount / as rw
    mount -o remount,rw /

#### Set hostname to something unique

    hostname NEWHOSTNAME

#### Setup networking with DHCP

    dhclient eth0

#### Setup a swap file so we don't run out of memory

    dd if=/dev/zero of=/swapfile bs=1M count=4096
    chmod 600 /swapfile
    mkswap /swapfile
    swapon /swapfile

#### Install ssh and other packages
  - Ubuntu

        apt-get install ssh openssh-server perl netcat netcat6 bind9utils dnsutils libio-socket-ssl-perl libnet-ssleay-perl ldap-utils libtime-modules-perl lsb sysv-rc-conf dkms linux-headers-generic make bzip2 git curl build-essential

  - Debian

        apt-get install ssh openssh-server perl netcat netcat6 bind9utils dnsutils libio-socket-ssl-perl libnet-ssleay-perl ldap-utils libtime-modules-perl lsb sysv-rc-conf dkms linux-headers-arm64 make bzip2 git curl build-essential

#### Test sshd starts and enable on boot

    sudo service ssh start
    update-rc.d ssh defaults

#### Additional Configuration
  * add swap to /etc/fstab if desired
  * set root password and user if desired
  * set networking to static if desired

#### halt VM

    halt -p

#### Start VM with 8GB of memory, bridge networking, initrd, and rw / which will fully boot

    /usr/local/bin/qemu-system-aarch64 -cpu cortex-a57 -machine type=virt -nographic -smp 1 -m 8192 -kernel /srv/chroots/vmlinuz-3.13.0-34-generic -initrd /srv/chroots/initrd.img-3.13.0-34-generic -drive file=/srv/chroots/trusty.qcow2,if=none,id=blk -device virtio-blk-device,drive=blk -device virtio-net-device,netdev=net0,mac=00:00:00:00:00:00 -netdev tap,id=net0 --append "root=/dev/vda1 rw console=ttyAMA0 --"

#### Log in via ssh as root or user and use system normally

## References
- http://www.bennee.com/~alex/blog/2014/05/09/running-linux-in-qemus-aarch64-system-emulation-mode/
- https://gmplib.org/~tege/qemu.html