Dual boot UEFI Linux/FreeBSD

There a lot of tutorials out there explaining how to dual boot Linux and FreeBSD on legacy BIOS but not so much for UEFI only systems. So I will share my experience installing Debian 10.2 and FreeBSD 12.1 on my ThinkPad X250 in UEFI only mode. It should be easy to adapt this to other Linux distributions and other systems than FreeBSD.

This post will be soon followed by another post explaining how FreeBSD and Linux can get along with each other after being installed. But now for the installation.

First ensure in your UEFI/BIOS settings that boot is set to UEFI only and CSM disabled. You don’t want to boot the installer in legacy mode by accident.

We will use a GTP partition table and the create the following partitions:

  1. EFI System Partition (ESP): To store the UEFI bootloaders
  2. SWAP + ext4: For Linux
  3. SWAP + UFS: For FreeBSD

Note that we could technically use the same SWAP for both FreeBSD and Linux. Still I prefer to use 2 SWAP partitions and use both of them in each OS. It’s a matter of preference I guess.

Install Linux and FreeBSD

Install Linux

We start installing Debian so that Linux stays in charge of GRUB. We do so because it’s frequent on Linux to have multiple kernel variants, for instance a more recent version of the kernel, custom or a RT patched kernel. So we let Linux’s package manager handle all of that.

At the partitioning step, select “Manual partitioning” and create a GPT partition table. For UEFI to function properly, we need a EFI System Partition (ESP), 500MB should be more than enough for this. Then a Linux swap partition and a Linux ext4 partition for the data. Leave some space unallocated for FreeBSD.

It is possible to use Linux’s swap partition in FreeBSD. More about that later. For now we will let each OS have its own swap partition.

Complete the Debian installation. It should install grub bootloader in the EFI partition. Check that Debian boots properly. Then start the FreeBSD install.

Install FreeBSD

There used to be a separate installation image for FreeBSD UEFI. This is not the case anymore, so you can use the AMD64 memstick image for 12.1-RELEASE on the FreeBSD download page.

Boot the installer and go ahead up to the partitioning step. Use the space you left unallocated for the freebsd-swap and freebsd-ufs partitions. The installer will complain that an EFI partition is required for the system to work properly and propose to create it. Ignore this as the partition was already created under Linux. It is weird though that the FreeBSD installer does not detect this, but there is a FreeBSD forum post about this issue.

Proceed and complete the FreeBSD installation. Then reboot into Debian to configure the dual boot.

GRUB dual boot

It is now time to tell Linux’s GRUB about our newly installed FreeBSD system.

We created the swap partitions before the data partitions for each OS, so to resume our partition table we now have:

  1. EFI System Partition
  2. Linux SWAP
  3. Linux ext4
  4. FreeBSD SWAP
  5. FreeBSD UFS

So our FreeBSD partition is (hd0, gpt5) in GRUB parlance. You may need to adapt this to your own partition scheme though. Once in Debian as root edit /etc/grub.d/40_custom and after the comment add:


menuentry 'FreeBSD' {
insmod ufs2
set root='(hd0,gpt5)'
chainloader /boot/loader.efi
}

Then update grub with update-grub2, finally reboot and select FreeBSD in the GRUB menu. You can now boot both Linux and FreeBSD.

FreeBSD aware UEFI

It is now possible to boot both Debian and FreeBSD from GRUB. However it is not yet possible to boot FreeBSD directly from UEFI. To do so we need to copy the FreeBSD UEFI loader in to EFI partition and register it. Debian already mounts the EFI partition but FreeBSD doesn’t, so for the fun of it, let’s manage all that under FreeBSD and install the FreeBSD UEFI loader. /dev/ada0p1 is the EFI partition, but you may need to adapt this to your partition scheme though.

# We mount the EFI partition on /boot/efi similarly to Linux.
mkdir /boot/efi
echo '/dev/ada0p1 /boot/efi msdosfs rw,noatime 0 0' >> /etc/fstab
mount /boot/efi

# Install the FreeBSD UEFI loader.
mkdir /boot/efi/EFI/freebsd
cp /boot/boot1.efi /boot/efi/EFI/freebsd/bootx64.efi

Now let’s create an UEFI entry for this loader. Note that this is for FreeBSD’s efibootmgr, not the Linux’s one.

# Create the boot variable.
efibootmgr -c -l /boot/efi/EFI/freebsd/bootx64.efi -L "FreeBSD"

# Check the variable number for the new boot variable and activate it.
efibootmgr
efibootmgr -a 15

# Change the boot order to leave Debian and GRUB in charge.
efibootmgr -o 14,15

