Hypercall 操作码 (hcalls)

概述

64 位 Power Book3S 平台上的虚拟化基于 PAPR 规范 [1],该规范描述了客户机操作系统的运行时环境,以及它应如何就特权操作与 hypervisor 进行交互。目前有两个兼容 PAPR 的 hypervisor:

  • IBM PowerVM (PHYP):IBM 的专有 hypervisor,支持 AIX、IBM-i 和 Linux 作为受支持的客户机(称为逻辑分区或 LPAR)。它支持完整的 PAPR 规范。

  • Qemu/KVM:支持运行在 PPC64 Linux 主机上的 PPC64 Linux 客户机。不过它仅实现了 PAPR 规范的一个子集,称为 LoPAPR [2]

在 PPC64 架构上,运行在 PAPR hypervisor 之上的客户机内核称为 pSeries 客户机。pSeries 客户机运行在监管模式(supervisor mode,HV=0)下,每当它需要执行 hypervisor 特权操作 [3] 或请求由 hypervisor 管理的其他服务时,必须向 hypervisor 发出 hypercall。

因此,Hypercall (hcall) 本质上是 pseries 客户机请求 hypervisor 代表客户机执行特权操作的请求。客户机带着必要的输入操作数发出该请求。hypervisor 在执行完该特权操作后,会将状态码和输出操作数返回给客户机。

HCALL ABI

pseries 客户机与 PAPR hypervisor 之间 hcall 的 ABI 规范在参考文献 [2] 的 14.5.3 节中有介绍。切换到 Hypervisor 上下文是通过 HVCS 指令完成的,该指令要求在 r3 中设置 hcall 的操作码,并在寄存器 r4-r12 中提供 hcall 的所有输入参数。如果必须通过内存缓冲区传递值,则存储在该缓冲区中的数据应为大端(Big-endian)字节序。

一旦 hypervisor 处理完 ‘HVCS’ 指令并将控制权交还给客户机,hcall 的返回值就可以在 r3 中获取,任何输出值都在寄存器 r4-r12 中返回。与输入参数的情况类似,存储在内存缓冲区中的任何输出值也将是大端字节序。

PowerPC 架构代码提供了名为 plpar_hcall_xxx 的便捷包装函数,这些函数定义在架构特定的头文件 [4] 中,用于从作为 pseries 客户机运行的 Linux 内核中发出 hcall。

寄存器约定

任何 hcall 都应遵循“64-Bit ELF V2 ABI Specification: Power Architecture”[5] 第 2.2.1.1 节中所述的相同寄存器约定。下表总结了这些约定:

寄存器范围

易失性 (Y/N)

用途

r0

Y

可选用法

r1

N

栈指针

r2

N

TOC

r3

Y

hcall 操作码 / 返回值

r4-r10

Y

输入和输出值

r11

Y

可选用法 / 环境变量指针

r12

Y

可选用法 / 全局入口点的函数入口地址

r13

N

线程指针

r14-r31

N

局部变量

LR

Y

链接寄存器

CTR

Y

循环计数器

XER

Y

定点异常寄存器。

CR0-1

Y

条件寄存器字段。

CR2-4

N

条件寄存器字段。

CR5-7

Y

条件寄存器字段。

其他

N

DRC & DRC 索引

DR1                                  Guest
+--+        +------------+         +---------+
|  | <----> |            |         |  User   |
+--+  DRC1  |            |   DRC   |  Space  |
            |    PAPR    |  Index  +---------+
DR2         | Hypervisor |         |         |
+--+        |            | <-----> |  Kernel |
|  | <----> |            |  Hcall  |         |
+--+  DRC2  +------------+         +---------+

PAPR hypervisor 将诸如 PCI 设备、NVDIMM 等共享硬件资源称为可供 LPAR 使用的动态资源 (DR)。当向 LPAR 分配 DR 时,PHYP 会创建一个名为动态资源连接器 (DRC) 的数据结构来管理 LPAR 访问。LPAR 通过一个称为 DRC-Index 的不透明 32 位数字引用 DRC。DRC-index 值通过设备树提供给 LPAR,它作为与 DR 关联的设备树节点中的一个属性存在。

HCALL 返回值

在处理完 hcall 后,hypervisor 会在 r3 中设置返回值,以指示 hcall 的成功或失败。如果失败,错误代码将指示错误原因。这些代码在架构特定的头文件 [4] 中有定义和文档说明。

在某些情况下,hcall 可能会耗费很长时间,需要多次发出才能完全处理完毕。这些 hcall 通常在其参数列表中接受一个不透明的值 continue-token,返回值为 H_CONTINUE 表示 hypervisor 尚未完成对该 hcall 的处理。

