]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
drive mirror: cleanup exception handling code a bit
[qemu-server.git] / PVE / QemuServer.pm
index 277bc58309e3ae6d741854448f3169eacc1bee90..fd854c4e1a984897b79f7e062773ead17bb9b97a 100644 (file)
@@ -2,6 +2,7 @@ package PVE::QemuServer;
 
 use strict;
 use warnings;
 
 use strict;
 use warnings;
+
 use POSIX;
 use IO::Handle;
 use IO::Select;
 use POSIX;
 use IO::Handle;
 use IO::Select;
@@ -30,17 +31,28 @@ use PVE::ProcFSTools;
 use PVE::QemuConfig;
 use PVE::QMPClient;
 use PVE::RPCEnvironment;
 use PVE::QemuConfig;
 use PVE::QMPClient;
 use PVE::RPCEnvironment;
+use PVE::GuestHelpers;
 use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr);
 use PVE::QemuServer::Memory;
 use PVE::QemuServer::USB qw(parse_usb_device);
 use PVE::QemuServer::Cloudinit;
 use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr);
 use PVE::QemuServer::Memory;
 use PVE::QemuServer::USB qw(parse_usb_device);
 use PVE::QemuServer::Cloudinit;
+use PVE::SysFSTools;
+use PVE::Systemd;
 use Time::HiRes qw(gettimeofday);
 use File::Copy qw(copy);
 use URI::Escape;
 
 my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
 use Time::HiRes qw(gettimeofday);
 use File::Copy qw(copy);
 use URI::Escape;
 
 my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
-my $OVMF_CODE = "$EDK2_FW_BASE/OVMF_CODE.fd";
-my $OVMF_VARS = "$EDK2_FW_BASE/OVMF_VARS.fd";
+my $OVMF = {
+    x86_64 => [
+       "$EDK2_FW_BASE/OVMF_CODE.fd",
+       "$EDK2_FW_BASE/OVMF_VARS.fd"
+    ],
+    aarch64 => [
+       "$EDK2_FW_BASE/AAVMF_CODE.fd",
+       "$EDK2_FW_BASE/AAVMF_VARS.fd"
+    ],
+};
 
 my $qemu_snap_storage = {rbd => 1, sheepdog => 1};
 
 
 my $qemu_snap_storage = {rbd => 1, sheepdog => 1};
 
@@ -66,12 +78,6 @@ PVE::JSONSchema::register_standard_option('pve-qm-stateuri', {
     optional => 1,
 });
 
     optional => 1,
 });
 
