1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! Performance timer implementation for DXE core and components.
//!
//! ## License
//!
//! Copyright (c) Microsoft Corporation.
//!
//! SPDX-License-Identifier: Apache-2.0
//!
use core::sync::atomic::{AtomicU64, Ordering};
use patina::component::service::{IntoService, perf_timer::ArchTimerFunctionality};
/// Performance timer implementation.
#[derive(IntoService)]
#[service(dyn ArchTimerFunctionality)]
pub struct PerfTimer {
frequency: AtomicU64,
}
impl ArchTimerFunctionality for PerfTimer {
/// Value of the counter (ticks).
fn cpu_count(&self) -> u64 {
arch_cpu_count()
}
/// Frequency of `cpu_count` increments (in Hz).
/// If a platform has provided a custom frequency via `PERF_FREQUENCY`, that value is used.
/// Otherwise, an architecture-specific method is attempted to determine the frequency.
fn perf_frequency(&self) -> u64 {
if self.frequency.load(Ordering::Relaxed) == 0 {
self.frequency.store(arch_perf_frequency(), Ordering::Relaxed);
}
self.frequency.load(Ordering::Relaxed)
}
}
impl PerfTimer {
/// Creates a new `PerfTimer` instance.
pub fn new() -> Self {
Self { frequency: AtomicU64::new(0) }
}
/// Creates a new `PerfTimer` instance with a specified frequency.
pub fn with_frequency(frequency: u64) -> Self {
Self { frequency: AtomicU64::new(frequency) }
}
/// Sets the performance frequency.
pub fn set_frequency(&self, frequency: u64) {
self.frequency.store(frequency, Ordering::Relaxed);
}
/// Gets the stored performance frequency.
pub fn get_stored_frequency(&self) -> u64 {
self.frequency.load(Ordering::Relaxed)
}
}
impl Default for PerfTimer {
fn default() -> Self {
Self::new()
}
}
/// Returns the current CPU count using architecture-specific methods.
///
/// Skip coverage as any value could be valid, including 0.
#[coverage(off)]
pub(crate) fn arch_cpu_count() -> u64 {
#[cfg(target_arch = "x86_64")]
{
use core::arch::x86_64;
unsafe { x86_64::_rdtsc() }
}
#[cfg(target_arch = "aarch64")]
{
use aarch64_cpu::registers::{self, Readable};
registers::CNTPCT_EL0.get()
}
}
/// Returns the performance frequency using architecture-specific methods.
/// In general, the performance frequency is a configurable value that may be
/// provided by the platform. This function is a fallback when no
/// platform-specific configuration is provided.
///
/// Skip coverage as any value could be valid, including 0.
#[coverage(off)]
pub(crate) fn arch_perf_frequency() -> u64 {
// Try to get TSC frequency from CPUID (most Intel and AMD platforms).
#[cfg(target_arch = "x86_64")]
{
use core::arch::{x86_64, x86_64::CpuidResult};
let CpuidResult { eax, ebx, ecx, .. } = unsafe { x86_64::__cpuid(0x15) };
if eax != 0 && ebx != 0 && ecx != 0 {
// CPUID 0x15 gives TSC_frequency = (ECX * EAX) / EBX.
// Most modern x86 platforms support this leaf.
return (ecx as u64 * ebx as u64) / eax as u64;
}
// CPUID 0x16 gives base frequency in MHz in EAX.
// This is supported on some older x86 platforms.
// This is a nominal frequency and is less accurate for reflecting actual operating conditions.
let CpuidResult { eax, .. } = unsafe { x86_64::__cpuid(0x16) };
if eax != 0 {
return (eax * 1_000_000) as u64;
}
0
}
// Use CNTFRQ_EL0 for aarch64 platforms.
#[cfg(target_arch = "aarch64")]
{
use patina::read_sysreg;
return read_sysreg!(CNTFRQ_EL0);
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
0
}
#[cfg(test)]
#[coverage(off)]
mod tests {
use patina::component::service::perf_timer::ArchTimerFunctionality as _;
use crate::perf_timer::PerfTimer;
#[test]
fn test_set_frequency() {
let frequency = 19191919;
let timer = PerfTimer::new();
assert_eq!(timer.get_stored_frequency(), 0);
timer.set_frequency(frequency);
assert_eq!(timer.perf_frequency(), 19191919);
let frequency = 20252025;
let timer = PerfTimer::with_frequency(frequency);
assert_eq!(timer.get_stored_frequency(), frequency);
let timer = PerfTimer::default();
assert_eq!(timer.get_stored_frequency(), 0);
}
}