mirror of
https://github.com/siderolabs/talos.git
synced 2025-12-14 14:01:12 +01:00
Support booting with SecureBoot on aarch64 with `talosctl cluster create` with QEMU provisioner. Signed-off-by: Noel Georgi <git@frezbo.dev>
255 lines
5.8 KiB
Go
255 lines
5.8 KiB
Go
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package qemu
|
|
|
|
import (
|
|
"fmt"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"slices"
|
|
)
|
|
|
|
// Arch abstracts away differences between different architectures.
|
|
type Arch string
|
|
|
|
// Arch constants.
|
|
const (
|
|
ArchAmd64 Arch = "amd64"
|
|
ArchArm64 Arch = "arm64"
|
|
)
|
|
|
|
// Valid checks whether the architecture is supported.
|
|
func (arch Arch) Valid() bool {
|
|
switch arch {
|
|
case ArchAmd64, ArchArm64:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// QemuArch defines which qemu binary to use.
|
|
func (arch Arch) QemuArch() string {
|
|
switch arch {
|
|
case ArchAmd64:
|
|
return "x86_64"
|
|
case ArchArm64:
|
|
return "aarch64"
|
|
default:
|
|
panic("unsupported architecture")
|
|
}
|
|
}
|
|
|
|
// QemuMachine defines the machine type for qemu.
|
|
func (arch Arch) QemuMachine() string {
|
|
switch arch {
|
|
case ArchAmd64:
|
|
return "q35"
|
|
case ArchArm64:
|
|
return "virt,gic-version=max"
|
|
default:
|
|
panic("unsupported architecture")
|
|
}
|
|
}
|
|
|
|
// Console defines proper argument for the kernel to send logs to serial console.
|
|
func (arch Arch) Console() string {
|
|
switch arch {
|
|
case ArchAmd64:
|
|
return "ttyS0"
|
|
case ArchArm64:
|
|
return "ttyAMA0,115200n8"
|
|
default:
|
|
panic("unsupported architecture")
|
|
}
|
|
}
|
|
|
|
// PFlash for UEFI boot.
|
|
type PFlash struct {
|
|
Size int64
|
|
SourcePaths []string
|
|
}
|
|
|
|
// PFlash returns settings for parallel flash.
|
|
func (arch Arch) PFlash(uefiEnabled bool, extraUEFISearchPaths []string) []PFlash {
|
|
switch arch {
|
|
case ArchArm64:
|
|
// default search paths
|
|
uefiSourcePathPrefixes := []string{
|
|
"/usr/share/AAVMF", // most standard location
|
|
"/usr/share/qemu-efi-aarch64",
|
|
"/usr/share/OVMF",
|
|
"/usr/share/edk2/aarch64", // Fedora
|
|
"/usr/share/edk2/experimental", // Fedora
|
|
}
|
|
|
|
// Secure boot enabled firmware files
|
|
uefiSourceFiles := []string{
|
|
"AAVMF_CODE.secboot.fd", // debian, EFI vars not protected
|
|
"QEMU_EFI.secboot.testonly.fd", // Fedora, ref: https://bugzilla.redhat.com/show_bug.cgi?id=1882135
|
|
}
|
|
|
|
// Non-secure boot firmware files
|
|
uefiSourceFilesInsecure := []string{
|
|
"AAVMF_CODE.fd",
|
|
"QEMU_EFI.fd",
|
|
"OVMF.stateless.fd",
|
|
}
|
|
|
|
// Empty vars files
|
|
uefiVarsFiles := []string{
|
|
"AAVMF_VARS.fd",
|
|
"QEMU_VARS.fd",
|
|
}
|
|
|
|
// Append extra search paths
|
|
uefiSourcePathPrefixes = append(uefiSourcePathPrefixes, extraUEFISearchPaths...)
|
|
|
|
uefiSourcePaths, uefiVarsPaths := generateUEFIPFlashList(uefiSourcePathPrefixes, uefiSourceFiles, uefiVarsFiles, uefiSourceFilesInsecure)
|
|
|
|
return []PFlash{
|
|
{
|
|
Size: 64 * 1024 * 1024,
|
|
SourcePaths: uefiSourcePaths,
|
|
},
|
|
{
|
|
SourcePaths: uefiVarsPaths,
|
|
Size: 64 * 1024 * 1024,
|
|
},
|
|
}
|
|
case ArchAmd64:
|
|
if !uefiEnabled {
|
|
return nil
|
|
}
|
|
|
|
// Default search paths
|
|
uefiSourcePathPrefixes := []string{
|
|
"/usr/share/ovmf",
|
|
"/usr/share/OVMF",
|
|
"/usr/share/qemu",
|
|
}
|
|
|
|
// Secure boot enabled firmware files
|
|
uefiSourceFiles := []string{
|
|
"OVMF_CODE_4M.secboot.fd",
|
|
"OVMF_CODE.secboot.fd",
|
|
"OVMF.secboot.fd",
|
|
"edk2-x86_64-secure-code.fd", // Alpine Linux
|
|
"ovmf-x86_64-ms-4m-code.bin",
|
|
}
|
|
|
|
// Non-secure boot firmware files
|
|
uefiSourceFilesInsecure := []string{
|
|
"OVMF_CODE_4M.fd",
|
|
"OVMF_CODE.fd",
|
|
"OVMF.fd",
|
|
"ovmf-x86_64-4m-code.bin",
|
|
}
|
|
|
|
// Empty vars files
|
|
uefiVarsFiles := []string{
|
|
"OVMF_VARS_4M.fd",
|
|
"OVMF_VARS.fd",
|
|
"ovmf-x86_64-4m-vars.bin",
|
|
}
|
|
|
|
// Append extra search paths
|
|
uefiSourcePathPrefixes = append(uefiSourcePathPrefixes, extraUEFISearchPaths...)
|
|
|
|
uefiSourcePaths, uefiVarsPaths := generateUEFIPFlashList(uefiSourcePathPrefixes, uefiSourceFiles, uefiVarsFiles, uefiSourceFilesInsecure)
|
|
|
|
return []PFlash{
|
|
{
|
|
Size: 0,
|
|
SourcePaths: uefiSourcePaths,
|
|
},
|
|
{
|
|
Size: 0,
|
|
SourcePaths: uefiVarsPaths,
|
|
},
|
|
}
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func generateUEFIPFlashList(uefiSourcePathPrefixes, uefiSourceFiles, uefiVarsFiles, uefiSourceFilesInsecure []string) (uefiSourcePaths, uefiVarsPaths []string) {
|
|
for _, p := range uefiSourcePathPrefixes {
|
|
for _, f := range uefiSourceFiles {
|
|
uefiSourcePaths = append(uefiSourcePaths, filepath.Join(p, f))
|
|
}
|
|
|
|
for _, f := range uefiVarsFiles {
|
|
uefiVarsPaths = append(uefiVarsPaths, filepath.Join(p, f))
|
|
}
|
|
}
|
|
|
|
for _, p := range uefiSourcePathPrefixes {
|
|
for _, f := range uefiSourceFilesInsecure {
|
|
uefiSourcePaths = append(uefiSourcePaths, filepath.Join(p, f))
|
|
}
|
|
}
|
|
|
|
return uefiSourcePaths, uefiVarsPaths
|
|
}
|
|
|
|
// QemuExecutable returns name of qemu executable for the arch.
|
|
func (arch Arch) QemuExecutable() string {
|
|
binaries := []string{
|
|
"qemu-system-" + arch.QemuArch(),
|
|
"qemu-kvm",
|
|
"/usr/libexec/qemu-kvm",
|
|
}
|
|
|
|
for _, binary := range binaries {
|
|
if path, err := exec.LookPath(binary); err == nil {
|
|
return path
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
// TPMDeviceArgs returns arguments for qemu to enable TPM device.
|
|
func (arch Arch) TPMDeviceArgs(socketPath string) []string {
|
|
tpmDeviceArgs := []string{
|
|
"-chardev",
|
|
fmt.Sprintf("socket,id=chrtpm,path=%s", socketPath),
|
|
"-tpmdev",
|
|
"emulator,id=tpm0,chardev=chrtpm",
|
|
"-device",
|
|
}
|
|
|
|
switch arch {
|
|
case ArchAmd64:
|
|
return slices.Concat(tpmDeviceArgs, []string{"tpm-tis,tpmdev=tpm0"})
|
|
case ArchArm64:
|
|
return slices.Concat(tpmDeviceArgs, []string{"tpm-tis-device,tpmdev=tpm0"})
|
|
default:
|
|
panic("unsupported architecture")
|
|
}
|
|
}
|
|
|
|
// KVMArgs returns arguments for qemu to enable KVM.
|
|
func (arch Arch) KVMArgs(kvmEnabled bool) []string {
|
|
if !kvmEnabled {
|
|
return []string{"-machine", arch.QemuMachine()}
|
|
}
|
|
|
|
machineArg := arch.QemuMachine() + ",accel=kvm"
|
|
|
|
switch arch {
|
|
case ArchAmd64:
|
|
machineArg += ",smm=on"
|
|
|
|
return []string{"-machine", machineArg}
|
|
case ArchArm64:
|
|
// smm is not supported on aarch64
|
|
return []string{"-machine", machineArg}
|
|
default:
|
|
panic("unsupported architecture")
|
|
}
|
|
}
|