2025年9月12日 浏览:16 作者: 进迭时空

RISC-V ACPI 二三事

熟悉 x86 电脑的朋友估计对 ACPI 这个名词不会陌生,在如今的计算机市场上,ACPI 广泛应用于笔记本电脑、台式机、工作站以及服务器等产品上。新兴的 RISC-V 架构要进入桌面/服务器领域,不可避免地会与 ACPI 打上交道。

什么是 ACPI?

ACPI 全称是 Advanced Configuration and Power Interface(高级配置与电源接口),它是一套开放标准,可供操作系统用于发现和配置硬件、自动配置即插即用和热插拔设备、进行电源管理以及状态监控等 [1]

ACPI 的初版规范由英特尔(Intel)、微软(Microsoft)和东芝(Toshiba)共同制定,在 1996 年 12 月发布。随后越来越多的公司加入到 ACPI 规范的改进中。2013 年 10 月,ACPI 规范转交由 UEFI 论坛维护,一直至今。最新的 ACPI 规范已经演变到了 6.6 版本,在 2025 年 5 月发布。

在 ACPI 之前,计算机的电源管理几乎完全交由底层的 BIOS 固件来控制,这大大限制了操作系统在能耗控制上的功能。ACPI 的出现,取代了 APM(Advanced Power Management)、MPS(MultiProcessor Specification)、Legacy PnP(Plug and Play BIOS)等规范,让操作系统在电源管理和硬件配置上获得了更多的自主权,从而实现了 OSPM(Operating System-directed configuration and Power Management,操作系统导向的配置和电源管理)系统。

ACPI 框架

一个使用 ACPI 的系统的完整框图如下:

取自 ACPI specification v6.6 [2]


▲橙色部分是 ACPI 规范所覆盖的内容,因此 ACPI 定义的是一种软件和硬件组件的接口规范:

  • 硬件上:
    • ACPI Register Set:在硬件上实现的 ACPI 寄存器组,为 x86 架构特有。ARM 和 RISC-V 通常用的是 hardware-reduced ACPI,可无需在硬件上实现这些寄存器。
  • 软件上,定义了多个 ACPI 表(Table),这些表整体可分为两种类型:
    • Static Tables:又称为 Data Tables,由裸数据组成,ACPI Driver 可直接读取表中的内容。
    • Definition Block Tables:由 AML(ACPI Machine Language)字节码(byte code)组成,它的内容要经 AML Interpreter(解释器)解释后才能被 ACPI Driver 读取到。

▲绿色部分是操作系统中的相关组件,其中:

  • ACPI Driver操作系统用于对接 ACPI 的驱动,AML Interpreter 也是属于其中的一部分。其中有一个比较出名的开源工程 ACPICA(ACPI Component Architecture)[3],它提供了与操作系统无关的一些 ACPI 相关代码实现,也包括一个 AML Interpreter 的实现。目前 ACPICA 的代码被用于 Linux、FreeBSD 等系统中,作为 ACPI Driver 的一部分。
  • OSPM(Operating System-directed configuration and Power Management):以 ACPI 为核心、由操作系统主导的电源管理子系统的统称。

▲蓝色部分是硬件或平台相关的组件,其中:

  • Platform Firmware 是负责启动机器、实现休眠/唤醒/重启等接口的固件,通常指 BIOS/UEFI。它也负责生成各个 ACPI 表并提供给操作系统。

ACPI Namespace

前面提到 ACPI 的 Definition Block Tables 由 AML 字节码组成,而 AML 字节码的生成流程通常如下图:工程师使用 ASL(ACPI Source Language)语言对硬件信息进行描述,随后通过 ASL 编译器(例如 iASL)将其编译为 AML 字节码,生成 Definition Block Tables,并打包进固件:

取自 Basic ACPI Source Language (ASL)  Constructs Tutorial [4]

从固件中获取到 ACPI 表后,操作系统中的 AML Interpreter 会解析 AML 字节码,构造出硬件设备的树状层次结构,这种结构称为 ACPI namespace。操作系统从 ACPI namespace 中即可获取到硬件的各种信息:

