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