]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
conf: split out virDomainFeaturesDefParse
authorJán Tomko <jtomko@redhat.com>
Tue, 21 Apr 2020 16:35:59 +0000 (18:35 +0200)
committerJán Tomko <jtomko@redhat.com>
Wed, 22 Apr 2020 08:46:37 +0000 (10:46 +0200)
The virDomainDefParseXML function has grown so large it broke the build:
../../src/conf/domain_conf.c:20362:1: error: stack frame size of 4168 bytes
in function 'virDomainDefParseXML' [-Werror,-Wframe-larger-than=]

Signed-off-by: Ján Tomko <jtomko@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
src/conf/domain_conf.c

index 9f3362c9346637444f0221c88173351588b9e433..d9454575f22d482f90fdde01d722da2918d04503 100644 (file)
@@ -19181,400 +19181,577 @@ virDomainResourceDefParse(xmlNodePtr node,
     return NULL;
 }
 
+
 static int
-virDomainDefMaybeAddHostdevSCSIcontroller(virDomainDefPtr def)
+virDomainFeaturesDefParse(virDomainDefPtr def,
+                          xmlXPathContextPtr ctxt)
 {
-    /* Look for any hostdev scsi dev */
+    g_autofree xmlNodePtr *nodes = NULL;
+    g_autofree char *tmp = NULL;
+    xmlNodePtr node = NULL;
+    int gic_version;
     size_t i;
-    int maxController = -1;
-    virDomainHostdevDefPtr hostdev;
-    int newModel = -1;
+    int n;
 
-    for (i = 0; i < def->nhostdevs; i++) {
-        hostdev = def->hostdevs[i];
-        if (virHostdevIsSCSIDevice(hostdev) &&
-            (int)hostdev->info->addr.drive.controller > maxController) {
-            virDomainControllerDefPtr cont;
+    if ((n = virXPathNodeSet("./features/*", ctxt, &nodes)) < 0)
+        goto error;
 
-            maxController = hostdev->info->addr.drive.controller;
-            /* We may be creating a new controller because this one is full.
-             * So let's grab the model from it and update the model we're
-             * going to add as long as this one isn't undefined. The premise
-             * being keeping the same controller model for all SCSI hostdevs. */
-            cont = virDomainDeviceFindSCSIController(def, &hostdev->info->addr.drive);
-            if (cont && cont->model != -1)
-                newModel = cont->model;
+    for (i = 0; i < n; i++) {
+        int val = virDomainFeatureTypeFromString((const char *)nodes[i]->name);
+        if (val < 0) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("unexpected feature '%s'"), nodes[i]->name);
+            goto error;
         }
-    }
 
-    if (maxController == -1)
-        return 0;
+        switch ((virDomainFeature) val) {
+        case VIR_DOMAIN_FEATURE_APIC:
+            if ((tmp = virXPathString("string(./features/apic/@eoi)", ctxt))) {
+                int eoi;
+                if ((eoi = virTristateSwitchTypeFromString(tmp)) <= 0) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                   _("unknown value for attribute eoi: '%s'"),
+                                   tmp);
+                    goto error;
+                }
+                def->apic_eoi = eoi;
+                VIR_FREE(tmp);
+            }
+            G_GNUC_FALLTHROUGH;
+        case VIR_DOMAIN_FEATURE_ACPI:
+        case VIR_DOMAIN_FEATURE_PAE:
+        case VIR_DOMAIN_FEATURE_VIRIDIAN:
+        case VIR_DOMAIN_FEATURE_PRIVNET:
+        case VIR_DOMAIN_FEATURE_HYPERV:
+        case VIR_DOMAIN_FEATURE_KVM:
+        case VIR_DOMAIN_FEATURE_MSRS:
+        case VIR_DOMAIN_FEATURE_XEN:
+            def->features[val] = VIR_TRISTATE_SWITCH_ON;
+            break;
 
-    for (i = 0; i <= maxController; i++) {
-        if (virDomainDefMaybeAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_SCSI,
-                                           i, newModel) < 0)
-            return -1;
-    }
+        case VIR_DOMAIN_FEATURE_CAPABILITIES:
+            if ((tmp = virXMLPropString(nodes[i], "policy"))) {
+                if ((def->features[val] = virDomainCapabilitiesPolicyTypeFromString(tmp)) == -1) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                   _("unknown policy attribute '%s' of feature '%s'"),
+                                   tmp, virDomainFeatureTypeToString(val));
+                    goto error;
+                }
+                VIR_FREE(tmp);
+            } else {
+                def->features[val] = VIR_TRISTATE_SWITCH_ABSENT;
+            }
+            break;
 
-    return 0;
-}
+        case VIR_DOMAIN_FEATURE_VMCOREINFO:
+        case VIR_DOMAIN_FEATURE_HAP:
+        case VIR_DOMAIN_FEATURE_PMU:
+        case VIR_DOMAIN_FEATURE_PVSPINLOCK:
+        case VIR_DOMAIN_FEATURE_VMPORT:
+        case VIR_DOMAIN_FEATURE_SMM:
+            if ((tmp = virXMLPropString(nodes[i], "state"))) {
+                if ((def->features[val] = virTristateSwitchTypeFromString(tmp)) == -1) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                   _("unknown state attribute '%s' of feature '%s'"),
+                                   tmp, virDomainFeatureTypeToString(val));
+                    goto error;
+                }
+                VIR_FREE(tmp);
+            } else {
+                def->features[val] = VIR_TRISTATE_SWITCH_ON;
+            }
+            break;
 
-static int
-virDomainLoaderDefParseXML(xmlNodePtr node,
-                           virDomainLoaderDefPtr loader,
-                           bool fwAutoSelect)
-{
-    g_autofree char *readonly_str = NULL;
-    g_autofree char *secure_str = NULL;
-    g_autofree char *type_str = NULL;
+        case VIR_DOMAIN_FEATURE_GIC:
+            if ((tmp = virXMLPropString(nodes[i], "version"))) {
+                gic_version = virGICVersionTypeFromString(tmp);
+                if (gic_version < 0 || gic_version == VIR_GIC_VERSION_NONE) {
+                    virReportError(VIR_ERR_XML_ERROR,
+                                   _("malformed gic version: %s"), tmp);
+                    goto error;
+                }
+                def->gic_version = gic_version;
+                VIR_FREE(tmp);
+            }
+            def->features[val] = VIR_TRISTATE_SWITCH_ON;
+            break;
 
-    secure_str = virXMLPropString(node, "secure");
+        case VIR_DOMAIN_FEATURE_IOAPIC:
+            tmp = virXMLPropString(nodes[i], "driver");
+            if (tmp) {
+                int value = virDomainIOAPICTypeFromString(tmp);
+                if (value < 0 || value == VIR_DOMAIN_IOAPIC_NONE) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                   _("Unknown driver mode: %s"),
+                                   tmp);
+                    goto error;
+                }
+                def->features[val] = value;
+                VIR_FREE(tmp);
+            }
+            break;
 
-    if (!fwAutoSelect) {
-        readonly_str = virXMLPropString(node, "readonly");
-        type_str = virXMLPropString(node, "type");
-        loader->path = (char *) xmlNodeGetContent(node);
-        if (STREQ_NULLABLE(loader->path, ""))
-            VIR_FREE(loader->path);
-    }
+        case VIR_DOMAIN_FEATURE_HPT:
+            tmp = virXMLPropString(nodes[i], "resizing");
+            if (tmp) {
+                int value = virDomainHPTResizingTypeFromString(tmp);
+                if (value < 0 || value == VIR_DOMAIN_HPT_RESIZING_NONE) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                   _("Unknown HPT resizing setting: %s"),
+                                   tmp);
+                    goto error;
+                }
+                def->hpt_resizing = (virDomainHPTResizing) value;
+                VIR_FREE(tmp);
+            }
 
-    if (readonly_str &&
-        (loader->readonly = virTristateBoolTypeFromString(readonly_str)) <= 0) {
-        virReportError(VIR_ERR_XML_DETAIL,
-                       _("unknown readonly value: %s"), readonly_str);
-        return -1;
-    }
+            if (virDomainParseScaledValue("./features/hpt/maxpagesize",
+                                          NULL,
+                                          ctxt,
+                                          &def->hpt_maxpagesize,
+                                          1024,
+                                          ULLONG_MAX,
+                                          false) < 0) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               "%s",
+                               _("Unable to parse HPT maxpagesize setting"));
+                goto error;
+            }
+            def->hpt_maxpagesize = VIR_DIV_UP(def->hpt_maxpagesize, 1024);
 
-    if (secure_str &&
-        (loader->secure = virTristateBoolTypeFromString(secure_str)) <= 0) {
-        virReportError(VIR_ERR_XML_DETAIL,
-                       _("unknown secure value: %s"), secure_str);
-        return -1;
+            if (def->hpt_resizing != VIR_DOMAIN_HPT_RESIZING_NONE ||
+                def->hpt_maxpagesize > 0) {
+                def->features[val] = VIR_TRISTATE_SWITCH_ON;
+            }
+            break;
+
+        case VIR_DOMAIN_FEATURE_HTM:
+        case VIR_DOMAIN_FEATURE_NESTED_HV:
+        case VIR_DOMAIN_FEATURE_CCF_ASSIST:
+            if (!(tmp = virXMLPropString(nodes[i], "state"))) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("missing state attribute '%s' of feature '%s'"),
+                               tmp, virDomainFeatureTypeToString(val));
+                goto error;
+            }
+            if ((def->features[val] = virTristateSwitchTypeFromString(tmp)) < 0) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("unknown state attribute '%s' of feature '%s'"),
+                               tmp, virDomainFeatureTypeToString(val));
+                goto error;
+            }
+            VIR_FREE(tmp);
+            break;
+
+        /* coverity[dead_error_begin] */
+        case VIR_DOMAIN_FEATURE_LAST:
+            break;
+        }
     }
+    VIR_FREE(nodes);
+
+    if (def->features[VIR_DOMAIN_FEATURE_HYPERV] == VIR_TRISTATE_SWITCH_ON) {
+        int feature;
+        int value;
+        node = ctxt->node;
+        if ((n = virXPathNodeSet("./features/hyperv/*", ctxt, &nodes)) < 0)
+            goto error;
+
+        for (i = 0; i < n; i++) {
+            feature = virDomainHypervTypeFromString((const char *)nodes[i]->name);
+            if (feature < 0) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("unsupported HyperV Enlightenment feature: %s"),
+                               nodes[i]->name);
+                goto error;
+            }
+
+            ctxt->node = nodes[i];
+
+            if (!(tmp = virXMLPropString(nodes[i], "state"))) {
+                virReportError(VIR_ERR_XML_ERROR,
+                               _("missing 'state' attribute for "
+                                 "HyperV Enlightenment feature '%s'"),
+                               nodes[i]->name);
+                goto error;
+            }
+
+            if ((value = virTristateSwitchTypeFromString(tmp)) < 0) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("invalid value of state argument "
+                                 "for HyperV Enlightenment feature '%s'"),
+                               nodes[i]->name);
+                goto error;
+            }
+
+            VIR_FREE(tmp);
+            def->hyperv_features[feature] = value;
+
+            switch ((virDomainHyperv) feature) {
+            case VIR_DOMAIN_HYPERV_RELAXED:
+            case VIR_DOMAIN_HYPERV_VAPIC:
+            case VIR_DOMAIN_HYPERV_VPINDEX:
+            case VIR_DOMAIN_HYPERV_RUNTIME:
+            case VIR_DOMAIN_HYPERV_SYNIC:
+            case VIR_DOMAIN_HYPERV_STIMER:
+            case VIR_DOMAIN_HYPERV_RESET:
+            case VIR_DOMAIN_HYPERV_FREQUENCIES:
+            case VIR_DOMAIN_HYPERV_REENLIGHTENMENT:
+            case VIR_DOMAIN_HYPERV_TLBFLUSH:
+            case VIR_DOMAIN_HYPERV_IPI:
+            case VIR_DOMAIN_HYPERV_EVMCS:
+                break;
+
+            case VIR_DOMAIN_HYPERV_SPINLOCKS:
+                if (value != VIR_TRISTATE_SWITCH_ON)
+                    break;
+
+                if (virXPathUInt("string(./@retries)", ctxt,
+                             &def->hyperv_spinlocks) < 0) {
+                    virReportError(VIR_ERR_XML_ERROR, "%s",
+                                   _("invalid HyperV spinlock retry count"));
+                    goto error;
+                }
+
+                if (def->hyperv_spinlocks < 0xFFF) {
+                    virReportError(VIR_ERR_XML_ERROR, "%s",
+                                   _("HyperV spinlock retry count must be "
+                                     "at least 4095"));
+                    goto error;
+                }
+                break;
+
+            case VIR_DOMAIN_HYPERV_VENDOR_ID:
+                if (value != VIR_TRISTATE_SWITCH_ON)
+                    break;
+
+                if (!(def->hyperv_vendor_id = virXMLPropString(nodes[i],
+                                                               "value"))) {
+                    virReportError(VIR_ERR_XML_ERROR, "%s",
+                                   _("missing 'value' attribute for "
+                                     "HyperV feature 'vendor_id'"));
+                    goto error;
+                }
+
+                if (strlen(def->hyperv_vendor_id) > VIR_DOMAIN_HYPERV_VENDOR_ID_MAX) {
+                    virReportError(VIR_ERR_XML_ERROR,
+                                   _("HyperV vendor_id value must not be more "
+                                     "than %d characters."),
+                                   VIR_DOMAIN_HYPERV_VENDOR_ID_MAX);
+                    goto error;
+                }
 
-    if (type_str) {
-        int type;
-        if ((type = virDomainLoaderTypeFromString(type_str)) <= 0) {
-            virReportError(VIR_ERR_XML_DETAIL,
-                           _("unknown type value: %s"), type_str);
-            return -1;
+                /* ensure that the string can be passed to qemu */
+                if (strchr(def->hyperv_vendor_id, ',')) {
+                    virReportError(VIR_ERR_XML_ERROR, "%s",
+                                   _("HyperV vendor_id value is invalid"));
+                    goto error;
+                }
+
+            /* coverity[dead_error_begin] */
+            case VIR_DOMAIN_HYPERV_LAST:
+                break;
+            }
         }
-        loader->type = type;
+        VIR_FREE(nodes);
+        ctxt->node = node;
     }
 
-    return 0;
-}
+    if (def->features[VIR_DOMAIN_HYPERV_STIMER] == VIR_TRISTATE_SWITCH_ON) {
+        int value;
+        if ((n = virXPathNodeSet("./features/hyperv/stimer/*", ctxt, &nodes)) < 0)
+            goto error;
 
+        for (i = 0; i < n; i++) {
+            if (STRNEQ((const char *)nodes[i]->name, "direct")) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("unsupported Hyper-V stimer feature: %s"),
+                               nodes[i]->name);
+                goto error;
+            }
 
-static int
-virDomainSchedulerParseCommonAttrs(xmlNodePtr node,
-                                   virProcessSchedPolicy *policy,
-                                   int *priority)
-{
-    int pol = 0;
-    g_autofree char *tmp = NULL;
+            if (!(tmp = virXMLPropString(nodes[i], "state"))) {
+                virReportError(VIR_ERR_XML_ERROR,
+                               _("missing 'state' attribute for "
+                                 "Hyper-V stimer '%s' feature"), "direct");
+                        goto error;
+            }
 
-    if (!(tmp = virXMLPropString(node, "scheduler"))) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("Missing scheduler attribute"));
-        return -1;
-    }
+            if ((value = virTristateSwitchTypeFromString(tmp)) < 0) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("invalid value of state argument "
+                                 "for Hyper-V stimer '%s' feature"), "direct");
+                goto error;
+            }
 
-    if ((pol = virProcessSchedPolicyTypeFromString(tmp)) <= 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Invalid scheduler attribute: '%s'"), tmp);
-        return -1;
+            VIR_FREE(tmp);
+            def->hyperv_stimer_direct = value;
+        }
+        VIR_FREE(nodes);
     }
-    *policy = pol;
 
-    VIR_FREE(tmp);
+    if (def->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) {
+        int feature;
+        int value;
+        if ((n = virXPathNodeSet("./features/kvm/*", ctxt, &nodes)) < 0)
+            goto error;
 
-    if (pol == VIR_PROC_POLICY_FIFO ||
-        pol == VIR_PROC_POLICY_RR) {
-        if (!(tmp = virXMLPropString(node, "priority"))) {
-            virReportError(VIR_ERR_XML_ERROR, "%s",
-                           _("Missing scheduler priority"));
-            return -1;
-        }
+        for (i = 0; i < n; i++) {
+            feature = virDomainKVMTypeFromString((const char *)nodes[i]->name);
+            if (feature < 0) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("unsupported KVM feature: %s"),
+                               nodes[i]->name);
+                goto error;
+            }
 
-        if (virStrToLong_i(tmp, NULL, 10, priority) < 0) {
-            virReportError(VIR_ERR_XML_ERROR, "%s",
-                           _("Invalid value for element priority"));
-            return -1;
+            switch ((virDomainKVM) feature) {
+                case VIR_DOMAIN_KVM_HIDDEN:
+                case VIR_DOMAIN_KVM_DEDICATED:
+                    if (!(tmp = virXMLPropString(nodes[i], "state"))) {
+                        virReportError(VIR_ERR_XML_ERROR,
+                                       _("missing 'state' attribute for "
+                                         "KVM feature '%s'"),
+                                       nodes[i]->name);
+                        goto error;
+                    }
+
+                    if ((value = virTristateSwitchTypeFromString(tmp)) < 0) {
+                        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                       _("invalid value of state argument "
+                                         "for KVM feature '%s'"),
+                                       nodes[i]->name);
+                        goto error;
+                    }
+
+                    VIR_FREE(tmp);
+                    def->kvm_features[feature] = value;
+                    break;
+
+                /* coverity[dead_error_begin] */
+                case VIR_DOMAIN_KVM_LAST:
+                    break;
+            }
         }
