X-Git-Url: https://git.proxmox.com/?p=qemu-server.git;a=blobdiff_plain;f=PVE%2FQemuServer%2FPCI.pm;fp=PVE%2FQemuServer%2FPCI.pm;h=39f997023abc244779614ded74a303588d0ab25d;hp=4d9ce2640b87f6a45dbd76461585809d212aca0d;hb=74c17b7a23c8a953d1dcec9bd53449d71d88cd5d;hpb=5a92276e2dd17a5ba1b86a7a9b2c1dfdd2e44903 diff --git a/PVE/QemuServer/PCI.pm b/PVE/QemuServer/PCI.pm index 4d9ce26..39f9970 100644 --- a/PVE/QemuServer/PCI.pm +++ b/PVE/QemuServer/PCI.pm @@ -1,13 +1,89 @@ package PVE::QemuServer::PCI; +use PVE::JSONSchema; +use PVE::SysFSTools; + use base 'Exporter'; our @EXPORT_OK = qw( print_pci_addr print_pcie_addr print_pcie_root_port +parse_hostpci ); +our $MAX_HOSTPCI_DEVICES = 16; + +my $PCIRE = qr/([a-f0-9]{4}:)?[a-f0-9]{2}:[a-f0-9]{2}(?:\.[a-f0-9])?/; +my $hostpci_fmt = { + host => { + default_key => 1, + type => 'string', + pattern => qr/$PCIRE(;$PCIRE)*/, + format_description => 'HOSTPCIID[;HOSTPCIID2...]', + description => < { + type => 'boolean', + description => "Specify whether or not the device's ROM will be visible in the guest's memory map.", + optional => 1, + default => 1, + }, + romfile => { + type => 'string', + pattern => '[^,;]+', + format_description => 'string', + description => "Custom pci device rom filename (must be located in /usr/share/kvm/).", + optional => 1, + }, + pcie => { + type => 'boolean', + description => "Choose the PCI-express bus (needs the 'q35' machine model).", + optional => 1, + default => 0, + }, + 'x-vga' => { + type => 'boolean', + description => "Enable vfio-vga device support.", + optional => 1, + default => 0, + }, + 'mdev' => { + type => 'string', + format_description => 'string', + pattern => '[^/\.:]+', + optional => 1, + description => < 1, + type => 'string', format => 'pve-qm-hostpci', + description => "Map host PCI devices into guest.", + verbose_description => <{host}); + delete $res->{host}; + foreach my $id (@idlist) { + my $devs = PVE::SysFSTools::lspci($id); + die "no PCI device found for '$id'\n" if !scalar(@$devs); + push @{$res->{pciid}}, @$devs; + } + return $res; +} + +sub print_hostpci_devices { + my ($conf, $devices, $winversion, $q35, $bridges, $arch, $machine_type) = @_; + + my $kvm_off = 0; + my $gpu_passthrough = 0; + + for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) { + my $id = "hostpci$i"; + my $d = parse_hostpci($conf->{$id}); + next if !$d; + + if (my $pcie = $d->{pcie}) { + die "q35 machine model is not enabled" if !$q35; + # 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("${id}bus0"); + } else { + # add more root ports if needed, 4 are present by default + # by pve-q35 cfgs, rest added here on demand. + if ($i > 3) { + push @$devices, '-device', print_pcie_root_port($i); + } + $pciaddr = print_pcie_addr($id); + } + } else { + $pciaddr = print_pci_addr($id, $bridges, $arch, $machine_type); + } + + my $xvga = ''; + if ($d->{'x-vga'}) { + $xvga = ',x-vga=on' if !($conf->{bios} && $conf->{bios} eq 'ovmf'); + $kvm_off = 1; + $vga->{type} = 'none' if !defined($conf->{vga}); + $gpu_passthrough = 1; + } + + my $pcidevices = $d->{pciid}; + my $multifunction = 1 if @$pcidevices > 1; + + my $sysfspath; + if ($d->{mdev} && scalar(@$pcidevices) == 1) { + my $pci_id = $pcidevices->[0]->{id}; + my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i); + $sysfspath = "/sys/bus/pci/devices/$pci_id/$uuid"; + } elsif ($d->{mdev}) { + warn "ignoring mediated device '$id' with multifunction device\n"; + } + + my $j=0; + foreach my $pcidevice (@$pcidevices) { + my $devicestr = "vfio-pci"; + + if ($sysfspath) { + $devicestr .= ",sysfsdev=$sysfspath"; + } else { + $devicestr .= ",host=$pcidevice->{id}"; + } + + my $mf_addr = $multifunction ? ".$j" : ''; + $devicestr .= ",id=${id}${mf_addr}${pciaddr}${mf_addr}"; + + if ($j == 0) { + $devicestr .= ',rombar=0' if defined($d->{rombar}) && !$d->{rombar}; + $devicestr .= "$xvga"; + $devicestr .= ",multifunction=on" if $multifunction; + $devicestr .= ",romfile=/usr/share/kvm/$d->{romfile}" if $d->{romfile}; + } + + push @$devices, '-device', $devicestr; + $j++; + } + } + + return ($kvm_off, $gpu_passthrough); +} + 1;