Ubuntu Dual Boot on Dell XPS 15 9570

My main computer is a Dell XPS 15 9570. Hardware spec highlights:

It is a monster of a machine, but of course it suffers from Windows 10 as most machines do when you buy them. It’s OK though, Windows 10 is good for gaming, and for running Citrix to work remote. I enjoy the former, and need the latter every once in a while.

At home I connect to a Dell TB16 docking station with a Dell P2418D display. You’re supposed to just plug in one cable from the docking station, and then you’re good, but even on Windows that is unreliable, useless on Linux, so I have my USB peripherals plugged in via a USB hub that I also need to connect to the laptop when “docking.”

My first choice in operating systems is OpenBSD, but the first hurdle was that the “Killer” wifi does not have an OpenBSD driver. It is easy and cheap to change the wifi card, so I opted for an Intel® 8265 card, iwm(4) on OpenBSD, and at least that worked. Still, the graphics performance was atrocious, and suspend/resume didn’t work, it just sucked.

So I gave up and went the Linux route, and settled on Ubuntu. It’s the boring choice, in a good way. Like buying a Mac or something. It gets the job done, and there’s plenty of Ubuntu specific hits on your favourite search engine when you need to solve a problem.

First step was making a clean Windows 10 install. I downloaded a fresh image from Microsoft and wrote it to a USB stick. You need to do that with the tool they make available, you can’t just dd(1) it to the USB stick like with all other installation ISOs. I know, because I tried, and it sucked.

I gave Windows 10 200 GB at the start of the disk, and let the installer do its thing. It is real easy, and on this particular machine everything just works, Microsoft has done a great job with their installer. I will not bore neither you, nor yours truly, with the details of it, there are plenty of installation instructions you can follow if you need it.

After you have installed Windows and turned BitLocker on, make sure you have your BitLocker recovery key ready, because you will need it later, after ubuntu has fiddled with the EFI partition.

The interesting part is now, of course …

Dual Booting

It is easy running Windows 10 with BitLocker disk encryption. It is also easy to run Ubuntu with LUKS disk encryption, but it is not easy to have them co-exist, the Ubuntu installer simply does not let you set it up when you want to dual boot.

But it can be done.

I used another Linux machine to create the installer. You can use Rufus for ISO writing on Windows, but I have no idea how to check SHA256 checksums and all that on Windows. Surely it can be done, but I don’t know how. I just know exactly how on Linux and BSD, so that’s what I do.

Get the SHA256.gpg and SHA256 files, along with the ubuntu-18.10-desktop-amd64.iso ISO from your closest mirror. I had to rename SHA256SUMS.gpg to SHA256SUMS.asc to make GnuPG 2 want to check it. I was very surprised to find a UNIX native program that care about file name extensions.

% gpg --verify SHA256SUMS.asc
gpg: assuming signed data in 'SHA256SUMS'
gpg: Signature made Thu 18 Oct 2018 02:16:16 PM CEST
gpg:                using DSA key 46181433FBB75451
gpg: Good signature from "Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: C598 6B4F 1257 FFA8 6632  CBA7 4618 1433 FBB7 5451
gpg: Signature made Thu 18 Oct 2018 02:16:16 PM CEST
gpg:                using RSA key D94AA3F0EFE21092
gpg: Good signature from "Ubuntu CD Image Automatic Signing Key (2012) <cdimage@ubuntu.com>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 8439 38DF 228D 22F7 B374  2BC0 D94A A3F0 EFE2 1092

OK, SHA256SUMS is good, then check the ISO image:

% sha256sum -c SHA256SUMS
ubuntu-18.10-desktop-amd64.iso: OK
sha256sum: ubuntu-18.10-live-server-amd64.iso: No such file or directory
ubuntu-18.10-live-server-amd64.iso: FAILED open or read
sha256sum: WARNING: 1 listed file could not be read

Don’t worry about the ubuntu-18.10-live-server-amd64.iso error, the thing that matters is that ubuntu-18.10-desktop-amd64.iso is OK, this is what we are now going to write to the USB stick.

First run lsblk and note the block devices. Then insert the USB stick, and run lsblk again. Notice the new device? In my case the USB stick shows up as sda1, but on your machine it may be under another name. Be 100% certain that you have the right device name to write to, lest you accidentally write the image to your boot disk (please don’t do that).

% sudo dd if=ubuntu-18.10-desktop-amd64.iso of=/dev/sda1 bs=4M
476+1 records in
476+1 records out
1999503360 bytes (2.0 GB, 1.9 GiB) copied, 464.829 s, 4.3 MB/s

