Skip to content

Commit

Permalink
VM: Fix nvram file update from 2MB OVMF and CSM mode (#14555)
Browse files Browse the repository at this point in the history
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. LXD 6.1 <= didn't check for the
existence of the firmware candidates which meant the upgrade logic still
worked.

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.
  • Loading branch information
tomponline authored Nov 29, 2024
2 parents 6c29a10 + a014ad5 commit dc7b9b1
Showing 1 changed file with 33 additions and 17 deletions.
50 changes: 33 additions & 17 deletions lxd/instance/drivers/driver_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit dc7b9b1

Please sign in to comment.