QEMU >= 2.12 provides 'sev-guest' object which is used to launch encrypted
VMs on AMD platform using SEV feature. The various inputs required to
launch SEV guest is provided through the <launch-security> tag. A typical
SEV guest launch command line looks like this:
# $QEMU ...\
-object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=5 ...\
-machine memory-encryption=sev0 \
Signed-off-by: Brijesh Singh <brijesh.singh(a)amd.com>
Reviewed-by: Erik Skultety <eskultet(a)redhat.com>
---
src/qemu/qemu_command.c | 41 ++++++++++++++++
src/qemu/qemu_process.c | 62 +++++++++++++++++++++++++
tests/qemuxml2argvdata/launch-security-sev.args | 29 ++++++++++++
tests/qemuxml2argvdata/launch-security-sev.xml | 37 +++++++++++++++
tests/qemuxml2argvtest.c | 4 ++
5 files changed, 173 insertions(+)
create mode 100644 tests/qemuxml2argvdata/launch-security-sev.args
create mode 100644 tests/qemuxml2argvdata/launch-security-sev.xml
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index bd54d17..6a95344 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -7287,6 +7287,9 @@ qemuBuildMachineCommandLine(virCommandPtr cmd,
virQEMUCapsGet(qemuCaps, QEMU_CAPS_LOADPARM))
qemuAppendLoadparmMachineParm(&buf, def);
+ if (def->sev)
+ virBufferAddLit(&buf, ",memory-encryption=sev0");
+
virCommandAddArgBuffer(cmd, &buf);
ret = 0;
@@ -9687,6 +9690,41 @@ qemuBuildTPMCommandLine(virCommandPtr cmd,
return 0;
}
+static int
+qemuBuildSevCommandLine(virDomainObjPtr vm, virCommandPtr cmd,
+ virDomainSevDefPtr sev)
+{
+ virBuffer obj = VIR_BUFFER_INITIALIZER;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ char *path = NULL;
+
+ if (!sev)
+ return 0;
+
+ VIR_DEBUG("policy=0x%x cbitpos=%d reduced_phys_bits=%d",
+ sev->policy, sev->cbitpos, sev->reduced_phys_bits);
+
+ virBufferAsprintf(&obj, "sev-guest,id=sev0,cbitpos=%d",
sev->cbitpos);
+ virBufferAsprintf(&obj, ",reduced-phys-bits=%d",
sev->reduced_phys_bits);
+ virBufferAsprintf(&obj, ",policy=0x%x", sev->policy);
+
+ if (sev->dh_cert) {
+ if (virAsprintf(&path, "%s/dh_cert.base64", priv->libDir) <
0)
+ return -1;
+ virBufferAsprintf(&obj, ",dh-cert-file=%s", path);
+ VIR_FREE(path);
+ }
+
+ if (sev->session) {
+ if (virAsprintf(&path, "%s/session.base64", priv->libDir) <
0)
+ return -1;
+ virBufferAsprintf(&obj, ",session-file=%s", path);
+ VIR_FREE(path);
+ }
+
+ virCommandAddArgList(cmd, "-object", virBufferContentAndReset(&obj),
NULL);
+ return 0;
+}
static int
qemuBuildVMCoreInfoCommandLine(virCommandPtr cmd,
@@ -10283,6 +10321,9 @@ qemuBuildCommandLine(virQEMUDriverPtr driver,
if (qemuBuildVMCoreInfoCommandLine(cmd, def, qemuCaps) < 0)
goto error;
+ if (qemuBuildSevCommandLine(vm, cmd, def->sev) < 0)
+ goto error;
+
if (snapshot)
virCommandAddArgList(cmd, "-loadvm", snapshot->def->name, NULL);
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 1606f4c..480bc8c 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -5826,6 +5826,65 @@ qemuProcessPrepareDomain(virQEMUDriverPtr driver,
static int
+qemuBuildSevCreateFile(const char *configDir,
+ const char *name,
+ const char *data)
+{
+ char *configFile;
+
+ if (!(configFile = virFileBuildPath(configDir, name, ".base64")))
+ return -1;
+
+ if (virFileRewriteStr(configFile, S_IRUSR | S_IWUSR, data) < 0) {
+ virReportSystemError(errno, _("failed to write data to config
'%s'"),
+ configFile);
+ goto error;
+ }
+
+ VIR_FREE(configFile);
+ return 0;
+
+ error:
+ VIR_FREE(configFile);
+ return -1;
+}
+
+
+static int
+qemuProcessPrepareSevGuestInput(virDomainObjPtr vm)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ virDomainDefPtr def = vm->def;
+ virQEMUCapsPtr qemuCaps = priv->qemuCaps;
+ virDomainSevDefPtr sev = def->sev;
+
+ if (!sev)
+ return 0;
+
+ VIR_DEBUG("Prepare SEV guest");
+
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Domain %s asked for 'sev' launch but this "
+ "QEMU does not support SEV feature"),
vm->def->name);
+ return -1;
+ }
+
+ if (sev->dh_cert) {
+ if (qemuBuildSevCreateFile(priv->libDir, "dh_cert", sev->dh_cert)
< 0)
+ return -1;
+ }
+
+ if (sev->session) {
+ if (qemuBuildSevCreateFile(priv->libDir, "session", sev->session)
< 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
qemuProcessPrepareHostStorage(virQEMUDriverPtr driver,
virDomainObjPtr vm,
unsigned int flags)
@@ -5987,6 +6046,9 @@ qemuProcessPrepareHost(virQEMUDriverPtr driver,
if (qemuExtDevicesPrepareHost(driver, vm->def) < 0)
goto cleanup;
+ if (qemuProcessPrepareSevGuestInput(vm) < 0)
+ goto cleanup;
+
ret = 0;
cleanup:
virObjectUnref(cfg);
diff --git a/tests/qemuxml2argvdata/launch-security-sev.args
b/tests/qemuxml2argvdata/launch-security-sev.args
new file mode 100644
index 0000000..db0be1a
--- /dev/null
+++ b/tests/qemuxml2argvdata/launch-security-sev.args
@@ -0,0 +1,29 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/home/test \
+USER=test \
+LOGNAME=test \
+QEMU_AUDIO_DRV=none \
+/usr/bin/qemu-system-x86_64 \
+-name QEMUGuest1 \
+-S \
+-machine pc-1.0,accel=kvm,usb=off,dump-guest-core=off,memory-encryption=sev0 \
+-m 214 \
+-smp 1,sockets=1,cores=1,threads=1 \
+-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,\
+server,nowait \
+-mon chardev=charmonitor,id=monitor,mode=control \
+-rtc base=utc \
+-no-shutdown \
+-no-acpi \
+-boot c \
+-usb \
+-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \
+-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \
+-object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1,policy=0x1,\
+dh-cert-file=/tmp/lib/domain--1-QEMUGuest1/dh_cert.base64,\
+session-file=/tmp/lib/domain--1-QEMUGuest1/session.base64
diff --git a/tests/qemuxml2argvdata/launch-security-sev.xml
b/tests/qemuxml2argvdata/launch-security-sev.xml
new file mode 100644
index 0000000..5ae83f6
--- /dev/null
+++ b/tests/qemuxml2argvdata/launch-security-sev.xml
@@ -0,0 +1,37 @@
+<domain type='kvm'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219100</memory>
+ <currentMemory unit='KiB'>219100</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-1.0'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw'/>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0'
target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <memballoon model='none'/>
+ </devices>
+ <launch-security type='sev'>
+ <cbitpos>47</cbitpos>
+ <reduced-phys-bits>1</reduced-phys-bits>
+ <policy>0x0001</policy>
+
<dh-cert>AQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAA</dh-cert>
+ <session>IHAVENOIDEABUTJUSTPROVIDINGASTRING</session>
+ </launch-security>
+</domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index c81caec..f630185 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -2897,6 +2897,10 @@ mymain(void)
DO_TEST_CAPS_LATEST("vhost-vsock");
DO_TEST_CAPS_LATEST("vhost-vsock-auto");
+ DO_TEST("launch-security-sev",
+ QEMU_CAPS_KVM,
+ QEMU_CAPS_SEV_GUEST);
+
if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL)
virFileDeleteTree(fakerootdir);
--
2.7.4