undefined behavior

tb;dr - too boring; don’t read

Recent All Posts Categories Tags About Search

Arch Linux on Laptop

I recently got my new laptop and I found that some additional tweaks need to be made for laptops. Thus, this post is to have a record in case I forget when I have to reinstall the system.

Some basic setup

Unlike desktop, laptops have to be secure so encrytion is a must.

Details of how-to can be found on Arch wiki. I’m not going to go through that here. However, I’ll note down some considerations and things that need to pay attention to.

Encryption

Arch wiki about encryption has very good examples.

In my case I use LUKS2. As for the scheme, I leave EFI partition unencrypted and encrypt the whole BTRFS partition (See Partitioning). EFI partition also acts as boot partition (ramfs and kernel reside inside). Reason to take this approach is encrypted boot partition makes things more complicated (Like you have to decrypt the partition twice. Once from bootloader and the other one from kernel). Plus I have secure boot enabled (see Secure boot) so unencrypted boot partition is not really a big problem.

Quick setup.

# cryptsetup -y -v luksFormat /dev/sda2
# cryptsetup open /dev/sda2 myroot

A passphrase will be created to unlock the partition in slot 0.

To tell kernel to decrypt the disk on startup, ramfs and kernel parameters have to be updated. I prefer using systemd rather than busybox provided by udev. In this case sd-encrypt has to be added instead of encrypt

# /etc/mkinitcpio.conf
...
HOOKS=(base systemd autodetect keyboard modconf block sd-encrypt filesystems fsck)

Then update kernel parameters to unlock and map the encrypted partition.

# /boot/loader/entries/arch.conf
...
options rd.luks.name=51d2be03-b9a4-4d4d-bc5a-0a9dba854c1f=ffroot root=/dev/mapper/ffroot

Then we’re going to use TPM2 to automatically decrypt on bootup. For PCR see this help page.

# systemd-cryptenroll /dev/sda2 --tpm2-device=auto --tpm2-pcrs=0+7 --tpm2-with-pin=true

Alternatively you can use a more secure recovery key generated randomly instead of using your own passphrase. Assume the passphrase that was created previously is in slot 0.

# systemd-cryptenroll /dev/sda2 --recovery-key
# systemd-cryptenroll /dev/sda2 --wipe-slot=0
# systemd-cryptenroll /dev/sda2 --tpm2-device=auto --tpm2-pcrs=0+7 --tpm2-with-pin=true

Note: It is important to keep at least a backup decryption method (recovery key or passphrase) when making modifications to slots. If only TPM slot left then you’re probably fucked up because you can no longer add or remove key slots. Once the boot is tampered you would not be able to recover anymore.

Partitioning

See Arch wiki about BTRFS to get to know how to setup BTRFS file system.

Using too many partitions is not good for SSD so I only have two partitions on the disk: EFI partition (also as boot) + a big partition formated with BTRFS. The second partition is encrypted, which will be demonstrated in the next topic.

With BTRFS, I can use subvolume to achieve the similar effect like what partition does, but it is more flexible.

These subvolumes are created under the big BTRFS’s root.

SubvolumeMount PointNote
@os-arch/System root
@os-arch-var/varAvoid getting snapshot
@home/homeSeparated home
@swap/swapSwap files (no compression, no CoW)
@snapshots/snapshotsSnapshots

Quick setup. Note that the swap subvolume should have no-COW flag.

# mkfs.btrfs -L ffroot /dev/mapper/ffroot
# mount /dev/mapper/ffroot /mnt
# btrfs subvolume create /mnt/@os-arch
# btrfs subvolume create /mnt/@os-arch-var
# btrfs subvolume create /mnt/@home
# btrfs subvolume create /mnt/@vm
# btrfs subvolume create /mnt/@swap
# chattr +C /mnt/@vm
# chattr +C /mnt/@swap
# btrfs subvolume create /mnt/@snapshots

An example of how fstab is set up. For SSD, I also have discard=async enabled.

