Page 1 of 1
PCI configuration space detection
Posted: Sun Aug 12, 2012 9:54 am
by eino
Hi again. I'm trying to loop throught all pci busses, devices and functions in order to find USB devices but I'm not quite sure that my code works since no class 0x0C devices with 0x03 subclass can't be found (at least on QEMU)
Code: Select all
uint32_t read_pci ( const uint8_t bus, const uint8_t device, const uint8_t func, const uint8_t port /*, const uint8_t len */ ) {
uint32_t ret;
const uint32_t val = 0x80000000 | ( bus << 16 ) | ( device << 11 ) | ( func << 8 ) | ( port & 0xFC );
outd ( 0x0CF8, val );
extern uint32_t ind( uint32_t in_port );
/* ret = ind( 0x0CFC + ( port & 0x3) ); */
/* ret &= ( 0xFFFFFFFF >> ( ( 4 - len ) * 8 ) ); */
ret = ( ( ind ( 0xCFC ) >> ( ( port & 2 ) * 8 ) ) & 0xFFFF );
return ret;
}
/*
Scan all pci busses, devices and functions
*/
void enumerate_pci () {
uint8_t bus = 0;
uint8_t device = 0;
uint8_t func = 0;
uint32_t vendor_id;
uint32_t device_id;
uint32_t sub_class;
uint32_t class;
for ( bus = 0; bus < 255; bus++ ) {
for ( device = 0; device < 32; device++ ) {
for ( func = 0; func < 8; func++ ) {
/* vendor id at offset 0 */
vendor_id = read_pci ( bus, device, func, 0 );
/* vendor found check for device id, subclass and class */
if ( vendor_id != 0xFFFF ) {
/* vendor id at offset 2 */
device_id = read_pci ( bus, device, func, 2 );
/* subclass at offset 0x0A */
sub_class = read_pci ( bus, device, func, 0x0A );
/* class at offset 0x0B */
class = read_pci ( bus, device, func, 0x0B );
log(DEBUG, "Vend: %p did: %p sub: %p c: %p", vendor_id, device_id, sub_class, class);
}
}
}
}
}
Am I doing something completely wrong here?
Here's the output (on QEMU)
Vend: 00008086 did: 00001237 sub: 00000600 c: 00000600
Vend: 00008086 did: 00007000 sub: 00000601 c: 00000601
Vend: 00008086 did: 00007010 sub: 00000101 c: 00000101
Vend: 00008086 did: 00007113 sub: 00000680 c: 00000680
Vend: 00001013 did: 000000B8 sub: 00000300 c: 00000300
Vend: 00008086 did: 0000100E sub: 00000200 c: 00000200
Seems that the sub class and class are the same.
Re: PCI configuration space detection
Posted: Sun Aug 12, 2012 11:13 am
by JamesM
And what does your code actually output?
Re: PCI configuration space detection
Posted: Sun Aug 12, 2012 11:22 am
by eino
Ah. Sorry that was meant to be in the first post, it's now there. It's been a long day. Again.
Re: PCI configuration space detection
Posted: Sun Aug 12, 2012 11:29 am
by NickJohnson
Your read_pci() function only reads word-aligned values from the configuration space, so offset 0xA and 0xB give you the same thing.
Personally, I usually just declare a PCI configuration space structure in memory and read the entire contents at once. It helps to avoid annoying alignment calculations.
Re: PCI configuration space detection
Posted: Sun Aug 12, 2012 11:57 am
by eino
Ach! Ofcourse. I changed the code a bit:
Code: Select all
uint32_t read_pci ( const uint8_t bus, const uint8_t device, const uint8_t func, const uint8_t port, const uint8_t len ) {
uint32_t ret;
const uint32_t val = 0x80000000 | ( bus << 16 ) | ( device << 11 ) | ( func << 8 ) | ( port & 0xFC );
outd ( 0x0CF8, val );
extern uint32_t ind( uint32_t in_port );
ret = ind( 0x0CFC + ( port & 0x3) );
ret &= ( 0xFFFFFFFF >> ( ( 4 - len ) * 8 ) );
/* ret = ( ( ind ( 0xCFC ) >> ( ( port & 2 ) * 8 ) ) & 0xFFFF ); */
return ret;
}
/*
Scan all pci busses, devices and functions
*/
void enumerate_pci () {
uint8_t bus = 0;
uint8_t device = 0;
uint8_t func = 0;
uint32_t vendor_id;
uint32_t device_id;
uint32_t sub_class;
uint32_t class;
for ( bus = 0; bus < 255; bus++ ) {
for ( device = 0; device < 32; device++ ) {
for ( func = 0; func < 8; func++ ) {
/* vendor id at offset 0 */
vendor_id = read_pci ( bus, device, func, 0, 2 );
/* vendor found check for device id, subclass and class */
if ( vendor_id != 0xFFFF ) {
/* vendor id at offset 2 */
device_id = read_pci ( bus, device, func, 2, 2 );
/* subclass at offset 0x0A */
sub_class = read_pci ( bus, device, func, 0x0A, 1 );
/* class at offset 0x0B */
class = read_pci ( bus, device, func, 0x0B, 1 );
log(DEBUG, "B:%d V: %p D: %p SC: %p C: %p", bus, vendor_id, device_id, sub_class, class);
}
}
}
}
}
The output is now
Vend: 00008086 did: 00001237 sub: 00000000 c: 00000006
Vend: 00008086 did: 00007000 sub: 00000001 c: 00000000
Vend: 00008086 did: 00007010 sub: 00000001 c: 00000001
Vend: 00008086 did: 00007113 sub: 00000080 c: 00000006
Vend: 00001013 did: 000000B8 sub: 00000000 c: 00000003
Vend: 00008086 did: 0000100E sub: 00000000 c: 00000002
So there is three class 6 devices which are which are bridge devices. A host bridge, isa bridge and other bridge device. Sounds reasonable? Then there is a vga compatible device and ethernet controller all from Intel. And a vga compatible controller by Cirrus logic...
So... I guess there are more devices to be found beyond those bridge devices... This is something I haven't read about yet. Any links or a direct suggestions? Or maybe some pseudo code?
Btw Nick, I'm writing this on top of Pinion.
Re: PCI configuration space detection
Posted: Sun Aug 12, 2012 2:20 pm
by JamesM
You're missing multi-function devices.
The bridge devices are multi-function (I forget what indicates that - google it
) - each function is a different base device.
That's where your missing devices are.
Re: PCI configuration space detection
Posted: Sun Aug 12, 2012 2:47 pm
by eino
Well okay I tuned up the code a bit. Made a couple of structures and functions that handle them. Thou I will need to define more in detail what they will be, but they will suffice for now.
I tried this on real hardware and I think I'm getting results I want. For example there were three class 0x0C subclass 0x03 devices that I tracked down to be:
0x28308086 (ICH8 Family) USB UHCI Controller #1
0x28318086 (ICH8 Family) USB UHCI Controller #2
0x28328086 (ICH8 Family) USB UHCI Controller #3
The complete list was quite a bit longer on real hardware than on QEMU.
JamesM: I'm actually doing the function loop from 0 to 3 on each device... Or have I understood something wrong about multifunctions maybe...
Anyways here's the code... Maybe you can have one more look on it? =) (anyways hopefully this will be usefull for someone else too)
Code: Select all
#define MAX_PCI_DEVICES 100 /* 100 PCI devices should be enough? */
/*
Structure with all variables needed for a pci address
*/
struct pci_address {
uint8_t enable_reserved; /* 10000000b - 0x80h */
uint8_t bus_number;
uint8_t device_and_function; /* & with 0xF8 to get device and & with 0x7 to get the function */
uint8_t register_number; /* and with 0xFC to get the register number */
};
/*
Structure to hold a complete pci device information in 16 different 32 bit registers
*/
struct pci_device_registers {
uint32_t reg[16];
};
/*
List that holds all pci devices raw register data
*/
struct pci_device_registers pci_devices[MAX_PCI_DEVICES];
/*
Structure that holds all variables needed to generate an pci port address (on register 0 (vendor/device))
*/
struct pci_address create_pci_address ( uint8_t bus, uint8_t device, uint8_t func ) {
struct pci_address address;
address.enable_reserved = 0x80;
address.bus_number = bus;
address.device_and_function = ( device << 3 ) | func;
address.register_number = 0;
return address;
}
/*
Gets an 32 bit pci address with specified register (0 = vendor/device, 08 = class, sub, prog if, revision id, ...)
*/
uint32_t get_pci_address_for_register ( struct pci_address address, uint8_t reg ) {
uint32_t addr = ( address.enable_reserved << 24 ) |
( address.bus_number << 16 ) |
( address.device_and_function << 8 ) |
( reg & 0xFC );
return addr;
}
/*
Reads an 32 bit addres/register from the hardware
*/
uint32_t read_pci_register ( uint32_t address ) {
outd ( 0x0CF8, address );
extern uint32_t ind( uint32_t in_port );
uint32_t result = ind ( 0xCFC );
return result;
}
/*
Scan all pci busses, devices and functions
*/
void enumerate_pci () {
struct pci_address address;
uint8_t bus = 0;
uint8_t device = 0;
uint8_t func = 0;
uint8_t reg = 0;
uint32_t vendor_and_device;
int counter = 0;
short i = 0;
for ( bus = 0; bus < 255; bus++ ) {
for ( device = 0; device < 32; device++ ) {
for ( func = 0; func < 3; func++ ) {
address = create_pci_address ( bus, device, func );
vendor_and_device = read_pci_register ( get_pci_address_for_register ( address, reg ) );
if ( vendor_and_device != 0xFFFFFFFF ) {
for ( i = 0; i < 16; i++ ) {
if ( i == 0 ) {
pci_devices[counter].reg[i] = vendor_and_device;
}
else {
pci_devices[counter].reg[i] = read_pci_register ( get_pci_address_for_register ( address, ( i * 4 ) ) );
}
}
log(DEBUG, "V & D: %p S & C: %p", pci_devices[counter].reg[0], ( pci_devices[counter].reg[2] >> 16 ) );
counter++;
}
}
}
}
}
Re: PCI configuration space detection
Posted: Sun Aug 12, 2012 2:54 pm
by JamesM
Ah bugger, sorry I misread your code.
In which case, what's the problem. Qemu won't put any USB devices on the bus unless you've added one with a command line parameter.
Re: PCI configuration space detection
Posted: Sun Aug 12, 2012 3:23 pm
by eino
Ah. Should be more careful and add the qemu options to the right place... Anyways
did it.
Thanks!