取自 Basic ACPI Source Language (ASL)  Constructs Tutorial [4]

ACPI 与 UEFI 的关系

在 ACPI 出现的场合,通常也能见到 UEFI 的身影。UEFI 全称是 Unified Extensible Firmware Interface(统一可扩展固件接口),它是计算机固件的一种规范,旨在替代 BIOS。

它的前身是英特尔于 1998 年提出的 Intel Boot Initiative,后改名为 EFI(Extensible Firmware Interface,可扩展固件接口),再于 2005 年改名为 UEFI 并转交由 UEFI 论坛维护发展至今 [5]。UEFI 规范的一个比较出名的实现是由 TianoCore 社区开源的 EDK II 工程 [6]。

虽然都是规范,但 ACPI 与 UEFI 关注的点不同:ACPI 是从硬件抽象的角度定义了操作系统发现、配置、管理硬件的规范接口,而 UEFI 定义的是固件与操作系统间软件层面的规范接口。从两者最初制定的目的也能看出差异:ACPI 是为了将 BIOS 固件的电源管理等功能以规范的接口交由操作系统来主导,而 UEFI 则是为了替代 BIOS 固件本身。

ACPI 与 UEFI 初版规范的提出都有英特尔的参与,并后面都交由 UEFI 论坛 [7]维护,两者间存在着诸多联系,但并非强绑定,其他 bootloader(如 U-Boot)也可以支持 ACPI,UEFI 也支持安装其他的配置表(如 Device Tree)供操作系统使用。不过在个人电脑/服务器领域,UEFI + ACPI 还是目前最常见、最成熟的使用组合。

为什么 RISC-V 需要支持 ACPI?

在讨论 RISC-V 为什么需要支持 ACPI 之前,我们可以看一下发展路线十分相似的 ARM:同样都是起家于嵌入式领域,随后向个人电脑/服务器领域发起进攻。ARM 所经历过的挑战,在 RISC-V 上也同样存在。

嵌入式与个人电脑/服务器的产品生态差异

嵌入式领域与个人电脑/服务器领域,两者的产品生态形式有较大的不同:

▲嵌入式领域的产品,通常会存在一个品牌厂商,整合上游厂商提供的软、硬件,做成一个高度定制化的最终产品,并对其负责。例如手机,普通用户通常接触到的只有苹果/华为/小米等最终的手机品牌厂商,无法轻易更换手机主板上的硬件,不同手机的固件/操作系统通常也互不通用。

▲个人电脑/服务器领域的产品,普通用户能直接接触到的厂商会多很多,CPU、主板、电源、硬盘、显卡等均可能来自不同的厂商,用户可选择合适的零部件产品,自行搭建出满足自身需求的机器。各零部件厂商也只对自身的零部件产品负责。软件操作系统的通用性也更强,一台机器可以运行 Windows、Ubuntu 等不同的操作系统,同一个操作系统通常也只需一个镜像就可适配不同的硬件机器。

(当然,目前市面上不乏很多集成化、定制化程度很高的个人电脑产品,例如苹果公司的 Mac 电脑,或其他一些笔记本电脑,它们的产品形态更接近于手机这样的嵌入式产品,普通用户无法轻易自行更换其软、硬件。不过从整体生态而言,这样的产品还是相对少数。)

个人电脑/服务器生态中如此多厂商的软、硬件能相互通用,不可或缺地会存在 “标准”。

个人电脑/服务器生态标准的形成

在 20 世纪 70~80 年代,个人电脑(PC,Personal Computer)刚面向市场时,并不存在统一的标准,不同厂商间的个人电脑互不兼容。1981 年 8 月,IBM 推出了 IBM PC,并随后公布了上面除 BIOS 外的全部技术资料,并允许第三方公司生产与其兼容的硬件,即采用所谓的开放架构(open architecture)。

此举大大促进了整个个人电脑产业的发展,IBM PC 逐渐成为事实上的个人电脑 “开放标准”,与该标准兼容的机器都统称为 “IBM PC 兼容机”(IBM PC Compatible)[8]

