Signed-off-by: Fiona Ebner <f.eb...@proxmox.com> --- src/PVE/API2/Qemu.pm | 13 ++- src/PVE/QemuMigrate.pm | 3 +- src/PVE/QemuServer.pm | 168 +--------------------------- src/PVE/QemuServer/Makefile | 1 + src/PVE/QemuServer/OVMF.pm | 186 +++++++++++++++++++++++++++++++ src/test/MigrationTest/Shared.pm | 4 + 6 files changed, 207 insertions(+), 168 deletions(-) create mode 100644 src/PVE/QemuServer/OVMF.pm
diff --git a/src/PVE/API2/Qemu.pm b/src/PVE/API2/Qemu.pm index ce6f362d..44322db6 100644 --- a/src/PVE/API2/Qemu.pm +++ b/src/PVE/API2/Qemu.pm @@ -36,6 +36,7 @@ use PVE::QemuServer::Monitor qw(mon_cmd); use PVE::QemuServer::Machine; use PVE::QemuServer::Memory qw(get_current_memory); use PVE::QemuServer::MetaInfo; +use PVE::QemuServer::OVMF; use PVE::QemuServer::PCI; use PVE::QemuServer::QMPHelpers; use PVE::QemuServer::RNG; @@ -540,8 +541,10 @@ my sub create_disks : prototype($$$$$$$$$$$) { format => $disk->{format}, }; - $dest_info->{efisize} = PVE::QemuServer::get_efivars_size($conf, $disk) - if $ds eq 'efidisk0'; + if ($ds eq 'efidisk0') { + $dest_info->{efisize} = + PVE::QemuServer::OVMF::get_efivars_size($conf, $disk); + } eval { ($dst_volid, $size) = @@ -612,7 +615,7 @@ my sub create_disks : prototype($$$$$$$$$$$) { "SEV-SNP uses consolidated read-only firmware and does not require an EFI disk\n" if $amd_sev_type && $amd_sev_type eq 'snp'; - ($volid, $size) = PVE::QemuServer::create_efidisk( + ($volid, $size) = PVE::QemuServer::OVMF::create_efidisk( $storecfg, $storeid, $vmid, $fmt, $arch, $disk, $smm, $amd_sev_type, ); } elsif ($ds eq 'tpmstate0') { @@ -4455,7 +4458,7 @@ __PACKAGE__->register_method({ format => $format, }; - $dest_info->{efisize} = PVE::QemuServer::get_efivars_size($oldconf) + $dest_info->{efisize} = PVE::QemuServer::OVMF::get_efivars_size($oldconf) if $opt eq 'efidisk0'; my $newdrive = PVE::QemuServer::clone_disk( @@ -4733,7 +4736,7 @@ __PACKAGE__->register_method({ format => $format, }; - $dest_info->{efisize} = PVE::QemuServer::get_efivars_size($conf) + $dest_info->{efisize} = PVE::QemuServer::OVMF::get_efivars_size($conf) if $disk eq 'efidisk0'; my $newdrive = PVE::QemuServer::clone_disk( diff --git a/src/PVE/QemuMigrate.pm b/src/PVE/QemuMigrate.pm index 28d7ac56..9ccaf7e0 100644 --- a/src/PVE/QemuMigrate.pm +++ b/src/PVE/QemuMigrate.pm @@ -31,6 +31,7 @@ use PVE::QemuServer::Helpers qw(min_version); use PVE::QemuServer::Machine; use PVE::QemuServer::Monitor qw(mon_cmd); use PVE::QemuServer::Memory qw(get_current_memory); +use PVE::QemuServer::OVMF; use PVE::QemuServer::QMPHelpers; use PVE::QemuServer; @@ -635,7 +636,7 @@ sub config_update_local_disksizes { # we want to set the efidisk size in the config to the size of the # real OVMF_VARS.fd image, else we can create a too big image, which does not work if (defined($conf->{efidisk0})) { - PVE::QemuServer::update_efidisk_size($conf); + PVE::QemuServer::OVMF::update_efidisk_size($conf); } # TPM state might have an irregular filesize, to avoid problems on transfer diff --git a/src/PVE/QemuServer.pm b/src/PVE/QemuServer.pm index 63b4d469..719687dc 100644 --- a/src/PVE/QemuServer.pm +++ b/src/PVE/QemuServer.pm @@ -56,7 +56,7 @@ use PVE::QemuServer::Helpers use PVE::QemuServer::Cloudinit; use PVE::QemuServer::CGroup; use PVE::QemuServer::CPUConfig - qw(print_cpu_device get_cpu_options get_cpu_bitness is_native_arch get_amd_sev_object get_amd_sev_type); + qw(print_cpu_device get_cpu_options is_native_arch get_amd_sev_object get_amd_sev_type); use PVE::QemuServer::Drive qw( is_valid_drivename checked_volume_format @@ -71,6 +71,7 @@ use PVE::QemuServer::Machine; use PVE::QemuServer::Memory qw(get_current_memory); use PVE::QemuServer::MetaInfo; use PVE::QemuServer::Monitor qw(mon_cmd); +use PVE::QemuServer::OVMF; use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr print_pcie_root_port parse_hostpci); use PVE::QemuServer::QemuImage; use PVE::QemuServer::QMPHelpers qw(qemu_deviceadd qemu_devicedel qemu_objectadd qemu_objectdel); @@ -98,41 +99,6 @@ my sub vm_is_ha_managed { return PVE::HA::Config::vm_is_ha_managed($vmid); } -my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/'; -my $OVMF = { - x86_64 => { - '4m-no-smm' => [ - "$EDK2_FW_BASE/OVMF_CODE_4M.fd", "$EDK2_FW_BASE/OVMF_VARS_4M.fd", - ], - '4m-no-smm-ms' => [ - "$EDK2_FW_BASE/OVMF_CODE_4M.fd", "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd", - ], - '4m' => [ - "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd", "$EDK2_FW_BASE/OVMF_VARS_4M.fd", - ], - '4m-ms' => [ - "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd", "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd", - ], - '4m-sev' => [ - "$EDK2_FW_BASE/OVMF_CVM_CODE_4M.fd", "$EDK2_FW_BASE/OVMF_CVM_VARS_4M.fd", - ], - '4m-snp' => [ - "$EDK2_FW_BASE/OVMF_CVM_4M.fd", - ], - # FIXME: These are legacy 2MB-sized images that modern OVMF doesn't supports to build - # anymore. how can we deperacate this sanely without breaking existing instances, or using - # older backups and snapshot? - default => [ - "$EDK2_FW_BASE/OVMF_CODE.fd", "$EDK2_FW_BASE/OVMF_VARS.fd", - ], - }, - aarch64 => { - default => [ - "$EDK2_FW_BASE/AAVMF_CODE.fd", "$EDK2_FW_BASE/AAVMF_VARS.fd", - ], - }, -}; - my $cpuinfo = PVE::ProcFSTools::read_cpuinfo(); # Note about locking: we use flock on the config file protect against concurrent actions. @@ -3293,36 +3259,6 @@ sub vga_conf_has_spice { return $1 || 1; } -sub get_ovmf_files($$$$) { - my ($arch, $efidisk, $smm, $amd_sev_type) = @_; - - my $types = $OVMF->{$arch} - or die "no OVMF images known for architecture '$arch'\n"; - - my $type = 'default'; - if ($arch eq 'x86_64') { - if ($amd_sev_type && $amd_sev_type eq 'snp') { - $type = "4m-snp"; - my ($ovmf) = $types->{$type}->@*; - die "EFI base image '$ovmf' not found\n" if !-f $ovmf; - return ($ovmf); - } elsif ($amd_sev_type) { - $type = "4m-sev"; - } elsif (defined($efidisk->{efitype}) && $efidisk->{efitype} eq '4m') { - $type = $smm ? "4m" : "4m-no-smm"; - $type .= '-ms' if $efidisk->{'pre-enrolled-keys'}; - } else { - # TODO: log_warn about use of legacy images for x86_64 with Promxox VE 9 - } - } - - my ($ovmf_code, $ovmf_vars) = $types->{$type}->@*; - die "EFI base image '$ovmf_code' not found\n" if !-f $ovmf_code; - die "EFI vars image '$ovmf_vars' not found\n" if !-f $ovmf_vars; - - return ($ovmf_code, $ovmf_vars); -} - # To use query_supported_cpu_flags and query_understood_cpu_flags to get flags # to use in a QEMU command line (-cpu element), first array_intersect the result # of query_supported_ with query_understood_. This is necessary because: @@ -3464,48 +3400,6 @@ my sub should_disable_smm { && $vga->{type} =~ m/^(serial\d+|none)$/; } -my sub print_ovmf_drive_commandlines { - my ($conf, $storecfg, $vmid, $arch, $q35, $version_guard) = @_; - - my $d = $conf->{efidisk0} ? parse_drive('efidisk0', $conf->{efidisk0}) : undef; - - my $amd_sev_type = get_amd_sev_type($conf); - die "Attempting to configure SEV-SNP with pflash devices instead of using `-bios`\n" - if $amd_sev_type && $amd_sev_type eq 'snp'; - - my ($ovmf_code, $ovmf_vars) = get_ovmf_files($arch, $d, $q35, $amd_sev_type); - - my $var_drive_str = "if=pflash,unit=1,id=drive-efidisk0"; - if ($d) { - my ($storeid, $volname) = PVE::Storage::parse_volume_id($d->{file}, 1); - my ($path, $format) = $d->@{ 'file', 'format' }; - if ($storeid) { - $path = PVE::Storage::path($storecfg, $d->{file}); - $format //= checked_volume_format($storecfg, $d->{file}); - } elsif (!defined($format)) { - die "efidisk format must be specified\n"; - } - # SPI flash does lots of read-modify-write OPs, without writeback this gets really slow #3329 - if ($path =~ m/^rbd:/) { - $var_drive_str .= ',cache=writeback'; - $path .= ':rbd_cache_policy=writeback'; # avoid write-around, we *need* to cache writes too - } - $var_drive_str .= ",format=$format,file=$path"; - - $var_drive_str .= ",size=" . (-s $ovmf_vars) - if $format eq 'raw' && $version_guard->(4, 1, 2); - $var_drive_str .= ',readonly=on' if drive_is_read_only($conf, $d); - } else { - log_warn("no efidisk configured! Using temporary efivars disk."); - my $path = "/tmp/$vmid-ovmf.fd"; - PVE::Tools::file_copy($ovmf_vars, $path, -s $ovmf_vars); - $var_drive_str .= ",format=raw,file=$path"; - $var_drive_str .= ",size=" . (-s $ovmf_vars) if $version_guard->(4, 1, 2); - } - - return ("if=pflash,unit=0,format=raw,readonly=on,file=$ovmf_code", $var_drive_str); -} - my sub get_vga_properties { my ($conf, $arch, $machine_version, $winversion) = @_; @@ -3680,21 +3574,10 @@ sub config_to_command { } if ($conf->{bios} && $conf->{bios} eq 'ovmf') { - die "OVMF (UEFI) BIOS is not supported on 32-bit CPU types\n" - if !$forcecpu && get_cpu_bitness($conf->{cpu}, $arch) == 32; - - my $amd_sev_type = get_amd_sev_type($conf); - if ($amd_sev_type && $amd_sev_type eq 'snp') { - if (defined($conf->{efidisk0})) { - log_warn("EFI disks are not supported with SEV-SNP and will be ignored"); - } - push $cmd->@*, '-bios', get_ovmf_files($arch, undef, undef, $amd_sev_type); - } else { - my ($code_drive_str, $var_drive_str) = - print_ovmf_drive_commandlines($conf, $storecfg, $vmid, $arch, $q35, $version_guard); - push $cmd->@*, '-drive', $code_drive_str; - push $cmd->@*, '-drive', $var_drive_str; - } + my $ovmf_cmd = PVE::QemuServer::OVMF::print_ovmf_commandline( + $conf, $storecfg, $vmid, $arch, $q35, $version_guard, $forcecpu, + ); + push $cmd->@*, $ovmf_cmd->@*; } if ($q35) { # tell QEMU to load q35 config early @@ -8853,29 +8736,6 @@ sub qemu_use_old_bios_files { return ($use_old_bios_files, $machine_type); } -sub get_efivars_size { - my ($conf, $efidisk) = @_; - - my $arch = PVE::QemuServer::Helpers::get_vm_arch($conf); - $efidisk //= $conf->{efidisk0} ? parse_drive('efidisk0', $conf->{efidisk0}) : undef; - my $smm = PVE::QemuServer::Machine::machine_type_is_q35($conf); - my $amd_sev_type = get_amd_sev_type($conf); - my (undef, $ovmf_vars) = get_ovmf_files($arch, $efidisk, $smm, $amd_sev_type); - return -s $ovmf_vars; -} - -sub update_efidisk_size { - my ($conf) = @_; - - return if !defined($conf->{efidisk0}); - - my $disk = PVE::QemuServer::parse_drive('efidisk0', $conf->{efidisk0}); - $disk->{size} = get_efivars_size($conf); - $conf->{efidisk0} = print_drive($disk); - - return; -} - sub update_tpmstate_size { my ($conf) = @_; @@ -8884,22 +8744,6 @@ sub update_tpmstate_size { $conf->{tpmstate0} = print_drive($disk); } -sub create_efidisk($$$$$$$$) { - my ($storecfg, $storeid, $vmid, $fmt, $arch, $efidisk, $smm, $amd_sev_type) = @_; - - my (undef, $ovmf_vars) = get_ovmf_files($arch, $efidisk, $smm, $amd_sev_type); - - my $vars_size_b = -s $ovmf_vars; - my $vars_size = PVE::Tools::convert_size($vars_size_b, 'b' => 'kb'); - my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $fmt, undef, $vars_size); - PVE::Storage::activate_volumes($storecfg, [$volid]); - - PVE::QemuServer::QemuImage::convert($ovmf_vars, $volid, $vars_size_b); - my $size = PVE::Storage::volume_size_info($storecfg, $volid, 3); - - return ($volid, $size / 1024); -} - sub vm_iothreads_list { my ($vmid) = @_; diff --git a/src/PVE/QemuServer/Makefile b/src/PVE/QemuServer/Makefile index a34ec83b..dd6fe505 100644 --- a/src/PVE/QemuServer/Makefile +++ b/src/PVE/QemuServer/Makefile @@ -14,6 +14,7 @@ SOURCES=Agent.pm \ Memory.pm \ MetaInfo.pm \ Monitor.pm \ + OVMF.pm \ PCI.pm \ QemuImage.pm \ QMPHelpers.pm \ diff --git a/src/PVE/QemuServer/OVMF.pm b/src/PVE/QemuServer/OVMF.pm new file mode 100644 index 00000000..70c626a5 --- /dev/null +++ b/src/PVE/QemuServer/OVMF.pm @@ -0,0 +1,186 @@ +package PVE::QemuServer::OVMF; + +use strict; +use warnings; + +use PVE::RESTEnvironment qw(log_warn); +use PVE::Storage; +use PVE::Tools; + +use PVE::QemuServer::Drive qw(checked_volume_format drive_is_read_only parse_drive print_drive); +use PVE::QemuServer::CPUConfig qw(get_amd_sev_type get_cpu_bitness); +use PVE::QemuServer::Helpers; +use PVE::QemuServer::Machine; +use PVE::QemuServer::QemuImage; + +my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/'; +my $OVMF = { + x86_64 => { + '4m-no-smm' => [ + "$EDK2_FW_BASE/OVMF_CODE_4M.fd", "$EDK2_FW_BASE/OVMF_VARS_4M.fd", + ], + '4m-no-smm-ms' => [ + "$EDK2_FW_BASE/OVMF_CODE_4M.fd", "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd", + ], + '4m' => [ + "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd", "$EDK2_FW_BASE/OVMF_VARS_4M.fd", + ], + '4m-ms' => [ + "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd", "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd", + ], + '4m-sev' => [ + "$EDK2_FW_BASE/OVMF_CVM_CODE_4M.fd", "$EDK2_FW_BASE/OVMF_CVM_VARS_4M.fd", + ], + '4m-snp' => [ + "$EDK2_FW_BASE/OVMF_CVM_4M.fd", + ], + # FIXME: These are legacy 2MB-sized images that modern OVMF doesn't supports to build + # anymore. how can we deperacate this sanely without breaking existing instances, or using + # older backups and snapshot? + default => [ + "$EDK2_FW_BASE/OVMF_CODE.fd", "$EDK2_FW_BASE/OVMF_VARS.fd", + ], + }, + aarch64 => { + default => [ + "$EDK2_FW_BASE/AAVMF_CODE.fd", "$EDK2_FW_BASE/AAVMF_VARS.fd", + ], + }, +}; + +my sub get_ovmf_files($$$$) { + my ($arch, $efidisk, $smm, $amd_sev_type) = @_; + + my $types = $OVMF->{$arch} + or die "no OVMF images known for architecture '$arch'\n"; + + my $type = 'default'; + if ($arch eq 'x86_64') { + if ($amd_sev_type && $amd_sev_type eq 'snp') { + $type = "4m-snp"; + my ($ovmf) = $types->{$type}->@*; + die "EFI base image '$ovmf' not found\n" if !-f $ovmf; + return ($ovmf); + } elsif ($amd_sev_type) { + $type = "4m-sev"; + } elsif (defined($efidisk->{efitype}) && $efidisk->{efitype} eq '4m') { + $type = $smm ? "4m" : "4m-no-smm"; + $type .= '-ms' if $efidisk->{'pre-enrolled-keys'}; + } else { + # TODO: log_warn about use of legacy images for x86_64 with Promxox VE 9 + } + } + + my ($ovmf_code, $ovmf_vars) = $types->{$type}->@*; + die "EFI base image '$ovmf_code' not found\n" if !-f $ovmf_code; + die "EFI vars image '$ovmf_vars' not found\n" if !-f $ovmf_vars; + + return ($ovmf_code, $ovmf_vars); +} + +my sub print_ovmf_drive_commandlines { + my ($conf, $storecfg, $vmid, $arch, $q35, $version_guard) = @_; + + my $d = $conf->{efidisk0} ? parse_drive('efidisk0', $conf->{efidisk0}) : undef; + + my $amd_sev_type = get_amd_sev_type($conf); + die "Attempting to configure SEV-SNP with pflash devices instead of using `-bios`\n" + if $amd_sev_type && $amd_sev_type eq 'snp'; + + my ($ovmf_code, $ovmf_vars) = get_ovmf_files($arch, $d, $q35, $amd_sev_type); + + my $var_drive_str = "if=pflash,unit=1,id=drive-efidisk0"; + if ($d) { + my ($storeid, $volname) = PVE::Storage::parse_volume_id($d->{file}, 1); + my ($path, $format) = $d->@{ 'file', 'format' }; + if ($storeid) { + $path = PVE::Storage::path($storecfg, $d->{file}); + $format //= checked_volume_format($storecfg, $d->{file}); + } elsif (!defined($format)) { + die "efidisk format must be specified\n"; + } + # SPI flash does lots of read-modify-write OPs, without writeback this gets really slow #3329 + if ($path =~ m/^rbd:/) { + $var_drive_str .= ',cache=writeback'; + $path .= ':rbd_cache_policy=writeback'; # avoid write-around, we *need* to cache writes too + } + $var_drive_str .= ",format=$format,file=$path"; + + $var_drive_str .= ",size=" . (-s $ovmf_vars) + if $format eq 'raw' && $version_guard->(4, 1, 2); + $var_drive_str .= ',readonly=on' if drive_is_read_only($conf, $d); + } else { + log_warn("no efidisk configured! Using temporary efivars disk."); + my $path = "/tmp/$vmid-ovmf.fd"; + PVE::Tools::file_copy($ovmf_vars, $path, -s $ovmf_vars); + $var_drive_str .= ",format=raw,file=$path"; + $var_drive_str .= ",size=" . (-s $ovmf_vars) if $version_guard->(4, 1, 2); + } + + return ("if=pflash,unit=0,format=raw,readonly=on,file=$ovmf_code", $var_drive_str); +} + +sub get_efivars_size { + my ($conf, $efidisk) = @_; + + my $arch = PVE::QemuServer::Helpers::get_vm_arch($conf); + $efidisk //= $conf->{efidisk0} ? parse_drive('efidisk0', $conf->{efidisk0}) : undef; + my $smm = PVE::QemuServer::Machine::machine_type_is_q35($conf); + my $amd_sev_type = get_amd_sev_type($conf); + my (undef, $ovmf_vars) = get_ovmf_files($arch, $efidisk, $smm, $amd_sev_type); + return -s $ovmf_vars; +} + +sub update_efidisk_size { + my ($conf) = @_; + + return if !defined($conf->{efidisk0}); + + my $disk = parse_drive('efidisk0', $conf->{efidisk0}); + $disk->{size} = get_efivars_size($conf); + $conf->{efidisk0} = print_drive($disk); + + return; +} + +sub create_efidisk($$$$$$$$) { + my ($storecfg, $storeid, $vmid, $fmt, $arch, $efidisk, $smm, $amd_sev_type) = @_; + + my (undef, $ovmf_vars) = get_ovmf_files($arch, $efidisk, $smm, $amd_sev_type); + + my $vars_size_b = -s $ovmf_vars; + my $vars_size = PVE::Tools::convert_size($vars_size_b, 'b' => 'kb'); + my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $fmt, undef, $vars_size); + PVE::Storage::activate_volumes($storecfg, [$volid]); + + PVE::QemuServer::QemuImage::convert($ovmf_vars, $volid, $vars_size_b); + my $size = PVE::Storage::volume_size_info($storecfg, $volid, 3); + + return ($volid, $size / 1024); +} + +sub print_ovmf_commandline { + my ($conf, $storecfg, $vmid, $arch, $q35, $version_guard, $forcecpu) = @_; + + my $cmd = []; + + die "OVMF (UEFI) BIOS is not supported on 32-bit CPU types\n" + if !$forcecpu && get_cpu_bitness($conf->{cpu}, $arch) == 32; + + my $amd_sev_type = get_amd_sev_type($conf); + if ($amd_sev_type && $amd_sev_type eq 'snp') { + if (defined($conf->{efidisk0})) { + log_warn("EFI disks are not supported with SEV-SNP and will be ignored"); + } + push $cmd->@*, '-bios', get_ovmf_files($arch, undef, undef, $amd_sev_type); + } else { + my ($code_drive_str, $var_drive_str) = + print_ovmf_drive_commandlines($conf, $storecfg, $vmid, $arch, $q35, $version_guard); + push $cmd->@*, '-drive', $code_drive_str; + push $cmd->@*, '-drive', $var_drive_str; + } + + return $cmd; +} + +1; diff --git a/src/test/MigrationTest/Shared.pm b/src/test/MigrationTest/Shared.pm index 0b1ac7a0..e29cd1df 100644 --- a/src/test/MigrationTest/Shared.pm +++ b/src/test/MigrationTest/Shared.pm @@ -150,6 +150,10 @@ $qemu_server_module->mock( vm_stop_cleanup => sub { return; }, +); + +our $qemu_server_ovmf_module = Test::MockModule->new("PVE::QemuServer::OVMF"); +$qemu_server_ovmf_module->mock( get_efivars_size => sub { return 128 * 1024; }, -- 2.47.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel