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