./c0de/perl/dump_pirq.txt

0.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
#!/usr/bin/perl
#
# dump_pirq 1.21 2001/09/05 02:59:31
#
# A utility to parse the BIOS PCI IRQ Routing Table
#
# Copyright (C) 2000 David A. Hinds -- [email protected]
#
 
#-----------------------------------------------------------------------
 
sub dev {
    my($devfn) = @_;
    sprintf "%02x.%d", ($devfn>>3), ($devfn&7);
}
 
sub print_mask
{
    my($mask) = @_;
    printf "0x%04x [", $mask;
    for (my $i = 0; $i < 16; $i++) {
	next if (!($mask & (1<<$i)));
	$mask &= ~(1<<$i);
	print (($mask) ? "$i," : "$i");
    }
    print "]\n";
}
 
sub row {
    my($tag, $link, $mask) = @_;
    if ($link != 0) {
	printf "  INT$tag: link 0x%02x, irq mask ", $link;
	print_mask($mask);
    }
}
 
sub class_of
{
    my($slot) = @_;
    open(IN, "/sbin/lspci -s $slot |");
    $_ = <IN>;
    close(IN);
    return (/^....... ([^:]+):/) ? $1 : "";
}
 
sub parse_pirq
{
    my($buf) = @_;
 
    my($p) = index($buf, "\$PIR");
    my($minor,$major,$size,$rbus,$rdev,$mask,$cvd,$mini) =
	unpack "CCSCCSLL", substr($buf, $p+4, 16);
 
    printf "Interrupt routing table found at address 0xf%04x:\n", $p;
    printf "  Version $major.$minor, size 0x%04x\n", $size;
    printf "  Interrupt router is device %02x:%s\n", $rbus, dev($rdev);
    print "  PCI exclusive interrupt mask: ";
    print_mask($mask);
    if ($cvd) {
	printf("  Compatible router: vendor 0x%04x device 0x%04x\n",
	       ($cvd & 0xffff), ($cvd >> 16));
    }
 
    $ofs = 32;
    while ($ofs < $size) {
	# Parse a table entry describing a single PCI device
	($bus, $devfn, $la, $ma, $lb, $mb, $lc, $mc, $ld, $md, $slot) =
	    unpack "CCCSCSCSCSC", substr($buf, $p+$ofs, 15);
	$s = sprintf "%02x:%s", $bus, dev($devfn);
	printf "\nDevice $s (slot $slot): %s\n", class_of($s);
	row("A", $la, $ma); row("B", $lb, $mb);
	row("C", $lc, $mc); row("D", $ld, $md);
	push(@{$dev{$la}}, $s . "1");
	push(@{$dev{$lb}}, $s . "2");
	push(@{$dev{$lc}}, $s . "3");
	push(@{$dev{$ld}}, $s . "4");
	$ofs += 16;
    }
    return ($rbus, $rdev, $cvd);
}
 
#-----------------------------------------------------------------------
 
# The link values in the interrupt routing table are implementation
# dependent.  Here, we'll try to interpret the link values for some
# known PCI bridge types.
 
%pIIx = (0x122e8086, "82371FB PIIX",
	 0x70008086, "82371SB PIIX3",
	 0x71108086, "82371AB PIIX4/PIIX4E",
	 0x71988086, "82443MX",
	 0x24108086, "82801AA ICH",
	 0x24208086, "82801AB ICH0",
	 0x24408086, "82801BA ICH2",
	 0x244c8086, "82801BAM ICH2-M");
 
%via = (0x05861106, "82C586",
	0x05961106, "82C596",
	0x06861106, "82C686");
 
%opti = (0xc7001045, "82C700");
 
%pico = (0x00021066, "PT86C523");
 
%ali = (0x153310b9, "Aladdin M1533");
 
%sis = (0x04961039, "85C496/497",
	0x00081039, "85C503");
 
%cyrix = (0x01001078, "5530");
 
%all_routers = (%pIIx, %via, %opti, %pico, %ali, %sis, %cyrix);
 
sub outb
{
    my($data,$port) = @_;
    open(IO, ">/dev/port") || die;
    sysseek(IO, $port, 0) || die;
    my $x = pack "C", $data;
    syswrite(IO, $x, 1);
    close(IO);
}
 