Time to reboot! Select the boot menu with (generally with F12, at least on my ThinkPad X250) and FreeBSD should appear. Select it and it should boot FreeBSD directly.

You are done! Next time how to let FreeBSD and Linux talk to each other.

FreeBSD on Intel Broadwell

Around two years ago I posted about using FreeBSD 10 on the X250. A great deal has happened since then.

It is now possible to use the Intel Broadwell integrated graphic card (among others) under FreeBSD-CURRENT FreeBSD-STABLE! Also if I’m right, this will be integrated in FreeBSD 11.2-RELEASE. What a great day it is for FreeBSD on the desktop. I bet FreeBSD 12 will be truly great!

Note that it works much better on STABLE now than CURRENT because CURRENT is -well- not that stable…

So if you want to try this now, what you first need to do is to upgrade to the STABLE branch. For this you need to fetch the source, buildworld, buildkernel and installworld. Here is a very quick tuto (that you may need to adapt though). You can also find this here.


# Don't forget to upgrade RELEASE
# in any case that there were any bugs in the building tools.
freebsd-update fetch
freebsd-update install
reboot
freebsd-update install

# Replace the current source tree with STABLE.
mv /usr/src /usr/src-RELEASE
svn checkout svn://svn.freebsd.org/base/stable/11 /usr/src

# Build! Build! Build!
cd /usr/src
make buildworld -j4

# Customize (while you are at it) and build STABLE kernel.
cd /usr/src/sys/amd64/conf
cp GENERIC {YOUR-CONF}
vim {YOUR-CONF}
cd /usr/src
make buildkernel KERNCONF={YOUR-CONF}
make installkernel KERNCONF={YOUR-CONF}
reboot

# Now it's time to install world over the new kernel.
# In the meantime we also update configuration files
# with mergemaster.
mergemaster -p
cd /usr/src
make installworld
mergemaster -Ui
reboot

Now that your are on the latest STABLE, you can update the ports tree and install drm-next.


# Install ports tree if needed.
portsnap fetch
portsnap extract

# Install drm-next.
cd /usr/ports/graphics/drm-next-kmod
make install clean

Finally you must tell rc.conf to use the new i915 module instead. That is, add in /etc/rc.conf:


kld_list="/boot/modules/i915kms.ko"

Just one final reboot and you are done! Test this with the xorg and mesa-demos ports. Just startx from your user and check /var/log/Xorg.0.log to see if the intel driver was correctly loaded.

Disable XF86Back/Forward

Real ThinkPad keyboards (not this monstruous ignominy) have directly accessible keys for XF86Back and XF86Forward. That is really problematic with web browsers such as Firefox or Chromium since pressing those keys transparently go back or forward into your history, discarding anything you were typing in the process, including that 3 hours long bug report you were just about to submit. That’s rather annoying, to say the least.

Some other blog post suggest to simply disable them with xmodmap. That is in ~/.xmodmaprc (or whatever it is you use):

keycode 166 = NoSymbol
keycode 167 = NoSymbol

I personally prefer to remap them to Next/Prior keys. Having these near the navigation keys might come up handy:

keycode 166 = Next
keycode 167 = Prior

That’s on Linux though, on FreeBSD the keycodes are 233 and 234:

keycode 233 = Next
keycode 234 = Prior

Anyway use the xev command and xmodmap -pke to find the keycodes and remap them to any other interesting key symbol.

Suspend on lid closed + battery

If you use FreeBSD 11 on the good ol’ ThinkPad X201, you’ve probably noticed now that suspend and resume work perfectly flawlessly with the latest FreeBSD release.

You probably wish to take advantage of this and suspend your laptop automatically when the lid is closed. Nothing could be easier:

sysctl hw.acpi.lid_switch_state=S3

Save this option /etc/sysctl.conf for it to be permanent.
Close the lid, the laptop goes to sleep, you’re done!
It’s that simple.

But, with this method it will go to sleep each time the lid is closed, completely ignoring whether the laptop is on battery or not. And if you’re like me you probably don’t want this. Instead you want it to suspend itself when the lid is closed and the laptop is on battery. Easy enough! We just have to invoke the mighty power of devd along with a very little shell script.

First we need to tell devd how to react when the lid is closed. Put this in /etc/devd/lid.conf, then restart (service devd restart):

# Notify lid close/open events.
notify 10 {
  match  "system"    "ACPI";
  match  "subsystem" "Lid";
  action "/etc/acpi/lid.sh $notify";
};

Now we will make a script that checks the lid and AC line states and chooses to suspend the laptop when both the lid is closed and AC line is disconnected. This goes in /etc/acpi/lid.sh:

