Skip to content

Commit 7732575

Browse files
NOISSUE - Add TDX support to Manager (#446)
* Add TDX support on manager * Add functions to check platform * Search for tdx in kernel parameters * Modify based on comments
1 parent 3e47433 commit 7732575

File tree

11 files changed

+203
-20
lines changed

11 files changed

+203
-20
lines changed

cmd/manager/main.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,24 @@ func main() {
8484
}()
8585
tracer := tp.Tracer(svcName)
8686

87-
qemuCfg := qemu.Config{}
88-
if err := env.ParseWithOptions(&qemuCfg, env.Options{Prefix: envPrefixQemu}); err != nil {
87+
qemuCfg, err := qemu.NewConfig()
88+
if err != nil {
89+
logger.Error(fmt.Sprintf("failed to create config: %v", err))
90+
exitCode = 1
91+
return
92+
}
93+
94+
if qemuCfg.EnableTDX {
95+
logger.Info("Manager started with TDX enabled")
96+
} else if qemuCfg.EnableSEVSNP {
97+
logger.Info("Manager started with SEV-SNP enabled")
98+
} else if qemuCfg.EnableSEV {
99+
logger.Info("Manager started with SEV enabled")
100+
} else {
101+
logger.Info("Manager started without confidential computing support")
102+
}
103+
104+
if err := env.ParseWithOptions(qemuCfg, env.Options{Prefix: envPrefixQemu}); err != nil {
89105
logger.Error(fmt.Sprintf("failed to load QEMU configuration: %s", err))
90106
exitCode = 1
91107
return
@@ -100,7 +116,7 @@ func main() {
100116
return
101117
}
102118

103-
svc, err := newService(ctx, logger, tracer, qemuCfg, cfg.AttestationPolicyBinary, cfg.IgvmMeasureBinary, cfg.PcrValues, cfg.EosVersion)
119+
svc, err := newService(ctx, logger, tracer, *qemuCfg, cfg.AttestationPolicyBinary, cfg.IgvmMeasureBinary, cfg.PcrValues, cfg.EosVersion)
104120
if err != nil {
105121
logger.Error(err.Error())
106122
exitCode = 1

cocos-manager.env

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,17 @@ MANAGER_QEMU_SEV_ID=sev0
4444
MANAGER_QEMU_SEV_CBITPOS=51
4545
MANAGER_QEMU_SEV_REDUCED_PHYS_BITS=1
4646
MANAGER_QEMU_HOST_DATA=
47+
MANAGER_QEMU_TDX_ID=tdx0
48+
MANAGER_QEMU_QUOTE_GENERATION_PORT=4050
49+
MANAGER_QEMU_OVMF_FILE=/usr/share/ovmf/OVMF.fd
4750
MANAGER_QEMU_VSOCK_ID=vhost-vsock-pci0
4851
MANAGER_QEMU_VSOCK_GUEST_CID=3
4952
MANAGER_QEMU_VSOCK_VNC=0
5053
MANAGER_QEMU_BIN_PATH=qemu-system-x86_64
5154
MANAGER_QEMU_USE_SUDO=true
5255
MANAGER_QEMU_ENABLE_SEV=false
5356
MANAGER_QEMU_ENABLE_SEV_SNP=false
57+
MANAGER_QEMU_ENABLE_TDX=false
5458
MANAGER_QEMU_IGVM_FILE=/etc/cocos/coconut-qemu.igvm
5559
MANAGER_QEMU_ENABLE_KVM=true
5660
MANAGER_QEMU_MACHINE=q35

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ require (
2727

2828
require (
2929
cloud.google.com/go/storage v1.51.0
30+
github.com/caarlos0/env/v10 v10.0.0
3031
github.com/golang-jwt/jwt/v5 v5.2.2
3132
github.com/google/gce-tcb-verifier v0.3.1
3233
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ github.com/absmach/magistrala v0.15.1 h1:3Bk2hlyWcV591LxPYwlvRcyCXTfuZ1g/EkNmU+o
4040
github.com/absmach/magistrala v0.15.1/go.mod h1:9pto6xuBt/IuCtZRdEha0iDQKNQ5tyNOjLXJgUiikYk=
4141
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
4242
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
43+
github.com/caarlos0/env/v10 v10.0.0 h1:yIHUBZGsyqCnpTkbjk8asUlx6RFhhEs+h7TOBdgdzXA=
44+
github.com/caarlos0/env/v10 v10.0.0/go.mod h1:ZfulV76NvVPw3tm591U4SwL3Xx9ldzBP9aGxzeN7G18=
4345
github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
4446
github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
4547
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=

manager/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ The service is configured using the environment variables from the following tab
4848
| MANAGER_QEMU_SEV_REDUCED_PHYS_BITS | The number of reduced physical address bits for SEV. | 1 |
4949
| MANAGER_QEMU_ENABLE_HOST_DATA | Enable additional data for the SEV host. | false |
5050
| MANAGER_QEMU_HOST_DATA | Additional data for the SEV host. | |
51+
| MANAGER_QEMU_TDX_ID | The ID for the Trust Domain Extensions (TDX) device. | tdx0 |
52+
| MANAGER_QEMU_QUOTE_GENERATION_PORT | The port number for virtual socket used to communicate with the Quote Generation Service (QGS). | 4050 |
53+
| MANAGER_QEMU_OVMF_FILE | The file path for the OVMF file (combined OVMF_CODE and OVMF_VARS file). | /usr/share/ovmf/OVMF.fd |
5154
| MANAGER_QEMU_IGVM_ID | The ID of the IGVM file. | igvm0 |
5255
| MANAGER_QEMU_IGVM_FILE | The file path to the IGVM file. | /root/coconut-qemu.igvm |
5356
| MANAGER_QEMU_VSOCK_ID | The ID for the virtual socket device. | vhost-vsock-pci0 |
@@ -57,6 +60,7 @@ The service is configured using the environment variables from the following tab
5760
| MANAGER_QEMU_USE_SUDO | Whether to use sudo to run QEMU. | false |
5861
| MANAGER_QEMU_ENABLE_SEV | Whether to enable Secure Encrypted Virtualization (SEV). | false |
5962
| MANAGER_QEMU_ENABLE_SEV_SNP | Whether to enable Secure Nested Paging (SEV-SNP). | true |
63+
| MANAGER_QEMU_ENABLE_TDX | Whether to enable Trust Domain Extensions (TDX). | false |
6064
| MANAGER_QEMU_ENABLE_KVM | Whether to enable the Kernel-based Virtual Machine (KVM) acceleration. | true |
6165
| MANAGER_QEMU_MACHINE | The machine type for QEMU. | q35 |
6266
| MANAGER_QEMU_CPU | The CPU model for QEMU. | EPYC |
@@ -268,6 +272,20 @@ MANAGER_QEMU_IGVM_FILE=<path to IGVM file> \
268272
./build/cocos-manager
269273
```
270274

275+
To enable [TDX](https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/overview.html) support, start manager like this
276+
277+
```sh
278+
MANAGER_GRPC_URL=localhost:7001 \
279+
MANAGER_LOG_LEVEL=debug \
280+
MANAGER_QEMU_ENABLE_SEV=false \
281+
MANAGER_QEMU_ENABLE_SEV_SNP=false \
282+
MANAGER_QEMU_ENABLE_TDX=true \
283+
MANAGER_QEMU_CPU=host \
284+
MANAGER_QEMU_BIN_PATH=<path to QEMU binary> \
285+
MANAGER_QEMU_OVMF_FILE=<path to OVMF file> \
286+
./build/cocos-manager
287+
```
288+
271289
### Troubleshooting
272290

273291
If the `ps aux | grep qemu-system-x86_64` give you something like this

manager/attestation_policy.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ func (ms *managerService) FetchAttestationPolicy(_ context.Context, computationI
110110
attestationPolicy.Config.Policy.Measurement = measurement
111111
}
112112

113-
if vmi.Config.SevConfig.EnableHostData {
114-
hostData, err := base64.StdEncoding.DecodeString(vmi.Config.SevConfig.HostData)
113+
if vmi.Config.SEVConfig.EnableHostData {
114+
hostData, err := base64.StdEncoding.DecodeString(vmi.Config.SEVConfig.HostData)
115115
if err != nil {
116116
return nil, err
117117
}

manager/qemu/config.go

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ package qemu
55
import (
66
"fmt"
77
"strconv"
8+
9+
"github.com/caarlos0/env/v10"
810
)
911

1012
const (
1113
BaseGuestCID = 3
1214
KernelCommandLine = "quiet console=null"
15+
TDXObject = "'{\"qom-type\":\"tdx-guest\",\"id\":\"%s\",\"quote-generation-socket\":{\"type\": \"vsock\", \"cid\":\"2\",\"port\":\"%d\"}}'"
1316
)
1417

1518
type MemoryConfig struct {
@@ -52,14 +55,20 @@ type DiskImgConfig struct {
5255
RootFsFile string `env:"DISK_IMG_ROOTFS_FILE" envDefault:"img/rootfs.cpio.gz"`
5356
}
5457

55-
type SevConfig struct {
58+
type SEVConfig struct {
5659
ID string `env:"SEV_ID" envDefault:"sev0"`
5760
CBitPos int `env:"SEV_CBITPOS" envDefault:"51"`
5861
ReducedPhysBits int `env:"SEV_REDUCED_PHYS_BITS" envDefault:"1"`
5962
EnableHostData bool `env:"ENABLE_HOST_DATA" envDefault:"false"`
6063
HostData string `env:"HOST_DATA" envDefault:""`
6164
}
6265

66+
type TDXConfig struct {
67+
ID string `env:"TDX_ID" envDefault:"tdx0"`
68+
QuoteGenerationPort int `env:"QUOTE_GENERATION_PORT" envDefault:"4050"`
69+
OVMF string `env:"OVMF_FILE" envDefault:"/usr/share/ovmf/OVMF.fd"`
70+
}
71+
6372
type IGVMConfig struct {
6473
ID string `env:"IGVM_ID" envDefault:"igvm0"`
6574
File string `env:"IGVM_FILE" envDefault:"/root/coconut-qemu.igvm"`
@@ -71,10 +80,11 @@ type VSockConfig struct {
7180
}
7281

7382
type Config struct {
83+
EnableSEV bool
84+
EnableSEVSNP bool
85+
EnableTDX bool
7486
QemuBinPath string `env:"BIN_PATH" envDefault:"qemu-system-x86_64"`
7587
UseSudo bool `env:"USE_SUDO" envDefault:"false"`
76-
EnableSEV bool `env:"ENABLE_SEV" envDefault:"false"`
77-
EnableSEVSNP bool `env:"ENABLE_SEV_SNP" envDefault:"true"`
7888

7989
EnableKVM bool `env:"ENABLE_KVM" envDefault:"true"`
8090

@@ -101,7 +111,10 @@ type Config struct {
101111
DiskImgConfig
102112

103113
// SEV
104-
SevConfig
114+
SEVConfig
115+
116+
// TDX
117+
TDXConfig
105118

106119
// vTPM
107120
IGVMConfig
@@ -142,7 +155,7 @@ func (config Config) ConstructQemuArgs() []string {
142155
config.MemoryConfig.Slots,
143156
config.MemoryConfig.Max))
144157

145-
if !config.EnableSEVSNP {
158+
if !config.EnableSEVSNP && !config.EnableTDX {
146159
// OVMF
147160
args = append(args, "-drive",
148161
fmt.Sprintf("if=%s,format=%s,unit=%d,file=%s,readonly=%s",
@@ -183,15 +196,15 @@ func (config Config) ConstructQemuArgs() []string {
183196

184197
args = append(args, "-machine",
185198
fmt.Sprintf("confidential-guest-support=%s,memory-backend=%s,igvm-cfg=%s",
186-
config.SevConfig.ID,
199+
config.SEVConfig.ID,
187200
config.MemID,
188201
config.IGVMConfig.ID))
189202

190203
if config.EnableSEVSNP {
191204
sevType = "sev-snp-guest"
192205

193-
if config.SevConfig.EnableHostData {
194-
hostData = fmt.Sprintf(",host-data=%s", config.SevConfig.HostData)
206+
if config.SEVConfig.EnableHostData {
207+
hostData = fmt.Sprintf(",host-data=%s", config.SEVConfig.HostData)
195208
}
196209
}
197210

@@ -203,9 +216,9 @@ func (config Config) ConstructQemuArgs() []string {
203216
args = append(args, "-object",
204217
fmt.Sprintf("%s,id=%s,cbitpos=%d,reduced-phys-bits=%d%s",
205218
sevType,
206-
config.SevConfig.ID,
207-
config.SevConfig.CBitPos,
208-
config.SevConfig.ReducedPhysBits,
219+
config.SEVConfig.ID,
220+
config.SEVConfig.CBitPos,
221+
config.SEVConfig.ReducedPhysBits,
209222
hostData))
210223

211224
args = append(args, "-object",
@@ -214,6 +227,26 @@ func (config Config) ConstructQemuArgs() []string {
214227
config.IGVMConfig.File))
215228
}
216229

230+
if config.EnableTDX {
231+
args = append(args, "-object",
232+
fmt.Sprintf(TDXObject,
233+
config.TDXConfig.ID,
234+
config.TDXConfig.QuoteGenerationPort))
235+
236+
args = append(args, "-machine",
237+
fmt.Sprintf("confidential-guest-support=%s,memory-backend=%s,hpet=off",
238+
config.TDXConfig.ID,
239+
config.MemID))
240+
241+
args = append(args, "-object",
242+
fmt.Sprintf("memory-backend-memfd,id=%s,size=%s,share=true,prealloc=false",
243+
config.MemID,
244+
config.MemoryConfig.Size))
245+
246+
args = append(args, "-bios", config.TDXConfig.OVMF)
247+
args = append(args, "-nodefaults")
248+
}
249+
217250
args = append(args, "-kernel", config.DiskImgConfig.KernelFile)
218251
args = append(args, "-append", strconv.Quote(KernelCommandLine))
219252
args = append(args, "-initrd", config.DiskImgConfig.RootFsFile)
@@ -237,3 +270,17 @@ func (config Config) ConstructQemuArgs() []string {
237270

238271
return args
239272
}
273+
274+
func NewConfig() (*Config, error) {
275+
cfg := Config{}
276+
277+
if err := env.Parse(&cfg); err != nil {
278+
return nil, err
279+
}
280+
281+
cfg.EnableSEV = SEVEnabledOnHost()
282+
cfg.EnableSEVSNP = SEVSNPEnabledOnHost()
283+
cfg.EnableTDX = TDXEnabledOnHost()
284+
285+
return &cfg, nil
286+
}

manager/qemu/config_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func TestConstructQemuArgs(t *testing.T) {
127127
KernelFile: "img/bzImage",
128128
RootFsFile: "img/rootfs.cpio.gz",
129129
},
130-
SevConfig: SevConfig{
130+
SEVConfig: SEVConfig{
131131
ID: "sev0",
132132
CBitPos: 51,
133133
ReducedPhysBits: 1,
@@ -174,7 +174,7 @@ func TestConstructQemuArgs(t *testing.T) {
174174
func TestConstructQemuArgs_HostData(t *testing.T) {
175175
config := Config{
176176
EnableSEVSNP: true,
177-
SevConfig: SevConfig{
177+
SEVConfig: SEVConfig{
178178
ID: "sev0",
179179
CBitPos: 51,
180180
ReducedPhysBits: 1,

manager/qemu/vm.go

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"log/slog"
88
"os"
99
"os/exec"
10+
"strings"
1011
"syscall"
1112
"time"
1213

@@ -60,7 +61,8 @@ func (v *qemuVM) Start() (err error) {
6061
}
6162

6263
v.vmi.Config.NetDevConfig.ID = fmt.Sprintf("%s-%s", v.vmi.Config.NetDevConfig.ID, id)
63-
v.vmi.Config.SevConfig.ID = fmt.Sprintf("%s-%s", v.vmi.Config.SevConfig.ID, id)
64+
v.vmi.Config.SEVConfig.ID = fmt.Sprintf("%s-%s", v.vmi.Config.SEVConfig.ID, id)
65+
v.vmi.Config.TDXConfig.ID = fmt.Sprintf("%s-%s", v.vmi.Config.TDXConfig.ID, id)
6466

6567
if !v.vmi.Config.EnableSEVSNP {
6668
// Copy firmware vars file.
@@ -197,3 +199,54 @@ func (v *qemuVM) GetCID() int {
197199
func (v *qemuVM) GetConfig() interface{} {
198200
return v.vmi
199201
}
202+
203+
func SEVEnabled(cpuinfo string, sevPresent bool) bool {
204+
return strings.Contains(cpuinfo, "sev") && sevPresent
205+
}
206+
207+
func SEVSNPEnabled(cpuinfo, kernelParam string) bool {
208+
return strings.Contains(cpuinfo, "sev_snp") && strings.TrimSpace(kernelParam) == "1"
209+
}
210+
211+
func TDXEnabled(cpuinfo, kernelParam string) bool {
212+
return strings.Contains(cpuinfo, "tdx_host_platform") && strings.TrimSpace(kernelParam) == "1"
213+
}
214+
215+
// Checks if SEV is supported and usable by verifying both CPU flags and the /dev/sev device.
216+
func SEVEnabledOnHost() bool {
217+
cpuinfo, err := os.ReadFile("/proc/cpuinfo")
218+
if err != nil {
219+
return false
220+
}
221+
222+
_, err = os.Stat("/dev/sev")
223+
return SEVEnabled(string(cpuinfo), err == nil)
224+
}
225+
226+
func SEVSNPEnabledOnHost() bool {
227+
cpuinfo, err := os.ReadFile("/proc/cpuinfo")
228+
if err != nil {
229+
return false
230+
}
231+
232+
kernelParam, err := os.ReadFile("/sys/module/kvm_amd/parameters/sev_snp")
233+
if err != nil {
234+
return false
235+
}
236+
237+
return SEVSNPEnabled(string(cpuinfo), string(kernelParam))
238+
}
239+
240+
func TDXEnabledOnHost() bool {
241+
cpuinfo, err := os.ReadFile("/proc/cpuinfo")
242+
if err != nil {
243+
return false
244+
}
245+
246+
kernelParam, err := os.ReadFile("/sys/module/kvm_intel/parameters/tdx")
247+
if err != nil {
248+
return false
249+
}
250+
251+
return TDXEnabled(string(cpuinfo), string(kernelParam))
252+
}

0 commit comments

Comments
 (0)