/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    pci.c

Abstract:

    WinDbg Extension Api

Author:

    Ken Reneris (kenr) 18-August-1997

Environment:

    User Mode.

Revision History:

--*/

#include "precomp.h"
#include "pci.h"
#pragma hdrstop


#define DUMP_VERBOSE                    0x01
#define DUMP_TO_MAX_BUS                 0x02        // from 0 to max
#define DUMP_RAW_BYTES                  0x04        // hex dump dump raw bytes
#define DUMP_RAW_DWORDS                 0x08        // hex dump dump raw dwords

#define DUMP_ALLOW_INVALID_DEVICE       0x10
#define DUMP_ALLOW_INVALID_FUNCTION     0x20
#define DUMP_CAPABILITIES               0x40
#define DUMP_INTEL                      0x80

#define DUMP_CONFIGSPACE               0x100




#define ANY                 0xFF

PSZ GetClassDesc(BYTE bBaseClass, BYTE bSubClass, BYTE bProgIF);

UCHAR PCIDeref[4][4] = { {4,1,2,2},{1,1,1,1},{2,1,2,2},{1,1,1,1} };

VOID
DumpCfgSpace (
    IN PPCI_COMMON_CONFIG pcs
    );

VOID
HexDump (
    IN ULONG    indent,
    IN ULONG    va,
    IN ULONG    len,
    IN ULONG    width,
    IN PUCHAR   buf
    )
{
    UCHAR   s[80], t[80];
    PUCHAR  ps, pt;
    ULONG   i;
    static  UCHAR rgHexDigit[] = "0123456789abcdef";

    i = 0;

    //
    // If width = 4, dull dump, similar to debugger's dd command.
    //

    if (width == 4) {
        if (len & 3) {
            dprintf("hexdump internal error, dump dword, (len & 3) != 0\n");

            // round up.

            len += 3;
            len &= ~3;
        }
        while (len) {
            if (i == 0) {
                dprintf("%*s%08x: ", indent, "", va);
                va += 16;
            }
            dprintf(" %08x", *(ULONG UNALIGNED *)buf);
            len -= 4;
            buf += 4;
            if (i == 3) {
                dprintf("\n");
                i = 0;
            } else {
                i++;
            }
        }
        return;
    }

    if (width != 1) {
        dprintf ("hexdump internal error\n");
        return ;
    }

    //
    // Width = 1, pretty dump, similar to debugger's db command.
    //

    while (len) {
        ps = s;
        pt = t;

        ps[0] = 0;
        pt[0] = '*';
        pt++;

        for (i=0; i < 16; i++) {
            ps[0] = ' ';
            ps[1] = ' ';
            ps[2] = ' ';

            if (len) {
                ps[0] = rgHexDigit[buf[0] >> 4];
                ps[1] = rgHexDigit[buf[0] & 0xf];
                pt[0] = buf[0] < ' ' || buf[0] > 'z' ? '.' : buf[0];

                len -= 1;
                buf += 1;
                pt  += 1;
            }
            ps += 3;
        }

        ps[0] = 0;
        pt[0] = '*';
        pt[1] = 0;
        s[23] = '-';

        if (s[0]) {
            dprintf ("%*s%08lx: %s  %s\n", indent, "", va, s, t);
            va += 16;
        }
    }

}