随着计算机技术的发展,到了 1990 年代,IBM 对个人电脑架构的影响力逐渐下降,取而代之的业界标准变成了 “Wintel”  [9] :Windows + Intel,代指基于英特尔(Intel)的 x86 处理器、运行微软(Microsoft)的 Windows 操作系统的个人电脑。由英特尔和微软主导的行业联盟发布了诸多的软、硬件规范:UEFI、ACPI、PCI/PCIe 等,“IBM PC 兼容机” 的说法也逐渐被 “标准 PC”(standard PC)以及后续的 “ACPI PC” 所取代 [8]。

Wintel 架构在 x86 计算机中极具统治力,一直至今。除了台式机外,笔记本电脑或服务器产品也深受其影响,并且即使机器是基于其他厂商(如 AMD)的 x86 处理器,或运行其他的操作系统(如 Linux),也在很大程度上兼容该架构。

2000 年代的个人电脑主板结构框图 [10]

Device Tree vs ACPI

前面提到说 ACPI 的 ASL 语言可以通过 ACPI namespace 这样的硬件抽象结构来描述硬件信息,供操作系统使用,而嵌入式领域常用的另外一套技术方案 —— Device Tree,也能对硬件进行抽象。单论硬件抽象功能,两者并无孰优孰劣之分,那随着 ARM/RISC-V 进入个人电脑/服务器领域,Device Tree 是否有可能伴随 ARM/RISC-V 的使用惯性而带入进来,并成为与 ACPI 抗衡甚至取代 ACPI 的新行业标准?

目前来看暂无这样的趋势。因为在个人电脑/服务器领域,Device Tree 相比 ACPI 还是有以下两点不足:

▲一是规范的涉及范围:Device Tree 的规范 [11] 只涉及如何使用 DTS(Device Tree Source)语言来描述硬件信息,而 ACPI 规范涉及的范围更广:除了如何使用 ASL 语言描述硬件信息,ACPI 规范还规定了操作系统如何与底层固件、硬件交互的很多要求。这些规范的约束,是实现 “一个通用操作系统可运行在不同机器上” 的必要条件之一。

▲二是灵活性:ASL 语法上更加灵活,如下面的例子,它支持变量赋值、流程控制等运算符,可以让固件在无需修改操作系统的情况下,实现更加灵活的行为:

DefinitionBlock ("", "DSDT", 2, "", "", 0x0)
{
    Name (INT1, 0x02)
    Method (CTDW, 1) {
        local0 = arg0
        while (local0) {
            printf ("%o",local0)
            local0--
        }
    }
    
    Method (CTUP, 1) {
        local0 = arg0
        while (local0 < 10) {
            if (!(local0 % 2)) {
                printf ("%o is even", local0)
            } else {
                printf ("%o is odd", local0)
            }
            local0++
        }
    }
}

因此,目前 Device Tree 有的,ACPI 有;Device Tree 没有的,ACPI 也有。并且在 ACPI 已经是个人电脑/服务器领域生态标准的前提下,要扩展 Device Tree 让它成为一个并没有明显优于 ACPI 的新标准,成为一件费力又不讨好的事情。自然而然地,在嵌入式领域使用更加轻量的 Device Tree,在个人电脑/服务器领域使用更符合行业生态标准的 ACPI,是更加合适的选择。

ARM 的选择

嵌入式领域高度定制化的产品特点,难以形成行业内通用的标准。在面对个人电脑/服务器这些有着几十年历史、涉及诸多零部件厂商、并且相互间遵循一定行业标准的 x86 传统生态领域时,ARM 嵌入式领域常用的 U-Boot、Device Tree 等技术方案也难以施展拳脚。那么 ARM 的选择自然不言而喻:在个人电脑/服务器也全面拥抱起了 x86 常用的 UEFI、ACPI、SMBIOS 等标准。

对此,ARM 于 2020 年发布了 Arm SystemReady 标准,对不同产品的软、硬件做出规范要求:

