After about 3 years it was time to refresh my hardware. Though I’ve long used MacBook Pro’s as my daily drivers the new MBP with touchbar wasn’t getting me excited and the new keyboard feels downright awful to me. So, I decided this was going to be the year of the Linux Desktop and I’ve switched to a Dell XPS 13 (9360, Kaby Lake) Developer Edition (comes pre-loaded with Ubuntu).
Though Ubuntu is entirely fine for servers it tends to get on my nerves as my daily driver since it’s hard to get up to date software. Arch Linux is exactly the opposite, bleeding edge every day.
I decided to risk Arch Linux b/c in general I like the concept, knowing full well I might end up spending some time fighting bugs. I did, a few times over (and I’m still dealing with one) but overall it’s been rather pleasant.
This post is basically going to get you through an Arch Linux installation, with full disk encryption, hibernate and hybrid sleep support.
Warning: The 4.15 kernel series is extremely wonky on this laptop. Don’t know why, and don’t really feel like digging into it now that 4.16 is out and everything works just fine. If you can’t get to 4.16, anything from 4.12 through 4.14 works fine too. Just skip 4.15.
Update 2018-04-21:
- Remove the
intel_iommu=igfx_off
setting as we’re now running the 4.16 kernel. It shouldn’t have had any effect even on 4.15, but it was easily observable that it did.
Update 2018-04-16:
- Change i915 options to
enable_guc_loading=-1 enable_guc_submission=-1
. RC6 is automatically enabled, PSR doesn’t seem to do much and neither doesmodeset
. However, the kernel still defaults to not turning GuC on. Setting it to-1
puts it in auto mode however, letting the system detect if the firmware is there and load it. For some reason this still causes the kernel to be marked as tainted b/c the option is considered dangerous. Don’t force frame buffer compression, doesn’t seem to do much in the way of power savings and can cause some weird rendering issues. - Add
intel_iommu=igfx_off
to kernel parameters. Without it I can successfully boot a >= 4.15 kernel but the whole system becomes unresponsive once we start a GUI. This disables Intel VT-d for the graphics card. If you’re running into this error you’ll see something along the lines of:DMAR: [INTR-REMAP] Request device [f0:1f.0] fault index 0 [fault reason 37] Blocked a compatibility format interrupt request
in the journal. The purpose of Intel IOMMU/VT-d and DMAR is to allow direct assignment of hardware to virtualised guests, more or less PCI passthrough. This option disables it for the graphics card, which is fine since I have no intention of giving it to a guest.
Once you’ve loaded the Arch Linux LiveCD and booted from it you’ll find yourself at a prompt. Graphical installations is not a thing Arch Linux does, so this is where we’ll start.
The prompt should say root@archiso ~ #
to indicate you’re at a root
prompt. I’ll be shortening that to $
in this guide, because #
trips up
the syntax highlighting.
First things first, lets ensure we can actually read the console:
$ setfont latarcyrheb-sun32
Now, configure the network. I connected over WiFi, which you can set up
by launching wifi-menu
.
The hostname of my system is monoceros
, aka unicorn. You’ll see this
come back in a few places, especially when setting up the volumes in
LVM. Swap it out for your own :). Or don’t use the hostname at all but
some generic like myvol
or archlinux
.
Once that’s done, we can start building up to the installation.
Disk partitioning
My layout is as follows:
- /dev/nvme0n1p1: /boot
- /dev/nvmen0n1p2: LUKS
- /dev/mapper/cryptlvm: lvm
- /dev/mapper/monoceros-swap: swap
- /dev/mapper/monoceros-root: /
- /dev/mapper/monoceros-home: /home
- /dev/mapper/cryptlvm: lvm
This results in a fully encrypted system, aside from the boot partition. This is good enough for my threat model, which is mostly to ensure my data doesn’t land on the street if someone steals my laptop. It’ll give most law enforcement agencies a run for their money too, though if you want to protect from nation state actors you’ll probably need to come up with something more.
I did this with parted:
$ parted /dev/nvme0n1
(parted) mklabel gpt # wipes out existing partitioning
(parted) mkpart ESP fat32 1MiB 513MiB # create the UEFI boot partition
(parted) set 1 boot on # mark the first partition as bootable
(parted) mkpart primary # turn the remaining space in one big partition
File system type: ext2 # don't worry about this, we'll format it after anyway
Start: 514MiB
End: 100%
If you now check the layout:
(parted) print
Model: Unknown (unknown)
Disk /dev/nvme0n1: 512GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 1049kB 538MB 537MB fat32 boot, esp
2 539MB 512GB 512GB ext2
(parted) exit
Setting up disk encryption
This will encrypt the second partition, which we’ll then hand off to LVM to manage the rest of our partitions. Doing it this way means everything is protected by a single password. This is good enough for me, especially since I don’t share this machine with anyone else.
$ cryptsetup luksFormat /dev/nvme0n1p2
WARNING!
========
This will overwrite data on /dev/nvme0n1p2 irrevocably.
Are you sure? (Type uppercase yes): YES
Enter passphrase:
Verify passphrase:
Now we need to open the encrypted disk so LVM can do its thing:
$ cryptsetup open /dev/nvme0n1p2 cryptlvm
Enter passphrase for /dev/nvme0n1p2:
LVM
Time to setup LVM.
$ pvcreate /dev/mapper/cryptlvm # create the physical volume
Physical volume "/dev/mapper/cryptlvm" successfully created.
$ vgcreate monoceros /dev/mapper/cryptlvm # create the volume group
Volume group "monoceros" successfully created
$ lvcreate -L 60G monoceros -n root # create a 60GB root partition
Logical volume "root" created.
$ lvcreate -L 18G monoceros -n swap # create a RAM+2GB swap, must be bigger than RAM for hibernate
Logical volume "swap" created.
$ lvcreate -l 100%FREE monoceros -n home # assign the rest to home
Logical volume "home" created.
You can check the layout by running lvs
:
$ lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
home monoceros -wi-a----- 398.43g
root monoceros -wi-a----- 60.00g
swap monoceros -wi-a----- 18.00g
Format all the partitions
Now we’re going to format all the partitions we’ve created so we can actually use them.
First the root partition:
$ mkfs.ext4 /dev/mapper/monoceros-root
mke2fs 1.43.6 (29-Aug-2017)
Creating filesystem with 15728640 4k blocks and 3932160 inodes
Filesystem UUID: 055d8ed0-19c3-4c20-bcfe-b296939f7b9b
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424
Allocating group tables: done
Writing inode tables: done
Creating journal (65536 blocks): done
Writing superblocks and filesystem accounting information: done
Now our home partition:
$ mkfs.ext4 /dev/mapper/monoceros-home
mke2fs 1.43.6 (29-Aug-2017)
Creating filesystem with 104446976 4k blocks and 26116096 inodes
Filesystem UUID: 71738ad7-a620-496b-98a1-2b1e27b6a5e7
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
102400000
Allocating group tables: done
Writing inode tables: done
Creating journal (262144 blocks): done
Writing superblocks and filesystem accounting information: done
Swap:
$ mkswap /dev/mapper/monoceros-swap
Setting up swapspace version 1, size = 18 GiB (19327348736 bytes)
no label, UUID=8d7a92ae-0c61-4105-aaf0-71aa61082124
And boot. This must be a FAT32 formatted partition b/c UEFI:
$ mkfs.fat -F32 /dev/nvme0n1p1
mkfs.fat 4.1 (2017-01-24)
Installing the base system
It’s time to install the base system, which we can then chroot into and further customise our installation.
Mount all the partitions
Before we can install the OS we need to mount all the partitions and then chroot into the moutpoint of the root partition.
$ mount /dev/mapper/monoceros-root /mnt
$ mount /dev/mapper/monoceros-home /mnt/home
$ mount /dev/nvme0n1p1 /mnt/boot
$ swapon /dev/mapper/monoceros-swap
Setting up the mirrorlist
Last step is to edit /etc/pacman.d/mirrorlist
and put the mirrors closest
to you at the top. This’ll help speed up the installation. You can also
generate a mirrorlist. I’d highly
recommend doing so and ensure you unckeck the http
checkbox so you only
use mirrors you can fetch from over https
.
Installing base
Now that everything is set up we need to bootstrap the OS:
$ pacstrap -i /mnt base base-devel
It’ll now prompt you to confirm your package selection and then start with the installation of the base system.
Configuring the new installation
Now that the base system is there, we can chroot into it to customise our installation and finish it.
$ genfstab -U /mnt >> /mnt/etc/fstab
$ arch-chroot /mnt
Your prompt will now change to: [root@archiso /]#
.
The first thing I do is install vim
so I can edit and customise my the
configuration:
$ pacman -Sy vim
Locale
In order to setup your locale edit /etc/locale.gen
and uncomment the locales
you want.
After that execute the following:
$ locale-gen
$ echo LANG=en_US.UTF-8 > /etc/locale.conf
$ export LANG=en_US.UTF-8
Timezone
Set your timezone by running:
$ tzselect
Once you’ve selected your timezone we need to update a few more things. First
override the /etc/localtime
file and symlink it to your timezone:
$ ln -sf /usr/share/zoneinfo/<continent>/<location> /etc/localtime
Sync the clock settings and set the hardware clock to UTC:
$ hwclock --systohc --utc
vconsole
Set the keyboard layout and font to be used by default for the virtual
console. Create /etc/vconsole.conf
:
FONT=latarcyrheb-sun32
KEYMAP=colemak
Careful with the keymap, you probably don’t want to set that to colemak
on
your system. If you don’t set it you’ll get the default of us
.
Hostname
Time to give your system a name by adding that to /etc/hostname
.
Also, add a line for that same hostname to /etc/hosts
:
127.0.1.1 monoceros.localdomain monoceros
And tell systemd about it:
$ hostnamectl set-hostname monoceros
Graphics card power saving options
Create /etc/modprobe.d/i915.conf
with the following content:
options i915 enable_guc_loading=-1 enable_guc_submission=-1
mkinitcpio
mkinitcpio
is what is used to generate the initramfs
you’ll soon boot form.
However, due to the hardware in this laptop and our disk partitioning we have
to update it a bit. This configuration will use a full systemd based boot
stack.
- set
MODULES
to:(nvme i915 intel_agp)
- set
HOOKS
to:(base systemd autodetect keyboard sd-vconsole modconf block sd-encrypt sd-lvm2 filesystems fsck)
Now regenerate the initramfs:
$ mkinitcpio -p linux
==> Building image from preset: /etc/mkinitcpio.d/linux.preset: 'default'
-> -k /boot/vmlinuz-linux -c /etc/mkinitcpio.conf -g /boot/initramfs-linux.img
==> Starting build: 4.13.9-1-ARCH
-> Running build hook: [base]
-> Running build hook: [systemd]
-> Running build hook: [autodetect]
-> Running build hook: [keyboard]
-> Running build hook: [sd-vconsole]
-> Running build hook: [modconf]
-> Running build hook: [block]
-> Running build hook: [sd-encrypt]
-> Running build hook: [sd-lvm2]
-> Running build hook: [filesystems]
-> Running build hook: [fsck]
==> Generating module dependencies
==> Creating gzip-compressed initcpio image: /boot/initramfs-linux.img
==> Image generation successful
==> Building image from preset: /etc/mkinitcpio.d/linux.preset: 'fallback'
-> -k /boot/vmlinuz-linux -c /etc/mkinitcpio.conf -g /boot/initramfs-linux-fallback.img -S autodetect
==> Starting build: 4.13.9-1-ARCH
-> Running build hook: [base]
-> Running build hook: [systemd]
-> Running build hook: [keyboard]
-> Running build hook: [sd-vconsole]
-> Running build hook: [modconf]
-> Running build hook: [block]
==> WARNING: Possibly missing firmware for module: wd719x
==> WARNING: Possibly missing firmware for module: aic94xx
-> Running build hook: [sd-encrypt]
-> Running build hook: [sd-lvm2]
-> Running build hook: [filesystems]
-> Running build hook: [fsck]
==> Generating module dependencies
==> Creating gzip-compressed initcpio image: /boot/initramfs-linux-fallback.img
==> Image generation successful
Don’t worry about those two warnings, the XPS 13 doesn’t have any hardware on board that needs those drivers.
Microcode
Sometimes bugs are discovered in processors for which microcode updates are released. These are loaded together with the initramfs when your system boots but we need to install the package for it first:
$ pacman -Sy intel-ucode
Setting up the bootloader
First, we need to tell bootctl
to install the necessary things onto /boot
:
$ bootctl install --path=/boot
In the future you won’t need to call install
, but update
instead.
Now, edit /boot/loader/loader.conf
and make it look like this:
timeout 10
default arch
editor 1
One note, by setting editor 1
it’s possible for anyone to edit the kernel
boot parameters, add init=/bin/bash
and become root on your system. However,
since the disk is still encrypted at this point they can’t do much with it and
I find it rather convenient to be able to edit those options when something does
go wrong.
We now need to create the boot entry named arch
. To that end, create the file
/boot/loader/entries/arch.conf
with the following content:
title Arch Linux
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux.img
options rd.luks.uuid=$UUID rd.luks.name=$UUID=cryptlvm root=/dev/mapper/monoceros-root rw resume=/dev/mapper/monoceros-swap ro intel_iommu=igfx_off
Replace $UUID
with the value from cryptsetup luksUUID /dev/nvme0n1p2
.
I usually also create another entry that allows me to boot with resume support
disabled, in case that’s broken. To that end, create a file like
/boot/loader/entries/arch-noresume.conf
with the same content as above, but
simply omit the resume=/dev/mapper/monceros-swap ro
option.
sudo
I prefer using sudo
over changing to root. In order to do so we need to
install the sudo
package and update the configuration:
$ pacman -Sy sudo
Now that the package is installed update the configuration and uncomment the
line that reads %wheel ALL=(ALL) ALL
:
$ EDITOR=vim visudo
Creating a user account
Create a user account for yourself and ensure you’re added to the wheel
group:
$ useradd -m -G wheel,users -s /bin/bash daenney
$ passwd daenney
New password:
Retype new password:
passwd: password updated successfully
Installing GNOME
I like to use GNOME as my DE so it’s time to install that:
$ pacman -Sy gnome gnome-extra dhclient iw wpa_supplicant dialog network-manager-applet networkmanager xf86-input-libinput
I explicitly install dhclient
because dhcpd
isn’t very good at dealing with
non-spec compliant DHCP implementations. Especially if you have a D-Link router
or might encounter one, install this package. It also avoids some issues I’ve
had on large networks like at the office, Eduroam etc.
Now, enable GDM and Network Manager:
$ systemctl enable gdm
$ systemctl enable NetworkManager
Boot into the new installation
We’re actually done now. What a ride. First, exit the chroot by issuing exit
.
Now, unmount our filesystems: umount -R /mnt
.
And finally, reboot
.
Fan control
One of the things you’ll notice pretty quickly is that the fans kick in really early. The fans are controlled by the BIOS and Dell has set some rather low thresholds. It seems they try to keep the system below 40 degrees Celcius at all times.
However, it’s possible to fix this, but it comes with a few warnings. Doing so essentially takes away control from the BIOS by writing directly to the BIOS using an I/O port. It’s hinky but I’ve verified it works correctly on my laptop. When you do this the keys on the keyboard that allow you to control the brightness of the display no longer work, but you can still use the controls in GNOME to do the same thing.
In order to be able to install the necessary tooling you’ll need to first
get access to the Archlinux User Repository as that’s where we’ll need to
install the utilities from. I use a helper called pacaur
, there’s a number
of installation guides out there you can follow.
Once you have an AUR helper installed, we need to install i8kutils
:
$ pacaur -Sy i8kutils
With that done, go and edit /etc/modprobe.d/dell-smm-hwmon.conf
and append
ignore_dmi=1
to that line. Right now the module checks the DMI to figure
out if it can load and support for the 9360 hasn’t been added yet.
You can now modprobe dell-smm-hwmon
and in a couple of seconds you’ll get
a /proc/i8k
file you can cat
which returns information about the fans.
If you install a GNMOE extension like Freon you’ll be able to keep tabs on the fan speed, but not control it.
Patching dell-smm-hwmon
In order to be able to control the fans we need to patch the dell-smm-hwmon
kernel module. This is not for the faint of heart.
There is an outdated package on AUR that we can use to help us do this. First,
fetch the PKGBUILD
:
$ curl -O https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=dell-smm-hwmon-i8kutils
$ curl -O https://aur.archlinux.org/cgit/aur.git/plain/dell-smm-hwmon-i8kutils.install?h=dell-smm-hwmon-i8kutils
Rename the output to PKGBUILD
and edit the pkgver
and pkgrel
variables to
reflect the output in uname -r
(at the time of writing 4.13.11 and 1). Also
rename the other file to dell-smm-hwmon-i8kutils.install
.
Change the source
and md5sums
lines too:
source=("https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-$pkgver.tar.xz"
'dell-smm-hwmon.patch::https://aur.archlinux.org/cgit/aur.git/plain/dell-smm-hwmon.patch?h=dell-smm-hwmon-i8kutils'
'smm.c::https://aur.archlinux.org/cgit/aur.git/plain/smm.c?h=dell-smm-hwmon-i8kutils'
)
md5sums=('<md5sum of kernel tarbal>'
'ccfe8a74d9cbf587d5a9cb395cae2834'
'a5e33f4c632c3055bd51853edeef51fc')
Now, build the package: makepkg
. This’ll take a while but it should result in
a dell-smm-hwmon-i8kutils-4.13.11-1-any.pkg.tar.xz
in the current directory.
Install the package with pacman -U dell-smm-hwmon-i8kutils-4.13.11-1-any.pkg.tar.xz
.
Enabling i8kmon
In order to monitor and control the fans, we need to update the i8kmon configuraiton. i8kmon was installed as part of i8kutils.
Edit /etc/i8kmon/i8kmon.conf
and add:
# Control the fans
set config(auto) 1
Next, update the “Temperature thresholds” section like this:
{% raw %}
set config(0) {{0 0} -1 55 -1 55}
set config(1) {{1 1} 45 75 45 75}
set config(2) {{2 2} 65 128 65 128}
{% endraw %}
If you’re wondering how those numbers work, read the page linked above and
see man i8kmon
. I’ve also remvoed the set status
lines since I’d rather
i8kmon
probe for those at startup.
Finally, create a systemd unit file in /etc/systemd/system/i8kmon.service
:
[Unit]
Description=Dell laptop thermal monitoring
Documentation=man:i8kmon
ConditionPathExists=/proc/i8k
[Service]
ExecStartPre=/usr/bin/smm 30a3
ExecStopPost=/usr/bin/smm 31a3
ExecStart=/usr/bin/i8kmon --nouserconfig
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
And enable the service: systemctl enable i8kmon.service
.
When we go into sleep, hybrid sleep or hibernate we should hand the fan
control back to the BIOS. In order to do that, create an executable shell
script in /usr/lib/systemd/system-sleep
named i8kmon
with the following
content:
#!/bin/sh
if [ "${1}" == "pre" ]; then
echo "Handing back fan control to BIOS" | systemd-cat
systemctl stop i8kmon
elif [ "${1}" == "post" ]; then
echo "Taking over fan control from BIOS" | systemd-cat
systemctl start i8kmon
fi
Don’t forget to chmod +x
the script or nothing will happen.
Now it’s time to reboot and if everything went well you should see
something like this in journalctl -u i8kmon
:
-- Reboot --
nov 11 16:58:24 monoceros systemd[1]: Starting Dell laptop thermal monitoring...
nov 11 16:58:24 monoceros systemd[1]: Started Dell laptop thermal monitoring.
When you go into suspend and afterwards wake up, you should find the following
entries in journalctl
:
nov 11 01:42:43 monoceros systemd[1]: Starting Suspend...
nov 11 01:42:43 monoceros cat[22921]: Handing back fan control to BIOS
nov 11 01:42:43 monoceros systemd[1]: Stopping Dell laptop thermal monitoring...
nov 11 01:42:43 monoceros systemd[1]: Stopped Dell laptop thermal monitoring.
nov 11 01:42:43 monoceros systemd-sleep[22917]: Suspending system...
nov 11 16:35:47 monoceros systemd-sleep[22917]: System resumed.
nov 11 16:35:47 monoceros unknown[23048]: Taking over fan control from BIOS
nov 11 16:35:47 monoceros systemd[1]: Starting Dell laptop thermal monitoring...
nov 11 16:35:47 monoceros systemd[1]: Started Dell laptop thermal monitoring.