BOOL
ReadPci (
    IN PPCI_TYPE1_CFG_BITS      PciCfg1,
    OUT PUCHAR                  Buffer,
    IN ULONG                    Offset,
    IN ULONG                    Length
    )
{
    ULONG                   InputSize;
    ULONG                   IoSize;
    ULONG                   i;
    BUSDATA                 busData;
    PCI_SLOT_NUMBER         slot;
    BOOL                    b;
    
    //
    // Zap input buffer
    //

    for (i=0; i < Length; i++) {
        Buffer[i] = 0xff;
    }

    //
    // It appears that we are only safe to call the HAL for reading
    // configuration space if the HAL has actually been initialized far
    // enough to do so.  Since we have already hit a case where we hadnt
    // initialized everything and it crashed the debugger, we are restoring
    // X86 so that it reads configspace the way it always used to do.
    //
    // For non-X86 (i.e IA64) we are forced to call the HAL because we
    // currently have no other option.  This means we may still crash on
    // those platforms in the case where
    // the HAL hasnt been initialized enough to handle it.
    //

    if (TargetMachine == IMAGE_FILE_MACHINE_I386) {

        while (Length) {
            PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG);
            IoSize = sizeof(ULONG);
#ifdef IG_IO_SPACE_RETURN
            b = 
#else
            b = TRUE;
#endif          
                WriteIoSpace64 ( PCI_TYPE1_ADDR_PORT, PciCfg1->u.AsULONG, &IoSize );
            if (!b) {
                return FALSE;
            }
            IoSize = PCIDeref[Offset % sizeof(ULONG)][Length % sizeof(ULONG)];
            i = IoSize;
    
            ReadIoSpace64 (
                PCI_TYPE1_DATA_PORT + (Offset % sizeof(ULONG)),
                (PULONG) Buffer,
                &i
                );
    
            Offset += IoSize;
            Buffer += IoSize;
            Length -= IoSize;
        }
    }else{
    
        //
        //  Here we are going to call the debugger api that results in the 
        //  call to HalGetBusDataByOffset for the read.
        //  
        //  Note: This will crash the current debug session of attempted too
        //  early in boot.
        //

        slot.u.AsULONG              = 0;
        slot.u.bits.DeviceNumber    = PciCfg1->u.bits.DeviceNumber;
        slot.u.bits.FunctionNumber  = PciCfg1->u.bits.FunctionNumber;
    
        busData.BusDataType         = PCIConfiguration;
        busData.BusNumber           = PciCfg1->u.bits.BusNumber;
        busData.SlotNumber          = slot.u.AsULONG;
        busData.Offset              = Offset;
        busData.Buffer              = Buffer;
        busData.Length              = Length;
    
        //
        // Read it
        //
#ifdef IG_IO_SPACE_RETURN
        b = 
#else
        b = TRUE;
#endif          
        Ioctl(IG_GET_BUS_DATA, &busData, sizeof(busData));
        if (!b) {
            return FALSE;
        }
    }
    return TRUE;
}

BOOLEAN
WritePci (
    IN PPCI_TYPE1_CFG_BITS      PciCfg1,
    IN PUCHAR                   Buffer,
    IN ULONG                    Offset,
    IN ULONG                    Length
    )
{
    
    ULONG                   IoSize;
    ULONG                   i;
    BUSDATA                 busData;
    PCI_SLOT_NUMBER         slot;
    
    if (TargetMachine == IMAGE_FILE_MACHINE_I386) {

        //
        //  For the same reasons as the read, we are only calling the HAL
        //  on non-x86 targets for now.
        // 
    
        while (Length) {
            PciCfg1->u.bits.RegisterNumber = Offset / sizeof(ULONG);
            IoSize = sizeof(ULONG);
            WriteIoSpace64 ((ULONG) PCI_TYPE1_ADDR_PORT, PciCfg1->u.AsULONG, &IoSize );
    
            IoSize = PCIDeref[Offset % sizeof(ULONG)][Length % sizeof(ULONG)];
            i = IoSize;
    
            WriteIoSpace64 (
                PCI_TYPE1_DATA_PORT + (Offset % sizeof(ULONG)),
                *(PULONG)Buffer,
                &i);
    
            Offset += IoSize;
            Buffer += IoSize;
            Length -= IoSize;
        }
    }else{

        slot.u.AsULONG              = 0;
        slot.u.bits.DeviceNumber    = PciCfg1->u.bits.DeviceNumber;
        slot.u.bits.FunctionNumber  = PciCfg1->u.bits.FunctionNumber;
    
        busData.BusDataType         = PCIConfiguration;
        busData.BusNumber           = PciCfg1->u.bits.BusNumber;
        busData.SlotNumber          = slot.u.AsULONG;
        busData.Offset              = Offset;
        busData.Buffer              = Buffer;
        busData.Length              = Length;

        //
        // Write it
        //
        if (!(Ioctl(IG_SET_BUS_DATA, &busData, sizeof(busData)))){
            return FALSE;
        }


    }

    return TRUE;
}

