ax_cpu/arch/aarch64/pmu/
mod.rs1use core::{arch::asm, marker::PhantomData};
5
6mod capability;
7mod registers;
8pub use capability::{EventSupport, PmuInfo};
9
10macro_rules! read_reg {
11 ($register:literal) => {{
12 let value: u64;
13 unsafe { asm!(concat!("mrs {}, ", $register), out(reg) value, options(nomem, nostack)); }
15 value
16 }};
17}
18
19macro_rules! write_reg {
20 ($register:literal, $value:expr) => {{
21 unsafe { asm!(concat!("msr ", $register, ", {}"), in(reg) $value as u64, options(nostack)); }
23 }};
24}
25
26mod access;
27mod counter;
28mod overflow;
29
30fn isb() {
31 unsafe {
33 asm!("isb", options(nostack));
34 }
35}
36
37#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
39pub enum PmuError {
40 #[error("PMUv3 is unavailable on this CPU")]
42 Unavailable,
43 #[error("counter is not implemented on this CPU")]
45 InvalidCounter,
46 #[error("event is reported as unsupported")]
48 UnsupportedEvent,
49 #[error("configuration does not apply to this counter")]
51 InvalidConfiguration,
52 #[error("period is zero or exceeds the counter width")]
54 InvalidPeriod,
55}
56
57#[derive(Clone, Copy, Debug, Eq, PartialEq)]
59pub struct CounterId(u8);
60
61impl CounterId {
62 pub const CYCLE: Self = Self(31);
64
65 pub const INSTRUCTIONS: Self = Self(32);
67
68 pub const fn index(self) -> usize {
70 self.0 as usize
71 }
72}
73
74#[derive(Clone, Copy, Debug, Eq, PartialEq)]
76pub struct EventConfig {
77 pub event: u16,
79 pub exclude_user: bool,
81 pub exclude_kernel: bool,
83 pub include_hypervisor: bool,
85}
86
87pub struct Pmu {
90 info: PmuInfo,
91 _not_send_sync: PhantomData<*mut ()>,
92}
93
94impl Pmu {
95 pub unsafe fn current() -> Result<Self, PmuError> {
102 let version = ((read_reg!("ID_AA64DFR0_EL1") >> 8) & 15) as u8;
103 if version == 0 || version == 15 {
104 return Err(PmuError::Unavailable);
105 }
106 let pmcr = read_reg!("PMCR_EL0");
107 Ok(Self {
108 info: PmuInfo {
109 version,
110 num_counters: ((pmcr >> 11) & 31) as usize,
111 has_instruction_counter: (read_reg!("ID_AA64DFR1_EL1") >> 36) & 15 != 0,
112 counter_width: if version >= 6 && pmcr & (1 << 7) != 0 {
113 64
114 } else {
115 32
116 },
117 cycle_counter_width: if pmcr & (1 << 6) != 0 { 64 } else { 32 },
118 pmceid0: read_reg!("PMCEID0_EL0"),
119 pmceid1: read_reg!("PMCEID1_EL0"),
120 },
121 _not_send_sync: PhantomData,
122 })
123 }
124
125 pub const fn info(&self) -> PmuInfo {
127 self.info
128 }
129
130 pub fn counter(&self, index: usize) -> Result<CounterId, PmuError> {
132 if index < self.info.num_counters {
133 Ok(CounterId(index as u8))
134 } else {
135 Err(PmuError::InvalidCounter)
136 }
137 }
138
139 fn validate(&self, id: CounterId) -> Result<(), PmuError> {
140 if id == CounterId::CYCLE
141 || (id == CounterId::INSTRUCTIONS && self.info.has_instruction_counter)
142 || id.index() < self.info.num_counters
143 {
144 Ok(())
145 } else {
146 Err(PmuError::InvalidCounter)
147 }
148 }
149
150 pub fn width(&self, id: CounterId) -> Result<u8, PmuError> {
152 self.validate(id)?;
153 Ok(if id == CounterId::INSTRUCTIONS {
154 64
155 } else if id == CounterId::CYCLE {
156 self.info.cycle_counter_width
157 } else {
158 self.info.counter_width
159 })
160 }
161
162 fn mask(&self, id: CounterId) -> Result<u64, PmuError> {
163 Ok(u64::MAX >> (64 - self.width(id)?))
164 }
165
166 fn implemented_mask(&self) -> u64 {
167 ((1u64 << self.info.num_counters) - 1)
168 | (1u64 << 31)
169 | (u64::from(self.info.has_instruction_counter) << 32)
170 }
171
172 pub fn is_running(&self) -> bool {
174 read_reg!("PMCR_EL0") & 1 != 0
175 }
176
177 pub unsafe fn with_counting_paused<R>(&mut self, operation: impl FnOnce(&mut Self) -> R) -> R {
185 struct Restore<'a> {
186 pmu: &'a mut Pmu,
187 running: bool,
188 }
189 impl Drop for Restore<'_> {
190 fn drop(&mut self) {
191 if self.running {
192 self.pmu.start();
193 } else {
194 self.pmu.stop();
195 }
196 }
197 }
198 let running = self.is_running();
199 self.stop();
200 let restore = Restore { pmu: self, running };
201 operation(restore.pmu)
202 }
203
204 pub fn start(&mut self) {
206 let value = (read_reg!("PMCR_EL0") & 0xf9) | 1;
207 isb();
208 write_reg!("PMCR_EL0", value);
209 }
210
211 pub fn stop(&mut self) {
213 let value = read_reg!("PMCR_EL0") & 0xf8;
214 isb();
215 write_reg!("PMCR_EL0", value);
216 isb();
217 }
218
219 pub unsafe fn reset(&mut self) {
226 let mask = self.implemented_mask();
227 write_reg!("PMCNTENCLR_EL0", mask);
228 isb();
229 write_reg!("PMINTENCLR_EL1", mask);
230 isb();
231 write_reg!("PMOVSCLR_EL0", mask);
232 isb();
233 write_reg!("PMUSERENR_EL0", 0);
234 let value = 2
235 | 4
236 | 64
237 | if self.info.has_long_counters() {
238 128
239 } else {
240 0
241 };
242 isb();
243 write_reg!("PMCR_EL0", value);
244 isb();
245 if self.info.has_instruction_counter {
246 write_reg!("S3_3_C9_C4_0", 0); }
248 self.info.counter_width = if self.info.has_long_counters() {
249 64
250 } else {
251 32
252 };
253 self.info.cycle_counter_width = 64;
254 }
255}