特权层二进制接口规范标准(0.3版)
特权层二进制接口规范标准 0.3版 作者:RISC-V Unix工作组 2021年6月8日 译者:洛佳 |
1.0.0版 |
简介
[编辑]本标准描述了RISC-V架构的特权层二进制接口。它使用平台或虚拟化软件特有的功能,定义一组抽象接口,以便于在不同的RISC-V实现下移植特权层软件和虚拟化的特权层软件。特权层接口的设计遵守了RISC-V的设计哲学,即它必须实现较少的核心功能,以模块化方式提供更多可选的功能扩展。
特权层接口的模块化扩展是可选的,但它必须被完整地实现。如果“探测特权层接口模块函数”认为一个扩展是可用的,它的所有功能必须与“获取特权层接口规范版本号函数”得到的版本相符合。
提供特权层接口的软件被称作特权层接口的实现。特权层接口的实现可以是机器模式下运行的固件,也可以是宿主特权态下运行的虚拟化监视软件。
特权层接口标准并未规定发现硬件的方法。特权层软件必须辅助使用其它的规范标准,来完成特定平台下发现硬件的流程。
二进制编码
[编辑]所有的特权层接口函数具有相同的二进制编码,它反映了不同的特权层接口扩展。特权层接口的调用约定描述如下:
- 使用ECALL指令来转移指令流的控制权,而不是使用CALL指令;
- a7寄存器用于保存扩展编号;
- a6寄存器用于保存函数编号;
- 除了a0和a1外,所有其余的寄存器上下文都应当被特权层接口实现保护;
- 特权层接口函数的返回值有两个,分别保存到a0和a1寄存器。其中,a0寄存器返回错误编号。返回值的描述和下表是相同的。
寄存器 | 名称 | 类型 |
---|---|---|
a0 | 错误编号 | usize |
a1 | 函数的返回值 | usize |
所有的扩展编号和函数编号都是32位的整数。传入扩展编号和函数编号,应当符合上述的调用约定。
返回值中的错误编号定义如下表。
错误类型 | a0值 |
---|---|
调用成功 | 0 |
发生了错误 | -1 |
不支持的功能 | -2 |
非法参数 | -3 |
禁止此操作 | -4 |
非法地址 | -5 |
资源已生效 | -6 |
已经启动 | -7 |
已经停止 | -8 |
当扩展编号和函数编号不受当前环境支持时,特权层接口应当返回不支持的功能作为错误编号。
所有的接口函数应尽量选用特权层环境下寄存器宽度的类型作为传入的数据类型,这样在不同的RISC-V基础扩展下都容易使用特权层接口。除此之外,如果一些参数的数据类型可能固定为32位宽,那么特权层软件应当只使用数据的低32位。
为便于选择处理器核,特权层二进制接口定义统一的位集数据结构如下。所有需要选择处理器核的特权层接口函数必须使用标准的位集数据结构。
标准的位集数据结构由遮罩位集和起始编号两个参数组成。
参数名称 | 参数类型 |
---|---|
遮罩位集 | usize |
起始编号 | usize |
当只调用一次特权层接口函数时,最大支持处理器核的数量与特权层下整数寄存器的位宽相同。如果更高权限的层级需要传递多于一个位宽的处理器核,它应当多次调用特权层接口函数。除此之外,如果起始编号的值是2^XLEN-1,那么遮罩位集将被忽略,此时位集数据结构表示平台上所有可用的核都应被函数考虑在内。
对所有使用位集数据结构的函数来说,如果起始编号无效或遮罩位集给定的处理器核编号无效,即它表示选中的核至少有一个不能在特权层下使用时,应当返回非法参数错误。
错误类型 | 发生条件 |
---|---|
非法参数 | 至少有一个选中的核不能在当前特权层环境下使用 |
基础扩展
[编辑]扩展编号为0x10。基础扩展被设计得越小越好,它只包含探测标准版本和扩展模块的最小功能。所有的特权层接口必须实现基础扩展;基础扩展的函数不会返回错误。
获取特权层接口规范版本号函数
[编辑]这个函数没有输入参数。
返回当前的特权层规范版本号。这个函数不能返回错误。返回值中,低24位代表特权层规范的子版本号,而后较高的7位表示母版本号。返回值的第31位必须为0,它是为未来的扩展而保留的。
规范版本号的返回值 | |||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
保留 | 母版本号 | 子版本号 |
获取特权层接口实现编号函数
[编辑]这个函数没有输入参数。
返回当前的特权层接口实现编号,每个特权层实现的返回都将不同。它是为了探测特权层实现的类型而设计的,允许软件根据不同的特权层实现,具备不同的行为。
获取特权层接口实现版本号函数
[编辑]这个函数没有输入参数。
返回当前的特权层实现版本。特权层实现版本的具体意义,由特权层实现来规定。
探测特权层接口模块函数
[编辑]这个函数输入一个参数,即待探测的扩展模块编号。
顺序 | 名称 | 类型 |
---|---|---|
0 | 扩展模块编号 | usize |
如果待测的扩展模块不可用,返回0。当扩展的模块可用,返回1,除非特权层实现规定的另外的非零返回值。
获取机器制造商编号函数
[编辑]这个函数没有输入参数。
从mvendorid寄存器中返回机器制造商的编号;0也是一个合法的返回值。
获取机器微架构编号函数
[编辑]这个函数没有输入参数。
从marchid寄存器中返回机器微架构的编号;0也是一个合法的返回值。
获取机器实现编号函数
[编辑]这个函数没有输入参数。
从mimpid寄存器中返回机器微架构的编号;0也是一个合法的返回值。
基础扩展函数列表
[编辑]函数名称 | 规范版本 | 函数编号 | 扩展编号 |
---|---|---|---|
获取特权层接口规范版本号函数 | 0.2 | 0 | 0x10 |
获取特权层接口实现编号函数 | 0.2 | 1 | 0x10 |
获取特权层接口实现版本号函数 | 0.2 | 2 | 0x10 |
探测特权层接口模块函数 | 0.2 | 3 | 0x10 |
获取机器制造商编号函数 | 0.2 | 4 | 0x10 |
获取机器微架构编号函数 | 0.2 | 5 | 0x10 |
获取机器实现编号函数 | 0.2 | 6 | 0x10 |
基础扩展实现编号列表
[编辑]实现编号 | 名称 |
---|---|
0 | Berkley Bootloader |
1 | OpenSBI |
2 | Xvisor |
3 | KVM |
4 | RustSBI |
5 | Diosix |
兼容性扩展
[编辑]扩展编号为0x00至0x0F。兼容性扩展忽略了函数编号,它直接占用了多个扩展编号。每个扩展编号必须单独被使用探测函数来探测可用性。
兼容性扩展已经被弃用,它的功能被其它的扩展模块替代。然而,控制台输出和控制台输入暂时没有替代的模块。
时钟扩展
[编辑]替代了兼容性扩展0x00,时钟扩展的编号为0x54494D45。
时钟设置函数
[编辑]这个函数输入一个参数,即下一个时钟中断发生的时刻。无论特权层的寄存器宽度如何,时刻值永远是位宽为64的整数。
顺序 | 名称 | 类型 |
---|---|---|
0 | 时刻值 | u64 |
使用函数后,下一个时钟中断将在参数指定的时刻值发生。区分于时间的变化量即间隔,时刻值是时间数值的状态。这个函数还将清除时钟中断的等待位。
如果特权层希望清除等待位,而不设置下一个时刻值,特权层可以选择将时钟设置到很远的将来——比如2^64-1,或者可以清除sie寄存器的STIE位来关闭时钟中断。
这个函数一定成功,不会返回错误编码。
时钟扩展函数列表
[编辑]函数名称 | 规范版本 | 函数编号 | 扩展编号 |
---|---|---|---|
时钟设置函数 | 0.2 | 0 | 0x54494D45 |
核间中断扩展
[编辑]这个扩展的编号是0x735049,它替代了兼容性扩展0x04。另一个与核间中断有关的扩展0x03已经被弃用。所有的函数使用的位集参数都由二进制编码章节规定。
核间中断发送函数
[编辑]这个函数输入两个参数,它们共同选择需要接收核间中断的处理器核。
顺序 | 名称 | 类型 |
---|---|---|
0 | 处理器核遮罩位集 | usize |
1 | 处理器核位集起始编号 | usize |
函数将为所有选中的处理器核发送核间中断。对所有的接收核来说,核间中断将被表示为特权层的软件中断。
这个函数将只会成功,无论位集参数选中的核是否存在。
时钟扩展函数列表
[编辑]函数名称 | 规范版本 | 函数编号 | 扩展编号 |
---|---|---|---|
核间中断发送函数 | 0.2 | 0 | 0x735049 |
远程栅栏扩展
[编辑]扩展编号为0x52464E43。这个扩展定义了所有的远程栅栏,替代了兼容性扩展0x05到0x07。所有函数使用的遮罩位集和起始编号参数,都由二进制编码章节的标准位集数据结构规定。
所有刷新页表缓存的函数都使用了起始地址和地址长度参数。通常,它们定义一个待刷新的虚拟地址区间。但当以下情况发生时:
- 起始地址和地址长度都是0,或者
- 地址长度为2^XLEN-1
则表示远程栅栏函数将刷新完整的页表缓存。下文将不再赘述遮罩位集、起始编号、起始地址和地址长度的参数意义。
远程指令栅栏函数
[编辑]这个函数输入两个参数,决定要选取即将要发送栅栏操作的处理器核。
顺序 | 名称 | 类型 |
---|---|---|
0 | 遮罩位集 | usize |
1 | 起始编号 | usize |
函数将请求对应的处理器核执行一条FENCE.I指令。
本函数不会失败。
错误类型 | 发生条件 |
---|---|
调用成功 | 成功发送刷新请求到选中的所有核 |
远程全空间特权内存栅栏函数
[编辑]这个函数输入四个参数,决定要选取即将要发送栅栏操作的处理器核,以及需要刷新的虚拟地址区间。
顺序 | 名称 | 类型 |
---|---|---|
0 | 遮罩位集 | usize |
1 | 起始编号 | usize |
2 | 起始地址 | usize |
3 | 地址长度 | usize |
函数将请求对应的处理器核执行一条SFENCE.VMA指令,这条指令包含起始地址之后地址长度区间内的虚拟地址段。
如果发生错误,将会返回以下的错误代码。
错误类型 | 发生条件 |
---|---|
调用成功 | 成功发送刷新请求到选中的所有核 |
非法参数 | 起始地址和地址长度的参数数值不合法 |
远程特定空间特权栅栏函数
[编辑]这个函数输入五个参数,决定要选取即将要发送栅栏操作的处理器核,以及需要刷新的虚拟地址区间,以及需要刷新的地址空间的编号。
顺序 | 名称 | 类型 |
---|---|---|
0 | 遮罩位集 | usize |
1 | 起始编号 | usize |
2 | 起始地址 | usize |
3 | 地址长度 | usize |
4 | 地址空间编号 | usize |
函数将请求对应的处理器核执行一条带地址空间编号的SFENCE.VMA指令,这条指令包含起始地址之后地址长度区间内的虚拟地址段。
如果发生错误,将会返回以下的错误代码。
错误类型 | 发生条件 |
---|---|
调用成功 | 成功发送刷新请求到选中的所有核 |
非法参数 | 起始地址和地址长度的参数数值不合法 |
远程特定虚拟机虚拟化内存栅栏函数
[编辑]这个函数输入四个参数,决定要选取即将要发送栅栏操作的处理器核,以及需要刷新的虚拟地址区间,以及需要刷新的虚拟机的编号。
顺序 | 名称 | 类型 |
---|---|---|
0 | 遮罩位集 | usize |
1 | 起始编号 | usize |
2 | 起始地址 | usize |
3 | 地址长度 | usize |
4 | 虚拟机编号 | usize |
函数将请求对应的处理器核执行一条带虚拟机编号的HFENCE.GVMA指令,这条指令包含起始地址之后地址长度区间内的虚拟地址段。
只有特权层环境支持虚拟化扩展时,这条函数有效,否则它将返回不支持的功能错误。如果发生其它错误,将会返回以下的错误代码。
错误类型 | 发生条件 |
---|---|
调用成功 | 成功发送刷新请求到选中的所有核 |
不支持的功能 | 至少有一个选中的处理器核的特权层环境不支持虚拟化扩展 |
非法参数 | 起始地址和地址长度的参数数值不合法 |
远程虚拟化全局阶段内存栅栏函数
[编辑]这个函数输入四个参数,决定要选取即将要发送栅栏操作的处理器核,以及需要刷新的虚拟地址区间。
顺序 | 名称 | 类型 |
---|---|---|
0 | 遮罩位集 | usize |
1 | 起始编号 | usize |
2 | 起始地址 | usize |
3 | 地址长度 | usize |
函数将请求对应的处理器核执行一条HFENCE.GVMA指令,这条指令包含起始地址之后地址长度区间内的虚拟地址段。
只有特权层环境支持虚拟化扩展时,这条函数有效,否则它将返回不支持的功能错误。如果发生其它错误,将会返回以下的错误代码。
错误类型 | 发生条件 |
---|---|
调用成功 | 成功发送刷新请求到选中的所有核 |
不支持的功能 | 至少有一个选中的处理器核的特权层环境不支持虚拟化扩展 |
非法参数 | 起始地址和地址长度的参数数值不合法 |
远程虚拟机内特定空间栅栏函数
[编辑]这个函数输入五个参数,决定要选取即将要发送栅栏操作的处理器核,以及需要刷新的虚拟地址区间,以及需要刷新的地址空间的编号。
顺序 | 名称 | 类型 |
---|---|---|
0 | 遮罩位集 | usize |
1 | 起始编号 | usize |
2 | 起始地址 | usize |
3 | 地址长度 | usize |
4 | 地址空间编号 | usize |
函数将请求对应的处理器核执行一条带地址空间编号的HFENCE.VVMA指令,这条指令包含起始地址之后地址长度区间内的虚拟地址段。
只有特权层环境支持虚拟化扩展时,这条函数有效,否则它将返回不支持的功能错误。如果发生其它错误,将会返回以下的错误代码。
错误类型 | 发生条件 |
---|---|
调用成功 | 成功发送刷新请求到选中的所有核 |
不支持的功能 | 至少有一个选中的处理器核的特权层环境不支持虚拟化扩展 |
非法参数 | 起始地址和地址长度的参数数值不合法 |
远程虚拟机内全空间栅栏函数
[编辑]这个函数输入四个参数,决定要选取即将要发送栅栏操作的处理器核,以及需要刷新的虚拟地址区间。
顺序 | 名称 | 类型 |
---|---|---|
0 | 遮罩位集 | usize |
1 | 起始编号 | usize |
2 | 起始地址 | usize |
3 | 地址长度 | usize |
函数将请求对应的处理器核执行一条HFENCE.VVMA指令,这条指令包含起始地址之后地址长度区间内的虚拟地址段。
只有特权层环境支持虚拟化扩展时,这条函数有效,否则它将返回不支持的功能错误。如果发生其它错误,将会返回以下的错误代码。
错误类型 | 发生条件 |
---|---|
调用成功 | 成功发送刷新请求到选中的所有核 |
不支持的功能 | 至少有一个选中的处理器核的特权层环境不支持虚拟化扩展 |
非法参数 | 起始地址和地址长度的参数数值不合法 |
远程栅栏扩展函数列表
[编辑]函数名称 | 规范版本 | 函数编号 | 扩展编号 |
---|---|---|---|
远程指令栅栏函数 | 0.2 | 0 | 0x52464E43 |
远程全空间特权内存栅栏函数 | 0.2 | 1 | 0x52464E43 |
远程特定空间特权栅栏函数 | 0.2 | 2 | 0x52464E43 |
远程特定虚拟机虚拟化内存栅栏函数 | 0.2 | 3 | 0x52464E43 |
远程虚拟化全局阶段内存栅栏函数 | 0.2 | 4 | 0x52464E43 |
远程虚拟机内特定空间栅栏函数 | 0.2 | 5 | 0x52464E43 |
远程虚拟机内全空间栅栏函数 | 0.2 | 6 | 0x52464E43 |
核状态管理扩展
[编辑]编号为0x48534D。核状态管理扩展引入了核的一系列状态核一组函数,允许特权层软件提交请求来更改核的状态。
每个处理器核在特权层接口下能显示不同的核状态。所有可能的核状态在下表中定义。
编号 | 状态名称 | 描述 |
---|---|---|
0 | 已启动 | 处理器核已通电,正在正常运行 |
1 | 已停止 | 处理器核不在特权态或其它更低权限的状态运行。如果特权层环境支持断电操作,它可能已被特权层环境断电 |
2 | 正在启动 | 其它的核已请求该核从已停止状态启动,它仍在执行若干操作,以进入已启动状态 |
3 | 正在停止 | 其它的核已请求该核从已启动状态停止,它仍在执行若干操作,以进入已停止状态 |
4 | 已暂停 | 处理器核正在平台定义的暂停或低电量状态 |
5 | 正在暂停 | 其它的核已请求该核从已停止状态暂停,它仍在执行若干操作,以进入已暂停状态 |
6 | 正在恢复 | 其它的核已请求该核从已暂停状态恢复,它仍在执行若干操作,以进入已启动状态 |
在任何的时间点上,处理器核应当处于以上状态中的任意一种。
在一些情况下,一个平台可能将多个核捆绑在一种层级性的拓扑组织,它们可能共同组成簇团、节点或其它结构的组合,组合和高层级拓扑间不共享电源设置,或者定义了专门的低功耗状态。这些平台定义的低功耗状态可以被归纳为单独处理器核的暂停状态。特权层接口的实现可能使用不同的方法,来充分利用这些暂停状态。
- 平台协调法。当一个处理器核在特权层开始空闲等待时,特权层上运行的电源管理软件将通过特权层环境,向层级组织的更高级请求最深的暂停状态。特权层接口的实现可能选择不比给定暂停状态更深,而且唤醒延迟不比暂停状态更高的平台暂停状态;
- 系统发起法。这种方法的特权层在所有某一层级组织的核全部暂停后,系统的电源管理软件才将向更高层级的组织请求最深的暂停状态。此时,特权层接口的实现从不为更高层级的组织选择暂停状态,而总是倾向于选择最近一个为更高层级的组织选择的挂起状态。
处理器核启动函数
[编辑]处理器核停止函数
[编辑]获取处理器核当前状态函数
[编辑]处理器核暂停函数
[编辑]核状态管理扩展函数列表
[编辑]函数名称 | 规范版本 | 函数编号 | 扩展编号 |
---|---|---|---|
处理器核启动函数 | 0.2 | 0 | 0x48534D |
处理器核停止函数 | 0.2 | 1 | 0x48534D |
获取处理器核当前状态函数 | 0.2 | 2 | 0x48534D |
处理器核暂停函数 | 0.3 | 3 | 0x48534D |
系统复位扩展
[编辑]编号为0x53525354。系统复位扩展允许特权层软件重启或关闭当前的系统;这里,“系统”可能是一台机器或者一个虚拟化软件。
系统复位函数
[编辑]这个函数输入两个参数,分别代表系统复位的类型和原因。
顺序 | 名称 | 类型 |
---|---|---|
0 | 复位类型 | u32 |
1 | 复位原因 | u32 |
函数将根据类型和原因来复位系统。这是一个阻塞的函数,如果复位操作成功,函数将不会返回。
复位类型是必须给定的,而复位原因是可选的。它们的定义如下表。
复位类型值 | 说明 |
---|---|
0x00000000 | 关机 |
0x00000001 | 冷重启 |
0x00000002 | 热重启 |
0x00000003 - 0xEFFFFFFF | 保留作未来使用 |
0xF0000000 - 0xFFFFFFFF | 供应商或平台定义的复位类型 |
> 0xFFFFFFFF | 保留作未来使用 |
复位原因值 | 说明 |
---|---|
0x00000000 | 未提供复位原因 |
0x00000001 | 系统错误 |
0x00000002 - 0xDFFFFFFF | 保留作未来使用 |
0xE0000000 - 0xEFFFFFFF | 特权层接口实现定义的复位原因 |
0xF0000000 - 0xFFFFFFFF | 供应商或平台定义的复位原因 |
> 0xFFFFFFFF | 保留作未来使用 |
当特权模式的软件在硬件环境上运行时,特权层接口的实现称作机器模式的固件。在这种情况下,关机操作相当于关闭物理系统的供电电源,而冷重启相当于在物理供电上重新开始上电过程。除此之外,热重启相当于处理器和部分外设系统的电源重新启动,而整个系统的供电并未重新启动。特别地,拥有板载管理芯片的服务器主板上,热重启不会重启此管理芯片,而冷重启会重新启动管理芯片。
当特权软件在虚拟机中运行,特权层接口的实现通常是一个虚拟化软件的管理模块。关机、冷重启、热重启在功能上与物理系统类似,但可能不会导致物理供电的变化。
当特权层软件无法识别复位类型、复位原因参数时,系统复位函数将会返回错误。出于其它的原因,系统复位函数仍可能返回错误。所有失败的错误编号列于下表。
错误类型 | 发生条件 |
---|---|
非法参数 | 复位类型或者复位参数不合法 |
不支持的功能 | 复位类型是合法的,但当前的特权层接口实现不支持此复位类型 |
发生了错误 | 发生了其它的错误 |
注意,系统复位函数成功时,函数将不会返回,所以错误编号列表中不包含成功的选项。
系统复位扩展函数列表
[编辑]函数名称 | 规范版本 | 函数编号 | 扩展编号 |
---|---|---|---|
系统复位函数 | 0.3 | 0 | 0x53525354 |
性能监视器扩展
[编辑]性能监视器扩展的编号为0x504D55。
RISC-V硬件定义了若干性能计数器,如mcycle、minstret和mhpmcounterX寄存器,这些寄存器能在特权态下以cycle、instret和hpmcounterX的形式只读地访问。特权层接口的性能监视器扩展允许特权态在机器态的协助下,配置和使用RISC-V硬件上的性能计数器。这些硬件性能计数器只能在机器态下启动、停止或者动态配置,这需要操作机器态寄存器mcountinhibit和mhpmeventX来完成这一点。因此,如果硬件上未实现mcountinhibit寄存器,机器态的特权层接口实现会禁用性能监视器扩展。
无论是否有特权层接口,一般的RISC-V平台通常提供一些硬件的事件监视器,它们以一个64位宽的数字值代表硬件计数器的状态。而一个特权层接口实现也提供固件的性能监视器,这些监视器能监视固件的事件状态,比如非对齐读写的次数,远程栅栏的次数,跨核中断次数等等。
特权层接口的性能计数器会提供:
- 特权态发现和配置每个核硬件、固件计数器的接口
- 一个与perf工具兼容的性能计数器接口
- 微架构完整的事件编码
为了定义特权层接口的性能监视器模块,我们定义计数器编号、事件编号和事件数据三个变量。硬件或固件定义了计数器和事件,它们的编号分别称作计数器编号和事件编号。除此之外,事件的额外信息称作事件数据。无论RISC-V架构的寄存器宽度如何,事件数据永远是位宽为64的整数。
事件编号是一个位宽为20的非零整数,由事件类型和事件代码组成。它的定义如下表。
事件编号的二进制位 | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
事件类型 | 事件代码 |
通用硬件事件
[编辑]事件编号定义了事件类型字段和事件代码字段。当事件类型字段的值为0x0时,表示这是一个通用硬件事件。以事件代码区分不同的通用硬件事件,下表给出了通用硬件事件和它们名称、描述的关系。
事件代码0的通用硬件事件不被定义,因为事件编号被定义为非零的整数。
事件名称 | 代码 | 触发前提 |
---|---|---|
硬件处理器运行周期事件 | 1 | 非暂停状态的处理器核经历的每个时钟周期 |
硬件指令处理事件 | 2 | 每次完整地处理一条指令 |
硬件缓存命中事件 | 3 | 每次缓存命中 |
硬件缓存未命中事件 | 4 | 每次缓存未命中 |
硬件分支指令事件 | 5 | 每次执行分支指令 |
硬件分支预测失败事件 | 6 | 每次分支预测不成功 |
硬件总线时钟周期事件 | 7 | 总线时钟经历的每个周期 |
硬件前端失速周期事件 | 8 | 微架构前端空转经历的每个时钟周期 |
硬件后端失速周期事件 | 9 | 微架构后端空转经历的每个时钟周期 |
硬件处理器参考周期事件 | 10 | 处理器核参考时钟经历的每个周期 |
通用硬件事件暂时不包含事件数据,非零的事件数据值保留做未来使用。
硬件处理器的运行周期和参考周期是不同的。处理器处于等待状态时,运行周期事件不会被触发,而参考周期事件会被触发。处理器处于非等待状态时,前后两个事件都会被触发。
硬件处理器的运行周期永远和cycle寄存器的处理器时钟是相同的。而硬件处理器的参考周期是对某一固定处理器频率的计数,无论是否进入等待状态,都会记入时钟周期;这个固定频率可能会和time寄存器的值一致。
使用WFI等待,或使用特权层接口的暂停函数后,一些RISC-V平台会可能会暂停处理器时钟,另一些RISC-V平台可能不会。处理器时钟暂停时,硬件处理器的运行周期和时钟周期事件都不会被触发。
硬件总线时钟周期事件定义为对某一固定总线频率的计数。这个固定频率可能和time寄存器的值一致,也可能和处理器核与总线边界的某一计数器一致。
硬件缓存事件
[编辑]当事件类型为0x1时,表示发生了硬件缓存事件。所有的硬件缓存事件都通过事件编号来区分。硬件缓存事件的事件代码拥有缓存区域类型、操作类型和结果类型三个字段,它们的定义如下。
事件类型 | 事件代码 | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
都是0x1 | 缓存区域 | 操作 | 结果 |
对发生的所有硬件缓存事件,它们的缓存区域编号、操作编号和结果编号定义如下。
缓存区域名称 | 编号 | 描述 |
---|---|---|
一级数据缓存 | 0 | 事件与一级数据缓存有关 |
一级指令缓存 | 1 | 事件与一级指令缓存有关 |
最高级缓存 | 2 | 事件与等级最高的缓存区域有关 |
数据快表缓存 | 3 | 事件与数据快表有关 |
指令快表缓存 | 4 | 事件与指令快表有关 |
分支预测单元缓存 | 5 | 事件与分支预测单元有关 |
节点缓存 | 6 | 事件与NUMA节点缓存有关 |
缓存操作 | 编号 | 描述 |
---|---|---|
读操作 | 0 | 读缓存行操作 |
写操作 | 1 | 写缓存行操作 |
预取操作 | 2 | 缓存行数据的预取操作 |
结果 | 编号 | 描述 |
---|---|---|
成功 | 0 | 成功访问了缓存 |
失败 | 1 | 本次事件属于缓存未命中 |
硬件缓存事件暂未使用事件数据,非零的事件数据值保留做未来使用。
原始硬件事件
[编辑]当事件类型为0x2时,发生的是原始硬件事件。原始硬件事件的事件编号恒为零。
事件数据是一个64位的整数值。如果RISC-V环境的mhpmeventX位宽为32,事件数据包含将被填写到mhpmeventX寄存器的32位值。
如果RISC-V环境的mhpmeventX位宽为64,事件数据的值具有两个部分。低48位包含将被填写到mhpmeventX中的低48位值。高16位将被填写到mhpmeventX的值将由特权层二进制接口的实现决定。
一些RISC-V硬件实现会定义一些在事件发生时会被写入mhpmeventX寄存器的值。发生通用硬件时间和硬件缓存事件时,为了简单期间,RISC-V的硬件实现可以将零扩展的事件编号作为预期的值。
固件事件
[编辑]事件类型为0xf时,表示发生了固件事件。每个固件事件都通过不同的事件代码来区分。固件事件的事件代码定义如下。
事件名称 | 代码 | 发生条件 |
---|---|---|
固件非对齐读取事件 | 0 | 固件处理的每次次非对称读取陷入 |
固件非对齐写入事件 | 1 | 固件处理的每次非对称写入陷入 |
固件处理无权读取事件 | 2 | 固件处理的每次权限不足读取陷入 |
固件处理无权写入事件 | 3 | 固件处理的每次权限不足写入陷入 |
固件处理非法指令事件 | 4 | 固件处理的每次非法指令陷入 |
固件设置时钟事件 | 5 | 每次由固件设置时钟当前值 |
固件跨核中断发送事件 | 6 | 每次固件发送跨核中断到另一个核 |
固件跨核中断接收事件 | 7 | 每次固件从另一个核接收到跨核中断 |
固件远程指令栅栏发送事件 | 8 | 每次固件请求另一核执行FENCE.I指令 |
固件远程指令栅栏接收事件 | 9 | 每次固件接收到FENCI.I执行请求 |
固件远程全空间特权内存栅栏发送事件 | 10 | 固件请求另一核执行SFENCE.VMA指令 |
固件远程全空间特权内存栅栏接收事件 | 11 | 固件接收到SFENCE.VMA执行请求 |
固件远程特定空间特权栅栏发送事件 | 12 | 固件发送具有地址空间编号的SFENCE.VMA指令 |
固件远程特定空间特权栅栏接收事件 | 13 | 固件接收具有地址空间编号的SFENCE.VMA执行请求 |
固件远程虚拟化全局阶段内存栅栏发送事件 | 14 | 固件请求另一核执行HFENCE.GVMA指令 |
固件远程虚拟化全局阶段内存栅栏接收事件 | 15 | 固件接收到的HFENCE.GVMA执行请求 |
固件远程特定虚拟机虚拟化内存栅栏发送事件 | 16 | 固件发送具有虚拟机编号的HFENCE.GVMA指令 |
固件远程特定虚拟机虚拟化内存栅栏接收事件 | 17 | 固件接收具有虚拟机编号的HFENCE.GVMA执行请求 |
固件远程虚拟机内全空间栅栏发送事件 | 18 | 固件请求另一核执行HFENCE.VVMA指令 |
固件远程虚拟机内全空间栅栏接收事件 | 19 | 固件接收到的HFENCE.VVMA执行请求 |
固件远程虚拟机内特定空间栅栏发送事件 | 20 | 固件发送具有地址空间编号的HFENCE.VVMA指令 |
固件远程虚拟机内特定空间栅栏接收事件 | 21 | 固件接收具有地址空间编号的HFENCE.VVMA执行请求 |
固件事件暂未使用事件数据,非零的事件数据保留做未来使用。
获取计数器数量函数
[编辑]这个函数没有输入参数。
函数返回当前特权层环境支持的计数器数量,这将包括硬件计数器和固件计数器。获取计数器数量函数总会执行成功。
获取计数器细节信息函数
[编辑]这个函数输入一个参数,即待查询细节信息的计数器编号。
顺序 | 名称 | 类型 |
---|---|---|
0 | 计数器编号 | usize |
函数返回对应计数器的细节信息。无论是固件或硬件计数器,都将返回计数器的类型。如果是硬件计数器,还将额外返回计数器的位宽,以及计数器对应控制与状态寄存器的编号。
返回的细节定义包含在返回值的字段中,字段的定义如下。
计数器的细节信息 | |||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
XLEN-1 | XLEN-2 | ... | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
类型 | 保留作未来使用 | 计数值的位宽减去1 | 控制状态寄存器编号 |
返回值的类型为0或1;0代表硬件计数器,1代表固件计数器。当类型为1时,控制状态寄存器编号和位宽字段应当被忽略。
位宽字段编码为计数值的位宽减去1。当此字段为0,说明位宽为1;以此类推,字段为最大值63时,代表位宽为64。
当这个函数失败时,它应当返回错误编号。它可能返回的所有错误编号如下。
错误类型 | 发生条件 |
---|---|
调用成功 | 成功返回了计数器的细节信息 |
非法参数 | 不存在编号为传入参数的计数器 |
寻找和配置计数器函数
[编辑]这个函数输入的参数依次如下表。
顺序 | 名称 | 类型 |
---|---|---|
0 | 计数器集合起始编号 | usize |
1 | 计数器集合编号遮罩 | usize |
2 | 配置位集* | usize |
3 | 事件编号 | usize |
4 | 事件数据 | u64 |
此函数将寻找和配置能监视给定事件的一个计数器。起始编号和编号遮罩参数将用于选取计数器,选中的计数器必须处于停止或者禁用状态。事件编号表示计数器监视的事件,而事件数据表示额外的事件配置信息。
计数器的额外配置信息和过滤提示将由配置位集提供,它的每个位表示一种函数操作。每个位的定义如下表。
名称 | 位编号 | 描述 |
---|---|---|
跳过匹配 | 0 | 函数将跳过匹配过程 |
清除计数器值 | 1 | 函数将清除选中计数器的值 |
自动启动 | 2 | 配置完毕后,选中的计数器将自动启动 |
虚拟用户态不计数 | 3 | 进入VU态后停止计数 |
虚拟特权态不计数 | 4 | 进入VS态后停止计数 |
用户态不计数 | 5 | 进入U态后停止计数 |
特权态不计数 | 6 | 进入S态后停止计数 |
机器态不计数 | 7 | 进入M态后停止计数 |
保留 | 8..XLEN | 留作未来使用 |
当跳过匹配被置位,特权层接口实现将从起始编号和编号遮罩决定的计数器集中。无条件地选择第一个寄存器。
无论自动启动计数器是否被置位,在执行函数的过程中,计数器的值都不会被改变。
配置位集第3至第7位是一种提示位,它提示特权层实现应当在不同的态过滤事件。当且仅当特权层实现认为这会带来安全问题,或硬件不支持按特权层的事件过滤时,特权层实现可以忽略这些提示位。
当函数成功执行时,说明有一个计数器成功被配置,函数将返回它的编号。函数失败时可能返回的所有错误编号如下。
错误类型 | 发生条件 |
---|---|
调用成功 | 成功找到并配置对应的计数器 |
非法参数 | 计数器集包含非法的计数器 |
不支持的功能 | 选中的计数器不能监视给定的事件 |
计数器启动函数
[编辑]这个函数输入的参数依次如下表。
顺序 | 名称 | 类型 |
---|---|---|
0 | 计数器集合起始编号 | usize |
1 | 计数器集合编号遮罩 | usize |
2 | 启动配置位集* | usize |
3 | 初始值 | u64 |
此函数将在调用者的硬件线程中,启动或者启用计数器集中的所有计数器。计数器集将由起始编号和编号遮罩给定。初始值参数将决定选中计数器在启动前设定的初始值。
函数的功能受到启动配置位集的影响,它的定义如下。
名称 | 位编号 | 描述 |
---|---|---|
设置初始值 | 0 | 函数将在启动前先设置初始值 |
保留 | 1..XLEN | 所有非零值均留作未来使用 |
当设置初始值位未被设置时,计数器的值不会被修改。启动后,计数器将从当前的值继续计数。
此函数不会提供返回值,可能返回的所有错误编号如下。
错误类型 | 发生条件 |
---|---|
调用成功 | 成功启动对应的计数器 |
非法参数 | 至少有一个选中的计数器无效 |
已经启动 | 至少有一个选中的计数器已经启动 |
计数器停止函数
[编辑]这个函数输入的参数依次如下表。
顺序 | 名称 | 类型 |
---|---|---|
0 | 计数器集合起始编号 | usize |
1 | 计数器集合编号遮罩 | usize |
2 | 停止配置位集* | usize |
此函数将在调用者的硬件线程中,停止或者禁用计数器集中的所有计数器。计数器集将由起始编号和编号遮罩给定。函数的功能受到停止配置位集的影响,它的定义如下。
名称 | 位编号 | 描述 |
---|---|---|
复位初始值 | 0 | 函数将在启动前先复位初始值 |
保留 | 1..XLEN | 所有非零值均留作未来使用 |
此函数不会提供返回值,可能返回的所有错误编号如下。
错误类型 | 发生条件 |
---|---|
调用成功 | 成功停止对应的计数器 |
非法参数 | 至少有一个选中的计数器无效 |
已经停止 | 至少有一个选中的计数器已经停止 |
固件计数器读取函数
[编辑]本函数将提供固件计数器当前的值。函数输入一个参数,即待查询细节信息的固件计数器编号。
顺序 | 名称 | 类型 |
---|---|---|
0 | 计数器编号 | usize |
本函数不能用于查询硬件计数器的当前值。
函数可能返回的所有错误编号如下。
错误类型 | 发生条件 |
---|---|
调用成功 | 成功返回了计数器的细节信息 |
非法参数 | 指定计数器不存在,或指定计数器是硬件计数器 |
性能监视器扩展函数列表
[编辑]函数名称 | 规范版本 | 函数编号 | 扩展编号 |
---|---|---|---|
获取计数器数量函数 | 0.3 | 0 | 0x504D55 |
获取计数器细节信息函数 | 0.3 | 1 | 0x504D55 |
寻找和配置计数器函数 | 0.3 | 2 | 0x504D55 |
计数器启动函数 | 0.3 | 3 | 0x504D55 |
计数器停止函数 | 0.3 | 4 | 0x504D55 |
固件计数器读取函数 | 0.3 | 5 | 0x504D55 |
实验性扩展空间
[编辑]占用了扩展编号0x08000000至0x08FFFFFF,目前没有定义。
供应商定义的扩展空间
[编辑]占用了扩展编号0x09000000至0x09FFFFFF;其中,低位与mvendorid的值相符。
固件定义的扩展空间
[编辑]占用了扩展编号0x0A000000至0x0AFFFFFF,低位是特权层接口的实现编号。
本译文与其原文有分别的版权许可。译文版权状况仅适用于本版本。
原文 | |
---|---|
译文 | Public domainPublic domainfalsefalse |