FrameWork 13 (Intel Ultra) & ArchLinux
My trusty Thinkpad is now over a decade old and I was eyeing a Framework Laptop for a while now. So when the opportunity arose to get it as a work laptop I couldn't refuse
I picked the Intel Ultra Core 165H model with 96GB of RAM and a 4TB NVME disk. This beast should have plenty of power for the foreseeable future.
Of course Framework announced that they would release a new generation on February 25th just a couple of days after my order.
Below are my notes on how to set it up on Arch Linux. I might extend the post over the coming weeks…
Hardware
I got the DIY version which means some slight assembly is required. The installation guide is great and reminded me a lot of the Prusa assembly guides (including helpful user comments on each step). It took me and Kaddi about 20 minutes to assemble.
When buying the laptop, you can pick each individual component. Here's what I selected:
- DIY Edition
- Intel Core
- System: Ultra 7 165H
- Display: 2.8K Display (the “non-retina” display is not available for the Intel Core models)
- Storage: WD_BLACK™ SN850X NVMe™- M.2 2280 - 4TB
- Operating System: none
- Bezel: black
- Keyboard: German
- Power Adapter: none
- Expansion Cards: 2x USB-C, 1x USB-A, 1x HDMI
If this hadn't been a company laptop I probably would have bought RAM and storage somewhere else to save some money, but it was easier this way.
I did not get their power supply and instead bought a uGreen Nexode 65W Power Supply which is absolutely tiny.
I also ordered a laptop sleeve from CushCase but it hasn't arrived, yet. I'll add a picture when it does.
References
For the remaining setup I relied heavily on these sources:
- Arch Linux: An 𝔼𝕟𝕔𝕣𝕪𝕡𝕥𝕖𝕕 Guide (YouTube)
Bios Update
Before installing the OS I wanted to update the BIOS. To get the current version, press F2 during boot, then go to Setup Utility → Main → InsydeH2O Version. Mine said KFM30.03.01
. Which the Framework people call just 03.01
.
To update you can use an UEFI shellscript. You can find the zip file here: Linux/Other/UEFI Shell update.
TBH I found their BIOS upgrade guide a mess and no comparison to their assembly guide. Framework should really improve this.
To use the EFI Shell update, a FAT formatted USB stick with a GPT partition is needed:
$> sudo gdisk /dev/sde p # to print current partitition table d # delete existing partitition n # for new partition [enter] # for partition number 1 [enter] # for start sector default [enter] # for end sector default ef00 # for efi partition type w # for writing table to disk $> sudo mkfs.vfat /dev/sde1 $> sudo mount /dev/sde1 /mnt $> cd /mnt $> sudo unzip ~/temp/00-today/Framework_Laptop_13_Intel_Core_Ultra_Series1_capsule_signed_allsku_3.03_3.04_EFI.zip $> cd $> sudo umount /mnt
Now you can boot from that stick and the update should happen automatically. It should update first to 3.03, then to 3.04.
That seemed to work fine until the whole process somehow got stuck and did the same thing again and again. It would apply some update named “Port01: 270 → Port01: 270”, show “Update complete!!”, reboot and do the same thing again.
At some point I removed the USB stick and saw that the update to 3.03 had been applied, but 3.04 was nowhere to be seen.
I created a forum post asking for help and moved on to install the OS.
Boot Media
First step is preparing the bootable USB stick with the current ArchLinux ISO. I used Balena Etcher to “burn” the stick.
Once booted, I changed to a German keyboard layout with loadkeys de
.
To connect to the internet, simply use iwctl
:
#> iwctl device list station wlan0 scan station wlan0 get-networks station wlan0 connect YOURNETWORK
Encrypted File System
One thing mandated by my company is that all work related stuff has to be stored on an encrypted device. In the past I used ecryptFS to encrypt my home directory. But since I was starting from scratch here, I wanted to properly encrypt the whole root partition.
I decided for the following layout:
- 1GB EXT4 mounted to
/boot
- 1GB FAT32 mounted to
/efi
- ~3.6TB LUKS2 encrypted
- EXT4 mounted to
/
The boot and EFI partitions are quite spacious, but better safe than sorry later.
Note that I decided against any LVM setup here. The only reason I can see to do so is if I wanted to have different partitions inside the LUKS container, but I prefer one huge root partition so I kept things simple here.
To create the partition table gdisk
is used:
#> gdisk /dev/nvme01n1 n # new partition [enter] # partition number [enter] # first available sector +1g # last sector (at 1GB after the first) [enter] # type Linux Filesystem n # new partition [enter] # partition number [enter] # first available sector +1g # last sector (at 1GB after the first) ef00 # type EFI system partition n # new partition [enter] # partition number [enter] # first available sector [enter] # last available sector [enter] # type Linux Filesystem p # print current setup Disk /dev/nvme0n1: 7814037168 sectors, 3.6 TiB Model: WD_BLACK SN850X 4000GB Sector size (logical/physical): 512/512 bytes Disk identifier (GUID): F6D832C4-EE21-4995-A468-59BD03C351F7 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 7814037134 Partitions will be aligned on 2048-sector boundaries Total free space is 3693 sectors (1.8 MiB) Number Start (sector) End (sector) Size Code Name 1 2048 2099199 1024.0 MiB 8300 Linux filesystem 2 2099200 4196351 1024.0 MiB EF00 EFI system partition 3 4196352 7814035455 3.6 TiB 8300 Linux filesystem w # write to disk y # confirm
Next the encryption for partition 3 is set up. I made sure to use the newer LUKS2 format, but kept everything else at its defaults:
cryptsetup luksFormat --type luks2 /dev/nvme0n1p3 cryptsetup open /dev/nvme0n1p3 luks
The opened encrypted block device is now available as /dev/mapper/luks
. Time to create the file systems:
mkfs.ext4 /dev/nvme0n1p1 # boot partition mkfs.fat -F32 /dev/nvme0n1p2 # efi partition mkfs.ext4 /dev/mapper/luks # root partition
Finally the file systems can be mounted.
mount /dev/mapper/luks /mnt mkdir /mnt/efi mount /dev/nvme0n1p2 /mnt/efi mkdir /mnt/boot mount /dev/nvme0n1p1 /mnt/boot
@mortzu on Mastodon points out that the encrypted device should be opened with TRIM support to reduce wear on the SSD. The same ArchWiki page also recommends disabling work queues on SSDs.
Both changes can easily be enabled after the fact:
#> cryptsetup --allow-discards --perf-no_read_workqueue --perf-no_write_workqueue --persistent refresh luks #> cryptsetup luksDump /dev/nvme0n1p3 |grep Flags Flags: allow-discards no-read-workqueue no-write-workqueue
Bootstrapping
Now the base ArchLinux system can be installed. Only the bare minimum here:
pacstrap /mnt base linux linux-firmware mkinitcpio vim dhcpcd wpa_supplicant intel-ucode iwd grub
A file system table can be auto generated. Disabling access times improves performance and reduces wear on the SSD:
genfstab -pU /mnt | sed 's/relatime/noatime/' >> /mnt/etc/fstab
Time to chroot into the new system:
arch-chroot /mnt
First set the timezone, hardware clock and hostname:
ln -s /usr/share/zoneinfo/Europe/Berlin /etc/localtime hwclock --systohc --utc echo ni > /etc/hostname
Uncomment en_US.UTF-8 UTF-8
in /etc/locale.gen
and generate, then set up the locale
locale-gen echo 'LANG=en_US.UTF-8' >> /etc/locale.conf
Don't forget to keep the German keyboard layout. The following sets /etc/vconsole.conf
as well as the xorg default for later.
localectl set-x11-keymap de
Set up the hosts file:
- /etc/hosts
127.0.0.1 localhost ni ::1 localhost
And create a root password:
passwd
Grub Setup
As the bootloader I used grub. To make it aware of the encrypted root disk some adjustments need to be made.
First we need to make sure the initramfs can decrypt our file system. This is done by adding the encrypt
hook after the block
hook in /etc/mkinitcpio.conf
.
- /etc/mkinitcpio.conf
HOOKS=(base udev autodetect keyboard modconf block encrypt filesystems fsck)
Be sure to recreate the initramfs after the change.
mkinitcpio -p linux
Next we need the UUID of the encrypted partition:
#> blkid -o value -s UUID /dev/nvme0n1p3 ce7318b0-4302-442c-aaff-6e9c26f7547e
Now the grub configuration can be adjusted. Find and edit the appropriate options in /etc/default/grub
- /etc/default/grub
GRUB_CMDLINE_LINUX="cryptdevice=UUID=ce7318b0-4302-442c-aaff-6e9c26f7547e:luks" GRUB_ENABLE_CRYPTODISK=y GRUB_TERMINAL_OUTPUT=gfxterm GRUB_GFXMODE=800x600
The first line tells the kernel to decrypt our root partition. The second tells grub about it. The last two lines simply reduce the graphics resolution so you don't have to squint at tiny pixels in your boot loader.
Next the bootloader can be installed:
mkdir /boot/grub grub-mkconfig -o /boot/grub/grub.cfg grub-install --target=x86_64-efi --efi-directory=/efi --removable
With all this done, it's time to reboot. Exit the chroot environment, unmount the disks and reboot (remove the USB stick).
exit umount /mnt/efi /mnt/boot /mnt reboot
XFCE & XOrg & UI Scaling
Once successfully booted and logged in, I needed network access again. It can be done the same way as in the install environment above. In theory you can simply enable both services at boot, but I will use the graphical network manager once X is up, so this is only temporary:
systemctl start iwd dhcpcd iwctl
You should also create a local user now and add it to sudoers.
Time to install a bunch of packages. I am using XFCE for decades now and am perfectly happy with it. The same goes for Xorg. At some point in the future I might make the switch to Wayland, but for now I don't have any compelling reason.
One thing that threw me off, was that you should NOT install the xf86-video-intel
package. It will not work with the modern hardware.
#> pacman -S xorg mesa mesa-utils xfce4 xfce4-goodies lightdm lightdm-gtk-greeter light-locker gnome-keyring ttf-dejavu ttf-liberation ttf-droid ttf-ubuntu-font-family ttf-roboto noto-fonts noto-fonts-emoji ttf-bitstream-vera pulseaudio pulseaudio-alsa alsa-utils pavucontrol networkmanager network-manager-applet nm-connection-editor networkmanager-openvpn openvpn gigolo gvfs gvfs-smb bind-tools colord xiccd #> systemctl enable NetworkManager lightdm #> systemctl start NetworkManager lightdm
You should now have a running XFCE desktop you can login to. Running at a ridiculous resolution of 2880×1920. Time to do some tweaking.
Proper “retina” style scaling is still a major PITA on Linux. The tweaks below are the best I could achieve so far. Individual tools might still trip over the settings.
First download the color profile for the screen:
$> wget --user-agent="Mozilla/5.0" https://www.notebookcheck.net/uploads/tx_nbc2/BOE0CB4.icm
Then open the Settings Manager and adjust some settings:
- Appearance
- Settings
- Window Scaling: 2
- Window Manager
- Style
- Theme: Default-xhdpi
- Display
- General
- Scale: 0.75
- Color Profiles
- Select the Monitor
- Click Add
- Browse for the downloaded BOE0CB4.icm and add it
- Select BOE0CB4 from the list
- Mouse and Touchpad
- Select the Touchpad Device
- Buttons and Feedback
- Pointer Speed
- Acceleration: 8.0
- Touchpad
- Tap touchpad to click: enable
- Click Method: Click 1,2 or 3 fingers…
The above will first set the scaling to 2x
, then zoom out slightly again via xrandr. Feel free to play with the settings according to your preferences.
However some programs may still not pick up the correct scaling. In my case IntelliJ Idea failed to understand the settings. The solution was to set the GDK_SCALE
env variable.
- ~/.xprofile
export GDK_SCALE=2
More issues can potentially pop up with every single program you run . To make Gimp correctly scale the UI, I had to install
gimp-devel
from AUR. With Release Candidate 3 having been released just a week ago this will hopefully not be needed for much longer.
One issue I couldn't fix yet is that the mouse pointer size is quite tiny. And the size setting has absolutely no effect. I assume this is a bug that will be fixed in future updates.
External Displays
When you connect the laptop to an external non-retina display, you need to further downscale everything again. Luckily this is simple to do in the Settings Manager under Display → General → Scale where you can set a scale for each individual monitor. For non-retina monitors setting the scale to 0.5
will undo all the previous enlargements.
Under Advanced you can save different display settings as profiles and have them applied automatically when you connect a new display.
If the profile auto-selection fails sometimes, you can manually trigger it by running:
xrandr --auto
When connecting an external display via USB-C (opposed to using the HDMI expansion card) you need to make sure your cable is actually capable of doing video transfer. Chances are high that none of your existing cables are. When buying, either look for “Thunderbolt”, “USB Gen 4.0” or descriptors like “4K” or “8k”. Buying USB-Cables is a mess.
Auto Login
Since booting up already requires to unlock the encrypted root disk and this is a single user system, there is no reason to have lightdm ask me for a password for logging into my xsession. Instead I simply enable it's auto login feature:
- /etc/lightdm/lightdm.conf
[Seat:*] autologin-user=andi autologin-user-timeout=0
To make that work, my user needs to be in a autologin
group:
sudo groupadd -r autologin sudo usermod -aG autologin andi
One issue I ran into is that gnome-keyring is usually unlocked by the user password. With auto-login that doesn't exist anymore. But again with the whole disk encrypted, additional encryption for gnome-keyring is superfluous.
To create a password-less keyring, I logged out and connected via SSH. Then deleted ~/.local/share/keyrings
and rebooted. When I started Chrome (which uses the keyring) I was asked to create a new one and give it a password. I chose an empty one and confirmed that I am fine with unencrypted data.
Apparently the only way to change the password of an existing keyring is to use the graphical tool Seahorse. It's super weird to me that there is no (documented) way to do it from the command line. But I guess it's a Gnome tool, so they expect you to run their full suite of GUI tools…
Energy Management
I didn't do much for energy management yet except installing and enabling the recommended tools. Seems to work fine.
sudo pacman -S thermald tlp ethtool smartmontools sudo systemctl enable thermald tlp sudo systemctl start thermald tlp
Bluetooth
Getting Bluetooth to work was super easy. Just install the requirements and start the daemon.
sudo pacman -S bluez bluez-utils blueman pulseaudio-bluetooth sudo systemctl enable bluetooth sudo systemctl start bluetooth
This was enough to pair my Sony headphones but the actual audio connection wouldn't work until after a reboot 🤷♂️.
Fingerprint Sensor
The Framework 13 has a built-in fingerprint reader:
Bus 003 Device 003: ID 27c6:609c Shenzhen Goodix Technology Co.,Ltd. Goodix Fingerprint USB Device
It should work fine with the fprint daemon, but since I am logging into Xorg automatically and the LUKS passphrase is queried long before the system and fprint are initialized, there's not much sense in setting that up.
BIOS Update again
With the system running and no answer to my forum post, I decided to tackle the BIOS update again. This time not using the EFI shell but doing it from the installed system.
First install the needed packages and check the firmware version.
$> sudo pacman -S dmidecode fwupd $> sudo dmidecode -s bios-version 03.03
Then basically
$ fwupdmgr get-devices $ fwupdmgr refresh $ fwupdmgr get-updates $ sudo fwupdmgr update
Reboot and the familiar update screen will pop up again. However this time it worked. As confirmed after booting into Arch again:
$ sudo dmidecode -s bios-version 03.04
I think something in the update process did not like that there was only an UEFI partition on the USB stick but not on the main disk, yet.
Sensors
When using the sensors
command from the lm_sensors
package, a whole bunch of them are reported.
ucsi_source_psy_USBC000:004-isa-0000 Adapter: ISA adapter in0: 20.00 V (min = +5.00 V, max = +38.80 V) curr1: 2.25 A (max = +3.16 A) ucsi_source_psy_USBC000:001-isa-0000 Adapter: ISA adapter in0: 0.00 V (min = +0.00 V, max = +0.00 V) curr1: 0.00 A (max = +0.00 A) iwlwifi_1-virtual-0 Adapter: Virtual device temp1: +55.0°C spd5118-i2c-14-50 Adapter: SMBus I801 adapter at efa0 temp1: +50.5°C (low = +0.0°C, high = +55.0°C) (crit low = +0.0°C, crit = +85.0°C) coretemp-isa-0000 Adapter: ISA adapter Package id 0: +56.0°C (high = +110.0°C, crit = +110.0°C) Core 0: +52.0°C (high = +110.0°C, crit = +110.0°C) Core 1: +51.0°C (high = +110.0°C, crit = +110.0°C) Core 2: +52.0°C (high = +110.0°C, crit = +110.0°C) Core 3: +52.0°C (high = +110.0°C, crit = +110.0°C) Core 4: +51.0°C (high = +110.0°C, crit = +110.0°C) Core 5: +52.0°C (high = +110.0°C, crit = +110.0°C) Core 6: +52.0°C (high = +110.0°C, crit = +110.0°C) Core 7: +52.0°C (high = +110.0°C, crit = +110.0°C) Core 8: +57.0°C (high = +110.0°C, crit = +110.0°C) Core 12: +51.0°C (high = +110.0°C, crit = +110.0°C) Core 16: +54.0°C (high = +110.0°C, crit = +110.0°C) Core 20: +51.0°C (high = +110.0°C, crit = +110.0°C) Core 24: +51.0°C (high = +110.0°C, crit = +110.0°C) Core 28: +50.0°C (high = +110.0°C, crit = +110.0°C) Core 32: +55.0°C (high = +110.0°C, crit = +110.0°C) Core 33: +55.0°C (high = +110.0°C, crit = +110.0°C) BAT1-acpi-0 Adapter: ACPI interface in0: 16.21 V curr1: 1.96 A acpi_fan-acpi-0 Adapter: ACPI interface fan1: 0 RPM power1: 0.00 W cros_ec-isa-0000 Adapter: ISA adapter fan1: 0 RPM local_f75397@4c: +44.9°C cpu_f75303@4d: +47.9°C battery_temp@b: +39.9°C ddr_f75303@4d: +42.9°C peci-temp: +54.9°C ucsi_source_psy_USBC000:003-isa-0000 Adapter: ISA adapter in0: 5.00 V (min = +5.00 V, max = +5.00 V) curr1: 0.00 A (max = +3.00 A) spd5118-i2c-14-52 Adapter: SMBus I801 adapter at efa0 temp1: +49.2°C (low = +0.0°C, high = +55.0°C) (crit low = +0.0°C, crit = +85.0°C) ucsi_source_psy_USBC000:002-isa-0000 Adapter: ISA adapter in0: 0.00 V (min = +0.00 V, max = +0.00 V) curr1: 680.00 mA (max = +0.00 A) nvme-pci-a900 Adapter: PCI adapter Composite: +45.9°C (low = -5.2°C, high = +89.8°C) (crit = +93.8°C) acpitz-acpi-0 Adapter: ACPI interface temp1: +54.8°C temp2: +47.9°C temp3: +42.9°C temp4: +44.9°C temp5: +40.0°C acpi_fan-acpi-0 Adapter: ACPI interface fan1: N/A power1: N/A
I asked in a forum thread about what the different values represent. It seems most important are the coretemp
values (with Package id 0
being the overall processor temperature as reported by the CPU itself) and the values in cros_ec
which are sensors near components on the motherboard, including the current fan speed.
You can easily add any of those sensors to an XFCE panel using the xfce4-sensors-plugin
.