Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial support for ARM CPUs #200

Closed
wants to merge 30 commits into from

Conversation

TheTumultuousUnicornOfDarkness
Copy link
Collaborator

I know #96 is opening since 7 years, but better late that never. ARM CPUs are more and more popular (Raspberry Pi, Apple M, and so on...) and some users demand ARM CPUs support, so I started to invest some time in such feature.

My goal is to add ARM support along side x86: libcpuid on ARM can decode x86 dumps, and vice-versa. The drawback is it requires a little more memory in struct cpu_raw_data_t, but I think it is acceptable.
Only ARM64 is supported because we cannot access MIDR from userspace in 32-bit mode. Linux and FreeBSD allow to access some EL1 registers in AArch64 mode, support for other OS is TBC. In other words, I do not see any way to support ARM CPUs up to ARMv7.

This PR adds logically more vendors in enum cpu_vendor_t and more features in enum cpu_feature_t. There is CPU_FEATURE_AES on both x86 and ARM thus the error with consistency checks (I may update the check later).

In struct cpu_id_t , some fields do not have equivalent, that is why I added implementer/architecture_version/variant/part_num/revision. Some fields like family/model/ext_family/ext_model/stepping are x86 only. I do not know if the approach is correct, otherwise it will require a major change (like a union containing x86-specific and ARM-specific fields, depending on architecture value).
I tried to reuse some fields like vendor_str and brand_str, but these values are not stored in registers unlike x86.

Please note that we cannot decode cache information from userspace. I do not know if hardcoding all values for all ARM CPUs in the DB is a great idea, because the size is not fixed. Example for Cortex-A55 :

  • L1: 16KB to 64KB
  • L2: Optional, 64KB to 256KB
  • L3: Optional, 512KB to 4MB

Anyway, here is the cpuid_tool output for the Cortex-A57 CPU running on Debian 12 under a VM (QEMU TGC):

CPUID is present
CPU Info for type #0:
------------------
  arch       : ARM
  purpose    : general
  vendor_str : `ARM'
  vendor id  : 11
  brand_str  : `Cortex-A57'
  implementer: 65 (41h)
  arch_ver   : 15 (0Fh)
  variant    : 1 (01h)
  part_num   : 3335 (D07h)
  revision   : 0 (00h)
  num_cores  : 2
  num_logical: 2
  tot_logical: 2
  affi_mask  : 0x00000003
  features   : advsimd crc32 doublelock fp pmull sha1 sha256

@anrieff do you have time to provide a feedback? I plan to add more features (ARMv8.1 to ARMv8.9, and ARMv9.0 to ARMv9.4), I focused mostly on ARMv8.0 for now but it is just for the start. Work is required to support CPU topology (for big.LITTLE CPUs).
I use Arm Architecture Reference Manual for A-profile architecture as reference.

@TheTumultuousUnicornOfDarkness
Copy link
Collaborator Author

macOS does not allow to access EL1 registers:

/Users/runner/work/_temp/9f7b08bc-2a8a-459c-9461-d49e6b1338ae.sh: line 2:  3949 Illegal instruction: 4  ./build/cpuid_tool/cpuid_tool --save=-

@TheTumultuousUnicornOfDarkness
Copy link
Collaborator Author

I did a good progress targeting ARMv8/9-A in AArch64 mode, I added more than 2k LOC.

I replaced arch_ver by feat_level to provide a more accurate information:

CPU Info for type #0:
------------------
  arch       : ARM
  feat_level : ARMv8.1-A
  purpose    : performance
  vendor_str : `ARM'
  vendor id  : 11
  brand_str  : `Cortex-A57'
  implementer: 65 (41h)
  variant    : 1 (01h)
  part_num   : 3335 (D07h)
  revision   : 0 (00h)
  num_cores  : 2
  num_logical: 4
  tot_logical: 4
  affi_mask  : 0x0000000F
  features   : advsimd crc32 doublelock fp pmull sha1 sha256 sm4 sve bbm dotprod fhm sel2

That is all I can do from userspace. I can write a kernel module to access registers not available from userspace, I found a good example here.

@anrieff I would like to know if my approach in struct cpu_id_t is OK for you? I mean by mixing x86/ARM data in architecture, feature_level and flags fields.

ARMv9.0-A is a fork of ARMv8.5-A, so it cannot includes ARMv8.6-A+ mandatory features
@TheTumultuousUnicornOfDarkness
Copy link
Collaborator Author

I added some codenames and tests are now working for ARM CPUs.

I think the code is ready to be merged in master. @anrieff, any final thoughts before merging? These changes are backward compatibles.
I may work on kernel modules, caches detection and AArch32 support in a second time (in a new PR if you consider it necessary).

@TheTumultuousUnicornOfDarkness TheTumultuousUnicornOfDarkness marked this pull request as ready for review June 26, 2024 19:30
@anrieff
Copy link
Owner

anrieff commented Jun 28, 2024

Wonderful work!

You can merge now, but before making a release, it would be good to mark the deprecated cpu_id_t fields, with __attribute__((deprecated)) (which only works on GCC and Clang, but maybe this is as good as it gets).

Since GCC shows the offending line, we can make it even better, by including the "how to fix this" hint in a comment on the same line, i.e. you can have something like

...
    int32_t sse_size LIBCPUID_DEPRECATED; /* replace with ".x86.sse_size" in your code to fix the warning */
...

I also see two CPU cpu_feature_t's commented out (CPU_FEATURE_AA32HPD and CPU_FEATURE_AA32I8MM), what's the rub there?

@TheTumultuousUnicornOfDarkness
Copy link
Collaborator Author

I also see two CPU cpu_feature_t's commented out (CPU_FEATURE_AA32HPD and CPU_FEATURE_AA32I8MM), what's the rub there?

Ah, yes, these features are supported in AArch32 state only, but I focused on AArch64, that is why I was not able to detect FEAT_AA32.
It will require a kernel module for AArch32 in order to use the MRC instruction.

@TheTumultuousUnicornOfDarkness
Copy link
Collaborator Author

You can merge now, but before making a release, it would be good to mark the deprecated cpu_id_t fields, with __attribute__((deprecated)) (which only works on GCC and Clang, but maybe this is as good as it gets).

Since GCC shows the offending line, we can make it even better, by including the "how to fix this" hint in a comment on the same line, i.e. you can have something like

...
    int32_t sse_size LIBCPUID_DEPRECATED; /* replace with ".x86.sse_size" in your code to fix the warning */
...

Done in 7fca7c3.

@TheTumultuousUnicornOfDarkness
Copy link
Collaborator Author

Manually merged in c588569 (GitHub Pull Requests are degraded right now).
Thank you for your time @anrieff!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants