Robust yet fragile —— 主力设备迁移记

Last updated on August 22, 2023 pm

高考结束,我终于迎来了第一次数码设备自由。为之后移动办公做准备,我购买了一台性能相对强劲的轻薄本:Lenovo Thinkbook 16p Gen4,搭载 13 代移动 intel i5 和 NVIDIA RTX4060。
虽然这台设备如我所期待的一样优雅而灵活,但无缝迁移我当前的系统和 Workflow 还是颇废了我一番周折。本文作为这次探索的记录。

System Info

从数据开始——龟速的 dd 与不稳定的 pv

标题的 Robust,正说的是 Linux 系统在硬件更换时的极度鲁棒性。现代 Fedora 安装会将整个 EFI 分区和系统根分区都以 UUID 的形式记录在 /etc/fstab 中,而 UUID 作为分区的固有性质,只要在迁移时以磁盘镜像的形式将包括分区表的部分全部迁移,就可以完美的保证启动 Mapping 关系不损坏。而对于硬件,Linux 的驱动方案是 “Batteries Included”。比起 Windows 系统在安装时安装且仅安装一套系统硬件的关键驱动,Linux 内核几乎自带了所有硬件的驱动。在开机时,只需要根据总线上报的硬件 ID 加载驱动即可。因此,一次无缝的 Linux 迁移可以简化为:源机全硬盘镜像 -> 目标机镜像覆盖 -> 调整未分区空间 -> 启动。
这样的优点比起全新安装再数据迁移显而易见。Linux 系统是一个非常依赖累积魔改的环境,使用者长期积累下来的配置文件等若无自行文档记录几乎不可能完全还原。当然缺点也同样存在,例如依赖于源机环境的一些部件(比如 EFI Boot Screen)不一定会跟随新系统而更新。不过在我的体验中,这样的问题实际上并不多见,因为大部分受到影响的部件都会在一次大版本更新后得到恢复(或者手动重新生成一个 initramfs)。同样,这也是 Linux 系统相较于 Windows 的优势:即使在折腾中部分损坏或者体验降级,关键的系统部件在一次大版本更新后就能基本完全恢复。
提到全盘镜像,很多人都会立刻想到 dd 工具。dd 工具以字节块(Block)为单位在 Linux 系统的抽象文件空间中进行字节对字节准确的拷贝。尽管这一工具有着广泛应用,但是其体验并非那么的优秀。正如这篇 StackOverflow 问答中所言,dd 默认的分块策略太过保守,仅有 512 字节。虽然这一设置是为了兼容旧硬盘(其物理扇区大小通常为 512 字节,因此扇区访问和 Buffer 发送可以交替异步),但是在 4k 字节已经成为主流、扇区读取彻底脱离 I/O 瓶颈的今天,dd 的默认分块策略已经不再使用。即使这一设置被适当更改,dd 依然在大量传输时十分缓慢。其访问依赖 Linux 抽象文件空间的文件 I/O,速度上天生存在劣势。在动辄几百个 GiB 甚至几个 TiB 的全盘镜像场景下,直接使用其他支援原始字节的工具调用字节管道,例如 catcp 等能够取得更加优秀的速度。

`dd`: Slowwwwwww
`pv`: Much better

在我的这次使用中,还出现了一个额外的问题。我需要备份笔记本自带的原装 Windows 系统(其中包含关键的 Lenovo 原厂恢复工具),然而这一份备份显然需要一个中间的压缩方案(原装的 SSD 原始字节读出后为 1000GB,压缩后仅为 26GB)。压缩,同样,会严重影响中间速度。为了充分利用目标机的多线程调度,我选择了 gzip 的多线程变体 pigz 进行中间压缩。
最后得到的命令如下 (pv 可以提供一个可视化的进程进度条):

  • 镜像:pv <device> | pigz -9 > <file>
  • 还原:pigz -cd <file> | pv > device

