Files
cocos/manager/qemu/config.go
T
Jovan Djukic 1f0eccfae7
CI / lint (push) Has been cancelled
CI / test (agent) (push) Has been cancelled
CI / test (cli) (push) Has been cancelled
CI / test (cmd) (push) Has been cancelled
CI / test (internal) (push) Has been cancelled
CI / test (manager, true) (push) Has been cancelled
CI / test (pkg) (push) Has been cancelled
CI / upload-coverage (push) Has been cancelled
Manager can start CVM with NVIDIA GPU support (#595)
2026-05-25 12:32:00 +02:00

358 lines
10 KiB
Go

// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package qemu
import (
"fmt"
"strings"
"github.com/caarlos0/env/v10"
)
const (
defaultKernelCommandLine = "quiet console=null"
TDXObject = "{\"qom-type\":\"tdx-guest\",\"id\":\"%s\",\"quote-generation-socket\":{\"type\": \"vsock\", \"cid\":\"2\",\"port\":\"%d\"}}"
)
type MemoryConfig struct {
Size string `env:"MEMORY_SIZE" envDefault:"2048M"`
Slots int `env:"MEMORY_SLOTS" envDefault:"5"`
Max string `env:"MAX_MEMORY" envDefault:"30G"`
}
type OVMFCodeConfig struct {
If string `env:"OVMF_CODE_IF" envDefault:"pflash"`
Format string `env:"OVMF_CODE_FORMAT" envDefault:"raw"`
Unit int `env:"OVMF_CODE_UNIT" envDefault:"0"`
File string `env:"OVMF_CODE_FILE" envDefault:"/usr/share/OVMF/OVMF_CODE.fd"`
ReadOnly string `env:"OVMF_CODE_READONLY" envDefault:"on"`
Version string `env:"OVMF_VERSION" envDefault:""`
}
type OVMFVarsConfig struct {
If string `env:"OVMF_VARS_IF" envDefault:"pflash"`
Format string `env:"OVMF_VARS_FORMAT" envDefault:"raw"`
Unit int `env:"OVMF_VARS_UNIT" envDefault:"1"`
File string `env:"OVMF_VARS_FILE" envDefault:"/usr/share/OVMF/OVMF_VARS.fd"`
}
type NetDevConfig struct {
ID string `env:"NETDEV_ID" envDefault:"vmnic"`
HostFwdAgent int `env:"HOST_FWD_AGENT" envDefault:"7020"`
GuestFwdAgent int `env:"GUEST_FWD_AGENT" envDefault:"7002"`
}
type VirtioNetPciConfig struct {
DisableLegacy string `env:"VIRTIO_NET_PCI_DISABLE_LEGACY" envDefault:"on"`
IOMMUPlatform bool `env:"VIRTIO_NET_PCI_IOMMU_PLATFORM" envDefault:"true"`
Addr string `env:"VIRTIO_NET_PCI_ADDR" envDefault:"0x2"`
ROMFile string `env:"VIRTIO_NET_PCI_ROMFILE"`
}
type KernelConfig struct {
KernelFile string `env:"DISK_IMG_KERNEL_FILE" envDefault:"img/bzImage"`
RootFsFile string `env:"DISK_IMG_ROOTFS_FILE" envDefault:"img/rootfs.cpio.gz"`
}
type SEVSNPConfig struct {
ID string `env:"SEV_SNP_ID" envDefault:"sev0"`
CBitPos int `env:"SEV_SNP_CBIT_POS" envDefault:"51"`
ReducedPhysBits int `env:"SEV_SNP_REDUCED_PHYS_BITS" envDefault:"1"`
EnableHostData bool `env:"ENABLE_HOST_DATA" envDefault:"false"`
HostData string `env:"HOST_DATA" envDefault:""`
}
type TDXConfig struct {
ID string `env:"TDX_ID" envDefault:"tdx0"`
QuoteGenerationPort int `env:"QUOTE_GENERATION_PORT" envDefault:"4050"`
OVMF string `env:"OVMF_FILE" envDefault:"/usr/share/ovmf/OVMF.fd"`
}
type IGVMConfig struct {
ID string `env:"IGVM_ID" envDefault:"igvm0"`
File string `env:"IGVM_FILE" envDefault:"/root/coconut-qemu.igvm"`
}
type DiskConfig struct {
SrcFile string `env:"SRC_DISK_FILE" envDefault:"img/enc_os.qcow2"`
DstFile string `env:"DST_DISK_FILE" envDefault:""`
ID string `env:"DISK_ID" envDefault:"disk0"`
Format string `env:"DISK_FORMAT" envDefault:"qcow2"`
SCSIID string `env:"DISK_SCSI_ID" envDefault:"scsi0"`
}
type GPUConfig struct {
EnableGPU bool
GPUBDF string `env:"GPU_BDF" envDefault:""`
PCIeRootPort string `env:"GPU_PCIE_ROOT_PORT" envDefault:"pci.1"`
PCIeBus string `env:"GPU_PCIE_BUS" envDefault:"pcie.0"`
FWCfgPciMmio string `env:"GPU_FW_CFG_MMIO_MB" envDefault:"262144"`
}
type Config struct {
EnableSEVSNP bool
EnableTDX bool
EnableDisk bool `env:"ENABLE_DISK" envDefault:"false"`
QemuBinPath string `env:"BIN_PATH" envDefault:"qemu-system-x86_64"`
UseSudo bool `env:"USE_SUDO" envDefault:"false"`
EnableKVM bool `env:"ENABLE_KVM" envDefault:"true"`
// machine, CPU, RAM
Machine string `env:"MACHINE" envDefault:"q35"`
CPU string `env:"CPU" envDefault:"EPYC"`
SMPCount int `env:"SMP_COUNT" envDefault:"4"`
MaxCPUs int `env:"SMP_MAXCPUS" envDefault:"64"`
MemID string `env:"MEM_ID" envDefault:"ram1"`
MemoryConfig
// OVMF
OVMFCodeConfig
OVMFVarsConfig
// network
NetDevConfig
VirtioNetPciConfig
// disk config
DiskConfig
// kernel and initramfs
KernelConfig
// SEV-SNP
SEVSNPConfig
// TDX
TDXConfig
// vTPM
IGVMConfig
// GPU passthrough
GPUConfig
// display
NoGraphic bool `env:"NO_GRAPHIC" envDefault:"true"`
Monitor string `env:"MONITOR" envDefault:"pty"`
// kernel
KernelCommandLine string `env:"KERNEL_CMDLINE" envDefault:"quiet console=null"`
// ports
HostFwdRange string `env:"HOST_FWD_RANGE" envDefault:"6100-6200"`
// mounts
CertsMount string `env:"CERTS_MOUNT" envDefault:""`
EnvMount string `env:"ENV_MOUNT" envDefault:""`
}
func (config Config) ValidateBootConfig() error {
if config.EnableDisk {
if strings.TrimSpace(config.DiskConfig.DstFile) == "" {
return fmt.Errorf("disk boot enabled but destination disk image is not set")
}
return nil
}
if strings.TrimSpace(config.KernelConfig.KernelFile) == "" {
return fmt.Errorf("kernel boot enabled but kernel image is not set")
}
if strings.TrimSpace(config.KernelConfig.RootFsFile) == "" {
return fmt.Errorf("kernel boot enabled but initramfs image is not set")
}
return nil
}
func (config Config) ConstructQemuArgs() []string {
args := []string{}
// virtualization
if config.EnableKVM {
args = append(args, "-enable-kvm")
}
// machine, CPU, RAM
if config.Machine != "" {
args = append(args, "-machine", config.Machine)
}
if config.CPU != "" {
args = append(args, "-cpu", config.CPU)
}
args = append(args, "-smp", fmt.Sprintf("%d,maxcpus=%d", config.SMPCount, config.MaxCPUs))
args = append(args, "-m", fmt.Sprintf("%s,slots=%d,maxmem=%s",
config.MemoryConfig.Size,
config.MemoryConfig.Slots,
config.MemoryConfig.Max))
if !config.EnableSEVSNP && !config.EnableTDX {
// OVMF
args = append(args, "-drive",
fmt.Sprintf("if=%s,format=%s,unit=%d,file=%s,readonly=%s",
config.OVMFCodeConfig.If,
config.OVMFCodeConfig.Format,
config.OVMFCodeConfig.Unit,
config.OVMFCodeConfig.File,
config.OVMFCodeConfig.ReadOnly))
args = append(args, "-drive",
fmt.Sprintf("if=%s,format=%s,unit=%d,file=%s",
config.OVMFVarsConfig.If,
config.OVMFVarsConfig.Format,
config.OVMFVarsConfig.Unit,
config.OVMFVarsConfig.File))
}
// network
args = append(args, "-netdev",
fmt.Sprintf("user,id=%s,hostfwd=tcp::%d-:%d",
config.NetDevConfig.ID,
config.NetDevConfig.HostFwdAgent, config.NetDevConfig.GuestFwdAgent))
args = append(args, "-device",
fmt.Sprintf("virtio-net-pci,disable-legacy=%s,iommu_platform=%v,netdev=%s,addr=%s,romfile=%s",
config.VirtioNetPciConfig.DisableLegacy,
config.VirtioNetPciConfig.IOMMUPlatform,
config.NetDevConfig.ID,
config.VirtioNetPciConfig.Addr,
config.VirtioNetPciConfig.ROMFile))
if config.EnableDisk {
// disk image
args = append(args, "-drive",
fmt.Sprintf("file=%s,if=none,id=%s,format=%s",
config.DiskConfig.DstFile,
config.DiskConfig.ID,
config.DiskConfig.Format))
args = append(args, "-device",
fmt.Sprintf("virtio-scsi-pci,id=%s,disable-legacy=on,iommu_platform=true",
config.DiskConfig.SCSIID))
args = append(args, "-device",
fmt.Sprintf("scsi-hd,drive=%s,bus=%s.0",
config.DiskConfig.ID,
config.DiskConfig.SCSIID))
}
// GPU passthrough via VFIO
if config.GPUConfig.EnableGPU {
args = append(args, "-device",
fmt.Sprintf("pcie-root-port,id=%s,bus=%s",
config.GPUConfig.PCIeRootPort,
config.GPUConfig.PCIeBus))
args = append(args, "-device",
fmt.Sprintf("vfio-pci,host=%s,bus=%s",
config.GPUConfig.GPUBDF,
config.GPUConfig.PCIeRootPort))
args = append(args, "-fw_cfg",
fmt.Sprintf("name=opt/ovmf/X-PciMmio64Mb,string=%s",
config.GPUConfig.FWCfgPciMmio))
}
// SEV-SNP
if config.EnableSEVSNP {
sevSnpType := "sev-snp-guest"
hostData := ""
args = append(args, "-machine",
fmt.Sprintf("confidential-guest-support=%s,memory-backend=%s,igvm-cfg=%s",
config.SEVSNPConfig.ID,
config.MemID,
config.IGVMConfig.ID))
if config.SEVSNPConfig.EnableHostData {
hostData = fmt.Sprintf(",host-data=%s", config.SEVSNPConfig.HostData)
}
args = append(args, "-object",
fmt.Sprintf("memory-backend-memfd,id=%s,size=%s,share=true,prealloc=false",
config.MemID,
config.MemoryConfig.Size))
args = append(args, "-object",
fmt.Sprintf("%s,id=%s,cbitpos=%d,reduced-phys-bits=%d%s",
sevSnpType,
config.SEVSNPConfig.ID,
config.SEVSNPConfig.CBitPos,
config.SEVSNPConfig.ReducedPhysBits,
hostData))
args = append(args, "-object",
fmt.Sprintf("igvm-cfg,id=%s,file=%s",
config.IGVMConfig.ID,
config.IGVMConfig.File))
}
if config.EnableTDX {
args = append(args, "-object",
fmt.Sprintf(TDXObject,
config.TDXConfig.ID,
config.TDXConfig.QuoteGenerationPort))
args = append(args, "-machine",
fmt.Sprintf("confidential-guest-support=%s,memory-backend=%s,hpet=off",
config.TDXConfig.ID,
config.MemID))
args = append(args, "-object",
fmt.Sprintf("memory-backend-memfd,id=%s,size=%s,share=true,prealloc=false",
config.MemID,
config.MemoryConfig.Size))
args = append(args, "-bios", config.TDXConfig.OVMF)
args = append(args, "-nodefaults")
}
if !config.EnableDisk {
args = append(args, "-kernel", config.KernelConfig.KernelFile)
args = append(args, "-append", config.KernelCommandLine)
args = append(args, "-initrd", config.KernelConfig.RootFsFile)
}
// display
if config.NoGraphic {
args = append(args, "-nographic")
}
args = append(args, "-monitor", config.Monitor)
if config.CertsMount != "" {
args = append(args, "-fsdev", fmt.Sprintf("local,id=cert_fs,path=%s,security_model=mapped", config.CertsMount))
args = append(args, "-device", "virtio-9p-pci,fsdev=cert_fs,mount_tag=certs_share")
}
if config.EnvMount != "" {
args = append(args, "-fsdev", fmt.Sprintf("local,id=env_fs,path=%s,security_model=mapped", config.EnvMount))
args = append(args, "-device", "virtio-9p-pci,fsdev=env_fs,mount_tag=env_share")
}
return args
}
func NewConfig() (*Config, error) {
cfg := Config{}
if err := env.Parse(&cfg); err != nil {
return nil, err
}
cfg.EnableSEVSNP = SEVSNPEnabledOnHost()
cfg.EnableTDX = TDXEnabledOnHost()
bdf, detected := GPUPassthroughAvailable()
if cfg.GPUConfig.GPUBDF != "" {
cfg.GPUConfig.EnableGPU = true
} else if detected {
cfg.GPUConfig.EnableGPU = true
cfg.GPUConfig.GPUBDF = bdf
}
return &cfg, nil
}