mirror of
https://git.proxmox.com/git/qemu-server
synced 2025-05-28 18:45:07 +00:00
143 lines
3.5 KiB
Perl
Executable File
143 lines
3.5 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
|
|
use strict;
|
|
use warnings;
|
|
use experimental 'smartmatch';
|
|
|
|
use lib qw(..);
|
|
|
|
use Test::More;
|
|
|
|
use PVE::Tools qw(file_get_contents);
|
|
use PVE::QemuServer::PCI;
|
|
|
|
my $qemu_cfg_base_path = "../qemu-configs";
|
|
|
|
# not our format but that what QEMU gets passed with '-readconfig'
|
|
sub slurp_qemu_config {
|
|
my ($fn) = @_;
|
|
|
|
my $raw = file_get_contents($fn);
|
|
|
|
my $lineno = 0;
|
|
my $cfg = {};
|
|
my $group;
|
|
my $skip_to_next_group;
|
|
while ($raw =~ /^\h*(.*?)\h*$/gm) {
|
|
my $line = $1;
|
|
$lineno++;
|
|
next if !$line || $line =~ /^#/;
|
|
|
|
# tried to follow qemu's qemu_config_parse function
|
|
if ($line =~ /\[(\S{1,63}) "([^"\]]{1,63})"\]/) {
|
|
$group = $2;
|
|
$skip_to_next_group = 0;
|
|
if ($1 ne 'device') {
|
|
$group = undef;
|
|
$skip_to_next_group = 1;
|
|
}
|
|
} elsif ($line =~ /\[([^\]]{1,63})\]/) {
|
|
$group = undef;
|
|
$skip_to_next_group = 1;
|
|
} elsif ($group) {
|
|
if ($line =~ /(\S{1,63}) = "([^\"]{1,1023})"/) {
|
|
my ($k, $v) = ($1, $2);
|
|
$cfg->{$group}->{$k} = $v;
|
|
} else {
|
|
print "ignoring $fn:$lineno: $line\n";
|
|
}
|
|
} else {
|
|
warn "ignore $fn:$lineno, currently no group\n" if !$skip_to_next_group;
|
|
}
|
|
}
|
|
|
|
return $cfg;
|
|
}
|
|
|
|
sub extract_qemu_config_addrs {
|
|
my ($qemu_cfg) = @_;
|
|
|
|
my $addr_map = {};
|
|
for my $k (keys %$qemu_cfg) {
|
|
my $v = $qemu_cfg->{$k};
|
|
next if !$v || !defined($v->{bus}) || !defined($v->{addr});
|
|
|
|
my $bus = $v->{bus};
|
|
$bus =~ s/pci\.//;
|
|
|
|
$addr_map->{$k} = { bus => $bus, addr => $v->{addr} };
|
|
}
|
|
|
|
return $addr_map;
|
|
}
|
|
|
|
print "testing PCI(e) address conflicts\n";
|
|
|
|
# exec tests
|
|
|
|
#FIXME: make cross PCI <-> PCIe check sense at all??
|
|
my $addr_map = {};
|
|
my ($fail, $ignored) = (0, 0);
|
|
sub check_conflict {
|
|
my ($id, $what, $ignore_if_same_key) = @_;
|
|
|
|
my ($bus, $addr) = $what->@{qw(bus addr)};
|
|
my $full_addr = "$bus:$addr";
|
|
|
|
if (defined(my $conflict = $addr_map->{$full_addr})) {
|
|
if (my @ignores = $what->{conflict_ok}) {
|
|
if ($conflict ~~ @ignores) {
|
|
note("OK: ignore conflict for '$full_addr' between '$id' and '$conflict'");
|
|
$ignored++;
|
|
return;
|
|
}
|
|
}
|
|
# this allows to read multiple pve-*.cfg qemu configs, and check them
|
|
# normally their OK if they conflict is on the same key. Else TODO??
|
|
return if $ignore_if_same_key && $id eq $conflict;
|
|
|
|
note("ERR: conflict for '$full_addr' between '$id' and '$conflict'");
|
|
$fail++;
|
|
} else {
|
|
$addr_map->{$full_addr} = $id;
|
|
}
|
|
}
|
|
|
|
|
|
my $pci_map = PVE::QemuServer::PCI::get_pci_addr_map();
|
|
while (my ($id, $what) = each %$pci_map) {
|
|
check_conflict($id, $what);
|
|
}
|
|
|
|
my $pcie_map = PVE::QemuServer::PCI::get_pcie_addr_map();
|
|
while (my ($id, $what) = each %$pcie_map) {
|
|
check_conflict($id, $what);
|
|
}
|
|
|
|
my $pve_qm_cfg = slurp_qemu_config("$qemu_cfg_base_path/pve-q35.cfg");
|
|
my $pve_qm_cfg_map = extract_qemu_config_addrs($pve_qm_cfg);
|
|
while (my ($id, $what) = each %$pve_qm_cfg_map) {
|
|
check_conflict($id, $what);
|
|
}
|
|
|
|
# FIXME: restart with clean conflict $addr_map with only get_pci*_addr_map ones?
|
|
my $pve_qm4_cfg = slurp_qemu_config("$qemu_cfg_base_path/pve-q35-4.0.cfg");
|
|
my $pve_qm4_cfg_map = extract_qemu_config_addrs($pve_qm4_cfg);
|
|
while (my ($id, $what) = each %$pve_qm4_cfg_map) {
|
|
check_conflict($id, $what, 1);
|
|
}
|
|
my $pve_qm_usb_cfg = slurp_qemu_config("$qemu_cfg_base_path/pve-usb.cfg");
|
|
my $pve_qm_usb_cfg_map = extract_qemu_config_addrs($pve_qm_usb_cfg);
|
|
while (my ($id, $what) = each %$pve_qm_usb_cfg_map) {
|
|
check_conflict($id, $what, 1);
|
|
}
|
|
|
|
|
|
if ($fail) {
|
|
fail("PCI(e) address conflict check, ignored: $ignored, conflicts: $fail");
|
|
} else {
|
|
pass("PCI(e) address conflict check, ignored: $ignored");
|
|
}
|
|
|
|
done_testing();
|