]>
Commit | Line | Data |
---|---|---|
d7d698f6 TL |
1 | #!/usr/bin/perl |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | use experimental 'smartmatch'; | |
6 | ||
7 | use lib qw(..); | |
8 | ||
9 | use Test::More; | |
10 | ||
d3815507 | 11 | use PVE::Tools qw(file_get_contents); |
d7d698f6 TL |
12 | use PVE::QemuServer::PCI; |
13 | ||
d3815507 TL |
14 | # not our format but that what QEMU gets passed with '-readconfig' |
15 | sub 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 | ||
55 | sub 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 |
72 | print "testing PCI(e) address conflicts\n"; |
73 | ||
74 | # exec tests | |
75 | ||
76 | #FIXME: make cross PCI <-> PCIe check sense at all?? | |
77 | my $addr_map = {}; | |
78 | my ($fail, $ignored) = (0, 0); | |
79 | sub 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 | ||
105 | my $pci_map = PVE::QemuServer::PCI::get_pci_addr_map(); | |
106 | while (my ($id, $what) = each %$pci_map) { | |
107 | check_conflict($id, $what); | |
108 | } | |
109 | ||
110 | my $pcie_map = PVE::QemuServer::PCI::get_pcie_addr_map(); | |
111 | while (my ($id, $what) = each %$pcie_map) { | |
112 | check_conflict($id, $what); | |
113 | } | |
114 | ||
6cb7b041 TL |
115 | my $pve_qm_cfg = slurp_qemu_config('../pve-q35.cfg'); |
116 | my $pve_qm_cfg_map = extract_qemu_config_addrs($pve_qm_cfg); | |
117 | while (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? | |
122 | my $pve_qm4_cfg = slurp_qemu_config('../pve-q35-4.0.cfg'); | |
123 | my $pve_qm4_cfg_map = extract_qemu_config_addrs($pve_qm4_cfg); | |
124 | while (my ($id, $what) = each %$pve_qm4_cfg_map) { | |
125 | check_conflict($id, $what, 1); | |
126 | } | |
127 | my $pve_qm_usb_cfg = slurp_qemu_config('../pve-usb.cfg'); | |
128 | my $pve_qm_usb_cfg_map = extract_qemu_config_addrs($pve_qm_usb_cfg); | |
129 | while (my ($id, $what) = each %$pve_qm_usb_cfg_map) { | |
130 | check_conflict($id, $what, 1); | |
131 | } | |
132 | ||
133 | ||
d7d698f6 TL |
134 | if ($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 | ||
140 | done_testing(); |