Skip to content

Commit 8d4792a

Browse files
authored
Merge pull request #2061 from AkihiroSuda/custom-firmware
qemu, aarch64: use EDK2 from "Firmware/edk2 20231213 patches" (needed for QEMU v8.2.0)
2 parents 9e839a6 + b0bcd65 commit 8d4792a

File tree

8 files changed

+127
-6
lines changed

8 files changed

+127
-6
lines changed

examples/default.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,14 @@ firmware:
263263
# Use legacy BIOS instead of UEFI. Ignored for aarch64.
264264
# 🟢 Builtin default: false
265265
legacyBIOS: null
266+
# # Override UEFI images
267+
# # 🟢 Builtin default: uses VM's default UEFI, except for qemu + aarch64.
268+
# # See <https://lists.gnu.org/archive/html/qemu-devel/2023-12/msg01694.html>
269+
# images:
270+
# - location: "~/Downloads/edk2-aarch64-code.fd.gz"
271+
# arch: "aarch64"
272+
# digest: "sha256:..."
273+
# vmType: "qemu"
266274

267275
audio:
268276
# EXPERIMENTAL

pkg/limayaml/defaults.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,37 @@ func defaultGuestInstallPrefix() string {
112112
return "/usr/local"
113113
}
114114

115+
func defaultFirmwareImages() []FileWithVMType {
116+
return []FileWithVMType{
117+
/*
118+
The "Firmware/edk2 20231213" patches <https://lists.gnu.org/archive/html/qemu-devel/2023-12/msg01694.html>
119+
are necessary to boot most aarch64 images (except Debian, which does not use UEFI shim).
120+
121+
The patches are proposed for the QEMU v8.2.0 milestone, but likely to be postponed to v8.2.1.
122+
Until the patches get accepted in the QEMU upstream, Lima fetches the patched edk2 binary from
123+
<https://gitlab.com/kraxel/qemu/-/tags/firmware%2Fedk2-20231213-pull-request>.
124+
*/
125+
{
126+
File: File{
127+
Location: "https://gitlab.com/kraxel/qemu/-/raw/704f7cad5105246822686f65765ab92045f71a3b/pc-bios/edk2-aarch64-code.fd.bz2",
128+
Arch: AARCH64,
129+
Digest: "sha256:a5fc228623891297f2d82e22ea56ec57cde93fea5ec01abf543e4ed5cacaf277",
130+
},
131+
VMType: QEMU,
132+
},
133+
// Mirror
134+
{
135+
File: File{
136+
Location: "https://github.com/AkihiroSuda/qemu/raw/704f7cad5105246822686f65765ab92045f71a3b/pc-bios/edk2-aarch64-code.fd.bz2",
137+
Arch: AARCH64,
138+
Digest: "sha256:a5fc228623891297f2d82e22ea56ec57cde93fea5ec01abf543e4ed5cacaf277",
139+
},
140+
VMType: QEMU,
141+
},
142+
// TODO: what about ARMv7?
143+
}
144+
}
145+
115146
// FillDefault updates undefined fields in y with defaults from d (or built-in default), and overwrites with values from o.
116147
// Both d and o may be empty.
117148
//
@@ -283,6 +314,17 @@ func FillDefault(y, d, o *LimaYAML, filePath string) {
283314
y.Firmware.LegacyBIOS = ptr.Of(false)
284315
}
285316

317+
y.Firmware.Images = append(append(o.Firmware.Images, y.Firmware.Images...), d.Firmware.Images...)
318+
if len(y.Firmware.Images) == 0 {
319+
y.Firmware.Images = defaultFirmwareImages()
320+
}
321+
for i := range y.Firmware.Images {
322+
f := &y.Firmware.Images[i]
323+
if f.Arch == "" {
324+
f.Arch = *y.Arch
325+
}
326+
}
327+
286328
if y.SSH.LocalPort == nil {
287329
y.SSH.LocalPort = d.SSH.LocalPort
288330
}

