In this guide we see how to install Debian on a Qemu MIPS virtual machine. The tutorial assumes to work on a Debian-based Linux distribution (though it's really easy to adapt to others) and requires basic shell skills.
- Dependencies and preparation
- Debian installation
- Update the initrd
- Create the startup script
- Post installation
- Create a shared directory
-
Install Qemu and the other dependencies with
apt
:sudo apt install -y qemu qemu-system-mips wget
See the wiki page for more info on installing Qemu.
-
Create our working directory an
cd
to it.mkdir debian-mips # you might choose any name you like ... cd debian-mips
-
We shall perform a netboot based installation, so we need to get the initrd file and the kernel image. The initrd (initial RAM disk) will be used by the kernel as a root file system and, basically, contains our Debian installer. The kernel image is the one provided for the MIPS Malta board. We'll use the mipsel architecture (i.e. little endian MIPS, which has a bit more extensive support), but you may choose any other on your discretion.
# download initrd and kernel image wget -r -np -nd -A 'initrd.gz' -A 'vmlinu?-*-malta' 'http://ftp.debian.org/debian/dists/testing/main/installer-mipsel/current/images/malta/netboot/'
-
Create a virtual disk image to use for the installation. For the installation we must ensure a minimum size of about 1GB, but a bit more for our work won't hurt.
# creates a virtual disk of 4GB in qcow2 format qemu-img create -f qcow2 hda.img 4G
-
Boot the machine and launch the installer
qemu-system-mipsel \ -M malta \ -m 512 \ -hda hda.img \ -kernel vmlinu?-*-malta \ -initrd initrd.gz \ -nographic \ -append "console=ttyS0 nokaslr"
-M malta
: board: Malta-m 512
: main memory (RAM): 512MB-hda hda.img
: disk: the disk image we just created-kernel vmlinux-*-malta
: kernel: the image of the kernel we just downloaded-initrd initrd.gz
: initrd: the initrd we just downloaded-nographic
we don't need GUIs-append "console=ttys0 nokaslr"
: additional kernel parameters: we'll need a console, address space layout randomization will not be necessary
-
Proceed with the installation. At this point the Debian installer should start and the installation of Debian should proceed on the virtual machine. A number of prompts will be presented:
- don't install a desktop environment, but make sure to install the ssh server (we'll need it for remote access to the machine)
- choose a root password and create a user. Let us assume that we created a user named
mips1
- Once the installation is completed and we turn off the machine, we need to change the initrd before restarting everything otherwise the installer will be run again upon boot. The initrd we need is the one that the installation procedure created within the boot partition of our virtual disk image. We need to fetch that and use it in our launching script. To access the virtual disk image we shall use the NBD protocol, supported by Qemu. The steps are as follows:
sudo modprobe nbd max_part=63 # enable the ndb kernel module on the host, set max partitions number to 63 sudo qemu-nbd -c /dev/nbd0 hda.img # export the virtual disk to a nbd device in our local file system sudo mount /dev/nbd0p1 /mnt # mount the disk's boot partition under /mnt so we can access it cp /mnt/boot/initrd.img-*-malta ./ # copy the initrd from the boot partition into the working directory sudo umount /mnt # unmount the partition sudo qemu-nbd -d /dev/nbd0 # detatch the nbd device
- We are ready to prepare a script for starting the virtual machine, let's save it in a file called
start.sh
that we shall place in our working directory:#!/bin/sh qemu-system-mipsel \ -M malta \ -m 512 \ -hda hda.img \ -kernel vmlinu?-*-malta \ -initrd initrd.img-*-malta \ -nographic \ -append "root=/dev/sda1 console=ttyS0 nokaslr" \ -net user,hostfwd=tcp::10022-:22 \ -net nic
- the
-initrd
option we now have the new fetched initrd - the
-append
option now includes the main disk - the last two options create a virtual network card to connect between host and guest and to the internet. It has the port 22, the ssh server default, mapped to the host's port 10022.
- By running the above script, Debian OS should boot and we should see the command prompt. Thanks to the port mapping we applied in the script, we can ssh to the machine from a new local terminal:
ssh -p 10022 mips1@localhost
Upon login we will be presented with our freshly installed Debian, let's take care of some basic tools that will come handy:
su # become root
apt install sudo # install sudo
/sbin/usermod -aG sudo mips1 # add user mips1 to the sudoers group
After logging out and back in, we can use sudo to run commands with superuser rights:
sudo echo "I am (g)root" # test if sudo works
sudo apt install vim # let's install our favorite text editor
sudo apt install build-essential # installs various compiling tools including gcc
This section is completely optional and lets you set up a shared directory between your host and the MIPS VM by mounting a 9p filesystem using the 9p network protocol. Doing this is actually surprisingly simple and can be done with only a couple of tweaks. Among other benefits, a shared directory allows to code using your preferred editor and configuration (including GUI applications which are hard to use on a VM of this kind) and to do so while having bare-metal performance and still being able to access the edited files from within the VM. Obviously, you'll have to run your final product on the VM since you're programming in MIPS assembly (unless you're using a physical MIPS machine, in which case this guide is completely pointless to you). In theory, you could also compile and link your code from the host machine, but doing so would require setting up assembler and linker to build something for a MIPS architecture and it's generally easier to just do it in the VM.
An in-depth guide to virtual filesystems using 9p on Qemu is available in the official documentation page.
You can setup a 9p directory sharing between host and guest by simply appending this option to the start.sh
script Qemu command:
-virtfs local,path=<path/to/directory>,mount_tag=<tag>,security_model=mapped-xattr
Where <path/to/directory>
the relative or absolute path to the shared directory on the host and <tag>
is a tag of your choice.
Once booted the VM, you can mount the shared directory similarly to what you would do with a normal volume:
mount -t 9p -o trans=virtio,version=9p2000.L,msize=50000 <tag> <mountpoint>
Or by adding the following entry to /etc/fstab
, which will automatically mount the drive at boot time.
<tag> <mountpoint> 9p trans=virtio,version=9p2000.L,_netdev,msize=50000 0 0
Note that msize
should be set depending on the physical drive on which the shared directory resides, as described in the documentation.
- The
version
parameter specifies, as you might have guessed, the version of the protocol (if omitted, the version is9P2000.u
). More information on the version can be found in the documentation of the implementation Diod and its bibliography. - In the fstab entry, the
_netdev
option specifies that the guest must wait for the network libraries to load before attempting to mount the drive. This is necessary since the 9p libraries aren't loaded right away. While this is by far the easiest solution, you could also compile the initrd so that these libraries are instantly loaded, as described in this Superuser answer, and then replace the old initrd with the new one instart.sh
. - You might get the error "Socket operation on non socket" when trying to run executables on the shared volume. More detail here.