sub inb
{
    my($port) = @_;
    my($data);
    open(IO, "/dev/port") || die;
    sysseek(IO, $port, 0) || die;
    sysread(IO, $data, 1);
    close(IO);
    return unpack "C", $data;
}
 
sub dump_router
{
    my($rbus, $rdev, $cvd) = @_;
    my($buf, @p, $i, $irq);
 
    printf "\nInterrupt router at %02x:%s: ", $rbus, dev($rdev);
    $rf = sprintf "/proc/bus/pci/%02x/%s", $rbus, dev($rdev);
    open(IN, $rf);
    if (sysread(IN, $buf, 0x100) != 0x100) {
	print "\nCould not read router info from $rf.\n";
	exit;
    }
    close(IN);
    my $vd = unpack "L", substr($buf, 0, 4);
 
    if ((defined $pIIx{$vd}) || (defined $pIIx{$cvd})) {
 
	$name = (defined $pIIx{$vd}) ? $pIIx{$vd} : $pIIx{$cvd};
	printf "Intel $name PCI-to-ISA bridge\n";
	@p = unpack "CCCCC", substr($buf, 0x60, 5);
	for ($i = 0; $i < 4; $i++) {
	    printf "  PIRQ%d (link 0x%02x): ", $i+1, 0x60+$i;
	    print (($p[$i] < 16) ? "irq $p[$i]\n" : "unrouted\n");
	}
	print "  Serial IRQ:";
	print (($p[4] & 0x80) ? " [enabled]" : " [disabled]");
	print (($p[4] & 0x40) ? " [continuous]" : " [quiet]");
	print " [frame=", (($p[4] >> 2) & 15) + 17, "]";
	print " [pulse=", (($p[4] & 3) * 4 + 4), "]\n";
	print "  Level mask: "; print_mask((inb(0x4d1)<<8) + inb(0x4d0));
 
    } elsif ((defined $via{$vd}) || (defined $via{$cvd})) {
 
	$name = (defined $via{$vd}) ? $via{$vd} : $via{$cvd};
	printf "VIA $name PCI-to-ISA bridge\n";
	$p = unpack "L", substr($buf, 0x55, 4);
	%tag = (1, "A", 2, "B", 3, "C", 5, "D");
	foreach $link (1,2,3,5) {
	    $irq = ($p >> ($link * 4)) & 15;
	    print "  PIRQ$tag{$link} (link 0x0$link): ";
	    print ($irq ? "irq $irq\n" : "unrouted\n");
	}
 
    } elsif ((defined $opti{$vd}) || (defined $opti{$cvd})) {
 
	$name = (defined $opti{$vd}) ? $opti{$vd} : $opti{$cvd};
	printf "OPTi $name PCI-to-ISA bridge\n";
	$p = unpack "S", substr($buf, 0xb8, 2);
	for ($i = 0; $i < 4; $i++) {
	    $irq = ($p >> ($i * 4)) & 15;
	    printf "  PCIRQ$i (link 0x%02x): ", ($i<<4)+0x02;
	    print ($irq ? "irq $irq\n" : "unrouted\n");
	}
 
    } elsif ((defined $pico{$vd} || defined $pico{$cvd})) {
 
	$name = (defined $pico{$vd}) ? $pico{$vd} : $pico{$cvd};
	printf "PicoPower $name PCI-to-ISA bridge\n";
	outb(0x10, 0x24); $p = inb(0x26);
	outb(0x11, 0x24); $p += inb(0x26)<<8;
	@tag = ("A", "B", "C", "D");
	for ($i = 0; $i < 4; $i++) {
	    $irq = ($p >> ($i * 4)) & 15;
	    print "  INT$tag[$i] (link 0x0", $i+1, "): ";
	    print ($irq ? "irq $irq\n" : "unrouted\n");
	}
 
    } elsif ((defined $ali{$vd} || defined $ali{$cvd})) {
 
	$name = (defined $ali{$vd}) ? $ali{$vd} : $ali{$cvd};
	printf "AcerLabs $name PCI-to-ISA bridge\n";
	$p = unpack "L", substr($buf, 0x48, 4);
	# This mapping is insane!
	@map = (0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15);
	for ($i = 0; $i < 8; $i++) {
	    $irq = ($p >> ($i*4)) & 15;
	    print "  INT", $i+1, " (link ", $i+1, "): ";
	    print ($map[$irq] ? "irq $map[$irq]\n" : "unrouted\n");
	}
	$s = unpack "C", substr($buf, 0x70, 1);
	print "  Serial IRQ:";
	print (($s & 0x80) ? " [enabled]" : " [disabled]");
	print (($s & 0x40) ? " [continuous]" : " [quiet]");
	print " [frame=", (($s >> 2) & 15) + 17, "]";
	print " [pulse=", (($s & 3) * 4 + 4), "]\n";
 
    } elsif ((defined $sis{$vd}) || (defined $sis{$cvd})) {
 
	$name = (defined $sis{$vd}) ? $sis{$vd} : $sis{$cvd};
	printf "SiS $name PCI-to-ISA bridge\n";
	$base = ($name eq "85C496/497") ? 0xc0 : 0x41;
	@p = unpack "CCCC", substr($buf, $base, 4);
	@tag = ("A", "B", "C", "D");
	for ($i = 0; $i < 4; $i++) {
	    $irq = ($p[$i] & 0x80) ? 0 : ($p[$i] & 0x0f);
	    printf "  INT$tag[$i] (link 0x%02x): ", $i+$base;
	    print ($irq ? "irq $irq\n" : "unrouted\n");
	}
 
    } elsif ((defined $cyrix{$vd}) || (defined $cyrix{$cvd})) {
 
	$name = (defined $cyrix{$vd}) ? $cyrix{$vd} : $cyrix{$cvd};
	printf "CYRIX $name PCI-to-ISA bridge\n";
	$p = unpack "S", substr($buf, 0x5c, 2);
	%tag = ("A", "B", "C", "D");
	for ($i = 0; $i < 4; $i++) {
	    $irq = ($p >> ($i * 4)) & 15;
	    printf "  PIRQ$tag{$i} (link 0x%02x): ", $i+1;
	    print ($irq ? "irq $irq\n" : "unrouted\n");
	}
	print "  Level mask: "; print_mask((inb(0x4d1)<<8) + inb(0x4d0));
 
    } else {
 
	printf("unknown vendor 0x%04x device 0x%04x\n",
	       ($vd & 0xffff), ($vd >> 16));
	foreach $k (sort keys %dev) {
	    next if ($k == 0);
	    printf "  PIRQ? (link 0x%02x): ", $k;
	    $irq = 0;
	    foreach $d (@{$dev{$k}}) {
		$d =~ /(..):(..)\..(.)/;
		($bus,$dev,$pin) = ($1,$2,$3);
		for ($fn = 0; $fn < 8; $fn++) {
		    open(IN, "/proc/bus/pci/$bus/$dev.$fn") || last;
		    sysread(IN, $buf, 0x100);
		    close(IN);
		    ($i,$p) = unpack "CC", substr($buf, 0x3c, 2);
		    $irq = $i if (($p == $pin) && $i && ($i != 255));
		}
	    }
	    print ($irq ? "irq $irq\n" : "unrouted?\n");
	}
    }
 
}
 
#-----------------------------------------------------------------------
 
# Grab the BIOS from 0xf0000-0xfffff
open(IN, "/dev/mem") || die "cannot open /dev/mem\n";
sysseek(IN, 0xf0000, 0) || die;
die if (sysread(IN, $buf, 0x10000) != 0x10000);
close(IN);
 
if (index($buf, "\$PIR") >= 0) {
 
    # Dump the PIRQ table, and the router information
    ($rbus, $rdev, $cvd) = parse_pirq($buf);
    dump_router($rbus, $rdev, $cvd);
 
} else {
 
    # Scan for any interrupt router device we recognize
    print "No PCI interrupt routing table was found.\n";
    open(DEV, "/proc/bus/pci/devices");
    while (<DEV>) {
	($rbus,$rdev,$vd) = /^(..)(..)\s+(........)/;
	$rbus = hex($rbus); $rdev = hex($rdev); $vd = hex($vd);
	$vd = (($vd & 0xffff0000) >> 16) | (($vd & 0xffff) << 16);
	if (defined $all_routers{$vd}) {
	    dump_router($rbus, $rdev, $vd);
	    $nr++;
	}
    }
    print "\nNo known PCI interrupt routers were found.\n" if (!$nr);
 
}
 

Back