+        VIR_FREE(nodes);
     }
 
-    return 0;
-}
-
+    if (def->features[VIR_DOMAIN_FEATURE_XEN] == VIR_TRISTATE_SWITCH_ON) {
+        int feature;
+        int value;
+        g_autofree char *ptval = NULL;
 
-static int
-virDomainEmulatorSchedParse(xmlNodePtr node,
-                            virDomainDefPtr def)
-{
-    g_autofree virDomainThreadSchedParamPtr sched = NULL;
+        if ((n = virXPathNodeSet("./features/xen/*", ctxt, &nodes)) < 0)
+            goto error;
 
-    if (VIR_ALLOC(sched) < 0)
-        return -1;
+        for (i = 0; i < n; i++) {
+            feature = virDomainXenTypeFromString((const char *)nodes[i]->name);
+            if (feature < 0) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("unsupported Xen feature: %s"),
+                               nodes[i]->name);
+                goto error;
+            }
 
-    if (virDomainSchedulerParseCommonAttrs(node,
-                                           &sched->policy,
-                                           &sched->priority) < 0)
-        return -1;
+            if (!(tmp = virXMLPropString(nodes[i], "state"))) {
+                virReportError(VIR_ERR_XML_ERROR,
+                               _("missing 'state' attribute for "
+                                 "Xen feature '%s'"),
+                               nodes[i]->name);
+                goto error;
+            }
 
-    def->cputune.emulatorsched = g_steal_pointer(&sched);
-    return 0;
-}
+            if ((value = virTristateSwitchTypeFromString(tmp)) < 0) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("invalid value of state argument "
+                                 "for Xen feature '%s'"),
+                               nodes[i]->name);
+                goto error;
+            }
 
+            VIR_FREE(tmp);
+            def->xen_features[feature] = value;
 
-static virBitmapPtr
-virDomainSchedulerParse(xmlNodePtr node,
-                        const char *name,
-                        virProcessSchedPolicy *policy,
-                        int *priority)
-{
-    virBitmapPtr ret = NULL;
-    g_autofree char *tmp = NULL;
+            switch ((virDomainXen) feature) {
+                case VIR_DOMAIN_XEN_E820_HOST:
+                    break;
 
-    if (!(tmp = virXMLPropString(node, name))) {
-        virReportError(VIR_ERR_XML_ERROR,
-                       _("Missing attribute '%s' in element '%sched'"),
-                       name, name);
-        goto error;
-    }
+            case VIR_DOMAIN_XEN_PASSTHROUGH:
+                if (value != VIR_TRISTATE_SWITCH_ON)
+                    break;
 
-    if (virBitmapParse(tmp, &ret, VIR_DOMAIN_CPUMASK_LEN) < 0)
-        goto error;
+                if ((ptval = virXMLPropString(nodes[i], "mode"))) {
+                    int mode = virDomainXenPassthroughModeTypeFromString(ptval);
 
-    if (virBitmapIsAllClear(ret)) {
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                       _("'%s' scheduler bitmap '%s' is empty"),
-                       name, tmp);
-        goto error;
-    }
+                    if (mode < 0) {
+                        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                       _("unsupported mode '%s' for Xen passthrough feature"),
+                                       ptval);
+                        goto error;
+                    }
 
-    if (virDomainSchedulerParseCommonAttrs(node, policy, priority) < 0)
-        goto error;
+                    if (mode != VIR_DOMAIN_XEN_PASSTHROUGH_MODE_SYNC_PT &&
+                        mode != VIR_DOMAIN_XEN_PASSTHROUGH_MODE_SHARE_PT) {
+                        virReportError(VIR_ERR_XML_ERROR, "%s",
+                                       _("'mode' attribute for Xen feature "
+                                         "'passthrough' must be 'sync_pt' or 'share_pt'"));
+                        goto error;
+                    }
+                    def->xen_passthrough_mode = mode;
+                }
+                break;
 
-    return ret;
+                /* coverity[dead_error_begin] */
+                case VIR_DOMAIN_XEN_LAST:
+                    break;
+            }
+        }
+        VIR_FREE(nodes);
+    }
 
- error:
-    virBitmapFree(ret);
-    return NULL;
-}
+    if (def->features[VIR_DOMAIN_FEATURE_SMM] == VIR_TRISTATE_SWITCH_ON) {
+        int rv = virDomainParseScaledValue("string(./features/smm/tseg)",
+                                           "string(./features/smm/tseg/@unit)",
+                                           ctxt,
+                                           &def->tseg_size,
+                                           1024 * 1024, /* Defaults to mebibytes */
+                                           ULLONG_MAX,
+                                           false);
+        if (rv < 0)
+            goto error;
+        def->tseg_specified = rv;
+    }
 
+    if (def->features[VIR_DOMAIN_FEATURE_MSRS] == VIR_TRISTATE_SWITCH_ON) {
+        if ((node = virXPathNode("./features/msrs", ctxt)) == NULL)
+            goto error;
 
-static int
-virDomainThreadSchedParseHelper(xmlNodePtr node,
-                                const char *name,
-                                virDomainThreadSchedParamPtr (*func)(virDomainDefPtr, unsigned int),
-                                virDomainDefPtr def)
-{
-    ssize_t next = -1;
-    virDomainThreadSchedParamPtr sched = NULL;
-    virProcessSchedPolicy policy = 0;
-    int priority = 0;
-    g_autoptr(virBitmap) map = NULL;
+        if (!(tmp = virXMLPropString(node, "unknown"))) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("missing 'unknown' attribute for feature '%s'"),
+                           virDomainFeatureTypeToString(VIR_DOMAIN_FEATURE_MSRS));
+            goto error;
+        }
 
-    if (!(map = virDomainSchedulerParse(node, name, &policy, &priority)))
-        return -1;
+        if ((def->msrs_features[VIR_DOMAIN_MSRS_UNKNOWN] = virDomainMsrsUnknownTypeFromString(tmp)) < 0) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("unknown 'unknown' value '%s'"),
+                           tmp);
+            goto error;
+        }
+        VIR_FREE(tmp);
+    }
 
-    while ((next = virBitmapNextSetBit(map, next)) > -1) {
-        if (!(sched = func(def, next)))
-            return -1;
+    if ((n = virXPathNodeSet("./features/capabilities/*", ctxt, &nodes)) < 0)
+        goto error;
 
-        if (sched->policy != VIR_PROC_POLICY_NONE) {
-            virReportError(VIR_ERR_XML_DETAIL,
-                           _("%ssched attributes 'vcpus' must not overlap"),
-                           name);
-            return -1;
+    for (i = 0; i < n; i++) {
+        int val = virDomainProcessCapsFeatureTypeFromString((const char *)nodes[i]->name);
+        if (val < 0) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("unexpected capability feature '%s'"), nodes[i]->name);
+            goto error;
         }
 
-        sched->policy = policy;
-        sched->priority = priority;
+        if ((tmp = virXMLPropString(nodes[i], "state"))) {
+            if ((def->caps_features[val] = virTristateSwitchTypeFromString(tmp)) == -1) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("unknown state attribute '%s' of feature capability '%s'"),
+                               tmp, virDomainProcessCapsFeatureTypeToString(val));
+                goto error;
+            }
+            VIR_FREE(tmp);
+        } else {
+            def->caps_features[val] = VIR_TRISTATE_SWITCH_ON;
+        }
     }
-
+    VIR_FREE(nodes);
     return 0;
+
+ error:
+    return -1;
 }
 
 
 static int
-virDomainVcpuThreadSchedParse(xmlNodePtr node,
-                              virDomainDefPtr def)
+virDomainDefMaybeAddHostdevSCSIcontroller(virDomainDefPtr def)
 {
-    return virDomainThreadSchedParseHelper(node, "vcpus",
-                                           virDomainDefGetVcpuSched,
-                                           def);
-}
-
+    /* Look for any hostdev scsi dev */
+    size_t i;
+    int maxController = -1;
+    virDomainHostdevDefPtr hostdev;
+    int newModel = -1;
 
-static virDomainThreadSchedParamPtr
-virDomainDefGetIOThreadSched(virDomainDefPtr def,
-                             unsigned int iothread)
-{
-    virDomainIOThreadIDDefPtr iothrinfo;
+    for (i = 0; i < def->nhostdevs; i++) {
+        hostdev = def->hostdevs[i];
+        if (virHostdevIsSCSIDevice(hostdev) &&
+            (int)hostdev->info->addr.drive.controller > maxController) {
+            virDomainControllerDefPtr cont;
 
-    if (!(iothrinfo = virDomainIOThreadIDFind(def, iothread))) {
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                       _("Cannot find 'iothread' : %u"),
-                       iothread);
-        return NULL;
+            maxController = hostdev->info->addr.drive.controller;
+            /* We may be creating a new controller because this one is full.
+             * So let's grab the model from it and update the model we're
+             * going to add as long as this one isn't undefined. The premise
+             * being keeping the same controller model for all SCSI hostdevs. */
+            cont = virDomainDeviceFindSCSIController(def, &hostdev->info->addr.drive);
+            if (cont && cont->model != -1)
+                newModel = cont->model;
+        }
     }
 
-    return &iothrinfo->sched;
-}
+    if (maxController == -1)
+        return 0;
 
+    for (i = 0; i <= maxController; i++) {
+        if (virDomainDefMaybeAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_SCSI,
+                                           i, newModel) < 0)
+            return -1;
+    }
 
-static int
-virDomainIOThreadSchedParse(xmlNodePtr node,
-                            virDomainDefPtr def)
-{
-    return virDomainThreadSchedParseHelper(node, "iothreads",
-                                           virDomainDefGetIOThreadSched,
-                                           def);
+    return 0;
 }
 
-
 static int
-virDomainVcpuParse(virDomainDefPtr def,
-                   xmlXPathContextPtr ctxt,
-                   virDomainXMLOptionPtr xmlopt)
+virDomainLoaderDefParseXML(xmlNodePtr node,
+                           virDomainLoaderDefPtr loader,
+                           bool fwAutoSelect)
 {
-    int n;
-    xmlNodePtr vcpuNode;
-    size_t i;
-    unsigned int maxvcpus;
-    unsigned int vcpus;
-    g_autofree char *tmp = NULL;
-    g_autofree xmlNodePtr *nodes = NULL;
-
-    vcpus = maxvcpus = 1;
-
-    if ((vcpuNode = virXPathNode("./vcpu[1]", ctxt))) {
-        if ((tmp = virXMLNodeContentString(vcpuNode))) {
-            if (virStrToLong_ui(tmp, NULL, 10, &maxvcpus) < 0) {
-                virReportError(VIR_ERR_XML_ERROR, "%s",
-                               _("maximum vcpus count must be an integer"));
-                return -1;
-            }
-            VIR_FREE(tmp);
-        }
-
-        if ((tmp = virXMLPropString(vcpuNode, "current"))) {
-            if (virStrToLong_ui(tmp, NULL, 10, &vcpus) < 0) {
-                virReportError(VIR_ERR_XML_ERROR, "%s",
-                               _("current vcpus count must be an integer"));
-                return -1;
-            }
-            VIR_FREE(tmp);
-        } else {
-            vcpus = maxvcpus;
-        }
-
-        tmp = virXMLPropString(vcpuNode, "placement");
-        if (tmp) {
-            if ((def->placement_mode =
-                 virDomainCpuPlacementModeTypeFromString(tmp)) < 0) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("Unsupported CPU placement mode '%s'"),
-                               tmp);
-                return -1;
-            }
-            VIR_FREE(tmp);
-        } else {
-            def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC;
-        }
-
-        if (def->placement_mode != VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO) {
-            tmp = virXMLPropString(vcpuNode, "cpuset");
-            if (tmp) {
-                if (virBitmapParse(tmp, &def->cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0)
-                    return -1;
+    g_autofree char *readonly_str = NULL;
+    g_autofree char *secure_str = NULL;
+    g_autofree char *type_str = NULL;
 
-                if (virBitmapIsAllClear(def->cpumask)) {
-                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                   _("Invalid value of 'cpuset': %s"), tmp);
-                    return -1;
-                }
+    secure_str = virXMLPropString(node, "secure");
 
-                VIR_FREE(tmp);
-            }
-        }
+    if (!fwAutoSelect) {
+        readonly_str = virXMLPropString(node, "readonly");
+        type_str = virXMLPropString(node, "type");
+        loader->path = (char *) xmlNodeGetContent(node);
+        if (STREQ_NULLABLE(loader->path, ""))
+            VIR_FREE(loader->path);
     }
 
-    if (virDomainDefSetVcpusMax(def, maxvcpus, xmlopt) < 0)
+    if (readonly_str &&
+        (loader->readonly = virTristateBoolTypeFromString(readonly_str)) <= 0) {
+        virReportError(VIR_ERR_XML_DETAIL,
+                       _("unknown readonly value: %s"), readonly_str);
         return -1;
+    }
 
-    if ((n = virXPathNodeSet("./vcpus/vcpu", ctxt, &nodes)) < 0)
+    if (secure_str &&
+        (loader->secure = virTristateBoolTypeFromString(secure_str)) <= 0) {
+        virReportError(VIR_ERR_XML_DETAIL,
+                       _("unknown secure value: %s"), secure_str);
         return -1;
+    }
 
-    if (n) {
-        /* if individual vcpu states are provided take them as master */
-        def->individualvcpus = true;
-
-        for (i = 0; i < n; i++) {
-            virDomainVcpuDefPtr vcpu;
-            int state;
-            unsigned int id;
-            unsigned int order;
-
-            if (!(tmp = virXMLPropString(nodes[i], "id")) ||
-                virStrToLong_uip(tmp, NULL, 10, &id) < 0) {
-                virReportError(VIR_ERR_XML_ERROR, "%s",
-                               _("missing or invalid vcpu id"));
-                return -1;
-            }
-
-            VIR_FREE(tmp);
-
-            if (id >= def->maxvcpus) {
-                virReportError(VIR_ERR_XML_ERROR,
-                               _("vcpu id '%u' is out of range of maximum "
-                                 "vcpu count"), id);
-                return -1;
-            }
-
-            vcpu = virDomainDefGetVcpu(def, id);
-
-            if (!(tmp = virXMLPropString(nodes[i], "enabled"))) {
-                virReportError(VIR_ERR_XML_ERROR, "%s",
-                               _("missing vcpu enabled state"));
-                return -1;
-            }
-
-            if ((state = virTristateBoolTypeFromString(tmp)) < 0) {
-                virReportError(VIR_ERR_XML_ERROR,
-                               _("invalid vcpu 'enabled' value '%s'"), tmp);
-                return -1;
-            }
-            VIR_FREE(tmp);
-
-            vcpu->online = state == VIR_TRISTATE_BOOL_YES;
-
-            if ((tmp = virXMLPropString(nodes[i], "hotpluggable"))) {
-                int hotpluggable;
-                if ((hotpluggable = virTristateBoolTypeFromString(tmp)) < 0) {
-                    virReportError(VIR_ERR_XML_ERROR,
-                                   _("invalid vcpu 'hotpluggable' value '%s'"), tmp);
-                    return -1;
-                }
-                vcpu->hotpluggable = hotpluggable;
-                VIR_FREE(tmp);
-            }
-
-            if ((tmp = virXMLPropString(nodes[i], "order"))) {
-                if (virStrToLong_uip(tmp, NULL, 10, &order) < 0) {
-                    virReportError(VIR_ERR_XML_ERROR, "%s",
-                                   _("invalid vcpu order"));
-                    return -1;
-                }
-                vcpu->order = order;
-                VIR_FREE(tmp);
-            }
-        }
-    } else {
-        if (virDomainDefSetVcpus(def, vcpus) < 0)
+    if (type_str) {
+        int type;
+        if ((type = virDomainLoaderTypeFromString(type_str)) <= 0) {
+            virReportError(VIR_ERR_XML_DETAIL,
+                           _("unknown type value: %s"), type_str);
             return -1;
+        }
+        loader->type = type;
     }
 
     return 0;
@@ -19582,155 +19759,131 @@ virDomainVcpuParse(virDomainDefPtr def,
 
 
 static int
-virDomainDefParseBootOptions(virDomainDefPtr def,
-                             xmlXPathContextPtr ctxt)
+virDomainSchedulerParseCommonAttrs(xmlNodePtr node,
+                                   virProcessSchedPolicy *policy,
+                                   int *priority)
 {
-    char *name = NULL;
-    size_t i;
-    int n;
-    g_autofree xmlNodePtr *nodes = NULL;
+    int pol = 0;
     g_autofree char *tmp = NULL;
 
-    /*
-     * Booting options for different OS types....
-     *
-     *   - A bootloader (and optional kernel+initrd)  (xen)
-     *   - A kernel + initrd                          (xen)
-     *   - A boot device (and optional kernel+initrd) (hvm)
-     *   - An init script                             (exe)
-     */
+    if (!(tmp = virXMLPropString(node, "scheduler"))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Missing scheduler attribute"));
+        return -1;
+    }
 
-    if (def->os.type == VIR_DOMAIN_OSTYPE_EXE) {
-        def->os.init = virXPathString("string(./os/init[1])", ctxt);
-        def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt);
-        def->os.initdir = virXPathString("string(./os/initdir[1])", ctxt);
-        def->os.inituser = virXPathString("string(./os/inituser[1])", ctxt);
-        def->os.initgroup = virXPathString("string(./os/initgroup[1])", ctxt);
+    if ((pol = virProcessSchedPolicyTypeFromString(tmp)) <= 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Invalid scheduler attribute: '%s'"), tmp);
+        return -1;
+    }
+    *policy = pol;
 
-        if ((n = virXPathNodeSet("./os/initarg", ctxt, &nodes)) < 0)
-            return -1;
+    VIR_FREE(tmp);
 
-        if (VIR_ALLOC_N(def->os.initargv, n+1) < 0)
+    if (pol == VIR_PROC_POLICY_FIFO ||
+        pol == VIR_PROC_POLICY_RR) {
+        if (!(tmp = virXMLPropString(node, "priority"))) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Missing scheduler priority"));
             return -1;
-        for (i = 0; i < n; i++) {
-            if (!nodes[i]->children ||
-                !nodes[i]->children->content) {
-                virReportError(VIR_ERR_XML_ERROR, "%s",
-                               _("No data supplied for <initarg> element"));
-                return -1;
-            }
-            def->os.initargv[i] = g_strdup((const char *)nodes[i]->children->content);
         }
-        def->os.initargv[n] = NULL;
-        VIR_FREE(nodes);
 
-        if ((n = virXPathNodeSet("./os/initenv", ctxt, &nodes)) < 0)
+        if (virStrToLong_i(tmp, NULL, 10, priority) < 0) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Invalid value for element priority"));
             return -1;
+        }
+    }
 
-        if (VIR_ALLOC_N(def->os.initenv, n+1) < 0)
-            return -1;
-        for (i = 0; i < n; i++) {
-            if (!(name = virXMLPropString(nodes[i], "name"))) {
-                virReportError(VIR_ERR_XML_ERROR, "%s",
-                                _("No name supplied for <initenv> element"));
-                return -1;
-            }
+    return 0;
+}
 
-            if (!nodes[i]->children ||
-                !nodes[i]->children->content) {
-                virReportError(VIR_ERR_XML_ERROR,
-                               _("No value supplied for <initenv name='%s'> element"),
-                               name);
-                return -1;
-            }
 
-            if (VIR_ALLOC(def->os.initenv[i]) < 0)
-                return -1;
+static int
+virDomainEmulatorSchedParse(xmlNodePtr node,
+                            virDomainDefPtr def)
+{
+    g_autofree virDomainThreadSchedParamPtr sched = NULL;
 
-            def->os.initenv[i]->name = name;
-            def->os.initenv[i]->value = g_strdup((const char *)nodes[i]->children->content);
-        }
-        def->os.initenv[n] = NULL;
-        VIR_FREE(nodes);
-    }
+    if (VIR_ALLOC(sched) < 0)
+        return -1;
 
-    if (def->os.type == VIR_DOMAIN_OSTYPE_XEN ||
-        def->os.type == VIR_DOMAIN_OSTYPE_XENPVH ||
-        def->os.type == VIR_DOMAIN_OSTYPE_HVM ||
-        def->os.type == VIR_DOMAIN_OSTYPE_UML) {
-        g_autofree char *firmware = NULL;
-        xmlNodePtr loader_node;
+    if (virDomainSchedulerParseCommonAttrs(node,
+                                           &sched->policy,
+                                           &sched->priority) < 0)
+        return -1;
 
-        def->os.kernel = virXPathString("string(./os/kernel[1])", ctxt);
-        def->os.initrd = virXPathString("string(./os/initrd[1])", ctxt);
-        def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt);
-        def->os.dtb = virXPathString("string(./os/dtb[1])", ctxt);
-        def->os.root = virXPathString("string(./os/root[1])", ctxt);
+    def->cputune.emulatorsched = g_steal_pointer(&sched);
+    return 0;
+}
 