ARM SystemReady 对不同产品的要求。取自 arm-systemready-whitepaper.pdf [12] 

在 2024 年 11 月 21 日发布的 Arm SystemReady Requirements Specification v3.0 [13]中,ARM 调整了 SystemReady 的分类:

  • SystemReady LS 被弃用
  • SystemReady ES 和 SR 统称为新的 “SystemReady”,用于那些可运行一个无需定制化修改的、通用的操作系统的产品,它要求 ACPI 是必须实现的
  • SystemReady IR 则改名为 “SystemReady Devicetree”,用于那些运行嵌入式 Linux/BSD 操作系统的产品。

取自 Arm SystemReady Requirements Specification v3.0 [13]

可见,对于个人电脑/服务器领域,ACPI 是十分重要的,它是实现操作系统通用化不可或缺的一环。

Hardware-reduced ACPI

ACPI 诞生于 x86 架构,它的规范中也规定了需在硬件上实现的 ACPI 寄存器组,这些寄存器跟 x86 自然有着千丝万缕的关系。但 ARM/RISC-V 硬件上不一定实现有这些寄存器,那它们要如何支持 ACPI 呢?

ACPI 规范 v5.0 引入了一种新的方法,称为 hardware-reduced ACPI,它不再要求必须实现以下这些 ACPI 硬件特性 [14] :

  • Power Management (PM) timer
  • Real Time Clock (RTC) wake alarm
  • System Control Interrupt (SCI)
  • Fixed Hardware register set (PMx_* event/control/status registers)
  • GPE block registers (GPEx_* event/control/status registers)
  • Embedded controller

相对应地,某些功能有了替代的实现手法。例如 ACPI 的事件通知模型,在传统 x86 上它是通过 SCI(System Control Interrupt)中断和 GPE(General-Purpose Event)寄存器实现,由 SCI 中断来触发 ACPI 事件的产生;而在 hardware-reduced ACPI 中,则变成可通过 GPIO-signaled ACPI Events 或 Interrupt-signaled ACPI Events 这两种机制实现,前者是由 GPIO 中断来触发 ACPI 事件,后者则可由任意中断来触发 ACPI 事件。

ACPI 电源管理

ACPI 电源管理相关状态分类

关于电源管理,ACPI 规范中为系统以及设备定义了以下状态(state):

取自 ACPI specification v6.6 [2]

▲系统的全局状态 Gx(Global System State)与睡眠状态 Sx(Sleeping State):

  • G0(S0)- Working:正常工作状态。
  • G1 – Sleeping:以较低功耗运行的状态。从用户角度 “看起来” 系统像是关闭的,但无需重启操作系统就可回到 G0/S0 状态。G1 又细分为 S1~S4 四种传统状态,以及一种相对较近新提出的 S0ix 状态:
    • S1 – POS(Power on Suspend):最耗电的睡眠状态。该状态下会维持 CPU 和内存的供电,操作系统的上下文不会丢失。
    • S2:一种比 S1 更深的睡眠状态。该状态下 CPU 会断电。
    • S3 – STR(Suspend to RAM)/ Standby / Sleep:该状态下只有内存被供电,除了内存外的其他上下文都不会保留。
    • S4 – STD(Suspend to Disk)/ Hibernation / Non-Volatile Sleep :该状态允许主板断电,操作系统会将上下文保存在非易失存储介质(Non-Volatile Storage)中。
    • S0ix – 该状态有多种名称:ACPI 规范中称之为 Low Power S0 Idl[2],英特尔称之为 S0ix [15],微软称之为 Modern Standby [16],Linux 内核称之为 Suspend o Idle 或 S2Idle [17]该睡眠状态的功耗接近甚至低于传统的S3 状态,并且能更快地恢复到 G0/S0 状态。该状态还可以继续细分为 S0i1、S0i2、S0i3 等子状态。
  • G2(S5)- Soft Off:以最低功耗运行的状态。硬件不保留操作系统的上下文,操作系统经重启后才能回到 G0/S0 状态。该状态下并不一定能安全地移除硬件。
  • G3 – Mechanical Off:机械上的断电状态。不保留硬件上下文,可安全移除硬件。操作系统经重启后才能回到 G0/S0 状态。该状态下除了 RTC(real-time clock)外,功耗为零。
  • Legacy:如果系统不支持 ACPI,则运行在该状态。该状态下的电源管理通过 APM、Legacy PnP 等规范实现。

