Fiddling with Ubuntu Server Images
Oddly enough, the latest version of Ubuntu Server failed to
install on my MacBook Pro (2012). It’s due to some bug in
efibootmgr
. Ubuntu Server Installer uses
efibootmgr
to create some NVRAM variables for the
UEFI boot process. Unfortunately, efibootmgr
failed
right in the middle and printed an empty string to stderr,
abruptly ending the installation process.
To fix this, I hypothesized that I could somehow redirect the
command’s stderr to /dev/null
using a file
descriptor redirect. So, no error would be printed and the
installation would complete as usual. But, when further
inspecting the image, I couldn’t seem to find the installation
scripts to modify.
I decided to sidestep the issue by installing Debian Stretch
instead. Because I used the ‘CD installer’ (rather than the DVD
installer), many of the ‘popular packages’ were not
pre-installed. I underestimated the limitations of this setup,
and I bailed out of setting up Debian. I only bailed out after
manually configuring the network, when
systemd-resolved
did not seem to be reading the
DNS=
entry in my
systemd.network (5)
configuration file.
I then decided to retry installing Ubuntu, but by using an Ubuntu Cloud image because I knew it did not have an installation wizard.
Copying the rootfs
I installed the Ubuntu Cloud image from the official source.
wget https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img
Because the downloaded image is in qcow
format, I
converted into a raw
format. (you can use
qemu-img info
to query for this information).
qemu-img convert -p \
-f qcow2 bionic-server-cloudimg-amd64.img \
-O raw bionic.img \
Because we will be accessing the filesystem as a block device, we mount the file to a loop device.
sudo losetup -fP bionic.img
Now, we can finally dd
the data to an actual
partition.
sudo dd if=/dev/loop7p1 of=/dev/vg/ubuntu-server \
status=progress conv=fsync bs=2048
After this process, I remounted the partition, chrooted into it, and added a password for the root user.
Making the Server Bootable
The distribution’s files for the EFI System Partition (ESP) are
in dev/loop7p3
(or your equivalent). It only
contains grub-related files. Rather than simply copying the grub
files over and manually creating an NVRAM varaible entry for
UEFI, I simply created an \EFI\ubuntu-server
folder
in my ESP, and moved the bootloader and EFI stub kernel into it,
from /boot/
. I then created an fstab entry to mount
the ESP to /efi
, and from that, bind mount
/efi/EFI/ubuntu-server
to /boot
and
bind mount /efi
to /boot/efi
. At the
time, I crossed my fingers that nothing related to updating the
kernels and initramfs required features not supported by vfat,
such as symlinks (spoiler alert: it does).
Now we have all the files needed to boot, we just have to tell our boot loader (or boot manager) to use them. Because I was already using an SSD with Refind installed, I added an entry for my Ubuntu Server. I did not configure GRUB.
menuentry ubuntu-server {
loader \EFI\ubuntu-server\vmlinuz
initrd \EFI\ubuntu-server\initrd.img
options "root=UUID=65e98e43-8591-48f8-8746-34c8b3819ee7 rw"
submenuentry "boot to tty" {
add_options "systemd.unit=multi-user.target"
}
submenuentry "boot fallback initramfs" {
initrd /boot/initrd.img.old
}
submenuentry "boot fallback kernel" {
loader /boot/vmlinuz.old
}
}
When rebooting, I was pretty certain I did not need to do any more work and that I was almost finished with the setup. The computer booted properly, getty opened some virtual terminals, and login ran without any hiccups. However, I was unable to type at all. My laptop keyboard simply did not work. However, I was able to login with a nearby bluetooth keyboard!
After poking around the system, I found the problem.
ls -alF "/lib/modules/$(uname -r)/kernel/drivers/hid"
showed me that there were almost no human interface driver kernel
modules that were loaded. Of course, this is an Ubuntu cloud
image, so it makes sense that login can only be done through
serial and ssh.
Anyways, to fix this, I simply downloaded the
drivers/hid/hid-apple.c
kernel driver (along with
drivers/hid/hid-ids.h
), placing them in a
subdirectory.
I then ran a Makefile with the following content
obj-m += hid-apple.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
This built the proper modules. After inserting the modules into
the kernel, it worked flawlessly! Hooray! 🎉 I added the modules
to /lib/modules/$(uname -r)/updates
, and did some
other miscellaneous cleanup. Of course, if I try to boot the
system from a different computer, the keyboard will likely not
work.
A few other problems later arose. Remember I added the boot files
in a directory of the ESP? It turned out symlinks are used in the
kernel upgrade process, so I had to recreate a
/boot
partition for my Ubuntu server, copy over
everything, and update my /etc/fstab
.
If I were to do this again, I would look into creating a preseed file, (which I did not know about at the beginning of this process), and optionally use packer to uniformly perform the installation process in a virtual machine.