-        if (def->os.type == VIR_DOMAIN_OSTYPE_HVM &&
-            (firmware = virXPathString("string(./os/@firmware)", ctxt))) {
-            int fw = virDomainOsDefFirmwareTypeFromString(firmware);
 
-            if (fw <= 0) {
-                virReportError(VIR_ERR_XML_ERROR,
-                               _("unknown firmware value %s"),
-                               firmware);
-                return -1;
-            }
+static virBitmapPtr
+virDomainSchedulerParse(xmlNodePtr node,
+                        const char *name,
+                        virProcessSchedPolicy *policy,
+                        int *priority)
+{
+    virBitmapPtr ret = NULL;
+    g_autofree char *tmp = NULL;
 
-            def->os.firmware = fw;
-        }
+    if (!(tmp = virXMLPropString(node, name))) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Missing attribute '%s' in element '%sched'"),
+                       name, name);
+        goto error;
+    }
 
-        if ((loader_node = virXPathNode("./os/loader[1]", ctxt))) {
-            const bool fwAutoSelect = def->os.firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_NONE;
+    if (virBitmapParse(tmp, &ret, VIR_DOMAIN_CPUMASK_LEN) < 0)
+        goto error;
 
-            if (VIR_ALLOC(def->os.loader) < 0)
-                return -1;
+    if (virBitmapIsAllClear(ret)) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("'%s' scheduler bitmap '%s' is empty"),
+                       name, tmp);
+        goto error;
+    }
 
-            if (virDomainLoaderDefParseXML(loader_node,
-                                           def->os.loader,
-                                           fwAutoSelect) < 0)
-                return -1;
+    if (virDomainSchedulerParseCommonAttrs(node, policy, priority) < 0)
+        goto error;
 
-            def->os.loader->nvram = virXPathString("string(./os/nvram[1])", ctxt);
-            if (!fwAutoSelect)
-                def->os.loader->templt = virXPathString("string(./os/nvram[1]/@template)", ctxt);
-        }
-    }
+    return ret;
 
-    if (def->os.type == VIR_DOMAIN_OSTYPE_HVM) {
-        if ((n = virXPathNodeSet("./os/acpi/table", ctxt, &nodes)) < 0)
-            return -1;
+ error:
+    virBitmapFree(ret);
+    return NULL;
+}
 
-        if (n > 1) {
-            virReportError(VIR_ERR_XML_ERROR, "%s",
-                           _("Only one acpi table is supported"));
-            return -1;
-        }
 
-        if (n == 1) {
-            tmp = virXMLPropString(nodes[0], "type");
+static int
+virDomainThreadSchedParseHelper(xmlNodePtr node,
+                                const char *name,
+                                virDomainThreadSchedParamPtr (*func)(virDomainDefPtr, unsigned int),
+                                virDomainDefPtr def)
+{
+    ssize_t next = -1;
+    virDomainThreadSchedParamPtr sched = NULL;
+    virProcessSchedPolicy policy = 0;
+    int priority = 0;
+    g_autoptr(virBitmap) map = NULL;
 
-            if (!tmp) {
-                virReportError(VIR_ERR_XML_ERROR, "%s",
-                               _("Missing acpi table type"));
-                return -1;
-            }
+    if (!(map = virDomainSchedulerParse(node, name, &policy, &priority)))
+        return -1;
 
-            if (STREQ_NULLABLE(tmp, "slic")) {
-                VIR_FREE(tmp);
-                tmp = virXMLNodeContentString(nodes[0]);
-                def->os.slic_table = virFileSanitizePath(tmp);
-            } else {
-                virReportError(VIR_ERR_XML_ERROR,
-                               _("Unknown acpi table type: %s"),
-                               tmp);
-                return -1;
-            }
-        }
+    while ((next = virBitmapNextSetBit(map, next)) > -1) {
+        if (!(sched = func(def, next)))
+            return -1;
 
-        if (virDomainDefParseBootXML(ctxt, def) < 0)
+        if (sched->policy != VIR_PROC_POLICY_NONE) {
+            virReportError(VIR_ERR_XML_DETAIL,
+                           _("%ssched attributes 'vcpus' must not overlap"),
+                           name);
             return -1;
+        }
+
+        sched->policy = policy;
+        sched->priority = priority;
     }
 
     return 0;
@@ -19738,1586 +19891,1454 @@ virDomainDefParseBootOptions(virDomainDefPtr def,
 
 
 static int
-virDomainResctrlParseVcpus(virDomainDefPtr def,
-                           xmlNodePtr node,
-                           virBitmapPtr *vcpus)
+virDomainVcpuThreadSchedParse(xmlNodePtr node,
+                              virDomainDefPtr def)
 {
-    g_autofree char *vcpus_str = NULL;
+    return virDomainThreadSchedParseHelper(node, "vcpus",
+                                           virDomainDefGetVcpuSched,
+                                           def);
+}
 
-    vcpus_str = virXMLPropString(node, "vcpus");
-    if (!vcpus_str) {
-        virReportError(VIR_ERR_XML_ERROR, _("Missing %s attribute 'vcpus'"),
-                       node->name);
-        return -1;
-    }
-    if (virBitmapParse(vcpus_str, vcpus, VIR_DOMAIN_CPUMASK_LEN) < 0) {
-        virReportError(VIR_ERR_XML_ERROR,
-                       _("Invalid %s attribute 'vcpus' value '%s'"),
-                       node->name, vcpus_str);
-        return -1;
-    }
 
-    /* We need to limit the bitmap to number of vCPUs.  If there's nothing left,
-     * then we can just clean up and return 0 immediately */
-    virBitmapShrink(*vcpus, def->maxvcpus);
+static virDomainThreadSchedParamPtr
+virDomainDefGetIOThreadSched(virDomainDefPtr def,
+                             unsigned int iothread)
+{
+    virDomainIOThreadIDDefPtr iothrinfo;
 
-    return 0;
+    if (!(iothrinfo = virDomainIOThreadIDFind(def, iothread))) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("Cannot find 'iothread' : %u"),
+                       iothread);
+        return NULL;
+    }
+
+    return &iothrinfo->sched;
 }
 
 
 static int
-virDomainResctrlVcpuMatch(virDomainDefPtr def,
-                          virBitmapPtr vcpus,
-                          virDomainResctrlDefPtr *resctrl)
+virDomainIOThreadSchedParse(xmlNodePtr node,
+                            virDomainDefPtr def)
 {
-    ssize_t i = 0;
-
-    for (i = 0; i < def->nresctrls; i++) {
-        /* vcpus group has been created, directly use the existing one.
-         * Just updating memory allocation information of that group
-         */
-        if (virBitmapEqual(def->resctrls[i]->vcpus, vcpus)) {
-            *resctrl = def->resctrls[i];
-            break;
-        }
-        if (virBitmapOverlaps(def->resctrls[i]->vcpus, vcpus)) {
-            virReportError(VIR_ERR_XML_ERROR, "%s",
-                           _("Overlapping vcpus in resctrls"));
-            return -1;
-        }
-    }
-    return 0;
+    return virDomainThreadSchedParseHelper(node, "iothreads",
+                                           virDomainDefGetIOThreadSched,
+                                           def);
 }
 
 
 static int
-virDomainCachetuneDefParseCache(xmlXPathContextPtr ctxt,
-                                xmlNodePtr node,
-                                virResctrlAllocPtr alloc)
+virDomainVcpuParse(virDomainDefPtr def,
+                   xmlXPathContextPtr ctxt,
+                   virDomainXMLOptionPtr xmlopt)
 {
-    VIR_XPATH_NODE_AUTORESTORE(ctxt);
-    unsigned int level;
-    unsigned int cache;
-    int type;
-    unsigned long long size;
+    int n;
+    xmlNodePtr vcpuNode;
+    size_t i;
+    unsigned int maxvcpus;
+    unsigned int vcpus;
     g_autofree char *tmp = NULL;
+    g_autofree xmlNodePtr *nodes = NULL;
 
-    ctxt->node = node;
+    vcpus = maxvcpus = 1;
 
-    tmp = virXMLPropString(node, "id");
-    if (!tmp) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("Missing cachetune attribute 'id'"));
-        return -1;
-    }
-    if (virStrToLong_uip(tmp, NULL, 10, &cache) < 0) {
-        virReportError(VIR_ERR_XML_ERROR,
-                       _("Invalid cachetune attribute 'id' value '%s'"),
-                       tmp);
-        return -1;
-    }
-    VIR_FREE(tmp);
+    if ((vcpuNode = virXPathNode("./vcpu[1]", ctxt))) {
+        if ((tmp = virXMLNodeContentString(vcpuNode))) {
+            if (virStrToLong_ui(tmp, NULL, 10, &maxvcpus) < 0) {
+                virReportError(VIR_ERR_XML_ERROR, "%s",
+                               _("maximum vcpus count must be an integer"));
+                return -1;
+            }
+            VIR_FREE(tmp);
+        }
 
-    tmp = virXMLPropString(node, "level");
-    if (!tmp) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("Missing cachetune attribute 'level'"));
-        return -1;
-    }
-    if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) {
-        virReportError(VIR_ERR_XML_ERROR,
-                       _("Invalid cachetune attribute 'level' value '%s'"),
-                       tmp);
-        return -1;
-    }
-    VIR_FREE(tmp);
+        if ((tmp = virXMLPropString(vcpuNode, "current"))) {
+            if (virStrToLong_ui(tmp, NULL, 10, &vcpus) < 0) {
+                virReportError(VIR_ERR_XML_ERROR, "%s",
+                               _("current vcpus count must be an integer"));
+                return -1;
+            }
+            VIR_FREE(tmp);
+        } else {
+            vcpus = maxvcpus;
+        }
 
-    tmp = virXMLPropString(node, "type");
-    if (!tmp) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("Missing cachetune attribute 'type'"));
-        return -1;
-    }
-    type = virCacheTypeFromString(tmp);
-    if (type < 0) {
-        virReportError(VIR_ERR_XML_ERROR,
-                       _("Invalid cachetune attribute 'type' value '%s'"),
-                       tmp);
-        return -1;
+        tmp = virXMLPropString(vcpuNode, "placement");
+        if (tmp) {
+            if ((def->placement_mode =
+                 virDomainCpuPlacementModeTypeFromString(tmp)) < 0) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("Unsupported CPU placement mode '%s'"),
+                               tmp);
+                return -1;
+            }
+            VIR_FREE(tmp);
+        } else {
+            def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC;
+        }
+
+        if (def->placement_mode != VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO) {
+            tmp = virXMLPropString(vcpuNode, "cpuset");
+            if (tmp) {
+                if (virBitmapParse(tmp, &def->cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0)
+                    return -1;
+
+                if (virBitmapIsAllClear(def->cpumask)) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                   _("Invalid value of 'cpuset': %s"), tmp);
+                    return -1;
+                }
+
+                VIR_FREE(tmp);
+            }
+        }
     }
 
-    if (virDomainParseScaledValue("./@size", "./@unit",
-                                  ctxt, &size, 1024,
-                                  ULLONG_MAX, true) < 0)
+    if (virDomainDefSetVcpusMax(def, maxvcpus, xmlopt) < 0)
         return -1;
 
-    if (virResctrlAllocSetCacheSize(alloc, level, type, cache, size) < 0)
+    if ((n = virXPathNodeSet("./vcpus/vcpu", ctxt, &nodes)) < 0)
         return -1;
 
