1 package PVE
::QemuServer
::CPUConfig
;
7 use PVE
::Cluster
qw(cfs_register_file cfs_read_file);
8 use PVE
::Tools
qw(get_host_arch);
9 use PVE
::QemuServer
::Helpers
qw(min_version);
11 use base
qw(PVE::SectionConfig Exporter);
20 # under certain race-conditions, this module might be loaded before pve-cluster
21 # has started completely, so ensure we don't prevent the FUSE mount with our dir
22 if (PVE
::Cluster
::check_cfs_is_mounted
(1)) {
23 mkdir "/etc/pve/virtual-guest";
26 my $default_filename = "virtual-guest/cpu-models.conf";
29 sub { PVE
::QemuServer
::CPUConfig-
>parse_config(@_); },
30 sub { PVE
::QemuServer
::CPUConfig-
>write_config(@_); },
33 sub load_custom_model_conf
{
34 return cfs_read_file
($default_filename);
37 #builtin models : reported-model is mandatory
38 my $builtin_models = {
40 'reported-model' => 'qemu64',
41 flags
=> "+popcnt;+pni;+sse4.1;+sse4.2;+ssse3",
44 'reported-model' => 'qemu64',
45 flags
=> "+aes;+popcnt;+pni;+sse4.1;+sse4.2;+ssse3",
48 'reported-model' => 'qemu64',
49 flags
=> "+aes;+popcnt;+pni;+sse4.1;+sse4.2;+ssse3;+avx;+avx2;+bmi1;+bmi2;+f16c;+fma;+abm;+movbe;+xsave",
52 'reported-model' => 'qemu64',
53 flags
=> "+aes;+popcnt;+pni;+sse4.1;+sse4.2;+ssse3;+avx;+avx2;+bmi1;+bmi2;+f16c;+fma;+abm;+movbe;+xsave;+avx512f;+avx512bw;+avx512cd;+avx512dq;+avx512vl",
57 my $depreacated_cpu_map = {
58 # there never was such a client CPU, so map it to the server one for backward compat
59 'Icelake-Client' => 'Icelake-Server',
60 'Icelake-Client-noTSX' => 'Icelake-Server-noTSX',
63 my $cputypes_32bit = {
74 my $cpu_vendor_list = {
76 486 => 'GenuineIntel',
77 pentium
=> 'GenuineIntel',
78 pentium2
=> 'GenuineIntel',
79 pentium3
=> 'GenuineIntel',
80 coreduo
=> 'GenuineIntel',
81 core2duo
=> 'GenuineIntel',
82 Conroe
=> 'GenuineIntel',
83 Penryn
=> 'GenuineIntel',
84 Nehalem
=> 'GenuineIntel',
85 'Nehalem-IBRS' => 'GenuineIntel',
86 Westmere
=> 'GenuineIntel',
87 'Westmere-IBRS' => 'GenuineIntel',
88 SandyBridge
=> 'GenuineIntel',
89 'SandyBridge-IBRS' => 'GenuineIntel',
90 IvyBridge
=> 'GenuineIntel',
91 'IvyBridge-IBRS' => 'GenuineIntel',
92 Haswell
=> 'GenuineIntel',
93 'Haswell-IBRS' => 'GenuineIntel',
94 'Haswell-noTSX' => 'GenuineIntel',
95 'Haswell-noTSX-IBRS' => 'GenuineIntel',
96 Broadwell
=> 'GenuineIntel',
97 'Broadwell-IBRS' => 'GenuineIntel',
98 'Broadwell-noTSX' => 'GenuineIntel',
99 'Broadwell-noTSX-IBRS' => 'GenuineIntel',
100 'Skylake-Client' => 'GenuineIntel',
101 'Skylake-Client-IBRS' => 'GenuineIntel',
102 'Skylake-Client-noTSX-IBRS' => 'GenuineIntel',
103 'Skylake-Client-v4' => 'GenuineIntel',
104 'Skylake-Server' => 'GenuineIntel',
105 'Skylake-Server-IBRS' => 'GenuineIntel',
106 'Skylake-Server-noTSX-IBRS' => 'GenuineIntel',
107 'Skylake-Server-v4' => 'GenuineIntel',
108 'Skylake-Server-v5' => 'GenuineIntel',
109 'Cascadelake-Server' => 'GenuineIntel',
110 'Cascadelake-Server-v2' => 'GenuineIntel',
111 'Cascadelake-Server-noTSX' => 'GenuineIntel',
112 'Cascadelake-Server-v4' => 'GenuineIntel',
113 'Cascadelake-Server-v5' => 'GenuineIntel',
114 'Cooperlake' => 'GenuineIntel',
115 'Cooperlake-v2' => 'GenuineIntel',
116 KnightsMill
=> 'GenuineIntel',
117 'Icelake-Client' => 'GenuineIntel', # depreacated, removed with QEMU 7.1
118 'Icelake-Client-noTSX' => 'GenuineIntel', # depreacated, removed with QEMU 7.1
119 'Icelake-Server' => 'GenuineIntel',
120 'Icelake-Server-noTSX' => 'GenuineIntel',
121 'Icelake-Server-v3' => 'GenuineIntel',
122 'Icelake-Server-v4' => 'GenuineIntel',
123 'Icelake-Server-v5' => 'GenuineIntel',
124 'Icelake-Server-v6' => 'GenuineIntel',
125 'SapphireRapids' => 'GenuineIntel',
126 'SapphireRapids-v2' => 'GenuineIntel',
127 'GraniteRapids' => 'GenuineIntel',
130 athlon
=> 'AuthenticAMD',
131 phenom
=> 'AuthenticAMD',
132 Opteron_G1
=> 'AuthenticAMD',
133 Opteron_G2
=> 'AuthenticAMD',
134 Opteron_G3
=> 'AuthenticAMD',
135 Opteron_G4
=> 'AuthenticAMD',
136 Opteron_G5
=> 'AuthenticAMD',
137 EPYC
=> 'AuthenticAMD',
138 'EPYC-IBPB' => 'AuthenticAMD',
139 'EPYC-v3' => 'AuthenticAMD',
140 'EPYC-v4' => 'AuthenticAMD',
141 'EPYC-Rome' => 'AuthenticAMD',
142 'EPYC-Rome-v2' => 'AuthenticAMD',
143 'EPYC-Rome-v3' => 'AuthenticAMD',
144 'EPYC-Rome-v4' => 'AuthenticAMD',
145 'EPYC-Milan' => 'AuthenticAMD',
146 'EPYC-Milan-v2' => 'AuthenticAMD',
147 'EPYC-Genoa' => 'AuthenticAMD',
149 # generic types, use vendor from host node
158 my @supported_cpu_flags = (
172 my $cpu_flag_supported_re = qr/([+-])(@{[join('|', @supported_cpu_flags)]})/;
173 my $cpu_flag_any_re = qr/([+-])([a-zA-Z0-9\-_\.]+)/;
175 our $qemu_cmdline_cpu_re = qr/^((?>[+-]?[\w\-\._=]+,?)+)$/;
179 description
=> "Emulated CPU type. Can be default or custom name (custom model names must be prefixed with 'custom-').",
181 format_description
=> 'string',
186 'reported-model' => {
187 description
=> "CPU model and vendor to report to the guest. Must be a QEMU/KVM supported model."
188 ." Only valid for custom CPU model definitions, default models will always report themselves to the guest OS.",
190 enum
=> [ sort { lc("$a") cmp lc("$b") } keys %$cpu_vendor_list ],
195 description
=> "Do not identify as a KVM virtual machine.",
202 pattern
=> qr/[a-zA-Z0-9]{1,12}/,
203 format_description
=> 'vendor-id',
204 description
=> 'The Hyper-V vendor ID. Some drivers or programs inside Windows guests need a specific ID.',
208 description
=> "List of additional CPU flags separated by ';'. Use '+FLAG' to enable,"
209 ." '-FLAG' to disable a flag. Custom CPU models can specify any flag supported by"
210 ." QEMU/KVM, VM-specific flags must be from the following set for security reasons: "
211 . join(', ', @supported_cpu_flags),
212 format_description
=> '+FLAG[;-FLAG...]',
214 pattern
=> qr/$cpu_flag_any_re(;$cpu_flag_any_re)*/,
219 format
=> 'pve-phys-bits',
220 format_description
=> '8-64|host',
221 description
=> "The physical memory address bits that are reported to the guest OS. Should"
222 ." be smaller or equal to the host's. Set to 'host' to use value from host CPU, but"
223 ." note that doing so will break live migration to CPUs with other values.",
228 PVE
::JSONSchema
::register_format
('pve-phys-bits', \
&parse_phys_bits
);
229 sub parse_phys_bits
{
230 my ($str, $noerr) = @_;
232 my $err_msg = "value must be an integer between 8 and 64 or 'host'\n";
234 if ($str !~ m/^(host|\d{1,2})$/) {
235 die $err_msg if !$noerr;
239 if ($str =~ m/^\d+$/ && (int($str) < 8 || int($str) > 64)) {
240 die $err_msg if !$noerr;
247 # $cpu_fmt describes both the CPU config passed as part of a VM config, as well
248 # as the definition of a custom CPU model. There are some slight differences
249 # though, which we catch in the custom validation functions below.
250 PVE
::JSONSchema
::register_format
('pve-cpu-conf', $cpu_fmt, \
&validate_cpu_conf
);
251 sub validate_cpu_conf
{
253 # required, but can't be forced in schema since it's encoded in section header for custom models
254 die "CPU is missing cputype\n" if !$cpu->{cputype
};
257 PVE
::JSONSchema
::register_format
('pve-vm-cpu-conf', $cpu_fmt, \
&validate_vm_cpu_conf
);
258 sub validate_vm_cpu_conf
{
261 validate_cpu_conf
($cpu);
263 my $cputype = $cpu->{cputype
};
265 # a VM-specific config is only valid if the cputype exists
266 if (is_custom_model
($cputype)) {
267 # dies on unknown model
268 get_custom_model
($cputype);
270 die "Built-in cputype '$cputype' is not defined (missing 'custom-' prefix?)\n"
271 if !defined($cpu_vendor_list->{$cputype}) && !defined($builtin_models->{$cputype});
274 # in a VM-specific config, certain properties are limited/forbidden
276 die "VM-specific CPU flags must be a subset of: @{[join(', ', @supported_cpu_flags)]}\n"
277 if ($cpu->{flags
} && $cpu->{flags
} !~ m/^$cpu_flag_supported_re(;$cpu_flag_supported_re)*$/);
279 die "Property 'reported-model' not allowed in VM-specific CPU config.\n"
280 if defined($cpu->{'reported-model'});
285 # Section config settings
287 # shallow copy, since SectionConfig modifies propertyList internally
288 propertyList
=> { %$cpu_fmt },
296 return { %$cpu_fmt };
303 sub parse_section_header
{
304 my ($class, $line) = @_;
306 my ($type, $sectionId, $errmsg, $config) =
307 $class->SUPER::parse_section_header
($line);
310 return ($type, $sectionId, $errmsg, {
311 # name is given by section header, and we can always prepend 'custom-'
312 # since we're reading the custom CPU file
313 cputype
=> "custom-$sectionId",
318 my ($class, $filename, $cfg) = @_;
320 mkdir "/etc/pve/virtual-guest";
322 for my $model (keys %{$cfg->{ids
}}) {
323 my $model_conf = $cfg->{ids
}->{$model};
325 die "internal error: tried saving built-in CPU model (or missing prefix): $model_conf->{cputype}\n"
326 if !is_custom_model
($model_conf->{cputype
});
328 die "internal error: tried saving custom cpumodel with cputype (ignoring prefix: $model_conf->{cputype}) not equal to \$cfg->ids entry ($model)\n"
329 if "custom-$model" ne $model_conf->{cputype
};
331 # saved in section header
332 delete $model_conf->{cputype
};
335 $class->SUPER::write_config
($filename, $cfg);
338 sub add_cpu_json_properties
{
341 foreach my $opt (keys %$cpu_fmt) {
342 $prop->{$opt} = $cpu_fmt->{$opt};
349 my ($include_custom) = @_;
353 for my $default_model (keys %{$cpu_vendor_list}) {
355 name
=> $default_model,
357 vendor
=> $cpu_vendor_list->{$default_model},
361 for my $model (keys %{$builtin_models}) {
362 my $reported_model = $builtin_models->{$model}->{'reported-model'};
363 my $vendor = $cpu_vendor_list->{$reported_model};
371 return $models if !$include_custom;
373 my $conf = load_custom_model_conf
();
374 for my $custom_model (keys %{$conf->{ids
}}) {
375 my $reported_model = $conf->{ids
}->{$custom_model}->{'reported-model'};
376 $reported_model //= $cpu_fmt->{'reported-model'}->{default};
377 my $vendor = $cpu_vendor_list->{$reported_model};
379 name
=> "custom-$custom_model",
388 sub is_custom_model
{
390 return $cputype =~ m/^custom-/;
393 # Use this to get a single model in the format described by $cpu_fmt.
394 # Allows names with and without custom- prefix.
395 sub get_custom_model
{
396 my ($name, $noerr) = @_;
398 $name =~ s/^custom-//;
399 my $conf = load_custom_model_conf
();
401 my $entry = $conf->{ids
}->{$name};
402 if (!defined($entry)) {
403 die "Custom cputype '$name' not found\n" if !$noerr;
408 for my $property (keys %$cpu_fmt) {
409 if (my $value = $entry->{$property}) {
410 $model->{$property} = $value;
417 # Print a QEMU device node for a given VM configuration for hotplugging CPUs
418 sub print_cpu_device
{
419 my ($conf, $arch, $id) = @_;
421 # FIXME: hot plugging other architectures like our unofficial aarch64 support?
422 die "Hotplug of non x86_64 CPU not yet supported" if $arch ne 'x86_64';
424 my $kvm = $conf->{kvm
} // is_native_arch
($arch);
425 my $cpu = get_default_cpu_type
('x86_64', $kvm);
426 if (my $cputype = $conf->{cpu
}) {
427 my $cpuconf = PVE
::JSONSchema
::parse_property_string
('pve-vm-cpu-conf', $cputype)
428 or die "Cannot parse cpu description: $cputype\n";
429 $cpu = $cpuconf->{cputype
};
431 if (my $model = $builtin_models->{$cpu}) {
432 $cpu = $model->{'reported-model'};
433 } elsif (is_custom_model
($cputype)) {
434 my $custom_cpu = get_custom_model
($cpu);
436 $cpu = $custom_cpu->{'reported-model'} // $cpu_fmt->{'reported-model'}->{default};
438 if (my $replacement_type = $depreacated_cpu_map->{$cpu}) {
439 $cpu = $replacement_type;
443 my $cores = $conf->{cores
} || 1;
445 my $current_core = ($id - 1) % $cores;
446 my $current_socket = int(($id - 1 - $current_core)/$cores);
448 return "$cpu-x86_64-cpu,id=cpu$id,socket-id=$current_socket,core-id=$current_core,thread-id=0";
451 # Resolves multiple arrays of hashes representing CPU flags with metadata to a
452 # single string in QEMU "-cpu" compatible format. Later arrays have higher
455 # Hashes take the following format:
458 # op => "+", # defaults to "" if undefined
459 # reason => "to support AES acceleration", # for override warnings
460 # value => "" # needed for kvm=off (value: off) etc...
464 sub resolve_cpu_flags
{
468 for my $flag_name (keys %$hash) {
469 my $flag = $hash->{$flag_name};
470 my $old_flag = $flags->{$flag_name};
473 $flag->{reason
} //= "unknown origin";
476 my $value_changed = (defined($flag->{value
}) != defined($old_flag->{value
})) ||
477 (defined($flag->{value
}) && $flag->{value
} ne $old_flag->{value
});
479 if ($old_flag->{op
} eq $flag->{op
} && !$value_changed) {
480 $flags->{$flag_name}->{reason
} .= " & $flag->{reason}";
484 my $old = print_cpuflag_hash
($flag_name, $flags->{$flag_name});
485 my $new = print_cpuflag_hash
($flag_name, $flag);
486 warn "warning: CPU flag/setting $new overwrites $old\n";
489 $flags->{$flag_name} = $flag;
494 # sort for command line stability
495 for my $flag_name (sort keys %$flags) {
497 $flag_str .= $flags->{$flag_name}->{op
};
498 $flag_str .= $flag_name;
499 $flag_str .= "=$flags->{$flag_name}->{value}"
500 if $flags->{$flag_name}->{value
};
506 sub print_cpuflag_hash
{
507 my ($flag_name, $flag) = @_;
508 my $formatted = "'$flag->{op}$flag_name";
509 $formatted .= "=$flag->{value}" if defined($flag->{value
});
511 $formatted .= " ($flag->{reason})" if defined($flag->{reason
});
515 sub parse_cpuflag_list
{
516 my ($re, $reason, $flaglist) = @_;
519 return $res if !$flaglist;
521 foreach my $flag (split(";", $flaglist)) {
522 if ($flag =~ m/^$re$/) {
523 $res->{$2} = { op
=> $1, reason
=> $reason };
530 # Calculate QEMU's '-cpu' argument from a given VM configuration
531 sub get_cpu_options
{
532 my ($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough) = @_;
534 my $cputype = get_default_cpu_type
($arch, $kvm);
540 if (my $cpu_prop_str = $conf->{cpu
}) {
541 $cpu = PVE
::JSONSchema
::parse_property_string
('pve-vm-cpu-conf', $cpu_prop_str)
542 or die "Cannot parse cpu description: $cpu_prop_str\n";
544 $cputype = $cpu->{cputype
};
545 if (my $model = $builtin_models->{$cputype}) {
546 $cputype = $model->{'reported-model'};
547 $builtin_cpu->{flags
} = $model->{'flags'};
548 } elsif (is_custom_model
($cputype)) {
549 $custom_cpu = get_custom_model
($cputype);
551 $cputype = $custom_cpu->{'reported-model'} // $cpu_fmt->{'reported-model'}->{default};
552 $kvm_off = $custom_cpu->{hidden
} if defined($custom_cpu->{hidden
});
553 $hv_vendor_id = $custom_cpu->{'hv-vendor-id'};
556 if (my $replacement_type = $depreacated_cpu_map->{$cputype}) {
557 $cputype = $replacement_type;
560 # VM-specific settings override custom CPU config
561 $kvm_off = $cpu->{hidden
} if defined($cpu->{hidden
});
562 $hv_vendor_id = $cpu->{'hv-vendor-id'} if defined($cpu->{'hv-vendor-id'});
565 my $pve_flags = get_pve_cpu_flags
($conf, $kvm, $cputype, $arch, $machine_version);
568 ? get_hyperv_enlightenments
(
577 my $builtin_cputype_flags = parse_cpuflag_list
(
578 $cpu_flag_any_re, "set by builtin CPU model", $builtin_cpu->{flags
});
580 my $custom_cputype_flags = parse_cpuflag_list
(
581 $cpu_flag_any_re, "set by custom CPU model", $custom_cpu->{flags
});
583 my $vm_flags = parse_cpuflag_list
(
584 $cpu_flag_supported_re, "manually set for VM", $cpu->{flags
});
586 my $pve_forced_flags = {};
587 $pve_forced_flags->{'enforce'} = {
588 reason
=> "error if requested CPU settings not available",
589 } if $cputype ne 'host' && $kvm && $arch eq 'x86_64';
590 $pve_forced_flags->{'kvm'} = {
592 reason
=> "hide KVM virtualization from guest",
595 # $cputype is the "reported-model" for custom types, so we can just look up
596 # the vendor in the default list
597 my $cpu_vendor = $cpu_vendor_list->{$cputype};
599 $pve_forced_flags->{'vendor'} = {
600 value
=> $cpu_vendor,
601 } if $cpu_vendor ne 'default';
602 } elsif ($arch ne 'aarch64') {
603 die "internal error"; # should not happen
606 my $cpu_str = $cputype;
608 # will be resolved in parameter order
609 $cpu_str .= resolve_cpu_flags
(
610 $pve_flags, $hv_flags, $builtin_cputype_flags, $custom_cputype_flags, $vm_flags, $pve_forced_flags);
613 foreach my $conf ($custom_cpu, $cpu) {
614 next if !defined($conf);
615 my $conf_val = $conf->{'phys-bits'};
617 if ($conf_val eq 'host') {
618 $phys_bits = ",host-phys-bits=true";
620 $phys_bits = ",phys-bits=$conf_val";
623 $cpu_str .= $phys_bits;
625 return ('-cpu', $cpu_str);
628 # Some hardcoded flags required by certain configurations
629 sub get_pve_cpu_flags
{
630 my ($conf, $kvm, $cputype, $arch, $machine_version) = @_;
633 my $pve_msg = "set by PVE;";
635 $pve_flags->{'lahf_lm'} = {
637 reason
=> "$pve_msg to support Windows 8.1+",
638 } if $cputype eq 'kvm64' && $arch eq 'x86_64';
640 $pve_flags->{'x2apic'} = {
642 reason
=> "$pve_msg incompatible with Solaris",
643 } if $conf->{ostype
} && $conf->{ostype
} eq 'solaris';
645 $pve_flags->{'sep'} = {
647 reason
=> "$pve_msg to support Windows 8+ and improve Windows XP+",
648 } if $cputype eq 'kvm64' || $cputype eq 'kvm32';
650 $pve_flags->{'rdtscp'} = {
652 reason
=> "$pve_msg broken on AMD Opteron",
653 } if $cputype =~ m/^Opteron/;
655 if (min_version
($machine_version, 2, 3) && $kvm && $arch eq 'x86_64') {
656 $pve_flags->{'kvm_pv_unhalt'} = {
658 reason
=> "$pve_msg to improve Linux guest spinlock performance",
660 $pve_flags->{'kvm_pv_eoi'} = {
662 reason
=> "$pve_msg to improve Linux guest interrupt performance",
669 sub get_hyperv_enlightenments
{
670 my ($winversion, $machine_version, $bios, $gpu_passthrough, $hv_vendor_id) = @_;
672 return if $winversion < 6;
673 return if $bios && $bios eq 'ovmf' && $winversion < 8;
676 my $default_reason = "automatic Hyper-V enlightenment for Windows";
678 my ($flag, $value, $reason) = @_;
680 reason
=> $reason // $default_reason,
685 my $hv_vendor_set = defined($hv_vendor_id);
686 if ($gpu_passthrough || $hv_vendor_set) {
687 $hv_vendor_id //= 'proxmox';
688 $flagfn->('hv_vendor_id', $hv_vendor_id, $hv_vendor_set ?
689 "custom hv_vendor_id set" : "NVIDIA workaround for GPU passthrough");
692 if (min_version
($machine_version, 2, 3)) {
693 $flagfn->('hv_spinlocks', '0x1fff');
694 $flagfn->('hv_vapic');
695 $flagfn->('hv_time');
697 $flagfn->('hv_spinlocks', '0xffff');
700 if (min_version
($machine_version, 2, 6)) {
701 $flagfn->('hv_reset');
702 $flagfn->('hv_vpindex');
703 $flagfn->('hv_runtime');
706 if ($winversion >= 7) {
707 my $win7_reason = $default_reason . " 7 and higher";
708 $flagfn->('hv_relaxed', undef, $win7_reason);
710 if (min_version
($machine_version, 2, 12)) {
711 $flagfn->('hv_synic', undef, $win7_reason);
712 $flagfn->('hv_stimer', undef, $win7_reason);
715 if (min_version
($machine_version, 3, 1)) {
716 $flagfn->('hv_ipi', undef, $win7_reason);
723 sub get_cpu_from_running_vm
{
726 my $cmdline = PVE
::QemuServer
::Helpers
::parse_cmdline
($pid);
727 die "could not read commandline of running machine\n"
728 if !$cmdline->{cpu
}->{value
};
730 # sanitize and untaint value
731 $cmdline->{cpu
}->{value
} =~ $qemu_cmdline_cpu_re;
735 sub get_default_cpu_type
{
736 my ($arch, $kvm) = @_;
738 my $cputype = $kvm ?
'kvm64' : 'qemu64';
739 $cputype = 'cortex-a57' if $arch eq 'aarch64';
744 sub is_native_arch
($) {
746 return get_host_arch
() eq $arch;
749 sub get_cpu_bitness
{
750 my ($cpu_prop_str, $arch) = @_;
752 $arch //= get_host_arch
();
754 my $cputype = get_default_cpu_type
($arch, 0);
757 my $cpu = PVE
::JSONSchema
::parse_property_string
('pve-vm-cpu-conf', $cpu_prop_str)
758 or die "Cannot parse cpu description: $cpu_prop_str\n";
760 my $cputype = $cpu->{cputype
};
762 if (my $model = $builtin_models->{$cputype}) {
763 $cputype = $model->{'reported-model'};
764 } elsif (is_custom_model
($cputype)) {
765 my $custom_cpu = get_custom_model
($cputype);
766 $cputype = $custom_cpu->{'reported-model'} // $cpu_fmt->{'reported-model'}->{default};
770 return $cputypes_32bit->{$cputype} ?
32 : 64 if $arch eq 'x86_64';
771 return 64 if $arch eq 'aarch64';
773 die "unsupported architecture '$arch'\n";
776 __PACKAGE__-
>register();