From b730775eea4e05596942f47d8c3c3a3315a25f77 Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Sat, 22 Jul 2023 16:27:38 +0200 Subject: [PATCH] Add support for arm64 hosts This adds infrastructure for support different types of host systems, specifically adding arm64 support. Tested on an rpi4 with Debian bullseye Based on a patch by Ryan Gonzalez Signed-off-by: Sjoerd Simons --- backend.go | 4 ++-- backend_qemu.go | 46 +++++++++++++++++++++++++++++++++++++++++----- backend_uml.go | 9 +++++++-- machine.go | 33 ++++++++++++++++++++++++++++----- 4 files changed, 78 insertions(+), 14 deletions(-) diff --git a/backend.go b/backend.go index 7fbada5..478911a 100644 --- a/backend.go +++ b/backend.go @@ -1,5 +1,5 @@ -//go:build linux && amd64 -// +build linux,amd64 +//go:build linux +// +build linux package fakemachine diff --git a/backend_qemu.go b/backend_qemu.go index ab44790..840e7bd 100644 --- a/backend_qemu.go +++ b/backend_qemu.go @@ -1,5 +1,4 @@ -//go:build linux && amd64 -// +build linux,amd64 +//go:build linux && (arm64 || amd64) package fakemachine @@ -35,8 +34,40 @@ func (b qemuBackend) Supported() (bool, error) { return true, nil } +type qemuMachine struct { + binary string + console string + machine string + /* Cpu to use for qemu backend if the architecture doesn't have a good default */ + qemuCPU string +} + +var qemuMachines = map[Arch]qemuMachine{ + Amd64: { + binary: "qemu-system-x86_64", + console: "ttyS0", + machine: "pc", + }, + Arm64: { + binary: "qemu-system-aarch64", + console: "ttyAMA0", + machine: "virt", + /* The default cpu is a 32 bit one, which isn't very usefull + * for 64 bit arm. There is no cpu setting for "minimal" 64 + * bit linux capable processor. The only generic setting + * is "max", but that can be very slow to emulate. So pick + * a specific small cortex-a processor instead */ + qemuCPU: "cortex-a53", + }, +} + func (b qemuBackend) QemuPath() (string, error) { - return exec.LookPath("qemu-system-x86_64") + machine, ok := qemuMachines[b.machine.arch] + if !ok { + return "", fmt.Errorf("unsupported arch for qemu: %s", b.machine.arch) + } + + return exec.LookPath(machine.binary) } func (b qemuBackend) KernelRelease() (string, error) { @@ -166,6 +197,7 @@ func (b qemuBackend) Start() (bool, error) { func (b qemuBackend) StartQemu(kvm bool) (bool, error) { m := b.machine + qemuMachine := qemuMachines[m.arch] kernelPath, err := b.KernelPath() if err != nil { @@ -173,7 +205,7 @@ func (b qemuBackend) StartQemu(kvm bool) (bool, error) { } memory := fmt.Sprintf("%d", m.memory) numcpus := fmt.Sprintf("%d", m.numcpus) - qemuargs := []string{"qemu-system-x86_64", + qemuargs := []string{qemuMachine.binary, "-smp", numcpus, "-m", memory, "-kernel", kernelPath, @@ -185,9 +217,13 @@ func (b qemuBackend) StartQemu(kvm bool) (bool, error) { qemuargs = append(qemuargs, "-cpu", "host", "-enable-kvm") + } else if qemuMachine.qemuCPU != "" { + qemuargs = append(qemuargs, "-cpu", qemuMachine.qemuCPU) } - kernelargs := []string{"console=ttyS0", "panic=-1", + qemuargs = append(qemuargs, "-machine", qemuMachine.machine) + console := fmt.Sprintf("console=%s", qemuMachine.console) + kernelargs := []string{console, "panic=-1", "systemd.unit=fakemachine.service"} if m.showBoot { diff --git a/backend_uml.go b/backend_uml.go index 36f48d0..fa5a589 100644 --- a/backend_uml.go +++ b/backend_uml.go @@ -1,5 +1,5 @@ -//go:build linux && amd64 -// +build linux,amd64 +//go:build linux +// +build linux package fakemachine @@ -27,6 +27,11 @@ func (b umlBackend) Name() string { } func (b umlBackend) Supported() (bool, error) { + // only support amd64 + if b.machine.arch != Amd64 { + return false, fmt.Errorf("unsupported arch: %s", b.machine.arch) + } + // check the kernel exists if _, err := b.KernelPath(); err != nil { return false, err diff --git a/machine.go b/machine.go index bd94f94..1fdfc35 100644 --- a/machine.go +++ b/machine.go @@ -1,5 +1,4 @@ -//go:build linux && amd64 -// +build linux,amd64 +//go:build linux && (arm64 || amd64) package fakemachine @@ -153,6 +152,23 @@ func realDir(path string) (string, error) { return filepath.Dir(p), nil } +type Arch string + +const ( + Amd64 Arch = "amd64" + Arm64 Arch = "arm64" +) + +var archMap = map[string]Arch{ + "amd64": Amd64, + "arm64": Arm64, +} + +var archDynamicLinker = map[Arch]string{ + Amd64: "/lib64/ld-linux-x86-64.so.2", + Arm64: "/lib/ld-linux-aarch64.so.1", +} + type mountPoint struct { hostDirectory string machineDirectory string @@ -166,6 +182,7 @@ type image struct { } type Machine struct { + arch Arch backend backend mounts []mountPoint count int @@ -193,6 +210,11 @@ func NewMachineWithBackend(backendName string) (*Machine, error) { var err error m := &Machine{memory: 2048, numcpus: runtime.NumCPU()} + var ok bool + if m.arch, ok = archMap[runtime.GOARCH]; !ok { + return nil, fmt.Errorf("unsupported arch %s", runtime.GOARCH) + } + m.backend, err = newBackend(backendName, m) if err != nil { return nil, err @@ -669,6 +691,7 @@ func (m *Machine) startup(command string, extracontent [][2]string) (int, error) {Target: "/usr/sbin", Link: "/sbin", Perm: 0755}, {Target: "/usr/bin", Link: "/bin", Perm: 0755}, {Target: "/usr/lib", Link: "/lib", Perm: 0755}, + {Target: "/usr/lib64", Link: "/lib64", Perm: 0755}, }) if err != nil { return -1, err @@ -704,14 +727,14 @@ func (m *Machine) startup(command string, extracontent [][2]string) (int, error) return -1, err } - /* Amd64 dynamic linker */ - err = w.CopyFile("/lib64/ld-linux-x86-64.so.2") + dynamicLinker := archDynamicLinker[m.arch] + err = w.CopyFile(prefix + dynamicLinker) if err != nil { return -1, err } /* C libraries */ - libraryDir, err := realDir("/lib64/ld-linux-x86-64.so.2") + libraryDir, err := realDir(dynamicLinker) if err != nil { return -1, err }