Asahi Linux 7.2 发布:实现基于 UEFI 的 PSCI 电源管理
Asahi Linux 7.2 进展报告发布
/ Blog / Progress Report: Linux 7.2
/ 博客 / 进度报告:Linux 7.2
Progress Report: Linux 7.2
进度报告:Linux 7.2
- Previous
- 上一篇
Linux 7.2 has been released! That was fast. Let’s dive in to yet another Asahi Linux progress report. We’ve got a lot of interesting developments for you today, so make yourself a cuppa and enjoy.
Linux 7.2 已经发布了!真快。让我们再次深入 Asahi Linux 的进度报告。今天我们有很多有趣的进展要告诉你,所以泡杯茶,慢慢享受吧。
Think Different… again
再次“不同凡想”
The Apple Silicon platform’s power management infrastructure is complicated. Responsibilities are divided between multiple hardware blocks, including the SMC, PMGR, and PMP, all of which have featured before on this blog. While supporting these blocks is important for power use, one of the biggest obstacles to improving battery life has been the application cores themselves.
Apple Silicon 平台的电源管理基础设施非常复杂。职责分布在多个硬件模块之间,包括 SMC、PMGR 和 PMP,这些都在本博客中介绍过。虽然支持这些模块对功耗很重要,但提升电池续航的最大障碍之一在于应用核心本身。
There are multiple ways to “sleep” a CPU core, and each one should be used in a specific context. The most basic way to sleep an ARM CPU core is to use a Wait For Interrupt (WFI) instruction. This tells the core to stop doing things until it is woken up by an interrupt from an interrupt source. While this does save power by virtue of stopping the core from executing any code, the core stays powered up and retains enough state for it to resume work extremely quickly. As such, WFI is typically only used for parking a core on a running system. Apple cores include a “deep” WFI mode, which shuts down more of the core at the expense of losing its state. Our downstream cpuidle driver operates by setting WFI up in this mode, saving the core’s state, then issuing a WFI loop.
有多种方式可以“休眠”CPU 核心,每种方式应在特定场景下使用。休眠 ARM CPU 核心最基本的方法是使用等待中断(WFI)指令。这会让核心停止执行操作,直到被某个中断源唤醒。虽然这通过停止核心执行代码来节省电力,但核心仍保持供电,并保留足够的状态以便极快地恢复工作。因此,WFI 通常仅用于在运行中的系统上停放核心。Apple 核心包含一种“深度”WFI 模式,它会关闭核心的更多部分,但代价是丢失其状态。我们的下游 cpuidle 驱动程序通过将 WFI 设置为此模式、保存核心状态,然后执行 WFI 循环来工作。
Vendor-specific power management oddities like this are quite common. Thankfully for kernel maintainers, there is a standard way to deal with them: the Power State Coordination Interface. PSCI defines a standard interface that allows an operating system to call into a defined set of CPU core power management functions implemented by system firmware, including to prepare them for sleep.
像这样的供应商特定电源管理怪癖相当常见。幸运的是,对于内核维护者来说,有一种标准方式来处理它们:电源状态协调接口(PSCI)。PSCI 定义了一个标准接口,允许操作系统调用由系统固件实现的一组定义的 CPU 核心电源管理功能,包括准备核心进入睡眠状态。
To avoid a proliferation of vendor-specific power management hacks inside the Linux kernel, the maintainers of the arm64 arch-specific code have mandated that all upstream hardware must use PSCI for power management. As such, we are not able to upstream our Apple-specific cpuidle driver. Why are we still using it then?
为了避免 Linux 内核中供应商特定电源管理补丁的泛滥,arm64 架构特定代码的维护者已规定所有上游硬件必须使用 PSCI 进行电源管理。因此,我们无法将 Apple 特定的 cpuidle 驱动程序上游化。那为什么我们还在使用它呢?
PSCI defines “conduits” through which calls to firmware are to be dispatched from the kernel. The two currently supported conduits in the kernel are the SMC (Secure Monitor Call) and HVC (Hypervisor Call) instructions, which are used to yield execution to a higher Exception Level. The Linux kernel expects to be running at EL2, which means that its PSCI calls must yield to firmware running in EL3. Except Apple’s cores do not implement EL3…
PSCI定义了“通道”,内核通过这些通道向固件分发调用。目前内核支持的两种通道是SMC(安全监控器调用)和HVC(虚拟机监控器调用)指令,它们用于将执行权交给更高的异常级别。Linux内核期望在EL2运行,这意味着其PSCI调用必须交给在EL3运行的固件。但苹果的核并未实现EL3……
With the kernel already running in EL2 and no firmware running in EL3 to talk to, we are a bit stuck. Linux cannot issue an SMC or HVC instruction since there is no EL3 to yield execution to, which means we cannot make use of PSCI. Being able to properly power-manage the CPU cores is vital for battery life and efficiency, so the status quo simply will not do. One quick and dirty solution would be to have m1n1 load the kernel into EL1, and host a PSCI implementation in EL2. While this would theoretically work, it would also break a lot of architectural features, such as virtualisation. There must be something else we can do…
由于内核已在EL2运行,且没有EL3中的固件可与之通信,我们陷入了困境。Linux无法发出SMC或HVC指令,因为没有EL3可让出执行权,这意味着我们无法使用PSCI。能够正确管理CPU核的电源对电池寿命和效率至关重要,因此现状不可接受。一个快速而粗糙的解决方案是让m1n1将内核加载到EL1,并在EL2中托管PSCI实现。虽然这在理论上可行,但会破坏许多架构特性,如虚拟化。我们肯定还有其他办法……
If you think about it, m1n1 is almost like our own firmware for Apple Silicon. mBoot (formerly iBoot) starts it in EL2, it does its job, then jumps to whatever payload is attached to it. m1n1 does not reserve any memory for itself and does not have any code that must stay resident, so its payload is free to reclaim and overwrite that memory.
仔细想想,m1n1几乎就像我们自己的Apple Silicon固件。mBoot(原iBoot)在EL2启动它,它完成工作后,跳转到附加的任何负载。m1n1不为自己保留任何内存,也没有任何必须驻留的代码,因此其负载可以自由回收并覆盖这些内存。
On production Asahi Linux systems, m1n1 loads U-Boot rather than the kernel directly. We do this to make use of U-Boot’s UEFI implementation, allowing distros and users to utilise whichever standard UEFI bootloader (GRUB, systemd-boot, etc.) they want. UEFI also provides another feature, Runtime Services. Much as the BIOS interrupts of old did, UEFI Runtime Services provide a way for the operating system to access code originating in system firmware.
在生产版Asahi Linux系统中,m1n1加载U-Boot而非直接加载内核。我们这样做是为了利用U-Boot的UEFI实现,让发行版和用户可以使用他们想要的任何标准UEFI引导加载程序(如GRUB、systemd-boot等)。UEFI还提供了另一项功能:运行时服务。正如旧时的BIOS中断一样,UEFI运行时服务为操作系统提供了一种访问源自系统固件的代码的方式。
Reading the PSCI standard as published by Arm, one will notice that it deliberately defines the API without reference to any specific conduit, and only lists SMC and HVC as examples. If we take a broad interpretation of this, we could conclude that this means other conduits are allowed by the spec…
阅读Arm发布的PSCI标准,人们会注意到它刻意定义了API而不引用任何特定通道,仅将SMC和HVC列为示例。如果我们对此采取宽泛的解释,可以得出结论,即规范允许其他通道……
To this end, Sven has been working on implementing a UEFI Runtime Service based PSCI conduit. With m1n1’s memory region carved out like other firmware regions, this will allow the kernel to call back into it for PSCI services, even though it is running at the same Exception Level. Sven has already modified m1n1 to reserve its memory and leave behind a PSCI implementation, and the patches to the kernel enabling its use are already on the mailing list as an RFC!
为此,Sven一直在致力于实现一个基于UEFI运行时服务的PSCI通道。通过像其他固件区域一样划出m1n1的内存区域,即使内核运行在相同的异常级别,也能回调它来获取PSCI服务。Sven已经修改了m1n1以保留其内存并留下一个PSCI实现,而启用该功能的内核补丁已经作为RFC发布在邮件列表上!
Please stop Thinking Different
请停止“不同凡想”
Given that the cpuidle situation saw no progress until very recently, one might assume that some event has catalysed work in this space. One would be correct.
鉴于cpuidle的情况直到最近才有所进展,人们可能会认为有某个事件催化了这一领域的工作。这种猜测是正确的。
The ARM specification mandates that cores in WFI loops should preserve all state. This is not the default mode on Apple Silicon. On M1 through M3 series SoCs, state retention can be configured on a per-core basis using chicken bits.
ARM规范要求处于WFI循环中的核心应保留所有状态。但这并非Apple Silicon的默认模式。在M1至M3系列SoC上,可以使用“鸡毛位”按核心配置状态保留。
Due to a number of reasons that are not worth mentioning, Apple now sets each core’s chicken bits in mBoot and then locks down the registers controlling them starting with the M4 series. This makes our life a little easier as m1n1 now has marginally less work to do, however it also means that we cannot fine tune low level CPU behaviour. This is an issue on M4 particularly, as calling WFI causes the core to lose its state and crash whatever was running on it.
由于一些不值一提的原因,Apple现在在mBoot中设置每个核心的“鸡毛位”,并从M4系列开始锁定控制这些位的寄存器。这让我们的工作稍微轻松了一些,因为m1n1现在需要做的事情少了,但这也意味着我们无法微调底层CPU行为。这在M4上尤其是个问题,因为调用WFI会导致核心丢失状态并使运行在其上的程序崩溃。
Yureka noticed this while doing M4 bringup work, and added a kernel command line parameter to make idle loop behaviour configurable. The parameter allows us to tell the kernel how it should park cores in idle loops, including by doing a basic no-op loop. This prevents M4 machines from crashing during early kernel initialisation, before our cpuidle driver has been loaded. Once the driver takes over, it saves the the lost state before issuing WFI. The patches to enable this are already in linux-next.
Yureka在M4启动工作中注意到了这一点,并添加了一个内核命令行参数,使空闲循环行为可配置。该参数允许我们告诉内核如何在空闲循环中停放核心,包括执行基本的空操作循环。这可以防止M4机器在内核初始化早期、我们的cpuidle驱动加载之前崩溃。一旦驱动接管,它会在发出WFI之前保存丢失的状态。启用此功能的补丁已经进入linux-next。
They won’t stop Thinking Different
他们不会停止“不同凡想”
Apple takes its reputation for platform security very seriously. As such, a lot of engineering effort goes in to features that make exploiting vulnerabilities in their ecosystem infeasible for all but the most sophisticated of attackers. One such feature is the Secure Page Table Monitor, or SPTM.
Apple非常重视其在平台安全方面的声誉。因此,大量工程努力投入到使利用其生态系统中的漏洞变得对除最老练的攻击者外都不可行的特性上。其中一个特性就是安全页表监视器,即SPTM。
Traditionally, the operating system kernel has been directly responsible for managing memory. This includes handling memory allocations, virtual mappings to physical addresses, and MMU/IOMMU management. A vulnerability in the code responsible for these operations could give an attacker access to the platform’s entire address space. In other words, you’re cooked.
传统上,操作系统内核直接负责内存管理,包括处理内存分配、虚拟地址到物理地址的映射,以及MMU/IOMMU的管理。负责这些操作的代码若存在漏洞,可能让攻击者访问平台的整个地址空间。换句话说,你就完蛋了。
Naturally this makes memory management code a very common target for attackers, and given that its job is fundamentally insecure (applications may want arbitrary allocations for just about anything) it is incredibly difficult to lock down correctly.
自然,这使得内存管理代码成为攻击者的常见目标,而且鉴于其职责本质上就不安全(应用程序可能为了任何目的请求任意分配),要正确锁定它极其困难。
Many years ago, Apple introduced the Page Protection Layer to XNU. PPL uses Apple’s hardware security features to isolate pagetable management from the rest of the kernel at the hardware level. This works very well, however attackers eventually caught up and found ways inside PPL that allowed them full access to the system.
多年前,苹果在XNU中引入了页面保护层(PPL)。PPL利用苹果的硬件安全特性,在硬件层面将页表管理与内核的其他部分隔离。这效果很好,但攻击者最终追了上来,找到了进入PPL内部的方法,使他们能够完全访问系统。
更进一步:量化金融体系
看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力