Reading PCI configuration space

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
computafreak
Member
Member
Posts: 76
Joined: Sun Dec 14, 2008 1:53 pm

Reading PCI configuration space

Post by computafreak »

I've been working on PCI enumeration, and so far it's working quite well. I can read 16 and 32 bit integers very well, and I've gotten copies of most of the free PCI specifications. However, according to wiki's PCI entry, there are a collection of 8-bit fields starting at offset 0x8. I'm not certain how I would go about reading these. I know that I could read an integer from the configuration space and split it, but I'm not certain how. Besides that, this sounds like a slightly repetitive method. What I would really like is a method which reads a char from an offset. I've been trying to understand the logic behind my readInteger and readShort methods, and I think I understand them

My problem is that I can't read a byte (an unsigned char) at a given point. My current code looks like this:

Code: Select all

uchar PCI::readChar(ushort bus, ushort device, ushort function, ushort offset)
{
       outportLong(0xCF8, (ulong)((bus << 16) |
		(device << 11) |
		(function << 8) |
		(offset & ~3) |
		0x80000000));
	return (uchar)(inportLong(0xCFC) >> ((offset & 2) * 8) & 0xFF);
}

//class contains the wrong value, see below
uchar class = readChar(bus, device, function, 11);
The problem is that when I try to read the class (bits 24:31, register 0x8) I get 0x0 instead of 0x600, 0x601 and 0x680, and 0x80 instead of 0x101. I'm testing this on Bochs, version 2.4
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Reading PCI configuration space

Post by Combuster »

Code: Select all

(offset & 2)
or

Code: Select all

(offset & 3)
:wink:
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
-m32
Member
Member
Posts: 120
Joined: Thu Feb 21, 2008 5:59 am
Location: Ottawa, Canada

Re: Reading PCI configuration space

Post by -m32 »

Code: Select all


readDWord(bus, device, function,  0x08) & 0xff            // for byte 0 (offset 0x08)
(readDWord(bus, device, function, 0x08) >> 8) & 0xff   // for byte 1 (offset 0x09)
(readDWord(bus, device, function, 0x08) >> 16) & 0xff // for byte 2 (offset 0x0A)
(readDWord(bus, device, function, 0x08) >> 24) & 0xff //for byte 3  (offset 0x0B)

computafreak
Member
Member
Posts: 76
Joined: Sun Dec 14, 2008 1:53 pm

Re: Reading PCI configuration space

Post by computafreak »

I think that I'm missing something really obvious. My outportLong code clearly isn't where the problem resides - it's standardised across multiple functions, each returning a different size. I've tried multiple solutions to this, and none of them worked. This is the relevant line at the moment

Code: Select all

return (uchar)((inportLong(0xCFC) >> ((offset & 3) * 8)) & 0xFF);
However, this returns 0x6, 0x6, 0x1, 0x6 for the class codes 0x600, 0x601, 0x101, 0x680 respectively. I noticed that there isn't usually a fixed number of bytes in each header structure (particularly the PCI-to-PCI bridge), so added a byteIndex parameter, which resulted in the following code

Code: Select all

return (uchar)((inportLong(0xCFC) >> (byteIndex * 8)) & 0xFF);
This has the same result. It looks to me like the last two numbers are getting removed. I know that it isn't the AND, but it can't be the right binary shift either. It's almost as though the port isn't returning the right value. This would normally mean that I'd written the wrong value to 0xCF8, but it's the same across every function

Just for context, I call the new function like this

Code: Select all

uchar class = readChar(bus, function, device, 8, 3);
8 is the register, 3 is the byte offset
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Reading PCI configuration space

Post by Combuster »

However, this returns 0x6, 0x6, 0x1, 0x6 for the class codes 0x600, 0x601, 0x101, 0x680 respectively.
What did you expect? you are reading 8 bits of a field that's 24(!) bits in size. You'd need to read the bytes at 0xB, 0xA, 0x9 to get the class:subclass:interface fields (in that order).

I think you tripped over byte ordering somewhere since the output looks fine...
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
kop99
Member
Member
Posts: 120
Joined: Fri May 15, 2009 2:58 am

Re: Reading PCI configuration space

Post by kop99 »

computafreak, here is the windows pci driver's source code.
You could reference from that code.

01000101: Code removed pending discussions
Last edited by kop99 on Wed Jun 17, 2009 2:39 am, edited 1 time in total.
computafreak
Member
Member
Posts: 76
Joined: Sun Dec 14, 2008 1:53 pm

Re: Reading PCI configuration space

Post by computafreak »

Thanks for the replies; I definitely need to brush up on my byte ordering! There's a fair amount of food for thought here, and I've got a lot to think about. Once again, thanks
computafreak
Member
Member
Posts: 76
Joined: Sun Dec 14, 2008 1:53 pm

Re: Reading PCI configuration space

Post by computafreak »

I've read the configuration space, but a few fields are annoying me slightly. They're related to CardBus and its ilk. At offset 0x28 for a normal PCI device's configuration space, there is a CardBus CIS pointer. Additionally, when the Header Type is 2, the device is a CardBus bridge. However, both of these structures are defined in the PC Card Specification, which is expensive. Is there any way I can get the definitions for these structures?

Additionally, something else is troubling me slightly. At the moment, I iterate through each of the 256 buses, then going through every device and function. However, according to this post by Combuster, there is an element of recursion involved. Is it simply a matter of doing the same as what I'm doing now, taking the bus to be 0 initially, then looking for the PCI bridges and calling oneself again, passing a bus number? Effectively, by going in a rather ad-hoc method, am I missing some devices? Also, which bus field do I use? Is it SubordinateBus, SecondaryBus, PrimaryBus or all of them?

Finally, writing to the address space. Do I repeat the first line of code which selects the bus, device and function, then doing an outLong(0xCFC, value), or is it something more complex?
Post Reply