Skip to content

Commit

Permalink
qemu: adjust the memory size for workarounding M1 panic
Browse files Browse the repository at this point in the history
Adjust the memory to be less than 4 GiB, only when the following conditions are met:

- memBytes  >= 4 GiB
- Host OS   <  macOS 12.4
- Host Arch == arm64
- Accel     == hvf
- QEMU      >= 7.0

This adjustment is required for avoiding host kernel panic. The issue was fixed in macOS 12.4 Beta 1.
See https://gitlab.com/qemu-project/qemu/-/issues/903#note_911000975

Signed-off-by: Akihiro Suda <[email protected]>
  • Loading branch information
AkihiroSuda committed Apr 14, 2022
1 parent 41e3bb7 commit 64111b0
Showing 1 changed file with 66 additions and 8 deletions.
74 changes: 66 additions & 8 deletions pkg/qemu/qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strconv"
"strings"

"github.com/coreos/go-semver/semver"
"github.com/docker/go-units"
"github.com/lima-vm/lima/pkg/downloader"
"github.com/lima-vm/lima/pkg/iso9660util"
Expand Down Expand Up @@ -246,6 +247,60 @@ func showDarwinARM64HVFQEMU620Warning(exe, accel string, features *features) {
logrus.Warn(w)
}

func getMacOSProductVersion() (*semver.Version, error) {
cmd := exec.Command("sw_vers", "-productVersion")
b, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("failed to execute %v: %w", cmd.Args, err)
}
ver, err := semver.NewVersion(string(b))
if err != nil {
return nil, fmt.Errorf("failed to parse macOS version %q: %w", string(b), err)
}
return ver, nil
}

// adjustMemBytesDarwinARM64HVF adjusts the memory to be less than 4 GiB, only when the following conditions are met:
//
// - memBytes >= 4 GiB
// - Host OS < macOS 12.4
// - Host Arch == arm64
// - Accel == hvf
// - QEMU >= 7.0
//
// This adjustment is required for avoiding host kernel panic. The issue was fixed in macOS 12.4 Beta 1.
// See https://github.com/lima-vm/lima/issues/795 https://gitlab.com/qemu-project/qemu/-/issues/903#note_911000975
func adjustMemBytesDarwinARM64HVF(memBytes int64, accel string, features *features) int64 {
if memBytes < (1 << 32) { // less than 4GiB
return memBytes
}
if runtime.GOOS != "darwin" {
return memBytes
}
if runtime.GOARCH != "arm64" {
return memBytes
}
if accel != "hvf" {
return memBytes
}
if !features.VersionGEQ7 {
return memBytes
}
macOSProductVersion, err := getMacOSProductVersion()
if err != nil {
logrus.Warn(err)
return memBytes
}
if !macOSProductVersion.LessThan(*semver.New("12.4.0")) {
return memBytes
}
const safeSize = 1 << 31 // 2GiB
logrus.Warnf("Reducing the guest memory from %s to %s, to avoid host kernel panic on macOS <= 12.3; Please update macOS to 12.4 or later",
units.BytesSize(float64(memBytes)), units.BytesSize(float64(safeSize)))
memBytes = safeSize
return memBytes
}

func Cmdline(cfg Config) (string, []string, error) {
y := cfg.LimaYAML
exe, args, err := getExe(*y.Arch)
Expand All @@ -265,6 +320,15 @@ func Cmdline(cfg Config) (string, []string, error) {
}
showDarwinARM64HVFQEMU620Warning(exe, accel, features)

// Memory
memBytes, err := units.RAMInBytes(*y.Memory)
if err != nil {
return "", nil, err
}
memBytes = adjustMemBytesDarwinARM64HVF(memBytes, accel, features)
args = appendArgsIfNoConflict(args, "-m", strconv.Itoa(int(memBytes>>20)))

// CPU
cpu := y.CPUType[*y.Arch]
args = appendArgsIfNoConflict(args, "-cpu", cpu)
switch *y.Arch {
Expand All @@ -288,7 +352,8 @@ func Cmdline(cfg Config) (string, []string, error) {
// 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)"
// https://github.com/lima-vm/lima/issues/680
// https://github.com/lima-vm/lima/pull/24
if !features.VersionGEQ7 {
// But when the memory size is less than 4 GiB, we can always set highmem=off.
if !features.VersionGEQ7 || memBytes < (1<<32) {
machine += ",highmem=off"
}
args = appendArgsIfNoConflict(args, "-machine", machine)
Expand All @@ -298,13 +363,6 @@ func Cmdline(cfg Config) (string, []string, error) {
args = appendArgsIfNoConflict(args, "-smp",
fmt.Sprintf("%d,sockets=1,cores=%d,threads=1", *y.CPUs, *y.CPUs))

// Memory
memBytes, err := units.RAMInBytes(*y.Memory)
if err != nil {
return "", nil, err
}
args = appendArgsIfNoConflict(args, "-m", strconv.Itoa(int(memBytes>>20)))

// Firmware
legacyBIOS := *y.Firmware.LegacyBIOS
if legacyBIOS && *y.Arch != limayaml.X8664 {
Expand Down

0 comments on commit 64111b0

Please sign in to comment.