VOID
DumpPciBar (
    IN ULONG        barno,
    IN ULONG        indent,
    IN ULONG        bar,
    IN OUT PULONG   state
    )
{
    ULONG       type, i;
    CHAR        m[20], str[80];

    if (bar) {

        if (bar & 1) {
            sprintf (str, "IO[%d]:%x  ", barno, bar);

        } else {
            type = (bar >> 1) & 0x3;

            if (bar & 8) {
                strcpy (m, "MPF");
            } else {
                strcpy (m, "MEM");
            }

            if (type == 0x01) {
                m[1] = '1';         // less then 1M
            }

            sprintf (str, "%s[%d]:%x  ", m, barno, bar);

            if (type == 0x10) {
                dprintf ("warning - 64bit bar not decoded\n");
                *state = 0;
            }
            if (type == 0x11) {
                dprintf ("bar type is reserved\n");
                *state = 0;
            }
        }

        if (!*state) {
            dprintf ("%*s", indent, "");
        }

        i = strlen(str);
        dprintf("%s%*s", str, 17-i, "");
        *state += i;
    }
}


VOID
DumpPciType2Bar(
    IN BOOLEAN    barIsIo,
    IN BOOLEAN    barIsBase,
    IN ULONG      barno,
    IN ULONG      indent,
    IN ULONG      bar,
    IN OUT PULONG state
    )
{
   ULONG      i;
   CHAR       str[80];
   if (bar) {
     if (barIsIo) {
          sprintf (str, "IO[%d].%s:%x  ", barno, (barIsBase?"base":"limit"), bar);
     } else {
          sprintf (str, "MEM[%d].%s:%x  ", barno, (barIsBase?"base":"limit"), bar);
     }
     if (!*state) {
          dprintf("%*s", indent, "");
     }
     i = strlen(str);
     dprintf("%s%*s", str, 17-i, "");
     *state += i;
   }
}
    
               
VOID
DumpPciBarComplete(
    IN OUT PULONG   state
    )
{
    if (*state) {
        dprintf ("\n");
        *state = 0;
    }
}


