mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-06 21:15:22 +00:00
cd1e837c22
If flag VIR_CONNECT_GET_DOMAIN_CAPABILITIES_DISABLE_DEPRECATED_FEATURES is passed to qemuConnectGetDomainCapabilities, then the domain's CPU model features will be updated to set any deprecated features to the 'disabled' policy. Signed-off-by: Collin Walling <walling@linux.ibm.com> Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
7061 lines
237 KiB
C
7061 lines
237 KiB
C
/*
|
|
* qemu_capabilities.c: QEMU capabilities generation
|
|
*
|
|
* Copyright (C) 2006-2016 Red Hat, Inc.
|
|
* Copyright (C) 2006 Daniel P. Berrange
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "qemu_capabilities.h"
|
|
#include "viralloc.h"
|
|
#include "virarch.h"
|
|
#include "virlog.h"
|
|
#include "virerror.h"
|
|
#include "virfile.h"
|
|
#include "virfilecache.h"
|
|
#include "cpu/cpu.h"
|
|
#include "cpu/cpu_x86.h"
|
|
#include "domain_conf.h"
|
|
#include "virbitmap.h"
|
|
#include "virnodesuspend.h"
|
|
#include "virhostcpu.h"
|
|
#include "qemu_monitor.h"
|
|
#include "virstring.h"
|
|
#include "qemu_hostdev.h"
|
|
#include "qemu_domain.h"
|
|
#define LIBVIRT_QEMU_CAPSPRIV_H_ALLOW
|
|
#include "qemu_capspriv.h"
|
|
#include "qemu_qapi.h"
|
|
#include "qemu_process.h"
|
|
#include "qemu_firmware.h"
|
|
#include "virutil.h"
|
|
#include "virtpm.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <sys/utsname.h>
|
|
#ifdef __APPLE__
|
|
# include <sys/types.h>
|
|
# include <sys/sysctl.h>
|
|
#endif
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_QEMU
|
|
|
|
VIR_LOG_INIT("qemu.qemu_capabilities");
|
|
|
|
/* While not public, these strings must not change. They
|
|
* are used in domain status files which are read on
|
|
* daemon restarts
|
|
*/
|
|
VIR_ENUM_IMPL(virQEMUCaps,
|
|
QEMU_CAPS_LAST, /* virQEMUCaps grouping marker */
|
|
/* 0 */
|
|
"vnc-colon", /* X_QEMU_CAPS_VNC_COLON */
|
|
"no-reboot", /* X_QEMU_CAPS_NO_REBOOT */
|
|
"drive", /* X_QEMU_CAPS_DRIVE */
|
|
"drive-boot", /* X_QEMU_CAPS_DRIVE_BOOT */
|
|
"name", /* X_QEMU_CAPS_NAME */
|
|
|
|
/* 5 */
|
|
"uuid", /* X_QEMU_CAPS_UUID */
|
|
"domid", /* X_QEMU_CAPS_DOMID */
|
|
"vnet-hdr", /* X_QEMU_CAPS_VNET_HDR */
|
|
"migrate-kvm-stdio", /* X_QEMU_CAPS_MIGRATE_KVM_STDIO */
|
|
"migrate-qemu-tcp", /* X_QEMU_CAPS_MIGRATE_QEMU_TCP */
|
|
|
|
/* 10 */
|
|
"migrate-qemu-exec", /* X_QEMU_CAPS_MIGRATE_QEMU_EXEC */
|
|
"drive-cache-v2", /* X_QEMU_CAPS_DRIVE_CACHE_V2 */
|
|
"kvm", /* QEMU_CAPS_KVM */
|
|
"drive-format", /* X_QEMU_CAPS_DRIVE_FORMAT */
|
|
"vga", /* X_QEMU_CAPS_VGA */
|
|
|
|
/* 15 */
|
|
"0.10", /* X_QEMU_CAPS_0_10 */
|
|
"pci-device", /* X_QEMU_CAPS_PCIDEVICE */
|
|
"mem-path", /* X_QEMU_CAPS_MEM_PATH */
|
|
"drive-serial", /* X_QEMU_CAPS_DRIVE_SERIAL */
|
|
"xen-domid", /* X_QEMU_CAPS_XEN_DOMID */
|
|
|
|
/* 20 */
|
|
"migrate-qemu-unix", /* X_QEMU_CAPS_MIGRATE_QEMU_UNIX */
|
|
"chardev", /* X_QEMU_CAPS_CHARDEV */
|
|
"enable-kvm", /* X_QEMU_CAPS_ENABLE_KVM */
|
|
"monitor-json", /* X_QEMU_CAPS_MONITOR_JSON */
|
|
"balloon", /* X_QEMU_CAPS_BALLOON */
|
|
|
|
/* 25 */
|
|
"device", /* X_QEMU_CAPS_DEVICE */
|
|
"sdl", /* QEMU_CAPS_SDL */
|
|
"smp-topology", /* X_QEMU_CAPS_SMP_TOPOLOGY */
|
|
"netdev", /* X_QEMU_CAPS_NETDEV */
|
|
"rtc", /* X_QEMU_CAPS_RTC */
|
|
|
|
/* 30 */
|
|
"vhost-net", /* X_QEMU_CAPS_VHOST_NET */
|
|
"rtc-td-hack", /* X_QEMU_CAPS_RTC_TD_HACK */
|
|
"no-hpet", /* X_QEMU_CAPS_NO_HPET */
|
|
"no-kvm-pit", /* X_QEMU_CAPS_NO_KVM_PIT */
|
|
"tdf", /* X_QEMU_CAPS_TDF */
|
|
|
|
/* 35 */
|
|
"pci-configfd", /* X_QEMU_CAPS_PCI_CONFIGFD */
|
|
"nodefconfig", /* X_QEMU_CAPS_NODEFCONFIG */
|
|
"boot-menu", /* X_QEMU_CAPS_BOOT_MENU */
|
|
"fsdev", /* X_QEMU_CAPS_FSDEV */
|
|
"nesting", /* X_QEMU_CAPS_NESTING */
|
|
|
|
/* 40 */
|
|
"name-process", /* X_QEMU_CAPS_NAME_PROCESS */
|
|
"drive-readonly", /* X_QEMU_CAPS_DRIVE_READONLY */
|
|
"smbios-type", /* X_QEMU_CAPS_SMBIOS_TYPE */
|
|
"vga-qxl", /* X_QEMU_CAPS_VGA_QXL */
|
|
"spice", /* QEMU_CAPS_SPICE */
|
|
|
|
/* 45 */
|
|
"vga-none", /* X_QEMU_CAPS_VGA_NONE */
|
|
"migrate-qemu-fd", /* X_QEMU_CAPS_MIGRATE_QEMU_FD */
|
|
"boot-index", /* X_QEMU_CAPS_BOOTINDEX */
|
|
"hda-duplex", /* QEMU_CAPS_HDA_DUPLEX */
|
|
"drive-aio", /* X_QEMU_CAPS_DRIVE_AIO */
|
|
|
|
/* 50 */
|
|
"pci-multibus", /* X_QEMU_CAPS_PCI_MULTIBUS */
|
|
"pci-bootindex", /* X_QEMU_CAPS_PCI_BOOTINDEX */
|
|
"ccid-emulated", /* QEMU_CAPS_CCID_EMULATED */
|
|
"ccid-passthru", /* QEMU_CAPS_CCID_PASSTHRU */
|
|
"chardev-spicevmc", /* X_QEMU_CAPS_CHARDEV_SPICEVMC */
|
|
|
|
/* 55 */
|
|
"device-spicevmc", /* X_QEMU_CAPS_DEVICE_SPICEVMC */
|
|
"virtio-tx-alg", /* X_QEMU_CAPS_VIRTIO_TX_ALG */
|
|
"device-qxl-vga", /* X_QEMU_CAPS_DEVICE_QXL_VGA */
|
|
"pci-multifunction", /* X_QEMU_CAPS_PCI_MULTIFUNCTION */
|
|
"virtio-blk-pci.ioeventfd", /* X_QEMU_CAPS_VIRTIO_IOEVENTFD */
|
|
|
|
/* 60 */
|
|
"sga", /* X_QEMU_CAPS_SGA */
|
|
"virtio-blk-pci.event_idx", /* X_QEMU_CAPS_VIRTIO_BLK_EVENT_IDX */
|
|
"virtio-net-pci.event_idx", /* X_QEMU_CAPS_VIRTIO_NET_EVENT_IDX */
|
|
"cache-directsync", /* X_QEMU_CAPS_DRIVE_CACHE_DIRECTSYNC */
|
|
"piix3-usb-uhci", /* QEMU_CAPS_PIIX3_USB_UHCI */
|
|
|
|
/* 65 */
|
|
"piix4-usb-uhci", /* QEMU_CAPS_PIIX4_USB_UHCI */
|
|
"usb-ehci", /* QEMU_CAPS_USB_EHCI */
|
|
"ich9-usb-ehci1", /* QEMU_CAPS_ICH9_USB_EHCI1 */
|
|
"vt82c686b-usb-uhci", /* QEMU_CAPS_VT82C686B_USB_UHCI */
|
|
"pci-ohci", /* QEMU_CAPS_PCI_OHCI */
|
|
|
|
/* 70 */
|
|
"usb-redir", /* QEMU_CAPS_USB_REDIR */
|
|
"usb-hub", /* QEMU_CAPS_USB_HUB */
|
|
"no-shutdown", /* X_QEMU_CAPS_NO_SHUTDOWN */
|
|
"cache-unsafe", /* X_QEMU_CAPS_DRIVE_CACHE_UNSAFE */
|
|
"rombar", /* X_QEMU_CAPS_PCI_ROMBAR */
|
|
|
|
/* 75 */
|
|
"ich9-ahci", /* QEMU_CAPS_ICH9_AHCI */
|
|
"no-acpi", /* X_QEMU_CAPS_NO_ACPI */
|
|
"fsdev-readonly", /* X_QEMU_CAPS_FSDEV_READONLY */
|
|
"virtio-blk-pci.scsi", /* QEMU_CAPS_VIRTIO_BLK_SCSI */
|
|
"blk-sg-io", /* X_QEMU_CAPS_VIRTIO_BLK_SG_IO */
|
|
|
|
/* 80 */
|
|
"drive-copy-on-read", /* X_QEMU_CAPS_DRIVE_COPY_ON_READ */
|
|
"cpu-host", /* X_QEMU_CAPS_CPU_HOST */
|
|
"fsdev-writeout", /* X_QEMU_CAPS_FSDEV_WRITEOUT */
|
|
"drive-iotune", /* X_QEMU_CAPS_DRIVE_IOTUNE */
|
|
"system_wakeup", /* X_QEMU_CAPS_WAKEUP */
|
|
|
|
/* 85 */
|
|
"scsi-disk.channel", /* QEMU_CAPS_SCSI_DISK_CHANNEL */
|
|
"scsi-block", /* QEMU_CAPS_SCSI_BLOCK */
|
|
"transaction", /* X_QEMU_CAPS_TRANSACTION */
|
|
"block-job-sync", /* X_QEMU_CAPS_BLOCKJOB_SYNC */
|
|
"block-job-async", /* X_QEMU_CAPS_BLOCKJOB_ASYNC */
|
|
|
|
/* 90 */
|
|
"scsi-cd", /* X_QEMU_CAPS_SCSI_CD */
|
|
"ide-cd", /* X_QEMU_CAPS_IDE_CD */
|
|
"no-user-config", /* X_QEMU_CAPS_NO_USER_CONFIG */
|
|
"hda-micro", /* QEMU_CAPS_HDA_MICRO */
|
|
"dump-guest-memory", /* X_QEMU_CAPS_DUMP_GUEST_MEMORY */
|
|
|
|
/* 95 */
|
|
"nec-usb-xhci", /* QEMU_CAPS_NEC_USB_XHCI */
|
|
"virtio-s390", /* X_QEMU_CAPS_VIRTIO_S390 */
|
|
"balloon-event", /* X_QEMU_CAPS_BALLOON_EVENT */
|
|
"bridge", /* X_QEMU_CAPS_NETDEV_BRIDGE */
|
|
"lsi", /* QEMU_CAPS_SCSI_LSI */
|
|
|
|
/* 100 */
|
|
"virtio-scsi-pci", /* QEMU_CAPS_VIRTIO_SCSI */
|
|
"blockio", /* X_QEMU_CAPS_BLOCKIO */
|
|
"disable-s3", /* X_QEMU_CAPS_PIIX_DISABLE_S3 */
|
|
"disable-s4", /* X_QEMU_CAPS_PIIX_DISABLE_S4 */
|
|
"usb-redir.filter", /* QEMU_CAPS_USB_REDIR_FILTER */
|
|
|
|
/* 105 */
|
|
"ide-drive.wwn", /* X_QEMU_CAPS_IDE_DRIVE_WWN */
|
|
"scsi-disk.wwn", /* X_QEMU_CAPS_SCSI_DISK_WWN */
|
|
"seccomp-sandbox", /* QEMU_CAPS_SECCOMP_SANDBOX */
|
|
"reboot-timeout", /* X_QEMU_CAPS_REBOOT_TIMEOUT */
|
|
"dump-guest-core", /* X_QEMU_CAPS_DUMP_GUEST_CORE */
|
|
|
|
/* 110 */
|
|
"seamless-migration", /* X_QEMU_CAPS_SEAMLESS_MIGRATION */
|
|
"block-commit", /* X_QEMU_CAPS_BLOCK_COMMIT */
|
|
"vnc", /* QEMU_CAPS_VNC */
|
|
"drive-mirror", /* X_QEMU_CAPS_DRIVE_MIRROR */
|
|
"usb-redir.bootindex", /* X_QEMU_CAPS_USB_REDIR_BOOTINDEX */
|
|
|
|
/* 115 */
|
|
"usb-host.bootindex", /* X_QEMU_CAPS_USB_HOST_BOOTINDEX */
|
|
"blockdev-snapshot-sync", /* X_QEMU_CAPS_DISK_SNAPSHOT */
|
|
"qxl", /* QEMU_CAPS_DEVICE_QXL */
|
|
"VGA", /* QEMU_CAPS_DEVICE_VGA */
|
|
"cirrus-vga", /* QEMU_CAPS_DEVICE_CIRRUS_VGA */
|
|
|
|
/* 120 */
|
|
"vmware-svga", /* QEMU_CAPS_DEVICE_VMWARE_SVGA */
|
|
"device-video-primary", /* X_QEMU_CAPS_DEVICE_VIDEO_PRIMARY */
|
|
"s390-sclp", /* QEMU_CAPS_DEVICE_SCLPCONSOLE */
|
|
"usb-serial", /* QEMU_CAPS_DEVICE_USB_SERIAL */
|
|
"usb-net", /* X_QEMU_CAPS_DEVICE_USB_NET */
|
|
|
|
/* 125 */
|
|
"add-fd", /* X_QEMU_CAPS_ADD_FD */
|
|
"nbd-server", /* X_QEMU_CAPS_NBD_SERVER */
|
|
"virtio-rng", /* QEMU_CAPS_DEVICE_VIRTIO_RNG */
|
|
"rng-random", /* QEMU_CAPS_OBJECT_RNG_RANDOM */
|
|
"rng-egd", /* QEMU_CAPS_OBJECT_RNG_EGD */
|
|
|
|
/* 130 */
|
|
"virtio-ccw", /* QEMU_CAPS_VIRTIO_CCW */
|
|
"dtb", /* X_QEMU_CAPS_DTB */
|
|
"megasas", /* QEMU_CAPS_SCSI_MEGASAS */
|
|
"ipv6-migration", /* X_QEMU_CAPS_IPV6_MIGRATION */
|
|
"machine-opt", /* X_QEMU_CAPS_MACHINE_OPT */
|
|
|
|
/* 135 */
|
|
"machine-usb-opt", /* X_QEMU_CAPS_MACHINE_USB_OPT */
|
|
"tpm-passthrough", /* QEMU_CAPS_DEVICE_TPM_PASSTHROUGH */
|
|
"tpm-tis", /* QEMU_CAPS_DEVICE_TPM_TIS */
|
|
"nvram", /* QEMU_CAPS_DEVICE_NVRAM */
|
|
"pci-bridge", /* QEMU_CAPS_DEVICE_PCI_BRIDGE */
|
|
|
|
/* 140 */
|
|
"vfio-pci", /* QEMU_CAPS_DEVICE_VFIO_PCI */
|
|
"vfio-pci.bootindex", /* X_QEMU_CAPS_VFIO_PCI_BOOTINDEX */
|
|
"scsi-generic", /* X_QEMU_CAPS_DEVICE_SCSI_GENERIC */
|
|
"scsi-generic.bootindex", /* X_QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX */
|
|
"mem-merge", /* X_QEMU_CAPS_MEM_MERGE */
|
|
|
|
/* 145 */
|
|
"vnc-websocket", /* X_QEMU_CAPS_VNC_WEBSOCKET */
|
|
"drive-discard", /* X_QEMU_CAPS_DRIVE_DISCARD */
|
|
"mlock", /* X_QEMU_CAPS_REALTIME_MLOCK */
|
|
"vnc-share-policy", /* X_QEMU_CAPS_VNC_SHARE_POLICY */
|
|
"device-del-event", /* X_QEMU_CAPS_DEVICE_DEL_EVENT */
|
|
|
|
/* 150 */
|
|
"dmi-to-pci-bridge", /* QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE */
|
|
"i440fx-pci-hole64-size", /* X_QEMU_CAPS_I440FX_PCI_HOLE64_SIZE */
|
|
"q35-pci-hole64-size", /* X_QEMU_CAPS_Q35_PCI_HOLE64_SIZE */
|
|
"usb-storage", /* QEMU_CAPS_DEVICE_USB_STORAGE */
|
|
"usb-storage.removable", /* X_QEMU_CAPS_USB_STORAGE_REMOVABLE */
|
|
|
|
/* 155 */
|
|
"virtio-mmio", /* QEMU_CAPS_DEVICE_VIRTIO_MMIO */
|
|
"ich9-intel-hda", /* QEMU_CAPS_DEVICE_ICH9_INTEL_HDA */
|
|
"kvm-pit-lost-tick-policy", /* QEMU_CAPS_KVM_PIT_TICK_POLICY */
|
|
"boot-strict", /* X_QEMU_CAPS_BOOT_STRICT */
|
|
"pvpanic", /* QEMU_CAPS_DEVICE_PANIC */
|
|
|
|
/* 160 */
|
|
"enable-fips", /* X_QEMU_CAPS_ENABLE_FIPS */
|
|
"spice-file-xfer-disable", /* X_QEMU_CAPS_SPICE_FILE_XFER_DISABLE */
|
|
"spiceport", /* X_QEMU_CAPS_CHARDEV_SPICEPORT */
|
|
"usb-kbd", /* QEMU_CAPS_DEVICE_USB_KBD */
|
|
"host-pci-multidomain", /* X_QEMU_CAPS_HOST_PCI_MULTIDOMAIN */
|
|
|
|
/* 165 */
|
|
"msg-timestamp", /* X_QEMU_CAPS_MSG_TIMESTAMP */
|
|
"active-commit", /* X_QEMU_CAPS_ACTIVE_COMMIT */
|
|
"change-backing-file", /* X_QEMU_CAPS_CHANGE_BACKING_FILE */
|
|
"memory-backend-ram", /* X_QEMU_CAPS_OBJECT_MEMORY_RAM */
|
|
"numa", /* X_QEMU_CAPS_NUMA */
|
|
|
|
/* 170 */
|
|
"memory-backend-file", /* X_QEMU_CAPS_OBJECT_MEMORY_FILE */
|
|
"usb-audio", /* QEMU_CAPS_OBJECT_USB_AUDIO */
|
|
"rtc-reset-reinjection", /* QEMU_CAPS_RTC_RESET_REINJECTION */
|
|
"splash-timeout", /* X_QEMU_CAPS_SPLASH_TIMEOUT */
|
|
"iothread", /* X_QEMU_CAPS_OBJECT_IOTHREAD */
|
|
|
|
/* 175 */
|
|
"migrate-rdma", /* QEMU_CAPS_MIGRATE_RDMA */
|
|
"ivshmem", /* X_QEMU_CAPS_DEVICE_IVSHMEM */
|
|
"drive-iotune-max", /* X_QEMU_CAPS_DRIVE_IOTUNE_MAX */
|
|
"VGA.vgamem_mb", /* QEMU_CAPS_VGA_VGAMEM */
|
|
"vmware-svga.vgamem_mb", /* QEMU_CAPS_VMWARE_SVGA_VGAMEM */
|
|
|
|
/* 180 */
|
|
"qxl.vgamem_mb", /* QEMU_CAPS_QXL_VGAMEM */
|
|
"qxl-vga.vgamem_mb", /* X_QEMU_CAPS_QXL_VGA_VGAMEM */
|
|
"pc-dimm", /* QEMU_CAPS_DEVICE_PC_DIMM */
|
|
"machine-vmport-opt", /* QEMU_CAPS_MACHINE_VMPORT_OPT */
|
|
"aes-key-wrap", /* X_QEMU_CAPS_AES_KEY_WRAP */
|
|
|
|
/* 185 */
|
|
"dea-key-wrap", /* X_QEMU_CAPS_DEA_KEY_WRAP */
|
|
"pci-serial", /* QEMU_CAPS_DEVICE_PCI_SERIAL */
|
|
"aarch64-off", /* X_QEMU_CAPS_CPU_AARCH64_OFF */
|
|
"vhost-user-multiqueue", /* X_QEMU_CAPS_VHOSTUSER_MULTIQUEUE */
|
|
"migration-event", /* X_QEMU_CAPS_MIGRATION_EVENT */
|
|
|
|
/* 190 */
|
|
"gpex-pcihost", /* X_QEMU_CAPS_OBJECT_GPEX */
|
|
"ioh3420", /* QEMU_CAPS_DEVICE_IOH3420 */
|
|
"x3130-upstream", /* QEMU_CAPS_DEVICE_X3130_UPSTREAM */
|
|
"xio3130-downstream", /* QEMU_CAPS_DEVICE_XIO3130_DOWNSTREAM */
|
|
"rtl8139", /* QEMU_CAPS_DEVICE_RTL8139 */
|
|
|
|
/* 195 */
|
|
"e1000", /* QEMU_CAPS_DEVICE_E1000 */
|
|
"virtio-net", /* QEMU_CAPS_DEVICE_VIRTIO_NET */
|
|
"gic-version", /* X_QEMU_CAPS_MACH_VIRT_GIC_VERSION */
|
|
"incoming-defer", /* X_QEMU_CAPS_INCOMING_DEFER */
|
|
"virtio-gpu", /* QEMU_CAPS_DEVICE_VIRTIO_GPU */
|
|
|
|
/* 200 */
|
|
"virtio-gpu.virgl", /* QEMU_CAPS_VIRTIO_GPU_VIRGL */
|
|
"virtio-keyboard", /* QEMU_CAPS_VIRTIO_KEYBOARD */
|
|
"virtio-mouse", /* QEMU_CAPS_VIRTIO_MOUSE */
|
|
"virtio-tablet", /* QEMU_CAPS_VIRTIO_TABLET */
|
|
"virtio-input-host", /* QEMU_CAPS_VIRTIO_INPUT_HOST */
|
|
|
|
/* 205 */
|
|
"chardev-file-append", /* X_QEMU_CAPS_CHARDEV_FILE_APPEND */
|
|
"ich9-disable-s3", /* X_QEMU_CAPS_ICH9_DISABLE_S3 */
|
|
"ich9-disable-s4", /* X_QEMU_CAPS_ICH9_DISABLE_S4 */
|
|
"vserport-change-event", /* X_QEMU_CAPS_VSERPORT_CHANGE */
|
|
"virtio-balloon-pci.deflate-on-oom", /* QEMU_CAPS_VIRTIO_BALLOON_AUTODEFLATE */
|
|
|
|
/* 210 */
|
|
"mptsas1068", /* QEMU_CAPS_SCSI_MPTSAS1068 */
|
|
"spice-gl", /* QEMU_CAPS_SPICE_GL */
|
|
"qxl.vram64_size_mb", /* QEMU_CAPS_QXL_VRAM64 */
|
|
"qxl-vga.vram64_size_mb", /* X_QEMU_CAPS_QXL_VGA_VRAM64 */
|
|
"chardev-logfile", /* X_QEMU_CAPS_CHARDEV_LOGFILE */
|
|
|
|
/* 215 */
|
|
"debug-threads", /* X_QEMU_CAPS_NAME_DEBUG_THREADS */
|
|
"secret", /* X_QEMU_CAPS_OBJECT_SECRET */
|
|
"pxb", /* QEMU_CAPS_DEVICE_PXB */
|
|
"pxb-pcie", /* QEMU_CAPS_DEVICE_PXB_PCIE */
|
|
"device-tray-moved-event", /* X_QEMU_CAPS_DEVICE_TRAY_MOVED */
|
|
|
|
/* 220 */
|
|
"nec-usb-xhci-ports", /* X_QEMU_CAPS_NEC_USB_XHCI_PORTS */
|
|
"virtio-scsi-pci.iothread", /* X_QEMU_CAPS_VIRTIO_SCSI_IOTHREAD */
|
|
"name-guest", /* X_QEMU_CAPS_NAME_GUEST */
|
|
"qxl.max_outputs", /* X_QEMU_CAPS_QXL_MAX_OUTPUTS */
|
|
"qxl-vga.max_outputs", /* X_QEMU_CAPS_QXL_VGA_MAX_OUTPUTS */
|
|
|
|
/* 225 */
|
|
"spice-unix", /* X_QEMU_CAPS_SPICE_UNIX */
|
|
"drive-detect-zeroes", /* X_QEMU_CAPS_DRIVE_DETECT_ZEROES */
|
|
"tls-creds-x509", /* X_QEMU_CAPS_OBJECT_TLS_CREDS_X509 */
|
|
"display", /* X_QEMU_CAPS_DISPLAY */
|
|
"intel-iommu", /* QEMU_CAPS_DEVICE_INTEL_IOMMU */
|
|
|
|
/* 230 */
|
|
"smm", /* X_QEMU_CAPS_MACHINE_SMM_OPT */
|
|
"virtio-pci-disable-legacy", /* X_QEMU_CAPS_VIRTIO_PCI_DISABLE_LEGACY */
|
|
"query-hotpluggable-cpus", /* X_QEMU_CAPS_QUERY_HOTPLUGGABLE_CPUS */
|
|
"virtio-net.rx_queue_size", /* X_QEMU_CAPS_VIRTIO_NET_RX_QUEUE_SIZE */
|
|
"machine-iommu", /* X_QEMU_CAPS_MACHINE_IOMMU */
|
|
|
|
/* 235 */
|
|
"virtio-vga", /* QEMU_CAPS_DEVICE_VIRTIO_VGA */
|
|
"drive-iotune-max-length", /* X_QEMU_CAPS_DRIVE_IOTUNE_MAX_LENGTH */
|
|
"ivshmem-plain", /* QEMU_CAPS_DEVICE_IVSHMEM_PLAIN */
|
|
"ivshmem-doorbell", /* QEMU_CAPS_DEVICE_IVSHMEM_DOORBELL */
|
|
"query-qmp-schema", /* X_QEMU_CAPS_QUERY_QMP_SCHEMA */
|
|
|
|
/* 240 */
|
|
"gluster.debug_level", /* X_QEMU_CAPS_GLUSTER_DEBUG_LEVEL */
|
|
"vhost-scsi", /* QEMU_CAPS_DEVICE_VHOST_SCSI */
|
|
"drive-iotune-group", /* X_QEMU_CAPS_DRIVE_IOTUNE_GROUP */
|
|
"query-cpu-model-expansion", /* QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION */
|
|
"virtio-net.host_mtu", /* X_QEMU_CAPS_VIRTIO_NET_HOST_MTU */
|
|
|
|
/* 245 */
|
|
"spice-rendernode", /* QEMU_CAPS_SPICE_RENDERNODE */
|
|
"nvdimm", /* QEMU_CAPS_DEVICE_NVDIMM */
|
|
"pcie-root-port", /* QEMU_CAPS_DEVICE_PCIE_ROOT_PORT */
|
|
"query-cpu-definitions", /* QEMU_CAPS_QUERY_CPU_DEFINITIONS */
|
|
"block-write-threshold", /* X_QEMU_CAPS_BLOCK_WRITE_THRESHOLD */
|
|
|
|
/* 250 */
|
|
"query-named-block-nodes", /* X_QEMU_CAPS_QUERY_NAMED_BLOCK_NODES */
|
|
"cpu-cache", /* X_QEMU_CAPS_CPU_CACHE */
|
|
"qemu-xhci", /* QEMU_CAPS_DEVICE_QEMU_XHCI */
|
|
"kernel-irqchip", /* X_QEMU_CAPS_MACHINE_KERNEL_IRQCHIP */
|
|
"kernel-irqchip.split", /* X_QEMU_CAPS_MACHINE_KERNEL_IRQCHIP_SPLIT */
|
|
|
|
/* 255 */
|
|
"intel-iommu.intremap", /* QEMU_CAPS_INTEL_IOMMU_INTREMAP */
|
|
"intel-iommu.caching-mode", /* QEMU_CAPS_INTEL_IOMMU_CACHING_MODE */
|
|
"intel-iommu.eim", /* QEMU_CAPS_INTEL_IOMMU_EIM */
|
|
"intel-iommu.device-iotlb", /* QEMU_CAPS_INTEL_IOMMU_DEVICE_IOTLB */
|
|
"virtio.iommu_platform", /* X_QEMU_CAPS_VIRTIO_PCI_IOMMU_PLATFORM */
|
|
|
|
/* 260 */
|
|
"virtio.ats", /* X_QEMU_CAPS_VIRTIO_PCI_ATS */
|
|
"loadparm", /* X_QEMU_CAPS_LOADPARM */
|
|
"spapr-pci-host-bridge", /* QEMU_CAPS_DEVICE_SPAPR_PCI_HOST_BRIDGE */
|
|
"spapr-pci-host-bridge.numa_node", /* QEMU_CAPS_SPAPR_PCI_HOST_BRIDGE_NUMA_NODE */
|
|
"vnc-multi-servers", /* X_QEMU_CAPS_VNC_MULTI_SERVERS */
|
|
|
|
/* 265 */
|
|
"virtio-net.tx_queue_size", /* X_QEMU_CAPS_VIRTIO_NET_TX_QUEUE_SIZE */
|
|
"chardev-reconnect", /* QEMU_CAPS_CHARDEV_RECONNECT */
|
|
"virtio-gpu.max_outputs", /* X_QEMU_CAPS_VIRTIO_GPU_MAX_OUTPUTS */
|
|
"vxhs", /* X_QEMU_CAPS_VXHS */
|
|
"virtio-blk.num-queues", /* X_QEMU_CAPS_VIRTIO_BLK_NUM_QUEUES */
|
|
|
|
/* 270 */
|
|
"machine.pseries.resize-hpt", /* X_QEMU_CAPS_MACHINE_PSERIES_RESIZE_HPT */
|
|
"vmcoreinfo", /* QEMU_CAPS_DEVICE_VMCOREINFO */
|
|
"spapr-vty", /* QEMU_CAPS_DEVICE_SPAPR_VTY */
|
|
"sclplmconsole", /* QEMU_CAPS_DEVICE_SCLPLMCONSOLE */
|
|
"numa.dist", /* X_QEMU_CAPS_NUMA_DIST */
|
|
|
|
/* 275 */
|
|
"disk-share-rw", /* X_QEMU_CAPS_DISK_SHARE_RW */
|
|
"iscsi.password-secret", /* X_QEMU_CAPS_ISCSI_PASSWORD_SECRET */
|
|
"isa-serial", /* QEMU_CAPS_DEVICE_ISA_SERIAL */
|
|
"pl011", /* QEMU_CAPS_DEVICE_PL011 */
|
|
"machine.pseries.max-cpu-compat", /* X_QEMU_CAPS_MACHINE_PSERIES_MAX_CPU_COMPAT */
|
|
|
|
/* 280 */
|
|
"dump-completed", /* X_QEMU_CAPS_DUMP_COMPLETED */
|
|
"virtio-gpu-ccw", /* QEMU_CAPS_DEVICE_VIRTIO_GPU_CCW */
|
|
"virtio-keyboard-ccw", /* QEMU_CAPS_DEVICE_VIRTIO_KEYBOARD_CCW */
|
|
"virtio-mouse-ccw", /* QEMU_CAPS_DEVICE_VIRTIO_MOUSE_CCW */
|
|
"virtio-tablet-ccw", /* QEMU_CAPS_DEVICE_VIRTIO_TABLET_CCW */
|
|
|
|
/* 285 */
|
|
"qcow2-luks", /* X_QEMU_CAPS_QCOW2_LUKS */
|
|
"pcie-pci-bridge", /* QEMU_CAPS_DEVICE_PCIE_PCI_BRIDGE */
|
|
"seccomp-blacklist", /* X_QEMU_CAPS_SECCOMP_BLACKLIST */
|
|
"query-cpus-fast", /* X_QEMU_CAPS_QUERY_CPUS_FAST */
|
|
"disk-write-cache", /* X_QEMU_CAPS_DISK_WRITE_CACHE */
|
|
|
|
/* 290 */
|
|
"nbd-tls", /* QEMU_CAPS_NBD_TLS */
|
|
"tpm-crb", /* QEMU_CAPS_DEVICE_TPM_CRB */
|
|
"pr-manager-helper", /* QEMU_CAPS_PR_MANAGER_HELPER */
|
|
"qom-list-properties", /* X_QEMU_CAPS_QOM_LIST_PROPERTIES */
|
|
"memory-backend-file.discard-data", /* X_QEMU_CAPS_OBJECT_MEMORY_FILE_DISCARD */
|
|
|
|
/* 295 */
|
|
"virtual-css-bridge", /* X_QEMU_CAPS_CCW */
|
|
"virtual-css-bridge.cssid-unrestricted", /* X_QEMU_CAPS_CCW_CSSID_UNRESTRICTED */
|
|
"vfio-ccw", /* QEMU_CAPS_DEVICE_VFIO_CCW */
|
|
"sdl-gl", /* X_QEMU_CAPS_SDL_GL */
|
|
"screendump_device", /* QEMU_CAPS_SCREENDUMP_DEVICE */
|
|
|
|
/* 300 */
|
|
"hda-output", /* QEMU_CAPS_HDA_OUTPUT */
|
|
"blockdev-del", /* X_QEMU_CAPS_BLOCKDEV_DEL */
|
|
"vmgenid", /* QEMU_CAPS_DEVICE_VMGENID */
|
|
"vhost-vsock", /* QEMU_CAPS_DEVICE_VHOST_VSOCK */
|
|
"chardev-fd-pass", /* X_QEMU_CAPS_CHARDEV_FD_PASS_COMMANDLINE */
|
|
|
|
/* 305 */
|
|
"tpm-emulator", /* QEMU_CAPS_DEVICE_TPM_EMULATOR */
|
|
"mch", /* QEMU_CAPS_DEVICE_MCH */
|
|
"mch.extended-tseg-mbytes", /* QEMU_CAPS_MCH_EXTENDED_TSEG_MBYTES */
|
|
"sev-guest", /* QEMU_CAPS_SEV_GUEST */
|
|
"machine.pseries.cap-hpt-max-page-size", /* X_QEMU_CAPS_MACHINE_PSERIES_CAP_HPT_MAX_PAGE_SIZE */
|
|
|
|
/* 310 */
|
|
"machine.pseries.cap-htm", /* X_QEMU_CAPS_MACHINE_PSERIES_CAP_HTM */
|
|
"usb-storage.werror", /* X_QEMU_CAPS_USB_STORAGE_WERROR */
|
|
"egl-headless", /* QEMU_CAPS_EGL_HEADLESS */
|
|
"vfio-pci.display", /* X_QEMU_CAPS_VFIO_PCI_DISPLAY */
|
|
"blockdev", /* X_QEMU_CAPS_BLOCKDEV */
|
|
|
|
/* 315 */
|
|
"vfio-ap", /* QEMU_CAPS_DEVICE_VFIO_AP */
|
|
"zpci", /* QEMU_CAPS_DEVICE_ZPCI */
|
|
"memory-backend-memfd", /* QEMU_CAPS_OBJECT_MEMORY_MEMFD */
|
|
"memory-backend-memfd.hugetlb", /* QEMU_CAPS_OBJECT_MEMORY_MEMFD_HUGETLB */
|
|
"iothread.poll-max-ns", /* X_QEMU_CAPS_IOTHREAD_POLLING */
|
|
|
|
/* 320 */
|
|
"machine.pseries.cap-nested-hv", /* X_QEMU_CAPS_MACHINE_PSERIES_CAP_NESTED_HV */
|
|
"egl-headless.rendernode", /* QEMU_CAPS_EGL_HEADLESS_RENDERNODE */
|
|
"memory-backend-file.align", /* X_QEMU_CAPS_OBJECT_MEMORY_FILE_ALIGN */
|
|
"memory-backend-file.pmem", /* QEMU_CAPS_OBJECT_MEMORY_FILE_PMEM */
|
|
"nvdimm.unarmed", /* QEMU_CAPS_DEVICE_NVDIMM_UNARMED */
|
|
|
|
/* 325 */
|
|
"scsi-disk.device_id", /* X_QEMU_CAPS_SCSI_DISK_DEVICE_ID */
|
|
"virtio-pci-non-transitional", /* QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL */
|
|
"overcommit", /* X_QEMU_CAPS_OVERCOMMIT */
|
|
"query-current-machine", /* X_QEMU_CAPS_QUERY_CURRENT_MACHINE */
|
|
"machine.virt.iommu", /* QEMU_CAPS_MACHINE_VIRT_IOMMU */
|
|
|
|
/* 330 */
|
|
"bitmap-merge", /* X_QEMU_CAPS_BITMAP_MERGE */
|
|
"nbd-bitmap", /* QEMU_CAPS_NBD_BITMAP */
|
|
"x86-max-cpu", /* QEMU_CAPS_X86_MAX_CPU */
|
|
"cpu-unavailable-features", /* QEMU_CAPS_CPU_UNAVAILABLE_FEATURES */
|
|
"canonical-cpu-features", /* QEMU_CAPS_CANONICAL_CPU_FEATURES */
|
|
|
|
/* 335 */
|
|
"bochs-display", /* QEMU_CAPS_DEVICE_BOCHS_DISPLAY */
|
|
"migration-file-drop-cache", /* QEMU_CAPS_MIGRATION_FILE_DROP_CACHE */
|
|
"dbus-vmstate", /* QEMU_CAPS_DBUS_VMSTATE */
|
|
"vhost-user-gpu", /* QEMU_CAPS_DEVICE_VHOST_USER_GPU */
|
|
"vhost-user-vga", /* QEMU_CAPS_DEVICE_VHOST_USER_VGA */
|
|
|
|
/* 340 */
|
|
"incremental-backup", /* QEMU_CAPS_INCREMENTAL_BACKUP */
|
|
"query-cpu-model-baseline", /* QEMU_CAPS_QUERY_CPU_MODEL_BASELINE */
|
|
"query-cpu-model-comparison", /* QEMU_CAPS_QUERY_CPU_MODEL_COMPARISON */
|
|
"ramfb", /* QEMU_CAPS_DEVICE_RAMFB */
|
|
"machine.pseries.cap-ccf-assist", /* X_QEMU_CAPS_MACHINE_PSERIES_CAP_CCF_ASSIST */
|
|
|
|
/* 345 */
|
|
"arm-max-cpu", /* QEMU_CAPS_ARM_MAX_CPU */
|
|
"blockdev-file-dynamic-auto-read-only", /* X_QEMU_CAPS_BLOCK_FILE_AUTO_READONLY_DYNAMIC */
|
|
"savevm-monitor-nodes", /* X_QEMU_CAPS_SAVEVM_MONITOR_NODES */
|
|
"drive-nvme", /* QEMU_CAPS_DRIVE_NVME */
|
|
"smp-dies", /* QEMU_CAPS_SMP_DIES */
|
|
|
|
/* 350 */
|
|
"i8042", /* QEMU_CAPS_DEVICE_I8042 */
|
|
"rng-builtin", /* QEMU_CAPS_OBJECT_RNG_BUILTIN */
|
|
"virtio-net.failover", /* X_QEMU_CAPS_VIRTIO_NET_FAILOVER */
|
|
"tpm-spapr", /* QEMU_CAPS_DEVICE_TPM_SPAPR */
|
|
"cpu.kvm-no-adjvtime", /* QEMU_CAPS_CPU_KVM_NO_ADJVTIME */
|
|
|
|
/* 355 */
|
|
"vhost-user-fs", /* QEMU_CAPS_DEVICE_VHOST_USER_FS */
|
|
"query-named-block-nodes.flat", /* QEMU_CAPS_QMP_QUERY_NAMED_BLOCK_NODES_FLAT */
|
|
"blockdev-snapshot.allow-write-only-overlay", /* QEMU_CAPS_BLOCKDEV_SNAPSHOT_ALLOW_WRITE_ONLY */
|
|
"blockdev-reopen", /* QEMU_CAPS_BLOCKDEV_REOPEN */
|
|
"storage.werror", /* X_QEMU_CAPS_STORAGE_WERROR */
|
|
|
|
/* 360 */
|
|
"fsdev.multidevs", /* QEMU_CAPS_FSDEV_MULTIDEVS */
|
|
"virtio.packed", /* X_QEMU_CAPS_VIRTIO_PACKED_QUEUES */
|
|
"pcie-root-port.hotplug", /* QEMU_CAPS_PCIE_ROOT_PORT_HOTPLUG */
|
|
"aio.io_uring", /* QEMU_CAPS_AIO_IO_URING */
|
|
"machine.pseries.cap-cfpc", /* X_QEMU_CAPS_MACHINE_PSERIES_CAP_CFPC */
|
|
|
|
/* 365 */
|
|
"machine.pseries.cap-sbbc", /* X_QEMU_CAPS_MACHINE_PSERIES_CAP_SBBC */
|
|
"machine.pseries.cap-ibs", /* X_QEMU_CAPS_MACHINE_PSERIES_CAP_IBS */
|
|
"tcg", /* QEMU_CAPS_TCG */
|
|
"virtio-blk-pci.scsi.default.disabled", /* QEMU_CAPS_VIRTIO_BLK_SCSI_DEFAULT_DISABLED */
|
|
"pvscsi", /* QEMU_CAPS_SCSI_PVSCSI */
|
|
|
|
/* 370 */
|
|
"cpu.migratable", /* QEMU_CAPS_CPU_MIGRATABLE */
|
|
"query-cpu-model-expansion.migratable", /* X_QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION_MIGRATABLE */
|
|
"fw_cfg", /* X_QEMU_CAPS_FW_CFG */
|
|
"migration-param.bandwidth", /* X_QEMU_CAPS_MIGRATION_PARAM_BANDWIDTH */
|
|
"migration-param.downtime", /* X_QEMU_CAPS_MIGRATION_PARAM_DOWNTIME */
|
|
|
|
/* 375 */
|
|
"migration-param.xbzrle-cache-size", /* X_QEMU_CAPS_MIGRATION_PARAM_XBZRLE_CACHE_SIZE */
|
|
"intel-iommu.aw-bits", /* QEMU_CAPS_INTEL_IOMMU_AW_BITS */
|
|
"spapr-tpm-proxy", /* QEMU_CAPS_DEVICE_SPAPR_TPM_PROXY */
|
|
"numa.hmat", /* QEMU_CAPS_NUMA_HMAT */
|
|
"blockdev-hostdev-scsi", /* X_QEMU_CAPS_BLOCKDEV_HOSTDEV_SCSI */
|
|
|
|
/* 380 */
|
|
"usb-host.hostdevice", /* QEMU_CAPS_USB_HOST_HOSTDEVICE */
|
|
"virtio-balloon.free-page-reporting", /* QEMU_CAPS_VIRTIO_BALLOON_FREE_PAGE_REPORTING */
|
|
"block-export-add", /* QEMU_CAPS_BLOCK_EXPORT_ADD */
|
|
"netdev.vhost-vdpa", /* QEMU_CAPS_NETDEV_VHOST_VDPA */
|
|
"fsdev.createmode", /* X_QEMU_CAPS_FSDEV_CREATEMODE */
|
|
|
|
/* 385 */
|
|
"ncr53c90", /* QEMU_CAPS_SCSI_NCR53C90 */
|
|
"dc390", /* QEMU_CAPS_SCSI_DC390 */
|
|
"am53c974", /* QEMU_CAPS_SCSI_AM53C974 */
|
|
"virtio-pmem-pci", /* QEMU_CAPS_DEVICE_VIRTIO_PMEM_PCI */
|
|
"vhost-user-fs.bootindex", /* QEMU_CAPS_VHOST_USER_FS_BOOTINDEX */
|
|
|
|
/* 390 */
|
|
"vhost-user-blk", /* QEMU_CAPS_DEVICE_VHOST_USER_BLK */
|
|
"cpu-max", /* QEMU_CAPS_CPU_MAX */
|
|
"memory-backend-file.x-use-canonical-path-for-ramblock-id", /* QEMU_CAPS_X_USE_CANONICAL_PATH_FOR_RAMBLOCK_ID */
|
|
"vnc-opts", /* X_QEMU_CAPS_VNC_OPTS */
|
|
"migration-param.block-bitmap-mapping", /* QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING */
|
|
|
|
/* 395 */
|
|
"vnc-power-control", /* QEMU_CAPS_VNC_POWER_CONTROL */
|
|
"audiodev", /* X_QEMU_CAPS_AUDIODEV */
|
|
"blockdev-backup", /* X_QEMU_CAPS_BLOCKDEV_BACKUP */
|
|
"object.qapified", /* QEMU_CAPS_OBJECT_JSON */
|
|
"rotation-rate", /* QEMU_CAPS_ROTATION_RATE */
|
|
|
|
/* 400 */
|
|
"compat-deprecated", /* QEMU_CAPS_COMPAT_DEPRECATED */
|
|
"acpi-index", /* QEMU_CAPS_ACPI_INDEX */
|
|
"input-linux", /* QEMU_CAPS_INPUT_LINUX */
|
|
"virtio-gpu-gl-pci", /* QEMU_CAPS_VIRTIO_GPU_GL_PCI */
|
|
"virtio-vga-gl", /* QEMU_CAPS_VIRTIO_VGA_GL */
|
|
|
|
/* 405 */
|
|
"confidential-guest-support", /* QEMU_CAPS_MACHINE_CONFIDENTAL_GUEST_SUPPORT */
|
|
"query-display-options", /* X_QEMU_CAPS_QUERY_DISPLAY_OPTIONS */
|
|
"s390-pv-guest", /* QEMU_CAPS_S390_PV_GUEST */
|
|
"set-action", /* QEMU_CAPS_SET_ACTION */
|
|
"virtio-blk.queue-size", /* QEMU_CAPS_VIRTIO_BLK_QUEUE_SIZE */
|
|
|
|
/* 410 */
|
|
"virtio-mem-pci", /* QEMU_CAPS_DEVICE_VIRTIO_MEM_PCI */
|
|
"memory-backend-file.reserve", /* QEMU_CAPS_MEMORY_BACKEND_RESERVE */
|
|
"piix4.acpi-root-pci-hotplug", /* QEMU_CAPS_PIIX4_ACPI_ROOT_PCI_HOTPLUG */
|
|
"netdev.json", /* QEMU_CAPS_NETDEV_JSON */
|
|
"chardev.json", /* QEMU_CAPS_CHARDEV_JSON */
|
|
|
|
/* 415 */
|
|
"device.json", /* X_QEMU_CAPS_DEVICE_JSON_BROKEN_HOTPLUG */
|
|
"query-dirty-rate", /* QEMU_CAPS_QUERY_DIRTY_RATE */
|
|
"rbd-encryption", /* QEMU_CAPS_RBD_ENCRYPTION */
|
|
"sev-guest-kernel-hashes", /* QEMU_CAPS_SEV_GUEST_KERNEL_HASHES */
|
|
"sev-inject-launch-secret", /* QEMU_CAPS_SEV_INJECT_LAUNCH_SECRET */
|
|
|
|
/* 420 */
|
|
"device.json+hotplug", /* QEMU_CAPS_DEVICE_JSON */
|
|
"hvf", /* QEMU_CAPS_HVF */
|
|
"virtio-mem-pci.prealloc", /* QEMU_CAPS_DEVICE_VIRTIO_MEM_PCI_PREALLOC */
|
|
"calc-dirty-rate", /* QEMU_CAPS_CALC_DIRTY_RATE */
|
|
"dirtyrate-param.mode", /* QEMU_CAPS_DIRTYRATE_MODE */
|
|
|
|
/* 425 */
|
|
"blockdev.nbd.tls-hostname", /* QEMU_CAPS_BLOCKDEV_NBD_TLS_HOSTNAME */
|
|
"memory-backend-file.prealloc-threads", /* QEMU_CAPS_MEMORY_BACKEND_PREALLOC_THREADS */
|
|
"virtio-iommu-pci", /* QEMU_CAPS_DEVICE_VIRTIO_IOMMU_PCI */
|
|
"virtio-iommu.boot-bypass", /* QEMU_CAPS_VIRTIO_IOMMU_BOOT_BYPASS */
|
|
"virtio-net.rss", /* QEMU_CAPS_VIRTIO_NET_RSS */
|
|
|
|
/* 430 */
|
|
"chardev.qemu-vdagent", /* QEMU_CAPS_CHARDEV_QEMU_VDAGENT */
|
|
"display-dbus", /* QEMU_CAPS_DISPLAY_DBUS */
|
|
"iothread.thread-pool-max", /* QEMU_CAPS_IOTHREAD_THREAD_POOL_MAX */
|
|
"usb-host.guest-resets-all", /* QEMU_CAPS_USB_HOST_GUESTS_RESETS_ALL */
|
|
"migration.blocked-reasons", /* QEMU_CAPS_MIGRATION_BLOCKED_REASONS */
|
|
|
|
/* 435 */
|
|
"query-stats", /* QEMU_CAPS_QUERY_STATS */
|
|
"query-stats-schemas", /* QEMU_CAPS_QUERY_STATS_SCHEMAS */
|
|
"sgx-epc", /* QEMU_CAPS_SGX_EPC */
|
|
"thread-context", /* QEMU_CAPS_THREAD_CONTEXT */
|
|
"screenshot-format-png", /* QEMU_CAPS_SCREENSHOT_FORMAT_PNG */
|
|
|
|
/* 440 */
|
|
"machine-hpet", /* QEMU_CAPS_MACHINE_HPET */
|
|
"netdev.stream", /* QEMU_CAPS_NETDEV_STREAM */
|
|
"virtio-crypto", /* QEMU_CAPS_DEVICE_VIRTIO_CRYPTO */
|
|
"cryptodev-backend-lkcf", /* QEMU_CAPS_OBJECT_CRYPTO_LKCF */
|
|
"pvpanic-pci", /* QEMU_CAPS_DEVICE_PANIC_PCI */
|
|
|
|
/* 445 */
|
|
"netdev.stream.reconnect", /* QEMU_CAPS_NETDEV_STREAM_RECONNECT */
|
|
"virtio-gpu.blob", /* QEMU_CAPS_VIRTIO_GPU_BLOB */
|
|
"rbd-encryption-layering", /* QEMU_CAPS_RBD_ENCRYPTION_LAYERING */
|
|
"rbd-encryption-luks-any", /* QEMU_CAPS_RBD_ENCRYPTION_LUKS_ANY */
|
|
"qcow2-discard-no-unref", /* QEMU_CAPS_QCOW2_DISCARD_NO_UNREF */
|
|
|
|
/* 450 */
|
|
"run-with.async-teardown", /* QEMU_CAPS_RUN_WITH_ASYNC_TEARDOWN */
|
|
"virtio-blk-vhost-vdpa", /* QEMU_CAPS_DEVICE_VIRTIO_BLK_VHOST_VDPA */
|
|
"virtio-blk.iothread-mapping", /* QEMU_CAPS_VIRTIO_BLK_IOTHREAD_MAPPING */
|
|
"smp-clusters", /* QEMU_CAPS_SMP_CLUSTERS */
|
|
"virtio-mem-pci.dynamic-memslots", /* QEMU_CAPS_DEVICE_VIRTIO_MEM_PCI_DYNAMIC_MEMSLOTS */
|
|
|
|
/* 455 */
|
|
"blockjob.backing-mask-protocol", /* QEMU_CAPS_BLOCKJOB_BACKING_MASK_PROTOCOL */
|
|
"display-reload", /* QEMU_CAPS_DISPLAY_RELOAD */
|
|
"usb-mtp", /* QEMU_CAPS_DEVICE_USB_MTP */
|
|
"machine.virt.ras", /* QEMU_CAPS_MACHINE_VIRT_RAS */
|
|
"virtio-sound", /* QEMU_CAPS_DEVICE_VIRTIO_SOUND */
|
|
|
|
/* 460 */
|
|
"sev-snp-guest", /* QEMU_CAPS_SEV_SNP_GUEST */
|
|
"netdev.user", /* QEMU_CAPS_NETDEV_USER */
|
|
"acpi-erst", /* QEMU_CAPS_DEVICE_ACPI_ERST */
|
|
"intel-iommu.dma-translation", /* QEMU_CAPS_INTEL_IOMMU_DMA_TRANSLATION */
|
|
"machine-i8042-opt", /* QEMU_CAPS_MACHINE_I8042_OPT */
|
|
|
|
/* 465 */
|
|
"snapshot-internal-qmp", /* QEMU_CAPS_SNAPSHOT_INTERNAL_QMP */
|
|
"chardev-reconnect-miliseconds", /* QEMU_CAPS_CHARDEV_RECONNECT_MILISECONDS */
|
|
"virtio-ccw.loadparm", /* QEMU_CAPS_VIRTIO_CCW_DEVICE_LOADPARM */
|
|
"netdev-stream-reconnect-miliseconds", /* QEMU_CAPS_NETDEV_STREAM_RECONNECT_MILISECONDS */
|
|
"query-cpu-model-expansion.deprecated-props", /* QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION_DEPRECATED_PROPS */
|
|
);
|
|
|
|
|
|
typedef struct _virQEMUCapsMachineType virQEMUCapsMachineType;
|
|
struct _virQEMUCapsMachineType {
|
|
char *name;
|
|
char *alias;
|
|
unsigned int maxCpus;
|
|
bool hotplugCpus;
|
|
bool qemuDefault;
|
|
char *defaultCPU;
|
|
bool numaMemSupported;
|
|
char *defaultRAMid;
|
|
bool deprecated;
|
|
virTristateBool acpi;
|
|
};
|
|
|
|
typedef struct _virQEMUCapsHostCPUData virQEMUCapsHostCPUData;
|
|
struct _virQEMUCapsHostCPUData {
|
|
/* Only the "info" part is stored in the capabilities cache, the rest is
|
|
* re-computed from other fields and external data sources every time we
|
|
* probe QEMU or load the cache.
|
|
*/
|
|
qemuMonitorCPUModelInfo *info;
|
|
/* Physical address size of the host CPU or 0 if unknown or not applicable. */
|
|
unsigned int physAddrSize;
|
|
/* Host CPU definition reported in domain capabilities. */
|
|
virCPUDef *reported;
|
|
/* Migratable host CPU definition used for updating guest CPU. */
|
|
virCPUDef *migratable;
|
|
/* CPU definition with features detected by libvirt using virCPUGetHost
|
|
* combined with features reported by QEMU. This is used for backward
|
|
* compatible comparison between a guest CPU and a host CPU. */
|
|
virCPUDef *full;
|
|
};
|
|
|
|
typedef struct _virQEMUCapsAccel virQEMUCapsAccel;
|
|
struct _virQEMUCapsAccel {
|
|
size_t nmachineTypes;
|
|
virQEMUCapsMachineType *machineTypes;
|
|
virQEMUCapsHostCPUData hostCPU;
|
|
qemuMonitorCPUDefs *cpuModels;
|
|
};
|
|
|
|
|
|
/*
|
|
* Update the XML parser/formatter when adding more
|
|
* information to this struct so that it gets cached
|
|
* correctly. It does not have to be ABI-stable, as
|
|
* the cache will be discarded & repopulated if the
|
|
* timestamp on the libvirtd binary changes.
|
|
*
|
|
* And don't forget to update virQEMUCapsNewCopy.
|
|
*/
|
|
struct _virQEMUCaps {
|
|
virObject parent;
|
|
|
|
bool kvmSupportsNesting;
|
|
bool kvmSupportsSecureGuest;
|
|
|
|
char *binary;
|
|
time_t ctime;
|
|
time_t libvirtCtime;
|
|
time_t modDirMtime;
|
|
bool invalidation;
|
|
|
|
virBitmap *flags;
|
|
|
|
unsigned int version;
|
|
unsigned int libvirtVersion;
|
|
unsigned int microcodeVersion;
|
|
char *hostCPUSignature;
|
|
char *package;
|
|
char *kernelVersion;
|
|
|
|
virArch arch;
|
|
virCPUData *cpuData;
|
|
|
|
size_t ngicCapabilities;
|
|
virGICCapability *gicCapabilities;
|
|
|
|
virSEVCapability *sevCapabilities;
|
|
|
|
virSGXCapability *sgxCapabilities;
|
|
|
|
virDomainCapsFeatureHyperv *hypervCapabilities;
|
|
|
|
/* Capabilities which may differ depending on the accelerator. */
|
|
virQEMUCapsAccel kvm;
|
|
virQEMUCapsAccel hvf;
|
|
virQEMUCapsAccel tcg;
|
|
};
|
|
|
|
static virClass *virQEMUCapsClass;
|
|
static void virQEMUCapsDispose(void *obj);
|
|
|
|
static int virQEMUCapsOnceInit(void)
|
|
{
|
|
if (!VIR_CLASS_NEW(virQEMUCaps, virClassForObject()))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virQEMUCaps);
|
|
|
|
virArch virQEMUCapsArchFromString(const char *arch)
|
|
{
|
|
if (STREQ(arch, "i386"))
|
|
return VIR_ARCH_I686;
|
|
if (STREQ(arch, "arm"))
|
|
return VIR_ARCH_ARMV7L;
|
|
if (STREQ(arch, "or32"))
|
|
return VIR_ARCH_OR32;
|
|
|
|
return virArchFromString(arch);
|
|
}
|
|
|
|
|
|
const char *virQEMUCapsArchToString(virArch arch)
|
|
{
|
|
if (arch == VIR_ARCH_I686)
|
|
return "i386";
|
|
if (arch == VIR_ARCH_ARMV6L || arch == VIR_ARCH_ARMV7L)
|
|
return "arm";
|
|
if (arch == VIR_ARCH_OR32)
|
|
return "or32";
|
|
|
|
return virArchToString(arch);
|
|
}
|
|
|
|
|
|
/* Checks whether a domain with @guest arch can run natively on @host.
|
|
*/
|
|
bool
|
|
virQEMUCapsGuestIsNative(virArch host,
|
|
virArch guest)
|
|
{
|
|
/* host & guest arches match */
|
|
if (host == guest)
|
|
return true;
|
|
|
|
/* hostarch is x86_64 and guest arch is i686 (needs -cpu qemu32) */
|
|
if (host == VIR_ARCH_X86_64 && guest == VIR_ARCH_I686)
|
|
return true;
|
|
|
|
/* hostarch is aarch64 and guest arch is armv7l (needs -cpu aarch64=off) */
|
|
if (host == VIR_ARCH_AARCH64 && guest == VIR_ARCH_ARMV7L)
|
|
return true;
|
|
|
|
/* hostarch and guestarch are both ppc64 */
|
|
if (ARCH_IS_PPC64(host) && ARCH_IS_PPC64(guest))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/* Given a host and guest architectures, find a suitable QEMU target.
|
|
*
|
|
* This is meant to be used as a second attempt if qemu-system-$guestarch
|
|
* can't be found, eg. on a x86_64 host you want to use qemu-system-i386,
|
|
* if available, instead of qemu-system-x86_64 to run i686 guests */
|
|
static virArch
|
|
virQEMUCapsFindTarget(virArch hostarch,
|
|
virArch guestarch)
|
|
{
|
|
if (virQEMUCapsGuestIsNative(hostarch, guestarch))
|
|
guestarch = hostarch;
|
|
|
|
/* Both ppc64 and ppc64le guests can use the ppc64 target */
|
|
if (ARCH_IS_PPC64(guestarch))
|
|
guestarch = VIR_ARCH_PPC64;
|
|
|
|
return guestarch;
|
|
}
|
|
|
|
|
|
static bool
|
|
virQEMUCapsTypeIsAccelerated(virDomainVirtType type)
|
|
{
|
|
return type != VIR_DOMAIN_VIRT_QEMU;
|
|
}
|
|
|
|
|
|
bool
|
|
virQEMUCapsHaveAccel(virQEMUCaps *qemuCaps)
|
|
{
|
|
return virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM) ||
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_HVF);
|
|
}
|
|
|
|
|
|
static const char *
|
|
virQEMUCapsAccelStr(virDomainVirtType type)
|
|
{
|
|
if (type == VIR_DOMAIN_VIRT_KVM)
|
|
return "kvm";
|
|
else if (type == VIR_DOMAIN_VIRT_HVF)
|
|
return "hvf";
|
|
|
|
return "tcg";
|
|
}
|
|
|
|
|
|
static virQEMUCapsAccel *
|
|
virQEMUCapsGetAccel(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type)
|
|
{
|
|
if (type == VIR_DOMAIN_VIRT_KVM)
|
|
return &qemuCaps->kvm;
|
|
else if (type == VIR_DOMAIN_VIRT_HVF)
|
|
return &qemuCaps->hvf;
|
|
|
|
return &qemuCaps->tcg;
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsSetDefaultMachine(virQEMUCapsAccel *caps,
|
|
size_t defIdx)
|
|
{
|
|
virQEMUCapsMachineType tmp = caps->machineTypes[defIdx];
|
|
|
|
memmove(caps->machineTypes + 1,
|
|
caps->machineTypes,
|
|
sizeof(caps->machineTypes[0]) * defIdx);
|
|
|
|
caps->machineTypes[0] = tmp;
|
|
}
|
|
|
|
|
|
static char *
|
|
virQEMUCapsFindBinary(const char *format,
|
|
const char *archstr)
|
|
{
|
|
char *ret = NULL;
|
|
char *binary = NULL;
|
|
|
|
binary = g_strdup_printf(format, archstr);
|
|
|
|
ret = virFindFileInPath(binary);
|
|
VIR_FREE(binary);
|
|
return ret;
|
|
}
|
|
|
|
char *
|
|
virQEMUCapsGetDefaultEmulator(virArch hostarch,
|
|
virArch guestarch)
|
|
{
|
|
char *binary;
|
|
const char *archstr;
|
|
virArch target;
|
|
|
|
/* armv7l guests can only take advantage of KVM on aarch64 hosts by
|
|
* using the qemu-system-aarch64 binary, so look for that one first
|
|
* to avoid using qemu-system-arm (and thus TCG) instead */
|
|
if (hostarch == VIR_ARCH_AARCH64 && guestarch == VIR_ARCH_ARMV7L) {
|
|
archstr = virQEMUCapsArchToString(hostarch);
|
|
if ((binary = virQEMUCapsFindBinary("qemu-system-%s", archstr)))
|
|
return binary;
|
|
}
|
|
|
|
/* First attempt: try the guest architecture as it is */
|
|
archstr = virQEMUCapsArchToString(guestarch);
|
|
if ((binary = virQEMUCapsFindBinary("qemu-system-%s", archstr)))
|
|
return binary;
|
|
|
|
/* Second attempt: try looking up by target instead */
|
|
target = virQEMUCapsFindTarget(hostarch, guestarch);
|
|
if (target != guestarch) {
|
|
archstr = virQEMUCapsArchToString(target);
|
|
if ((binary = virQEMUCapsFindBinary("qemu-system-%s", archstr)))
|
|
return binary;
|
|
}
|
|
|
|
/* RHEL doesn't follow the usual naming for QEMU binaries and ships
|
|
* a single binary named qemu-kvm outside of $PATH instead */
|
|
if (virQEMUCapsGuestIsNative(hostarch, guestarch)) {
|
|
if ((binary = virFindFileInPath("/usr/libexec/qemu-kvm")))
|
|
return binary;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsInitGuest(virCaps *caps,
|
|
virFileCache *cache,
|
|
virArch hostarch,
|
|
virArch guestarch)
|
|
{
|
|
g_autofree char *binary = NULL;
|
|
g_autoptr(virQEMUCaps) qemuCaps = NULL;
|
|
|
|
binary = virQEMUCapsGetDefaultEmulator(hostarch, guestarch);
|
|
|
|
/* Ignore binary if extracting version info fails */
|
|
if (binary) {
|
|
if (!(qemuCaps = virQEMUCapsCacheLookup(cache, binary))) {
|
|
virResetLastError();
|
|
return;
|
|
}
|
|
}
|
|
|
|
virQEMUCapsInitGuestFromBinary(caps, binary, qemuCaps, guestarch);
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsGetMachineTypesCaps(virQEMUCaps *qemuCaps,
|
|
size_t *nmachines,
|
|
virCapsGuestMachine ***machines)
|
|
{
|
|
size_t i;
|
|
virQEMUCapsAccel *accel;
|
|
g_autoptr(GPtrArray) array = NULL;
|
|
|
|
/* Guest capabilities do not report TCG vs. KVM caps separately. We just
|
|
* take the set of machine types we probed first. */
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM))
|
|
accel = &qemuCaps->kvm;
|
|
else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_HVF))
|
|
accel = &qemuCaps->hvf;
|
|
else
|
|
accel = &qemuCaps->tcg;
|
|
|
|
*machines = NULL;
|
|
*nmachines = accel->nmachineTypes;
|
|
|
|
if (*nmachines == 0)
|
|
return;
|
|
|
|
array = g_ptr_array_sized_new(*nmachines);
|
|
|
|
for (i = 0; i < accel->nmachineTypes; i++) {
|
|
virCapsGuestMachine *mach = g_new0(virCapsGuestMachine, 1);
|
|
if (accel->machineTypes[i].alias) {
|
|
mach->name = g_strdup(accel->machineTypes[i].alias);
|
|
mach->canonical = g_strdup(accel->machineTypes[i].name);
|
|
} else {
|
|
mach->name = g_strdup(accel->machineTypes[i].name);
|
|
}
|
|
mach->maxCpus = accel->machineTypes[i].maxCpus;
|
|
mach->deprecated = accel->machineTypes[i].deprecated;
|
|
g_ptr_array_add(array, mach);
|
|
}
|
|
|
|
/* Make sure all canonical machine types also have their own entry so that
|
|
* /capabilities/guest/arch[@name='...']/machine/text() XPath selects all
|
|
* supported machine types.
|
|
*/
|
|
i = 0;
|
|
while (i < array->len) {
|
|
size_t j;
|
|
bool found = false;
|
|
virCapsGuestMachine *machine = g_ptr_array_index(array, i);
|
|
|
|
if (!machine->canonical) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
for (j = 0; j < array->len; j++) {
|
|
virCapsGuestMachine *mach = g_ptr_array_index(array, j);
|
|
if (STREQ(machine->canonical, mach->name)) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
virCapsGuestMachine *mach;
|
|
mach = g_new0(virCapsGuestMachine, 1);
|
|
mach->name = g_strdup(machine->canonical);
|
|
mach->maxCpus = machine->maxCpus;
|
|
mach->deprecated = machine->deprecated;
|
|
g_ptr_array_insert(array, i, mach);
|
|
i++;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
*nmachines = array->len;
|
|
*machines = g_new0(virCapsGuestMachine *, array->len);
|
|
for (i = 0; i < array->len; ++i)
|
|
(*machines)[i] = g_ptr_array_index(array, i);
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsInitGuestFromBinary(virCaps *caps,
|
|
const char *binary,
|
|
virQEMUCaps *qemuCaps,
|
|
virArch guestarch)
|
|
{
|
|
virCapsGuest *guest;
|
|
virCapsGuestMachine **machines = NULL;
|
|
size_t nmachines = 0;
|
|
|
|
if (!binary)
|
|
return;
|
|
|
|
virQEMUCapsGetMachineTypesCaps(qemuCaps, &nmachines, &machines);
|
|
|
|
/* We register kvm as the base emulator too, since we can
|
|
* just give -no-kvm to disable acceleration if required */
|
|
guest = virCapabilitiesAddGuest(caps, VIR_DOMAIN_OSTYPE_HVM,
|
|
guestarch, binary,
|
|
NULL, nmachines, machines);
|
|
|
|
/* CPU selection is always available, because all QEMU versions
|
|
* we support can use at least '-cpu host' */
|
|
virCapabilitiesAddGuestFeature(guest, VIR_CAPS_GUEST_FEATURE_TYPE_CPUSELECTION);
|
|
virCapabilitiesAddGuestFeature(guest, VIR_CAPS_GUEST_FEATURE_TYPE_DEVICEBOOT);
|
|
virCapabilitiesAddGuestFeatureWithToggle(guest, VIR_CAPS_GUEST_FEATURE_TYPE_DISKSNAPSHOT,
|
|
true, false);
|
|
virCapabilitiesAddGuestFeature(guest, VIR_CAPS_GUEST_FEATURE_TYPE_EXTERNAL_SNAPSHOT);
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_TCG)) {
|
|
virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_QEMU,
|
|
NULL, NULL, 0, NULL);
|
|
}
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM)) {
|
|
virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_KVM,
|
|
NULL, NULL, 0, NULL);
|
|
}
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_HVF)) {
|
|
virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_HVF,
|
|
NULL, NULL, 0, NULL);
|
|
}
|
|
|
|
if (ARCH_IS_X86(guestarch) || guestarch == VIR_ARCH_AARCH64 ||
|
|
ARCH_IS_LOONGARCH(guestarch))
|
|
virCapabilitiesAddGuestFeatureWithToggle(guest, VIR_CAPS_GUEST_FEATURE_TYPE_ACPI,
|
|
true, true);
|
|
|
|
if (ARCH_IS_X86(guestarch))
|
|
virCapabilitiesAddGuestFeatureWithToggle(guest, VIR_CAPS_GUEST_FEATURE_TYPE_APIC,
|
|
true, false);
|
|
|
|
if (guestarch == VIR_ARCH_I686) {
|
|
virCapabilitiesAddGuestFeature(guest, VIR_CAPS_GUEST_FEATURE_TYPE_PAE);
|
|
virCapabilitiesAddGuestFeature(guest, VIR_CAPS_GUEST_FEATURE_TYPE_NONPAE);
|
|
}
|
|
}
|
|
|
|
|
|
virCPUDef *
|
|
virQEMUCapsProbeHostCPU(virArch hostArch,
|
|
virDomainCapsCPUModels *models)
|
|
{
|
|
return virCPUGetHost(hostArch, VIR_CPU_TYPE_GUEST, NULL, models);
|
|
}
|
|
|
|
|
|
virCaps *
|
|
virQEMUCapsInit(virFileCache *cache)
|
|
{
|
|
g_autoptr(virCaps) caps = NULL;
|
|
size_t i;
|
|
virArch hostarch = virArchFromHost();
|
|
|
|
if ((caps = virCapabilitiesNew(hostarch,
|
|
true, true)) == NULL)
|
|
return NULL;
|
|
|
|
if (virCapabilitiesInitCaches(caps) < 0)
|
|
VIR_WARN("Failed to get host CPU cache info");
|
|
|
|
/* Add the power management features of the host */
|
|
if (virNodeSuspendGetTargetMask(&caps->host.powerMgmt) < 0)
|
|
VIR_WARN("Failed to get host power management capabilities");
|
|
|
|
/* Add IOMMU info */
|
|
virCapabilitiesHostInitIOMMU(caps);
|
|
|
|
/* Add huge pages info */
|
|
if (virCapabilitiesInitPages(caps) < 0)
|
|
VIR_WARN("Failed to get pages info");
|
|
|
|
/* Add domain migration transport URIs */
|
|
virCapabilitiesAddHostMigrateTransport(caps, "tcp");
|
|
virCapabilitiesAddHostMigrateTransport(caps, "rdma");
|
|
|
|
/* QEMU can support pretty much every arch that exists,
|
|
* so just probe for them all - we gracefully fail
|
|
* if a qemu-system-$ARCH binary can't be found
|
|
*/
|
|
for (i = 0; i < VIR_ARCH_LAST; i++)
|
|
virQEMUCapsInitGuest(caps, cache, hostarch, i);
|
|
|
|
return g_steal_pointer(&caps);
|
|
}
|
|
|
|
|
|
struct virQEMUCapsStringFlags {
|
|
const char *value;
|
|
int flag;
|
|
};
|
|
|
|
|
|
struct virQEMUCapsStringFlags virQEMUCapsCommands[] = {
|
|
{ "query-vnc", QEMU_CAPS_VNC },
|
|
{ "rtc-reset-reinjection", QEMU_CAPS_RTC_RESET_REINJECTION },
|
|
{ "query-cpu-model-expansion", QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION },
|
|
{ "query-cpu-definitions", QEMU_CAPS_QUERY_CPU_DEFINITIONS },
|
|
{ "query-cpu-model-baseline", QEMU_CAPS_QUERY_CPU_MODEL_BASELINE },
|
|
{ "query-cpu-model-comparison", QEMU_CAPS_QUERY_CPU_MODEL_COMPARISON },
|
|
{ "block-export-add", QEMU_CAPS_BLOCK_EXPORT_ADD },
|
|
{ "blockdev-reopen", QEMU_CAPS_BLOCKDEV_REOPEN },
|
|
{ "set-action", QEMU_CAPS_SET_ACTION },
|
|
{ "query-dirty-rate", QEMU_CAPS_QUERY_DIRTY_RATE },
|
|
{ "sev-inject-launch-secret", QEMU_CAPS_SEV_INJECT_LAUNCH_SECRET },
|
|
{ "calc-dirty-rate", QEMU_CAPS_CALC_DIRTY_RATE },
|
|
{ "query-stats", QEMU_CAPS_QUERY_STATS },
|
|
{ "query-stats-schemas", QEMU_CAPS_QUERY_STATS_SCHEMAS },
|
|
{ "display-reload", QEMU_CAPS_DISPLAY_RELOAD },
|
|
{ "snapshot-save", QEMU_CAPS_SNAPSHOT_INTERNAL_QMP },
|
|
};
|
|
|
|
struct virQEMUCapsStringFlags virQEMUCapsMigration[] = {
|
|
{ "rdma-pin-all", QEMU_CAPS_MIGRATE_RDMA },
|
|
};
|
|
|
|
struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = {
|
|
{ "hda-duplex", QEMU_CAPS_HDA_DUPLEX },
|
|
{ "hda-micro", QEMU_CAPS_HDA_MICRO },
|
|
{ "ccid-card-emulated", QEMU_CAPS_CCID_EMULATED },
|
|
{ "ccid-card-passthru", QEMU_CAPS_CCID_PASSTHRU },
|
|
{ "piix3-usb-uhci", QEMU_CAPS_PIIX3_USB_UHCI },
|
|
{ "piix4-usb-uhci", QEMU_CAPS_PIIX4_USB_UHCI },
|
|
{ "usb-ehci", QEMU_CAPS_USB_EHCI },
|
|
{ "ich9-usb-ehci1", QEMU_CAPS_ICH9_USB_EHCI1 },
|
|
{ "vt82c686b-usb-uhci", QEMU_CAPS_VT82C686B_USB_UHCI },
|
|
{ "pci-ohci", QEMU_CAPS_PCI_OHCI },
|
|
{ "nec-usb-xhci", QEMU_CAPS_NEC_USB_XHCI },
|
|
{ "usb-redir", QEMU_CAPS_USB_REDIR },
|
|
{ "usb-hub", QEMU_CAPS_USB_HUB },
|
|
{ "ich9-ahci", QEMU_CAPS_ICH9_AHCI },
|
|
{ "virtio-blk-ccw", QEMU_CAPS_VIRTIO_CCW },
|
|
{ "sclpconsole", QEMU_CAPS_DEVICE_SCLPCONSOLE },
|
|
{ "lsi53c895a", QEMU_CAPS_SCSI_LSI },
|
|
{ "virtio-scsi-pci", QEMU_CAPS_VIRTIO_SCSI },
|
|
{ "virtio-scsi-ccw", QEMU_CAPS_VIRTIO_SCSI },
|
|
{ "virtio-scsi-device", QEMU_CAPS_VIRTIO_SCSI },
|
|
{ "megasas", QEMU_CAPS_SCSI_MEGASAS },
|
|
{ "qxl", QEMU_CAPS_DEVICE_QXL },
|
|
{ "scsi-block", QEMU_CAPS_SCSI_BLOCK },
|
|
{ "VGA", QEMU_CAPS_DEVICE_VGA },
|
|
{ "cirrus-vga", QEMU_CAPS_DEVICE_CIRRUS_VGA },
|
|
{ "vmware-svga", QEMU_CAPS_DEVICE_VMWARE_SVGA },
|
|
{ "usb-serial", QEMU_CAPS_DEVICE_USB_SERIAL },
|
|
{ "virtio-rng-pci", QEMU_CAPS_DEVICE_VIRTIO_RNG },
|
|
{ "virtio-rng-ccw", QEMU_CAPS_DEVICE_VIRTIO_RNG },
|
|
{ "virtio-rng-device", QEMU_CAPS_DEVICE_VIRTIO_RNG },
|
|
{ "rng-random", QEMU_CAPS_OBJECT_RNG_RANDOM },
|
|
{ "rng-egd", QEMU_CAPS_OBJECT_RNG_EGD },
|
|
{ "spapr-nvram", QEMU_CAPS_DEVICE_NVRAM },
|
|
{ "pci-bridge", QEMU_CAPS_DEVICE_PCI_BRIDGE },
|
|
{ "vfio-pci", QEMU_CAPS_DEVICE_VFIO_PCI },
|
|
{ "i82801b11-bridge", QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE },
|
|
{ "usb-storage", QEMU_CAPS_DEVICE_USB_STORAGE },
|
|
{ "virtio-mmio", QEMU_CAPS_DEVICE_VIRTIO_MMIO },
|
|
{ "ich9-intel-hda", QEMU_CAPS_DEVICE_ICH9_INTEL_HDA },
|
|
{ "pvpanic", QEMU_CAPS_DEVICE_PANIC },
|
|
{ "usb-kbd", QEMU_CAPS_DEVICE_USB_KBD },
|
|
{ "usb-audio", QEMU_CAPS_OBJECT_USB_AUDIO },
|
|
{ "pc-dimm", QEMU_CAPS_DEVICE_PC_DIMM },
|
|
{ "pci-serial", QEMU_CAPS_DEVICE_PCI_SERIAL },
|
|
{ "ioh3420", QEMU_CAPS_DEVICE_IOH3420 },
|
|
{ "x3130-upstream", QEMU_CAPS_DEVICE_X3130_UPSTREAM },
|
|
{ "xio3130-downstream", QEMU_CAPS_DEVICE_XIO3130_DOWNSTREAM },
|
|
{ "rtl8139", QEMU_CAPS_DEVICE_RTL8139 },
|
|
{ "e1000", QEMU_CAPS_DEVICE_E1000 },
|
|
{ "virtio-net-pci", QEMU_CAPS_DEVICE_VIRTIO_NET },
|
|
{ "virtio-net-ccw", QEMU_CAPS_DEVICE_VIRTIO_NET },
|
|
{ "virtio-net-device", QEMU_CAPS_DEVICE_VIRTIO_NET },
|
|
{ "virtio-gpu-pci", QEMU_CAPS_DEVICE_VIRTIO_GPU },
|
|
{ "virtio-gpu-device", QEMU_CAPS_DEVICE_VIRTIO_GPU },
|
|
{ "virtio-vga", QEMU_CAPS_DEVICE_VIRTIO_VGA },
|
|
{ "virtio-keyboard-device", QEMU_CAPS_VIRTIO_KEYBOARD },
|
|
{ "virtio-keyboard-pci", QEMU_CAPS_VIRTIO_KEYBOARD },
|
|
{ "virtio-mouse-device", QEMU_CAPS_VIRTIO_MOUSE },
|
|
{ "virtio-mouse-pci", QEMU_CAPS_VIRTIO_MOUSE },
|
|
{ "virtio-tablet-device", QEMU_CAPS_VIRTIO_TABLET },
|
|
{ "virtio-tablet-pci", QEMU_CAPS_VIRTIO_TABLET },
|
|
{ "virtio-input-host-device", QEMU_CAPS_VIRTIO_INPUT_HOST },
|
|
{ "virtio-input-host-pci", QEMU_CAPS_VIRTIO_INPUT_HOST },
|
|
{ "mptsas1068", QEMU_CAPS_SCSI_MPTSAS1068 },
|
|
{ "pxb", QEMU_CAPS_DEVICE_PXB },
|
|
{ "pxb-pcie", QEMU_CAPS_DEVICE_PXB_PCIE },
|
|
{ "intel-iommu", QEMU_CAPS_DEVICE_INTEL_IOMMU },
|
|
{ "ivshmem-plain", QEMU_CAPS_DEVICE_IVSHMEM_PLAIN },
|
|
{ "ivshmem-doorbell", QEMU_CAPS_DEVICE_IVSHMEM_DOORBELL },
|
|
{ "vhost-scsi", QEMU_CAPS_DEVICE_VHOST_SCSI },
|
|
{ "nvdimm", QEMU_CAPS_DEVICE_NVDIMM },
|
|
{ "pcie-root-port", QEMU_CAPS_DEVICE_PCIE_ROOT_PORT },
|
|
{ "qemu-xhci", QEMU_CAPS_DEVICE_QEMU_XHCI },
|
|
{ "spapr-pci-host-bridge", QEMU_CAPS_DEVICE_SPAPR_PCI_HOST_BRIDGE },
|
|
{ "vmcoreinfo", QEMU_CAPS_DEVICE_VMCOREINFO },
|
|
{ "spapr-vty", QEMU_CAPS_DEVICE_SPAPR_VTY },
|
|
{ "sclplmconsole", QEMU_CAPS_DEVICE_SCLPLMCONSOLE },
|
|
{ "isa-serial", QEMU_CAPS_DEVICE_ISA_SERIAL },
|
|
{ "pl011", QEMU_CAPS_DEVICE_PL011 },
|
|
{ "virtio-gpu-ccw", QEMU_CAPS_DEVICE_VIRTIO_GPU_CCW },
|
|
{ "virtio-keyboard-ccw", QEMU_CAPS_DEVICE_VIRTIO_KEYBOARD_CCW },
|
|
{ "virtio-mouse-ccw", QEMU_CAPS_DEVICE_VIRTIO_MOUSE_CCW },
|
|
{ "virtio-tablet-ccw", QEMU_CAPS_DEVICE_VIRTIO_TABLET_CCW },
|
|
{ "pcie-pci-bridge", QEMU_CAPS_DEVICE_PCIE_PCI_BRIDGE },
|
|
{ "pr-manager-helper", QEMU_CAPS_PR_MANAGER_HELPER },
|
|
{ "vfio-ccw", QEMU_CAPS_DEVICE_VFIO_CCW },
|
|
{ "hda-output", QEMU_CAPS_HDA_OUTPUT },
|
|
{ "vmgenid", QEMU_CAPS_DEVICE_VMGENID },
|
|
{ "vhost-vsock-device", QEMU_CAPS_DEVICE_VHOST_VSOCK },
|
|
{ "mch", QEMU_CAPS_DEVICE_MCH },
|
|
{ "sev-guest", QEMU_CAPS_SEV_GUEST },
|
|
{ "vfio-ap", QEMU_CAPS_DEVICE_VFIO_AP },
|
|
{ "zpci", QEMU_CAPS_DEVICE_ZPCI },
|
|
{ "memory-backend-memfd", QEMU_CAPS_OBJECT_MEMORY_MEMFD },
|
|
{ "virtio-blk-pci-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "virtio-blk-pci-non-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "virtio-net-pci-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "virtio-net-pci-non-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "vhost-scsi-pci-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "vhost-scsi-pci-non-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "virtio-rng-pci-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "virtio-rng-pci-non-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "virtio-9p-pci-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "virtio-9p-pci-non-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "virtio-balloon-pci-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "virtio-balloon-pci-non-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "vhost-vsock-pci-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "vhost-vsock-pci-non-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "virtio-scsi-pci-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "virtio-scsi-pci-non-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "virtio-serial-pci-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "virtio-serial-pci-non-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL },
|
|
{ "max-x86_64-cpu", QEMU_CAPS_X86_MAX_CPU },
|
|
{ "bochs-display", QEMU_CAPS_DEVICE_BOCHS_DISPLAY },
|
|
{ "dbus-vmstate", QEMU_CAPS_DBUS_VMSTATE },
|
|
{ "vhost-user-gpu", QEMU_CAPS_DEVICE_VHOST_USER_GPU },
|
|
{ "vhost-user-vga", QEMU_CAPS_DEVICE_VHOST_USER_VGA },
|
|
{ "ramfb", QEMU_CAPS_DEVICE_RAMFB },
|
|
{ "max-arm-cpu", QEMU_CAPS_ARM_MAX_CPU },
|
|
{ "i8042", QEMU_CAPS_DEVICE_I8042 },
|
|
{ "rng-builtin", QEMU_CAPS_OBJECT_RNG_BUILTIN },
|
|
{ "tpm-spapr", QEMU_CAPS_DEVICE_TPM_SPAPR },
|
|
{ "vhost-user-fs-device", QEMU_CAPS_DEVICE_VHOST_USER_FS },
|
|
{ "tcg-accel", QEMU_CAPS_TCG },
|
|
{ "pvscsi", QEMU_CAPS_SCSI_PVSCSI },
|
|
{ "spapr-tpm-proxy", QEMU_CAPS_DEVICE_SPAPR_TPM_PROXY },
|
|
{ "vmport", QEMU_CAPS_MACHINE_VMPORT_OPT },
|
|
/*
|
|
* We don't probe 'esp' directly, because it is often reported
|
|
* as present for all QEMU binaries, due to it being enabled
|
|
* for built as a dependency of dc390/am53c974 PCI SCSI
|
|
* controllers.
|
|
*
|
|
* The base 'esp' device is only used as a built-in device
|
|
* and is not user-creatable. So we turn this cap on later
|
|
* based on arch.
|
|
*
|
|
* { "esp", QEMU_CAPS_SCSI_NCR53C90 },
|
|
*/
|
|
{ "dc390", QEMU_CAPS_SCSI_DC390 },
|
|
{ "am53c974", QEMU_CAPS_SCSI_AM53C974 },
|
|
{ "virtio-pmem-pci", QEMU_CAPS_DEVICE_VIRTIO_PMEM_PCI },
|
|
{ "vhost-user-blk", QEMU_CAPS_DEVICE_VHOST_USER_BLK },
|
|
{ "input-linux", QEMU_CAPS_INPUT_LINUX },
|
|
{ "virtio-gpu-gl-pci", QEMU_CAPS_VIRTIO_GPU_GL_PCI },
|
|
{ "virtio-vga-gl", QEMU_CAPS_VIRTIO_VGA_GL },
|
|
{ "s390-pv-guest", QEMU_CAPS_S390_PV_GUEST },
|
|
{ "virtio-mem-pci", QEMU_CAPS_DEVICE_VIRTIO_MEM_PCI },
|
|
{ "virtio-iommu-pci", QEMU_CAPS_DEVICE_VIRTIO_IOMMU_PCI },
|
|
{ "sgx-epc", QEMU_CAPS_SGX_EPC },
|
|
{ "thread-context", QEMU_CAPS_THREAD_CONTEXT },
|
|
{ "virtio-crypto-pci", QEMU_CAPS_DEVICE_VIRTIO_CRYPTO },
|
|
{ "virtio-crypto-device", QEMU_CAPS_DEVICE_VIRTIO_CRYPTO },
|
|
{ "cryptodev-backend-lkcf", QEMU_CAPS_OBJECT_CRYPTO_LKCF },
|
|
{ "pvpanic-pci", QEMU_CAPS_DEVICE_PANIC_PCI },
|
|
{ "usb-mtp", QEMU_CAPS_DEVICE_USB_MTP },
|
|
{ "virtio-sound-pci", QEMU_CAPS_DEVICE_VIRTIO_SOUND },
|
|
{ "virtio-sound-device", QEMU_CAPS_DEVICE_VIRTIO_SOUND },
|
|
{ "sev-snp-guest", QEMU_CAPS_SEV_SNP_GUEST },
|
|
{ "acpi-erst", QEMU_CAPS_DEVICE_ACPI_ERST },
|
|
};
|
|
|
|
|
|
struct virQEMUCapsDevicePropsFlags {
|
|
const char *value;
|
|
int flag;
|
|
int (*cb)(virJSONValue *props, virQEMUCaps *caps);
|
|
};
|
|
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVirtioBalloon[] = {
|
|
{ "deflate-on-oom", QEMU_CAPS_VIRTIO_BALLOON_AUTODEFLATE, NULL },
|
|
{ "free-page-reporting", QEMU_CAPS_VIRTIO_BALLOON_FREE_PAGE_REPORTING, NULL },
|
|
{ "acpi-index", QEMU_CAPS_ACPI_INDEX, NULL },
|
|
};
|
|
|
|
|
|
static int
|
|
virQEMUCapsDevicePropsVirtioBlkSCSIDefault(virJSONValue *props,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
bool def = false;
|
|
|
|
if (virJSONValueObjectGetBoolean(props, "default-value", &def) < 0)
|
|
return 0;
|
|
|
|
if (def == false)
|
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_SCSI_DEFAULT_DISABLED);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVirtioBlk[] = {
|
|
{ "scsi", QEMU_CAPS_VIRTIO_BLK_SCSI, virQEMUCapsDevicePropsVirtioBlkSCSIDefault },
|
|
{ "queue-size", QEMU_CAPS_VIRTIO_BLK_QUEUE_SIZE, NULL },
|
|
{ "acpi-index", QEMU_CAPS_ACPI_INDEX, NULL },
|
|
{ "iothread-vq-mapping", QEMU_CAPS_VIRTIO_BLK_IOTHREAD_MAPPING, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVirtioNet[] = {
|
|
{ "acpi-index", QEMU_CAPS_ACPI_INDEX, NULL },
|
|
{ "rss", QEMU_CAPS_VIRTIO_NET_RSS, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsPCIeRootPort[] = {
|
|
{ "hotplug", QEMU_CAPS_PCIE_ROOT_PORT_HOTPLUG, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsUSBHost[] = {
|
|
{ "hostdevice", QEMU_CAPS_USB_HOST_HOSTDEVICE, NULL },
|
|
{ "guest-resets-all", QEMU_CAPS_USB_HOST_GUESTS_RESETS_ALL, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsSpaprPCIHostBridge[] = {
|
|
{ "numa_node", QEMU_CAPS_SPAPR_PCI_HOST_BRIDGE_NUMA_NODE, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVirtioSCSI[] = {
|
|
{ "acpi-index", QEMU_CAPS_ACPI_INDEX, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVfioPCI[] = {
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsSCSIDisk[] = {
|
|
{ "channel", QEMU_CAPS_SCSI_DISK_CHANNEL, NULL },
|
|
{ "rotation_rate", QEMU_CAPS_ROTATION_RATE, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsIDEDrive[] = {
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsPiix4PM[] = {
|
|
{ "acpi-root-pci-hotplug", QEMU_CAPS_PIIX4_ACPI_ROOT_PCI_HOTPLUG, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsUSBRedir[] = {
|
|
{ "filter", QEMU_CAPS_USB_REDIR_FILTER, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsUSBStorage[] = {
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsKVMPit[] = {
|
|
{ "lost_tick_policy", QEMU_CAPS_KVM_PIT_TICK_POLICY, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVGA[] = {
|
|
{ "vgamem_mb", QEMU_CAPS_VGA_VGAMEM, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVmwareSvga[] = {
|
|
{ "vgamem_mb", QEMU_CAPS_VMWARE_SVGA_VGAMEM, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsQxl[] = {
|
|
{ "vgamem_mb", QEMU_CAPS_QXL_VGAMEM, NULL },
|
|
{ "vram64_size_mb", QEMU_CAPS_QXL_VRAM64, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVirtioGpu[] = {
|
|
{ "virgl", QEMU_CAPS_VIRTIO_GPU_VIRGL, NULL },
|
|
{ "acpi-index", QEMU_CAPS_ACPI_INDEX, NULL },
|
|
{ "blob", QEMU_CAPS_VIRTIO_GPU_BLOB, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsICH9[] = {
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsIntelIOMMU[] = {
|
|
{ "intremap", QEMU_CAPS_INTEL_IOMMU_INTREMAP, NULL },
|
|
{ "caching-mode", QEMU_CAPS_INTEL_IOMMU_CACHING_MODE, NULL },
|
|
{ "eim", QEMU_CAPS_INTEL_IOMMU_EIM, NULL },
|
|
{ "device-iotlb", QEMU_CAPS_INTEL_IOMMU_DEVICE_IOTLB, NULL },
|
|
{ "aw-bits", QEMU_CAPS_INTEL_IOMMU_AW_BITS, NULL },
|
|
{ "dma-translation", QEMU_CAPS_INTEL_IOMMU_DMA_TRANSLATION, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsMCH[] = {
|
|
{ "extended-tseg-mbytes", QEMU_CAPS_MCH_EXTENDED_TSEG_MBYTES, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsNVDIMM[] = {
|
|
{ "unarmed", QEMU_CAPS_DEVICE_NVDIMM_UNARMED, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVhostUserFS[] = {
|
|
{ "bootindex", QEMU_CAPS_VHOST_USER_FS_BOOTINDEX, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVirtioMemPCI[] = {
|
|
{ "prealloc", QEMU_CAPS_DEVICE_VIRTIO_MEM_PCI_PREALLOC, NULL },
|
|
{ "dynamic-memslots", QEMU_CAPS_DEVICE_VIRTIO_MEM_PCI_DYNAMIC_MEMSLOTS, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVirtioIOMMU[] = {
|
|
{ "boot-bypass", QEMU_CAPS_VIRTIO_IOMMU_BOOT_BYPASS, NULL },
|
|
};
|
|
|
|
static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVirtioBlkCCW[] = {
|
|
{ "loadparm", QEMU_CAPS_VIRTIO_CCW_DEVICE_LOADPARM, NULL },
|
|
};
|
|
|
|
/* see documentation for virQEMUQAPISchemaPathGet for the query format */
|
|
static struct virQEMUCapsStringFlags virQEMUCapsQMPSchemaQueries[] = {
|
|
{ "blockdev-add/arg-type/+file/drop-cache", QEMU_CAPS_MIGRATION_FILE_DROP_CACHE },
|
|
{ "blockdev-add/arg-type/+nvme", QEMU_CAPS_DRIVE_NVME },
|
|
{ "blockdev-add/arg-type/+file/aio/^io_uring", QEMU_CAPS_AIO_IO_URING },
|
|
{ "blockdev-add/arg-type/+rbd/encrypt", QEMU_CAPS_RBD_ENCRYPTION },
|
|
{ "blockdev-add/arg-type/+rbd/encrypt/parent", QEMU_CAPS_RBD_ENCRYPTION_LAYERING },
|
|
{ "blockdev-add/arg-type/+rbd/encrypt/format/^luks-any", QEMU_CAPS_RBD_ENCRYPTION_LUKS_ANY },
|
|
{ "blockdev-add/arg-type/+nbd/tls-hostname", QEMU_CAPS_BLOCKDEV_NBD_TLS_HOSTNAME },
|
|
{ "blockdev-add/arg-type/+qcow2/discard-no-unref", QEMU_CAPS_QCOW2_DISCARD_NO_UNREF },
|
|
{ "blockdev-add/arg-type/+virtio-blk-vhost-vdpa/$fdset", QEMU_CAPS_DEVICE_VIRTIO_BLK_VHOST_VDPA},
|
|
{ "blockdev-snapshot/$allow-write-only-overlay", QEMU_CAPS_BLOCKDEV_SNAPSHOT_ALLOW_WRITE_ONLY },
|
|
{ "calc-dirty-rate/arg-type/mode", QEMU_CAPS_DIRTYRATE_MODE },
|
|
{ "chardev-add/arg-type/backend/+socket/data/reconnect", QEMU_CAPS_CHARDEV_RECONNECT },
|
|
{ "chardev-add/arg-type/backend/+socket/data/reconnect-ms", QEMU_CAPS_CHARDEV_RECONNECT_MILISECONDS },
|
|
{ "chardev-add/arg-type/backend/+qemu-vdagent", QEMU_CAPS_CHARDEV_QEMU_VDAGENT },
|
|
{ "device_add/$json-cli-hotplug", QEMU_CAPS_DEVICE_JSON },
|
|
{ "migrate-set-parameters/arg-type/block-bitmap-mapping/bitmaps/transform", QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING },
|
|
{ "nbd-server-start/arg-type/tls-creds", QEMU_CAPS_NBD_TLS },
|
|
{ "nbd-server-add/arg-type/bitmap", QEMU_CAPS_NBD_BITMAP },
|
|
{ "netdev_add/arg-type/+stream", QEMU_CAPS_NETDEV_STREAM },
|
|
{ "netdev_add/arg-type/+stream/reconnect", QEMU_CAPS_NETDEV_STREAM_RECONNECT },
|
|
{ "netdev_add/arg-type/+vhost-vdpa", QEMU_CAPS_NETDEV_VHOST_VDPA },
|
|
/* JSON support for -netdev was introduced for the 'dgram' netdev type */
|
|
{ "netdev_add/arg-type/type/^dgram", QEMU_CAPS_NETDEV_JSON },
|
|
{ "netdev_add/arg-type/+user", QEMU_CAPS_NETDEV_USER },
|
|
{ "netdev_add/arg-type/+stream/reconnect-ms", QEMU_CAPS_NETDEV_STREAM_RECONNECT_MILISECONDS },
|
|
{ "object-add/arg-type/qom-type/^secret", QEMU_CAPS_OBJECT_JSON },
|
|
{ "object-add/arg-type/+sev-guest/kernel-hashes", QEMU_CAPS_SEV_GUEST_KERNEL_HASHES },
|
|
{ "object-add/arg-type/+iothread/thread-pool-max", QEMU_CAPS_IOTHREAD_THREAD_POOL_MAX },
|
|
{ "query-display-options/ret-type/+egl-headless/rendernode", QEMU_CAPS_EGL_HEADLESS_RENDERNODE },
|
|
{ "query-display-options/ret-type/+sdl", QEMU_CAPS_SDL },
|
|
{ "query-display-options/ret-type/+egl-headless", QEMU_CAPS_EGL_HEADLESS },
|
|
{ "query-display-options/ret-type/+dbus", QEMU_CAPS_DISPLAY_DBUS },
|
|
{ "query-hotpluggable-cpus/ret-type/props/die-id", QEMU_CAPS_SMP_DIES },
|
|
{ "query-hotpluggable-cpus/ret-type/props/cluster-id", QEMU_CAPS_SMP_CLUSTERS },
|
|
{ "query-migrate/ret-type/blocked-reasons", QEMU_CAPS_MIGRATION_BLOCKED_REASONS },
|
|
{ "query-named-block-nodes/arg-type/flat", QEMU_CAPS_QMP_QUERY_NAMED_BLOCK_NODES_FLAT },
|
|
{ "screendump/arg-type/device", QEMU_CAPS_SCREENDUMP_DEVICE },
|
|
{ "screendump/arg-type/format/^png", QEMU_CAPS_SCREENSHOT_FORMAT_PNG },
|
|
{ "set-numa-node/arg-type/+hmat-lb", QEMU_CAPS_NUMA_HMAT },
|
|
{ "query-cpu-model-expansion/ret-type/deprecated-props", QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION_DEPRECATED_PROPS },
|
|
};
|
|
|
|
typedef struct _virQEMUCapsObjectTypeProps virQEMUCapsObjectTypeProps;
|
|
struct _virQEMUCapsObjectTypeProps {
|
|
const char *type;
|
|
struct virQEMUCapsStringFlags *props;
|
|
size_t nprops;
|
|
int capsCondition;
|
|
};
|
|
|
|
|
|
typedef struct _virQEMUCapsDeviceTypeProps virQEMUCapsDeviceTypeProps;
|
|
struct _virQEMUCapsDeviceTypeProps {
|
|
const char *type;
|
|
struct virQEMUCapsDevicePropsFlags *props;
|
|
size_t nprops;
|
|
int capsCondition;
|
|
};
|
|
|
|
|
|
static virQEMUCapsDeviceTypeProps virQEMUCapsDeviceProps[] = {
|
|
{ "virtio-blk-pci", virQEMUCapsDevicePropsVirtioBlk,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioBlk),
|
|
-1 },
|
|
{ "virtio-net-pci", virQEMUCapsDevicePropsVirtioNet,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioNet),
|
|
QEMU_CAPS_DEVICE_VIRTIO_NET },
|
|
{ "virtio-scsi-pci", virQEMUCapsDevicePropsVirtioSCSI,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioSCSI),
|
|
QEMU_CAPS_VIRTIO_SCSI },
|
|
{ "virtio-blk-ccw", virQEMUCapsDevicePropsVirtioBlk,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioBlk),
|
|
QEMU_CAPS_VIRTIO_CCW },
|
|
{ "virtio-net-ccw", virQEMUCapsDevicePropsVirtioNet,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioNet),
|
|
QEMU_CAPS_DEVICE_VIRTIO_NET },
|
|
{ "virtio-scsi-ccw", virQEMUCapsDevicePropsVirtioSCSI,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioSCSI),
|
|
QEMU_CAPS_VIRTIO_SCSI },
|
|
{ "vfio-pci", virQEMUCapsDevicePropsVfioPCI,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVfioPCI),
|
|
QEMU_CAPS_DEVICE_VFIO_PCI },
|
|
{ "scsi-hd", virQEMUCapsDevicePropsSCSIDisk,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsSCSIDisk),
|
|
-1 },
|
|
{ "ide-hd", virQEMUCapsDevicePropsIDEDrive,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsIDEDrive),
|
|
-1 },
|
|
{ "PIIX4_PM", virQEMUCapsDevicePropsPiix4PM,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsPiix4PM),
|
|
-1 },
|
|
{ "usb-redir", virQEMUCapsDevicePropsUSBRedir,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsUSBRedir),
|
|
QEMU_CAPS_USB_REDIR },
|
|
{ "usb-storage", virQEMUCapsDevicePropsUSBStorage,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsUSBStorage),
|
|
QEMU_CAPS_DEVICE_USB_STORAGE },
|
|
{ "kvm-pit", virQEMUCapsDevicePropsKVMPit,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsKVMPit),
|
|
-1 },
|
|
{ "VGA", virQEMUCapsDevicePropsVGA,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVGA),
|
|
QEMU_CAPS_DEVICE_VGA },
|
|
{ "vmware-svga", virQEMUCapsDevicePropsVmwareSvga,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVmwareSvga),
|
|
QEMU_CAPS_DEVICE_VMWARE_SVGA },
|
|
{ "qxl", virQEMUCapsDevicePropsQxl,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsQxl),
|
|
QEMU_CAPS_DEVICE_QXL },
|
|
{ "virtio-gpu-pci", virQEMUCapsDevicePropsVirtioGpu,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioGpu),
|
|
QEMU_CAPS_DEVICE_VIRTIO_GPU },
|
|
{ "virtio-gpu-device", virQEMUCapsDevicePropsVirtioGpu,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioGpu),
|
|
QEMU_CAPS_DEVICE_VIRTIO_GPU },
|
|
{ "ICH9-LPC", virQEMUCapsDevicePropsICH9,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsICH9),
|
|
-1 },
|
|
{ "virtio-balloon-pci", virQEMUCapsDevicePropsVirtioBalloon,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioBalloon),
|
|
-1 },
|
|
{ "virtio-balloon-ccw", virQEMUCapsDevicePropsVirtioBalloon,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioBalloon),
|
|
-1 },
|
|
{ "virtio-balloon-device", virQEMUCapsDevicePropsVirtioBalloon,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioBalloon),
|
|
-1 },
|
|
{ "intel-iommu", virQEMUCapsDevicePropsIntelIOMMU,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsIntelIOMMU),
|
|
QEMU_CAPS_DEVICE_INTEL_IOMMU },
|
|
{ "spapr-pci-host-bridge", virQEMUCapsDevicePropsSpaprPCIHostBridge,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsSpaprPCIHostBridge),
|
|
QEMU_CAPS_DEVICE_SPAPR_PCI_HOST_BRIDGE },
|
|
{ "virtio-gpu-ccw", virQEMUCapsDevicePropsVirtioGpu,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioGpu),
|
|
QEMU_CAPS_DEVICE_VIRTIO_GPU_CCW },
|
|
{ "mch", virQEMUCapsDevicePropsMCH,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsMCH),
|
|
QEMU_CAPS_DEVICE_MCH },
|
|
{ "nvdimm", virQEMUCapsDevicePropsNVDIMM,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsNVDIMM),
|
|
QEMU_CAPS_DEVICE_NVDIMM },
|
|
{ "pcie-root-port", virQEMUCapsDevicePropsPCIeRootPort,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsPCIeRootPort),
|
|
QEMU_CAPS_DEVICE_PCIE_ROOT_PORT },
|
|
{ "usb-host", virQEMUCapsDevicePropsUSBHost,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsUSBHost),
|
|
-1 },
|
|
{ "vhost-user-fs-device", virQEMUCapsDevicePropsVhostUserFS,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVhostUserFS),
|
|
QEMU_CAPS_DEVICE_VHOST_USER_FS },
|
|
{ "virtio-mem-pci", virQEMUCapsDevicePropsVirtioMemPCI,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioMemPCI),
|
|
QEMU_CAPS_DEVICE_VIRTIO_MEM_PCI },
|
|
{ "virtio-iommu-pci", virQEMUCapsDevicePropsVirtioIOMMU,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioIOMMU),
|
|
QEMU_CAPS_DEVICE_VIRTIO_IOMMU_PCI },
|
|
{ "virtio-blk-ccw", virQEMUCapsDevicePropsVirtioBlkCCW,
|
|
G_N_ELEMENTS(virQEMUCapsDevicePropsVirtioBlkCCW),
|
|
QEMU_CAPS_VIRTIO_CCW },
|
|
};
|
|
|
|
static struct virQEMUCapsStringFlags virQEMUCapsObjectPropsMemoryBackendFile[] = {
|
|
{ "pmem", QEMU_CAPS_OBJECT_MEMORY_FILE_PMEM },
|
|
/* As of QEMU commit 8db0b20415c129cf5e577a593a4a0372d90b7cc9 the
|
|
* "x-use-canonical-path-for-ramblock-id" property is considered stable and
|
|
* supported. The 'x-' prefix was kept for compatibility with already
|
|
* released qemu versions. */
|
|
{ "x-use-canonical-path-for-ramblock-id", QEMU_CAPS_X_USE_CANONICAL_PATH_FOR_RAMBLOCK_ID },
|
|
{ "reserve", QEMU_CAPS_MEMORY_BACKEND_RESERVE },
|
|
{ "prealloc-threads", QEMU_CAPS_MEMORY_BACKEND_PREALLOC_THREADS },
|
|
};
|
|
|
|
static struct virQEMUCapsStringFlags virQEMUCapsObjectPropsMemoryBackendMemfd[] = {
|
|
{ "hugetlb", QEMU_CAPS_OBJECT_MEMORY_MEMFD_HUGETLB },
|
|
};
|
|
|
|
static struct virQEMUCapsStringFlags virQEMUCapsObjectPropsMaxCPU[] = {
|
|
{ "unavailable-features", QEMU_CAPS_CPU_UNAVAILABLE_FEATURES },
|
|
{ "kvm-no-adjvtime", QEMU_CAPS_CPU_KVM_NO_ADJVTIME },
|
|
{ "migratable", QEMU_CAPS_CPU_MIGRATABLE },
|
|
};
|
|
|
|
static virQEMUCapsObjectTypeProps virQEMUCapsObjectProps[] = {
|
|
{ "memory-backend-file", virQEMUCapsObjectPropsMemoryBackendFile,
|
|
G_N_ELEMENTS(virQEMUCapsObjectPropsMemoryBackendFile),
|
|
-1 },
|
|
{ "memory-backend-memfd", virQEMUCapsObjectPropsMemoryBackendMemfd,
|
|
G_N_ELEMENTS(virQEMUCapsObjectPropsMemoryBackendMemfd),
|
|
QEMU_CAPS_OBJECT_MEMORY_MEMFD },
|
|
{ "max-x86_64-cpu", virQEMUCapsObjectPropsMaxCPU,
|
|
G_N_ELEMENTS(virQEMUCapsObjectPropsMaxCPU),
|
|
QEMU_CAPS_X86_MAX_CPU },
|
|
{ "max-arm-cpu", virQEMUCapsObjectPropsMaxCPU,
|
|
G_N_ELEMENTS(virQEMUCapsObjectPropsMaxCPU),
|
|
QEMU_CAPS_ARM_MAX_CPU },
|
|
};
|
|
|
|
static struct virQEMUCapsStringFlags virQEMUCapsMachinePropsPSeries[] = {
|
|
};
|
|
|
|
static struct virQEMUCapsStringFlags virQEMUCapsMachinePropsVirt[] = {
|
|
{ "iommu", QEMU_CAPS_MACHINE_VIRT_IOMMU },
|
|
{ "ras", QEMU_CAPS_MACHINE_VIRT_RAS },
|
|
};
|
|
|
|
static struct virQEMUCapsStringFlags virQEMUCapsMachinePropsGeneric[] = {
|
|
{ "confidential-guest-support", QEMU_CAPS_MACHINE_CONFIDENTAL_GUEST_SUPPORT },
|
|
};
|
|
|
|
static struct virQEMUCapsStringFlags virQEMUCapsMachinePropsGenericPC[] = {
|
|
{ "i8042", QEMU_CAPS_MACHINE_I8042_OPT },
|
|
};
|
|
|
|
static virQEMUCapsObjectTypeProps virQEMUCapsMachineProps[] = {
|
|
{ "pseries", virQEMUCapsMachinePropsPSeries,
|
|
G_N_ELEMENTS(virQEMUCapsMachinePropsPSeries),
|
|
-1 },
|
|
{ "virt", virQEMUCapsMachinePropsVirt,
|
|
G_N_ELEMENTS(virQEMUCapsMachinePropsVirt),
|
|
-1 },
|
|
{ "none", virQEMUCapsMachinePropsGeneric,
|
|
G_N_ELEMENTS(virQEMUCapsMachinePropsGeneric),
|
|
-1 },
|
|
{ "generic-pc", virQEMUCapsMachinePropsGenericPC,
|
|
G_N_ELEMENTS(virQEMUCapsMachinePropsGenericPC),
|
|
-1 },
|
|
};
|
|
|
|
static void
|
|
virQEMUCapsProcessStringFlags(virQEMUCaps *qemuCaps,
|
|
size_t nflags,
|
|
struct virQEMUCapsStringFlags *flags,
|
|
char **values)
|
|
{
|
|
size_t i;
|
|
char **value;
|
|
|
|
for (i = 0; i < nflags; i++) {
|
|
if (virQEMUCapsGet(qemuCaps, flags[i].flag))
|
|
continue;
|
|
|
|
for (value = values; *value; value++) {
|
|
if (STREQ(*value, flags[i].value)) {
|
|
virQEMUCapsSet(qemuCaps, flags[i].flag);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int virQEMUCapsGetDefaultVersion(virCaps *caps,
|
|
virFileCache *capsCache,
|
|
unsigned int *version)
|
|
{
|
|
virQEMUCaps *qemucaps;
|
|
virArch hostarch;
|
|
virCapsDomainData *capsdata;
|
|
|
|
if (*version > 0)
|
|
return 0;
|
|
|
|
hostarch = virArchFromHost();
|
|
if (!(capsdata = virCapabilitiesDomainDataLookup(caps,
|
|
VIR_DOMAIN_OSTYPE_HVM,
|
|
hostarch,
|
|
VIR_DOMAIN_VIRT_NONE,
|
|
NULL, NULL,
|
|
true))) {
|
|
return -1;
|
|
}
|
|
|
|
qemucaps = virQEMUCapsCacheLookup(capsCache, capsdata->emulator);
|
|
VIR_FREE(capsdata);
|
|
if (!qemucaps)
|
|
return -1;
|
|
|
|
*version = virQEMUCapsGetVersion(qemucaps);
|
|
virObjectUnref(qemucaps);
|
|
return 0;
|
|
}
|
|
|
|
|
|
virQEMUCaps *
|
|
virQEMUCapsNew(void)
|
|
{
|
|
virQEMUCaps *qemuCaps;
|
|
|
|
if (virQEMUCapsInitialize() < 0)
|
|
abort();
|
|
|
|
qemuCaps = virObjectNew(virQEMUCapsClass);
|
|
qemuCaps->invalidation = true;
|
|
qemuCaps->flags = virBitmapNew(QEMU_CAPS_LAST);
|
|
|
|
return qemuCaps;
|
|
}
|
|
|
|
|
|
virQEMUCaps *
|
|
virQEMUCapsNewBinary(const char *binary)
|
|
{
|
|
virQEMUCaps *qemuCaps = virQEMUCapsNew();
|
|
|
|
qemuCaps->binary = g_strdup(binary);
|
|
|
|
return qemuCaps;
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsHostCPUDataCopy(virQEMUCapsHostCPUData *dst,
|
|
virQEMUCapsHostCPUData *src)
|
|
{
|
|
if (src->info)
|
|
dst->info = qemuMonitorCPUModelInfoCopy(src->info);
|
|
|
|
if (src->reported)
|
|
dst->reported = virCPUDefCopy(src->reported);
|
|
|
|
if (src->migratable)
|
|
dst->migratable = virCPUDefCopy(src->migratable);
|
|
|
|
if (src->full)
|
|
dst->full = virCPUDefCopy(src->full);
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsHostCPUDataClear(virQEMUCapsHostCPUData *cpuData)
|
|
{
|
|
qemuMonitorCPUModelInfoFree(cpuData->info);
|
|
virCPUDefFree(cpuData->reported);
|
|
virCPUDefFree(cpuData->migratable);
|
|
virCPUDefFree(cpuData->full);
|
|
|
|
memset(cpuData, 0, sizeof(*cpuData));
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsSEVInfoCopy(virSEVCapability **dst,
|
|
virSEVCapability *src)
|
|
{
|
|
g_autoptr(virSEVCapability) tmp = NULL;
|
|
|
|
if (!src) {
|
|
*dst = NULL;
|
|
return;
|
|
}
|
|
|
|
tmp = g_new0(virSEVCapability, 1);
|
|
|
|
tmp->pdh = g_strdup(src->pdh);
|
|
tmp->cert_chain = g_strdup(src->cert_chain);
|
|
if (src->cpu0_id != NULL) {
|
|
tmp->cpu0_id = g_strdup(src->cpu0_id);
|
|
}
|
|
|
|
tmp->cbitpos = src->cbitpos;
|
|
tmp->reduced_phys_bits = src->reduced_phys_bits;
|
|
tmp->max_guests = src->max_guests;
|
|
tmp->max_es_guests = src->max_es_guests;
|
|
|
|
*dst = g_steal_pointer(&tmp);
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsSGXInfoCopy(virSGXCapability **dst,
|
|
virSGXCapability *src)
|
|
{
|
|
g_autoptr(virSGXCapability) tmp = NULL;
|
|
|
|
if (!src) {
|
|
*dst = NULL;
|
|
return;
|
|
}
|
|
|
|
tmp = g_new0(virSGXCapability, 1);
|
|
|
|
tmp->flc = src->flc;
|
|
tmp->sgx1 = src->sgx1;
|
|
tmp->sgx2 = src->sgx2;
|
|
tmp->section_size = src->section_size;
|
|
|
|
if (src->nSgxSections > 0) {
|
|
tmp->sgxSections = g_new0(virSGXSection, src->nSgxSections);
|
|
memcpy(tmp->sgxSections, src->sgxSections,
|
|
src->nSgxSections * sizeof(*tmp->sgxSections));
|
|
tmp->nSgxSections = src->nSgxSections;
|
|
}
|
|
|
|
*dst = g_steal_pointer(&tmp);
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsAccelCopyMachineTypes(virQEMUCapsAccel *dst,
|
|
virQEMUCapsAccel *src)
|
|
{
|
|
size_t i;
|
|
|
|
dst->machineTypes = g_new0(virQEMUCapsMachineType, src->nmachineTypes);
|
|
|
|
dst->nmachineTypes = src->nmachineTypes;
|
|
for (i = 0; i < src->nmachineTypes; i++) {
|
|
dst->machineTypes[i].name = g_strdup(src->machineTypes[i].name);
|
|
dst->machineTypes[i].alias = g_strdup(src->machineTypes[i].alias);
|
|
dst->machineTypes[i].defaultCPU = g_strdup(src->machineTypes[i].defaultCPU);
|
|
dst->machineTypes[i].maxCpus = src->machineTypes[i].maxCpus;
|
|
dst->machineTypes[i].hotplugCpus = src->machineTypes[i].hotplugCpus;
|
|
dst->machineTypes[i].qemuDefault = src->machineTypes[i].qemuDefault;
|
|
dst->machineTypes[i].numaMemSupported = src->machineTypes[i].numaMemSupported;
|
|
dst->machineTypes[i].defaultRAMid = g_strdup(src->machineTypes[i].defaultRAMid);
|
|
dst->machineTypes[i].deprecated = src->machineTypes[i].deprecated;
|
|
dst->machineTypes[i].acpi = src->machineTypes[i].acpi;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsAccelCopy(virQEMUCapsAccel *dst,
|
|
virQEMUCapsAccel *src)
|
|
{
|
|
virQEMUCapsAccelCopyMachineTypes(dst, src);
|
|
|
|
virQEMUCapsHostCPUDataCopy(&dst->hostCPU, &src->hostCPU);
|
|
|
|
dst->cpuModels = qemuMonitorCPUDefsCopy(src->cpuModels);
|
|
}
|
|
|
|
|
|
virQEMUCaps *virQEMUCapsNewCopy(virQEMUCaps *qemuCaps)
|
|
{
|
|
g_autoptr(virQEMUCaps) ret = virQEMUCapsNewBinary(qemuCaps->binary);
|
|
size_t i;
|
|
|
|
ret->invalidation = qemuCaps->invalidation;
|
|
ret->kvmSupportsNesting = qemuCaps->kvmSupportsNesting;
|
|
ret->kvmSupportsSecureGuest = qemuCaps->kvmSupportsSecureGuest;
|
|
|
|
ret->ctime = qemuCaps->ctime;
|
|
|
|
virBitmapFree(ret->flags);
|
|
ret->flags = virBitmapNewCopy(qemuCaps->flags);
|
|
|
|
ret->version = qemuCaps->version;
|
|
ret->microcodeVersion = qemuCaps->microcodeVersion;
|
|
ret->hostCPUSignature = g_strdup(qemuCaps->hostCPUSignature);
|
|
|
|
ret->package = g_strdup(qemuCaps->package);
|
|
ret->kernelVersion = g_strdup(qemuCaps->kernelVersion);
|
|
|
|
ret->arch = qemuCaps->arch;
|
|
ret->cpuData = virCPUDataNewCopy(qemuCaps->cpuData);
|
|
|
|
virQEMUCapsAccelCopy(&ret->kvm, &qemuCaps->kvm);
|
|
virQEMUCapsAccelCopy(&ret->hvf, &qemuCaps->hvf);
|
|
virQEMUCapsAccelCopy(&ret->tcg, &qemuCaps->tcg);
|
|
|
|
ret->gicCapabilities = g_new0(virGICCapability, qemuCaps->ngicCapabilities);
|
|
ret->ngicCapabilities = qemuCaps->ngicCapabilities;
|
|
for (i = 0; i < qemuCaps->ngicCapabilities; i++)
|
|
ret->gicCapabilities[i] = qemuCaps->gicCapabilities[i];
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST))
|
|
virQEMUCapsSEVInfoCopy(&ret->sevCapabilities, qemuCaps->sevCapabilities);
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SGX_EPC))
|
|
virQEMUCapsSGXInfoCopy(&ret->sgxCapabilities, qemuCaps->sgxCapabilities);
|
|
|
|
ret->hypervCapabilities = g_memdup(qemuCaps->hypervCapabilities,
|
|
sizeof(virDomainCapsFeatureHyperv));
|
|
|
|
return g_steal_pointer(&ret);
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsAccelClear(virQEMUCapsAccel *caps)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < caps->nmachineTypes; i++) {
|
|
VIR_FREE(caps->machineTypes[i].name);
|
|
VIR_FREE(caps->machineTypes[i].alias);
|
|
VIR_FREE(caps->machineTypes[i].defaultCPU);
|
|
VIR_FREE(caps->machineTypes[i].defaultRAMid);
|
|
}
|
|
VIR_FREE(caps->machineTypes);
|
|
|
|
virQEMUCapsHostCPUDataClear(&caps->hostCPU);
|
|
qemuMonitorCPUDefsFree(caps->cpuModels);
|
|
}
|
|
|
|
|
|
void virQEMUCapsDispose(void *obj)
|
|
{
|
|
virQEMUCaps *qemuCaps = obj;
|
|
|
|
virBitmapFree(qemuCaps->flags);
|
|
|
|
g_free(qemuCaps->package);
|
|
g_free(qemuCaps->kernelVersion);
|
|
g_free(qemuCaps->binary);
|
|
g_free(qemuCaps->hostCPUSignature);
|
|
|
|
g_free(qemuCaps->gicCapabilities);
|
|
|
|
virCPUDataFree(qemuCaps->cpuData);
|
|
|
|
virSEVCapabilitiesFree(qemuCaps->sevCapabilities);
|
|
virSGXCapabilitiesFree(qemuCaps->sgxCapabilities);
|
|
|
|
g_free(qemuCaps->hypervCapabilities);
|
|
|
|
virQEMUCapsAccelClear(&qemuCaps->kvm);
|
|
virQEMUCapsAccelClear(&qemuCaps->hvf);
|
|
virQEMUCapsAccelClear(&qemuCaps->tcg);
|
|
}
|
|
|
|
void
|
|
virQEMUCapsSet(virQEMUCaps *qemuCaps,
|
|
virQEMUCapsFlags flag)
|
|
{
|
|
ignore_value(virBitmapSetBit(qemuCaps->flags, flag));
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsClear(virQEMUCaps *qemuCaps,
|
|
virQEMUCapsFlags flag)
|
|
{
|
|
ignore_value(virBitmapClearBit(qemuCaps->flags, flag));
|
|
}
|
|
|
|
|
|
bool
|
|
virQEMUCapsGet(virQEMUCaps *qemuCaps,
|
|
virQEMUCapsFlags flag)
|
|
{
|
|
return qemuCaps && virBitmapIsBitSet(qemuCaps->flags, flag);
|
|
}
|
|
|
|
|
|
const char *virQEMUCapsGetBinary(virQEMUCaps *qemuCaps)
|
|
{
|
|
return qemuCaps->binary;
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsSetArch(virQEMUCaps *qemuCaps,
|
|
virArch arch)
|
|
{
|
|
qemuCaps->arch = arch;
|
|
}
|
|
|
|
|
|
virArch virQEMUCapsGetArch(virQEMUCaps *qemuCaps)
|
|
{
|
|
return qemuCaps->arch;
|
|
}
|
|
|
|
|
|
unsigned int virQEMUCapsGetVersion(virQEMUCaps *qemuCaps)
|
|
{
|
|
return qemuCaps->version;
|
|
}
|
|
|
|
|
|
const char *virQEMUCapsGetPackage(virQEMUCaps *qemuCaps)
|
|
{
|
|
return qemuCaps->package;
|
|
}
|
|
|
|
|
|
bool virQEMUCapsGetKVMSupportsSecureGuest(virQEMUCaps *qemuCaps)
|
|
{
|
|
return qemuCaps->kvmSupportsSecureGuest;
|
|
}
|
|
|
|
|
|
static virDomainCapsCPUModels *
|
|
virQEMUCapsCPUDefsToModels(virArch arch,
|
|
qemuMonitorCPUDefs *defs,
|
|
const char **modelAllowed,
|
|
const char **modelForbidden,
|
|
bool extraInfo)
|
|
{
|
|
virDomainCapsCPUModels *cpuModels = NULL;
|
|
size_t i;
|
|
|
|
if (!(cpuModels = virDomainCapsCPUModelsNew(defs->ncpus)))
|
|
return NULL;
|
|
|
|
for (i = 0; i < defs->ncpus; i++) {
|
|
qemuMonitorCPUDefInfo *cpu = defs->cpus + i;
|
|
const char *vendor = NULL;
|
|
const char *canonical = NULL;
|
|
|
|
if (modelAllowed && !g_strv_contains(modelAllowed, cpu->name))
|
|
continue;
|
|
|
|
if (modelForbidden && g_strv_contains(modelForbidden, cpu->name))
|
|
continue;
|
|
|
|
if (extraInfo) {
|
|
vendor = virCPUGetVendorForModel(arch, cpu->name);
|
|
canonical = virCPUGetCanonicalModel(arch, cpu->name);
|
|
}
|
|
|
|
virDomainCapsCPUModelsAdd(cpuModels, cpu->name, cpu->usable,
|
|
cpu->blockers, cpu->deprecated,
|
|
vendor, canonical);
|
|
}
|
|
|
|
virDomainCapsCPUModelsSort(cpuModels);
|
|
return cpuModels;
|
|
}
|
|
|
|
|
|
virDomainCapsCPUModels *
|
|
virQEMUCapsGetCPUModels(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type,
|
|
const char **modelAllowed,
|
|
const char **modelForbidden)
|
|
{
|
|
qemuMonitorCPUDefs *defs;
|
|
|
|
if (!(defs = virQEMUCapsGetAccel(qemuCaps, type)->cpuModels))
|
|
return NULL;
|
|
|
|
return virQEMUCapsCPUDefsToModels(qemuCaps->arch, defs,
|
|
modelAllowed, modelForbidden, true);
|
|
}
|
|
|
|
|
|
virCPUDef *
|
|
virQEMUCapsGetHostModel(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type,
|
|
virQEMUCapsHostCPUType cpuType)
|
|
{
|
|
virQEMUCapsHostCPUData *cpuData;
|
|
|
|
cpuData = &virQEMUCapsGetAccel(qemuCaps, type)->hostCPU;
|
|
switch (cpuType) {
|
|
case VIR_QEMU_CAPS_HOST_CPU_REPORTED:
|
|
return cpuData->reported;
|
|
|
|
case VIR_QEMU_CAPS_HOST_CPU_MIGRATABLE:
|
|
return cpuData->migratable;
|
|
|
|
case VIR_QEMU_CAPS_HOST_CPU_FULL:
|
|
/* 'full' is non-NULL only if we have data from both QEMU and
|
|
* virCPUGetHost */
|
|
return cpuData->full ? cpuData->full : cpuData->reported;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsSetHostModel(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type,
|
|
unsigned int physAddrSize,
|
|
virCPUDef *reported,
|
|
virCPUDef *migratable,
|
|
virCPUDef *full)
|
|
{
|
|
virQEMUCapsHostCPUData *cpuData;
|
|
|
|
cpuData = &virQEMUCapsGetAccel(qemuCaps, type)->hostCPU;
|
|
cpuData->physAddrSize = physAddrSize;
|
|
cpuData->reported = reported;
|
|
cpuData->migratable = migratable;
|
|
cpuData->full = full;
|
|
}
|
|
|
|
|
|
static virCPUMaxPhysAddrDef *
|
|
virQEMUCapsGetHostPhysAddr(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type)
|
|
{
|
|
virQEMUCapsHostCPUData *cpuData;
|
|
virCPUMaxPhysAddrDef *addr = NULL;
|
|
|
|
cpuData = &virQEMUCapsGetAccel(qemuCaps, type)->hostCPU;
|
|
|
|
if (cpuData->physAddrSize != 0) {
|
|
addr = g_new0(virCPUMaxPhysAddrDef, 1);
|
|
|
|
addr->mode = VIR_CPU_MAX_PHYS_ADDR_MODE_PASSTHROUGH;
|
|
addr->limit = cpuData->physAddrSize;
|
|
addr->bits = -1;
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
|
|
bool
|
|
virQEMUCapsIsArchSupported(virQEMUCaps *qemuCaps,
|
|
virArch arch)
|
|
{
|
|
if (arch == qemuCaps->arch)
|
|
return true;
|
|
|
|
if (qemuCaps->arch == VIR_ARCH_X86_64 && arch == VIR_ARCH_I686)
|
|
return true;
|
|
|
|
if (qemuCaps->arch == VIR_ARCH_AARCH64 && arch == VIR_ARCH_ARMV7L)
|
|
return true;
|
|
|
|
if (qemuCaps->arch == VIR_ARCH_ARMV7L && arch == VIR_ARCH_ARMV6L)
|
|
return true;
|
|
|
|
if (qemuCaps->arch == VIR_ARCH_PPC64 && arch == VIR_ARCH_PPC64LE)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool
|
|
virQEMUCapsIsVirtTypeSupported(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType)
|
|
{
|
|
if (virtType == VIR_DOMAIN_VIRT_QEMU &&
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_TCG))
|
|
return true;
|
|
|
|
if (virtType == VIR_DOMAIN_VIRT_HVF &&
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_HVF))
|
|
return true;
|
|
|
|
if (virtType == VIR_DOMAIN_VIRT_KVM &&
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
const char *s390HostPassthroughOnlyMachines[] = {
|
|
"s390-ccw-virtio-2.4",
|
|
"s390-ccw-virtio-2.5",
|
|
"s390-ccw-virtio-2.6",
|
|
"s390-ccw-virtio-2.7",
|
|
NULL
|
|
};
|
|
|
|
bool
|
|
virQEMUCapsIsCPUModeSupported(virQEMUCaps *qemuCaps,
|
|
virArch hostarch,
|
|
virDomainVirtType type,
|
|
virCPUMode mode,
|
|
const char *machineType)
|
|
{
|
|
qemuMonitorCPUDefs *cpus;
|
|
|
|
/* CPU models (except for "host") are not supported by QEMU for on s390
|
|
* KVM domains with old machine types regardless on QEMU version. */
|
|
if (ARCH_IS_S390(qemuCaps->arch) &&
|
|
type == VIR_DOMAIN_VIRT_KVM &&
|
|
mode != VIR_CPU_MODE_HOST_PASSTHROUGH &&
|
|
machineType &&
|
|
g_strv_contains(s390HostPassthroughOnlyMachines, machineType)) {
|
|
return false;
|
|
}
|
|
|
|
switch (mode) {
|
|
case VIR_CPU_MODE_HOST_PASSTHROUGH:
|
|
return virQEMUCapsTypeIsAccelerated(type) &&
|
|
virQEMUCapsGuestIsNative(hostarch, qemuCaps->arch);
|
|
|
|
case VIR_CPU_MODE_HOST_MODEL:
|
|
return !!virQEMUCapsGetHostModel(qemuCaps, type,
|
|
VIR_QEMU_CAPS_HOST_CPU_REPORTED);
|
|
|
|
case VIR_CPU_MODE_CUSTOM:
|
|
cpus = virQEMUCapsGetAccel(qemuCaps, type)->cpuModels;
|
|
return cpus && cpus->ncpus > 0;
|
|
|
|
case VIR_CPU_MODE_MAXIMUM:
|
|
return virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_MAX);
|
|
|
|
case VIR_CPU_MODE_LAST:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsGetCanonicalMachine:
|
|
* @qemuCaps: qemu capabilities object
|
|
* @name: machine name
|
|
*
|
|
* Resolves aliased machine names to the actual machine name. If qemuCaps isn't
|
|
* present @name is returned.
|
|
*/
|
|
const char *
|
|
virQEMUCapsGetCanonicalMachine(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType,
|
|
const char *name)
|
|
{
|
|
virQEMUCapsAccel *accel;
|
|
size_t i;
|
|
|
|
if (!name || !qemuCaps)
|
|
return name;
|
|
|
|
accel = virQEMUCapsGetAccel(qemuCaps, virtType);
|
|
|
|
for (i = 0; i < accel->nmachineTypes; i++) {
|
|
if (!accel->machineTypes[i].alias)
|
|
continue;
|
|
if (STREQ(accel->machineTypes[i].alias, name))
|
|
return accel->machineTypes[i].name;
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
|
|
int
|
|
virQEMUCapsGetMachineMaxCpus(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType,
|
|
const char *name)
|
|
{
|
|
virQEMUCapsAccel *accel;
|
|
size_t i;
|
|
|
|
if (!name)
|
|
return 0;
|
|
|
|
accel = virQEMUCapsGetAccel(qemuCaps, virtType);
|
|
|
|
for (i = 0; i < accel->nmachineTypes; i++) {
|
|
if (!accel->machineTypes[i].maxCpus)
|
|
continue;
|
|
if (STREQ(accel->machineTypes[i].name, name))
|
|
return accel->machineTypes[i].maxCpus;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool
|
|
virQEMUCapsGetMachineHotplugCpus(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType,
|
|
const char *name)
|
|
{
|
|
virQEMUCapsAccel *accel;
|
|
size_t i;
|
|
|
|
accel = virQEMUCapsGetAccel(qemuCaps, virtType);
|
|
|
|
for (i = 0; i < accel->nmachineTypes; i++) {
|
|
if (STREQ_NULLABLE(accel->machineTypes[i].name, name))
|
|
return accel->machineTypes[i].hotplugCpus;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
const char *
|
|
virQEMUCapsGetMachineDefaultCPU(virQEMUCaps *qemuCaps,
|
|
const char *name,
|
|
virDomainVirtType type)
|
|
{
|
|
virQEMUCapsAccel *accel = virQEMUCapsGetAccel(qemuCaps, type);
|
|
qemuMonitorCPUDefs *defs = accel->cpuModels;
|
|
const char *cpuType = NULL;
|
|
size_t i;
|
|
|
|
if (!name || !defs)
|
|
return NULL;
|
|
|
|
for (i = 0; i < accel->nmachineTypes; i++) {
|
|
if (STREQ(accel->machineTypes[i].name, name)) {
|
|
cpuType = accel->machineTypes[i].defaultCPU;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!cpuType)
|
|
return NULL;
|
|
|
|
for (i = 0; i < defs->ncpus; i++) {
|
|
if (STREQ_NULLABLE(defs->cpus[i].type, cpuType))
|
|
return defs->cpus[i].name;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
bool
|
|
virQEMUCapsIsCPUDeprecated(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type,
|
|
const char *model)
|
|
{
|
|
virQEMUCapsAccel *accel = virQEMUCapsGetAccel(qemuCaps, type);
|
|
qemuMonitorCPUDefs *defs = accel->cpuModels;
|
|
size_t i;
|
|
|
|
for (i = 0; i < defs->ncpus; i++) {
|
|
if (STREQ_NULLABLE(defs->cpus[i].name, model))
|
|
return defs->cpus[i].deprecated;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsIsCPUUsable:
|
|
* @qemuCaps: QEMU capabilities
|
|
* @type: virtualization type (kvm vs tcg)
|
|
* @cpu: CPU definition to check, including explicitly configured features
|
|
*
|
|
* Checks whether @cpu is considered usable by QEMU, i.e., all features
|
|
* required by the CPU model are supported and can be enabled. If so, we can
|
|
* avoid checking the CPU according to its definition in the CPU map when
|
|
* starting a domain with check='partial'. Our checks could be inaccurate
|
|
* anyway because QEMU might have changed the definition of the CPU model
|
|
* since we added it into the CPU map.
|
|
*
|
|
* The CPU is considered usable based on the QEMU capabilities if QEMU reports
|
|
* it as such and @cpu would only request removal of features.
|
|
*
|
|
* Returns true iff @cpu is usable.
|
|
*/
|
|
bool
|
|
virQEMUCapsIsCPUUsable(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type,
|
|
virCPUDef *cpu)
|
|
{
|
|
qemuMonitorCPUDefs *defs;
|
|
size_t i;
|
|
|
|
if (!cpu->model ||
|
|
!(defs = virQEMUCapsGetAccel(qemuCaps, type)->cpuModels))
|
|
return false;
|
|
|
|
/* CPU model usability is valid only when CPU def does not contain any
|
|
* features or all features are disabled.
|
|
*/
|
|
for (i = 0; i < cpu->nfeatures; i++) {
|
|
if (cpu->features[i].policy != VIR_CPU_FEATURE_DISABLE)
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < defs->ncpus; i++) {
|
|
if (STREQ(defs->cpus[i].name, cpu->model))
|
|
return defs->cpus[i].usable == VIR_DOMCAPS_CPU_USABLE_YES;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsGetCPUBlockers:
|
|
* @qemuCaps: QEMU capabilities
|
|
* @type: virtualization type
|
|
* @cpu: CPU model
|
|
* @blockers: where to store the list of features
|
|
*
|
|
* Get a list of features that prevent @cpu from being usable. The pointer to
|
|
* the list will be stored in @blockers and the caller must not free it. The
|
|
* pointer is valid as long as there is an active reference to @qemuCaps.
|
|
*
|
|
* Returns 0 on success, -1 when @cpu is not found in @qemuCaps.
|
|
*/
|
|
int
|
|
virQEMUCapsGetCPUBlockers(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type,
|
|
const char *cpu,
|
|
char ***blockers)
|
|
{
|
|
qemuMonitorCPUDefs *defs;
|
|
size_t i;
|
|
|
|
defs = virQEMUCapsGetAccel(qemuCaps, type)->cpuModels;
|
|
|
|
if (!defs)
|
|
return -1;
|
|
|
|
for (i = 0; i < defs->ncpus; i++) {
|
|
if (STREQ(defs->cpus[i].name, cpu)) {
|
|
*blockers = defs->cpus[i].blockers;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
bool
|
|
virQEMUCapsIsMachineDeprecated(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type,
|
|
const char *machine)
|
|
{
|
|
virQEMUCapsAccel *accel = virQEMUCapsGetAccel(qemuCaps, type);
|
|
size_t i;
|
|
|
|
for (i = 0; i < accel->nmachineTypes; i++) {
|
|
if (STREQ_NULLABLE(accel->machineTypes[i].name, machine))
|
|
return accel->machineTypes[i].deprecated;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
virTristateBool
|
|
virQEMUCapsMachineSupportsACPI(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type,
|
|
const char *machine)
|
|
{
|
|
virQEMUCapsAccel *accel = virQEMUCapsGetAccel(qemuCaps, type);
|
|
size_t i;
|
|
|
|
for (i = 0; i < accel->nmachineTypes; i++) {
|
|
if (STREQ_NULLABLE(accel->machineTypes[i].name, machine))
|
|
return accel->machineTypes[i].acpi;
|
|
}
|
|
return VIR_TRISTATE_BOOL_ABSENT;
|
|
}
|
|
|
|
|
|
bool
|
|
virQEMUCapsGetMachineNumaMemSupported(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType,
|
|
const char *name)
|
|
{
|
|
virQEMUCapsAccel *accel;
|
|
size_t i;
|
|
|
|
accel = virQEMUCapsGetAccel(qemuCaps, virtType);
|
|
|
|
for (i = 0; i < accel->nmachineTypes; i++) {
|
|
if (STREQ(accel->machineTypes[i].name, name))
|
|
return accel->machineTypes[i].numaMemSupported;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
const char *
|
|
virQEMUCapsGetMachineDefaultRAMid(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType,
|
|
const char *name)
|
|
{
|
|
virQEMUCapsAccel *accel;
|
|
size_t i;
|
|
|
|
accel = virQEMUCapsGetAccel(qemuCaps, virtType);
|
|
|
|
for (i = 0; i < accel->nmachineTypes; i++) {
|
|
if (STREQ(accel->machineTypes[i].name, name))
|
|
return accel->machineTypes[i].defaultRAMid;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsSetGICCapabilities:
|
|
* @qemuCaps: QEMU capabilities
|
|
* @capabilities: GIC capabilities
|
|
* @ncapabilities: number of GIC capabilities
|
|
*
|
|
* Set the GIC capabilities for @qemuCaps.
|
|
*
|
|
* The ownership of @capabilities is taken away from the caller, ie. this
|
|
* function will not make a copy of @capabilities, so releasing that memory
|
|
* after it's been called is a bug.
|
|
*/
|
|
void
|
|
virQEMUCapsSetGICCapabilities(virQEMUCaps *qemuCaps,
|
|
virGICCapability *capabilities,
|
|
size_t ncapabilities)
|
|
{
|
|
VIR_FREE(qemuCaps->gicCapabilities);
|
|
|
|
qemuCaps->gicCapabilities = capabilities;
|
|
qemuCaps->ngicCapabilities = ncapabilities;
|
|
}
|
|
|
|
|
|
virSEVCapability *
|
|
virQEMUCapsGetSEVCapabilities(virQEMUCaps *qemuCaps)
|
|
{
|
|
return qemuCaps->sevCapabilities;
|
|
}
|
|
|
|
|
|
virSGXCapability *
|
|
virQEMUCapsGetSGXCapabilities(virQEMUCaps *qemuCaps)
|
|
{
|
|
return qemuCaps->sgxCapabilities;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsProbeQMPObjectTypes(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
g_auto(GStrv) values = NULL;
|
|
|
|
if (qemuMonitorGetObjectTypes(mon, &values) < 0)
|
|
return -1;
|
|
virQEMUCapsProcessStringFlags(qemuCaps,
|
|
G_N_ELEMENTS(virQEMUCapsObjectTypes),
|
|
virQEMUCapsObjectTypes,
|
|
values);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsProbeQMPDeviceProperties(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(virQEMUCapsDeviceProps); i++) {
|
|
virQEMUCapsDeviceTypeProps *device = virQEMUCapsDeviceProps + i;
|
|
g_autoptr(GHashTable) qemuprops = NULL;
|
|
size_t j;
|
|
|
|
if (device->capsCondition >= 0 &&
|
|
!virQEMUCapsGet(qemuCaps, device->capsCondition))
|
|
continue;
|
|
|
|
if (!(qemuprops = qemuMonitorGetDeviceProps(mon, device->type)))
|
|
return -1;
|
|
|
|
for (j = 0; j < device->nprops; j++) {
|
|
virJSONValue *entry = virHashLookup(qemuprops, device->props[j].value);
|
|
|
|
if (!entry)
|
|
continue;
|
|
|
|
virQEMUCapsSet(qemuCaps, device->props[j].flag);
|
|
|
|
if (device->props[j].cb &&
|
|
device->props[j].cb(entry, qemuCaps) < 0)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsProbeQMPObjectProperties(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(virQEMUCapsObjectProps); i++) {
|
|
virQEMUCapsObjectTypeProps *props = virQEMUCapsObjectProps + i;
|
|
g_auto(GStrv) values = NULL;
|
|
|
|
if (props->capsCondition >= 0 &&
|
|
!virQEMUCapsGet(qemuCaps, props->capsCondition))
|
|
continue;
|
|
|
|
if (qemuMonitorGetObjectProps(mon, props->type, &values) < 0)
|
|
return -1;
|
|
|
|
virQEMUCapsProcessStringFlags(qemuCaps,
|
|
props->nprops,
|
|
props->props,
|
|
values);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Historically QEMU x86 targets defaulted to 'pc' machine type but
|
|
* in future x86_64 might switch to 'q35'. Such a change is considered
|
|
* an ABI break from libvirt's POV. Other QEMU targets may not declare
|
|
* a default machine at all, causing libvirt to use the first reported
|
|
* machine in the list.
|
|
*
|
|
* Here we record a preferred default machine for all arches, so
|
|
* that we're not vulnerable to changes in QEMU defaults or machine
|
|
* list ordering.
|
|
*/
|
|
static const char *preferredMachines[] =
|
|
{
|
|
NULL, /* VIR_ARCH_NONE (not a real arch :) */
|
|
"clipper", /* VIR_ARCH_ALPHA */
|
|
"integratorcp", /* VIR_ARCH_ARMV6L */
|
|
"virt", /* VIR_ARCH_ARMV7L */
|
|
"virt", /* VIR_ARCH_ARMV7B */
|
|
|
|
"virt", /* VIR_ARCH_AARCH64 */
|
|
"axis-dev88", /* VIR_ARCH_CRIS */
|
|
"pc", /* VIR_ARCH_I686 */
|
|
NULL, /* VIR_ARCH_ITANIUM (doesn't exist in QEMU any more) */
|
|
"lm32-evr", /* VIR_ARCH_LM32 */
|
|
|
|
"virt", /* VIR_ARCH_LOONGARCH64 */
|
|
"mcf5208evb", /* VIR_ARCH_M68K */
|
|
"petalogix-s3adsp1800", /* VIR_ARCH_MICROBLAZE */
|
|
"petalogix-s3adsp1800", /* VIR_ARCH_MICROBLAZEEL */
|
|
"malta", /* VIR_ARCH_MIPS */
|
|
|
|
"malta", /* VIR_ARCH_MIPSEL */
|
|
"malta", /* VIR_ARCH_MIPS64 */
|
|
"malta", /* VIR_ARCH_MIPS64EL */
|
|
"or1k-sim", /* VIR_ARCH_OR32 */
|
|
NULL, /* VIR_ARCH_PARISC (no QEMU impl) */
|
|
|
|
NULL, /* VIR_ARCH_PARISC64 (no QEMU impl) */
|
|
"g3beige", /* VIR_ARCH_PPC */
|
|
"g3beige", /* VIR_ARCH_PPCLE */
|
|
"pseries", /* VIR_ARCH_PPC64 */
|
|
"pseries", /* VIR_ARCH_PPC64LE */
|
|
|
|
"bamboo", /* VIR_ARCH_PPCEMB */
|
|
"virt", /* VIR_ARCH_RISCV32 */
|
|
"virt", /* VIR_ARCH_RISCV64 */
|
|
NULL, /* VIR_ARCH_S390 (no QEMU impl) */
|
|
"s390-ccw-virtio", /* VIR_ARCH_S390X */
|
|
|
|
"shix", /* VIR_ARCH_SH4 */
|
|
"shix", /* VIR_ARCH_SH4EB */
|
|
"SS-5", /* VIR_ARCH_SPARC */
|
|
"sun4u", /* VIR_ARCH_SPARC64 */
|
|
"puv3", /* VIR_ARCH_UNICORE32 */
|
|
|
|
"pc", /* VIR_ARCH_X86_64 */
|
|
"sim", /* VIR_ARCH_XTENSA */
|
|
"sim", /* VIR_ARCH_XTENSAEB */
|
|
};
|
|
G_STATIC_ASSERT(G_N_ELEMENTS(preferredMachines) == VIR_ARCH_LAST);
|
|
|
|
|
|
static void
|
|
virQEMUCapsAddMachine(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType,
|
|
const char *name,
|
|
const char *alias,
|
|
const char *defaultCPU,
|
|
int maxCpus,
|
|
bool hotplugCpus,
|
|
bool isDefault,
|
|
bool numaMemSupported,
|
|
const char *defaultRAMid,
|
|
bool deprecated,
|
|
virTristateBool acpi)
|
|
{
|
|
virQEMUCapsAccel *accel = virQEMUCapsGetAccel(qemuCaps, virtType);
|
|
virQEMUCapsMachineType *mach;
|
|
|
|
accel->machineTypes = g_renew(virQEMUCapsMachineType,
|
|
accel->machineTypes,
|
|
++accel->nmachineTypes);
|
|
|
|
mach = &(accel->machineTypes[accel->nmachineTypes - 1]);
|
|
|
|
mach->alias = g_strdup(alias);
|
|
mach->name = g_strdup(name);
|
|
mach->defaultCPU = g_strdup(defaultCPU);
|
|
|
|
mach->maxCpus = maxCpus;
|
|
mach->hotplugCpus = hotplugCpus;
|
|
|
|
mach->qemuDefault = isDefault;
|
|
|
|
mach->numaMemSupported = numaMemSupported;
|
|
|
|
mach->defaultRAMid = g_strdup(defaultRAMid);
|
|
mach->deprecated = deprecated;
|
|
mach->acpi = acpi;
|
|
}
|
|
|
|
/**
|
|
* virQEMUCapsHasMachines:
|
|
* @qemuCaps: qemu capabilities object
|
|
*
|
|
* Returns true if @qemuCaps has at least one machine type defined. This is
|
|
* called by the test suite to figure out whether to populate fake machine types
|
|
* into the list.
|
|
*/
|
|
bool
|
|
virQEMUCapsHasMachines(virQEMUCaps *qemuCaps)
|
|
{
|
|
|
|
return !!qemuCaps->kvm.nmachineTypes ||
|
|
!!qemuCaps->hvf.nmachineTypes ||
|
|
!!qemuCaps->tcg.nmachineTypes;
|
|
}
|
|
|
|
|
|
int
|
|
virQEMUCapsProbeQMPMachineTypes(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType,
|
|
qemuMonitor *mon)
|
|
{
|
|
qemuMonitorMachineInfo **machines = NULL;
|
|
int nmachines = 0;
|
|
size_t i;
|
|
ssize_t defIdx = -1;
|
|
ssize_t preferredIdx = -1;
|
|
const char *preferredMachine = preferredMachines[qemuCaps->arch];
|
|
virQEMUCapsAccel *accel = virQEMUCapsGetAccel(qemuCaps, virtType);
|
|
|
|
if ((nmachines = qemuMonitorGetMachines(mon, &machines)) < 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < nmachines; i++) {
|
|
if (STREQ(machines[i]->name, "none"))
|
|
continue;
|
|
|
|
virQEMUCapsAddMachine(qemuCaps,
|
|
virtType,
|
|
machines[i]->name,
|
|
machines[i]->alias,
|
|
machines[i]->defaultCPU,
|
|
machines[i]->maxCpus,
|
|
machines[i]->hotplugCpus,
|
|
machines[i]->isDefault,
|
|
machines[i]->numaMemSupported,
|
|
machines[i]->defaultRAMid,
|
|
machines[i]->deprecated,
|
|
machines[i]->acpi);
|
|
|
|
if (preferredMachine &&
|
|
(STREQ_NULLABLE(machines[i]->alias, preferredMachine) ||
|
|
STREQ(machines[i]->name, preferredMachine))) {
|
|
preferredIdx = accel->nmachineTypes - 1;
|
|
}
|
|
|
|
if (machines[i]->isDefault)
|
|
defIdx = accel->nmachineTypes - 1;
|
|
}
|
|
|
|
/*
|
|
* We'll prefer to use our own historical default machine
|
|
* to avoid mgmt apps seeing semantics changes when QEMU
|
|
* alters its defaults.
|
|
*
|
|
* Our preferred machine might have been compiled out of
|
|
* QEMU at build time though, so we still fallback to honouring
|
|
* QEMU's reported default in that case
|
|
*/
|
|
if (preferredIdx == -1)
|
|
preferredIdx = defIdx;
|
|
if (preferredIdx != -1)
|
|
virQEMUCapsSetDefaultMachine(accel, preferredIdx);
|
|
|
|
for (i = 0; i < nmachines; i++)
|
|
qemuMonitorMachineInfoFree(machines[i]);
|
|
VIR_FREE(machines);
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool
|
|
virQEMUCapsIsMachineSupported(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType,
|
|
const char *canonical_machine)
|
|
{
|
|
virQEMUCapsAccel *accel = virQEMUCapsGetAccel(qemuCaps, virtType);
|
|
size_t i;
|
|
|
|
for (i = 0; i < accel->nmachineTypes; i++) {
|
|
if (STREQ(canonical_machine, accel->machineTypes[i].name))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsProbeQMPMachineProps(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType,
|
|
qemuMonitor *mon)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(virQEMUCapsMachineProps); i++) {
|
|
virQEMUCapsObjectTypeProps props = virQEMUCapsMachineProps[i];
|
|
const char *canon = virQEMUCapsGetCanonicalMachine(qemuCaps, virtType, props.type);
|
|
g_autofree char *type = NULL;
|
|
g_auto(GStrv) values = NULL;
|
|
|
|
if (STRNEQ(canon, "none") &&
|
|
(!ARCH_IS_X86(qemuCaps->arch) || STRNEQ(canon, "generic-pc")) &&
|
|
!virQEMUCapsIsMachineSupported(qemuCaps, virtType, canon)) {
|
|
continue;
|
|
}
|
|
|
|
/* The QOM type for machine types is the machine type name
|
|
* followed by the -machine suffix */
|
|
type = g_strdup_printf("%s-machine", canon);
|
|
|
|
if (qemuMonitorGetObjectProps(mon, type, &values) < 0)
|
|
return -1;
|
|
|
|
virQEMUCapsProcessStringFlags(qemuCaps,
|
|
props.nprops,
|
|
props.props,
|
|
values);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsFetchCPUDefinitions(qemuMonitor *mon,
|
|
virArch arch,
|
|
qemuMonitorCPUDefs **cpuDefs)
|
|
{
|
|
g_autoptr(qemuMonitorCPUDefs) defs = NULL;
|
|
size_t i;
|
|
|
|
*cpuDefs = NULL;
|
|
|
|
if (qemuMonitorGetCPUDefinitions(mon, &defs) < 0)
|
|
return -1;
|
|
|
|
if (!defs)
|
|
return 0;
|
|
|
|
/* QEMU 2.11 for Power renamed all CPU models to lower case, we need to
|
|
* translate them back to libvirt's upper case model names. */
|
|
if (ARCH_IS_PPC64(arch)) {
|
|
g_auto(GStrv) libvirtModels = NULL;
|
|
char **name;
|
|
|
|
if (virCPUGetModels(arch, &libvirtModels) < 0)
|
|
return -1;
|
|
|
|
for (name = libvirtModels; name && *name; name++) {
|
|
for (i = 0; i < defs->ncpus; i++) {
|
|
if (STRCASENEQ(defs->cpus[i].name, *name))
|
|
continue;
|
|
|
|
VIR_FREE(defs->cpus[i].name);
|
|
defs->cpus[i].name = g_strdup(*name);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < defs->ncpus; i++) {
|
|
qemuMonitorCPUDefInfo *cpu = &defs->cpus[i];
|
|
char **blocker;
|
|
|
|
if (!cpu->blockers)
|
|
continue;
|
|
|
|
for (blocker = cpu->blockers; *blocker; blocker++)
|
|
virQEMUCapsCPUFeatureFromQEMUInPlace(arch, blocker);
|
|
}
|
|
|
|
*cpuDefs = g_steal_pointer(&defs);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virQEMUCapsFetchCPUModels(qemuMonitor *mon,
|
|
virArch arch,
|
|
virDomainCapsCPUModels **cpuModels)
|
|
{
|
|
g_autoptr(qemuMonitorCPUDefs) defs = NULL;
|
|
|
|
*cpuModels = NULL;
|
|
|
|
if (virQEMUCapsFetchCPUDefinitions(mon, arch, &defs) < 0)
|
|
return -1;
|
|
|
|
if (defs &&
|
|
!(*cpuModels = virQEMUCapsCPUDefsToModels(arch, defs, NULL, NULL, false)))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsProbeQMPCPUDefinitions(virQEMUCaps *qemuCaps,
|
|
virQEMUCapsAccel *accel,
|
|
qemuMonitor *mon)
|
|
{
|
|
qemuMonitorCPUDefs *defs;
|
|
size_t i;
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_CPU_DEFINITIONS))
|
|
return 0;
|
|
|
|
if (virQEMUCapsFetchCPUDefinitions(mon, qemuCaps->arch, &accel->cpuModels) < 0)
|
|
return -1;
|
|
|
|
defs = accel->cpuModels;
|
|
for (i = 0; i < defs->ncpus; i++) {
|
|
if (STREQ_NULLABLE(defs->cpus[i].name, "max")) {
|
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_CPU_MAX);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virQEMUCapsProbeCPUDefinitionsTest(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
return virQEMUCapsProbeQMPCPUDefinitions(qemuCaps, &qemuCaps->kvm, mon);
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsProbeHypervCapabilities(virQEMUCaps *qemuCaps,
|
|
qemuMonitorCPUModelInfo *fullQEMU)
|
|
{
|
|
g_autofree virDomainCapsFeatureHyperv *hvcaps = NULL;
|
|
size_t i;
|
|
|
|
if (!fullQEMU)
|
|
return 0;
|
|
|
|
hvcaps = g_new0(virDomainCapsFeatureHyperv, 1);
|
|
hvcaps->supported = VIR_TRISTATE_BOOL_YES;
|
|
hvcaps->features.report = true;
|
|
|
|
for (i = 0; i < fullQEMU->nprops; i++) {
|
|
qemuMonitorCPUProperty prop = fullQEMU->props[i];
|
|
const char *name;
|
|
int hvprop;
|
|
|
|
if (!(name = STRSKIP(prop.name, "hv-")))
|
|
continue;
|
|
|
|
hvprop = virDomainHypervTypeFromString(name);
|
|
|
|
if (hvprop < 0) {
|
|
/* Some names are different. For instance QEMU reports hv-vendor-id
|
|
* but we have it as vendor_id (because of XML). Replace hyphens
|
|
* with underscores and try again. */
|
|
g_autofree char *underscoreName = NULL;
|
|
|
|
underscoreName = virStringReplace(name, "-", "_");
|
|
|
|
hvprop = virDomainHypervTypeFromString(underscoreName);
|
|
if (hvprop < 0) {
|
|
VIR_DEBUG("Not yet implement Hyper-V enlightenment: %s",
|
|
prop.name);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((prop.type == QEMU_MONITOR_CPU_PROPERTY_BOOLEAN &&
|
|
prop.value.boolean) ||
|
|
(prop.type == QEMU_MONITOR_CPU_PROPERTY_NUMBER &&
|
|
prop.value.number > 0) ||
|
|
(prop.type == QEMU_MONITOR_CPU_PROPERTY_STRING &&
|
|
prop.value.string))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(hvcaps->features, hvprop);
|
|
|
|
}
|
|
|
|
if (hvcaps->features.values == 0) {
|
|
/* No capabilities detected. This is probably because we're talking to
|
|
* older QEMU which did not report error but did not expand HyperV
|
|
* features either. */
|
|
return 0;
|
|
}
|
|
|
|
qemuCaps->hypervCapabilities = g_steal_pointer(&hvcaps);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsProbeFullDeprecatedProperties
|
|
* @mon: QEMU monitor
|
|
* @cpu: CPU definition to be expanded
|
|
* @props: the array to be filled with deprecated features
|
|
*
|
|
* Performs a full CPU model expansion to retrieve an array of deprecated
|
|
* properties. If the expansion succeeds, then data previously stored in
|
|
* @props is freed.
|
|
*
|
|
* Returns: -1 if the expansion failed; otherwise 0.
|
|
*/
|
|
static int
|
|
virQEMUCapsProbeFullDeprecatedProperties(qemuMonitor *mon,
|
|
virCPUDef *cpu,
|
|
GStrv *props)
|
|
{
|
|
g_autoptr(qemuMonitorCPUModelInfo) propsInfo = NULL;
|
|
|
|
if (qemuMonitorGetCPUModelExpansion(mon, QEMU_MONITOR_CPU_MODEL_EXPANSION_FULL,
|
|
cpu, true, false, false, &propsInfo) < 0)
|
|
return -1;
|
|
|
|
if (propsInfo && propsInfo->deprecated_props) {
|
|
g_free(*props);
|
|
*props = g_steal_pointer(&propsInfo->deprecated_props);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsProbeQMPHostCPU(virQEMUCaps *qemuCaps,
|
|
virQEMUCapsAccel *accel,
|
|
qemuMonitor *mon,
|
|
virDomainVirtType virtType)
|
|
{
|
|
const char *model = virQEMUCapsTypeIsAccelerated(virtType) ? "host" : "max";
|
|
g_autoptr(qemuMonitorCPUModelInfo) modelInfo = NULL;
|
|
g_autoptr(qemuMonitorCPUModelInfo) nonMigratable = NULL;
|
|
g_autoptr(virCPUDef) cpu = NULL;
|
|
qemuMonitorCPUModelExpansionType type;
|
|
bool fail_no_props = true;
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION))
|
|
return 0;
|
|
|
|
cpu = virCPUDefNew();
|
|
|
|
cpu->model = g_strdup(model);
|
|
|
|
/* Some x86_64 features defined in src/cpu_map/ use spelling which differ
|
|
* from the one preferred by QEMU. Static expansion would give us only the
|
|
* preferred spelling. With new QEMU we always use the QEMU's canonical
|
|
* names of all features and translate between them and our names. But for
|
|
* older version of QEMU we need to do a full expansion on the result of
|
|
* the initial static expansion to get all variants of feature names.
|
|
*/
|
|
if (ARCH_IS_X86(qemuCaps->arch) &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CANONICAL_CPU_FEATURES)) {
|
|
type = QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC_FULL;
|
|
} else if (ARCH_IS_ARM(qemuCaps->arch)) {
|
|
type = QEMU_MONITOR_CPU_MODEL_EXPANSION_FULL;
|
|
} else {
|
|
type = QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC;
|
|
}
|
|
|
|
/* Older s390 models do not report a feature set */
|
|
if (ARCH_IS_S390(qemuCaps->arch))
|
|
fail_no_props = false;
|
|
|
|
if (qemuMonitorGetCPUModelExpansion(mon, type, cpu, true, false,
|
|
fail_no_props, &modelInfo) < 0)
|
|
return -1;
|
|
|
|
/* Try to check migratability of each feature. */
|
|
if (modelInfo &&
|
|
ARCH_IS_X86(qemuCaps->arch) &&
|
|
qemuMonitorGetCPUModelExpansion(mon, type, cpu, false, false,
|
|
fail_no_props, &nonMigratable) < 0)
|
|
return -1;
|
|
|
|
if (nonMigratable) {
|
|
g_autoptr(GHashTable) hash = virHashNew(NULL);
|
|
qemuMonitorCPUProperty *prop;
|
|
qemuMonitorCPUProperty *nmProp;
|
|
size_t i;
|
|
|
|
for (i = 0; i < modelInfo->nprops; i++) {
|
|
prop = modelInfo->props + i;
|
|
if (virHashAddEntry(hash, prop->name, prop) < 0)
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < nonMigratable->nprops; i++) {
|
|
nmProp = nonMigratable->props + i;
|
|
if (!(prop = virHashLookup(hash, nmProp->name)) ||
|
|
prop->type != QEMU_MONITOR_CPU_PROPERTY_BOOLEAN ||
|
|
prop->type != nmProp->type)
|
|
continue;
|
|
|
|
if (prop->value.boolean) {
|
|
prop->migratable = VIR_TRISTATE_BOOL_YES;
|
|
} else if (nmProp->value.boolean) {
|
|
prop->value.boolean = true;
|
|
prop->migratable = VIR_TRISTATE_BOOL_NO;
|
|
}
|
|
}
|
|
|
|
modelInfo->migratability = true;
|
|
}
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION_DEPRECATED_PROPS) &&
|
|
virQEMUCapsProbeFullDeprecatedProperties(mon, cpu, &modelInfo->deprecated_props) < 0)
|
|
return -1;
|
|
|
|
if (virQEMUCapsTypeIsAccelerated(virtType) &&
|
|
(ARCH_IS_X86(qemuCaps->arch) || ARCH_IS_ARM(qemuCaps->arch))) {
|
|
g_autoptr(qemuMonitorCPUModelInfo) fullQEMU = NULL;
|
|
|
|
if (qemuMonitorGetCPUModelExpansion(mon, QEMU_MONITOR_CPU_MODEL_EXPANSION_FULL,
|
|
cpu, false, true, true, &fullQEMU) < 0)
|
|
return -1;
|
|
|
|
if (virQEMUCapsProbeHypervCapabilities(qemuCaps, fullQEMU) < 0)
|
|
return -1;
|
|
}
|
|
|
|
accel->hostCPU.info = g_steal_pointer(&modelInfo);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get NULL terminated list of features supported by QEMU.
|
|
*
|
|
* Returns -1 on error,
|
|
* 0 on success (@features will be NULL if QEMU does not support this),
|
|
* 1 when @features is filled in, but migratability info is not available.
|
|
*/
|
|
int
|
|
virQEMUCapsGetCPUFeatures(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType,
|
|
bool migratable,
|
|
char ***features)
|
|
{
|
|
qemuMonitorCPUModelInfo *modelInfo;
|
|
g_auto(GStrv) list = NULL;
|
|
size_t i;
|
|
size_t n;
|
|
|
|
*features = NULL;
|
|
modelInfo = virQEMUCapsGetCPUModelInfo(qemuCaps, virtType);
|
|
|
|
if (!modelInfo)
|
|
return 0;
|
|
|
|
list = g_new0(char *, modelInfo->nprops + 1);
|
|
|
|
n = 0;
|
|
for (i = 0; i < modelInfo->nprops; i++) {
|
|
qemuMonitorCPUProperty *prop = modelInfo->props + i;
|
|
|
|
if (migratable && prop->migratable == VIR_TRISTATE_BOOL_NO)
|
|
continue;
|
|
|
|
list[n++] = g_strdup(virQEMUCapsCPUFeatureFromQEMU(qemuCaps->arch, prop->name));
|
|
}
|
|
|
|
*features = g_steal_pointer(&list);
|
|
|
|
if (migratable && !modelInfo->migratability)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsUpdateCPUDeprecatedFeatures(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType,
|
|
virCPUDef *cpu)
|
|
{
|
|
qemuMonitorCPUModelInfo *modelInfo;
|
|
size_t i;
|
|
|
|
modelInfo = virQEMUCapsGetCPUModelInfo(qemuCaps, virtType);
|
|
|
|
if (!modelInfo || !modelInfo->deprecated_props)
|
|
return;
|
|
|
|
for (i = 0; i < g_strv_length(modelInfo->deprecated_props); i++) {
|
|
virCPUDefUpdateFeature(cpu, modelInfo->deprecated_props[i],
|
|
VIR_CPU_FEATURE_DISABLE);
|
|
}
|
|
}
|
|
|
|
|
|
struct tpmTypeToCaps {
|
|
int type;
|
|
virQEMUCapsFlags caps;
|
|
};
|
|
|
|
static const struct tpmTypeToCaps virQEMUCapsTPMTypesToCaps[] = {
|
|
{
|
|
.type = VIR_DOMAIN_TPM_TYPE_PASSTHROUGH,
|
|
.caps = QEMU_CAPS_DEVICE_TPM_PASSTHROUGH,
|
|
},
|
|
{
|
|
.type = VIR_DOMAIN_TPM_TYPE_EMULATOR,
|
|
.caps = QEMU_CAPS_DEVICE_TPM_EMULATOR,
|
|
},
|
|
};
|
|
|
|
const struct tpmTypeToCaps virQEMUCapsTPMModelsToCaps[] = {
|
|
{
|
|
.type = VIR_DOMAIN_TPM_MODEL_TIS,
|
|
.caps = QEMU_CAPS_DEVICE_TPM_TIS,
|
|
},
|
|
{
|
|
.type = VIR_DOMAIN_TPM_MODEL_CRB,
|
|
.caps = QEMU_CAPS_DEVICE_TPM_CRB,
|
|
},
|
|
{
|
|
.type = VIR_DOMAIN_TPM_MODEL_SPAPR,
|
|
.caps = QEMU_CAPS_DEVICE_TPM_SPAPR,
|
|
},
|
|
};
|
|
|
|
static int
|
|
virQEMUCapsProbeQMPTPM(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
g_auto(GStrv) models = NULL;
|
|
g_auto(GStrv) types = NULL;
|
|
size_t i;
|
|
|
|
if (qemuMonitorGetTPMModels(mon, &models) < 0)
|
|
return -1;
|
|
|
|
if (!models)
|
|
return 0;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(virQEMUCapsTPMModelsToCaps); i++) {
|
|
const char *needle = virDomainTPMModelTypeToString(virQEMUCapsTPMModelsToCaps[i].type);
|
|
if (g_strv_contains((const char **)models, needle))
|
|
virQEMUCapsSet(qemuCaps, virQEMUCapsTPMModelsToCaps[i].caps);
|
|
}
|
|
|
|
if (qemuMonitorGetTPMTypes(mon, &types) < 0)
|
|
return -1;
|
|
|
|
if (!types)
|
|
return 0;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(virQEMUCapsTPMTypesToCaps); i++) {
|
|
const char *needle = virDomainTPMBackendTypeToString(virQEMUCapsTPMTypesToCaps[i].type);
|
|
if (g_strv_contains((const char **)types, needle))
|
|
virQEMUCapsSet(qemuCaps, virQEMUCapsTPMTypesToCaps[i].caps);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsProbeQMPKVMState(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
bool enabled = false;
|
|
bool present = false;
|
|
|
|
if (qemuMonitorGetKVMState(mon, &enabled, &present) < 0)
|
|
return -1;
|
|
|
|
if (present && enabled)
|
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_KVM);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef __APPLE__
|
|
bool
|
|
virQEMUCapsProbeHVF(virQEMUCaps *qemuCaps)
|
|
{
|
|
int hv_support = 0;
|
|
size_t len = sizeof(hv_support);
|
|
virArch hostArch = virArchFromHost();
|
|
|
|
/* Guest and host arch need to match for hardware acceleration
|
|
* to be usable */
|
|
if (qemuCaps->arch != hostArch)
|
|
return false;
|
|
|
|
/* We need the OS to report Hypervisor.framework availability */
|
|
if (sysctlbyname("kern.hv_support", &hv_support, &len, NULL, 0) < 0)
|
|
return false;
|
|
|
|
return !!hv_support;
|
|
}
|
|
#else
|
|
bool
|
|
virQEMUCapsProbeHVF(virQEMUCaps *qemuCaps G_GNUC_UNUSED)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
struct virQEMUCapsCommandLineProps {
|
|
const char *option;
|
|
const char *param;
|
|
int flag;
|
|
};
|
|
|
|
|
|
/* This uses 'query-command-line-options' which uses old-style argument parsers
|
|
* in qemu and thus isn't being properly extended. Other means to detect
|
|
* features should be used if possible. */
|
|
static struct virQEMUCapsCommandLineProps virQEMUCapsCommandLine[] = {
|
|
{ "fsdev", "multidevs", QEMU_CAPS_FSDEV_MULTIDEVS },
|
|
{ "machine", "hpet", QEMU_CAPS_MACHINE_HPET },
|
|
{ "sandbox", NULL, QEMU_CAPS_SECCOMP_SANDBOX },
|
|
{ "spice", NULL, QEMU_CAPS_SPICE },
|
|
{ "spice", "gl", QEMU_CAPS_SPICE_GL },
|
|
{ "spice", "rendernode", QEMU_CAPS_SPICE_RENDERNODE },
|
|
{ "vnc", "power-control", QEMU_CAPS_VNC_POWER_CONTROL },
|
|
{ "run-with", "async-teardown", QEMU_CAPS_RUN_WITH_ASYNC_TEARDOWN },
|
|
};
|
|
|
|
static int
|
|
virQEMUCapsProbeQMPCommandLine(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
g_autoptr(GHashTable) options = NULL;
|
|
size_t i;
|
|
|
|
if (!(options = qemuMonitorGetCommandLineOptions(mon)))
|
|
return -1;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(virQEMUCapsCommandLine); i++) {
|
|
virJSONValue *option = g_hash_table_lookup(options, virQEMUCapsCommandLine[i].option);
|
|
size_t j;
|
|
|
|
if (!option)
|
|
continue;
|
|
|
|
/* not looking for a specific argument */
|
|
if (!virQEMUCapsCommandLine[i].param) {
|
|
virQEMUCapsSet(qemuCaps, virQEMUCapsCommandLine[i].flag);
|
|
continue;
|
|
}
|
|
|
|
for (j = 0; j < virJSONValueArraySize(option); j++) {
|
|
virJSONValue *param = virJSONValueArrayGet(option, j);
|
|
const char *paramname = virJSONValueObjectGetString(param, "name");
|
|
|
|
if (STREQ_NULLABLE(virQEMUCapsCommandLine[i].param, paramname))
|
|
virQEMUCapsSet(qemuCaps, virQEMUCapsCommandLine[i].flag);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virQEMUCapsProbeQMPMigrationCapabilities(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
g_auto(GStrv) caps = NULL;
|
|
|
|
if (qemuMonitorGetMigrationCapabilities(mon, &caps) < 0)
|
|
return -1;
|
|
|
|
virQEMUCapsProcessStringFlags(qemuCaps,
|
|
G_N_ELEMENTS(virQEMUCapsMigration),
|
|
virQEMUCapsMigration,
|
|
caps);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* virQEMUCapsProbeQMPGICCapabilities:
|
|
* @qemuCaps: QEMU binary capabilities
|
|
* @mon: QEMU monitor
|
|
*
|
|
* Use @mon to obtain information about the GIC capabilities for the
|
|
* corresponding QEMU binary, and store them in @qemuCaps.
|
|
*
|
|
* Returns: 0 on success, <0 on failure
|
|
*/
|
|
static int
|
|
virQEMUCapsProbeQMPGICCapabilities(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
virGICCapability *caps = NULL;
|
|
int ncaps;
|
|
|
|
if (!(qemuCaps->arch == VIR_ARCH_AARCH64 ||
|
|
qemuCaps->arch == VIR_ARCH_ARMV6L ||
|
|
qemuCaps->arch == VIR_ARCH_ARMV7L))
|
|
return 0;
|
|
|
|
if ((ncaps = qemuMonitorGetGICCapabilities(mon, &caps)) < 0)
|
|
return -1;
|
|
|
|
virQEMUCapsSetGICCapabilities(qemuCaps, caps, ncaps);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsGetSEVMaxGuests(virSEVCapability *caps)
|
|
{
|
|
/*
|
|
* From Secure Encrypted Virtualization API v0.24, section 6.19.1
|
|
*
|
|
* If the guest is SEV-ES enabled, then the ASID must be at least
|
|
* 1h and at most (MIN_SEV_ASID-1). If the guest is not SEV-ES
|
|
* enabled, then the ASID must be at least MIN_SEV_ASID and at
|
|
* most the maximum SEV ASID available. The MIN_SEV_ASID value
|
|
* is discovered by CPUID Fn8000_001F[EDX]. The maximum SEV ASID
|
|
* available is discovered by CPUID Fn8000_001F[ECX].
|
|
*/
|
|
uint32_t min_asid, max_asid;
|
|
virHostCPUX86GetCPUID(0x8000001F, 0, NULL, NULL,
|
|
&max_asid, &min_asid);
|
|
|
|
if (max_asid != 0 && min_asid != 0) {
|
|
caps->max_guests = max_asid - min_asid + 1;
|
|
caps->max_es_guests = min_asid - 1;
|
|
} else {
|
|
caps->max_guests = caps->max_es_guests = 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
virQEMUCapsProbeQMPSEVCapabilities(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
int rc = -1;
|
|
virSEVCapability *caps = NULL;
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST) &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_SNP_GUEST))
|
|
return 0;
|
|
|
|
if ((rc = qemuMonitorGetSEVCapabilities(mon, &caps)) < 0)
|
|
return -1;
|
|
|
|
/* SEV isn't actually supported */
|
|
if (rc == 0) {
|
|
virQEMUCapsClear(qemuCaps, QEMU_CAPS_SEV_GUEST);
|
|
virQEMUCapsClear(qemuCaps, QEMU_CAPS_SEV_SNP_GUEST);
|
|
return 0;
|
|
}
|
|
|
|
virQEMUCapsGetSEVMaxGuests(caps);
|
|
|
|
virSEVCapabilitiesFree(qemuCaps->sevCapabilities);
|
|
qemuCaps->sevCapabilities = caps;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsProbeQMPSGXCapabilities(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
int rc = -1;
|
|
virSGXCapability *caps = NULL;
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SGX_EPC))
|
|
return 0;
|
|
|
|
if ((rc = qemuMonitorGetSGXCapabilities(mon, &caps)) < 0)
|
|
return -1;
|
|
|
|
/* SGX isn't actually supported */
|
|
if (rc == 0) {
|
|
virQEMUCapsClear(qemuCaps, QEMU_CAPS_SGX_EPC);
|
|
return 0;
|
|
}
|
|
|
|
virSGXCapabilitiesFree(qemuCaps->sgxCapabilities);
|
|
qemuCaps->sgxCapabilities = caps;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Filter for features which should never be passed to QEMU. Either because
|
|
* QEMU never supported them or they were dropped as they never did anything
|
|
* useful.
|
|
*/
|
|
const char *ignoredFeatures[] = {
|
|
"cmt", "mbm_total", "mbm_local", /* never supported by QEMU */
|
|
"osxsave", "ospke", /* dropped from QEMU */
|
|
"vmx-ept-uc", "vmx-ept-wb", /* never supported by QEMU */
|
|
"vmx-invvpid-single-context", /* never supported by QEMU */
|
|
};
|
|
|
|
bool
|
|
virQEMUCapsCPUFilterFeatures(const char *name,
|
|
virCPUFeaturePolicy policy G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virArch *arch = opaque;
|
|
size_t i;
|
|
|
|
if (!ARCH_IS_X86(*arch))
|
|
return true;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(ignoredFeatures); i++) {
|
|
if (STREQ(name, ignoredFeatures[i]))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
typedef struct _virQEMUCapsCPUFeatureTranslationTable virQEMUCapsCPUFeatureTranslationTable;
|
|
struct _virQEMUCapsCPUFeatureTranslationTable {
|
|
const char *libvirt;
|
|
const char *qemu;
|
|
};
|
|
|
|
virQEMUCapsCPUFeatureTranslationTable virQEMUCapsCPUFeaturesX86[] = {
|
|
{"cmp_legacy", "cmp-legacy"},
|
|
{"ds_cpl", "ds-cpl"},
|
|
{"fxsr_opt", "fxsr-opt"},
|
|
{"kvm_pv_eoi", "kvm-pv-eoi"},
|
|
{"kvm_pv_unhalt", "kvm-pv-unhalt"},
|
|
{"lahf_lm", "lahf-lm"},
|
|
{"nodeid_msr", "nodeid-msr"},
|
|
{"pclmuldq", "pclmulqdq"},
|
|
{"perfctr_core", "perfctr-core"},
|
|
{"perfctr_nb", "perfctr-nb"},
|
|
{"tsc_adjust", "tsc-adjust"},
|
|
{"vmx-invvpid-single-context-noglobals", "vmx-invept-single-context-noglobals"},
|
|
{"hv-vendor_id", "hv-vendor-id"},
|
|
{"hv-emsr_bitmap", "hv-emsr-bitmap"},
|
|
{"hv-xmm_input", "hv-xmm-input"},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
|
|
static const char *
|
|
virQEMUCapsCPUFeatureTranslate(virArch arch,
|
|
const char *feature,
|
|
bool reversed)
|
|
{
|
|
virQEMUCapsCPUFeatureTranslationTable *table = NULL;
|
|
virQEMUCapsCPUFeatureTranslationTable *entry;
|
|
|
|
if (ARCH_IS_X86(arch))
|
|
table = virQEMUCapsCPUFeaturesX86;
|
|
|
|
if (!table ||
|
|
!feature)
|
|
return feature;
|
|
|
|
for (entry = table; entry->libvirt; entry++) {
|
|
const char *key = reversed ? entry->qemu : entry->libvirt;
|
|
|
|
if (STREQ(feature, key))
|
|
return reversed ? entry->libvirt : entry->qemu;
|
|
}
|
|
|
|
return feature;
|
|
}
|
|
|
|
|
|
const char *
|
|
virQEMUCapsCPUFeatureToQEMU(virArch arch,
|
|
const char *feature)
|
|
{
|
|
return virQEMUCapsCPUFeatureTranslate(arch, feature, false);
|
|
}
|
|
|
|
|
|
const char *
|
|
virQEMUCapsCPUFeatureFromQEMU(virArch arch,
|
|
const char *feature)
|
|
{
|
|
return virQEMUCapsCPUFeatureTranslate(arch, feature, true);
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsCPUFeatureFromQEMUInPlace(virArch arch,
|
|
char **feature)
|
|
{
|
|
const char *tmp = virQEMUCapsCPUFeatureFromQEMU(arch, *feature);
|
|
|
|
if (tmp != *feature) {
|
|
VIR_FREE(*feature);
|
|
*feature = g_strdup(tmp);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns 0 when host CPU model provided by QEMU was filled in qemuCaps,
|
|
* 1 when the caller should fall back to using virCaps *->host.cpu,
|
|
* 2 when cpu model info is not supported for this configuration,
|
|
* -1 on error.
|
|
*/
|
|
static int
|
|
virQEMUCapsInitCPUModelS390(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type,
|
|
qemuMonitorCPUModelInfo *modelInfo,
|
|
virCPUDef *cpu,
|
|
bool migratable)
|
|
{
|
|
size_t i;
|
|
|
|
if (!modelInfo) {
|
|
if (type == VIR_DOMAIN_VIRT_KVM) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("missing host CPU model info from QEMU capabilities for binary %1$s"),
|
|
qemuCaps->binary);
|
|
return -1;
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
cpu->model = g_strdup(modelInfo->name);
|
|
cpu->features = g_new0(virCPUFeatureDef, modelInfo->nprops);
|
|
|
|
cpu->nfeatures_max = modelInfo->nprops;
|
|
cpu->nfeatures = 0;
|
|
|
|
for (i = 0; i < modelInfo->nprops; i++) {
|
|
virCPUFeatureDef *feature = cpu->features + cpu->nfeatures;
|
|
qemuMonitorCPUProperty *prop = modelInfo->props + i;
|
|
const char *name = virQEMUCapsCPUFeatureFromQEMU(qemuCaps->arch, prop->name);
|
|
|
|
if (prop->type != QEMU_MONITOR_CPU_PROPERTY_BOOLEAN)
|
|
continue;
|
|
|
|
feature->name = g_strdup(name);
|
|
|
|
if (!prop->value.boolean ||
|
|
(migratable && prop->migratable == VIR_TRISTATE_BOOL_NO))
|
|
feature->policy = VIR_CPU_FEATURE_DISABLE;
|
|
else
|
|
feature->policy = VIR_CPU_FEATURE_REQUIRE;
|
|
cpu->nfeatures++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
virCPUData *
|
|
virQEMUCapsGetCPUModelX86Data(virQEMUCaps *qemuCaps,
|
|
qemuMonitorCPUModelInfo *model,
|
|
bool migratable)
|
|
{
|
|
unsigned long long sigFamily = 0;
|
|
unsigned long long sigModel = 0;
|
|
unsigned long long sigStepping = 0;
|
|
g_autoptr(virCPUData) data = NULL;
|
|
size_t i;
|
|
|
|
if (!(data = virCPUDataNew(VIR_ARCH_X86_64)))
|
|
return NULL;
|
|
|
|
for (i = 0; i < model->nprops; i++) {
|
|
qemuMonitorCPUProperty *prop = model->props + i;
|
|
const char *name = virQEMUCapsCPUFeatureFromQEMU(qemuCaps->arch, prop->name);
|
|
|
|
switch (prop->type) {
|
|
case QEMU_MONITOR_CPU_PROPERTY_BOOLEAN:
|
|
if (!prop->value.boolean ||
|
|
(migratable && prop->migratable == VIR_TRISTATE_BOOL_NO))
|
|
continue;
|
|
|
|
if (virCPUDataAddFeature(data, name) < 0)
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
case QEMU_MONITOR_CPU_PROPERTY_STRING:
|
|
if (STREQ(name, "vendor") &&
|
|
virCPUx86DataSetVendor(data, prop->value.string) < 0)
|
|
return NULL;
|
|
break;
|
|
|
|
case QEMU_MONITOR_CPU_PROPERTY_NUMBER:
|
|
if (STREQ(name, "family"))
|
|
sigFamily = prop->value.number;
|
|
else if (STREQ(name, "model"))
|
|
sigModel = prop->value.number;
|
|
else if (STREQ(name, "stepping"))
|
|
sigStepping = prop->value.number;
|
|
break;
|
|
|
|
case QEMU_MONITOR_CPU_PROPERTY_LAST:
|
|
break;
|
|
}
|
|
}
|
|
|
|
virCPUx86DataSetSignature(data, sigFamily, sigModel, sigStepping);
|
|
|
|
return g_steal_pointer(&data);
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns 0 when host CPU model provided by QEMU was filled in qemuCaps,
|
|
* 1 when the caller should fall back to using virCaps *->host.cpu,
|
|
* -1 on error.
|
|
*/
|
|
static int
|
|
virQEMUCapsInitCPUModelX86(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type,
|
|
qemuMonitorCPUModelInfo *model,
|
|
virCPUDef *cpu,
|
|
bool migratable)
|
|
{
|
|
g_autoptr(virDomainCapsCPUModels) cpuModels = NULL;
|
|
g_autoptr(virCPUData) data = NULL;
|
|
|
|
if (!model)
|
|
return 1;
|
|
|
|
if (!(data = virQEMUCapsGetCPUModelX86Data(qemuCaps, model, migratable)))
|
|
return -1;
|
|
|
|
cpuModels = virQEMUCapsGetCPUModels(qemuCaps, type, NULL, NULL);
|
|
|
|
if (cpuDecode(cpu, data, cpuModels) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns 0 when host CPU model provided by QEMU was filled in qemuCaps,
|
|
* 1 when the caller should fall back to other methods,
|
|
* 2 when cpu model info is not supported for this configuration,
|
|
* -1 on error.
|
|
*/
|
|
int
|
|
virQEMUCapsInitCPUModel(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type,
|
|
virCPUDef *cpu,
|
|
bool migratable)
|
|
{
|
|
qemuMonitorCPUModelInfo *modelInfo = virQEMUCapsGetCPUModelInfo(qemuCaps, type);
|
|
int ret = 1;
|
|
|
|
if (migratable && modelInfo && !modelInfo->migratability)
|
|
return 1;
|
|
|
|
if (ARCH_IS_S390(qemuCaps->arch)) {
|
|
ret = virQEMUCapsInitCPUModelS390(qemuCaps, type, modelInfo,
|
|
cpu, migratable);
|
|
} else if (ARCH_IS_X86(qemuCaps->arch)) {
|
|
ret = virQEMUCapsInitCPUModelX86(qemuCaps, type, modelInfo,
|
|
cpu, migratable);
|
|
} else if (ARCH_IS_ARM(qemuCaps->arch) || ARCH_IS_LOONGARCH(qemuCaps->arch)) {
|
|
ret = 2;
|
|
}
|
|
|
|
if (ret == 0)
|
|
cpu->fallback = VIR_CPU_FALLBACK_FORBID;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static virCPUDef *
|
|
virQEMUCapsNewHostCPUModel(void)
|
|
{
|
|
virCPUDef *cpu = virCPUDefNew();
|
|
|
|
cpu->type = VIR_CPU_TYPE_GUEST;
|
|
cpu->mode = VIR_CPU_MODE_CUSTOM;
|
|
cpu->match = VIR_CPU_MATCH_EXACT;
|
|
cpu->fallback = VIR_CPU_FALLBACK_ALLOW;
|
|
|
|
return cpu;
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsInitHostCPUModel(virQEMUCaps *qemuCaps,
|
|
virArch hostArch,
|
|
virDomainVirtType type)
|
|
{
|
|
virCPUDef *cpu = NULL;
|
|
virCPUDef *cpuExpanded = NULL;
|
|
virCPUDef *migCPU = NULL;
|
|
virCPUDef *hostCPU = NULL;
|
|
virCPUDef *fullCPU = NULL;
|
|
unsigned int physAddrSize = 0;
|
|
size_t i;
|
|
int rc;
|
|
|
|
if (!virQEMUCapsGuestIsNative(hostArch, qemuCaps->arch))
|
|
return;
|
|
|
|
if (!(cpu = virQEMUCapsNewHostCPUModel()))
|
|
goto error;
|
|
|
|
if ((rc = virQEMUCapsInitCPUModel(qemuCaps, type, cpu, false)) < 0) {
|
|
goto error;
|
|
} else if (rc == 1) {
|
|
g_autoptr(virDomainCapsCPUModels) cpuModels = NULL;
|
|
|
|
VIR_DEBUG("No host CPU model info from QEMU; probing host CPU directly");
|
|
|
|
cpuModels = virQEMUCapsGetCPUModels(qemuCaps, type, NULL, NULL);
|
|
if (!(hostCPU = virQEMUCapsProbeHostCPU(hostArch, cpuModels)))
|
|
goto error;
|
|
|
|
virCPUDefCopyModelFilter(cpu, hostCPU, true, virQEMUCapsCPUFilterFeatures,
|
|
&qemuCaps->arch);
|
|
} else if (rc == 2) {
|
|
VIR_DEBUG("QEMU does not provide CPU model for arch=%s virttype=%s",
|
|
virArchToString(qemuCaps->arch),
|
|
virDomainVirtTypeToString(type));
|
|
goto error;
|
|
} else if (virQEMUCapsTypeIsAccelerated(type) &&
|
|
virCPUGetHostIsSupported(qemuCaps->arch)) {
|
|
if (!(fullCPU = virQEMUCapsProbeHostCPU(qemuCaps->arch, NULL)))
|
|
goto error;
|
|
|
|
cpuExpanded = virCPUDefCopy(cpu);
|
|
|
|
if (virCPUExpandFeatures(qemuCaps->arch, cpuExpanded) < 0)
|
|
goto error;
|
|
|
|
for (i = 0; i < cpuExpanded->nfeatures; i++) {
|
|
if (cpuExpanded->features[i].policy == VIR_CPU_FEATURE_REQUIRE)
|
|
virCPUDefUpdateFeature(fullCPU, cpuExpanded->features[i].name,
|
|
VIR_CPU_FEATURE_REQUIRE);
|
|
}
|
|
}
|
|
|
|
if (!(migCPU = virQEMUCapsNewHostCPUModel()))
|
|
goto error;
|
|
|
|
if ((rc = virQEMUCapsInitCPUModel(qemuCaps, type, migCPU, true)) < 0) {
|
|
goto error;
|
|
} else if (rc == 1) {
|
|
VIR_DEBUG("CPU migratability not provided by QEMU");
|
|
|
|
virCPUDefFree(migCPU);
|
|
if (!(migCPU = virCPUCopyMigratable(qemuCaps->arch, cpu)))
|
|
goto error;
|
|
}
|
|
|
|
if (ARCH_IS_X86(qemuCaps->arch) &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_UNAVAILABLE_FEATURES)) {
|
|
if (cpu &&
|
|
virCPUDefFilterFeatures(cpu, virCPUx86FeatureFilterDropMSR, NULL) < 0)
|
|
goto error;
|
|
|
|
if (migCPU &&
|
|
virCPUDefFilterFeatures(migCPU, virCPUx86FeatureFilterDropMSR, NULL) < 0)
|
|
goto error;
|
|
|
|
if (fullCPU &&
|
|
virCPUDefFilterFeatures(fullCPU, virCPUx86FeatureFilterDropMSR, NULL) < 0)
|
|
goto error;
|
|
}
|
|
|
|
if (virQEMUCapsTypeIsAccelerated(type))
|
|
virHostCPUGetPhysAddrSize(hostArch, &physAddrSize);
|
|
|
|
virQEMUCapsSetHostModel(qemuCaps, type, physAddrSize, cpu, migCPU, fullCPU);
|
|
|
|
cleanup:
|
|
virCPUDefFree(cpuExpanded);
|
|
virCPUDefFree(hostCPU);
|
|
return;
|
|
|
|
error:
|
|
virCPUDefFree(cpu);
|
|
virCPUDefFree(migCPU);
|
|
virCPUDefFree(fullCPU);
|
|
virResetLastError();
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsUpdateHostCPUModel(virQEMUCaps *qemuCaps,
|
|
virArch hostArch,
|
|
virDomainVirtType type)
|
|
{
|
|
virQEMUCapsHostCPUDataClear(&virQEMUCapsGetAccel(qemuCaps, type)->hostCPU);
|
|
virQEMUCapsInitHostCPUModel(qemuCaps, hostArch, type);
|
|
}
|
|
|
|
qemuMonitorCPUModelInfo *
|
|
virQEMUCapsGetCPUModelInfo(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type)
|
|
{
|
|
return virQEMUCapsGetAccel(qemuCaps, type)->hostCPU.info;
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsSetCPUModelInfo(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType type,
|
|
qemuMonitorCPUModelInfo *modelInfo)
|
|
{
|
|
virQEMUCapsGetAccel(qemuCaps, type)->hostCPU.info = modelInfo;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsLoadHostCPUModelInfo(virQEMUCapsAccel *caps,
|
|
xmlXPathContextPtr ctxt,
|
|
const char *typeStr)
|
|
{
|
|
xmlNodePtr hostCPUNode;
|
|
xmlNodePtr deprecated_props;
|
|
g_autofree xmlNodePtr *nodes = NULL;
|
|
VIR_XPATH_NODE_AUTORESTORE(ctxt)
|
|
g_autoptr(qemuMonitorCPUModelInfo) hostCPU = NULL;
|
|
g_autofree char *xpath = g_strdup_printf("./hostCPU[@type='%s']", typeStr);
|
|
size_t i;
|
|
int n;
|
|
virTristateBool migratability;
|
|
int val;
|
|
|
|
if (!(hostCPUNode = virXPathNode(xpath, ctxt))) {
|
|
return 0;
|
|
}
|
|
|
|
hostCPU = g_new0(qemuMonitorCPUModelInfo, 1);
|
|
|
|
if (!(hostCPU->name = virXMLPropString(hostCPUNode, "model"))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing host CPU model name in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
if (virXMLPropTristateBool(hostCPUNode, "migratability",
|
|
VIR_XML_PROP_REQUIRED,
|
|
&migratability) < 0)
|
|
return -1;
|
|
|
|
virTristateBoolToBool(migratability, &hostCPU->migratability);
|
|
|
|
ctxt->node = hostCPUNode;
|
|
|
|
if ((n = virXPathNodeSet("./property", ctxt, &nodes)) > 0) {
|
|
hostCPU->props = g_new0(qemuMonitorCPUProperty, n);
|
|
hostCPU->nprops = n;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
qemuMonitorCPUProperty *prop = hostCPU->props + i;
|
|
g_autofree char *type = NULL;
|
|
|
|
ctxt->node = nodes[i];
|
|
|
|
if (!(prop->name = virXMLPropString(ctxt->node, "name"))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing 'name' attribute for a host CPU model property in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(type = virXMLPropString(ctxt->node, "type")) ||
|
|
(val = qemuMonitorCPUPropertyTypeFromString(type)) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing or invalid CPU model property type in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
prop->type = val;
|
|
switch (prop->type) {
|
|
case QEMU_MONITOR_CPU_PROPERTY_BOOLEAN:
|
|
if (virXPathBoolean("./@value='true'", ctxt))
|
|
prop->value.boolean = true;
|
|
break;
|
|
|
|
case QEMU_MONITOR_CPU_PROPERTY_STRING:
|
|
prop->value.string = virXMLPropString(ctxt->node, "value");
|
|
if (!prop->value.string) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("invalid string value for '%1$s' host CPU model property in QEMU capabilities cache"),
|
|
prop->name);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case QEMU_MONITOR_CPU_PROPERTY_NUMBER:
|
|
if (virXPathLongLong("string(./@value)", ctxt,
|
|
&prop->value.number) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("invalid number value for '%1$s' host CPU model property in QEMU capabilities cache"),
|
|
prop->name);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case QEMU_MONITOR_CPU_PROPERTY_LAST:
|
|
break;
|
|
}
|
|
|
|
if (virXMLPropTristateBool(ctxt->node, "migratable",
|
|
VIR_XML_PROP_NONE,
|
|
&prop->migratable) < 0)
|
|
return -1;
|
|
|
|
}
|
|
}
|
|
|
|
ctxt->node = hostCPUNode;
|
|
|
|
if ((deprecated_props = virXPathNode("./deprecatedFeatures", ctxt))) {
|
|
g_autoptr(GPtrArray) props = virXMLNodeGetSubelementList(deprecated_props, NULL);
|
|
|
|
hostCPU->deprecated_props = g_new0(char *, props->len + 1);
|
|
|
|
for (i = 0; i < props->len; i++) {
|
|
xmlNodePtr prop = g_ptr_array_index(props, i);
|
|
|
|
if (!(hostCPU->deprecated_props[i] = virXMLPropString(prop, "name"))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing 'name' attribute for a host CPU model deprecated property in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
caps->hostCPU.info = g_steal_pointer(&hostCPU);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsLoadCPUModels(virArch arch,
|
|
virQEMUCapsAccel *caps,
|
|
xmlXPathContextPtr ctxt,
|
|
const char *typeStr)
|
|
{
|
|
g_autoptr(qemuMonitorCPUDefs) defs = NULL;
|
|
g_autofree xmlNodePtr * nodes = NULL;
|
|
g_autofree char *xpath = g_strdup_printf("./cpu[@type='%s']", typeStr);
|
|
size_t i;
|
|
int n;
|
|
xmlNodePtr node;
|
|
|
|
if ((n = virXPathNodeSet(xpath, ctxt, &nodes)) < 0)
|
|
return -1;
|
|
|
|
if (n == 0)
|
|
return 0;
|
|
|
|
if (!(defs = qemuMonitorCPUDefsNew(n)))
|
|
return -1;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
qemuMonitorCPUDefInfo *cpu = defs->cpus + i;
|
|
int usable = VIR_DOMCAPS_CPU_USABLE_UNKNOWN;
|
|
g_autofree char * strUsable = NULL;
|
|
g_autofree xmlNodePtr * blockerNodes = NULL;
|
|
g_autofree char *deprecated = NULL;
|
|
int nblockers;
|
|
|
|
if ((strUsable = virXMLPropString(nodes[i], "usable")) &&
|
|
(usable = virDomainCapsCPUUsableTypeFromString(strUsable)) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unknown value '%1$s' in attribute 'usable'"),
|
|
strUsable);
|
|
return -1;
|
|
}
|
|
cpu->usable = usable;
|
|
|
|
if (!(cpu->name = virXMLPropString(nodes[i], "name"))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing cpu name in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
cpu->type = virXMLPropString(nodes[i], "typename");
|
|
|
|
node = ctxt->node;
|
|
ctxt->node = nodes[i];
|
|
nblockers = virXPathNodeSet("./blocker", ctxt, &blockerNodes);
|
|
ctxt->node = node;
|
|
|
|
if (nblockers < 0)
|
|
return -1;
|
|
|
|
if (nblockers > 0) {
|
|
size_t j;
|
|
|
|
cpu->blockers = g_new0(char *, nblockers + 1);
|
|
|
|
for (j = 0; j < nblockers; j++) {
|
|
if (!(cpu->blockers[j] = virXMLPropString(blockerNodes[j], "name"))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing blocker name in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
virQEMUCapsCPUFeatureFromQEMUInPlace(arch, &cpu->blockers[j]);
|
|
}
|
|
}
|
|
|
|
deprecated = virXMLPropString(nodes[i], "deprecated");
|
|
if (deprecated &&
|
|
STREQ(deprecated, "yes"))
|
|
cpu->deprecated = true;
|
|
}
|
|
|
|
caps->cpuModels = g_steal_pointer(&defs);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsLoadMachines(virQEMUCapsAccel *caps,
|
|
xmlXPathContextPtr ctxt,
|
|
const char *typeStr)
|
|
{
|
|
g_autofree char *xpath = g_strdup_printf("./machine[@type='%s']", typeStr);
|
|
g_autofree xmlNodePtr *nodes = NULL;
|
|
size_t i;
|
|
int n;
|
|
|
|
if ((n = virXPathNodeSet(xpath, ctxt, &nodes)) < 0)
|
|
return -1;
|
|
|
|
if (n == 0)
|
|
return 0;
|
|
|
|
caps->nmachineTypes = n;
|
|
caps->machineTypes = g_new0(virQEMUCapsMachineType, caps->nmachineTypes);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
virTristateBool tmp;
|
|
|
|
if (!(caps->machineTypes[i].name = virXMLPropStringRequired(nodes[i], "name")))
|
|
return -1;
|
|
caps->machineTypes[i].alias = virXMLPropString(nodes[i], "alias");
|
|
|
|
if (virXMLPropUInt(nodes[i], "maxCpus", 10, VIR_XML_PROP_NONE,
|
|
&(caps->machineTypes[i].maxCpus)) < 0)
|
|
return -1;
|
|
|
|
if (virXMLPropTristateBool(nodes[i], "hotplugCpus", VIR_XML_PROP_NONE, &tmp) < 0)
|
|
return -1;
|
|
|
|
virTristateBoolToBool(tmp, &caps->machineTypes[i].hotplugCpus);
|
|
|
|
if (virXMLPropTristateBool(nodes[i], "default", VIR_XML_PROP_NONE, &tmp) < 0)
|
|
return -1;
|
|
|
|
virTristateBoolToBool(tmp, &caps->machineTypes[i].qemuDefault);
|
|
|
|
if (virXMLPropTristateBool(nodes[i], "numaMemSupported", VIR_XML_PROP_NONE, &tmp) < 0)
|
|
return -1;
|
|
|
|
virTristateBoolToBool(tmp, &caps->machineTypes[i].numaMemSupported);
|
|
|
|
caps->machineTypes[i].defaultCPU = virXMLPropString(nodes[i], "defaultCPU");
|
|
caps->machineTypes[i].defaultRAMid = virXMLPropString(nodes[i], "defaultRAMid");
|
|
|
|
if (virXMLPropTristateBool(nodes[i], "deprecated", VIR_XML_PROP_NONE, &tmp) < 0)
|
|
return -1;
|
|
|
|
virTristateBoolToBool(tmp, &caps->machineTypes[i].deprecated);
|
|
|
|
if (virXMLPropTristateBool(nodes[i], "acpi", VIR_XML_PROP_NONE, &caps->machineTypes[i].acpi) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsLoadAccel(virQEMUCaps *qemuCaps,
|
|
xmlXPathContextPtr ctxt,
|
|
virDomainVirtType type)
|
|
{
|
|
virQEMUCapsAccel *caps = virQEMUCapsGetAccel(qemuCaps, type);
|
|
const char *typeStr = virQEMUCapsAccelStr(type);
|
|
|
|
if (virQEMUCapsLoadHostCPUModelInfo(caps, ctxt, typeStr) < 0)
|
|
return -1;
|
|
|
|
if (virQEMUCapsLoadCPUModels(qemuCaps->arch, caps, ctxt, typeStr) < 0)
|
|
return -1;
|
|
|
|
if (virQEMUCapsLoadMachines(caps, ctxt, typeStr) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
struct _virQEMUCapsCachePriv {
|
|
char *libDir;
|
|
uid_t runUid;
|
|
gid_t runGid;
|
|
virArch hostArch;
|
|
unsigned int microcodeVersion;
|
|
virCPUData *cpuData;
|
|
char *kernelVersion;
|
|
char *hostCPUSignature;
|
|
|
|
/* cache whether /dev/kvm is usable as runUid:runGuid */
|
|
virTristateBool kvmUsable;
|
|
time_t kvmCtime;
|
|
};
|
|
typedef struct _virQEMUCapsCachePriv virQEMUCapsCachePriv;
|
|
|
|
|
|
static void
|
|
virQEMUCapsCachePrivFree(void *privData)
|
|
{
|
|
virQEMUCapsCachePriv *priv = privData;
|
|
|
|
g_free(priv->libDir);
|
|
g_free(priv->kernelVersion);
|
|
virCPUDataFree(priv->cpuData);
|
|
g_free(priv->hostCPUSignature);
|
|
g_free(priv);
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsParseSEVInfo(virQEMUCaps *qemuCaps, xmlXPathContextPtr ctxt)
|
|
{
|
|
g_autoptr(virSEVCapability) sev = NULL;
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST))
|
|
return 0;
|
|
|
|
if (virXPathBoolean("boolean(./sev)", ctxt) == 0) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("missing SEV platform data in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
sev = g_new0(virSEVCapability, 1);
|
|
|
|
if (virXPathUInt("string(./sev/cbitpos)", ctxt, &sev->cbitpos) < 0) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("missing or malformed SEV cbitpos information in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
if (virXPathUInt("string(./sev/reducedPhysBits)", ctxt,
|
|
&sev->reduced_phys_bits) < 0) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("missing or malformed SEV reducedPhysBits information in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(sev->pdh = virXPathString("string(./sev/pdh)", ctxt))) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("missing SEV pdh information in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(sev->cert_chain = virXPathString("string(./sev/certChain)", ctxt))) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("missing SEV certChain information in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* We probe this every time because the values
|
|
* can change on every reboot via firmware
|
|
* config tunables. It is cheap to query so
|
|
* lack of caching is a non-issue
|
|
*/
|
|
virQEMUCapsGetSEVMaxGuests(sev);
|
|
|
|
qemuCaps->sevCapabilities = g_steal_pointer(&sev);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsParseSGXInfo(virQEMUCaps *qemuCaps,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
g_autoptr(virSGXCapability) sgx = NULL;
|
|
xmlNodePtr sgxSections = NULL;
|
|
g_autofree char *flc = NULL;
|
|
g_autofree char *sgx1 = NULL;
|
|
g_autofree char *sgx2 = NULL;
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SGX_EPC))
|
|
return 0;
|
|
|
|
if (virXPathBoolean("boolean(./sgx)", ctxt) == 0) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("missing SGX platform data in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
sgx = g_new0(virSGXCapability, 1);
|
|
|
|
if ((!(flc = virXPathString("string(./sgx/flc)", ctxt))) ||
|
|
virStringParseYesNo(flc, &sgx->flc) < 0) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("missing or invalid SGX platform flc in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
if ((!(sgx1 = virXPathString("string(./sgx/sgx1)", ctxt))) ||
|
|
virStringParseYesNo(sgx1, &sgx->sgx1) < 0) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("missing or invalid SGX platform sgx1 in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
if ((!(sgx2 = virXPathString("string(./sgx/sgx2)", ctxt))) ||
|
|
virStringParseYesNo(sgx2, &sgx->sgx2) < 0) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("missing or invalid SGX platform sgx2 in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
if (virXPathULongLong("string(./sgx/section_size)", ctxt,
|
|
&sgx->section_size) < 0) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("missing or malformed SGX platform section_size in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
if ((sgxSections = virXPathNode("./sgx/sections", ctxt))) {
|
|
g_autofree xmlNodePtr *sectionNodes = NULL;
|
|
int nSgxSections = 0;
|
|
size_t i;
|
|
VIR_XPATH_NODE_AUTORESTORE(ctxt);
|
|
|
|
ctxt->node = sgxSections;
|
|
nSgxSections = virXPathNodeSet("./section", ctxt, §ionNodes);
|
|
|
|
if (nSgxSections < 0)
|
|
return -1;
|
|
|
|
sgx->nSgxSections = nSgxSections;
|
|
sgx->sgxSections = g_new0(virSGXSection, nSgxSections);
|
|
|
|
for (i = 0; i < nSgxSections; i++) {
|
|
if (virXMLPropUInt(sectionNodes[i], "node", 10,
|
|
VIR_XML_PROP_REQUIRED,
|
|
&(sgx->sgxSections[i].node)) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing node name in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
if (virXMLPropULongLong(sectionNodes[i], "size", 10,
|
|
VIR_XML_PROP_REQUIRED,
|
|
&(sgx->sgxSections[i].size)) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing size name in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
qemuCaps->sgxCapabilities = g_steal_pointer(&sgx);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsParseHypervCapabilities(virQEMUCaps *qemuCaps,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
g_autofree virDomainCapsFeatureHyperv *hvcaps = NULL;
|
|
xmlNodePtr n = NULL;
|
|
g_autofree xmlNodePtr *capNodes = NULL;
|
|
int ncapNodes;
|
|
size_t i;
|
|
|
|
if (!(n = virXPathNode("./hypervCapabilities", ctxt)))
|
|
return 0;
|
|
|
|
hvcaps = g_new0(virDomainCapsFeatureHyperv, 1);
|
|
if (virXMLPropTristateBool(n, "supported", VIR_XML_PROP_REQUIRED,
|
|
&hvcaps->supported) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if ((ncapNodes = virXPathNodeSet("./hypervCapabilities/cap",
|
|
ctxt, &capNodes)) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
hvcaps->features.report = ncapNodes > 0;
|
|
for (i = 0; i < ncapNodes; i++) {
|
|
g_autofree char *name = virXMLPropStringRequired(capNodes[i], "name");
|
|
int val;
|
|
|
|
if (!name)
|
|
return -1;
|
|
|
|
if ((val = virDomainHypervTypeFromString(name)) < 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("unsupported HyperV Enlightenment feature: %1$s"),
|
|
name);
|
|
return -1;
|
|
}
|
|
|
|
VIR_DOMAIN_CAPS_ENUM_SET(hvcaps->features, val);
|
|
}
|
|
|
|
qemuCaps->hypervCapabilities = g_steal_pointer(&hvcaps);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsParseFlags(virQEMUCaps *qemuCaps, xmlXPathContextPtr ctxt)
|
|
{
|
|
g_autofree xmlNodePtr *nodes = NULL;
|
|
size_t i;
|
|
int n;
|
|
|
|
if ((n = virXPathNodeSet("./flag", ctxt, &nodes)) < 0)
|
|
return -1;
|
|
|
|
VIR_DEBUG("Got flags %d", n);
|
|
for (i = 0; i < n; i++) {
|
|
g_autofree char *str = NULL;
|
|
int flag;
|
|
|
|
if (!(str = virXMLPropString(nodes[i], "name"))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing flag name in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
flag = virQEMUCapsTypeFromString(str);
|
|
if (flag < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Unknown qemu capabilities flag %1$s"), str);
|
|
return -1;
|
|
}
|
|
|
|
virQEMUCapsSet(qemuCaps, flag);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsParseGIC(virQEMUCaps *qemuCaps, xmlXPathContextPtr ctxt)
|
|
{
|
|
g_autofree xmlNodePtr *nodes = NULL;
|
|
size_t i;
|
|
int n;
|
|
|
|
if ((n = virXPathNodeSet("./gic", ctxt, &nodes)) < 0)
|
|
return -1;
|
|
|
|
if (n > 0) {
|
|
unsigned int uintValue;
|
|
bool boolValue;
|
|
|
|
qemuCaps->ngicCapabilities = n;
|
|
qemuCaps->gicCapabilities = g_new0(virGICCapability, n);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
virGICCapability *cap = &qemuCaps->gicCapabilities[i];
|
|
g_autofree char *version = NULL;
|
|
g_autofree char *kernel = NULL;
|
|
g_autofree char *emulated = NULL;
|
|
|
|
if (!(version = virXMLPropString(nodes[i], "version"))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing GIC version in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
if (virStrToLong_ui(version, NULL, 10, &uintValue) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("malformed GIC version in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
cap->version = uintValue;
|
|
|
|
if (!(kernel = virXMLPropString(nodes[i], "kernel"))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing in-kernel GIC information in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
if (!(boolValue = STREQ(kernel, "yes")) && STRNEQ(kernel, "no")) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("malformed in-kernel GIC information in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
if (boolValue)
|
|
cap->implementation |= VIR_GIC_IMPLEMENTATION_KERNEL;
|
|
|
|
if (!(emulated = virXMLPropString(nodes[i], "emulated"))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing emulated GIC information in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
if (!(boolValue = STREQ(emulated, "yes")) && STRNEQ(emulated, "no")) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("malformed emulated GIC information in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
if (boolValue)
|
|
cap->implementation |= VIR_GIC_IMPLEMENTATION_EMULATED;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsValidateEmulator(virQEMUCaps *qemuCaps, xmlXPathContextPtr ctxt)
|
|
{
|
|
g_autofree char *str = NULL;
|
|
|
|
if (!(str = virXPathString("string(./emulator)", ctxt))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing emulator in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
if (STRNEQ(str, qemuCaps->binary)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Expected caps for '%1$s' but saw '%2$s'"),
|
|
qemuCaps->binary, str);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsValidateArch(virQEMUCaps *qemuCaps, xmlXPathContextPtr ctxt)
|
|
{
|
|
g_autofree char *str = NULL;
|
|
|
|
if (!(str = virXPathString("string(./arch)", ctxt))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing arch in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
if (!(qemuCaps->arch = virArchFromString(str))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unknown arch %1$s in QEMU capabilities cache"), str);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Parsing a doc that looks like
|
|
*
|
|
* <qemuCaps>
|
|
* <emulator>/some/path</emulator>
|
|
* <qemuctime>234235253</qemuctime>
|
|
* <qemumoddirmtime>234235253</qemumoddirmtime>
|
|
* <selfctime>234235253</selfctime>
|
|
* <selfvers>1002016</selfvers>
|
|
* <flag name='foo'/>
|
|
* <flag name='bar'/>
|
|
* ...
|
|
* <cpu name="pentium3"/>
|
|
* ...
|
|
* <machine name='pc-1.0' alias='pc' hotplugCpus='yes' maxCpus='4' default='yes' numaMemSupported='yes'/>
|
|
* ...
|
|
* </qemuCaps>
|
|
*
|
|
* Returns 0 on success, 1 if outdated, -1 on error
|
|
*/
|
|
int
|
|
virQEMUCapsLoadCache(virArch hostArch,
|
|
virQEMUCaps *qemuCaps,
|
|
const char *filename,
|
|
bool skipInvalidation)
|
|
{
|
|
g_autoptr(xmlDoc) doc = NULL;
|
|
g_autoptr(xmlXPathContext) ctxt = NULL;
|
|
long long int l;
|
|
|
|
if (!(doc = virXMLParse(filename, NULL, NULL, "qemuCaps", &ctxt, NULL, false)))
|
|
return -1;
|
|
|
|
if (virXPathLongLong("string(./selfctime)", ctxt, &l) < 0) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("missing selfctime in QEMU capabilities XML"));
|
|
return -1;
|
|
}
|
|
qemuCaps->libvirtCtime = (time_t)l;
|
|
|
|
if (virXPathUInt("string(./selfvers)", ctxt,
|
|
&qemuCaps->libvirtVersion) < 0)
|
|
return -1;
|
|
|
|
if (!skipInvalidation &&
|
|
(qemuCaps->libvirtCtime != virGetSelfLastChanged() ||
|
|
qemuCaps->libvirtVersion != LIBVIR_VERSION_NUMBER)) {
|
|
VIR_DEBUG("Outdated capabilities in %s: libvirt changed "
|
|
"(%lld vs %lld, %lu vs %lu), stopping load",
|
|
qemuCaps->binary,
|
|
(long long)qemuCaps->libvirtCtime,
|
|
(long long)virGetSelfLastChanged(),
|
|
(unsigned long)qemuCaps->libvirtVersion,
|
|
(unsigned long)LIBVIR_VERSION_NUMBER);
|
|
return 1;
|
|
}
|
|
|
|
if (virQEMUCapsValidateEmulator(qemuCaps, ctxt) < 0)
|
|
return -1;
|
|
|
|
if (virXPathLongLong("string(./qemuctime)", ctxt, &l) < 0) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("missing qemuctime in QEMU capabilities XML"));
|
|
return -1;
|
|
}
|
|
qemuCaps->ctime = (time_t)l;
|
|
|
|
if (virXPathLongLong("string(./qemumoddirmtime)", ctxt, &l) == 0)
|
|
qemuCaps->modDirMtime = (time_t)l;
|
|
|
|
if (virQEMUCapsParseFlags(qemuCaps, ctxt) < 0)
|
|
return -1;
|
|
|
|
if (virXPathUInt("string(./version)", ctxt, &qemuCaps->version) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing version in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
if (virXPathUInt("string(./microcodeVersion)", ctxt,
|
|
&qemuCaps->microcodeVersion) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing microcode version in QEMU capabilities cache"));
|
|
return -1;
|
|
}
|
|
|
|
qemuCaps->hostCPUSignature = virXPathString("string(./hostCPUSignature)", ctxt);
|
|
|
|
if (virXPathBoolean("boolean(./package)", ctxt) > 0) {
|
|
qemuCaps->package = virXPathString("string(./package)", ctxt);
|
|
if (!qemuCaps->package)
|
|
qemuCaps->package = g_strdup("");
|
|
}
|
|
|
|
if (virXPathBoolean("boolean(./kernelVersion)", ctxt) > 0) {
|
|
qemuCaps->kernelVersion = virXPathString("string(./kernelVersion)", ctxt);
|
|
if (!qemuCaps->kernelVersion)
|
|
return -1;
|
|
}
|
|
|
|
if (virQEMUCapsValidateArch(qemuCaps, ctxt) < 0)
|
|
return -1;
|
|
|
|
if (virXPathBoolean("boolean(./cpudata)", ctxt) > 0) {
|
|
qemuCaps->cpuData = virCPUDataParseNode(virXPathNode("./cpudata", ctxt));
|
|
if (!qemuCaps->cpuData)
|
|
return -1;
|
|
}
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM) &&
|
|
virQEMUCapsLoadAccel(qemuCaps, ctxt, VIR_DOMAIN_VIRT_KVM) < 0) {
|
|
return -1;
|
|
}
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_HVF) &&
|
|
virQEMUCapsLoadAccel(qemuCaps, ctxt, VIR_DOMAIN_VIRT_HVF) < 0) {
|
|
return -1;
|
|
}
|
|
if (virQEMUCapsLoadAccel(qemuCaps, ctxt, VIR_DOMAIN_VIRT_QEMU) < 0)
|
|
return -1;
|
|
|
|
if (virQEMUCapsParseGIC(qemuCaps, ctxt) < 0)
|
|
return -1;
|
|
|
|
if (virQEMUCapsParseSEVInfo(qemuCaps, ctxt) < 0)
|
|
return -1;
|
|
|
|
if (virQEMUCapsParseSGXInfo(qemuCaps, ctxt) < 0)
|
|
return -1;
|
|
|
|
if (virQEMUCapsParseHypervCapabilities(qemuCaps, ctxt) < 0)
|
|
return -1;
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM))
|
|
virQEMUCapsInitHostCPUModel(qemuCaps, hostArch, VIR_DOMAIN_VIRT_KVM);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_HVF))
|
|
virQEMUCapsInitHostCPUModel(qemuCaps, hostArch, VIR_DOMAIN_VIRT_HVF);
|
|
virQEMUCapsInitHostCPUModel(qemuCaps, hostArch, VIR_DOMAIN_VIRT_QEMU);
|
|
|
|
if (virXPathBoolean("boolean(./kvmSupportsNesting)", ctxt) > 0)
|
|
qemuCaps->kvmSupportsNesting = true;
|
|
|
|
if (virXPathBoolean("boolean(./kvmSupportsSecureGuest)", ctxt) > 0)
|
|
qemuCaps->kvmSupportsSecureGuest = true;
|
|
|
|
if (skipInvalidation)
|
|
qemuCaps->invalidation = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsFormatHostCPUModelInfo(virQEMUCapsAccel *caps,
|
|
virBuffer *buf,
|
|
const char *typeStr)
|
|
{
|
|
qemuMonitorCPUModelInfo *model = caps->hostCPU.info;
|
|
size_t i;
|
|
|
|
if (!model)
|
|
return;
|
|
|
|
virBufferAsprintf(buf,
|
|
"<hostCPU type='%s' model='%s' migratability='%s'>\n",
|
|
typeStr, model->name,
|
|
model->migratability ? "yes" : "no");
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
for (i = 0; i < model->nprops; i++) {
|
|
qemuMonitorCPUProperty *prop = model->props + i;
|
|
|
|
virBufferAsprintf(buf, "<property name='%s' type='%s' ",
|
|
prop->name,
|
|
qemuMonitorCPUPropertyTypeToString(prop->type));
|
|
|
|
switch (prop->type) {
|
|
case QEMU_MONITOR_CPU_PROPERTY_BOOLEAN:
|
|
virBufferAsprintf(buf, "value='%s'",
|
|
prop->value.boolean ? "true" : "false");
|
|
break;
|
|
|
|
case QEMU_MONITOR_CPU_PROPERTY_STRING:
|
|
virBufferEscapeString(buf, "value='%s'", prop->value.string);
|
|
break;
|
|
|
|
case QEMU_MONITOR_CPU_PROPERTY_NUMBER:
|
|
virBufferAsprintf(buf, "value='%lld'", prop->value.number);
|
|
break;
|
|
|
|
case QEMU_MONITOR_CPU_PROPERTY_LAST:
|
|
break;
|
|
}
|
|
|
|
if (prop->migratable > 0)
|
|
virBufferAsprintf(buf, " migratable='%s'",
|
|
virTristateBoolTypeToString(prop->migratable));
|
|
|
|
virBufferAddLit(buf, "/>\n");
|
|
}
|
|
|
|
if (model->deprecated_props) {
|
|
virBufferAddLit(buf, "<deprecatedFeatures>\n");
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
for (i = 0; i < g_strv_length(model->deprecated_props); i++)
|
|
virBufferAsprintf(buf, "<property name='%s'/>\n",
|
|
model->deprecated_props[i]);
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
virBufferAddLit(buf, "</deprecatedFeatures>\n");
|
|
}
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
virBufferAddLit(buf, "</hostCPU>\n");
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsFormatCPUModels(virArch arch,
|
|
virQEMUCapsAccel *caps,
|
|
virBuffer *buf,
|
|
const char *typeStr)
|
|
{
|
|
qemuMonitorCPUDefs *defs = caps->cpuModels;
|
|
size_t i;
|
|
|
|
if (!defs)
|
|
return;
|
|
|
|
for (i = 0; i < defs->ncpus; i++) {
|
|
qemuMonitorCPUDefInfo *cpu = defs->cpus + i;
|
|
|
|
virBufferAsprintf(buf, "<cpu type='%s' ", typeStr);
|
|
virBufferEscapeString(buf, "name='%s'", cpu->name);
|
|
virBufferEscapeString(buf, " typename='%s'", cpu->type);
|
|
if (cpu->usable) {
|
|
virBufferAsprintf(buf, " usable='%s'",
|
|
virDomainCapsCPUUsableTypeToString(cpu->usable));
|
|
}
|
|
if (cpu->deprecated)
|
|
virBufferAddLit(buf, " deprecated='yes'");
|
|
|
|
if (cpu->blockers) {
|
|
size_t j;
|
|
|
|
virBufferAddLit(buf, ">\n");
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
for (j = 0; cpu->blockers[j]; j++) {
|
|
virBufferAsprintf(buf, "<blocker name='%s'/>\n",
|
|
virQEMUCapsCPUFeatureToQEMU(arch, cpu->blockers[j]));
|
|
}
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
virBufferAddLit(buf, "</cpu>\n");
|
|
} else {
|
|
virBufferAddLit(buf, "/>\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsFormatMachines(virQEMUCapsAccel *caps,
|
|
virBuffer *buf,
|
|
const char *typeStr)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < caps->nmachineTypes; i++) {
|
|
virBufferAsprintf(buf, "<machine type='%s'", typeStr);
|
|
virBufferEscapeString(buf, " name='%s'",
|
|
caps->machineTypes[i].name);
|
|
virBufferEscapeString(buf, " alias='%s'",
|
|
caps->machineTypes[i].alias);
|
|
if (caps->machineTypes[i].hotplugCpus)
|
|
virBufferAddLit(buf, " hotplugCpus='yes'");
|
|
virBufferAsprintf(buf, " maxCpus='%u'",
|
|
caps->machineTypes[i].maxCpus);
|
|
if (caps->machineTypes[i].qemuDefault)
|
|
virBufferAddLit(buf, " default='yes'");
|
|
virBufferEscapeString(buf, " defaultCPU='%s'",
|
|
caps->machineTypes[i].defaultCPU);
|
|
if (caps->machineTypes[i].numaMemSupported)
|
|
virBufferAddLit(buf, " numaMemSupported='yes'");
|
|
virBufferEscapeString(buf, " defaultRAMid='%s'",
|
|
caps->machineTypes[i].defaultRAMid);
|
|
if (caps->machineTypes[i].deprecated)
|
|
virBufferAddLit(buf, " deprecated='yes'");
|
|
|
|
if (caps->machineTypes[i].acpi != VIR_TRISTATE_BOOL_ABSENT)
|
|
virBufferAsprintf(buf, " acpi='%s'",
|
|
virTristateBoolTypeToString(caps->machineTypes[i].acpi));
|
|
|
|
virBufferAddLit(buf, "/>\n");
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsFormatAccel(virQEMUCaps *qemuCaps,
|
|
virBuffer *buf,
|
|
virDomainVirtType type)
|
|
{
|
|
virQEMUCapsAccel *caps = virQEMUCapsGetAccel(qemuCaps, type);
|
|
const char *typeStr = virQEMUCapsAccelStr(type);
|
|
|
|
virQEMUCapsFormatHostCPUModelInfo(caps, buf, typeStr);
|
|
virQEMUCapsFormatCPUModels(qemuCaps->arch, caps, buf, typeStr);
|
|
virQEMUCapsFormatMachines(caps, buf, typeStr);
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsFormatSEVInfo(virQEMUCaps *qemuCaps, virBuffer *buf)
|
|
{
|
|
virSEVCapability *sev = virQEMUCapsGetSEVCapabilities(qemuCaps);
|
|
|
|
virBufferAddLit(buf, "<sev>\n");
|
|
virBufferAdjustIndent(buf, 2);
|
|
virBufferAsprintf(buf, "<cbitpos>%u</cbitpos>\n", sev->cbitpos);
|
|
virBufferAsprintf(buf, "<reducedPhysBits>%u</reducedPhysBits>\n",
|
|
sev->reduced_phys_bits);
|
|
virBufferEscapeString(buf, "<pdh>%s</pdh>\n", sev->pdh);
|
|
virBufferEscapeString(buf, "<certChain>%s</certChain>\n",
|
|
sev->cert_chain);
|
|
if (sev->cpu0_id != NULL) {
|
|
virBufferEscapeString(buf, "<cpu0Id>%s</cpu0Id>\n",
|
|
sev->cpu0_id);
|
|
}
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
virBufferAddLit(buf, "</sev>\n");
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsFormatSGXInfo(virQEMUCaps *qemuCaps,
|
|
virBuffer *buf)
|
|
{
|
|
virSGXCapability *sgx = virQEMUCapsGetSGXCapabilities(qemuCaps);
|
|
|
|
virBufferAddLit(buf, "<sgx supported='yes'>\n");
|
|
virBufferAdjustIndent(buf, 2);
|
|
virBufferAsprintf(buf, "<flc>%s</flc>\n", sgx->flc ? "yes" : "no");
|
|
virBufferAsprintf(buf, "<sgx1>%s</sgx1>\n", sgx->sgx1 ? "yes" : "no");
|
|
virBufferAsprintf(buf, "<sgx2>%s</sgx2>\n", sgx->sgx2 ? "yes" : "no");
|
|
virBufferAsprintf(buf, "<section_size unit='KiB'>%llu</section_size>\n", sgx->section_size);
|
|
|
|
if (sgx->nSgxSections > 0) {
|
|
size_t i;
|
|
virBufferAddLit(buf, "<sections>\n");
|
|
|
|
for (i = 0; i < sgx->nSgxSections; i++) {
|
|
virBufferAdjustIndent(buf, 2);
|
|
virBufferAsprintf(buf, "<section node='%u' ", sgx->sgxSections[i].node);
|
|
virBufferAsprintf(buf, "size='%llu' ", sgx->sgxSections[i].size);
|
|
virBufferAddLit(buf, "unit='KiB'/>\n");
|
|
virBufferAdjustIndent(buf, -2);
|
|
}
|
|
virBufferAddLit(buf, "</sections>\n");
|
|
}
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
virBufferAddLit(buf, "</sgx>\n");
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsFormatHypervCapabilities(virQEMUCaps *qemuCaps,
|
|
virBuffer *buf)
|
|
{
|
|
virDomainCapsFeatureHyperv *hvcaps = qemuCaps->hypervCapabilities;
|
|
g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
|
|
g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
|
|
|
|
virBufferAsprintf(&attrBuf, " supported='%s'",
|
|
virTristateBoolTypeToString(hvcaps->supported));
|
|
|
|
if (hvcaps->supported) {
|
|
size_t i;
|
|
|
|
for (i = 0; i < sizeof(hvcaps->features.values) * CHAR_BIT; i++) {
|
|
if (!(hvcaps->features.values & (1U << i)))
|
|
continue;
|
|
|
|
virBufferAsprintf(&childBuf, "<cap name='%s'/>\n",
|
|
virDomainHypervTypeToString(i));
|
|
}
|
|
}
|
|
|
|
return virXMLFormatElement(buf, "hypervCapabilities", &attrBuf, &childBuf);
|
|
}
|
|
|
|
|
|
char *
|
|
virQEMUCapsFormatCache(virQEMUCaps *qemuCaps)
|
|
{
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
|
size_t i;
|
|
|
|
virBufferAddLit(&buf, "<qemuCaps>\n");
|
|
virBufferAdjustIndent(&buf, 2);
|
|
|
|
virBufferEscapeString(&buf, "<emulator>%s</emulator>\n",
|
|
qemuCaps->binary);
|
|
virBufferAsprintf(&buf, "<qemuctime>%llu</qemuctime>\n",
|
|
(long long)qemuCaps->ctime);
|
|
if (qemuCaps->modDirMtime > 0) {
|
|
virBufferAsprintf(&buf, "<qemumoddirmtime>%llu</qemumoddirmtime>\n",
|
|
(long long)qemuCaps->modDirMtime);
|
|
}
|
|
virBufferAsprintf(&buf, "<selfctime>%llu</selfctime>\n",
|
|
(long long)qemuCaps->libvirtCtime);
|
|
virBufferAsprintf(&buf, "<selfvers>%lu</selfvers>\n",
|
|
(unsigned long)qemuCaps->libvirtVersion);
|
|
|
|
for (i = 0; i < QEMU_CAPS_LAST; i++) {
|
|
if (virQEMUCapsGet(qemuCaps, i)) {
|
|
virBufferAsprintf(&buf, "<flag name='%s'/>\n",
|
|
virQEMUCapsTypeToString(i));
|
|
}
|
|
}
|
|
|
|
virBufferAsprintf(&buf, "<version>%d</version>\n",
|
|
qemuCaps->version);
|
|
|
|
virBufferAsprintf(&buf, "<microcodeVersion>%u</microcodeVersion>\n",
|
|
qemuCaps->microcodeVersion);
|
|
virBufferEscapeString(&buf, "<hostCPUSignature>%s</hostCPUSignature>\n",
|
|
qemuCaps->hostCPUSignature);
|
|
|
|
if (qemuCaps->package)
|
|
virBufferAsprintf(&buf, "<package>%s</package>\n",
|
|
qemuCaps->package);
|
|
|
|
if (qemuCaps->kernelVersion)
|
|
virBufferAsprintf(&buf, "<kernelVersion>%s</kernelVersion>\n",
|
|
qemuCaps->kernelVersion);
|
|
|
|
if (qemuCaps->cpuData) {
|
|
g_autofree char * cpudata = virCPUDataFormat(qemuCaps->cpuData);
|
|
virBufferAddStr(&buf, cpudata);
|
|
}
|
|
|
|
virBufferAsprintf(&buf, "<arch>%s</arch>\n",
|
|
virArchToString(qemuCaps->arch));
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM))
|
|
virQEMUCapsFormatAccel(qemuCaps, &buf, VIR_DOMAIN_VIRT_KVM);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_HVF))
|
|
virQEMUCapsFormatAccel(qemuCaps, &buf, VIR_DOMAIN_VIRT_HVF);
|
|
virQEMUCapsFormatAccel(qemuCaps, &buf, VIR_DOMAIN_VIRT_QEMU);
|
|
|
|
for (i = 0; i < qemuCaps->ngicCapabilities; i++) {
|
|
virGICCapability *cap;
|
|
bool kernel;
|
|
bool emulated;
|
|
|
|
cap = &qemuCaps->gicCapabilities[i];
|
|
kernel = (cap->implementation & VIR_GIC_IMPLEMENTATION_KERNEL);
|
|
emulated = (cap->implementation & VIR_GIC_IMPLEMENTATION_EMULATED);
|
|
|
|
virBufferAsprintf(&buf,
|
|
"<gic version='%d' kernel='%s' emulated='%s'/>\n",
|
|
cap->version,
|
|
kernel ? "yes" : "no",
|
|
emulated ? "yes" : "no");
|
|
}
|
|
|
|
if (qemuCaps->sevCapabilities)
|
|
virQEMUCapsFormatSEVInfo(qemuCaps, &buf);
|
|
|
|
if (qemuCaps->sgxCapabilities)
|
|
virQEMUCapsFormatSGXInfo(qemuCaps, &buf);
|
|
|
|
if (qemuCaps->hypervCapabilities)
|
|
virQEMUCapsFormatHypervCapabilities(qemuCaps, &buf);
|
|
|
|
if (qemuCaps->kvmSupportsNesting)
|
|
virBufferAddLit(&buf, "<kvmSupportsNesting/>\n");
|
|
|
|
if (qemuCaps->kvmSupportsSecureGuest)
|
|
virBufferAddLit(&buf, "<kvmSupportsSecureGuest/>\n");
|
|
|
|
virBufferAdjustIndent(&buf, -2);
|
|
virBufferAddLit(&buf, "</qemuCaps>\n");
|
|
|
|
return virBufferContentAndReset(&buf);
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsSaveFile(void *data,
|
|
const char *filename,
|
|
void *privData G_GNUC_UNUSED)
|
|
{
|
|
virQEMUCaps *qemuCaps = data;
|
|
g_autofree char *xml = NULL;
|
|
|
|
xml = virQEMUCapsFormatCache(qemuCaps);
|
|
|
|
if (virFileWriteStr(filename, xml, 0600) < 0) {
|
|
virReportSystemError(errno,
|
|
_("Failed to save '%1$s' for '%2$s'"),
|
|
filename, qemuCaps->binary);
|
|
return -1;
|
|
}
|
|
|
|
VIR_DEBUG("Saved caps '%s' for '%s' with (%lld, %lld)",
|
|
filename, qemuCaps->binary,
|
|
(long long)qemuCaps->ctime,
|
|
(long long)qemuCaps->libvirtCtime);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Check whether IBM Secure Execution (S390) is enabled
|
|
*/
|
|
static bool
|
|
virQEMUCapsKVMSupportsSecureGuestS390(void)
|
|
{
|
|
|
|
g_autofree char *cmdline = NULL;
|
|
static const char *kValues[] = {"y", "Y", "on", "ON", "oN", "On", "1"};
|
|
|
|
if (!virFileIsDir("/sys/firmware/uv"))
|
|
return false;
|
|
|
|
if (virFileReadValueString(&cmdline, "/proc/cmdline") < 0)
|
|
return false;
|
|
|
|
/* we're prefix matching rather than equality matching here, because kernel
|
|
* would treat even something like prot_virt='yFOO' as enabled */
|
|
if (virKernelCmdlineMatchParam(cmdline, "prot_virt", kValues,
|
|
G_N_ELEMENTS(kValues),
|
|
VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST |
|
|
VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* Check whether AMD Secure Encrypted Virtualization (x86) is enabled
|
|
*/
|
|
static bool
|
|
virQEMUCapsKVMSupportsSecureGuestAMD(void)
|
|
{
|
|
g_autofree char *modValue = NULL;
|
|
|
|
if (virFileReadValueString(&modValue, "/sys/module/kvm_amd/parameters/sev") < 0)
|
|
return false;
|
|
|
|
if (modValue[0] != '1' && modValue[0] != 'Y' && modValue[0] != 'y')
|
|
return false;
|
|
|
|
if (virFileExists(QEMU_DEV_SEV))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* Check whether the secure guest functionality is enabled.
|
|
* See the specific architecture function for details on the verifications made.
|
|
*/
|
|
static bool
|
|
virQEMUCapsKVMSupportsSecureGuest(void)
|
|
{
|
|
virArch arch = virArchFromHost();
|
|
|
|
if (ARCH_IS_S390(arch))
|
|
return virQEMUCapsKVMSupportsSecureGuestS390();
|
|
|
|
if (ARCH_IS_X86(arch))
|
|
return virQEMUCapsKVMSupportsSecureGuestAMD();
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/* Check the kernel module parameters 'nested' file to determine if enabled
|
|
*
|
|
* Intel: 'kvm_intel' uses 'Y'
|
|
* AMD: 'kvm_amd' uses '1'
|
|
* PPC64: 'kvm_hv' uses 'Y'
|
|
* S390: 'kvm' uses '1'
|
|
*/
|
|
static bool
|
|
virQEMUCapsKVMSupportsNesting(void)
|
|
{
|
|
static char const * const kmod[] = {"kvm_intel", "kvm_amd",
|
|
"kvm_hv", "kvm"};
|
|
g_autofree char *value = NULL;
|
|
int rc;
|
|
size_t i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(kmod); i++) {
|
|
VIR_FREE(value);
|
|
rc = virFileReadValueString(&value, "/sys/module/%s/parameters/nested",
|
|
kmod[i]);
|
|
if (rc == -2)
|
|
continue;
|
|
if (rc < 0) {
|
|
virResetLastError();
|
|
return false;
|
|
}
|
|
|
|
if (value[0] == 'Y' || value[0] == 'y' || value[0] == '1')
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/* Determine whether '/dev/kvm' is usable as QEMU user:QEMU group. */
|
|
static bool
|
|
virQEMUCapsKVMUsable(virQEMUCapsCachePriv *priv)
|
|
{
|
|
struct stat sb;
|
|
static const char *kvm_device = "/dev/kvm";
|
|
virTristateBool value;
|
|
virTristateBool cached_value = priv->kvmUsable;
|
|
time_t kvm_ctime;
|
|
time_t cached_kvm_ctime = priv->kvmCtime;
|
|
|
|
if (stat(kvm_device, &sb) < 0) {
|
|
if (errno != ENOENT) {
|
|
virReportSystemError(errno,
|
|
_("Failed to stat %1$s"), kvm_device);
|
|
}
|
|
return false;
|
|
}
|
|
kvm_ctime = sb.st_ctime;
|
|
|
|
if (kvm_ctime != cached_kvm_ctime) {
|
|
VIR_DEBUG("%s has changed (%lld vs %lld)", kvm_device,
|
|
(long long)kvm_ctime, (long long)cached_kvm_ctime);
|
|
cached_value = VIR_TRISTATE_BOOL_ABSENT;
|
|
}
|
|
|
|
if (cached_value != VIR_TRISTATE_BOOL_ABSENT)
|
|
return cached_value == VIR_TRISTATE_BOOL_YES;
|
|
|
|
if (virFileAccessibleAs(kvm_device, R_OK | W_OK,
|
|
priv->runUid, priv->runGid) == 0) {
|
|
value = VIR_TRISTATE_BOOL_YES;
|
|
} else {
|
|
value = VIR_TRISTATE_BOOL_NO;
|
|
}
|
|
|
|
/* There is a race window between 'stat' and
|
|
* 'virFileAccessibleAs'. However, since we're only interested in
|
|
* detecting changes *after* the virFileAccessibleAs check, we can
|
|
* neglect this here.
|
|
*/
|
|
priv->kvmCtime = kvm_ctime;
|
|
priv->kvmUsable = value;
|
|
|
|
return value == VIR_TRISTATE_BOOL_YES;
|
|
}
|
|
|
|
|
|
static bool
|
|
virQEMUCapsIsValid(void *data,
|
|
void *privData)
|
|
{
|
|
virQEMUCaps *qemuCaps = data;
|
|
virQEMUCapsCachePriv *priv = privData;
|
|
bool kvmUsable;
|
|
struct stat sb;
|
|
bool kvmSupportsNesting;
|
|
|
|
if (!qemuCaps->invalidation)
|
|
return true;
|
|
|
|
if (!qemuCaps->binary)
|
|
return true;
|
|
|
|
if (virFileExists(QEMU_MODDIR)) {
|
|
if (stat(QEMU_MODDIR, &sb) < 0) {
|
|
VIR_DEBUG("Failed to stat QEMU module directory '%s': %s",
|
|
QEMU_MODDIR,
|
|
g_strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
if (sb.st_mtime != qemuCaps->modDirMtime) {
|
|
VIR_DEBUG("Outdated capabilities for '%s': QEMU modules "
|
|
"directory '%s' changed (%lld vs %lld)",
|
|
qemuCaps->binary, QEMU_MODDIR,
|
|
(long long)sb.st_mtime, (long long)qemuCaps->modDirMtime);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (qemuCaps->libvirtCtime != virGetSelfLastChanged() ||
|
|
qemuCaps->libvirtVersion != LIBVIR_VERSION_NUMBER) {
|
|
VIR_DEBUG("Outdated capabilities for '%s': libvirt changed "
|
|
"(%lld vs %lld, %lu vs %lu)",
|
|
qemuCaps->binary,
|
|
(long long)qemuCaps->libvirtCtime,
|
|
(long long)virGetSelfLastChanged(),
|
|
(unsigned long)qemuCaps->libvirtVersion,
|
|
(unsigned long)LIBVIR_VERSION_NUMBER);
|
|
return false;
|
|
}
|
|
|
|
if (stat(qemuCaps->binary, &sb) < 0) {
|
|
VIR_DEBUG("Failed to stat QEMU binary '%s': %s",
|
|
qemuCaps->binary,
|
|
g_strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
if (sb.st_ctime != qemuCaps->ctime) {
|
|
VIR_DEBUG("Outdated capabilities for '%s': QEMU binary changed "
|
|
"(%lld vs %lld)",
|
|
qemuCaps->binary,
|
|
(long long)sb.st_ctime, (long long)qemuCaps->ctime);
|
|
return false;
|
|
}
|
|
|
|
if (!virQEMUCapsGuestIsNative(priv->hostArch, qemuCaps->arch)) {
|
|
VIR_DEBUG("Guest arch (%s) is not native to host arch (%s), "
|
|
"skipping KVM-related checks",
|
|
virArchToString(qemuCaps->arch),
|
|
virArchToString(priv->hostArch));
|
|
return true;
|
|
}
|
|
|
|
kvmUsable = virQEMUCapsKVMUsable(priv);
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM) &&
|
|
kvmUsable) {
|
|
VIR_DEBUG("KVM was not enabled when probing '%s', "
|
|
"but it should be usable now",
|
|
qemuCaps->binary);
|
|
return false;
|
|
}
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM) &&
|
|
!kvmUsable) {
|
|
VIR_DEBUG("KVM was enabled when probing '%s', "
|
|
"but it is not available now",
|
|
qemuCaps->binary);
|
|
return false;
|
|
}
|
|
|
|
if (virQEMUCapsHaveAccel(qemuCaps)) {
|
|
if (STRNEQ_NULLABLE(priv->hostCPUSignature, qemuCaps->hostCPUSignature)) {
|
|
VIR_DEBUG("Outdated capabilities for '%s': host CPU changed "
|
|
"('%s' vs '%s')",
|
|
qemuCaps->binary,
|
|
priv->hostCPUSignature,
|
|
qemuCaps->hostCPUSignature);
|
|
return false;
|
|
}
|
|
|
|
if (priv->microcodeVersion != qemuCaps->microcodeVersion) {
|
|
VIR_DEBUG("Outdated capabilities for '%s': microcode version "
|
|
"changed (%u vs %u)",
|
|
qemuCaps->binary,
|
|
priv->microcodeVersion,
|
|
qemuCaps->microcodeVersion);
|
|
return false;
|
|
}
|
|
|
|
if (STRNEQ_NULLABLE(priv->kernelVersion, qemuCaps->kernelVersion)) {
|
|
VIR_DEBUG("Outdated capabilities for '%s': kernel version changed "
|
|
"('%s' vs '%s')",
|
|
qemuCaps->binary,
|
|
priv->kernelVersion,
|
|
qemuCaps->kernelVersion);
|
|
return false;
|
|
}
|
|
|
|
if (priv->cpuData &&
|
|
virCPUDataIsIdentical(priv->cpuData, qemuCaps->cpuData) != VIR_CPU_COMPARE_IDENTICAL) {
|
|
VIR_DEBUG("Outdated capabilities for '%s': host cpuid changed",
|
|
qemuCaps->binary);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM)) {
|
|
kvmSupportsNesting = virQEMUCapsKVMSupportsNesting();
|
|
if (kvmSupportsNesting != qemuCaps->kvmSupportsNesting) {
|
|
VIR_DEBUG("Outdated capabilities for '%s': kvm kernel nested "
|
|
"value changed from %d",
|
|
qemuCaps->binary, qemuCaps->kvmSupportsNesting);
|
|
return false;
|
|
}
|
|
|
|
if (virQEMUCapsKVMSupportsSecureGuest() != qemuCaps->kvmSupportsSecureGuest) {
|
|
VIR_DEBUG("Outdated capabilities for '%s': kvm kernel secure guest "
|
|
"value changed from %d",
|
|
qemuCaps->binary, qemuCaps->kvmSupportsSecureGuest);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsInitQMPArch:
|
|
* @qemuCaps: QEMU capabilities
|
|
* @mon: QEMU monitor
|
|
*
|
|
* Initialize the architecture for @qemuCaps by asking @mon.
|
|
*
|
|
* Returns: 0 on success, <0 on failure
|
|
*/
|
|
int
|
|
virQEMUCapsInitQMPArch(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
g_autofree char *archstr = NULL;
|
|
|
|
if (!(archstr = qemuMonitorGetTargetArch(mon)))
|
|
return -1;
|
|
|
|
if ((qemuCaps->arch = virQEMUCapsArchFromString(archstr)) == VIR_ARCH_NONE) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Unknown QEMU arch %1$s"), archstr);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsInitQMPVersionCaps:
|
|
* @qemuCaps: QEMU capabilities
|
|
*
|
|
* Add all QEMU capabilities based on version of QEMU.
|
|
*/
|
|
static void
|
|
virQEMUCapsInitQMPVersionCaps(virQEMUCaps *qemuCaps G_GNUC_UNUSED)
|
|
{
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsInitProcessCapsInterlock:
|
|
* @qemuCaps: QEMU capabilities
|
|
*
|
|
* A capability which requires a different capability being present in order
|
|
* for libvirt to be able to drive it properly should be processed here.
|
|
*/
|
|
void
|
|
virQEMUCapsInitProcessCapsInterlock(virQEMUCaps *qemuCaps)
|
|
{
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_BLOCKDEV_REOPEN) &&
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_MIGRATION_PARAM_BLOCK_BITMAP_MAPPING))
|
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_INCREMENTAL_BACKUP);
|
|
|
|
/* The -compat qemu command line argument is implemented using a newer
|
|
* method which doesn't show up in query-command-line-options. As we'll use
|
|
* it only for development and testing purposes we can base the capability
|
|
* on a not entirely related witness. */
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_JSON))
|
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_COMPAT_DEPRECATED);
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsInitProcessCaps:
|
|
* @qemuCaps: QEMU capabilities
|
|
*
|
|
* Some capability bits are enabled or disabled according to specific logic.
|
|
* This function collects all capability processing after the capabilities
|
|
* are detected.
|
|
*/
|
|
static void
|
|
virQEMUCapsInitProcessCaps(virQEMUCaps *qemuCaps)
|
|
{
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_UNAVAILABLE_FEATURES))
|
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_CANONICAL_CPU_FEATURES);
|
|
|
|
/* We can't probe "esp" as a type via virQEMUCapsObjectTypes
|
|
* array as it is only usable when builtin to the machine type
|
|
*/
|
|
if (qemuCaps->arch == VIR_ARCH_SPARC ||
|
|
qemuCaps->arch == VIR_ARCH_M68K ||
|
|
qemuCaps->arch == VIR_ARCH_MIPS)
|
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_SCSI_NCR53C90);
|
|
|
|
virQEMUCapsInitProcessCapsInterlock(qemuCaps);
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsProbeQMPSchemaCapabilities(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
virJSONValue *schemareply;
|
|
g_autoptr(GHashTable) schema = NULL;
|
|
size_t i;
|
|
|
|
if (!(schemareply = qemuMonitorQueryQMPSchema(mon)))
|
|
return -1;
|
|
|
|
if (!(schema = virQEMUQAPISchemaConvert(schemareply)))
|
|
return -1;
|
|
schemareply = NULL;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(virQEMUCapsQMPSchemaQueries); i++) {
|
|
struct virQEMUCapsStringFlags *entry = virQEMUCapsQMPSchemaQueries + i;
|
|
|
|
if (virQEMUQAPISchemaPathExists(entry->value, schema))
|
|
virQEMUCapsSet(qemuCaps, entry->flag);
|
|
}
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(virQEMUCapsCommands); i++) {
|
|
struct virQEMUCapsStringFlags *cmd = virQEMUCapsCommands + i;
|
|
|
|
if (virQEMUQAPISchemaPathExists(cmd->value, schema))
|
|
virQEMUCapsSet(qemuCaps, cmd->flag);
|
|
}
|
|
|
|
if (virQEMUQAPISchemaPathExists("block-commit/arg-type/backing-mask-protocol", schema) &&
|
|
virQEMUQAPISchemaPathExists("block-stream/arg-type/backing-mask-protocol", schema))
|
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_BLOCKJOB_BACKING_MASK_PROTOCOL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define QEMU_MIN_MAJOR 5
|
|
#define QEMU_MIN_MINOR 2
|
|
#define QEMU_MIN_MICRO 0
|
|
|
|
virDomainVirtType
|
|
virQEMUCapsGetVirtType(virQEMUCaps *qemuCaps)
|
|
{
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM))
|
|
return VIR_DOMAIN_VIRT_KVM;
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_HVF))
|
|
return VIR_DOMAIN_VIRT_HVF;
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_TCG))
|
|
return VIR_DOMAIN_VIRT_QEMU;
|
|
|
|
return VIR_DOMAIN_VIRT_NONE;
|
|
}
|
|
|
|
int
|
|
virQEMUCapsInitQMPMonitor(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
int major, minor, micro;
|
|
g_autofree char *package = NULL;
|
|
virQEMUCapsAccel *accel;
|
|
virDomainVirtType type;
|
|
|
|
/* @mon is supposed to be locked by callee */
|
|
|
|
if (qemuMonitorGetVersion(mon, &major, &minor, µ, &package) < 0)
|
|
return -1;
|
|
|
|
VIR_DEBUG("Got version %d.%d.%d (%s)",
|
|
major, minor, micro, NULLSTR(package));
|
|
|
|
if (major < QEMU_MIN_MAJOR ||
|
|
(major == QEMU_MIN_MAJOR && minor < QEMU_MIN_MINOR)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("QEMU version >= %1$d.%2$d.%3$d is required, but %4$d.%5$d.%6$d found"),
|
|
QEMU_MIN_MAJOR, QEMU_MIN_MINOR, QEMU_MIN_MICRO,
|
|
major, minor, micro);
|
|
return -1;
|
|
}
|
|
|
|
qemuCaps->version = major * 1000000 + minor * 1000 + micro;
|
|
qemuCaps->package = g_steal_pointer(&package);
|
|
|
|
if (virQEMUCapsInitQMPArch(qemuCaps, mon) < 0)
|
|
return -1;
|
|
|
|
/* initiate all capabilities based on qemu version */
|
|
virQEMUCapsInitQMPVersionCaps(qemuCaps);
|
|
|
|
if (virQEMUCapsProbeQMPSchemaCapabilities(qemuCaps, mon) < 0)
|
|
return -1;
|
|
|
|
/* Some capabilities may differ depending on KVM state */
|
|
if (virQEMUCapsProbeQMPKVMState(qemuCaps, mon) < 0)
|
|
return -1;
|
|
|
|
if (virQEMUCapsProbeHVF(qemuCaps))
|
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_HVF);
|
|
|
|
type = virQEMUCapsGetVirtType(qemuCaps);
|
|
accel = virQEMUCapsGetAccel(qemuCaps, type);
|
|
|
|
if (virQEMUCapsProbeQMPObjectTypes(qemuCaps, mon) < 0)
|
|
return -1;
|
|
if (virQEMUCapsProbeQMPDeviceProperties(qemuCaps, mon) < 0)
|
|
return -1;
|
|
if (virQEMUCapsProbeQMPObjectProperties(qemuCaps, mon) < 0)
|
|
return -1;
|
|
if (virQEMUCapsProbeQMPMachineTypes(qemuCaps, type, mon) < 0)
|
|
return -1;
|
|
if (virQEMUCapsProbeQMPMachineProps(qemuCaps, type, mon) < 0)
|
|
return -1;
|
|
if (virQEMUCapsProbeQMPCPUDefinitions(qemuCaps, accel, mon) < 0)
|
|
return -1;
|
|
if (virQEMUCapsProbeQMPTPM(qemuCaps, mon) < 0)
|
|
return -1;
|
|
if (virQEMUCapsProbeQMPCommandLine(qemuCaps, mon) < 0)
|
|
return -1;
|
|
if (virQEMUCapsProbeQMPMigrationCapabilities(qemuCaps, mon) < 0)
|
|
return -1;
|
|
if (virQEMUCapsProbeQMPGICCapabilities(qemuCaps, mon) < 0)
|
|
return -1;
|
|
if (virQEMUCapsProbeQMPSEVCapabilities(qemuCaps, mon) < 0)
|
|
return -1;
|
|
if (virQEMUCapsProbeQMPSGXCapabilities(qemuCaps, mon) < 0)
|
|
return -1;
|
|
|
|
virQEMUCapsInitProcessCaps(qemuCaps);
|
|
|
|
/* The following probes rely on other previously probed capabilities.
|
|
* No capabilities bits should be set below this point. */
|
|
|
|
if (virQEMUCapsProbeQMPHostCPU(qemuCaps, accel, mon, type) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virQEMUCapsInitQMPMonitorTCG(virQEMUCaps *qemuCaps,
|
|
qemuMonitor *mon)
|
|
{
|
|
virQEMUCapsAccel *accel = virQEMUCapsGetAccel(qemuCaps, VIR_DOMAIN_VIRT_QEMU);
|
|
|
|
if (virQEMUCapsProbeQMPCPUDefinitions(qemuCaps, accel, mon) < 0)
|
|
return -1;
|
|
|
|
if (virQEMUCapsProbeQMPHostCPU(qemuCaps, accel, mon, VIR_DOMAIN_VIRT_QEMU) < 0)
|
|
return -1;
|
|
|
|
if (virQEMUCapsProbeQMPMachineTypes(qemuCaps, VIR_DOMAIN_VIRT_QEMU, mon) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define MESSAGE_ID_CAPS_PROBE_FAILURE "8ae2f3fb-2dbe-498e-8fbd-012d40afa361"
|
|
|
|
static void
|
|
virQEMUCapsLogProbeFailure(const char *binary)
|
|
{
|
|
virLogMetadata meta[] = {
|
|
{ .key = "MESSAGE_ID", .s = MESSAGE_ID_CAPS_PROBE_FAILURE, .iv = 0 },
|
|
{ .key = "LIBVIRT_QEMU_BINARY", .s = binary, .iv = 0 },
|
|
{ .key = NULL },
|
|
};
|
|
|
|
virLogMessage(&virLogSelf,
|
|
VIR_LOG_WARN,
|
|
__FILE__, __LINE__, __func__,
|
|
meta,
|
|
_("Failed to probe capabilities for %1$s: %2$s"),
|
|
binary, virGetLastErrorMessage());
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsInitQMPSingle(virQEMUCaps *qemuCaps,
|
|
const char *libDir,
|
|
uid_t runUid,
|
|
gid_t runGid,
|
|
bool onlyTCG)
|
|
{
|
|
g_autoptr(qemuProcessQMP) proc = NULL;
|
|
int ret = -1;
|
|
|
|
if (!(proc = qemuProcessQMPNew(qemuCaps->binary, libDir,
|
|
runUid, runGid, onlyTCG)))
|
|
goto cleanup;
|
|
|
|
if (qemuProcessQMPStart(proc) < 0)
|
|
goto cleanup;
|
|
|
|
if (onlyTCG)
|
|
ret = virQEMUCapsInitQMPMonitorTCG(qemuCaps, proc->mon);
|
|
else
|
|
ret = virQEMUCapsInitQMPMonitor(qemuCaps, proc->mon);
|
|
|
|
cleanup:
|
|
if (ret < 0)
|
|
virQEMUCapsLogProbeFailure(qemuCaps->binary);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsInitQMP(virQEMUCaps *qemuCaps,
|
|
const char *libDir,
|
|
uid_t runUid,
|
|
gid_t runGid)
|
|
{
|
|
if (virQEMUCapsInitQMPSingle(qemuCaps, libDir, runUid, runGid, false) < 0)
|
|
return -1;
|
|
|
|
/*
|
|
* If acceleration was enabled during the first probe, we need to explicitly
|
|
* probe for TCG capabilities by asking the same binary again and turning KVM
|
|
* off.
|
|
*/
|
|
if (virQEMUCapsHaveAccel(qemuCaps) &&
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_TCG) &&
|
|
virQEMUCapsInitQMPSingle(qemuCaps, libDir, runUid, runGid, true) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
virQEMUCaps *
|
|
virQEMUCapsNewForBinaryInternal(virArch hostArch,
|
|
const char *binary,
|
|
const char *libDir,
|
|
uid_t runUid,
|
|
gid_t runGid,
|
|
const char *hostCPUSignature,
|
|
unsigned int microcodeVersion,
|
|
const char *kernelVersion,
|
|
virCPUData* cpuData)
|
|
{
|
|
g_autoptr(virQEMUCaps) qemuCaps = virQEMUCapsNewBinary(binary);
|
|
struct stat sb;
|
|
|
|
/* We would also want to check faccessat if we cared about ACLs,
|
|
* but we don't. */
|
|
if (stat(binary, &sb) < 0) {
|
|
virReportSystemError(errno, _("Cannot check QEMU binary %1$s"),
|
|
binary);
|
|
return NULL;
|
|
}
|
|
qemuCaps->ctime = sb.st_ctime;
|
|
|
|
/* Make sure the binary we are about to try exec'ing exists.
|
|
* Technically we could catch the exec() failure, but that's
|
|
* in a sub-process so it's hard to feed back a useful error.
|
|
*/
|
|
if (!virFileIsExecutable(binary)) {
|
|
virReportSystemError(errno, _("QEMU binary %1$s is not executable"),
|
|
binary);
|
|
return NULL;
|
|
}
|
|
|
|
if (virFileExists(QEMU_MODDIR)) {
|
|
if (stat(QEMU_MODDIR, &sb) < 0) {
|
|
virReportSystemError(errno, _("Cannot check QEMU module directory %1$s"),
|
|
QEMU_MODDIR);
|
|
return NULL;
|
|
}
|
|
qemuCaps->modDirMtime = sb.st_mtime;
|
|
}
|
|
|
|
if (virQEMUCapsInitQMP(qemuCaps, libDir, runUid, runGid) < 0)
|
|
return NULL;
|
|
|
|
qemuCaps->libvirtCtime = virGetSelfLastChanged();
|
|
qemuCaps->libvirtVersion = LIBVIR_VERSION_NUMBER;
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM))
|
|
virQEMUCapsInitHostCPUModel(qemuCaps, hostArch, VIR_DOMAIN_VIRT_KVM);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_HVF))
|
|
virQEMUCapsInitHostCPUModel(qemuCaps, hostArch, VIR_DOMAIN_VIRT_HVF);
|
|
virQEMUCapsInitHostCPUModel(qemuCaps, hostArch, VIR_DOMAIN_VIRT_QEMU);
|
|
|
|
if (virQEMUCapsHaveAccel(qemuCaps)) {
|
|
qemuCaps->hostCPUSignature = g_strdup(hostCPUSignature);
|
|
qemuCaps->microcodeVersion = microcodeVersion;
|
|
qemuCaps->cpuData = virCPUDataNewCopy(cpuData);
|
|
|
|
qemuCaps->kernelVersion = g_strdup(kernelVersion);
|
|
}
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM)) {
|
|
qemuCaps->kvmSupportsNesting = virQEMUCapsKVMSupportsNesting();
|
|
|
|
qemuCaps->kvmSupportsSecureGuest = virQEMUCapsKVMSupportsSecureGuest();
|
|
}
|
|
|
|
return g_steal_pointer(&qemuCaps);
|
|
}
|
|
|
|
static void *
|
|
virQEMUCapsNewData(const char *binary,
|
|
void *privData)
|
|
{
|
|
virQEMUCapsCachePriv *priv = privData;
|
|
|
|
return virQEMUCapsNewForBinaryInternal(priv->hostArch,
|
|
binary,
|
|
priv->libDir,
|
|
priv->runUid,
|
|
priv->runGid,
|
|
priv->hostCPUSignature,
|
|
virHostCPUGetMicrocodeVersion(priv->hostArch),
|
|
priv->kernelVersion,
|
|
priv->cpuData);
|
|
}
|
|
|
|
|
|
static void *
|
|
virQEMUCapsLoadFile(const char *filename,
|
|
const char *binary,
|
|
void *privData,
|
|
bool *outdated)
|
|
{
|
|
g_autoptr(virQEMUCaps) qemuCaps = virQEMUCapsNewBinary(binary);
|
|
virQEMUCapsCachePriv *priv = privData;
|
|
int ret;
|
|
|
|
ret = virQEMUCapsLoadCache(priv->hostArch, qemuCaps, filename, false);
|
|
if (ret < 0)
|
|
return NULL;
|
|
if (ret == 1) {
|
|
*outdated = true;
|
|
return NULL;
|
|
}
|
|
|
|
return g_steal_pointer(&qemuCaps);
|
|
}
|
|
|
|
|
|
virFileCacheHandlers qemuCapsCacheHandlers = {
|
|
.isValid = virQEMUCapsIsValid,
|
|
.newData = virQEMUCapsNewData,
|
|
.loadFile = virQEMUCapsLoadFile,
|
|
.saveFile = virQEMUCapsSaveFile,
|
|
.privFree = virQEMUCapsCachePrivFree,
|
|
};
|
|
|
|
|
|
virFileCache *
|
|
virQEMUCapsCacheNew(const char *libDir,
|
|
const char *cacheDir,
|
|
uid_t runUid,
|
|
gid_t runGid)
|
|
{
|
|
g_autofree char *capsCacheDir = NULL;
|
|
virFileCache *cache = NULL;
|
|
virQEMUCapsCachePriv *priv = NULL;
|
|
struct utsname uts;
|
|
|
|
capsCacheDir = g_strdup_printf("%s/capabilities", cacheDir);
|
|
|
|
if (!(cache = virFileCacheNew(capsCacheDir, "xml", &qemuCapsCacheHandlers)))
|
|
goto error;
|
|
|
|
priv = g_new0(virQEMUCapsCachePriv, 1);
|
|
virFileCacheSetPriv(cache, priv);
|
|
|
|
priv->libDir = g_strdup(libDir);
|
|
|
|
priv->hostArch = virArchFromHost();
|
|
|
|
if (virHostCPUGetSignature(&priv->hostCPUSignature) < 0)
|
|
goto error;
|
|
|
|
priv->runUid = runUid;
|
|
priv->runGid = runGid;
|
|
priv->kvmUsable = VIR_TRISTATE_BOOL_ABSENT;
|
|
|
|
if (uname(&uts) == 0)
|
|
priv->kernelVersion = g_strdup_printf("%s %s", uts.release, uts.version);
|
|
|
|
priv->cpuData = virCPUDataGetHost();
|
|
return cache;
|
|
|
|
error:
|
|
virObjectUnref(cache);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
virQEMUCaps *
|
|
virQEMUCapsCacheLookup(virFileCache *cache,
|
|
const char *binary)
|
|
{
|
|
virQEMUCapsCachePriv *priv = virFileCacheGetPriv(cache);
|
|
virQEMUCaps *ret = NULL;
|
|
|
|
priv->microcodeVersion = virHostCPUGetMicrocodeVersion(priv->hostArch);
|
|
|
|
ret = virFileCacheLookup(cache, binary);
|
|
|
|
VIR_DEBUG("Returning caps %p for %s", ret, binary);
|
|
return ret;
|
|
}
|
|
|
|
|
|
virQEMUCaps *
|
|
virQEMUCapsCacheLookupCopy(virFileCache *cache,
|
|
const char *binary)
|
|
{
|
|
virQEMUCaps *qemuCaps = virQEMUCapsCacheLookup(cache, binary);
|
|
virQEMUCaps *ret;
|
|
|
|
if (!qemuCaps)
|
|
return NULL;
|
|
|
|
ret = virQEMUCapsNewCopy(qemuCaps);
|
|
virObjectUnref(qemuCaps);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsCacheLookupDefault:
|
|
* @cache: QEMU capabilities cache
|
|
* @binary: optional path to QEMU binary
|
|
* @archStr: optional guest architecture
|
|
* @virttypeStr: optional virt type
|
|
* @machine: optional machine type
|
|
* @retArch: if non-NULL, guest architecture will be returned here
|
|
* @retVirttype: if non-NULL, domain virt type will be returned here
|
|
* @retMachine: if non-NULL, canonical machine type will be returned here
|
|
*
|
|
* Looks up the QEMU binary specified by @binary and @archStr, checks it can
|
|
* provide the required @virttypeStr and @machine and returns its capabilities.
|
|
* Sensible defaults are used for any argument which is NULL (the function can
|
|
* even be called with all NULL arguments).
|
|
*
|
|
* Returns QEMU capabilities matching the requirements, NULL on error.
|
|
*/
|
|
virQEMUCaps *
|
|
virQEMUCapsCacheLookupDefault(virFileCache *cache,
|
|
const char *binary,
|
|
const char *archStr,
|
|
const char *virttypeStr,
|
|
const char *machine,
|
|
virArch *retArch,
|
|
virDomainVirtType *retVirttype,
|
|
const char **retMachine)
|
|
{
|
|
int virttype = VIR_DOMAIN_VIRT_NONE;
|
|
virArch hostarch = virArchFromHost();
|
|
virArch arch = hostarch;
|
|
virDomainVirtType capsType;
|
|
g_autoptr(virQEMUCaps) qemuCaps = NULL;
|
|
virArch arch_from_caps;
|
|
g_autofree char *probedbinary = NULL;
|
|
|
|
if (virttypeStr &&
|
|
(virttype = virDomainVirtTypeFromString(virttypeStr)) < 0) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("unknown virttype: %1$s"), virttypeStr);
|
|
return NULL;
|
|
}
|
|
|
|
if (archStr &&
|
|
(arch = virArchFromString(archStr)) == VIR_ARCH_NONE) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("unknown architecture: %1$s"), archStr);
|
|
return NULL;
|
|
}
|
|
|
|
if (!binary) {
|
|
probedbinary = virQEMUCapsGetDefaultEmulator(hostarch, arch);
|
|
binary = probedbinary;
|
|
}
|
|
if (!binary) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("unable to find any emulator to serve '%1$s' architecture"),
|
|
archStr);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(qemuCaps = virQEMUCapsCacheLookup(cache, binary)))
|
|
return NULL;
|
|
|
|
arch_from_caps = virQEMUCapsGetArch(qemuCaps);
|
|
|
|
if (arch_from_caps != arch &&
|
|
!((ARCH_IS_X86(arch) && ARCH_IS_X86(arch_from_caps)) ||
|
|
(ARCH_IS_PPC(arch) && ARCH_IS_PPC(arch_from_caps)) ||
|
|
(ARCH_IS_ARM(arch) && ARCH_IS_ARM(arch_from_caps)) ||
|
|
(ARCH_IS_S390(arch) && ARCH_IS_S390(arch_from_caps)))) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("architecture from emulator '%1$s' doesn't match given architecture '%2$s'"),
|
|
virArchToString(arch_from_caps),
|
|
virArchToString(arch));
|
|
return NULL;
|
|
}
|
|
|
|
capsType = virQEMUCapsGetVirtType(qemuCaps);
|
|
|
|
if (virttype == VIR_DOMAIN_VIRT_NONE)
|
|
virttype = capsType;
|
|
|
|
if (virQEMUCapsTypeIsAccelerated(virttype) && capsType == VIR_DOMAIN_VIRT_QEMU) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("the accel '%1$s' is not supported by '%2$s' on this host"),
|
|
virQEMUCapsAccelStr(virttype), binary);
|
|
return NULL;
|
|
}
|
|
|
|
if (machine) {
|
|
/* Turn @machine into canonical name */
|
|
machine = virQEMUCapsGetCanonicalMachine(qemuCaps, virttype, machine);
|
|
|
|
if (!virQEMUCapsIsMachineSupported(qemuCaps, virttype, machine)) {
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
_("the machine '%1$s' is not supported by emulator '%2$s'"),
|
|
machine, binary);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
machine = virQEMUCapsGetPreferredMachine(qemuCaps, virttype);
|
|
}
|
|
|
|
if (retArch)
|
|
*retArch = arch;
|
|
if (retVirttype)
|
|
*retVirttype = virttype;
|
|
if (retMachine)
|
|
*retMachine = machine;
|
|
|
|
return g_steal_pointer(&qemuCaps);
|
|
}
|
|
|
|
bool
|
|
virQEMUCapsSupportsVmport(virQEMUCaps *qemuCaps,
|
|
const virDomainDef *def)
|
|
{
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_VMPORT_OPT))
|
|
return false;
|
|
|
|
return qemuDomainIsI440FX(def) ||
|
|
qemuDomainIsQ35(def) ||
|
|
STREQ(def->os.machine, "isapc");
|
|
}
|
|
|
|
bool
|
|
virQEMUCapsSupportsI8042(virQEMUCaps *qemuCaps,
|
|
const virDomainDef *def)
|
|
{
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_I8042))
|
|
return false;
|
|
|
|
return qemuDomainIsI440FX(def) ||
|
|
qemuDomainIsQ35(def) ||
|
|
qemuDomainIsXenFV(def) ||
|
|
STREQ(def->os.machine, "isapc");
|
|
}
|
|
|
|
bool
|
|
virQEMUCapsSupportsI8042Toggle(virQEMUCaps *qemuCaps,
|
|
const char *machine,
|
|
const virArch arch)
|
|
{
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_I8042_OPT))
|
|
return false;
|
|
|
|
return qemuDomainMachineIsI440FX(machine, arch) ||
|
|
qemuDomainMachineIsQ35(machine, arch) ||
|
|
qemuDomainMachineIsXenFV(machine, arch) ||
|
|
STREQ(machine, "isapc");
|
|
}
|
|
|
|
|
|
/*
|
|
* The preferred machine to use if none is listed explicitly
|
|
* Note that this may differ from QEMU's own default machine
|
|
*/
|
|
const char *
|
|
virQEMUCapsGetPreferredMachine(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType)
|
|
{
|
|
virQEMUCapsAccel *accel = virQEMUCapsGetAccel(qemuCaps, virtType);
|
|
|
|
if (!accel->nmachineTypes)
|
|
return NULL;
|
|
return accel->machineTypes[0].name;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsFillDomainLoaderCaps(virDomainCapsLoader *capsLoader,
|
|
bool secure,
|
|
virFirmware **firmwares,
|
|
size_t nfirmwares)
|
|
{
|
|
size_t i;
|
|
|
|
capsLoader->supported = VIR_TRISTATE_BOOL_YES;
|
|
capsLoader->type.report = true;
|
|
capsLoader->readonly.report = true;
|
|
capsLoader->secure.report = true;
|
|
|
|
capsLoader->values.values = g_new0(char *, nfirmwares);
|
|
|
|
for (i = 0; i < nfirmwares; i++) {
|
|
const char *filename = firmwares[i]->name;
|
|
size_t j;
|
|
|
|
if (!virFileExists(filename)) {
|
|
VIR_DEBUG("loader filename=%s does not exist", filename);
|
|
continue;
|
|
}
|
|
|
|
/* Put only unique FW images onto the list */
|
|
for (j = 0; j < capsLoader->values.nvalues; j++) {
|
|
if (STREQ(filename, capsLoader->values.values[j]))
|
|
break;
|
|
}
|
|
|
|
if (j != capsLoader->values.nvalues)
|
|
continue;
|
|
|
|
capsLoader->values.values[capsLoader->values.nvalues] = g_strdup(filename);
|
|
capsLoader->values.nvalues++;
|
|
}
|
|
|
|
VIR_DOMAIN_CAPS_ENUM_SET(capsLoader->type,
|
|
VIR_DOMAIN_LOADER_TYPE_ROM);
|
|
|
|
VIR_DOMAIN_CAPS_ENUM_SET(capsLoader->type,
|
|
VIR_DOMAIN_LOADER_TYPE_PFLASH);
|
|
|
|
|
|
VIR_DOMAIN_CAPS_ENUM_SET(capsLoader->readonly,
|
|
VIR_TRISTATE_BOOL_YES,
|
|
VIR_TRISTATE_BOOL_NO);
|
|
|
|
VIR_DOMAIN_CAPS_ENUM_SET(capsLoader->secure,
|
|
VIR_TRISTATE_BOOL_NO);
|
|
|
|
if (secure)
|
|
VIR_DOMAIN_CAPS_ENUM_SET(capsLoader->secure,
|
|
VIR_TRISTATE_BOOL_YES);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virQEMUCapsFillDomainOSCaps(virDomainCapsOS *os,
|
|
const char *machine,
|
|
virArch arch,
|
|
bool privileged,
|
|
virFirmware **firmwares,
|
|
size_t nfirmwares)
|
|
{
|
|
virDomainCapsLoader *capsLoader = &os->loader;
|
|
uint64_t autoFirmwares = 0;
|
|
bool secure = false;
|
|
virFirmware **firmwaresAlt = NULL;
|
|
size_t nfirmwaresAlt = 0;
|
|
int ret = -1;
|
|
|
|
os->supported = VIR_TRISTATE_BOOL_YES;
|
|
os->firmware.report = true;
|
|
|
|
if (qemuFirmwareGetSupported(machine, arch, privileged,
|
|
&autoFirmwares, &secure,
|
|
&firmwaresAlt, &nfirmwaresAlt) < 0)
|
|
return -1;
|
|
|
|
if (autoFirmwares & (1ULL << VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(os->firmware, VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS);
|
|
if (autoFirmwares & (1ULL << VIR_DOMAIN_OS_DEF_FIRMWARE_EFI))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(os->firmware, VIR_DOMAIN_OS_DEF_FIRMWARE_EFI);
|
|
|
|
if (virQEMUCapsFillDomainLoaderCaps(capsLoader, secure,
|
|
firmwaresAlt ? firmwaresAlt : firmwares,
|
|
firmwaresAlt ? nfirmwaresAlt : nfirmwares) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virFirmwareFreeList(firmwaresAlt, nfirmwaresAlt);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsFillDomainCPUCaps(virQEMUCaps *qemuCaps,
|
|
virArch hostarch,
|
|
virDomainCaps *domCaps)
|
|
{
|
|
if (virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, domCaps->virttype,
|
|
VIR_CPU_MODE_HOST_PASSTHROUGH,
|
|
domCaps->machine)) {
|
|
domCaps->cpu.hostPassthrough = true;
|
|
|
|
domCaps->cpu.hostPassthroughMigratable.report = true;
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_MIGRATABLE)) {
|
|
VIR_DOMAIN_CAPS_ENUM_SET(domCaps->cpu.hostPassthroughMigratable,
|
|
VIR_TRISTATE_SWITCH_ON);
|
|
}
|
|
VIR_DOMAIN_CAPS_ENUM_SET(domCaps->cpu.hostPassthroughMigratable,
|
|
VIR_TRISTATE_SWITCH_OFF);
|
|
}
|
|
|
|
if (virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, domCaps->virttype,
|
|
VIR_CPU_MODE_MAXIMUM,
|
|
domCaps->machine)) {
|
|
domCaps->cpu.maximum = true;
|
|
|
|
domCaps->cpu.maximumMigratable.report = true;
|
|
VIR_DOMAIN_CAPS_ENUM_SET(domCaps->cpu.maximumMigratable,
|
|
VIR_TRISTATE_SWITCH_ON);
|
|
VIR_DOMAIN_CAPS_ENUM_SET(domCaps->cpu.maximumMigratable,
|
|
VIR_TRISTATE_SWITCH_OFF);
|
|
}
|
|
|
|
if (virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, domCaps->virttype,
|
|
VIR_CPU_MODE_HOST_MODEL,
|
|
domCaps->machine)) {
|
|
virCPUDef *cpu = virQEMUCapsGetHostModel(qemuCaps, domCaps->virttype,
|
|
VIR_QEMU_CAPS_HOST_CPU_REPORTED);
|
|
domCaps->cpu.hostModel = virCPUDefCopy(cpu);
|
|
domCaps->cpu.hostModel->addr = virQEMUCapsGetHostPhysAddr(qemuCaps,
|
|
domCaps->virttype);
|
|
}
|
|
|
|
if (virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, domCaps->virttype,
|
|
VIR_CPU_MODE_CUSTOM,
|
|
domCaps->machine)) {
|
|
const char *forbidden[] = { "host", NULL };
|
|
g_auto(GStrv) models = NULL;
|
|
|
|
if (virCPUGetModels(domCaps->arch, &models) >= 0) {
|
|
domCaps->cpu.custom = virQEMUCapsGetCPUModels(qemuCaps,
|
|
domCaps->virttype,
|
|
(const char **)models,
|
|
forbidden);
|
|
} else {
|
|
domCaps->cpu.custom = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
struct virQEMUCapsDomainFeatureCapabilityTuple {
|
|
virDomainCapsFeature domcap;
|
|
virQEMUCapsFlags qemucap;
|
|
};
|
|
|
|
/**
|
|
* This maps the qemu features to the entries in <features> of the domain
|
|
* capability XML. Use QEMU_CAPS_LAST to always enable a domain feature.
|
|
* */
|
|
static const struct virQEMUCapsDomainFeatureCapabilityTuple domCapsTuples[] = {
|
|
{ VIR_DOMAIN_CAPS_FEATURE_IOTHREADS, QEMU_CAPS_LAST },
|
|
{ VIR_DOMAIN_CAPS_FEATURE_VMCOREINFO, QEMU_CAPS_DEVICE_VMCOREINFO },
|
|
{ VIR_DOMAIN_CAPS_FEATURE_GENID, QEMU_CAPS_DEVICE_VMGENID },
|
|
{ VIR_DOMAIN_CAPS_FEATURE_BACKING_STORE_INPUT, QEMU_CAPS_LAST },
|
|
{ VIR_DOMAIN_CAPS_FEATURE_BACKUP, QEMU_CAPS_INCREMENTAL_BACKUP },
|
|
{ VIR_DOMAIN_CAPS_FEATURE_ASYNC_TEARDOWN, QEMU_CAPS_RUN_WITH_ASYNC_TEARDOWN },
|
|
};
|
|
|
|
|
|
static void
|
|
virQEMUCapsFillDomainFeaturesFromQEMUCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCaps *domCaps)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(domCapsTuples); i++) {
|
|
if (virQEMUCapsGet(qemuCaps, domCapsTuples[i].qemucap) ||
|
|
domCapsTuples[i].qemucap == QEMU_CAPS_LAST)
|
|
domCaps->features[domCapsTuples[i].domcap] = VIR_TRISTATE_BOOL_YES;
|
|
else
|
|
domCaps->features[domCapsTuples[i].domcap] = VIR_TRISTATE_BOOL_NO;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsFillDomainMemoryBackingCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCapsMemoryBacking *memoryBacking)
|
|
{
|
|
memoryBacking->supported = VIR_TRISTATE_BOOL_YES;
|
|
memoryBacking->sourceType.report = true;
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_MEMFD))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(memoryBacking->sourceType,
|
|
VIR_DOMAIN_MEMORY_SOURCE_MEMFD);
|
|
|
|
VIR_DOMAIN_CAPS_ENUM_SET(memoryBacking->sourceType,
|
|
VIR_DOMAIN_MEMORY_SOURCE_ANONYMOUS,
|
|
VIR_DOMAIN_MEMORY_SOURCE_FILE);
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsFillDomainDeviceDiskCaps(virQEMUCaps *qemuCaps,
|
|
const char *machine,
|
|
virDomainCapsDeviceDisk *disk)
|
|
{
|
|
disk->supported = VIR_TRISTATE_BOOL_YES;
|
|
disk->diskDevice.report = true;
|
|
disk->bus.report = true;
|
|
disk->model.report = true;
|
|
|
|
/* QEMU supports all of these */
|
|
VIR_DOMAIN_CAPS_ENUM_SET(disk->diskDevice,
|
|
VIR_DOMAIN_DISK_DEVICE_DISK,
|
|
VIR_DOMAIN_DISK_DEVICE_CDROM,
|
|
VIR_DOMAIN_DISK_DEVICE_LUN);
|
|
|
|
/* PowerPC pseries based VMs do not support floppy device */
|
|
if (!qemuDomainMachineIsPSeries(machine, qemuCaps->arch)) {
|
|
VIR_DOMAIN_CAPS_ENUM_SET(disk->diskDevice, VIR_DOMAIN_DISK_DEVICE_FLOPPY);
|
|
VIR_DOMAIN_CAPS_ENUM_SET(disk->bus, VIR_DOMAIN_DISK_BUS_FDC);
|
|
}
|
|
|
|
if (qemuDomainMachineHasBuiltinIDE(machine, qemuCaps->arch))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(disk->bus, VIR_DOMAIN_DISK_BUS_IDE);
|
|
|
|
VIR_DOMAIN_CAPS_ENUM_SET(disk->bus,
|
|
VIR_DOMAIN_DISK_BUS_SCSI,
|
|
VIR_DOMAIN_DISK_BUS_VIRTIO,
|
|
/* VIR_DOMAIN_DISK_BUS_SD */);
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(disk->bus, VIR_DOMAIN_DISK_BUS_USB);
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_AHCI))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(disk->bus, VIR_DOMAIN_DISK_BUS_SATA);
|
|
|
|
/* disk->model values */
|
|
VIR_DOMAIN_CAPS_ENUM_SET(disk->model, VIR_DOMAIN_DISK_MODEL_VIRTIO);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL)) {
|
|
VIR_DOMAIN_CAPS_ENUM_SET(disk->model,
|
|
VIR_DOMAIN_DISK_MODEL_VIRTIO_TRANSITIONAL);
|
|
VIR_DOMAIN_CAPS_ENUM_SET(disk->model,
|
|
VIR_DOMAIN_DISK_MODEL_VIRTIO_NON_TRANSITIONAL);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsFillDomainDeviceGraphicsCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCapsDeviceGraphics *dev)
|
|
{
|
|
dev->supported = VIR_TRISTATE_BOOL_YES;
|
|
dev->type.report = true;
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SDL))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_SDL);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_VNC);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SPICE))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_SPICE);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_EGL_HEADLESS))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DISPLAY_DBUS))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_DBUS);
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsFillDomainDeviceVideoCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCapsDeviceVideo *dev)
|
|
{
|
|
dev->supported = VIR_TRISTATE_BOOL_YES;
|
|
dev->modelType.report = true;
|
|
|
|
VIR_DOMAIN_CAPS_ENUM_SET(dev->modelType, VIR_DOMAIN_VIDEO_TYPE_NONE);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VGA))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(dev->modelType, VIR_DOMAIN_VIDEO_TYPE_VGA);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_CIRRUS_VGA))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(dev->modelType, VIR_DOMAIN_VIDEO_TYPE_CIRRUS);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VMWARE_SVGA))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(dev->modelType, VIR_DOMAIN_VIDEO_TYPE_VMVGA);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QXL))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(dev->modelType, VIR_DOMAIN_VIDEO_TYPE_QXL);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_GPU))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(dev->modelType, VIR_DOMAIN_VIDEO_TYPE_VIRTIO);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_BOCHS_DISPLAY))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(dev->modelType, VIR_DOMAIN_VIDEO_TYPE_BOCHS);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_RAMFB))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(dev->modelType, VIR_DOMAIN_VIDEO_TYPE_RAMFB);
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsFillDomainDeviceHostdevCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCapsDeviceHostdev *hostdev)
|
|
{
|
|
bool supportsPassthroughVFIO = virHostdevHostSupportsPassthroughVFIO();
|
|
|
|
hostdev->supported = VIR_TRISTATE_BOOL_YES;
|
|
hostdev->mode.report = true;
|
|
hostdev->startupPolicy.report = true;
|
|
hostdev->subsysType.report = true;
|
|
hostdev->capsType.report = true;
|
|
hostdev->pciBackend.report = true;
|
|
|
|
/* VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES is for containers only */
|
|
VIR_DOMAIN_CAPS_ENUM_SET(hostdev->mode,
|
|
VIR_DOMAIN_HOSTDEV_MODE_SUBSYS);
|
|
|
|
VIR_DOMAIN_CAPS_ENUM_SET(hostdev->startupPolicy,
|
|
VIR_DOMAIN_STARTUP_POLICY_DEFAULT,
|
|
VIR_DOMAIN_STARTUP_POLICY_MANDATORY,
|
|
VIR_DOMAIN_STARTUP_POLICY_REQUISITE,
|
|
VIR_DOMAIN_STARTUP_POLICY_OPTIONAL);
|
|
|
|
VIR_DOMAIN_CAPS_ENUM_SET(hostdev->subsysType,
|
|
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI,
|
|
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI);
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX3_USB_UHCI) ||
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX4_USB_UHCI) ||
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_EHCI) ||
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_USB_EHCI1) ||
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VT82C686B_USB_UHCI) ||
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI) ||
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI) ||
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI)) {
|
|
VIR_DOMAIN_CAPS_ENUM_SET(hostdev->subsysType,
|
|
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB);
|
|
}
|
|
|
|
/* No virDomainHostdevCapsType for QEMU */
|
|
virDomainCapsEnumClear(&hostdev->capsType);
|
|
|
|
virDomainCapsEnumClear(&hostdev->pciBackend);
|
|
if (supportsPassthroughVFIO &&
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) {
|
|
VIR_DOMAIN_CAPS_ENUM_SET(hostdev->pciBackend,
|
|
VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_DEFAULT,
|
|
VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsFillDomainDeviceRNGCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCapsDeviceRNG *rng)
|
|
{
|
|
rng->supported = VIR_TRISTATE_BOOL_YES;
|
|
rng->model.report = true;
|
|
rng->backendModel.report = true;
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_RNG)) {
|
|
VIR_DOMAIN_CAPS_ENUM_SET(rng->model, VIR_DOMAIN_RNG_MODEL_VIRTIO);
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL)) {
|
|
VIR_DOMAIN_CAPS_ENUM_SET(rng->model,
|
|
VIR_DOMAIN_RNG_MODEL_VIRTIO_TRANSITIONAL,
|
|
VIR_DOMAIN_RNG_MODEL_VIRTIO_NON_TRANSITIONAL);
|
|
}
|
|
}
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_RNG_EGD))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(rng->backendModel, VIR_DOMAIN_RNG_BACKEND_EGD);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_RNG_RANDOM))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(rng->backendModel, VIR_DOMAIN_RNG_BACKEND_RANDOM);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_RNG_BUILTIN))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(rng->backendModel, VIR_DOMAIN_RNG_BACKEND_BUILTIN);
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsFillDomainDeviceFSCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCapsDeviceFilesystem *filesystem)
|
|
{
|
|
filesystem->supported = VIR_TRISTATE_BOOL_YES;
|
|
filesystem->driverType.report = true;
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VHOST_USER_FS))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(filesystem->driverType,
|
|
VIR_DOMAIN_FS_DRIVER_TYPE_VIRTIOFS);
|
|
|
|
VIR_DOMAIN_CAPS_ENUM_SET(filesystem->driverType,
|
|
VIR_DOMAIN_FS_DRIVER_TYPE_PATH,
|
|
VIR_DOMAIN_FS_DRIVER_TYPE_HANDLE);
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsFillDomainDeviceTPMCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCapsDeviceTPM *tpm)
|
|
{
|
|
tpm->supported = VIR_TRISTATE_BOOL_YES;
|
|
tpm->model.report = true;
|
|
tpm->backendModel.report = true;
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_TIS))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(tpm->model, VIR_DOMAIN_TPM_MODEL_TIS);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_CRB))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(tpm->model, VIR_DOMAIN_TPM_MODEL_CRB);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_SPAPR))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(tpm->model, VIR_DOMAIN_TPM_MODEL_SPAPR);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SPAPR_TPM_PROXY))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(tpm->model, VIR_DOMAIN_TPM_MODEL_SPAPR_PROXY);
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_PASSTHROUGH))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(tpm->backendModel, VIR_DOMAIN_TPM_TYPE_PASSTHROUGH);
|
|
if (virTPMHasSwtpm()) {
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_EMULATOR)) {
|
|
VIR_DOMAIN_CAPS_ENUM_SET(tpm->backendModel, VIR_DOMAIN_TPM_TYPE_EMULATOR);
|
|
VIR_DOMAIN_CAPS_ENUM_SET(tpm->backendModel, VIR_DOMAIN_TPM_TYPE_EXTERNAL);
|
|
}
|
|
if (virTPMSwtpmSetupCapsGet(VIR_TPM_SWTPM_SETUP_FEATURE_TPM_1_2)) {
|
|
VIR_DOMAIN_CAPS_ENUM_SET(tpm->backendVersion, VIR_DOMAIN_TPM_VERSION_1_2);
|
|
tpm->backendVersion.report = true;
|
|
}
|
|
if (virTPMSwtpmSetupCapsGet(VIR_TPM_SWTPM_SETUP_FEATURE_TPM_2_0)) {
|
|
VIR_DOMAIN_CAPS_ENUM_SET(tpm->backendVersion, VIR_DOMAIN_TPM_VERSION_2_0);
|
|
tpm->backendVersion.report = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Need at least one frontend if it is to be usable by applications
|
|
*/
|
|
if (!tpm->model.values)
|
|
tpm->supported = VIR_TRISTATE_BOOL_NO;
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsFillDomainDeviceRedirdevCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCapsDeviceRedirdev *redirdev)
|
|
{
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_REDIR)) {
|
|
redirdev->supported = VIR_TRISTATE_BOOL_YES;
|
|
redirdev->bus.report = true;
|
|
VIR_DOMAIN_CAPS_ENUM_SET(redirdev->bus, VIR_DOMAIN_REDIRDEV_BUS_USB);
|
|
} else {
|
|
redirdev->supported = VIR_TRISTATE_BOOL_NO;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsFillDomainDeviceChannelCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCapsDeviceChannel *channel)
|
|
{
|
|
channel->supported = VIR_TRISTATE_BOOL_YES;
|
|
channel->type.report = true;
|
|
VIR_DOMAIN_CAPS_ENUM_SET(channel->type,
|
|
VIR_DOMAIN_CHR_TYPE_PTY, VIR_DOMAIN_CHR_TYPE_UNIX);
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SPICE))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(channel->type, VIR_DOMAIN_CHR_TYPE_SPICEVMC);
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsFillDomainDeviceCryptoCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCapsDeviceCrypto *crypto)
|
|
{
|
|
crypto->supported = VIR_TRISTATE_BOOL_YES;
|
|
crypto->model.report = true;
|
|
crypto->type.report = true;
|
|
crypto->backendModel.report = true;
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_CRYPTO))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(crypto->model, VIR_DOMAIN_CRYPTO_MODEL_VIRTIO);
|
|
|
|
VIR_DOMAIN_CAPS_ENUM_SET(crypto->type, VIR_DOMAIN_CRYPTO_TYPE_QEMU);
|
|
|
|
VIR_DOMAIN_CAPS_ENUM_SET(crypto->backendModel, VIR_DOMAIN_CRYPTO_BACKEND_BUILTIN);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_CRYPTO_LKCF))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(crypto->backendModel, VIR_DOMAIN_CRYPTO_BACKEND_LKCF);
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsFillDomainLaunchSecurity(virQEMUCaps *qemuCaps,
|
|
virDomainCapsLaunchSecurity *launchSecurity)
|
|
{
|
|
launchSecurity->supported = VIR_TRISTATE_BOOL_YES;
|
|
launchSecurity->sectype.report = true;
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(launchSecurity->sectype, VIR_DOMAIN_LAUNCH_SECURITY_SEV);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_SNP_GUEST))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(launchSecurity->sectype, VIR_DOMAIN_LAUNCH_SECURITY_SEV_SNP);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_S390_PV_GUEST) &&
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_CONFIDENTAL_GUEST_SUPPORT))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(launchSecurity->sectype, VIR_DOMAIN_LAUNCH_SECURITY_PV);
|
|
|
|
if (launchSecurity->sectype.values == 0) {
|
|
launchSecurity->supported = VIR_TRISTATE_BOOL_NO;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsFillDomainDeviceNetCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCapsDeviceNet *net)
|
|
{
|
|
net->supported = VIR_TRISTATE_BOOL_YES;
|
|
net->backendType.report = true;
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NETDEV_USER))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(net->backendType, VIR_DOMAIN_NET_BACKEND_DEFAULT);
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NETDEV_STREAM))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(net->backendType, VIR_DOMAIN_NET_BACKEND_PASST);
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsFillDomainDevicePanicCaps(virQEMUCaps *qemuCaps,
|
|
const char *machine,
|
|
virDomainCapsDevicePanic *panic)
|
|
{
|
|
panic->model.report = true;
|
|
|
|
if (ARCH_IS_S390(qemuCaps->arch))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(panic->model, VIR_DOMAIN_PANIC_MODEL_S390);
|
|
|
|
if (ARCH_IS_X86(qemuCaps->arch))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(panic->model, VIR_DOMAIN_PANIC_MODEL_HYPERV);
|
|
|
|
if (qemuDomainMachineIsPSeries(machine, qemuCaps->arch))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(panic->model, VIR_DOMAIN_PANIC_MODEL_PSERIES);
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PANIC))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(panic->model, VIR_DOMAIN_PANIC_MODEL_ISA);
|
|
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PANIC_PCI))
|
|
VIR_DOMAIN_CAPS_ENUM_SET(panic->model, VIR_DOMAIN_PANIC_MODEL_PVPANIC);
|
|
|
|
if (panic->model.values)
|
|
panic->supported = VIR_TRISTATE_BOOL_YES;
|
|
else
|
|
panic->supported = VIR_TRISTATE_BOOL_NO;
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsSupportsGICVersion:
|
|
* @qemuCaps: QEMU capabilities
|
|
* @virtType: domain type
|
|
* @version: GIC version
|
|
*
|
|
* Checks the QEMU binary with capabilities @qemuCaps supports a specific
|
|
* GIC version for a domain of type @virtType. If @qemuCaps is NULL, the GIC
|
|
* @version is considered unsupported.
|
|
*
|
|
* Returns: true if the binary supports the requested GIC version, false
|
|
* otherwise
|
|
*/
|
|
bool
|
|
virQEMUCapsSupportsGICVersion(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType,
|
|
virGICVersion version)
|
|
{
|
|
size_t i;
|
|
|
|
if (!qemuCaps)
|
|
return false;
|
|
|
|
for (i = 0; i < qemuCaps->ngicCapabilities; i++) {
|
|
virGICCapability *cap = &(qemuCaps->gicCapabilities[i]);
|
|
|
|
if (cap->version != version)
|
|
continue;
|
|
|
|
if (virtType == VIR_DOMAIN_VIRT_KVM &&
|
|
cap->implementation & VIR_GIC_IMPLEMENTATION_KERNEL)
|
|
return true;
|
|
|
|
if (virtType == VIR_DOMAIN_VIRT_QEMU &&
|
|
cap->implementation & VIR_GIC_IMPLEMENTATION_EMULATED)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsFillDomainFeatureGICCaps:
|
|
* @qemuCaps: QEMU capabilities
|
|
* @domCaps: domain capabilities
|
|
*
|
|
* Take the information about GIC capabilities that has been obtained
|
|
* using the 'query-gic-capabilities' QMP command and stored in @qemuCaps
|
|
* and convert it to a form suitable for @domCaps.
|
|
*
|
|
* @qemuCaps contains complete information about the GIC capabilities for
|
|
* the corresponding QEMU binary, stored as custom objects; @domCaps, on
|
|
* the other hand, should only contain information about the GIC versions
|
|
* available for the specific combination of architecture, machine type
|
|
* and virtualization type. Moreover, a common format is used to store
|
|
* information about enumerations in @domCaps, so further processing is
|
|
* required.
|
|
*/
|
|
static void
|
|
virQEMUCapsFillDomainFeatureGICCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCaps *domCaps)
|
|
{
|
|
virDomainCapsFeatureGIC *gic = &domCaps->gic;
|
|
virGICVersion version;
|
|
|
|
gic->supported = VIR_TRISTATE_BOOL_NO;
|
|
|
|
if (!qemuDomainMachineIsARMVirt(domCaps->machine, domCaps->arch))
|
|
return;
|
|
|
|
for (version = VIR_GIC_VERSION_LAST - 1;
|
|
version > VIR_GIC_VERSION_NONE;
|
|
version--) {
|
|
if (!virQEMUCapsSupportsGICVersion(qemuCaps,
|
|
domCaps->virttype,
|
|
version))
|
|
continue;
|
|
|
|
gic->supported = VIR_TRISTATE_BOOL_YES;
|
|
gic->version.report = true;
|
|
VIR_DOMAIN_CAPS_ENUM_SET(gic->version,
|
|
version);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsFillDomainFeatureSEVCaps:
|
|
* @qemuCaps: QEMU capabilities
|
|
* @domCaps: domain capabilities
|
|
*
|
|
* Take the information about SEV capabilities that has been obtained
|
|
* using the 'query-sev-capabilities' QMP command and stored in @qemuCaps
|
|
* and convert it to a form suitable for @domCaps.
|
|
*/
|
|
static void
|
|
virQEMUCapsFillDomainFeatureSEVCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCaps *domCaps)
|
|
{
|
|
virQEMUCapsSEVInfoCopy(&domCaps->sev, qemuCaps->sevCapabilities);
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsFillDomainFeatureS390PVCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCaps *domCaps)
|
|
{
|
|
if (ARCH_IS_S390(qemuCaps->arch)) {
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_CONFIDENTAL_GUEST_SUPPORT) &&
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_S390_PV_GUEST) &&
|
|
virQEMUCapsGetKVMSupportsSecureGuest(qemuCaps))
|
|
domCaps->features[VIR_DOMAIN_CAPS_FEATURE_S390_PV] = VIR_TRISTATE_BOOL_YES;
|
|
else
|
|
domCaps->features[VIR_DOMAIN_CAPS_FEATURE_S390_PV] = VIR_TRISTATE_BOOL_NO;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsFillDomainFeaturePS2Caps(virQEMUCaps *qemuCaps,
|
|
virDomainCaps *domCaps)
|
|
{
|
|
if (virQEMUCapsSupportsI8042Toggle(qemuCaps, domCaps->machine, domCaps->arch))
|
|
domCaps->features[VIR_DOMAIN_CAPS_FEATURE_PS2] = VIR_TRISTATE_BOOL_YES;
|
|
else
|
|
domCaps->features[VIR_DOMAIN_CAPS_FEATURE_PS2] = VIR_TRISTATE_BOOL_NO;
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsFillDomainFeatureSGXCaps:
|
|
* @qemuCaps: QEMU capabilities
|
|
* @domCaps: domain capabilities
|
|
*
|
|
* Take the information about SGX capabilities that has been obtained
|
|
* using the 'query-sgx-capabilities' QMP command and stored in @qemuCaps
|
|
* and convert it to a form suitable for @domCaps.
|
|
*/
|
|
static void
|
|
virQEMUCapsFillDomainFeatureSGXCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCaps *domCaps)
|
|
{
|
|
virQEMUCapsSGXInfoCopy(&domCaps->sgx, qemuCaps->sgxCapabilities);
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsFillDomainFeatureHypervCaps(virQEMUCaps *qemuCaps,
|
|
virDomainCaps *domCaps)
|
|
{
|
|
domCaps->hyperv = g_memdup(qemuCaps->hypervCapabilities,
|
|
sizeof(virDomainCapsFeatureHyperv));
|
|
}
|
|
|
|
|
|
int
|
|
virQEMUCapsFillDomainCaps(virQEMUCaps *qemuCaps,
|
|
virArch hostarch,
|
|
virDomainCaps *domCaps,
|
|
bool privileged,
|
|
virFirmware **firmwares,
|
|
size_t nfirmwares)
|
|
{
|
|
virDomainCapsOS *os = &domCaps->os;
|
|
virDomainCapsDeviceDisk *disk = &domCaps->disk;
|
|
virDomainCapsDeviceHostdev *hostdev = &domCaps->hostdev;
|
|
virDomainCapsDeviceGraphics *graphics = &domCaps->graphics;
|
|
virDomainCapsDeviceVideo *video = &domCaps->video;
|
|
virDomainCapsDeviceRNG *rng = &domCaps->rng;
|
|
virDomainCapsDeviceFilesystem *filesystem = &domCaps->filesystem;
|
|
virDomainCapsDeviceTPM *tpm = &domCaps->tpm;
|
|
virDomainCapsDeviceRedirdev *redirdev = &domCaps->redirdev;
|
|
virDomainCapsDeviceChannel *channel = &domCaps->channel;
|
|
virDomainCapsMemoryBacking *memoryBacking = &domCaps->memoryBacking;
|
|
virDomainCapsDeviceCrypto *crypto = &domCaps->crypto;
|
|
virDomainCapsLaunchSecurity *launchSecurity = &domCaps->launchSecurity;
|
|
virDomainCapsDeviceNet *net = &domCaps->net;
|
|
virDomainCapsDevicePanic *panic = &domCaps->panic;
|
|
|
|
virQEMUCapsFillDomainFeaturesFromQEMUCaps(qemuCaps, domCaps);
|
|
|
|
domCaps->maxvcpus = virQEMUCapsGetMachineMaxCpus(qemuCaps,
|
|
domCaps->virttype,
|
|
domCaps->machine);
|
|
if (domCaps->virttype == VIR_DOMAIN_VIRT_KVM) {
|
|
int hostmaxvcpus;
|
|
|
|
if ((hostmaxvcpus = virHostCPUGetKVMMaxVCPUs()) < 0)
|
|
return -1;
|
|
|
|
domCaps->maxvcpus = MIN(domCaps->maxvcpus, hostmaxvcpus);
|
|
}
|
|
|
|
if (virQEMUCapsFillDomainOSCaps(os,
|
|
domCaps->machine,
|
|
domCaps->arch,
|
|
privileged,
|
|
firmwares, nfirmwares) < 0)
|
|
return -1;
|
|
|
|
virQEMUCapsFillDomainCPUCaps(qemuCaps, hostarch, domCaps);
|
|
virQEMUCapsFillDomainMemoryBackingCaps(qemuCaps, memoryBacking);
|
|
virQEMUCapsFillDomainDeviceDiskCaps(qemuCaps, domCaps->machine, disk);
|
|
virQEMUCapsFillDomainDeviceGraphicsCaps(qemuCaps, graphics);
|
|
virQEMUCapsFillDomainDeviceVideoCaps(qemuCaps, video);
|
|
virQEMUCapsFillDomainDeviceHostdevCaps(qemuCaps, hostdev);
|
|
virQEMUCapsFillDomainDeviceRNGCaps(qemuCaps, rng);
|
|
virQEMUCapsFillDomainDeviceFSCaps(qemuCaps, filesystem);
|
|
virQEMUCapsFillDomainDeviceTPMCaps(qemuCaps, tpm);
|
|
virQEMUCapsFillDomainDeviceRedirdevCaps(qemuCaps, redirdev);
|
|
virQEMUCapsFillDomainDeviceChannelCaps(qemuCaps, channel);
|
|
virQEMUCapsFillDomainFeatureGICCaps(qemuCaps, domCaps);
|
|
virQEMUCapsFillDomainFeatureSEVCaps(qemuCaps, domCaps);
|
|
virQEMUCapsFillDomainFeatureS390PVCaps(qemuCaps, domCaps);
|
|
virQEMUCapsFillDomainFeaturePS2Caps(qemuCaps, domCaps);
|
|
virQEMUCapsFillDomainFeatureSGXCaps(qemuCaps, domCaps);
|
|
virQEMUCapsFillDomainFeatureHypervCaps(qemuCaps, domCaps);
|
|
virQEMUCapsFillDomainDeviceCryptoCaps(qemuCaps, crypto);
|
|
virQEMUCapsFillDomainLaunchSecurity(qemuCaps, launchSecurity);
|
|
virQEMUCapsFillDomainDeviceNetCaps(qemuCaps, net);
|
|
virQEMUCapsFillDomainDevicePanicCaps(qemuCaps, domCaps->machine, panic);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
virQEMUCapsSetMicrocodeVersion(virQEMUCaps *qemuCaps,
|
|
unsigned int microcodeVersion)
|
|
{
|
|
qemuCaps->microcodeVersion = microcodeVersion;
|
|
}
|
|
|
|
|
|
static void
|
|
virQEMUCapsStripMachineAliasesForVirtType(virQEMUCaps *qemuCaps,
|
|
virDomainVirtType virtType)
|
|
{
|
|
virQEMUCapsAccel *accel = virQEMUCapsGetAccel(qemuCaps, virtType);
|
|
size_t i;
|
|
|
|
for (i = 0; i < accel->nmachineTypes; i++) {
|
|
virQEMUCapsMachineType *mach = &accel->machineTypes[i];
|
|
|
|
if (mach->alias) {
|
|
g_autofree char *origName = g_steal_pointer(&mach->name);
|
|
|
|
/* The preferred machine type, which is generally the default by qemu
|
|
* in most cases but also generally an alias needs to be kept at the
|
|
* first entry in the list, because virQEMUCapsGetPreferredMachine
|
|
* picks the first element. Thus we create a new entry for the
|
|
* original machine name and replace the existing one by the alias. */
|
|
mach->name = g_steal_pointer(&mach->alias);
|
|
|
|
/* also de-assert the copy's default flag, to ensure we don't have multiple
|
|
* machines with it */
|
|
virQEMUCapsAddMachine(qemuCaps, virtType, origName, NULL, mach->defaultCPU,
|
|
mach->maxCpus, mach->hotplugCpus, false,
|
|
mach->numaMemSupported, mach->defaultRAMid,
|
|
mach->deprecated, mach->acpi);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* virQEMUCapsStripMachineAliases:
|
|
* @qemuCaps: capabilities object to process
|
|
*
|
|
* Replace all aliases by the copy of the machine type they point to without
|
|
* actually having to modify the name. This allows us to add tests with the
|
|
* aliased machine without having to change the output files all the time.
|
|
*
|
|
* Remove all aliases so that the tests depending on the latest capabilities
|
|
* file can be stable when new files are added.
|
|
*
|
|
* Additionally if a machine type is default, only the copy created for the
|
|
* alias will be kept as default to eliminate churn in output files.
|
|
*/
|
|
void
|
|
virQEMUCapsStripMachineAliases(virQEMUCaps *qemuCaps)
|
|
{
|
|
virQEMUCapsStripMachineAliasesForVirtType(qemuCaps, VIR_DOMAIN_VIRT_KVM);
|
|
virQEMUCapsStripMachineAliasesForVirtType(qemuCaps, VIR_DOMAIN_VIRT_HVF);
|
|
virQEMUCapsStripMachineAliasesForVirtType(qemuCaps, VIR_DOMAIN_VIRT_QEMU);
|
|
}
|