【问题标题】:How to detect crc32 on aarch64如何在 aarch64 上检测 crc32
【发布时间】:2018-12-29 00:33:54
【问题描述】:

aarch64 上的 user 程序是否可以检测 crc32 指令是否可用?我找到了references to kernel support 进行此类检测,这意味着包含有关哪些指令将在用户模式下工作的信息的寄存器在用户模式下不可用(!)。

是这样吗?或者是否有一种可移植的方式来确定 crc32 指令是否可用?

注意:我所说的“用户程序”和“可移植”是一种不需要特权指令或操作系统特定调用或文件(例如 /proc/cpuinfo)的方法。代码本身需要能够检测指令是否可用,如果可用则使用它们,如果不可用则回退到替代方案。例如,英特尔处理器为此目的提供了cpuid 指令。

更新:

翻遍 ARM 架构描述,我发现了一个用户级寄存器PMCR_EL0,它为处理器提供了一个 8 位实现代码和一个 8 位 ID 代码。也许如果我能找到这些代码的列表,我可能会更接近我正在寻找的东西。

更新 2

但是,当我尝试读取该寄存器时,我得到了一个非法指令异常。那么即使是 EL0 寄存器也需要特权访问?

【问题讨论】:

  • 是的,可以在 EL0 访问 PMCR_EL0,但前提是之前通过配置 PMUSERENR_EL0 启用了 EL0 访问。奇怪的是,似乎需要在 EL1 上运行才能这样做,请参阅zhiyisun.github.io/2016/03/02/…。我在 EL0 上试了一下,遇到了非法指令错误。
  • ARM 不会让我在这里休息。

标签: portability arm64 crc32


【解决方案1】:

据我所知,并非如此。

我在 Chromium 的 zlib 中实现它的方式是使用可用的操作系统功能: https://cs.chromium.org/chromium/src/third_party/zlib/arm_features.c?l=29

值得一提的是,ARMv8 上的 crc32 指令是加密扩展的一部分,在 ARMv8 上是可选的,在 ARMv8-1 上是强制性的。这也意味着运行时特征检测是必要的,更多细节请查看: https://cs.chromium.org/chromium/src/third_party/zlib/BUILD.gn?l=64

我会避免直接从 /proc/cpuinfo 读取,因为这在某些情况下可能不可用(也取决于 Android 风格,它可能是假阴性)。

在 Chromium 中,zlib 将在特权上下文(即主浏览器进程中的网络代码的一部分)和沙盒上下文(即选项卡中 RendererProcess 的一部分)中运行。在 RendererProcess 中,从 /proc/cpuinfo 读取应该会失败。

一个大锤方法是安装一个信号处理程序并使用内联 asm 执行指令,如果指令不可用(并且可能被处理程序捕获),这将导致错误。不过不推荐。