VOID
DumpCapabilities(
    IN UCHAR                    CapPtr,
    IN PPCI_TYPE1_CFG_BITS      PciCfg1
    )
{
    union _cap_buffer {
        PCI_CAPABILITIES_HEADER header;
        PCI_PM_CAPABILITY       pm;
        PCI_AGP_CAPABILITY      agp;
    } cap;

    PCHAR ts;
    ULONG  t;

    do {
        if (CapPtr < PCI_COMMON_HDR_LENGTH) {

            dprintf("       Error: Capability pointer 0x%02x points to common header (invalid).\n",
                CapPtr
                );
            break;
        }

        if (CapPtr & 0x3) {

            dprintf("       Error: Capability pointer 0x%02x not DWORD aligned (invalid).\n",
                CapPtr
                );
            break;
        }

        ReadPci (
            PciCfg1,
            (PUCHAR)&cap,
            CapPtr,
            sizeof(cap.header)
            );

        switch (cap.header.CapabilityID) {
            case PCI_CAPABILITY_ID_POWER_MANAGEMENT:

                ReadPci (
                    PciCfg1,
                    (PUCHAR)&cap,
                    CapPtr,
                    sizeof(cap.pm)
                    );
                t = cap.pm.PMC.AsUSHORT;
                dprintf("      Cap[%02x] ID %02x \n",
                    CapPtr,
                    cap.header.CapabilityID
                    );
                dprintf("          PMC   %04x (%s%s%s%s%s%s %s%s%s%s%s%sv%x)\n",
                    t,
                    t & 0xf800 ? "PME from D" : "<No PME>",
                    t & 0x8000 ? "3C" : "",
                    t & 0x4000 ? "3H" : "",
                    t & 0x2000 ? "2" : "",
                    t & 0x1000 ? "1" : "",
                    t & 0x0800 ? "0" : "",
                    t & 0x0600 ? "Supports D" : "",
                    cap.pm.PMC.Capabilities.Support.D2 ? "2" : "",
                    cap.pm.PMC.Capabilities.Support.D1 ? "1" : "",
                    t & 0x0600 ? " " : "",
                    cap.pm.PMC.Capabilities.DeviceSpecificInitialization ?
                        "DSI " : "",
                    cap.pm.PMC.Capabilities.PMEClock ? "PME needs Clock, " : "",
                    cap.pm.PMC.Capabilities.Version
                    );
                        
                t &= 0x01d0;
                if (t) {
                    dprintf("                         WARNING PMC non-zero reserved fields %04x\n",
                        t
                        );
                }

                t = cap.pm.PMCSR.AsUSHORT;
                dprintf("          PMCSR %04x (PME_Status=%d PME_En=%d State=D%d%s)\n",
                    t,
                    cap.pm.PMCSR.ControlStatus.PMEStatus,
                    cap.pm.PMCSR.ControlStatus.PMEEnable,
                    cap.pm.PMCSR.ControlStatus.PowerState,
                    cap.pm.PMCSR.ControlStatus.PowerState == 3 ?
                        "hot" : ""
                    );

                //
                // Here would be a good time to run
                // run down the data registers if
                // they exist.
                //

                break;

            case PCI_CAPABILITY_ID_AGP:

                ReadPci (
                    PciCfg1,
                    (PUCHAR)&cap,
                    CapPtr,
                    sizeof(cap.agp)
                    );

                switch (cap.agp.AGPStatus.Rate) {
                    case 1:
                        ts = "1X";
                        break;
                    case 2:
                        ts = "2X";
                        break;
                    case 3:
                        ts = "1,2X";
                        break;
                    default:
                        ts = "<inv>";
                        break;
                }
                t = *(PULONG)&cap.agp.AGPStatus;

                dprintf("      Cap[%02x] ID %02x AGP mj/mn=%x/%x\n",
                    CapPtr,
                    cap.header.CapabilityID,
                    cap.agp.Major,
                    cap.agp.Minor
                    );

                dprintf("          Status  %08x (Rq:%02x SBA:%x Rate:%x (%s))\n",
                    t,
                    cap.agp.AGPStatus.RequestQueueDepthMaximum,
                    cap.agp.AGPStatus.SideBandAddressing,
                    cap.agp.AGPStatus.Rate,
                    ts
                    );

                switch (cap.agp.AGPCommand.Rate) {
                    case 1:
                        ts = "1X";
                        break;
                    case 2:
                        ts = "2X";
                        break;
                    case 4:
                        ts = "4X";
                        break;
                    default:
                        ts = "<not set>";
                        break;
                }
                t = *(PULONG)&cap.agp.AGPCommand;

                dprintf("          Command %08x (Rq:%02x SBA:%x AGP:%x Rate:%x (%s)\n",
                    t,
                    cap.agp.AGPCommand.RequestQueueDepth,
                    cap.agp.AGPCommand.SBAEnable,
                    cap.agp.AGPCommand.AGPEnable,
                    cap.agp.AGPCommand.Rate,
                    ts
                    );

                break;

            default:

                break;
        }
        CapPtr = cap.header.Next;
    } while (CapPtr != 0);
}

