xref: /aosp_15_r20/external/crosvm/x86_64/src/cpuid.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2017 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::arch::x86_64::CpuidResult;
6 use std::arch::x86_64::__cpuid;
7 use std::arch::x86_64::__cpuid_count;
8 use std::cmp;
9 use std::result;
10 
11 use devices::Apic;
12 use devices::IrqChipCap;
13 use devices::IrqChipX86_64;
14 use hypervisor::CpuConfigX86_64;
15 use hypervisor::CpuHybridType;
16 use hypervisor::CpuIdEntry;
17 use hypervisor::HypervisorCap;
18 use hypervisor::HypervisorX86_64;
19 use hypervisor::VcpuX86_64;
20 use remain::sorted;
21 use thiserror::Error;
22 
23 use crate::CpuManufacturer;
24 
25 #[sorted]
26 #[derive(Error, Debug, PartialEq, Eq)]
27 pub enum Error {
28     #[error("GetSupportedCpus ioctl failed: {0}")]
29     GetSupportedCpusFailed(base::Error),
30     #[error("SetSupportedCpus ioctl failed: {0}")]
31     SetSupportedCpusFailed(base::Error),
32 }
33 
34 pub type Result<T> = result::Result<T, Error>;
35 
36 // CPUID bits in ebx, ecx, and edx.
37 pub const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size.
38 pub const EBX_CLFLUSH_SIZE_SHIFT: u32 = 8; // Bytes flushed when executing CLFLUSH.
39 pub const EBX_CPU_COUNT_SHIFT: u32 = 16; // Index of this CPU.
40 pub const EBX_CPUID_SHIFT: u32 = 24; // Index of this CPU.
41 pub const ECX_EPB_SHIFT: u32 = 3; // "Energy Performance Bias" bit.
42 pub const ECX_X2APIC_SHIFT: u32 = 21; // APIC supports extended xAPIC (x2APIC) standard.
43 pub const ECX_TSC_DEADLINE_TIMER_SHIFT: u32 = 24; // TSC deadline mode of APIC timer.
44 pub const ECX_HYPERVISOR_SHIFT: u32 = 31; // Flag to be set when the cpu is running on a hypervisor.
45 pub const EDX_HTT_SHIFT: u32 = 28; // Hyper Threading Enabled.
46 pub const ECX_TOPO_TYPE_SHIFT: u32 = 8; // Topology Level type.
47 pub const ECX_TOPO_SMT_TYPE: u32 = 1; // SMT type.
48 pub const ECX_TOPO_CORE_TYPE: u32 = 2; // CORE type.
49 pub const ECX_HCFC_PERF_SHIFT: u32 = 0; // Presence of IA32_MPERF and IA32_APERF.
50 pub const EAX_CPU_CORES_SHIFT: u32 = 26; // Index of cpu cores in the same physical package.
51 pub const EDX_HYBRID_CPU_SHIFT: u32 = 15; // Hybrid. The processor is identified as a hybrid part.
52 pub const EAX_HWP_SHIFT: u32 = 7; // Intel Hardware P-states.
53 pub const EAX_HWP_NOTIFICATION_SHIFT: u32 = 8; // IA32_HWP_INTERRUPT MSR is supported
54 pub const EAX_HWP_EPP_SHIFT: u32 = 10; // HWP Energy Perf. Preference.
55 pub const EAX_ITMT_SHIFT: u32 = 14; // Intel Turbo Boost Max Technology 3.0 available.
56 pub const EAX_CORE_TEMP: u32 = 0; // Core Temperature
57 pub const EAX_PKG_TEMP: u32 = 6; // Package Temperature
58 pub const EAX_CORE_TYPE_SHIFT: u32 = 24; // Hybrid information. Hybrid core type.
59 
60 const EAX_CORE_TYPE_ATOM: u32 = 0x20; // Hybrid Atom CPU.
61 const EAX_CORE_TYPE_CORE: u32 = 0x40; // Hybrid Core CPU.
62 
63 /// All of the context required to emulate the CPUID instruction.
64 #[derive(Clone, Debug, PartialEq, Eq)]
65 pub struct CpuIdContext {
66     /// Id of the Vcpu associated with this context.
67     vcpu_id: usize,
68     /// The total number of vcpus on this VM.
69     cpu_count: usize,
70     /// Whether or not the IrqChip's APICs support X2APIC.
71     x2apic: bool,
72     /// Whether or not the IrqChip's APICs support a TSC deadline timer.
73     tsc_deadline_timer: bool,
74     /// The frequency at which the IrqChip's APICs run.
75     apic_frequency: u32,
76     /// The TSC frequency in Hz, if it could be determined.
77     tsc_frequency: Option<u64>,
78     /// CPU feature configurations.
79     cpu_config: CpuConfigX86_64,
80     /// __cpuid_count or a fake function for test.
81     cpuid_count: unsafe fn(u32, u32) -> CpuidResult,
82     /// __cpuid or a fake function for test.
83     cpuid: unsafe fn(u32) -> CpuidResult,
84 }
85 
86 impl CpuIdContext {
new( vcpu_id: usize, cpu_count: usize, irq_chip: Option<&dyn IrqChipX86_64>, cpu_config: CpuConfigX86_64, calibrated_tsc_leaf_required: bool, cpuid_count: unsafe fn(u32, u32) -> CpuidResult, cpuid: unsafe fn(u32) -> CpuidResult, ) -> CpuIdContext87     pub fn new(
88         vcpu_id: usize,
89         cpu_count: usize,
90         irq_chip: Option<&dyn IrqChipX86_64>,
91         cpu_config: CpuConfigX86_64,
92         calibrated_tsc_leaf_required: bool,
93         cpuid_count: unsafe fn(u32, u32) -> CpuidResult,
94         cpuid: unsafe fn(u32) -> CpuidResult,
95     ) -> CpuIdContext {
96         CpuIdContext {
97             vcpu_id,
98             cpu_count,
99             x2apic: irq_chip.map_or(false, |chip| chip.check_capability(IrqChipCap::X2Apic)),
100             tsc_deadline_timer: irq_chip.map_or(false, |chip| {
101                 chip.check_capability(IrqChipCap::TscDeadlineTimer)
102             }),
103             apic_frequency: irq_chip.map_or(Apic::frequency(), |chip| chip.lapic_frequency()),
104             tsc_frequency: if calibrated_tsc_leaf_required || cpu_config.force_calibrated_tsc_leaf {
105                 devices::tsc::tsc_frequency().ok()
106             } else {
107                 None
108             },
109             cpu_config,
110             cpuid_count,
111             cpuid,
112         }
113     }
114 }
115 
116 /// Adjust a CPUID instruction result to return values that work with crosvm.
117 ///
118 /// Given an input CpuIdEntry `entry`, which represents what the Hypervisor would normally return
119 /// for a given CPUID instruction result, adjust that result to reflect the capabilities of crosvm.
120 /// The `ctx` argument contains all of the Vm-specific and Vcpu-specific information required to
121 /// return the appropriate results.
adjust_cpuid(entry: &mut CpuIdEntry, ctx: &CpuIdContext)122 pub fn adjust_cpuid(entry: &mut CpuIdEntry, ctx: &CpuIdContext) {
123     match entry.function {
124         0 => {
125             if ctx.tsc_frequency.is_some() {
126                 // We add leaf 0x15 for the TSC frequency if it is available.
127                 entry.cpuid.eax = cmp::max(0x15, entry.cpuid.eax);
128             }
129         }
130         1 => {
131             // X86 hypervisor feature
132             if entry.index == 0 {
133                 entry.cpuid.ecx |= 1 << ECX_HYPERVISOR_SHIFT;
134             }
135             if ctx.x2apic {
136                 entry.cpuid.ecx |= 1 << ECX_X2APIC_SHIFT;
137             } else {
138                 entry.cpuid.ecx &= !(1 << ECX_X2APIC_SHIFT);
139             }
140             if ctx.tsc_deadline_timer {
141                 entry.cpuid.ecx |= 1 << ECX_TSC_DEADLINE_TIMER_SHIFT;
142             }
143 
144             if ctx.cpu_config.host_cpu_topology {
145                 entry.cpuid.ebx |= EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT;
146 
147                 // Expose HT flag to Guest.
148                 // SAFETY: trivially safe
149                 let result = unsafe { (ctx.cpuid)(entry.function) };
150                 entry.cpuid.edx |= result.edx & (1 << EDX_HTT_SHIFT);
151                 return;
152             }
153 
154             entry.cpuid.ebx = (ctx.vcpu_id << EBX_CPUID_SHIFT) as u32
155                 | (EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT);
156             if ctx.cpu_count > 1 {
157                 // This field is only valid if CPUID.1.EDX.HTT[bit 28]= 1.
158                 entry.cpuid.ebx |= (ctx.cpu_count as u32) << EBX_CPU_COUNT_SHIFT;
159                 // A value of 0 for HTT indicates there is only a single logical
160                 // processor in the package and software should assume only a
161                 // single APIC ID is reserved.
162                 entry.cpuid.edx |= 1 << EDX_HTT_SHIFT;
163             }
164         }
165         2 | // Cache and TLB Descriptor information
166         0x80000002 | 0x80000003 | 0x80000004 | // Processor Brand String
167         0x80000005 | 0x80000006 // L1 and L2 cache information
168             => entry.cpuid = {
169                 // SAFETY: trivially safe
170                 unsafe { (ctx.cpuid)(entry.function) }},
171         4 => {
172             entry.cpuid = {
173                 // SAFETY: trivially safe
174                 unsafe { (ctx.cpuid_count)(entry.function, entry.index) }};
175 
176             if ctx.cpu_config.host_cpu_topology {
177                 return;
178             }
179 
180             entry.cpuid.eax &= !0xFC000000;
181             if ctx.cpu_count > 1 {
182                 let cpu_cores = if ctx.cpu_config.no_smt {
183                     ctx.cpu_count as u32
184                 } else if ctx.cpu_count % 2 == 0 {
185                     (ctx.cpu_count >> 1) as u32
186                 } else {
187                     1
188                 };
189                 entry.cpuid.eax |= (cpu_cores - 1) << EAX_CPU_CORES_SHIFT;
190             }
191         }
192         6 => {
193             let result = {
194                 // SAFETY:
195                 // Safe because we pass 6 for this call and the host
196                 // supports the `cpuid` instruction
197                 unsafe { (ctx.cpuid)(entry.function) }};
198 
199             if ctx.cpu_config.enable_hwp {
200                 entry.cpuid.eax |= result.eax & (1 << EAX_HWP_SHIFT);
201                 entry.cpuid.eax |= result.eax & (1 << EAX_HWP_NOTIFICATION_SHIFT);
202                 entry.cpuid.eax |= result.eax & (1 << EAX_HWP_EPP_SHIFT);
203                 entry.cpuid.ecx |= result.ecx & (1 << ECX_EPB_SHIFT);
204 
205                 if ctx.cpu_config.itmt {
206                     entry.cpuid.eax |= result.eax & (1 << EAX_ITMT_SHIFT);
207                 }
208             }
209         }
210         7 => {
211             if ctx.cpu_config.host_cpu_topology && entry.index == 0 {
212                 // SAFETY:
213                 // Safe because we pass 7 and 0 for this call and the host supports the
214                 // `cpuid` instruction
215                 let result = unsafe { (ctx.cpuid_count)(entry.function, entry.index) };
216                 entry.cpuid.edx |= result.edx & (1 << EDX_HYBRID_CPU_SHIFT);
217             }
218             if ctx.cpu_config.hybrid_type.is_some() && entry.index == 0 {
219                 entry.cpuid.edx |= 1 << EDX_HYBRID_CPU_SHIFT;
220             }
221         }
222         0x15 => {
223             if let Some(tsc_freq) = ctx.tsc_frequency {
224                 // A calibrated TSC is required by the hypervisor or was forced by the user.
225                 entry.cpuid = devices::tsc::fake_tsc_frequency_cpuid(tsc_freq, ctx.apic_frequency);
226             }
227         }
228         0x1A => {
229             // Hybrid information leaf.
230             if ctx.cpu_config.host_cpu_topology {
231                 // SAFETY:
232                 // Safe because we pass 0x1A for this call and the host supports the
233                 // `cpuid` instruction
234                 entry.cpuid = unsafe { (ctx.cpuid)(entry.function) };
235             }
236             if let Some(hybrid) = &ctx.cpu_config.hybrid_type {
237                 match hybrid {
238                     CpuHybridType::Atom => {
239                         entry.cpuid.eax |= EAX_CORE_TYPE_ATOM << EAX_CORE_TYPE_SHIFT;
240                     }
241                     CpuHybridType::Core => {
242                         entry.cpuid.eax |= EAX_CORE_TYPE_CORE << EAX_CORE_TYPE_SHIFT;
243                     }
244                 }
245             }
246         }
247         0xB | 0x1F => {
248             if ctx.cpu_config.host_cpu_topology {
249                 return;
250             }
251             // Extended topology enumeration / V2 Extended topology enumeration
252             // NOTE: these will need to be split if any of the fields that differ between
253             // the two versions are to be set.
254             // On AMD, these leaves are not used, so it is currently safe to leave in.
255             entry.cpuid.edx = ctx.vcpu_id as u32; // x2APIC ID
256             if entry.index == 0 {
257                 if ctx.cpu_config.no_smt || (ctx.cpu_count == 1) {
258                     // Make it so that all VCPUs appear as different,
259                     // non-hyperthreaded cores on the same package.
260                     entry.cpuid.eax = 0; // Shift to get id of next level
261                     entry.cpuid.ebx = 1; // Number of logical cpus at this level
262                 } else if ctx.cpu_count % 2 == 0 {
263                     // Each core has 2 hyperthreads
264                     entry.cpuid.eax = 1; // Shift to get id of next level
265                     entry.cpuid.ebx = 2; // Number of logical cpus at this level
266                 } else {
267                     // One core contain all the cpu_count hyperthreads
268                     let cpu_bits: u32 = 32 - ((ctx.cpu_count - 1) as u32).leading_zeros();
269                     entry.cpuid.eax = cpu_bits; // Shift to get id of next level
270                     entry.cpuid.ebx = ctx.cpu_count as u32; // Number of logical cpus at this level
271                 }
272                 entry.cpuid.ecx = (ECX_TOPO_SMT_TYPE << ECX_TOPO_TYPE_SHIFT) | entry.index;
273             } else if entry.index == 1 {
274                 let cpu_bits: u32 = 32 - ((ctx.cpu_count - 1) as u32).leading_zeros();
275                 entry.cpuid.eax = cpu_bits;
276                 // Number of logical cpus at this level
277                 entry.cpuid.ebx = (ctx.cpu_count as u32) & 0xffff;
278                 entry.cpuid.ecx = (ECX_TOPO_CORE_TYPE << ECX_TOPO_TYPE_SHIFT) | entry.index;
279             } else {
280                 entry.cpuid.eax = 0;
281                 entry.cpuid.ebx = 0;
282                 entry.cpuid.ecx = 0;
283             }
284         }
285         _ => (),
286     }
287 }
288 
289 /// Adjust all the entries in `cpuid` based on crosvm's cpuid logic and `ctx`. Calls `adjust_cpuid`
290 /// on each entry in `cpuid`, and adds any entries that should exist and are missing from `cpuid`.
filter_cpuid(cpuid: &mut hypervisor::CpuId, ctx: &CpuIdContext)291 pub fn filter_cpuid(cpuid: &mut hypervisor::CpuId, ctx: &CpuIdContext) {
292     // Add an empty leaf 0x15 if we have a tsc_frequency and it's not in the current set of leaves.
293     // It will be filled with the appropriate frequency information by `adjust_cpuid`.
294     if ctx.tsc_frequency.is_some()
295         && !cpuid
296             .cpu_id_entries
297             .iter()
298             .any(|entry| entry.function == 0x15)
299     {
300         cpuid.cpu_id_entries.push(CpuIdEntry {
301             function: 0x15,
302             index: 0,
303             flags: 0,
304             cpuid: CpuidResult {
305                 eax: 0,
306                 ebx: 0,
307                 ecx: 0,
308                 edx: 0,
309             },
310         })
311     }
312 
313     let entries = &mut cpuid.cpu_id_entries;
314     for entry in entries.iter_mut() {
315         adjust_cpuid(entry, ctx);
316     }
317 }
318 
319 /// Sets up the cpuid entries for the given vcpu.  Can fail if there are too many CPUs specified or
320 /// if an ioctl returns an error.
321 ///
322 /// # Arguments
323 ///
324 /// * `hypervisor` - `HypervisorX86_64` impl for getting supported CPU IDs.
325 /// * `irq_chip` - `IrqChipX86_64` for adjusting appropriate IrqChip CPUID bits.
326 /// * `vcpu` - `VcpuX86_64` for setting CPU ID.
327 /// * `vcpu_id` - The vcpu index of `vcpu`.
328 /// * `cpu_config` - CPU feature configurations.
setup_cpuid( hypervisor: &dyn HypervisorX86_64, irq_chip: &dyn IrqChipX86_64, vcpu: &dyn VcpuX86_64, vcpu_id: usize, nrcpus: usize, cpu_config: CpuConfigX86_64, ) -> Result<()>329 pub fn setup_cpuid(
330     hypervisor: &dyn HypervisorX86_64,
331     irq_chip: &dyn IrqChipX86_64,
332     vcpu: &dyn VcpuX86_64,
333     vcpu_id: usize,
334     nrcpus: usize,
335     cpu_config: CpuConfigX86_64,
336 ) -> Result<()> {
337     let mut cpuid = hypervisor
338         .get_supported_cpuid()
339         .map_err(Error::GetSupportedCpusFailed)?;
340 
341     filter_cpuid(
342         &mut cpuid,
343         &CpuIdContext::new(
344             vcpu_id,
345             nrcpus,
346             Some(irq_chip),
347             cpu_config,
348             hypervisor.check_capability(HypervisorCap::CalibratedTscLeafRequired),
349             __cpuid_count,
350             __cpuid,
351         ),
352     );
353 
354     vcpu.set_cpuid(&cpuid)
355         .map_err(Error::SetSupportedCpusFailed)
356 }
357 
358 const MANUFACTURER_ID_FUNCTION: u32 = 0x00000000;
359 const AMD_EBX: u32 = u32::from_le_bytes([b'A', b'u', b't', b'h']);
360 const AMD_EDX: u32 = u32::from_le_bytes([b'e', b'n', b't', b'i']);
361 const AMD_ECX: u32 = u32::from_le_bytes([b'c', b'A', b'M', b'D']);
362 const INTEL_EBX: u32 = u32::from_le_bytes([b'G', b'e', b'n', b'u']);
363 const INTEL_EDX: u32 = u32::from_le_bytes([b'i', b'n', b'e', b'I']);
364 const INTEL_ECX: u32 = u32::from_le_bytes([b'n', b't', b'e', b'l']);
365 
cpu_manufacturer() -> CpuManufacturer366 pub fn cpu_manufacturer() -> CpuManufacturer {
367     // SAFETY:
368     // safe because MANUFACTURER_ID_FUNCTION is a well known cpuid function,
369     // and we own the result value afterwards.
370     let result = unsafe { __cpuid(MANUFACTURER_ID_FUNCTION) };
371     if result.ebx == AMD_EBX && result.edx == AMD_EDX && result.ecx == AMD_ECX {
372         return CpuManufacturer::Amd;
373     } else if result.ebx == INTEL_EBX && result.edx == INTEL_EDX && result.ecx == INTEL_ECX {
374         return CpuManufacturer::Intel;
375     }
376     CpuManufacturer::Unknown
377 }
378 
379 #[cfg(test)]
380 mod tests {
381     use super::*;
382 
383     #[test]
cpu_manufacturer_test()384     fn cpu_manufacturer_test() {
385         // this should be amd or intel. We don't support other processors for virtualization.
386         let manufacturer = cpu_manufacturer();
387         assert_ne!(manufacturer, CpuManufacturer::Unknown);
388     }
389 
390     #[test]
cpuid_copies_register()391     fn cpuid_copies_register() {
392         let fake_cpuid_count = |_function: u32, _index: u32| CpuidResult {
393             eax: 27,
394             ebx: 18,
395             ecx: 28,
396             edx: 18,
397         };
398         let fake_cpuid = |_function: u32| CpuidResult {
399             eax: 0,
400             ebx: 0,
401             ecx: 0,
402             edx: 0,
403         };
404         let cpu_config = CpuConfigX86_64 {
405             force_calibrated_tsc_leaf: false,
406             host_cpu_topology: true,
407             enable_hwp: false,
408             no_smt: false,
409             itmt: false,
410             hybrid_type: None,
411         };
412         let ctx = CpuIdContext {
413             vcpu_id: 0,
414             cpu_count: 0,
415             x2apic: false,
416             tsc_deadline_timer: false,
417             apic_frequency: 0,
418             tsc_frequency: None,
419             cpu_config,
420             cpuid_count: fake_cpuid_count,
421             cpuid: fake_cpuid,
422         };
423         let mut cpu_id_entry = CpuIdEntry {
424             function: 0x4,
425             index: 0,
426             flags: 0,
427             cpuid: CpuidResult {
428                 eax: 31,
429                 ebx: 41,
430                 ecx: 59,
431                 edx: 26,
432             },
433         };
434         adjust_cpuid(&mut cpu_id_entry, &ctx);
435         assert_eq!(cpu_id_entry.cpuid.eax, 27)
436     }
437 }
438