As for the compression, it seems to be enabled for the whole file system once one of the subvolume has it enabled. A workaround is to set the property by chattr +c. Based on the document statement, even if I don’t have compression option for the swap subvolume, it is enabled implicitly. The reason that I don’t want to use property-based compression option is that it cannot specify compression method. And btrfs property set <file> compression <zlib|lzo|zstd> seems to have some quirks.

So far I haven’t found any issues with this setup. Hope per-subvolume compression will be implemented soon.

# <file system> <dir> <type> <options> <dump> <pass>
# /dev/mapper/ffroot
/dev/mapper/ffroot      /               btrfs           rw,noatime,ssd,discard=async,space_cache=v2,compress=zstd:3,subvol=/@os-arch    0 1

# /dev/nvme0n1p1 LABEL=EFI_BOOT
UUID=4652-2467          /boot           vfat            rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro   0 2

# /dev/mapper/ffroot
/dev/mapper/ffroot      /var            btrfs           rw,noatime,ssd,discard=async,space_cache=v2,compress=zstd:3,subvol=/@os-arch-var        0 2

# /dev/mapper/ffroot
/dev/mapper/ffroot      /home           btrfs           rw,noatime,ssd,discard=async,space_cache=v2,compress=zstd:3,subvol=/@home       0 2

# /dev/mapper/ffroot
/dev/mapper/ffroot      /swap           btrfs           rw,noatime,ssd,discard=async,space_cache=v2,subvol=/@swap       0 2

# /dev/mapper/ffroot
/dev/mapper/ffroot      /vm             btrfs           rw,noatime,ssd,discard=async,space_cache=v2,subvol=/@vm         0 2

# swap files
/swap/swap32gb.img      none    swap    defaults        0       0

Snapshots

Thanks to CoW feature provided by BTRFS, taking snapshots is easy.

btrfs subvolume snapshot source [dest/]name

Read-only snapshot requires additional work when it gets restored. Usually I don’t use read-only argument when creating a snapshot.

Secure boot

Arch wiki has a very clear tutorial but eh. It’s too cumbersome. I choose to use sbctl to save my life.

The readme is very easy to follow but one thing needs to pay attention to is, grub doesn’t work with it. If you sign grub’s EFI file it will fail when secure boot is enforced. I switched to systemd-boot due to this reason. And I also find that systemd-boot is way easier to configure.

For a quick start.

# sbctl status
# sbctl create-keys
# sbctl enroll-keys
# sbctl status
# sbctl sign -s /efi/EFI/BOOT/BOOTX64.EFI
# sbctl sign -s /efi/EFI/systemd/systemd-bootx64.efi
# sbctl sign -s /boot/vmlinuz-linux

Use bundles (optional)

Alternatively, you can also bundle kernel image and ramdisk image together as an EFI file.

# sbctl bundle -s -i /boot/intel-ucode.img -k /boot/vmlinuz-linux -f /boot/initramfs-linux.img -c /proc/cmdline /boot/EFI/Linux/linux.efi
# sbctl bundle -s -i /boot/intel-ucode.img -k /boot/vmlinuz-linux-zen -f /boot/initramfs-linux-zen.img -c /proc/cmdline /boot/EFI/Linux/linux-zen.efi

Since the bundles are registered in sbctl’s database, the EFI files will be automatically re-generated when kernel is updated.

However, in case if kernel parameter or ramdisk is updated, run this manually to re-generate new bundles.

# sbctl generate-bundles

Hibernation

When creating swap files, keep in mind of the following:

  1. Swap files should NOT be set with CoW attribute on BTRFS file system.
  2. Use dd to ensure swap files don’t have holes.
# chattr +C /mnt/@swap
# dd if=/dev/zero of=/mnt/@swap/swapfile bs=1M count=8192 status=progress

Then follow this wiki to calculate swap file physical offset on BTRFS partition.

