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