Add element "quoteGenerationService" to tdx launch security type. Currently it contains only one sub-element "SocketAddress". "SocketAddress" is modelized according to QEMU QAPI, supporting inet, unix, vsock and fd type and variant attributes depending on type. XML example: <launchSecurity type='tdx'> <policy>0x0</policy> <mrConfigId>xxx</mrConfigId> <mrOwner>xxx</mrOwner> <mrOwnerConfig>xxx</mrOwnerConfig> <quoteGenerationService> <SocketAddress type='vsock' cid='xxx' port='xxx'/> </quoteGenerationService> </launchSecurity> QEMU command line example: qemu-system-x86_64 \ -object '{"qom-type":"tdx-guest","id":"lsec0","sept-ve-disable":false,"mrconfigid":"xxx","mrowner":"xxx","mrownerconfig":"xxx","quote-generation-socket":{"type":"vsock","cid":"xxx","port":"xxx"}}' \ -machine pc-q35-6.0,confidential-guest-support=lsec0 Signed-off-by: Zhenzhong Duan <zhenzhong.duan@xxxxxxxxx> --- src/conf/domain_conf.c | 272 +++++++++++++++++++++++++++++- src/conf/domain_conf.h | 61 +++++++ src/conf/schemas/domaincommon.rng | 106 ++++++++++++ src/qemu/qemu_command.c | 106 ++++++++++++ 4 files changed, 544 insertions(+), 1 deletion(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index c557da0c65..e1ae8295e5 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1511,6 +1511,15 @@ VIR_ENUM_IMPL(virDomainLaunchSecurity, "tdx", ); +VIR_ENUM_IMPL(virDomainSocketAddress, + VIR_DOMAIN_SOCKET_ADDRESS_LAST, + "", + "inet", + "unix", + "vsock", + "fd", +); + typedef enum { VIR_DOMAIN_NET_VHOSTUSER_MODE_NONE, VIR_DOMAIN_NET_VHOSTUSER_MODE_CLIENT, @@ -13654,6 +13663,190 @@ virDomainSEVDefParseXML(virDomainSEVDef *def, } +static int +virDomainInetSocketAddressDefParseXML(InetSocketAddress *inet, + xmlNodePtr node) +{ + int ret; + + if (!(inet->host = virXMLPropString(node, "host"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing host for inet socket address")); + return -1; + } + + if (!(inet->port = virXMLPropString(node, "port"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing port for inet socket address")); + return -1; + } + + if (virXMLPropTristateBool(node, "numeric", VIR_XML_PROP_NONE, + &inet->numeric) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("wrong attribute numeric for inet socket address")); + return -1; + } + + if ((ret = virXMLPropUInt(node, "to", 10, VIR_XML_PROP_NONE, + &inet->to)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("wrong attribute to for inet socket address")); + return -1; + } + if (!ret) + inet->has_to = true; + + if (virXMLPropTristateBool(node, "ipv4", VIR_XML_PROP_NONE, + &inet->ipv4) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("wrong attribute ipv4 for inet socket address")); + return -1; + } + + if (virXMLPropTristateBool(node, "ipv6", VIR_XML_PROP_NONE, + &inet->ipv6) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("wrong attribute ipv6 for inet socket address")); + return -1; + } + + if (virXMLPropTristateBool(node, "keep_alive", VIR_XML_PROP_NONE, + &inet->keep_alive) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("wrong attribute keep_alive for inet socket address")); + return -1; + } + + if (virXMLPropTristateBool(node, "mptcp", VIR_XML_PROP_NONE, + &inet->mptcp) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("wrong attribute mptcp for inet socket address")); + return -1; + } + + return 0; +} + + +static int +virDomainUnixSocketAddressDefParseXML(UnixSocketAddress *Unix, + xmlNodePtr node) +{ + if (!(Unix->path = virXMLPropString(node, "path"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing path for unix socket address")); + return -1; + } + + if (virXMLPropTristateBool(node, "abstract", VIR_XML_PROP_NONE, + &Unix->abstract) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("wrong attribute abstract for unix socket address")); + return -1; + } + + if (virXMLPropTristateBool(node, "tight", VIR_XML_PROP_NONE, + &Unix->tight) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("wrong attribute tight for unix socket address")); + return -1; + } + + return 0; +} + + +static int +virDomainVsockSocketAddressDefParseXML(VsockSocketAddress *vsock, + xmlNodePtr node) +{ + if (!(vsock->cid = virXMLPropString(node, "cid"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing cid for vsock socket address")); + return -1; + } + + if (!(vsock->port = virXMLPropString(node, "port"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing port for vsock socket address")); + return -1; + } + + return 0; +} + + +static int +virDomainFdSocketAddressDefParseXML(FdSocketAddress *fd, + xmlNodePtr node) +{ + if (!(fd->str = virXMLPropString(node, "str"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing fd for fd socket address")); + return -1; + } + + return 0; +} + + +static int +virDomainTDXQGSDefParseXML(virDomainTDXDef *def, xmlXPathContextPtr ctxt) +{ + g_autofree xmlNodePtr *nodes = NULL; + SocketAddress *sa = &def->qgs_sa; + xmlNodePtr node; + int n; + + if ((n = virXPathNodeSet("./quoteGenerationService/SocketAddress", + ctxt, &nodes)) < 0) + return -1; + + if (!n) + return 0; + + if (n > 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("only a single QGS element is supported")); + return -1; + } + node = nodes[0]; + + if (virXMLPropEnum(node, "type", virDomainSocketAddressTypeFromString, + VIR_XML_PROP_REQUIRED, &sa->type) < 0) + return -1; + + switch ((virDomainSocketAddress) def->qgs_sa.type) { + case VIR_DOMAIN_SOCKET_ADDRESS_INET: + if (virDomainInetSocketAddressDefParseXML(&sa->u.inet, node) < 0) + return -1; + break; + case VIR_DOMAIN_SOCKET_ADDRESS_UNIX: + if (virDomainUnixSocketAddressDefParseXML(&sa->u.Unix, node) < 0) + return -1; + break; + case VIR_DOMAIN_SOCKET_ADDRESS_VSOCK: + if (virDomainVsockSocketAddressDefParseXML(&sa->u.vsock, node) < 0) + return -1; + break; + case VIR_DOMAIN_SOCKET_ADDRESS_FD: + if (virDomainFdSocketAddressDefParseXML(&sa->u.fd, node) < 0) + return -1; + break; + case VIR_DOMAIN_SOCKET_ADDRESS_NONE: + case VIR_DOMAIN_SOCKET_ADDRESS_LAST: + default: + virReportError(VIR_ERR_XML_ERROR, + _("unsupported socket address type '%1$s'"), + virDomainSocketAddressTypeToString(def->qgs_sa.type)); + return -1; + } + + return 0; +} + + static int virDomainTDXDefParseXML(virDomainTDXDef *def, xmlXPathContextPtr ctxt) @@ -13668,7 +13861,7 @@ virDomainTDXDefParseXML(virDomainTDXDef *def, def->mrowner = virXPathString("string(./mrOwner)", ctxt); def->mrownerconfig = virXPathString("string(./mrOwnerConfig)", ctxt); - return 0; + return virDomainTDXQGSDefParseXML(def, ctxt); } @@ -26652,6 +26845,82 @@ virDomainKeyWrapDefFormat(virBuffer *buf, virDomainKeyWrapDef *keywrap) } +static void +virDomainTDXQGSDefFormat(virBuffer *buf, virDomainTDXDef *tdx) +{ + SocketAddress *sa = &tdx->qgs_sa; + + if (sa->type == VIR_DOMAIN_SOCKET_ADDRESS_NONE) + return; + + virBufferAddLit(buf, "<quoteGenerationService>\n"); + virBufferAdjustIndent(buf, 2); + virBufferAsprintf(buf, "<SocketAddress type='%s'", + virDomainSocketAddressTypeToString(sa->type)); + + switch ((virDomainSocketAddress) sa->type) { + case VIR_DOMAIN_SOCKET_ADDRESS_INET: + { + InetSocketAddress *inet = &sa->u.inet; + + virBufferAsprintf(buf, " host='%s' port='%s'", inet->host, inet->port); + if (inet->numeric != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " numeric='%s'", + virTristateBoolTypeToString(inet->numeric)); + if (inet->to) + virBufferAsprintf(buf, " to='%d'", inet->to); + if (inet->ipv4 != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " ipv4='%s'", + virTristateBoolTypeToString(inet->ipv4)); + if (inet->ipv6 != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " ipv6='%s'", + virTristateBoolTypeToString(inet->ipv6)); + if (inet->keep_alive != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " keep_alive='%s'", + virTristateBoolTypeToString(inet->keep_alive)); + if (inet->mptcp != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " mptcp='%s'", + virTristateBoolTypeToString(inet->mptcp)); + break; + } + case VIR_DOMAIN_SOCKET_ADDRESS_UNIX: + { + UnixSocketAddress *Unix = &sa->u.Unix; + + virBufferAsprintf(buf, " path='%s'", Unix->path); + if (Unix->abstract != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " abstract='%s'", + virTristateBoolTypeToString(Unix->abstract)); + if (Unix->tight != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " tight='%s'", + virTristateBoolTypeToString(Unix->tight)); + break; + } + case VIR_DOMAIN_SOCKET_ADDRESS_VSOCK: + { + VsockSocketAddress *vsock = &sa->u.vsock; + + virBufferAsprintf(buf, " cid='%s' port='%s'", vsock->cid, vsock->port); + break; + } + case VIR_DOMAIN_SOCKET_ADDRESS_FD: + { + FdSocketAddress *fd = &sa->u.fd; + + virBufferAsprintf(buf, " str='%s'", fd->str); + break; + } + case VIR_DOMAIN_SOCKET_ADDRESS_NONE: + case VIR_DOMAIN_SOCKET_ADDRESS_LAST: + default: + break; + } + virBufferAddLit(buf, "/>\n"); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</quoteGenerationService>\n"); +} + + static void virDomainSecDefFormat(virBuffer *buf, virDomainSecDef *sec) { @@ -26699,6 +26968,7 @@ virDomainSecDefFormat(virBuffer *buf, virDomainSecDef *sec) virBufferEscapeString(&childBuf, "<mrOwner>%s</mrOwner>\n", tdx->mrowner); if (tdx->mrownerconfig) virBufferEscapeString(&childBuf, "<mrOwnerConfig>%s</mrOwnerConfig>\n", tdx->mrownerconfig); + virDomainTDXQGSDefFormat(&childBuf, tdx); break; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index bb4973fce8..15cdb3e0e6 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2852,6 +2852,55 @@ struct _virDomainKeyWrapDef { virTristateSwitch dea; }; +typedef enum { + VIR_DOMAIN_SOCKET_ADDRESS_NONE, + VIR_DOMAIN_SOCKET_ADDRESS_INET, + VIR_DOMAIN_SOCKET_ADDRESS_UNIX, + VIR_DOMAIN_SOCKET_ADDRESS_VSOCK, + VIR_DOMAIN_SOCKET_ADDRESS_FD, + + VIR_DOMAIN_SOCKET_ADDRESS_LAST +} virDomainSocketAddress; + +typedef struct _InetSocketAddress InetSocketAddress; +typedef struct _UnixSocketAddress UnixSocketAddress; +typedef struct _VsockSocketAddress VsockSocketAddress; +typedef struct _FdSocketAddress FdSocketAddress; + +struct _InetSocketAddress { + char *host; + char *port; + bool has_numeric; + virTristateBool numeric; + bool has_to; + unsigned int to; + bool has_ipv4; + virTristateBool ipv4; + bool has_ipv6; + virTristateBool ipv6; + bool has_keep_alive; + virTristateBool keep_alive; + bool has_mptcp; + virTristateBool mptcp; +}; + +struct _UnixSocketAddress { + char *path; + bool has_abstract; + virTristateBool abstract; + bool has_tight; + virTristateBool tight; +}; + +struct _VsockSocketAddress { + char *cid; + char *port; +}; + +struct _FdSocketAddress { + char *str; +}; + typedef enum { VIR_DOMAIN_LAUNCH_SECURITY_NONE, VIR_DOMAIN_LAUNCH_SECURITY_SEV, @@ -2873,11 +2922,22 @@ struct _virDomainSEVDef { virTristateBool kernel_hashes; }; +typedef struct SocketAddress { + virDomainSocketAddress type; + union { + InetSocketAddress inet; + UnixSocketAddress Unix; + VsockSocketAddress vsock; + FdSocketAddress fd; + } u; +} SocketAddress; + struct _virDomainTDXDef { unsigned long long policy; char *mrconfigid; char *mrowner; char *mrownerconfig; + SocketAddress qgs_sa; }; #define VIR_DOMAIN_TDX_POLICY_DEBUG 0x1 @@ -4258,6 +4318,7 @@ VIR_ENUM_DECL(virDomainCryptoBackend); VIR_ENUM_DECL(virDomainShmemModel); VIR_ENUM_DECL(virDomainShmemRole); VIR_ENUM_DECL(virDomainLaunchSecurity); +VIR_ENUM_DECL(virDomainSocketAddress); /* from libvirt.h */ VIR_ENUM_DECL(virDomainState); VIR_ENUM_DECL(virDomainNostateReason); diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index f6e1782b33..e8cc78992a 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -591,9 +591,115 @@ <data type="string"/> </element> </optional> + <optional> + <element name="quoteGenerationService"> + <ref name="SocketAddress"/> + </element> + </optional> </interleave> </define> + <define name="SocketAddress"> + <element name="SocketAddress"> + <choice> + <group> + <ref name="InetSocketAddress"/> + </group> + <group> + <ref name="UnixSocketAddress"/> + </group> + <group> + <ref name="VsockSocketAddress"/> + </group> + <group> + <ref name="FdSocketAddress"/> + </group> + </choice> + </element> + </define> + + <define name="InetSocketAddress"> + <attribute name="type"> + <value>inet</value> + </attribute> + <attribute name="host"> + <data type="string"/> + </attribute> + <attribute name="port"> + <data type="string"/> + </attribute> + <optional> + <attribute name="numeric"> + <ref name="virYesNo"/> + </attribute> + </optional> + <optional> + <attribute name="to"> + <ref name="uint16"/> + </attribute> + </optional> + <optional> + <attribute name="ipv4"> + <ref name="virYesNo"/> + </attribute> + </optional> + <optional> + <attribute name="ipv6"> + <ref name="virYesNo"/> + </attribute> + </optional> + <optional> + <attribute name="keep_alive"> + <ref name="virYesNo"/> + </attribute> + </optional> + <optional> + <attribute name="mptcp"> + <ref name="virYesNo"/> + </attribute> + </optional> + </define> + + <define name="UnixSocketAddress"> + <attribute name="type"> + <value>unix</value> + </attribute> + <attribute name="path"> + <data type="string"/> + </attribute> + <optional> + <attribute name="abstract"> + <ref name="virYesNo"/> + </attribute> + </optional> + <optional> + <attribute name="tight"> + <ref name="virYesNo"/> + </attribute> + </optional> + </define> + + <define name="VsockSocketAddress"> + <attribute name="type"> + <value>vsock</value> + </attribute> + <attribute name="cid"> + <data type="string"/> + </attribute> + <attribute name="port"> + <data type="string"/> + </attribute> + </define> + + <define name="FdSocketAddress"> + <attribute name="type"> + <value>fd</value> + </attribute> + <attribute name="str"> + <data type="string"/> + </attribute> + </define> + <!-- Enable or disable perf events for the domain. For each of the events the following rules apply: diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d212d80038..05fec05cd5 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -9745,21 +9745,127 @@ qemuBuildPVCommandLine(virDomainObj *vm, virCommand *cmd) } +static virJSONValue * +qemuJSONBuildInetSocketAddress(const InetSocketAddress *inet) +{ + g_autoptr(virJSONValue) addr = NULL; + g_autoptr(virJSONValue) data = NULL; + + if (virJSONValueObjectAdd(&addr, + "s:type", "inet", + "s:host", inet->host, + "s:port", inet->port, + "T:numeric", inet->numeric, + "p:to", inet->to, + "T:ipv4", inet->ipv4, + "T:ipv6", inet->ipv6, + "T:keep-alive", inet->keep_alive, + "T:mptcp", inet->mptcp, + NULL) < 0) + return NULL; + + return g_steal_pointer(&addr); +} + + +static virJSONValue * +qemuJSONBuildUnixSocketAddress(const UnixSocketAddress *Unix) +{ + g_autoptr(virJSONValue) addr = NULL; + g_autoptr(virJSONValue) data = NULL; + + if (virJSONValueObjectAdd(&addr, + "s:type", "unix", + "s:path", Unix->path, + "T:abstract", Unix->abstract, + "T:tight", Unix->tight, + NULL) < 0) + return NULL; + + return g_steal_pointer(&addr); +} + + +static virJSONValue * +qemuJSONBuildVsockSocketAddress(const VsockSocketAddress *vsock) +{ + g_autoptr(virJSONValue) addr = NULL; + g_autoptr(virJSONValue) data = NULL; + + if (virJSONValueObjectAdd(&addr, + "s:type", "vsock", + "s:cid", vsock->cid, + "s:port", vsock->port, + NULL) < 0) + return NULL; + + return g_steal_pointer(&addr); +} + + +static virJSONValue * +qemuJSONBuildFdSocketAddress(const FdSocketAddress *fd) +{ + g_autoptr(virJSONValue) addr = NULL; + g_autoptr(virJSONValue) data = NULL; + + if (virJSONValueObjectAdd(&addr, + "s:type", "fd", + "s:str", fd->str, + NULL) < 0) + return NULL; + + return g_steal_pointer(&addr); +} + + +static virJSONValue * +qemuBuildTDXQGSCommandLine(virDomainTDXDef *tdx) +{ + g_autoptr(virJSONValue) addr = NULL; + + switch ((virDomainSocketAddress) tdx->qgs_sa.type) { + case VIR_DOMAIN_SOCKET_ADDRESS_INET: + addr = qemuJSONBuildInetSocketAddress(&tdx->qgs_sa.u.inet); + break; + case VIR_DOMAIN_SOCKET_ADDRESS_UNIX: + addr = qemuJSONBuildUnixSocketAddress(&tdx->qgs_sa.u.Unix); + break; + case VIR_DOMAIN_SOCKET_ADDRESS_VSOCK: + addr = qemuJSONBuildVsockSocketAddress(&tdx->qgs_sa.u.vsock); + break; + case VIR_DOMAIN_SOCKET_ADDRESS_FD: + addr = qemuJSONBuildFdSocketAddress(&tdx->qgs_sa.u.fd); + break; + case VIR_DOMAIN_SOCKET_ADDRESS_NONE: + case VIR_DOMAIN_SOCKET_ADDRESS_LAST: + default: + return NULL; + } + + return g_steal_pointer(&addr); +} + + static int qemuBuildTDXCommandLine(virDomainObj *vm, virCommand *cmd, virDomainTDXDef *tdx) { + g_autoptr(virJSONValue) addr = NULL; g_autoptr(virJSONValue) props = NULL; qemuDomainObjPrivate *priv = vm->privateData; bool sept_ve_disable = tdx->policy & VIR_DOMAIN_TDX_POLICY_SEPT_VE_DISABLE; VIR_DEBUG("policy=0x%llx", tdx->policy); + addr = qemuBuildTDXQGSCommandLine(tdx); + if (qemuMonitorCreateObjectProps(&props, "tdx-guest", "lsec0", "B:debug", !!(tdx->policy & VIR_DOMAIN_TDX_POLICY_DEBUG), "S:mrconfigid", tdx->mrconfigid, "S:mrowner", tdx->mrowner, "S:mrownerconfig", tdx->mrownerconfig, + "A:quote-generation-socket", &addr, NULL) < 0) return -1; -- 2.34.1