]>
Commit | Line | Data |
---|---|---|
d786a274 SR |
1 | package PVE::QemuServer::CPUConfig; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
6 | use PVE::JSONSchema; | |
7 | use PVE::QemuServer::Helpers qw(min_version); | |
8 | ||
9 | use base qw(Exporter); | |
10 | ||
11 | our @EXPORT_OK = qw( | |
12 | print_cpu_device | |
13 | get_cpu_options | |
14 | ); | |
15 | ||
16 | my $cpu_vendor_list = { | |
17 | # Intel CPUs | |
18 | 486 => 'GenuineIntel', | |
19 | pentium => 'GenuineIntel', | |
20 | pentium2 => 'GenuineIntel', | |
21 | pentium3 => 'GenuineIntel', | |
22 | coreduo => 'GenuineIntel', | |
23 | core2duo => 'GenuineIntel', | |
24 | Conroe => 'GenuineIntel', | |
25 | Penryn => 'GenuineIntel', | |
26 | Nehalem => 'GenuineIntel', | |
27 | 'Nehalem-IBRS' => 'GenuineIntel', | |
28 | Westmere => 'GenuineIntel', | |
29 | 'Westmere-IBRS' => 'GenuineIntel', | |
30 | SandyBridge => 'GenuineIntel', | |
31 | 'SandyBridge-IBRS' => 'GenuineIntel', | |
32 | IvyBridge => 'GenuineIntel', | |
33 | 'IvyBridge-IBRS' => 'GenuineIntel', | |
34 | Haswell => 'GenuineIntel', | |
35 | 'Haswell-IBRS' => 'GenuineIntel', | |
36 | 'Haswell-noTSX' => 'GenuineIntel', | |
37 | 'Haswell-noTSX-IBRS' => 'GenuineIntel', | |
38 | Broadwell => 'GenuineIntel', | |
39 | 'Broadwell-IBRS' => 'GenuineIntel', | |
40 | 'Broadwell-noTSX' => 'GenuineIntel', | |
41 | 'Broadwell-noTSX-IBRS' => 'GenuineIntel', | |
42 | 'Skylake-Client' => 'GenuineIntel', | |
43 | 'Skylake-Client-IBRS' => 'GenuineIntel', | |
bb84db9d | 44 | 'Skylake-Client-noTSX-IBRS' => 'GenuineIntel', |
d786a274 SR |
45 | 'Skylake-Server' => 'GenuineIntel', |
46 | 'Skylake-Server-IBRS' => 'GenuineIntel', | |
bb84db9d | 47 | 'Skylake-Server-noTSX-IBRS' => 'GenuineIntel', |
d786a274 | 48 | 'Cascadelake-Server' => 'GenuineIntel', |
bb84db9d | 49 | 'Cascadelake-Server-noTSX' => 'GenuineIntel', |
d786a274 | 50 | KnightsMill => 'GenuineIntel', |
257ae687 | 51 | 'Icelake-Client' => 'GenuineIntel', |
bb84db9d AD |
52 | 'Icelake-Client-noTSX' => 'GenuineIntel', |
53 | 'Icelake-Server' => 'GenuineIntel', | |
d0cdb1de | 54 | 'Icelake-Server-noTSX' => 'GenuineIntel', |
d786a274 SR |
55 | |
56 | # AMD CPUs | |
57 | athlon => 'AuthenticAMD', | |
58 | phenom => 'AuthenticAMD', | |
59 | Opteron_G1 => 'AuthenticAMD', | |
60 | Opteron_G2 => 'AuthenticAMD', | |
61 | Opteron_G3 => 'AuthenticAMD', | |
62 | Opteron_G4 => 'AuthenticAMD', | |
63 | Opteron_G5 => 'AuthenticAMD', | |
64 | EPYC => 'AuthenticAMD', | |
65 | 'EPYC-IBPB' => 'AuthenticAMD', | |
66 | ||
67 | # generic types, use vendor from host node | |
68 | host => 'default', | |
69 | kvm32 => 'default', | |
70 | kvm64 => 'default', | |
71 | qemu32 => 'default', | |
72 | qemu64 => 'default', | |
73 | max => 'default', | |
74 | }; | |
75 | ||
76 | my @supported_cpu_flags = ( | |
77 | 'pcid', | |
78 | 'spec-ctrl', | |
79 | 'ibpb', | |
80 | 'ssbd', | |
81 | 'virt-ssbd', | |
82 | 'amd-ssbd', | |
83 | 'amd-no-ssb', | |
84 | 'pdpe1gb', | |
85 | 'md-clear', | |
86 | 'hv-tlbflush', | |
87 | 'hv-evmcs', | |
88 | 'aes' | |
89 | ); | |
90 | my $cpu_flag = qr/[+-](@{[join('|', @supported_cpu_flags)]})/; | |
91 | ||
92 | our $cpu_fmt = { | |
93 | cputype => { | |
94 | description => "Emulated CPU type.", | |
95 | type => 'string', | |
96 | enum => [ sort { "\L$a" cmp "\L$b" } keys %$cpu_vendor_list ], | |
97 | default => 'kvm64', | |
98 | default_key => 1, | |
99 | }, | |
100 | hidden => { | |
101 | description => "Do not identify as a KVM virtual machine.", | |
102 | type => 'boolean', | |
103 | optional => 1, | |
104 | default => 0 | |
105 | }, | |
106 | 'hv-vendor-id' => { | |
107 | type => 'string', | |
108 | pattern => qr/[a-zA-Z0-9]{1,12}/, | |
109 | format_description => 'vendor-id', | |
110 | description => 'The Hyper-V vendor ID. Some drivers or programs inside Windows guests need a specific ID.', | |
111 | optional => 1, | |
112 | }, | |
113 | flags => { | |
114 | description => "List of additional CPU flags separated by ';'." | |
115 | . " Use '+FLAG' to enable, '-FLAG' to disable a flag." | |
116 | . " Currently supported flags: @{[join(', ', @supported_cpu_flags)]}.", | |
117 | format_description => '+FLAG[;-FLAG...]', | |
118 | type => 'string', | |
119 | pattern => qr/$cpu_flag(;$cpu_flag)*/, | |
120 | optional => 1, | |
121 | }, | |
122 | }; | |
123 | ||
124 | # Print a QEMU device node for a given VM configuration for hotplugging CPUs | |
125 | sub print_cpu_device { | |
126 | my ($conf, $id) = @_; | |
127 | ||
128 | my $kvm = $conf->{kvm} // 1; | |
129 | my $cpu = $kvm ? "kvm64" : "qemu64"; | |
130 | if (my $cputype = $conf->{cpu}) { | |
131 | my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype) | |
132 | or die "Cannot parse cpu description: $cputype\n"; | |
133 | $cpu = $cpuconf->{cputype}; | |
134 | } | |
135 | ||
136 | my $cores = $conf->{cores} || 1; | |
137 | ||
138 | my $current_core = ($id - 1) % $cores; | |
139 | my $current_socket = int(($id - 1 - $current_core)/$cores); | |
140 | ||
141 | return "$cpu-x86_64-cpu,id=cpu$id,socket-id=$current_socket,core-id=$current_core,thread-id=0"; | |
142 | } | |
143 | ||
144 | # Calculate QEMU's '-cpu' argument from a given VM configuration | |
145 | sub get_cpu_options { | |
146 | my ($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough) = @_; | |
147 | ||
148 | my $cpuFlags = []; | |
149 | my $ostype = $conf->{ostype}; | |
150 | ||
151 | my $cpu = $kvm ? "kvm64" : "qemu64"; | |
152 | if ($arch eq 'aarch64') { | |
153 | $cpu = 'cortex-a57'; | |
154 | } | |
155 | my $hv_vendor_id; | |
156 | if (my $cputype = $conf->{cpu}) { | |
157 | my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype) | |
158 | or die "Cannot parse cpu description: $cputype\n"; | |
159 | $cpu = $cpuconf->{cputype}; | |
160 | $kvm_off = 1 if $cpuconf->{hidden}; | |
161 | $hv_vendor_id = $cpuconf->{'hv-vendor-id'}; | |
162 | ||
163 | if (defined(my $flags = $cpuconf->{flags})) { | |
164 | push @$cpuFlags, split(";", $flags); | |
165 | } | |
166 | } | |
167 | ||
168 | push @$cpuFlags , '+lahf_lm' if $cpu eq 'kvm64' && $arch eq 'x86_64'; | |
169 | ||
170 | push @$cpuFlags , '-x2apic' if $ostype && $ostype eq 'solaris'; | |
171 | ||
172 | push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32'; | |
173 | ||
174 | push @$cpuFlags, '-rdtscp' if $cpu =~ m/^Opteron/; | |
175 | ||
176 | if (min_version($machine_version, 2, 3) && $arch eq 'x86_64') { | |
177 | ||
178 | push @$cpuFlags , '+kvm_pv_unhalt' if $kvm; | |
179 | push @$cpuFlags , '+kvm_pv_eoi' if $kvm; | |
180 | } | |
181 | ||
182 | add_hyperv_enlightenments($cpuFlags, $winversion, $machine_version, $conf->{bios}, $gpu_passthrough, $hv_vendor_id) if $kvm; | |
183 | ||
184 | push @$cpuFlags, 'enforce' if $cpu ne 'host' && $kvm && $arch eq 'x86_64'; | |
185 | ||
186 | push @$cpuFlags, 'kvm=off' if $kvm_off; | |
187 | ||
188 | if (my $cpu_vendor = $cpu_vendor_list->{$cpu}) { | |
189 | push @$cpuFlags, "vendor=${cpu_vendor}" | |
190 | if $cpu_vendor ne 'default'; | |
191 | } elsif ($arch ne 'aarch64') { | |
192 | die "internal error"; # should not happen | |
193 | } | |
194 | ||
195 | $cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags); | |
196 | ||
197 | return ('-cpu', $cpu); | |
198 | } | |
199 | ||
200 | sub add_hyperv_enlightenments { | |
201 | my ($cpuFlags, $winversion, $machine_version, $bios, $gpu_passthrough, $hv_vendor_id) = @_; | |
202 | ||
203 | return if $winversion < 6; | |
204 | return if $bios && $bios eq 'ovmf' && $winversion < 8; | |
205 | ||
206 | if ($gpu_passthrough || defined($hv_vendor_id)) { | |
207 | $hv_vendor_id //= 'proxmox'; | |
208 | push @$cpuFlags , "hv_vendor_id=$hv_vendor_id"; | |
209 | } | |
210 | ||
211 | if (min_version($machine_version, 2, 3)) { | |
212 | push @$cpuFlags , 'hv_spinlocks=0x1fff'; | |
213 | push @$cpuFlags , 'hv_vapic'; | |
214 | push @$cpuFlags , 'hv_time'; | |
215 | } else { | |
216 | push @$cpuFlags , 'hv_spinlocks=0xffff'; | |
217 | } | |
218 | ||
219 | if (min_version($machine_version, 2, 6)) { | |
220 | push @$cpuFlags , 'hv_reset'; | |
221 | push @$cpuFlags , 'hv_vpindex'; | |
222 | push @$cpuFlags , 'hv_runtime'; | |
223 | } | |
224 | ||
225 | if ($winversion >= 7) { | |
226 | push @$cpuFlags , 'hv_relaxed'; | |
227 | ||
228 | if (min_version($machine_version, 2, 12)) { | |
229 | push @$cpuFlags , 'hv_synic'; | |
230 | push @$cpuFlags , 'hv_stimer'; | |
231 | } | |
232 | ||
233 | if (min_version($machine_version, 3, 1)) { | |
234 | push @$cpuFlags , 'hv_ipi'; | |
235 | } | |
236 | } | |
237 | } | |
238 | ||
239 | 1; |