On Thu, Mar 08, 2018 at 11:12:02AM -0600, 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@xxxxxxx> > --- > docs/formatdomain.html.in | 120 ++++++++++++++++++++++++++++++++++++++++++ > docs/schemas/domaincommon.rng | 39 ++++++++++++++ > src/conf/domain_conf.c | 111 ++++++++++++++++++++++++++++++++++++++ > src/conf/domain_conf.h | 27 ++++++++++ > 4 files changed, 297 insertions(+) > > diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in > index 6fd2189cd2f4..830d2a3c59be 100644 > --- a/docs/formatdomain.html.in > +++ b/docs/formatdomain.html.in > @@ -8195,6 +8195,126 @@ 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 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.2.0</span> > + </p> > + <pre> > +<domain> > + ... > + <launch-security type='sev'> > + <policy> 0 </policy> > + <cbitpos> 47 </cbitpos> > + <reduced-phys-bits> 5 </reduced-phys-bits> > + <session> ... </session> > + <dh-cert> ... </dh> > + </sev> > + ... > +</domain> > +</pre> > + > + <p> > + A least <code>cbitpos</code> and <code>reduced-phys-bits</code> must be > + nested within the <code>launch-security</code> element. > + </p> > + <dl> > + <dt><code>cbitpos</code></dt> > + <dd>The <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 domaincapabilities. > + </dd> > + <dt><code>reduced-phys-bits</code></dt> > + <dd>The <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 domaincapabilities. > + </dd> > + <dt><code>policy</code></dt> > + <dd>The optional <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-byte structure 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> 15:6 </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> > + Default value is 0x1 > + > + </dd> > + <dt><code>dh-cert</code></dt> > + <dd>The optional <code>dh-cert</code> element provides the guest owners public > + 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. The value must be encoded in base64. > + </dd> > + <dt><code>session</code></dt> > + <dd>The optional <code>session</code> element provides the guest owners > + session blob defined in SEV API spec. The value must be encoded in base64. > + > + See SEV spec LAUNCH_START section for 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 8165e699d67e..e3dcc69067c2 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,42 @@ > </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> > + <optional> > + <element name="policy"> > + <ref name='hexuint'/> > + </element> > + </optional> > + <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 fcafc8b2fafe..52d34de69d45 100644 > --- a/src/conf/domain_conf.c > +++ b/src/conf/domain_conf.c > @@ -929,6 +929,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); > @@ -2897,6 +2901,14 @@ virDomainCachetuneDefFree(virDomainCachetuneDefPtr cachetune) > VIR_FREE(cachetune); > } > > +static void > +virDomainSevDefFree(virDomainSevDefPtr def) > +{ > + VIR_FREE(def->dh_cert); > + VIR_FREE(def->session); > + > + VIR_FREE(def); > +} > > void virDomainDefFree(virDomainDefPtr def) > { > @@ -3079,6 +3091,9 @@ void virDomainDefFree(virDomainDefPtr def) > if (def->namespaceData && def->ns.free) > (def->ns.free)(def->namespaceData); > > + if (def->sev) > + virDomainSevDefFree(def->sev); > + > xmlFreeNode(def->metadata); > > VIR_FREE(def); > @@ -15539,6 +15554,71 @@ virDomainMemoryTargetDefParseXML(xmlNodePtr node, > return ret; > } > > +static virDomainSevDefPtr > +virDomainSevDefParseXML(xmlNodePtr sevNode, > + xmlXPathContextPtr ctxt) > +{ > + char *tmp = NULL, *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; > + } > + > + if (virDomainLaunchSecurityTypeFromString(type) != > + VIR_DOMAIN_LAUNCH_SECURITY_SEV) { > + goto error; > + } > + > + if (virXPathInt("string(./cbitpos)", ctxt, &def->cbitpos) < 0) { > + virReportError(VIR_ERR_XML_ERROR, "%s", _("failed to get cbitpos")); > + goto error; > + } > + > + if (virXPathInt("string(./reduced-phys-bits)", ctxt, > + &def->reduced_phys_bits) < 0) { > + virReportError(VIR_ERR_XML_ERROR, "%s", > + _("failed to get reduced-phys-bits")); > + goto error; > + } > + > + if (virXPathULongHex("string(./policy)", ctxt, &policy) < 0) > + policy = 0x1; > + > + 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, > @@ -20212,6 +20292,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; > @@ -26102,6 +26189,27 @@ virDomainKeyWrapDefFormat(virBufferPtr buf, virDomainKeyWrapDefPtr keywrap) > virBufferAddLit(buf, "</keywrap>\n"); > } > > +static void > +virDomainSevDefFormat(virBufferPtr buf, virDomainSevDefPtr sev) > +{ > + virBufferAddLit(buf, "<launch-security type='sev'>\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); > + virBufferAsprintf(buf, "<policy>%d</policy>\n", sev->policy); > + if (sev->dh_cert) > + virBufferAsprintf(buf, "<dh_cert>%s</dh_cert>\n", sev->dh_cert); > + > + if (sev->session) > + virBufferAsprintf(buf, "<session>%s</session>\n", sev->session); > + > + virBufferAdjustIndent(buf, -2); > + virBufferAddLit(buf, "</launch-security>\n"); > +} > + > + > static void > virDomainPerfDefFormat(virBufferPtr buf, virDomainPerfDefPtr perf) > { > @@ -27274,6 +27382,9 @@ virDomainDefFormatInternal(virDomainDefPtr def, > if (def->keywrap) > virDomainKeyWrapDefFormat(buf, def->keywrap); > > + if (def->sev) > + 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 368f16f3fbf9..ff625a0aff1b 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 > */ > @@ -2289,6 +2292,26 @@ struct _virDomainKeyWrapDef { > int dea; /* enum virTristateSwitch */ > }; > > +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 { > + char *dh_cert; > + char *session; > + char *configDir; > + int policy; > + int cbitpos; > + int reduced_phys_bits; > +}; I'd suggested 'unsigned int' for these 3 variables, assuming they don't ever need -ve values. Reviewed-by: Daniel P. Berrangé <berrange@xxxxxxxxxx> Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :| -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list