-    return 0;
-}
+    if (n) {
+        /* if individual vcpu states are provided take them as master */
+        def->individualvcpus = true;
 
+        for (i = 0; i < n; i++) {
+            virDomainVcpuDefPtr vcpu;
+            int state;
+            unsigned int id;
+            unsigned int order;
 
-/* Checking if the monitor's vcpus and tag is conflicted with existing
- * allocation and monitors.
- *
- * Returns 1 if @monitor->vcpus equals to @resctrl->vcpus, then the monitor
- * will share the underlying resctrl group with @resctrl->alloc. Returns -1
- * if any conflict found. Returns 0 if no conflict and @monitor->vcpus is
- * not equal  to @resctrl->vcpus.
- */
-static int
-virDomainResctrlValidateMonitor(virDomainResctrlDefPtr resctrl,
-                                virDomainResctrlMonDefPtr monitor)
-{
-    size_t i = 0;
-    int vcpu = -1;
-    bool vcpus_overlap_any = false;
-    bool vcpus_equal_to_resctrl = false;
-    bool vcpus_overlap_no_resctrl = false;
-    bool default_alloc_monitor = virResctrlAllocIsEmpty(resctrl->alloc);
+            if (!(tmp = virXMLPropString(nodes[i], "id")) ||
+                virStrToLong_uip(tmp, NULL, 10, &id) < 0) {
+                virReportError(VIR_ERR_XML_ERROR, "%s",
+                               _("missing or invalid vcpu id"));
+                return -1;
+            }
 
-    if (virBitmapIsAllClear(monitor->vcpus)) {
-        virReportError(VIR_ERR_INVALID_ARG, "%s",
-                       _("vcpus is empty"));
-        return -1;
-    }
+            VIR_FREE(tmp);
 
-    while ((vcpu = virBitmapNextSetBit(monitor->vcpus, vcpu)) >= 0) {
-        if (!virBitmapIsBitSet(resctrl->vcpus, vcpu)) {
-            virReportError(VIR_ERR_INVALID_ARG, "%s",
-                           _("Monitor vcpus conflicts with allocation"));
-            return -1;
-        }
-    }
+            if (id >= def->maxvcpus) {
+                virReportError(VIR_ERR_XML_ERROR,
+                               _("vcpu id '%u' is out of range of maximum "
+                                 "vcpu count"), id);
+                return -1;
+            }
 
-    vcpus_equal_to_resctrl = virBitmapEqual(monitor->vcpus, resctrl->vcpus);
+            vcpu = virDomainDefGetVcpu(def, id);
 
-    for (i = 0; i < resctrl->nmonitors; i++) {
-        if (virBitmapEqual(monitor->vcpus, resctrl->monitors[i]->vcpus)) {
-            if (monitor->tag != resctrl->monitors[i]->tag) {
-                continue;
-            } else {
-                virReportError(VIR_ERR_INVALID_ARG, "%s",
-                               _("Identical vcpus found in same type monitors"));
+            if (!(tmp = virXMLPropString(nodes[i], "enabled"))) {
+                virReportError(VIR_ERR_XML_ERROR, "%s",
+                               _("missing vcpu enabled state"));
                 return -1;
             }
-        }
 
-        if (virBitmapOverlaps(monitor->vcpus, resctrl->monitors[i]->vcpus))
-            vcpus_overlap_any = true;
+            if ((state = virTristateBoolTypeFromString(tmp)) < 0) {
+                virReportError(VIR_ERR_XML_ERROR,
+                               _("invalid vcpu 'enabled' value '%s'"), tmp);
+                return -1;
+            }
+            VIR_FREE(tmp);
 
-        if (vcpus_equal_to_resctrl ||
-            virBitmapEqual(resctrl->monitors[i]->vcpus, resctrl->vcpus))
-            continue;
+            vcpu->online = state == VIR_TRISTATE_BOOL_YES;
 
-        if (virBitmapOverlaps(monitor->vcpus, resctrl->monitors[i]->vcpus))
-            vcpus_overlap_no_resctrl = true;
-    }
+            if ((tmp = virXMLPropString(nodes[i], "hotpluggable"))) {
+                int hotpluggable;
+                if ((hotpluggable = virTristateBoolTypeFromString(tmp)) < 0) {
+                    virReportError(VIR_ERR_XML_ERROR,
+                                   _("invalid vcpu 'hotpluggable' value '%s'"), tmp);
+                    return -1;
+                }
+                vcpu->hotpluggable = hotpluggable;
+                VIR_FREE(tmp);
+            }
 
-    if (vcpus_overlap_no_resctrl ||
-        (default_alloc_monitor && vcpus_overlap_any)) {
-        virReportError(VIR_ERR_INVALID_ARG, "%s",
-                       _("vcpus overlaps in resctrl groups"));
-        return -1;
+            if ((tmp = virXMLPropString(nodes[i], "order"))) {
+                if (virStrToLong_uip(tmp, NULL, 10, &order) < 0) {
+                    virReportError(VIR_ERR_XML_ERROR, "%s",
+                                   _("invalid vcpu order"));
+                    return -1;
+                }
+                vcpu->order = order;
+                VIR_FREE(tmp);
+            }
+        }
+    } else {
+        if (virDomainDefSetVcpus(def, vcpus) < 0)
+            return -1;
     }
 
-    if (vcpus_equal_to_resctrl && !default_alloc_monitor)
-        return 1;
-
     return 0;
 }
 
 
-#define VIR_DOMAIN_RESCTRL_MONITOR_CACHELEVEL 3
-
 static int
-virDomainResctrlMonDefParse(virDomainDefPtr def,
-                            xmlXPathContextPtr ctxt,
-                            xmlNodePtr node,
-                            virResctrlMonitorType tag,
-                            virDomainResctrlDefPtr resctrl)
+virDomainDefParseBootOptions(virDomainDefPtr def,
+                             xmlXPathContextPtr ctxt)
 {
-    virDomainResctrlMonDefPtr domresmon = NULL;
-    VIR_XPATH_NODE_AUTORESTORE(ctxt);
-    unsigned int level = 0;
-    size_t i = 0;
-    int n = 0;
-    int rv = -1;
-    int ret = -1;
+    char *name = NULL;
+    size_t i;
+    int n;
     g_autofree xmlNodePtr *nodes = NULL;
     g_autofree char *tmp = NULL;
-    g_autofree char *id = NULL;
-
-    ctxt->node = node;
 
-    if ((n = virXPathNodeSet("./monitor", ctxt, &nodes)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("Cannot extract monitor nodes"));
-        goto cleanup;
-    }
+    /*
+     * Booting options for different OS types....
+     *
+     *   - A bootloader (and optional kernel+initrd)  (xen)
+     *   - A kernel + initrd                          (xen)
+     *   - A boot device (and optional kernel+initrd) (hvm)
+     *   - An init script                             (exe)
+     */
 
-    for (i = 0; i < n; i++) {
-        if (VIR_ALLOC(domresmon) < 0)
-            goto cleanup;
+    if (def->os.type == VIR_DOMAIN_OSTYPE_EXE) {
+        def->os.init = virXPathString("string(./os/init[1])", ctxt);
+        def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt);
+        def->os.initdir = virXPathString("string(./os/initdir[1])", ctxt);
+        def->os.inituser = virXPathString("string(./os/inituser[1])", ctxt);
+        def->os.initgroup = virXPathString("string(./os/initgroup[1])", ctxt);
 
-        domresmon->tag = tag;
+        if ((n = virXPathNodeSet("./os/initarg", ctxt, &nodes)) < 0)
+            return -1;
 
-        domresmon->instance = virResctrlMonitorNew();
-        if (!domresmon->instance) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("Could not create monitor"));
-            goto cleanup;
+        if (VIR_ALLOC_N(def->os.initargv, n+1) < 0)
+            return -1;
+        for (i = 0; i < n; i++) {
+            if (!nodes[i]->children ||
+                !nodes[i]->children->content) {
+                virReportError(VIR_ERR_XML_ERROR, "%s",
+                               _("No data supplied for <initarg> element"));
+                return -1;
+            }
+            def->os.initargv[i] = g_strdup((const char *)nodes[i]->children->content);
         }
+        def->os.initargv[n] = NULL;
+        VIR_FREE(nodes);
 
-        if (tag == VIR_RESCTRL_MONITOR_TYPE_CACHE) {
-            tmp = virXMLPropString(nodes[i], "level");
-            if (!tmp) {
+        if ((n = virXPathNodeSet("./os/initenv", ctxt, &nodes)) < 0)
+            return -1;
+
+        if (VIR_ALLOC_N(def->os.initenv, n+1) < 0)
+            return -1;
+        for (i = 0; i < n; i++) {
+            if (!(name = virXMLPropString(nodes[i], "name"))) {
                 virReportError(VIR_ERR_XML_ERROR, "%s",
-                               _("Missing monitor attribute 'level'"));
-                goto cleanup;
+                                _("No name supplied for <initenv> element"));
+                return -1;
             }
 
-            if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) {
+            if (!nodes[i]->children ||
+                !nodes[i]->children->content) {
                 virReportError(VIR_ERR_XML_ERROR,
-                               _("Invalid monitor attribute 'level' value '%s'"),
-                               tmp);
-                goto cleanup;
+                               _("No value supplied for <initenv name='%s'> element"),
+                               name);
+                return -1;
             }
 
-            if (level != VIR_DOMAIN_RESCTRL_MONITOR_CACHELEVEL) {
+            if (VIR_ALLOC(def->os.initenv[i]) < 0)
+                return -1;
+
+            def->os.initenv[i]->name = name;
+            def->os.initenv[i]->value = g_strdup((const char *)nodes[i]->children->content);
+        }
+        def->os.initenv[n] = NULL;
+        VIR_FREE(nodes);
+    }
+
+    if (def->os.type == VIR_DOMAIN_OSTYPE_XEN ||
+        def->os.type == VIR_DOMAIN_OSTYPE_XENPVH ||
+        def->os.type == VIR_DOMAIN_OSTYPE_HVM ||
+        def->os.type == VIR_DOMAIN_OSTYPE_UML) {
+        g_autofree char *firmware = NULL;
+        xmlNodePtr loader_node;
+
+        def->os.kernel = virXPathString("string(./os/kernel[1])", ctxt);
+        def->os.initrd = virXPathString("string(./os/initrd[1])", ctxt);
+        def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt);
+        def->os.dtb = virXPathString("string(./os/dtb[1])", ctxt);
+        def->os.root = virXPathString("string(./os/root[1])", ctxt);
+
+        if (def->os.type == VIR_DOMAIN_OSTYPE_HVM &&
+            (firmware = virXPathString("string(./os/@firmware)", ctxt))) {
+            int fw = virDomainOsDefFirmwareTypeFromString(firmware);
+
+            if (fw <= 0) {
                 virReportError(VIR_ERR_XML_ERROR,
-                               _("Invalid monitor cache level '%d'"),
-                               level);
-                goto cleanup;
+                               _("unknown firmware value %s"),
+                               firmware);
+                return -1;
             }
 
-            VIR_FREE(tmp);
+            def->os.firmware = fw;
         }
 
-        if (virDomainResctrlParseVcpus(def, nodes[i], &domresmon->vcpus) < 0)
-            goto cleanup;
+        if ((loader_node = virXPathNode("./os/loader[1]", ctxt))) {
+            const bool fwAutoSelect = def->os.firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_NONE;
 
-        rv = virDomainResctrlValidateMonitor(resctrl, domresmon);
-        if (rv < 0)
-            goto cleanup;
+            if (VIR_ALLOC(def->os.loader) < 0)
+                return -1;
 
-        /* If monitor's vcpu list is identical to the vcpu list of the
-         * associated allocation, set monitor's id to the same value
-         * as the allocation. */
-        if (rv == 1) {
-            const char *alloc_id = virResctrlAllocGetID(resctrl->alloc);
+            if (virDomainLoaderDefParseXML(loader_node,
+                                           def->os.loader,
+                                           fwAutoSelect) < 0)
+                return -1;
 
-            id = g_strdup(alloc_id);
-        } else {
-            if (!(tmp = virBitmapFormat(domresmon->vcpus)))
-                goto cleanup;
+            def->os.loader->nvram = virXPathString("string(./os/nvram[1])", ctxt);
+            if (!fwAutoSelect)
+                def->os.loader->templt = virXPathString("string(./os/nvram[1]/@template)", ctxt);
+        }
+    }
 
-            id = g_strdup_printf("vcpus_%s", tmp);
+    if (def->os.type == VIR_DOMAIN_OSTYPE_HVM) {
+        if ((n = virXPathNodeSet("./os/acpi/table", ctxt, &nodes)) < 0)
+            return -1;
+
+        if (n > 1) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Only one acpi table is supported"));
+            return -1;
         }
 
-        virResctrlMonitorSetAlloc(domresmon->instance, resctrl->alloc);
+        if (n == 1) {
+            tmp = virXMLPropString(nodes[0], "type");
 
-        if (virResctrlMonitorSetID(domresmon->instance, id) < 0)
-            goto cleanup;
+            if (!tmp) {
+                virReportError(VIR_ERR_XML_ERROR, "%s",
+                               _("Missing acpi table type"));
+                return -1;
+            }
 
-        if (VIR_APPEND_ELEMENT(resctrl->monitors,
-                               resctrl->nmonitors,
-                               domresmon) < 0)
-            goto cleanup;
+            if (STREQ_NULLABLE(tmp, "slic")) {
+                VIR_FREE(tmp);
+                tmp = virXMLNodeContentString(nodes[0]);
+                def->os.slic_table = virFileSanitizePath(tmp);
+            } else {
+                virReportError(VIR_ERR_XML_ERROR,
+                               _("Unknown acpi table type: %s"),
+                               tmp);
+                return -1;
+            }
+        }
 
-        VIR_FREE(id);
-        VIR_FREE(tmp);
+        if (virDomainDefParseBootXML(ctxt, def) < 0)
+            return -1;
     }
 
-    ret = 0;
- cleanup:
-    virDomainResctrlMonDefFree(domresmon);
-    return ret;
+    return 0;
 }
 
 
-static virDomainResctrlDefPtr
-virDomainResctrlNew(xmlNodePtr node,
-                    virResctrlAllocPtr alloc,
-                    virBitmapPtr vcpus,
-                    unsigned int flags)
+static int
+virDomainResctrlParseVcpus(virDomainDefPtr def,
+                           xmlNodePtr node,
+                           virBitmapPtr *vcpus)
 {
-    virDomainResctrlDefPtr resctrl = NULL;
-    virDomainResctrlDefPtr ret = NULL;
     g_autofree char *vcpus_str = NULL;
-    g_autofree char *alloc_id = NULL;
 
-    /* We need to format it back because we need to be consistent in the naming
-     * even when users specify some "sub-optimal" string there. */
-    vcpus_str = virBitmapFormat(vcpus);
-    if (!vcpus_str)
-        return NULL;
+    vcpus_str = virXMLPropString(node, "vcpus");
+    if (!vcpus_str) {
+        virReportError(VIR_ERR_XML_ERROR, _("Missing %s attribute 'vcpus'"),
+                       node->name);
+        return -1;
+    }
+    if (virBitmapParse(vcpus_str, vcpus, VIR_DOMAIN_CPUMASK_LEN) < 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Invalid %s attribute 'vcpus' value '%s'"),
+                       node->name, vcpus_str);
+        return -1;
+    }
 
-    if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE))
-        alloc_id = virXMLPropString(node, "id");
+    /* We need to limit the bitmap to number of vCPUs.  If there's nothing left,
+     * then we can just clean up and return 0 immediately */
+    virBitmapShrink(*vcpus, def->maxvcpus);
 
-    if (!alloc_id) {
-        /* The number of allocations is limited and the directory structure is flat,
-         * not hierarchical, so we need to have all same allocations in one
-         * directory, so it's nice to have it named appropriately.  For now it's
-         * 'vcpus_...' but it's designed in order for it to be changeable in the
-         * future (it's part of the status XML). */
-        alloc_id = g_strdup_printf("vcpus_%s", vcpus_str);
-    }
+    return 0;
+}
 
-    if (virResctrlAllocSetID(alloc, alloc_id) < 0)
-        goto cleanup;
 
-    if (VIR_ALLOC(resctrl) < 0)
-        goto cleanup;
+static int
+virDomainResctrlVcpuMatch(virDomainDefPtr def,
+                          virBitmapPtr vcpus,
+                          virDomainResctrlDefPtr *resctrl)
+{
+    ssize_t i = 0;
 
-    if (!(resctrl->vcpus = virBitmapNewCopy(vcpus))) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("failed to copy 'vcpus'"));
-        goto cleanup;
+    for (i = 0; i < def->nresctrls; i++) {
+        /* vcpus group has been created, directly use the existing one.
+         * Just updating memory allocation information of that group
+         */
+        if (virBitmapEqual(def->resctrls[i]->vcpus, vcpus)) {
+            *resctrl = def->resctrls[i];
+            break;
+        }
+        if (virBitmapOverlaps(def->resctrls[i]->vcpus, vcpus)) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Overlapping vcpus in resctrls"));
+            return -1;
+        }
     }
-
-    resctrl->alloc = virObjectRef(alloc);
-
-    ret = g_steal_pointer(&resctrl);
- cleanup:
-    virDomainResctrlDefFree(resctrl);
-    return ret;
+    return 0;
 }
 
 
 static int
-virDomainCachetuneDefParse(virDomainDefPtr def,
-                           xmlXPathContextPtr ctxt,
-                           xmlNodePtr node,
-                           unsigned int flags)
+virDomainCachetuneDefParseCache(xmlXPathContextPtr ctxt,
+                                xmlNodePtr node,
+                                virResctrlAllocPtr alloc)
 {
     VIR_XPATH_NODE_AUTORESTORE(ctxt);
-    virDomainResctrlDefPtr resctrl = NULL;
-    ssize_t i = 0;
-    int n;
-    int ret = -1;
-    g_autoptr(virBitmap) vcpus = NULL;
-    g_autofree xmlNodePtr *nodes = NULL;
-    g_autoptr(virResctrlAlloc) alloc = NULL;
+    unsigned int level;
+    unsigned int cache;
+    int type;
+    unsigned long long size;
+    g_autofree char *tmp = NULL;
 
     ctxt->node = node;
 
-    if (virDomainResctrlParseVcpus(def, node, &vcpus) < 0)
+    tmp = virXMLPropString(node, "id");
+    if (!tmp) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("Missing cachetune attribute 'id'"));
         return -1;
-
-    if (virBitmapIsAllClear(vcpus))
-        return 0;
-
-    if ((n = virXPathNodeSet("./cache", ctxt, &nodes)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("Cannot extract cache nodes under cachetune"));
+    }
+    if (virStrToLong_uip(tmp, NULL, 10, &cache) < 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Invalid cachetune attribute 'id' value '%s'"),
+                       tmp);
         return -1;
     }
+    VIR_FREE(tmp);
 
-    if (virDomainResctrlVcpuMatch(def, vcpus, &resctrl) < 0)
+    tmp = virXMLPropString(node, "level");
+    if (!tmp) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("Missing cachetune attribute 'level'"));
+        return -1;
+    }
+    if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Invalid cachetune attribute 'level' value '%s'"),
+                       tmp);
         return -1;
+    }
+    VIR_FREE(tmp);
 
-    if (resctrl) {
+    tmp = virXMLPropString(node, "type");
+    if (!tmp) {
         virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("Identical vcpus in cachetunes found"));
+                       _("Missing cachetune attribute 'type'"));
         return -1;
     }
-
-    if (!(alloc = virResctrlAllocNew()))
+    type = virCacheTypeFromString(tmp);
+    if (type < 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Invalid cachetune attribute 'type' value '%s'"),
+                       tmp);
         return -1;
-
-    for (i = 0; i < n; i++) {
-        if (virDomainCachetuneDefParseCache(ctxt, nodes[i], alloc) < 0)
-            return -1;
     }
 
-    if (!(resctrl = virDomainResctrlNew(node, alloc, vcpus, flags)))
+    if (virDomainParseScaledValue("./@size", "./@unit",
+                                  ctxt, &size, 1024,
+                                  ULLONG_MAX, true) < 0)
         return -1;
 
-    if (virDomainResctrlMonDefParse(def, ctxt, node,
-                                    VIR_RESCTRL_MONITOR_TYPE_CACHE,
-                                    resctrl) < 0)
-        goto cleanup;
-
-    /* If no <cache> element or <monitor> element in <cachetune>, do not
-     * append any resctrl element */
-    if (!resctrl->nmonitors && n == 0) {
-        ret = 0;
-        goto cleanup;
-    }
-
-    if (VIR_APPEND_ELEMENT(def->resctrls, def->nresctrls, resctrl) < 0)
-        goto cleanup;
+    if (virResctrlAllocSetCacheSize(alloc, level, type, cache, size) < 0)
+        return -1;
 
