Asahi Linux 7.1 进度报告:M3支持与固件逆向
Linux 7.1 正式发布,本次进度报告也同步出炉。内容涵盖 M3 系列适配进展、Apple 系统兼容性问题等诸多更新!
主引导记录回归
在 Mac 上长按电源键调出启动选择器(或使用「启动磁盘」应用)时,你看到的「Asahi」选项,并非真正存放操作系统的分区。Apple 的启动工具仅支持识别 APFS 容器内的「合法」macOS 安装环境。为了让用户无需每次进入恢复模式执行命令就能启动 Asahi,Asahi 安装器会创建一个 2.5GB 的小型 APFS 容器,里面仅保留足够的 macOS 组件,以此骗过 Apple 的工具,让它认为这是一个以 m1n1 为内核的可启动 macOS 系统。从 macOS 12 到 macOS 26,这套方案一直稳定运行,Apple 甚至修复了其工具在启动非 XNU 内核的原始二进制文件时出现的几个漏洞。
然而,macOS 27 Golden Gate 开发者测试版发布后不久,我们陆续收到反馈:用户无法再启动 Linux,启动磁盘和启动选择器里的 Asahi 选项直接消失了!这显然是个严重问题,我们立即将其列为优先排查项。
用 diskutil 检查磁盘后发现,升级到 macOS 27 后,所有与 Asahi 相关的分区仍完好无损,没有数据丢失,这是个好消息。此外,在同一台机器上使用 macOS 26 的启动工具,仍能正常启动 Asahi。
chaos_princess 开始研究 Apple 官方的 macOS 安装器,以及我们早期探索 Apple 启动工具时留存的旧数据。发现 macOS 安装器会在重启前设置一项 APFS 元数据——这是一个标记分区可启动的标志。在 macOS 27 之前,启动工具完全忽略这个标志;而手动给 Asahi 的 APFS 容器设置该标志后,它就能在 macOS 27 的启动选择器中正常显示,无需其他改动。
未来,所有新安装的 Asahi 都会由安装器自动设置这个标志。我们还新增了一个安装器模式,可修复已有的 Asahi 安装。如果你升级了 macOS 27 开发者测试版后无法访问 Asahi,请重新运行安装器,选择「修复 macOS 27 启动选择器兼容性」选项。
chaos_princess 还开发了一个可在 Linux 环境运行的修复程序。我们最终希望能自动推送该修复,但目前需要更多测试数据来验证其可靠性,确保不会损坏文件系统。这就需要你的帮助:如果你愿意参与测试,请克隆这个代码库,在升级到 macOS 27 之前在 Linux 环境编译并运行它。如果之后在 macOS 中仍能选择 Asahi 作为启动项,就说明修复成功。欢迎通过 OFTC 或 Matrix 上的频道告知我们测试结果,遇到问题也请及时反馈。
三个字节引发的强制关机
macOS 27 还推送了全局固件更新,覆盖所有外设,包括 SMC(系统管理控制器)。SMC 的众多功能之一是电池管理,我们的 Linux 电源驱动通过与 SMC 通信获取充电状态、电压、剩余续航和电池健康度等信息,还会利用 SMC 的固件接口配置充电起止阈值,延长电池寿命。但 macOS 27 的 SMC 固件将其中一个电池管理接口的返回值从 32 位整数改为单字节,这导致我们的驱动出现识别错误:在某些情况下,驱动会判定电池故障,触发紧急关机以保护系统。目前我们已在下游内核中修复了该问题,从 7.0.12 版本开始,电源驱动可兼容新旧两种固件 ABI(应用二进制接口)。
关于安装测试版
这类问题提醒我们:开发者测试版本质就是「测试版」,绝不建议在主力设备上安装。目前遇到的两个问题都不算严重,但不代表未来的问题也会如此。而且全局固件更新基本是不可逆的,只能通过 DFU 恢复才能回滚。请大家不要轻易安装开发者测试版,我们有专门的测试设备替大家验证风险,没必要拿自己昂贵的硬件和重要数据冒险。
万变不离其宗
计算机平台和内部芯片的设计与验证成本极高、耗时极长,因此若非必要,厂商不会轻易改动现有设计。项目初期我们就判断,Apple 会遵循这一逻辑,不会频繁做出破坏性改动。除了 GPU 这类几乎每代都必须更新的大型 SoC 模块外,我们的判断基本正确。
Apple Silicon 笔记本的音频系统涉及多个芯片和 SoC 模块。音频芯片的行业标准是 I2S——一种基于 I2C、专为音频数据优化的总线。Apple 的 I2S 控制器自 M1 以来从未改动。所有音频芯片还需要稳定且可配置的时钟源,以适配不同的音频数据速率,而 Apple 的数控振荡器(NCO)同样自 M1 起就没有变化。此外,几乎所有 Apple Silicon 机型都使用完全相同的扬声器和耳机放大器芯片。因此,当 chaos_princess 为 M3 机型添加扬声器和耳机插孔支持时,仅需添加少量简单的设备树(Devicetree)配置,以及 asahi-audio 和 speakersafetyd 的配置文件即可。如今,Asahi Linux 已为 M3 机型提供高品质音频输出!
M3 机型还新增了 CPU 调频功能和完善的 big.LITTLE 任务调度支持。自基础款 M2 以来,Apple 并未改变 CPU 调频的工作机制,因此所有 M3、M3 Pro/Max/Ultra 系列 SoC 仅需修改设备树,就能适配我们现有的 cpufreq 驱动。现在,系统会根据任务需求,智能地将其分配到能效核或性能核;CPU 核心也会根据负载动态调整频率,既能节省能耗,又能提升性能!
SMC 硬件传感器的适配工作同样简单:不同机型的 SMC 固件差异极小,只需修改几处设备树配置即可完成。
除上述功能外,M3 系列机型的 PCIe、WiFi、蓝牙、NVMe、键盘、触控板及其他核心 SoC 模块的 Linux 驱动也已正常工作。大部分工作由Yureka完成,她一直使用 M3 系列机型,专注于 m1n1 和 Linux 的开发工作。距离为这些机型开启 Asahi 安装器支持还有一段路要走,但目前进展迅速,请持续关注!
我们居然开始写固件了?
该平台上的大部分复杂硬件都依赖专用固件 blob,其中多数基于 RTKit——Apple 开发的类实时操作系统固件框架,为内核与各类硬件提供标准化交互接口。但也有例外:DCP 和 AOP 等模块以 RTKit 为基础,还额外添加了名为 EPIC 的抽象层;博通 WiFi/蓝牙芯片则使用 Apple 无法直接控制的第三方固件;而 Apple 视频解码器(AVD)更是特殊的存在。
AVD 的固件既非 RTKit 也非 EPIC,而是第三种独立框架。其硬件本质是一个 ARM Cortex-M3 内核,控制一系列固定功能单元,负责解码 AVC(H.264)、HEVC(H.265)、VP9 格式的视频帧,最新款 SoC 还支持 AV1 解码。Cortex-M3 运行的固件 blob 为 XNU 内核提供接口,接收视频数据后,再自行配置解码器硬件。这本无可厚非,但 Apple 做出了一个特殊选择:将 AVD 固件和大量配置数据打包在 AVD 内核扩展(kext)中。更麻烦的是,不同 SoC 的 AVD 版本略有差异,这给 Asahi 安装器带来了极大挑战——我们需要持续跟踪并更新固件数据在 kext 中的偏移量。理论上我们可以这么做,但或许有更好的方案?
XNU 加载的固件不会被 Cortex-M3 验证,只要收到信号,它就会从复位向量开始执行,无论加载的内容是什么。那我们能不能……用自己的固件?
固件的核心作用是抽象底层视频解码器硬件,只要能为各个硬件模块安装中断处理程序,具体实现方式并不重要。如果我们能理解底层硬件的需求,就可以直接在 Linux 驱动中完成所有配置。要做到这一点,我们需要先搞清楚原版固件是如何驱动各个解码器的。
AVD 固件是标准的 Cortex-M3 代码,因此可以在模拟器中运行,比如 QEMU,它支持单步执行程序并检查总线和寄存器操作。Jamie、R 和 Eileen 早在多年前就打下了基础,他们通过合作逆向分析出了 AVC 和 VP9 解码器所需的指令和数据格式。
XNU 的 kext 还会为每个 AVD 版本设置一组独特的可调参数,我们目前还不完全清楚这些参数的作用,因此只能原样复现 XNU 对 MMIO(内存映射 I/O)的写入操作。我们需要跟踪每个 AVD 版本对应的参数集,这在 Linux 上游内核驱动中很难长期维护,因此最好将这部分逻辑放在固件中。
长期以来,AVD 相关工作进展缓慢,但新贡献者sofus最近填补了这一空白。他编写了一个极简的自定义 AVD 固件,仅负责安装中断处理程序和应用各版本的可调参数,在此基础上开发出了可用的 AVC 硬件 V4L2 驱动!该驱动可解码最高 4K 分辨率的 10 位 AVC 视频,完美支持实现 V4L2 请求 API 的软件。由于固件设计得简洁无状态,视频数据解析和解码器配置都由用户空间和内核负责,未来我们也能更轻松地适配 VA-API 和 Vulkan Video 等其他视频加速 API。
距离向用户推送 AVD 支持还有一些工作要做:AVD 支持 VP9、HEVC,部分 SoC 还支持 AV1,但这些格式的解码功能尚未实现;部分设备存在特殊兼容性问题,需要在驱动中进行测试和适配。我们希望能在不久的将来为大家带来可用版本!
m1n1 重大版本发布
我们最近还发布了 m1n1 1.6.0 版本。这对发行版来说是一个重要里程碑,因为这是首个第二阶段构建需要 Rust 语言的版本。此前,m1n1 仅在启用链式加载支持时才会用到 Rust。第一阶段 m1n1 会替换 Apple 启动工具中的 XNU 内核,仅用于挂载 EFI 系统分区并从中链式加载第二阶段 m1n1。不久前,我们决定将 GPU 初始化逻辑移入 m1n1,这样内核驱动就无需处理 Apple 硬件初始化数据中的浮点数,还大幅简化了设备树绑定。未来提交到 Linux 内核邮件列表的 GPU 驱动,将依赖 m1n1 完成初始化工作。我们还将 Apple 设备树解析代码移植到了 Rust,m1n1 的几乎所有其他模块都会用到这部分代码。
由于 m1n1 本质上属于固件,它使用 no_std Rust 环境,目标架构为 aarch64-none-softfloat。为避免引入多余依赖,你可以在执行 make 时传入 BUILDSTD=1 参数,这样无需安装完整的软浮点工具链就能构建 core 和 alloc 组件。
1.6.0 版本还大幅提升了 M3 系列的支持,包括新增 SPMI 控制器和 PCIe 初始化支持。我们现在还能通过kisd,将 SoC 的硬件 UART 直接通过 DebugUSB 隧道传输,实现类似 Central Scrutiniser 的功能。这些工作同样大部分由 Yureka 完成。
我们还在为 M4 和 A18 Pro(MacBook Neo)的支持做准备,优化了对 Apple 非 macOS 启动模式的处理,并支持 Apple 设备树中新增的电源域元数据。
再次致谢!
一如既往,我们要感谢GitHub Sponsors和Open Collective上的慷慨支持者。没有你们的支持,我们无法继续完善 M1、M2 机型的未完成功能,也无法推进 M3、M4 和 A18 Pro 的适配工作,更无法支持热情满满的新贡献者!