chore: aarch64 qemu local secureboot support

Support booting with SecureBoot on aarch64 with `talosctl cluster
create` with QEMU provisioner.

Signed-off-by: Noel Georgi <git@frezbo.dev>
This commit is contained in:
Noel Georgi 2024-08-28 18:03:29 +05:30
parent da6263506a
commit 106c17d0b5
No known key found for this signature in database
GPG Key ID: 21A9F444075C9E36
3 changed files with 102 additions and 53 deletions

View File

@ -5,8 +5,10 @@
package qemu package qemu
import ( import (
"fmt"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"slices"
) )
// Arch abstracts away differences between different architectures. // Arch abstracts away differences between different architectures.
@ -74,18 +76,47 @@ type PFlash struct {
func (arch Arch) PFlash(uefiEnabled bool, extraUEFISearchPaths []string) []PFlash { func (arch Arch) PFlash(uefiEnabled bool, extraUEFISearchPaths []string) []PFlash {
switch arch { switch arch {
case ArchArm64: case ArchArm64:
uefiSourcePaths := []string{"/usr/share/qemu-efi-aarch64/QEMU_EFI.fd", "/usr/share/OVMF/QEMU_EFI.fd", "/usr/share/edk2/aarch64/QEMU_EFI.fd"} // default search paths
for _, p := range extraUEFISearchPaths { uefiSourcePathPrefixes := []string{
uefiSourcePaths = append(uefiSourcePaths, filepath.Join(p, "QEMU_EFI.fd")) "/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{ return []PFlash{
{ {
Size: 64 * 1024 * 1024, Size: 64 * 1024 * 1024,
SourcePaths: uefiSourcePaths, SourcePaths: uefiSourcePaths,
}, },
{ {
Size: 64 * 1024 * 1024, SourcePaths: uefiVarsPaths,
Size: 64 * 1024 * 1024,
}, },
} }
case ArchAmd64: case ArchAmd64:
@ -127,25 +158,7 @@ func (arch Arch) PFlash(uefiEnabled bool, extraUEFISearchPaths []string) []PFlas
// Append extra search paths // Append extra search paths
uefiSourcePathPrefixes = append(uefiSourcePathPrefixes, extraUEFISearchPaths...) uefiSourcePathPrefixes = append(uefiSourcePathPrefixes, extraUEFISearchPaths...)
var uefiSourcePaths []string uefiSourcePaths, uefiVarsPaths := generateUEFIPFlashList(uefiSourcePathPrefixes, uefiSourceFiles, uefiVarsFiles, uefiSourceFilesInsecure)
var 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 []PFlash{ return []PFlash{
{ {
@ -162,6 +175,26 @@ func (arch Arch) PFlash(uefiEnabled bool, extraUEFISearchPaths []string) []PFlas
} }
} }
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. // QemuExecutable returns name of qemu executable for the arch.
func (arch Arch) QemuExecutable() string { func (arch Arch) QemuExecutable() string {
binaries := []string{ binaries := []string{
@ -179,7 +212,43 @@ func (arch Arch) QemuExecutable() string {
return "" return ""
} }
// Architecture returns the architecture. // TPMDeviceArgs returns arguments for qemu to enable TPM device.
func (arch Arch) Architecture() string { func (arch Arch) TPMDeviceArgs(socketPath string) []string {
return string(arch) 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")
}
} }

View File

@ -43,14 +43,11 @@ type LaunchConfig struct {
DiskDrivers []string DiskDrivers []string
VCPUCount int64 VCPUCount int64
MemSize int64 MemSize int64
QemuExecutable string
Architecture string
KernelImagePath string KernelImagePath string
InitrdPath string InitrdPath string
ISOPath string ISOPath string
PFlashImages []string PFlashImages []string
KernelArgs string KernelArgs string
MachineType string
MonitorPath string MonitorPath string
DefaultBootOrder string DefaultBootOrder string
EnableKVM bool EnableKVM bool
@ -58,6 +55,7 @@ type LaunchConfig struct {
TPM2Config tpm2Config TPM2Config tpm2Config
NodeUUID uuid.UUID NodeUUID uuid.UUID
BadRTC bool BadRTC bool
ArchitectureData Arch
// Talos config // Talos config
Config string Config string
@ -390,18 +388,7 @@ func launchVM(config *LaunchConfig) error {
} }
} }
machineArg := config.MachineType args = append(args, config.ArchitectureData.KVMArgs(config.EnableKVM)...)
if config.EnableKVM {
machineArg += ",accel=kvm"
// smm is not supported on aarch64
if Arch(config.QemuExecutable) == ArchAmd64 {
machineArg += ",smm=on"
}
}
args = append(args, "-machine", machineArg)
pflashArgs := make([]string, 2*len(config.PFlashImages)) pflashArgs := make([]string, 2*len(config.PFlashImages))
for i := range config.PFlashImages { for i := range config.PFlashImages {
@ -440,12 +427,7 @@ func launchVM(config *LaunchConfig) error {
} }
args = append(args, args = append(args,
"-chardev", config.ArchitectureData.TPMDeviceArgs(tpm2SocketPath)...,
fmt.Sprintf("socket,id=chrtpm,path=%s", tpm2SocketPath),
"-tpmdev",
"emulator,id=tpm0,chardev=chrtpm",
"-device",
"tpm-tis,tpmdev=tpm0",
) )
} }
@ -470,9 +452,9 @@ func launchVM(config *LaunchConfig) error {
) )
} }
fmt.Fprintf(os.Stderr, "starting %s with args:\n%s\n", config.QemuExecutable, strings.Join(args, " ")) fmt.Fprintf(os.Stderr, "starting %s with args:\n%s\n", config.ArchitectureData.QemuExecutable(), strings.Join(args, " "))
cmd := exec.Command( cmd := exec.Command(
config.QemuExecutable, config.ArchitectureData.QemuExecutable(),
args..., args...,
) )

View File

@ -129,16 +129,14 @@ func (p *provisioner) createNode(state *vm.State, clusterReq provision.ClusterRe
} }
launchConfig := LaunchConfig{ launchConfig := LaunchConfig{
QemuExecutable: arch.QemuExecutable(), ArchitectureData: arch,
Architecture: arch.Architecture(), DiskPaths: diskPaths,
DiskPaths: diskPaths,
DiskDrivers: xslices.Map(nodeReq.Disks, func(disk *provision.Disk) string { DiskDrivers: xslices.Map(nodeReq.Disks, func(disk *provision.Disk) string {
return disk.Driver return disk.Driver
}), }),
VCPUCount: vcpuCount, VCPUCount: vcpuCount,
MemSize: memSize, MemSize: memSize,
KernelArgs: cmdline.String(), KernelArgs: cmdline.String(),
MachineType: arch.QemuMachine(),
PFlashImages: pflashImages, PFlashImages: pflashImages,
MonitorPath: state.GetRelativePath(fmt.Sprintf("%s.monitor", nodeReq.Name)), MonitorPath: state.GetRelativePath(fmt.Sprintf("%s.monitor", nodeReq.Name)),
EnableKVM: opts.TargetArch == runtime.GOARCH, EnableKVM: opts.TargetArch == runtime.GOARCH,