但是同样的,这一方案本身也存在自己的缺点。无论是 dd 还是 pv,断点续传都不能达到。这意味着如果传输过程中受到了干扰,就需要重头开始。我这个倒霉蛋体质当然也不例外:两个数据过程我都重试了三次。
数据导入,启动系统。当然不会有那么顺利:掉进了 Emergency mode。当然这最终是一个小 Bug:我的系统在 /etc/fstab 中设定了自动挂载几块分做不同用途的硬盘,迁移过程中有几块系统启动硬依赖的盘丢失了挂载。暂时禁用后,成功进入了系统。

微调系统——五年未修的 BIOS Bug

然而,又一个问题随即出现:独立 GPU 没能被成功识别。当时的我只从 NVIDIA PRIME offload 没能成功启动意识到了存在问题,没能发现问题的根源,以为是系统缺失了 GLX 库文件,便想到可以通过 BIOS 设置强制系统仅使用独立 GPU。就是这样一个小小更改,让我掉进了近一天的 Bug 漩涡。
修改完毕,重启系统。屏幕没有如正常时亮起。没关系,也许是什么奇怪的问题,再试一遍。还是没有亮起,屏幕甚至连 miniLED 通电应有的屏幕点亮都没有发生。一种巨大的恐慌突然席卷上来:我意识到自己估计触发了什么奇怪的 Bug。
一顿搜索,最终在 v2ex 上找到了一个相似的问题回报。看起来不是什么好搞的问题。然后就是如下一系列线索,和其所确定的情况:

  • 启动时键盘灯亮起的顺序(自检)和正常启动没有什么区别 -> 这个 Bug 不影响正常启动,只是禁用了显示输出;
  • 开机不按任何按键等待一会后再短按电源键,不会直接关闭电源 -> 应该正常进入了系统;
  • 开机快速短按 Enter,等待一会后键盘灯不再亮起,短按电源键可以直接关闭电源 -> 进入 BIOS 设置的接口应该还是可用的;

得出结论:这个 Bug 没有影响任何启动结构,只是禁用了显示输出。那么,我们就有了一个希望:可以通过盲操进入 BIOS,将这个设置修改回来/重置设置。

