1 /*
2  * domain_capabilities.c: domain capabilities XML processing
3  *
4  * Copyright (C) 2014 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <config.h>
22 
23 #include "device_conf.h"
24 #include "domain_capabilities.h"
25 #include "domain_conf.h"
26 #include "viralloc.h"
27 #include "virstring.h"
28 
29 #define VIR_FROM_THIS VIR_FROM_CAPABILITIES
30 
31 VIR_ENUM_IMPL(virDomainCapsCPUUsable,
32               VIR_DOMCAPS_CPU_USABLE_LAST,
33               "unknown", "yes", "no",
34 );
35 
36 
37 VIR_ENUM_DECL(virDomainCapsFeature);
38 VIR_ENUM_IMPL(virDomainCapsFeature,
39               VIR_DOMAIN_CAPS_FEATURE_LAST,
40               "iothreads",
41               "vmcoreinfo",
42               "genid",
43               "backingStoreInput",
44               "backup",
45               "s390-pv",
46 );
47 
48 static virClass *virDomainCapsClass;
49 static virClass *virDomainCapsCPUModelsClass;
50 
51 static void virDomainCapsDispose(void *obj);
52 static void virDomainCapsCPUModelsDispose(void *obj);
53 
virDomainCapsOnceInit(void)54 static int virDomainCapsOnceInit(void)
55 {
56     if (!VIR_CLASS_NEW(virDomainCaps, virClassForObjectLockable()))
57         return -1;
58 
59     if (!VIR_CLASS_NEW(virDomainCapsCPUModels, virClassForObject()))
60         return -1;
61 
62     return 0;
63 }
64 
65 
66 VIR_ONCE_GLOBAL_INIT(virDomainCaps);
67 
68 
69 void
virSEVCapabilitiesFree(virSEVCapability * cap)70 virSEVCapabilitiesFree(virSEVCapability *cap)
71 {
72     if (!cap)
73         return;
74 
75     g_free(cap->pdh);
76     g_free(cap->cert_chain);
77     g_free(cap);
78 }
79 
80 
81 static void
virDomainCapsDispose(void * obj)82 virDomainCapsDispose(void *obj)
83 {
84     virDomainCaps *caps = obj;
85     virDomainCapsStringValues *values;
86     size_t i;
87 
88     g_free(caps->path);
89     g_free(caps->machine);
90     virObjectUnref(caps->cpu.custom);
91     virCPUDefFree(caps->cpu.hostModel);
92     virSEVCapabilitiesFree(caps->sev);
93 
94     values = &caps->os.loader.values;
95     for (i = 0; i < values->nvalues; i++)
96         g_free(values->values[i]);
97     g_free(values->values);
98 }
99 
100 
101 static void
virDomainCapsCPUModelsDispose(void * obj)102 virDomainCapsCPUModelsDispose(void *obj)
103 {
104     virDomainCapsCPUModels *cpuModels = obj;
105     size_t i;
106 
107     for (i = 0; i < cpuModels->nmodels; i++) {
108         g_free(cpuModels->models[i].name);
109         g_strfreev(cpuModels->models[i].blockers);
110     }
111 
112     g_free(cpuModels->models);
113 }
114 
115 
116 virDomainCaps *
virDomainCapsNew(const char * path,const char * machine,virArch arch,virDomainVirtType virttype)117 virDomainCapsNew(const char *path,
118                  const char *machine,
119                  virArch arch,
120                  virDomainVirtType virttype)
121 {
122     virDomainCaps *caps = NULL;
123 
124     if (virDomainCapsInitialize() < 0)
125         return NULL;
126 
127     if (!(caps = virObjectLockableNew(virDomainCapsClass)))
128         return NULL;
129 
130     caps->path = g_strdup(path);
131     caps->machine = g_strdup(machine);
132     caps->arch = arch;
133     caps->virttype = virttype;
134 
135     return caps;
136 }
137 
138 
139 virDomainCapsCPUModels *
virDomainCapsCPUModelsNew(size_t nmodels)140 virDomainCapsCPUModelsNew(size_t nmodels)
141 {
142     virDomainCapsCPUModels *cpuModels = NULL;
143 
144     if (virDomainCapsInitialize() < 0)
145         return NULL;
146 
147     if (!(cpuModels = virObjectNew(virDomainCapsCPUModelsClass)))
148         return NULL;
149 
150     cpuModels->models = g_new0(virDomainCapsCPUModel, nmodels);
151     cpuModels->nmodels_max = nmodels;
152 
153     return cpuModels;
154 }
155 
156 
157 virDomainCapsCPUModels *
virDomainCapsCPUModelsCopy(virDomainCapsCPUModels * old)158 virDomainCapsCPUModelsCopy(virDomainCapsCPUModels *old)
159 {
160     g_autoptr(virDomainCapsCPUModels) cpuModels = NULL;
161     size_t i;
162 
163     if (!(cpuModels = virDomainCapsCPUModelsNew(old->nmodels)))
164         return NULL;
165 
166     for (i = 0; i < old->nmodels; i++) {
167         if (virDomainCapsCPUModelsAdd(cpuModels,
168                                       old->models[i].name,
169                                       old->models[i].usable,
170                                       old->models[i].blockers,
171                                       old->models[i].deprecated) < 0)
172             return NULL;
173     }
174 
175     return g_steal_pointer(&cpuModels);
176 }
177 
178 
179 int
virDomainCapsCPUModelsAdd(virDomainCapsCPUModels * cpuModels,const char * name,virDomainCapsCPUUsable usable,char ** blockers,bool deprecated)180 virDomainCapsCPUModelsAdd(virDomainCapsCPUModels *cpuModels,
181                           const char *name,
182                           virDomainCapsCPUUsable usable,
183                           char **blockers,
184                           bool deprecated)
185 {
186     g_autofree char * nameCopy = NULL;
187     virDomainCapsCPUModel *cpu;
188 
189     nameCopy = g_strdup(name);
190 
191     VIR_RESIZE_N(cpuModels->models, cpuModels->nmodels_max,
192                  cpuModels->nmodels, 1);
193 
194     cpu = cpuModels->models + cpuModels->nmodels;
195     cpuModels->nmodels++;
196 
197     cpu->usable = usable;
198     cpu->name = g_steal_pointer(&nameCopy);
199     cpu->blockers = g_strdupv(blockers);
200     cpu->deprecated = deprecated;
201 
202     return 0;
203 }
204 
205 
206 virDomainCapsCPUModel *
virDomainCapsCPUModelsGet(virDomainCapsCPUModels * cpuModels,const char * name)207 virDomainCapsCPUModelsGet(virDomainCapsCPUModels *cpuModels,
208                           const char *name)
209 {
210     size_t i;
211 
212     if (!cpuModels)
213         return NULL;
214 
215     for (i = 0; i < cpuModels->nmodels; i++) {
216         if (STREQ(cpuModels->models[i].name, name))
217             return cpuModels->models + i;
218     }
219 
220     return NULL;
221 }
222 
223 
224 int
virDomainCapsEnumSet(virDomainCapsEnum * capsEnum,const char * capsEnumName,size_t nvalues,unsigned int * values)225 virDomainCapsEnumSet(virDomainCapsEnum *capsEnum,
226                      const char *capsEnumName,
227                      size_t nvalues,
228                      unsigned int *values)
229 {
230     size_t i;
231 
232     for (i = 0; i < nvalues; i++) {
233         unsigned int val = 1 << values[i];
234 
235         if (!val) {
236             /* Integer overflow */
237             virReportError(VIR_ERR_INTERNAL_ERROR,
238                            _("integer overflow on %s. Please contact the "
239                              "libvirt development team at libvir-list@redhat.com"),
240                            capsEnumName);
241             return -1;
242         }
243 
244         capsEnum->values |= val;
245     }
246 
247     return 0;
248 }
249 
250 
251 void
virDomainCapsEnumClear(virDomainCapsEnum * capsEnum)252 virDomainCapsEnumClear(virDomainCapsEnum *capsEnum)
253 {
254     capsEnum->values = 0;
255 }
256 
257 
258 static int
virDomainCapsEnumFormat(virBuffer * buf,const virDomainCapsEnum * capsEnum,const char * capsEnumName,virDomainCapsValToStr valToStr)259 virDomainCapsEnumFormat(virBuffer *buf,
260                         const virDomainCapsEnum *capsEnum,
261                         const char *capsEnumName,
262                         virDomainCapsValToStr valToStr)
263 {
264     size_t i;
265 
266     if (!capsEnum->report)
267         return 0;
268 
269     virBufferAsprintf(buf, "<enum name='%s'", capsEnumName);
270     if (!capsEnum->values) {
271         virBufferAddLit(buf, "/>\n");
272         return 0;
273     }
274     virBufferAddLit(buf, ">\n");
275     virBufferAdjustIndent(buf, 2);
276 
277     for (i = 0; i < sizeof(capsEnum->values) * CHAR_BIT; i++) {
278         const char *val;
279 
280         if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(*capsEnum, i))
281             continue;
282 
283         if ((val = (valToStr)(i)))
284             virBufferAsprintf(buf, "<value>%s</value>\n", val);
285     }
286     virBufferAdjustIndent(buf, -2);
287     virBufferAddLit(buf, "</enum>\n");
288 
289     return 0;
290 }
291 
292 
293 static void
virDomainCapsStringValuesFormat(virBuffer * buf,const virDomainCapsStringValues * values)294 virDomainCapsStringValuesFormat(virBuffer *buf,
295                                 const virDomainCapsStringValues *values)
296 {
297     size_t i;
298 
299     for (i = 0; i < values->nvalues; i++)
300         virBufferEscapeString(buf, "<value>%s</value>\n", values->values[i]);
301 }
302 
303 
304 #define FORMAT_PROLOGUE(item) \
305     do { \
306         if (item->supported == VIR_TRISTATE_BOOL_ABSENT) \
307             return; \
308         virBufferAsprintf(buf, "<" #item " supported='%s'%s\n", \
309                 (item->supported == VIR_TRISTATE_BOOL_YES) ? "yes" : "no", \
310                 (item->supported == VIR_TRISTATE_BOOL_YES) ? ">" : "/>"); \
311         if (item->supported == VIR_TRISTATE_BOOL_NO) \
312             return; \
313         virBufferAdjustIndent(buf, 2); \
314     } while (0)
315 
316 #define FORMAT_EPILOGUE(item) \
317     do { \
318         virBufferAdjustIndent(buf, -2); \
319         virBufferAddLit(buf, "</" #item ">\n"); \
320     } while (0)
321 
322 #define ENUM_PROCESS(master, capsEnum, valToStr) \
323     do { \
324         virDomainCapsEnumFormat(buf, &master->capsEnum, \
325                                 #capsEnum, valToStr); \
326     } while (0)
327 
328 
329 static void
qemuDomainCapsFeatureFormatSimple(virBuffer * buf,const char * featurename,virTristateBool supported)330 qemuDomainCapsFeatureFormatSimple(virBuffer *buf,
331                                   const char *featurename,
332                                   virTristateBool supported)
333 {
334     if (supported == VIR_TRISTATE_BOOL_ABSENT)
335         return;
336 
337     virBufferAsprintf(buf, "<%s supported='%s'/>\n", featurename,
338                       virTristateBoolTypeToString(supported));
339 }
340 
341 
342 static void
virDomainCapsLoaderFormat(virBuffer * buf,const virDomainCapsLoader * loader)343 virDomainCapsLoaderFormat(virBuffer *buf,
344                           const virDomainCapsLoader *loader)
345 {
346     FORMAT_PROLOGUE(loader);
347 
348     virDomainCapsStringValuesFormat(buf, &loader->values);
349     ENUM_PROCESS(loader, type, virDomainLoaderTypeToString);
350     ENUM_PROCESS(loader, readonly, virTristateBoolTypeToString);
351     ENUM_PROCESS(loader, secure, virTristateBoolTypeToString);
352 
353     FORMAT_EPILOGUE(loader);
354 }
355 
356 static void
virDomainCapsOSFormat(virBuffer * buf,const virDomainCapsOS * os)357 virDomainCapsOSFormat(virBuffer *buf,
358                       const virDomainCapsOS *os)
359 {
360     const virDomainCapsLoader *loader = &os->loader;
361 
362     FORMAT_PROLOGUE(os);
363 
364     ENUM_PROCESS(os, firmware, virDomainOsDefFirmwareTypeToString);
365 
366     virDomainCapsLoaderFormat(buf, loader);
367 
368     FORMAT_EPILOGUE(os);
369 }
370 
371 static void
virDomainCapsCPUCustomFormat(virBuffer * buf,virDomainCapsCPUModels * custom)372 virDomainCapsCPUCustomFormat(virBuffer *buf,
373                              virDomainCapsCPUModels *custom)
374 {
375     size_t i;
376 
377     virBufferAdjustIndent(buf, 2);
378 
379     for (i = 0; i < custom->nmodels; i++) {
380         virDomainCapsCPUModel *model = custom->models + i;
381         virBufferAsprintf(buf, "<model usable='%s'",
382                           virDomainCapsCPUUsableTypeToString(model->usable));
383         if (model->deprecated)
384             virBufferAddLit(buf, " deprecated='yes'");
385         virBufferAsprintf(buf, ">%s</model>\n",
386                           model->name);
387     }
388 
389     virBufferAdjustIndent(buf, -2);
390 }
391 
392 static void
virDomainCapsCPUFormat(virBuffer * buf,const virDomainCapsCPU * cpu)393 virDomainCapsCPUFormat(virBuffer *buf,
394                        const virDomainCapsCPU *cpu)
395 {
396     virBufferAddLit(buf, "<cpu>\n");
397     virBufferAdjustIndent(buf, 2);
398 
399     virBufferAsprintf(buf, "<mode name='%s' supported='%s'",
400                       virCPUModeTypeToString(VIR_CPU_MODE_HOST_PASSTHROUGH),
401                       cpu->hostPassthrough ? "yes" : "no");
402 
403     if (cpu->hostPassthrough && cpu->hostPassthroughMigratable.report) {
404         virBufferAddLit(buf, ">\n");
405         virBufferAdjustIndent(buf, 2);
406         ENUM_PROCESS(cpu, hostPassthroughMigratable,
407                      virTristateSwitchTypeToString);
408         virBufferAdjustIndent(buf, -2);
409         virBufferAddLit(buf, "</mode>\n");
410     } else {
411         virBufferAddLit(buf, "/>\n");
412     }
413 
414     virBufferAsprintf(buf, "<mode name='%s' supported='%s'",
415                       virCPUModeTypeToString(VIR_CPU_MODE_MAXIMUM),
416                       cpu->maximum ? "yes" : "no");
417 
418     if (cpu->maximum && cpu->maximumMigratable.report) {
419         virBufferAddLit(buf, ">\n");
420         virBufferAdjustIndent(buf, 2);
421         ENUM_PROCESS(cpu, maximumMigratable,
422                      virTristateSwitchTypeToString);
423         virBufferAdjustIndent(buf, -2);
424         virBufferAddLit(buf, "</mode>\n");
425     } else {
426         virBufferAddLit(buf, "/>\n");
427     }
428 
429     virBufferAsprintf(buf, "<mode name='%s' ",
430                       virCPUModeTypeToString(VIR_CPU_MODE_HOST_MODEL));
431     if (cpu->hostModel) {
432         virBufferAddLit(buf, "supported='yes'>\n");
433         virBufferAdjustIndent(buf, 2);
434 
435         virCPUDefFormatBuf(buf, cpu->hostModel);
436 
437         virBufferAdjustIndent(buf, -2);
438         virBufferAddLit(buf, "</mode>\n");
439     } else {
440         virBufferAddLit(buf, "supported='no'/>\n");
441     }
442 
443     virBufferAsprintf(buf, "<mode name='%s' ",
444                       virCPUModeTypeToString(VIR_CPU_MODE_CUSTOM));
445     if (cpu->custom && cpu->custom->nmodels) {
446         virBufferAddLit(buf, "supported='yes'>\n");
447         virDomainCapsCPUCustomFormat(buf, cpu->custom);
448         virBufferAddLit(buf, "</mode>\n");
449     } else {
450         virBufferAddLit(buf, "supported='no'/>\n");
451     }
452 
453     virBufferAdjustIndent(buf, -2);
454     virBufferAddLit(buf, "</cpu>\n");
455 }
456 
457 static void
virDomainCapsMemoryBackingFormat(virBuffer * buf,const virDomainCapsMemoryBacking * memoryBacking)458 virDomainCapsMemoryBackingFormat(virBuffer *buf,
459                                  const virDomainCapsMemoryBacking *memoryBacking)
460 {
461     FORMAT_PROLOGUE(memoryBacking);
462 
463     ENUM_PROCESS(memoryBacking, sourceType, virDomainMemorySourceTypeToString);
464 
465     FORMAT_EPILOGUE(memoryBacking);
466 }
467 
468 
469 static void
virDomainCapsDeviceDiskFormat(virBuffer * buf,const virDomainCapsDeviceDisk * disk)470 virDomainCapsDeviceDiskFormat(virBuffer *buf,
471                               const virDomainCapsDeviceDisk *disk)
472 {
473     FORMAT_PROLOGUE(disk);
474 
475     ENUM_PROCESS(disk, diskDevice, virDomainDiskDeviceTypeToString);
476     ENUM_PROCESS(disk, bus, virDomainDiskBusTypeToString);
477     ENUM_PROCESS(disk, model, virDomainDiskModelTypeToString);
478 
479     FORMAT_EPILOGUE(disk);
480 }
481 
482 
483 static void
virDomainCapsDeviceGraphicsFormat(virBuffer * buf,const virDomainCapsDeviceGraphics * graphics)484 virDomainCapsDeviceGraphicsFormat(virBuffer *buf,
485                                   const virDomainCapsDeviceGraphics *graphics)
486 {
487     FORMAT_PROLOGUE(graphics);
488 
489     ENUM_PROCESS(graphics, type, virDomainGraphicsTypeToString);
490 
491     FORMAT_EPILOGUE(graphics);
492 }
493 
494 
495 static void
virDomainCapsDeviceVideoFormat(virBuffer * buf,const virDomainCapsDeviceVideo * video)496 virDomainCapsDeviceVideoFormat(virBuffer *buf,
497                                const virDomainCapsDeviceVideo *video)
498 {
499     FORMAT_PROLOGUE(video);
500 
501     ENUM_PROCESS(video, modelType, virDomainVideoTypeToString);
502 
503     FORMAT_EPILOGUE(video);
504 }
505 
506 
507 static void
virDomainCapsDeviceHostdevFormat(virBuffer * buf,const virDomainCapsDeviceHostdev * hostdev)508 virDomainCapsDeviceHostdevFormat(virBuffer *buf,
509                                  const virDomainCapsDeviceHostdev *hostdev)
510 {
511     FORMAT_PROLOGUE(hostdev);
512 
513     ENUM_PROCESS(hostdev, mode, virDomainHostdevModeTypeToString);
514     ENUM_PROCESS(hostdev, startupPolicy, virDomainStartupPolicyTypeToString);
515     ENUM_PROCESS(hostdev, subsysType, virDomainHostdevSubsysTypeToString);
516     ENUM_PROCESS(hostdev, capsType, virDomainHostdevCapsTypeToString);
517     ENUM_PROCESS(hostdev, pciBackend, virDomainHostdevSubsysPCIBackendTypeToString);
518 
519     FORMAT_EPILOGUE(hostdev);
520 }
521 
522 
523 static void
virDomainCapsDeviceRNGFormat(virBuffer * buf,const virDomainCapsDeviceRNG * rng)524 virDomainCapsDeviceRNGFormat(virBuffer *buf,
525                              const virDomainCapsDeviceRNG *rng)
526 {
527     FORMAT_PROLOGUE(rng);
528 
529     ENUM_PROCESS(rng, model, virDomainRNGModelTypeToString);
530     ENUM_PROCESS(rng, backendModel, virDomainRNGBackendTypeToString);
531 
532     FORMAT_EPILOGUE(rng);
533 }
534 
535 
536 static void
virDomainCapsDeviceFilesystemFormat(virBuffer * buf,const virDomainCapsDeviceFilesystem * filesystem)537 virDomainCapsDeviceFilesystemFormat(virBuffer *buf,
538                                     const virDomainCapsDeviceFilesystem *filesystem)
539 {
540     FORMAT_PROLOGUE(filesystem);
541 
542     ENUM_PROCESS(filesystem, driverType, virDomainFSDriverTypeToString);
543 
544     FORMAT_EPILOGUE(filesystem);
545 }
546 
547 
548 /**
549  * virDomainCapsFeatureGICFormat:
550  * @buf: target buffer
551  * @gic: GIC features
552  *
553  * Format GIC features for inclusion in the domcapabilities XML.
554  *
555  * The resulting XML will look like
556  *
557  *   <gic supported='yes'>
558  *     <enum name='version>
559  *       <value>2</value>
560  *       <value>3</value>
561  *     </enum>
562  *   </gic>
563  */
564 static void
virDomainCapsFeatureGICFormat(virBuffer * buf,const virDomainCapsFeatureGIC * gic)565 virDomainCapsFeatureGICFormat(virBuffer *buf,
566                               const virDomainCapsFeatureGIC *gic)
567 {
568     FORMAT_PROLOGUE(gic);
569 
570     ENUM_PROCESS(gic, version, virGICVersionTypeToString);
571 
572     FORMAT_EPILOGUE(gic);
573 }
574 
575 static void
virDomainCapsFeatureSEVFormat(virBuffer * buf,const virSEVCapability * sev)576 virDomainCapsFeatureSEVFormat(virBuffer *buf,
577                               const virSEVCapability *sev)
578 {
579     if (!sev) {
580         virBufferAddLit(buf, "<sev supported='no'/>\n");
581     } else {
582         virBufferAddLit(buf, "<sev supported='yes'>\n");
583         virBufferAdjustIndent(buf, 2);
584         virBufferAsprintf(buf, "<cbitpos>%d</cbitpos>\n", sev->cbitpos);
585         virBufferAsprintf(buf, "<reducedPhysBits>%d</reducedPhysBits>\n",
586                           sev->reduced_phys_bits);
587         virBufferAdjustIndent(buf, -2);
588         virBufferAddLit(buf, "</sev>\n");
589     }
590 
591     return;
592 }
593 
594 
595 static void
virDomainCapsFormatFeatures(const virDomainCaps * caps,virBuffer * buf)596 virDomainCapsFormatFeatures(const virDomainCaps *caps,
597                             virBuffer *buf)
598 {
599     g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
600     size_t i;
601 
602     virDomainCapsFeatureGICFormat(&childBuf, &caps->gic);
603 
604     for (i = 0; i < VIR_DOMAIN_CAPS_FEATURE_LAST; i++) {
605         if (i == VIR_DOMAIN_CAPS_FEATURE_IOTHREADS)
606             continue;
607 
608         qemuDomainCapsFeatureFormatSimple(&childBuf,
609                                           virDomainCapsFeatureTypeToString(i),
610                                           caps->features[i]);
611     }
612 
613     virDomainCapsFeatureSEVFormat(&childBuf, caps->sev);
614 
615     virXMLFormatElement(buf, "features", NULL, &childBuf);
616 }
617 
618 
619 char *
virDomainCapsFormat(const virDomainCaps * caps)620 virDomainCapsFormat(const virDomainCaps *caps)
621 {
622     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
623     const char *virttype_str = virDomainVirtTypeToString(caps->virttype);
624     const char *arch_str = virArchToString(caps->arch);
625 
626     virBufferAddLit(&buf, "<domainCapabilities>\n");
627     virBufferAdjustIndent(&buf, 2);
628 
629     virBufferEscapeString(&buf, "<path>%s</path>\n", caps->path);
630     virBufferAsprintf(&buf, "<domain>%s</domain>\n", virttype_str);
631     if (caps->machine)
632         virBufferAsprintf(&buf, "<machine>%s</machine>\n", caps->machine);
633     virBufferAsprintf(&buf, "<arch>%s</arch>\n", arch_str);
634 
635     if (caps->maxvcpus)
636         virBufferAsprintf(&buf, "<vcpu max='%d'/>\n", caps->maxvcpus);
637 
638     qemuDomainCapsFeatureFormatSimple(&buf, "iothreads",
639                                       caps->features[VIR_DOMAIN_CAPS_FEATURE_IOTHREADS]);
640 
641     virDomainCapsOSFormat(&buf, &caps->os);
642     virDomainCapsCPUFormat(&buf, &caps->cpu);
643 
644     virDomainCapsMemoryBackingFormat(&buf, &caps->memoryBacking);
645 
646     virBufferAddLit(&buf, "<devices>\n");
647     virBufferAdjustIndent(&buf, 2);
648 
649     virDomainCapsDeviceDiskFormat(&buf, &caps->disk);
650     virDomainCapsDeviceGraphicsFormat(&buf, &caps->graphics);
651     virDomainCapsDeviceVideoFormat(&buf, &caps->video);
652     virDomainCapsDeviceHostdevFormat(&buf, &caps->hostdev);
653     virDomainCapsDeviceRNGFormat(&buf, &caps->rng);
654     virDomainCapsDeviceFilesystemFormat(&buf, &caps->filesystem);
655 
656     virBufferAdjustIndent(&buf, -2);
657     virBufferAddLit(&buf, "</devices>\n");
658 
659     virDomainCapsFormatFeatures(caps, &buf);
660 
661     virBufferAdjustIndent(&buf, -2);
662     virBufferAddLit(&buf, "</domainCapabilities>\n");
663 
664     return virBufferContentAndReset(&buf);
665 }
666