#!/bin/sh

lid="$1" # 0x00 is closed, 0x01 is open (see devd.conf)
acline=$(sysctl -n hw.acpi.acline) # 0 is battery, 1 is online (man acpi)

if [ \( "$lid" = "0x00" \) -a \( "$acline" -eq 0 \) ]
then
  logger "Lid closed on battery. Going to sleep."
  acpiconf -s3
fi

Try it out! Disconnect the AC line, close the lid, it should go to sleep within seconds. However if it doesn’t work you might want to modify the script above a little to check whether lid events are received correctly or not.

Now you may use a modified version of this script to lock xscreensaver when the lid is closed and before the actual suspend. Well OK, I’ve done that for you:

#!/bin/sh
# XScreensaver should be called BEFORE going to sleep to avoid the desktop to be
# shown for a few seconds when the system resumes from sleep.
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin

lid="$1" # 0x00 is closed, 0x01 is open (see devd.conf)
acline=$(sysctl -n hw.acpi.acline) # 0 is battery, 1 is online (man acpi)

lock_display() (
  socket="$1"
  display=$(echo "$socket" | tr "X" ":")

  # Temporary pid file for the watching command
  tpid=$(mktemp)

  # Wait until the display is actually locked.
  (timeout 2s xscreensaver-command -display "$display" -watch & echo $! > $tpid) | (
    # Issue the lock command only when we know that
    # the watching pipe is ready.
    xscreensaver-command -display "$display" -lock

    while read line
    do
      line=$(echo $line | cut -d' ' -f 1)

      if [ "$line" = LOCK ]
      then
        # We have to kill the watching command manually before breaking.
        kill -TERM $(cat $tpid)
        break
      fi
    done
  )

  rm $tpid
)

# The X server may not be running
if [ ! -d /tmp/.X11-unix ]
then
  exit 0
fi

# Lock each available display
for socket in $(ls /tmp/.X11-unix)
do
  # Lock the display
  logger "Locking xscreensaver on $socket"
  lock_display $socket &
done

# Wait until every displays are locked
wait

# Now we can suspend if needed
if [ \( "$lid" = "0x00" \) -a \( "$acline" -eq 0 \) ]
then
  logger "Lid closed on battery. Going to sleep."
  acpiconf -s3
fi

FreeBSD 10 on ThinkPad X250

UPDATE:


I’m now using FreeBSD 10-RELEASE on a ThinkPad X250. It’s been quite a while now, and I should have written this long ago. Well… Here we go! Everything works fine except for:

  • Intel Broadwell integrated graphics
  • Intel Wireless 7265
  • Resume/Suspend

For the integrated graphics, the Broadwell architecture is not yet supported [1, 2]. But it works well using VESA at native resolution (1920×1080). I had to tweak the MTTR a little to get reasonable performance. Of course you cannot use your VGA and miniDP anymore. That’s a bummer for giving presentations. To this regard, I still rely on Linux. Also you cannot adjust screen luminosity, it’s always at full power. So if you are in a dark room or a car at night, you will transform yourself into a lamppost.

Here is what I have in my /etc/rc.local for the MTTRs:

memcontrol clear -b 0xc0000000 -l 0x20000000
memcontrol set -b 0xc0000000 -l 0x20000000 -o BIOS write-combine

I also had to force the DPI in /usr/local/etc/X11/xorg.conf.d/dpi.conf

Section "Monitor"
  Identifier "Monitor0"
  Option     "DPI" "96x96"
EndSection

For the WiFi, there is an iwm driver coming in FreeBSD 11 that was ported from OpenBSD. Until recently I used a Ralink RT2500USB card to get it working. But last week, I did a quick and dirty backport for FreeBSD 10, it is available here. This is a fork of the iwm driver before its inclusion in HEAD. It works somewhat. But please keep in mind that I have no clue here. I still have to read more and get into the FreeBSD kernel. And this is a temporary solution while we are waiting for the release of FreeBSD 11.

The driver still crashes when loaded with virtual box modules. Also the channel is currently locked. So if you want to use it, you need to find the correct channel first, select it manually, and then try to associate. There is a small script on the repo to do so.

FreeBSD suspend and ttys.

I use FreeBSD 11-CURRENT on a Thinkpad X201 laptop. I said in a preceding post (Hello FreeBSD!) that although the Intel KMS driver works, I did not have access to the ttys and also that suspend and resume did not work.

This is no longer the case! I rebuiIt world two days ago (r274088) and I now have access to the ttys, and suspend/resume also works without any apparent problem (thanks to KMS/VT (NewCons) I guess). FreeBSD 11 is going to be great!