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
66pub trait Cpuid {
68 fn read_raw(&self, leaf: u32, subleaf: u32) -> CpuidRawResult;
70
71 fn supports<const LEAF: u32, const SUBLEAF: u32, const REG: u8, Layout: LayoutOver<u32>>(
74 &self,
75 _cpuid: CpuidValue<LEAF, SUBLEAF, REG, Layout>,
76 ) -> bool {
77 const MAX_BASE_LEAF: CpuidValue<0x0000_0000, 0x0, EAX, u32> = CpuidValue::new();
78 const MAX_HYPERVISOR_LEAF: CpuidValue<0x4000_0000, 0x0, EAX, u32> = CpuidValue::new();
79 const MAX_EXTENDED_LEAF: CpuidValue<0x8000_0000, 0x0, EAX, u32> = CpuidValue::new();
80
81 const AMD_EXTENDED_FEATURES_C: CpuidValue<0x8000_0001, 0x0, ECX, u32> = CpuidValue::new();
82 const AMD_EXTENDED_FEATURES_C_TOPOLOGY_EXTENSIONS_BIT: u32 = 22;
83
84 const AMD_CACHE_TOPOLOGY_LEAF: u32 = 0x8000_001d;
85 const AMD_PROCESSOR_TOPOLOGY_LEAF: u32 = 0x8000_001e;
86
87 if LEAF >= MAX_EXTENDED_LEAF.leaf() {
88 if LEAF > self.read(MAX_EXTENDED_LEAF) {
89 return false;
90 }
91
92 if LEAF == AMD_CACHE_TOPOLOGY_LEAF || LEAF == AMD_PROCESSOR_TOPOLOGY_LEAF {
94 return (self.read(AMD_EXTENDED_FEATURES_C)
95 & (1 << AMD_EXTENDED_FEATURES_C_TOPOLOGY_EXTENSIONS_BIT))
96 != 0;
97 }
98
99 return true;
100 }
101
102 if LEAF >= MAX_HYPERVISOR_LEAF.leaf() {
103 return LEAF <= self.read(MAX_HYPERVISOR_LEAF);
104 }
105
106 LEAF <= self.read(MAX_BASE_LEAF)
107 }
108
109 fn read<const LEAF: u32, const SUBLEAF: u32, const REG: u8, Layout: LayoutOver<u32>>(
111 &self,
112 _value: CpuidValue<LEAF, SUBLEAF, REG, Layout>,
113 ) -> Layout {
114 let raw = self.read_raw(LEAF, SUBLEAF);
115 match REG {
116 EAX => raw.eax,
117 EBX => raw.ebx,
118 ECX => raw.ecx,
119 EDX => raw.edx,
120 _ => unreachable!("REG validated on Cpuid construction"),
121 }
122 .into()
123 }
124
125 fn try_read<const LEAF: u32, const SUBLEAF: u32, const REG: u8, Layout: LayoutOver<u32>>(
128 &self,
129 value: CpuidValue<LEAF, SUBLEAF, REG, Layout>,
130 ) -> Option<Layout> {
131 self.supports(value).then(|| self.read(value))
132 }
133}
134
135impl<C: Cpuid> Cpuid for &C {
136 fn read_raw(&self, leaf: u32, subleaf: u32) -> CpuidRawResult {
137 (*self).read_raw(leaf, subleaf)
138 }
139}
140
141#[derive(Clone, Copy, Debug)]
143pub struct DirectCpuid;
144
145#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
146mod x86_only {
147 #[cfg(target_arch = "x86_64")]
148 use core::arch::x86_64::{__cpuid_count, CpuidResult as ArchCpuidResult};
149
150 #[cfg(target_arch = "x86")]
151 use core::arch::x86::{__cpuid_count, CpuidResult as ArchCpuidResult};
152
153 use super::*;
154
155 impl Cpuid for DirectCpuid {
156 #[inline]
157 fn read_raw(&self, leaf: u32, subleaf: u32) -> CpuidRawResult {
158 let ArchCpuidResult { eax, ebx, ecx, edx } = __cpuid_count(leaf, subleaf);
159 CpuidRawResult { eax, ebx, ecx, edx }
160 }
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use crate::testing::x86::FakeCpuid;
168
169 const MAX_BASIC_LEAF: CpuidValue<0x0000_0000, 0x0, EAX, u32> = CpuidValue::new();
170 const FEATURE_LEAF_1_ECX: CpuidValue<0x0000_0001, 0x0, ECX, u32> = CpuidValue::new();
171 const FEATURE_LEAF_7_EBX: CpuidValue<0x0000_0007, 0x0, EBX, u32> = CpuidValue::new();
172
173 const MAX_HYPERVISOR_LEAF: CpuidValue<0x4000_0000, 0x0, EAX, u32> = CpuidValue::new();
174 const HYPERVISOR_LEAF_1_EAX: CpuidValue<0x4000_0001, 0x0, EAX, u32> = CpuidValue::new();
175 const HYPERVISOR_LEAF_2_EAX: CpuidValue<0x4000_0002, 0x0, EAX, u32> = CpuidValue::new();
176
177 const MAX_EXTENDED_LEAF: CpuidValue<0x8000_0000, 0x0, EAX, u32> = CpuidValue::new();
178 const AMD_EXTENDED_FEATURES_C: CpuidValue<0x8000_0001, 0x0, ECX, u32> = CpuidValue::new();
179 const AMD_CACHE_TOPOLOGY_A: CpuidValue<0x8000_001d, 0x0, EAX, u32> = CpuidValue::new();
180
181 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
183 #[test]
184 fn direct_cpuid() {
185 const CPUID_MAX_LEAF: CpuidValue<0x0, 0x0, EAX, u32> = CpuidValue::new();
186 println!("Maximum CPUID leaf number: {:#x}", DirectCpuid {}.read(CPUID_MAX_LEAF));
187 }
188
189 #[test]
190 fn cpuid_supports_basic_leaves() {
191 let mut cpuid = FakeCpuid::new();
192 cpuid.set(MAX_BASIC_LEAF, 1);
193 assert!(cpuid.supports(FEATURE_LEAF_1_ECX));
194 assert!(!cpuid.supports(FEATURE_LEAF_7_EBX));
195
196 assert_eq!(cpuid.try_read(FEATURE_LEAF_1_ECX), Some(0));
197 assert_eq!(cpuid.try_read(FEATURE_LEAF_7_EBX), None);
198 }
199
200 #[test]
201 fn cpuid_supports_hypervisor_leaves() {
202 let mut cpuid = FakeCpuid::new();
203 cpuid.set(MAX_HYPERVISOR_LEAF, 0x4000_0001);
204 assert!(cpuid.supports(HYPERVISOR_LEAF_1_EAX));
205
206 assert!(!cpuid.supports(HYPERVISOR_LEAF_2_EAX));
207 }
208
209 #[test]
210 fn cpuid_supports_extended_and_topology_leaves() {
211 let mut cpuid = FakeCpuid::new();
212 cpuid.set(MAX_EXTENDED_LEAF, 0x8000_001e);
213 assert!(!cpuid.supports(AMD_CACHE_TOPOLOGY_A));
215
216 cpuid.set(AMD_EXTENDED_FEATURES_C, 1 << 22);
218 assert!(cpuid.supports(AMD_CACHE_TOPOLOGY_A));
219 }
220}