Is there any prebuilt QEMU Ubuntu image(32bit) online?
A quick Google search revealed the following (I have not tried any of them):
- stacklet.com
- Ubuntu Cloud Images
Also, you can use vmbuilder(referred here as ubuntu-vmbuilder
) to quickly create Ubuntu images to KVM, VirtualBox, etc.
As a last resort, you can use qemu-img
command to convert disk images from VirtualBox/VMware to a format more suited to QEMU/KVM (this may not be needed: I think QEMU/KVM can work with other image types like vdi or vmdk).
$ qemu-img convert -f [vdi|vmdk|...] -O qcow2 OriginalImage NewImage
NOTE: If you are using a 32bit OS you cannot run 64bit virtual machines with KVM. But QEMU is an emulator, so it should let you run 64bit vm's on a 32bit operating system. But the performance overhead will probably be huge!
This answer contains detailed steps for the following setups:
- cloud image amd64 and arm64
- debootstrap amd64 and arm64
- desktop image amd64
All was tested on an Ubuntu 18.04 host targeting 18.04 guests.
Cloud image amd64
The Ubuntu cloud images are pre-installed images that allow your to boot directly without doing the usual desktop system installation. See also: https://serverfault.com/questions/438611/what-are-ubuntu-cloud-images
#!/usr/bin/env bash
sudo apt-get install cloud-image-utils qemu
# This is already in qcow2 format.
img=ubuntu-18.04-server-cloudimg-amd64.img
if [ ! -f "$img" ]; then
wget "https://cloud-images.ubuntu.com/releases/18.04/release/${img}"
# sparse resize: does not use any extra space, just allows the resize to happen later on.
# https://superuser.com/questions/1022019/how-to-increase-size-of-an-ubuntu-cloud-image
qemu-img resize "$img" +128G
fi
user_data=user-data.img
if [ ! -f "$user_data" ]; then
# For the password.
# https://stackoverflow.com/questions/29137679/login-credentials-of-ubuntu-cloud-server-image/53373376#53373376
# https://serverfault.com/questions/920117/how-do-i-set-a-password-on-an-ubuntu-cloud-image/940686#940686
# https://askubuntu.com/questions/507345/how-to-set-a-password-for-ubuntu-cloud-images-ie-not-use-ssh/1094189#1094189
cat >user-data <<EOF
#cloud-config
password: asdfqwer
chpasswd: { expire: False }
ssh_pwauth: True
EOF
cloud-localds "$user_data" user-data
fi
qemu-system-x86_64 \
-drive "file=${img},format=qcow2" \
-drive "file=${user_data},format=raw" \
-device rtl8139,netdev=net0 \
-enable-kvm \
-m 2G \
-netdev user,id=net0 \
-serial mon:stdio \
-smp 2 \
-vga virtio \
;
GitHub upstream.
After QEMU starts, you might have to hit enter for the boot menu to show. Select Ubuntu
from there.
Then, the beginning of boot says:
error: no such device: root.
Press any key to continue...
but even if you don't press any key, boot continues after a short timeout. Go upvote this bug report: https://bugs.launchpad.net/cloud-images/+bug/1726476
Once boot finishes, login with:
- username:
ubuntu
- password:
asdfqwer
Internet works normally.
Cloud image arm64
TODO: I noticed that there is a bug that happens sometimes when using this: https://bugs.launchpad.net/cloud-images/+bug/1818197
Very similar to amd64, but we need some UEFI black magic for it to boot.
sudo apt-get install cloud-image-utils qemu-system-arm qemu-efi
# Get the image.
img=ubuntu-18.04-server-cloudimg-arm64.img
if [ ! -f "$img" ]; then
wget "https://cloud-images.ubuntu.com/releases/18.04/release/${img}"
qemu-img resize "$img" +128G
fi
# For the password.
user_data=user-data.img
if [ ! -f "$user_data" ]; then
cat >user-data <<EOF
#cloud-config
password: asdfqwer
chpasswd: { expire: False }
ssh_pwauth: True
EOF
cloud-localds "$user_data" user-data
# Use the EFI magic. Picked up from:
# https://wiki.ubuntu.com/ARM64/QEMU
dd if=/dev/zero of=flash0.img bs=1M count=64
dd if=/usr/share/qemu-efi/QEMU_EFI.fd of=flash0.img conv=notrunc
dd if=/dev/zero of=flash1.img bs=1M count=64
fi
qemu-system-aarch64 \
-M virt \
-cpu cortex-a57 \
-device rtl8139,netdev=net0 \
-m 4096 \
-netdev user,id=net0 \
-nographic \
-smp 4 \
-drive "if=none,file=${img},id=hd0" \
-device virtio-blk-device,drive=hd0 \
-drive "file=${user_data},format=raw" \
-pflash flash0.img \
-pflash flash1.img \
;
GitHub upstream.
debootstrap
amd64
Not a pre-made image, but it downloads all pre-built packages, so it is also fast, but also much more configurable and useful.
#!/usr/bin/env bash
set -eux
debootstrap_dir=debootstrap
root_filesystem=debootstrap.ext2.qcow2
sudo apt-get install \
debootstrap \
libguestfs-tools \
qemu-system-x86 \
;
if [ ! -d "$debootstrap_dir" ]; then
# Create debootstrap directory.
# - linux-image-generic: downloads the kernel image we will use under /boot
# - network-manager: automatically starts the network at boot for us
sudo debootstrap \
--include linux-image-generic \
bionic \
"$debootstrap_dir" \
http://archive.ubuntu.com/ubuntu \
;
sudo rm -f "$root_filesystem"
fi
linux_image="$(printf "${debootstrap_dir}/boot/vmlinuz-"*)"
if [ ! -f "$root_filesystem" ]; then
# Set root password.
echo 'root:root' | sudo chroot "$debootstrap_dir" chpasswd
# Remount root filesystem as rw.
cat << EOF | sudo tee "${debootstrap_dir}/etc/fstab"
/dev/sda / ext4 errors=remount-ro,acl 0 1
EOF
# Automaticaly start networking.
# Otherwise network commands fail with:
# Temporary failure in name resolution
# https://askubuntu.com/questions/1045278/ubuntu-server-18-04-temporary-failure-in-name-resolution/1080902#1080902
cat << EOF | sudo tee "$debootstrap_dir/etc/systemd/system/dhclient.service"
[Unit]
Description=DHCP Client
Documentation=man:dhclient(8)
Wants=network.target
Before=network.target
[Service]
Type=forking
PIDFile=/var/run/dhclient.pid
ExecStart=/sbin/dhclient -4 -q
[Install]
WantedBy=multi-user.target
EOF
sudo ln -sf "$debootstrap_dir/etc/systemd/system/dhclient.service" \
"${debootstrap_dir}/etc/systemd/system/multi-user.target.wants/dhclient.service"
# Why Ubuntu, why.
# https://bugs.launchpad.net/ubuntu/+source/linux/+bug/759725
sudo chmod +r "${linux_image}"
# Generate image file from debootstrap directory.
# Leave 1Gb extra empty space in the image.
sudo virt-make-fs \
--format qcow2 \
--size +1G \
--type ext2 \
"$debootstrap_dir" \
"$root_filesystem" \
;
sudo chmod 666 "$root_filesystem"
fi
qemu-system-x86_64 \
-append 'console=ttyS0 root=/dev/sda' \
-drive "file=${root_filesystem},format=qcow2" \
-enable-kvm \
-serial mon:stdio \
-m 2G \
-kernel "${linux_image}" \
-device rtl8139,netdev=net0 \
-netdev user,id=net0 \
;
GitHub upstream.
This boots without any systemd errors or warnings.
Now from the terminal, login with root
/ root
, and then check that the Internet works with the following commands:
printf 'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n' | nc example.com 80
apt-get update
apt-get install hello
hello
We used nc
as explained at https://stackoverflow.com/questions/32341518/how-to-make-an-http-get-request-manually-with-netcat/52662497#52662497 because:
wget
andcurl
are not installed by defaultping
does not work from QEMU by default: https://unix.stackexchange.com/questions/473448/how-to-ping-from-the-qemu-guest-to-an-external-url
Analogous Debian version: https://unix.stackexchange.com/questions/275429/creating-bootable-debian-image-with-debootstrap/473256#473256
Build your own kernel
Since we are here:
git clone git://kernel.ubuntu.com/ubuntu/ubuntu-bionic.git
cd ubuntu-bionic
# Tag matches the working kernel that debootstrap downloaded for us.
git checkout Ubuntu-4.15.0-20.21
fakeroot debian/rules clean
debian/rules updateconfigs
fakeroot debian/rules build-generic
linux_image="$(pwd)/debian/build/build-generic/arch/x86_64/boot/bzImage"
This produced the exact same config and I believe used the exact same source code as the packaged Ubuntu that debootstrap
downloaded as explained at: Where can I get the 11.04 kernel .config file?
Then I patched it with:
diff --git a/init/main.c b/init/main.c
index b8b121c17ff1..542229349efc 100644
--- a/init/main.c
+++ b/init/main.c
@@ -516,6 +516,8 @@ asmlinkage __visible void __init start_kernel(void)
char *command_line;
char *after_dashes;
+ pr_info("I'VE HACKED THE LINUX KERNEL!!!");
+
set_task_stack_end_magic(&init_task);
smp_setup_processor_id();
debug_objects_early_init();
and rebuild:
fakeroot debian/rules build-generic
and it did print my message during boot:
I'VE HACKED THE LINUX KERNEL!!!
The rebuild was not very fast though, so maybe there is a better command? I just waited for it to say:
Kernel: arch/x86/boot/bzImage is ready (#3)
and went ahead with the run.
debootstrap arm64
The procedure was similar to the amd64 one, but with the following differences:
1)
We must do a two stage debootstrap
:
- first with
--foreign
to just download the packages - then we install QEMU static into the
chroot
- then we do the package installation with
--second-stage
using QEMU user mode emulation +binfmt_misc
See also: What is debootstrap --second-stage for
the default kernel boot fails at the end with:
[ 0.773665] Please append a correct "root=" boot option; here are the available partitions: [ 0.774033] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
The empty partition list indicates that there is a serious error with the disk driver, after a bit of trying the missing option is:
CONFIG_VIRTIO_BLK=y
I think it works when I use the ISO because the modules must get loaded from the initrd.
I tried to use other disk types, but virtio is the only valid value for -drive if=
when -M virt
, which is the saner machine type nowadays.
Therefore we must recompile our own kernel with that option enabled, as explained here: When cross-compiling the kernel, how can I stop it making from clean every time when I just want to modify one file?
Ubuntu devs should turn this CONFIG y
by default! It is very useful!
TODO: the network is not working, the error message is:
root@ciro-p51:~# systemctl status dhclient.service
root@ciro-p51:~# cat f
● dhclient.service - DHCP Client
Loaded: loaded (/etc/systemd/system/dhclient.service; enabled; vendor preset: enabled)
Active: failed (Result: protocol) since Sun 2018-01-28 15:58:42 UTC; 2min 2s ago
Docs: man:dhclient(8)
Process: 171 ExecStart=/sbin/dhclient -4 -q (code=exited, status=0/SUCCESS)
Jan 28 15:58:40 ciro-p51 systemd[1]: Starting DHCP Client...
Jan 28 15:58:42 ciro-p51 dhclient[171]: No broadcast interfaces found - exiting.
Jan 28 15:58:42 ciro-p51 systemd[1]: dhclient.service: Can't open PID file /var/run/dhclient.pid (yet?) after start: No such file or directory
Jan 28 15:58:42 ciro-p51 systemd[1]: dhclient.service: Failed with result 'protocol'.
Jan 28 15:58:42 ciro-p51 systemd[1]: Failed to start DHCP Client.
Here is the fully automated script:
#!/usr/bin/env bash
# https://askubuntu.com/questions/281763/is-there-any-prebuilt-qemu-ubuntu-image32bit-online/1081171#1081171
set -eux
debootstrap_dir=debootstrap
root_filesystem=debootstrap.ext2.qcow2
sudo apt-get install \
gcc-aarch64-linux-gnu \
debootstrap \
libguestfs-tools \
qemu-system-aarch64 \
qemu-user-static \
;
if [ ! -d "$debootstrap_dir" ]; then
sudo debootstrap \
--arch arm64 \
--foreign \
bionic \
"$debootstrap_dir" \
http://ports.ubuntu.com/ubuntu-ports \
;
sudo mkdir -p "${debootstrap_dir}/usr/bin"
sudo cp "$(which qemu-aarch64-static)" "${debootstrap_dir}/usr/bin"
sudo chroot "$debootstrap_dir" /debootstrap/debootstrap --second-stage
sudo rm -f "$root_filesystem"
fi
linux_image="$(printf "${debootstrap_dir}/boot/vmlinuz-"*)"
if [ ! -f "$root_filesystem" ]; then
# Set root password.
echo 'root:root' | sudo chroot "$debootstrap_dir" chpasswd
# Remount root filesystem as rw.
cat << EOF | sudo tee "${debootstrap_dir}/etc/fstab"
/dev/sda / ext4 errors=remount-ro,acl 0 1
EOF
# Automaticaly start networking.
# Otherwise network commands fail with:
# Temporary failure in name resolution
# https://askubuntu.com/questions/1045278/ubuntu-server-18-04-temporary-failure-in-name-resolution/1080902#1080902
cat << EOF | sudo tee "${debootstrap_dir}/etc/systemd/system/dhclient.service"
[Unit]
Description=DHCP Client
Documentation=man:dhclient(8)
Wants=network.target
Before=network.target
[Service]
Type=forking
PIDFile=/var/run/dhclient.pid
ExecStart=/sbin/dhclient -4 -q
[Install]
WantedBy=multi-user.target
EOF
sudo ln -sf "${debootstrap_dir}/etc/systemd/system/dhclient.service" \
"${debootstrap_dir}/etc/systemd/system/multi-user.target.wants/dhclient.service"
# Why Ubuntu, why.
# https://bugs.launchpad.net/ubuntu/+source/linux/+bug/759725
sudo chmod +r "${linux_image}"
# Generate image file from debootstrap directory.
# Leave 1Gb extra empty space in the image.
sudo virt-make-fs \
--format qcow2 \
--size +1G \
--type ext2 \
"$debootstrap_dir" \
"$root_filesystem" \
;
sudo chmod 666 "$root_filesystem"
fi
# Build the Linux kernel.
linux_image="$(pwd)/linux/debian/build/build-generic/arch/arm64/boot/Image"
if [ ! -f "$linux_image" ]; then
git clone --branch Ubuntu-4.15.0-20.21 --depth 1 git://kernel.ubuntu.com/ubuntu/ubuntu-bionic.git linux
cd linux
patch -p1 << EOF
diff --git a/debian.master/config/config.common.ubuntu b/debian.master/config/config.common.ubuntu
index 5ff32cb997e9..8a190d3a0299 100644
--- a/debian.master/config/config.common.ubuntu
+++ b/debian.master/config/config.common.ubuntu
@@ -10153,7 +10153,7 @@ CONFIG_VIDEO_ZORAN_ZR36060=m
CONFIG_VIPERBOARD_ADC=m
CONFIG_VIRTIO=y
CONFIG_VIRTIO_BALLOON=y
-CONFIG_VIRTIO_BLK=m
+CONFIG_VIRTIO_BLK=y
CONFIG_VIRTIO_BLK_SCSI=y
CONFIG_VIRTIO_CONSOLE=y
CONFIG_VIRTIO_INPUT=m
EOF
export ARCH=arm64
export $(dpkg-architecture -aarm64)
export CROSS_COMPILE=aarch64-linux-gnu-
fakeroot debian/rules clean
debian/rules updateconfigs
fakeroot debian/rules DEB_BUILD_OPTIONS=parallel=`nproc` build-generic
cd -
fi
qemu-system-aarch64 \
-append 'console=ttyAMA0 root=/dev/vda rootfstype=ext2' \
-device rtl8139,netdev=net0 \
-drive "file=${root_filesystem},format=qcow2" \
-kernel "${linux_image}" \
-m 2G \
-netdev user,id=net0 \
-serial mon:stdio \
-M virt,highmem=off \
-cpu cortex-a57 \
-nographic \
;
GitHub Upstream.
Desktop image
See: How to run Ubuntu desktop on QEMU?
It does require going through the installer manually, but it is the most stable thing you can possibly do, and totally fine if you just want to get a VM for interactive use running from time to time.
For aarch64, I haven't gotten the desktop working yet maybe keep an eye out for: How to run Ubuntu 16.04 ARM in QEMU?
Debian aarch64
https://gist.github.com/philipz/04a9a165f8ce561f7ddd and change sudo mount /dev/nbd0p2
to nbd0p1
.