-    ret = 0;
- cleanup:
-    virDomainResctrlDefFree(resctrl);
-    return ret;
+    return 0;
 }
 
 
+/* Checking if the monitor's vcpus and tag is conflicted with existing
+ * allocation and monitors.
+ *
+ * Returns 1 if @monitor->vcpus equals to @resctrl->vcpus, then the monitor
+ * will share the underlying resctrl group with @resctrl->alloc. Returns -1
+ * if any conflict found. Returns 0 if no conflict and @monitor->vcpus is
+ * not equal  to @resctrl->vcpus.
+ */
 static int
-virDomainDefParseCaps(virDomainDefPtr def,
-                      xmlXPathContextPtr ctxt,
-                      virDomainXMLOptionPtr xmlopt)
+virDomainResctrlValidateMonitor(virDomainResctrlDefPtr resctrl,
+                                virDomainResctrlMonDefPtr monitor)
 {
-    g_autofree char *virttype = NULL;
-    g_autofree char *arch = NULL;
-    g_autofree char *ostype = NULL;
-
-    virttype = virXPathString("string(./@type)", ctxt);
-    ostype = virXPathString("string(./os/type[1])", ctxt);
-    arch = virXPathString("string(./os/type[1]/@arch)", ctxt);
-
-    def->os.bootloader = virXPathString("string(./bootloader)", ctxt);
-    def->os.bootloaderArgs = virXPathString("string(./bootloader_args)", ctxt);
-    def->os.machine = virXPathString("string(./os/type[1]/@machine)", ctxt);
-    def->emulator = virXPathString("string(./devices/emulator[1])", ctxt);
+    size_t i = 0;
+    int vcpu = -1;
+    bool vcpus_overlap_any = false;
+    bool vcpus_equal_to_resctrl = false;
+    bool vcpus_overlap_no_resctrl = false;
+    bool default_alloc_monitor = virResctrlAllocIsEmpty(resctrl->alloc);
 
-    if (!virttype) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("missing domain type attribute"));
-        return -1;
-    }
-    if ((def->virtType = virDomainVirtTypeFromString(virttype)) < 0) {
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                       _("invalid domain type %s"), virttype);
+    if (virBitmapIsAllClear(monitor->vcpus)) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                       _("vcpus is empty"));
         return -1;
     }
 
-    if (!ostype) {
-        if (def->os.bootloader) {
-            def->os.type = VIR_DOMAIN_OSTYPE_XEN;
-        } else {
-            virReportError(VIR_ERR_XML_ERROR, "%s",
-                           _("an os <type> must be specified"));
-            return -1;
-        }
-    } else {
-        if ((def->os.type = virDomainOSTypeFromString(ostype)) < 0) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("unknown OS type '%s'"), ostype);
+    while ((vcpu = virBitmapNextSetBit(monitor->vcpus, vcpu)) >= 0) {
+        if (!virBitmapIsBitSet(resctrl->vcpus, vcpu)) {
+            virReportError(VIR_ERR_INVALID_ARG, "%s",
+                           _("Monitor vcpus conflicts with allocation"));
             return -1;
         }
     }
 
-    /*
-     * HACK: For xen driver we previously used bogus 'linux' as the
-     * os type for paravirt, whereas capabilities declare it to
-     * be 'xen'. So we accept the former and convert
-     */
-    if (def->os.type == VIR_DOMAIN_OSTYPE_LINUX &&
-        def->virtType == VIR_DOMAIN_VIRT_XEN) {
-        def->os.type = VIR_DOMAIN_OSTYPE_XEN;
+    vcpus_equal_to_resctrl = virBitmapEqual(monitor->vcpus, resctrl->vcpus);
+
+    for (i = 0; i < resctrl->nmonitors; i++) {
+        if (virBitmapEqual(monitor->vcpus, resctrl->monitors[i]->vcpus)) {
+            if (monitor->tag != resctrl->monitors[i]->tag) {
+                continue;
+            } else {
+                virReportError(VIR_ERR_INVALID_ARG, "%s",
+                               _("Identical vcpus found in same type monitors"));
+                return -1;
+            }
+        }
+
+        if (virBitmapOverlaps(monitor->vcpus, resctrl->monitors[i]->vcpus))
+            vcpus_overlap_any = true;
+
+        if (vcpus_equal_to_resctrl ||
+            virBitmapEqual(resctrl->monitors[i]->vcpus, resctrl->vcpus))
+            continue;
+
+        if (virBitmapOverlaps(monitor->vcpus, resctrl->monitors[i]->vcpus))
+            vcpus_overlap_no_resctrl = true;
     }
 
-    if (arch && !(def->os.arch = virArchFromString(arch))) {
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                       _("Unknown architecture %s"), arch);
+    if (vcpus_overlap_no_resctrl ||
+        (default_alloc_monitor && vcpus_overlap_any)) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                       _("vcpus overlaps in resctrl groups"));
         return -1;
     }
 
-    if (def->os.arch == VIR_ARCH_NONE) {
-        if (xmlopt && xmlopt->config.defArch != VIR_ARCH_NONE)
-            def->os.arch = xmlopt->config.defArch;
-        else
-            def->os.arch = virArchFromHost();
-    }
+    if (vcpus_equal_to_resctrl && !default_alloc_monitor)
+        return 1;
 
     return 0;
 }
 
 
+#define VIR_DOMAIN_RESCTRL_MONITOR_CACHELEVEL 3
+
 static int
-virDomainMemorytuneDefParseMemory(xmlXPathContextPtr ctxt,
-                                  xmlNodePtr node,
-                                  virResctrlAllocPtr alloc)
+virDomainResctrlMonDefParse(virDomainDefPtr def,
+                            xmlXPathContextPtr ctxt,
+                            xmlNodePtr node,
+                            virResctrlMonitorType tag,
+                            virDomainResctrlDefPtr resctrl)
 {
+    virDomainResctrlMonDefPtr domresmon = NULL;
     VIR_XPATH_NODE_AUTORESTORE(ctxt);
-    unsigned int id;
-    unsigned int bandwidth;
+    unsigned int level = 0;
+    size_t i = 0;
+    int n = 0;
+    int rv = -1;
+    int ret = -1;
+    g_autofree xmlNodePtr *nodes = NULL;
     g_autofree char *tmp = NULL;
+    g_autofree char *id = NULL;
 
     ctxt->node = node;
 
-    tmp = virXMLPropString(node, "id");
-    if (!tmp) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("Missing memorytune attribute 'id'"));
-        return -1;
-    }
-    if (virStrToLong_uip(tmp, NULL, 10, &id) < 0) {
-        virReportError(VIR_ERR_XML_ERROR,
-                       _("Invalid memorytune attribute 'id' value '%s'"),
-                       tmp);
-        return -1;
-    }
-    VIR_FREE(tmp);
-
-    tmp = virXMLPropString(node, "bandwidth");
-    if (!tmp) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("Missing memorytune attribute 'bandwidth'"));
-        return -1;
-    }
-    if (virStrToLong_uip(tmp, NULL, 10, &bandwidth) < 0) {
-        virReportError(VIR_ERR_XML_ERROR,
-                       _("Invalid memorytune attribute 'bandwidth' value '%s'"),
-                       tmp);
-        return -1;
-    }
-    if (virResctrlAllocSetMemoryBandwidth(alloc, id, bandwidth) < 0)
-        return -1;
+    if ((n = virXPathNodeSet("./monitor", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Cannot extract monitor nodes"));
+        goto cleanup;
+    }
 
-    return 0;
-}
+    for (i = 0; i < n; i++) {
+        if (VIR_ALLOC(domresmon) < 0)
+            goto cleanup;
 
+        domresmon->tag = tag;
 
-static int
-virDomainMemorytuneDefParse(virDomainDefPtr def,
-                            xmlXPathContextPtr ctxt,
-                            xmlNodePtr node,
-                            unsigned int flags)
-{
-    VIR_XPATH_NODE_AUTORESTORE(ctxt);
-    virDomainResctrlDefPtr resctrl = NULL;
-    virDomainResctrlDefPtr newresctrl = NULL;
-    g_autoptr(virBitmap) vcpus = NULL;
-    g_autofree xmlNodePtr *nodes = NULL;
-    g_autoptr(virResctrlAlloc) alloc = NULL;
-    ssize_t i = 0;
-    size_t nmons = 0;
-    size_t ret = -1;
+        domresmon->instance = virResctrlMonitorNew();
+        if (!domresmon->instance) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Could not create monitor"));
+            goto cleanup;
+        }
 
-    int n;
+        if (tag == VIR_RESCTRL_MONITOR_TYPE_CACHE) {
+            tmp = virXMLPropString(nodes[i], "level");
+            if (!tmp) {
+                virReportError(VIR_ERR_XML_ERROR, "%s",
+                               _("Missing monitor attribute 'level'"));
+                goto cleanup;
+            }
 
-    ctxt->node = node;
+            if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) {
+                virReportError(VIR_ERR_XML_ERROR,
+                               _("Invalid monitor attribute 'level' value '%s'"),
+                               tmp);
+                goto cleanup;
+            }
 
-    if (virDomainResctrlParseVcpus(def, node, &vcpus) < 0)
-        return -1;
+            if (level != VIR_DOMAIN_RESCTRL_MONITOR_CACHELEVEL) {
+                virReportError(VIR_ERR_XML_ERROR,
+                               _("Invalid monitor cache level '%d'"),
+                               level);
+                goto cleanup;
+            }
 
-    if (virBitmapIsAllClear(vcpus))
-        return 0;
+            VIR_FREE(tmp);
+        }
 
-    if ((n = virXPathNodeSet("./node", ctxt, &nodes)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("Cannot extract memory nodes under memorytune"));
-        return -1;
-    }
+        if (virDomainResctrlParseVcpus(def, nodes[i], &domresmon->vcpus) < 0)
+            goto cleanup;
 
-    if (virDomainResctrlVcpuMatch(def, vcpus, &resctrl) < 0)
-        return -1;
+        rv = virDomainResctrlValidateMonitor(resctrl, domresmon);
+        if (rv < 0)
+            goto cleanup;
 
-    if (resctrl) {
-        alloc = virObjectRef(resctrl->alloc);
-    } else {
-        if (!(alloc = virResctrlAllocNew()))
-            return -1;
-    }
+        /* If monitor's vcpu list is identical to the vcpu list of the
+         * associated allocation, set monitor's id to the same value
+         * as the allocation. */
+        if (rv == 1) {
+            const char *alloc_id = virResctrlAllocGetID(resctrl->alloc);
 
-    /* First, parse <memorytune/node> element if any <node> element exists */
-    for (i = 0; i < n; i++) {
-        if (virDomainMemorytuneDefParseMemory(ctxt, nodes[i], alloc) < 0)
-            return -1;
-    }
+            id = g_strdup(alloc_id);
+        } else {
+            if (!(tmp = virBitmapFormat(domresmon->vcpus)))
+                goto cleanup;
 
-    /*
-     * If this is a new allocation, format ID and append to resctrl, otherwise
-     * just update the existing alloc information, which is done in above
-     * virDomainMemorytuneDefParseMemory */
-    if (!resctrl) {
-        if (!(newresctrl = virDomainResctrlNew(node, alloc, vcpus, flags)))
-            return -1;
+            id = g_strdup_printf("vcpus_%s", tmp);
+        }
 
-        resctrl = newresctrl;
-    }
+        virResctrlMonitorSetAlloc(domresmon->instance, resctrl->alloc);
 
-    /* Next, parse <memorytune/monitor> element */
-    nmons = resctrl->nmonitors;
-    if (virDomainResctrlMonDefParse(def, ctxt, node,
-                                    VIR_RESCTRL_MONITOR_TYPE_MEMBW,
-                                    resctrl) < 0)
-        goto cleanup;
+        if (virResctrlMonitorSetID(domresmon->instance, id) < 0)
+            goto cleanup;
 
-    nmons = resctrl->nmonitors - nmons;
-    /* Now @nmons contains the new <monitor> element number found in current
-     * <memorytune> element, and @n holds the number of new <node> element,
-     * only append the new @newresctrl object to domain if any of them is
-     * not zero. */
-    if (newresctrl && (nmons || n)) {
-        if (VIR_APPEND_ELEMENT(def->resctrls, def->nresctrls, newresctrl) < 0)
+        if (VIR_APPEND_ELEMENT(resctrl->monitors,
+                               resctrl->nmonitors,
+                               domresmon) < 0)
             goto cleanup;
+
+        VIR_FREE(id);
+        VIR_FREE(tmp);
     }
 
     ret = 0;
  cleanup:
-    virDomainResctrlDefFree(newresctrl);
+    virDomainResctrlMonDefFree(domresmon);
     return ret;
 }
 
 
-static virDomainDefPtr
-virDomainDefParseXML(xmlDocPtr xml,
-                     xmlXPathContextPtr ctxt,
-                     virDomainXMLOptionPtr xmlopt,
-                     unsigned int flags)
+static virDomainResctrlDefPtr
+virDomainResctrlNew(xmlNodePtr node,
+                    virResctrlAllocPtr alloc,
+                    virBitmapPtr vcpus,
+                    unsigned int flags)
 {
-    xmlNodePtr node = NULL;
-    size_t i, j;
-    int n, gic_version;
-    long id = -1;
-    virDomainDefPtr def;
-    bool uuid_generated = false;
-    bool usb_none = false;
-    bool usb_other = false;
-    bool usb_master = false;
-    g_autofree xmlNodePtr *nodes = NULL;
-    g_autofree char *tmp = NULL;
-
-    if (flags & VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA) {
-        g_autofree char *schema = NULL;
-
-        schema = virFileFindResource("domain.rng",
-                                     abs_top_srcdir "/docs/schemas",
-                                     PKGDATADIR "/schemas");
-        if (!schema)
-            return NULL;
-        if (virXMLValidateAgainstSchema(schema, xml) < 0)
-            return NULL;
-    }
+    virDomainResctrlDefPtr resctrl = NULL;
+    virDomainResctrlDefPtr ret = NULL;
+    g_autofree char *vcpus_str = NULL;
+    g_autofree char *alloc_id = NULL;
 
-    if (!(def = virDomainDefNew()))
+    /* We need to format it back because we need to be consistent in the naming
+     * even when users specify some "sub-optimal" string there. */
+    vcpus_str = virBitmapFormat(vcpus);
+    if (!vcpus_str)
         return NULL;
 
     if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE))
-        if (virXPathLong("string(./@id)", ctxt, &id) < 0)
-            id = -1;
-    def->id = (int)id;
-
-    if (virDomainDefParseCaps(def, ctxt, xmlopt) < 0)
-        goto error;
+        alloc_id = virXMLPropString(node, "id");
 
-    /* Extract domain name */
-    if (!(def->name = virXPathString("string(./name[1])", ctxt))) {
-        virReportError(VIR_ERR_NO_NAME, NULL);
-        goto error;
+    if (!alloc_id) {
+        /* The number of allocations is limited and the directory structure is flat,
+         * not hierarchical, so we need to have all same allocations in one
+         * directory, so it's nice to have it named appropriately.  For now it's
+         * 'vcpus_...' but it's designed in order for it to be changeable in the
+         * future (it's part of the status XML). */
+        alloc_id = g_strdup_printf("vcpus_%s", vcpus_str);
     }
 
-    /* Extract domain uuid. If both uuid and sysinfo/system/entry/uuid
-     * exist, they must match; and if only the latter exists, it can
-     * also serve as the uuid. */
-    tmp = virXPathString("string(./uuid[1])", ctxt);
-    if (!tmp) {
-        if (virUUIDGenerate(def->uuid) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           "%s", _("Failed to generate UUID"));
-            goto error;
-        }
-        uuid_generated = true;
-    } else {
-        if (virUUIDParse(tmp, def->uuid) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           "%s", _("malformed uuid element"));
-            goto error;
-        }
-        VIR_FREE(tmp);
+    if (virResctrlAllocSetID(alloc, alloc_id) < 0)
+        goto cleanup;
+
+    if (VIR_ALLOC(resctrl) < 0)
+        goto cleanup;
+
+    if (!(resctrl->vcpus = virBitmapNewCopy(vcpus))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("failed to copy 'vcpus'"));
+        goto cleanup;
     }
 
-    /* Extract domain genid - a genid can either be provided or generated */
-    if ((n = virXPathNodeSet("./genid", ctxt, &nodes)) < 0)
-        goto error;
+    resctrl->alloc = virObjectRef(alloc);
 
