@@ -12,6 +12,7 @@ import (
12
12
"strconv"
13
13
"strings"
14
14
15
+ "github.com/coreos/go-semver/semver"
15
16
"github.com/docker/go-units"
16
17
"github.com/lima-vm/lima/pkg/downloader"
17
18
"github.com/lima-vm/lima/pkg/iso9660util"
@@ -155,6 +156,9 @@ type features struct {
155
156
// e.g. "Supported machines are:\nakita...\n...virt-6.2...\n...virt-7.0...\n...\n"
156
157
// Not machine-readable, but checking strings.Contains() should be fine.
157
158
MachineHelp []byte
159
+
160
+ // VersionGEQ7 is true when the QEMU version seems v7.0.0 or later
161
+ VersionGEQ7 bool
158
162
}
159
163
160
164
func inspectFeatures (exe string ) (* features , error ) {
@@ -198,6 +202,7 @@ func inspectFeatures(exe string) (*features, error) {
198
202
f .MachineHelp = stderr .Bytes ()
199
203
}
200
204
}
205
+ f .VersionGEQ7 = strings .Contains (string (f .MachineHelp ), "-7.0" )
201
206
202
207
return & f , nil
203
208
}
@@ -218,8 +223,7 @@ func showDarwinARM64HVFQEMU620Warning(exe, accel string, features *features) {
218
223
if accel != "hvf" {
219
224
return
220
225
}
221
- if strings .Contains (string (features .MachineHelp ), "virt-7.0" ) {
222
- // QEMU 7.0.0 or later
226
+ if features .VersionGEQ7 {
223
227
return
224
228
}
225
229
if exeFull , err := exec .LookPath (exe ); err == nil {
@@ -243,6 +247,63 @@ func showDarwinARM64HVFQEMU620Warning(exe, accel string, features *features) {
243
247
logrus .Warn (w )
244
248
}
245
249
250
+ func getMacOSProductVersion () (* semver.Version , error ) {
251
+ cmd := exec .Command ("sw_vers" , "-productVersion" )
252
+ // output is like "12.3.1\n"
253
+ b , err := cmd .Output ()
254
+ if err != nil {
255
+ return nil , fmt .Errorf ("failed to execute %v: %w" , cmd .Args , err )
256
+ }
257
+ verTrimmed := strings .TrimSpace (string (b ))
258
+ verSem , err := semver .NewVersion (verTrimmed )
259
+ if err != nil {
260
+ return nil , fmt .Errorf ("failed to parse macOS version %q: %w" , verTrimmed , err )
261
+ }
262
+ return verSem , nil
263
+ }
264
+
265
+ // adjustMemBytesDarwinARM64HVF adjusts the memory to be <= 3 GiB, only when the following conditions are met:
266
+ //
267
+ // - Host OS < macOS 12.4
268
+ // - Host Arch == arm64
269
+ // - Accel == hvf
270
+ // - QEMU >= 7.0
271
+ //
272
+ // This adjustment is required for avoiding host kernel panic. The issue was fixed in macOS 12.4 Beta 1.
273
+ // See https://github.com/lima-vm/lima/issues/795 https://gitlab.com/qemu-project/qemu/-/issues/903#note_911000975
274
+ func adjustMemBytesDarwinARM64HVF (memBytes int64 , accel string , features * features ) int64 {
275
+ const safeSize = 3 * 1024 * 1024 * 1024 // 3 GiB
276
+ if memBytes <= safeSize {
277
+ return memBytes
278
+ }
279
+ if runtime .GOOS != "darwin" {
280
+ return memBytes
281
+ }
282
+ if runtime .GOARCH != "arm64" {
283
+ return memBytes
284
+ }
285
+ if accel != "hvf" {
286
+ return memBytes
287
+ }
288
+ if ! features .VersionGEQ7 {
289
+ return memBytes
290
+ }
291
+ macOSProductVersion , err := getMacOSProductVersion ()
292
+ if err != nil {
293
+ logrus .Warn (err )
294
+ return memBytes
295
+ }
296
+ if ! macOSProductVersion .LessThan (* semver .New ("12.4.0" )) {
297
+ return memBytes
298
+ }
299
+ logrus .Warnf ("Reducing the guest memory from %s to %s, to avoid host kernel panic on macOS <= 12.3 with QEMU >= 7.0; " +
300
+ "Please update macOS to 12.4 or later, or downgrade QEMU to 6.2; " +
301
+ "See https://github.com/lima-vm/lima/issues/795 for the further background." ,
302
+ units .BytesSize (float64 (memBytes )), units .BytesSize (float64 (safeSize )))
303
+ memBytes = safeSize
304
+ return memBytes
305
+ }
306
+
246
307
func Cmdline (cfg Config ) (string , []string , error ) {
247
308
y := cfg .LimaYAML
248
309
exe , args , err := getExe (* y .Arch )
@@ -262,6 +323,15 @@ func Cmdline(cfg Config) (string, []string, error) {
262
323
}
263
324
showDarwinARM64HVFQEMU620Warning (exe , accel , features )
264
325
326
+ // Memory
327
+ memBytes , err := units .RAMInBytes (* y .Memory )
328
+ if err != nil {
329
+ return "" , nil , err
330
+ }
331
+ memBytes = adjustMemBytesDarwinARM64HVF (memBytes , accel , features )
332
+ args = appendArgsIfNoConflict (args , "-m" , strconv .Itoa (int (memBytes >> 20 )))
333
+
334
+ // CPU
265
335
cpu := y .CPUType [* y .Arch ]
266
336
args = appendArgsIfNoConflict (args , "-cpu" , cpu )
267
337
switch * y .Arch {
@@ -285,7 +355,8 @@ func Cmdline(cfg Config) (string, []string, error) {
285
355
// QEMU < 7.0 requires highmem=off to be set, otherwise fails with "VCPU supports less PA bits (36) than requested by the memory map (40)"
286
356
// https://github.com/lima-vm/lima/issues/680
287
357
// https://github.com/lima-vm/lima/pull/24
288
- if ! strings .Contains (string (features .MachineHelp ), "virt-7.0" ) {
358
+ // But when the memory size is <= 3 GiB, we can always set highmem=off.
359
+ if ! features .VersionGEQ7 || memBytes <= 3 * 1024 * 1024 * 1024 {
289
360
machine += ",highmem=off"
290
361
}
291
362
args = appendArgsIfNoConflict (args , "-machine" , machine )
@@ -295,13 +366,6 @@ func Cmdline(cfg Config) (string, []string, error) {
295
366
args = appendArgsIfNoConflict (args , "-smp" ,
296
367
fmt .Sprintf ("%d,sockets=1,cores=%d,threads=1" , * y .CPUs , * y .CPUs ))
297
368
298
- // Memory
299
- memBytes , err := units .RAMInBytes (* y .Memory )
300
- if err != nil {
301
- return "" , nil , err
302
- }
303
- args = appendArgsIfNoConflict (args , "-m" , strconv .Itoa (int (memBytes >> 20 )))
304
-
305
369
// Firmware
306
370
legacyBIOS := * y .Firmware .LegacyBIOS
307
371
if legacyBIOS && * y .Arch != limayaml .X8664 {
0 commit comments