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