pkg/limayaml/defaults_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,27 @@ func TestFillDefault(t *testing.T) {
181181
"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT\n-----END CERTIFICATE-----\n",
182182
},
183183
},
184+
Firmware: Firmware{
185+
LegacyBIOS: ptr.Of(false),
186+
Images: []FileWithVMType{
187+
{
188+
File: File{
189+
Location: "https://gitlab.com/kraxel/qemu/-/raw/704f7cad5105246822686f65765ab92045f71a3b/pc-bios/edk2-aarch64-code.fd.bz2",
190+
Arch: AARCH64,
191+
Digest: "sha256:a5fc228623891297f2d82e22ea56ec57cde93fea5ec01abf543e4ed5cacaf277",
192+
},
193+
VMType: QEMU,
194+
},
195+
{
196+
File: File{
197+
Location: "https://github.com/AkihiroSuda/qemu/raw/704f7cad5105246822686f65765ab92045f71a3b/pc-bios/edk2-aarch64-code.fd.bz2",
198+
Arch: AARCH64,
199+
Digest: "sha256:a5fc228623891297f2d82e22ea56ec57cde93fea5ec01abf543e4ed5cacaf277",
200+
},
201+
VMType: QEMU,
202+
},
203+
},
204+
},
184205
}
185206

186207
expect := builtin
@@ -252,6 +273,8 @@ func TestFillDefault(t *testing.T) {
252273
},
253274
}
254275

