Skip to content

Commit

Permalink
chore: aarch64 qemu local secureboot support
Browse files Browse the repository at this point in the history
Support booting with SecureBoot on aarch64 with `talosctl cluster
create` with QEMU provisioner.

Signed-off-by: Noel Georgi <git@frezbo.dev>
  • Loading branch information
frezbo committed Aug 28, 2024
1 parent da62635 commit 106c17d
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 53 deletions.
121 changes: 95 additions & 26 deletions pkg/provision/providers/qemu/arch.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
package qemu

import (
"fmt"
"os/exec"
"path/filepath"
"slices"
)

// Arch abstracts away differences between different architectures.
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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{
{
Expand All @@ -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{
Expand All @@ -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")
}
}
28 changes: 5 additions & 23 deletions pkg/provision/providers/qemu/launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,19 @@ 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
BootloaderEnabled bool
TPM2Config tpm2Config
NodeUUID uuid.UUID
BadRTC bool
ArchitectureData Arch

// Talos config
Config string
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)...,
)
}

Expand All @@ -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...,
)

Expand Down
6 changes: 2 additions & 4 deletions pkg/provision/providers/qemu/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 106c17d

Please sign in to comment.