Surface Pro 8 安装 Arch Linux
最近实在忍受不了 Windows 放着什么也不干功耗都能到达 10W,所以打算给 Surface Pro 8 装个 Arch (with ZFS on LUKS) 看看。目前看起来使用 linux surface kernel 之后除了摄像头和以外都可以得到支持。Surface Pen 也有相应的软件可以支持压感和手写笔记,并且换成 Linux 后机器的续航明显提高了!现在不干什么事情的话亮度在 30% 的时候一般是 5-6W,普通浏览网页是 7W 左右的功耗。
这篇文章应该会持续更新。
准备工作
首先需要在 Windows 上给将要安装的系统分出一定的空间,这可以直接用 Windows 自带的 Create and format hard disk partitions 来完成。接下来需要准备一个启动盘,可以直接用现成的 Arch LiveCD,也可以自己按照文档做一个。
准备好启动盘和分配好空闲的硬盘空间之后关机,插入启动盘然后开机进 BIOS,松开开机键之后立即按住音量上键,等微软的图标闪了一下之后就可以进入 Surface UEFI 界面。在此关闭安全启动,然后选择同 USB 设备启动,就可以进入安装环境了。
安装系统
安装的过程基本上按照 Arch Wiki 的操作来就行了1。
准备分区和挂载对应目录
这里准备两个 zpool,分别是不加密的挂载到 /boot 的 bpool2,和在 LUKS 上的 rpool。如下所示,此处新增加的两个分区是 /dev/nvme0n1p5 和 /dev/nvme0n1p6。
nvme0n1 259:0 0 953.9G 0 disk
├─nvme0n1p1 259:12 0 260M 0 part
├─nvme0n1p2 259:13 0 16M 0 part
├─nvme0n1p3 259:14 0 698.6G 0 part
├─nvme0n1p4 259:15 0 1G 0 part
├─nvme0n1p5 259:16 0 8G 0 part
└─nvme0n1p6 259:17 0 244G 0 part
接下来创建 LUKS 设备3:
cryptsetup luksFormat --sector-size=4096 /dev/nvme0n1p6
cryptsetup open /dev/nvme0n1p6 root_crypt
然后创建 zpool:
zpool create \
-o ashift=12 \
-o autotrim=on \
-o compatibility=grub2 \
-o cachefile=/etc/zfs/zpool.cache \
-O devices=off \
-O acltype=posixacl -O xattr=sa \
-O compression=lz4 \
-O normalization=formD \
-O relatime=on \
-O canmount=off -O mountpoint=/boot -R /mnt \
bpool /dev/nvme0n1p5
zpool create \
-o ashift=12 \
-o autotrim=on \
-O acltype=posixacl -O xattr=sa -O dnodesize=auto \
-O compression=zstd \
-O normalization=formD \
-O relatime=on \
-O canmount=off -O mountpoint=/ -R /mnt \
rpool /dev/mapper/root_crypt
zpool export bpool
zpool export rpool
zpool import bpool -R /mnt -d /dev/disk/by-id
zpool import rpool -R /mnt -d /dev/disk/by-id
接下来创建 zfs datasets:
zfs create -o canmount=off -o mountpoint=none rpool/archlinux
zfs create -o canmount=off -o mountpoint=none bpool/archlinux
zfs create -o canmount=noauto -o mountpoint=/ rpool/archlinux/root
zfs mount rpool/archlinux/root
# /boot
zfs create -o mountpoint=/boot bpool/archlinux/root
# /home and /root
zfs create -o mountpoint=/home rpool/home
zfs create -o mountpoint=/root rpool/home/root
chmod 700 /mnt/root
# /var
zfs create -o canmount=off rpool/var
zfs create -o canmount=off rpool/var/lib
zfs create rpool/var/lib/log
zfs create rpool/var/lib/libvirt
安装 Arch
接下来在 /mnt 安装必要的包并 chroot:
pacstrap -K /mnt base base-devel linux-firmware
arch-chroot /mnt
然后将 ArchZFS 和 LinuxSurface 的仓库加入 /etc/pacman.conf
并且加入相关 key,并且安装相关的包:
pacman -Sy
pacman -S linux-surface linux-surface-headers iptsd intel-ucode
pacman -S zfs-dkms zfs-utils
修改 /etc/mkinitcpio.conf
,分别在 MODULES 和 HOOK 加入 zfs, luks 还有 surface 键盘相关模块,其中 surface 相关模块是为了在解密 LUKS 的时候可以用 Type Cover 键盘输入密码4。
MODULES=(zfs surface_aggregator surface_aggregator_registry surface_aggregator_hub surface_hid_core 8250_dw surface_hid surface_kbd intel_lpss intel_lpss_pci pinctrl_tigerlake)
HOOKS=(base udev autodetect modconf kms keyboard keymap consolefont block encrypt zfs filesystems fsck)
修改完成后使用 mkinitcpio -P
重新生成 initramfs,并且启用下列服务:
zpool set cachefile=/etc/zfs/zpool.cache rpool
zpool set cachefile=/etc/zfs/zpool.cache bpool
systemctl enable zfs.target
systemctl enable zfs-import.target
systemctl enable zfs-import-cache.service
systemctl enable zfs-mount.service
安装引导
下面安装 Grub:
pacman -S grub efibootmgr
因为 Grub 可能无法自动识别根目录对应的 zpool,所以需要手动修改指定(这在每次更新 Grub 后都需要进行):
sed -i 's/rpool=/rpool="rpool" #/' /etc/grub.d/10_linux
接下来修改 /etc/default/grub
中的内核参数,指定启动时需要解密的 LUKS 设备:
GRUB_CMDLINE_LINUX="cryptdevice=UUID=<uuid of your LUKS partition>:root_crypt:discard zfs_import_dir=/dev/disk/by-id/"
之后创建 /boot/efi
并且暂时退出 chroot 环境挂载 EFI 分区,然后再回到 chroot 安装 Grub:
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB
grub-mkconfig -o /boot/grub/grub.cfg
Update: 见评论区,root=
也可以直接指定。因为内核按照顺序解析参数,root=
交由 init/do_mounts.c
中的 root_dev_setup
处理,它干的事情只是把对应参数拷贝到某个缓冲区(就是一个 strscpy
),因此重复的参数最终起效的会是后面的那个5。
安装系统剩余部分
剩下的事情就是安装图形界面和创建账户了,这就和普通的安装过程没有区别了。
另外,如果觉得机器的内存不太够,也可以适当考虑打开 zram。
Secure Boot
目前安装的系统是不支持 Secure Boot 的,如果需要启用 Secure Boot,需要进行额外的一些操作6。例如使用 shim-signed。但是我暂时没有成功。实际是可以的,看了 menci 的这篇博文之后才发现我没认真读 archwiki,忘记把模块打包进 Grub 了7。
按照 archwiki 的描述8,需要把 Grub 模块全部嵌入 grubx64.efi
(具体的模块见 wiki),并且需要在该文件中包含 sbat section,假设 GRUB_MODULES
环境变量包含了所需的模块,那么前边的 Grub 安装需要改成
grub-install --target=x86_64-efi --efi-directory=esp --modules=${GRUB_MODULES} --sbat /usr/share/grub/sbat.csv
然后做一个 MOK 证书,用 sbsign
对 grubx64.efi
以及 zfs
模块签名即可7。
用 TPM 来解锁 LUKS
用 clevis 可以不用每次都输入密码解锁 LUKS:
clevis luks bind -d /dev/nvme0n1p6 tpm2 '{"pcr_bank":"sha256","pcr_ids":"1,7"}'
完事后按照上面 wiki 安装 mkinitcpio-clevis-hook
并且修改 mkinitcpio.conf
加入相应 hook。
Hibernate
虽然 linux-surface 里 hibernate 的支持是 ?
,但是实际上按照 archwiki 这部分是可以成功挂起硬盘的(需要 6.6.x 版本的内核,6.8.x 似乎有时候会失败)。此外,如果只想在 hibernate 的时候启用 swap,可以参考这里。
功耗控制
在 Windows 上 SP8 有三种不同的性能模式,再加上是 AC 还是电池供电有六种情况,它们的 PL1 和 PL2 分别如下:
性能模式 | PL1 / PL2 (AC) | PL1 / PL2 (BAT) |
---|---|---|
Recommend | 28W / 35W | 13W / 35W |
Performance | 28W / 45W | 28W / 35W |
Maximum Performance | 35W / 60W | 28W / 45W |
Linux 下面默认应该是 35W / 60W 这个状态,一不小心就过热了,可以在 crontab
写一个在启动时修改的脚本:
@reboot echo 28000000 > /sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw
@reboot echo 35000000 > /sys/class/powercap/intel-rapl:0/constraint_1_power_limit_uw
如果要在插入电源和使用电池间切换,那需要修改 udev 规则:首先编辑 /usr/local/bin/power_supply_change.sh
#!/usr/bin/bash
if [ $1 = "ac" ]; then
echo 28000000 > /sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw
echo 35000000 > /sys/class/powercap/intel-rapl:0/constraint_1_power_limit_uw
elif [ $1 = "bat" ]; then
echo 15000000 > /sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw
echo 35000000 > /sys/class/powercap/intel-rapl:0/constraint_1_power_limit_uw
fi
然后编辑 /etc/udev/rules.d/60-onbattery.rules
:
# Rule for when switching to battery
SUBSYSTEM=="power_supply",ENV{POWER_SUPPLY_ONLINE}=="0",RUN+="/usr/local/bin/power_supply_change.sh bat"
# Rule for when switching to AC
SUBSYSTEM=="power_supply",ENV{POWER_SUPPLY_ONLINE}=="1",RUN+="/usr/local/bin/power_supply_change.sh ac"
最后重新加载 udev 规则,并给相关脚本加入可执行权限:
chmod +x /usr/local/bin/power_supply_change.sh
udevadm control --reload-rules
此外,机器是不能长时间运行在 28W PL1 下的,过热的时候 surface 固件会把 CPU 频率设置为 200MHz 并且风扇开到最大,持续一段时间温度下来之后才会恢复9。可以参考这里的 issue 用 thermald 来控制温度,具体配置 /etc/thermald/thermal-conf.xml
如下(不确定和上面电池的 PL1/PL2 修改冲不冲突):
<?xml version="1.0"?>
<ThermalConfiguration>
<Platform>
<Name>Surface Pro 8 Thermal</Name>
<ProductName>*</ProductName>
<Preference>QUIET</Preference>
<ThermalZones>
<ThermalZone>
<Type>cpu</Type>
<TripPoints>
<TripPoint>
<SensorType>x86_pkg_temp</SensorType>
<Temperature>65000</Temperature>
<type>passive</type>
<ControlType>SEQUENTIAL</ControlType>
<CoolingDevice>
<index>1</index>
<type>rapl_controller</type>
<influence>100</influence>
<SamplingPeriod>10</SamplingPeriod>
</CoolingDevice>
</TripPoint>
</TripPoints>
</ThermalZone>
</ThermalZones>
</Platform>
</ThermalConfiguration>
Plasma 相关设置
窗口修改 titlebar 为黑色
如果在浅色主题下想要修改 Konsole 的标题栏为黑色,可以打开 System Settings - Window Rules, 添加如下规则10:
- Window class (application): Exact Match - org.kde.konsole
- Appearance & Fixes: Titlebar color scheme - Force - Breeze Dark
VSCode 也可以类似操作,然后在其内部修改 window.menuBarVisibility 为不可见就可以关闭菜单栏。
Konsole 修改 Tab Bar 菜单为深色
编辑 Consigure - Tab Bar / Splitters 下方的 Miscellaneous,选择 Use user-defined stylesheet 并且使用如下 CSS 文件:
/* Based on codemedic's work (https://gist.github.com/codemedic/f11cc460b8d9544f9afc) */
QWidget, QTabWidget::pane, QTabWidget::tab-bar {
background-color: #383c4a;
}
QTabBar::tab {
color: #777;
background-color: #383c4a; /* Pick from current color scheme from System Settings > Color > Modify > Colors > Window Background */
font-size: 11px;
padding: 6px;
}
QTabBar::tab:selected, QTabBar::tab:hover {
color: #d3dae3; /* Pick from current color scheme from System Settings > Color > Modify > Colors > Window Text */
background: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #d3dae3, stop: 0.001 #d3dae3, stop: 0.07 #383c4a);
/* Same as color Same as color Same as background-color*/
}
Super + Tab
如果要实现类似 Windows 下 Super + Tab
的功能,需要修改 System Settings - Desktop Effects 下面 Present Windows 项的快捷键。
VA-API 加速视频解码
参照 ArchWiki,需要安装 intel-media-driver
。之后可以播放视频并且通过 intel_gpu_top
查看 ENGINES 下方 Video 部分是否非零(这个程序在 intel-gpu-tools
里)。 对于 Microsoft Edge 还需要添加 --enable-features=VaapiVideoDecodeLinuxGL
11。对于 OBS Studio,硬件加速编码需要额外安装 onevpl-intel-gpu
,具体参考相关 issue。
字体配置
编辑 ~/.config/fontconfig/fonts.conf
:
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<!-- Serif -->
<alias>
<family>serif</family>
<prefer>
<family>DejaVu Serif</family>
<family>Source Han Serif CN</family>
<family>Noto Serif CJK SC</family>
</prefer>
</alias>
<!-- Sans-serif -->
<alias>
<family>sans-serif</family>
<prefer>
<family>DejaVu Sans</family>
<family>Source Han Sans CN</family>
<family>Noto Sans CJK SC</family>
</prefer>
</alias>
<!-- Monospace -->
<alias>
<family>monospace</family>
<prefer>
<family>DejaVu Sans Mono</family>
<family>Source Han Sans CN</family>
<family>Noto Sans CJK SC</family>
</prefer>
</alias>
</fontconfig>
另外,建议安装 ttf-hack-nerd
,会有一些图标的 patch。
中文输入
安装 fcitx5-im
并且按照这里的文档处理。如果使用 Microsoft Edge 或者 Google Chrome 需要增加如下参数,否则触控板是无法通过手势放大和缩小网页的。中文输入法安装 fcitx5-chinese-addons
即可。还有些例如 Sogou 输入法和中文维基百科的词库,可以参考 ArchWiki。
microsoft-edge-stable --enable-features=UseOzonePlatform,VaapiVideoDecodeLinuxGL --ozone-platform=wayland --enable-wayland-ime
Windows 上的相关软件
- 微信:现在有原生的 Linux 版本了,参见 ArchWiki
- Office:用 WPS,显示效果比较正常,如果使用 fcitx 可以参考这里的 fix。
- 笔记软件:Xournal++,支持 Surface Pen 以及压感,挺不错的
- EmbyTheater:这东西全屏的时候老是会拿窗口焦点,让人干不了别的事情,可以把对应代码删掉
sed -i 's/mainWindow.focus();//' /usr/lib/emby-theater/resources/app/main.js
Surface Pen 以及触摸屏相关
Surface Pen 距离屏幕比较近的时候一般关闭触摸屏功能会比较合适,这可以修改 /etc/iptsd.conf
的如下参数:
##
## Ignore all touch inputs if a stylus is in proximity.
##
DisableOnStylus = true
支持压感的笔记和 PDF 标注软件推荐 Xournal++。另外,ArchWiki 还有一些其它的推荐12。
LED 彩蛋
虽然摄像头不能工作,但是摄像头的指示灯还是可以被系统识别的,这里的 INT33BE_00::privacy_led
和 OVTID858_00::privacy_led
分别是前后两个摄像头旁边的灯。
$ ls /sys/class/leds/
input32::capslock input32::scrolllock OVTID858_00::privacy_led SMO55F0_00::privacy_led
input32::numlock INT33BE_00::privacy_led phy0-led
既然可以控制它,那就可以用它来干点好玩的事情,比如用来显示网卡的工作情况:
led="INT33BE_00::privacy_led"
modprobe ledtrig-netdev
echo netdev > "/sys/class/leds/$led/trigger"
echo 1 > "/sys/class/leds/$led/tx"
echo 1 > "/sys/class/leds/$led/rx"
echo 1 > "/sys/class/leds/$led/link"
echo wlp0s20f3 > "/sys/class/leds/$led/device_name"
参考资料
-
Grub2 好像也可以处理完全加密的情况,但是这里还是分开成 bpool 和 rpool 两个部分。 ↩
-
此处可以参考 dm-crypt/Device encryption - ArchWiki (archlinux.org)。 ↩
-
Disk Encryption · linux-surface/linux-surface Wiki (github.com) ↩
-
Unified Extensible Firmware Interface/Secure Boot - ArchWiki (archlinux.org) ↩
-
https://wiki.archlinux.org/title/Chromium#Hardware_video_acceleration ↩