当然,这个结论没有立即起效。当时的我先后做了如下尝试:

  • 断开外界电源后长按三十秒电源键释放静电:显然没有什么卵用;
  • 开机快速短按 Enter,键盘灯不再亮起后短按 F1(进入 BIOS 设置),尝试通过盲操修改对应设置:并没能成功保存;
  • 用短针短戳位于 D 面的 Novo 孔试图进入 BIOS 设置,盲操修改:同上;
  • 用短针长按 Novo 孔试图重置 BIOS 设置:并没有这个功能;
  • 开机快速短按 Enter,键盘灯不再亮起后短按 F1(进入 BIOS 设置),尝试通过 F9 重置到默认设置:重启后没有反应;(注意这一条,后面会有用

就这样一直折腾到了凌晨三点,并没有起到任何作用。气恼的我只能带着自己的怒气先行睡下,第二天再找策略。

第二天,我打通了 Lenovo 的客服热线。对接的客服也不外乎是上述这些操作步骤,当然也并没有起到作用。最终,客服觉得线上不能解决这个问题,向我提供了线下维修点的联系方式——远在 200km 之外。没办法,小城市,只能等两天亲自带上设备去一趟了。于是这一天在原来设备上 Fedora LiveCD 和我硬盘的媒体库存中度过。
直到下午六点,我又一次打开了笔记本重试。我又一次重试了上述的 5。在过程中,我突然意识到,F9 重置到默认后,会不会还需要 F10 保存?
BIOS 界面
于是:
开机 -> 快速短按 Enter 到键盘灯全部熄灭 -> F1(按且仅按一次) -> F9 -> Enter(确认重置) -> F10 -> Enter(确认保存)

然后,电源灯灭了!(成功保存电源灯会直接熄灭,进行冷重启)

再次开机,我激动的等待着。屏幕很快亮起,出现的是 Lenovo 的红色徽标。

如果你也遇到了这个可笑的问题,这里是解决方案。截止发布时,全网没有其他这一问题的解决方案,除非送修换主板。

GPU——又见自断手脚的限制

又一次成功进入了系统,短暂的兴奋后,现实的问题又会到了眼前。独立 GPU 没有被识别的问题依然没有解决。那么也没有办法,只能从底至顶,一点一点排查。
跟随着互联网上各路来源的教程,我一点一点排查,但是越排查越不对:xrandr --listproviders 应该显示已有的两个 GPU,但却一个也没有,glxinfo | grep vendor 返回 Bad Value 错误,甚至连 nvidia-smi 都不再显示 GPU……直到最后,我突然想到,不会是压根就没有载入驱动吧?
拉出 Log 一看,还真是。
相关 Log
重新安装,问题依然存在。正在烧死脑细胞的过程中,突然发觉,如果是已经配置好的 Secure Boot MoK Key 被清理了呢?
modprobe nvidiaKey was rejected by servicepesign 看一眼 EFI Shim 签名,也丢了。懂了,重新配置一遍 Secure Boot 就解决了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
GUID="$(uuidgen)"

mkdir keys

# Prepare keys from xca database
# ├── db.crt
# ├── db.key
# ├── KEK.crt
# ├── KEK.key
# ├── PK.crt
# └── PK.key
# (I use a xca DB to manage all my self-signed key)

mkdir backups

# Do fullchain backup
efi-readvar > backups/efi-original-keys.txt
efi-readvar -v PK -o backups/PK.original.esl
efi-readvar -v KEK -o backups/KEK.original.esl
efi-readvar -v db -o backups/db.original.esl
efi-readvar -v dbx -o backups/dbx.original.esl

# Generate PK, KEK and Sign db for insertion

cert-to-efi-sig-list -g "$GUID" keys/PK.crt keys/PK.esl
sign-efi-sig-list -g "$GUID" -t "$(date +'%F %T')" -k keys/PK.key -c keys/PK.crt PK keys/PK.esl keys/PK.auth

cert-to-efi-sig-list -g "$GUID" keys/KEK.crt keys/KEK.esl
sign-efi-sig-list -g "$GUID" -t "$(date +'%F %T')" -k keys/PK.key -c keys/PK.crt KEK keys/KEK.esl keys/KEK.auth

cert-to-efi-sig-list -g "$GUID" keys/db.crt keys/db.esl
sign-efi-sig-list -g "$GUID" -t "$(date +'%F %T')" -k keys/KEK.key -c keys/KEK.crt db keys/db.esl keys/db.auth

efi-updatevar -f keys/db.auth db
efi-updatevar -f keys/KEK.auth KEK
efi-updatevar -f keys/PK.auth PK

# Sign main boot EFI binaries(shim)

cp /boot/efi/EFI/fedora/shimx64.efi backups/
pesign --remove-signature --signature-number=0 --in=/boot/efi/EFI/fedora/shimx64.efi --out=/boot/efi/EFI/fedora/shimx64.efi.empty
sbsign --key keys/db.key --cert keys/db.crt --output /boot/efi/EFI/fedora/shimx64.efi /boot/efi/EFI/fedora/shimx64.efi.empty

# Reboot

# Copy MoK key to /etc/pki/akmods/certs and .../keys, point the soft link (public_key.der) to it.

# Enroll MoK key

mokutil --import /etc/pki/akmods/certs/public_key.der

# Reboot, then confirm in MoK interface.

于是这次迁移就到这里。又一次完美迁移的 Fedora,支持新系统上所有的功能,包括指纹、IR 人脸,多显卡输出等等等等。

后记

硬件素质上来讲,这台设备绝对过硬,是一台不折不扣的水桶机。对于我这样不追求极限性能的中度办公/轻度娱乐党来说已然足够。此外,其素质优秀的屏幕、手感优秀的键盘、充足的外置接口、便捷的认证机制等等都为整套系统的体验加了不少分。虽然 BIOS 存在这样一个十分可笑且长久未修的问题,但是我依然相信这台设备能够为我未来更多的创造赋能。


Robust yet fragile —— 主力设备迁移记
http://elfile4138.moe/2023/06/New-Device/
Author
Matrew File
Posted on
June 28, 2023
Updated on
August 22, 2023
Licensed under