/*---------------------------------------------------------------------------- PCI demo for DJGPP, Turbo C, or Watcom C, using I/O ports (configuration mechanism 1) Chris Giese http://SisAndHappy.com/ChrisGiese/ I, the copyright holder of this work, hereby release it into the public domain. This applies worldwide. If this is not legally possible: I grant any entity the right to use this work for any purpose, without any conditions, unless such conditions are required by law. 4 Mar 2016: - pci_iterate() cleaned up. The comment about an error in the PCI spec was wrong; it was a misundestanding on my part. - display_device_class() updated with more device classes/subclasses, and it now displays the numeric values for class & subclass - Demo code in main() now displays the value in BAR4 16 Jan 2015: - (reg & ~3) changed to (reg & 0xFC) to mask off high 8 bits - fixed FN() macro; was & 0x03, is now 0x07 - added display of IRQ, BAR0, and revision 22 Mar 2012: - replaced pci_t struct with 16-bit 'bdf' value - removed "_config" from function names to shorten them - pci_read_(blah) now returns value read instead of useless error code - pci_write_(blah) no longer returns useless error code 27 Augt 2011: - changed device validation code - stylistic changes to pci_iterate() 23 Jul 2009: modified to display device programming interface (PI) 25 Apr 2007: initial release ----------------------------------------------------------------------------*/ #include #include #if defined(__TURBOC__) #include /* inport[b](), outport[b]() */ #define inportw(P) inport(P) #define outportw(P,V) outport(P,V) /* [in|out]portl() defined below */ #elif defined(__WATCOMC__) #include #define inportb(P) inp(P) #define inportw(P) inpw(P) #define outportb(P,V) outp(P,V) #define outportw(P,V) outpw(P,V) #if defined(__386__) #define inportl(P) inpd(P) #define outportl(P,V) outpd(P,V) /* else [in|out]portl() defined below */ #endif #elif defined(__DJGPP__) #include /* inport[b|w|l](), outport[b|w|l]() */ #else #error Sorry, unsupported compiler #endif #define PCI_ADDR_REG 0xCF8 #define PCI_DATA_REG 0xCFC /***************************************************************************** These functions are _code_, but are stored in the _data_ segment. Declare them 'far' and end them with 'retf' instead of 'ret'. For 16-bit Watcom C, use 'cdecl' to force usage of normal, stack calling convention instead of Watcom register calling convention. *****************************************************************************/ #if defined(__TURBOC__) || (defined(__WATCOMC__)&&!defined(__386__)) static const unsigned char g_inportl[] = { /* BITS 16 */ 0x55, /* push bp */ 0x8B, 0xEC, /* mov bp,sp */ 0x8B, 0x56, 0x06, /* mov dx,[bp + 6] */ 0x66, 0xED, /* in eax,dx */ 0x8B, 0xD0, /* mov dx,ax */ 0x66, 0xC1, 0xE8, 0x10, /* shr eax,16 */ 0x92, /* xchg dx,ax */ 0x5D, /* pop bp */ 0xCB /* retf */ }; static unsigned long far cdecl (*inportl)(unsigned port) = (unsigned long far (*)(unsigned))g_inportl; /****************************************************************************/ static const unsigned char g_outportl[] = { /* BITS 16 */ 0x55, /* push bp */ 0x8B, 0xEC, /* mov bp,sp */ 0x8B, 0x56, 0x06, /* mov dx,[bp + 6] */ 0x66, 0x8B, 0x46, 0x08, /* mov eax,[bp + 8] */ 0x66, 0xEF, /* out dx,eax */ 0x5D, /* pop bp */ 0xCB /* retf */ }; static void far cdecl (*outportl)(unsigned port, unsigned long val) = (void far (*)(unsigned, unsigned long))g_outportl; #endif /****************************************************************************/ static int pci_detect(void) { printf("PCI controller..."); /* poke 32-bit I/O register at 0xCF8 to see if there's a PCI controller there */ outportl(PCI_ADDR_REG, 0x80000000L); if(inportl(PCI_ADDR_REG) != 0x80000000L) { printf("not found\n"); return -1; } printf("found\n"); return 0; } /***************************************************************************** 'bdf' = bus (b15-b8), device (b7-b3), and function (b2-b0) Bitfields for PCI configuration address port: [I/O port 0xCF8] Bit(s) Description (Table P0944) 1-0 reserved (00) 7-2 configuration register number (see #00878) 10-8 function 15-11 device number 23-16 bus number 30-24 reserved (0) 31 enable configuration space mapping *****************************************************************************/ static unsigned pci_read_byte(unsigned bdf, unsigned reg) { /* Turbo C shift bug */ #if 0 outportl(PCI_ADDR_REG, 0x80000000L /* "enable configuration space mapping" */ | ((unsigned long)bdf << 8) | (reg & ~3)); #else unsigned long i = bdf; i <<= 8; i |= 0x80000000L; /* "enable configuration space mapping" */ i |= (reg & 0xFC); outportl(PCI_ADDR_REG, i); #endif return inportb(PCI_DATA_REG + (reg & 3)); } /***************************************************************************** b0 of 'reg' is ignored -- must read word from even-numbered register *****************************************************************************/ static unsigned pci_read_word(unsigned bdf, unsigned reg) { /* outportl(PCI_ADDR_REG, 0x80000000L | ((unsigned long)bdf << 8) | (reg & ~3)); */ unsigned long i = bdf; i <<= 8; i |= 0x80000000L; i |= (reg & 0xFC); outportl(PCI_ADDR_REG, i); return inportw(PCI_DATA_REG + (reg & 2)); } /***************************************************************************** b1 and b0 of 'reg' are ignored -- must read dword from register modulo 4 *****************************************************************************/ static unsigned long pci_read_dword(unsigned bdf, unsigned reg) { /* outportl(PCI_ADDR_REG, 0x80000000L | ((unsigned long)bdf << 8) | (reg & ~3)); */ unsigned long i = bdf; i <<= 8; i |= 0x80000000L; i |= (reg & 0xFC); outportl(PCI_ADDR_REG, i); return inportl(PCI_DATA_REG); } /****************************************************************************/ static void pci_write_byte(unsigned bdf, unsigned reg, unsigned val) { /* outportl(PCI_ADDR_REG, 0x80000000L | ((unsigned long)bdf << 8) | (reg & ~3)); */ unsigned long i = bdf; i <<= 8; i |= 0x80000000L; i |= (reg & 0xFC); outportl(PCI_ADDR_REG, i); outportb(PCI_DATA_REG + (reg & 3), val); } /***************************************************************************** note that b0 of 'reg' is ignored *****************************************************************************/ static void pci_write_word(unsigned bdf, unsigned reg, unsigned val) { /* outportl(PCI_ADDR_REG, 0x80000000L | ((unsigned long)bdf << 8) | (reg & ~3)); */ unsigned long i = bdf; i <<= 8; i |= 0x80000000L; i |= (reg & 0xFC); outportl(PCI_ADDR_REG, i); outportw(PCI_DATA_REG + (reg & 2), val); } /***************************************************************************** note that b0 and b1 of 'reg' are ignored *****************************************************************************/ static void pci_write_dword(unsigned bdf, unsigned reg, unsigned long val) { /* outportl(PCI_ADDR_REG, 0x80000000L | ((unsigned long)bdf << 8) | (reg & ~3)); */ unsigned long i = bdf; i <<= 8; i |= 0x80000000L; i |= (reg & 0xFC); outportl(PCI_ADDR_REG, i); outportl(PCI_DATA_REG, val); } /***************************************************************************** 'bdf' = bus (b15-b8), device (b7-b3), and function (b2-b0) "Brute-force" iteration. Can check if device is a PCI-PCI bridge (class 6, subclass 4 or 9), read secondary bus number from byte at location 0x19 in config space, and enumerate _that_ bus; instead of merely incrementing through all possible bus numbers. *****************************************************************************/ #define FN(BDF) (((BDF) >> 0) & 0x07) #define DEV(BDF) (((BDF) >> 3) & 0x1F) #define BUS(BDF) (((BDF) >> 8) & 0xFF) int pci_iterate(unsigned *bdf_ptr) { unsigned char hdr_type, multi_fn = 0; unsigned bdf = *bdf_ptr, next; if(FN(bdf) == 0) { /* 0x0E=PCI_HEADER_TYPE (byte) */ hdr_type = pci_read_byte(bdf, 0x0E); multi_fn = ((hdr_type & 0x80) == 0); } next = (bdf + (multi_fn ? 8 : 1)) & 0xFFFF; if(next == 0) return 1; /* done */ *bdf_ptr = next; return 0; } /****************************************************************************/ /* number of elements in static array: */ #define COUNT(N) (sizeof(N) / sizeof((N)[0])) void display_device_class(unsigned c, unsigned sc) { static const char *c_name[] = { /* 0 */ "?", "disk ctrl", "network ctrl", "display ctrl", /* 4 */ "multimedia ctrl", "memory", "bridge", "communications device", /* 8 */ "system device", "HID", "dock", "CPU", /* 12 */ "serial bus ctrl", "wireless ctrl", "intelligent I/O", "satellite equip", /* 16 */ "en-/decryption", "data acq & DSP" }; static const char *sc_name1[] = { "SCSI", "IDE", "floppy", "IPI", "RAID", "ATA", "SATA", "SA-SCSI" }; static const char *sc_name2[] = { "Ethernet", "Token Ring", "FDDI", "ATM", "ISDN", "WorldFip", "PICMG" }; static const char *sc_name3[] = { "VGA", "XGA", "3D" }; static const char *sc_name4[] = { "video", "audio", "telephony" }; static const char *sc_name5[] = { "RAM", "Flash" }; static const char *sc_name6[] = { "CPU", "ISA", "EISA", "MicroChannel", "PCI", "PCMCIA", "NuBus", "CardBus", "RACEway", "PCI", "InfiniBand" }; static const char *sc_name7[] = { "serial", "parallel", "multiport serial", "modem", "GPIB", "SmartCard" }; static const char *sc_name8[] = { "interrupt ctrl", "DMA ctrl", "timer", "RTC", "hotplug ctrl" }; static const char *sc_name9[] = { "keyboard", "digitizer/pen", "mouse", "scanner", "gameport" }; static const char *sc_name10[] = { "generic" }; static const char *sc_name11[] = { "386", "486", "Pentium"//, "Pentium Pro (P6)" /* 0x10=DEC Alpha, 0x20=PowerPC, 0x30=MIPS, 0x40=coprocessor */ }; static const char *sc_name12[] = { "Firewire (IEEE 1394)", "ACCESS.bus", "SSA", "USB", "Fibre Channel", "InfiniBand", "IPMI", "SERCOS", "CANbus" }; static const char *sc_name13[] = { /* 0 */ "IRDA ctrl", "IR remote", "?", "?", "?", "?", "?", "?", /* 8 */ "?", "?", "?", "?", "?", "?", "?", "?", /* 16 */ "RF", "Bluetooth", "broadband", "?", "?", "?", "?", "?", /* 24 */ "?", "?", "?", "?", "?", "?", "?", "?", /* 32 */ "WiFi (802.11a)", "WiFi (802.11b)" }; static const char *sc_name14[] = { "I2O" }; static const char *sc_name15[] = { "?", "TV", "audio", "voice", "data" }; static const char *sc_name16[] = { "network", "entertainment" }; static const char *sc_name17[] = { "DPIO", "perf counters" /* ...other stuff here */ }; /**/ printf("%2u:%-3u = %s:", c, sc, (c < COUNT(c_name)) ? c_name[c] : "?"); switch(c) { case 1: printf("%s", (sc < COUNT(sc_name1)) ? sc_name1[sc] : "?"); break; case 2: printf("%s", (sc < COUNT(sc_name2)) ? sc_name2[sc] : "?"); break; case 3: printf("%s", (sc < COUNT(sc_name3)) ? sc_name3[sc] : "?"); break; case 4: printf("%s", (sc < COUNT(sc_name4)) ? sc_name4[sc] : "?"); break; case 5: printf("%s", (sc < COUNT(sc_name5)) ? sc_name5[sc] : "?"); break; case 6: printf("%s", (sc < COUNT(sc_name6)) ? sc_name6[sc] : "?"); break; case 7: printf("%s", (sc < COUNT(sc_name7)) ? sc_name7[sc] : "?"); break; case 8: printf("%s", (sc < COUNT(sc_name8)) ? sc_name8[sc] : "?"); break; case 9: printf("%s", (sc < COUNT(sc_name9)) ? sc_name9[sc] : "?"); break; case 10: printf("%s", (sc < COUNT(sc_name10)) ? sc_name10[sc] : "?"); break; case 11: printf("%s", (sc < COUNT(sc_name11)) ? sc_name11[sc] : "?"); break; case 12: printf("%s", (sc < COUNT(sc_name12)) ? sc_name12[sc] : "?"); break; case 13: printf("%s", (sc < COUNT(sc_name13)) ? sc_name13[sc] : "?"); break; case 14: printf("%s", (sc < COUNT(sc_name14)) ? sc_name14[sc] : "?"); break; case 15: printf("%s", (sc < COUNT(sc_name15)) ? sc_name15[sc] : "?"); break; case 16: printf("%s", (sc < COUNT(sc_name16)) ? sc_name16[sc] : "?"); break; case 17: printf("%s", (sc < COUNT(sc_name17)) ? sc_name17[sc] : "?"); break; default: printf("?"); break; } } /****************************************************************************/ int main(void) { unsigned bdf, vendor, device, _class, subclass, pi;//, rev; unsigned long i; /* check for PCI */ if(pci_detect()) return 1; printf( " Vend Dev\n" "Bus Dev Fn ID x ID x IRQ BAR0 x BAR4 x PIx Class:Subclass\n" "--- --- -- ---- ---- --- -------- -------- --- ----------------------\n"); bdf = 0; do { /* 0x00=PCI_VENDOR_ID (word), 0x02=PCI_DEVICE_ID (word) */ i = pci_read_dword(bdf, 0x00); vendor = (unsigned)(i & 0xFFFF); device = (unsigned)(i >> 16); /* anything there? if(i != 0xFFFFFFFFL) if(vendor != 0 && vendor != 0xFFFF) "Since 0FFFFh is an invalid Vendor ID, it is adequate for the host bus to PCI bridge to return a value of all 1's on read accesses to Configratuion Space registers of non-existent devices." */ if(vendor != 0xFFFF) { printf("%3u %3u %2u %4X %4X ", BUS(bdf), DEV(bdf), FN(bdf), vendor, device); /* 0x3C=INTERRUPT, 0x10=PCI_BASE_ADDR0 (BAR0), 0x20=PCI_BASE_ADDR4 (BAR4) */ printf("%3u %08lX %08lX ", pci_read_byte(bdf, 0x3C), pci_read_dword(bdf, 0x10), pci_read_dword(bdf, 0x20)); /* 0x08=PCI_REVISION_ID, 0x09=PCI_PROGRAMMING_INTERFACE, 0x0A=PCI_SUBCLASS, 0x0B=PCI_CLASS */ i = pci_read_dword(bdf, 0x08); _class = (unsigned)((i >> 24) & 0xFF); subclass = (unsigned)((i >> 16) & 0xFF); pi = (unsigned)((i >> 8) & 0xFF); /* rev = (unsigned)((i >> 0) & 0xFF); */ printf(" %02X ", pi); display_device_class(_class, subclass); printf("\n"); } } while(!pci_iterate(&bdf)); return 0; }