-    if (n > 0) {
-        if (n != 1) {
-            virReportError(VIR_ERR_XML_ERROR, "%s",
-                           _("element 'genid' can only appear once"));
-            goto error;
-        }
-        def->genidRequested = true;
-        if (!(tmp = virXPathString("string(./genid)", ctxt))) {
-            if (virUUIDGenerate(def->genid) < 0) {
-                virReportError(VIR_ERR_INTERNAL_ERROR,
-                               "%s", _("Failed to generate genid"));
-                goto error;
-            }
-            def->genidGenerated = true;
-        } else {
-            if (virUUIDParse(tmp, def->genid) < 0) {
-                virReportError(VIR_ERR_INTERNAL_ERROR,
-                               "%s", _("malformed genid element"));
-                goto error;
-            }
-            VIR_FREE(tmp);
-        }
+    ret = g_steal_pointer(&resctrl);
+ cleanup:
+    virDomainResctrlDefFree(resctrl);
+    return ret;
+}
+
+
+static int
+virDomainCachetuneDefParse(virDomainDefPtr def,
+                           xmlXPathContextPtr ctxt,
+                           xmlNodePtr node,
+                           unsigned int flags)
+{
+    VIR_XPATH_NODE_AUTORESTORE(ctxt);
+    virDomainResctrlDefPtr resctrl = NULL;
+    ssize_t i = 0;
+    int n;
+    int ret = -1;
+    g_autoptr(virBitmap) vcpus = NULL;
+    g_autofree xmlNodePtr *nodes = NULL;
+    g_autoptr(virResctrlAlloc) alloc = NULL;
+
+    ctxt->node = node;
+
+    if (virDomainResctrlParseVcpus(def, node, &vcpus) < 0)
+        return -1;
+
+    if (virBitmapIsAllClear(vcpus))
+        return 0;
+
+    if ((n = virXPathNodeSet("./cache", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Cannot extract cache nodes under cachetune"));
+        return -1;
     }
-    VIR_FREE(nodes);
 
-    /* Extract short description of domain (title) */
-    def->title = virXPathString("string(./title[1])", ctxt);
-    if (def->title && strchr(def->title, '\n')) {
+    if (virDomainResctrlVcpuMatch(def, vcpus, &resctrl) < 0)
+        return -1;
+
+    if (resctrl) {
         virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("Domain title can't contain newlines"));
-        goto error;
+                       _("Identical vcpus in cachetunes found"));
+        return -1;
     }
 
-    /* Extract documentation if present */
-    def->description = virXPathString("string(./description[1])", ctxt);
+    if (!(alloc = virResctrlAllocNew()))
+        return -1;
 
-    /* analysis of security label, done early even though we format it
-     * late, so devices can refer to this for defaults */
-    if (!(flags & VIR_DOMAIN_DEF_PARSE_SKIP_SECLABEL)) {
-        if (virSecurityLabelDefsParseXML(def, ctxt, xmlopt, flags) == -1)
-            goto error;
+    for (i = 0; i < n; i++) {
+        if (virDomainCachetuneDefParseCache(ctxt, nodes[i], alloc) < 0)
+            return -1;
     }
 
-    /* Extract domain memory */
-    if (virDomainParseMemory("./memory[1]", NULL, ctxt,
-                             &def->mem.total_memory, false, true) < 0)
-        goto error;
-
-    if (virDomainParseMemory("./currentMemory[1]", NULL, ctxt,
-                             &def->mem.cur_balloon, false, true) < 0)
-        goto error;
+    if (!(resctrl = virDomainResctrlNew(node, alloc, vcpus, flags)))
+        return -1;
 
-    if (virDomainParseMemory("./maxMemory[1]", NULL, ctxt,
-                             &def->mem.max_memory, false, false) < 0)
-        goto error;
+    if (virDomainResctrlMonDefParse(def, ctxt, node,
+                                    VIR_RESCTRL_MONITOR_TYPE_CACHE,
+                                    resctrl) < 0)
+        goto cleanup;
 
-    if (virXPathUInt("string(./maxMemory[1]/@slots)", ctxt, &def->mem.memory_slots) == -2) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("Failed to parse memory slot count"));
-        goto error;
+    /* If no <cache> element or <monitor> element in <cachetune>, do not
+     * append any resctrl element */
+    if (!resctrl->nmonitors && n == 0) {
+        ret = 0;
+        goto cleanup;
     }
 
-    /* and info about it */
-    if ((tmp = virXPathString("string(./memory[1]/@dumpCore)", ctxt)) &&
-        (def->mem.dump_core = virTristateSwitchTypeFromString(tmp)) <= 0) {
+    if (VIR_APPEND_ELEMENT(def->resctrls, def->nresctrls, resctrl) < 0)
+        goto cleanup;
+
+    ret = 0;
+ cleanup:
+    virDomainResctrlDefFree(resctrl);
+    return ret;
+}
+
+
+static int
+virDomainDefParseCaps(virDomainDefPtr def,
+                      xmlXPathContextPtr ctxt,
+                      virDomainXMLOptionPtr xmlopt)
+{
+    g_autofree char *virttype = NULL;
+    g_autofree char *arch = NULL;
+    g_autofree char *ostype = NULL;
+
+    virttype = virXPathString("string(./@type)", ctxt);
+    ostype = virXPathString("string(./os/type[1])", ctxt);
+    arch = virXPathString("string(./os/type[1]/@arch)", ctxt);
+
+    def->os.bootloader = virXPathString("string(./bootloader)", ctxt);
+    def->os.bootloaderArgs = virXPathString("string(./bootloader_args)", ctxt);
+    def->os.machine = virXPathString("string(./os/type[1]/@machine)", ctxt);
+    def->emulator = virXPathString("string(./devices/emulator[1])", ctxt);
+
+    if (!virttype) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("missing domain type attribute"));
+        return -1;
+    }
+    if ((def->virtType = virDomainVirtTypeFromString(virttype)) < 0) {
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                       _("Invalid memory core dump attribute value '%s'"), tmp);
-        goto error;
+                       _("invalid domain type %s"), virttype);
+        return -1;
     }
-    VIR_FREE(tmp);
 
-    tmp = virXPathString("string(./memoryBacking/source/@type)", ctxt);
-    if (tmp) {
-        if ((def->mem.source = virDomainMemorySourceTypeFromString(tmp)) <= 0) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("unknown memoryBacking/source/type '%s'"), tmp);
-            goto error;
+    if (!ostype) {
+        if (def->os.bootloader) {
+            def->os.type = VIR_DOMAIN_OSTYPE_XEN;
+        } else {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("an os <type> must be specified"));
+            return -1;
         }
-        VIR_FREE(tmp);
-    }
-
-    tmp = virXPathString("string(./memoryBacking/access/@mode)", ctxt);
-    if (tmp) {
-        if ((def->mem.access = virDomainMemoryAccessTypeFromString(tmp)) <= 0) {
+    } else {
+        if ((def->os.type = virDomainOSTypeFromString(ostype)) < 0) {
             virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("unknown memoryBacking/access/mode '%s'"), tmp);
-            goto error;
+                           _("unknown OS type '%s'"), ostype);
+            return -1;
         }
-        VIR_FREE(tmp);
     }
 
-    tmp = virXPathString("string(./memoryBacking/allocation/@mode)", ctxt);
-    if (tmp) {
-        if ((def->mem.allocation = virDomainMemoryAllocationTypeFromString(tmp)) <= 0) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("unknown memoryBacking/allocation/mode '%s'"), tmp);
-            goto error;
-        }
-        VIR_FREE(tmp);
+    /*
+     * HACK: For xen driver we previously used bogus 'linux' as the
+     * os type for paravirt, whereas capabilities declare it to
+     * be 'xen'. So we accept the former and convert
+     */
+    if (def->os.type == VIR_DOMAIN_OSTYPE_LINUX &&
+        def->virtType == VIR_DOMAIN_VIRT_XEN) {
+        def->os.type = VIR_DOMAIN_OSTYPE_XEN;
     }
 
-    if (virXPathNode("./memoryBacking/hugepages", ctxt)) {
-        /* hugepages will be used */
-        if ((n = virXPathNodeSet("./memoryBacking/hugepages/page", ctxt, &nodes)) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("cannot extract hugepages nodes"));
-            goto error;
-        }
-
-        if (n) {
-            if (VIR_ALLOC_N(def->mem.hugepages, n) < 0)
-                goto error;
-
-            for (i = 0; i < n; i++) {
-                if (virDomainHugepagesParseXML(nodes[i], ctxt,
-                                               &def->mem.hugepages[i]) < 0)
-                    goto error;
-                def->mem.nhugepages++;
-            }
-
-            VIR_FREE(nodes);
-        } else {
-            /* no hugepage pages */
-            if (VIR_ALLOC(def->mem.hugepages) < 0)
-                goto error;
+    if (arch && !(def->os.arch = virArchFromString(arch))) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("Unknown architecture %s"), arch);
+        return -1;
+    }
 
-            def->mem.nhugepages = 1;
-        }
+    if (def->os.arch == VIR_ARCH_NONE) {
+        if (xmlopt && xmlopt->config.defArch != VIR_ARCH_NONE)
+            def->os.arch = xmlopt->config.defArch;
+        else
+            def->os.arch = virArchFromHost();
     }
 
-    if ((node = virXPathNode("./memoryBacking/nosharepages", ctxt)))
-        def->mem.nosharepages = true;
+    return 0;
+}
 
-    if (virXPathBoolean("boolean(./memoryBacking/locked)", ctxt))
-        def->mem.locked = true;
 
-    if (virXPathBoolean("boolean(./memoryBacking/discard)", ctxt))
-        def->mem.discard = VIR_TRISTATE_BOOL_YES;
+static int
+virDomainMemorytuneDefParseMemory(xmlXPathContextPtr ctxt,
+                                  xmlNodePtr node,
+                                  virResctrlAllocPtr alloc)
+{
+    VIR_XPATH_NODE_AUTORESTORE(ctxt);
+    unsigned int id;
+    unsigned int bandwidth;
+    g_autofree char *tmp = NULL;
 
-    /* Extract blkio cgroup tunables */
-    if (virXPathUInt("string(./blkiotune/weight)", ctxt,
-                     &def->blkio.weight) < 0)
-        def->blkio.weight = 0;
+    ctxt->node = node;
 
-    if ((n = virXPathNodeSet("./blkiotune/device", ctxt, &nodes)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("cannot extract blkiotune nodes"));
-        goto error;
+    tmp = virXMLPropString(node, "id");
+    if (!tmp) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("Missing memorytune attribute 'id'"));
+        return -1;
     }
-    if (n && VIR_ALLOC_N(def->blkio.devices, n) < 0)
-        goto error;
-
-    for (i = 0; i < n; i++) {
-        if (virDomainBlkioDeviceParseXML(nodes[i],
-                                         &def->blkio.devices[i]) < 0)
-            goto error;
-        def->blkio.ndevices++;
-        for (j = 0; j < i; j++) {
-            if (STREQ(def->blkio.devices[j].path,
-                      def->blkio.devices[i].path)) {
-                virReportError(VIR_ERR_XML_ERROR,
-                               _("duplicate blkio device path '%s'"),
-                               def->blkio.devices[i].path);
-                goto error;
-            }
-        }
+    if (virStrToLong_uip(tmp, NULL, 10, &id) < 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Invalid memorytune attribute 'id' value '%s'"),
+                       tmp);
+        return -1;
     }
-    VIR_FREE(nodes);
-
-    /* Extract other memory tunables */
-    if (virDomainParseMemoryLimit("./memtune/hard_limit[1]", NULL, ctxt,
-                                  &def->mem.hard_limit) < 0)
-        goto error;
+    VIR_FREE(tmp);
 
-    if (virDomainParseMemoryLimit("./memtune/soft_limit[1]", NULL, ctxt,
-                                  &def->mem.soft_limit) < 0)
-        goto error;
+    tmp = virXMLPropString(node, "bandwidth");
+    if (!tmp) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("Missing memorytune attribute 'bandwidth'"));
+        return -1;
+    }
+    if (virStrToLong_uip(tmp, NULL, 10, &bandwidth) < 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Invalid memorytune attribute 'bandwidth' value '%s'"),
+                       tmp);
+        return -1;
+    }
+    if (virResctrlAllocSetMemoryBandwidth(alloc, id, bandwidth) < 0)
+        return -1;
 
-    if (virDomainParseMemory("./memtune/min_guarantee[1]", NULL, ctxt,
-                             &def->mem.min_guarantee, false, false) < 0)
-        goto error;
+    return 0;
+}
 
-    if (virDomainParseMemoryLimit("./memtune/swap_hard_limit[1]", NULL, ctxt,
-                                  &def->mem.swap_hard_limit) < 0)
-        goto error;
 
-    if (virDomainVcpuParse(def, ctxt, xmlopt) < 0)
-        goto error;
+static int
+virDomainMemorytuneDefParse(virDomainDefPtr def,
+                            xmlXPathContextPtr ctxt,
+                            xmlNodePtr node,
+                            unsigned int flags)
+{
+    VIR_XPATH_NODE_AUTORESTORE(ctxt);
+    virDomainResctrlDefPtr resctrl = NULL;
+    virDomainResctrlDefPtr newresctrl = NULL;
+    g_autoptr(virBitmap) vcpus = NULL;
+    g_autofree xmlNodePtr *nodes = NULL;
+    g_autoptr(virResctrlAlloc) alloc = NULL;
+    ssize_t i = 0;
+    size_t nmons = 0;
+    size_t ret = -1;
 
-    if (virDomainDefParseIOThreads(def, ctxt) < 0)
-        goto error;
+    int n;
 
-    /* Extract cpu tunables. */
-    if ((n = virXPathULongLong("string(./cputune/shares[1])", ctxt,
-                               &def->cputune.shares)) < -1) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("can't parse cputune shares value"));
-        goto error;
-    } else if (n == 0) {
-        def->cputune.sharesSpecified = true;
-    }
+    ctxt->node = node;
 
-    if (virXPathULongLong("string(./cputune/period[1])", ctxt,
-                          &def->cputune.period) < -1) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("can't parse cputune period value"));
-        goto error;
-    }
+    if (virDomainResctrlParseVcpus(def, node, &vcpus) < 0)
+        return -1;
 
-    if (virXPathLongLong("string(./cputune/quota[1])", ctxt,
-                         &def->cputune.quota) < -1) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("can't parse cputune quota value"));
-        goto error;
-    }
+    if (virBitmapIsAllClear(vcpus))
+        return 0;
 
-    if (virXPathULongLong("string(./cputune/global_period[1])", ctxt,
-                          &def->cputune.global_period) < -1) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("can't parse cputune global period value"));
-        goto error;
+    if ((n = virXPathNodeSet("./node", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Cannot extract memory nodes under memorytune"));
+        return -1;
     }
 
-    if (virXPathLongLong("string(./cputune/global_quota[1])", ctxt,
-                         &def->cputune.global_quota) < -1) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("can't parse cputune global quota value"));
-        goto error;
-    }
+    if (virDomainResctrlVcpuMatch(def, vcpus, &resctrl) < 0)
+        return -1;
 
-    if (virXPathULongLong("string(./cputune/emulator_period[1])", ctxt,
-                          &def->cputune.emulator_period) < -1) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("can't parse cputune emulator period value"));
-        goto error;
+    if (resctrl) {
+        alloc = virObjectRef(resctrl->alloc);
+    } else {
+        if (!(alloc = virResctrlAllocNew()))
+            return -1;
     }
 
-    if (virXPathLongLong("string(./cputune/emulator_quota[1])", ctxt,
-                         &def->cputune.emulator_quota) < -1) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("can't parse cputune emulator quota value"));
-        goto error;
+    /* First, parse <memorytune/node> element if any <node> element exists */
+    for (i = 0; i < n; i++) {
+        if (virDomainMemorytuneDefParseMemory(ctxt, nodes[i], alloc) < 0)
+            return -1;
     }
 
+    /*
+     * If this is a new allocation, format ID and append to resctrl, otherwise
+     * just update the existing alloc information, which is done in above
+     * virDomainMemorytuneDefParseMemory */
+    if (!resctrl) {
+        if (!(newresctrl = virDomainResctrlNew(node, alloc, vcpus, flags)))
+            return -1;
 
-    if (virXPathULongLong("string(./cputune/iothread_period[1])", ctxt,
-                          &def->cputune.iothread_period) < -1) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("can't parse cputune iothread period value"));
-        goto error;
-    }
-
-    if (virXPathLongLong("string(./cputune/iothread_quota[1])", ctxt,
-                         &def->cputune.iothread_quota) < -1) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("can't parse cputune iothread quota value"));
-        goto error;
+        resctrl = newresctrl;
     }
 
-    if ((n = virXPathNodeSet("./cputune/vcpupin", ctxt, &nodes)) < 0)
-        goto error;
+    /* Next, parse <memorytune/monitor> element */
+    nmons = resctrl->nmonitors;
+    if (virDomainResctrlMonDefParse(def, ctxt, node,
+                                    VIR_RESCTRL_MONITOR_TYPE_MEMBW,
+                                    resctrl) < 0)
+        goto cleanup;
 
-    for (i = 0; i < n; i++) {
-        if (virDomainVcpuPinDefParseXML(def, nodes[i]))
-            goto error;
+    nmons = resctrl->nmonitors - nmons;
+    /* Now @nmons contains the new <monitor> element number found in current
+     * <memorytune> element, and @n holds the number of new <node> element,
+     * only append the new @newresctrl object to domain if any of them is
+     * not zero. */
+    if (newresctrl && (nmons || n)) {
+        if (VIR_APPEND_ELEMENT(def->resctrls, def->nresctrls, newresctrl) < 0)
+            goto cleanup;
     }
-    VIR_FREE(nodes);
 
-    if ((n = virXPathNodeSet("./cputune/emulatorpin", ctxt, &nodes)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("cannot extract emulatorpin nodes"));
-        goto error;
-    }
+    ret = 0;
+ cleanup:
+    virDomainResctrlDefFree(newresctrl);
+    return ret;
+}
 