上述示例 (https://github.com/torvalds/linux/blob/master/Documentation/arm64/cpu-feature-registers.txt) 在我测试过的 1 个 ARM 板 (MachiatoBin) 中工作,但在其他 2 个 (rock64 和 nanopi m4) 中失败。

在 Chromium 中实现的方法适用于所有电路板(以及我测试过的一些手机)。

关于 getauxval 的另一个细节:如果在 32 位或 64 位上运行,正确的标志将会改变。所以在 64 位中是 HWCAP_CRC32,而在 32 位中则是 HWCAP2_CRC32。

关于 sledgehammer 方法:信号容易出现竞争条件,而且您仍将依赖于使用特定于操作系统的 API(即安装信号处理程序)。

最后,根据上下文,如果给定任务崩溃(即使是设计使然并与执行上下文隔离),它将触发危险信号。

这是 x86 上的生活更轻松的一点(即特征检测)。

话虽如此,依赖操作系统功能可能是一个可以接受的折衷方案。自发布 M66(当前稳定版为 M72)以来,我们一直在 Chromium 中发布链接代码,大约一年前首次登陆,没有任何不良报告。

Android 的一个考虑因素是,NDK 内部可能使用 dlopen()/dlsym() 实现 android_getCpuFeatures(),这可以在第一次启动时增加大约 500us 到 1000us,这就是我们缓存 CPU 特征检测结果的原因.

多线程应用程序(如 Chromium)的另一个考虑因素是需要线程屏障(即 pthread_once_t)以避免在执行 CPU 功能检测时出现竞争条件。

【讨论】:

  • 谢谢。大锤的缺点是什么?
  • 我假设这是 Openssl 使用的基于信号的方案,通过安装信号处理程序来捕获非法指令来检测 ARM 指令是否可用 - 请参阅上面我的初始 cmets 之一。因此没有缺点,但您的要求仍然无法满足:仍然需要 libc 和操作系统级别的特定支持,这正是您想要避免的。
【解决方案2】:

更新:原始答案没有回答问题,因为它的作者想要在 EL0 上运行的代码的一些通用部分能够确定是否存在 CRC32 功能,而不需要对操作系统或裸机环境有任何要求用过。

我的理解是这样的代码需要访问 ID_AA64ISAR0_EL1,并且因为运行在 EL0 的代码无法访问它,所以无论如何都需要切换到更高特权的异常级别。

以同样的方式,使用“可移植”代码部分捕获非法指令需要访问 VBAR_ELx 寄存器,这无法通过在 EL0 上运行且不依赖任何底层操作系统/特权监视器的程序来实现。

因此,我对问题“是这样吗?”的回答应该是:是的,它是在 EL0 上运行的可移植/通用代码部分无法确定 CRC32 功能是否可用。

话虽如此,问题中文档referenced 中提供的示例代码在运行 aarch64 linux 4.14.80 的 Expressobin 上运行良好,并且由于内核中解释的原因,应该优先使用 getauxval()文档。

【讨论】:

  • 我在我的问题中链接到相同的解决方案。这是一个 Linux 内核调用,因此不可移植。
  • 有趣的是,它甚至不能在 Linux 上移植。刚刚在 Debian 和 Ubuntu ARM64 系统上试了一下,都说HWCAP2_CRC32 是未定义的。
  • 我不同意,根据 ARM 的文章,这应该是用户模式解决方案,更具体地说,调用 getauxval(AT_HWCAP2) 的用户模式程序确实编译/执行,即使 AT_HWCAP2我们的 asm/hwcap.h 文件中不支持/不存在。我目前正在调查这个问题。
  • 感谢您提供的精心整理的信息。但是,这并不能满足我对便携性的需求。 getauxval()/proc/cpuinfo 都不存在于 Linux 系统之外,因此不可移植。 getauxval()是对内核的用户态调用,让内核可以为用户访问受保护的寄存器。理想情况下,我正在寻找一种可以完全在用户模式程序中运行的方法,而不需要操作系统的帮助。这在 Intel CPU 上是完全可能的。
  • 我明白了。如果需要访问 ID_AA64ISAR0_EL1,则在 EL0 上运行的应用程序可能需要使用底层操作系统。另一种选择是在使用 signal() 函数捕获对非法指令的调用后使用 CRC32 指令。请注意,如果这可能适用于 Linux(OpenSSL 曾一度使用此方案),它可能不适用于您的其他目标,因为它们不会提供等效方案来捕获对非法指令的调用。
【解决方案3】:

这可能无法直接访问;但ARM 将为每个处理器提供specifications - 因此有机会创建一个图表,可用于通过型号名称查找CPU 功能。 /proc/cpuinfo 是特定于 Linux 的; Windows 等效为WMI; OSX 不能在ARM 上运行(据我所知)。除非它是完全绕过操作系统的 1 类管理程序,否则必须有特定于操作系统的代码(用户也可以禁用 VT)。

【讨论】:

  • 见上面 Mark Adler 的注释,我认为你的回答也不符合他的要求。
  • @Frant 因为这个要求没有意义,除非绕过操作系统——这可能会也可能不会,取决于设置——这并不能真正使其便携。型号名称和带有规格的图表将是最少打扰的,最有可能区分功能支持。
  • 我不会说这个要求没有意义,我只是认为由于 Armv8-a 架构设计它无法满足。你所提议的虽然可行。我只是说这不是 Mark Adler 想要的。
  • @Frant 这不一定是因为硬件设计,只要任何现代操作系统启动,问题就会出现,这保护了它的资源并且只允许通过驱动程序访问。同时,人们甚至不能再直接访问内存(除非它在仿真中运行)......仍然有一些工具可以直接从操作系统中刷新 BIOS/UEFI,这些工具也是特定于供应商和操作系统的。唯一真正独立于操作系统的是操作系统。
  • ARM 甚至具有 8 级硬件保护...运行中的操作系统可能会利用这些保护。
猜你喜欢
  • 2018-01-19
  • 2011-07-28
  • 2020-04-03
  • 2015-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
相关资源
最近更新 更多