VOID
pcidump (
    IN ULONG        Flags,
    IN ULONG        MinBus,
    IN ULONG        MaxBus,
    IN ULONG        MinDevice,
    IN ULONG        MaxDevice,
    IN ULONG        MinFunction,
    IN ULONG        MaxFunction,
    IN ULONG        MinAddr,
    IN ULONG        MaxAddr
    )
{
    ULONG                   Bus, Device, Function;
    PCI_TYPE1_CFG_BITS      PciCfg1;
    PCI_COMMON_CONFIG       PciHdr;
    BOOLEAN                 BusHeader, SkipLine, BarIsIo;
    ULONG                   Type, Len, i;
    UCHAR                   s[40];
    PUCHAR                  Buf;
    ULONG                   state;
    ULONG                   bar, barno;

    if (MinBus > 0xFF || MaxBus > 0xFF ||
        MinDevice > PCI_MAX_DEVICES || MaxDevice > PCI_MAX_DEVICES ||
        MinFunction > PCI_MAX_FUNCTION || MaxFunction > PCI_MAX_FUNCTION ||
        MinAddr > 0xFF || MaxAddr > 0x100 || MinAddr > MaxAddr) {

        dprintf ("Bad pci dump parameter\n");

        //dprintf ("Flags %d  MinBus %d  MaxBus %d\n", Flags, MinBus, MaxBus);
        //dprintf ("MinDev %d  MaxDev %d  MinFnc %d MinFnc %d\n", MinDevice, MaxDevice, MinFunction, MaxFunction);

        return ;
    }

    //dprintf ("Flags %d  MinAddr %d  MaxAddr %d\n", Flags, MinAddr, MaxAddr);

    for (Bus=MinBus; Bus <= MaxBus; Bus++) {

        BusHeader = FALSE;

        for (Device=MinDevice; Device <= MaxDevice; Device++) {

            if (CheckControlC()) {
                return;
            }

            //
            // Read the device ID
            //

            PciCfg1.u.AsULONG = 0;
            PciCfg1.u.bits.BusNumber = Bus;
            PciCfg1.u.bits.DeviceNumber = Device;
            PciCfg1.u.bits.FunctionNumber = 0;
            PciCfg1.u.bits.Enable = TRUE;

            ReadPci (&PciCfg1, (PUCHAR) &PciHdr, 0, sizeof(ULONG));

            //
            // If not a valid ID, skip to next device

            if (PciHdr.VendorID == PCI_INVALID_VENDORID) {
                if (!(Flags & DUMP_ALLOW_INVALID_DEVICE)) {
                    dprintf ("%02x\r", Device);
                    continue;
                }
            }


            for (Function=MinFunction; Function <= MaxFunction; Function++) {

                if (CheckControlC()) {
                    return;
                }

                PciCfg1.u.bits.FunctionNumber = Function;

                //
                // Read device ID
                //

                if (Function) {
                    ReadPci (&PciCfg1, (PUCHAR) &PciHdr, 0, sizeof(ULONG));
                }

                if (PciHdr.VendorID == PCI_INVALID_VENDORID) {
                    if (!(Flags & DUMP_ALLOW_INVALID_DEVICE)) {
                        continue;
                    }
                }

                //
                // Dump ID
                //

                if (!BusHeader) {
                    dprintf ("PCI Bus %d\n", Bus);
                    BusHeader = TRUE;
                }

                dprintf ("%02x:%x  %04x:%04x",
                        Device,
                        Function,
                        PciHdr.VendorID,
                        PciHdr.DeviceID
                        );

                //
                // Read the rest of the common header
                //

                ReadPci (
                    &PciCfg1,
                    ((PUCHAR) &PciHdr)  + sizeof(ULONG),
                    0                   + sizeof(ULONG),
                    PCI_COMMON_HDR_LENGTH
                    );

                Type = PciHdr.HeaderType & ~PCI_MULTIFUNCTION;

                if (Type == 0x7f && PciHdr.BaseClass == 0xff && PciHdr.SubClass == 0xff) {
                    if (!(Flags & DUMP_ALLOW_INVALID_FUNCTION)) {
                        dprintf ("  bogus, skipping rest of device\n");
                        break;
                    }
                }

                //
                // Dump it
                //

                s[0] = PciHdr.Command & PCI_ENABLE_IO_SPACE                 ? 'i' : '.';
                s[1] = PciHdr.Command & PCI_ENABLE_MEMORY_SPACE             ? 'm' : '.';
                s[2] = PciHdr.Command & PCI_ENABLE_BUS_MASTER               ? 'b' : '.';
                s[3] = PciHdr.Command & PCI_ENABLE_VGA_COMPATIBLE_PALETTE   ? 'v' : '.';
                s[4] = PciHdr.Command & PCI_ENABLE_PARITY                   ? 'p' : '.';
                s[5] = PciHdr.Command & PCI_ENABLE_SERR                     ? 's' : '.';
                s[6] = 0;
                dprintf (".%02x  Cmd[%04x:%s]  ", PciHdr.RevisionID, PciHdr.Command, s);

                s[0] = PciHdr.Status & PCI_STATUS_CAPABILITIES_LIST        ? 'c' : '.';
                s[1] = PciHdr.Status & PCI_STATUS_66MHZ_CAPABLE            ? '6' : '.';
                s[2] = PciHdr.Status & PCI_STATUS_DATA_PARITY_DETECTED     ? 'P' : '.';
                s[3] = PciHdr.Status & PCI_STATUS_SIGNALED_TARGET_ABORT    ? 'A' : '.';
                s[4] = PciHdr.Status & PCI_STATUS_SIGNALED_SYSTEM_ERROR    ? 'S' : '.';
                s[5] = 0;
                dprintf ("Sts[%04x:%s]  ", PciHdr.Status, s);


                switch (Type) {
                    case PCI_DEVICE_TYPE:
                        dprintf ("Device");

                        if (PciHdr.u.type0.SubVendorID || PciHdr.u.type0.SubSystemID) {
                            dprintf ("  SubID:%04x:%04x",
                                PciHdr.u.type0.SubVendorID,
                                PciHdr.u.type0.SubSystemID
                                );
                        }
                        break;

                    case PCI_BRIDGE_TYPE:
                        dprintf ("PciBridge %d->%d-%d",
                            PciHdr.u.type1.PrimaryBus,
                            PciHdr.u.type1.SecondaryBus,
                            PciHdr.u.type1.SubordinateBus
                            );
                        break;

                    case PCI_CARDBUS_BRIDGE_TYPE:
                        dprintf ("CardbusBridge  %d->%d-%d",
                            PciHdr.u.type2.PrimaryBus,
                            PciHdr.u.type2.SecondaryBus,
                            PciHdr.u.type2.SubordinateBus
                            );
                        break;

                    default:
                        dprintf ("type %x", Type);
                        break;
                }

                //
                // Search for a class code match
                //
                PCHAR Desc = GetClassDesc(PciHdr.BaseClass, PciHdr.SubClass, PciHdr.ProgIf);

                if (Desc) {
                    dprintf ("  %s", Desc);
                } else {
                    dprintf ("  Class:%x:%x:%x",
                        PciHdr.BaseClass,
                        PciHdr.SubClass,
                        PciHdr.ProgIf
                        );
                }

                dprintf ("\n");
                SkipLine = FALSE;

                if (Flags & DUMP_VERBOSE) {
                    SkipLine = TRUE;
                    PciCfg1.u.bits.RegisterNumber = 0;
                    switch (Type) {
                        case PCI_DEVICE_TYPE:
                            dprintf ("      cf8:%x  IntPin:%x  IntLine:%x  Rom:%x  cis:%x  cap:%x\n",
                                PciCfg1.u.AsULONG,
                                PciHdr.u.type0.InterruptPin,
                                PciHdr.u.type0.InterruptLine,
                                PciHdr.u.type0.ROMBaseAddress,
                                PciHdr.u.type0.CIS,
                                PciHdr.u.type0.CapabilitiesPtr
                            );

                            state = 0;
                            for (i=0; i < PCI_TYPE0_ADDRESSES; i++) {
                                bar = PciHdr.u.type0.BaseAddresses[i];
                                DumpPciBar(i, 6, bar, &state);
                            }
                            DumpPciBarComplete(&state);
                            break;

                        case PCI_BRIDGE_TYPE:
                            i = PciHdr.u.type1.BridgeControl;
                            dprintf ("      cf8:%x  IntPin:%x  IntLine:%x  Rom:%x  cap:%x  2sts:%x  BCtrl:%x%s%s%s\n",
                                PciCfg1.u.AsULONG,
                                PciHdr.u.type1.InterruptPin,
                                PciHdr.u.type1.InterruptLine,
                                PciHdr.u.type1.ROMBaseAddress,
                                PciHdr.u.type1.CapabilitiesPtr,
                                PciHdr.u.type1.SecondaryStatus,
                                PciHdr.u.type1.BridgeControl,
                                i & PCI_ENABLE_BRIDGE_VGA   ? " VGA" : "",
                                i & PCI_ENABLE_BRIDGE_ISA   ? " ISA" : "",
                                i & PCI_ASSERT_BRIDGE_RESET ? " RESET" : ""
                                );

                            dprintf ("      IO:%x-%x  Mem:%x-%x  PMem:%x-%x\n",
                                PciBridgeIO2Base (PciHdr.u.type1.IOBase,  PciHdr.u.type1.IOBaseUpper16),
                                PciBridgeIO2Limit(PciHdr.u.type1.IOLimit, PciHdr.u.type1.IOLimitUpper16),
                                PciBridgeMemory2Base (PciHdr.u.type1.MemoryBase),
                                PciBridgeMemory2Limit(PciHdr.u.type1.MemoryLimit),
                                PciBridgeMemory2Base (PciHdr.u.type1.PrefetchBase),
                                PciBridgeMemory2Limit(PciHdr.u.type1.PrefetchLimit)
                                );

                            state = 0;
                            for (i=0; i < PCI_TYPE1_ADDRESSES; i++) {
                                bar = PciHdr.u.type1.BaseAddresses[i];
                                DumpPciBar(i, 6, bar, &state);
                            }
                            DumpPciBarComplete(&state);
                            break;

                        case PCI_CARDBUS_BRIDGE_TYPE:
                            i = PciHdr.u.type2.BridgeControl;
                            dprintf ("      cf8:%x  IntPin:%x  IntLine:%x  SocketRegBase:%x  cap:%x  2sts:%x  BCtrl:%x%s%s%s\n",
                                PciCfg1.u.AsULONG,
                                PciHdr.u.type2.InterruptPin,
                                PciHdr.u.type2.InterruptLine,
                                PciHdr.u.type2.SocketRegistersBaseAddress,
                                PciHdr.u.type2.CapabilitiesPtr,
                                PciHdr.u.type2.SecondaryStatus,
                                PciHdr.u.type2.BridgeControl,
                                i & PCI_ENABLE_BRIDGE_VGA   ? " VGA" : "",
                                i & PCI_ENABLE_BRIDGE_ISA   ? " ISA" : "",
                                i & PCI_ASSERT_BRIDGE_RESET ? " RESET" : ""
                                );
                            dprintf("\n");
                            state=0;
                            for (i = 0; i < (PCI_TYPE2_ADDRESSES - 1); i++) {
                                bar = PciHdr.u.type2.Range[i].Base;
                                //
                                // First 2 BARs (base+limit) are memory
                                //
                                BarIsIo =  (i > 1);
                                barno =  i;
                                if (BarIsIo) {
                                      barno -= 2;
                                }
                                DumpPciType2Bar(BarIsIo,TRUE, barno, 6, bar, &state);

                                bar = PciHdr.u.type2.Range[i].Limit;
                                DumpPciType2Bar(BarIsIo, FALSE, i, 6, bar, &state);
                            }
                            DumpPciBarComplete(&state);
                            break;
                    }
                }

                //
                // Dump CAPABILITIES if any.
                //

                if (Flags & DUMP_CAPABILITIES) {
                    if (PciHdr.Status & PCI_STATUS_CAPABILITIES_LIST) {
                        UCHAR capPtr = 0;

                        SkipLine = TRUE;

                        switch (Type) {
                            case PCI_DEVICE_TYPE:
                                capPtr = PciHdr.u.type0.CapabilitiesPtr;
                                break;
        
                            case PCI_BRIDGE_TYPE:
                                capPtr = PciHdr.u.type1.CapabilitiesPtr;
                                break;
        
                            case PCI_CARDBUS_BRIDGE_TYPE:
                                capPtr = PciHdr.u.type2.CapabilitiesPtr;
                                break;
                        }

                        if (capPtr != 0) {
                            DumpCapabilities(capPtr, &PciCfg1);
                        } else {

                            //
                            // Capabilities flag is set in Status but
                            // pointer is 0???  Something's broken.
                            //

                            dprintf("       Warning: Capability bit set in Status but capability pointer is 0.\n");
                        }
                    }
                }

                //
                // Dump hex bytes
                //

                if (Flags & DUMP_RAW_BYTES) {

                    ULONG w;

                    //
                    // Raw dump requested, if no range default to common
                    // config.
                    //

                    if (!MinAddr && !MaxAddr) {
                        MaxAddr = PCI_COMMON_HDR_LENGTH - 1;
                    }

                    //
                    // Default width to 1.  If dumping dwords, set width
                    // width to 4 and round min and max accordingly.
                    //

                    w = 1;
                    if (Flags & DUMP_RAW_DWORDS) {
                        w = 4;
                        MinAddr &= ~3;
                        MaxAddr &= ~3;
                        MaxAddr += 3;
                    }
                    Buf = ((PUCHAR) &PciHdr) + MinAddr;
                    Len = MaxAddr - MinAddr + 1;

                    if (MinAddr <= PCI_COMMON_HDR_LENGTH) {
                        if (MaxAddr > PCI_COMMON_HDR_LENGTH) {
                            ReadPci (
                                &PciCfg1,
                                PciHdr.DeviceSpecific,
                                PCI_COMMON_HDR_LENGTH,
                                MaxAddr - PCI_COMMON_HDR_LENGTH
                                );
                        }

                    } else {
                        ReadPci (&PciCfg1, Buf, MinAddr, Len);
                    }

                    HexDump (w == 4 ? 6 : 1, MinAddr, Len, w, Buf);
                    SkipLine = TRUE;

                } else if ((Flags & DUMP_INTEL) && PciHdr.VendorID == 0x8086) {

                    Buf = PciHdr.DeviceSpecific;
                    Len = sizeof (PciHdr) - PCI_COMMON_HDR_LENGTH;

                    ReadPci (&PciCfg1, Buf, PCI_COMMON_HDR_LENGTH, Len);
                    HexDump (1, PCI_COMMON_HDR_LENGTH,  Len, 1, Buf);
                    SkipLine = TRUE;
                }

                if (Flags & DUMP_CONFIGSPACE) {
                    PCI_COMMON_CONFIG  cs;
                    
                    ReadPci(&PciCfg1, (PUCHAR)&cs, 0, sizeof(cs));
                    dprintf ("Config Space:\n", 
                             PciCfg1.u.bits.BusNumber,
                             PciCfg1.u.bits.DeviceNumber,
                             PciCfg1.u.bits.FunctionNumber);
                    DumpCfgSpace(&cs);
                    dprintf ("\n");
                }

                if (SkipLine) {
                    dprintf ("\n");
                }

                //
                // If no more functions on this device, skip the rest
                // of the functions
                //

                if (Function == 0 && !(PciHdr.HeaderType & PCI_MULTIFUNCTION)) {
                    if (!(Flags & DUMP_ALLOW_INVALID_FUNCTION)) {
                        break;
                    }
                }

            }
        }
    }
}


