ax_cpu/arch/aarch64/capability.rs
1//! AArch64 CPU identity.
2
3/// Architectural MIDR_EL1 identity, independent of platform CPU numbering.
4/// Cluster grouping and Linux PMU source names belong to the caller.
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6pub struct Midr(u64);
7
8impl Midr {
9 /// Reads this CPU's architectural identity.
10 pub fn read() -> Self {
11 Self(read_midr_el1())
12 }
13
14 /// Wraps a previously captured identity without accessing hardware.
15 pub const fn from_raw(value: u64) -> Self {
16 Self(value)
17 }
18
19 /// Returns the complete captured MIDR value.
20 pub const fn raw(self) -> u64 {
21 self.0
22 }
23
24 /// Returns the implementer code in bits 31:24.
25 pub const fn implementer(self) -> u8 {
26 (self.0 >> 24) as u8
27 }
28
29 /// Returns the implementation's part number in bits 15:4.
30 pub const fn part_number(self) -> u16 {
31 ((self.0 >> 4) & 0xfff) as u16
32 }
33
34 /// Returns the major revision in bits 23:20.
35 pub const fn variant(self) -> u8 {
36 ((self.0 >> 20) & 15) as u8
37 }
38
39 /// Returns the minor revision in bits 3:0.
40 pub const fn revision(self) -> u8 {
41 (self.0 & 15) as u8
42 }
43}
44
45/// Reads the raw MIDR_EL1 identity of the current CPU.
46pub fn read_midr_el1() -> u64 {
47 let value;
48 // SAFETY: callers of CPU primitives execute in privileged kernel context.
49 unsafe {
50 core::arch::asm!("mrs {}, MIDR_EL1", out(reg) value, options(nomem, nostack));
51 }
52 value
53}
54
55/// Raw architectural feature registers used by upper-layer capability policy.
56/// Values are not filtered for any operating system's supported user context.
57#[derive(Clone, Copy, Debug, Eq, PartialEq)]
58pub enum IdRegister {
59 /// Processor features, including implemented exception levels and FP/SIMD.
60 Pfr0,
61 /// Instruction-set attributes, first group.
62 Isar0,
63 /// Instruction-set attributes, second group.
64 Isar1,
65 /// Memory-model attributes, first group.
66 Mmfr0,
67 /// Memory-model attributes, second group.
68 Mmfr1,
69 /// Memory-model attributes, third group.
70 Mmfr2,
71}
72
73impl IdRegister {
74 /// Reads this CPU's unmodified architectural feature value at a privileged EL.
75 pub fn read(self) -> u64 {
76 let value;
77 // SAFETY: the CPU API is used in privileged context; these architectural
78 // identification registers are read-only and do not transfer ownership.
79 unsafe {
80 match self {
81 Self::Pfr0 => {
82 core::arch::asm!("mrs {}, ID_AA64PFR0_EL1", out(reg) value, options(nomem, nostack))
83 }
84 Self::Isar0 => {
85 core::arch::asm!("mrs {}, ID_AA64ISAR0_EL1", out(reg) value, options(nomem, nostack))
86 }
87 Self::Isar1 => {
88 core::arch::asm!("mrs {}, ID_AA64ISAR1_EL1", out(reg) value, options(nomem, nostack))
89 }
90 Self::Mmfr0 => {
91 core::arch::asm!("mrs {}, ID_AA64MMFR0_EL1", out(reg) value, options(nomem, nostack))
92 }
93 Self::Mmfr1 => {
94 core::arch::asm!("mrs {}, ID_AA64MMFR1_EL1", out(reg) value, options(nomem, nostack))
95 }
96 Self::Mmfr2 => {
97 core::arch::asm!("mrs {}, ID_AA64MMFR2_EL1", out(reg) value, options(nomem, nostack))
98 }
99 }
100 }
101 value
102 }
103}
104
105/// Returns the implemented physical-address width from ID_AA64MMFR0_EL1.
106/// Reserved encodings return None; consumers choose their supported format cap.
107pub fn physical_address_bits() -> Option<usize> {
108 match IdRegister::Mmfr0.read() & 15 {
109 0 => Some(32),
110 1 => Some(36),
111 2 => Some(40),
112 3 => Some(42),
113 4 => Some(44),
114 5 => Some(48),
115 6 => Some(52),
116 7 => Some(56),
117 _ => None,
118 }
119}