]> git.proxmox.com Git - qemu-server.git/blame - PVE/QemuServer/PCI.pm
add new 'boot' property format and introduce legacy conversion helpers
[qemu-server.git] / PVE / QemuServer / PCI.pm
CommitLineData
de9768f0
DC
1package PVE::QemuServer::PCI;
2
74c17b7a
SR
3use PVE::JSONSchema;
4use PVE::SysFSTools;
5
de9768f0
DC
6use base 'Exporter';
7
8our @EXPORT_OK = qw(
9print_pci_addr
10print_pcie_addr
c4e16381 11print_pcie_root_port
74c17b7a 12parse_hostpci
de9768f0
DC
13);
14
74c17b7a
SR
15our $MAX_HOSTPCI_DEVICES = 16;
16
17my $PCIRE = qr/([a-f0-9]{4}:)?[a-f0-9]{2}:[a-f0-9]{2}(?:\.[a-f0-9])?/;
18my $hostpci_fmt = {
19 host => {
20 default_key => 1,
21 type => 'string',
22 pattern => qr/$PCIRE(;$PCIRE)*/,
23 format_description => 'HOSTPCIID[;HOSTPCIID2...]',
24 description => <<EODESCR,
25Host PCI device pass through. The PCI ID of a host's PCI device or a list
26of PCI virtual functions of the host. HOSTPCIID syntax is:
27
28'bus:dev.func' (hexadecimal numbers)
29
30You can us the 'lspci' command to list existing PCI devices.
31EODESCR
32 },
33 rombar => {
34 type => 'boolean',
1fac3a0b
TL
35 description => "Specify whether or not the device's ROM will be visible in the"
36 ." guest's memory map.",
74c17b7a
SR
37 optional => 1,
38 default => 1,
39 },
40 romfile => {
1fac3a0b
TL
41 type => 'string',
42 pattern => '[^,;]+',
43 format_description => 'string',
44 description => "Custom pci device rom filename (must be located in /usr/share/kvm/).",
45 optional => 1,
74c17b7a
SR
46 },
47 pcie => {
48 type => 'boolean',
1fac3a0b 49 description => "Choose the PCI-express bus (needs the 'q35' machine model).",
74c17b7a
SR
50 optional => 1,
51 default => 0,
52 },
53 'x-vga' => {
54 type => 'boolean',
1fac3a0b 55 description => "Enable vfio-vga device support.",
74c17b7a
SR
56 optional => 1,
57 default => 0,
58 },
13d68979
SR
59 'legacy-igd' => {
60 type => 'boolean',
1fac3a0b
TL
61 description => "Pass this device in legacy IGD mode, making it the primary and exclusive"
62 ." graphics device in the VM. Requires 'pc-i440fx' machine type and VGA set to 'none'.",
13d68979
SR
63 optional => 1,
64 default => 0,
65 },
74c17b7a
SR
66 'mdev' => {
67 type => 'string',
1fac3a0b 68 format_description => 'string',
74c17b7a
SR
69 pattern => '[^/\.:]+',
70 optional => 1,
71 description => <<EODESCR
72The type of mediated device to use.
73An instance of this type will be created on startup of the VM and
74will be cleaned up when the VM stops.
75EODESCR
76 }
77};
78PVE::JSONSchema::register_format('pve-qm-hostpci', $hostpci_fmt);
79
80our $hostpcidesc = {
1fac3a0b
TL
81 optional => 1,
82 type => 'string', format => 'pve-qm-hostpci',
83 description => "Map host PCI devices into guest.",
74c17b7a
SR
84 verbose_description => <<EODESCR,
85Map host PCI devices into guest.
86
87NOTE: This option allows direct access to host hardware. So it is no longer
88possible to migrate such machines - use with special care.
89
90CAUTION: Experimental! User reported problems with this option.
91EODESCR
92};
93PVE::JSONSchema::register_standard_option("pve-qm-hostpci", $hostpcidesc);
94
d7d698f6
TL
95my $pci_addr_map;
96sub get_pci_addr_map {
97 $pci_addr_map = {
98 piix3 => { bus => 0, addr => 1, conflict_ok => qw(ehci) },
99 ehci => { bus => 0, addr => 1, conflict_ok => qw(piix3) }, # instead of piix3 on arm
13d68979
SR
100 vga => { bus => 0, addr => 2, conflict_ok => qw(legacy-igd) },
101 'legacy-igd' => { bus => 0, addr => 2, conflict_ok => qw(vga) }, # legacy-igd requires vga=none
d7d698f6
TL
102 balloon0 => { bus => 0, addr => 3 },
103 watchdog => { bus => 0, addr => 4 },
104 scsihw0 => { bus => 0, addr => 5, conflict_ok => qw(pci.3) },
105 'pci.3' => { bus => 0, addr => 5, conflict_ok => qw(scsihw0) }, # also used for virtio-scsi-single bridge
106 scsihw1 => { bus => 0, addr => 6 },
107 ahci0 => { bus => 0, addr => 7 },
108 qga0 => { bus => 0, addr => 8 },
109 spice => { bus => 0, addr => 9 },
110 virtio0 => { bus => 0, addr => 10 },
111 virtio1 => { bus => 0, addr => 11 },
112 virtio2 => { bus => 0, addr => 12 },
113 virtio3 => { bus => 0, addr => 13 },
114 virtio4 => { bus => 0, addr => 14 },
115 virtio5 => { bus => 0, addr => 15 },
116 hostpci0 => { bus => 0, addr => 16 },
117 hostpci1 => { bus => 0, addr => 17 },
118 net0 => { bus => 0, addr => 18 },
119 net1 => { bus => 0, addr => 19 },
120 net2 => { bus => 0, addr => 20 },
121 net3 => { bus => 0, addr => 21 },
122 net4 => { bus => 0, addr => 22 },
123 net5 => { bus => 0, addr => 23 },
124 vga1 => { bus => 0, addr => 24 },
125 vga2 => { bus => 0, addr => 25 },
126 vga3 => { bus => 0, addr => 26 },
127 hostpci2 => { bus => 0, addr => 27 },
128 hostpci3 => { bus => 0, addr => 28 },
129 #addr29 : usb-host (pve-usb.cfg)
130 'pci.1' => { bus => 0, addr => 30 },
131 'pci.2' => { bus => 0, addr => 31 },
132 'net6' => { bus => 1, addr => 1 },
133 'net7' => { bus => 1, addr => 2 },
134 'net8' => { bus => 1, addr => 3 },
135 'net9' => { bus => 1, addr => 4 },
136 'net10' => { bus => 1, addr => 5 },
137 'net11' => { bus => 1, addr => 6 },
138 'net12' => { bus => 1, addr => 7 },
139 'net13' => { bus => 1, addr => 8 },
140 'net14' => { bus => 1, addr => 9 },
141 'net15' => { bus => 1, addr => 10 },
142 'net16' => { bus => 1, addr => 11 },
143 'net17' => { bus => 1, addr => 12 },
144 'net18' => { bus => 1, addr => 13 },
145 'net19' => { bus => 1, addr => 14 },
146 'net20' => { bus => 1, addr => 15 },
147 'net21' => { bus => 1, addr => 16 },
148 'net22' => { bus => 1, addr => 17 },
149 'net23' => { bus => 1, addr => 18 },
150 'net24' => { bus => 1, addr => 19 },
151 'net25' => { bus => 1, addr => 20 },
152 'net26' => { bus => 1, addr => 21 },
153 'net27' => { bus => 1, addr => 22 },
154 'net28' => { bus => 1, addr => 23 },
155 'net29' => { bus => 1, addr => 24 },
156 'net30' => { bus => 1, addr => 25 },
157 'net31' => { bus => 1, addr => 26 },
158 'xhci' => { bus => 1, addr => 27 },
2513b862 159 'pci.4' => { bus => 1, addr => 28 },
2cf61f33 160 'rng0' => { bus => 1, addr => 29 },
13d68979 161 'pci.2-igd' => { bus => 1, addr => 30 }, # replaces pci.2 in case a legacy IGD device is passed through
d7d698f6
TL
162 'virtio6' => { bus => 2, addr => 1 },
163 'virtio7' => { bus => 2, addr => 2 },
164 'virtio8' => { bus => 2, addr => 3 },
165 'virtio9' => { bus => 2, addr => 4 },
166 'virtio10' => { bus => 2, addr => 5 },
167 'virtio11' => { bus => 2, addr => 6 },
168 'virtio12' => { bus => 2, addr => 7 },
169 'virtio13' => { bus => 2, addr => 8 },
170 'virtio14' => { bus => 2, addr => 9 },
171 'virtio15' => { bus => 2, addr => 10 },
172 'ivshmem' => { bus => 2, addr => 11 },
173 'audio0' => { bus => 2, addr => 12 },
174 hostpci4 => { bus => 2, addr => 13 },
175 hostpci5 => { bus => 2, addr => 14 },
176 hostpci6 => { bus => 2, addr => 15 },
177 hostpci7 => { bus => 2, addr => 16 },
178 hostpci8 => { bus => 2, addr => 17 },
179 hostpci9 => { bus => 2, addr => 18 },
180 hostpci10 => { bus => 2, addr => 19 },
181 hostpci11 => { bus => 2, addr => 20 },
182 hostpci12 => { bus => 2, addr => 21 },
183 hostpci13 => { bus => 2, addr => 22 },
184 hostpci14 => { bus => 2, addr => 23 },
185 hostpci15 => { bus => 2, addr => 24 },
186 'virtioscsi0' => { bus => 3, addr => 1 },
187 'virtioscsi1' => { bus => 3, addr => 2 },
188 'virtioscsi2' => { bus => 3, addr => 3 },
189 'virtioscsi3' => { bus => 3, addr => 4 },
190 'virtioscsi4' => { bus => 3, addr => 5 },
191 'virtioscsi5' => { bus => 3, addr => 6 },
192 'virtioscsi6' => { bus => 3, addr => 7 },
193 'virtioscsi7' => { bus => 3, addr => 8 },
194 'virtioscsi8' => { bus => 3, addr => 9 },
195 'virtioscsi9' => { bus => 3, addr => 10 },
196 'virtioscsi10' => { bus => 3, addr => 11 },
197 'virtioscsi11' => { bus => 3, addr => 12 },
198 'virtioscsi12' => { bus => 3, addr => 13 },
199 'virtioscsi13' => { bus => 3, addr => 14 },
200 'virtioscsi14' => { bus => 3, addr => 15 },
201 'virtioscsi15' => { bus => 3, addr => 16 },
202 'virtioscsi16' => { bus => 3, addr => 17 },
203 'virtioscsi17' => { bus => 3, addr => 18 },
204 'virtioscsi18' => { bus => 3, addr => 19 },
205 'virtioscsi19' => { bus => 3, addr => 20 },
206 'virtioscsi20' => { bus => 3, addr => 21 },
207 'virtioscsi21' => { bus => 3, addr => 22 },
208 'virtioscsi22' => { bus => 3, addr => 23 },
209 'virtioscsi23' => { bus => 3, addr => 24 },
210 'virtioscsi24' => { bus => 3, addr => 25 },
211 'virtioscsi25' => { bus => 3, addr => 26 },
212 'virtioscsi26' => { bus => 3, addr => 27 },
213 'virtioscsi27' => { bus => 3, addr => 28 },
214 'virtioscsi28' => { bus => 3, addr => 29 },
215 'virtioscsi29' => { bus => 3, addr => 30 },
216 'virtioscsi30' => { bus => 3, addr => 31 },
2513b862
DC
217 'scsihw2' => { bus => 4, addr => 1 },
218 'scsihw3' => { bus => 4, addr => 2 },
219 'scsihw4' => { bus => 4, addr => 3 },
d7d698f6
TL
220 } if !defined($pci_addr_map);
221 return $pci_addr_map;
222}
223
224my $get_addr_mapping_from_id = sub {
225 my ($map, $id) = @_;
226
227 my $d = $map->{$id};
228 return undef if !defined($d) || !defined($d->{bus}) || !defined($d->{addr});
229
230 return { bus => $d->{bus}, addr => sprintf("0x%x", $d->{addr}) };
de9768f0
DC
231};
232
233sub print_pci_addr {
d559309f 234 my ($id, $bridges, $arch, $machine) = @_;
de9768f0
DC
235
236 my $res = '';
237
d7d698f6 238 # using same bus slots on all HW, so we need to check special cases here:
d559309f
WB
239 my $busname = 'pci';
240 if ($arch eq 'aarch64' && $machine =~ /^virt/) {
d7d698f6 241 die "aarch64/virt cannot use IDE devices\n" if $id =~ /^ide/;
d559309f
WB
242 $busname = 'pcie';
243 }
244
d7d698f6
TL
245 my $map = get_pci_addr_map();
246 if (my $d = $get_addr_mapping_from_id->($map, $id)) {
247 $res = ",bus=$busname.$d->{bus},addr=$d->{addr}";
248 $bridges->{$d->{bus}} = 1 if $bridges;
de9768f0 249 }
de9768f0 250
d7d698f6 251 return $res;
de9768f0
DC
252}
253
d7d698f6
TL
254my $pcie_addr_map;
255sub get_pcie_addr_map {
256 $pcie_addr_map = {
55655ebc 257 vga => { bus => 'pcie.0', addr => 1 },
de9768f0
DC
258 hostpci0 => { bus => "ich9-pcie-port-1", addr => 0 },
259 hostpci1 => { bus => "ich9-pcie-port-2", addr => 0 },
260 hostpci2 => { bus => "ich9-pcie-port-3", addr => 0 },
261 hostpci3 => { bus => "ich9-pcie-port-4", addr => 0 },
c4e16381
AL
262 hostpci4 => { bus => "ich9-pcie-port-5", addr => 0 },
263 hostpci5 => { bus => "ich9-pcie-port-6", addr => 0 },
264 hostpci6 => { bus => "ich9-pcie-port-7", addr => 0 },
265 hostpci7 => { bus => "ich9-pcie-port-8", addr => 0 },
266 hostpci8 => { bus => "ich9-pcie-port-9", addr => 0 },
267 hostpci9 => { bus => "ich9-pcie-port-10", addr => 0 },
268 hostpci10 => { bus => "ich9-pcie-port-11", addr => 0 },
269 hostpci11 => { bus => "ich9-pcie-port-12", addr => 0 },
270 hostpci12 => { bus => "ich9-pcie-port-13", addr => 0 },
271 hostpci13 => { bus => "ich9-pcie-port-14", addr => 0 },
272 hostpci14 => { bus => "ich9-pcie-port-15", addr => 0 },
273 hostpci15 => { bus => "ich9-pcie-port-16", addr => 0 },
739ba340
DC
274 # win7 is picky about pcie assignments
275 hostpci0bus0 => { bus => "pcie.0", addr => 16 },
276 hostpci1bus0 => { bus => "pcie.0", addr => 17 },
277 hostpci2bus0 => { bus => "pcie.0", addr => 18 },
278 hostpci3bus0 => { bus => "pcie.0", addr => 19 },
6dbcb073 279 ivshmem => { bus => 'pcie.0', addr => 20 },
c4e16381
AL
280 hostpci4bus0 => { bus => "pcie.0", addr => 9 },
281 hostpci5bus0 => { bus => "pcie.0", addr => 10 },
282 hostpci6bus0 => { bus => "pcie.0", addr => 11 },
283 hostpci7bus0 => { bus => "pcie.0", addr => 12 },
284 hostpci8bus0 => { bus => "pcie.0", addr => 13 },
285 hostpci9bus0 => { bus => "pcie.0", addr => 14 },
286 hostpci10bus0 => { bus => "pcie.0", addr => 15 },
e2b0d85d
TL
287 hostpci11bus0 => { bus => "pcie.0", addr => 21 },
288 hostpci12bus0 => { bus => "pcie.0", addr => 22 },
289 hostpci13bus0 => { bus => "pcie.0", addr => 23 },
290 hostpci14bus0 => { bus => "pcie.0", addr => 24 },
291 hostpci15bus0 => { bus => "pcie.0", addr => 25 },
d7d698f6
TL
292 } if !defined($pcie_addr_map);
293
294 return $pcie_addr_map;
295}
296
297sub print_pcie_addr {
298 my ($id) = @_;
299
300 my $res = '';
de9768f0 301
d7d698f6
TL
302 my $map = get_pcie_addr_map($id);
303 if (my $d = $get_addr_mapping_from_id->($map, $id)) {
304 $res = ",bus=$d->{bus},addr=$d->{addr}";
de9768f0 305 }
de9768f0 306
d7d698f6 307 return $res;
de9768f0 308}
b71351a7 309
c4e16381
AL
310# Generates the device strings for additional pcie root ports. The first 4 pcie
311# root ports are defined in the pve-q35*.cfg files.
312sub print_pcie_root_port {
313 my ($i) = @_;
314 my $res = '';
315
c4e16381 316 my $root_port_addresses = {
e2b0d85d
TL
317 4 => "10.0",
318 5 => "10.1",
319 6 => "10.2",
320 7 => "10.3",
321 8 => "10.4",
322 9 => "10.5",
c4e16381
AL
323 10 => "10.6",
324 11 => "10.7",
325 12 => "11.0",
326 13 => "11.1",
327 14 => "11.2",
328 15 => "11.3",
329 };
330
331 if (defined($root_port_addresses->{$i})) {
e2b0d85d 332 my $id = $i + 1;
c4e16381
AL
333 $res = "pcie-root-port,id=ich9-pcie-port-${id}";
334 $res .= ",addr=$root_port_addresses->{$i}";
335 $res .= ",x-speed=16,x-width=32,multifunction=on,bus=pcie.0";
336 $res .= ",port=${id},chassis=${id}";
337 }
338
339 return $res;
340}
341
74c17b7a
SR
342sub parse_hostpci {
343 my ($value) = @_;
344
345 return undef if !$value;
346
347 my $res = PVE::JSONSchema::parse_property_string($hostpci_fmt, $value);
348
349 my @idlist = split(/;/, $res->{host});
350 delete $res->{host};
351 foreach my $id (@idlist) {
352 my $devs = PVE::SysFSTools::lspci($id);
353 die "no PCI device found for '$id'\n" if !scalar(@$devs);
354 push @{$res->{pciid}}, @$devs;
355 }
356 return $res;
357}
358
359sub print_hostpci_devices {
7de7f675 360 my ($vmid, $conf, $devices, $winversion, $q35, $bridges, $arch, $machine_type) = @_;
74c17b7a
SR
361
362 my $kvm_off = 0;
363 my $gpu_passthrough = 0;
13d68979 364 my $legacy_igd = 0;
74c17b7a
SR
365
366 for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
367 my $id = "hostpci$i";
368 my $d = parse_hostpci($conf->{$id});
369 next if !$d;
370
371 if (my $pcie = $d->{pcie}) {
372 die "q35 machine model is not enabled" if !$q35;
373 # win7 wants to have the pcie devices directly on the pcie bus
374 # instead of in the root port
375 if ($winversion == 7) {
376 $pciaddr = print_pcie_addr("${id}bus0");
377 } else {
378 # add more root ports if needed, 4 are present by default
379 # by pve-q35 cfgs, rest added here on demand.
380 if ($i > 3) {
381 push @$devices, '-device', print_pcie_root_port($i);
382 }
383 $pciaddr = print_pcie_addr($id);
384 }
385 } else {
13d68979
SR
386 my $pci_name = $d->{'legacy-igd'} ? 'legacy-igd' : $id;
387 $pciaddr = print_pci_addr($pci_name, $bridges, $arch, $machine_type);
388 }
389
390 my $pcidevices = $d->{pciid};
391 my $multifunction = 1 if @$pcidevices > 1;
392
393 if ($d->{'legacy-igd'}) {
394 die "only one device can be assigned in legacy-igd mode\n"
395 if $legacy_igd;
396 $legacy_igd = 1;
397
398 die "legacy IGD assignment requires VGA mode to be 'none'\n"
399 if !defined($conf->{'vga'}) || $conf->{'vga'} ne 'none';
400 die "legacy IGD assignment requires rombar to be enabled\n"
401 if defined($d->{rombar}) && !$d->{rombar};
402 die "legacy IGD assignment is not compatible with x-vga\n"
403 if $d->{'x-vga'};
404 die "legacy IGD assignment is not compatible with mdev\n"
405 if $d->{mdev};
406 die "legacy IGD assignment is not compatible with q35\n"
407 if $q35;
408 die "legacy IGD assignment is not compatible with multifunction devices\n"
409 if $multifunction;
410 die "legacy IGD assignment only works for devices on host bus 00:02.0\n"
411 if $pcidevices->[0]->{id} !~ m/02\.0$/;
74c17b7a
SR
412 }
413
414 my $xvga = '';
415 if ($d->{'x-vga'}) {
416 $xvga = ',x-vga=on' if !($conf->{bios} && $conf->{bios} eq 'ovmf');
417 $kvm_off = 1;
418 $vga->{type} = 'none' if !defined($conf->{vga});
419 $gpu_passthrough = 1;
420 }
421
74c17b7a
SR
422 my $sysfspath;
423 if ($d->{mdev} && scalar(@$pcidevices) == 1) {
424 my $pci_id = $pcidevices->[0]->{id};
425 my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i);
426 $sysfspath = "/sys/bus/pci/devices/$pci_id/$uuid";
427 } elsif ($d->{mdev}) {
428 warn "ignoring mediated device '$id' with multifunction device\n";
429 }
430
1fac3a0b 431 my $j = 0;
74c17b7a
SR
432 foreach my $pcidevice (@$pcidevices) {
433 my $devicestr = "vfio-pci";
434
435 if ($sysfspath) {
436 $devicestr .= ",sysfsdev=$sysfspath";
437 } else {
438 $devicestr .= ",host=$pcidevice->{id}";
439 }
440
441 my $mf_addr = $multifunction ? ".$j" : '';
442 $devicestr .= ",id=${id}${mf_addr}${pciaddr}${mf_addr}";
443
444 if ($j == 0) {
445 $devicestr .= ',rombar=0' if defined($d->{rombar}) && !$d->{rombar};
446 $devicestr .= "$xvga";
447 $devicestr .= ",multifunction=on" if $multifunction;
448 $devicestr .= ",romfile=/usr/share/kvm/$d->{romfile}" if $d->{romfile};
449 }
450
451 push @$devices, '-device', $devicestr;
452 $j++;
453 }
454 }
455
13d68979 456 return ($kvm_off, $gpu_passthrough, $legacy_igd);
74c17b7a
SR
457}
458
b71351a7 4591;