That’s it. You now have a verified Ubuntu USB stick installer.

Now go and boot from it, tap F12 to get to the choice of boot device. When presented with the menu, choose “Try Ubuntu without installing” so we can then run the installer manually, instead of being forced into it, and more easily open the terminal that we will need later.

The installation of Ubuntu is as easy as Windows 10, DistroTube has a good look at it. Just go through it normally, until you get to the “Installation type” screen, where the option to “Erase disk and install Ubuntu” is selected by default. Don’t do that, you want to keep Windows around. Chose “Something else”, but do not continue yet.

This is where the fun begins, and I make myself a liar, when I said that it is easy. First, open a terminal (click the lower left corner icon, scroll down to find the terminal).

Become root and find out how the disk is laid out.

% sudo -s  # Become root.
# fdisk -l /dev/nvme0n1
Disk /dev/nvme0n1: 953.9 GiB, 1024209543168 bytes, 2000409264 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 7277CBA5-F303-4D56-BBAF-5D33787BFA11

Device             Start        End    Sectors   Size Type
/dev/nvme0n1p1      2048    1023999    1021952   499M Windows recovery environment
/dev/nvme0n1p2   1024000    1228799     204800   100M EFI System
/dev/nvme0n1p3   1228800    1261567      32768    16M Microsoft reserved
/dev/nvme0n1p4   1261568  459718438  458456871 218.6G Microsoft basic data
/dev/nvme0n1p5 459718656  461375487    1656832   809M Windows recovery environment

This is the partitions a normal Windows 10 installation has set up. Leave them alone.

Be mindful that the disk in your system may be named differently, and please don’t just cut and paste blindly. Think, carefully, first. Disk partitioning gymnastics aren’t difficult, but the consequences of mistakes can be quite time consuming to recover from. Because you do have a backup of the things you want to keep, right?

Create partitions for Ubuntu with fdisk(1) or cfdisk(1), whatever you prefer. Create a /boot partition of at least 100 MB with “Linux filesystem” type, and set the rest of the disk aside for a big “Linux LVM” partition. Like this:

Device             Start        End    Sectors   Size Type
/dev/nvme0n1p1      2048    1023999    1021952   499M Windows recovery environment
/dev/nvme0n1p2   1024000    1228799     204800   100M EFI System
/dev/nvme0n1p3   1228800    1261567      32768    16M Microsoft reserved
/dev/nvme0n1p4   1261568  459718438  458456871 218.6G Microsoft basic data
/dev/nvme0n1p5 459718656  461375487    1656832   809M Windows recovery environment
/dev/nvme0n1p6 461375488  462501887    1126400   550M Linux filesystem
/dev/nvme0n1p7 462501888 2000409230 1537907343 733.3G Linux LVM

Make a ext4 filesystem on the /boot partition.

# mkfs.ext4 /dev/nvme0n1p6

Sanitize the soon to be encrypted part of the disk with dd if=/dev/urandom of=/dev/nvme0n1p7 first. This takes a long while, and can be skipped, but I think it is worth doing this properly.

At this point you can run cryptsetup benchmark and see what encryption algorithms perform best on your machine. I my case it was the default aes-xts, with 256 bit key, so I choose that. If you have really secret stuff you need encrypted you may want to choose differently.

Encrypt the partition with cryptsetup -s 256 -y luksFormat /dev/nvme0n1p7. Read the man page for options to choose other algorithms. You will be prompted for a passphrase, and you will remember this passphrase, as long as you need access to the contents of the disk. This is Unix, there’s no recovery key like for BitLocker.

Next we need to open the encrypted partition, and then get to work and create a Linux LVM volume group with some logical volumes.

cryptsetup luksOpen /dev/nvme0n1p7 crypt
pvcreate /dev/mapper/crypt
vgcreate cryptvg /dev/mapper/crypt
lvcreate -L 40G -n root cryptvg        # 40 GB /
lvcreate -L 20G -n var cryptvg         # 20 GB /var
lvcreate -L 33G -n swap cryptvg        # 33 GB swap
lvcreate -l 100%FREE -n home cryptvg   # The rest for /home
vgscan --mknodes
vgchange -ay
mkswap /dev/cryptvg/swap

The separate /var and /home partitions are optional, you can run everything just fine off one / partition, but I am a bit old skool and like to have at least a separate /home, and a separate /var partition does not hurt either.

Leave the terminal running for now, as you return to the installer.

In partitioning, select “Something else.” and click next. Identify the newly created partitions, choose mount points (you can see the names we gave them, so they’re easily identified) and format them as you want (I use XFS).

