• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
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::{__cpuid, __cpuid_count};
6 use std::result;
7 
8 use devices::{IrqChipCap, IrqChipX86_64};
9 use hypervisor::{HypervisorX86_64, VcpuX86_64};
10 use remain::sorted;
11 use thiserror::Error;
12 
13 #[sorted]
14 #[derive(Error, Debug, PartialEq)]
15 pub enum Error {
16     #[error("GetSupportedCpus ioctl failed: {0}")]
17     GetSupportedCpusFailed(base::Error),
18     #[error("SetSupportedCpus ioctl failed: {0}")]
19     SetSupportedCpusFailed(base::Error),
20 }
21 
22 pub type Result<T> = result::Result<T, Error>;
23 
24 // CPUID bits in ebx, ecx, and edx.
25 const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size.
26 const EBX_CLFLUSH_SIZE_SHIFT: u32 = 8; // Bytes flushed when executing CLFLUSH.
27 const EBX_CPU_COUNT_SHIFT: u32 = 16; // Index of this CPU.
28 const EBX_CPUID_SHIFT: u32 = 24; // Index of this CPU.
29 const ECX_EPB_SHIFT: u32 = 3; // "Energy Performance Bias" bit.
30 const ECX_X2APIC_SHIFT: u32 = 21; // APIC supports extended xAPIC (x2APIC) standard.
31 const ECX_TSC_DEADLINE_TIMER_SHIFT: u32 = 24; // TSC deadline mode of APIC timer.
32 const ECX_HYPERVISOR_SHIFT: u32 = 31; // Flag to be set when the cpu is running on a hypervisor.
33 const EDX_HTT_SHIFT: u32 = 28; // Hyper Threading Enabled.
34 const ECX_TOPO_TYPE_SHIFT: u32 = 8; // Topology Level type.
35 const ECX_TOPO_SMT_TYPE: u32 = 1; // SMT type.
36 const ECX_TOPO_CORE_TYPE: u32 = 2; // CORE type.
37 const EAX_CPU_CORES_SHIFT: u32 = 26; // Index of cpu cores in the same physical package.
38 const EDX_HYBRID_CPU_SHIFT: u32 = 15; // Hybrid. The processor is identified as a hybrid part.
39 
filter_cpuid( vcpu_id: usize, cpu_count: usize, cpuid: &mut hypervisor::CpuId, irq_chip: &dyn IrqChipX86_64, no_smt: bool, host_cpu_topology: bool, )40 fn filter_cpuid(
41     vcpu_id: usize,
42     cpu_count: usize,
43     cpuid: &mut hypervisor::CpuId,
44     irq_chip: &dyn IrqChipX86_64,
45     no_smt: bool,
46     host_cpu_topology: bool,
47 ) {
48     let entries = &mut cpuid.cpu_id_entries;
49 
50     for entry in entries {
51         match entry.function {
52             1 => {
53                 // X86 hypervisor feature
54                 if entry.index == 0 {
55                     entry.ecx |= 1 << ECX_HYPERVISOR_SHIFT;
56                 }
57                 if irq_chip.check_capability(IrqChipCap::X2Apic) {
58                     entry.ecx |= 1 << ECX_X2APIC_SHIFT;
59                 } else {
60                     entry.ecx &= !(1 << ECX_X2APIC_SHIFT);
61                 }
62                 if irq_chip.check_capability(IrqChipCap::TscDeadlineTimer) {
63                     entry.ecx |= 1 << ECX_TSC_DEADLINE_TIMER_SHIFT;
64                 }
65 
66                 if host_cpu_topology {
67                     entry.ebx |= EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT;
68 
69                     // Expose HT flag to Guest.
70                     let result = unsafe { __cpuid(entry.function) };
71                     entry.edx |= result.edx & (1 << EDX_HTT_SHIFT);
72                     continue;
73                 }
74 
75                 entry.ebx = (vcpu_id << EBX_CPUID_SHIFT) as u32
76                     | (EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT);
77                 if cpu_count > 1 {
78                     // This field is only valid if CPUID.1.EDX.HTT[bit 28]= 1.
79                     entry.ebx |= (cpu_count as u32) << EBX_CPU_COUNT_SHIFT;
80                     // A value of 0 for HTT indicates there is only a single logical
81                     // processor in the package and software should assume only a
82                     // single APIC ID is reserved.
83                     entry.edx |= 1 << EDX_HTT_SHIFT;
84                 }
85             }
86             2 | // Cache and TLB Descriptor information
87             0x80000002 | 0x80000003 | 0x80000004 | // Processor Brand String
88             0x80000005 | 0x80000006 // L1 and L2 cache information
89               => unsafe {
90                 let result = __cpuid(entry.function);
91                 entry.eax = result.eax;
92                 entry.ebx = result.ebx;
93                 entry.ecx = result.ecx;
94                 entry.edx = result.edx;
95             },
96             4 => {
97                 unsafe {
98                     let result = __cpuid_count(entry.function, entry.index);
99                     entry.eax = result.eax;
100                     entry.ebx = result.ebx;
101                     entry.ecx = result.ecx;
102                     entry.edx = result.edx;
103                 }
104 
105                 if host_cpu_topology {
106                     continue;
107                 }
108 
109                 entry.eax &= !0xFC000000;
110                 if cpu_count > 1 {
111                     let cpu_cores = if no_smt {
112                         cpu_count as u32
113                     } else if cpu_count % 2 == 0 {
114                         (cpu_count >> 1) as u32
115                     } else {
116                         1
117                     };
118                     entry.eax |= (cpu_cores - 1) << EAX_CPU_CORES_SHIFT;
119                 }
120             }
121             6 => {
122                 // Clear X86 EPB feature.  No frequency selection in the hypervisor.
123                 entry.ecx &= !(1 << ECX_EPB_SHIFT);
124             }
125             7 => {
126                 if host_cpu_topology && entry.index == 0 {
127                     // Safe because we pass 7 and 0 for this call and the host supports the
128                     // `cpuid` instruction
129                     let result = unsafe { __cpuid_count(entry.function, entry.index) };
130                     entry.edx |= result.edx & (1 << EDX_HYBRID_CPU_SHIFT);
131                 }
132             }
133             0x1A => {
134                 // Hybrid information leaf.
135                 if host_cpu_topology {
136                     // Safe because we pass 0x1A for this call and the host supports the
137                     // `cpuid` instruction
138                     let result = unsafe { __cpuid(entry.function) };
139                     entry.eax = result.eax;
140                     entry.ebx = result.ebx;
141                     entry.ecx = result.ecx;
142                     entry.edx = result.edx;
143                 }
144             }
145             0xB | 0x1F => {
146                 if host_cpu_topology {
147                     continue;
148                 }
149                 // Extended topology enumeration / V2 Extended topology enumeration
150                 // NOTE: these will need to be split if any of the fields that differ between
151                 // the two versions are to be set.
152                 entry.edx = vcpu_id as u32; // x2APIC ID
153                 if entry.index == 0 {
154                     if no_smt || (cpu_count == 1) {
155                         // Make it so that all VCPUs appear as different,
156                         // non-hyperthreaded cores on the same package.
157                         entry.eax = 0; // Shift to get id of next level
158                         entry.ebx = 1; // Number of logical cpus at this level
159                     } else if cpu_count % 2 == 0 {
160                         // Each core has 2 hyperthreads
161                         entry.eax = 1; // Shift to get id of next level
162                         entry.ebx = 2; // Number of logical cpus at this level
163                     } else {
164                         // One core contain all the cpu_count hyperthreads
165                         let cpu_bits: u32 = 32 - ((cpu_count - 1) as u32).leading_zeros();
166                         entry.eax = cpu_bits; // Shift to get id of next level
167                         entry.ebx = cpu_count as u32; // Number of logical cpus at this level
168                     }
169                     entry.ecx = (ECX_TOPO_SMT_TYPE << ECX_TOPO_TYPE_SHIFT) | entry.index;
170                 } else if entry.index == 1 {
171                     let cpu_bits: u32 = 32 - ((cpu_count - 1) as u32).leading_zeros();
172                     entry.eax = cpu_bits;
173                     entry.ebx = (cpu_count as u32) & 0xffff; // Number of logical cpus at this level
174                     entry.ecx = (ECX_TOPO_CORE_TYPE << ECX_TOPO_TYPE_SHIFT) | entry.index;
175                 } else {
176                     entry.eax = 0;
177                     entry.ebx = 0;
178                     entry.ecx = 0;
179                 }
180             }
181             _ => (),
182         }
183     }
184 }
185 
186 /// Sets up the cpuid entries for the given vcpu.  Can fail if there are too many CPUs specified or
187 /// if an ioctl returns an error.
188 ///
189 /// # Arguments
190 ///
191 /// * `hypervisor` - `HypervisorX86_64` impl for getting supported CPU IDs.
192 /// * `vcpu` - `VcpuX86_64` for setting CPU ID.
193 /// * `vcpu_id` - The vcpu index of `vcpu`.
194 /// * `nrcpus` - The number of vcpus being used by this VM.
195 /// * `no_smt` - The flag indicates whether vCPUs supports SMT.
196 /// * `host_cpu_topology` - The flag indicates whether vCPUs use mirror CPU topology.
setup_cpuid( hypervisor: &dyn HypervisorX86_64, irq_chip: &dyn IrqChipX86_64, vcpu: &dyn VcpuX86_64, vcpu_id: usize, nrcpus: usize, no_smt: bool, host_cpu_topology: bool, ) -> Result<()>197 pub fn setup_cpuid(
198     hypervisor: &dyn HypervisorX86_64,
199     irq_chip: &dyn IrqChipX86_64,
200     vcpu: &dyn VcpuX86_64,
201     vcpu_id: usize,
202     nrcpus: usize,
203     no_smt: bool,
204     host_cpu_topology: bool,
205 ) -> Result<()> {
206     let mut cpuid = hypervisor
207         .get_supported_cpuid()
208         .map_err(Error::GetSupportedCpusFailed)?;
209 
210     filter_cpuid(
211         vcpu_id,
212         nrcpus,
213         &mut cpuid,
214         irq_chip,
215         no_smt,
216         host_cpu_topology,
217     );
218 
219     vcpu.set_cpuid(&cpuid)
220         .map_err(Error::SetSupportedCpusFailed)
221 }
222 
223 #[cfg(test)]
224 mod tests {
225     use super::*;
226     use hypervisor::{CpuIdEntry, ProtectionType};
227 
228     #[test]
feature_and_vendor_name()229     fn feature_and_vendor_name() {
230         let mut cpuid = hypervisor::CpuId::new(2);
231         let guest_mem =
232             vm_memory::GuestMemory::new(&[(vm_memory::GuestAddress(0), 0x10000)]).unwrap();
233         let kvm = hypervisor::kvm::Kvm::new().unwrap();
234         let vm = hypervisor::kvm::KvmVm::new(&kvm, guest_mem, ProtectionType::Unprotected).unwrap();
235         let irq_chip = devices::KvmKernelIrqChip::new(vm, 1).unwrap();
236 
237         let entries = &mut cpuid.cpu_id_entries;
238         entries.push(CpuIdEntry {
239             function: 0,
240             ..Default::default()
241         });
242         entries.push(CpuIdEntry {
243             function: 1,
244             ecx: 0x10,
245             edx: 0,
246             ..Default::default()
247         });
248         filter_cpuid(1, 2, &mut cpuid, &irq_chip, false, false);
249 
250         let entries = &mut cpuid.cpu_id_entries;
251         assert_eq!(entries[0].function, 0);
252         assert_eq!(1, (entries[1].ebx >> EBX_CPUID_SHIFT) & 0x000000ff);
253         assert_eq!(2, (entries[1].ebx >> EBX_CPU_COUNT_SHIFT) & 0x000000ff);
254         assert_eq!(
255             EBX_CLFLUSH_CACHELINE,
256             (entries[1].ebx >> EBX_CLFLUSH_SIZE_SHIFT) & 0x000000ff
257         );
258         assert_ne!(0, entries[1].ecx & (1 << ECX_HYPERVISOR_SHIFT));
259         assert_ne!(0, entries[1].edx & (1 << EDX_HTT_SHIFT));
260     }
261 }
262