[libvirt] [PATCH v8 0/9] x86: Secure Encrypted Virtualization (AMD)

This patch series provides support for launching an encrypted guest using AMD's new Secure Encrypted Virtualization (SEV) feature. SEV is an extension to the AMD-V architecture which supports running multiple VMs under the control of a hypervisor. When enabled, SEV feature allows the memory contents of a virtual machine (VM) to be transparently encrypted with a key unique to the guest VM. At very high level the flow looks this: 1. mgmt tool calls virConnectGetDomainCapabilities. This returns an XML document that includes the following <feature> ... <sev supported='yes'> <cbitpos> </cbitpos> <reduced-phys-bits> </reduced-phys-bits> </sev> </feature> If <sev> is provided then we indicate that hypervisor is capable of launching SEV guest. mgmt tool can call virNodeGetSEVCapabilities() to get the additional informations like PDH and certificate chain etc. 2. (optional) mgmt tool can provide the PDH and Cert-chain to guest owner in case if guest owner wish to establish a secure connection with SEV firmware to negotiate a key used for validating the measurement. 3. mgmt tool requests to start a guest calling virCreateXML(), passing \ VIR_DOMAIN_START_PAUSED. The xml would include <launch-security type='sev'> <cbitpos>47</cbitpos> <reduced-phys-bits>1</reduced-phys-bits> <policy>0x1</policy> (optional) <dh-cert> </dh-cert> /* Guest owners Diffie-Hellman key */ <session> </session> /* Guest owners Session blob */ </launch-security> 4. Libvirt generate the QEMU cli arg to enable the SEV feature, a typical args looks like this: # $QEMU .. -machine memory-encryption=sev0 \ -object sev-guest,id=sev0,dh-cert-file=<file>.... 5. Libvirt generates lifecycle VIR_DOMAIN_EVENT_SUSPENDED_PAUSED event 6. mgmt tool gets the VIR_DOMAIN_EVENT_SUSPENDED_PAUSED and calls \ virDomainGetLaunchSecretInfo() to retrieve the measurement of encrypted memory. 7. (optional) mgmt tool can provide the measurement value to guest owner, which can validate the measurement and gives GO/NO-GO answer. If mgmt tool gets GO then it resumes the guest otherwise it calls destroy() to kill the guest. 8. mgmt tool resumes the guest TODO: * SEV guest require to use DMA apis for the virtio devices. In order to use the DMA apis the virtio devices must have this tag <driver iommu=on ats=on> It is a bit unclear to me where these changes need to go. Do we need to modify the libvirt to automatically add these when SEV is enabled or we ask mgmt tool to make sure that it creates XML with right tag to enable the DMA APIs for virtio devices. I am looking for some suggestions. Using these patches we have succesfully booted and tested a guest both with and without SEV enabled. SEV Firmware API spec is available at: https://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf Change since v7: * rename virNodeSEVCapability() -> virNodeSEVInfo() * rebase the series Changes since v6: * add API to get SEV PDH and Certificate chain data * drop virsh command changes. We can revisit this later when we have more visibility on setter. Change since v5: * drop the seperate test patch and merge the code with other patches. * rename the xml from sev -> launch-security-sev * make policy field mandatory * address multiple feedback from previous reviews. Changes since v4: * add /dev/sev in shared device list Changes since v3: * rename QEMU_CAPS_SEV -> QEMU_CAPS_SEV_GUEST * update caps_2.12.0.x86_64.replies to include query-sev-capabilities data Changes since v2: * make cbitpos, policy and reduced-phys-bits as unsigned int * update virDomainGetLaunchSecurityInfo to accept virTypedParameterPtr *params instead of virTypedParameterPtr params. Changes since v1: * rename <sev> -> <launch-security> for domain * add more information about policy and other fields in domaincaps.html * split the domain_conf support in two patches * add virDomainGetLaunchInfo() to retrieve the SEV measurement * extend virsh command to show the domain's launch security information * add test cases to validate newly added <launch-security> element * fix issues reported with 'make check' and 'make syntax-check' The complete git tree is available at: https://github.com/codomania/libvirt/tree/v8 Brijesh Singh (11): qemu: provide support to query the SEV capability conf: expose SEV feature in domain capabilities libvirt: add new public API to get SEV Info remote: implement the remote protocol for virNodeSEVInfo() qemu: Add support to get the SEV info conf: introduce launch-security element in domain qemu/cgroup: add /dev/sev in shared devices list qemu: add support to launch SEV guest libvirt: add new public API to get launch security info remote: implement the remote protocol for launch security qemu: Add support to launch security info docs/drvqemu.html.in | 1 + docs/formatdomain.html.in | 115 +++++++++++++++ docs/formatdomaincaps.html.in | 30 ++++ docs/schemas/domaincaps.rng | 14 ++ docs/schemas/domaincommon.rng | 37 +++++ include/libvirt/libvirt-domain.h | 17 +++ include/libvirt/libvirt-host.h | 42 ++++++ src/conf/domain_capabilities.c | 31 +++- src/conf/domain_capabilities.h | 17 +++ src/conf/domain_conf.c | 133 +++++++++++++++++ src/conf/domain_conf.h | 27 ++++ src/driver-hypervisor.h | 14 ++ src/libvirt-domain.c | 48 +++++++ src/libvirt-host.c | 47 ++++++ src/libvirt_private.syms | 2 +- src/libvirt_public.syms | 2 + src/qemu/qemu.conf | 2 +- src/qemu/qemu_capabilities.c | 90 +++++++++++- src/qemu/qemu_capabilities.h | 4 + src/qemu/qemu_capspriv.h | 4 + src/qemu/qemu_cgroup.c | 2 +- src/qemu/qemu_command.c | 41 ++++++ src/qemu/qemu_driver.c | 160 +++++++++++++++++++++ src/qemu/qemu_monitor.c | 18 +++ src/qemu/qemu_monitor.h | 6 + src/qemu/qemu_monitor_json.c | 121 ++++++++++++++++ src/qemu/qemu_monitor_json.h | 5 + src/qemu/qemu_process.c | 62 ++++++++ src/qemu/test_libvirtd_qemu.aug.in | 1 + src/remote/remote_daemon_dispatch.c | 91 ++++++++++++ src/remote/remote_driver.c | 80 +++++++++++ src/remote/remote_protocol.x | 39 ++++- src/remote_protocol-structs | 24 ++++ tests/genericxml2xmlindata/launch-security-sev.xml | 24 ++++ tests/genericxml2xmltest.c | 2 + .../caps_2.12.0.x86_64.replies | 10 ++ tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml | 3 +- tests/qemuxml2argvdata/launch-security-sev.args | 29 ++++ tests/qemuxml2argvdata/launch-security-sev.xml | 37 +++++ tests/qemuxml2argvtest.c | 4 + 40 files changed, 1429 insertions(+), 7 deletions(-) create mode 100644 tests/genericxml2xmlindata/launch-security-sev.xml create mode 100644 tests/qemuxml2argvdata/launch-security-sev.args create mode 100644 tests/qemuxml2argvdata/launch-security-sev.xml -- 2.7.4

