From a014ad5514ee753d602b55ebce6f743a2efdbe5a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 29 Nov 2024 11:32:45 +0000 Subject: [PATCH] lxd/instance/drivers/driver/qemu: Fix nvram file update from 2MB OVMF and CSM mode Because the LXD snap no longer ships these files they were not being detected as candidates which meant the logic to detect their usage and trigger an upgrade was not working. Instead change the logic to resolve the nvram target path to the OVMF vars file and then perform the 2MB/CSM logic on the target path file name instead. Also move this out of `generateQemuConfigFile` and combine with the existing logic in `start` to detect when the nvram file needs to be (re)generated. Signed-off-by: Thomas Parrott --- lxd/instance/drivers/driver_qemu.go | 50 +++++++++++++++++++---------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 8498b1a1f097..83d9f63ab013 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -1224,12 +1224,42 @@ func (d *qemu) start(stateful bool, op *operationlock.InstanceOperation) error { // Copy EDK2 settings firmware to nvram file if needed. // This firmware file can be modified by the VM so it must be copied from the defaults. - if d.architectureSupportsUEFI(d.architecture) && (!shared.PathExists(d.nvramPath()) || shared.IsTrue(d.localConfig["volatile.apply_nvram"])) { - err = d.setupNvram() - if err != nil { + if d.architectureSupportsUEFI(d.architecture) { + // ovmfNeedsUpdate checks if nvram file needs to be regenerated using new template. + ovmfNeedsUpdate := func(nvramTarget string) bool { + if shared.InSnap() && strings.Contains(nvramTarget, "OVMF") { + // The 2MB firmware was deprecated in the LXD snap. + // Detect this by the absence of "4MB" in the nvram file target. + if !strings.Contains(nvramTarget, "4MB") { + return true + } + + // The EDK2-based CSM firmwares were replaced with Seabios in the LXD snap. + // Detect this by the presence of "CSM" in the nvram file target. + if strings.Contains(nvramTarget, "CSM") { + return true + } + } + + return false + } + + // Check if nvram path and its target exist. + nvramPath := d.nvramPath() + nvramTarget, err := filepath.EvalSymlinks(nvramPath) + if err != nil && !errors.Is(err, fs.ErrNotExist) { op.Done(err) return err } + + // Decide if nvram file needs to be setup/refreshed. + if errors.Is(err, fs.ErrNotExist) || shared.IsTrue(d.localConfig["volatile.apply_nvram"]) || ovmfNeedsUpdate(nvramTarget) { + err = d.setupNvram() + if err != nil { + op.Done(err) + return err + } + } } // Clear volatile.apply_nvram if set. @@ -3161,20 +3191,6 @@ func (d *qemu) generateQemuConfigFile(cpuInfo *cpuTopology, mountInfo *storagePo return "", nil, fmt.Errorf("Unable to locate matching VM firmware: %+v", firmwares) } - // As 2MB firmware was deprecated in the LXD snap we have to regenerate NVRAM for VMs which used the 2MB one. - // As EDK2-based CSM firmwares were deprecated in the LXD snap we want to force VMs to start using SeaBIOS directly. - isOVMF2MB := (strings.Contains(efiCode, "OVMF") && !strings.Contains(efiCode, "4MB")) - isOVMFCSM := (strings.Contains(efiCode, "OVMF") && strings.Contains(efiCode, "CSM")) - if shared.InSnap() && (isOVMF2MB || isOVMFCSM) { - err = d.setupNvram() - if err != nil { - return "", nil, err - } - - // force to use a top-priority firmware - efiCode = firmwares[0].Code - } - // Use debug version of firmware. (Only works for "preferred" (OVMF 4MB, no CSM) firmware flavor) if shared.IsTrue(d.localConfig["boot.debug_edk2"]) && efiCode == firmwares[0].Code { efiCode = filepath.Join(filepath.Dir(efiCode), edk2.OVMFDebugFirmware)