Skip to content

Commit 9846a6c

Browse files
committed
Add optional loading of UEFI firmware via -bios parameter in QEMU
Limiting this to X8664 arch as there is no information if this works for other architectures. This allows to load UEFI firmware on Windows hosts, where pflash can't be used in combination with WHPX acceleration. Default to false on non-Windows hosts and true on Windows (to have working default). Signed-off-by: Arthur Sengileyev <arthur.sengileyev@gmail.com>
1 parent b55086f commit 9846a6c

File tree

5 files changed

+108
-11
lines changed

5 files changed

+108
-11
lines changed

pkg/limayaml/defaults.go

+10
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,16 @@ func FillDefault(y, d, o *LimaYAML, filePath string, warn bool) {
394394
y.Firmware.LegacyBIOS = ptr.Of(false)
395395
}
396396

397+
if y.Firmware.CompatUEFIViaBIOS == nil {
398+
y.Firmware.CompatUEFIViaBIOS = d.Firmware.CompatUEFIViaBIOS
399+
}
400+
if o.Firmware.CompatUEFIViaBIOS != nil {
401+
y.Firmware.CompatUEFIViaBIOS = o.Firmware.CompatUEFIViaBIOS
402+
}
403+
if y.Firmware.CompatUEFIViaBIOS == nil {
404+
y.Firmware.CompatUEFIViaBIOS = ptr.Of(runtime.GOOS == "windows")
405+
}
406+
397407
y.Firmware.Images = append(append(o.Firmware.Images, y.Firmware.Images...), d.Firmware.Images...)
398408
for i := range y.Firmware.Images {
399409
f := &y.Firmware.Images[i]

pkg/limayaml/defaults_test.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ func TestFillDefault(t *testing.T) {
9292
},
9393
TimeZone: ptr.Of(hostTimeZone()),
9494
Firmware: Firmware{
95-
LegacyBIOS: ptr.Of(false),
95+
LegacyBIOS: ptr.Of(false),
96+
CompatUEFIViaBIOS: ptr.Of(runtime.GOOS == "windows"),
9697
},
9798
Audio: Audio{
9899
Device: ptr.Of(""),
@@ -207,6 +208,7 @@ func TestFillDefault(t *testing.T) {
207208
VMType: QEMU,
208209
},
209210
},
211+
CompatUEFIViaBIOS: ptr.Of(false),
210212
},
211213
}
212214

@@ -361,6 +363,7 @@ func TestFillDefault(t *testing.T) {
361363
},
362364
},
363365
},
366+
CompatUEFIViaBIOS: ptr.Of(runtime.GOOS != "windows"),
364367
},
365368
Audio: Audio{
366369
Device: ptr.Of("coreaudio"),
@@ -568,7 +571,8 @@ func TestFillDefault(t *testing.T) {
568571
},
569572
TimeZone: ptr.Of("Universal"),
570573
Firmware: Firmware{
571-
LegacyBIOS: ptr.Of(true),
574+
LegacyBIOS: ptr.Of(true),
575+
CompatUEFIViaBIOS: ptr.Of(true),
572576
},
573577
Audio: Audio{
574578
Device: ptr.Of("coreaudio"),

pkg/limayaml/limayaml.go

+5
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,11 @@ type Firmware struct {
182182
// LegacyBIOS is ignored for aarch64.
183183
LegacyBIOS *bool `yaml:"legacyBIOS,omitempty" json:"legacyBIOS,omitempty" jsonschema:"nullable"`
184184

185+
// CompatUEFIViaBIOS forces QEMU to load concatenated firmware with -bios option
186+
// CompatUEFIViaBIOS is ignored for non x86_64
187+
// This should be deprecated, if the issue in QEMU is fixed https://gitlab.com/qemu-project/qemu/-/issues/513
188+
CompatUEFIViaBIOS *bool `yaml:"compatUEFIInBIOS" json:"compatUEFIInBIOS,omitempty" jsonschema:"nullable"` // default: false on non Windows and true on Windows
189+
185190
// Images specify UEFI images (edk2-aarch64-code.fd.gz).
186191
// Defaults to built-in UEFI.
187192
Images []FileWithVMType `yaml:"images,omitempty" json:"images,omitempty"`

pkg/qemu/qemu.go

+76-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding/json"
77
"errors"
88
"fmt"
9+
"io"
910
"io/fs"
1011
"os"
1112
"os/exec"
@@ -624,9 +625,44 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
624625
return "", nil, err
625626
}
626627
logrus.Infof("Using system firmware (%q)", firmware)
628+
if *y.Firmware.CompatUEFIViaBIOS && *y.Arch == limayaml.X8664 {
629+
firmwareVars, err := getFirmwareVars(exe, *y.Arch)
630+
if err != nil {
631+
return "", nil, err
632+
}
633+
logrus.Infof("Using system firmware vars (%q)", firmwareVars)
634+
varsFile, err := os.Open(firmwareVars)
635+
if err != nil {
636+
return "", nil, err
637+
}
638+
defer varsFile.Close()
639+
codeFile, err := os.Open(firmware)
640+
if err != nil {
641+
return "", nil, err
642+
}
643+
defer codeFile.Close()
644+
downloadedFile, err := os.OpenFile(downloadedFirmware, os.O_CREATE|os.O_WRONLY, 0o644)
645+
if err != nil {
646+
return "", nil, err
647+
}
648+
defer downloadedFile.Close()
649+
_, err = io.Copy(downloadedFile, varsFile)
650+
if err != nil {
651+
return "", nil, err
652+
}
653+
_, err = io.Copy(downloadedFile, codeFile)
654+
if err != nil {
655+
return "", nil, err
656+
}
657+
firmware = downloadedFirmware
658+
}
627659
}
628660
if firmware != "" {
629-
args = append(args, "-drive", fmt.Sprintf("if=pflash,format=raw,readonly=on,file=%s", firmware))
661+
if *y.Firmware.CompatUEFIViaBIOS && *y.Arch == limayaml.X8664 {
662+
args = append(args, "-bios", firmware)
663+
} else {
664+
args = append(args, "-drive", fmt.Sprintf("if=pflash,format=raw,readonly=on,file=%s", firmware))
665+
}
630666
}
631667
}
632668

