将ThinkPad X61移植到Coreboot
摘要
一篇博客文章,详细介绍了使用Claude Opus 4.6进行AI辅助逆向工程,将ThinkPad X61移植到coreboot开源固件的过程。
暂无内容
查看缓存全文
缓存时间: 2026/06/09 08:42
# 将 ThinkPad X61 移植到 coreboot
来源:https://blog.aheymans.xyz/post/thinkpad_x61/
## 目录
- 引言:我的 IBM/Lenovo ThinkPad 成瘾史 (https://blog.aheymans.xyz/post/thinkpad_x61/#headline-1)
- AI 辅助逆向工程 (https://blog.aheymans.xyz/post/thinkpad_x61/#headline-2)
- 传统方式从厂商 BIOS 中提取信息 (https://blog.aheymans.xyz/post/thinkpad_x61/#headline-3)
- 配置 AI 代理工具及固件发现 (https://blog.aheymans.xyz/post/thinkpad_x61/#headline-4)
- 氛围逆向工程 (https://blog.aheymans.xyz/post/thinkpad_x61/#headline-5)
- LLM 辅助逆向工程 (https://blog.aheymans.xyz/post/thinkpad_x61/#headline-6)
- 我的刷机和测试工作流程 (https://blog.aheymans.xyz/post/thinkpad_x61/#headline-7)
- 将 libgfxinit 移植到 GM965 (https://blog.aheymans.xyz/post/thinkpad_x61/#headline-8)
- 上游化代码,修复更多错误 (https://blog.aheymans.xyz/post/thinkpad_x61/#headline-9)
- 未来会怎样? (https://blog.aheymans.xyz/post/thinkpad_x61/#headline-10)
- AI 辅助逆向工程 (https://blog.aheymans.xyz/post/thinkpad_x61/#headline-11)
- 移植到 fstart (https://blog.aheymans.xyz/post/thinkpad_x61/#headline-12)
## 引言:我的 IBM/Lenovo ThinkPad 成瘾史
十多年前,我得到了我的第一台 ThinkPad x60。通过阅读 GNU Emacs 编辑器中的关于 GNU 页面 (https://www.gnu.org/gnu/thegnuproject.html),我逐渐对自由软件产生了兴趣。当时的自由软件(现在更是如此)已经相当可用,通常很少需要闭源软件。自由软件欠缺的一个领域是固件,这促使我尝试在那台 ThinkPad x60 上运行 libreboot。几年后,我成为了 coreboot 贡献者,并最终因此获得了 9elements 的一份工作。在这段旅程中,我积累了一笔相当可观的 ThinkPad 收藏。我想要一台更快且支持 64 位的机器,于是买了一台 ThinkPad x200,用了几年。又过了几年,我得到了一台 ThinkPad x220,因为 Sandy Bridge 芯片比 x200 里的 Core 2 Duo 快得多。为了移植或改进现有的 coreboot 移植,我还收到了 ThinkPad x201 和 R500。几年前,有人找到了绕过 Intel Skylake/Kabylake 上 Boot Guard(deguard (https://github.com/coreboot/deguard))的方法,于是我又买了一台 ThinkPad t480,用得非常开心。
/thinkpad-addication.jpg
有没有人知道治疗这种成瘾的好康复中心?
在收藏 ThinkPad 的过程中,有一代机型缺失了:ThinkPad x61。它采用 GM965 北桥和 ICH8 南桥。北桥与 GM45(ThinkPad x200 支持)有些相似,只是仅支持 DDR2;ICH8 南桥与已经支持的 ICH9 有些相似。这个平台没有泄露的文档,因此逆向工程是唯一的途径。过去曾有人 (https://github.com/lkundrak/coreboot/tree/x61) 使用 SerialICE (https://www.serialice.com/) 等工具尝试过——该方法在 QEMU 中运行固件,并将 IO 和 MMIO 转发到实际硬件——但他们未能成功制作出可用的 coreboot 移植。
所以,我的梦想就是最终能移植它。
## AI 辅助逆向工程
三月,我在尝试更多地将 LLM 技术融入我的工作流程。对于快速原型设计,它效果相当不错,但我很好奇它在逆向工程方面的表现如何。请注意,我当时使用的是 Anthropic 的 Claude Opus 4.6,那是当时最先进的模型。
事实证明,它们能极大地加快进程。通常逆向工程需要相当大的投入。为整个平台(x61 的 gm965/ich8)完成移植,花费 3-6 个月并非不可能。但我肯定没有那么多时间来做这件事。长话短说:我在下载的厂商 BIOS 上试了一下,结果看起来很棒。之后我购买了设备并让它成功运行。下面解释整个过程是如何进行的。
### 传统方式从厂商 BIOS 中提取信息
在试图理解厂商固件的作用之前,我首先想从一个运行正常的系统中尽可能多地提取信息。当某些功能不如预期时(例如 DRAM 训练失败或 USB 突然停止工作),已知的良好值是非常宝贵的参考。它也能为平台的某些部分提供很好的线索,比如 EC 设置、ACPI 表、PCI 配置、GPIO 引脚布局和 HDA 动词表。
为此,我使用了常用的 coreboot 工具。`inteltool` 能很好地概览 PCI 配置空间以及几乎所有北桥和南桥寄存器。`lspci` 仍然是一个快速检查 Linux 如何识别该机器的有用手段。对于 ACPI,我使用 `acpidump` 转储表,用 `acpixtract` 拆分,并用 `iasl -d` 反编译。这样就能以可读的方式看到厂商固件如何向操作系统描述设备、电源管理和 EC 方法。`ectool` 有助于查看 EC RAM 和行为,因为 ThinkPad 倾向于将许多板级特定细节隐藏在那里。我还保存了 CPU 信息和 HDA 编解码器信息,因为在盯着固件代码时很容易丢失这些信息。
### 配置 AI 代理工具及固件发现
x61 采用 Phoenix BIOS,因此第一步是使用 `bios_extract` (https://github.com/coreboot/bios_extract) 将镜像拆分为单独的模块。之后,我给了 AI 代理一些可以直接使用的工具。最有用的是 `ghidra-cli` (https://github.com/akiselev/ghidra-cli) 及其 `SKILL.md` (https://github.com/akiselev/ghidra-cli/blob/master/.claude/skills/ghidra-cli/SKILL.md),这样它就可以向 Ghidra 提问,而无需我持续操作 GUI。我还使用了 radare2 skill (https://github.com/radareorg/radare2-skills),因为 radare2 对于固件中较旧的 16 位实模式部分来说非常方便。
这种区分很重要,因为固件大部分是 16 位实模式代码,但 raminit 本身是一个 PE32 模块,看起来来自 Intel MRC(内存参考代码)。对于这部分,`ghidra-cli` 效果更好,因为原始代码很可能是 C 语言,反编译器的输出实际上很有用。对于周围的粘合代码,AI 代理通常使用 radare2 取得了更好的成功。
一件事让我惊讶:在镜像中发现了至少 3 个版本的 raminit。我猜测这是一种某种 A/B 布局,带有一个只读的恢复副本。只读副本跳过了许多正常的 raminit 流程,可能仅用于将机器恢复到足够好的状态以便重新刷写 BIOS 的其余部分。这与有记载的 Lenovo ThinkPad 行为一致:闪存顶部的 64K 区域为恢复目的被写保护。
### 氛围逆向工程
LLM 能够提取出所有的 raminit 初始化序列。基于此,我编写了一个 coreboot 移植。只用了两次提示,搞定。我甚至没有看代码,因为当时我正在健身房狂飙 (https://media1.tenor.com/m/ZCFYIXOjMyUAAAAC/c4-arena.gif) 举铁,等我回来时它一次就成功了。难以置信!
/whoa-woah.gif
### LLM 辅助逆向工程
上一节完全是谎言。
模型需要大量的手把手指导。我对类似平台有相当深入的了解,并且熟知启动下一代(x200)和上一代(x60)所需的所有细节。这对本次尝试的成功至关重要。我曾在纸上记下了超过 20 个需要我根据先前工作来引导模型的地方,本打算用于这篇博文。但好心办坏事,我把那张纸弄丢了……所以以下是我回忆的部分内容:
- ThinkPad x61 在 GPIO42 上有一个用于 SMBUS 的 GPIO 多路复用器,因此要么 SPD 可见,要么 EEPROM 可见。当操作系统启动时,只有 EEPROM 可见,SPD 不可见。
- 内存控制器支持 DDR2 533MT/s 和 666MT/s,而不是 666MT/s 和 800MT/s,正确解码硬件寄存器很重要。
- GMCH FSB 频率需要从 MCHBAR 寄存器读取,而不是从某个 PCI 寄存器读取。
- 大量错误的寄存器读/写大小:向 16 位寄存器写入 32 位值可能会破坏需要保持不变的 16 位高位部分。
- CAS 语义:我们在 coreboot 中通常如何编码与 MRC 如何编码,模型对此经常混淆。
- 固件中的多个 raminit 也令模型非常困惑。
- ……只有垃圾收集员才可能知道的事情(假设他们阅读了收集到的内容,这不太可能)。
就像常规的人类逆向工程一样,进行跟踪、数值转储并与工作系统进行比较,有助于追踪大量问题。在这方面,Lubomir Rintel 先前使用 SerialICE 跟踪 raminit 的工作也派上了用场(让 SerialICE 工作起来并不容易),这里有一个 raminit 跟踪的示例 (https://github.com/lkundrak/coreboot/blob/x61/test/MCHINIT67)。
### 我的刷机和测试工作流程
不用说,第一次启动并没有成功。
/x61-on-desk.jpg
我的测试和刷写设置如下:主板有一个扩展坞,带有 RS232 UART 连接器,与 ThinkPad x60 兼容。让该接口工作的代码是相同的。就是图片中的白色连接器。
为了刷写,我会将主板从扩展坞中取出,并在主板底部的闪存上夹一个夹子。注意,芯片是 SOIC8 而不是夹子暗示的 SOIC16(较大的引脚恰好适用于较小的芯片)。这块主板还具有 Intel descriptor、GBE 和 ME 区域。我暂时没有动这些。使用 flashprog 并带上以下标志来刷写。
``
flashprog -p ch341a_spi --ifd -i bios -w build/coreboot.rom
``
/x61-flashed.jpg
注意,与 ich9 和 ich10 一样,应该可以将其缩减为仅 IFD + GBE 以及 BIOS 区域(即去掉 ME)。这样将为 coreboot 腾出更多空间。禁用 ich9 笔记本电脑上的 ME 固件 (https://doc.coreboot.org/mainboard/lenovo/montevina_series.html#installing-without-me-firmware) 的方法大致适用于 X61。
### 将 libgfxinit 移植到 GM965
让 libgfxinit 在 GM965 上工作是小菜一碟。这个硬件确实与下一代硬件非常相似。它的 PLL 时钟限制有些不同,用于 stolen memory 和 GTT 的 GMS 寄存器也略有不同。补丁请参见 https://review.coreboot.org/c/libgfxinit/+/91522。为此,我将 LLM 代理指向 Linux——它可以完全 modeset 这个硬件而无需固件——并在很短的时间内将其转换为可用的代码。我想我只是指出了 GTT 的语义不同,仅此而已。
## 上游化代码,修复更多错误
几天后,代码在多个 DIMM 的不同组合下似乎运行良好。是时候上游化了!在 x86 上(在 coreboot 中)做原生 raminit 可以说是一门失传的艺术。一位享受这种黑魔法的人是 Angel Pons。所以我问他是否愿意审阅这段代码(他是一位非常彻底的审阅者)。在我看来代码还行,但至少对于代码,他的眼力比我好得多。
以下是出错的总结:
首先,代码有时根本不知道自己在和什么硬件对话。一些寄存器名称是从相邻芯片组复制的,一些来自反编译器的氛围,还有一些完全是错的。Angel 指出了几个数据手册确实说了不同的情况:错误的寄存器名称、保留位被视为实际位、错误的访问大小。一个很好的例子是 GM965 的 `0xa00` MCHBAR 范围。代码最初有一些凭空想象的寄存器块语义。仔细一看发现它更接近于在其他 Intel 芯片组上看到的 EP channel / ME 相关东西。
raminit 也有实际的错误。某些时序表的索引搞反了,某些位域的含义错误,某些计算只是对我测试的 DIMM 来说侥幸正确。这是典型的“在我机器上能跑”问题。在其他许多地方也发生了类似情况:南桥中大量代码假设是 ThinkPad x61:
- 硬编码的初始化位
- 仅适用于 x61 的硬编码设备启用/禁用
因此,感谢 Angel 帮助将这个启动 hack 转变为可上游化的芯片组代码。
审查过程中最糟糕/最令人沮丧的部分与 LLM 毫无关系。它来自于 clang-format。LLM 代理和 Emacs 都默认设置了使用它。在 coreboot 代码库中,这个工具的结果至少可以说是糟糕的。我与这个工具斗智斗勇,它给我的好处还不如带来的麻烦多,于是我沮丧地发送了一个补丁 (https://review.coreboot.org/c/coreboot/+/92800),将 clang-format 配置从 coreboot 中彻底删除。
所以,“氛围逆向工程”如果没有真正的工程师参与,短期内是无法上游化的。
## 未来会怎样?
真棒,我把 coreboot 移植到了一台将近 20 年的旧笔记本电脑上,它太慢、RAM 太少,根本无法用于现代用途。我怀疑这些笔记本电脑的价格至少会像过去几个月 DRAM 价格一样上涨。我都是在 eBay 上买的,预计能大赚一笔!
### AI 辅助逆向工程
开个玩笑,这个实验的目的是测试 LLM 的逆向工程能力。实际上,我先是下载了厂商固件,后来才购买了设备,因为结果令我印象深刻,我必须试试它是否能用。结果是:它将数月的艰苦逆向工程工作缩短到几周内完成。请注意,这次尝试是以一种天真的、低效的方式进行的。例如,让代理指向已有的寄存器语义代码并为其提供数据手册,会大大提高质量,并使审查过程更加顺利。
现在真正的意义在于:逆向工程固件以前是一件困难且耗时的任务,现在变得可行了。我想到了一个可能有趣的领域:Intel FSP。没人喜欢它……以前逆向工程它需要太多精力,而且没有足够的好处来证明这种努力是值得的。现在情况不同了。FSP 是 32 位 PE 代码,Ghidra 很擅长理解它,甚至还有针对 UEFI FD 镜像的插件。当然,美国公民不被允许进行逆向工程,但欧盟公民被法律明确允许。我不是律师,所以请随意理解,不要把它当作可靠的法律建议。谁知道呢?也许芯片厂商会失去开发封闭二进制独占固件的动力,因为人们无论如何都会把它搞明白?
### 移植到 fstart
最后留个小悬念:我还将这个平台移植到了 fstart (https://github.com/fstart-io/fstart) 上——这是一个用 Rust 编写的 coreboot/u-boot 替代品,而 x61 是它支持的首批真实 x86 平台之一。更多细节将在之后公布,因为该项目目前正处于一个粗糙的实验阶段。
相似文章
将 ThinkPad T480 用作手机
一篇博客文章描述了如何将 ThinkPad T480 改造成功能完整的手机,使用 Libreboot、Quectel EG25-G 调制解调器和自定义固件,实现通话、短信和移动数据功能。
无需再等待——AMD StarBook 迎来 coreboot
StarLabs 为 AMD StarBook Mk VI 发布了可用的 coreboot 开源固件测试版,已支持 Wi-Fi、S3 睡眠及可回滚更新。
在旧款ThinkPad T60上安装SerenityOS
关于在ThinkPad T60上安装SerenityOS的指南,包括构建、刷写和解决启动问题的步骤。
在Lemote Yeeloong笔记本电脑上使用OpenBSD与“龙”共舞
一篇博客文章详细描述了作者在搭载中国龙芯MIPS处理器的Lemote Yeeloong笔记本电脑上运行OpenBSD的体验,讨论了硬件、软件挑战以及自由软件哲学。
用 x86_64 汇编写成的 Linux 桌面
一位开发者借助 Claude Code,用纯 x86_64 汇编重建了完整的 Linux 桌面栈——从 shell、终端、窗口管理器到各种工具,实现微秒级启动,并延长数小时续航。