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
import (
"fmt"
"os/exec"
"path/filepath"
"slices"
)
// Arch abstracts away differences between different architectures.
@ -74,18 +76,47 @@ type PFlash struct {
func (arch Arch) PFlash(uefiEnabled bool, extraUEFISearchPaths []string) []PFlash {
switch arch {
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"}
for _, p := range extraUEFISearchPaths {
uefiSourcePaths = append(uefiSourcePaths, filepath.Join(p, "QEMU_EFI.fd"))
// 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,
},
{
Size: 64 * 1024 * 1024,
SourcePaths: uefiVarsPaths,
Size: 64 * 1024 * 1024,
},
}
case ArchAmd64:
@ -127,25 +158,7 @@ func (arch Arch) PFlash(uefiEnabled bool, extraUEFISearchPaths []string) []PFlas
// Append extra search paths
uefiSourcePathPrefixes = append(uefiSourcePathPrefixes, extraUEFISearchPaths...)
var uefiSourcePaths []string
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))
}
}
uefiSourcePaths, uefiVarsPaths := generateUEFIPFlashList(uefiSourcePathPrefixes, uefiSourceFiles, uefiVarsFiles, uefiSourceFilesInsecure)
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.
func (arch Arch) QemuExecutable() string {
binaries := []string{
@ -179,7 +212,43 @@ func (arch Arch) QemuExecutable() string {
return ""
}
// Architecture returns the architecture.
func (arch Arch) Architecture() string {
return string(arch)
// 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")
}
}

View File

@ -43,14 +43,11 @@ type LaunchConfig struct {
DiskDrivers []string
VCPUCount int64
MemSize int64
QemuExecutable string
Architecture string
KernelImagePath string
InitrdPath string
ISOPath string
PFlashImages []string
KernelArgs string
MachineType string
MonitorPath string
DefaultBootOrder string
EnableKVM bool
@ -58,6 +55,7 @@ type LaunchConfig struct {
TPM2Config tpm2Config
NodeUUID uuid.UUID
BadRTC bool
ArchitectureData Arch
// Talos config
Config string
@ -390,18 +388,7 @@ func launchVM(config *LaunchConfig) error {
}
}
machineArg := config.MachineType
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)
args = append(args, config.ArchitectureData.KVMArgs(config.EnableKVM)...)
pflashArgs := make([]string, 2*len(config.PFlashImages))
for i := range config.PFlashImages {
@ -440,12 +427,7 @@ func launchVM(config *LaunchConfig) error {
}
args = append(args,
"-chardev",
fmt.Sprintf("socket,id=chrtpm,path=%s", tpm2SocketPath),
"-tpmdev",
"emulator,id=tpm0,chardev=chrtpm",
"-device",
"tpm-tis,tpmdev=tpm0",
config.ArchitectureData.TPMDeviceArgs(tpm2SocketPath)...,
)
}
@ -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(
config.QemuExecutable,
config.ArchitectureData.QemuExecutable(),
args...,
)

View File

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