要发起此类 hcall,客户机需要在初次调用时将 continue-token == 0,并在随后的每次 hcall 中使用 hypervisor 返回的 continue-token 值,直到 hypervisor 返回非 H_CONTINUE 的返回值。

HCALL 操作码

以下是 PHYP 支持的部分 HCALL 列表。有关对应的操作码值,请查阅架构特定的头文件 [4]

H_SCM_READ_METADATA

输入:drcIndex, offset, buffer-address, numBytesToRead
输出:numBytesRead
返回值:H_Success, H_Parameter, H_P2, H_P3, H_Hardware

给定 NVDIMM 的 DRC 索引,在其指定的偏移量处从与其关联的元数据区域读取 N 个字节,并将其复制到提供的缓冲区中。元数据区域存储配置信息,例如标签信息、坏块等。元数据区域位于 NVDIMM 存储区域的带外,因此提供了单独的访问语义。

H_SCM_WRITE_METADATA

输入:drcIndex, offset, data, numBytesToWrite
输出:None
返回值:H_Success, H_Parameter, H_P2, H_P4, H_Hardware

给定 NVDIMM 的 DRC 索引,将来自所提供缓冲区的 N 个字节写入与其关联的元数据区域中的指定偏移量处。

H_SCM_BIND_MEM

输入:drcIndex, startingScmBlockIndex, numScmBlocksToBind,
targetLogicalMemoryAddress, continue-token
输出:continue-token, targetLogicalMemoryAddress, numScmBlocksToBound
返回值:H_Success, H_Parameter, H_P2, H_P3, H_P4, H_Overlap,
H_Too_Big, H_P5, H_Busy

给定 NVDIMM 的 DRC-Index,将连续的 SCM 块范围 (startingScmBlockIndex, startingScmBlockIndex+numScmBlocksToBind) 映射到客户机物理地址空间内的 targetLogicalMemoryAddress 处。如果 targetLogicalMemoryAddress == 0xFFFFFFFF_FFFFFFFF,则 hypervisor 会为客户机分配一个目标地址。如果客户机对正在绑定的 SCM 块具有活动的 PTE 条目,则该 HCALL 可能会失败。

H_SCM_UNBIND_MEM | 输入:drcIndex, startingScmLogicalMemoryAddress, numScmBlocksToUnbind | 输出:numScmBlocksUnbound | 返回值:H_Success, H_Parameter, H_P2, H_P3, H_In_Use, H_Overlap, | H_Busy, H_LongBusyOrder1mSec, H_LongBusyOrder10mSec

给定 NVDIMM 的 DRC-Index,从客户机物理地址空间中取消映射从 startingScmLogicalMemoryAddress 开始的 numScmBlocksToUnbind 个 SCM 块。如果客户机对正在取消绑定的 SCM 块具有活动的 PTE 条目,则该 HCALL 可能会失败。

H_SCM_QUERY_BLOCK_MEM_BINDING

输入:drcIndex, scmBlockIndex
输出:Guest-Physical-Address
返回值:H_Success, H_Parameter, H_P2, H_NotFound

给定 DRC-Index 和 SCM 块索引,返回 SCM 块所映射到的客户机物理地址。

H_SCM_QUERY_LOGICAL_MEM_BINDING

输入:Guest-Physical-Address
输出:drcIndex, scmBlockIndex
返回值:H_Success, H_Parameter, H_P2, H_NotFound

给定客户机物理地址,返回映射到该地址的 DRC 索引和 SCM 块。

H_SCM_UNBIND_ALL

输入:scmTargetScope, drcIndex
输出:None
返回值:H_Success, H_Parameter, H_P2, H_P3, H_In_Use, H_Busy,
H_LongBusyOrder1mSec, H_LongBusyOrder10mSec

根据目标范围,从 LPAR 内存中取消映射属于所有 NVDIMM 的所有 SCM 块,或者取消映射由其 drcIndex 标识的单个 NVDIMM 所属的所有 SCM 块。

H_SCM_HEALTH

输入:drcIndex
输出:health-bitmap (r4), health-bit-valid-bitmap (r5)
返回值:H_Success, H_Parameter, H_Hardware

给定 DRC 索引,返回关于 PMEM 设备的预测性故障和整体健康状况的信息。健康位图中置位的位指示 PMEM 设备的一个或多个状态(如下表所述),健康位有效位图指示健康位图中的哪些位是有效的。这些位以相反的位顺序报告,例如,值 0xC400000000000000 表示第 0、1 和 5 位有效。

健康位图标志 (Health Bitmap Flags)

定义

00

PMEM 设备无法持久保存内存内容。如果系统断电,将不会保存任何内容。