-PVE::JSONSchema::register_standard_option('pve-snapshot-name', {
-    description => "The name of the snapshot.",
-    type => 'string', format => 'pve-configid',
-    maxLength => 40,
-});
-
 PVE::JSONSchema::register_standard_option('pve-qm-image-format', {
     type => 'string',
     enum => [qw(raw cow qcow qed qcow2 vmdk cloop)],
 PVE::JSONSchema::register_standard_option('pve-qm-image-format', {
     type => 'string',
     enum => [qw(raw cow qcow qed qcow2 vmdk cloop)],
@@ -79,6 +85,14 @@ PVE::JSONSchema::register_standard_option('pve-qm-image-format', {
     optional => 1,
 });
 
     optional => 1,
 });
 
+PVE::JSONSchema::register_standard_option('pve-qemu-machine', {
+       description => "Specifies the Qemu machine type.",
+       type => 'string',
+       pattern => '(pc|pc(-i440fx)?-\d+\.\d+(\.pxe)?|q35|pc-q35-\d+\.\d+(\.pxe)?|virt(?:-\d+\.\d+)?)',
+       maxLength => 40,
+       optional => 1,
+});
+
 #no warnings 'redefine';
 
 sub cgroups_write {
 #no warnings 'redefine';
 
 sub cgroups_write {
@@ -101,8 +115,6 @@ mkdir $var_run_tmpdir;
 my $lock_dir = "/var/lock/qemu-server";
 mkdir $lock_dir;
 
 my $lock_dir = "/var/lock/qemu-server";
 mkdir $lock_dir;
 
-my $pcisysfs = "/sys/bus/pci";
-
 my $cpu_vendor_list = {
     # Intel CPUs
     486 => 'GenuineIntel',
 my $cpu_vendor_list = {
     # Intel CPUs
     486 => 'GenuineIntel',
@@ -154,7 +166,7 @@ my $cpu_vendor_list = {
     max => 'default',
 };
 
     max => 'default',
 };
 
-my $cpu_flag = qr/[+-](pcid|spec-ctrl)/;
+my $cpu_flag = qr/[+-](pcid|spec-ctrl|ibpb|ssbd|virt-ssbd|amd-ssbd|amd-no-ssb|pdpe1gb)/;
 
 my $cpu_fmt = {
     cputype => {
 
 my $cpu_fmt = {
     cputype => {
@@ -170,10 +182,17 @@ my $cpu_fmt = {
        optional => 1,
        default => 0
     },
        optional => 1,
        default => 0
     },
+    'hv-vendor-id' => {
+       type => 'string',
+       pattern => qr/[a-zA-Z0-9]{1,12}/,
+       format_description => 'vendor-id',
+       description => 'The Hyper-V vendor ID. Some drivers or programs inside Windows guests need a specific ID.',
+       optional => 1,
+    },
     flags => {
        description => "List of additional CPU flags separated by ';'."
                     . " Use '+FLAG' to enable, '-FLAG' to disable a flag."
     flags => {
        description => "List of additional CPU flags separated by ';'."
                     . " Use '+FLAG' to enable, '-FLAG' to disable a flag."
-                    . " Currently supported flags: 'pcid', 'spec-ctrl'.",
+                    . " Currently supported flags: 'pcid', 'spec-ctrl', 'ibpb', 'ssbd', 'virt-ssbd', 'amd-ssbd', 'amd-no-ssb', 'pdpe1gb'.",
        format_description => '+FLAG[;-FLAG...]',
        type => 'string',
        pattern => qr/$cpu_flag(;$cpu_flag)*/,
        format_description => '+FLAG[;-FLAG...]',
        type => 'string',
        pattern => qr/$cpu_flag(;$cpu_flag)*/,
@@ -199,6 +218,54 @@ my $watchdog_fmt = {
 };
 PVE::JSONSchema::register_format('pve-qm-watchdog', $watchdog_fmt);
 
 };
 PVE::JSONSchema::register_format('pve-qm-watchdog', $watchdog_fmt);
 
+my $agent_fmt = {
+    enabled => {
+       description => "Enable/disable Qemu GuestAgent.",
+       type => 'boolean',
+       default => 0,
+       default_key => 1,
+    },
+    fstrim_cloned_disks => {
+       description => "Run fstrim after cloning/moving a disk.",
+       type => 'boolean',
+       optional => 1,
+       default => 0
+    },
+};
+
+my $vga_fmt = {
+    type => {
+       description => "Select the VGA type.",
+       type => 'string',
+       default => 'std',
+       optional => 1,
+       default_key => 1,
+       enum => [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio vmware)],
+    },
+    memory => {
+       description => "Sets the VGA memory (in MiB). Has no effect with serial display.",
+       type => 'integer',
+       optional => 1,
+       minimum => 4,
+       maximum => 512,
+    },
+};
+
+my $ivshmem_fmt = {
+    size => {
+       type => 'integer',
+       minimum => 1,
+       description => "The size of the file in MB.",
+    },
+    name => {
+       type => 'string',
+       pattern => '[a-zA-Z0-9\-]+',
+       optional => 1,
+       format_description => 'string',
+       description => "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
+    },
+};
+
 my $confdesc = {
     onboot => {
        optional => 1,
 my $confdesc = {
     onboot => {
        optional => 1,
@@ -228,7 +295,7 @@ my $confdesc = {
        optional => 1,
        type => 'string',
        description => "Lock/unlock the VM.",
        optional => 1,
        type => 'string',
        description => "Lock/unlock the VM.",
-       enum => [qw(migrate backup snapshot rollback)],
+       enum => [qw(backup clone create migrate rollback snapshot snapshot-delete suspending suspended)],
     },
     cpulimit => {
        optional => 1,
     },
     cpulimit => {
        optional => 1,
@@ -379,9 +446,9 @@ EODESC
     },
     agent => {
        optional => 1,
     },
     agent => {
        optional => 1,
-       type => 'boolean',
-       description => "Enable/disable Qemu GuestAgent.",
-       default => 0,
+       description => "Enable/disable Qemu GuestAgent and its properties.",
+       type => 'string',
+       format => $agent_fmt,
     },
     kvm => {
        optional => 1,
     },
     kvm => {
        optional => 1,
@@ -407,17 +474,16 @@ EODESC
     },
     vga => {
        optional => 1,
     },
     vga => {
        optional => 1,
-       type => 'string',
-       description => "Select the VGA type.",
-        verbose_description => "Select the VGA type. If you want to use high resolution" .
-           " modes (>= 1280x1024x16) then you should use the options " .
-           "'std' or 'vmware'. Default is 'std' for win8/win7/w2k8, and " .
-           "'cirrus' for other OS types. The 'qxl' option enables the SPICE " .
-           "display sever. For win* OS you can select how many independent " .
-           "displays you want, Linux guests can add displays them self. " .
-           "You can also run without any graphic card, using a serial device" .
-           " as terminal.",
-       enum => [qw(std cirrus vmware qxl serial0 serial1 serial2 serial3 qxl2 qxl3 qxl4)],
+       type => 'string', format => $vga_fmt,
+       description => "Configure the VGA hardware.",
+       verbose_description => "Configure the VGA Hardware. If you want to use ".
+           "high resolution modes (>= 1280x1024x16) you may need to increase " .
+           "the vga memory option. Since QEMU 2.9 the default VGA display type " .
+           "is 'std' for all OS types besides some Windows versions (XP and " .
+           "older) which use 'cirrus'. The 'qxl' option enables the SPICE " .
+           "display server. For win* OS you can select how many independent " .
+           "displays you want, Linux guests can add displays them self.\n".
+           "You can also run without any graphic card, using a serial device as terminal.",
     },
     watchdog => {
        optional => 1,
     },
     watchdog => {
        optional => 1,
@@ -512,12 +578,15 @@ EODESCR
        description => "Default storage for VM state volumes/files.",
        optional => 1,
     }),
        description => "Default storage for VM state volumes/files.",
        optional => 1,
     }),
-    machine => {
-       description => "Specific the Qemu machine type.",
-       type => 'string',
-       pattern => '(pc|pc(-i440fx)?-\d+\.\d+(\.pxe)?|q35|pc-q35-\d+\.\d+(\.pxe)?)',
-       maxLength => 40,
+    runningmachine => get_standard_option('pve-qemu-machine', {
+       description => "Specifies the Qemu machine type of the running vm. This is used internally for snapshots.",
+    }),
+    machine => get_standard_option('pve-qemu-machine'),
+    arch => {
+       description => "Virtual processor architecture. Defaults to the host.",
        optional => 1,
        optional => 1,
+       type => 'string',
+       enum => [qw(x86_64 aarch64)],
     },
     smbios1 => {
        description => "Specify SMBIOS type 1 fields.",
     },
     smbios1 => {
        description => "Specify SMBIOS type 1 fields.",
@@ -538,8 +607,63 @@ EODESCR
        description => "Select BIOS implementation.",
        default => 'seabios',
     },
        description => "Select BIOS implementation.",
        default => 'seabios',
     },
+    vmgenid => {
+       type => 'string',
+       pattern => '(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])',
+       format_description => 'UUID',
+       description => "Set VM Generation ID. Use '1' to autogenerate on create or update, pass '0' to disable explicitly.",
+       verbose_description => "The VM generation ID (vmgenid) device exposes a".
+           " 128-bit integer value identifier to the guest OS. This allows to".
+           " notify the guest operating system when the virtual machine is".
+           " executed with a different configuration (e.g. snapshot execution".
+           " or creation from a template). The guest operating system notices".
+           " the change, and is then able to react as appropriate by marking".
+           " its copies of distributed databases as dirty, re-initializing its".
+           " random number generator, etc.\n".
+           "Note that auto-creation only works when done throug API/CLI create".
+           " or update methods, but not when manually editing the config file.",
+       default => "1 (autogenerated)",
+       optional => 1,
+    },
+    hookscript => {
+       type => 'string',
+       format => 'pve-volume-id',
+       optional => 1,
+       description => "Script that will be executed during various steps in the vms lifetime.",
+    },
+    ivshmem => {
+       type => 'string',
+       format => $ivshmem_fmt,
+       description => "Inter-VM shared memory. Useful for direct communication between VMs, or to the host.",
+       optional => 1,
+    }
 };
 
 };
 
+my $cicustom_fmt = {
+    meta => {
+       type => 'string',
+       optional => 1,
+       description => 'Specify a custom file containing all meta data passed to the VM via cloud-init. This is provider specific meaning configdrive2 and nocloud differ.',
+       format => 'pve-volume-id',
+       format_description => 'volume',
+    },
+    network => {
+       type => 'string',
+       optional => 1,
+       description => 'Specify a custom file containing all network data passed to the VM via cloud-init.',
+       format => 'pve-volume-id',
+       format_description => 'volume',
+    },
+    user => {
+       type => 'string',
+       optional => 1,
+       description => 'Specify a custom file containing all user data passed to the VM via cloud-init.',
+       format => 'pve-volume-id',
+       format_description => 'volume',
+    },
+};
+PVE::JSONSchema::register_format('pve-qm-cicustom', $cicustom_fmt);
+
 my $confdesc_cloudinit = {
     citype => {
        optional => 1,
 my $confdesc_cloudinit = {
     citype => {
        optional => 1,
@@ -557,6 +681,12 @@ my $confdesc_cloudinit = {
        type => 'string',
        description => 'cloud-init: Password to assign the user. Using this is generally not recommended. Use ssh keys instead. Also note that older cloud-init versions do not support hashed passwords.',
     },
        type => 'string',
        description => 'cloud-init: Password to assign the user. Using this is generally not recommended. Use ssh keys instead. Also note that older cloud-init versions do not support hashed passwords.',
     },
+    cicustom => {
+       optional => 1,
+       type => 'string',
+       description => 'cloud-init: Specify custom files to replace the automatically generated ones at start.',
+       format => 'pve-qm-cicustom',
+    },
     searchdomain => {
        optional => 1,
        type => 'string',
     searchdomain => {
        optional => 1,
        type => 'string',
@@ -602,7 +732,7 @@ my $MAX_VIRTIO_DISKS = 16;
 my $MAX_SATA_DISKS = 6;
 my $MAX_USB_DEVICES = 5;
 my $MAX_NETS = 32;
 my $MAX_SATA_DISKS = 6;
 my $MAX_USB_DEVICES = 5;
 my $MAX_NETS = 32;
-my $MAX_UNUSED_DISKS = 8;
+my $MAX_UNUSED_DISKS = 256;
 my $MAX_HOSTPCI_DEVICES = 4;
 my $MAX_SERIAL_PORTS = 4;
 my $MAX_PARALLEL_PORTS = 3;
 my $MAX_HOSTPCI_DEVICES = 4;
 my $MAX_SERIAL_PORTS = 4;
 my $MAX_PARALLEL_PORTS = 3;
@@ -667,13 +797,9 @@ The DHCP server assign addresses to the guest starting from 10.0.2.15.
 __EOD__
 
 my $net_fmt = {
 __EOD__
 
 my $net_fmt = {
-    macaddr => {
-       type => 'string',
-       pattern => qr/[0-9a-f]{2}(?::[0-9a-f]{2}){5}/i,
+    macaddr  => get_standard_option('mac-addr', {
        description => "MAC address. That address must be unique withing your network. This is automatically generated if not specified.",
        description => "MAC address. That address must be unique withing your network. This is automatically generated if not specified.",
-       format_description => "XX:XX:XX:XX:XX:XX",
-       optional => 1,
-    },
+    }),
     model => {
        type => 'string',
        description => "Network Card Model. The 'virtio' model provides the best performance with very low CPU overhead. If your guest does not support this driver, it is usually best to use 'e1000'.",
     model => {
        type => 'string',
        description => "Network Card Model. The 'virtio' model provides the best performance with very low CPU overhead. If your guest does not support this driver, it is usually best to use 'e1000'.",
@@ -961,6 +1087,24 @@ my %scsiblock_fmt = (
     },
 );
 
     },
 );
 
+my %ssd_fmt = (
+    ssd => {
+       type => 'boolean',
+       description => "Whether to expose this drive as an SSD, rather than a rotational hard disk.",
+       optional => 1,
+    },
+);
+
+my %wwn_fmt = (
+    wwn => {
+       type => 'string',
+       pattern => qr/^(0x)[0-9a-fA-F]{16}/,
+       format_description => 'wwn',
+       description => "The drive's worldwide name, encoded as 16 bytes hex string, prefixed by '0x'.",
+       optional => 1,
+    },
+);
+
 my $add_throttle_desc = sub {
     my ($key, $type, $what, $unit, $longunit, $minimum) = @_;
     my $d = {
 my $add_throttle_desc = sub {
     my ($key, $type, $what, $unit, $longunit, $minimum) = @_;
     my $d = {
@@ -1008,6 +1152,8 @@ $drivedesc_base{'iops_wr_length'} = { alias => 'iops_wr_max_length' };
 my $ide_fmt = {
     %drivedesc_base,
     %model_fmt,
 my $ide_fmt = {
     %drivedesc_base,
     %model_fmt,
+    %ssd_fmt,
+    %wwn_fmt,
 };
 PVE::JSONSchema::register_format("pve-qm-ide", $ide_fmt);
 
 };
 PVE::JSONSchema::register_format("pve-qm-ide", $ide_fmt);
 
@@ -1023,6 +1169,8 @@ my $scsi_fmt = {
     %iothread_fmt,
     %queues_fmt,
     %scsiblock_fmt,
     %iothread_fmt,
     %queues_fmt,
     %scsiblock_fmt,
+    %ssd_fmt,
+    %wwn_fmt,
 };
 my $scsidesc = {
     optional => 1,
 };
 my $scsidesc = {
     optional => 1,
@@ -1033,6 +1181,8 @@ PVE::JSONSchema::register_standard_option("pve-qm-scsi", $scsidesc);
 
 my $sata_fmt = {
     %drivedesc_base,
 
 my $sata_fmt = {
     %drivedesc_base,
+    %ssd_fmt,
+    %wwn_fmt,
 };
 my $satadesc = {
     optional => 1,
 };
 my $satadesc = {
     optional => 1,
@@ -1058,6 +1208,8 @@ my $alldrive_fmt = {
     %model_fmt,
     %queues_fmt,
     %scsiblock_fmt,
     %model_fmt,
     %queues_fmt,
     %scsiblock_fmt,
+    %ssd_fmt,
+    %wwn_fmt,
 };
 
 my $efidisk_fmt = {
 };
 
 my $efidisk_fmt = {
@@ -1121,8 +1273,7 @@ my $usbdesc = {
 };
 PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
 
 };
 PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
 
-# NOTE: the match-groups of this regex are used in parse_hostpci
-my $PCIRE = qr/([a-f0-9]{2}:[a-f0-9]{2})(?:\.([a-f0-9]))?/;
+my $PCIRE = qr/[a-f0-9]{2}:[a-f0-9]{2}(?:\.[a-f0-9])?/;
 my $hostpci_fmt = {
     host => {
        default_key => 1,
 my $hostpci_fmt = {
     host => {
        default_key => 1,
@@ -1163,6 +1314,17 @@ EODESCR
        optional => 1,
        default => 0,
     },
        optional => 1,
        default => 0,
     },
+    'mdev' => {
+       type => 'string',
+        format_description => 'string',
+       pattern => '[^/\.:]+',
+       optional => 1,
+       description => <<EODESCR
+The type of mediated device to use.
+An instance of this type will be created on startup of the VM and
+will be cleaned up when the VM stops.
+EODESCR
+    }
 };
 PVE::JSONSchema::register_format('pve-qm-hostpci', $hostpci_fmt);
 
 };
 PVE::JSONSchema::register_format('pve-qm-hostpci', $hostpci_fmt);
 
@@ -1263,19 +1425,15 @@ for (my $i = 0; $i < $MAX_UNUSED_DISKS; $i++)  {
 my $kvm_api_version = 0;
 
 sub kvm_version {
 my $kvm_api_version = 0;
 
 sub kvm_version {
-
     return $kvm_api_version if $kvm_api_version;
 
     return $kvm_api_version if $kvm_api_version;
 
-    my $fh = IO::File->new("</dev/kvm") ||
-       return 0;
-
-    if (my $v = $fh->ioctl(KVM_GET_API_VERSION(), 0)) {
-       $kvm_api_version = $v;
-    }
+    open my $fh, '<', '/dev/kvm'
+       or return undef;
 
 
-    $fh->close();
+    # 0xae00 => KVM_GET_API_VERSION
+    $kvm_api_version = ioctl($fh, 0xae00, 0);
 
 
-    return  $kvm_api_version;
+    return $kvm_api_version;
 }
 
 my $kvm_user_version;
 }
 
 my $kvm_user_version;
@@ -1300,7 +1458,9 @@ sub kvm_user_version {
 
 }
 
 
 }
 
-my $kernel_has_vhost_net = -c '/dev/vhost-net';
+sub kernel_has_vhost_net {
+    return -c '/dev/vhost-net';
+}
 
 sub valid_drive_names {
     # order is important - used to autoselect boot disk
 
 sub valid_drive_names {
     # order is important - used to autoselect boot disk
@@ -1612,24 +1772,37 @@ sub machine_type_is_q35 {
 }
 
 sub print_tabletdevice_full {
 }
 
 sub print_tabletdevice_full {
-    my ($conf) = @_;
+    my ($conf, $arch) = @_;
 
     my $q35 = machine_type_is_q35($conf);
 
     # we use uhci for old VMs because tablet driver was buggy in older qemu
 
     my $q35 = machine_type_is_q35($conf);
 
     # we use uhci for old VMs because tablet driver was buggy in older qemu
-    my $usbbus = $q35 ? "ehci" : "uhci";
+    my $usbbus;
+    if (machine_type_is_q35($conf) || $arch eq 'aarch64') {
+       $usbbus = 'ehci';
+    } else {
+       $usbbus = 'uhci';
+    }
 
     return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
 }
 
 
     return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
 }
 
+sub print_keyboarddevice_full {
+    my ($conf, $arch, $machine) = @_;
+
+    return undef if $arch ne 'aarch64';
+
+    return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
+}
+
 sub print_drivedevice_full {
 sub print_drivedevice_full {
-    my ($storecfg, $conf, $vmid, $drive, $bridges) = @_;
+    my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
 
     my $device = '';
     my $maxdev = 0;
 
     if ($drive->{interface} eq 'virtio') {
 
     my $device = '';
     my $maxdev = 0;
 
     if ($drive->{interface} eq 'virtio') {
-       my $pciaddr = print_pci_addr("$drive->{interface}$drive->{index}", $bridges);
+       my $pciaddr = print_pci_addr("$drive->{interface}$drive->{index}", $bridges, $arch, $machine_type);
        $device = "virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}$pciaddr";
        $device .= ",iothread=iothread-$drive->{interface}$drive->{index}" if $drive->{iothread};
     } elsif ($drive->{interface} eq 'scsi') {
        $device = "virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}$pciaddr";
        $device .= ",iothread=iothread-$drive->{interface}$drive->{index}" if $drive->{iothread};
     } elsif ($drive->{interface} eq 'scsi') {
@@ -1665,21 +1838,35 @@ sub print_drivedevice_full {
            $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0,lun=$drive->{index},drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
        }
 
            $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0,lun=$drive->{index},drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
        }
 
-    } elsif ($drive->{interface} eq 'ide'){
-       $maxdev = 2;
+       if ($drive->{ssd} && ($devicetype eq 'block' || $devicetype eq 'hd')) {
+           $device .= ",rotation_rate=1";
+       }
+       $device .= ",wwn=$drive->{wwn}" if $drive->{wwn};
+
+    } elsif ($drive->{interface} eq 'ide' || $drive->{interface} eq 'sata') {
+       my $maxdev = ($drive->{interface} eq 'sata') ? $MAX_SATA_DISKS : 2;
        my $controller = int($drive->{index} / $maxdev);
        my $unit = $drive->{index} % $maxdev;
        my $devicetype = ($drive->{media} && $drive->{media} eq 'cdrom') ? "cd" : "hd";
 
        my $controller = int($drive->{index} / $maxdev);
        my $unit = $drive->{index} % $maxdev;
        my $devicetype = ($drive->{media} && $drive->{media} eq 'cdrom') ? "cd" : "hd";
 
-       $device = "ide-$devicetype,bus=ide.$controller,unit=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
-       if ($devicetype eq 'hd' && (my $model = $drive->{model})) {
-           $model = URI::Escape::uri_unescape($model);
-           $device .= ",model=$model";
+       $device = "ide-$devicetype";
+       if ($drive->{interface} eq 'ide') {
+           $device .= ",bus=ide.$controller,unit=$unit";
+       } else {
+           $device .= ",bus=ahci$controller.$unit";
        }
        }
-    } elsif ($drive->{interface} eq 'sata'){
-       my $controller = int($drive->{index} / $MAX_SATA_DISKS);
-       my $unit = $drive->{index} % $MAX_SATA_DISKS;
-       $device = "ide-drive,bus=ahci$controller.$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
+       $device .= ",drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
+
+       if ($devicetype eq 'hd') {
+           if (my $model = $drive->{model}) {
+               $model = URI::Escape::uri_unescape($model);
+               $device .= ",model=$model";
+           }
+           if ($drive->{ssd}) {
+               $device .= ",rotation_rate=1";
+           }
+       }
+       $device .= ",wwn=$drive->{wwn}" if $drive->{wwn};
     } elsif ($drive->{interface} eq 'usb') {
        die "implement me";
        #  -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
     } elsif ($drive->{interface} eq 'usb') {
        die "implement me";
        #  -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
@@ -1806,7 +1993,7 @@ sub print_drive_full {
 }
 
 sub print_netdevice_full {
 }
 
 sub print_netdevice_full {
-    my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files) = @_;
+    my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type) = @_;
 
     my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
 
 
     my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
 
@@ -1815,7 +2002,7 @@ sub print_netdevice_full {
          $device = 'virtio-net-pci';
      };
 
          $device = 'virtio-net-pci';
      };
 
-    my $pciaddr = print_pci_addr("$netid", $bridges);
+    my $pciaddr = print_pci_addr("$netid", $bridges, $arch, $machine_type);
     my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
     if ($net->{queues} && $net->{queues} > 1 && $net->{model} eq 'virtio'){
        #Consider we have N queues, the number of vectors needed is 2*N + 2 (plus one config interrupt and control vq)
     my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
     if ($net->{queues} && $net->{queues} > 1 && $net->{model} eq 'virtio'){
        #Consider we have N queues, the number of vectors needed is 2*N + 2 (plus one config interrupt and control vq)
@@ -1844,7 +2031,7 @@ sub print_netdevice_full {
 }
 
 sub print_netdev_full {
 }
 
 sub print_netdev_full {
-    my ($vmid, $conf, $net, $netid, $hotplug) = @_;
+    my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
 
     my $i = '';
     if ($netid =~ m/^net(\d+)$/) {
 
     my $i = '';
     if ($netid =~ m/^net(\d+)$/) {
@@ -1860,7 +2047,9 @@ sub print_netdev_full {
         if length($ifname) >= 16;
 
     my $vhostparam = '';
         if length($ifname) >= 16;
 
     my $vhostparam = '';
-    $vhostparam = ',vhost=on' if $kernel_has_vhost_net && $net->{model} eq 'virtio';
+    if (is_native($arch)) {
+       $vhostparam = ',vhost=on' if kernel_has_vhost_net() && $net->{model} eq 'virtio';
+    }
 
     my $vmname = $conf->{name} || "vm$vmid";
 
 
     my $vmname = $conf->{name} || "vm$vmid";
 
@@ -1898,6 +2087,58 @@ sub print_cpu_device {
     return "$cpu-x86_64-cpu,id=cpu$id,socket-id=$current_socket,core-id=$current_core,thread-id=0";
 }
 
     return "$cpu-x86_64-cpu,id=cpu$id,socket-id=$current_socket,core-id=$current_core,thread-id=0";
 }
 
+my $vga_map = {
+    'cirrus' => 'cirrus-vga',
+    'std' => 'VGA',
+    'vmware' => 'vmware-svga',
+    'virtio' => 'virtio-vga',
+};
+
+sub print_vga_device {
+    my ($conf, $vga, $arch, $machine, $id, $qxlnum, $bridges) = @_;
+
+    my $type = $vga_map->{$vga->{type}};
+    if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
+       $type = 'virtio-gpu';
+    }
+    my $vgamem_mb = $vga->{memory};
+    if ($qxlnum) {
+       $type = $id ? 'qxl' : 'qxl-vga';
+    }
+    die "no devicetype for $vga->{type}\n" if !$type;
+
+    my $memory = "";
+    if ($vgamem_mb) {
+       if ($vga->{type} eq 'virtio') {
+           my $bytes = PVE::Tools::convert_size($vgamem_mb, "mb" => "b");
+           $memory = ",max_hostmem=$bytes";
+       } elsif ($qxlnum) {
+           # from https://www.spice-space.org/multiple-monitors.html
+           $memory = ",vgamem_mb=$vga->{memory}";
+           my $ram = $vgamem_mb * 4;
+           my $vram = $vgamem_mb * 2;
+           $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
+       } else {
+           $memory = ",vgamem_mb=$vga->{memory}";
+       }
+    } elsif ($qxlnum && $id) {
+       $memory = ",ram_size=67108864,vram_size=33554432";
+    }
+
+    my $q35 = machine_type_is_q35($conf);
+    my $vgaid = "vga" . ($id // '');
+    my $pciaddr;
+
+    if ($q35 && $vgaid eq 'vga') {
+       # the first display uses pcie.0 bus on q35 machines
+       $pciaddr = print_pcie_addr($vgaid, $bridges, $arch, $machine);
+    } else {
+       $pciaddr = print_pci_addr($vgaid, $bridges, $arch, $machine);
+    }
+
+    return "$type,id=${vgaid}${memory}${pciaddr}";
+}
+
 sub drive_is_cloudinit {
     my ($drive) = @_;
     return $drive->{file} =~ m@[:/]vm-\d+-cloudinit(?:\.$QEMU_FORMAT_RE)?$@;
 sub drive_is_cloudinit {
     my ($drive) = @_;
     return $drive->{file} =~ m@[:/]vm-\d+-cloudinit(?:\.$QEMU_FORMAT_RE)?$@;
@@ -1945,16 +2186,12 @@ sub parse_hostpci {
     my @idlist = split(/;/, $res->{host});
     delete $res->{host};
     foreach my $id (@idlist) {
     my @idlist = split(/;/, $res->{host});
     delete $res->{host};
     foreach my $id (@idlist) {
-       if ($id =~ /^$PCIRE$/) {
-           if (defined($2)) {
-               push @{$res->{pciid}}, { id => $1, function => $2 };
-           } else {
-               my $pcidevices = lspci($1);
-               $res->{pciid} = $pcidevices->{$1};
-           }
-       } else {
-           # should have been caught by parse_property_string already
-           die "failed to parse PCI id: $id\n";
+       if ($id =~ m/\./) { # full id 00:00.1
+           push @{$res->{pciid}}, {
+               id => $id,
+           };
+       } else { # partial id 00:00
+           $res->{pciid} = PVE::SysFSTools::lspci($id);
        }
     }
     return $res;
        }
     }
     return $res;
@@ -2210,6 +2447,28 @@ sub parse_watchdog {
     return $res;
 }
 
     return $res;
 }
 
+sub parse_guest_agent {
+    my ($value) = @_;
+
+    return {} if !defined($value->{agent});
+
+    my $res = eval { PVE::JSONSchema::parse_property_string($agent_fmt, $value->{agent}) };
+    warn $@ if $@;
+
+    # if the agent is disabled ignore the other potentially set properties
+    return {} if !$res->{enabled};
+    return $res;
+}
+
+sub parse_vga {
+    my ($value) = @_;
+
+    return {} if !$value;
+    my $res = eval { PVE::JSONSchema::parse_property_string($vga_fmt, $value) };
+    warn $@ if $@;
+    return $res;
+}
+
 PVE::JSONSchema::register_format('pve-qm-usb-device', \&verify_usb_device);
 sub verify_usb_device {
     my ($value, $noerr) = @_;
 PVE::JSONSchema::register_format('pve-qm-usb-device', \&verify_usb_device);
 sub verify_usb_device {
     my ($value, $noerr) = @_;
@@ -2226,7 +2485,7 @@ sub json_config_properties {
     my $prop = shift;
 
     foreach my $opt (keys %$confdesc) {
     my $prop = shift;
 
     foreach my $opt (keys %$confdesc) {
-       next if $opt eq 'parent' || $opt eq 'snaptime' || $opt eq 'vmstate';
+       next if $opt eq 'parent' || $opt eq 'snaptime' || $opt eq 'vmstate' || $opt eq 'runningmachine';
        $prop->{$opt} = $confdesc->{$opt};
     }
 
        $prop->{$opt} = $confdesc->{$opt};
     }
 
@@ -2276,15 +2535,6 @@ sub check_type {
     }
 }
 
     }
 }
 
-sub check_iommu_support{
-    #fixme : need to check IOMMU support
-    #http://www.linux-kvm.org/page/How_to_assign_devices_with_VT-d_in_KVM
-
-    my $iommu=1;
-    return $iommu;
-
-}
-
 sub touch_config {
     my ($vmid) = @_;
 
 sub touch_config {
     my ($vmid) = @_;
 
@@ -2601,6 +2851,8 @@ sub check_local_resources {
     $loc_res = 1 if $conf->{hostusb}; # old syntax
     $loc_res = 1 if $conf->{hostpci}; # old syntax
 
     $loc_res = 1 if $conf->{hostusb}; # old syntax
     $loc_res = 1 if $conf->{hostpci}; # old syntax
 
+    $loc_res = 1 if $conf->{ivshmem};
+
     foreach my $k (keys %$conf) {
        next if $k =~ m/^usb/ && ($conf->{$k} eq 'spice');
        # sockets are safe: they will recreated be on the target side post-migrate
     foreach my $k (keys %$conf) {
        next if $k =~ m/^usb/ && ($conf->{$k} eq 'spice');
        # sockets are safe: they will recreated be on the target side post-migrate
@@ -2677,7 +2929,7 @@ sub check_cmdline {
        my @param = split(/\0/, $line);
 
        my $cmd = $param[0];
        my @param = split(/\0/, $line);
 
        my $cmd = $param[0];
-       return if !$cmd || ($cmd !~ m|kvm$| && $cmd !~ m|qemu-system-x86_64$|);
+       return if !$cmd || ($cmd !~ m|kvm$| && $cmd !~ m@(?:^|/)qemu-system-[^/]+$@);
 
        for (my $i = 0; $i < scalar (@param); $i++) {
            my $p = $param[$i];
 
        for (my $i = 0; $i < scalar (@param); $i++) {
            my $p = $param[$i];
@@ -2763,6 +3015,58 @@ sub disksize {
     return $drive->{size};
 }
 
     return $drive->{size};
 }
 
+our $vmstatus_return_properties = {
+    vmid => get_standard_option('pve-vmid'),
+    status => {
+       description => "Qemu process status.",
+       type => 'string',
+       enum => ['stopped', 'running'],
+    },
+    maxmem => {
+       description => "Maximum memory in bytes.",
+       type => 'integer',
+       optional => 1,
+       renderer => 'bytes',
+    },
+    maxdisk => {
+       description => "Root disk size in bytes.",
+       type => 'integer',
+       optional => 1,
+       renderer => 'bytes',
+    },
+    name => {
+       description => "VM name.",
+       type => 'string',
+       optional => 1,
+    },
+    qmpstatus => {
+       description => "Qemu QMP agent status.",
+       type => 'string',
+       optional => 1,
+    },
+    pid => {
+       description => "PID of running qemu process.",
+       type => 'integer',
+       optional => 1,
+    },
+    uptime => {
+       description => "Uptime.",
+       type => 'integer',
+       optional => 1,
+       renderer => 'duration',
+    },
+    cpus => {
+       description => "Maximum usable CPUs.",
+       type => 'number',
+       optional => 1,
+    },
+    lock => {
+       description => "The current config lock, if any.",
+       type => 'string',
+       optional => 1,
+    }
+};
+
 my $last_proc_pid_stat;
 
 # get VM status information
 my $last_proc_pid_stat;
 
 # get VM status information
@@ -2788,7 +3092,7 @@ sub vmstatus {
        my $cfspath = PVE::QemuConfig->cfs_config_path($vmid);
        my $conf = PVE::Cluster::cfs_read_file($cfspath) || {};
 
        my $cfspath = PVE::QemuConfig->cfs_config_path($vmid);
        my $conf = PVE::Cluster::cfs_read_file($cfspath) || {};
 
-       my $d = {};
+       my $d = { vmid => $vmid };
        $d->{pid} = $list->{$vmid}->{pid};
 
        # fixme: better status?
        $d->{pid} = $list->{$vmid}->{pid};
 
        # fixme: better status?
@@ -2831,6 +3135,7 @@ sub vmstatus {
         $d->{template} = PVE::QemuConfig->is_template($conf);
 
        $d->{serial} = 1 if conf_has_serial($conf);
         $d->{template} = PVE::QemuConfig->is_template($conf);
 
        $d->{serial} = 1 if conf_has_serial($conf);
+       $d->{lock} = $conf->{lock} if $conf->{lock};
 
        $res->{$vmid} = $d;
     }
 
        $res->{$vmid} = $d;
     }
@@ -3044,11 +3349,115 @@ sub conf_has_serial {
 sub vga_conf_has_spice {
     my ($vga) = @_;
 
 sub vga_conf_has_spice {
     my ($vga) = @_;
 
-    return 0 if !$vga || $vga !~ m/^qxl([234])?$/;
+    my $vgaconf = parse_vga($vga);
+    my $vgatype = $vgaconf->{type};
+    return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/;
 
     return $1 || 1;
 }
 
 
     return $1 || 1;
 }
 
+my $host_arch; # FIXME: fix PVE::Tools::get_host_arch
+sub get_host_arch() {
+    $host_arch = (POSIX::uname())[4] if !$host_arch;
+    return $host_arch;
+}
+
+sub is_native($) {
+    my ($arch) = @_;
+    return get_host_arch() eq $arch;
+}
+
+my $default_machines = {
+    x86_64 => 'pc',
+    aarch64 => 'virt',
+};
+
+sub get_basic_machine_info {
+    my ($conf, $forcemachine) = @_;
+
+    my $arch = $conf->{arch} // get_host_arch();
+    my $machine = $forcemachine || $conf->{machine} || $default_machines->{$arch};
+    return ($arch, $machine);
+}
+
+sub get_ovmf_files($) {
+    my ($arch) = @_;
+
+    my $ovmf = $OVMF->{$arch}
+       or die "no OVMF images known for architecture '$arch'\n";
+
+    return @$ovmf;
+}
+
+my $Arch2Qemu = {
+    aarch64 => '/usr/bin/qemu-system-aarch64',
+    x86_64 => '/usr/bin/qemu-system-x86_64',
+};
+sub get_command_for_arch($) {
+    my ($arch) = @_;
+    return '/usr/bin/kvm' if is_native($arch);
+
+    my $cmd = $Arch2Qemu->{$arch}
+       or die "don't know how to emulate architecture '$arch'\n";
+    return $cmd;
+}
+
+sub get_cpu_options {
+    my ($conf, $arch, $kvm, $machine_type, $kvm_off, $kvmver, $winversion, $gpu_passthrough) = @_;
+
+    my $cpuFlags = [];
+    my $ostype = $conf->{ostype};
+
+    my $cpu = $kvm ? "kvm64" : "qemu64";
+    if ($arch eq 'aarch64') {
+       $cpu = 'cortex-a57';
+    }
+    my $hv_vendor_id;
+    if (my $cputype = $conf->{cpu}) {
+       my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype)
+           or die "Cannot parse cpu description: $cputype\n";
+       $cpu = $cpuconf->{cputype};
+       $kvm_off = 1 if $cpuconf->{hidden};
+       $hv_vendor_id = $cpuconf->{'hv-vendor-id'};
+
+       if (defined(my $flags = $cpuconf->{flags})) {
+           push @$cpuFlags, split(";", $flags);
+       }
+    }
+
+    push @$cpuFlags , '+lahf_lm' if $cpu eq 'kvm64' && $arch eq 'x86_64';
+
+    push @$cpuFlags , '-x2apic'
+       if $conf->{ostype} && $conf->{ostype} eq 'solaris';
+
+    push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32';
+
+    push @$cpuFlags, '-rdtscp' if $cpu =~ m/^Opteron/;
+
+    if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3) && $arch eq 'x86_64') {
+
+       push @$cpuFlags , '+kvm_pv_unhalt' if $kvm;
+       push @$cpuFlags , '+kvm_pv_eoi' if $kvm;
+    }
+
+    add_hyperv_enlightenments($cpuFlags, $winversion, $machine_type, $kvmver, $conf->{bios}, $gpu_passthrough, $hv_vendor_id) if $kvm;
+
+    push @$cpuFlags, 'enforce' if $cpu ne 'host' && $kvm && $arch eq 'x86_64';
+
+    push @$cpuFlags, 'kvm=off' if $kvm_off;
+
+    if (my $cpu_vendor = $cpu_vendor_list->{$cpu}) {
+       push @$cpuFlags, "vendor=${cpu_vendor}"
+           if $cpu_vendor ne 'default';
+    } elsif ($arch ne 'aarch64') {
+       die "internal error"; # should not happen
+    }
+
+    $cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags);
+
+    return ('-cpu', $cpu);
+}
+
 sub config_to_command {
     my ($storecfg, $vmid, $conf, $defaults, $forcemachine) = @_;
 
 sub config_to_command {
     my ($storecfg, $vmid, $conf, $defaults, $forcemachine) = @_;
 
@@ -3056,7 +3465,6 @@ sub config_to_command {
     my $globalFlags = [];
     my $machineFlags = [];
     my $rtcFlags = [];
     my $globalFlags = [];
     my $machineFlags = [];
     my $rtcFlags = [];
-    my $cpuFlags = [];
     my $devices = [];
     my $pciaddr = '';
     my $bridges = {};
     my $devices = [];
     my $pciaddr = '';
     my $bridges = {};
@@ -3064,9 +3472,15 @@ sub config_to_command {
     my $vernum = 0; # unknown
     my $ostype = $conf->{ostype};
     my $winversion = windows_version($ostype);
     my $vernum = 0; # unknown
     my $ostype = $conf->{ostype};
     my $winversion = windows_version($ostype);
-    my $kvm = $conf->{kvm} // 1;
+    my $kvm = $conf->{kvm};
 
 
-    die "KVM virtualisation configured, but not available. Either disable in VM configuration or enable in BIOS.\n" if (!$cpuinfo->{hvm} && $kvm);
+    my ($arch, $machine_type) = get_basic_machine_info($conf, $forcemachine);
+    $kvm //= 1 if is_native($arch);
+
+    if ($kvm) {
+       die "KVM virtualisation configured, but not available. Either disable in VM configuration or enable in BIOS.\n"
+           if !defined kvm_version();
+    }
 
     if ($kvmver =~ m/^(\d+)\.(\d+)$/) {
        $vernum = $1*1000000+$2*1000;
 
     if ($kvmver =~ m/^(\d+)\.(\d+)$/) {
        $vernum = $1*1000000+$2*1000;
@@ -3080,14 +3494,13 @@ sub config_to_command {
 
     my $q35 = machine_type_is_q35($conf);
     my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
 
     my $q35 = machine_type_is_q35($conf);
     my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
-    my $machine_type = $forcemachine || $conf->{machine};
     my $use_old_bios_files = undef;
     ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type);
 
     my $cpuunits = defined($conf->{cpuunits}) ?
             $conf->{cpuunits} : $defaults->{cpuunits};
 
     my $use_old_bios_files = undef;
     ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type);
 
     my $cpuunits = defined($conf->{cpuunits}) ?
             $conf->{cpuunits} : $defaults->{cpuunits};
 
-    push @$cmd, '/usr/bin/kvm';
+    push @$cmd, get_command_for_arch($arch);
 
     push @$cmd, '-id', $vmid;
 
 
     push @$cmd, '-id', $vmid;
 
@@ -3101,6 +3514,10 @@ sub config_to_command {
     push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server,nowait";
     push @$cmd, '-mon', "chardev=qmp,mode=control";
 
     push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server,nowait";
     push @$cmd, '-mon', "chardev=qmp,mode=control";
 
+    if (qemu_machine_feature_enabled($machine_type, $kvmver, 2, 12)) {
+       push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
+       push @$cmd, '-mon', "chardev=qmp-event,mode=control";
+    }
 
     push @$cmd, '-pidfile' , pidfile_name($vmid);
 
 
     push @$cmd, '-pidfile' , pidfile_name($vmid);
 
@@ -3110,8 +3527,13 @@ sub config_to_command {
        push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
     }
 
        push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
     }
 
+    if ($conf->{vmgenid}) {
+       push @$devices, '-device', 'vmgenid,guid='.$conf->{vmgenid};
+    }
+
+    my ($ovmf_code, $ovmf_vars) = get_ovmf_files($arch);
     if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
     if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
-       die "uefi base image not found\n" if ! -f $OVMF_CODE;
+       die "uefi base image not found\n" if ! -f $ovmf_code;
 
        my $path;
        my $format;
 
        my $path;
        my $format;
@@ -3133,28 +3555,30 @@ sub config_to_command {
        } else {
            warn "no efidisk configured! Using temporary efivars disk.\n";
            $path = "/tmp/$vmid-ovmf.fd";
        } else {
            warn "no efidisk configured! Using temporary efivars disk.\n";
            $path = "/tmp/$vmid-ovmf.fd";
-           PVE::Tools::file_copy($OVMF_VARS, $path, -s $OVMF_VARS);
+           PVE::Tools::file_copy($ovmf_vars, $path, -s $ovmf_vars);
            $format = 'raw';
        }
 
            $format = 'raw';
        }
 
-       push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly,file=$OVMF_CODE";
+       push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly,file=$ovmf_code";
        push @$cmd, '-drive', "if=pflash,unit=1,format=$format,id=drive-efidisk0,file=$path";
     }
 
 
     # add usb controllers
        push @$cmd, '-drive', "if=pflash,unit=1,format=$format,id=drive-efidisk0,file=$path";
     }
 
 
     # add usb controllers
-    my @usbcontrollers = PVE::QemuServer::USB::get_usb_controllers($conf, $bridges, $q35, $usbdesc->{format}, $MAX_USB_DEVICES);
+    my @usbcontrollers = PVE::QemuServer::USB::get_usb_controllers($conf, $bridges, $arch, $machine_type, $usbdesc->{format}, $MAX_USB_DEVICES);
     push @$devices, @usbcontrollers if @usbcontrollers;
     push @$devices, @usbcontrollers if @usbcontrollers;
-    my $vga = $conf->{vga};
+    my $vga = parse_vga($conf->{vga});
 
 
-    my $qxlnum = vga_conf_has_spice($vga);
-    $vga = 'qxl' if $qxlnum;
+    my $qxlnum = vga_conf_has_spice($conf->{vga});
+    $vga->{type} = 'qxl' if $qxlnum;
 
 
-    if (!$vga) {
-       if (qemu_machine_feature_enabled($machine_type, $kvmver, 2, 9)) {
-           $vga = (!$winversion || $winversion >= 6) ? 'std' : 'cirrus';
+    if (!$vga->{type}) {
+       if ($arch eq 'aarch64') {
+           $vga->{type} = 'virtio';
+       } elsif (qemu_machine_feature_enabled($machine_type, $kvmver, 2, 9)) {
+           $vga->{type} = (!$winversion || $winversion >= 6) ? 'std' : 'cirrus';
        } else {
        } else {
-           $vga = ($winversion >= 6) ? 'std' : 'cirrus';
+           $vga->{type} = ($winversion >= 6) ? 'std' : 'cirrus';
        }
     }
 
        }
     }
 
@@ -3165,10 +3589,14 @@ sub config_to_command {
     } else {
        $tablet = $defaults->{tablet};
        $tablet = 0 if $qxlnum; # disable for spice because it is not needed
     } else {
        $tablet = $defaults->{tablet};
        $tablet = 0 if $qxlnum; # disable for spice because it is not needed
-       $tablet = 0 if $vga =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
+       $tablet = 0 if $vga->{type} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
     }
 
     }
 
-    push @$devices, '-device', print_tabletdevice_full($conf) if $tablet;
+    if ($tablet) {
+       push @$devices, '-device', print_tabletdevice_full($conf, $arch) if $tablet;
+       my $kbd = print_keyboarddevice_full($conf, $arch);
+       push @$devices, '-device', $kbd if defined($kbd);
+    }
 
     my $kvm_off = 0;
     my $gpu_passthrough;
 
     my $kvm_off = 0;
     my $gpu_passthrough;
@@ -3179,11 +3607,17 @@ sub config_to_command {
        next if !$d;
 
        my $pcie = $d->{pcie};
        next if !$d;
 
        my $pcie = $d->{pcie};
-       if($pcie){
+       if ($pcie) {
            die "q35 machine model is not enabled" if !$q35;
            die "q35 machine model is not enabled" if !$q35;
-           $pciaddr = print_pcie_addr("hostpci$i");
-       }else{
-           $pciaddr = print_pci_addr("hostpci$i", $bridges);
+           # win7 wants to have the pcie devices directly on the pcie bus
+           # instead of in the root port
+           if ($winversion == 7) {
+               $pciaddr = print_pcie_addr("hostpci${i}bus0");
+           } else {
+               $pciaddr = print_pcie_addr("hostpci$i");
+           }
+       } else {
+           $pciaddr = print_pci_addr("hostpci$i", $bridges, $arch, $machine_type);
        }
 
        my $rombar = defined($d->{rombar}) && !$d->{rombar} ? ',rombar=0' : '';
        }
 
        my $rombar = defined($d->{rombar}) && !$d->{rombar} ? ',rombar=0' : '';
@@ -3193,7 +3627,7 @@ sub config_to_command {
        if ($d->{'x-vga'}) {
            $xvga = ',x-vga=on';
            $kvm_off = 1;
        if ($d->{'x-vga'}) {
            $xvga = ',x-vga=on';
            $kvm_off = 1;
-           $vga = 'none';
+           $vga->{type} = 'none' if !defined($conf->{vga});
            $gpu_passthrough = 1;
 
            if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
            $gpu_passthrough = 1;
 
            if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
@@ -3202,6 +3636,14 @@ sub config_to_command {
        }
        my $pcidevices = $d->{pciid};
        my $multifunction = 1 if @$pcidevices > 1;
        }
        my $pcidevices = $d->{pciid};
        my $multifunction = 1 if @$pcidevices > 1;
+       my $sysfspath;
+       if ($d->{mdev} && scalar(@$pcidevices) == 1) {
+           my $id = $pcidevices->[0]->{id};
+           my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i);
+           $sysfspath = "/sys/bus/pci/devices/0000:$id/$uuid";
+       } elsif ($d->{mdev}) {
+           warn "ignoring mediated device with multifunction device\n";
+       }
 
        my $j=0;
         foreach my $pcidevice (@$pcidevices) {
 
        my $j=0;
         foreach my $pcidevice (@$pcidevices) {
@@ -3210,7 +3652,13 @@ sub config_to_command {
            $id .= ".$j" if $multifunction;
            my $addr = $pciaddr;
            $addr .= ".$j" if $multifunction;
            $id .= ".$j" if $multifunction;
            my $addr = $pciaddr;
            $addr .= ".$j" if $multifunction;
-           my $devicestr = "vfio-pci,host=$pcidevice->{id}.$pcidevice->{function},id=$id$addr";
+           my $devicestr = "vfio-pci";
+           if ($sysfspath) {
+               $devicestr .= ",sysfsdev=$sysfspath";
+           } else {
+               $devicestr .= ",host=$pcidevice->{id}";
+           }
+           $devicestr .= ",id=$id$addr";
 
            if($j == 0){
                $devicestr .= "$rombar$xvga";
 
            if($j == 0){
                $devicestr .= "$rombar$xvga";
@@ -3232,7 +3680,14 @@ sub config_to_command {
            if ($path eq 'socket') {
                my $socket = "/var/run/qemu-server/${vmid}.serial$i";
                push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server,nowait";
            if ($path eq 'socket') {
                my $socket = "/var/run/qemu-server/${vmid}.serial$i";
                push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server,nowait";
-               push @$devices, '-device', "isa-serial,chardev=serial$i";
+               # On aarch64, serial0 is the UART device. Qemu only allows
+               # connecting UART devices via the '-serial' command line, as
+               # the device has a fixed slot on the hardware...
+               if ($arch eq 'aarch64' && $i == 0) {
+                   push @$devices, '-serial', "chardev:serial$i";
+               } else {
+                   push @$devices, '-device', "isa-serial,chardev=serial$i";
+               }
            } else {
                die "no such serial device\n" if ! -c $path;
                push @$devices, '-chardev', "tty,id=serial$i,path=$path";
            } else {
                die "no such serial device\n" if ! -c $path;
                push @$devices, '-chardev', "tty,id=serial$i,path=$path";
@@ -3296,12 +3751,12 @@ sub config_to_command {
 
     push @$cmd, '-no-reboot' if  defined($conf->{reboot}) && $conf->{reboot} == 0;
 
 
     push @$cmd, '-no-reboot' if  defined($conf->{reboot}) && $conf->{reboot} == 0;
 
-    push @$cmd, '-vga', $vga if $vga && $vga !~ m/^serial\d+$/; # for kvm 77 and later
-
-    if ($vga && $vga !~ m/^serial\d+$/ && $vga ne 'none'){
+    if ($vga->{type} && $vga->{type} !~ m/^serial\d+$/ && $vga->{type} ne 'none'){
+       push @$devices, '-device', print_vga_device($conf, $vga, $arch, $machine_type, undef, $qxlnum, $bridges);
        my $socket = vnc_socket($vmid);
        push @$cmd,  '-vnc', "unix:$socket,x509,password";
     } else {
        my $socket = vnc_socket($vmid);
        push @$cmd,  '-vnc', "unix:$socket,x509,password";
     } else {
+       push @$cmd, '-vga', 'none' if $vga->{type} eq 'none';
        push @$cmd, '-nographic';
     }
 
        push @$cmd, '-nographic';
     }
 
@@ -3340,48 +3795,7 @@ sub config_to_command {
        push @$rtcFlags, 'base=localtime';
     }
 
        push @$rtcFlags, 'base=localtime';
     }
 
-    my $cpu = $kvm ? "kvm64" : "qemu64";
-    if (my $cputype = $conf->{cpu}) {
-       my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype)
-           or die "Cannot parse cpu description: $cputype\n";
-       $cpu = $cpuconf->{cputype};
-       $kvm_off = 1 if $cpuconf->{hidden};
-
-       if (defined(my $flags = $cpuconf->{flags})) {
-           push @$cpuFlags, split(";", $flags);
-       }
-    }
-
-    push @$cpuFlags , '+lahf_lm' if $cpu eq 'kvm64';
-
-    push @$cpuFlags , '-x2apic'
-       if $conf->{ostype} && $conf->{ostype} eq 'solaris';
-
-    push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32';
-
-    push @$cpuFlags, '-rdtscp' if $cpu =~ m/^Opteron/;
-
-    if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
-
-       push @$cpuFlags , '+kvm_pv_unhalt' if $kvm;
-       push @$cpuFlags , '+kvm_pv_eoi' if $kvm;
-    }
-
-    add_hyperv_enlightenments($cpuFlags, $winversion, $machine_type, $kvmver, $conf->{bios}, $gpu_passthrough) if $kvm;
-
-    push @$cpuFlags, 'enforce' if $cpu ne 'host' && $kvm;
-
-    push @$cpuFlags, 'kvm=off' if $kvm_off;
-
-    my $cpu_vendor = $cpu_vendor_list->{$cpu} ||
-       die "internal error"; # should not happen
-
-    push @$cpuFlags, "vendor=${cpu_vendor}"
-       if $cpu_vendor ne 'default';
-
-    $cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags);
-
-    push @$cmd, '-cpu', $cpu;
+    push @$cmd, get_cpu_options($conf, $arch, $kvm, $machine_type, $kvm_off, $kvmver, $winversion, $gpu_passthrough);
 
     PVE::QemuServer::Memory::config($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
     
 
     PVE::QemuServer::Memory::config($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
     
@@ -3394,9 +3808,9 @@ sub config_to_command {
     #push @$cmd, '-soundhw', 'es1370';
     #push @$cmd, '-soundhw', $soundhw if $soundhw;
 
     #push @$cmd, '-soundhw', 'es1370';
     #push @$cmd, '-soundhw', $soundhw if $soundhw;
 
-    if($conf->{agent}) {
+    if (parse_guest_agent($conf)->{enabled}) {
        my $qgasocket = qmp_socket($vmid, 1);
        my $qgasocket = qmp_socket($vmid, 1);
-       my $pciaddr = print_pci_addr("qga0", $bridges);
+       my $pciaddr = print_pci_addr("qga0", $bridges, $arch, $machine_type);
        push @$devices, '-chardev', "socket,path=$qgasocket,server,nowait,id=qga0";
        push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
        push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
        push @$devices, '-chardev', "socket,path=$qgasocket,server,nowait,id=qga0";
        push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
        push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
@@ -3408,17 +3822,21 @@ sub config_to_command {
        if ($qxlnum > 1) {
            if ($winversion){
                for(my $i = 1; $i < $qxlnum; $i++){
        if ($qxlnum > 1) {
            if ($winversion){
                for(my $i = 1; $i < $qxlnum; $i++){
-                   my $pciaddr = print_pci_addr("vga$i", $bridges);
-                   push @$cmd, '-device', "qxl,id=vga$i,ram_size=67108864,vram_size=33554432$pciaddr";
+                   push @$devices, '-device', print_vga_device($conf, $vga, $arch, $machine_type, $i, $qxlnum, $bridges);
                }
            } else {
                # assume other OS works like Linux
                }
            } else {
                # assume other OS works like Linux
-               push @$cmd, '-global', 'qxl-vga.ram_size=134217728';
-               push @$cmd, '-global', 'qxl-vga.vram_size=67108864';
+               my ($ram, $vram) = ("134217728", "67108864");
+               if ($vga->{memory}) {
+                   $ram = PVE::Tools::convert_size($qxlnum*4*$vga->{memory}, 'mb' => 'b');
+                   $vram = PVE::Tools::convert_size($qxlnum*2*$vga->{memory}, 'mb' => 'b');
+               }
+               push @$cmd, '-global', "qxl-vga.ram_size=$ram";
+               push @$cmd, '-global', "qxl-vga.vram_size=$vram";
            }
        }
 
            }
        }
 
-       my $pciaddr = print_pci_addr("spice", $bridges);
+       my $pciaddr = print_pci_addr("spice", $bridges, $arch, $machine_type);
 
        my $nodename = PVE::INotify::nodename();
        my $pfamily = PVE::Tools::get_host_address_family($nodename);
 
        my $nodename = PVE::INotify::nodename();
        my $pfamily = PVE::Tools::get_host_address_family($nodename);
@@ -3436,13 +3854,13 @@ sub config_to_command {
 
     # enable balloon by default, unless explicitly disabled
     if (!defined($conf->{balloon}) || $conf->{balloon}) {
 
     # enable balloon by default, unless explicitly disabled
     if (!defined($conf->{balloon}) || $conf->{balloon}) {
-       $pciaddr = print_pci_addr("balloon0", $bridges);
+       $pciaddr = print_pci_addr("balloon0", $bridges, $arch, $machine_type);
        push @$devices, '-device', "virtio-balloon-pci,id=balloon0$pciaddr";
     }
 
     if ($conf->{watchdog}) {
        my $wdopts = parse_watchdog($conf->{watchdog});
        push @$devices, '-device', "virtio-balloon-pci,id=balloon0$pciaddr";
     }
 
     if ($conf->{watchdog}) {
        my $wdopts = parse_watchdog($conf->{watchdog});
-       $pciaddr = print_pci_addr("watchdog", $bridges);
+       $pciaddr = print_pci_addr("watchdog", $bridges, $arch, $machine_type);
        my $watchdog = $wdopts->{model} || 'i6300esb';
        push @$devices, '-device', "$watchdog$pciaddr";
        push @$devices, '-watchdog-action', $wdopts->{action} if $wdopts->{action};
        my $watchdog = $wdopts->{model} || 'i6300esb';
        push @$devices, '-device', "$watchdog$pciaddr";
        push @$devices, '-watchdog-action', $wdopts->{action} if $wdopts->{action};
@@ -3490,7 +3908,7 @@ sub config_to_command {
 
            my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $drive);
 
 
            my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $drive);
 
-           $pciaddr = print_pci_addr("$controller_prefix$controller", $bridges);
+           $pciaddr = print_pci_addr("$controller_prefix$controller", $bridges, $arch, $machine_type);
            my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ? "virtio-scsi-pci" : $scsihw;
 
            my $iothread = '';
            my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ? "virtio-scsi-pci" : $scsihw;
 
            my $iothread = '';
@@ -3512,14 +3930,14 @@ sub config_to_command {
 
         if ($drive->{interface} eq 'sata') {
            my $controller = int($drive->{index} / $MAX_SATA_DISKS);
 
         if ($drive->{interface} eq 'sata') {
            my $controller = int($drive->{index} / $MAX_SATA_DISKS);
-           $pciaddr = print_pci_addr("ahci$controller", $bridges);
+           $pciaddr = print_pci_addr("ahci$controller", $bridges, $arch, $machine_type);
            push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr" if !$ahcicontroller->{$controller};
            $ahcicontroller->{$controller}=1;
         }
 
        my $drive_cmd = print_drive_full($storecfg, $vmid, $drive);
        push @$devices, '-drive',$drive_cmd;
            push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr" if !$ahcicontroller->{$controller};
            $ahcicontroller->{$controller}=1;
         }
 
        my $drive_cmd = print_drive_full($storecfg, $vmid, $drive);
        push @$devices, '-drive',$drive_cmd;
-       push @$devices, '-device', print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges);
+       push @$devices, '-device', print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type);
     });
 
     for (my $i = 0; $i < $MAX_NETS; $i++) {
     });
 
     for (my $i = 0; $i < $MAX_NETS; $i++) {
@@ -3534,13 +3952,30 @@ sub config_to_command {
             $bootindex_hash->{n} += 1;
          }
 
             $bootindex_hash->{n} += 1;
          }
 
-         my $netdevfull = print_netdev_full($vmid,$conf,$d,"net$i");
+         my $netdevfull = print_netdev_full($vmid, $conf, $arch, $d, "net$i");
          push @$devices, '-netdev', $netdevfull;
 
          push @$devices, '-netdev', $netdevfull;
 
-         my $netdevicefull = print_netdevice_full($vmid, $conf, $d, "net$i", $bridges, $use_old_bios_files);
+         my $netdevicefull = print_netdevice_full($vmid, $conf, $d, "net$i", $bridges, $use_old_bios_files, $arch, $machine_type);
          push @$devices, '-device', $netdevicefull;
     }
 
          push @$devices, '-device', $netdevicefull;
     }
 
+    if ($conf->{ivshmem}) {
+       my $ivshmem = PVE::JSONSchema::parse_property_string($ivshmem_fmt, $conf->{ivshmem});
+
+       my $bus;
+       if ($q35) {
+           $bus = print_pcie_addr("ivshmem");
+       } else {
+           $bus = print_pci_addr("ivshmem", $bridges, $arch, $machine_type);
+       }
+
+       my $ivshmem_name = $ivshmem->{name} // $vmid;
+       my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
+
+       push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
+       push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path,size=$ivshmem->{size}M";
+    }
+
     if (!$q35) {
        # add pci bridges
         if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
     if (!$q35) {
        # add pci bridges
         if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
@@ -3551,17 +3986,11 @@ sub config_to_command {
        $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
 
        while (my ($k, $v) = each %$bridges) {
        $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
 
        while (my ($k, $v) = each %$bridges) {
-           $pciaddr = print_pci_addr("pci.$k");
+           $pciaddr = print_pci_addr("pci.$k", undef, $arch, $machine_type);
            unshift @$devices, '-device', "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr" if $k > 0;
        }
     }
 
            unshift @$devices, '-device', "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr" if $k > 0;
        }
     }
 
-    # add custom args
-    if ($conf->{args}) {
-       my $aa = PVE::Tools::split_args($conf->{args});
-       push @$cmd, @$aa;
-    }
-
     push @$cmd, @$devices;
     push @$cmd, '-rtc', join(',', @$rtcFlags)
        if scalar(@$rtcFlags);
     push @$cmd, @$devices;
     push @$cmd, '-rtc', join(',', @$rtcFlags)
        if scalar(@$rtcFlags);
@@ -3570,6 +3999,18 @@ sub config_to_command {
     push @$cmd, '-global', join(',', @$globalFlags)
        if scalar(@$globalFlags);
 
     push @$cmd, '-global', join(',', @$globalFlags)
        if scalar(@$globalFlags);
 
+    if (my $vmstate = $conf->{vmstate}) {
+       my $statepath = PVE::Storage::path($storecfg, $vmstate);
+       PVE::Storage::activate_volumes($storecfg, [$vmstate]);
+       push @$cmd, '-loadstate', $statepath;
+    }
+
+    # add custom args
+    if ($conf->{args}) {
+       my $aa = PVE::Tools::split_args($conf->{args});
+       push @$cmd, @$aa;
+    }
+
     return wantarray ? ($cmd, $vollist, $spice_port) : $cmd;
 }
 
     return wantarray ? ($cmd, $vollist, $spice_port) : $cmd;
 }
 
@@ -3587,9 +4028,10 @@ sub spice_port {
 }
 
 sub qmp_socket {
 }
 
 sub qmp_socket {
-    my ($vmid, $qga) = @_;
+    my ($vmid, $qga, $name) = @_;
     my $sockettype = $qga ? 'qga' : 'qmp';
     my $sockettype = $qga ? 'qga' : 'qmp';
-    return "${var_run_tmpdir}/$vmid.$sockettype";
+    my $ext = $name ? '-'.$name : '';
+    return "${var_run_tmpdir}/$vmid$ext.$sockettype";
 }
 
 sub pidfile_name {
 }
 
 sub pidfile_name {
@@ -3648,18 +4090,22 @@ sub vm_devices_list {
 }
 
 sub vm_deviceplug {
 }
 
 sub vm_deviceplug {
-    my ($storecfg, $conf, $vmid, $deviceid, $device) = @_;
+    my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
 
     my $q35 = machine_type_is_q35($conf);
 
     my $devices_list = vm_devices_list($vmid);
     return 1 if defined($devices_list->{$deviceid});
 
 
     my $q35 = machine_type_is_q35($conf);
 
     my $devices_list = vm_devices_list($vmid);
     return 1 if defined($devices_list->{$deviceid});
 
-    qemu_add_pci_bridge($storecfg, $conf, $vmid, $deviceid); # add PCI bridge if we need it for the device
+    qemu_add_pci_bridge($storecfg, $conf, $vmid, $deviceid, $arch, $machine_type); # add PCI bridge if we need it for the device
 
     if ($deviceid eq 'tablet') {
 
 
     if ($deviceid eq 'tablet') {
 
-       qemu_deviceadd($vmid, print_tabletdevice_full($conf));
+       qemu_deviceadd($vmid, print_tabletdevice_full($conf, $arch));
+
+    } elsif ($deviceid eq 'keyboard') {
+
+       qemu_deviceadd($vmid, print_keyboarddevice_full($conf, $arch));
 
     } elsif ($deviceid =~ m/^usb(\d+)$/) {
 
 
     } elsif ($deviceid =~ m/^usb(\d+)$/) {
 
@@ -3674,7 +4120,7 @@ sub vm_deviceplug {
        qemu_iothread_add($vmid, $deviceid, $device);
 
         qemu_driveadd($storecfg, $vmid, $device);
        qemu_iothread_add($vmid, $deviceid, $device);
 
         qemu_driveadd($storecfg, $vmid, $device);
-        my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device);
+        my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device, $arch, $machine_type);
 
         qemu_deviceadd($vmid, $devicefull);
        eval { qemu_deviceaddverify($vmid, $deviceid); };
 
         qemu_deviceadd($vmid, $devicefull);
        eval { qemu_deviceaddverify($vmid, $deviceid); };
@@ -3688,7 +4134,7 @@ sub vm_deviceplug {
 
 
         my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : "lsi";
 
 
         my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : "lsi";
-        my $pciaddr = print_pci_addr($deviceid);
+        my $pciaddr = print_pci_addr($deviceid, undef, $arch, $machine_type);
        my $scsihw_type = $scsihw eq 'virtio-scsi-single' ? "virtio-scsi-pci" : $scsihw;
 
         my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
        my $scsihw_type = $scsihw eq 'virtio-scsi-single' ? "virtio-scsi-pci" : $scsihw;
 
         my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
@@ -3707,10 +4153,10 @@ sub vm_deviceplug {
 
     } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
 
 
     } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
 
-        qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device);
+        qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device, $arch, $machine_type);
         qemu_driveadd($storecfg, $vmid, $device);
 
         qemu_driveadd($storecfg, $vmid, $device);
 
-       my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device);
+       my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device, $arch, $machine_type);
        eval { qemu_deviceadd($vmid, $devicefull); };
        if (my $err = $@) {
            eval { qemu_drivedel($vmid, $deviceid); };
        eval { qemu_deviceadd($vmid, $devicefull); };
        if (my $err = $@) {
            eval { qemu_drivedel($vmid, $deviceid); };
@@ -3720,25 +4166,28 @@ sub vm_deviceplug {
 
     } elsif ($deviceid =~ m/^(net)(\d+)$/) {
 
 
     } elsif ($deviceid =~ m/^(net)(\d+)$/) {
 
-        return undef if !qemu_netdevadd($vmid, $conf, $device, $deviceid);
+       return undef if !qemu_netdevadd($vmid, $conf, $arch, $device, $deviceid);
 
 
-        my $machine_type = PVE::QemuServer::qemu_machine_pxe($vmid, $conf); 
-        my $use_old_bios_files = undef;
-        ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type);
+       my $machine_type = PVE::QemuServer::qemu_machine_pxe($vmid, $conf);
+       my $use_old_bios_files = undef;
+       ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type);
 
 
-        my $netdevicefull = print_netdevice_full($vmid, $conf, $device, $deviceid, undef, $use_old_bios_files);
-        qemu_deviceadd($vmid, $netdevicefull);
-        eval { qemu_deviceaddverify($vmid, $deviceid); };
+       my $netdevicefull = print_netdevice_full($vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type);
+       qemu_deviceadd($vmid, $netdevicefull);
+       eval {
+           qemu_deviceaddverify($vmid, $deviceid);
+           qemu_set_link_status($vmid, $deviceid, !$device->{link_down});
+       };
        if (my $err = $@) {
            eval { qemu_netdevdel($vmid, $deviceid); };
            warn $@ if $@;
            die $err;
        if (my $err = $@) {
            eval { qemu_netdevdel($vmid, $deviceid); };
            warn $@ if $@;
            die $err;
-        }
+       }
 
     } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
 
        my $bridgeid = $2;
 
     } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
 
        my $bridgeid = $2;
-       my $pciaddr = print_pci_addr($deviceid);
+       my $pciaddr = print_pci_addr($deviceid, undef, $arch, $machine_type);
        my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
 
        qemu_deviceadd($vmid, $devicefull);
        my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
 
        qemu_deviceadd($vmid, $devicefull);
@@ -3760,7 +4209,7 @@ sub vm_deviceunplug {
 
     die "can't unplug bootdisk" if $conf->{bootdisk} && $conf->{bootdisk} eq $deviceid;
 
 
     die "can't unplug bootdisk" if $conf->{bootdisk} && $conf->{bootdisk} eq $deviceid;
 
-    if ($deviceid eq 'tablet') {
+    if ($deviceid eq 'tablet' || $deviceid eq 'keyboard') {
 
        qemu_devicedel($vmid, $deviceid);
 
 
        qemu_devicedel($vmid, $deviceid);
 
@@ -3832,7 +4281,11 @@ sub qemu_iothread_add {
 sub qemu_iothread_del {
     my($conf, $vmid, $deviceid) = @_;
 
 sub qemu_iothread_del {
     my($conf, $vmid, $deviceid) = @_;
 
-    my $device = parse_drive($deviceid, $conf->{$deviceid});
+    my $confid = $deviceid;
+    if ($deviceid =~ m/^(?:virtioscsi|scsihw)(\d+)$/) {
+       $confid = 'scsi' . $1;
+    }
+    my $device = parse_drive($confid, $conf->{$confid});
     if ($device->{iothread}) {
        my $iothreads = vm_iothreads_list($vmid);
        qemu_objectdel($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
     if ($device->{iothread}) {
        my $iothreads = vm_iothreads_list($vmid);
        qemu_objectdel($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
@@ -3911,7 +4364,7 @@ sub qemu_devicedelverify {
 }
 
 sub qemu_findorcreatescsihw {
 }
 
 sub qemu_findorcreatescsihw {
-    my ($storecfg, $conf, $vmid, $device) = @_;
+    my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
 
     my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
 
 
     my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
 
@@ -3919,7 +4372,7 @@ sub qemu_findorcreatescsihw {
     my $devices_list = vm_devices_list($vmid);
 
     if(!defined($devices_list->{$scsihwid})) {
     my $devices_list = vm_devices_list($vmid);
 
     if(!defined($devices_list->{$scsihwid})) {
-       vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device);
+       vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device, $arch, $machine_type);
     }
 
     return 1;
     }
 
     return 1;
@@ -3955,13 +4408,13 @@ sub qemu_deletescsihw {
 }
 
 sub qemu_add_pci_bridge {
 }
 
 sub qemu_add_pci_bridge {
-    my ($storecfg, $conf, $vmid, $device) = @_;
+    my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
 
     my $bridges = {};
 
     my $bridgeid;
 
 
     my $bridges = {};
 
     my $bridgeid;
 
-    print_pci_addr($device, $bridges);
+    print_pci_addr($device, $bridges, $arch, $machine_type);
 
     while (my ($k, $v) = each %$bridges) {
        $bridgeid = $k;
 
     while (my ($k, $v) = each %$bridges) {
        $bridgeid = $k;
@@ -3972,7 +4425,7 @@ sub qemu_add_pci_bridge {
     my $devices_list = vm_devices_list($vmid);
 
     if (!defined($devices_list->{$bridge})) {
     my $devices_list = vm_devices_list($vmid);
 
     if (!defined($devices_list->{$bridge})) {
-       vm_deviceplug($storecfg, $conf, $vmid, $bridge);
+       vm_deviceplug($storecfg, $conf, $vmid, $bridge, $arch, $machine_type);
     }
 
     return 1;
     }
 
     return 1;
@@ -3986,9 +4439,9 @@ sub qemu_set_link_status {
 }
 
 sub qemu_netdevadd {
 }
 
 sub qemu_netdevadd {
-    my ($vmid, $conf, $device, $deviceid) = @_;
+    my ($vmid, $conf, $arch, $device, $deviceid) = @_;
 
 
-    my $netdev = print_netdev_full($vmid, $conf, $device, $deviceid, 1);
+    my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
     my %options =  split(/[=,]/, $netdev);
 
     vm_mon_cmd($vmid, "netdev_add",  %options);
     my %options =  split(/[=,]/, $netdev);
 
     vm_mon_cmd($vmid, "netdev_add",  %options);
@@ -4002,7 +4455,7 @@ sub qemu_netdevdel {
 }
 
 sub qemu_usb_hotplug {
 }
 
 sub qemu_usb_hotplug {
-    my ($storecfg, $conf, $vmid, $deviceid, $device) = @_;
+    my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
 
     return if !$device;
 
 
     return if !$device;
 
@@ -4015,7 +4468,7 @@ sub qemu_usb_hotplug {
        my $devicelist = vm_devices_list($vmid);
 
        if (!$devicelist->{xhci}) {
        my $devicelist = vm_devices_list($vmid);
 
        if (!$devicelist->{xhci}) {
-           my $pciaddr = print_pci_addr("xhci");
+           my $pciaddr = print_pci_addr("xhci", undef, $arch, $machine_type);
            qemu_deviceadd($vmid, "nec-usb-xhci,id=xhci$pciaddr");
        }
     }
            qemu_deviceadd($vmid, "nec-usb-xhci,id=xhci$pciaddr");
        }
     }
@@ -4023,7 +4476,7 @@ sub qemu_usb_hotplug {
     $d->{usb3} = $device->{usb3};
 
     # add the new one
     $d->{usb3} = $device->{usb3};
 
     # add the new one
-    vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $d);
+    vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $d, $arch, $machine_type);
 }
 
 sub qemu_cpu_hotplug {
 }
 
 sub qemu_cpu_hotplug {
@@ -4185,7 +4638,7 @@ sub qemu_volume_snapshot {
     my $running = check_running($vmid);
 
     if ($running && do_snapshots_with_qemu($storecfg, $volid)){
     my $running = check_running($vmid);
 
     if ($running && do_snapshots_with_qemu($storecfg, $volid)){
-       vm_mon_cmd($vmid, "snapshot-drive", device => $deviceid, name => $snap);
+       vm_mon_cmd($vmid, 'blockdev-snapshot-internal-sync', device => $deviceid, name => $snap);
     } else {
        PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
     }
     } else {
        PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
     }
@@ -4196,8 +4649,18 @@ sub qemu_volume_snapshot_delete {
 
     my $running = check_running($vmid);
 
 
     my $running = check_running($vmid);
 
+    if($running) {
+
+       $running = undef;
+       my $conf = PVE::QemuConfig->load_config($vmid);
+       foreach_drive($conf, sub {
+           my ($ds, $drive) = @_;
+           $running = 1 if $drive->{file} eq $volid;
+       });
+    }
+
     if ($running && do_snapshots_with_qemu($storecfg, $volid)){
     if ($running && do_snapshots_with_qemu($storecfg, $volid)){
-       vm_mon_cmd($vmid, "delete-drive-snapshot", device => $deviceid, name => $snap);
+       vm_mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync', device => $deviceid, name => $snap);
     } else {
        PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
     }
     } else {
        PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
     }
@@ -4237,6 +4700,7 @@ my $fast_plug_option = {
     'description' => 1,
     'protection' => 1,
     'vmstatestorage' => 1,
     'description' => 1,
     'protection' => 1,
     'vmstatestorage' => 1,
+    'hookscript' => 1,
 };
 
 # hotplug changes in [PENDING]
 };
 
 # hotplug changes in [PENDING]
@@ -4247,6 +4711,7 @@ sub vmconfig_hotplug_pending {
     my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
 
     my $defaults = load_defaults();
     my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
 
     my $defaults = load_defaults();
+    my ($arch, $machine_type) = get_basic_machine_info($conf, undef);
 
     # commit values which do not have any impact on running VM first
     # Note: those option cannot raise errors, we we do not care about
 
     # commit values which do not have any impact on running VM first
     # Note: those option cannot raise errors, we we do not care about
@@ -4282,9 +4747,12 @@ sub vmconfig_hotplug_pending {
            } elsif ($opt eq 'tablet') {
                die "skip\n" if !$hotplug_features->{usb};
                if ($defaults->{tablet}) {
            } elsif ($opt eq 'tablet') {
                die "skip\n" if !$hotplug_features->{usb};
                if ($defaults->{tablet}) {
-                   vm_deviceplug($storecfg, $conf, $vmid, $opt);
+                   vm_deviceplug($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
+                   vm_deviceplug($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
+                       if $arch eq 'aarch64';
                } else {
                } else {
-                   vm_deviceunplug($vmid, $conf, $opt);
+                   vm_deviceunplug($vmid, $conf, 'tablet');
+                   vm_deviceunplug($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
                }
            } elsif ($opt =~ m/^usb\d+/) {
                die "skip\n";
                }
            } elsif ($opt =~ m/^usb\d+/) {
                die "skip\n";
@@ -4357,9 +4825,12 @@ sub vmconfig_hotplug_pending {
            } elsif ($opt eq 'tablet') {
                die "skip\n" if !$hotplug_features->{usb};
                if ($value == 1) {
            } elsif ($opt eq 'tablet') {
                die "skip\n" if !$hotplug_features->{usb};
                if ($value == 1) {
-                   vm_deviceplug($storecfg, $conf, $vmid, $opt);
+                   vm_deviceplug($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
+                   vm_deviceplug($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
+                       if $arch eq 'aarch64';
                } elsif ($value == 0) {
                } elsif ($value == 0) {
-                   vm_deviceunplug($vmid, $conf, $opt);
+                   vm_deviceunplug($vmid, $conf, 'tablet');
+                   vm_deviceunplug($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
                }
            } elsif ($opt =~ m/^usb\d+$/) {
                die "skip\n";
                }
            } elsif ($opt =~ m/^usb\d+$/) {
                die "skip\n";
@@ -4368,7 +4839,7 @@ sub vmconfig_hotplug_pending {
                die "skip\n" if !$hotplug_features->{usb} || $value =~ m/spice/i;
                my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format}, $value) };
                die "skip\n" if !$d;
                die "skip\n" if !$hotplug_features->{usb} || $value =~ m/spice/i;
                my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format}, $value) };
                die "skip\n" if !$d;
-               qemu_usb_hotplug($storecfg, $conf, $vmid, $opt, $d);
+               qemu_usb_hotplug($storecfg, $conf, $vmid, $opt, $d, $arch, $machine_type);
            } elsif ($opt eq 'vcpus') {
                die "skip\n" if !$hotplug_features->{cpu};
                qemu_cpu_hotplug($vmid, $conf, $value);
            } elsif ($opt eq 'vcpus') {
                die "skip\n" if !$hotplug_features->{cpu};
                qemu_cpu_hotplug($vmid, $conf, $value);
@@ -4386,7 +4857,7 @@ sub vmconfig_hotplug_pending {
            } elsif ($opt =~ m/^net(\d+)$/) {
                # some changes can be done without hotplug
                vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
            } elsif ($opt =~ m/^net(\d+)$/) {
                # some changes can be done without hotplug
                vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
-                                   $vmid, $opt, $value);
+                                   $vmid, $opt, $value, $arch, $machine_type);
            } elsif (is_valid_drivename($opt)) {
                # some changes can be done without hotplug
                my $drive = parse_drive($opt, $value);
            } elsif (is_valid_drivename($opt)) {
                # some changes can be done without hotplug
                my $drive = parse_drive($opt, $value);
@@ -4394,7 +4865,7 @@ sub vmconfig_hotplug_pending {
                    &$apply_pending_cloudinit($opt, $value);
                }
                vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
                    &$apply_pending_cloudinit($opt, $value);
                }
                vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
-                                    $vmid, $opt, $value, 1);
+                                    $vmid, $opt, $value, 1, $arch, $machine_type);
            } elsif ($opt =~ m/^memory$/) { #dimms
                die "skip\n" if !$hotplug_features->{memory};
                $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
            } elsif ($opt =~ m/^memory$/) { #dimms
                die "skip\n" if !$hotplug_features->{memory};
                $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
@@ -4523,7 +4994,7 @@ my $safe_string_ne = sub {
 };
 
 sub vmconfig_update_net {
 };
 
 sub vmconfig_update_net {
-    my ($storecfg, $conf, $hotplug, $vmid, $opt, $value) = @_;
+    my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
 
     my $newnet = parse_net($value);
 
 
     my $newnet = parse_net($value);
 
@@ -4564,14 +5035,14 @@ sub vmconfig_update_net {
     }
 
     if ($hotplug) {
     }
 
     if ($hotplug) {
-       vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet);
+       vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
     } else {
        die "skip\n";
     }
 }
 
 sub vmconfig_update_disk {
     } else {
        die "skip\n";
     }
 }
 
 sub vmconfig_update_disk {
-    my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $force) = @_;
+    my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $force, $arch, $machine_type) = @_;
 
     # fixme: do we need force?
 
 
     # fixme: do we need force?
 
@@ -4672,7 +5143,7 @@ sub vmconfig_update_disk {
     die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
     # hotplug new disks
     PVE::Storage::activate_volumes($storecfg, [$drive->{file}]) if $drive->{file} !~ m|^/dev/.+|;
     die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
     # hotplug new disks
     PVE::Storage::activate_volumes($storecfg, [$drive->{file}]) if $drive->{file} !~ m|^/dev/.+|;
-    vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive);
+    vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
 }
 
 sub vm_start {
 }
 
 sub vm_start {
@@ -4684,7 +5155,10 @@ sub vm_start {
 
        die "you can't start a vm if it's a template\n" if PVE::QemuConfig->is_template($conf);
 
 
        die "you can't start a vm if it's a template\n" if PVE::QemuConfig->is_template($conf);
 
-       PVE::QemuConfig->check_lock($conf) if !$skiplock;
+       my $is_suspended = PVE::QemuConfig->has_lock($conf, 'suspended');
+
+       PVE::QemuConfig->check_lock($conf)
+           if !($skiplock || $is_suspended);
 
        die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
 
 
        die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
 
@@ -4748,6 +5222,14 @@ sub vm_start {
            }
        }
 
            }
        }
 
+       PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-start', 1);
+
+       if ($is_suspended) {
+           # enforce machine type on suspended vm to ensure HW compatibility
+           $forcemachine = $conf->{runningmachine};
+           print "Resuming suspended VM\n";
+       }
+
        my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
 
        my $migrate_port = 0;
        my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
 
        my $migrate_port = 0;
@@ -4808,19 +5290,27 @@ sub vm_start {
           next if !$d;
          my $pcidevices = $d->{pciid};
          foreach my $pcidevice (@$pcidevices) {
           next if !$d;
          my $pcidevices = $d->{pciid};
          foreach my $pcidevice (@$pcidevices) {
-               my $pciid = $pcidevice->{id}.".".$pcidevice->{function};
+               my $pciid = $pcidevice->{id};
 
 
-               my $info = pci_device_info("0000:$pciid");
-               die "IOMMU not present\n" if !check_iommu_support();
+               my $info = PVE::SysFSTools::pci_device_info("0000:$pciid");
+               die "IOMMU not present\n" if !PVE::SysFSTools::check_iommu_support();
                die "no pci device info for device '$pciid'\n" if !$info;
                die "no pci device info for device '$pciid'\n" if !$info;
-               die "can't unbind/bind pci group to vfio '$pciid'\n" if !pci_dev_group_bind_to_vfio($pciid);
-               die "can't reset pci device '$pciid'\n" if $info->{has_fl_reset} and !pci_dev_reset($info);
+
+               if ($d->{mdev}) {
+                   my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i);
+                   PVE::SysFSTools::pci_create_mdev_device($pciid, $uuid, $d->{mdev});
+               } else {
+                   die "can't unbind/bind pci group to vfio '$pciid'\n"
+                       if !PVE::SysFSTools::pci_dev_group_bind_to_vfio($pciid);
+                   die "can't reset pci device '$pciid'\n"
+                       if $info->{has_fl_reset} and !PVE::SysFSTools::pci_dev_reset($info);
+               }
          }
         }
 
        PVE::Storage::activate_volumes($storecfg, $vollist);
 
          }
         }
 
        PVE::Storage::activate_volumes($storecfg, $vollist);
 
-       if (!check_running($vmid, 1)) {
+       if (-d "/sys/fs/cgroup/systemd/qemu.slice/$vmid.scope") {
            eval {
                run_command(['/bin/systemctl', 'stop', "$vmid.scope"],
                    outfunc => sub {}, errfunc => sub {});
            eval {
                run_command(['/bin/systemctl', 'stop', "$vmid.scope"],
                    outfunc => sub {}, errfunc => sub {});
@@ -4830,7 +5320,7 @@ sub vm_start {
        my $cpuunits = defined($conf->{cpuunits}) ? $conf->{cpuunits}
                                                  : $defaults->{cpuunits};
 
        my $cpuunits = defined($conf->{cpuunits}) ? $conf->{cpuunits}
                                                  : $defaults->{cpuunits};
 
-       my $start_timeout = $conf->{hugepages} ? 300 : 30;
+       my $start_timeout = ($conf->{hugepages} || $is_suspended) ? 300 : 30;
        my %run_params = (timeout => $statefile ? undef : $start_timeout, umask => 0077);
 
        my %properties = (
        my %run_params = (timeout => $statefile ? undef : $start_timeout, umask => 0077);
 
        my %properties = (
@@ -4846,7 +5336,7 @@ sub vm_start {
 
        my $run_qemu = sub {
            PVE::Tools::run_fork sub {
 
        my $run_qemu = sub {
            PVE::Tools::run_fork sub {
-               PVE::Tools::enter_systemd_scope($vmid, "Proxmox VE VM $vmid", %properties);
+               PVE::Systemd::enter_systemd_scope($vmid, "Proxmox VE VM $vmid", %properties);
                run_command($cmd, %run_params);
            };
        };
                run_command($cmd, %run_params);
            };
        };
@@ -4937,6 +5427,15 @@ sub vm_start {
                    property => "guest-stats-polling-interval",
                    value => 2) if (!defined($conf->{balloon}) || $conf->{balloon});
 
                    property => "guest-stats-polling-interval",
                    value => 2) if (!defined($conf->{balloon}) || $conf->{balloon});
 
+       if ($is_suspended && (my $vmstate = $conf->{vmstate})) {
+           print "Resumed VM, removing state\n";
+           delete $conf->@{qw(lock vmstate runningmachine)};
+           PVE::Storage::deactivate_volumes($storecfg, [$vmstate]);
+           PVE::Storage::vdisk_free($storecfg, $vmstate);
+           PVE::QemuConfig->write_config($vmid, $conf);
+       }
+
+       PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'post-start');
     });
 }
 
     });
 }
 
@@ -4998,10 +5497,19 @@ sub vm_human_monitor_command {
 }
 
 sub vm_commandline {
 }
 
 sub vm_commandline {
-    my ($storecfg, $vmid) = @_;
+    my ($storecfg, $vmid, $snapname) = @_;
 
     my $conf = PVE::QemuConfig->load_config($vmid);
 
 
     my $conf = PVE::QemuConfig->load_config($vmid);
 
+    if ($snapname) {
+       my $snapshot = $conf->{snapshots}->{$snapname};
+       die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
+
+       $snapshot->{digest} = $conf->{digest}; # keep file digest for API
+
+       $conf = $snapshot;
+    }
+
     my $defaults = load_defaults();
 
     my $cmd = config_to_command($storecfg, $vmid, $conf, $defaults);
     my $defaults = load_defaults();
 
     my $cmd = config_to_command($storecfg, $vmid, $conf, $defaults);
@@ -5054,6 +5562,27 @@ sub vm_stop_cleanup {
            unlink "/var/run/qemu-server/${vmid}.$ext";
        }
 
            unlink "/var/run/qemu-server/${vmid}.$ext";
        }
 
+       if ($conf->{ivshmem}) {
+           my $ivshmem = PVE::JSONSchema::parse_property_string($ivshmem_fmt, $conf->{ivshmem});
+           # just delete it for now, VMs which have this already open do not
+           # are affected, but new VMs will get a separated one. If this
+           # becomes an issue we either add some sort of ref-counting or just
+           # add a "don't delete on stop" flag to the ivshmem format.
+           unlink '/dev/shm/pve-shm-' . ($ivshmem->{name} // $vmid);
+       }
+
+       foreach my $key (keys %$conf) {
+           next if $key !~ m/^hostpci(\d+)$/;
+           my $hostpciindex = $1;
+           my $d = parse_hostpci($conf->{$key});
+           my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $hostpciindex);
+
+           foreach my $pci (@{$d->{pciid}}) {
+               my $pciid = $pci->{id};
+               PVE::SysFSTools::pci_cleanup_mdev_device($pciid, $uuid);
+           }
+       }
+
        vmconfig_apply_pending($vmid, $conf, $storecfg) if $apply_pending_changes;
     };
     warn $@ if $@; # avoid errors - just warn
        vmconfig_apply_pending($vmid, $conf, $storecfg) if $apply_pending_changes;
     };
     warn $@ if $@; # avoid errors - just warn
@@ -5088,13 +5617,14 @@ sub vm_stop {
                my $opts = PVE::JSONSchema::pve_parse_startup_order($conf->{startup});
                $timeout = $opts->{down} if $opts->{down};
            }
                my $opts = PVE::JSONSchema::pve_parse_startup_order($conf->{startup});
                $timeout = $opts->{down} if $opts->{down};
            }
+           PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-stop');
        }
 
        $timeout = 60 if !defined($timeout);
 
        eval {
            if ($shutdown) {
        }
 
        $timeout = 60 if !defined($timeout);
 
        eval {
            if ($shutdown) {
-               if (defined($conf) && $conf->{agent}) {
+               if (defined($conf) && parse_guest_agent($conf)->{enabled}) {
                    vm_qmp_command($vmid, { execute => "guest-shutdown" }, $nocheck);
                } else {
                    vm_qmp_command($vmid, { execute => "system_powerdown" }, $nocheck);
                    vm_qmp_command($vmid, { execute => "guest-shutdown" }, $nocheck);
                } else {
                    vm_qmp_command($vmid, { execute => "system_powerdown" }, $nocheck);
@@ -5152,17 +5682,84 @@ sub vm_stop {
 }
 
 sub vm_suspend {
 }
 
 sub vm_suspend {
-    my ($vmid, $skiplock) = @_;
+    my ($vmid, $skiplock, $includestate, $statestorage) = @_;
+
+    my $conf;
+    my $path;
+    my $storecfg;
+    my $vmstate;
 
     PVE::QemuConfig->lock_config($vmid, sub {
 
 
     PVE::QemuConfig->lock_config($vmid, sub {
 
-       my $conf = PVE::QemuConfig->load_config($vmid);
+       $conf = PVE::QemuConfig->load_config($vmid);
 
 
+       my $is_backing_up = PVE::QemuConfig->has_lock($conf, 'backup');
        PVE::QemuConfig->check_lock($conf)
        PVE::QemuConfig->check_lock($conf)
-           if !($skiplock || PVE::QemuConfig->has_lock($conf, 'backup'));
+           if !($skiplock || $is_backing_up);
+
+       die "cannot suspend to disk during backup\n"
+           if $is_backing_up && $includestate;
 
 
-       vm_mon_cmd($vmid, "stop");
+       if ($includestate) {
+           $conf->{lock} = 'suspending';
+           my $date = strftime("%Y-%m-%d", localtime(time()));
+           $storecfg = PVE::Storage::config();
+           $vmstate = PVE::QemuConfig->__snapshot_save_vmstate($vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
+           $path = PVE::Storage::path($storecfg, $vmstate);
+           PVE::QemuConfig->write_config($vmid, $conf);
+       } else {
+           vm_mon_cmd($vmid, "stop");
+       }
     });
     });
+
+    if ($includestate) {
+       # save vm state
+       PVE::Storage::activate_volumes($storecfg, [$vmstate]);
+
+       eval {
+           vm_mon_cmd($vmid, "savevm-start", statefile => $path);
+           for(;;) {
+               my $state = vm_mon_cmd_nocheck($vmid, "query-savevm");
+               if (!$state->{status}) {
+                   die "savevm not active\n";
+               } elsif ($state->{status} eq 'active') {
+                   sleep(1);
+                   next;
+               } elsif ($state->{status} eq 'completed') {
+                   print "State saved, quitting\n";
+                   last;
+               } elsif ($state->{status} eq 'failed' && $state->{error}) {
+                   die "query-savevm failed with error '$state->{error}'\n"
+               } else {
+                   die "query-savevm returned status '$state->{status}'\n";
+               }
+           }
+       };
+       my $err = $@;
+
+       PVE::QemuConfig->lock_config($vmid, sub {
+           $conf = PVE::QemuConfig->load_config($vmid);
+           if ($err) {
+               # cleanup, but leave suspending lock, to indicate something went wrong
+               eval {
+                   vm_mon_cmd($vmid, "savevm-end");
+                   PVE::Storage::deactivate_volumes($storecfg, [$vmstate]);
+                   PVE::Storage::vdisk_free($storecfg, $vmstate);
+                   delete $conf->@{qw(vmstate runningmachine)};
+                   PVE::QemuConfig->write_config($vmid, $conf);
+               };
+               warn $@ if $@;
+               die $err;
+           }
+
+           die "lock changed unexpectedly\n"
+               if !PVE::QemuConfig->has_lock($conf, 'suspending');
+
+           vm_qmp_command($vmid, { execute => "quit" });
+           $conf->{lock} = 'suspended';
+           PVE::QemuConfig->write_config($vmid, $conf);
+       });
+    }
 }
 
 sub vm_resume {
 }
 
 sub vm_resume {
@@ -5219,123 +5816,6 @@ sub vm_destroy {
     });
 }
 
     });
 }
 
-# pci helpers
-
-sub file_write {
-    my ($filename, $buf) = @_;
-
-    my $fh = IO::File->new($filename, "w");
-    return undef if !$fh;
-
-    my $res = print $fh $buf;
-
-    $fh->close();
-
-    return $res;
-}
-
-sub pci_device_info {
-    my ($name) = @_;
-
-    my $res;
-
-    return undef if $name !~ m/^([a-f0-9]{4}):([a-f0-9]{2}):([a-f0-9]{2})\.([a-f0-9])$/;
-    my ($domain, $bus, $slot, $func) = ($1, $2, $3, $4);
-
-    my $irq = file_read_firstline("$pcisysfs/devices/$name/irq");
-    return undef if !defined($irq) || $irq !~ m/^\d+$/;
-
-    my $vendor = file_read_firstline("$pcisysfs/devices/$name/vendor");
-    return undef if !defined($vendor) || $vendor !~ s/^0x//;
-
-    my $product = file_read_firstline("$pcisysfs/devices/$name/device");
-    return undef if !defined($product) || $product !~ s/^0x//;
-
-    $res = {
-       name => $name,
-       vendor => $vendor,
-       product => $product,
-       domain => $domain,
-       bus => $bus,
-       slot => $slot,
-       func => $func,
-       irq => $irq,
-       has_fl_reset => -f "$pcisysfs/devices/$name/reset" || 0,
-    };
-
-    return $res;
-}
-
-sub pci_dev_reset {
-    my ($dev) = @_;
-
-    my $name = $dev->{name};
-
-    my $fn = "$pcisysfs/devices/$name/reset";
-
-    return file_write($fn, "1");
-}
-
-sub pci_dev_bind_to_vfio {
-    my ($dev) = @_;
-
-    my $name = $dev->{name};
-
-    my $vfio_basedir = "$pcisysfs/drivers/vfio-pci";
-
-    if (!-d $vfio_basedir) {
-       system("/sbin/modprobe vfio-pci >/dev/null 2>/dev/null");
-    }
-    die "Cannot find vfio-pci module!\n" if !-d $vfio_basedir;
-
-    my $testdir = "$vfio_basedir/$name";
-    return 1 if -d $testdir;
-
-    my $data = "$dev->{vendor} $dev->{product}";
-    return undef if !file_write("$vfio_basedir/new_id", $data);
-
-    my $fn = "$pcisysfs/devices/$name/driver/unbind";
-    if (!file_write($fn, $name)) {
-       return undef if -f $fn;
-    }
-
-    $fn = "$vfio_basedir/bind";
-    if (! -d $testdir) {
-       return undef if !file_write($fn, $name);
-    }
-
-    return -d $testdir;
-}
-
-sub pci_dev_group_bind_to_vfio {
-    my ($pciid) = @_;
-
-    my $vfio_basedir = "$pcisysfs/drivers/vfio-pci";
-
-    if (!-d $vfio_basedir) {
-       system("/sbin/modprobe vfio-pci >/dev/null 2>/dev/null");
-    }
-    die "Cannot find vfio-pci module!\n" if !-d $vfio_basedir;
-
-    # get IOMMU group devices
-    opendir(my $D, "$pcisysfs/devices/0000:$pciid/iommu_group/devices/") || die "Cannot open iommu_group: $!\n";
-      my @devs = grep /^0000:/, readdir($D);
-    closedir($D);
-
-    foreach my $pciid (@devs) {
-       $pciid =~ m/^([:\.\da-f]+)$/ or die "PCI ID $pciid not valid!\n";
-
-        # pci bridges, switches or root ports are not supported
-        # they have a pci_bus subdirectory so skip them
-        next if (-e "$pcisysfs/devices/$pciid/pci_bus");
-
-       my $info = pci_device_info($1);
-       pci_dev_bind_to_vfio($info) || die "Cannot bind $pciid to vfio\n";
-    }
-
-    return 1;
-}
-
 # vzdump restore implementaion
 
 sub tar_archive_read_firstfile {
 # vzdump restore implementaion
 
 sub tar_archive_read_firstfile {
@@ -5463,6 +5943,13 @@ sub restore_update_config_line {
        } else {
            print $outfd $line;
        }
        } else {
            print $outfd $line;
        }
+    } elsif (($line =~ m/^vmgenid: (.*)/)) {
+       my $vmgenid = $1;
+       if ($vmgenid ne '0') {
+           # always generate a new vmgenid if there was a valid one setup
+           $vmgenid = generate_uuid();
+       }
+       print $outfd "vmgenid: $vmgenid\n";
     } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
        my ($uuid, $uuid_str);
        UUID::generate($uuid);
     } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
        my ($uuid, $uuid_str);
        UUID::generate($uuid);
@@ -5537,6 +6024,7 @@ sub update_disksize {
     my ($vmid, $conf, $volid_hash) = @_;
 
     my $changes;
     my ($vmid, $conf, $volid_hash) = @_;
 
     my $changes;
+    my $prefix = "VM $vmid:";
 
     # used and unused disks
     my $referenced = {};
 
     # used and unused disks
     my $referenced = {};
@@ -5568,6 +6056,7 @@ sub update_disksize {
            if ($new ne $conf->{$opt}) {
                $changes = 1;
                $conf->{$opt} = $new;
            if ($new ne $conf->{$opt}) {
                $changes = 1;
                $conf->{$opt} = $new;
+               print "$prefix update disk '$opt' information.\n";
            }
        }
     }
            }
        }
     }
@@ -5578,6 +6067,7 @@ sub update_disksize {
        my $volid = $conf->{$opt};
        my $path = $volid_hash->{$volid}->{path} if $volid_hash->{$volid};
        if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
        my $volid = $conf->{$opt};
        my $path = $volid_hash->{$volid}->{path} if $volid_hash->{$volid};
        if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
+           print "$prefix remove entry '$opt', its volume '$volid' is in use.\n";
            $changes = 1;
            delete $conf->{$opt};
        }
            $changes = 1;
            delete $conf->{$opt};
        }
@@ -5593,7 +6083,8 @@ sub update_disksize {
        next if !$path; # just to be sure
        next if $referencedpath->{$path};
        $changes = 1;
        next if !$path; # just to be sure
        next if $referencedpath->{$path};
        $changes = 1;
-       PVE::QemuConfig->add_unused_volume($conf, $volid);
+       my $key = PVE::QemuConfig->add_unused_volume($conf, $volid);
+       print "$prefix add unreferenced volume '$volid' as '$key' to config.\n";
        $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
     }
 
        $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
     }
 
@@ -5601,10 +6092,17 @@ sub update_disksize {
 }
 
 sub rescan {
 }
 
 sub rescan {
-    my ($vmid, $nolock) = @_;
+    my ($vmid, $nolock, $dryrun) = @_;
 
     my $cfg = PVE::Storage::config();
 
 
     my $cfg = PVE::Storage::config();
 
+    # FIXME: Remove once our RBD plugin can handle CT and VM on a single storage
+    # see: https://pve.proxmox.com/pipermail/pve-devel/2018-July/032900.html
+    foreach my $stor (keys %{$cfg->{ids}}) {
+       delete($cfg->{ids}->{$stor}) if ! $cfg->{ids}->{$stor}->{content}->{images};
+    }
+
+    print "rescan volumes...\n";
     my $volid_hash = scan_volids($cfg, $vmid);
 
     my $updatefn =  sub {
     my $volid_hash = scan_volids($cfg, $vmid);
 
     my $updatefn =  sub {
@@ -5622,7 +6120,7 @@ sub rescan {
 
        my $changes = update_disksize($vmid, $conf, $vm_volids);
 
 
        my $changes = update_disksize($vmid, $conf, $vm_volids);
 
-       PVE::QemuConfig->write_config($vmid, $conf) if $changes;
+       PVE::QemuConfig->write_config($vmid, $conf) if $changes && !$dryrun;
     };
 
     if (defined($vmid)) {
     };
 
     if (defined($vmid)) {
@@ -6133,6 +6631,23 @@ sub template_create {
     });
 }
 
     });
 }
 
+sub convert_iscsi_path {
+    my ($path) = @_;
+
+    if ($path =~ m|^iscsi://([^/]+)/([^/]+)/(.+)$|) {
+       my $portal = $1;
+       my $target = $2;
+       my $lun = $3;
+
+       my $initiator_name = get_initiator_name();
+
+       return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
+              "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
+    }
+
+    die "cannot convert iscsi path '$path', unkown format\n";
+}
+
 sub qemu_img_convert {
     my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized) = @_;
 
 sub qemu_img_convert {
     my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized) = @_;
 
@@ -6153,13 +6668,32 @@ sub qemu_img_convert {
        my $src_path = PVE::Storage::path($storecfg, $src_volid, $snapname);
        my $dst_path = PVE::Storage::path($storecfg, $dst_volid);
 
        my $src_path = PVE::Storage::path($storecfg, $src_volid, $snapname);
        my $dst_path = PVE::Storage::path($storecfg, $dst_volid);
 
+       my $src_is_iscsi = ($src_path =~ m|^iscsi://|);
+       my $dst_is_iscsi = ($dst_path =~ m|^iscsi://|);
+
        my $cmd = [];
        push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
        my $cmd = [];
        push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
-       push @$cmd, '-s', $snapname if($snapname && $src_format eq "qcow2");
+       push @$cmd, '-l', "snapshot.name=$snapname" if($snapname && $src_format eq "qcow2");
        push @$cmd, '-t', 'none' if $dst_scfg->{type} eq 'zfspool';
        push @$cmd, '-T', 'none' if $src_scfg->{type} eq 'zfspool';
        push @$cmd, '-t', 'none' if $dst_scfg->{type} eq 'zfspool';
        push @$cmd, '-T', 'none' if $src_scfg->{type} eq 'zfspool';
-       push @$cmd, '-f', $src_format, '-O', $dst_format, $src_path;
-       if ($is_zero_initialized) {
+
+       if ($src_is_iscsi) {
+           push @$cmd, '--image-opts';
+           $src_path = convert_iscsi_path($src_path);
+       } else {
+           push @$cmd, '-f', $src_format;
+       }
+
+       if ($dst_is_iscsi) {
+           push @$cmd, '--target-image-opts';
+           $dst_path = convert_iscsi_path($dst_path);
+       } else {
+           push @$cmd, '-O', $dst_format;
+       }
+
+       push @$cmd, $src_path;
+
+       if (!$dst_is_iscsi && $is_zero_initialized) {
            push @$cmd, "zeroinit:$dst_path";
        } else {
            push @$cmd, $dst_path;
            push @$cmd, "zeroinit:$dst_path";
        } else {
            push @$cmd, $dst_path;
@@ -6223,11 +6757,12 @@ sub qemu_drive_mirror {
 
     print "drive mirror is starting for drive-$drive\n";
 
 
     print "drive mirror is starting for drive-$drive\n";
 
-    eval { vm_mon_cmd($vmid, "drive-mirror", %$opts); }; #if a job already run for this device,it's throw an error
-
+    # if a job already runs for this device we get an error, catch it for cleanup
+    eval { vm_mon_cmd($vmid, "drive-mirror", %$opts); };
     if (my $err = $@) {
        eval { PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs) };
     if (my $err = $@) {
        eval { PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs) };
-       die "mirroring error: $err";
+       warn "$@\n" if $@;
+       die "mirroring error: $err\n";
     }
 
     qemu_drive_mirror_monitor ($vmid, $vmiddst, $jobs, $skipcomplete, $qga);
     }
 
     qemu_drive_mirror_monitor ($vmid, $vmiddst, $jobs, $skipcomplete, $qga);
@@ -6385,6 +6920,7 @@ sub clone_disk {
        my $name = undef;
        if (drive_is_cloudinit($drive)) {
            $name = "vm-$newvmid-cloudinit";
        my $name = undef;
        if (drive_is_cloudinit($drive)) {
            $name = "vm-$newvmid-cloudinit";
+           $snapname = undef;
            # cloudinit only supports raw and qcow2 atm:
            if ($dst_format eq 'qcow2') {
                $name .= '.qcow2';
            # cloudinit only supports raw and qcow2 atm:
            if ($dst_format eq 'qcow2') {
                $name .= '.qcow2';
@@ -6452,7 +6988,7 @@ sub qemu_machine_feature_enabled {
     my $current_major;
     my $current_minor;
 
     my $current_major;
     my $current_minor;
 
-    if ($machine && $machine =~ m/^(pc(-i440fx|-q35)?-(\d+)\.(\d+))/) {
+    if ($machine && $machine =~ m/^((?:pc(-i440fx|-q35)?|virt)-(\d+)\.(\d+))/) {
 
        $current_major = $3;
        $current_minor = $4;
 
        $current_major = $3;
        $current_minor = $4;
@@ -6463,9 +6999,9 @@ sub qemu_machine_feature_enabled {
        $current_minor = $2;
     }
 
        $current_minor = $2;
     }
 
-    return 1 if $current_major >= $version_major && $current_minor >= $version_minor;
-
-
+    return 1 if $current_major > $version_major ||
+                ($current_major == $version_major &&
+                 $current_minor >= $version_minor);
 }
 
 sub qemu_machine_pxe {
 }
 
 sub qemu_machine_pxe {
@@ -6502,43 +7038,25 @@ sub qemu_use_old_bios_files {
     return ($use_old_bios_files, $machine_type);
 }
 
     return ($use_old_bios_files, $machine_type);
 }
 
-sub create_efidisk {
-    my ($storecfg, $storeid, $vmid, $fmt) = @_;
+sub create_efidisk($$$$$) {
+    my ($storecfg, $storeid, $vmid, $fmt, $arch) = @_;
 
 
-    die "EFI vars default image not found\n" if ! -f $OVMF_VARS;
+    my (undef, $ovmf_vars) = get_ovmf_files($arch);
+    die "EFI vars default image not found\n" if ! -f $ovmf_vars;
 
 
-    my $vars_size = PVE::Tools::convert_size(-s $OVMF_VARS, 'b' => 'kb');
+    my $vars_size = PVE::Tools::convert_size(-s $ovmf_vars, 'b' => 'kb');
     my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
     PVE::Storage::activate_volumes($storecfg, [$volid]);
 
     my $path = PVE::Storage::path($storecfg, $volid);
     eval {
     my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
     PVE::Storage::activate_volumes($storecfg, [$volid]);
 
     my $path = PVE::Storage::path($storecfg, $volid);
     eval {
-       run_command(['/usr/bin/qemu-img', 'convert', '-n', '-f', 'raw', '-O', $fmt, $OVMF_VARS, $path]);
+       run_command(['/usr/bin/qemu-img', 'convert', '-n', '-f', 'raw', '-O', $fmt, $ovmf_vars, $path]);
     };
     die "Copying EFI vars image failed: $@" if $@;
 
     return ($volid, $vars_size);
 }
 
     };
     die "Copying EFI vars image failed: $@" if $@;
 
     return ($volid, $vars_size);
 }
 
-sub lspci {
-
-    my $devices = {};
-
-    dir_glob_foreach("$pcisysfs/devices", '[a-f0-9]{4}:([a-f0-9]{2}:[a-f0-9]{2})\.([0-9])', sub {
-            my (undef, $id, $function) = @_;
-           my $res = { id => $id, function => $function};
-           push @{$devices->{$id}}, $res;
-    });
-
-    # Entries should be sorted by functions.
-    foreach my $id (keys %$devices) {
-       my $dev = $devices->{$id};
-       $devices->{$id} = [ sort { $a->{function} <=> $b->{function} } @$dev ];
-    }
-
-    return $devices;
-}
-
 sub vm_iothreads_list {
     my ($vmid) = @_;
 
 sub vm_iothreads_list {
     my ($vmid) = @_;
 
@@ -6572,12 +7090,15 @@ sub scsihw_infos {
 }
 
 sub add_hyperv_enlightenments {
 }
 
 sub add_hyperv_enlightenments {
-    my ($cpuFlags, $winversion, $machine_type, $kvmver, $bios, $gpu_passthrough) = @_;
+    my ($cpuFlags, $winversion, $machine_type, $kvmver, $bios, $gpu_passthrough, $hv_vendor_id) = @_;
 
     return if $winversion < 6;
     return if $bios && $bios eq 'ovmf' && $winversion < 8;
 
 
     return if $winversion < 6;
     return if $bios && $bios eq 'ovmf' && $winversion < 8;
 
-    push @$cpuFlags , 'hv_vendor_id=proxmox' if $gpu_passthrough;
+    if ($gpu_passthrough || defined($hv_vendor_id)) {
+       $hv_vendor_id //= 'proxmox';
+       push @$cpuFlags , "hv_vendor_id=$hv_vendor_id";
+    }
 
     if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
        push @$cpuFlags , 'hv_spinlocks=0x1fff';
 
     if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
        push @$cpuFlags , 'hv_spinlocks=0x1fff';
@@ -6595,6 +7116,11 @@ sub add_hyperv_enlightenments {
 
     if ($winversion >= 7) {
        push @$cpuFlags , 'hv_relaxed';
 
     if ($winversion >= 7) {
        push @$cpuFlags , 'hv_relaxed';
+
+       if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 12)) {
+           push @$cpuFlags , 'hv_synic';
+           push @$cpuFlags , 'hv_stimer';
+       }
     }
 }
 
     }
 }
 
@@ -6649,11 +7175,21 @@ sub resolve_first_disk {
     return $firstdisk;
 }
 
     return $firstdisk;
 }
 
-sub generate_smbios1_uuid {
+sub generate_uuid {
     my ($uuid, $uuid_str);
     UUID::generate($uuid);
     UUID::unparse($uuid, $uuid_str);
     my ($uuid, $uuid_str);
     UUID::generate($uuid);
     UUID::unparse($uuid, $uuid_str);
-    return "uuid=$uuid_str";
+    return $uuid_str;
+}
+
+sub generate_smbios1_uuid {
+    return "uuid=".generate_uuid();
+}
+
+sub nbd_stop {
+    my ($vmid) = @_;
+
+    vm_mon_cmd($vmid, 'nbd-server-stop');
 }
 
 # bash completion helper
 }
 
 # bash completion helper
@@ -6729,10 +7265,4 @@ sub complete_storage {
     return $res;
 }
 
     return $res;
 }
 
-sub nbd_stop {
-    my ($vmid) = @_;
-
-    vm_mon_cmd($vmid, 'nbd-server-stop');
-}
-
 1;
 1;