【ARMv8 编程】A64 系统控制和其他指令

A64 指令集包含与以下相关的指令:

  • 异常处理。
  • 系统寄存器访问。
  • 调试。
  • 提示指令,在许多系统中都有电源管理应用程序。

一、异常处理指令

存在三个异常处理指令,其目的是引发异常。这些用于调用在操作系统(EL1)、Hypervisor(EL2)或安全监视器(EL3) 中以更高异常级别运行的代码:

SVC #imm16 // Supervisor 调用,允许应用程序调用内核(EL1)。

HVC #imm16 // Hypervisor 调用,允许操作系统代码调用 hypervisor(EL2)。

SMC #imm16 // 安全监视器调用,允许操作系统或 hypervisor 调用安全监视器(EL3)。

立即值可用于异常“综合症”寄存器中的处理程序。这和 ARMv7 不同,在 ARMv7 中,必须通过读取调用指令的操作码来确定立即值。

要从异常中返回,请使用 ERET 指令。该指令通过将 SPSR_ELn 复制到 PSTATE 并分支到 ELR_ELn 中保存的返回地址来恢复处理器状态。

二、访问系统寄存器指令

为系统寄存器访问提供了两条指令:

MRS Xt, <system register> // 这会将系统寄存器复制到通用寄存器中
MSR <system register>, Xt // 这将通用寄存器复制到系统寄存器中

2.1 MRS

将系统寄存器移动到通用寄存器允许 PE 将 AArch64 系统寄存器读取到通用寄存器中。

在这里插入图片描述

MRS <Xt>, (<systemreg>|S<op0>_<op1>_<Cn>_<Cm>_<op2>)

<Xt> 是通用目标寄存器的 64 位名称,在“Rt”字段中编码。

<systemreg> 是一个系统寄存器名,编码在“o0:op1:CRn:CRm:op2”中。系统寄存器名称在系统寄存器 XML 的“AArch64 系统寄存器”中定义。

<op0> 是一个无符号立即数,编码为“o0”:

o0 op0
0 2
1 3

<op1> 是一个 3 位无符号立即数,范围为 0 到 7,在“op1”字段中编码。

<Cn> 是名称“Cn”,“n”的范围为 0 到 15,在“CRn”字段中编码。

<Cm> 是名称“Cm”,“m”的范围为 0 到 15,编码在“CRm”字段中。

<op2> 是一个 3 位无符号立即数,范围为 0 到 7,在“op2”字段中编码。

