- NVMe moved into the new laptop, no reinstall.
- Stack: LUKS2 → LVM → btrfs/snapper, systemd-boot, hibernate to a 40 GB swap LV, sway + kanshi + waybar.
- Everything identifying the system (LUKS UUID, VG name, FS UUIDs) is on the disk. Only the device-specific layer needed work.
Old: ThinkPad X13 Gen 1
- Ryzen 7 PRO 4750U, amdgpu, acpi-cpufreq
- 1920×1080, scale 1.0
- Battery 78 % health, 1389 cycles
New: Framework Laptop 13 Pro
- Core Ultra X7 358H (Panther Lake), Arc B390 (xe), intel_pstate + HWP
- 2880×1920 @ 120 Hz
- 73.1 Wh, BIOS 03.02, since 4 Nov 03.04
Preparation (on the X13)
- Intel packages installed in advance:
intel-ucode,sof-firmware,vulkan-intel,intel-media-driver,thermald. Both microcode packages stay; disk boots on either CPU. - modprobe leftovers deleted:
i915.conf, amdgpu overdrive mask, iwlwifi tweak, thinkfan blacklists. MODULES=()inmkinitcpio.conf(wasamdgpu). Autodetect picks the hardware.- USB wake rule: X13 PCI addresses removed, RTL8153 vendor/product line kept.
- sway:
input type:touchpadinstead of a device identifier. - NetworkManager:
interface-name=removed from ~100 Wi-Fi profiles. - Power script: Intel branch added, tested against mocked sysfs trees.
First boot
- Boot via firmware fallback
\EFI\BOOT\BOOTX64.EFI(new NVRAM knows no systemd-boot), fallback initramfs (regular one had only X13 modules). - No failed units, no AppArmor denials.
- RTC at 2025-01-01. NTP fixed it; early journal lines carry wrong timestamps.
| Component | Out of the box |
|---|---|
| Kernel / microcode | linux 7.2.7, intel-ucode early (0x11a → 0x11c), also from fallback initramfs |
| GPU | xe, Arc B390 (Xe3), GuC 70.72.1, HuC 10.3.3, GSC loaded |
| VA-API | iHD 26.2.4: AV1, HEVC, VP9 decode + encode |
| Display | eDP-1 2880×1920, sway auto-scale 2.0 |
| Wi-Fi | iwlwifi, CNVi 8086:e440 |
| Audio | SOF, PipeWire sink + source, mic ok |
| Bluetooth / webcam | controller up; /dev/video0, /dev/video1 |
| Fingerprint | Goodix MOC via fprintd |
| EC / sensors | cros_ec: temperatures, fan, battery via hwmon |
| Storage | LUKS discard reaches the LVs, 40 GB swap, zswap on |
| Power | intel_pstate + HWP, ppd with platform_profile (low-power / balanced / performance), suspend = s2idle only |
| Expansion cards | HDMI card, USB-Ethernet RTL8153 |
Day-one changes
/etc/modules-load.d/:acpi-cpufreqout (not on intel_pstate),acpi_callout (tainted kernel for years).- Packages removed:
thinkfan,acpi_call-dkms,xf86-video-amdgpu,radeontool.vulkan-radeonstays (steam, zed depend on it). - Bootloader into the new NVRAM. Entries, cmdline,
resume=, mapper paths unchanged:mkinitcpio -P # regular initramfs now has xe, iwlwifi, SOF bootctl install # "Linux Boot Manager" into NVRAM efibootmgr -B -b 0003 # dead Windows entries from the efibootmgr -B -b 0004 # Framework's original SSD thermald: installed but disabled → enabled.- Regulatory domain:
country 00: DFS-UNSETin dmesg →WIRELESS_REGDOM="DE"in/etc/conf.d/wireless-regdom. Check:iw reg get. - Lid is
LID0, notLID. Lid-close script globs/proc/acpi/button/lid/*/state. Old path: hibernate timer would never arm. - Taint after cleanup: 12288, only unsigned vboxhost DKMS modules.
HiDPI
- Console: 16 px default = 240×67 characters.
FONT=latarcyrheb-sun32(kbd) in/etc/vconsole.conf, thenmkinitcpio -P;sd-vconsoleembeds the font (LUKS prompt). - sway: auto scale 2.0 = 1440×960 logical, too small.
output eDP-1 scale 1.5= 1920×1280 logical, X13 width plus 3:2 height. - kanshi profiles:
mode 2880x1920@120Hz … scale 1.5. External monitors at 1.0. - swaylock:
scaling=centerfor the 1920×1080 lock image.
AC / battery switching
- One script,
/usr/local/bin/power-source ac|battery, one udev rule. Hardware detected from sysfs, same files on both laptops.
# /etc/udev/rules.d/61-power-source.rules (AC* matches "AC" on the X13 and "ACAD" on the Framework)
SUBSYSTEM=="power_supply", KERNEL=="AC*", ATTR{online}=="0", RUN+="/usr/local/bin/power-source battery"
SUBSYSTEM=="power_supply", KERNEL=="AC*", ATTR{online}=="1", RUN+="/usr/local/bin/power-source ac"
- Platform profile:
powerprofilesctl setpower-saver / performance. On Intel, ppd also sets EPP. - cpufreq by driver:
acpi-cpufreq(X13): boost 0/1, governor conservative/schedutil.intel_pstate(FW13): governor stays powersave, onlyintel_pstate/no_turbotoggled (1 = turbo off).
- xe GPU:
/sys/class/drm/card*/device/tile*/gt*/freq0/power_profile=power_savingon battery,baseon AC. Missing on amdgpu → skipped. - Backlight: first writable
/sys/class/backlight/*, 20 % on every switch.brightnessctlfails in the udev sandbox → raw sysfs. vm.dirty_writeback_centisecs1500 / 500.- Recoll indexer:
systemctl --user stopon battery,starton AC. Drop-inConditionACPower=truefor logins on battery. - Logging:
logger -t power-source. Debug:journalctl -b -u systemd-udevd | grep power-source.
Panel refresh: 120 Hz on AC, 60 Hz on battery
~/.bin/panel-refresh: readsAC*/online, runsswaymsg "output eDP-1 mode 2880x1920@${hz}Hz".- Not callable from udev directly. Even via
runuser: Unable to send IPC header (sandbox, no sway socket). - Fix: transient unit in the user’s systemd manager, which has
SWAYSOCKvia sway’simport-environment:for sock in /run/user/*/sway-ipc.*.sock; do uid=${sock#/run/user/}; uid=${uid%%/*} user=$(id -nu "$uid") || continue home=$(getent passwd "$user" | cut -d: -f6) systemd-run -M "$user@" --user --quiet --collect --wait "$home/.bin/panel-refresh" done - kanshi resets to 120 Hz on every profile switch → every profile ends with
exec ~/.bin/panel-refresh. - Disabled since 27 Oct (
exit 0): every runtime modeset made the page-flip bursts worse, see below. Panel stays at 120 Hz. - Known gap: charger plugged in while waking with lid closed → eDP-1 still off, panel-refresh fails, 60 Hz until kanshi re-applies on lid open.
Charge limit
framework_tool --charge-limit 80as root. Stored in the EC, survives reboots, no service (unlike the ThinkPad’s sysfs threshold).
Wake sources
- Goal: only lid, power button, internal keyboard wake from suspend. No USB, Thunderbolt, dock hotplug.
- Addresses from the last column of
/proc/acpi/wakeup.
# /etc/udev/rules.d/52-usb-wakeup.rules (Framework 13 Pro, Panther Lake)
SUBSYSTEM=="usb", ATTR{idVendor}=="0bda", ATTR{idProduct}=="8153", ATTR{power/wakeup}="disabled" # dock Ethernet
SUBSYSTEM=="pci", KERNEL=="0000:00:14.0", ATTR{power/wakeup}="disabled" # XHCI: camera, fingerprint, USB-2 side of the cards
SUBSYSTEM=="pci", KERNEL=="0000:00:0d.0", ATTR{power/wakeup}="disabled" # TXHC: USB-3 side of the four USB-C ports
SUBSYSTEM=="pci", KERNEL=="0000:00:0d.2", ATTR{power/wakeup}="disabled" # TDM0 Thunderbolt NHI
SUBSYSTEM=="pci", KERNEL=="0000:00:0d.3", ATTR{power/wakeup}="disabled" # TDM1
SUBSYSTEM=="pci", KERNEL=="0000:00:07.[0-3]", ATTR{power/wakeup}="disabled" # TRP0-3 Thunderbolt root ports
- PCI
power/wakeupis a value, not a toggle like/proc/acpi/wakeup. Rule is idempotent. - After
udevadm trigger: all eight entries*disabled. Keyboard wake works (/sys/power/pm_wakeup_irq= 1, i8042 via EC). - Hibernate unaffected.
- Opposite case: Dell mouse 413c:3010 on the USB-A card wakes from USB autosuspend only on click →
power/control=onexception in51-runtime-pm-exceptions.rules.
Battery tuning
- Method: unplugged, 3 min idle, usual apps open (Vivaldi, Firefox, Thunderbird, terminals).
- Package:
turbostat. System: mean ofBAT1 current_now × voltage_now. - No
Pkg%pc10on Panther Lake in turbostat;CPU%LPIinstead.
| Measurement | System | Package | CPU%LPI |
|---|---|---|---|
| 8 Oct, before | ≈ 5.3 W | 1.14 W | 24 % |
| 9 Oct, after all changes | 4.92 W | 1.11 W | 18 % |
| 9 Oct evening, baseline for single tests | 4.11 W | 0.91 W | 24 % |
- Under 0.2 W = noise (open tabs). SoC ~1 W, rest is display and platform.
- 73.1 Wh at 80 % limit → 12 to 14 h. GPU RC6 99 %, PSR2 with selective fetch active.
Helped
- Fewer display commits (powertop: ~2.6/s
intel_atomic_commit_work): waybarcpu/memoryinterval 10 s, custom module 60 s with signal refresh, btopupdate_ms = 5000. kernel.nmi_watchdog = 0in99-hardening.conf(a separate 90- file was overridden by the 99 file).- btrfs:
nodiscardinstead ofdiscard=asyncon all six mounts; weeklyfstrim.timer. - xe power profile and Recoll switching (above).
- sway: screen off after 5 min instead of ~16.
inhibit_idlerule for mpv dropped; paused window blocked screen-off, mpv inhibits by itself while playing.
Tested, rejected
- intel-lpmd 0.1.0. Default config does nothing (all
*Def= -1).PowersaverDef=0pinned user.slice and system.slice to LPE cores 12–15: CPU%LPI 0 %, package 2.16 W, system 8.94 W. Shipped Panther Lake config references CPUs 16–19, which don’t exist here. Upstream main: Panther Lake moved to mode -1, SoC power slider instead; slider already at 6 (max efficiency) via ppd power-saver. Package removed. - HDMI card pulled: 4.11 W vs 4.14 W. Already in USB suspend.
- Panel at 40 Hz (custom mode, stable): 4.16 W, no gain. PSR2 already holds static images. EDID: LCD, VRR 30–120 Hz, dark theme saves nothing either.
adaptive_syncnot tried. - Bluetooth off: not tested, needed for headphones.
Rejected page flips on xe (EBUSY)
- Symptom, in the sway log, for eDP-1 and DP-1:
[wlr] [backend/drm/atomic.c:82] connector eDP-1: Atomic commit failed: Device or resource busy [sway/desktop/output.c:307] Page-flip failed on output eDP-1 - Each rejected commit = one dropped frame. Visible: cursor and video freeze for hundreds of ms. Audio and input unaffected, scanout only.
- First seen 7 Oct 2026, 73 h into a session on 7.2.8. Zero occurrences 29 Sep to 6 Oct. No graphics package updated in that window. Persistent since.
- Kernel log: nothing. No
flip_done timed out, no FIFO underrun, no GPU reset, no WARNING. GPU load ~1 %, PSI for the compositor cgroup 0. - Setup: sway 1.12, wlroots 0.20.2, Mesa 26.2.5, linux-firmware 20260916. eDP-1 2880×1920@120 scale 1.5, DP-1 Lenovo T24m-29 1920×1080@75 via USB-C/DP. Firmware: DMC 2.36, GuC 70.72.1, HuC 10.3.3, GSC 105.0.2.1397.
| Kernel | Fresh boot | After hibernate (S4) resume |
|---|---|---|
| 7.2.9 / 7.2.11 | ~25/min from the first minute, bursts to ~500/min; 5100–11500/day | same |
| 6.18.58 LTS | ~2/min, no freezes | 20–100/min within 10–60 min, bursts 300–400/min, until reboot |
| 7.3.0 (v7.3, unmodified) | ~4/min, no freezes | same pattern, ~9600 in 4 h |
| 7.4.0-rc1 + BIOS 03.04 (from 4 Nov) | 1–5/min | no degradation in 9 cycles, baseline <3/min |
- Same userspace, displays and config across kernels. BIOS 03.02 for all rows except the last.
- Trigger on 6.18/7.3: S4 resume, persistent until reboot, 7 of 7 cycles.
- S3 (s2idle) on 7.3.x: transient. Burst of 300–2000 for 20–60 min after resume, then <1/min. Five cycles on one boot, same pattern. Suspend never heals an already degraded boot.
- Hibernate from an already degraded state: no change. swayidle disabled before hibernating (no DPMS cycle on resume): delays, does not prevent.
- DPMS off/on or output disable/enable on a degraded system: immediate burst, hundreds in 2–4 min. On a fresh boot: 4-minute burst, decays.
- Single output (external unplugged before hibernating): still 10–30/min after resume.
- Distribution: ~30 % within ±1.5 s of a pointer move between outputs (~15 % of the time), ~70 % without any output switch.
Ruled out
xe.enable_psr=0 xe.enable_dc=0 xe.enable_dsb=0, 15–24 Oct: no change in rate.enable_dsb=0added*ERROR* Atomic update failure on pipe A84×, otherwise never seen.- PSR off at runtime via
i915_edp_psr_debug: no change. - External at 60 Hz, both outputs at 60 Hz: no change (paired 10-min samples in the same degraded state: 101 at 120/75 vs 263 at 60/60).
- Software cursor
WLR_NO_HARDWARE_CURSORS=1(cursor planesfb=0indri/0/state): same rate. Only the distribution changed; with the HW cursor the error was mostly on the output the pointer had just left. - wlsunset gamma updates: no correlation. IO pressure: unrelated backup job, no correlation. Input: evdev events without gaps during freezes.
Interpretation, status
- EBUSY without kernel message: non-blocking commit meets a previous commit still pending on the same CRTC. Completion late or lost.
- Display state restored on S4 resume looks wrong, every later modeset keeps it wrong. 7.2.x: bad from boot.
- Refresh rates, PSR, DC states, DSB, HW/SW cursor, matched modes all varied without effect. Pipe-to-pipe refresh mismatch unlikely.
- Related upstream: drm/xe #9582 (PTL async flip timeout after PSR2 DEEP_SLEEP), #9253 (PTL VRR stutter, DSB poll errors). Not identical: no timeout logged here, PSR off doesn’t help.
- Report for the drm/xe tracker drafted, not filed. Attachments collected: dmesg,
journalctl -b -k,i915_display_info, both EDIDs, xe module parameters.i915_vbtread hung in the degraded state, omitted. - Snapshot script for state captures: debugfs dumps (dmc, power domains, PSR status, display info, MST, frontbuffer),
/proc/interruptsdelta over 10 s, 30 s ofdrm.debug=0x14, page-flip count of the last 2 min. - Not bisected. Workaround on 6.18/7.3: suspend instead of hibernate, reboot after a hibernate.
- Counting:
journalctl -b -t sway | grep -c 'Page-flip failed'.
Since 7.4-rc1 + BIOS 03.04
- 4 Nov: BIOS 03.02 → 03.04 (16 Oct 2026) and kernel 7.4.0-rc1 (linux-mainline) in the same reboot.
- One boot, 4.5 days, 9 hibernate cycles, several dock changes: no persistent degraded state. Before: 7 of 7.
- Baseline: median 2 per minute in minutes with any error, most minutes zero. Freezes no longer noticed.
- Total still 16 200 in 4.5 days. 62 % of that in 22 minutes. Three isolated bursts account for ~10 200: 5 Nov 06:35–06:42 (2500), 7 Nov 15:46–16:07 (5400), 8 Nov 09:49–10:01 (2300). No resume or modeset before them, cause unknown.
- Load tests during the first burst hunt:
stress -c 16for 120 s, 0 errors.ffmpeg hevc_vaapiloop for 120 s, 1 error. HandBrake rerun: not reproducible. - Kernel or firmware: not separated. 7.3.1 + BIOS 03.04 never tested.
- Tests ended 7 Nov. Hibernate on lid close back in use. Report stays unfiled unless it returns on 7.4 final.
- Remaining stutter after hibernate was a different problem: swap-in of desktop apps. Hibernate pushes 3–4 GB into swap before writing the image. Signature: libinput lag lines and cgroup
io.pressure, page flips quiet.vm.swappiness=10helps partly.
sysctl hardening
- Not hardware-specific, same pass. One file,
/etc/sysctl.d/99-hardening.conf. 99 wins over/usr/lib/sysctl.d/50-default.conf.
kernel.kptr_restrict = 1 # hide kernel addresses from non-root; root keeps perf/bpftrace/oops analysis
kernel.nmi_watchdog = 0 # not security: frees a perf counter, fewer interrupts
kernel.kexec_load_disabled = 1 # no other kernel at runtime; one-way until reboot, kdump unused
net.core.bpf_jit_harden = 2 # constant blinding for all BPF JIT code, also root's
fs.protected_fifos = 2 # sticky dirs: refuse O_CREAT on someone else's FIFO
fs.protected_regular = 2 # same for regular files; 2 = also group-writable sticky dirs
dev.tty.ldisc_autoload = 0 # no auto-load of line disciplines via TIOCSETD (CVE-2017-2636 class)
net.ipv4.conf.all.send_redirects = 0 # not a router
net.ipv4.conf.default.send_redirects = 0 # interfaces created later
net.ipv4.conf.*.send_redirects = 0 # interfaces that already existed; kernel evaluates "all OR interface"
- Kernel lockdown off: Arch doesn’t enforce it with Secure Boot, and it breaks hibernate.
- Apply:
sysctl --system. Check:sysctl kernel.kexec_load_disabled net.core.bpf_jit_harden kernel.nmi_watchdog.
Journal noise, no action
spd5118 20-0050: PM: dpm_run_callback(): spd5118_resume returns -6on every resume. RAM SPD sensor slow after s2idle; only memory temperature/EEPROM reads.ucsi_acpi USBC000:00: con3: failed to register partner alt modes (-5),unknown error 256. Known on Framework Panther Lake; USB-C and HDMI card work.Bluetooth: hci0: Failed to send Intel_Write_DDC (-95). Controller works.x86/CPU: Model not found in latest microcode list. Microcode is loaded.iio-sensor-proxy: Buffer /dev/iio:device0 did not have data within 0.5s. Orientation sensor, once at start.50-runtime-pm.rules … Could not chase sysfs attribute. USB hubs withoutpower/control; rule skips them.
Takeaways
- Moving the disk works. Device layer: regenerate initramfs, register bootloader in NVRAM, two modules and four packages out, one hardcoded ACPI path.
- Power-management glue against sysfs, not device names. Survives the hardware change.
- HiDPI (console font, compositor scale, refresh switching) took longer than all driver work combined.
- One hardware problem cost weeks: xe page flips after hibernate. Gone with 7.4-rc1 + BIOS 03.04, cause never identified.