QEMU version >= 2.12 provides support for launching an encrypted VMs on AMD x86 platform using Secure Encrypted Virtualization (SEV) feature. This patch adds support to query the SEV capability from the qemu. Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> --- src/conf/domain_capabilities.c | 12 ++++ src/conf/domain_capabilities.h | 16 +++++ src/libvirt_private.syms | 2 +- src/qemu/qemu_capabilities.c | 35 ++++++++++ src/qemu/qemu_capabilities.h | 2 +- src/qemu/qemu_capspriv.h | 4 ++ src/qemu/qemu_monitor.c | 10 +++ src/qemu/qemu_monitor.h | 3 + src/qemu/qemu_monitor_json.c | 79 ++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + .../caps_2.12.0.x86_64.replies | 10 +++ tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml | 3 +- 12 files changed, 176 insertions(+), 3 deletions(-) diff --git a/src/conf/domain_capabilities.c b/src/conf/domain_capabilities.c index c20358e..3589777 100644 --- a/src/conf/domain_capabilities.c +++ b/src/conf/domain_capabilities.c @@ -67,6 +67,18 @@ virDomainCapsStringValuesFree(virDomainCapsStringValuesPtr values) } +void +virSEVCapabilitiesFree(virSEVCapability *cap) +{ + if (!cap) + return; + + VIR_FREE(cap->pdh); + VIR_FREE(cap->cert_chain); + VIR_FREE(cap); +} + + static void virDomainCapsDispose(void *obj) { diff --git a/src/conf/domain_capabilities.h b/src/conf/domain_capabilities.h index b0eb4aa..30b3272 100644 --- a/src/conf/domain_capabilities.h +++ b/src/conf/domain_capabilities.h @@ -137,6 +137,22 @@ struct _virDomainCapsCPU { virDomainCapsCPUModelsPtr custom; }; +/* + * SEV capabilities + */ +typedef struct _virSEVCapability virSEVCapability; +typedef virSEVCapability *virSEVCapabilityPtr; +struct _virSEVCapability { + char *pdh; + char *cert_chain; + unsigned int cbitpos; + unsigned int reduced_phys_bits; +}; + +void +virSEVCapabilitiesFree(virSEVCapability *capabilities); + + struct _virDomainCaps { virObjectLockable parent; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 5540391..59a2efd 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -185,7 +185,7 @@ virDomainCapsEnumClear; virDomainCapsEnumSet; virDomainCapsFormat; virDomainCapsNew; - +virSEVCapabilitiesFree; # conf/domain_conf.h virBlkioDeviceArrayClear; diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index b20149b..70cf1e5 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -494,6 +494,7 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST, /* 305 */ "vhost-vsock", "chardev-fd-pass", + "sev-guest", ); @@ -560,6 +561,8 @@ struct _virQEMUCaps { size_t ngicCapabilities; virGICCapability *gicCapabilities; + virSEVCapability *sevCapabilities; + virQEMUCapsHostCPUData kvmCPU; virQEMUCapsHostCPUData tcgCPU; }; @@ -1131,6 +1134,7 @@ struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = { { "hda-output", QEMU_CAPS_HDA_OUTPUT }, { "vmgenid", QEMU_CAPS_DEVICE_VMGENID }, { "vhost-vsock-device", QEMU_CAPS_DEVICE_VHOST_VSOCK }, + { "sev-guest", QEMU_CAPS_SEV_GUEST }, }; static struct virQEMUCapsStringFlags virQEMUCapsDevicePropsVirtioBalloon[] = { @@ -2067,6 +2071,16 @@ virQEMUCapsSetGICCapabilities(virQEMUCapsPtr qemuCaps, } +void +virQEMUCapsSetSEVCapabilities(virQEMUCapsPtr qemuCaps, + virSEVCapability *capabilities) +{ + virSEVCapabilitiesFree(qemuCaps->sevCapabilities); + + qemuCaps->sevCapabilities = capabilities; +} + + static int virQEMUCapsProbeQMPCommands(virQEMUCapsPtr qemuCaps, qemuMonitorPtr mon) @@ -2650,6 +2664,21 @@ virQEMUCapsProbeQMPGICCapabilities(virQEMUCapsPtr qemuCaps, } +static int +virQEMUCapsProbeQMPSEVCapabilities(virQEMUCapsPtr qemuCaps, + qemuMonitorPtr mon) +{ + virSEVCapability *caps = NULL; + + if (qemuMonitorGetSEVCapabilities(mon, &caps) < 0) + return -1; + + virQEMUCapsSetSEVCapabilities(qemuCaps, caps); + + return 0; +} + + bool virQEMUCapsCPUFilterFeatures(const char *name, void *opaque) @@ -4049,6 +4078,12 @@ virQEMUCapsInitQMPMonitor(virQEMUCapsPtr qemuCaps, virQEMUCapsClear(qemuCaps, QEMU_CAPS_DEVICE_VFIO_CCW); } + /* Probe for SEV capabilities */ + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST)) { + if (virQEMUCapsProbeQMPSEVCapabilities(qemuCaps, mon) < 0) + virQEMUCapsClear(qemuCaps, QEMU_CAPS_SEV_GUEST); + } + ret = 0; cleanup: return ret; diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index f60dfb1..7390271 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -478,6 +478,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ /* 305 */ QEMU_CAPS_DEVICE_VHOST_VSOCK, /* -device vhost-vsock-* */ QEMU_CAPS_CHARDEV_FD_PASS, /* Passing pre-opened FDs for chardevs */ + QEMU_CAPS_SEV_GUEST, /* -object sev-guest,... */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; @@ -615,5 +616,4 @@ bool virQEMUCapsGuestIsNative(virArch host, bool virQEMUCapsCPUFilterFeatures(const char *name, void *opaque); - #endif /* __QEMU_CAPABILITIES_H__*/ diff --git a/src/qemu/qemu_capspriv.h b/src/qemu/qemu_capspriv.h index 989d183..29d7639 100644 --- a/src/qemu/qemu_capspriv.h +++ b/src/qemu/qemu_capspriv.h @@ -90,6 +90,10 @@ virQEMUCapsSetGICCapabilities(virQEMUCapsPtr qemuCaps, virGICCapability *capabilities, size_t ncapabilities); +void +virQEMUCapsSetSEVCapabilities(virQEMUCapsPtr qemuCaps, + virSEVCapability *capabilities); + int virQEMUCapsProbeQMPCPUDefinitions(virQEMUCapsPtr qemuCaps, qemuMonitorPtr mon, diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 215135a..fd6bce9 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3850,6 +3850,16 @@ qemuMonitorGetGICCapabilities(qemuMonitorPtr mon, int +qemuMonitorGetSEVCapabilities(qemuMonitorPtr mon, + virSEVCapability **capabilities) +{ + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONGetSEVCapabilities(mon, capabilities); +} + + +int qemuMonitorNBDServerStart(qemuMonitorPtr mon, const char *host, unsigned int port, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 4384372..75d5d98 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -711,6 +711,9 @@ int qemuMonitorSetMigrationCapabilities(qemuMonitorPtr mon, int qemuMonitorGetGICCapabilities(qemuMonitorPtr mon, virGICCapability **capabilities); +int qemuMonitorGetSEVCapabilities(qemuMonitorPtr mon, + virSEVCapability **capabilities); + typedef enum { QEMU_MONITOR_MIGRATE_BACKGROUND = 1 << 0, QEMU_MONITOR_MIGRATE_NON_SHARED_DISK = 1 << 1, /* migration with non-shared storage with full disk copy */ diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index e8a46d2..ba0da9a 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -6401,6 +6401,85 @@ qemuMonitorJSONGetGICCapabilities(qemuMonitorPtr mon, return ret; } + +int +qemuMonitorJSONGetSEVCapabilities(qemuMonitorPtr mon, + virSEVCapability **capabilities) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + virJSONValuePtr caps; + virSEVCapability *capability = NULL; + const char *pdh = NULL, *cert_chain = NULL; + unsigned int cbitpos, reduced_phys_bits; + + *capabilities = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("query-sev-capabilities", + NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + goto cleanup; + + caps = virJSONValueObjectGetObject(reply, "return"); + + if (virJSONValueObjectGetNumberUint(caps, "cbitpos", &cbitpos) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-sev-capabilities reply was missing" + " 'cbitpos' field")); + goto cleanup; + } + + if (virJSONValueObjectGetNumberUint(caps, "reduced-phys-bits", + &reduced_phys_bits) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-sev-capabilities reply was missing" + " 'reduced-phys-bits' field")); + goto cleanup; + } + + if (!(pdh = virJSONValueObjectGetString(caps, "pdh"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-sev-capabilities reply was missing" + " 'pdh' field")); + goto cleanup; + } + + if (!(cert_chain = virJSONValueObjectGetString(caps, "cert-chain"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-sev-capabilities reply was missing" + " 'cert-chain' field")); + goto cleanup; + } + + if (VIR_ALLOC(capability) < 0) + goto cleanup; + + if (VIR_STRDUP(capability->pdh, pdh) < 0) + goto cleanup; + + if (VIR_STRDUP(capability->cert_chain, cert_chain) < 0) + goto cleanup; + + capability->cbitpos = cbitpos; + capability->reduced_phys_bits = reduced_phys_bits; + VIR_STEAL_PTR(*capabilities, capability); + ret = 0; + + cleanup: + virSEVCapabilitiesFree(capability); + virJSONValueFree(cmd); + virJSONValueFree(reply); + + return ret; +} + static virJSONValuePtr qemuMonitorJSONBuildInetSocketAddress(const char *host, const char *port) diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 2ae0faa..4c10574 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -152,6 +152,9 @@ int qemuMonitorJSONSetMigrationCapabilities(qemuMonitorPtr mon, int qemuMonitorJSONGetGICCapabilities(qemuMonitorPtr mon, virGICCapability **capabilities); +int qemuMonitorJSONGetSEVCapabilities(qemuMonitorPtr mon, + virSEVCapability **capabilities); + int qemuMonitorJSONMigrate(qemuMonitorPtr mon, unsigned int flags, const char *uri); diff --git a/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.replies b/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.replies index c40046b..ace3537 100644 --- a/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.replies +++ b/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.replies @@ -18996,6 +18996,16 @@ } { + "return" : { + "reduced-phys-bits": 1, + "cbitpos": 47, + "cert-chain": "AQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAA", + "pdh": "AQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAA" + }, + "id": "libvirt-52" +} + +{ "return": { }, "id": "libvirt-1" diff --git a/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml index 038c92c..eb757cc 100644 --- a/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml @@ -207,9 +207,10 @@ <flag name='vmgenid'/> <flag name='vhost-vsock'/> <flag name='chardev-fd-pass'/> + <flag name='sev-guest'/> <version>2011090</version> <kvmVersion>0</kvmVersion> - <microcodeVersion>390813</microcodeVersion> + <microcodeVersion>391059</microcodeVersion> <package>v2.12.0-rc0</package> <arch>x86_64</arch> <hostCPU type='kvm' model='base' migratability='yes'> -- 2.7.4

On Wed, Jun 06, 2018 at 12:50:07PM -0500, Brijesh Singh wrote:
QEMU version >= 2.12 provides support for launching an encrypted VMs on AMD x86 platform using Secure Encrypted Virtualization (SEV) feature. This patch adds support to query the SEV capability from the qemu.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> --- src/conf/domain_capabilities.c | 12 ++++ src/conf/domain_capabilities.h | 16 +++++ src/libvirt_private.syms | 2 +- src/qemu/qemu_capabilities.c | 35 ++++++++++ src/qemu/qemu_capabilities.h | 2 +- src/qemu/qemu_capspriv.h | 4 ++ src/qemu/qemu_monitor.c | 10 +++ src/qemu/qemu_monitor.h | 3 + src/qemu/qemu_monitor_json.c | 79 ++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + .../caps_2.12.0.x86_64.replies | 10 +++ tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml | 3 +- 12 files changed, 176 insertions(+), 3 deletions(-)
With the following squashed in: Reviewed-by: Erik Skultety <eskultet@redhat.com> diff --git a/src/conf/domain_capabilities.h b/src/conf/domain_capabilities.h index 30b32725c0..56c1903cf4 100644 --- a/src/conf/domain_capabilities.h +++ b/src/conf/domain_capabilities.h @@ -137,9 +137,6 @@ struct _virDomainCapsCPU { virDomainCapsCPUModelsPtr custom; }; -/* - * SEV capabilities - */ typedef struct _virSEVCapability virSEVCapability; typedef virSEVCapability *virSEVCapabilityPtr; struct _virSEVCapability { @@ -149,10 +146,6 @@ struct _virSEVCapability { unsigned int reduced_phys_bits; }; -void -virSEVCapabilitiesFree(virSEVCapability *capabilities); - - struct _virDomainCaps { virObjectLockable parent; @@ -218,4 +211,7 @@ int virDomainCapsEnumSet(virDomainCapsEnumPtr capsEnum, void virDomainCapsEnumClear(virDomainCapsEnumPtr capsEnum); char * virDomainCapsFormat(virDomainCapsPtr const caps); + +void +virSEVCapabilitiesFree(virSEVCapability *capabilities); #endif /* __DOMAIN_CAPABILITIES_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ba435db07f..af66c7995f 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -187,6 +187,7 @@ virDomainCapsFormat; virDomainCapsNew; virSEVCapabilitiesFree; + # conf/domain_conf.h virBlkioDeviceArrayClear; virDiskNameParse;

Extend hypervisor capabilities to include sev feature. When available, hypervisor supports launching an encrypted VM on AMD platform. The sev feature tag provides additional details like Platform Diffie-Hellman (PDH) key and certificate chain which can be used by the guest owner to establish a cryptographic session with the SEV firmware to negotiate keys used for attestation or to provide secret during launch. Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> --- docs/formatdomaincaps.html.in | 30 ++++++++++++++++++++++++++ docs/schemas/domaincaps.rng | 14 ++++++++++++ src/conf/domain_capabilities.c | 19 ++++++++++++++++- src/conf/domain_capabilities.h | 1 + src/qemu/qemu_capabilities.c | 48 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 110 insertions(+), 2 deletions(-) diff --git a/docs/formatdomaincaps.html.in b/docs/formatdomaincaps.html.in index e0814cb..6be553a 100644 --- a/docs/formatdomaincaps.html.in +++ b/docs/formatdomaincaps.html.in @@ -435,6 +435,10 @@ </gic> <vmcoreinfo supported='yes'/> <genid supported='yes'/> + <sev> + <cbitpos>47</cbitpos> + <reduced-phys-bits>1</reduced-phys-bits> + </sev> </features> </domainCapabilities> </pre> @@ -467,5 +471,31 @@ <p>Reports whether the genid feature can be used by the domain.</p> + <h4><a id="elementsSEV">SEV capabilities</a></h4> + + <p>AMD Secure Encrypted Virtualization (SEV) capabilities are exposed under + the <code>sev</code> element. + SEV is an extension to the AMD-V architecture which supports running + virtual machines (VMs) under the control of a hypervisor. When supported, + guest owner can create a VM whose memory contents will be transparently + encrypted with a key unique to that VM.</p> + + <p> + For more details on SEV feature see: + <a href="https://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf"> + SEV API spec</a> and <a href="http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf"> + SEV White Paper</a> + </p> + + <dl> + <dt><code>cbitpos</code></dt> + <dd>When memory encryption is enabled, one of the physical address bits + (aka the C-bit) is utilized to mark if a memory page is protected. The + C-bit position is Hypervisor dependent.</dd> + <dt><code>reduced-phys-bits</code></dt> + <dd>When memory encryption is enabled, we lose certain bits in physical + address space. The number of bits we lose is hypervisor dependent.</dd> + </dl> + </body> </html> diff --git a/docs/schemas/domaincaps.rng b/docs/schemas/domaincaps.rng index 5ceabb0..1d0a2e1 100644 --- a/docs/schemas/domaincaps.rng +++ b/docs/schemas/domaincaps.rng @@ -185,6 +185,9 @@ <ref name='gic'/> <ref name='vmcoreinfo'/> <ref name='vmgenid'/> + <optional> + <ref name='sev'/> + </optional> </interleave> </element> </define> @@ -208,6 +211,17 @@ </element> </define> + <define name='sev'> + <element name='sev'> + <element name='cbitpos'> + <data type='unsignedInt'/> + </element> + <element name='reduced-phys-bits'> + <data type='unsignedInt'/> + </element> + </element> + </define> + <define name='value'> <zeroOrMore> <element name='value'> diff --git a/src/conf/domain_capabilities.c b/src/conf/domain_capabilities.c index 3589777..54b0878 100644 --- a/src/conf/domain_capabilities.c +++ b/src/conf/domain_capabilities.c @@ -88,6 +88,7 @@ virDomainCapsDispose(void *obj) VIR_FREE(caps->machine); virObjectUnref(caps->cpu.custom); virCPUDefFree(caps->cpu.hostModel); + virSEVCapabilitiesFree(caps->sev); virDomainCapsStringValuesFree(&caps->os.loader.values); } @@ -554,6 +555,22 @@ virDomainCapsFeatureGICFormat(virBufferPtr buf, FORMAT_EPILOGUE(gic); } +static void +virDomainCapsFeatureSEVFormat(virBufferPtr buf, + virSEVCapabilityPtr const sev) +{ + if (!sev) + return; + + virBufferAddLit(buf, "<sev supported='yes'>\n"); + virBufferAdjustIndent(buf, 2); + virBufferAsprintf(buf, "<cbitpos>%d</cbitpos>\n", sev->cbitpos); + virBufferAsprintf(buf, "<reduced-phys-bits>%d</reduced-phys-bits>\n", + sev->reduced_phys_bits); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</sev>\n"); +} + char * virDomainCapsFormat(virDomainCapsPtr const caps) @@ -597,9 +614,9 @@ virDomainCapsFormat(virDomainCapsPtr const caps) virDomainCapsFeatureGICFormat(&buf, &caps->gic); virBufferAsprintf(&buf, "<vmcoreinfo supported='%s'/>\n", caps->vmcoreinfo ? "yes" : "no"); - virBufferAsprintf(&buf, "<genid supported='%s'/>\n", caps->genid ? "yes" : "no"); + virDomainCapsFeatureSEVFormat(&buf, caps->sev); virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "</features>\n"); diff --git a/src/conf/domain_capabilities.h b/src/conf/domain_capabilities.h index 30b3272..17c1f1c 100644 --- a/src/conf/domain_capabilities.h +++ b/src/conf/domain_capabilities.h @@ -176,6 +176,7 @@ struct _virDomainCaps { virDomainCapsFeatureGIC gic; bool vmcoreinfo; bool genid; + virSEVCapabilityPtr sev; /* add new domain features here */ }; diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 70cf1e5..a2103e3 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -5151,6 +5151,50 @@ virQEMUCapsFillDomainFeatureGICCaps(virQEMUCapsPtr qemuCaps, } +/** + * virQEMUCapsFillDomainFeatureSEVCaps: + * @qemuCaps: QEMU capabilities + * @domCaps: domain capabilities + * + * Take the information about SEV capabilities that has been obtained + * using the 'query-sev-capabilities' QMP command and stored in @qemuCaps + * and convert it to a form suitable for @domCaps. + * + * Returns: 0 on success, <0 on failure + */ +static int +virQEMUCapsFillDomainFeatureSEVCaps(virQEMUCapsPtr qemuCaps, + virDomainCapsPtr domCaps) +{ + virSEVCapability *sev; + virSEVCapability *cap = qemuCaps->sevCapabilities; + + if (!cap) + return 0; + + if (VIR_ALLOC(sev) < 0) + return -1; + + if (VIR_STRDUP(sev->pdh, cap->pdh) < 0) + goto out; + + if (VIR_STRDUP(sev->cert_chain, cap->cert_chain) < 0) + goto out; + + sev->cbitpos = cap->cbitpos; + sev->reduced_phys_bits = cap->reduced_phys_bits; + domCaps->sev = sev; + + return 0; + + out: + VIR_FREE(sev->cert_chain); + VIR_FREE(sev->pdh); + VIR_FREE(sev); + return -1; +} + + int virQEMUCapsFillDomainCaps(virCapsPtr caps, virDomainCapsPtr domCaps, @@ -5188,8 +5232,10 @@ virQEMUCapsFillDomainCaps(virCapsPtr caps, virQEMUCapsFillDomainDeviceGraphicsCaps(qemuCaps, graphics) < 0 || virQEMUCapsFillDomainDeviceVideoCaps(qemuCaps, video) < 0 || virQEMUCapsFillDomainDeviceHostdevCaps(qemuCaps, hostdev) < 0 || - virQEMUCapsFillDomainFeatureGICCaps(qemuCaps, domCaps) < 0) + virQEMUCapsFillDomainFeatureGICCaps(qemuCaps, domCaps) < 0 || + virQEMUCapsFillDomainFeatureSEVCaps(qemuCaps, domCaps) < 0) return -1; + return 0; } -- 2.7.4

On Wed, Jun 06, 2018 at 12:50:08PM -0500, Brijesh Singh wrote:
Extend hypervisor capabilities to include sev feature. When available, hypervisor supports launching an encrypted VM on AMD platform. The sev feature tag provides additional details like Platform Diffie-Hellman (PDH) key and certificate chain which can be used by the guest owner to establish a cryptographic session with the SEV firmware to negotiate keys used for attestation or to provide secret during launch.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> --- docs/formatdomaincaps.html.in | 30 ++++++++++++++++++++++++++ docs/schemas/domaincaps.rng | 14 ++++++++++++ src/conf/domain_capabilities.c | 19 ++++++++++++++++- src/conf/domain_capabilities.h | 1 + src/qemu/qemu_capabilities.c | 48 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 110 insertions(+), 2 deletions(-)
With the diff below squashed in: Reviewed-by: Erik Skultety <eskultet@redhat.com> diff --git a/src/conf/domain_capabilities.c b/src/conf/domain_capabilities.c index 54b0878b78..ec469bfb9a 100644 --- a/src/conf/domain_capabilities.c +++ b/src/conf/domain_capabilities.c @@ -614,6 +614,7 @@ virDomainCapsFormat(virDomainCapsPtr const caps) virDomainCapsFeatureGICFormat(&buf, &caps->gic); virBufferAsprintf(&buf, "<vmcoreinfo supported='%s'/>\n", caps->vmcoreinfo ? "yes" : "no"); + virBufferAsprintf(&buf, "<genid supported='%s'/>\n", caps->genid ? "yes" : "no"); virDomainCapsFeatureSEVFormat(&buf, caps->sev); diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 503ed975eb..44ce12c7b9 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -5165,7 +5165,7 @@ virQEMUCapsFillDomainFeatureGICCaps(virQEMUCapsPtr qemuCaps, * using the 'query-sev-capabilities' QMP command and stored in @qemuCaps * and convert it to a form suitable for @domCaps. * - * Returns: 0 on success, <0 on failure + * Returns: 0 on success, -1 on failure */ static int virQEMUCapsFillDomainFeatureSEVCaps(virQEMUCapsPtr qemuCaps, @@ -5173,6 +5173,7 @@ virQEMUCapsFillDomainFeatureSEVCaps(virQEMUCapsPtr qemuCaps, { virSEVCapability *sev; virSEVCapability *cap = qemuCaps->sevCapabilities; + int ret = -1; if (!cap) return 0; @@ -5181,22 +5182,19 @@ virQEMUCapsFillDomainFeatureSEVCaps(virQEMUCapsPtr qemuCaps, return -1; if (VIR_STRDUP(sev->pdh, cap->pdh) < 0) - goto out; + goto cleanup; if (VIR_STRDUP(sev->cert_chain, cap->cert_chain) < 0) - goto out; + goto cleanup; sev->cbitpos = cap->cbitpos; sev->reduced_phys_bits = cap->reduced_phys_bits; - domCaps->sev = sev; + VIR_STEAL_PTR(domCaps->sev, sev); - return 0; - - out: - VIR_FREE(sev->cert_chain); - VIR_FREE(sev->pdh); - VIR_FREE(sev); - return -1; + ret = 0; + cleanup: + virSEVCapabilitiesFree(sev); + return ret; }

The API can be used by application to retrieve the Platform Diffie-Hellman Key and Platform Certificate chain. Signed-off-by: Brijesh Singh <<brijesh.singh@amd.com>> --- include/libvirt/libvirt-host.h | 42 +++++++++++++++++++++++++++++++++++++ src/driver-hypervisor.h | 6 ++++++ src/libvirt-host.c | 47 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 96 insertions(+) diff --git a/include/libvirt/libvirt-host.h b/include/libvirt/libvirt-host.h index 84f4858..e46f88b 100644 --- a/include/libvirt/libvirt-host.h +++ b/include/libvirt/libvirt-host.h @@ -432,6 +432,48 @@ typedef virNodeCPUStats *virNodeCPUStatsPtr; typedef virNodeMemoryStats *virNodeMemoryStatsPtr; + +/** + * + * SEV Parameters + */ + +/** + * VIR_NODE_SEV_PDH: + * + * Marco represents the Platform Diffie-Hellman key, as VIR_TYPED_PARAMS_STRING. + */ +# define VIR_NODE_SEV_PDH "pdh" + +/** + * VIR_NODE_SEV_CERT_CHAIN: + * + * Marco represents the Platform certificate chain that includes the + * endorsement key (PEK), owner certificate authority (OCD) and chip + * endorsement key (CEK), as VIR_TYPED_PARAMS_STRING. + */ +# define VIR_NODE_SEV_CERT_CHAIN "cert-chain" + +/** + * VIR_NODE_SEV_CBITPOS: + * + * Marco represents the CBit Position used by hypervisor when SEV is enabled. + */ +# define VIR_NODE_SEV_CBITPOS "cbitpos" + +/** + * VIR_NODE_SEV_REDUCED_PHYS_BITS: + * + * Marco represents the number of bits we lose in physical address space + * when SEV is enabled in the guest. + */ +# define VIR_NODE_SEV_REDUCED_PHYS_BITS "reduced-phys-bits" + +int virNodeGetSEVInfo (virConnectPtr conn, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags); + /** * virConnectFlags * diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index aa99cbb..c50d2a0 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1309,6 +1309,11 @@ typedef int unsigned int action, unsigned int flags); +typedef int +(*virDrvNodeGetSEVInfo)(virConnectPtr conn, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags); typedef struct _virHypervisorDriver virHypervisorDriver; typedef virHypervisorDriver *virHypervisorDriverPtr; @@ -1558,6 +1563,7 @@ struct _virHypervisorDriver { virDrvDomainSetLifecycleAction domainSetLifecycleAction; virDrvConnectCompareHypervisorCPU connectCompareHypervisorCPU; virDrvConnectBaselineHypervisorCPU connectBaselineHypervisorCPU; + virDrvNodeGetSEVInfo nodeGetSEVInfo; }; diff --git a/src/libvirt-host.c b/src/libvirt-host.c index 3aaf558..2a633f0 100644 --- a/src/libvirt-host.c +++ b/src/libvirt-host.c @@ -1639,3 +1639,50 @@ virNodeAllocPages(virConnectPtr conn, virDispatchError(conn); return -1; } + +/* + * virNodeGetSEVInfo: + * @conn: pointer to the hypervisor connection + * @params: where to store SEV information; output + * @nparams: pointer to number of SEV parameters; output + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * If hypervisor supports SEV then @params will contains PDH and + * certificate chain. + * + * Returns 0 in case of success, and -1 in case of failure. + */ +int +virNodeGetSEVInfo(virConnectPtr conn, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + VIR_DEBUG("conn=%p, params=%p, nparams=%p, flags=0x%x", + conn, params, nparams, flags); + + virResetLastError(); + + virCheckConnectReturn(conn, -1); + virCheckNonNullArgGoto(nparams, error); + virCheckNonNegativeArgGoto(*nparams, error); + virCheckReadOnlyGoto(conn->flags, error); + + if (VIR_DRV_SUPPORTS_FEATURE(conn->driver, conn, + VIR_DRV_FEATURE_TYPED_PARAM_STRING)) + flags |= VIR_TYPED_PARAM_STRING_OKAY; + + if (conn->driver->nodeGetSEVInfo) { + int ret; + ret = conn->driver->nodeGetSEVInfo(conn, params, nparams, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 4f54b84..524d5fd 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -796,6 +796,7 @@ LIBVIRT_4.5.0 { global: virGetLastErrorCode; virGetLastErrorDomain; + virNodeGetSEVInfo; } LIBVIRT_4.4.0; # .... define new API here using predicted next version number .... -- 2.7.4

A better commit subject would have been: libvirt: Introduce virNodeGetSEVInfo public API On Wed, Jun 06, 2018 at 12:50:09PM -0500, Brijesh Singh wrote:
The API can be used by application to retrieve the Platform Diffie-Hellman Key and Platform Certificate chain.
Signed-off-by: Brijesh Singh <<brijesh.singh@amd.com>> --- include/libvirt/libvirt-host.h | 42 +++++++++++++++++++++++++++++++++++++ src/driver-hypervisor.h | 6 ++++++ src/libvirt-host.c | 47 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 96 insertions(+)
There were a few typos in the macro description, please squash in the diff below. Reviewed-by: Erik Skultety <eskultet@redhat.com> diff --git a/include/libvirt/libvirt-host.h b/include/libvirt/libvirt-host.h index e46f88b5ce..a04d669901 100644 --- a/include/libvirt/libvirt-host.h +++ b/include/libvirt/libvirt-host.h @@ -441,33 +441,33 @@ typedef virNodeMemoryStats *virNodeMemoryStatsPtr; /** * VIR_NODE_SEV_PDH: * - * Marco represents the Platform Diffie-Hellman key, as VIR_TYPED_PARAMS_STRING. + * Macro represents the Platform Diffie-Hellman key, as VIR_TYPED_PARAMS_STRING. */ -# define VIR_NODE_SEV_PDH "pdh" +# define VIR_NODE_SEV_PDH "pdh" /** * VIR_NODE_SEV_CERT_CHAIN: * - * Marco represents the Platform certificate chain that includes the + * Macro represents the platform certificate chain that includes the platform * endorsement key (PEK), owner certificate authority (OCD) and chip * endorsement key (CEK), as VIR_TYPED_PARAMS_STRING. */ -# define VIR_NODE_SEV_CERT_CHAIN "cert-chain" +# define VIR_NODE_SEV_CERT_CHAIN "cert-chain" /** * VIR_NODE_SEV_CBITPOS: * - * Marco represents the CBit Position used by hypervisor when SEV is enabled. + * Macro represents the CBit Position used by hypervisor when SEV is enabled. */ -# define VIR_NODE_SEV_CBITPOS "cbitpos" +# define VIR_NODE_SEV_CBITPOS "cbitpos" /** * VIR_NODE_SEV_REDUCED_PHYS_BITS: * - * Marco represents the number of bits we lose in physical address space + * Macro represents the number of bits we lose in physical address space * when SEV is enabled in the guest. */ -# define VIR_NODE_SEV_REDUCED_PHYS_BITS "reduced-phys-bits" +# define VIR_NODE_SEV_REDUCED_PHYS_BITS "reduced-phys-bits" int virNodeGetSEVInfo (virConnectPtr conn, virTypedParameterPtr *params, diff --git a/src/libvirt-host.c b/src/libvirt-host.c index 2a633f01d5..e20d6ee250 100644 --- a/src/libvirt-host.c +++ b/src/libvirt-host.c @@ -1640,15 +1640,17 @@ virNodeAllocPages(virConnectPtr conn, return -1; } + /* * virNodeGetSEVInfo: * @conn: pointer to the hypervisor connection - * @params: where to store SEV information; output - * @nparams: pointer to number of SEV parameters; output + * @params: where to store SEV information + * @nparams: pointer to number of SEV parameters returned in @params * @flags: extra flags; not used yet, so callers should always pass 0 * - * If hypervisor supports SEV then @params will contains PDH and - * certificate chain. + * If hypervisor supports AMD's SEV feature, then @params will contain various + * platform specific information like PDH and certificate chain. Caller is + * responsible for freeing @params. * * Returns 0 in case of success, and -1 in case of failure. */

Add remote support for virNodeSEVInfo(). Signed-off-by: Brijesh Singh <<brijesh.singh@amd.com>> --- src/remote/remote_daemon_dispatch.c | 44 +++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 40 +++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 22 ++++++++++++++++++- src/remote_protocol-structs | 13 +++++++++++ 4 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c index 81d0445..959367f 100644 --- a/src/remote/remote_daemon_dispatch.c +++ b/src/remote/remote_daemon_dispatch.c @@ -5001,6 +5001,50 @@ remoteDispatchDomainGetDiskErrors(virNetServerPtr server ATTRIBUTE_UNUSED, static int +remoteDispatchNodeGetSevInfo(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_node_get_sev_info_args *args, + remote_node_get_sev_info_ret *ret) +{ + virTypedParameterPtr params = NULL; + int nparams = 0; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (virNodeGetSEVInfo(priv->conn, ¶ms, &nparams, args->flags) < 0) + goto cleanup; + + if (nparams > REMOTE_NODE_SEV_INFO_MAX) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large")); + goto cleanup; + } + + + if (virTypedParamsSerialize(params, nparams, + (virTypedParameterRemotePtr *) &ret->params.params_val, + &ret->params.params_len, + args->flags) < 0) + goto cleanup; + + rv = 0; + + cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virTypedParamsFree(params, nparams); + return rv; +} + + +static int remoteDispatchNodeGetMemoryParameters(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, virNetMessagePtr msg ATTRIBUTE_UNUSED, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index c22993c..8ac7264 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6775,6 +6775,45 @@ remoteNodeGetMemoryParameters(virConnectPtr conn, return rv; } + +static int +remoteNodeGetSEVInfo(virConnectPtr conn, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + int rv = -1; + remote_node_get_sev_info_args args; + remote_node_get_sev_info_ret ret; + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + args.flags = flags; + + memset(&ret, 0, sizeof(ret)); + if (call(conn, priv, 0, REMOTE_PROC_NODE_GET_SEV_INFO, + (xdrproc_t) xdr_remote_node_get_sev_info_args, (char *) &args, + (xdrproc_t) xdr_remote_node_get_sev_info_ret, (char *) &ret) == -1) + goto done; + + if (virTypedParamsDeserialize((virTypedParameterRemotePtr) ret.params.params_val, + ret.params.params_len, + REMOTE_NODE_SEV_INFO_MAX, + params, + nparams) < 0) + goto cleanup; + + rv = 0; + + cleanup: + xdr_free((xdrproc_t) xdr_remote_node_get_sev_info_ret, (char *) &ret); + done: + remoteDriverUnlock(priv); + return rv; +} + + static int remoteNodeGetCPUMap(virConnectPtr conn, unsigned char **cpumap, @@ -8451,6 +8490,7 @@ static virHypervisorDriver hypervisor_driver = { .domainSetLifecycleAction = remoteDomainSetLifecycleAction, /* 3.9.0 */ .connectCompareHypervisorCPU = remoteConnectCompareHypervisorCPU, /* 4.4.0 */ .connectBaselineHypervisorCPU = remoteConnectBaselineHypervisorCPU, /* 4.4.0 */ + .nodeGetSEVInfo = remoteNodeGetSEVInfo, /* 4.5.0 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index a0ab7e9..ec72afa 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -253,6 +253,9 @@ const REMOTE_DOMAIN_IP_ADDR_MAX = 2048; /* Upper limit on number of guest vcpu information entries */ const REMOTE_DOMAIN_GUEST_VCPU_PARAMS_MAX = 64; +/* Upper limit on number of SEV parameters */ +const REMOTE_NODE_SEV_INFO_MAX = 64; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -3480,6 +3483,17 @@ struct remote_connect_baseline_hypervisor_cpu_ret { remote_nonnull_string cpu; }; +struct remote_node_get_sev_info_args { + int nparams; + unsigned int flags; +}; + +struct remote_node_get_sev_info_ret { + remote_typed_param params<REMOTE_NODE_SEV_INFO_MAX>; + int nparams; +}; + + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -6187,5 +6201,11 @@ enum remote_procedure { * @generate: both * @acl: connect:write */ - REMOTE_PROC_CONNECT_BASELINE_HYPERVISOR_CPU = 394 + REMOTE_PROC_CONNECT_BASELINE_HYPERVISOR_CPU = 394, + + /** + * @generate: none + * @acl: connect:read + */ + REMOTE_PROC_NODE_GET_SEV_INFO = 395 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 0c4cfc6..afeefd3 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2907,6 +2907,18 @@ struct remote_connect_baseline_hypervisor_cpu_args { struct remote_connect_baseline_hypervisor_cpu_ret { remote_nonnull_string cpu; }; +struct remote_node_get_sev_capability_args { + int nparams; + u_int flags; +}; +struct remote_node_get_sev_capability_ret { + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + int nparams; +}; + enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3302,4 +3314,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_DETACH_DEVICE_ALIAS = 392, REMOTE_PROC_CONNECT_COMPARE_HYPERVISOR_CPU = 393, REMOTE_PROC_CONNECT_BASELINE_HYPERVISOR_CPU = 394, + REMOTE_PROC_NODE_SEV_INFO = 395, }; -- 2.7.4

On Wed, Jun 06, 2018 at 12:50:10PM -0500, Brijesh Singh wrote:
Add remote support for virNodeSEVInfo().
virNodeGetSEVInfo()
Signed-off-by: Brijesh Singh <<brijesh.singh@amd.com>> --- src/remote/remote_daemon_dispatch.c | 44 +++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 40 +++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 22 ++++++++++++++++++- src/remote_protocol-structs | 13 +++++++++++ 4 files changed, 118 insertions(+), 1 deletion(-)
With the following diff squashed in: Reviewed-by: Erik Skultety <eskultet@redhat.com> diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index afeefd32ad..dfc3624d1e 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2907,18 +2907,17 @@ struct remote_connect_baseline_hypervisor_cpu_args { struct remote_connect_baseline_hypervisor_cpu_ret { remote_nonnull_string cpu; }; -struct remote_node_get_sev_capability_args { - int nparams; - u_int flags; +struct remote_node_get_sev_info_args { + int nparams; + u_int flags; }; -struct remote_node_get_sev_capability_ret { +struct remote_node_get_sev_info_ret { struct { u_int params_len; remote_typed_param * params_val; } params; int nparams; }; - enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3314,5 +3313,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_DETACH_DEVICE_ALIAS = 392, REMOTE_PROC_CONNECT_COMPARE_HYPERVISOR_CPU = 393, REMOTE_PROC_CONNECT_BASELINE_HYPERVISOR_CPU = 394, - REMOTE_PROC_NODE_SEV_INFO = 395, + REMOTE_PROC_NODE_GET_SEV_INFO = 395, };

Signed-off-by: Brijesh Singh <<brijesh.singh@amd.com>> --- src/qemu/qemu_capabilities.c | 7 ++++ src/qemu/qemu_capabilities.h | 4 ++ src/qemu/qemu_driver.c | 91 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index a2103e3..2b82da2 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -2081,6 +2081,13 @@ virQEMUCapsSetSEVCapabilities(virQEMUCapsPtr qemuCaps, } +virSEVCapabilityPtr +virQEMUCapsGetSEVCapabilities(virQEMUCapsPtr qemuCaps) +{ + return qemuCaps->sevCapabilities; +} + + static int virQEMUCapsProbeQMPCommands(virQEMUCapsPtr qemuCaps, qemuMonitorPtr mon) diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 7390271..463d7d4 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -616,4 +616,8 @@ bool virQEMUCapsGuestIsNative(virArch host, bool virQEMUCapsCPUFilterFeatures(const char *name, void *opaque); + +virSEVCapabilityPtr +virQEMUCapsGetSEVCapabilities(virQEMUCapsPtr qemuCaps); + #endif /* __QEMU_CAPABILITIES_H__*/ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 38ea865..c289b21 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -21437,6 +21437,96 @@ qemuDomainSetLifecycleAction(virDomainPtr dom, } +static int +qemuGetSEVInfo(virQEMUCapsPtr qemuCaps, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + int maxpar = 0; + virSEVCapabilityPtr sev = virQEMUCapsGetSEVCapabilities(qemuCaps); + + virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1); + + if (virTypedParamsAddString(params, nparams, &maxpar, + VIR_NODE_SEV_PDH, sev->pdh) < 0) + return -1; + + if (virTypedParamsAddString(params, nparams, &maxpar, + VIR_NODE_SEV_CERT_CHAIN, sev->pdh) < 0) + goto cleanup; + + if (virTypedParamsAddUInt(params, nparams, &maxpar, + VIR_NODE_SEV_CBITPOS, sev->cbitpos) < 0) + goto cleanup; + + if (virTypedParamsAddUInt(params, nparams, &maxpar, + VIR_NODE_SEV_REDUCED_PHYS_BITS, + sev->reduced_phys_bits) < 0) + goto cleanup; + + return 0; + + cleanup: + return -1; +} + + +static int +qemuNodeGetSEVInfo(virConnectPtr conn, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + virQEMUDriverPtr driver = conn->privateData; + virCapsPtr caps = NULL; + virQEMUCapsPtr qemucaps = NULL; + virArch hostarch; + virCapsDomainDataPtr capsdata; + int ret = -1; + + if (virNodeGetSevInfoEnsureACL(conn) < 0) + return ret; + + if (!(caps = virQEMUDriverGetCapabilities(driver, true))) + return ret; + + hostarch = virArchFromHost(); + if (!(capsdata = virCapabilitiesDomainDataLookup(caps, + VIR_DOMAIN_OSTYPE_HVM, hostarch, VIR_DOMAIN_VIRT_QEMU, + NULL, NULL))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot find suitable emulator for %s"), + virArchToString(hostarch)); + goto UnrefCaps; + } + + qemucaps = virQEMUCapsCacheLookup(driver->qemuCapsCache, + capsdata->emulator); + VIR_FREE(capsdata); + if (!qemucaps) + goto UnrefCaps; + + if (!virQEMUCapsGet(qemucaps, QEMU_CAPS_SEV_GUEST)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("QEMU does not support SEV guest")); + goto UnrefQemuCaps; + } + + if (qemuGetSEVInfo(qemucaps, params, nparams, flags) < 0) + goto UnrefQemuCaps; + + ret = 0; + + UnrefQemuCaps: + virObjectUnref(qemucaps); + UnrefCaps: + virObjectUnref(caps); + + return ret; +} + + static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectURIProbe = qemuConnectURIProbe, @@ -21660,6 +21750,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainSetLifecycleAction = qemuDomainSetLifecycleAction, /* 3.9.0 */ .connectCompareHypervisorCPU = qemuConnectCompareHypervisorCPU, /* 4.4.0 */ .connectBaselineHypervisorCPU = qemuConnectBaselineHypervisorCPU, /* 4.4.0 */ + .nodeGetSEVInfo = qemuNodeGetSEVInfo, /* 4.5.0 */ }; -- 2.7.4

more verbose commit subject: qemu: Implement the driver backend for virNodeGetSEVInfo On Wed, Jun 06, 2018 at 12:50:11PM -0500, Brijesh Singh wrote:
Signed-off-by: Brijesh Singh <<brijesh.singh@amd.com>> --- src/qemu/qemu_capabilities.c | 7 ++++ src/qemu/qemu_capabilities.h | 4 ++ src/qemu/qemu_driver.c | 91 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+)
...
+static int +qemuGetSEVInfo(virQEMUCapsPtr qemuCaps,
qemuGetSEVInfoToParams would IMHO be a better (iow less confusing) name.
+ virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + int maxpar = 0; + virSEVCapabilityPtr sev = virQEMUCapsGetSEVCapabilities(qemuCaps); + + virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1); + + if (virTypedParamsAddString(params, nparams, &maxpar, + VIR_NODE_SEV_PDH, sev->pdh) < 0) + return -1; + + if (virTypedParamsAddString(params, nparams, &maxpar, + VIR_NODE_SEV_CERT_CHAIN, sev->pdh) < 0) + goto cleanup; + + if (virTypedParamsAddUInt(params, nparams, &maxpar, + VIR_NODE_SEV_CBITPOS, sev->cbitpos) < 0) + goto cleanup; + + if (virTypedParamsAddUInt(params, nparams, &maxpar, + VIR_NODE_SEV_REDUCED_PHYS_BITS, + sev->reduced_phys_bits) < 0) + goto cleanup; + + return 0; + + cleanup: + return -1;
So ^this cleanup label is pretty much unnecessary. In order to avoid weird errors, we usually create a local copy of the caller-provided argument - in this case params - work on it the whole time and only once it's safe, we do a VIR_STEAL_PTR to the original pointer, so we should do that here as well.
+} + + +static int +qemuNodeGetSEVInfo(virConnectPtr conn, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + virQEMUDriverPtr driver = conn->privateData; + virCapsPtr caps = NULL; + virQEMUCapsPtr qemucaps = NULL; + virArch hostarch; + virCapsDomainDataPtr capsdata; + int ret = -1; + + if (virNodeGetSevInfoEnsureACL(conn) < 0) + return ret; + + if (!(caps = virQEMUDriverGetCapabilities(driver, true))) + return ret; + + hostarch = virArchFromHost(); + if (!(capsdata = virCapabilitiesDomainDataLookup(caps, + VIR_DOMAIN_OSTYPE_HVM, hostarch, VIR_DOMAIN_VIRT_QEMU, + NULL, NULL))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot find suitable emulator for %s"), + virArchToString(hostarch)); + goto UnrefCaps; + }
If you use virQEMUCapsCacheLookupByArch below instead, we could drop ^this hunk above, am I right?
+ + qemucaps = virQEMUCapsCacheLookup(driver->qemuCapsCache, + capsdata->emulator); + VIR_FREE(capsdata);
^this could be dropped...
+ if (!qemucaps) + goto UnrefCaps; + + if (!virQEMUCapsGet(qemucaps, QEMU_CAPS_SEV_GUEST)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("QEMU does not support SEV guest")); + goto UnrefQemuCaps; + } + + if (qemuGetSEVInfo(qemucaps, params, nparams, flags) < 0) + goto UnrefQemuCaps; + + ret = 0; + + UnrefQemuCaps:
s/UnrefQemuCaps/cleanup
+ virObjectUnref(qemucaps); + UnrefCaps: + virObjectUnref(caps);
..^this one too... Erik

On 06/07/2018 11:37 AM, Erik Skultety wrote:
more verbose commit subject: qemu: Implement the driver backend for virNodeGetSEVInfo
On Wed, Jun 06, 2018 at 12:50:11PM -0500, Brijesh Singh wrote:
Signed-off-by: Brijesh Singh <<brijesh.singh@amd.com>> --- src/qemu/qemu_capabilities.c | 7 ++++ src/qemu/qemu_capabilities.h | 4 ++ src/qemu/qemu_driver.c | 91 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+)
...
+static int +qemuGetSEVInfo(virQEMUCapsPtr qemuCaps,
qemuGetSEVInfoToParams would IMHO be a better (iow less confusing) name.
Agreed, its much better name.
+ virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + int maxpar = 0; + virSEVCapabilityPtr sev = virQEMUCapsGetSEVCapabilities(qemuCaps); + + virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1); + + if (virTypedParamsAddString(params, nparams, &maxpar, + VIR_NODE_SEV_PDH, sev->pdh) < 0) + return -1; + + if (virTypedParamsAddString(params, nparams, &maxpar, + VIR_NODE_SEV_CERT_CHAIN, sev->pdh) < 0) + goto cleanup; + + if (virTypedParamsAddUInt(params, nparams, &maxpar, + VIR_NODE_SEV_CBITPOS, sev->cbitpos) < 0) + goto cleanup; + + if (virTypedParamsAddUInt(params, nparams, &maxpar, + VIR_NODE_SEV_REDUCED_PHYS_BITS, + sev->reduced_phys_bits) < 0) + goto cleanup; + + return 0; + + cleanup: + return -1;
So ^this cleanup label is pretty much unnecessary. In order to avoid weird errors, we usually create a local copy of the caller-provided argument - in this case params - work on it the whole time and only once it's safe, we do a VIR_STEAL_PTR to the original pointer, so we should do that here as well.
OK, will do so.
+} + + +static int +qemuNodeGetSEVInfo(virConnectPtr conn, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + virQEMUDriverPtr driver = conn->privateData; + virCapsPtr caps = NULL; + virQEMUCapsPtr qemucaps = NULL; + virArch hostarch; + virCapsDomainDataPtr capsdata; + int ret = -1; + + if (virNodeGetSevInfoEnsureACL(conn) < 0) + return ret; + + if (!(caps = virQEMUDriverGetCapabilities(driver, true))) + return ret; + + hostarch = virArchFromHost(); + if (!(capsdata = virCapabilitiesDomainDataLookup(caps, + VIR_DOMAIN_OSTYPE_HVM, hostarch, VIR_DOMAIN_VIRT_QEMU, + NULL, NULL))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot find suitable emulator for %s"), + virArchToString(hostarch)); + goto UnrefCaps; + }
If you use virQEMUCapsCacheLookupByArch below instead, we could drop ^this hunk above, am I right?
Ah, I was looking through code to find out suitable APIs for this. thanks for suggestion, it seems like it will work for us in this case.
+ + qemucaps = virQEMUCapsCacheLookup(driver->qemuCapsCache, + capsdata->emulator); + VIR_FREE(capsdata);
^this could be dropped...
If we use virQEMUCapsCacheLookupByArch() then we could drop this.
+ if (!qemucaps) + goto UnrefCaps; + + if (!virQEMUCapsGet(qemucaps, QEMU_CAPS_SEV_GUEST)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("QEMU does not support SEV guest")); + goto UnrefQemuCaps; + } + + if (qemuGetSEVInfo(qemucaps, params, nparams, flags) < 0) + goto UnrefQemuCaps; + + ret = 0; + + UnrefQemuCaps:
s/UnrefQemuCaps/cleanup
+ virObjectUnref(qemucaps); + UnrefCaps: + virObjectUnref(caps);
..^this one too...
Erik

The launch-security element can be used to define the security model to use when launching a domain. Currently we support 'sev'. When 'sev' is used, the VM will be launched with AMD SEV feature enabled. SEV feature supports running encrypted VM under the control of KVM. Encrypted VMs have their pages (code and data) secured such that only the guest itself has access to the unencrypted version. Each encrypted VM is associated with a unique encryption key; if its data is accessed to a different entity using a different key the encrypted guests data will be incorrectly decrypted, leading to unintelligible data. Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> --- docs/formatdomain.html.in | 115 ++++++++++++++++++ docs/schemas/domaincommon.rng | 37 ++++++ src/conf/domain_conf.c | 133 +++++++++++++++++++++ src/conf/domain_conf.h | 27 +++++ tests/genericxml2xmlindata/launch-security-sev.xml | 24 ++++ tests/genericxml2xmltest.c | 2 + 6 files changed, 338 insertions(+) create mode 100644 tests/genericxml2xmlindata/launch-security-sev.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 7f4de65..decd854 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -8367,6 +8367,121 @@ qemu-kvm -net nic,model=? /dev/null <p>Note: DEA/TDEA is synonymous with DES/TDES.</p> + <h3><a id="sev">Secure Encrypted Virtualization (SEV)</a></h3> + + <p> + The contents of the <code><launch-security type='sev'></code> element + is used to provide the guest owners input used for creating an encrypted + VM using the AMD SEV feature. + + SEV is an extension to the AMD-V architecture which supports running + encrypted virtual machine (VMs) under the control of KVM. Encrypted + VMs have their pages (code and data) secured such that only the guest + itself has access to the unencrypted version. Each encrypted VM is + associated with a unique encryption key; if its data is accessed to a + different entity using a different key the encrypted guests data will + be incorrectly decrypted, leading to unintelligible data. + + For more information see various input parameters and its format see the SEV API spec + <a href="https://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf"> https://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf </a> + <span class="since">Since 4.4.0</span> + </p> + <pre> +<domain> + ... + <launch-security type='sev'> + <policy> 0x0001 </policy> + <cbitpos> 47 </cbitpos> + <reduced-phys-bits> 1 </reduced-phys-bits> + <session> AAACCCDD=FFFCCCDSDS </session> + <dh-cert> RBBBSDDD=FDDCCCDDDG </dh> + </sev> + ... +</domain> +</pre> + + <dl> + <dt><code>cbitpos</code></dt> + <dd>The required <code>cbitpos</code> element provides the C-bit (aka encryption bit) + location in guest page table entry. The value of <code>cbitpos</code> is + hypervisor dependent and can be obtained through the <code>sev</code> element + from the domain capabilities. + </dd> + <dt><code>reduced-phys-bits</code></dt> + <dd>The required <code>reduced-phys-bits</code> element provides the physical + address bit reducation. Similar to <code>cbitpos</code> the value of <code> + reduced-phys-bit</code> is hypervisor dependent and can be obtained + through the <code>sev</code> element from the domain capabilities. + </dd> + <dt><code>policy</code></dt> + <dd>The required <code>policy</code> element provides the guest policy + which must be maintained by the SEV firmware. This policy is enforced by + the firmware and restricts what configuration and operational commands + can be performed on this guest by the hypervisor. The guest policy + provided during guest launch is bound to the guest and cannot be changed + throughout the lifetime of the guest. The policy is also transmitted + during snapshot and migration flows and enforced on the destination platform. + + The guest policy is a 4 unsigned byte with the fields shown in Table: + + <table class="top_table"> + <tr> + <th> Bit(s) </th> + <th> Description </th> + </tr> + <tr> + <td> 0 </td> + <td> Debugging of the guest is disallowed when set </td> + </tr> + <tr> + <td> 1 </td> + <td> Sharing keys with other guests is disallowed when set </td> + </tr> + <tr> + <td> 2 </td> + <td> SEV-ES is required when set</td> + </tr> + <tr> + <td> 3 </td> + <td> Sending the guest to another platform is disallowed when set</td> + </tr> + <tr> + <td> 4 </td> + <td> The guest must not be transmitted to another platform that is + not in the domain when set. </td> + </tr> + <tr> + <td> 5 </td> + <td> The guest must not be transmitted to another platform that is + not SEV capable when set. </td> + </tr> + <tr> + <td> 6:15 </td> + <td> reserved </td> + </tr> + <tr> + <td> 16:32 </td> + <td> The guest must not be transmitted to another platform with a + lower firmware version. </td> + </tr> + </table> + + </dd> + <dt><code>dh-cert</code></dt> + <dd>The optional <code>dh-cert</code> element provides the guest owners + base64 encoded Diffie-Hellman (DH) key. The key is used to negotiate a + master secret key between the SEV firmware and guest owner. This master + secret key is then used to establish a trusted channel between SEV + firmware and guest owner. + </dd> + <dt><code>session</code></dt> + <dd>The optional <code>session</code> element provides the guest owners + base64 encoded session blob defined in the SEV API spec. + + See SEV spec LAUNCH_START section for the session blob format. + </dd> + </dl> + <h2><a id="examples">Example configs</a></h2> <p> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 6379ab1..c6f3c7d 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -77,6 +77,9 @@ <optional> <ref name='keywrap'/> </optional> + <optional> + <ref name='launch-security'/> + </optional> </interleave> </element> </define> @@ -436,6 +439,40 @@ </element> </define> + <define name="launch-security"> + <element name="launch-security"> + <attribute name="type"> + <value>sev</value> + </attribute> + <interleave> + <element name="cbitpos"> + <data type='unsignedInt'/> + </element> + <element name="reduced-phys-bits"> + <data type='unsignedInt'/> + </element> + <element name="policy"> + <ref name='hexuint'/> + </element> + <optional> + <element name="handle"> + <ref name='unsignedInt'/> + </element> + </optional> + <optional> + <element name="dh-cert"> + <data type="string"/> + </element> + </optional> + <optional> + <element name="session"> + <data type="string"/> + </element> + </optional> + </interleave> + </element> + </define> + <!-- Enable or disable perf events for the domain. For each of the events the following rules apply: diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 4aaad2a..e03ac7f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -938,6 +938,10 @@ VIR_ENUM_IMPL(virDomainShmemModel, VIR_DOMAIN_SHMEM_MODEL_LAST, "ivshmem-plain", "ivshmem-doorbell") +VIR_ENUM_IMPL(virDomainLaunchSecurity, VIR_DOMAIN_LAUNCH_SECURITY_LAST, + "", + "sev") + static virClassPtr virDomainObjClass; static virClassPtr virDomainXMLOptionClass; static void virDomainObjDispose(void *obj); @@ -2946,6 +2950,19 @@ virDomainCachetuneDefFree(virDomainCachetuneDefPtr cachetune) } +static void +virDomainSEVDefFree(virDomainSevDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->dh_cert); + VIR_FREE(def->session); + + VIR_FREE(def); +} + + void virDomainDefFree(virDomainDefPtr def) { size_t i; @@ -3128,6 +3145,8 @@ void virDomainDefFree(virDomainDefPtr def) if (def->namespaceData && def->ns.free) (def->ns.free)(def->namespaceData); + virDomainSEVDefFree(def->sev); + xmlFreeNode(def->metadata); VIR_FREE(def); @@ -15794,6 +15813,85 @@ virDomainMemoryTargetDefParseXML(xmlNodePtr node, } +static virDomainSevDefPtr +virDomainSEVDefParseXML(xmlNodePtr sevNode, + xmlXPathContextPtr ctxt) +{ + char *tmp = NULL; + char *type = NULL; + xmlNodePtr save = ctxt->node; + virDomainSevDefPtr def; + unsigned long policy; + + ctxt->node = sevNode; + + if (VIR_ALLOC(def) < 0) + return NULL; + + if (!(type = virXMLPropString(sevNode, "type"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing launch-security type")); + goto error; + } + + def->sectype = virDomainLaunchSecurityTypeFromString(type); + switch ((virDomainLaunchSecurity) def->sectype) { + case VIR_DOMAIN_LAUNCH_SECURITY_SEV: + break; + case VIR_DOMAIN_LAUNCH_SECURITY_NONE: + case VIR_DOMAIN_LAUNCH_SECURITY_LAST: + default: + virReportError(VIR_ERR_XML_ERROR, + _("unsupported launch-security type '%s'"), + type); + goto error; + } + + if (virXPathUInt("string(./cbitpos)", ctxt, &def->cbitpos) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("failed to get launch-security cbitpos")); + goto error; + } + + if (virXPathUInt("string(./reduced-phys-bits)", ctxt, + &def->reduced_phys_bits) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("failed to get launch-security reduced-phys-bits")); + goto error; + } + + if (virXPathULongHex("string(./policy)", ctxt, &policy) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("failed to get launch-security policy")); + goto error; + } + + def->policy = policy; + + if ((tmp = virXPathString("string(./dh-cert)", ctxt))) { + if (VIR_STRDUP(def->dh_cert, tmp) < 0) + goto error; + + VIR_FREE(tmp); + } + + if ((tmp = virXPathString("string(./session)", ctxt))) { + if (VIR_STRDUP(def->session, tmp) < 0) + goto error; + + VIR_FREE(tmp); + } + + ctxt->node = save; + return def; + + error: + VIR_FREE(tmp); + virDomainSEVDefFree(def); + ctxt->node = save; + return NULL; +} + static virDomainMemoryDefPtr virDomainMemoryDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr memdevNode, @@ -20586,6 +20684,13 @@ virDomainDefParseXML(xmlDocPtr xml, ctxt->node = node; VIR_FREE(nodes); + /* Check for SEV feature */ + if ((node = virXPathNode("./launch-security", ctxt)) != NULL) { + def->sev = virDomainSEVDefParseXML(node, ctxt); + if (!def->sev) + goto error; + } + /* analysis of memory devices */ if ((n = virXPathNodeSet("./devices/memory", ctxt, &nodes)) < 0) goto error; @@ -26560,6 +26665,32 @@ virDomainKeyWrapDefFormat(virBufferPtr buf, virDomainKeyWrapDefPtr keywrap) virBufferAddLit(buf, "</keywrap>\n"); } + +static void +virDomainSEVDefFormat(virBufferPtr buf, virDomainSevDefPtr sev) +{ + if (!sev) + return; + + virBufferAsprintf(buf, "<launch-security type='%s'>\n", + virDomainLaunchSecurityTypeToString(sev->sectype)); + virBufferAdjustIndent(buf, 2); + + virBufferAsprintf(buf, "<cbitpos>%d</cbitpos>\n", sev->cbitpos); + virBufferAsprintf(buf, "<reduced-phys-bits>%d</reduced-phys-bits>\n", + sev->reduced_phys_bits); + virBufferAsprintf(buf, "<policy>0x%04x</policy>\n", sev->policy); + if (sev->dh_cert) + virBufferEscapeString(buf, "<dh-cert>%s</dh-cert>\n", sev->dh_cert); + + if (sev->session) + virBufferEscapeString(buf, "<session>%s</session>\n", sev->session); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</launch-security>\n"); +} + + static void virDomainPerfDefFormat(virBufferPtr buf, virDomainPerfDefPtr perf) { @@ -27799,6 +27930,8 @@ virDomainDefFormatInternal(virDomainDefPtr def, if (def->keywrap) virDomainKeyWrapDefFormat(buf, def->keywrap); + virDomainSEVDefFormat(buf, def->sev); + virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</domain>\n"); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index b6c4090..7babbaa 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -142,6 +142,9 @@ typedef virDomainPanicDef *virDomainPanicDefPtr; typedef struct _virDomainMemoryDef virDomainMemoryDef; typedef virDomainMemoryDef *virDomainMemoryDefPtr; +typedef struct _virDomainSevDef virDomainSevDef; +typedef virDomainSevDef *virDomainSevDefPtr; + /* forward declarations virDomainChrSourceDef, required by * virDomainNetDef */ @@ -2303,6 +2306,26 @@ struct _virDomainKeyWrapDef { }; typedef enum { + VIR_DOMAIN_LAUNCH_SECURITY_NONE, + VIR_DOMAIN_LAUNCH_SECURITY_SEV, + + VIR_DOMAIN_LAUNCH_SECURITY_LAST, +} virDomainLaunchSecurity; + +typedef struct _virDomainSevDef virDomainSevDef; +typedef virDomainSevDef *virDomainSevDefPtr; + +struct _virDomainSevDef { + int sectype; /* enum virDomainLaunchSecurity */ + char *dh_cert; + char *session; + unsigned int policy; + unsigned int cbitpos; + unsigned int reduced_phys_bits; +}; + + +typedef enum { VIR_DOMAIN_IOMMU_MODEL_INTEL, VIR_DOMAIN_IOMMU_MODEL_LAST @@ -2490,6 +2513,9 @@ struct _virDomainDef { virDomainKeyWrapDefPtr keywrap; + /* SEV-specific domain */ + virDomainSevDefPtr sev; + /* Application-specific custom metadata */ xmlNodePtr metadata; @@ -3399,6 +3425,7 @@ VIR_ENUM_DECL(virDomainMemoryAllocation) VIR_ENUM_DECL(virDomainIOMMUModel) VIR_ENUM_DECL(virDomainVsockModel) VIR_ENUM_DECL(virDomainShmemModel) +VIR_ENUM_DECL(virDomainLaunchSecurity) /* from libvirt.h */ VIR_ENUM_DECL(virDomainState) VIR_ENUM_DECL(virDomainNostateReason) diff --git a/tests/genericxml2xmlindata/launch-security-sev.xml b/tests/genericxml2xmlindata/launch-security-sev.xml new file mode 100644 index 0000000..fb64e1e --- /dev/null +++ b/tests/genericxml2xmlindata/launch-security-sev.xml @@ -0,0 +1,24 @@ +<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> + </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/genericxml2xmltest.c b/tests/genericxml2xmltest.c index d8270a6..366ac6f 100644 --- a/tests/genericxml2xmltest.c +++ b/tests/genericxml2xmltest.c @@ -141,6 +141,8 @@ mymain(void) DO_TEST_FULL("cachetune-colliding-types", false, true, TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE); + DO_TEST("launch-security-sev"); + virObjectUnref(caps); virObjectUnref(xmlopt); -- 2.7.4

On Wed, Jun 06, 2018 at 12:50:12PM -0500, Brijesh Singh wrote:
The launch-security element can be used to define the security model to use when launching a domain. Currently we support 'sev'.
When 'sev' is used, the VM will be launched with AMD SEV feature enabled. SEV feature supports running encrypted VM under the control of KVM. Encrypted VMs have their pages (code and data) secured such that only the guest itself has access to the unencrypted version. Each encrypted VM is associated with a unique encryption key; if its data is accessed to a different entity using a different key the encrypted guests data will be incorrectly decrypted, leading to unintelligible data.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> --- docs/formatdomain.html.in | 115 ++++++++++++++++++ docs/schemas/domaincommon.rng | 37 ++++++ src/conf/domain_conf.c | 133 +++++++++++++++++++++ src/conf/domain_conf.h | 27 +++++ tests/genericxml2xmlindata/launch-security-sev.xml | 24 ++++ tests/genericxml2xmltest.c | 2 + 6 files changed, 338 insertions(+) create mode 100644 tests/genericxml2xmlindata/launch-security-sev.xml
There were a few minor alignment issues, please squash in the diff below, with that: Reviewed-by: Erik Skultety <eskultet@redhat.com> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 05b3ffa689..051c54a609 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -15868,25 +15868,25 @@ virDomainSEVDefParseXML(xmlNodePtr sevNode, def->sectype = virDomainLaunchSecurityTypeFromString(type); switch ((virDomainLaunchSecurity) def->sectype) { - case VIR_DOMAIN_LAUNCH_SECURITY_SEV: - break; - case VIR_DOMAIN_LAUNCH_SECURITY_NONE: - case VIR_DOMAIN_LAUNCH_SECURITY_LAST: - default: - virReportError(VIR_ERR_XML_ERROR, - _("unsupported launch-security type '%s'"), - type); - goto error; + case VIR_DOMAIN_LAUNCH_SECURITY_SEV: + break; + case VIR_DOMAIN_LAUNCH_SECURITY_NONE: + case VIR_DOMAIN_LAUNCH_SECURITY_LAST: + default: + virReportError(VIR_ERR_XML_ERROR, + _("unsupported launch-security type '%s'"), + type); + goto error; } if (virXPathUInt("string(./cbitpos)", ctxt, &def->cbitpos) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", - _("failed to get launch-security cbitpos")); + _("failed to get launch-security cbitpos")); goto error; } if (virXPathUInt("string(./reduced-phys-bits)", ctxt, - &def->reduced_phys_bits) < 0) { + &def->reduced_phys_bits) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("failed to get launch-security reduced-phys-bits")); goto error; @@ -15894,7 +15894,7 @@ virDomainSEVDefParseXML(xmlNodePtr sevNode, if (virXPathULongHex("string(./policy)", ctxt, &policy) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", - _("failed to get launch-security policy")); + _("failed to get launch-security policy")); goto error; }

QEMU uses /dev/sev device while creating the SEV guest, lets add /dev/sev in the list of devices allowed to be accessed by the QEMU. Signed-off-by: Brijesh Singh <<brijesh.singh@amd.com>> Reviewed-by: Erik Skultety <eskultet@redhat.com> --- docs/drvqemu.html.in | 1 + src/qemu/qemu.conf | 2 +- src/qemu/qemu_cgroup.c | 2 +- src/qemu/test_libvirtd_qemu.aug.in | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/drvqemu.html.in b/docs/drvqemu.html.in index cbd159d..7c33a18 100644 --- a/docs/drvqemu.html.in +++ b/docs/drvqemu.html.in @@ -397,6 +397,7 @@ chmod o+x /path/to/directory /dev/random, /dev/urandom, /dev/ptmx, /dev/kvm, /dev/kqemu, /dev/rtc, /dev/hpet, /dev/net/tun +/dev/sev </pre> <p> diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index c8e1a62..3a733b3 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -485,7 +485,7 @@ # "/dev/null", "/dev/full", "/dev/zero", # "/dev/random", "/dev/urandom", # "/dev/ptmx", "/dev/kvm", "/dev/kqemu", -# "/dev/rtc","/dev/hpet" +# "/dev/rtc","/dev/hpet", "/dev/sev" #] # # RDMA migration requires the following extra files to be added to the list: diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 546a4c8..21dcb3c 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -47,7 +47,7 @@ const char *const defaultDeviceACL[] = { "/dev/null", "/dev/full", "/dev/zero", "/dev/random", "/dev/urandom", "/dev/ptmx", "/dev/kvm", "/dev/kqemu", - "/dev/rtc", "/dev/hpet", + "/dev/rtc", "/dev/hpet", "/dev/sev", NULL, }; #define DEVICE_PTY_MAJOR 136 diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in index 912161c..f127726 100644 --- a/src/qemu/test_libvirtd_qemu.aug.in +++ b/src/qemu/test_libvirtd_qemu.aug.in @@ -62,6 +62,7 @@ module Test_libvirtd_qemu = { "8" = "/dev/kqemu" } { "9" = "/dev/rtc" } { "10" = "/dev/hpet" } + { "11" = "/dev/sev" } } { "save_image_format" = "raw" } { "dump_image_format" = "raw" } -- 2.7.4

On Wed, Jun 06, 2018 at 12:50:13PM -0500, Brijesh Singh wrote:
QEMU uses /dev/sev device while creating the SEV guest, lets add /dev/sev in the list of devices allowed to be accessed by the QEMU.
Signed-off-by: Brijesh Singh <<brijesh.singh@amd.com>> Reviewed-by: Erik Skultety <eskultet@redhat.com> --- docs/drvqemu.html.in | 1 + src/qemu/qemu.conf | 2 +- src/qemu/qemu_cgroup.c | 2 +- src/qemu/test_libvirtd_qemu.aug.in | 1 + 4 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/docs/drvqemu.html.in b/docs/drvqemu.html.in index cbd159d..7c33a18 100644 --- a/docs/drvqemu.html.in +++ b/docs/drvqemu.html.in @@ -397,6 +397,7 @@ chmod o+x /path/to/directory /dev/random, /dev/urandom, /dev/ptmx, /dev/kvm, /dev/kqemu, /dev/rtc, /dev/hpet, /dev/net/tun
missing comma at the end of the line...
+/dev/sev </pre>
Reviewed-by: Erik Skultety <eskultet@redhat.com>

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@amd.com> Reviewed-by: Erik Skultety <eskultet@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 1324c67..6ffdf63 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -7295,6 +7295,9 @@ qemuBuildMachineCommandLine(virCommandPtr cmd, virQEMUCapsGet(qemuCaps, QEMU_CAPS_LOADPARM)) qemuAppendLoadparmMachineParm(&buf, def); + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST) && def->sev) + virBufferAddLit(&buf, ",memory-encryption=sev0"); + virCommandAddArgBuffer(cmd, &buf); ret = 0; @@ -9651,6 +9654,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, @@ -10245,6 +10283,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 9eb3ea0..5783627 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -5825,6 +5825,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) @@ -5982,6 +6041,9 @@ qemuProcessPrepareHost(virQEMUDriverPtr driver, if (qemuProcessPrepareHostStorage(driver, vm, flags) < 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 2092b8e..9586200 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -2862,6 +2862,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

On Wed, Jun 06, 2018 at 12:50:14PM -0500, Brijesh Singh wrote:
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@amd.com> Reviewed-by: Erik Skultety <eskultet@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 1324c67..6ffdf63 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -7295,6 +7295,9 @@ qemuBuildMachineCommandLine(virCommandPtr cmd, virQEMUCapsGet(qemuCaps, QEMU_CAPS_LOADPARM)) qemuAppendLoadparmMachineParm(&buf, def);
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST) && def->sev)
No need to check the capability again, it was checked while preparing the host. And a handy diff, per the custom in this thread: diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index cb6afd8c1c..8a71db9d2c 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -7295,7 +7295,7 @@ qemuBuildMachineCommandLine(virCommandPtr cmd, virQEMUCapsGet(qemuCaps, QEMU_CAPS_LOADPARM)) qemuAppendLoadparmMachineParm(&buf, def); - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST) && def->sev) + if (def->sev) virBufferAddLit(&buf, ",memory-encryption=sev0"); virCommandAddArgBuffer(cmd, &buf); Jano

The API can be used outside the libvirt to get the launch security information. When SEV is enabled, the API can be used to get the measurement of the launch process. Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> --- include/libvirt/libvirt-domain.h | 17 ++++++++++++++ src/driver-hypervisor.h | 8 +++++++ src/libvirt-domain.c | 48 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 74 insertions(+) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index da773b7..6a3d73f 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4767,4 +4767,21 @@ int virDomainSetLifecycleAction(virDomainPtr domain, unsigned int action, unsigned int flags); +/** + * Launch Security API + */ + +/** + * VIR_DOMAIN_LAUNCH_SECURITY_SEV_MEASUREMENT: + * + * Macro represents the launch measurement of the SEV guest, + * as VIR_TYPED_PARAM_STRING. + */ +#define VIR_DOMAIN_LAUNCH_SECURITY_SEV_MEASUREMENT "sev-measurement" + +int virDomainGetLaunchSecurityInfo(virDomainPtr domain, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags); + #endif /* __VIR_LIBVIRT_DOMAIN_H__ */ diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index c50d2a0..eef31eb 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1315,6 +1315,13 @@ typedef int int *nparams, unsigned int flags); +typedef int +(*virDrvDomainGetLaunchSecurityInfo)(virDomainPtr domain, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags); + + typedef struct _virHypervisorDriver virHypervisorDriver; typedef virHypervisorDriver *virHypervisorDriverPtr; @@ -1564,6 +1571,7 @@ struct _virHypervisorDriver { virDrvConnectCompareHypervisorCPU connectCompareHypervisorCPU; virDrvConnectBaselineHypervisorCPU connectBaselineHypervisorCPU; virDrvNodeGetSEVInfo nodeGetSEVInfo; + virDrvDomainGetLaunchSecurityInfo domainGetLaunchSecurityInfo; }; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index d44b553..dcfc7d4 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -12154,3 +12154,51 @@ int virDomainSetLifecycleAction(virDomainPtr domain, virDispatchError(domain->conn); return -1; } + +/** + * virDomainGetLaunchSecurityInfo: + * @domain: a domain object + * @params: where to store security info + * @nparams: number of items in @params + * @flags: currently used, set to 0. + * + * Get the launch security info. In case of the SEV guest, this will + * return the launch measurement. + * + * Returns -1 in case of failure, 0 in case of success. + */ +int virDomainGetLaunchSecurityInfo(virDomainPtr domain, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + virConnectPtr conn = domain->conn; + + VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%p flags=0x%x", + params, nparams, flags); + + virResetLastError(); + + virCheckDomainReturn(domain, -1); + virCheckNonNullArgGoto(params, error); + virCheckNonNullArgGoto(nparams, error); + virCheckReadOnlyGoto(conn->flags, error); + + if (VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_TYPED_PARAM_STRING)) + flags |= VIR_TYPED_PARAM_STRING_OKAY; + + if (conn->driver->domainGetLaunchSecurityInfo) { + int ret; + ret = conn->driver->domainGetLaunchSecurityInfo(domain, params, + nparams, flags); + if (ret < 0) + goto error; + return ret; + } + virReportUnsupportedError(); + + error: + virDispatchError(domain->conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 524d5fd..3bf3c3f 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -797,6 +797,7 @@ LIBVIRT_4.5.0 { virGetLastErrorCode; virGetLastErrorDomain; virNodeGetSEVInfo; + virDomainGetLaunchSecurityInfo; } LIBVIRT_4.4.0; # .... define new API here using predicted next version number .... -- 2.7.4

Better commit subject would be: libvirt: Introduce virDomainGetLaunchSecurityInfo public API On Wed, Jun 06, 2018 at 12:50:15PM -0500, Brijesh Singh wrote:
The API can be used outside the libvirt to get the launch security information. When SEV is enabled, the API can be used to get the measurement of the launch process.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> --- include/libvirt/libvirt-domain.h | 17 ++++++++++++++ src/driver-hypervisor.h | 8 +++++++ src/libvirt-domain.c | 48 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 74 insertions(+)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index da773b7..6a3d73f 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4767,4 +4767,21 @@ int virDomainSetLifecycleAction(virDomainPtr domain, unsigned int action, unsigned int flags);
+/** + * Launch Security API + */ + +/** + * VIR_DOMAIN_LAUNCH_SECURITY_SEV_MEASUREMENT: + * + * Macro represents the launch measurement of the SEV guest, + * as VIR_TYPED_PARAM_STRING. + */ +#define VIR_DOMAIN_LAUNCH_SECURITY_SEV_MEASUREMENT "sev-measurement"
^recurring, s/#define/# define/ otherwise fails the syntax-check... With that: Reviewed-by: Erik Skultety <eskultet@redhat.com>

On 06/07/2018 11:46 AM, Erik Skultety wrote:
Better commit subject would be: libvirt: Introduce virDomainGetLaunchSecurityInfo public API
On Wed, Jun 06, 2018 at 12:50:15PM -0500, Brijesh Singh wrote:
The API can be used outside the libvirt to get the launch security information. When SEV is enabled, the API can be used to get the measurement of the launch process.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> --- include/libvirt/libvirt-domain.h | 17 ++++++++++++++ src/driver-hypervisor.h | 8 +++++++ src/libvirt-domain.c | 48 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 74 insertions(+)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index da773b7..6a3d73f 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4767,4 +4767,21 @@ int virDomainSetLifecycleAction(virDomainPtr domain, unsigned int action, unsigned int flags);
+/** + * Launch Security API + */ + +/** + * VIR_DOMAIN_LAUNCH_SECURITY_SEV_MEASUREMENT: + * + * Macro represents the launch measurement of the SEV guest, + * as VIR_TYPED_PARAM_STRING. + */ +#define VIR_DOMAIN_LAUNCH_SECURITY_SEV_MEASUREMENT "sev-measurement"
^recurring, s/#define/# define/ otherwise fails the syntax-check...
I did ran through syntax-check but don't remember getting complain. I will fix in next rev.
With that: Reviewed-by: Erik Skultety <eskultet@redhat.com>

On Thu, Jun 07, 2018 at 11:54:30AM -0500, Brijesh Singh wrote:
On 06/07/2018 11:46 AM, Erik Skultety wrote:
Better commit subject would be: libvirt: Introduce virDomainGetLaunchSecurityInfo public API
On Wed, Jun 06, 2018 at 12:50:15PM -0500, Brijesh Singh wrote:
The API can be used outside the libvirt to get the launch security information. When SEV is enabled, the API can be used to get the measurement of the launch process.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> --- include/libvirt/libvirt-domain.h | 17 ++++++++++++++ src/driver-hypervisor.h | 8 +++++++ src/libvirt-domain.c | 48 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 74 insertions(+)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index da773b7..6a3d73f 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4767,4 +4767,21 @@ int virDomainSetLifecycleAction(virDomainPtr domain, unsigned int action, unsigned int flags);
+/** + * Launch Security API + */ + +/** + * VIR_DOMAIN_LAUNCH_SECURITY_SEV_MEASUREMENT: + * + * Macro represents the launch measurement of the SEV guest, + * as VIR_TYPED_PARAM_STRING. + */ +#define VIR_DOMAIN_LAUNCH_SECURITY_SEV_MEASUREMENT "sev-measurement"
^recurring, s/#define/# define/ otherwise fails the syntax-check...
I did ran through syntax-check but don't remember getting complain. I will fix in next rev.
cppi is the tool that needs to be installed for that part of syntax check to work But for API additions, having the 'pdwtags' tool (sometimes in the 'dwarves' package) is useful - the check for API stability uses it. (Also, some of the patches have double << >> around your e-mail, how did that happen?) Jano
With that: Reviewed-by: Erik Skultety <eskultet@redhat.com>
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

Add remote support for launch security info. Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Erik Skultety <eskultet@redhat.com> --- src/remote/remote_daemon_dispatch.c | 47 +++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 40 +++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 19 ++++++++++++++- src/remote_protocol-structs | 11 +++++++++ 4 files changed, 116 insertions(+), 1 deletion(-) diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c index 959367f..f1a5ba2 100644 --- a/src/remote/remote_daemon_dispatch.c +++ b/src/remote/remote_daemon_dispatch.c @@ -3110,6 +3110,53 @@ remoteDispatchNodeGetMemoryStats(virNetServerPtr server ATTRIBUTE_UNUSED, } static int +remoteDispatchDomainGetLaunchSecurityInfo(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_launch_security_info_args *args, + remote_domain_get_launch_security_info_ret *ret) +{ + virDomainPtr dom = NULL; + virTypedParameterPtr params = NULL; + int nparams = 0; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + if (virDomainGetLaunchSecurityInfo(dom, ¶ms, &nparams, args->flags) < 0) + goto cleanup; + + if (nparams > REMOTE_DOMAIN_LAUNCH_SECURITY_INFO_PARAMS_MAX) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large")); + goto cleanup; + } + + if (virTypedParamsSerialize(params, nparams, + (virTypedParameterRemotePtr *) &ret->params.params_val, + &ret->params.params_len, + args->flags) < 0) + goto cleanup; + + rv = 0; + + cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virTypedParamsFree(params, nparams); + virObjectUnref(dom); + return rv; +} + +static int remoteDispatchDomainGetPerfEvents(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, virNetMessagePtr msg ATTRIBUTE_UNUSED, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8ac7264..995d440 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -1966,6 +1966,45 @@ remoteDomainGetNumaParameters(virDomainPtr domain, } static int +remoteDomainGetLaunchSecurityInfo(virDomainPtr domain, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + int rv = -1; + remote_domain_get_launch_security_info_args args; + remote_domain_get_launch_security_info_ret ret; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain(&args.dom, domain); + args.flags = flags; + + memset(&ret, 0, sizeof(ret)); + if (call(domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_LAUNCH_SECURITY_INFO, + (xdrproc_t) xdr_remote_domain_get_launch_security_info_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_get_launch_security_info_ret, (char *) &ret) == -1) + goto done; + + if (virTypedParamsDeserialize((virTypedParameterRemotePtr) ret.params.params_val, + ret.params.params_len, + REMOTE_DOMAIN_LAUNCH_SECURITY_INFO_PARAMS_MAX, + params, + nparams) < 0) + goto cleanup; + + rv = 0; + + cleanup: + xdr_free((xdrproc_t) xdr_remote_domain_get_launch_security_info_ret, + (char *) &ret); + done: + remoteDriverUnlock(priv); + return rv; +} + +static int remoteDomainGetPerfEvents(virDomainPtr domain, virTypedParameterPtr *params, int *nparams, @@ -8491,6 +8530,7 @@ static virHypervisorDriver hypervisor_driver = { .connectCompareHypervisorCPU = remoteConnectCompareHypervisorCPU, /* 4.4.0 */ .connectBaselineHypervisorCPU = remoteConnectBaselineHypervisorCPU, /* 4.4.0 */ .nodeGetSEVInfo = remoteNodeGetSEVInfo, /* 4.5.0 */ + .domainGetLaunchSecurityInfo = remoteDomainGetLaunchSecurityInfo /* 4.5.0 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index ec72afa..162cf5e 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -256,6 +256,9 @@ const REMOTE_DOMAIN_GUEST_VCPU_PARAMS_MAX = 64; /* Upper limit on number of SEV parameters */ const REMOTE_NODE_SEV_INFO_MAX = 64; +/* Upper limit on number of launch security information entries */ +const REMOTE_DOMAIN_LAUNCH_SECURITY_INFO_PARAMS_MAX = 64; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -3493,6 +3496,14 @@ struct remote_node_get_sev_info_ret { int nparams; }; +struct remote_domain_get_launch_security_info_args { + remote_nonnull_domain dom; + unsigned int flags; +}; + +struct remote_domain_get_launch_security_info_ret { + remote_typed_param params<REMOTE_DOMAIN_LAUNCH_SECURITY_INFO_PARAMS_MAX>; +}; /*----- Protocol. -----*/ @@ -6207,5 +6218,11 @@ enum remote_procedure { * @generate: none * @acl: connect:read */ - REMOTE_PROC_NODE_GET_SEV_INFO = 395 + REMOTE_PROC_NODE_GET_SEV_INFO = 395, + + /** + * @generate: none + * @acl: domain:read + */ + REMOTE_PROC_DOMAIN_GET_LAUNCH_SECURITY_INFO = 396 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index afeefd3..f650723 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2918,6 +2918,16 @@ struct remote_node_get_sev_capability_ret { } params; int nparams; }; +struct remote_domain_get_launch_security_info_args { + remote_nonnull_domain dom; + u_int flags; +}; +struct remote_domain_get_launch_security_info_ret { + struct { + u_int params_len; + remote_typed_param * params_val; + } params; +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, @@ -3315,4 +3325,5 @@ enum remote_procedure { REMOTE_PROC_CONNECT_COMPARE_HYPERVISOR_CPU = 393, REMOTE_PROC_CONNECT_BASELINE_HYPERVISOR_CPU = 394, REMOTE_PROC_NODE_SEV_INFO = 395, + REMOTE_PROC_DOMAIN_GET_LAUNCH_SECURITY_INFO = 396, }; -- 2.7.4

This patch implements the internal driver API for launch event into qemu driver. When SEV is enabled, execute 'query-sev-launch-measurement' to get the measurement of memory encrypted through launch sequence. Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> --- src/qemu/qemu_driver.c | 69 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 8 +++++ src/qemu/qemu_monitor.h | 3 ++ src/qemu/qemu_monitor_json.c | 42 +++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 2 ++ 5 files changed, 124 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c289b21..c0785c9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -21527,6 +21527,74 @@ qemuNodeGetSEVInfo(virConnectPtr conn, } +static int +qemuDomainGetSEVMeasurement(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + int ret = -1; + char *tmp; + int maxpar = 0; + + virCheckFlags(VIR_TYPED_PARAM_STRING_OKAY, -1); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0) + return -1; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) + goto endjob; + + tmp = qemuMonitorGetSEVMeasurement(QEMU_DOMAIN_PRIVATE(vm)->mon); + if (tmp == NULL) + goto endjob; + + if (qemuDomainObjExitMonitor(driver, vm) < 0) + goto endjob; + + if (virTypedParamsAddString(params, nparams, &maxpar, + VIR_DOMAIN_LAUNCH_SECURITY_SEV_MEASUREMENT, + tmp) < 0) + goto endjob; + + VIR_FREE(tmp); + ret = 0; + + endjob: + qemuDomainObjEndJob(driver, vm); + return ret; +} + + +static int +qemuDomainGetLaunchSecurityInfo(virDomainPtr domain, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + virQEMUDriverPtr driver = domain->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + + if (!(vm = qemuDomObjFromDomain(domain))) + goto cleanup; + + if (virDomainGetLaunchSecurityInfoEnsureACL(domain->conn, vm->def) < 0) + goto cleanup; + + if (vm->def->sev) { + if (qemuDomainGetSEVMeasurement(driver, vm, params, nparams, flags) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectURIProbe = qemuConnectURIProbe, @@ -21751,6 +21819,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .connectCompareHypervisorCPU = qemuConnectCompareHypervisorCPU, /* 4.4.0 */ .connectBaselineHypervisorCPU = qemuConnectBaselineHypervisorCPU, /* 4.4.0 */ .nodeGetSEVInfo = qemuNodeGetSEVInfo, /* 4.5.0 */ + .domainGetLaunchSecurityInfo = qemuDomainGetLaunchSecurityInfo, /* 4.5.0 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index fd6bce9..6e0cdca 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4297,3 +4297,11 @@ qemuMonitorBlockdevDel(qemuMonitorPtr mon, return qemuMonitorJSONBlockdevDel(mon, nodename); } + +char * +qemuMonitorGetSEVMeasurement(qemuMonitorPtr mon) +{ + QEMU_CHECK_MONITOR_NULL(mon); + + return qemuMonitorJSONGetSEVMeasurement(mon); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 75d5d98..7432997 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1142,4 +1142,7 @@ int qemuMonitorBlockdevAdd(qemuMonitorPtr mon, int qemuMonitorBlockdevDel(qemuMonitorPtr mon, const char *nodename); +char * +qemuMonitorGetSEVMeasurement(qemuMonitorPtr mon); + #endif /* QEMU_MONITOR_H */ diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index ba0da9a..c5480a2 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -7994,3 +7994,45 @@ qemuMonitorJSONBlockdevDel(qemuMonitorPtr mon, virJSONValueFree(reply); return ret; } + +/** + * The function is used to retrieve the measurement of a SEV guest. + * The measurement is signature of the memory contents that was encrypted + * through the SEV launch flow. + * + * A example JSON output: + * + * { "execute" : "query-sev-launch-measure" } + * { "return" : { "data" : "4l8LXeNlSPUDlXPJG5966/8%YZ" } } + */ +char * +qemuMonitorJSONGetSEVMeasurement(qemuMonitorPtr mon) +{ + const char *tmp; + char *measurement = NULL; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + virJSONValuePtr data; + + if (!(cmd = qemuMonitorJSONMakeCommand("query-sev-launch-measure", NULL))) + return NULL; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) + goto cleanup; + + data = virJSONValueObjectGetObject(reply, "return"); + + if (!(tmp = virJSONValueObjectGetString(data, "data"))) + goto cleanup; + + if (VIR_STRDUP(measurement, tmp) < 0) + goto cleanup; + + cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return measurement; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 4c10574..6bc0dd3 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -343,6 +343,8 @@ int qemuMonitorJSONGetBlockIoThrottle(qemuMonitorPtr mon, int qemuMonitorJSONSystemWakeup(qemuMonitorPtr mon); +char *qemuMonitorJSONGetSEVMeasurement(qemuMonitorPtr mon); + int qemuMonitorJSONGetVersion(qemuMonitorPtr mon, int *major, int *minor, -- 2.7.4

Similarly to the NodeGetInfo, probably better subject: Implement the driver backend for virDomainGetLaunchSecurityInfo... On Wed, Jun 06, 2018 at 12:50:17PM -0500, Brijesh Singh wrote:
This patch implements the internal driver API for launch event into qemu driver. When SEV is enabled, execute 'query-sev-launch-measurement' to get the measurement of memory encrypted through launch sequence.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> --- src/qemu/qemu_driver.c | 69 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 8 +++++ src/qemu/qemu_monitor.h | 3 ++ src/qemu/qemu_monitor_json.c | 42 +++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 2 ++ 5 files changed, 124 insertions(+)
No changes needed, my R-b still stands Erik

Change since v7: * rename virNodeSEVCapability() -> virNodeSEVInfo() * rebase the series
The series looked solid, I found minor nits to which I provided you with a squash-in diff so that you don't have to spend any more time on it, I had a functional comment to patch 5 - might not work though, so please check, if it does, spinning a v9 should be very fast Thanks, Erik
participants (3)
-
Brijesh Singh
-
Erik Skultety
-
Ján Tomko