Finish the installation, it takes a few minutes, BUT DO NOT REBOOT when it is done. Seriously. The installer has finished, but the system is actually in a non-bootable state, and we need to create e new initrd that can boot the encrypted partition.

Go back to the terminal.

First we need to get the installed system isolated in a chroot in /mnt:

# mount /dev/mapper/cryptvg-root /mnt
# mount /dev/nvme0n1p6 /mnt/boot
# mount /dev/mapper/cryptvg-var /mnt/var
# mount /dev/mapper/cryptvg-home /mnt/home
# mount --bind /dev /mnt/dev
# chroot /mnt

We’re now in the chroot:

# mount -t proc proc /proc
# mount -t sysfs sys /sys
# mount -t devpts devpts /dev/pts

Find the UUID of the LUKS partition:

# blkid /dev/nvme0n1p7
/dev/nvme0n1p7: _UUID="03b8aeb0-b3ca-4082-a7f7-02e895d46853"_ TYPE="crypto_LUKS" PARTUUID="9ea40832-237c-3149-9101-15ff743ea6a6"

Write the UUID of the LUKS partition in /etc/crypttab like this:

# <target name> <source device> <key files> <options>
crypt UUID=03b8aeb0-b3ca-4082-a7f7-02e895d46853 none luks,tries=0

Then run update-initramfs -k all -c to create a crypto savvy initrd.

Now you can, finally, reboot.

When booting you should be prompted for a passphrase. That one you’re not allowed to forget.

After you have logged in, open a terminal and check that everything is OK with sudo mount, and swap with swapon -s, then update everything with sudo apt -y update && sudo apt -y upgrade. If the update includes a new kernel, reboot for good measure.

Tuning

Mount /tmp as a tmpfs in memory (for speed and assured deletion upon shutdown). After mounting / in /etc/fstab:

# /tmp is in 2 GB RAM
tmpfs /tmp tmpfs defaults,noatime,nosuid,nodev,mode=1777,size=2048M 0 0

Make sure you have the universe repository added:

# add-apt-repository -y universe
# apt -y update
# apt -y full-upgrade

Get the latest version of the TLP - Linux Advanced Power Management tools:

# add-apt-repository -y ppa:linrunner/tlp
# apt -y update
# apt -y install thermald tlp tlp-rdw powertop

Fix Sleep/Wake BlueTooth bug by setting RESTORE_DEVICE_STATE_ON_STARTUP to 1 in /etc/default/tlp and run systemctl restart tlp.

Get the, at the moment of writing, latest NVIDIA driver from the main Ubuntu 18.10 repo: apt -y install nvidia-driver-390

Various codecs: apt -y install ubuntu-restricted-extras va-driver-all vainfo libva2 gstreamer1.0-libav gstreamer1.0-vaapi

For the highest quality audio, which may impact battery life a bit: In /etc/pulse/daemon.conf uncomment and set the following lines:

daemonize = no
high-priority = yes
realtime-priority = 9
resample-method = soxr-vhq
enable-lfe-remixing = yes
flat-volumes = no
rlimit-rtprio = 9
default-sample-format = float32le
default-sample-rate = 48000
alternate-sample-rate = 44100
default-sample-channels = 2
default-channel-map = front-left,front-right
default-fragments = 2
default-fragment-size-msec = 125
deferred-volume-safety-margin-usec = 1

Get the latest pulseaudio:

# add-apt-repository ppa:eh5/pulseaudio-a2dp
# apt-get update
# apt-get install libavcodec-dev libldac pulseaudio-module-bluetooth

Get the microcode for the Intel i915 graphics card: apt -y install intel-microcode

Set options for the i915 module:

# echo "options i915 enable_psr=0 enable_fbc=1 enable_guc=3 disable_power_well=0 fastboot=1" > /etc/modprobe.d/i915.conf

Check that dell-smm-hwmon is in /etc/modules: fgrep dell-smm-hwmon /etc/modules

If not there, add it: echo dell-smm-hwmon >> /etc/modules and then run update-initramfs -u.

Switch to Intel graphics card, and check that it worked:

# prime-select intel
# prime-select query

Edit /etc/default/grub and change GRUB_CMDLINE_LINUX_DEFAULT from:

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"

to:

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash acpi_rev_override=1 acpi_osi=Linux nouveau.modeset=0 pcie_aspm=force drm.vblankoffdelay=1 scsi_mod.use_blk_mq=1 nouveau.runpm=0 mem_sleep_default=deep"

Reboot, and check that everything works, suspend, resume, etc.

Enjoy!