下面是使用 MRS 指令的例子:

    long long int x = 0;
    long long int y = 0;
    long long int z = 0;
    LOGD("x=%llx y=%llx z=%llx", x, y, z);
    asm volatile(
        "MOV X0, #2\n"
        "SUBS X0, X0, #1\n"
        "MRS %x[x], NZCV\n"
        "SUBS X0, X0, #1\n"
        "MRS %x[y], NZCV\n"
        "MRS %x[z], CNTFRQ_EL0\n"
    :[x] "+r"(x),
     [y] "+r"(y),
     [z] "+r"(z)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    LOGD("x=%llx y=%llx z=%llx", x, y, z);

NZCV —— PSTATE.{N, Z, C, V} 域可以在 EL0 访问。所有其他 PSTATE 字段都可以在 EL1 或更高级别执行,并且在 EL0 是未定义的。

CNTFRQ_EL0 —— 计数型计时器的频率寄存器(上报系统计时器的频率)

运行结果如下:

2023-05-09 07:33:57.504 8036-8152/com.demo.myapplication D/NativeCore: x=0 y=0 z=0
2023-05-09 07:33:57.504 8036-8152/com.demo.myapplication D/NativeCore: -----------------------------
2023-05-09 07:33:57.504 8036-8152/com.demo.myapplication D/NativeCore: x=20000000 y=60000000 z=1d4c00

2.2 MSR(立即数)

将立即值移动到特殊寄存器将立即值移动到 PSTATE 的选定位。该指令可以写入的位是:

  • PSTATE.D、PSTATE.A、PSTATE.I、PSTATE.F 和 PSTATE.SP。

  • 如果实现了 FEAT_SSBS,包括 PSTATE.SSBS。

  • 如果实现了 FEAT_PAN,包括 PSTATE.PAN。

  • 如果实现了 FEAT_UAO,包括 PSTATE.UAO。

  • 如果实现了 FEAT_DIT,包括 PSTATE.DIT。

  • 如果实现了 FEAT_MTE,包括 PSTATE.TCO。

  • 如果实现了 FEAT_NMI,包括 PSTATE.ALLINT。

  • 如果实现了 FEAT_SME,包括 PSTATE.SM 和 PSTATE.ZA。

  • 如果实现了 FEAT_EBEP,包括 PSTATE.PM。

该指令由别名 SMSTART 和 SMSTOP 使用。

在这里插入图片描述

MSR <pstatefield>, #<imm>

<pstatefield> 是 PSTATE 字段名称。对于 MSR 指令,这是在“op1:op2:CRm”中编码的:

对于 SMSTART 和 SMSTOP 别名,这是在“CRm<2:1>”中编码的,其中 0b01 指定 SVCRSM,0b10 指定 SVCRZA,0b11 指定 SVCRSMZA。

<imm> 是 4 位无符号立即数,范围为 0 到 15,在“CRm”字段中编码。 当 <pstatefield> 为 ALLINT、PM、SVCRSM、SVCRSMZA 或 SVCRZA 时,限制在 0 到 1 的范围内,在“CRm<0>”中编码。

例如:

也可以使用 MSR 或 MRS 访问 PSTATE 的各个字段。例如,要选择与 EL0 或当前异常级别关联的堆栈指针:

MSR SPSel, #imm —— 该寄存器中的值 0 或 1 用于在使用 EL0 堆栈指针或当前异常级别堆栈指针之间进行选择。

2.3 MSR(寄存器)

将通用寄存器移动到系统寄存器允许 PE 从通用寄存器写入 AArch64 系统寄存器。

在这里插入图片描述

MSR (<systemreg>|S<op0>_<op1>_<Cn>_<Cm>_<op2>), <Xt>

<systemreg> 是一个系统寄存器名,编码在“o0:op1:CRn:CRm:op2”中。系统寄存器名称在系统寄存器 XML 的“AArch64 系统寄存器”中定义。

<op0> 是一个无符号立即数,编码为“o0”:

o0 op0
0 2
1 3

<op1> 是一个 3 位无符号立即数,范围为 0 到 7,在“op1”字段中编码。

<Cn> 是名称“Cn”,“n”的范围为 0 到 15,在“CRn”字段中编码。

<Cm> 是名称“Cm”,“m”的范围为 0 到 15,编码在“CRm”字段中。

<op2> 是一个 3 位无符号立即数,范围为 0 到 7,在“op2”字段中编码。

<Xt> 是通用源寄存器的 64 位名称,在“Rt”字段中编码。

例如: MSR SPSR_EL1, X0 —— 将 X0 复制到 SPSR_EL1

三、Debug 指令

调试相关的指令有两条:

BRK #imm16 // 进入监控模式调试

HLT #imm16 // 进入暂停模式调试

四、提示指令

HINT 指令可以合法地视为 NOP,但它们可以具有特定于实现的效果:

NOP // 无操作 - 不保证需要时间来执行
YIELD // 提示当前线程正在执行可以换出的任务
WFE // 等待事件
WFI // 等待中断
SEV // 发送事件
SEVL // Send Event Local

4.1 NOP

无操作指令除了将程序计数器的值增加 4 外,什么都不做。该指令可用于指令对齐目的。

不保证在程序中包含 NOP 指令的时序效果。它可以增加执行时间,或者保持不变,甚至减少它。 因此,NOP 指令不适用于定时循环。

4.2 YIELD

YIELD 是一条提示指令。具有多线程功能的软件可以使用 YIELD 指令向 PE 指示它正在执行一项任务,例如自旋锁,可以将其换出以提高整体系统性能。如果 PE 支持此功能,则它可以使用此提示来挂起和恢复多个软件线程。

4.3 WFE

WFE(Wait For Event)是一条提示指令,表示 PE 可以进入低功耗状态,并一直保持到唤醒事件发生。唤醒事件包括由于在多处理器系统中的任何 PE 上执行 SEV 指令而发出的事件信号。执行 WFE 指令可能会导致进入低功耗状态,但可能会陷入更高的异常级别。

4.4 WFI

WFI(Wait For Interrupt)是一条提示指令,表示 PE 可以进入低功耗状态并一直保持到唤醒事件发生。WFI 指令的执行可能会导致进入低功耗状态,但可能会陷入更高的异常级别。

4.5 SEV

SEV(Send Event)是一条提示指令。它导致向多处理器系统中的所有 PE 发送一个事件信号。

4.6 SEVL

SEVL(Send Event Local)是一条提示指令,它使事件在本地发出信号,而不需要将事件信号通知给多处理器系统中的其他 PE。 它可以启动一个以 WFE 指令开始的等待循环。

五、NEON 指令

NEON 指令集也有一些增强,其中一些非常重要。A64 中对 NEON 的更改包括:

  • 支持双精度浮点,使使用双精度浮点的 C 代码能够被矢量化。

  • 用于操作存储在 NEON 寄存器中的标量数据的新指令。

  • 插入和提取向量元素的新指令。

  • 类型转换和饱和整数运算的新指令。

  • 浮点值规范化的新指令。

  • 用于向量缩减、求和以及取最小值或最大值的新跨通道指令。

  • 执行比较、加法、求绝对值和取反等操作的指令已扩展为能够对 64 位整数元素进行操作。

例如下面使用 NEON 指令的片段。

    long long int x = 0;
    long long int y = 0;

    char* srcArr = new char[16];
    for(int i = 0; i < 16; i++){
    
    
        srcArr[i] = i;
    }
    LOGD("x=%llx y=%llx", x, y);
    LOGD("srcArr dw: 0x%llx 0x%llx", *((long long int*)srcArr), *((long long int*)srcArr + 1));
    asm volatile(
        "LD1 {v0.16B},[%[srcArr]]\n"
        "MOV %[x],v0.D[0]\n"
        "MOV %[y],v0.D[1]\n"
    :[srcArr] "+r"(srcArr),
     [x] "+r"(x),
     [y] "+r"(y)
    :
    : "cc", "memory", "v0");

    LOGD("-----------------------------");
    LOGD("x=0x%llx y=0x%llx", x, y);

在这里插入图片描述

LD1 指令将 srcArr 指针指向的数组内容加载到 v0 寄存器的 16 个 B 通道,两条 MOV 指令则将 v0 寄存器按照 D 通道划分,分别将 D[0] 和 D[1] 写入 x 和 y。

运行结果如下:

2023-05-10 08:27:57.959 30077-30154/com.demo.myapplication D/NativeCore: x=0 y=0
2023-05-10 08:27:57.959 30077-30154/com.demo.myapplication D/NativeCore: srcArr dw: 0x706050403020100 0xf0e0d0c0b0a0908
2023-05-10 08:27:57.959 30077-30154/com.demo.myapplication D/NativeCore: -----------------------------
2023-05-10 08:27:57.959 30077-30154/com.demo.myapplication D/NativeCore: x=0x706050403020100 y=0xf0e0d0c0b0a0908

六、浮点指令

A64 提供一组与 ARMv7-A VFPv4 扩展类似的浮点指令集,它提供对标量浮点值的单精度和双精度数学运算。有许多更改和新功能:

  • 浮点比较直接设置条件标志(NZCV)。在 A64 中,不需要显式地将比较结果从浮点数标志转移到整数标志。

  • 添加了与 IEEE754-2008 标准相关的说明,例如计算一对数字的最小值和最大值。

  • 从整数格式转换为浮点格式时,现在可以明确指定舍入模式。当在特定舍入模式下需要简单转换时,不再需要设置全局 FPCR 标志。其中一些选项也可用于 ARMv8 A32 和 T32。

  • 添加了指令以支持 64 位整数和浮点格式之间的转换。

  • 在 A64 中,涉及整数类型的浮点运算直接作用于整数寄存器。无需手动在浮点寄存器和整数寄存器之间传递整数值以进行转换操作。

例如下面使用浮点指令的片段。

    double x = 0.0;
    double y = 0.0;

    auto* srcArr = new double[2];
    for(int i = 0; i < 2; i++){
    
    
        srcArr[i] = 1.121 * (i + 1);
    }
    char *src = (char *) srcArr;
    LOGD("x=%lf y=%lf", x, y);
    LOGD("srcArr: %lf %lf", srcArr[0], srcArr[1]);
    asm volatile(
        "LD1 {v0.16B},[%[src]]\n"
        "MOV v1.16B, v0.16B\n"
        "FADD v2.2D, v0.2D, v1.2D\n"
        "FMOV %[x], D2\n"
        "FMOV %[y],v2.D[1]\n"
    :[src] "+r"(src),
     [x] "+r"(x),
     [y] "+r"(y)
    :
    : "cc", "memory", "v0", "v1", "v2");

    LOGD("-----------------------------");
    LOGD("x=%lf y=%lf", x, y);

在这里插入图片描述

FADD v2.2D, v0.2D, v1.2D 将 v0 和 v1 寄存器中的 D 通道双精度浮点值相加,最后保存在 v2 寄存器中,FMOV 指令分别将 v2 寄存器中的两个 D 通道的值移动到 %[x] 和 %[y] 寄存器内。我们看到指令已按照我们预想的执行了!

运行结果如下:

2023-05-11 08:26:03.612 11758-11846/com.demo.myapplication D/NativeCore: x=0.000000 y=0.000000
2023-05-11 08:26:03.612 11758-11846/com.demo.myapplication D/NativeCore: srcArr: 1.121000 2.242000
2023-05-11 08:26:03.612 11758-11846/com.demo.myapplication D/NativeCore: -----------------------------
2023-05-11 08:26:03.612 11758-11846/com.demo.myapplication D/NativeCore: x=2.242000 y=4.484000

七、加密指令

ARMv8 的可选扩展添加了加密指令,可显着提高 AES 加密以及 SHA1 和 SHA256 散列等任务的性能。

参考资料

1.《ARMv8-A-Programmer-Guide》
2.《Arm® A64 Instruction Set Architecture Armv8, for Armv8-A architecture profile》

猜你喜欢

转载自blog.csdn.net/tyyj90/article/details/130717383