Any CPUID like instruction in armv8?

Viewed 4336

So I am programming in assembly ARMv8, I would like to know if there is any instruction, like in x86, to recover information about CPU. This is the information I need:

(CPUID) Signature: 660f01

(CPUID) Features: MMX, AMD64, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AES, AVX, CLMUL, FMA, FMA4, XOP, PSE, PSE-36, NX, SVM

(CPUID) Cache L1: 4 x 32 KB Data, 4 x 96 KB Instruction (CPUID) Cache L2: 4 x 1 MB Unified (CPUID) Cache L3: None

Or something equivalent in arm. For example, I know ARMS does not support MMX, but it supports NEON. How can I check if that processor supports it?

3 Answers

(This answer is for AArch64.)

Based on some reading and experimentation, there are some system registers that you can read for information about the CPU and its features, using the mrs instruction. The reference for this is the Armv8 Architecture Reference Manual; there's a list of ID registers at section K14.3.3 (in my revision which is possibly not the very latest).

A couple that might be of particular interest:

  • MIDR_EL1 has general information about the CPU: Implementer (e.g. Arm, Broadcom, NVidia), variant, architecture, part number, revision.

  • REVIDR_EL1 has implementation revision information. (It's zero on my Raspberry Pi 4.)

  • IID_AA64_ISAR{0,1}_EL1 have information about which instructions are implemented on the device: CRC. SHA, Atomic, random numbers, etc.

There doesn't seem to be anything directly comparable to the human-readable brand strings produced by x86's CPUID.

Here's some code I used for testing with gcc under Linux. Some registers may not be readable in userspace and result in an exception, so the program handles the resulting signal so as to be able to continue in this case.

I don't yet understand the exception level system well enough to be sure whether these really read the hardware register directly, or if they trap so that the kernel and/or hypervisor could emulate and/or censor the results.

#include <stdio.h>
#include <stdint.h>
#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>

sigjmp_buf go_here;

void sigill_handler(int signum) {
    (void)signum;
    siglongjmp(go_here, 1);
}

#define PRINT_REG(s)                                            \
    do {                                                        \
        if (sigsetjmp(go_here, 1)) {                            \
            printf("%s:\tSIGILL\n", s);                         \
        } else {                                                \
            unsigned long ret;                                  \
            asm("mrs %0, " s : "=r" (ret));                     \
            printf("%s:\t%#lx\n", s, ret);                      \
        }                                                       \
    } while (0)

int main(void) {
    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_handler = sigill_handler;
    if (sigaction(SIGILL, &sa, NULL) < 0) {
        perror("sigaction");
        exit(2);
    }
    
    PRINT_REG("MIDR_EL1");
    PRINT_REG("VPIDR_EL2");
    PRINT_REG("REVIDR_EL1");
    PRINT_REG("ID_AA64ISAR0_EL1");
    PRINT_REG("ID_AA64ISAR1_EL1");
    PRINT_REG("MVFR0_EL1");
    PRINT_REG("MVFR1_EL1");
    PRINT_REG("MVFR2_EL1");
    return 0;
}

There isn't any instruction in arm that provide CPU implemented features. But there are system registers which indicate what features are supported by CPU. These typically start as ID_* for example, ID_AA64PFR0_EL1 is architecture processor feature register. You can refer ARM Architecture Reference Manual to get the system register list and details regarding the features supported. These are not accessible from user application and are accessible only from higher exception level using MRS instruction. Also note that architecture provides means to trap the accesses as well.

You usually just need to check for NEON not all individual capabilities. aarch64 includes NEON. For arm32 on linux you usually read from /proc/self/auxv See eg https://github.com/steinwurf/cpuid/blob/master/src/cpuid/detail/init_linux_gcc_arm.hpp

On windows msvc you usually only need to check ifdef PLATFORM_WINDOWS_PHONE. This guarantees NEON. Similar on clang check only __ARM_NEON__ and various capabilities via static defines.

There is a status register, you can read from. I do also some arm specific asm intrinsics, but they are too fragile. The https://github.com/simd-everywhere/simde library can help much better.

Related