]> git.proxmox.com Git - qemu-server.git/blame - test/run_pci_addr_checks.pl
test: compare also with our qemu cfgs for pci addr conflicts
[qemu-server.git] / test / run_pci_addr_checks.pl
CommitLineData
d7d698f6
TL
1#!/usr/bin/perl
2
3use strict;
4use warnings;
5use experimental 'smartmatch';
6
7use lib qw(..);
8
9use Test::More;
10
d3815507 11use PVE::Tools qw(file_get_contents);
d7d698f6
TL
12use PVE::QemuServer::PCI;
13
d3815507
TL
14# not our format but that what QEMU gets passed with '-readconfig'
15sub slurp_qemu_config {
16 my ($fn) = @_;
17
18 my $raw = file_get_contents($fn);
19
20 my $lineno = 0;
21 my $cfg = {};
22 my $group;
23 my $skip_to_next_group;
24 while ($raw =~ /^\h*(.*?)\h*$/gm) {
25 my $line = $1;
26 $lineno++;
27 next if !$line || $line =~ /^#/;
28
29 # tried to follow qemu's qemu_config_parse function
30 if ($line =~ /\[(\S{1,63}) "([^"\]]{1,63})"\]/) {
31 $group = $2;
32 $skip_to_next_group = 0;
33 if ($1 ne 'device') {
34 $group = undef;
35 $skip_to_next_group = 1;
36 }
37 } elsif ($line =~ /\[([^\]]{1,63})\]/) {
38 $group = undef;
39 $skip_to_next_group = 1;
40 } elsif ($group) {
41 if ($line =~ /(\S{1,63}) = "([^\"]{1,1023})"/) {
42 my ($k, $v) = ($1, $2);
43 $cfg->{$group}->{$k} = $v;
44 } else {
45 print "ignoring $fn:$lineno: $line\n";
46 }
47 } else {
48 warn "ignore $fn:$lineno, currently no group\n" if !$skip_to_next_group;
49 }
50 }
51
6cb7b041
TL
52 return $cfg;
53}
54
55sub extract_qemu_config_addrs {
56 my ($qemu_cfg) = @_;
57
58 my $addr_map = {};
59 for my $k (keys %$qemu_cfg) {
60 my $v = $qemu_cfg->{$k};
61 next if !$v || !defined($v->{bus}) || !defined($v->{addr});
62
63 my $bus = $v->{bus};
64 $bus =~ s/pci\.//;
65
66 $addr_map->{$k} = { bus => $bus, addr => $v->{addr} };
67 }
68
69 return $addr_map;
d3815507 70}
d3815507 71
d7d698f6
TL
72print "testing PCI(e) address conflicts\n";
73
74# exec tests
75
76#FIXME: make cross PCI <-> PCIe check sense at all??
77my $addr_map = {};
78my ($fail, $ignored) = (0, 0);
79sub check_conflict {
6cb7b041 80 my ($id, $what, $ignore_if_same_key) = @_;
d7d698f6
TL
81
82 my ($bus, $addr) = $what->@{qw(bus addr)};
83 my $full_addr = "$bus:$addr";
84
85 if (defined(my $conflict = $addr_map->{$full_addr})) {
86 if (my @ignores = $what->{conflict_ok}) {
87 if ($conflict ~~ @ignores) {
88 note("OK: ignore conflict for '$full_addr' between '$id' and '$conflict'");
89 $ignored++;
90 return;
91 }
92 }
6cb7b041
TL
93 # this allows to read multiple pve-*.cfg qemu configs, and check them
94 # normally their OK if they conflict is on the same key. Else TODO??
95 return if $ignore_if_same_key && $id eq $conflict;
96
d7d698f6
TL
97 note("ERR: conflict for '$full_addr' between '$id' and '$conflict'");
98 $fail++;
99 } else {
100 $addr_map->{$full_addr} = $id;
101 }
102}
103
104
105my $pci_map = PVE::QemuServer::PCI::get_pci_addr_map();
106while (my ($id, $what) = each %$pci_map) {
107 check_conflict($id, $what);
108}
109
110my $pcie_map = PVE::QemuServer::PCI::get_pcie_addr_map();
111while (my ($id, $what) = each %$pcie_map) {
112 check_conflict($id, $what);
113}
114
6cb7b041
TL
115my $pve_qm_cfg = slurp_qemu_config('../pve-q35.cfg');
116my $pve_qm_cfg_map = extract_qemu_config_addrs($pve_qm_cfg);
117while (my ($id, $what) = each %$pve_qm_cfg_map) {
118 check_conflict($id, $what);
119}
120
121# FIXME: restart with clean conflict $addr_map with only get_pci*_addr_map ones?
122my $pve_qm4_cfg = slurp_qemu_config('../pve-q35-4.0.cfg');
123my $pve_qm4_cfg_map = extract_qemu_config_addrs($pve_qm4_cfg);
124while (my ($id, $what) = each %$pve_qm4_cfg_map) {
125 check_conflict($id, $what, 1);
126}
127my $pve_qm_usb_cfg = slurp_qemu_config('../pve-usb.cfg');
128my $pve_qm_usb_cfg_map = extract_qemu_config_addrs($pve_qm_usb_cfg);
129while (my ($id, $what) = each %$pve_qm_usb_cfg_map) {
130 check_conflict($id, $what, 1);
131}
132
133
d7d698f6
TL
134if ($fail) {
135 fail("PCI(e) address conflict check, ignored: $ignored, conflicts: $fail");
136} else {
137 pass("PCI(e) address conflict check, ignored: $ignored");
138}
139
140done_testing();