-    if (n) {
-        if (n > 1) {
-            virReportError(VIR_ERR_XML_ERROR, "%s",
-                           _("only one emulatorpin is supported"));
-            VIR_FREE(nodes);
-            goto error;
-        }
 
-        if (!(def->cputune.emulatorpin = virDomainEmulatorPinDefParseXML(nodes[0])))
-            goto error;
-    }
-    VIR_FREE(nodes);
+static virDomainDefPtr
+virDomainDefParseXML(xmlDocPtr xml,
+                     xmlXPathContextPtr ctxt,
+                     virDomainXMLOptionPtr xmlopt,
+                     unsigned int flags)
+{
+    xmlNodePtr node = NULL;
+    size_t i, j;
+    int n;
+    long id = -1;
+    virDomainDefPtr def;
+    bool uuid_generated = false;
+    bool usb_none = false;
+    bool usb_other = false;
+    bool usb_master = false;
+    g_autofree xmlNodePtr *nodes = NULL;
+    g_autofree char *tmp = NULL;
 
+    if (flags & VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA) {
+        g_autofree char *schema = NULL;
 
-    if ((n = virXPathNodeSet("./cputune/iothreadpin", ctxt, &nodes)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("cannot extract iothreadpin nodes"));
-        goto error;
+        schema = virFileFindResource("domain.rng",
+                                     abs_top_srcdir "/docs/schemas",
+                                     PKGDATADIR "/schemas");
+        if (!schema)
+            return NULL;
+        if (virXMLValidateAgainstSchema(schema, xml) < 0)
+            return NULL;
     }
 
-    for (i = 0; i < n; i++) {
-        if (virDomainIOThreadPinDefParseXML(nodes[i], def) < 0)
-            goto error;
-    }
-    VIR_FREE(nodes);
+    if (!(def = virDomainDefNew()))
+        return NULL;
 
-    if ((n = virXPathNodeSet("./cputune/vcpusched", ctxt, &nodes)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("cannot extract vcpusched nodes"));
-        goto error;
-    }
+    if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE))
+        if (virXPathLong("string(./@id)", ctxt, &id) < 0)
+            id = -1;
+    def->id = (int)id;
 
-    for (i = 0; i < n; i++) {
-        if (virDomainVcpuThreadSchedParse(nodes[i], def) < 0)
-            goto error;
-    }
-    VIR_FREE(nodes);
+    if (virDomainDefParseCaps(def, ctxt, xmlopt) < 0)
+        goto error;
 
-    if ((n = virXPathNodeSet("./cputune/iothreadsched", ctxt, &nodes)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("cannot extract iothreadsched nodes"));
+    /* Extract domain name */
+    if (!(def->name = virXPathString("string(./name[1])", ctxt))) {
+        virReportError(VIR_ERR_NO_NAME, NULL);
         goto error;
     }
 
-    for (i = 0; i < n; i++) {
-        if (virDomainIOThreadSchedParse(nodes[i], def) < 0)
+    /* Extract domain uuid. If both uuid and sysinfo/system/entry/uuid
+     * exist, they must match; and if only the latter exists, it can
+     * also serve as the uuid. */
+    tmp = virXPathString("string(./uuid[1])", ctxt);
+    if (!tmp) {
+        if (virUUIDGenerate(def->uuid) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           "%s", _("Failed to generate UUID"));
             goto error;
+        }
+        uuid_generated = true;
+    } else {
+        if (virUUIDParse(tmp, def->uuid) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           "%s", _("malformed uuid element"));
+            goto error;
+        }
+        VIR_FREE(tmp);
     }
-    VIR_FREE(nodes);
 
-    if ((n = virXPathNodeSet("./cputune/emulatorsched", ctxt, &nodes)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("cannot extract emulatorsched nodes"));
+    /* Extract domain genid - a genid can either be provided or generated */
+    if ((n = virXPathNodeSet("./genid", ctxt, &nodes)) < 0)
         goto error;
-    }
 
-    if (n) {
-        if (n > 1) {
+    if (n > 0) {
+        if (n != 1) {
             virReportError(VIR_ERR_XML_ERROR, "%s",
-                           _("only one emulatorsched is supported"));
-            VIR_FREE(nodes);
+                           _("element 'genid' can only appear once"));
             goto error;
         }
-
-        if (virDomainEmulatorSchedParse(nodes[0], def) < 0)
-            goto error;
+        def->genidRequested = true;
+        if (!(tmp = virXPathString("string(./genid)", ctxt))) {
+            if (virUUIDGenerate(def->genid) < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               "%s", _("Failed to generate genid"));
+                goto error;
+            }
+            def->genidGenerated = true;
+        } else {
+            if (virUUIDParse(tmp, def->genid) < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               "%s", _("malformed genid element"));
+                goto error;
+            }
+            VIR_FREE(tmp);
+        }
     }
     VIR_FREE(nodes);
 
-    if ((n = virXPathNodeSet("./cputune/cachetune", ctxt, &nodes)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("cannot extract cachetune nodes"));
+    /* Extract short description of domain (title) */
+    def->title = virXPathString("string(./title[1])", ctxt);
+    if (def->title && strchr(def->title, '\n')) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("Domain title can't contain newlines"));
         goto error;
     }
 
-    for (i = 0; i < n; i++) {
-        if (virDomainCachetuneDefParse(def, ctxt, nodes[i], flags) < 0)
-            goto error;
-    }
-    VIR_FREE(nodes);
-
-    if ((n = virXPathNodeSet("./cputune/memorytune", ctxt, &nodes)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("cannot extract memorytune nodes"));
-        goto error;
-    }
+    /* Extract documentation if present */
+    def->description = virXPathString("string(./description[1])", ctxt);
 
-    for (i = 0; i < n; i++) {
-        if (virDomainMemorytuneDefParse(def, ctxt, nodes[i], flags) < 0)
+    /* analysis of security label, done early even though we format it
+     * late, so devices can refer to this for defaults */
+    if (!(flags & VIR_DOMAIN_DEF_PARSE_SKIP_SECLABEL)) {
+        if (virSecurityLabelDefsParseXML(def, ctxt, xmlopt, flags) == -1)
             goto error;
     }
-    VIR_FREE(nodes);
 
-    if (virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST, &def->cpu) < 0)
+    /* Extract domain memory */
+    if (virDomainParseMemory("./memory[1]", NULL, ctxt,
+                             &def->mem.total_memory, false, true) < 0)
         goto error;
 
-    if (virDomainNumaDefCPUParseXML(def->numa, ctxt) < 0)
+    if (virDomainParseMemory("./currentMemory[1]", NULL, ctxt,
+                             &def->mem.cur_balloon, false, true) < 0)
         goto error;
 
-    if (virDomainNumaGetCPUCountTotal(def->numa) > virDomainDefGetVcpusMax(def)) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("Number of CPUs in <numa> exceeds the"
-                         " <vcpu> count"));
+    if (virDomainParseMemory("./maxMemory[1]", NULL, ctxt,
+                             &def->mem.max_memory, false, false) < 0)
         goto error;
-    }
 
-    if (virDomainNumaGetMaxCPUID(def->numa) >= virDomainDefGetVcpusMax(def)) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("CPU IDs in <numa> exceed the <vcpu> count"));
+    if (virXPathUInt("string(./maxMemory[1]/@slots)", ctxt, &def->mem.memory_slots) == -2) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("Failed to parse memory slot count"));
         goto error;
     }
 
-    if (virDomainNumatuneParseXML(def->numa,
-                                  def->placement_mode ==
-                                  VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC,
-                                  ctxt) < 0)
-        goto error;
-
-    if (virDomainNumatuneHasPlacementAuto(def->numa) &&
-        !def->cpumask && !virDomainDefHasVcpuPin(def) &&
-        !def->cputune.emulatorpin &&
-        !virDomainIOThreadIDArrayHasPin(def))
-        def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO;
-
-    if ((n = virXPathNodeSet("./resource", ctxt, &nodes)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("cannot extract resource nodes"));
+    /* and info about it */
+    if ((tmp = virXPathString("string(./memory[1]/@dumpCore)", ctxt)) &&
+        (def->mem.dump_core = virTristateSwitchTypeFromString(tmp)) <= 0) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("Invalid memory core dump attribute value '%s'"), tmp);
         goto error;
     }
+    VIR_FREE(tmp);
 
-    if (n > 1) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("only one resource element is supported"));
-        goto error;
+    tmp = virXPathString("string(./memoryBacking/source/@type)", ctxt);
+    if (tmp) {
+        if ((def->mem.source = virDomainMemorySourceTypeFromString(tmp)) <= 0) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("unknown memoryBacking/source/type '%s'"), tmp);
+            goto error;
+        }
+        VIR_FREE(tmp);
     }
 
-    if (n &&
-        !(def->resource = virDomainResourceDefParse(nodes[0], ctxt)))
-        goto error;
-    VIR_FREE(nodes);
-
-    if ((n = virXPathNodeSet("./features/*", ctxt, &nodes)) < 0)
-        goto error;
-
-    for (i = 0; i < n; i++) {
-        int val = virDomainFeatureTypeFromString((const char *)nodes[i]->name);
-        if (val < 0) {
+    tmp = virXPathString("string(./memoryBacking/access/@mode)", ctxt);
+    if (tmp) {
+        if ((def->mem.access = virDomainMemoryAccessTypeFromString(tmp)) <= 0) {
             virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("unexpected feature '%s'"), nodes[i]->name);
+                           _("unknown memoryBacking/access/mode '%s'"), tmp);
             goto error;
         }
+        VIR_FREE(tmp);
+    }
 
-        switch ((virDomainFeature) val) {
-        case VIR_DOMAIN_FEATURE_APIC:
-            if ((tmp = virXPathString("string(./features/apic/@eoi)", ctxt))) {
-                int eoi;
-                if ((eoi = virTristateSwitchTypeFromString(tmp)) <= 0) {
-                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                   _("unknown value for attribute eoi: '%s'"),
-                                   tmp);
-                    goto error;
-                }
-                def->apic_eoi = eoi;
-                VIR_FREE(tmp);
-            }
-            G_GNUC_FALLTHROUGH;
-        case VIR_DOMAIN_FEATURE_ACPI:
-        case VIR_DOMAIN_FEATURE_PAE:
-        case VIR_DOMAIN_FEATURE_VIRIDIAN:
-        case VIR_DOMAIN_FEATURE_PRIVNET:
-        case VIR_DOMAIN_FEATURE_HYPERV:
-        case VIR_DOMAIN_FEATURE_KVM:
-        case VIR_DOMAIN_FEATURE_MSRS:
-        case VIR_DOMAIN_FEATURE_XEN:
-            def->features[val] = VIR_TRISTATE_SWITCH_ON;
-            break;
-
-        case VIR_DOMAIN_FEATURE_CAPABILITIES:
-            if ((tmp = virXMLPropString(nodes[i], "policy"))) {
-                if ((def->features[val] = virDomainCapabilitiesPolicyTypeFromString(tmp)) == -1) {
-                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                   _("unknown policy attribute '%s' of feature '%s'"),
-                                   tmp, virDomainFeatureTypeToString(val));
-                    goto error;
-                }
-                VIR_FREE(tmp);
-            } else {
-                def->features[val] = VIR_TRISTATE_SWITCH_ABSENT;
-            }
-            break;
-
-        case VIR_DOMAIN_FEATURE_VMCOREINFO:
-        case VIR_DOMAIN_FEATURE_HAP:
-        case VIR_DOMAIN_FEATURE_PMU:
-        case VIR_DOMAIN_FEATURE_PVSPINLOCK:
-        case VIR_DOMAIN_FEATURE_VMPORT:
-        case VIR_DOMAIN_FEATURE_SMM:
-            if ((tmp = virXMLPropString(nodes[i], "state"))) {
-                if ((def->features[val] = virTristateSwitchTypeFromString(tmp)) == -1) {
-                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                   _("unknown state attribute '%s' of feature '%s'"),
-                                   tmp, virDomainFeatureTypeToString(val));
-                    goto error;
-                }
-                VIR_FREE(tmp);
-            } else {
-                def->features[val] = VIR_TRISTATE_SWITCH_ON;
-            }
-            break;
-
-        case VIR_DOMAIN_FEATURE_GIC:
-            if ((tmp = virXMLPropString(nodes[i], "version"))) {
-                gic_version = virGICVersionTypeFromString(tmp);
-                if (gic_version < 0 || gic_version == VIR_GIC_VERSION_NONE) {
-                    virReportError(VIR_ERR_XML_ERROR,
-                                   _("malformed gic version: %s"), tmp);
-                    goto error;
-                }
-                def->gic_version = gic_version;
-                VIR_FREE(tmp);
-            }
-            def->features[val] = VIR_TRISTATE_SWITCH_ON;
-            break;
-
-        case VIR_DOMAIN_FEATURE_IOAPIC:
-            tmp = virXMLPropString(nodes[i], "driver");
-            if (tmp) {
-                int value = virDomainIOAPICTypeFromString(tmp);
-                if (value < 0 || value == VIR_DOMAIN_IOAPIC_NONE) {
-                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                   _("Unknown driver mode: %s"),
-                                   tmp);
-                    goto error;
-                }
-                def->features[val] = value;
-                VIR_FREE(tmp);
-            }
-            break;
-
-        case VIR_DOMAIN_FEATURE_HPT:
-            tmp = virXMLPropString(nodes[i], "resizing");
-            if (tmp) {
-                int value = virDomainHPTResizingTypeFromString(tmp);
-                if (value < 0 || value == VIR_DOMAIN_HPT_RESIZING_NONE) {
-                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                   _("Unknown HPT resizing setting: %s"),
-                                   tmp);
-                    goto error;
-                }
-                def->hpt_resizing = (virDomainHPTResizing) value;
-                VIR_FREE(tmp);
-            }
-
-            if (virDomainParseScaledValue("./features/hpt/maxpagesize",
-                                          NULL,
-                                          ctxt,
-                                          &def->hpt_maxpagesize,
-                                          1024,
-                                          ULLONG_MAX,
-                                          false) < 0) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               "%s",
-                               _("Unable to parse HPT maxpagesize setting"));
-                goto error;
-            }
-            def->hpt_maxpagesize = VIR_DIV_UP(def->hpt_maxpagesize, 1024);
-
-            if (def->hpt_resizing != VIR_DOMAIN_HPT_RESIZING_NONE ||
-                def->hpt_maxpagesize > 0) {
-                def->features[val] = VIR_TRISTATE_SWITCH_ON;
-            }
-            break;
-
-        case VIR_DOMAIN_FEATURE_HTM:
-        case VIR_DOMAIN_FEATURE_NESTED_HV:
-        case VIR_DOMAIN_FEATURE_CCF_ASSIST:
-            if (!(tmp = virXMLPropString(nodes[i], "state"))) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("missing state attribute '%s' of feature '%s'"),
-                               tmp, virDomainFeatureTypeToString(val));
-                goto error;
-            }
-            if ((def->features[val] = virTristateSwitchTypeFromString(tmp)) < 0) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("unknown state attribute '%s' of feature '%s'"),
-                               tmp, virDomainFeatureTypeToString(val));
-                goto error;
-            }
-            VIR_FREE(tmp);
-            break;
-
-        /* coverity[dead_error_begin] */
-        case VIR_DOMAIN_FEATURE_LAST:
-            break;
+    tmp = virXPathString("string(./memoryBacking/allocation/@mode)", ctxt);
+    if (tmp) {
+        if ((def->mem.allocation = virDomainMemoryAllocationTypeFromString(tmp)) <= 0) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("unknown memoryBacking/allocation/mode '%s'"), tmp);
+            goto error;
         }
+        VIR_FREE(tmp);
     }
-    VIR_FREE(nodes);
 
-    if (def->features[VIR_DOMAIN_FEATURE_HYPERV] == VIR_TRISTATE_SWITCH_ON) {
-        int feature;
-        int value;
-        node = ctxt->node;
-        if ((n = virXPathNodeSet("./features/hyperv/*", ctxt, &nodes)) < 0)
+    if (virXPathNode("./memoryBacking/hugepages", ctxt)) {
+        /* hugepages will be used */
+        if ((n = virXPathNodeSet("./memoryBacking/hugepages/page", ctxt, &nodes)) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("cannot extract hugepages nodes"));
             goto error;
+        }
 
-        for (i = 0; i < n; i++) {
-            feature = virDomainHypervTypeFromString((const char *)nodes[i]->name);
-            if (feature < 0) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("unsupported HyperV Enlightenment feature: %s"),
-                               nodes[i]->name);
+        if (n) {
+            if (VIR_ALLOC_N(def->mem.hugepages, n) < 0)
                 goto error;
-            }
-
-            ctxt->node = nodes[i];
 
-            if (!(tmp = virXMLPropString(nodes[i], "state"))) {
-                virReportError(VIR_ERR_XML_ERROR,
-                               _("missing 'state' attribute for "
-                                 "HyperV Enlightenment feature '%s'"),
-                               nodes[i]->name);
-                goto error;
+            for (i = 0; i < n; i++) {
+                if (virDomainHugepagesParseXML(nodes[i], ctxt,
+                                               &def->mem.hugepages[i]) < 0)
+                    goto error;
+                def->mem.nhugepages++;
             }
 
-            if ((value = virTristateSwitchTypeFromString(tmp)) < 0) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("invalid value of state argument "
-                                 "for HyperV Enlightenment feature '%s'"),
-                               nodes[i]->name);
+            VIR_FREE(nodes);
+        } else {
+            /* no hugepage pages */
+            if (VIR_ALLOC(def->mem.hugepages) < 0)
                 goto error;
-            }
-
-            VIR_FREE(tmp);
-            def->hyperv_features[feature] = value;
-
-            switch ((virDomainHyperv) feature) {
-            case VIR_DOMAIN_HYPERV_RELAXED:
-            case VIR_DOMAIN_HYPERV_VAPIC:
-            case VIR_DOMAIN_HYPERV_VPINDEX:
-            case VIR_DOMAIN_HYPERV_RUNTIME:
-            case VIR_DOMAIN_HYPERV_SYNIC:
-            case VIR_DOMAIN_HYPERV_STIMER:
-            case VIR_DOMAIN_HYPERV_RESET:
-            case VIR_DOMAIN_HYPERV_FREQUENCIES:
-            case VIR_DOMAIN_HYPERV_REENLIGHTENMENT:
-            case VIR_DOMAIN_HYPERV_TLBFLUSH:
-            case VIR_DOMAIN_HYPERV_IPI:
-            case VIR_DOMAIN_HYPERV_EVMCS:
-                break;
 
-            case VIR_DOMAIN_HYPERV_SPINLOCKS:
-                if (value != VIR_TRISTATE_SWITCH_ON)
-                    break;
-
-                if (virXPathUInt("string(./@retries)", ctxt,
-                             &def->hyperv_spinlocks) < 0) {
-                    virReportError(VIR_ERR_XML_ERROR, "%s",
-                                   _("invalid HyperV spinlock retry count"));
-                    goto error;
-                }
+            def->mem.nhugepages = 1;
+        }
+    }
 
