]> git.proxmox.com Git - qemu-server.git/blame - PVE/QemuServer/CPUConfig.pm
schema: mention that migration with VNC clipboard is not yet supported
[qemu-server.git] / PVE / QemuServer / CPUConfig.pm
CommitLineData
d786a274
SR
1package PVE::QemuServer::CPUConfig;
2
3use strict;
4use warnings;
5
6use PVE::JSONSchema;
b3e89488 7use PVE::Cluster qw(cfs_register_file cfs_read_file);
d786a274
SR
8use PVE::QemuServer::Helpers qw(min_version);
9
b3e89488 10use base qw(PVE::SectionConfig Exporter);
d786a274
SR
11
12our @EXPORT_OK = qw(
13print_cpu_device
14get_cpu_options
15);
16
b3e89488
SR
17# under certain race-conditions, this module might be loaded before pve-cluster
18# has started completely, so ensure we don't prevent the FUSE mount with our dir
1dbe979c 19if (PVE::Cluster::check_cfs_is_mounted(1)) {
b3e89488
SR
20 mkdir "/etc/pve/virtual-guest";
21}
22
23my $default_filename = "virtual-guest/cpu-models.conf";
d1901fe2
TL
24cfs_register_file(
25 $default_filename,
26 sub { PVE::QemuServer::CPUConfig->parse_config(@_); },
27 sub { PVE::QemuServer::CPUConfig->write_config(@_); },
28);
b3e89488
SR
29
30sub load_custom_model_conf {
31 return cfs_read_file($default_filename);
32}
33
1359e23f
AD
34#builtin models : reported-model is mandatory
35my $builtin_models = {
36 'x86-64-v2' => {
37 'reported-model' => 'qemu64',
38 flags => "+popcnt;+pni;+sse4.1;+sse4.2;+ssse3",
39 },
40 'x86-64-v2-AES' => {
41 'reported-model' => 'qemu64',
42 flags => "+aes;+popcnt;+pni;+sse4.1;+sse4.2;+ssse3",
43 },
44 'x86-64-v3' => {
45 'reported-model' => 'qemu64',
46 flags => "+aes;+popcnt;+pni;+sse4.1;+sse4.2;+ssse3;+avx;+avx2;+bmi1;+bmi2;+f16c;+fma;+abm;+movbe;+xsave",
47 },
48 'x86-64-v4' => {
49 'reported-model' => 'qemu64',
50 flags => "+aes;+popcnt;+pni;+sse4.1;+sse4.2;+ssse3;+avx;+avx2;+bmi1;+bmi2;+f16c;+fma;+abm;+movbe;+xsave;+avx512f;+avx512bw;+avx512cd;+avx512dq;+avx512vl",
51 },
52};
53
0d6962f9
TL
54my $depreacated_cpu_map = {
55 # there never was such a client CPU, so map it to the server one for backward compat
56 'Icelake-Client' => 'Icelake-Server',
57 'Icelake-Client-noTSX' => 'Icelake-Server-noTSX',
58};
59
d786a274
SR
60my $cpu_vendor_list = {
61 # Intel CPUs
62 486 => 'GenuineIntel',
63 pentium => 'GenuineIntel',
64 pentium2 => 'GenuineIntel',
65 pentium3 => 'GenuineIntel',
66 coreduo => 'GenuineIntel',
67 core2duo => 'GenuineIntel',
68 Conroe => 'GenuineIntel',
69 Penryn => 'GenuineIntel',
70 Nehalem => 'GenuineIntel',
71 'Nehalem-IBRS' => 'GenuineIntel',
72 Westmere => 'GenuineIntel',
73 'Westmere-IBRS' => 'GenuineIntel',
74 SandyBridge => 'GenuineIntel',
75 'SandyBridge-IBRS' => 'GenuineIntel',
76 IvyBridge => 'GenuineIntel',
77 'IvyBridge-IBRS' => 'GenuineIntel',
78 Haswell => 'GenuineIntel',
79 'Haswell-IBRS' => 'GenuineIntel',
80 'Haswell-noTSX' => 'GenuineIntel',
81 'Haswell-noTSX-IBRS' => 'GenuineIntel',
82 Broadwell => 'GenuineIntel',
83 'Broadwell-IBRS' => 'GenuineIntel',
84 'Broadwell-noTSX' => 'GenuineIntel',
85 'Broadwell-noTSX-IBRS' => 'GenuineIntel',
86 'Skylake-Client' => 'GenuineIntel',
87 'Skylake-Client-IBRS' => 'GenuineIntel',
bb84db9d 88 'Skylake-Client-noTSX-IBRS' => 'GenuineIntel',
c961ab6d 89 'Skylake-Client-v4' => 'GenuineIntel',
d786a274
SR
90 'Skylake-Server' => 'GenuineIntel',
91 'Skylake-Server-IBRS' => 'GenuineIntel',
bb84db9d 92 'Skylake-Server-noTSX-IBRS' => 'GenuineIntel',
c961ab6d
AD
93 'Skylake-Server-v4' => 'GenuineIntel',
94 'Skylake-Server-v5' => 'GenuineIntel',
d786a274 95 'Cascadelake-Server' => 'GenuineIntel',
c961ab6d 96 'Cascadelake-Server-v2' => 'GenuineIntel',
bb84db9d 97 'Cascadelake-Server-noTSX' => 'GenuineIntel',
c961ab6d
AD
98 'Cascadelake-Server-v4' => 'GenuineIntel',
99 'Cascadelake-Server-v5' => 'GenuineIntel',
100 'Cooperlake' => 'GenuineIntel',
101 'Cooperlake-v2' => 'GenuineIntel',
d786a274 102 KnightsMill => 'GenuineIntel',
0d6962f9
TL
103 'Icelake-Client' => 'GenuineIntel', # depreacated, removed with QEMU 7.1
104 'Icelake-Client-noTSX' => 'GenuineIntel', # depreacated, removed with QEMU 7.1
bb84db9d 105 'Icelake-Server' => 'GenuineIntel',
d0cdb1de 106 'Icelake-Server-noTSX' => 'GenuineIntel',
c961ab6d
AD
107 'Icelake-Server-v3' => 'GenuineIntel',
108 'Icelake-Server-v4' => 'GenuineIntel',
109 'Icelake-Server-v5' => 'GenuineIntel',
110 'Icelake-Server-v6' => 'GenuineIntel',
111 'SapphireRapids' => 'GenuineIntel',
d786a274
SR
112
113 # AMD CPUs
114 athlon => 'AuthenticAMD',
115 phenom => 'AuthenticAMD',
116 Opteron_G1 => 'AuthenticAMD',
117 Opteron_G2 => 'AuthenticAMD',
118 Opteron_G3 => 'AuthenticAMD',
119 Opteron_G4 => 'AuthenticAMD',
120 Opteron_G5 => 'AuthenticAMD',
121 EPYC => 'AuthenticAMD',
122 'EPYC-IBPB' => 'AuthenticAMD',
c961ab6d 123 'EPYC-v3' => 'AuthenticAMD',
8cea210f 124 'EPYC-Rome' => 'AuthenticAMD',
c961ab6d 125 'EPYC-Rome-v2' => 'AuthenticAMD',
9b1971c5 126 'EPYC-Milan' => 'AuthenticAMD',
d786a274
SR
127
128 # generic types, use vendor from host node
129 host => 'default',
130 kvm32 => 'default',
131 kvm64 => 'default',
132 qemu32 => 'default',
133 qemu64 => 'default',
134 max => 'default',
135};
136
137my @supported_cpu_flags = (
138 'pcid',
139 'spec-ctrl',
140 'ibpb',
141 'ssbd',
142 'virt-ssbd',
143 'amd-ssbd',
144 'amd-no-ssb',
145 'pdpe1gb',
146 'md-clear',
147 'hv-tlbflush',
148 'hv-evmcs',
149 'aes'
150);
5d008ad3
SR
151my $cpu_flag_supported_re = qr/([+-])(@{[join('|', @supported_cpu_flags)]})/;
152my $cpu_flag_any_re = qr/([+-])([a-zA-Z0-9\-_\.]+)/;
d786a274 153
c15b5971 154our $qemu_cmdline_cpu_re = qr/^((?>[+-]?[\w\-\._=]+,?)+)$/;
58c64ad5 155
5d008ad3 156my $cpu_fmt = {
d786a274 157 cputype => {
b3e89488 158 description => "Emulated CPU type. Can be default or custom name (custom model names must be prefixed with 'custom-').",
d786a274 159 type => 'string',
b3e89488 160 format_description => 'string',
d786a274
SR
161 default => 'kvm64',
162 default_key => 1,
b3e89488
SR
163 optional => 1,
164 },
165 'reported-model' => {
166 description => "CPU model and vendor to report to the guest. Must be a QEMU/KVM supported model."
11f9264f 167 ." Only valid for custom CPU model definitions, default models will always report themselves to the guest OS.",
b3e89488
SR
168 type => 'string',
169 enum => [ sort { lc("$a") cmp lc("$b") } keys %$cpu_vendor_list ],
170 default => 'kvm64',
171 optional => 1,
d786a274
SR
172 },
173 hidden => {
174 description => "Do not identify as a KVM virtual machine.",
175 type => 'boolean',
176 optional => 1,
177 default => 0
178 },
179 'hv-vendor-id' => {
180 type => 'string',
181 pattern => qr/[a-zA-Z0-9]{1,12}/,
182 format_description => 'vendor-id',
183 description => 'The Hyper-V vendor ID. Some drivers or programs inside Windows guests need a specific ID.',
184 optional => 1,
185 },
186 flags => {
11f9264f
TL
187 description => "List of additional CPU flags separated by ';'. Use '+FLAG' to enable,"
188 ." '-FLAG' to disable a flag. Custom CPU models can specify any flag supported by"
189 ." QEMU/KVM, VM-specific flags must be from the following set for security reasons: "
190 . join(', ', @supported_cpu_flags),
d786a274
SR
191 format_description => '+FLAG[;-FLAG...]',
192 type => 'string',
5d008ad3 193 pattern => qr/$cpu_flag_any_re(;$cpu_flag_any_re)*/,
d786a274
SR
194 optional => 1,
195 },
9f9792d3
SR
196 'phys-bits' => {
197 type => 'string',
198 format => 'pve-phys-bits',
7b8c4de3 199 format_description => '8-64|host',
11f9264f
TL
200 description => "The physical memory address bits that are reported to the guest OS. Should"
201 ." be smaller or equal to the host's. Set to 'host' to use value from host CPU, but"
202 ." note that doing so will break live migration to CPUs with other values.",
9f9792d3
SR
203 optional => 1,
204 },
d786a274
SR
205};
206
9f9792d3
SR
207PVE::JSONSchema::register_format('pve-phys-bits', \&parse_phys_bits);
208sub parse_phys_bits {
209 my ($str, $noerr) = @_;
210
211 my $err_msg = "value must be an integer between 8 and 64 or 'host'\n";
212
213 if ($str !~ m/^(host|\d{1,2})$/) {
214 die $err_msg if !$noerr;
d1c1af4b 215 return;
9f9792d3
SR
216 }
217
218 if ($str =~ m/^\d+$/ && (int($str) < 8 || int($str) > 64)) {
219 die $err_msg if !$noerr;
d1c1af4b 220 return;
9f9792d3
SR
221 }
222
223 return $str;
224}
225
5d008ad3
SR
226# $cpu_fmt describes both the CPU config passed as part of a VM config, as well
227# as the definition of a custom CPU model. There are some slight differences
7b8c4de3
SR
228# though, which we catch in the custom validation functions below.
229PVE::JSONSchema::register_format('pve-cpu-conf', $cpu_fmt, \&validate_cpu_conf);
230sub validate_cpu_conf {
231 my ($cpu) = @_;
05eae0f2 232 # required, but can't be forced in schema since it's encoded in section header for custom models
7b8c4de3 233 die "CPU is missing cputype\n" if !$cpu->{cputype};
b2e813a6 234 return $cpu;
5d008ad3 235}
7b8c4de3
SR
236PVE::JSONSchema::register_format('pve-vm-cpu-conf', $cpu_fmt, \&validate_vm_cpu_conf);
237sub validate_vm_cpu_conf {
238 my ($cpu) = @_;
5d008ad3 239
7b8c4de3 240 validate_cpu_conf($cpu);
5d008ad3
SR
241
242 my $cputype = $cpu->{cputype};
243
244 # a VM-specific config is only valid if the cputype exists
245 if (is_custom_model($cputype)) {
7b8c4de3
SR
246 # dies on unknown model
247 get_custom_model($cputype);
5d008ad3 248 } else {
7b8c4de3 249 die "Built-in cputype '$cputype' is not defined (missing 'custom-' prefix?)\n"
1359e23f 250 if !defined($cpu_vendor_list->{$cputype}) && !defined($builtin_models->{$cputype});
5d008ad3
SR
251 }
252
253 # in a VM-specific config, certain properties are limited/forbidden
254
7b8c4de3 255 die "VM-specific CPU flags must be a subset of: @{[join(', ', @supported_cpu_flags)]}\n"
4893f9b9 256 if ($cpu->{flags} && $cpu->{flags} !~ m/^$cpu_flag_supported_re(;$cpu_flag_supported_re)*$/);
5d008ad3
SR
257
258 die "Property 'reported-model' not allowed in VM-specific CPU config.\n"
259 if defined($cpu->{'reported-model'});
260
261 return $cpu;
262}
263
b3e89488
SR
264# Section config settings
265my $defaultData = {
266 # shallow copy, since SectionConfig modifies propertyList internally
267 propertyList => { %$cpu_fmt },
268};
269
270sub private {
271 return $defaultData;
272}
273
274sub options {
275 return { %$cpu_fmt };
276}
277
278sub type {
279 return 'cpu-model';
280}
281
282sub parse_section_header {
283 my ($class, $line) = @_;
284
285 my ($type, $sectionId, $errmsg, $config) =
286 $class->SUPER::parse_section_header($line);
287
d1c1af4b 288 return if !$type;
b3e89488
SR
289 return ($type, $sectionId, $errmsg, {
290 # name is given by section header, and we can always prepend 'custom-'
291 # since we're reading the custom CPU file
292 cputype => "custom-$sectionId",
293 });
294}
295
296sub write_config {
297 my ($class, $filename, $cfg) = @_;
298
299 mkdir "/etc/pve/virtual-guest";
300
301 for my $model (keys %{$cfg->{ids}}) {
302 my $model_conf = $cfg->{ids}->{$model};
303
304 die "internal error: tried saving built-in CPU model (or missing prefix): $model_conf->{cputype}\n"
305 if !is_custom_model($model_conf->{cputype});
306
307 die "internal error: tried saving custom cpumodel with cputype (ignoring prefix: $model_conf->{cputype}) not equal to \$cfg->ids entry ($model)\n"
308 if "custom-$model" ne $model_conf->{cputype};
309
310 # saved in section header
311 delete $model_conf->{cputype};
312 }
313
314 $class->SUPER::write_config($filename, $cfg);
315}
316
1b7824d3
SR
317sub add_cpu_json_properties {
318 my ($prop) = @_;
319
320 foreach my $opt (keys %$cpu_fmt) {
321 $prop->{$opt} = $cpu_fmt->{$opt};
322 }
323
324 return $prop;
325}
326
a73cc993
SR
327sub get_cpu_models {
328 my ($include_custom) = @_;
329
330 my $models = [];
331
332 for my $default_model (keys %{$cpu_vendor_list}) {
333 push @$models, {
334 name => $default_model,
335 custom => 0,
336 vendor => $cpu_vendor_list->{$default_model},
337 };
338 }
339
1359e23f
AD
340 for my $model (keys %{$builtin_models}) {
341 my $reported_model = $builtin_models->{$model}->{'reported-model'};
342 my $vendor = $cpu_vendor_list->{$reported_model};
343 push @$models, {
344 name => $model,
345 custom => 0,
346 vendor => $vendor,
347 };
348 }
349
a73cc993
SR
350 return $models if !$include_custom;
351
352 my $conf = load_custom_model_conf();
353 for my $custom_model (keys %{$conf->{ids}}) {
354 my $reported_model = $conf->{ids}->{$custom_model}->{'reported-model'};
355 $reported_model //= $cpu_fmt->{'reported-model'}->{default};
356 my $vendor = $cpu_vendor_list->{$reported_model};
357 push @$models, {
358 name => "custom-$custom_model",
359 custom => 1,
360 vendor => $vendor,
361 };
362 }
363
364 return $models;
365}
366
b3e89488
SR
367sub is_custom_model {
368 my ($cputype) = @_;
369 return $cputype =~ m/^custom-/;
370}
371
372# Use this to get a single model in the format described by $cpu_fmt.
373# Allows names with and without custom- prefix.
374sub get_custom_model {
375 my ($name, $noerr) = @_;
376
377 $name =~ s/^custom-//;
378 my $conf = load_custom_model_conf();
379
380 my $entry = $conf->{ids}->{$name};
381 if (!defined($entry)) {
382 die "Custom cputype '$name' not found\n" if !$noerr;
d1c1af4b 383 return;
b3e89488
SR
384 }
385
386 my $model = {};
387 for my $property (keys %$cpu_fmt) {
388 if (my $value = $entry->{$property}) {
389 $model->{$property} = $value;
390 }
391 }
392
393 return $model;
394}
395
d786a274
SR
396# Print a QEMU device node for a given VM configuration for hotplugging CPUs
397sub print_cpu_device {
398 my ($conf, $id) = @_;
399
400 my $kvm = $conf->{kvm} // 1;
401 my $cpu = $kvm ? "kvm64" : "qemu64";
402 if (my $cputype = $conf->{cpu}) {
7b8c4de3 403 my $cpuconf = PVE::JSONSchema::parse_property_string('pve-vm-cpu-conf', $cputype)
d786a274
SR
404 or die "Cannot parse cpu description: $cputype\n";
405 $cpu = $cpuconf->{cputype};
b3e89488 406
1359e23f
AD
407 if (my $model = $builtin_models->{$cpu}) {
408 $cpu = $model->{'reported-model'};
409 } elsif (is_custom_model($cputype)) {
b3e89488
SR
410 my $custom_cpu = get_custom_model($cpu);
411
b0ab3463 412 $cpu = $custom_cpu->{'reported-model'} // $cpu_fmt->{'reported-model'}->{default};
adc67fe9
TL
413 }
414 if (my $replacement_type = $depreacated_cpu_map->{$cpu}) {
0d6962f9 415 $cpu = $replacement_type;
b3e89488 416 }
d786a274
SR
417 }
418
419 my $cores = $conf->{cores} || 1;
420
421 my $current_core = ($id - 1) % $cores;
422 my $current_socket = int(($id - 1 - $current_core)/$cores);
423
b0ab3463 424 # FIXME: hot plugging other architectures like our unofficial arch64 support?
d786a274
SR
425 return "$cpu-x86_64-cpu,id=cpu$id,socket-id=$current_socket,core-id=$current_core,thread-id=0";
426}
427
45619185
SR
428# Resolves multiple arrays of hashes representing CPU flags with metadata to a
429# single string in QEMU "-cpu" compatible format. Later arrays have higher
430# priority.
431#
432# Hashes take the following format:
433# {
434# aes => {
435# op => "+", # defaults to "" if undefined
436# reason => "to support AES acceleration", # for override warnings
437# value => "" # needed for kvm=off (value: off) etc...
438# },
439# ...
440# }
441sub resolve_cpu_flags {
442 my $flags = {};
443
444 for my $hash (@_) {
445 for my $flag_name (keys %$hash) {
446 my $flag = $hash->{$flag_name};
447 my $old_flag = $flags->{$flag_name};
448
449 $flag->{op} //= "";
450 $flag->{reason} //= "unknown origin";
451
452 if ($old_flag) {
453 my $value_changed = (defined($flag->{value}) != defined($old_flag->{value})) ||
454 (defined($flag->{value}) && $flag->{value} ne $old_flag->{value});
455
456 if ($old_flag->{op} eq $flag->{op} && !$value_changed) {
457 $flags->{$flag_name}->{reason} .= " & $flag->{reason}";
458 next;
459 }
460
461 my $old = print_cpuflag_hash($flag_name, $flags->{$flag_name});
462 my $new = print_cpuflag_hash($flag_name, $flag);
463 warn "warning: CPU flag/setting $new overwrites $old\n";
464 }
465
466 $flags->{$flag_name} = $flag;
467 }
468 }
469
470 my $flag_str = '';
471 # sort for command line stability
472 for my $flag_name (sort keys %$flags) {
473 $flag_str .= ',';
474 $flag_str .= $flags->{$flag_name}->{op};
475 $flag_str .= $flag_name;
476 $flag_str .= "=$flags->{$flag_name}->{value}"
477 if $flags->{$flag_name}->{value};
478 }
479
480 return $flag_str;
481}
482
483sub print_cpuflag_hash {
484 my ($flag_name, $flag) = @_;
485 my $formatted = "'$flag->{op}$flag_name";
486 $formatted .= "=$flag->{value}" if defined($flag->{value});
487 $formatted .= "'";
488 $formatted .= " ($flag->{reason})" if defined($flag->{reason});
489 return $formatted;
490}
491
c4581b9c
SR
492sub parse_cpuflag_list {
493 my ($re, $reason, $flaglist) = @_;
494
495 my $res = {};
496 return $res if !$flaglist;
497
498 foreach my $flag (split(";", $flaglist)) {
4893f9b9 499 if ($flag =~ m/^$re$/) {
c4581b9c
SR
500 $res->{$2} = { op => $1, reason => $reason };
501 }
502 }
503
504 return $res;
505}
506
d786a274
SR
507# Calculate QEMU's '-cpu' argument from a given VM configuration
508sub get_cpu_options {
509 my ($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough) = @_;
510
c4581b9c 511 my $cputype = $kvm ? "kvm64" : "qemu64";
d786a274 512 if ($arch eq 'aarch64') {
c4581b9c 513 $cputype = 'cortex-a57';
d786a274
SR
514 }
515
c4581b9c
SR
516 my $cpu = {};
517 my $custom_cpu;
1359e23f 518 my $builtin_cpu;
c4581b9c
SR
519 my $hv_vendor_id;
520 if (my $cpu_prop_str = $conf->{cpu}) {
7b8c4de3 521 $cpu = PVE::JSONSchema::parse_property_string('pve-vm-cpu-conf', $cpu_prop_str)
c4581b9c 522 or die "Cannot parse cpu description: $cpu_prop_str\n";
d786a274 523
c4581b9c 524 $cputype = $cpu->{cputype};
1359e23f
AD
525 if (my $model = $builtin_models->{$cputype}) {
526 $cputype = $model->{'reported-model'};
527 $builtin_cpu->{flags} = $model->{'flags'};
528 } elsif (is_custom_model($cputype)) {
c4581b9c 529 $custom_cpu = get_custom_model($cputype);
d786a274 530
b0ab3463
TL
531 $cputype = $custom_cpu->{'reported-model'} // $cpu_fmt->{'reported-model'}->{default};
532 $kvm_off = $custom_cpu->{hidden} if defined($custom_cpu->{hidden});
c4581b9c
SR
533 $hv_vendor_id = $custom_cpu->{'hv-vendor-id'};
534 }
d786a274 535
0d6962f9
TL
536 if (my $replacement_type = $depreacated_cpu_map->{$cputype}) {
537 $cputype = $replacement_type;
538 }
539
c4581b9c 540 # VM-specific settings override custom CPU config
b0ab3463
TL
541 $kvm_off = $cpu->{hidden} if defined($cpu->{hidden});
542 $hv_vendor_id = $cpu->{'hv-vendor-id'} if defined($cpu->{'hv-vendor-id'});
c4581b9c 543 }
d786a274 544
f7d1505b
TL
545 my $pve_flags = get_pve_cpu_flags($conf, $kvm, $cputype, $arch, $machine_version);
546
547 my $hv_flags = $kvm
548 ? get_hyperv_enlightenments(
549 $winversion,
550 $machine_version,
551 $conf->{bios},
552 $gpu_passthrough,
553 $hv_vendor_id,
554 )
555 : undef;
c4581b9c 556
1359e23f
AD
557 my $builtin_cputype_flags = parse_cpuflag_list(
558 $cpu_flag_any_re, "set by builtin CPU model", $builtin_cpu->{flags});
559
11f9264f
TL
560 my $custom_cputype_flags = parse_cpuflag_list(
561 $cpu_flag_any_re, "set by custom CPU model", $custom_cpu->{flags});
c4581b9c 562
11f9264f
TL
563 my $vm_flags = parse_cpuflag_list(
564 $cpu_flag_supported_re, "manually set for VM", $cpu->{flags});
c4581b9c
SR
565
566 my $pve_forced_flags = {};
567 $pve_forced_flags->{'enforce'} = {
568 reason => "error if requested CPU settings not available",
569 } if $cputype ne 'host' && $kvm && $arch eq 'x86_64';
570 $pve_forced_flags->{'kvm'} = {
571 value => "off",
572 reason => "hide KVM virtualization from guest",
573 } if $kvm_off;
574
575 # $cputype is the "reported-model" for custom types, so we can just look up
576 # the vendor in the default list
577 my $cpu_vendor = $cpu_vendor_list->{$cputype};
578 if ($cpu_vendor) {
579 $pve_forced_flags->{'vendor'} = {
580 value => $cpu_vendor,
581 } if $cpu_vendor ne 'default';
582 } elsif ($arch ne 'aarch64') {
583 die "internal error"; # should not happen
d786a274
SR
584 }
585
c4581b9c 586 my $cpu_str = $cputype;
d786a274 587
c4581b9c 588 # will be resolved in parameter order
11f9264f 589 $cpu_str .= resolve_cpu_flags(
1359e23f 590 $pve_flags, $hv_flags, $builtin_cputype_flags, $custom_cputype_flags, $vm_flags, $pve_forced_flags);
d786a274 591
9f9792d3
SR
592 my $phys_bits = '';
593 foreach my $conf ($custom_cpu, $cpu) {
594 next if !defined($conf);
595 my $conf_val = $conf->{'phys-bits'};
596 next if !$conf_val;
597 if ($conf_val eq 'host') {
598 $phys_bits = ",host-phys-bits=true";
599 } else {
600 $phys_bits = ",phys-bits=$conf_val";
601 }
602 }
603 $cpu_str .= $phys_bits;
604
c4581b9c
SR
605 return ('-cpu', $cpu_str);
606}
d786a274 607
c4581b9c
SR
608# Some hardcoded flags required by certain configurations
609sub get_pve_cpu_flags {
610 my ($conf, $kvm, $cputype, $arch, $machine_version) = @_;
611
612 my $pve_flags = {};
613 my $pve_msg = "set by PVE;";
614
615 $pve_flags->{'lahf_lm'} = {
616 op => '+',
617 reason => "$pve_msg to support Windows 8.1+",
618 } if $cputype eq 'kvm64' && $arch eq 'x86_64';
619
620 $pve_flags->{'x2apic'} = {
621 op => '-',
622 reason => "$pve_msg incompatible with Solaris",
623 } if $conf->{ostype} && $conf->{ostype} eq 'solaris';
624
625 $pve_flags->{'sep'} = {
626 op => '+',
627 reason => "$pve_msg to support Windows 8+ and improve Windows XP+",
628 } if $cputype eq 'kvm64' || $cputype eq 'kvm32';
629
630 $pve_flags->{'rdtscp'} = {
631 op => '-',
632 reason => "$pve_msg broken on AMD Opteron",
633 } if $cputype =~ m/^Opteron/;
634
635 if (min_version($machine_version, 2, 3) && $kvm && $arch eq 'x86_64') {
636 $pve_flags->{'kvm_pv_unhalt'} = {
637 op => '+',
638 reason => "$pve_msg to improve Linux guest spinlock performance",
639 };
640 $pve_flags->{'kvm_pv_eoi'} = {
641 op => '+',
642 reason => "$pve_msg to improve Linux guest interrupt performance",
643 };
d786a274
SR
644 }
645
c4581b9c 646 return $pve_flags;
d786a274
SR
647}
648
c4581b9c
SR
649sub get_hyperv_enlightenments {
650 my ($winversion, $machine_version, $bios, $gpu_passthrough, $hv_vendor_id) = @_;
d786a274
SR
651
652 return if $winversion < 6;
653 return if $bios && $bios eq 'ovmf' && $winversion < 8;
654
c4581b9c
SR
655 my $flags = {};
656 my $default_reason = "automatic Hyper-V enlightenment for Windows";
657 my $flagfn = sub {
658 my ($flag, $value, $reason) = @_;
659 $flags->{$flag} = {
660 reason => $reason // $default_reason,
661 value => $value,
662 }
663 };
664
665 my $hv_vendor_set = defined($hv_vendor_id);
666 if ($gpu_passthrough || $hv_vendor_set) {
d786a274 667 $hv_vendor_id //= 'proxmox';
c4581b9c
SR
668 $flagfn->('hv_vendor_id', $hv_vendor_id, $hv_vendor_set ?
669 "custom hv_vendor_id set" : "NVIDIA workaround for GPU passthrough");
d786a274
SR
670 }
671
672 if (min_version($machine_version, 2, 3)) {
c4581b9c
SR
673 $flagfn->('hv_spinlocks', '0x1fff');
674 $flagfn->('hv_vapic');
675 $flagfn->('hv_time');
d786a274 676 } else {
c4581b9c 677 $flagfn->('hv_spinlocks', '0xffff');
d786a274
SR
678 }
679
680 if (min_version($machine_version, 2, 6)) {
c4581b9c
SR
681 $flagfn->('hv_reset');
682 $flagfn->('hv_vpindex');
683 $flagfn->('hv_runtime');
d786a274
SR
684 }
685
686 if ($winversion >= 7) {
c4581b9c
SR
687 my $win7_reason = $default_reason . " 7 and higher";
688 $flagfn->('hv_relaxed', undef, $win7_reason);
d786a274
SR
689
690 if (min_version($machine_version, 2, 12)) {
c4581b9c
SR
691 $flagfn->('hv_synic', undef, $win7_reason);
692 $flagfn->('hv_stimer', undef, $win7_reason);
d786a274
SR
693 }
694
695 if (min_version($machine_version, 3, 1)) {
c4581b9c 696 $flagfn->('hv_ipi', undef, $win7_reason);
d786a274
SR
697 }
698 }
c4581b9c
SR
699
700 return $flags;
d786a274
SR
701}
702
58c64ad5
SR
703sub get_cpu_from_running_vm {
704 my ($pid) = @_;
705
706 my $cmdline = PVE::QemuServer::Helpers::parse_cmdline($pid);
707 die "could not read commandline of running machine\n"
708 if !$cmdline->{cpu}->{value};
709
710 # sanitize and untaint value
711 $cmdline->{cpu}->{value} =~ $qemu_cmdline_cpu_re;
712 return $1;
713}
714
b3e89488
SR
715__PACKAGE__->register();
716__PACKAGE__->init();
717
d786a274 7181;