DECLARE_API( pci )

/*++

Routine Description:

    Dumps pci type2 config data

Arguments:

    args - Supplies the address in hex.

Return Value:

    None

--*/
{
    LONG        noargs;
    ULONG       Flags;
    ULONG       MinBus, MaxBus;
    ULONG       Device, MinDevice, MaxDevice;
    ULONG       Function, MinFunction, MaxFunction;
    ULONG       MinAddr, MaxAddr;

    MinBus = 0;
    MaxBus = 0;
    MinDevice = 0;
    MaxDevice = PCI_MAX_DEVICES - 1;
    MinFunction = 0;
    MaxFunction = PCI_MAX_FUNCTION - 1;
    MinAddr = 0;
    MaxAddr = 0;
    Flags = 0;
    
   if (g_TargetQual == DEBUG_DUMP_SMALL || 
       g_TargetQual == DEBUG_DUMP_DEFAULT || 
       g_TargetQual == DEBUG_DUMP_FULL) {
        dprintf("!pci does not work for dump targets\n");
        return E_INVALIDARG;
    }
    
    {
    INIT_API();


    noargs = sscanf(args,"%lX %lX %lX %lX %lX %lX",
                    &Flags,         // 1
                    &MaxBus,        // 2
                    &Device,        // 3
                    &Function,      // 4
                    &MinAddr,       // 5
                    &MaxAddr        // 6
                    );

    MinBus = MaxBus;
    if (Flags & DUMP_TO_MAX_BUS) {
        MinBus = 0;
    }

    if (noargs >= 3) {
        Flags |= DUMP_ALLOW_INVALID_DEVICE;
        MinDevice = Device;
        MaxDevice = Device;
    }

    if (noargs >= 4) {
        MinFunction = Function;
        MaxFunction = Function;
    }

    if (MinAddr || MaxAddr) {
        Flags |= DUMP_RAW_BYTES;
    }

    if (Flags & DUMP_RAW_DWORDS) {
        Flags |= DUMP_RAW_BYTES;
    }

    pcidump (
        Flags,
        MinBus,        MaxBus,
        MinDevice,     MaxDevice,
        MinFunction,   MaxFunction,
        MinAddr,       MaxAddr
        );

    EXIT_API();
    }

    return S_OK;
}