use Storable qw(dclone);
use PVE::Exception qw(raise raise_param_exc);
use PVE::Storage;
-use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline);
+use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline dir_glob_foreach);
use PVE::JSONSchema qw(get_standard_option);
use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
use PVE::INotify;
optional => 1,
description => "Emulated CPU type.",
type => 'string',
- enum => [ qw(486 athlon pentium pentium2 pentium3 coreduo core2duo kvm32 kvm64 qemu32 qemu64 phenom Conroe Penryn Nehalem Westmere SandyBridge Haswell Opteron_G1 Opteron_G2 Opteron_G3 Opteron_G4 Opteron_G5 host) ],
+ enum => [ qw(486 athlon pentium pentium2 pentium3 coreduo core2duo kvm32 kvm64 qemu32 qemu64 phenom Conroe Penryn Nehalem Westmere SandyBridge Haswell Broadwell Opteron_G1 Opteron_G2 Opteron_G3 Opteron_G4 Opteron_G5 host) ],
default => 'kvm64',
},
parent => get_standard_option('pve-snapshot-name', {
maxLength => 40,
optional => 1,
},
+ smbios1 => {
+ description => "Specify SMBIOS type 1 fields.",
+ type => 'string', format => 'pve-qm-smbios1',
+ typetext => "[manufacturer=str][,product=str][,version=str][,serial=str] [,uuid=uuid][,sku=str][,family=str]",
+ maxLength => 256,
+ optional => 1,
+ },
};
# what about other qemu settings ?
my $MAX_USB_DEVICES = 5;
my $MAX_NETS = 32;
my $MAX_UNUSED_DISKS = 8;
-my $MAX_HOSTPCI_DEVICES = 2;
+my $MAX_HOSTPCI_DEVICES = 4;
my $MAX_SERIAL_PORTS = 4;
my $MAX_PARALLEL_PORTS = 3;
my $hostpcidesc = {
optional => 1,
type => 'string', format => 'pve-qm-hostpci',
- typetext => "[host=]HOSTPCIDEVICE [,driver=kvm|vfio] [,rombar=on|off]",
+ typetext => "[host=]HOSTPCIDEVICE [,driver=kvm|vfio] [,rombar=on|off] [,pcie=0|1] [,x-vga=on|off]",
description => <<EODESCR,
Map host pci devices. HOSTPCIDEVICE syntax is:
my $res = {};
foreach my $kv (@list) {
- if ($kv =~ m/^(host=)?([a-f0-9]{2}:[a-f0-9]{2}\.[a-f0-9])$/) {
+ if ($kv =~ m/^(host=)?([a-f0-9]{2}:[a-f0-9]{2})(\.([a-f0-9]))?$/) {
$found = 1;
- $res->{pciid} = $2;
+ if(defined($4)){
+ push @{$res->{pciid}}, { id => $2 , function => $4};
+
+ }else{
+ my $pcidevices = lspci($2);
+ $res->{pciid} = $pcidevices->{$2};
+ }
} elsif ($kv =~ m/^driver=(kvm|vfio)$/) {
$res->{driver} = $1;
} elsif ($kv =~ m/^rombar=(on|off)$/) {
$res->{rombar} = $1;
+ } elsif ($kv =~ m/^x-vga=(on|off)$/) {
+ $res->{'x-vga'} = $1;
+ } elsif ($kv =~ m/^pcie=(\d+)$/) {
+ $res->{pcie} = 1 if $1 == 1;
} else {
warn "unknown hostpci setting '$kv'\n";
}
return $key;
}
+my $valid_smbios1_options = {
+ manufacturer => '\S+',
+ product => '\S+',
+ version => '\S+',
+ serial => '\S+',
+ uuid => '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
+ sku => '\S+',
+ family => '\S+',
+};
+
+# smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str]
+sub parse_smbios1 {
+ my ($data) = @_;
+
+ my $res = {};
+
+ foreach my $kvp (split(/,/, $data)) {
+ return undef if $kvp !~ m/^(\S+)=(.+)$/;
+ my ($k, $v) = split(/=/, $kvp);
+ return undef if !defined($k) || !defined($v);
+ return undef if !$valid_smbios1_options->{$k};
+ return undef if $v !~ m/^$valid_smbios1_options->{$k}$/;
+ $res->{$k} = $v;
+ }
+
+ return $res;
+}
+
+sub print_smbios1 {
+ my ($smbios1) = @_;
+
+ my $data = '';
+ foreach my $k (keys %$smbios1) {
+ next if !defined($smbios1->{$k});
+ next if !$valid_smbios1_options->{$k};
+ $data .= ',' if $data;
+ $data .= "$k=$smbios1->{$k}";
+ }
+ return $data;
+}
+
+PVE::JSONSchema::register_format('pve-qm-smbios1', \&verify_smbios1);
+sub verify_smbios1 {
+ my ($value, $noerr) = @_;
+
+ return $value if parse_smbios1($value);
+
+ return undef if $noerr;
+
+ die "unable to parse smbios (type 1) options\n";
+}
+
PVE::JSONSchema::register_format('pve-qm-bootdisk', \&verify_bootdisk);
sub verify_bootdisk {
my ($value, $noerr) = @_;
push @$cmd, '-daemonize';
+ if ($conf->{smbios1}) {
+ push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
+ }
+
if ($q35) {
# the q35 chipset support native usb2, so we enable usb controller
# by default for this machine type
}
push @$devices, '-device', print_tabletdevice_full($conf) if $tablet;
-
+
# host pci devices
for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
- my $d = parse_hostpci($conf->{"hostpci$i"});
- next if !$d;
- $pciaddr = print_pci_addr("hostpci$i", $bridges);
- my $rombar = $d->{rombar} && $d->{rombar} eq 'off' ? ",rombar=0" : "";
- my $driver = $d->{driver} && $d->{driver} eq 'vfio' ? "vfio-pci" : "pci-assign";
- push @$devices, '-device', "$driver,host=$d->{pciid},id=hostpci$i$pciaddr$rombar";
+ my $d = parse_hostpci($conf->{"hostpci$i"});
+ next if !$d;
+
+ my $pcie = $d->{pcie};
+ if($pcie){
+ die "q35 machine model is not enabled" if !$q35;
+ $pciaddr = print_pcie_addr("hostpci$i");
+ }else{
+ $pciaddr = print_pci_addr("hostpci$i", $bridges);
+ }
+
+ my $rombar = $d->{rombar} && $d->{rombar} eq 'off' ? ",rombar=0" : "";
+ my $driver = $d->{driver} && $d->{driver} eq 'vfio' ? "vfio-pci" : "pci-assign";
+ my $xvga = $d->{'x-vga'} && $d->{'x-vga'} eq 'on' ? ",x-vga=on" : "";
+ if ($xvga && $xvga ne '') {
+ push @$cpuFlags, 'kvm=off';
+ $vga = 'none';
+ }
+ $driver = "vfio-pci" if $xvga ne '';
+ my $pcidevices = $d->{pciid};
+ my $multifunction = 1 if @$pcidevices > 1;
+
+ my $j=0;
+ foreach my $pcidevice (@$pcidevices) {
+
+ my $id = "hostpci$i";
+ $id .= ".$j" if $multifunction;
+ my $addr = $pciaddr;
+ $addr .= ".$j" if $multifunction;
+ my $devicestr = "$driver,host=$pcidevice->{id}.$pcidevice->{function},id=$id$addr";
+
+ if($j == 0){
+ $devicestr .= "$rombar$xvga";
+ $devicestr .= ",multifunction=on" if $multifunction;
+ }
+
+ push @$devices, '-device', $devicestr;
+ $j++;
+ }
}
# usb devices
$spice_port = PVE::Tools::next_spice_port();
- push @$cmd, '-spice', "tls-port=${spice_port},addr=127.0.0.1,tls-ciphers=DES-CBC3-SHA,seamless-migration=on";
+ push @$devices, '-spice', "tls-port=${spice_port},addr=127.0.0.1,tls-ciphers=DES-CBC3-SHA,seamless-migration=on";
- push @$cmd, '-device', "virtio-serial,id=spice$pciaddr";
- push @$cmd, '-chardev', "spicevmc,id=vdagent,name=vdagent";
- push @$cmd, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
+ push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
+ push @$devices, '-chardev', "spicevmc,id=vdagent,name=vdagent";
+ push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
}
# enable balloon by default, unless explicitly disabled
for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
my $d = parse_hostpci($conf->{"hostpci$i"});
next if !$d;
- my $info = pci_device_info("0000:$d->{pciid}");
- die "IOMMU not present\n" if !check_iommu_support();
- die "no pci device info for device '$d->{pciid}'\n" if !$info;
+ my $pcidevices = $d->{pciid};
+ foreach my $pcidevice (@$pcidevices) {
+ my $pciid = $pcidevice->{id}.".".$pcidevice->{function};
- if ($d->{driver} && $d->{driver} eq "vfio") {
- die "can't unbind/bind pci group to vfio '$d->{pciid}'\n" if !pci_dev_group_bind_to_vfio($d->{pciid});
- } else {
- die "can't unbind/bind to stub pci device '$d->{pciid}'\n" if !pci_dev_bind_to_stub($info);
- }
+ my $info = pci_device_info("0000:$pciid");
+ die "IOMMU not present\n" if !check_iommu_support();
+ die "no pci device info for device '$pciid'\n" if !$info;
- die "can't reset pci device '$d->{pciid}'\n" if !pci_dev_reset($info);
+ if ($d->{driver} && $d->{driver} eq "vfio") {
+ die "can't unbind/bind pci group to vfio '$pciid'\n" if !pci_dev_group_bind_to_vfio($pciid);
+ } else {
+ die "can't unbind/bind to stub pci device '$pciid'\n" if !pci_dev_bind_to_stub($info);
+ }
+
+ die "can't reset pci device '$pciid'\n" if $info->{has_fl_reset} and !pci_dev_reset($info);
+ }
}
PVE::Storage::activate_volumes($storecfg, $vollist);
vga1 => { bus => 0, addr => 24 },
vga2 => { bus => 0, addr => 25 },
vga3 => { bus => 0, addr => 26 },
+ hostpci2 => { bus => 0, addr => 27 },
+ hostpci3 => { bus => 0, addr => 28 },
#addr29 : usb-host (pve-usb.cfg)
'pci.1' => { bus => 0, addr => 30 },
'pci.2' => { bus => 0, addr => 31 },
}
+sub print_pcie_addr {
+ my ($id) = @_;
+
+ my $res = '';
+ my $devices = {
+ hostpci0 => { bus => "ich9-pcie-port-1", addr => 0 },
+ hostpci1 => { bus => "ich9-pcie-port-2", addr => 0 },
+ hostpci2 => { bus => "ich9-pcie-port-3", addr => 0 },
+ hostpci3 => { bus => "ich9-pcie-port-4", addr => 0 },
+ };
+
+ if (defined($devices->{$id}->{bus}) && defined($devices->{$id}->{addr})) {
+ my $addr = sprintf("0x%x", $devices->{$id}->{addr});
+ my $bus = $devices->{$id}->{bus};
+ $res = ",bus=$bus,addr=$addr";
+ }
+ return $res;
+
+}
+
# vzdump restore implementaion
sub tar_archive_read_firstfile {
die "snapshot '$snapname' does not exist\n" if !defined($snap);
# remove parent refs
- &$unlink_parent($conf, $snap->{parent});
- foreach my $sn (keys %{$conf->{snapshots}}) {
- next if $sn eq $snapname;
- &$unlink_parent($conf->{snapshots}->{$sn}, $snap->{parent});
+ if (!$prepare) {
+ &$unlink_parent($conf, $snap->{parent});
+ foreach my $sn (keys %{$conf->{snapshots}}) {
+ next if $sn eq $snapname;
+ &$unlink_parent($conf->{snapshots}->{$sn}, $snap->{parent});
+ }
}
if ($remove_drive) {
if (!$full) {
print "create linked clone of drive $drivename ($drive->{file})\n";
- $newvolid = PVE::Storage::vdisk_clone($storecfg, $drive->{file}, $newvmid);
+ $newvolid = PVE::Storage::vdisk_clone($storecfg, $drive->{file}, $newvmid, $snapname);
push @$newvollist, $newvolid;
} else {
my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file});
return $current || $default || 'pc';
}
+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;
+ });
+
+ return $devices;
+}
+
1;