01

PMEM 设备未能持久保存内存内容。要么是在断电时内容未成功保存,要么是在上电时未正确恢复。

02

PMEM 设备内容已从上一次 IPL 持久保存。上一次启动的数据已成功恢复。

03

PMEM 设备内容未从上一次 IPL 持久保存。没有可从上一次启动恢复的数据。

04

PMEM 设备剩余内存寿命极低

05

由于故障,PMEM 设备将在下一次 IPL 时被隔离

06

由于当前的平台健康状态,PMEM 设备内容无法持久保存。硬件故障可能会阻止数据的保存或恢复。

07

PMEM 设备在某些条件下无法持久保存内存内容

08

PMEM 设备已加密

09

PMEM 设备已成功完成请求的擦除或安全擦除程序。

10:63

保留 / 未使用

H_SCM_PERFORMANCE_STATS

输入:drcIndex, resultBuffer Addr
输出:None
返回值:H_Success, H_Parameter, H_Unsupported, H_Hardware, H_Authority, H_Privilege

给定 DRC 索引,收集 NVDIMM 的性能统计信息并将其复制到 resultBuffer 中。

H_SCM_FLUSH

输入:drcIndex, continue-token
输出:continue-token
返回值:H_SUCCESS, H_Parameter, H_P2, H_BUSY

给定 DRC 索引,将数据刷新到后端 NVDIMM 设备。

当刷新耗时较长且需要多次发出 hcall 才能完全处理完毕时,该 hcall 返回 H_BUSY。输出中的 continue-token 需要传递给 hypervisor 的后续 hcall 的参数列表中,直到 hcall 完全处理完毕,此时 hypervisor 将返回 H_SUCCESS 或其他错误。

H_HTM

输入:flags, target, operation (op), op-param1, op-param2, op-param3
输出:dumphtmbufferdata
返回值:H_Success,H_Busy,H_LongBusyOrder,H_Partial,H_Parameter, H_P2,H_P3,H_P4,H_P5,H_P6,H_State,H_Not_Available,H_Authority

H_HTM 支持硬件跟踪宏 (HTM) 功能及其数据的设置、配置、控制和转储。HTM 缓冲区存储用于核心指令、核心 LLAT 和嵌套等功能的跟踪数据。

H_PKS_GEN_KEY

输入:authorization, objectlabel, objectlabellen, policy, out, outlen
输出:Hypervisor 生成的密钥,或者在设置了包装密钥策略时为 None
返回值:H_SUCCESS, H_Function, H_State, H_R_State, H_Parameter, H_P2, H_P3, H_P4, H_P5, H_P6, H_Authority, H_Nomem, H_Busy, H_Resource, H_Aborted

H_PKS_GEN_KEY 用于让 hypervisor 生成一个新的随机密钥。该密钥作为对象存储在 Power LPAR 平台密钥库中,带有提供的对象标签。设置了包装密钥策略后,该密钥仅对 hypervisor 可见,而密钥的标签对用户仍然可见。仅支持生成大小为 32 字节的包装密钥。

H_PKS_WRAP_OBJECT

输入:authorization, wrapkeylabel, wrapkeylabellen, objectwrapflags, in,
inlen, out, outlen, continue-token
输出:continue-token, 包装后对象的字节大小, 包装后的对象
返回值:H_SUCCESS, H_Function, H_State, H_R_State, H_Parameter, H_P2, H_P3, H_P4, H_P5, H_P6, H_P7, H_P8, H_P9, H_Authority, H_Invalid_Key, H_NOT_FOUND, H_Busy, H_LongBusy, H_Aborted

H_PKS_WRAP_OBJECT 用于使用存储在 Power LPAR 平台密钥库中的包装密钥来包装对象,并将包装后的对象返回给调用方。调用方提供一个设置了“包装密钥”策略的包装密钥的标签,该密钥必须先前已通过 H_PKS_GEN_KEY 创建。然后使用包装密钥和附加元数据对所提供的对象进行加密,并将结果返回给调用方。

H_PKS_UNWRAP_OBJECT

输入:authorization, objectwrapflags, in, inlen, out, outlen, continue-token
输出:continue-token, 解包后对象的字节大小, 解包后的对象
返回值:H_SUCCESS, H_Function, H_State, H_R_State, H_Parameter, H_P2, H_P3, H_P4, H_P5, H_P6, H_P7, H_Authority, H_Unsupported, H_Bad_Data, H_NOT_FOUND, H_Invalid_Key, H_Busy, H_LongBusy, H_Aborted

H_PKS_UNWRAP_OBJECT 用于解包先前已用 H_PKS_WRAP_OBJECT 包装的对象。

参考资料