@@ -1120,9 +1156,11 @@ func getFirmware(qemuExe string, arch limayaml.Arch) (string, error) {
11201156
userLocalDir := filepath.Join(currentUser.HomeDir, ".local") // "$HOME/.local"
11211157

11221158
relativePath := fmt.Sprintf("share/qemu/edk2-%s-code.fd", qemuEdk2Arch(arch))
1159+
relativePathWin := fmt.Sprintf("share/edk2-%s-code.fd", qemuEdk2Arch(arch))
11231160
candidates := []string{
11241161
filepath.Join(userLocalDir, relativePath), // XDG-like
11251162
filepath.Join(localDir, relativePath), // macOS (homebrew)
1163+
filepath.Join(binDir, relativePathWin), // Windows installer
11261164
}
11271165

11281166
switch arch {
@@ -1164,3 +1202,40 @@ func getFirmware(qemuExe string, arch limayaml.Arch) (string, error) {
11641202
qemuArch := strings.TrimPrefix(filepath.Base(qemuExe), "qemu-system-")
11651203
return "", fmt.Errorf("could not find firmware for %q (hint: try copying the \"edk-%s-code.fd\" firmware to $HOME/.local/share/qemu/)", arch, qemuArch)
11661204
}
1205+
1206+
func getFirmwareVars(qemuExe string, arch limayaml.Arch) (string, error) {
1207+
var targetArch string
1208+
switch arch {
1209+
case limayaml.X8664:
1210+
targetArch = "i386"
1211+
default:
1212+
return "", fmt.Errorf("unexpected architecture: %q", arch)
1213+
}
1214+
1215+
currentUser, err := user.Current()
1216+
if err != nil {
1217+
return "", err
1218+
}
1219+
1220+
binDir := filepath.Dir(qemuExe) // "/usr/local/bin"
1221+
localDir := filepath.Dir(binDir) // "/usr/local"
1222+
userLocalDir := filepath.Join(currentUser.HomeDir, ".local") // "$HOME/.local"
1223+
1224+
relativePath := fmt.Sprintf("share/qemu/edk2-%s-vars.fd", qemuEdk2Arch(targetArch))
1225+
relativePathWin := fmt.Sprintf("share/edk2-%s-vars.fd", qemuEdk2Arch(targetArch))
1226+
candidates := []string{
1227+
filepath.Join(userLocalDir, relativePath), // XDG-like
1228+
filepath.Join(localDir, relativePath), // macOS (homebrew)
1229+
filepath.Join(binDir, relativePathWin), // Windows installer
1230+
}
1231+
1232+
logrus.Debugf("firmware vars candidates = %v", candidates)
1233+
1234+
for _, f := range candidates {
1235+
if _, err := os.Stat(f); err == nil {
1236+
return f, nil
1237+
}
1238+
}
1239+
1240+
return "", fmt.Errorf("could not find firmware vars for %q", arch)
1241+
}

templates/default.yaml

+11-8
Original file line numberDiff line numberDiff line change
@@ -337,14 +337,17 @@ firmware:
337337
# Use legacy BIOS instead of UEFI. Ignored for aarch64 and vz.
338338
# 🟢 Builtin default: false
339339
legacyBIOS: null
340-
# # Override UEFI images
341-
# # 🟢 Builtin default: uses VM's default UEFI, except for qemu + aarch64.
342-
# # See <https://lists.gnu.org/archive/html/qemu-devel/2023-12/msg01694.html>
343-
# images:
344-
# - location: "~/Downloads/edk2-aarch64-code.fd.gz"
345-
# arch: "aarch64"
346-
# digest: "sha256:..."
347-
# vmType: "qemu"
340+
# # Override UEFI images
341+
# # 🟢 Builtin default: uses VM's default UEFI, except for qemu + aarch64.
342+
# # See <https://lists.gnu.org/archive/html/qemu-devel/2023-12/msg01694.html>
343+
# images:
344+
# - location: "~/Downloads/edk2-aarch64-code.fd.gz"
345+
# arch: "aarch64"
346+
# digest: "sha256:..."
347+
# vmType: "qemu"
348+
# Loads UEFI usigin -bios parameter for compatibility reasons. Applied only for QEMU x86_64.
349+
# 🟢 Builtin default: false on non-Windows and true on Windows
350+
compatUEFIInBIOS: null
348351

349352
audio:
350353
# EXPERIMENTAL

0 commit comments

Comments
 (0)