-                if (def->hyperv_spinlocks < 0xFFF) {
-                    virReportError(VIR_ERR_XML_ERROR, "%s",
-                                   _("HyperV spinlock retry count must be "
-                                     "at least 4095"));
-                    goto error;
-                }
-                break;
+    if ((node = virXPathNode("./memoryBacking/nosharepages", ctxt)))
+        def->mem.nosharepages = true;
 
-            case VIR_DOMAIN_HYPERV_VENDOR_ID:
-                if (value != VIR_TRISTATE_SWITCH_ON)
-                    break;
+    if (virXPathBoolean("boolean(./memoryBacking/locked)", ctxt))
+        def->mem.locked = true;
 
-                if (!(def->hyperv_vendor_id = virXMLPropString(nodes[i],
-                                                               "value"))) {
-                    virReportError(VIR_ERR_XML_ERROR, "%s",
-                                   _("missing 'value' attribute for "
-                                     "HyperV feature 'vendor_id'"));
-                    goto error;
-                }
+    if (virXPathBoolean("boolean(./memoryBacking/discard)", ctxt))
+        def->mem.discard = VIR_TRISTATE_BOOL_YES;
 
-                if (strlen(def->hyperv_vendor_id) > VIR_DOMAIN_HYPERV_VENDOR_ID_MAX) {
-                    virReportError(VIR_ERR_XML_ERROR,
-                                   _("HyperV vendor_id value must not be more "
-                                     "than %d characters."),
-                                   VIR_DOMAIN_HYPERV_VENDOR_ID_MAX);
-                    goto error;
-                }
+    /* Extract blkio cgroup tunables */
+    if (virXPathUInt("string(./blkiotune/weight)", ctxt,
+                     &def->blkio.weight) < 0)
+        def->blkio.weight = 0;
 
-                /* ensure that the string can be passed to qemu */
-                if (strchr(def->hyperv_vendor_id, ',')) {
-                    virReportError(VIR_ERR_XML_ERROR, "%s",
-                                   _("HyperV vendor_id value is invalid"));
-                    goto error;
-                }
+    if ((n = virXPathNodeSet("./blkiotune/device", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("cannot extract blkiotune nodes"));
+        goto error;
+    }
+    if (n && VIR_ALLOC_N(def->blkio.devices, n) < 0)
+        goto error;
 
-            /* coverity[dead_error_begin] */
-            case VIR_DOMAIN_HYPERV_LAST:
-                break;
+    for (i = 0; i < n; i++) {
+        if (virDomainBlkioDeviceParseXML(nodes[i],
+                                         &def->blkio.devices[i]) < 0)
+            goto error;
+        def->blkio.ndevices++;
+        for (j = 0; j < i; j++) {
+            if (STREQ(def->blkio.devices[j].path,
+                      def->blkio.devices[i].path)) {
+                virReportError(VIR_ERR_XML_ERROR,
+                               _("duplicate blkio device path '%s'"),
+                               def->blkio.devices[i].path);
+                goto error;
             }
         }
-        VIR_FREE(nodes);
-        ctxt->node = node;
     }
+    VIR_FREE(nodes);
 
-    if (def->features[VIR_DOMAIN_HYPERV_STIMER] == VIR_TRISTATE_SWITCH_ON) {
-        int value;
-        if ((n = virXPathNodeSet("./features/hyperv/stimer/*", ctxt, &nodes)) < 0)
-            goto error;
+    /* Extract other memory tunables */
+    if (virDomainParseMemoryLimit("./memtune/hard_limit[1]", NULL, ctxt,
+                                  &def->mem.hard_limit) < 0)
+        goto error;
 
-        for (i = 0; i < n; i++) {
-            if (STRNEQ((const char *)nodes[i]->name, "direct")) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("unsupported Hyper-V stimer feature: %s"),
-                               nodes[i]->name);
-                goto error;
-            }
+    if (virDomainParseMemoryLimit("./memtune/soft_limit[1]", NULL, ctxt,
+                                  &def->mem.soft_limit) < 0)
+        goto error;
 
-            if (!(tmp = virXMLPropString(nodes[i], "state"))) {
-                virReportError(VIR_ERR_XML_ERROR,
-                               _("missing 'state' attribute for "
-                                 "Hyper-V stimer '%s' feature"), "direct");
-                        goto error;
-            }
+    if (virDomainParseMemory("./memtune/min_guarantee[1]", NULL, ctxt,
+                             &def->mem.min_guarantee, false, false) < 0)
+        goto error;
 
-            if ((value = virTristateSwitchTypeFromString(tmp)) < 0) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("invalid value of state argument "
-                                 "for Hyper-V stimer '%s' feature"), "direct");
-                goto error;
-            }
+    if (virDomainParseMemoryLimit("./memtune/swap_hard_limit[1]", NULL, ctxt,
+                                  &def->mem.swap_hard_limit) < 0)
+        goto error;
 
-            VIR_FREE(tmp);
-            def->hyperv_stimer_direct = value;
-        }
-        VIR_FREE(nodes);
+    if (virDomainVcpuParse(def, ctxt, xmlopt) < 0)
+        goto error;
+
+    if (virDomainDefParseIOThreads(def, ctxt) < 0)
+        goto error;
+
+    /* Extract cpu tunables. */
+    if ((n = virXPathULongLong("string(./cputune/shares[1])", ctxt,
+                               &def->cputune.shares)) < -1) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("can't parse cputune shares value"));
+        goto error;
+    } else if (n == 0) {
+        def->cputune.sharesSpecified = true;
     }
 
-    if (def->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) {
-        int feature;
-        int value;
-        if ((n = virXPathNodeSet("./features/kvm/*", ctxt, &nodes)) < 0)
-            goto error;
+    if (virXPathULongLong("string(./cputune/period[1])", ctxt,
+                          &def->cputune.period) < -1) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("can't parse cputune period value"));
+        goto error;
+    }
 
-        for (i = 0; i < n; i++) {
-            feature = virDomainKVMTypeFromString((const char *)nodes[i]->name);
-            if (feature < 0) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("unsupported KVM feature: %s"),
-                               nodes[i]->name);
-                goto error;
-            }
+    if (virXPathLongLong("string(./cputune/quota[1])", ctxt,
+                         &def->cputune.quota) < -1) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("can't parse cputune quota value"));
+        goto error;
+    }
 
-            switch ((virDomainKVM) feature) {
-                case VIR_DOMAIN_KVM_HIDDEN:
-                case VIR_DOMAIN_KVM_DEDICATED:
-                    if (!(tmp = virXMLPropString(nodes[i], "state"))) {
-                        virReportError(VIR_ERR_XML_ERROR,
-                                       _("missing 'state' attribute for "
-                                         "KVM feature '%s'"),
-                                       nodes[i]->name);
-                        goto error;
-                    }
+    if (virXPathULongLong("string(./cputune/global_period[1])", ctxt,
+                          &def->cputune.global_period) < -1) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("can't parse cputune global period value"));
+        goto error;
+    }
+
+    if (virXPathLongLong("string(./cputune/global_quota[1])", ctxt,
+                         &def->cputune.global_quota) < -1) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("can't parse cputune global quota value"));
+        goto error;
+    }
+
+    if (virXPathULongLong("string(./cputune/emulator_period[1])", ctxt,
+                          &def->cputune.emulator_period) < -1) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("can't parse cputune emulator period value"));
+        goto error;
+    }
+
+    if (virXPathLongLong("string(./cputune/emulator_quota[1])", ctxt,
+                         &def->cputune.emulator_quota) < -1) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("can't parse cputune emulator quota value"));
+        goto error;
+    }
 
-                    if ((value = virTristateSwitchTypeFromString(tmp)) < 0) {
-                        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                       _("invalid value of state argument "
-                                         "for KVM feature '%s'"),
-                                       nodes[i]->name);
-                        goto error;
-                    }
 
-                    VIR_FREE(tmp);
-                    def->kvm_features[feature] = value;
-                    break;
+    if (virXPathULongLong("string(./cputune/iothread_period[1])", ctxt,
+                          &def->cputune.iothread_period) < -1) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("can't parse cputune iothread period value"));
+        goto error;
+    }
 
-                /* coverity[dead_error_begin] */
-                case VIR_DOMAIN_KVM_LAST:
-                    break;
-            }
-        }
-        VIR_FREE(nodes);
+    if (virXPathLongLong("string(./cputune/iothread_quota[1])", ctxt,
+                         &def->cputune.iothread_quota) < -1) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("can't parse cputune iothread quota value"));
+        goto error;
     }
 
-    if (def->features[VIR_DOMAIN_FEATURE_XEN] == VIR_TRISTATE_SWITCH_ON) {
-        int feature;
-        int value;
-        g_autofree char *ptval = NULL;
+    if ((n = virXPathNodeSet("./cputune/vcpupin", ctxt, &nodes)) < 0)
+        goto error;
 
-        if ((n = virXPathNodeSet("./features/xen/*", ctxt, &nodes)) < 0)
+    for (i = 0; i < n; i++) {
+        if (virDomainVcpuPinDefParseXML(def, nodes[i]))
             goto error;
+    }
+    VIR_FREE(nodes);
 
-        for (i = 0; i < n; i++) {
-            feature = virDomainXenTypeFromString((const char *)nodes[i]->name);
-            if (feature < 0) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("unsupported Xen feature: %s"),
-                               nodes[i]->name);
-                goto error;
-            }
-
-            if (!(tmp = virXMLPropString(nodes[i], "state"))) {
-                virReportError(VIR_ERR_XML_ERROR,
-                               _("missing 'state' attribute for "
-                                 "Xen feature '%s'"),
-                               nodes[i]->name);
-                goto error;
-            }
+    if ((n = virXPathNodeSet("./cputune/emulatorpin", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot extract emulatorpin nodes"));
+        goto error;
+    }
 
-            if ((value = virTristateSwitchTypeFromString(tmp)) < 0) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("invalid value of state argument "
-                                 "for Xen feature '%s'"),
-                               nodes[i]->name);
-                goto error;
-            }
+    if (n) {
+        if (n > 1) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("only one emulatorpin is supported"));
+            VIR_FREE(nodes);
+            goto error;
+        }
 
-            VIR_FREE(tmp);
-            def->xen_features[feature] = value;
+        if (!(def->cputune.emulatorpin = virDomainEmulatorPinDefParseXML(nodes[0])))
+            goto error;
+    }
+    VIR_FREE(nodes);
 
-            switch ((virDomainXen) feature) {
-                case VIR_DOMAIN_XEN_E820_HOST:
-                    break;
 
-            case VIR_DOMAIN_XEN_PASSTHROUGH:
-                if (value != VIR_TRISTATE_SWITCH_ON)
-                    break;
+    if ((n = virXPathNodeSet("./cputune/iothreadpin", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot extract iothreadpin nodes"));
+        goto error;
+    }
 
-                if ((ptval = virXMLPropString(nodes[i], "mode"))) {
-                    int mode = virDomainXenPassthroughModeTypeFromString(ptval);
+    for (i = 0; i < n; i++) {
+        if (virDomainIOThreadPinDefParseXML(nodes[i], def) < 0)
+            goto error;
+    }
+    VIR_FREE(nodes);
 
-                    if (mode < 0) {
-                        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                       _("unsupported mode '%s' for Xen passthrough feature"),
-                                       ptval);
-                        goto error;
-                    }
+    if ((n = virXPathNodeSet("./cputune/vcpusched", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot extract vcpusched nodes"));
+        goto error;
+    }
 
-                    if (mode != VIR_DOMAIN_XEN_PASSTHROUGH_MODE_SYNC_PT &&
-                        mode != VIR_DOMAIN_XEN_PASSTHROUGH_MODE_SHARE_PT) {
-                        virReportError(VIR_ERR_XML_ERROR, "%s",
-                                       _("'mode' attribute for Xen feature "
-                                         "'passthrough' must be 'sync_pt' or 'share_pt'"));
-                        goto error;
-                    }
-                    def->xen_passthrough_mode = mode;
-                }
-                break;
+    for (i = 0; i < n; i++) {
+        if (virDomainVcpuThreadSchedParse(nodes[i], def) < 0)
+            goto error;
+    }
+    VIR_FREE(nodes);
 
-                /* coverity[dead_error_begin] */
-                case VIR_DOMAIN_XEN_LAST:
-                    break;
-            }
-        }
-        VIR_FREE(nodes);
+    if ((n = virXPathNodeSet("./cputune/iothreadsched", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot extract iothreadsched nodes"));
+        goto error;
     }
 
-    if (def->features[VIR_DOMAIN_FEATURE_SMM] == VIR_TRISTATE_SWITCH_ON) {
-        int rv = virDomainParseScaledValue("string(./features/smm/tseg)",
-                                           "string(./features/smm/tseg/@unit)",
-                                           ctxt,
-                                           &def->tseg_size,
-                                           1024 * 1024, /* Defaults to mebibytes */
-                                           ULLONG_MAX,
-                                           false);
-        if (rv < 0)
+    for (i = 0; i < n; i++) {
+        if (virDomainIOThreadSchedParse(nodes[i], def) < 0)
             goto error;
-        def->tseg_specified = rv;
     }
+    VIR_FREE(nodes);
 
-    if (def->features[VIR_DOMAIN_FEATURE_MSRS] == VIR_TRISTATE_SWITCH_ON) {
-        if ((node = virXPathNode("./features/msrs", ctxt)) == NULL)
-            goto error;
+    if ((n = virXPathNodeSet("./cputune/emulatorsched", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot extract emulatorsched nodes"));
+        goto error;
+    }
 
-        if (!(tmp = virXMLPropString(node, "unknown"))) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("missing 'unknown' attribute for feature '%s'"),
-                           virDomainFeatureTypeToString(VIR_DOMAIN_FEATURE_MSRS));
+    if (n) {
+        if (n > 1) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("only one emulatorsched is supported"));
+            VIR_FREE(nodes);
             goto error;
         }
 
-        if ((def->msrs_features[VIR_DOMAIN_MSRS_UNKNOWN] = virDomainMsrsUnknownTypeFromString(tmp)) < 0) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("unknown 'unknown' value '%s'"),
-                           tmp);
+        if (virDomainEmulatorSchedParse(nodes[0], def) < 0)
             goto error;
-        }
-        VIR_FREE(tmp);
     }
+    VIR_FREE(nodes);
 
-    if ((n = virXPathNodeSet("./features/capabilities/*", ctxt, &nodes)) < 0)
+    if ((n = virXPathNodeSet("./cputune/cachetune", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot extract cachetune nodes"));
         goto error;
+    }
 
     for (i = 0; i < n; i++) {
-        int val = virDomainProcessCapsFeatureTypeFromString((const char *)nodes[i]->name);
-        if (val < 0) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("unexpected capability feature '%s'"), nodes[i]->name);
+        if (virDomainCachetuneDefParse(def, ctxt, nodes[i], flags) < 0)
             goto error;
-        }
+    }
+    VIR_FREE(nodes);
 
-        if ((tmp = virXMLPropString(nodes[i], "state"))) {
-            if ((def->caps_features[val] = virTristateSwitchTypeFromString(tmp)) == -1) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("unknown state attribute '%s' of feature capability '%s'"),
-                               tmp, virDomainProcessCapsFeatureTypeToString(val));
-                goto error;
-            }
-            VIR_FREE(tmp);
-        } else {
-            def->caps_features[val] = VIR_TRISTATE_SWITCH_ON;
-        }
+    if ((n = virXPathNodeSet("./cputune/memorytune", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot extract memorytune nodes"));
+        goto error;
+    }
+
+    for (i = 0; i < n; i++) {
+        if (virDomainMemorytuneDefParse(def, ctxt, nodes[i], flags) < 0)
+            goto error;
+    }
+    VIR_FREE(nodes);
+
+    if (virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST, &def->cpu) < 0)
+        goto error;
+
+    if (virDomainNumaDefCPUParseXML(def->numa, ctxt) < 0)
+        goto error;
+
+    if (virDomainNumaGetCPUCountTotal(def->numa) > virDomainDefGetVcpusMax(def)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Number of CPUs in <numa> exceeds the"
+                         " <vcpu> count"));
+        goto error;
+    }
+
+    if (virDomainNumaGetMaxCPUID(def->numa) >= virDomainDefGetVcpusMax(def)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("CPU IDs in <numa> exceed the <vcpu> count"));
+        goto error;
+    }
+
+    if (virDomainNumatuneParseXML(def->numa,
+                                  def->placement_mode ==
+                                  VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC,
+                                  ctxt) < 0)
+        goto error;
+
+    if (virDomainNumatuneHasPlacementAuto(def->numa) &&
+        !def->cpumask && !virDomainDefHasVcpuPin(def) &&
+        !def->cputune.emulatorpin &&
+        !virDomainIOThreadIDArrayHasPin(def))
+        def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO;
+
+    if ((n = virXPathNodeSet("./resource", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("cannot extract resource nodes"));
+        goto error;
+    }
+
+    if (n > 1) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("only one resource element is supported"));
+        goto error;
     }
+
+    if (n &&
+        !(def->resource = virDomainResourceDefParse(nodes[0], ctxt)))
+        goto error;
     VIR_FREE(nodes);
 
+    if (virDomainFeaturesDefParse(def, ctxt) < 0)
+        goto error;
+
     if (virDomainEventActionParseXML(ctxt, "on_reboot",
                                      "string(./on_reboot[1])",
                                      &def->onReboot,