▲设备的电源状态 Dx(Device Power State):系统处于 G0/S0 状态时,设备可在以下电源状态之间切换(设备不一定会实现所有这些电源状态,实现的状态多寡会视设备类别而有所不同):

  • D0 – Fully-On:功耗最高的状态。设备完全活跃并能持续保持所有相关上下文。
  • D1:其含义视设备类别而定。通常会被定义为相比 D2 功耗更高、保留的上下文更多的状态。很多设备都没有定义该状态。
  • D2:其含义视设备类别而定。通常会被定义为相比 D1 或 D0 功耗更低、保留的上下文更少的状态。很多设备都没有定义该状态。
  • D3hot:其含义视设备类别而定。通常会被定义为在不影响设备枚举的情况下功耗尽可能低的状态。
  • D3(D3cold)- Off:设备完全断电。该设备所有上下文都会丢失。在软件上无法被枚举到。

▲CPU 的电源状态 Cx(Processor Power State):系统处于 G0/S0 状态时,CPU 可在以下电源状态之间切换:

  • C0:该状态下 CPU 会正常执行指令。
  • C1 – Halt:拥有最短唤醒延迟的状态。该状态下 CPU 不执行指令。
  • C2 – Stop Clock:比 C1 功耗更低,但唤醒延迟更长。该状态下 CPU 不执行指令。
  • C3 – Sleep:比 C2 功耗更低,但唤醒延迟更长。该状态下 CPU 不执行指令。

▲CPU 或设备的性能状态 Px(Device and Processor Performance State):当 CPU 或设备处于活跃状态时(C0/D0),可处于以下不同的性能状态:

  • P0:性能最高,功耗最大。
  • P1 ~ Pn:随着 n 增大,性能降低,功耗也降低。n 的大小视实际 CPU 和设备而定,最大不超过 255。

Hardware-reduced ACPI 的电源管理实现

系统级睡眠状态 Sx

传统 x86 ACPI 会借助硬件寄存器来实现 Sx 状态切换,而对于使用 hardware-reduced ACPI 的 ARM/RISC-V,通常也不与 x86 的 ACPI 方法兼容。但所幸,它们都定义有架构内通用的规范接口,并且也可使用 UEFI 的一些标准接口:

UEFI runtime service(运行时服务)ResetSystem() 可供操作系统调用来实现重启或关机的功能,从而实现 G0/S0 与 G2/S5 状态间的切换。通过 UEFI runtime service,与硬件平台相关的寄存器操作被下放到 UEFI 或更底层的固件中,对操作系统不可见,操作系统只需调用 UEFI 的标准接口即可实现重启和关机的功能。

▲ARM 定义有 SCMI(System Control and Management Interface)规范,可提供一套标准接口用于电源、性能以及系统管理 [18],从而实现 Sx 状态的切换。

▲RISC-V 定义 SBI(Supervisor Binary Interface)规范 [19],通过以下这些标准接口可实现 Sx 状态的切换:

  • SBI SRST(System Reset)Extension:可用于实现重启/关机功能。
  • SBI SUSP(System Suspend)Extension:可用于实现休眠功能。

设备电源状态 Dx

设备的 Dx 电源状态不涉及 ACPI 硬件寄存器,ARM/RISC-V 也与 x86 相同,通过 ACPI 规范中定义的 ASL 对象(object)来实现,具体包含以下这些:

取自 ACPI specification v6.6 [2]

取自 ACPI specification v6.6 [2]

CPU 电源状态 Cx 与性能状态 Px

对于 CPU 的电源状态 Cx 与性能状态 Px,ACPI 规范中均提供了方法供实现 hardware-reduced ACPI 的平台使用:

