1use core::marker::PhantomData;
6
7use super::{EAX, EBX, ECX, EDX};
8use crate::LayoutOver;
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub struct CpuidRawResult {
13 pub eax: u32,
15 pub ebx: u32,
17 pub ecx: u32,
19 pub edx: u32,
21}
22
23impl CpuidRawResult {
24 pub const fn zeroed() -> Self {
25 Self { eax: 0, ebx: 0, ecx: 0, edx: 0 }
26 }
27}
28
29#[derive(Clone, Copy, Debug)]
35pub struct CpuidValue<const LEAF: u32, const SUBLEAF: u32, const REG: u8, Layout: LayoutOver<u32>>(
36 PhantomData<Layout>,
37);
38
39impl<const LEAF: u32, const SUBLEAF: u32, const REG: u8, Layout: LayoutOver<u32>>
40 CpuidValue<LEAF, SUBLEAF, REG, Layout>
41{
42 pub const fn new() -> Self {
43 assert!(REG <= EDX);
44 Self(PhantomData {})
45 }
46
47 pub const fn leaf(&self) -> u32 {
53 LEAF
54 }
55
56 pub const fn subleaf(&self) -> u32 {
62 SUBLEAF
63 }
64}
65
66impl<const LEAF: u32, const SUBLEAF: u32, const REG: u8, Layout: LayoutOver<u32>> Default
67 for CpuidValue<LEAF, SUBLEAF, REG, Layout>
68{
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74pub trait Cpuid {
76 fn read_raw(&self, leaf: u32, subleaf: u32) -> CpuidRawResult;
78
79 fn supports<const LEAF: u32, const SUBLEAF: u32, const REG: u8, Layout: LayoutOver<u32>>(
82 &self,
83 _cpuid: CpuidValue<LEAF, SUBLEAF, REG, Layout>,
84 ) -> bool {
85 const MAX_BASE_LEAF: CpuidValue<0x0000_0000, 0x0, EAX, u32> = CpuidValue::new();
86 const MAX_HYPERVISOR_LEAF: CpuidValue<0x4000_0000, 0x0, EAX, u32> = CpuidValue::new();
87 const MAX_EXTENDED_LEAF: CpuidValue<0x8000_0000, 0x0, EAX, u32> = CpuidValue::new();
88
89 const AMD_EXTENDED_FEATURES_C: CpuidValue<0x8000_0001, 0x0, ECX, u32> = CpuidValue::new();
90 const AMD_EXTENDED_FEATURES_C_TOPOLOGY_EXTENSIONS_BIT: u32 = 22;
91
92 const AMD_CACHE_TOPOLOGY_LEAF: u32 = 0x8000_001d;
93 const AMD_PROCESSOR_TOPOLOGY_LEAF: u32 = 0x8000_001e;
94
95 if LEAF >= MAX_EXTENDED_LEAF.leaf() {
96 if LEAF > self.read(MAX_EXTENDED_LEAF) {
97 return false;
98 }
99
100 if LEAF == AMD_CACHE_TOPOLOGY_LEAF || LEAF == AMD_PROCESSOR_TOPOLOGY_LEAF {
102 return (self.read(AMD_EXTENDED_FEATURES_C)
103 & (1 << AMD_EXTENDED_FEATURES_C_TOPOLOGY_EXTENSIONS_BIT))
104 != 0;
105 }
106
107 return true;
108 }
109
110 if LEAF >= MAX_HYPERVISOR_LEAF.leaf() {
111 return LEAF <= self.read(MAX_HYPERVISOR_LEAF);
112 }
113
114 LEAF <= self.read(MAX_BASE_LEAF)
115 }
116
117 fn read<const LEAF: u32, const SUBLEAF: u32, const REG: u8, Layout: LayoutOver<u32>>(
119 &self,
120 _value: CpuidValue<LEAF, SUBLEAF, REG, Layout>,
121 ) -> Layout {
122 let raw = self.read_raw(LEAF, SUBLEAF);
123 match REG {
124 EAX => raw.eax,
125 EBX => raw.ebx,
126 ECX => raw.ecx,
127 EDX => raw.edx,
128 _ => unreachable!("REG validated on Cpuid construction"),
129 }
130 .into()
131 }
132
133 fn try_read<const LEAF: u32, const SUBLEAF: u32, const REG: u8, Layout: LayoutOver<u32>>(
136 &self,
137 value: CpuidValue<LEAF, SUBLEAF, REG, Layout>,
138 ) -> Option<Layout> {
139 self.supports(value).then(|| self.read(value))
140 }
141}
142
143impl<C: Cpuid> Cpuid for &C {
144 fn read_raw(&self, leaf: u32, subleaf: u32) -> CpuidRawResult {
145 (*self).read_raw(leaf, subleaf)
146 }
147}
148
149#[derive(Clone, Copy, Debug)]
151pub struct DirectCpuid;
152
153#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
154mod x86_only {
155 #[cfg(target_arch = "x86_64")]
156 use core::arch::x86_64::{__cpuid_count, CpuidResult as ArchCpuidResult};
157
158 #[cfg(target_arch = "x86")]
159 use core::arch::x86::{__cpuid_count, CpuidResult as ArchCpuidResult};
160
161 use super::*;
162
163 impl Cpuid for DirectCpuid {
164 #[inline]
165 fn read_raw(&self, leaf: u32, subleaf: u32) -> CpuidRawResult {
166 let ArchCpuidResult { eax, ebx, ecx, edx } = __cpuid_count(leaf, subleaf);
167 CpuidRawResult { eax, ebx, ecx, edx }
168 }
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175 use crate::testing::x86::FakeCpuid;
176
177 const MAX_BASIC_LEAF: CpuidValue<0x0000_0000, 0x0, EAX, u32> = CpuidValue::new();
178 const FEATURE_LEAF_1_ECX: CpuidValue<0x0000_0001, 0x0, ECX, u32> = CpuidValue::new();
179 const FEATURE_LEAF_7_EBX: CpuidValue<0x0000_0007, 0x0, EBX, u32> = CpuidValue::new();
180
181 const MAX_HYPERVISOR_LEAF: CpuidValue<0x4000_0000, 0x0, EAX, u32> = CpuidValue::new();
182 const HYPERVISOR_LEAF_1_EAX: CpuidValue<0x4000_0001, 0x0, EAX, u32> = CpuidValue::new();
183 const HYPERVISOR_LEAF_2_EAX: CpuidValue<0x4000_0002, 0x0, EAX, u32> = CpuidValue::new();
184
185 const MAX_EXTENDED_LEAF: CpuidValue<0x8000_0000, 0x0, EAX, u32> = CpuidValue::new();
186 const AMD_EXTENDED_FEATURES_C: CpuidValue<0x8000_0001, 0x0, ECX, u32> = CpuidValue::new();
187 const AMD_CACHE_TOPOLOGY_A: CpuidValue<0x8000_001d, 0x0, EAX, u32> = CpuidValue::new();
188
189 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
191 #[test]
192 fn direct_cpuid() {
193 const CPUID_MAX_LEAF: CpuidValue<0x0, 0x0, EAX, u32> = CpuidValue::new();
194 println!("Maximum CPUID leaf number: {:#x}", DirectCpuid {}.read(CPUID_MAX_LEAF));
195 }
196
197 #[test]
198 fn cpuid_supports_basic_leaves() {
199 let mut cpuid = FakeCpuid::new();
200 cpuid.set(MAX_BASIC_LEAF, 1);
201 assert!(cpuid.supports(FEATURE_LEAF_1_ECX));
202 assert!(!cpuid.supports(FEATURE_LEAF_7_EBX));
203
204 assert_eq!(cpuid.try_read(FEATURE_LEAF_1_ECX), Some(0));
205 assert_eq!(cpuid.try_read(FEATURE_LEAF_7_EBX), None);
206 }
207
208 #[test]
209 fn cpuid_supports_hypervisor_leaves() {
210 let mut cpuid = FakeCpuid::new();
211 cpuid.set(MAX_HYPERVISOR_LEAF, 0x4000_0001);
212 assert!(cpuid.supports(HYPERVISOR_LEAF_1_EAX));
213
214 assert!(!cpuid.supports(HYPERVISOR_LEAF_2_EAX));
215 }
216
217 #[test]
218 fn cpuid_supports_extended_and_topology_leaves() {
219 let mut cpuid = FakeCpuid::new();
220 cpuid.set(MAX_EXTENDED_LEAF, 0x8000_001e);
221 assert!(!cpuid.supports(AMD_CACHE_TOPOLOGY_A));
223
224 cpuid.set(AMD_EXTENDED_FEATURES_C, 1 << 22);
226 assert!(cpuid.supports(AMD_CACHE_TOPOLOGY_A));
227 }
228}