MODULE PCITools;
IMPORT
SYSTEM, PCI, KernelLog, Streams, Files, Commands, Options, DriverDatabase;
CONST
HdrType = 0EH;
Verbose = FALSE;
ShowDrivers = TRUE;
PCIIDS = "pci.ids";
TYPE
Device = POINTER TO RECORD
bus: Bus;
sibling: Device;
next, prev: Device;
devfn: LONGINT;
device: LONGINT;
vendor: LONGINT;
class: LONGINT;
revision : LONGINT;
irq, pin: LONGINT
END;
Bus = POINTER TO RECORD
parent : Bus;
children : Bus;
next : Bus;
self: Device;
devices: Device;
number: LONGINT;
primary, secondary: LONGINT;
subordinate: LONGINT
END;
PROCEDURE ScanBus(VAR bus: Bus; VAR devices : Device): LONGINT;
VAR devfn, max, x, hdrtype, ht, buses: LONGINT; ismulti: BOOLEAN; dev: Device; child: Bus;
BEGIN
max := bus.secondary; ismulti := FALSE;
FOR devfn := 0 TO 0FEH DO
IF (devfn MOD 8 = 0) OR ismulti THEN
ReadConfigByte(bus.number, devfn, HdrType, hdrtype);
IF devfn MOD 8 = 0 THEN ismulti := ODD(hdrtype DIV 80H) END;
ReadConfigDword(bus.number, devfn, PCI.DevReg, x);
IF (x # -1) & (x # 0) THEN
NEW(dev);
dev.bus := bus; dev.devfn := devfn;
dev.vendor := x MOD 10000H;
dev.device := ASH(x, -16) MOD 10000H;
ReadConfigByte(bus.number, devfn, PCI.IntlReg, dev.irq);
ReadConfigByte(bus.number, devfn, PCI.IntlReg+1, dev.pin);
ReadConfigDword(bus.number, devfn, PCI.RevIdReg, x);
dev.class := ASH(x, -8) MOD 1000000H;
dev.revision := x MOD 100H;
CASE ASH(dev.class, -8) OF
604H: ht := 1
|607H: ht := 2
ELSE ht := 0
END;
IF ht = hdrtype MOD 80H THEN
dev.next := devices; devices := dev; dev.prev := NIL;
dev.sibling := bus.devices; bus.devices := dev;
IF ASH(dev.class, -8) = 604H THEN
NEW(child);
child.next := bus.children; bus.children := child;
child.self := dev; child.parent := bus;
INC(max); child.secondary := max; child.number := max;
child.primary := bus.secondary; child.subordinate := 0FFH;
ReadConfigDword(bus.number, devfn, 18H, buses);
IF buses MOD 1000000 # 0 THEN
child.primary := buses MOD 100H;
child.secondary := ASH(buses, -8) MOD 100H;
child.subordinate := ASH(buses, -16) MOD 100H;
child.number := child.secondary;
max := ScanBus(child, devices)
ELSE
KernelLog.String("PCI: Warning: Bus numbers not configured."); KernelLog.Ln;
END
END
ELSE
KernelLog.String("PCI: Warning: Unknown header type (Bus: "); KernelLog.Int(bus.number, 0);
KernelLog.String(", device: "); KernelLog.Int(dev.devfn DIV 8, 0);
KernelLog.String(", function: "); KernelLog.Int(dev.devfn MOD 8, 0);
KernelLog.String(", Header Type: "); KernelLog.Int(hdrtype, 0); KernelLog.Ln;
END;
END
END
END;
RETURN max
END ScanBus;
PROCEDURE Extract(classcode : LONGINT; VAR class, subclass, protocol : LONGINT);
BEGIN
class := SYSTEM.LSH(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, classcode) * {16..23}), -16);
subclass := SYSTEM.LSH(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, classcode) * {8..15}), -8);
protocol := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, classcode) * {0..7});
END Extract;
PROCEDURE InstallPCIDrivers;
VAR root : Bus; device : Device; nbrOfDevices : LONGINT; class, subclass, protocol : LONGINT;
BEGIN {EXCLUSIVE}
KernelLog.String("Looking for PCI devices..."); KernelLog.Ln;
NEW(root);
nbrOfDevices := ScanBus(root, device);
WHILE(device # NIL) DO
IF Verbose THEN
KernelLog.String("Bus: "); IF device.bus = NIL THEN KernelLog.String("n/a"); ELSE KernelLog.Int(device.bus.number, 2); END;
KernelLog.String(", Device: "); KernelLog.Int(ASH(device.devfn, -3) MOD 20H, 2); KernelLog.String(", Function: "); KernelLog.Int(device.devfn MOD 8, 2);
KernelLog.String(": "); KernelLog.String("VendorID: "); KernelLog.Hex(device.vendor, -4); KernelLog.String(", DeviceID: "); KernelLog.Hex(device.device, -4);
KernelLog.Ln;
END;
IF DriverDatabase.InstallDeviceDriver(DriverDatabase.PCI, device.vendor, device.device, device.revision) THEN
ELSE
Extract(device.class, class, subclass, protocol);
IF DriverDatabase.InstallClassDriver(DriverDatabase.PCI, class, subclass, protocol, device.revision) THEN
END;
END;
device := device.next;
END;
END InstallPCIDrivers;
PROCEDURE HexDigit(ch: CHAR): BOOLEAN;
BEGIN
RETURN (ch >= "0") & (ch <= "9") OR (CAP(ch) >= "A") & (CAP(ch) <= "F")
END HexDigit;
PROCEDURE Read(VAR r: Files.Reader; VAR ch: CHAR);
BEGIN
IF ch = Streams.EOT THEN ch := 0X ELSE r.Char(ch) END
END Read;
PROCEDURE WriteDevice(w: Streams.Writer; class: BOOLEAN; p1, p2, p3: LONGINT; CONST l1, l2, l3: ARRAY OF CHAR; pciids : Files.File);
VAR r: Files.Reader; ch: CHAR; level, value: LONGINT;
PROCEDURE SkipLine(write: BOOLEAN);
BEGIN
WHILE (ch # 0X) & (ch # 0DX) & (ch # 0AX) DO
IF write THEN w.Char(ch) END;
Read(r, ch)
END;
REPEAT Read(r, ch) UNTIL (ch # 0DX) & (ch # 0AX)
END SkipLine;
PROCEDURE ReadHex(VAR x: LONGINT);
BEGIN
x := 0;
LOOP
IF (ch >= "0") & (ch <= "9") THEN
x := x * 16 + (ORD(ch)-ORD("0"))
ELSIF (CAP(ch) >= "A") & (CAP(ch) <= "F") THEN
x := x * 16 + (ORD(CAP(ch))-ORD("A")+10)
ELSE
EXIT
END;
Read(r, ch)
END
END ReadHex;
PROCEDURE GetLine(VAR level, value: LONGINT);
BEGIN
IF class THEN
IF ch = "C" THEN Read(r, ch); Read(r, ch) END
END;
WHILE (ch # 0X) & (ch # 9X) & ~HexDigit(ch) DO SkipLine(FALSE) END;
level := 0; WHILE ch = 9X DO INC(level); Read(r, ch) END;
ReadHex(value);
WHILE ch = " " DO Read(r, ch) END
END GetLine;
PROCEDURE Label(CONST l: ARRAY OF CHAR);
BEGIN
w.String(l); w.String(": ");
END Label;
BEGIN
IF pciids = NIL THEN
Label(l1); w.String("Unknown");
Label(l2); w.String("Unknown");
ELSE
NEW(r, pciids, 0); Read(r, ch);
IF class THEN WHILE (ch # 0X) & (ch # "C") DO SkipLine(FALSE) END; END;
LOOP
GetLine(level, value);
IF (ch = 0X) OR (level = 0) & (value = p1) THEN EXIT END;
SkipLine(FALSE)
END;
Label(l1);
IF (ch # 0X) & (level = 0) & (value = p1) THEN
SkipLine(TRUE); w.String(", ");
LOOP
GetLine(level, value);
IF (ch = 0X) OR (level = 0) OR (level = 1) & (value = p2) THEN EXIT END;
SkipLine(FALSE)
END;
Label(l2);
IF (ch # 0X) & (level = 1) & (value = p2) THEN
SkipLine(TRUE);
LOOP
GetLine(level, value);
IF (ch = 0X) OR (level < 2) OR (level = 2) & (value = p3) THEN EXIT END;
SkipLine(FALSE)
END;
IF (ch # 0X) & (level = 2) & (value = p3) THEN
w.String(", "); Label(l3); SkipLine(TRUE)
END
ELSE
w.String("Unknown")
END
ELSE
w.String("Unknown")
END
END;
END WriteDevice;
PROCEDURE WriteB(w: Streams.Writer; x: LONGINT);
CONST K = 1024; M = K*K; G = K*M;
VAR mult: CHAR;
BEGIN
IF x MOD K # 0 THEN
w.Int(x, 1)
ELSE
IF x MOD M # 0 THEN mult := "K"; x := x DIV K
ELSIF x MOD G # 0 THEN mult := "M"; x := x DIV M
ELSE mult := "G"; x := x DIV G
END;
w.Int(x, 1); w.Char(mult)
END;
w.String("B")
END WriteB;
PROCEDURE WriteBase(w: Streams.Writer; bus, devfn, reg: LONGINT; VAR double: BOOLEAN);
VAR base, basehi, type, size: LONGINT; mask: SET;
BEGIN
double := FALSE; basehi := 0; size := 0;
ReadConfigDword(bus, devfn, reg, base);
IF base # 0 THEN
WriteConfigDword(bus, devfn, reg, -1);
ReadConfigDword(bus, devfn, reg, size);
WriteConfigDword(bus, devfn, reg, base);
IF ODD(base) THEN
IF ASH(base, -16) = 0 THEN mask := {2..15} ELSE mask := {2..31} END;
type := base MOD 4
ELSE
mask := {4..31}; type := base MOD 10H
END;
size := SYSTEM.VAL(LONGINT, -(SYSTEM.VAL(SET, size) * mask))+1;
size := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, size) * mask);
IF type MOD 8 = 4 THEN
ReadConfigDword(bus, devfn, reg+4, basehi); double := TRUE
END;
DEC(base, type);
w.Char(9X); w.Char(9X);
WriteB(w, size); w.String(" ");
CASE type OF
0: w.String("32-bit memory")
|1: w.String("I/O")
|4: w.String("64-bit memory")
|8: w.String("prefetchable 32-bit memory")
|12: w.String("prefetchable 64-bit memory")
ELSE w.String("type "); w.Int(type, 1)
END;
w.String(" at ");
IF basehi # 0 THEN w.Hex(basehi, -2) END;
w.Hex(base, -8); w.String("-");
IF basehi # 0 THEN w.Hex(basehi, -8) END;
w.Hex(base + size - 1, -8); w.Ln;
END
END WriteBase;
PROCEDURE WriteDriver(w : Streams.Writer; dev : Device);
VAR d, c : DriverDatabase.Driver; class, subclass, progintf : LONGINT;
BEGIN
w.String("Driver: ");
d := DriverDatabase.GetDeviceSpecific(DriverDatabase.PCI, dev.vendor, dev.device, dev.revision);
Extract(dev.class, class, subclass, progintf);
c := DriverDatabase.GetClassSpecific(DriverDatabase.PCI, class, subclass, progintf, dev.revision);
IF (c = NIL) & (d = NIL) THEN w.String("n/a");
ELSIF (d # NIL) THEN
w.String(d.commands^);
ELSIF (c # NIL) THEN
w.String(c.commands^);
END;
END WriteDriver;
PROCEDURE WriteDev(w: Streams.Writer; dev: Device; pciids : Files.File; details : BOOLEAN);
VAR bus, devfn, hdrtype, classrev, vendor, device, cmd, status, lastreg, reg, base: LONGINT; double: BOOLEAN;
BEGIN
bus := dev.bus.number; devfn := dev.devfn;
ReadConfigByte(bus, devfn, HdrType, hdrtype);
ReadConfigDword(bus, devfn, PCI.RevIdReg, classrev);
ReadConfigWord(bus, devfn, PCI.DevReg, vendor);
ReadConfigWord(bus, devfn, PCI.DevReg+2, device);
ReadConfigWord(bus, devfn, PCI.CmdReg+2, status);
ReadConfigWord(bus, devfn, PCI.CmdReg, cmd);
w.String("Bus "); w.Int(bus, 1);
w.String(", device "); w.Int(ASH(devfn, -3) MOD 20H, 1);
w.String(", function "); w.Int(devfn MOD 8, 1);
w.String(": class/rev "); w.Hex(classrev, -8);
w.String(", vendor/device "); w.Hex(ASH(vendor, 16) + device, -8);
w.String(", status/cmd "); w.Hex(ASH(status, 16) + cmd, -8);
w.Ln;
w.Char(9X);
WriteDevice(w, TRUE, ASH(classrev, -24) MOD 100H, ASH(classrev, -16) MOD 100H, ASH(classrev, -8) MOD 100H, "Class", "Sub-class", "ProgIntfc", pciids);
w.Ln;
w.Char(9X);
WriteDevice(w, FALSE, vendor, device, -1, "Vendor", "Device", "", pciids);
w.Ln;
IF ShowDrivers THEN
w.Char(9X); WriteDriver(w, dev); w.Ln;
END;
IF (dev.irq # 0) OR (dev.pin # 0) THEN
w.Char( 9X); w.Char( 9X);
w.String("IRQ"); w.Int( dev.irq, 1);
IF dev.pin # 0 THEN
w.String(", INT"); w.Char( CHR(ORD("A")+dev.pin-1))
END;
w.Ln;
END;
CASE hdrtype MOD 80H OF
0: lastreg := PCI.Adr5Reg
|1: lastreg := PCI.Adr1Reg
ELSE lastreg := 0
END;
FOR reg := PCI.Adr0Reg TO lastreg BY 4 DO
WriteBase(w, bus, devfn, reg, double);
IF double THEN INC(reg, 4) END
END;
IF hdrtype MOD 80H = 0 THEN
ReadConfigDword(bus, devfn, PCI.ROMReg, base);
IF base # 0 THEN
w.Char(9X); w.Char(9X);
w.String("ROM at");
w.Hex(base, -8); w.Ln;
END
END;
IF details THEN WriteRegs(w, dev); WriteCmdSts(w, dev); END;
w.Ln;
END WriteDev;
PROCEDURE WriteRegs(w : Streams.Writer; dev : Device);
VAR value, offset : LONGINT;
BEGIN
w.Char(9X); w.String("PCI Configuration Space Registers: "); w.Ln;
w.Char(0EX);
FOR offset := 0 TO 3CH BY 4 DO
w.Char(9X); w.Char(9X); w.Hex(offset, -2); w.Char("h"); w.Char(9X); w.Char(9X);
ReadConfigDword(dev.bus.number, dev.devfn, PCI.DevReg + offset, value);
w.Hex(value, -8); w.Ln;
END;
w.Char(0FX);
END WriteRegs;
PROCEDURE WriteCmdSts(w : Streams.Writer; dev : Device);
VAR value : LONGINT; dword : SET;
BEGIN
ReadConfigDword(dev.bus.number, dev.devfn, PCI.CmdReg, value); dword := SYSTEM.VAL(SET, value);
w.Char(9X); w.String("Command Register: "); w.Ln;
w.Char(9X); w.Char(9X);
w.String("IO Space: "); IF 0 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
w.String(", Memory Space: "); IF 1 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
w.String(", Bus Master: "); IF 2 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
w.String(", Special Cycles: "); IF 3 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
w.String(", Memory Write and Invalidate: "); IF 4 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
w.Ln;
w.Char(9X); w.Char(9X);
w.String("VGA Palette Snoop: "); IF 5 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
w.String(", Parity Error Response: "); IF 6 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
w.String(", Stepping Control: "); IF 7 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
w.String(", SERR#: "); IF 8 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
w.String(", Fast Back-to-Back: "); IF 9 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
w.Ln;
w.Char(9X); w.String("Status Register: "); w.Ln;
w.Char(9X); w.Char(9X);
w.String("Capabilities List: "); IF 4 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
w.String(", 66MHz Capable: "); IF 5 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
w.String(", Fast Back-to-Back Capable: "); IF 7 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
w.String(", Master Data Parity Error: "); IF 8 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
w.Ln;
w.Char(9X); w.Char(9X);
w.String("DEVSEL timing: ");
IF {9+16, 10+16} * dword = {} THEN w.String("Fast");
ELSIF {9+16, 10+16} * dword = {9+16} THEN w.String("Medium");
ELSIF {9+16, 10+16} * dword = {10+16} THEN w.String("Slow");
ELSE w.String("ERROR");
END;
w.Ln;
w.Char(9X); w.Char(9X);
w.String("Signaled Target Abort: "); IF 11 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
w.String(", Received Target Abort: "); IF 12 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
w.String(", Received Master Abort: "); IF 13 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
w.String(", Signaled System Error: "); IF 14 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
w.String(", Detected Parity Error: "); IF 15 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
w.Ln;
END WriteCmdSts;
PROCEDURE ReadConfigByte(bus, devfn, ofs: LONGINT; VAR val: LONGINT);
VAR res: LONGINT;
BEGIN
res := PCI.ReadConfigByte(bus, ASH(devfn, -3) MOD 20H, devfn MOD 8, ofs, val);
ASSERT(res = PCI.Done)
END ReadConfigByte;
PROCEDURE ReadConfigWord(bus, devfn, ofs: LONGINT; VAR val: LONGINT);
VAR res: LONGINT;
BEGIN
res := PCI.ReadConfigWord(bus, ASH(devfn, -3) MOD 20H, devfn MOD 8, ofs, val);
ASSERT(res = PCI.Done)
END ReadConfigWord;
PROCEDURE ReadConfigDword(bus, devfn, ofs: LONGINT; VAR val: LONGINT);
VAR res: LONGINT;
BEGIN
res := PCI.ReadConfigDword(bus, ASH(devfn, -3) MOD 20H, devfn MOD 8, ofs, val);
ASSERT(res = PCI.Done)
END ReadConfigDword;
PROCEDURE WriteConfigDword(bus, devfn, ofs, val: LONGINT);
VAR res: LONGINT;
BEGIN
res := PCI.WriteConfigDword(bus, ASH(devfn, -3) MOD 20H, devfn MOD 8, ofs, val);
ASSERT(res = PCI.Done)
END WriteConfigDword;
PROCEDURE Scan*(context : Commands.Context);
VAR
options : Options.Options;
root : Bus; dev, prev: Device;
version, lastPCIBus, hw, count: LONGINT;
pciids : Files.File;
BEGIN {EXCLUSIVE}
NEW(options);
options.Add("d", "details", Options.Flag);
IF options.Parse(context.arg, context.out) THEN
context.out.String("PCITools: PCI bus enumeration:"); context.out.Ln;
IF PCI.PCIPresent(version, lastPCIBus, hw) = PCI.Done THEN
context.out.String("PCI Bus Information: "); context.out.Int(lastPCIBus + 1, 0); context.out.String(" bus(ses) found, PCI version: ");
context.out.Hex(version DIV 256, -2); context.out.Char("."); context.out.Hex(version MOD 256, -2); context.out.Ln;
context.out.Ln;
NEW(root);
root.subordinate := ScanBus(root, dev);
count := 0; prev := NIL;
WHILE dev # NIL DO
dev.prev := prev; prev := dev;
dev := dev.next; INC(count)
END;
pciids := Files.Old(PCIIDS);
WHILE prev # NIL DO
WriteDev(context.out, prev, pciids, options.GetFlag("details"));
prev := prev.prev
END;
context.out.Int(count, 1); context.out.String(" devices found"); context.out.Ln;
ELSE
context.out.String("PCI not present"); context.out.Ln;
END;
END;
END Scan;
PROCEDURE DetectHardware*(context : Commands.Context);
VAR ver, last, hw : LONGINT;
BEGIN
IF DriverDatabase.enabled THEN
IF (PCI.PCIPresent(ver, last, hw) = PCI.Done) THEN
InstallPCIDrivers;
ELSE
context.out.String("PCITools: No PCI bus found."); context.out.Ln;
END;
ELSE
context.out.String("PCITools: Automatic hardware detedtion is disabled."); context.out.Ln;
END;
END DetectHardware;
END PCITools.
PCITools.Scan ~ SystemTools.Free PCITools DriverDatabase ~
PCITools.Scan details ~
PCITools.DetectHardware ~