LPI(Low Power Idle)States 机制:结合 FFH 规范,通过该指令集架构平台自身的方法实现 CPU 的 Cx 状态控制。

CPPC(Collaborative Processor Performance Control)机制:结合 FFH 规范,通过协处理器对主处理器的 Px 状态进行控制。

LPI States 和 CPPC 两套机制都要结合 FFH(Functional Fixed Hardware)规范使用,FFH 规范让 ACPI 规范可以对接到各指令集架构平台自身内部的、架构间各异的一些实现方法。x86/ARM/RISC-V 都分别定义有自己的 FFH 规范[20][21][22]

例如 RISC-V,FFH 最终会对接到 SBI 扩展的实现中:

▲Cx 状态的控制由 “ACPI LPI States 机制 + RISC-V FFH 规范 + RISC-V SBI HSM(Hart State Management)Extension” 实现。

▲Px 状态的控制由 “ACPI CPPC 机制 + RISC-V FFH 规范 + RISC-V SBI CPPC Extension” 实现。

RISC-V ACPI 的相关规范

借 ARM 的 “他山之石”,RISC-V 社区早早地认识到了在个人电脑/服务器领域对接生态标准的重要性,因此十分积极地制定了一些相关规范,其中涉及 ACPI 的有:

RISC-V BRS(Boot and Runtime Services)Specification [23]:分别为通用系统和定制化系统提出了 BRS-I(Interoperable)和 BRS-B(Bespoke)两套标准,定义了它们在设备发现、系统管理等方面的软件要求(类似于 ARM 的 “SystemReady” 和 “SystemReady Devicetree”),其中也包括对 RISC-V ACPI 的一些新增定义与实现要求:

为 RISC-V 新增的 ACPI ID。取自 RISC-V BRS specification v0.91 [23]

RISC-V 必须实现的 ACPI 表。取自 RISC-V BRS specification v0.91[23]

RISC-V 可按需实现的 ACPI 表。取自 RISC-V BRS specification v0.91 [23]

▲ACPI 规范中一些原有内容也增加 RISC-V 的支持,例如新增了描述 RISC-V Hart 能力的 RHCT(RISC-V Hart Capabilities Table),在 MADT(Multiple APIC Description Table)中增加描述 RISC-V 中断控制器 AIA 和 PLIC 的结构,等等:

ACPI v6.6 的改动(截图中并没有囊括所有),其中包含很多 RISC-V 内容。取自 ACPI specification v6.6 [2]

RIMT(RISC-V IO Mapping Table)Specification [24]:定义了描述 RISC-V IOMMU 信息的 ACPI 表的规范。

RISC-V FFH(Functional Fixed Hardware)Specification [22]:定义了 RISC-V 的 ACPI FFH 规范,供 ACPI 的 LPI States 机制和 CPPC 机制使用,实现对 CPU Cx 状态和 Px 状态的控制。

进迭时空芯片对 ACPI 的支持

目前进迭时空已经量产的 8 核 64 位 RISC-V AI CPU 芯片 K1,已经基于开源的 Tianocore EDK II [6],完成了对 UEFI 的适配,预计在不远的将来也会加入 ACPI 的支持,在终端领域适配更加通用、开放的生态。

另外进迭时空正在研发中的服务器芯片平台,基于自研的 X100 高性能处理器核,完整支持虚拟化、安全、RAS 等服务器特性,符合 RISC-V Server SoC Specification v1.0 [25]规范的要求,目前也已经完成了 UEFI + ACPI 的适配,可基于 ACPI 启动 Linux 内核:

进迭时空服务器芯片平台 Linux 内核启动日志

结语

RISC-V 因 “开放” 的优势而发展迅猛,但 “碎片化” 的乌云也一直笼罩在其上空。为此 RISC-V 社区制定了各种软、硬件规范,也接受了源自于 x86 的 ACPI,让碎片化问题逐步得以改善。相信在不远将来的某一天,RISC-V 也能像 x86 那样,只需一个操作系统镜像就可在不同的机器上运行。