5 use experimental
'smartmatch';
11 use PVE
::Tools
qw(file_get_contents);
12 use PVE
::QemuServer
::PCI
;
14 my $qemu_cfg_base_path = "../qemu-configs";
16 # not our format but that what QEMU gets passed with '-readconfig'
17 sub slurp_qemu_config
{
20 my $raw = file_get_contents
($fn);
25 my $skip_to_next_group;
26 while ($raw =~ /^\h*(.*?)\h*$/gm) {
29 next if !$line || $line =~ /^#/;
31 # tried to follow qemu's qemu_config_parse function
32 if ($line =~ /\[(\S{1,63}) "([^"\]]{1,63})"\]/) {
34 $skip_to_next_group = 0;
37 $skip_to_next_group = 1;
39 } elsif ($line =~ /\[([^\]]{1,63})\]/) {
41 $skip_to_next_group = 1;
43 if ($line =~ /(\S{1,63}) = "([^\"]{1,1023})"/) {
44 my ($k, $v) = ($1, $2);
45 $cfg->{$group}->{$k} = $v;
47 print "ignoring $fn:$lineno: $line\n";
50 warn "ignore $fn:$lineno, currently no group\n" if !$skip_to_next_group;
57 sub extract_qemu_config_addrs
{
61 for my $k (keys %$qemu_cfg) {
62 my $v = $qemu_cfg->{$k};
63 next if !$v || !defined($v->{bus
}) || !defined($v->{addr
});
68 $addr_map->{$k} = { bus
=> $bus, addr
=> $v->{addr
} };
74 print "testing PCI(e) address conflicts\n";
78 #FIXME: make cross PCI <-> PCIe check sense at all??
80 my ($fail, $ignored) = (0, 0);
82 my ($id, $what, $ignore_if_same_key) = @_;
84 my ($bus, $addr) = $what->@{qw(bus addr)};
85 my $full_addr = "$bus:$addr";
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'");
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;
99 note
("ERR: conflict for '$full_addr' between '$id' and '$conflict'");
102 $addr_map->{$full_addr} = $id;
107 my $pci_map = PVE
::QemuServer
::PCI
::get_pci_addr_map
();
108 while (my ($id, $what) = each %$pci_map) {
109 check_conflict
($id, $what);
112 my $pcie_map = PVE
::QemuServer
::PCI
::get_pcie_addr_map
();
113 while (my ($id, $what) = each %$pcie_map) {
114 check_conflict
($id, $what);
117 my $pve_qm_cfg = slurp_qemu_config
("$qemu_cfg_base_path/pve-q35.cfg");
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);
123 # FIXME: restart with clean conflict $addr_map with only get_pci*_addr_map ones?
124 my $pve_qm4_cfg = slurp_qemu_config
("$qemu_cfg_base_path/pve-q35-4.0.cfg");
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);
129 my $pve_qm_usb_cfg = slurp_qemu_config
("$qemu_cfg_base_path/pve-usb.cfg");
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);
137 fail
("PCI(e) address conflict check, ignored: $ignored, conflicts: $fail");
139 pass
("PCI(e) address conflict check, ignored: $ignored");