Monday December 10 2018
Booting MS Windows, FreeBSD, and Linux off of the same drive.
First off, you’re probably thinking “WHY!?” The answer? Because I can. Also, because Virtualization sucks. Plus my laptop only has room for one 2.5” SATA 3 (6gb/s) drive, or a slow SATA 2 (3gb/s) mSATA drive–which I do not plan on using
Why does virtualization suck?
Well let’s say I want to run Windows for a particular piece of software, and it’s resource intensive. CPU wise you’re going to end up with a pretty decent performance hit if you’re running it in a VM. Say nothing of the almost non-existant support for accelerated graphics inside of a virtual machine if I dare try to do something graphics intensive such as play a video game.
Next up is networking, if you’re accustomed to most major desktop virtualization software you might know that it does some NAT trickery to make it “just work”–that is until it doesn’t, or need say IPv6. It’s a disaster and doesn’t solve the problem of having incompatible or poorly written non-portable software in the first place. Something I cover in more depth in future posts, so stay tuned.
Moving on,
What is covered here is setting up a laptop with a single disk to boot Windows, FreeBSD, and Linux via UEFI using the GPT partitioning scheme.
Installing Microsoft Windows
Fresh MS Windows install. I’m sure you’ll be able to handle this one. Just be sure to install it in GPT/UEFI mode. An easy way to do this is to download the ISO from Microsoft, and then write it to a flash drive ( be sure to select UEFI only mode ) via the Rufus tool.
Be sure to set the partition size accordingly and leave plenty of room for the other operating systems. Windows requires about 60GB minimum these days. I just gave it 200gb on my X230’s 1TB ssd.
Once it’s installed you may wish to set the system time to use UTC–especially if you’re installing a lot of Unix-like operating systems that expect that functionality.
Simply write this out to a registry file:
utc.reg
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation]
"RealTimeIsUniversal"=dword:00000001
You should then be able to double click on it, ignore the warning and install it.
Installing FreeBSD
This one is a bit tricky. The quick version is that you’ll boot to the install medium and perform a by hand installation.
The partitioning is the only real gotcha, so-to-speak:
# gpart show ada0
=> 34 1953525101 ada0 GPT (932G)
34 2014 - free - (1.0M)
2048 1021952 1 ms-recovery (499M)
1024000 204800 2 efi (100M)
1228800 32768 3 ms-reserved (16M)
1261568 410118144 4 ms-basic-data (196G)
411379712 2097152 5 freebsd-ufs (1.0G)
413476864 1048576000 6 freebsd-zfs (500G)
Above you can see how my partitions are setup, I was able to create them via:
# gpart add -t freebsd-ufs -s 1G ada0
# gpart add -t freebsd-zfs -s 500G ada0
Then (optionally) encrypt and create your filesystems:
# geli init -b -e AES-XTS -l 256 -s 4096 /dev/ada0p6
# geli attach /dev/ada0p6
# zpool create -O compress=lz4 -O canmount=off -R /mnt zroot ada0p6.eli
# zfs create -o canmount=off zroot/ROOT
# zfs create -o mountpoint=/ zroot/ROOT/default
# zfs create -o mountpoint=/home zroot/home
# for _n in usr var ; do
zfs create -o canmount=off -o mountpoint=/$_n zroot/$_n
done
# zfs create zroot/usr/ports
# zfs create zroot/var/log
# newfs -L boot /dev/ada0p5
Then go ahead and mount the filesystems:
# mkdir /mnt/boot_real && mount /dev/ufs/boot /mnt/boot_real
# tar -C /mnt -xJf /usr/freebsd-dist/base.txz
# tar -C /mnt -xJf /usr/freebsd-dist/kernel.txz
# mv /mnt/boot /mnt/boot_real/ && ln -s boot_real/boot /mnt/boot
# mkdir /mnt/boot/efi && mount -t msdosfs /dev/ada0p2 /mnt/boot/efi
Move Microsoft’s main bootloader out of the way, we’re going to use this to get going with FreeBSD. ( You’ll still be able to select Windows from the boot menu as it writes an EFI bootloader variable )
# mv /mnt/boot/efi/EFI/Boot/bootx64.efi /mnt/boot/efi/EFI/Boot/msft_bootx64.efi
# cp /mnt/boot/boot1.efi /mnt/boot/efi/EFI/Boot/bootx64.efi
Adjust boot/loader.conf
for our ZFS root and encrypted root partition:
# ed /mnt/boot/loader.conf
a
aesni_load="YES"
geom_eli_load="YES"
zfs_load="YES"
vfs.root.mountfrom="zfs:zroot/ROOT/default"
geom_eli_passphrase_prompt="YES"
.
w
q
#
Adjust /etc/rc.conf
in much the same way: ( #TODO: add other flags here, and
sysctl variables )
# ed /etc/rc.conf
a
hostname="$MY_HOSTNAME"
zfs_enable="YES"
.
w
q
#
Set our system’s timezone
# ln -s /usr/share/zoneinfo/America/Detroit /etc/localtime
Make sure our filesystems are in the etc/fstab
( Note that ZFS is not
managed through the fstab
)
# ed etc/fstab
a
/dev/ufs/boot /boot_real ufs rw 0 0
# Read only, if we want to reference it.
# Otherwise we can do `mount -u -o rw /boot/efi` to write to it
/dev/ada0p2 /boot/efi msdosfs ro 0 0
.
w
q
Finally,
# sync && reboot
And that should take care of most of the install of FreeBSD. There are still a lot of other little pieces that need to be put in place if you want a usable desktop experience. Stay tuned for that post as well.
Installing Linux
These days I find myself using Gentoo most of the time. It’s stable, easy
to specify the build options I want, it’s not subject to the bloat of
systemd
and binary package are trivial.
For my x230 I purposely wanted to do something different I’ve used Void
Linux in the past for a few things. It’s a somewhat unique distribution that
uses runit
as the default init scheme, libressl
as the TLS implementation,
and optionally musl
as the system’s libc. I’m currently using the musl
libc variant although the setup should be similar if you go with glibc
First boot to some sort of Linux distribution, this actually matters very little as we’ll be installing via a chroot.
Partition the disk
# gdisk -l /dev/sda
GPT fdisk (gdisk) version 1.0.4
Partition table scan:
MBR: protective
BSD: not present
APM: not present
GPT: present
Found valid GPT with protective MBR; using GPT.
Disk /dev/sda: 1953525168 sectors, 931.5 GiB
Model: CT1000MX500SSD1
Sector size (logical/physical): 512/4096 bytes
Disk identifier (GUID): C0C481E6-25C4-4837-8610-3F2D5DCAE3B6
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 1953525134
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)
Number Start (sector) End (sector) Size Code Name
1 2048 1023999 499.0 MiB 2700 Basic data partition
2 1024000 1228799 100.0 MiB EF00 EFI system partition
3 1228800 1261567 16.0 MiB 0C01 Microsoft reserved ...
4 1261568 411379711 195.6 GiB 0700 Basic data partition
5 411379712 413476863 1024.0 MiB A503
6 413476864 1462052863 500.0 GiB A503
7 1462052864 1464150015 1024.0 MiB 8300
8 1464150016 1883580415 200.0 GiB 8300
Basically, create a 1GB boot partition and a 200GB partition for the OS much like you did with FreeBSD a moment ago.
Setup the filesystems, encryption, and LVM.
There will be free space in the volume group with the configuration below, this makes snapshotting easy.
# mkfs.ext4 /dev/sda7
# cryptsetup luksFormat /dev/sda8
# cryptsetup luksOpen /dev/sda8 root
# pvcreate /dev/mapper/root
# vgcreate vg /dev/mapper/root
# lvcreate vg --name root --size 10G
# lvcreate vg --name home --size 100G
# mkfs.xfs /dev/vg/root
# mkfs.xfs /dev/vg/home
Then go ahead and mount the filesystems:
# mount /dev/vg/root /mnt
# mkdir /mnt/{boot,home}
# mount /dev/vg/home /mnt/home
# mount /dev/sda7 /mnt/boot
# mkdir /mnt/boot/efi
# mount /dev/sda2 /mnt/boot/efi
Download and extract the minimal root filesystem
Be sure to check the website for a mirror
near you and change the URL accordingly. You might need to browse the
live/current/
path to figure out the current ROOTFS
tarball
# cd /mnt
# wget http://mirror.clarkson.edu/voidlinux/live/current/void-x86_64-musl-ROOTFS-20181111.tar.xz
# tar xJf void-*ROOTFS*
Chroot into the new system and setup the basics
# cd /mnt
# mount -t proc none proc
# mount -o bind /sys sys
# mount -o bind /dev dev
# chroot /mnt /bin/bash
bash-4.4# export PS1="(CHROOT) # "
Setup nameservers
(CHROOT) # echo nameserver 8.8.8.8 > /etc/resolv.conf
Setup the package repository to point at your local mirror
(CHROOT) # echo repository=http://mirror.clarkson.edu/voidlinux/current/musl \
> /etc/xbps.d/00-repository-main.conf
Install the base system, lvm2
, and a few other useful packages
(CHROOT) # xbps-install -Syu \
base-system lvm2 curl wget ed grub gptfdisk lzop cryptsetup \
lzip openvpn nmap pv rrdtool rsync sipcalc whois xz wireless_tools \
net-tools wpa_supplicant grub-x86_64-efi
Configure bootloader, initrd and /etc/fstab
Kind of a hack to setup the fstab
, but it works:
(CHROOT) # cat /proc/mounts | grep xfs >> /etc/fstab
The Void Linux kernel by default does not build in the necessary module to
handle booting off of a logical volume with associated snapshots. To make
matters worse, it does not load it automatically even if you put it in the
initial ramdisk. Thankfully dracut
has an option to force load kernel modules
Simply:
# echo 'force_drivers="dm_snapshot"' >> /etc/dracut.conf
Then go ahead and rebuild the initrd:
(CHROOT) # dracut -f --kver $(ls -t /lib/modules | sed 1q)
Now to check the generated initrd and make sure that it has LVM and Cryptsetup
(CHROOT) # lsinitrd /boot/initrd* | grep lvm >/dev/null || echo "No LVM support"
(CHROOT) # lsinitrd /boot/initrd* | grep cryptsetup >/dev/null || echo "No crypt support"
(CHROOT) #
Instlalling grub
It’s pretty straightforward:
(CHROOT) # grub-install --target=x86_64-efi --efi-directory=/boot/efi \
--bootloader-id void
Configuring grub
We’re going to need to tell the grub configuration generator how to get to the
filesystem. Adjust /etc/default/grub
like so:
GRUB_CMDLINE_LINUX_DEFAULT="rd.luks.uuid=$LUKS_UUID rd.luks.allow-discards rd.luks.crypttab=0"
where $LUKS_UUID
is actually your UUID, you can get this via:
(CHROOT) # blkid -o export /dev/sda8 | grep '^UUID'
You can leave or remove the extra arguments that are already there. ( Not
listed above) You can change the loglevel
option if you want to have a little
bit more verbose boot.
You will need to remove the rd.luks.allow-discards
if you’re not booting to a
solid state drive that supports trim. There are also some security concerns
with enabling it centering around the fact that it will espose what data
is and is not encrypted.
Finally, create the grub configuration file
(CHROOT) # grub-mkconfig -o /boot/grub/grub.cfg
Installing rEFInd
Now that we have more operating systems than we eaisly can select from the boot menu it’s about time we fix that problem by using a UEFI boot manager.
Why not just set some EFI variables and be done with it? Because UEFI is buggy. And on many systems it’s not that simple–or even respected.
I downloaded rEFInd from here I used the “A binary zip file”
I didn’t use the install scripts, instead I just copied the binary over to the EFI directory like so:
$ cd ~/Downloads/
$ bsdtar -xf refind-bin-0.11.4
$ cd refind-bin-0.11.4
$ sudo cp refind/refind_x64.efi /boot/efi/EFI/Boot/bootx64.efi
I then proceeded to create a configuration file like so:
timeout 20
menuentry Void {
loader /EFI/void/grubx64.efi
icon /EFI/refind/icons/os_void.png
}
menuentry FreeBSD {
loader /EFI/freebsd/bootx64.efi
icon /EFI/refind/icons/os_freebsd.png
}
menuentry "Windows 10" {
loader \EFI\Microsoft\Boot\bootmgfw.efi
icon \EFI\refind\icons\os_win.png
}
If you would like the icons and themes as mentioned above, then you can copy over the `refind/icons/’ folder:
$ sudo cp -R refind/icons /boot/efi/EFI/Boot/
If you’re on a system that supports EFI variables you can always add a special entry for it as well:
$ sudo -i
# cd /boot/efi/EFI
# mkdir refind
# cp -R Boot/* refind/
# efibootmgr --create --disk /dev/sda --part 2 --loader \
/EFI/refind/bootx64.efi --label "rEFInd Boot Manager"
This will obviously not carry over to other systems you install the
drive to so I highly recommend that you use the default EFI/Boot
directory.
Final thoughts
This is obviously not a common configuration. Very few people have any desire, let alone a need to run multiple operating systems on the same machine, let alone three of them.
I run Linux and FreeBSD production servers for some clients.
Although this configuration is not strictly necessary–I could always
login to another host via ssh
. It does offer a little bit more experience
with the operating system in question when you run it with a desktop
environment–especially on a laptop.
If I didn’t know how to setup this kind of thing I would be much worse at my job. When shit hits the fan, everything is broken–it’s 04:00 and you’re trying to restore from a backup after you repeatedly told the client to upgrade the hardware before it failed–you don’t have time to place blame or complain. You just have to figure out the damn bootloader and get the system running. Besides, clients are a lot more receptive to the expensive service call and a plan to prevent the problem in the future if you resolve it quickly and without much fuss.
So go on, mess with your systems, multi boot. You’ll learn something and it might come in handy if you work on these kinds of things.
If anyone is interested in how my desktop is setup, feel free to send me a
message and I’ll do a writeup on i3
, the applications I use, etc. I don’t
think it’s anything special however and as such do not plan on writing
about it at this time.