5 use experimental
'smartmatch';
11 use PVE
::Tools
qw(file_get_contents);
12 use PVE
::QemuServer
::PCI
;
14 # not our format but that what QEMU gets passed with '-readconfig'
15 sub slurp_qemu_config
{
18 my $raw = file_get_contents
($fn);
23 my $skip_to_next_group;
24 while ($raw =~ /^\h*(.*?)\h*$/gm) {
27 next if !$line || $line =~ /^#/;
29 # tried to follow qemu's qemu_config_parse function
30 if ($line =~ /\[(\S{1,63}) "([^"\]]{1,63})"\]/) {
32 $skip_to_next_group = 0;
35 $skip_to_next_group = 1;
37 } elsif ($line =~ /\[([^\]]{1,63})\]/) {
39 $skip_to_next_group = 1;
41 if ($line =~ /(\S{1,63}) = "([^\"]{1,1023})"/) {
42 my ($k, $v) = ($1, $2);
43 $cfg->{$group}->{$k} = $v;
45 print "ignoring $fn:$lineno: $line\n";
48 warn "ignore $fn:$lineno, currently no group\n" if !$skip_to_next_group;
55 sub extract_qemu_config_addrs
{
59 for my $k (keys %$qemu_cfg) {
60 my $v = $qemu_cfg->{$k};
61 next if !$v || !defined($v->{bus
}) || !defined($v->{addr
});
66 $addr_map->{$k} = { bus
=> $bus, addr
=> $v->{addr
} };
72 print "testing PCI(e) address conflicts\n";
76 #FIXME: make cross PCI <-> PCIe check sense at all??
78 my ($fail, $ignored) = (0, 0);
80 my ($id, $what, $ignore_if_same_key) = @_;
82 my ($bus, $addr) = $what->@{qw(bus addr)};
83 my $full_addr = "$bus:$addr";
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'");
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;
97 note
("ERR: conflict for '$full_addr' between '$id' and '$conflict'");
100 $addr_map->{$full_addr} = $id;
105 my $pci_map = PVE
::QemuServer
::PCI
::get_pci_addr_map
();
106 while (my ($id, $what) = each %$pci_map) {
107 check_conflict
($id, $what);
110 my $pcie_map = PVE
::QemuServer
::PCI
::get_pcie_addr_map
();
111 while (my ($id, $what) = each %$pcie_map) {
112 check_conflict
($id, $what);
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);
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);
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);
135 fail
("PCI(e) address conflict check, ignored: $ignored, conflicts: $fail");
137 pass
("PCI(e) address conflict check, ignored: $ignored");