Then set kernel parameters. The UUID should be the UUID of decrypted BTRFS partition. Differentiate from the UUID above.

# Kernel parameters
... resume=UUID=dcc33411-f4ae-46e0-ba7a-f285301b25f6 resume_offset=3420784 ...

Update ramfs. Add resume hook.

HOOKS=(base systemd autodetect keyboard modconf block sd-encrypt filesystems fsck resume)

Overall kernel parameters

Kernel parameters used for the previous sections include partitioning, encryption and hibernation.

title Arch Liunx
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux.img
options loglevel=4 quiet rw rd.luks.name=51d2be03-b9a4-4d4d-bc5a-0a9dba854c1f=ffroot root=/dev/mapper/ffroot rootflags=subvol=@arch-root mem_sleep_default=deep nvme.noacpi=1 module_blacklist=hid_sensor_hub resume=UUID=dcc33411-f4ae-46e0-ba7a-f285301b25f6 resume_offset=3420784

Installing Arch Linux

I have a script to handle this: https://github.com/peromage/pew/blob/master/rice/setup-scripts/arch-install.sh

Power management

For power management, there are two options: TLP and power-profiles-daemon.

TLP is powerful but it needs quite a bit attention to manually manage the configuration. And most of desktop environments don’t have integration with it by default.

power-profiles-daemon can be used out of the box and desktop like KDE detects it automatically when it is installed.

Both power manager can only exist one at a time because they conflict with each other. You can mask one of them in systemd though. I don’t think that’s a good idea. To avoid scratching my head and save some hair, usually I use power-profiles-daemon.

TLP

My TLP preferences. Not too much. It might be a little aggressive since I want to have maximal battery life.

# /etc/tlp.d/my-power-plan.conf
TLP_DEFAULT_MODE=AC
TLP_PERSISTENT_DEFAULT=0

CPU_SCALING_GOVERNOR_ON_AC=performance
CPU_SCALING_GOVERNOR_ON_BAT=powersave

CPU_ENERGY_PERF_POLICY_ON_AC=balance_performance
CPU_ENERGY_PERF_POLICY_ON_BAT=balance_power

CPU_MIN_PERF_ON_AC=0
CPU_MAX_PERF_ON_AC=100
CPU_MIN_PERF_ON_BAT=0
CPU_MAX_PERF_ON_BAT=20

CPU_BOOST_ON_AC=1
CPU_BOOST_ON_BAT=0

CPU_HWP_DYN_BOOST_ON_AC=1
CPU_HWP_DYN_BOOST_ON_BAT=0

SCHED_POWERSAVE_ON_AC=0
SCHED_POWERSAVE_ON_BAT=1

power-profiles-daemon

No configuration needed. Just remember to enable the daemon service once it’s installed.

HiDPI

I previously used XFCE but now I’ve switched to KDE as the default desktop environment so HiDPI is not a problem anymore.

Firefox scrolling with touchpad

Yuck. Default scrolling experience on touchpad is just disgusting – it lacks kinetic scrolling. This mostly because Firefox is not configured properly.

For X11, add following to the profile file.

export MOZ_USE_XINPUT2=1

For Wayland, same in the profile file.

export MOZ_ENABLE_WAYLAND=1

Framework laptop specific

Framework laptop needs some additional tweaks.

Ambient Light Sensor

# pacman -S iio-sensor-proxy

Fingerprint

# pacman -S fprintd

Bluetooth

# pacman -S bluez bluez-utils
# systemctl enable --now bluetooth

Touchpad Two-finger/Three-finger Click

  1. Get touchpad device id
# xinput
  1. Add to .xinitrc
# xinput set-prop <device> 'libinput Click Method Enabled' 0 1

Brightness Keys

Add to kernel parameters:

# Kernel parameters
... module_blacklist=hid_sensor_hub ...

Suspend Power

Add to kernel parameters:

# Kernel parameters
... mem_sleep_default=deep nvme.noacpi=1 ...