276+
expect.Firmware = y.Firmware
277+
255278
expect.Rosetta = Rosetta{
256279
Enabled: ptr.Of(false),
257280
BinFmt: ptr.Of(false),
@@ -299,6 +322,14 @@ func TestFillDefault(t *testing.T) {
299322
},
300323
Firmware: Firmware{
301324
LegacyBIOS: ptr.Of(true),
325+
Images: []FileWithVMType{
326+
{
327+
File: File{
328+
Location: "/dummy",
329+
Arch: X8664,
330+
},
331+
},
332+
},
302333
},
303334
Audio: Audio{
304335
Device: ptr.Of("coreaudio"),
@@ -427,6 +458,7 @@ func TestFillDefault(t *testing.T) {
427458
expect.CopyToHost = append(append([]CopyToHost{}, y.CopyToHost...), d.CopyToHost...)
428459
expect.Containerd.Archives = append(append([]File{}, y.Containerd.Archives...), d.Containerd.Archives...)
429460
expect.AdditionalDisks = append(append([]Disk{}, y.AdditionalDisks...), d.AdditionalDisks...)
461+
expect.Firmware.Images = append(append([]FileWithVMType{}, y.Firmware.Images...), d.Firmware.Images...)
430462

431463
// Mounts and Networks start with lowest priority first, so higher priority entries can overwrite
432464
expect.Mounts = append(append([]Mount{}, d.Mounts...), y.Mounts...)
@@ -580,6 +612,7 @@ func TestFillDefault(t *testing.T) {
580612
expect.CopyToHost = append(append(o.CopyToHost, y.CopyToHost...), d.CopyToHost...)
581613
expect.Containerd.Archives = append(append(o.Containerd.Archives, y.Containerd.Archives...), d.Containerd.Archives...)
582614
expect.AdditionalDisks = append(append(o.AdditionalDisks, y.AdditionalDisks...), d.AdditionalDisks...)
615+
expect.Firmware.Images = append(append(o.Firmware.Images, y.Firmware.Images...), d.Firmware.Images...)
583616

584617
expect.HostResolver.Hosts["default"] = d.HostResolver.Hosts["default"]
585618
expect.HostResolver.Hosts["MY.Host"] = d.HostResolver.Hosts["host.lima.internal"]

pkg/limayaml/limayaml.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ type File struct {
7777
Digest digest.Digest `yaml:"digest,omitempty" json:"digest,omitempty"`
7878
}
7979

80+
type FileWithVMType struct {
81+
File `yaml:",inline"`
82+
VMType VMType `yaml:"vmType,omitempty" json:"vmType,omitempty"`
83+
}
84+
8085
type Kernel struct {
8186
File `yaml:",inline"`
8287
Cmdline string `yaml:"cmdline,omitempty" json:"cmdline,omitempty"`
@@ -142,6 +147,10 @@ type Firmware struct {
142147
// LegacyBIOS disables UEFI if set.
143148
// LegacyBIOS is ignored for aarch64.
144149
LegacyBIOS *bool `yaml:"legacyBIOS,omitempty" json:"legacyBIOS,omitempty"`
150+
151+
// Images specify UEFI images (edk2-aarch64-code.fd.gz).
152+
// Defaults to built-in UEFI.
153+
Images []FileWithVMType `yaml:"images,omitempty" json:"images,omitempty"`
145154
}
146155

147156
type Audio struct {

pkg/qemu/qemu.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -569,12 +569,31 @@ func Cmdline(cfg Config) (exe string, args []string, err error) {
569569
logrus.Warnf("field `firmware.legacyBIOS` is not supported for architecture %q, ignoring", *y.Arch)
570570
legacyBIOS = false
571571
}
572-
if !legacyBIOS && *y.Arch != limayaml.RISCV64 {
573-
firmware, err := getFirmware(exe, *y.Arch)
574-
if err != nil {
575-
return "", nil, err
572+
if !legacyBIOS {
573+
var firmware string
574+
for _, f := range y.Firmware.Images {
575+
switch f.VMType {
576+
case "", limayaml.QEMU:
577+
if f.Arch == *y.Arch {
578+
firmwareCandidate := filepath.Join(cfg.InstanceDir, filenames.QemuEfiCodeFD)
579+
if _, err = fileutils.DownloadFile(firmwareCandidate, f.File, true, "UEFI code", *y.Arch); err != nil {
580+
logrus.WithError(err).Warnf("failed to download %q", f.Location)
581+
continue
582+
}
583+
firmware = firmwareCandidate
584+
break
585+
}
586+
}
587+
}
588+
if firmware == "" && *y.Arch != limayaml.RISCV64 {
589+
firmware, err = getFirmware(exe, *y.Arch)
590+
if err != nil {
591+
return "", nil, err
592+
}
593+
}
594+
if firmware != "" {
595+
args = append(args, "-drive", fmt.Sprintf("if=pflash,format=raw,readonly=on,file=%s", firmware))
576596
}
577-
args = append(args, "-drive", fmt.Sprintf("if=pflash,format=raw,readonly=on,file=%s", firmware))
578597
}
579598

580599
// Disk

pkg/store/filenames/filenames.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ const (
5353
HostAgentStdoutLog = "ha.stdout.log"
5454
HostAgentStderrLog = "ha.stderr.log"
5555
VzIdentifier = "vz-identifier"
56-
VzEfi = "vz-efi"
56+
VzEfi = "vz-efi" // efi variable store
57+
QemuEfiCodeFD = "qemu-efi-code.fd" // efi code; not always created
5758

5859
// SocketDir is the default location for forwarded sockets with a relative paths in HostSocket
5960
SocketDir = "sock"

pkg/vz/vz_driver_darwin.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ func (l *LimaVzDriver) Validate() error {
7878
if *l.Yaml.Firmware.LegacyBIOS {
7979
return fmt.Errorf("`firmware.legacyBIOS` configuration is not supported for VZ driver")
8080
}
81+
for _, f := range l.Yaml.Firmware.Images {
82+
switch f.VMType {
83+
case "", limayaml.VZ:
84+
if f.Arch == *l.Yaml.Arch {
85+
return fmt.Errorf("`firmware.images` configuration is not supported for VZ driver")
86+
}
87+
}
88+
}
8189
if unknown := reflectutil.UnknownNonEmptyFields(l.Yaml, knownYamlProperties...); len(unknown) > 0 {
8290
logrus.Warnf("vmType %s: ignoring %+v", *l.Yaml.VMType, unknown)
8391
}

website/content/en/docs/dev/Internals/_index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ kernel:
4848
QEMU:
4949
- `qemu.pid`: QEMU PID
5050
- `qmp.sock`: QMP socket
51+
- `qemu-efi-code.fd`: QEMU UEFI code (not always present)
5152

5253
VZ:
5354
- `vz.pid`: VZ PID

0 commit comments

Comments
 (0)