PSCI接口规范
1 介绍
本文主要是在ARM架构的不同异常等级上工作的软件之间,提供一个标准的电源管理接口。这些软件,比如Linux
、Hypervisor
、安全Firmware
和可信OS之间必须能够实现互相操作。而这些软件可能由不同厂商提供,本标准就是为这些软件的集成提供便利。
这些PSCI接口有利于电源管理
代码通用化、模块化:
- CPU核的空闲管理
- CPU核的动态添加、删除,以及辅核引导
- 系统关机和复位
该接口规范不会包含动态电压频率调节(DVFS
)或设备电源管理(比如,GPU
等外设的管理).
该接口规范设计用来与硬件探测技术(如ACPI
和FDT
)等配合使用,并不是要取代ACPI
或FDT
。
本文描述了PSCI版本1.1
,1.0
和0.2
。对于0.2
,还提供了一个勘误更新。
本文包含以下内容:
- 第1章 提供介绍和文档引用
- 第2-4章 提供背景知识,包括:
- PSCI的设计目的
- 电源状态术语的定义
- 发送PSCI请求的方法
- ARM架构知识
- 第5章 提供PSCI功能的主要描述
- 第6章 提供PSCI功能的实现细节
- 第7-8章 提供PSCI规范的修订历史和术语表
1.1 ARM官方文档
下面这些文档包含与本文档相关的信息:
- [1] ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition (ARM DDI 0406).
- [2] Embedded Trace Macrocell Architecture Specification (ARM IHI 0014).
- [3] Program Flow Trace Architecture Specification (ARM IHI 0035).
- [4] SMC调用约定(ARM DEN 0028).
- [5] ARM Architecture Reference Manual ARMv8, for ARMv8-A architecture profile (ARM DDI 0487).
- [6] ACPI规范.
- [7] PSCI设备树定义.
- [8] ARM可信固件(
ATF
). - [9] Power Control System Architecture Specification (ARM DEN 0050).
2 背景知识
支持电源管理的操作系统,能够动态改变CPU核的电源状态,根据进行负载均衡,努力降低功耗。电源管理技术主要分为两类:
- 空闲管理:当在某个核上,OS的内核没有线程可以调度时,可以将该核置于
clock-gated
、retention
、power-gated
状态。但是,该核对于OS而言还是可用的。 Hotplug
热插拔:根据对算力的需求变化,而将CPU核置于离线、在线。离线时,OS负责将所有中断和线程从核上迁移走;当在线时,OS负责重新进行负载均衡。
尽管由单个供应商提供嵌入式系统的软件可能更简单,但事实并非如此。为了安全,ARM将硬件架构划分为4个不同的异常级(EL
)以及2个安全状态,以便支持不同特权的软件分区。
下表是基于ARM架构的硬件平台上的软件划分:
AArch32 | AArch64 | 软件和供应商 |
---|---|---|
NS_EL0 (PL0) | NS_EL0 | 非特权应用,如APP |
NS_EL1 (PL1) | NS_EL1 | 富操作系统内核,如Linux、Windows、iOS |
NS_EL2 (PL2) | NS_EL2 | Hypervisor(Citrix、VMWare、OK-Labs) |
S_EL0 (PL0) | S_EL0 | 可信OS的应用 |
S_EL3 (PL1) | S_EL1 | 可信OS的内核(Trustonic) |
S_EL3 (PL1) | S_EL3 | 安全Monitor,执行安全固件(ATF) |
AArch32是ARMv8架构的32位执行状态,在ARMv8架构之前采用的执行状态。在ARMv7处理器中,异常级的概念是隐含的,相关文档也并未说明。那时候,虚拟化扩展提供
EL2
功能,安全扩展提供EL3
功能,安全状态由特权级(PL-Privilege Level
)标识异常的层次结构。更多信息,参考第3.2节。
因为不同供应商的不同操作系统都可运行在ARM系统上,电源管理就需要一个协作的方法。考虑到运行在EL1
上的类OS软件或运行在EL2
上的hypervisor
软件,一般处于非安全状态,如果想要进入idle
状态、给一个核上电或掉电、或复位或关闭系统,更高异常级的监控软件必须能够响应这种电源状态变化的请求。
同样,如果某个wakeup
事件改变了核的电源状态,那么这些监管软件需要执行诸如恢复上下文
之类的操作。PSCI提供了不同监管软件之间互操作和集成的标准接口定义。本文档会详细描述这类接口,以及在idle
、hotplug
、shutdown
和reset
情况下如何使用它们。
3 假设和建议
本文档定义了在不同监管软件之间协调电源管理的API。这种API允许监管软件请求给核上电、掉电,安全上下文的核间迁移(可信OS需要)。本文中,假设EL2
和EL3
都已经实现。
3.1 PSCI用途
PSCI
具有下面的用途:
- 提供给监控程序通用接口,可以在下列情况时,管理功耗:
- CPU核的空闲管理;
- 系统中的CPU核动态添加和删除,也称之为
hotplug
热插拔; - 其它辅核的启动;
- 将受信任的操作系统上下文从一个核迁移到另一个核;
- 系统关机和复位。
- 提供给监控管理程序通用接口,结合
FDT
和ACPI
描述去支持电源管理代码的泛化。
PSCI
不包括:外设电源管理
和 动态电压和频率调节(DVFS)
。
PSCI
不提供给监控软件电源状态表示。但是,可以和ACPI
或FDT
等硬件描述技术结合使用。
3.2 异常级别、ARMv7特权级别和最高特权级别
ARMv8架构明确提出了EL
的概念,也定义了安全状态下软件执行特权的层次结构。
ARMv7架构中,EL
概念是隐含在架构体系中的:
- 虚拟化扩展提供了
EL2
的功能(只存在非安全状态下)。 - 安全扩展提供了
EL3
功能,包含对两种安全状态的支持。控制是在Monitor
模式下完成的,该模式也只存在于安全状态下。
ARMv7使用PL
(特权级)的概念描述软件执行的特权层级结构。因为Monitor
模式的存在,ARMv7的非安全状态和安全状态是非对称的,如下所述:
- 非安全状态,特权层次结构是:
PL0
, 非特权,适用于User
模式PL1
,OS级特权
,适用于System
,FIQ
,IRQ
,Supervisor
,Abort
和Undefined
模式PL2
,hypervisor
特权,适用于Hyp
模式
- 安全状态,特权层次结构是:
Secure PL0
, 非特权,仅适用于User
模式Secure PL1
, 可信OS和Monitor
级特权,适用于System
,FIQ
,IRQ
,Supervisor
,Abort
,Undefined
和Monitor
模式
AArch32执行状态下,异常级和之前的运行模式对应关系如下:
- 非安全状态:
EL0
:User
模式EL1
:System
,FIQ
,IRQ
,Supervisor
,Abort
和Undefined
模式EL2
:Hyp
模式
- 安全状态:
Secure EL0
:User
模式EL3
:System
,FIQ
,IRQ
,Supervisor
,Abort
,Undefined
和Monitor
模式
本文档中如果不特殊声明,则使用异常级别(EL
)的术语:
- 通常情况下,提及的
EL1
和EL0
就是指非安全EL1和EL0
,除非有特殊说明;
3.3 ARM架构上的软件栈
ARM设备上可能有的软件栈,如下所示:
如图所示,非安全空间中的拥有特权的代码:
Rich-OS
内核:比如,Linux
或Windows
,运行在非安全EL1
。如果运行在Hypervisor
之上,则Rich-OS
内核作为hypervisor
的一个客户机运行。Hypervisor
:运行在EL2
,只有非安全状态(新ARMv8架构中,已经存在安全状态的hypervisor
)。
安全空间中拥有特权的代码:
安全平台固件(SPF)
: 芯片或OEM厂商提供。也是系统启动阶段,运行的第一段程序。它提供的服务有,平台初始化
、可信OS的安装
、SMC调用的调度执行
。有些调用的目标可能是SPF
,另一些则可能是可信OS
。SPF
可以运行在EL3
,也可以运行在安全EL1
(但是,条件是EL3
运行在AArch64
状态)。ARM提供了一个开源的可信固件代码。可信OS
: 为normal
空间提供安全服务,即为安全应用提供一个运行时环境。在AArch32
状态下,可信OS
运行在安全EL3
,如果是AArch64
状态,运行在EL1
。
PSCI规范主要关注安全、非安全世界之间的电源管理接口。它提供了发送电源管理请求的方法。所以,为了处理这些请求,SPF
必须包含PSCI实现。
另外,PSCI实现也可能需要在SPF
和可信OS之间建立通信。当然,它们之间的处理根据厂商不同而有所不同。
尽管,PSCI主要关注安全和非安全世界之间的电源管理请求,但是,也可以在Rich OS
和hypervisor
之间使用。
3.4 通道
在不同的异常级别之间提供可以传送消息的通道(一般使用SMC
异常指令实现),这样才能提供相同的PSCI电源管理接口。具体参考SMCCC
。
3.5 安全软件和电源管理
许多可信OS不支持SMP。即使在多核平台上,也是运行在某个指定的核上。所以,期望发起SMC调用的核与可信OS运行的核是同一个。不支持多核,可以保证可信OS小而美,容易通过功能安全认证。
基于ARM架构的系统通常会包含一个电源控制器,或者电源管理电路,以便管理CPU核的电源。它会提供许多电源管理功能,比如将核
、簇
或者集群
转变为低功耗状态。在低功耗状态,核可以完全关掉,也可以不执行代码而处于静默状态。ARM 强烈推荐由安全空间控制电源状态的变化。否则,在进入低功耗状态之前,不能清除安全状态(包括安全cache的清零)。其它的电源管理,比如动态电源性能管理(通过调节电压和频率实现)不在本接口规范的范围内。
3.6 虚拟化和CPU核电源管理策略
虚拟机可以分为两种类型
Type-1
: 有时候称为native
或bare metal
。通俗理解的话,就是hypervisor代码直接接管硬件,对上提供统一的虚拟隔离空间。所以,Guest OS
看到的都是虚拟设备。Type-2
: 有时候称为hosted
,或者托管类型hypervisor
。这类虚拟程序需要依赖一个主机OS,作为宿主。该Host OS
看到的是真实硬件,但是Guest OS
看到的是虚拟设备。
这是一个泛泛的分类,可能会有许多变种。ARMv8架构允许在EL1
或EL2
运行一个Type-2
类型的hypervisor
,这更加模糊了类型-1
和类型-2
的差异。但是,对于电源管理来说,不论哪种类型的hypervisor
,都需要捕获这种PSCI调用。
从电源管理和虚拟化的角度来看的话,有两种类型的OSPM
:
- 物理OSPM: 这个概念包含管理物理电源状态的软件。
- 虚拟OSPM: 这是存在于虚拟机的
Guest OS
中的OSPM
,它管理的是虚拟的,而不是物理电源状态。
对于type-2 hypervisor
,物理OSPM
存在于主机OS中。电源管理策略由运行在EL1
的Rich OS
负责管理。物理OSPM就包含在这种Rich OS
中。在该层,直接拥有物理核的视角。下图的左边部分就是示例。
对于本文涉及的电源管理,type-2 hypervisor
的行为取决于调用者。如果调用者是Host OS
,hypervisor
允许调用直接穿透到安全平台固件(SPF
)。在这种情况下,hypervisor
只需执行必要的操作,比如保存powerdown
时的状态。然后,使用调用者传递过来的参数调用SPF
。如果没有特殊操作,hypervisor
甚至不会捕获来自Host OS
的调用,直接将其路由给SPF
。Guest OS
使用虚拟OSPM
,也是通过PSCI API
发出电源管理请求,但是发送的目标是虚拟核和虚拟电源状态。hypervisor
会捕获这些请求,然后将其发送到物理OSPM
。然后,由物理OSPM
决定是否请求真实的物理电源管理。对于虚拟机来说,电源管理到hypervisor
就结束了。
对于type-1 hypervisor
,电源管理策略通常是hypervisor
自身管理的。如上图右半部分所示。hypervisor
实现物理OSPM
模块。这种情况下,虚拟机拥有的是虚拟核。由hypervisor
决定虚拟机的虚拟电源状态是否请求物理电源控制,如果需要,则使用PSCI API
调用安全平台固件SPF
。同样,Guest OS
也使用PSCI API
接口将虚拟电源管理请求发送给hypervisor
。对于虚拟机来说,电源管理到hypervisor
就结束了。
在某些情况下,type-1 hypervisor
委托一个特权Guest OS
管理电源。这种情况下,物理OSPM
在特权Guest OS
中实现。大概的电源管理方法与type-2 hypervisor
类似。
4 PSCI使用场景和要求
4.1 空闲管理
当一个核处于idle
状态时,OSPM
将其置于低功耗状态。通常,选择进入不同的电源状态,会有不同的entry
和exit
延迟,也会有不同的功耗。想要进入哪种电源状态,依赖于核重新工作的时间。除了核之外,电源状态也可能依赖于SoC中的其它组件的活动。每种状态都由一组组件的状态共同决定,进入该状态时,这些组件通过时钟控制(clock-gated
)或电源控制(power-gated
)。这些状态有时候也描述为浅睡眠
或深度睡眠
。通常,文献描述使用X
标识深度睡眠,Y
标识浅睡眠:
X
状态应该是Y
状态的超集。X
状态比Y
状态更省电。
从低功耗状态进入运行状态所需要的时间称为唤醒延迟
。通常,深度睡眠状态具有更长的唤醒延迟。
尽管空闲电源管理是由核上的线程行为引起的,但是OSPM
将硬件平台设置的状态,也可能会影响除CPU核之外的其它组件。比如,如果SoC
中的最后一个核进入idle
状态,OSPM
就可以考虑整个SoC
的电源状态了。此时的选择也会受系统中的其它组件影响,所以,应该在SPF
、hypervisor
、OS
之间协调电源管理。典型的例子是,当所有核,和其它请求者都处于空闲状态时,将系统置于一种状态,在这种状态下,将上下文保存在内存中(内存不断刷新中)。OSPM
必须提供必要的电源管理软件基础设施,确定能够对电源状态作出正确的选择。
在空闲管理中,当一个核被置于低功耗状态,它可能随时被唤醒事件激活,比如说中断。
ARM架构划分的电源状态有四种:
Run
CPU核上电,且可以正常运行的状态。Standby
CPU核上电。通过WFI
或WFE
指令进入该状态,由唤醒事件唤醒。此过程中,CPU核保持所有状态。也就是说,从standby
到run
状态,不会复位CPU核。CPU核的上下文都会被保持,唤醒即可访问这些内容。处于该核所在电源域的外部调试器(debugger
),能够访问debug
寄存器。换句话说,standby
状态不会影响调试器的使用。Retention
CPU核的状态,包括debug
设置等,保存在低功耗的保持寄存器中,这样允许CPU核至少可以关闭部分电源。从低功耗状态到运行态的转变,不用复位CPU核。低功耗转变到运行态时,原先保存的状态数据从保持寄存器中恢复。所以,从OS的角度来说,Retention
和Standby
状态没有什么差别,除了恢复时的入口地址,延迟和使用上的一些限制之外。但是,对于外部debugger
来说,就不一样了,外部debug
请求事件会被挂起,debug
寄存器无法访问。Powerdown
该状态下,CPU核会被掉电。软件需要保存所有的核状态数据。从掉电到恢复运行,需要:- 上电后,需要复位CPU核
- 恢复之前保存的核的状态数据
Powerdown
状态对上下文是破坏性的。不仅仅是CPU核的状态数据,如果是更深的休眠状态,可能包括GIC或者平台依赖的其它一些IP核也会掉电。所以,相关数据或状态必须保存。根据debug
和trace
电源域的组织架构,其上下文内容也有可能会丢失。所以,OS必须提供保存和恢复这些内容的机制。
对于OS来说,standby
和retention
都是一样的,除了debugger
之外。所以,后面我们使用standby
代表这两种状态。
ARM期望在具有最高特权的异常级别上,实现管理电源控制器的代码(通常就是ATF
)。那么就必须提供接口,以便OSPM
将CPU核置于低功耗状态的消息事件,从不同的异常级层层往下传递(假设越往下,异常级别越高)。而PSCI
就是这样的一种机制,将OSPM
的电源请求传递给下一个异常级EL
。
对于standby
状态,直接使用WFI
或WFE
指令即可进入。但是,更深层的standby
或retention
状态,则要求对电源控制器
进行编程,而PSCI
仅提供访问电源控制器的接口,并隐藏了与平台相关的代码。
对于Powerdown
状态,则要求提供每一级EL
下保存和恢复上下文的接口。而且,对于Powerdown
状态还要求一个return
地址。这地址是被唤醒时,OS期望的开始运行的地方。对于Powerdown
状态,CPU核从reset
复位向量(安全状态)开始运行。待完成初始化,则跳转到Powerdown
之前要求的那个返回地址
处开始执行。PSCI
提供了传递返回地址
和上下文
的参数。
4.2 电源状态系统拓扑
多核系统中,不同的电源域
控制系统的不同部分。每一个电源域可能是一个或多个PE(CPU核
、协处理器
、GPU
),内存(Cache
、DRAM
),簇内、簇间一致性部件
等组成。
电源域中的每个组件的电源状态,都可以影响电源域中的其它组件。虽然,从物理上来说,电源域不是一个必须存在的层次结构。但是,从软件的角度来说,必须进行一个逻辑上的划分。如果想要改变电源域的电源状态,必须对其依赖项进行排序。比如,共享Cache
的电源域,以及使用共享Cache
的CPU核的电源域,它们之间的依赖关系。在这样一个系统中,为了保证数据的一致性,必须先关闭CPU核的电源,然后再关闭共享Cache
的电源。
上图展示了一个系统级的电源域的拓扑结构的示例。它拥有两个子电源域,每一个包含一个cluster簇,支持一组cluster电源状态。每一个cluster电源域,又包含两个子电源域,每一个包含一个CPU核,并额外支持一个电源状态。
从硬件的角度来看,一个系统被划分为多个单独或共享的电源域。每个电源域都可以表示为电源域拓扑树中的一个节点。兄弟电源域是互斥的。父电源域由子电源域共享。树中的各种级别(示例中的核、cluster簇和系统)称为电源级别
。较高的级别,更接近树的根(系统),较低的级别更接近树叶(核)。
4.2.1 局部电源状态和组合电源状态
上面拓扑结构中的各个节点都有自己的电源状态,我们称为局部电源状态
。当处于空闲核上的OS请求电源状态的改变时,不仅需要请求改变CPU核的局部电源状态,还需要请求改变父节点的电源状态。比如,上图的示例中,假设核1是簇0内最后进入空闲状态的,对于OS来说,就需要同时为簇0和核1请求电源状态改变。这种组合的电源状态我们就称为组合电源状态
。这种组合电源状态可不是随意组合的,而是电源等级越高的节点上,其电源状态越浅。换句话说,子节点进入休眠的程度应该大于等于父节点。规则如下:
- 电源等级高的节点掉电(
Powerdown
),电源等级低的节点也必须掉电; - 电源等级高的节点保持(
Retention
),电源等级低的节点只能是保持或掉电; - 电源等级高的节点待机(
Standby
),电源等级低的节点只能是待机、保持或掉电; - 电源等级高的节点运行(
Run
),电源等级低的节点可以是任意状态。
如下表所示:
系统级 | 簇级 | 核级 |
---|---|---|
Run | Run | Standby |
Run | Run | Retention |
Run | Run | Powerdown |
Run | Retention | Retention |
Run | Retention | Powerdown |
Run | Powerdown | Powerdown |
Retention | Retention | Retention |
Retention | Retention | Powerdown |
Retention | Powerdown | Powerdown |
Powerdown | Powerdown | Powerdown |
4.2.2 亲和力的层次结构
ARM系统通常是多核、或多簇的处理器。本文档使用亲和力的层次结构
描述CPU核和簇的架构关系。亲和力层次结构往往直接对应系统的电源拓扑,但这也不是绝对的。
4.2.3 电源状态协调
高层的节点进入某种局部电源状态,必须与子节点的电源状态进行协调。比如说,想要使一个簇(cluster
)进入Powerdown
状态,那么该簇内所有的核也必须进入Powerdown
状态。实现方式就是,其它核都进入Powerdown
状态,最后一个核再把自己和簇置于Powerdown
状态。
对于这种对子节点的电源状态进行协调的方式,PSCI
支持两种:平台协调模式、OS发起模式。
- 平台协调模式
这是默认的电源协调模式。在该模式下,PSCI实现者(如
ATF
等)负责协调电源状态。当某个核上没有任务时,OSPM为该核、以及所在簇请求允许范围内的最深休眠状态。因为该核的电源状态改变请求,可能影响其父节点,所以,PSCI实现者得根据簇内所有节点的情况选择最深的休眠状态。实际上,电源状态请求表达了两种限制: PSCI实现就会根据这两个限制,为指定的节点内所有核,选择一个最深的电源状态。下表展示了OSPM请求不同的电源组合状态,而PSCI最终能够响应的状态。假设簇1一直处于掉电状态中。
通常情况下,电源状态越深,唤醒延迟越长。所以,我们假设保持状态比掉电状态具有更短的唤醒延迟。但是,事实并非如此。根据第2个限制,PSCI实现者必须满足每个核唤醒时间的请求。假设双核系统,具有3层系统状态,状态A、B、C,电源休眠程度A < B < C
,而唤醒延迟则是A < C < B
。如果核0选择状态B,而核1选择状态C,系统将会进入状态A。状态B和C,不能同时满足2个核的要求。
PSCI 1.0
之前的版本只支持平台协调模式。
- 调用者请求的电源状态已经是最深休眠状态了;
- 调用者请求的电源状态的唤醒延迟不能比这更长了;
- OS协调模式
PSCI 1.0
引入,这种模式将电源协调的权利交给了OS。这种模式下,只有节点内的最后一个核进入空闲状态,OSPM才会为该节点申请空闲状态。 相比平台协调方式,OS协调方式则是将怎么选择最合适的休眠节点交给了OS,PSCI实现者只需实现,给我什么请求,我就响应什么动作即可
。示例如下表所示:
如表所示,我们可以看到OS视角和PSCI实现的视角有些不同的地方(红色标记)。这是因为OS虽然请求了,但是PSCI实现还未响应导致的。在上电的时候也会发生,因为PSCI实现会比OS更早看到CPU核。为了实现OS协调方式,必须解决竞争问题
。
- PSCI实现必须怀疑与自己视角不一样的电源状态请求;
- OS必须指明哪个核是最后一个,而且还要指明该核处于哪一级电源中,如,是簇内的最后一个核,还是系统内最后的一个核。
4.3 CPU热插拔和辅核启动
CPU热插拔的概念不需要再重复。但是,CPU热插拔
和空闲管理中的powerdown
还是有一些差异:
- 当核被拔出时,监控软件会停止在中断和线程处理中对该核的所有使用。调用监控软件认为核不再可用。
- 想要使用该核,必须发送命令将核再插入。
- 唤醒事件不会唤醒被拔出的核。
操作系统通常在主核上完成内核的引导过程,然后再启动辅核。所以,对于支持热插拔的系统来说,辅核的启动和hotplug的操作是相同的,可以提供一套接口。
对于运行在单核上的可信OS,移除该核可能不可行,除非把可信OS迁移到其它核上。
PSCI提供具有下列属性的接口:
- 监控软件可以请求给一个核上电。监控软件还必须提供一个启动地址,作为它从安全固件退出时,继续执行的地方。通过提供一个入口地址,监控软件可以直接在自己的地址空间中干一些特殊的事情。为此,监控软件还必须使用内部的
per-CPU
数据结构保存这些值。 - 监控软件可以请求给一个核掉电。并且还能通知更高异常级别上的软件。
- 监控软件还能请求将可信OS迁移到另一个核上。
监控软件,狭义上理解为操作系统的内核。
4.4 系统级的shutdown、reset和suspend
PSCI提供了接口,允许OS请求系统system shutdown
、system reset
和system suspend(suspend-to-RAM)
。芯片供应商应该提供这些函数的统一实现,它们与监控软件是独立的。没有提供suspend-to-disk
,这是因为它是系统关机的一种特殊情况。
这儿,system
的意思是从整台机器的视角看待问题。也就是说,不是单一关闭某个核或者簇那么简单。当然,运行在虚拟机中的客户机OS,如果调用这些接口不会发生物理状态的变化。
但是,如果没有hypervisor
,或者调用者是hypervisor
,则会导致电源状态的物理变化。即使调用者在物理机器上运行,术语系统可能也不是指整个物理机器。例如,假设一个高级服务器系统由多个单板组成,每个单板具有一个BMC (board management controller
),每个单板包含多个SoC。这样的系统可以在每个SoC上运行一个OS实例。在本例中,用于关闭系统的PSCI命令应用于单个SoC,而关闭整个单板需要通过管理接口访问BMC,而这个管理接口是调用操作系统或PSCI实现无法访问的。在本文档中,术语系统仅指对操作系统可见的机器视图。反映到本文档中,就是指一个SOC。
5 函数接口描述
功能接口描述。这些API
描述不包含底层的SMC
或HVC
调用。但是,这些函数却都遵守SMCCC
调用规约。如果实现了EL2
却没有实现EL3
,则hypervisor
使用HVC
为运行在EL1
的Guest OS
提供调用支持。调用格式都是一样的。PSCI
函数只能由非安全空间发起调用(EL1
或EL2
)。
5.1 PSCI_VERSION
- 功能描述
返回
PSCI
实现的版本号。 - 参数
uint32 Function ID
:0x8400 0000
- 返回值
uint32
:- 位
[31:16]
-主版本号; - 位
[15:0]
-次版本号;
- 位
- 注意
对于没有实现的
PSCI
函数,返回NOT_SUPPORTED
。
5.2 CPU_SUSPEND
- 功能描述 挂起CPU核或它之上的更高拓扑结构中的节点。用于空闲状态管理,期望CPU核唤醒时从之前的执行位置继续执行。
- 参数
- 对于
powerdown
请求,调用者必须保存复位重新运行时所需要的状态。也就是说保存的上下文必须是调用者在发生powerdown
调用之前power_state
参数所指示的电源级别(power_level
字段)下所有可见的状态。(就是调用者自己保存自己的状态,被调用者不管) - 对于掉电请求,调用者无需执行
Cache
或一致性操作。PSCI
实现者完成(PSCI实现侧负责内存一致性)。 - 调用者不能假设
powerdown
请求使用指定的entry point
地址返回。因为,powerdown
可能不能完成,比如因为中断挂起。也有可能因为与其它核的协调,真正进入的是浅睡眠模式(相比请求的休眠模式)。因此,PSCI
实现可能将请求的powerdown
状态降为standby
状态。如果降为standby
状态,PSCI
实现返回到PSCI
调用之后的指令,而不是指定的entry_point
入口地址。此时,返回码也是成功的。如果发生比较早的wakeup
事件,实现也是返回下一条指令,返回码也是成功的,也有可能成功的在指定的entry point
地址处返回。 - 正确的唤醒事件必须能够保证恢复到之前的状态。
CPU_SUSPEND
调用传递的入口地址必须是调用者视角下的物理地址。- 上下文标识符只对调用者有意义。PSCI实现者保存,唤醒时在返回的异常级别下,再传递给CPU核,通过该值,恢复掉电前的上下文。
INVALID_PARAMETERS
:如果发生下面的情况,就会返回该错误。INVALID_ADDRESS
如果传递的入口地址,PSCI实现者认为是非法的,就返回该值。PSCI 1.0
之前使用INVALID_PARAMETERS
代替该值。- 在
OS
协调模式下,如果发生以下两种情况,就会返回DENIED
: 在OS
协调模式下,如果系统的状态和请求状态不一致,会返回INVALID_PARAMETERS
,不同之处在于: - 为高于
核
电源级别的拓扑节点请求低功耗电源状态 - 该节点中至少一个子节点与请求的电源状态不兼容(比如,一个核发起请求,将系统级节点置于
powerdown
状态,但是,该系统节点中的另一个核处于retention
状态时,就会返回参数错误。) - 提供的
power_state
参数不正确。预期是与平台固件表(如ACPI
或FDT
一致) - 在OS协调模式下,发生以下两种条件时,也会返回参数错误:
- 在
DENIED
情况中,不一致的核必须运行中。错误会出现在调用者和实现者两侧。 - 在
INVALID_PARAMETERS
情况中,不一致的节点必须处于低功耗状态,不一致只能通过调用者(OS)的错误产生。 - 为高于
核
电源级别的拓扑节点请求低功耗电源状态 - 所有与请求不一致的核必须处于运行中,而不是低功耗状态
- 原始格式
PSCI 1.0
之前的版本支持的形式。当使用这种格式时,PSCI_FEATURES
使用CPU_SUSPEND
功能ID返回的标志字段的bit[1]
位被设置为0
。 各比特位的意义: 保留,必须是0 - 扩展
StateID
: 对于一个硬件平台,支持每个核、簇或整个系统固定组合状态。这些状态产生了一组合法的power_state
值。这些状态应该通过固件表(如ACPI
或FDT
)表示给OSPM
。为此,PSCI 1.0
引入了一个新的扩展StateID
格式。这种格式对于PSCI
的实现者来说更为灵活,方便开发者实现PSCI
,可以通过ACPI
或FDT
电源状态的描述进行改进。这样的情况下,原先的格式有些字段就多余了。 使用这种格式的时候,PSCI_FEATURES
函数的返回标志中的bit[1]
会被设为1
(传递CPU_SUSPEND
功能ID)。 注意:一种实现中不可能混用这两种格式。 下表是power_state
参数的位域(扩展StateID
格式)。 保留,必须是0 推荐的编码格式:参考前面。StateID
示例编码
0
,表示standby
或retention
状态;1
,表示powerdown
状态。另外,还说明entry_point_address
和context_id
的值合法;Level 0
: 核Level 1
: 簇Level 2
: 系统PowerLevel
,定义的电源域级别,也就是表示是核,簇还是系统层电源请求。PSCI 1.0
之前的版本称为AffinityLevel
。 但是电源域级别的命名,却是实现者定义的。一般情况下,按照如下方式命名:StateType
:状态类型StateID
:状态ID 对请求的组合电源状态进行标识。一般是实现者定义的。在OS协调模式下,StateID
必须能够表示哪个核是最后一个进入idle状态的。 这些信息必须体现在FDT
或ACPI
固件表中,以便在请求电源状态时,将这些信息添加到StateID
字段中。推荐编码格式可以参考第6.5小节。 核是电源等级中的最后一个• 0: Core Level• 1: Cluster Level• 2: System Level0x8400 0001
-SMC32
版本0xC400 0001
-SMC64
版本uint32 Function ID
:uint32 power_state
从PSCI 1.0
开始,支持两种格式。entry_point_address
唤醒时,程序继续执行的起始地址。可以是PA(物理地址)或IPA(中间物理地址)。context_id
该参数只对调用者有用。PSCI实现者只需保留一下该参数的备份即可。从掉电状态唤醒时,PSCI
将该值写入到R0
、W0
或X0
通用寄存器中,进入异常的程序会通过该寄存器将保存的上下文内容恢复。- 调用者的责任
在发起
CPU_SUSPEND
调用之前,非安全空间必须遵守以下规则: 调用者必须处理可能的错误码: - PSCI实现者的责任:状态协调
在
平台
协调者模式中,调用者通过power_state
参数传递的指定进入的电源状态,在语义上不是强制的。相反,它代表的是调用者容忍的最深的电源状态。此种情况下,是通过PSCI
实现真正进入的电源状态。为此,如果一个核没有调用CPU_ON
而上电,或者调用了CPU_OFF
而关闭的情况下,假定该核进入了最深的电源状态。 而在OS
协调模式中,调用者显式请求某个特定的电源状态,而不是让PSCI
实现决定。实现必须遵循请求,除非与实现当前的状态不一致。 - PSCI实现者的责任:与可信OS或PF进行交互
PSCI实现必须能够与可信OS或SP进行通信。交互方法请参考
ARMv8-A
的Firmware Framework
。 因为某些原因,可信OS或SP可能不兼容某种特殊的状态。这种情况下,ARM建议:可信OS或SP使用自定义的机制与非安全空间通信,保证它的限制可以被非安全空间的代码考虑。 - PSCI实现者的责任:Cache和内存一致性管理
Powerdown
状态要求清除Cache。PSCI实现者必须在掉电一个节点之前,为该节点中所有的Cache和正在最后关闭的那个核执行清除操作。另外,PSCI实现还需要在启动阶段执行对Cache的失效操作,除非这是硬件能够自动完成的。在相关处理器和互连IP的技术参考手册中可以看到,上电或掉电应该遵守的顺序。 - PSCI实现者的责任:返回状态
当从
standby
状态返回时,对于调用者来说,CPU核的状态应该没有变化,除了定时器和由于唤醒中断造成的CPU interface的变化之外。对于核来说,standby
状态和使用WFI
指令没有什么不同。唯一的不同就是,调用SMC指令造成的寄存器变化。R0
或W0
返回的值是错误码。对于standby
状态,成功时返回SUCCESS
。对于powerdown
状态,如果成功不会返回,因为唤醒时,从传递的入口地址处开始执行。如果不成功,返回错误码,表明错误原因。
- 对于
- 返回值
int32
:SUCCESS
;INVALID_PARAMETERS
;INVALID_ADDRESS
;DENIED
;
- 注意
对于没有实现的PSCI函数,返回
NOT_SUPPORTED
。
5.3 CPU_OFF
- 功能描述
关闭核。用于
hotplug
。只能使用CPU_ON
调用重新开启一个核。 - 参数
0x8400 0002
uint32 Function ID
:
- 返回值
int32
:成功不会返回;否则返回DENIED
。
5.4 CPU_ON
- 功能描述
启动一个核。有两种情况:(1)启动阶段时调用;(2)该核之前被
CPU_OFF
关闭。 - 参数
[24:31]
: 必须是0[16:23]
: 匹配MPIDR.Aff2
位域[8:15]
: 匹配MPIDR.Aff1
位域[0:7]
: 匹配MPIDR.Aff0
位域[40:63]
: 必须是0[32:39]
: 匹配MPIDR.Aff3
位域[24:31]
: 必须是0[16:23]
: 匹配MPIDR.Aff2
位域[8:15]
: 匹配MPIDR.Aff1
位域[0:7]
: 匹配MPIDR.Aff0
位域0x8400 0003
-SMC32
版本0xC400 0003
-SMC64
版本uint32 Function ID
: 功能IDuint32/uint64 target_cpu
:目标核MPIDR
寄存器的备份。如果是AArch32
: 如果是AArch64
:uint32/uint64 entry_point_address
:入口地址 当核返回到非安全异常级时必须执行的地址。SMC64
版本时,是64位的物理地址或中间物理地址;SMC32
版本时,是32位的物理地址或中间物理地址;uint32/uint64 context_id
:上下文地址 当核返回到非安全异常级时:SMC64
版本时,该值必须保存在X0
寄存器;SMC32
版本时,该值必须保存在R0
寄存器。需要把该地址的上下文内容恢复(堆栈、执行状态、中断状态等)。
- 返回值
int32
:SUCCESS
,成功则返回该值;INVALID_PARAMETERS
,描述了一个无效的MPIDR
;INVALID_ADDRESS
,ATF
认为传递进来的入口地址非法;ALREADY_ON
,ATF
认为该核已经启动;ON_PENDING
,已经发起了CPU_ON
请求,ATF
还未处理;INTERNAL_FAILURE
,因为物理原因,不能启动CPU核。
5.5 AFFINITY_INFO
- 功能描述 请求某个亲和力等级上的信息。
- 参数
0
:target_affinity
中的所有位域都是有效的。在不支持硬件线程化的处理器系统中,target_affinity
将会表示单个核。1
:表示target_affinity
中,忽略Aff0
位域。target_affinity
表示亲和力等级为1的处理单元。2
:表示target_affinity
中,忽略Aff0
和Aff1
位域。target_affinity
表示亲和力等级为2的处理单元。3
:表示target_affinity
中,忽略Aff0
、Aff1
和Aff2
位域。target_affinity
表示亲和力等级为3的处理单元。0x8400 0004
-SMC32
版本0xC400 0004
-SMC64
版本uint32 Function ID
:target_affinity
同CPU_ON
的target_cpu
参数格式一样。(SMC32
或SMC64
)lowest_affinity_level
表示target_affinity
参数中有效的最低亲和力级别。该参数允许AFFINITY_INFO
调用者请求大于0的亲和力等级的信息。 可能的值: 从PSCI 1.0
版本开始,AFFINITY_INFO
不再需要支持高于0
的亲和级别。
- 返回值
int32
:2 ON_PENDING
,亲和力对象正在转换为ON
状态的过程中;1 OFF
,亲和力对象中,所有核都关闭;0 ON
,亲和力对象中,至少有一个核开启;INVALID_PARAMETERS
(PSCI 1.0
以上,请求亲和力值大于0的请求会返回该值);DISABLED
,由于物理原因禁止。
5.6 MIGRATE
- 功能描述 (可选的)请求将可信OS迁移到另一个核上。
- 参数
0x8400 0005
-SMC32
版本0xC400 0005
-SMC64
版本uint32 Function ID
:target_cpu
同CPU_ON
调用的target_cpu
参数一样。
- 返回值
int32
:SUCCESS
,成功则返回该值;NOT_SUPPORTED
,不支持该功能,或不需要迁移;INVALID_PARAMETERS
,描述了一个无效的MPIDR
;DENIED
,可信OS启动,但是不可迁移;INTERNAL_FAILURE
,因为物理原因,不能迁移;NOT_PRESENT
,可信OS不在请求的核上。
5.7 MIGRATE_INFO_TYPE
- 功能描述 (可选的)请求可信OS支持多核的情况。
- 参数
0x8400 0006
uint32 Function ID
:
- 返回值
int32
:0
支持单核迁移的可信OS。可信OS只能运行在一个核上。可信OS支持迁移功能,可以被迁移到任意一个核上。如果尝试对运行可信OS的核调用CPU_OFF
,请求会被拒绝(DENIED
)。1
不支持单核迁移的可信OS。可信OS只能运行在一个核上。可信OS不支持迁移功能。调用MIGRATE
会被拒绝。2
可信OS既不存在、也不需要迁移。这类系统不要求调用者使用MIGRATE
功能。如果硬要调用,返回NOT_SUPPORTED
。NOT_SUPPORTED
调用操作系统可以认为等价于返回值为2
的情况。
5.8 MIGRATE_INFO_UP_CPU
- 功能描述 (可选的)返回单核可信OS所在的核。
- 参数
0x8400 0007
-SMC32
版本0xC400 0007
-SMC64
版本uint32 Function ID
:
- 返回值
可能是32位或64位:
UNDEFINED
:如果MIGRATE_INFO_TYPE
调用返回2
或NOT_SUPPORTED
;基于MPIDR的值
:格式与CPU_ON
调用中的target_cpu
参数一样。
5.9 SYSTEM_OFF
- 功能描述
关闭系统。
SYSTEM_OFF
提供了一个系统关闭的接口。在调用该接口之前,调用者必须将所有的核置于已知状态。调用也只能是由非安全空间发起。一旦发起该调用,PSCI实现将会完全关闭最高等级的电源(也就是系统电源)。 启动必须是冷启动。 - 参数
0x8400 0008
uint32 Function ID
:
- 返回值 不需要返回。
5.10 SYSTEM_RESET
- 功能描述 提供系统冷复位的方法。
- 参数
0x8400 0009
uint32 Function ID
:
- 返回值 不需要返回。
5.11 SYSTEM_RESET2
- 功能描述
(可选)对
SYSTEM_RESET
的扩展,PSCI 1.1
引入。提供:- 架构相关的
reset
方法 - 供应商提供的
reset
方法
- 架构相关的
- 参数
Bit[31]
,保留的话就必须为0.Bits[30:0]
- 设为1,则使用供应商提供的reset方法;
- 设为0,则为架构提供的reset方法;
0x0
SYSTEM_WARM_RESET
.- 其它值保留.
- 对于供应商提供的reset方法,这些位的意义由供应商定义。
- 对于架构提供的reset方法,定义如下:
0x8400 0012
-SMC32
版本0xC400 0012
-SMC64
版本uint32 Function ID
:reset_type
32位值,被分为两部分:cookie
32位或64位值。用来传递额外的reset
信息。
- 返回值
int32
:SUCCESS
,成功不返回NOT_SUPPORTED
;INVALID_PARAMETERS
;
5.12 MEM_PROTECT
- 功能描述
(可选)通过在将内存移交给操作系统加载程序之前,重写这段内存,来提供针对冷重启攻击的保护。
PSCI 1.1
引入。 - 参数
0x8400 0013
uint32 Function ID
:enable
32位值,非0值表示内存保护被启动。0值表示禁止保护功能。
- 返回值
int32
:- 成功,则返回之前的使能状态:0,表示之前是被禁止的;1,表示之前是使能的。
- 失败,则返回
NOT_SUPPORTED
。
5.13 MEM_PROTECT_CHECK_RANGE
- 功能描述
(可选)可以检查某段内存是否被
MEM_PROTECT
保护。PSCI 1.1
引入。 - 参数
0x8400 0014
-SMC32
版本0xC400 0014
-SMC64
版本uint32 Function ID
:uint32/64 base
要检查的内存的基地址;uint32/64 length
要检查的内存的长度;
- 返回值
int32
:SUCCESS
DENIED
NOT_SUPPORTED
5.14 PSCI_FEATURES
- 功能描述
查询
SMCCC_VERSION
或者某个PSCI
功能是否被实现。PSCI 1.0
引入。 - 参数
0x8400 000A
uint32 Function ID
:psci_func_id
功能ID:PSCI
或SMCCC_VERSION
- 返回值
如果功能实现,则意义如下:
psci_func_id标志位描述
CPU_SUSPEND
功能ID[31:2]
保留,等于0[1]0
,power_state
使用原始格式(PSCI 2.0
)1
,power_state
新的扩展StateID
格式[0]0
,不支持OS协调方式1
,支持OS协调方式其它功能ID[31:0]
保留都是0
5.15 CPU_FREEZE
- 功能描述
(可选)将核置于供应商自定义的低功耗状态中。与
CPU_OFF
不同,中断仍然可以传递给该核。但是,该核一直会处于低功耗状态中,直到CPU_ON
调用将其启动。PSCI 1.0
引入。 - 参数
0x8400 000B
uint32 Function ID
:
- 返回值
int32
:- 成功,不返回。
- 失败,则返回
NOT_SUPPORTED
或DENIED
。
5.16 CPU_DEFAULT_SUSPEND
- 功能描述
(可选)将核置于供应商自定义的低功耗状态中。与
CPU_SUSPEND
不同的是,不需要指定power_state
参数。PSCI 1.0
引入。 - 参数
0x8400 000C
-SMC32
版本0xC400 000C
-SMC64
版本uint32 Function ID
:entry_point_address
参考CPU_SUSPEND
;context_id
参考CPU_SUSPEND
;
- 返回值
int32
:SUCCESS
INVALID_ADDRESS
5.17 NODE_HW_STATE
- 功能描述
(可选)返回系统的电源域拓扑结构中一个节点的硬件状态。
PSCI 1.0
引入。 - 参数
0x8400 000D
-SMC32
版本0xC400 000D
-SMC64
版本uint32 Function ID
:target_cpu
参考CPU_ON
;power_level
表示想要请求的节点,在电源域拓扑结构的层级。这是供应商自定义的,但是0保留给CPU核。
- 返回值
int32
:2 HW_STANDBY
:返回2
,表示处于standby
或retention
电源状态;1 HW_OFF
:返回1
,表示处于powerdown
状态;0 HW_ON
:返回0
,表示处于run
状态;NOT_SUPPORTED
INVALID_PARAMETERS
5.18 SYSTEM_SUSPEND
- 功能描述
语义等价于
CPU_SUSPEND
,将系统置于最低功耗状态。该调用是实现system suspend-to-RAM
的基础(ACPI规范中描述的S2和S3状态)。值得注意的是,系统进入S2或S3状态,需要几个前提条件。系统中所有设备必须与进入该系统挂起状态兼容,可能需要在调用之前,优雅地处理各个外设。这些前提条件不在本文的讨论范围内。SYSTEM_SUSPEND
仅限于提供进入S2或S3状态的机制,所有必要的条件都由调用OS满足。尽管ACPI将suspend-to-RAM
功能分为S2
或S3
两种状态,但是PSCI
只提供了一个API。 同SYSTEM_SHUTDOWN
和SYSTEM_RESET
一样,该函数适用于调用OS的机器视角。 为了使用该函数调用,调用者必须使用CPU_OFF
关闭所有的核,但保留一个核。剩下的这个核调用SYSTEM_SUSPEND
,传递entry_point_address
和context_id
参数(唤醒时用),进入挂起状态。调用者(OS)可以在调用SYSTEM_SUSPEND
之前,使用AFFINITY_INFO
函数保证所有其它核都已关闭。 - 参数
0x8400 000E
-SMC32
版本0xC400 000E
-SMC64
版本uint32 Function ID
:entry_point_address
:参考CPU_SUSPEND
context_id
:参考CPU_SUSPEND
- 返回值
返回32位值。
NOT_SUPPORTED
INVALID_ADDRESS
ALREADY_ON
- 成功不会返回;
- 失败则返回:
5.19 PSCI_SET_SUSPEND_MODE
- 功能描述
(可选)设置电源状态协调方式。
PSCI 1.0
引入。 - 参数
0
: 平台协调方式1
: OS协调方式0x8400 000F
uint32 Function ID
:mode
- 返回值
int32
SUCCESS
NOT_SUPPORTED
INVALID_PARAMETERS
DENIED
5.20 PSCI_STAT_RESIDENCY
- 功能描述
(可选)返回冷启动之后,在某种状态下的度过时间。
PSCI 1.0
引入。 - 参数
0x8400 0010
-SMC32
版本0xC400 0010
-SMC64
版本uint32 Function ID
:target_cpu
格式与CPU_ON
调用相同;power_state
指定的电源状态。
- 返回值 可能是32位或64位。返回处于指定电源状态的时间。
5.21 PSCI_STAT_COUNT
- 功能描述
(可选)返回冷启动之后,进入某种状态下的次数。
PSCI 1.0
引入。 - 参数
0x8400 0010
-SMC32
版本0xC400 0010
-SMC64
版本uint32 Function ID
:target_cpu
格式与CPU_ON
调用相同;power_state
指定的电源状态。
- 返回值 可能是32位或64位。进入某种状态下的次数。
5.22 错误码
定义 | 值 |
---|---|
SUCCESS | 0 |
NOT_SUPPORTED | -1 |
INVALID_PARAMETERS | -2 |
DENIED | -3 |
ALREADY_ON | -4 |
ON_PENDING | -5 |
INTERNAL_FAILURE | -6 |
NOT_PRESENT | -7 |
DISABLED | -8 |
INVALID_ADDRESS | -9 |
6 其它实现细节
6.1 PSCI调用流程
6.1.1 CPU_SUSPEND、CPU_DEFAULT_SUSPEND和SYSTEM_SUSPEND调用流程
6.1.2 CPU_OFF调用流程
6.1.3 CPU_ON调用流程
相关文章
- java springboot中调用第三方接口「建议收藏」
- restful api接口规范和服务调用的区别_rest接口规范
- 【openssl】从openssl的常用接口调用浅谈【内存泄漏】的风险和规避
- Python 调用Zoomeye搜索接口
- MetaDaily|俄罗斯最大社交媒体推出NFT服务,脑机接口平台技术企业“博睿康”获数亿元C轮融资
- 开发私有chatGPT(四)openai接口文档-快速开始
- 很火的AI绘图web网页源码附带接口教程
- chatGPT辅助测试登录接口
- PHP腾讯地图地图接口调用提示{ “status”: 110, “message”: “请求来源未被授权,此次请求无来源信息” }详解编程语言
- Hibernate Criteria接口 add方法:设置查询条件
- Linux网络接口关闭指南(linux关闭接口)
- Redis访问接口勇夺秒杀档次(redis 访问 接口)
- java自定义注解接口实现方案
- 深入分析php中接口与抽象类的区别