Page 1 of 1

PCI ?

Posted: Sun May 09, 2010 3:24 pm
by Sam111
Ok , I am trying to understand how the PCI bus works.
the os dev wiki is really helpful with this but I have just a few questions that I am confused about.

Question 1)
From my understanding PCI works like this
their is the configuration address register (at 0xCF8 ) and the configuration data register
(at 0xCFC)

the configuration address register is 32 bits long and by writing to it a specific value based on the spec's on the wiki
follows this format

Code: Select all

Enable Bit 	 Reserved 	 Bus Number 	 Device Number 	 Function Number 	 Register Number 	 00 
After you write to the 0xCF8 address register with the info bus , device ,...etc info that you want to obtain from
Then you read from the data register at 0xCFC to get the info for that Bus , Device ,...etc that you wrote to 0xCF8

If their is no device their or anything then 0xCFC will return 0xFFFF.
But after those 2 examples on the osdev wiki they go to list a few configuration tables.
I have read that the tables should be 64 entries of 32bits

I know the 0xCF8 address register is 32bit's long but is the 0xCFC data register 256 bytes long?

Because I am having a hard time knowing how to access the fields in the 256 configuration space tables?


Is it just that the configuration whole 256byte table is return starting at port 0xCFC
And ending at DFC ( so to access any register or data values in the table all you would have to do is start at 0xCFC and read from an offset from that data register?)

If that is all it is then that would be easy.


For example to see if I got this correct if I want to read the values for the 3rd 32bit entry in the configuration table for a particular device
i.e
Class code Subclass Prog IF Revision ID
I would read a 32bit value starting at address 0xCFC + 0x08
tmp = SystemINLONG( 0xCFC + 0x08 ) ;

Thanks for any help

Re: PCI ?

Posted: Sun May 09, 2010 5:00 pm
by bewing
No, you just read the 0xcfc io port 256 times (reading 32bit dwords), after selecting 256 different register numbers via the 0xcf8 port. You have to select each configuration space "register" individually.

Re: PCI ?

Posted: Sun May 09, 2010 10:02 pm
by lemonyii
did you noticed the REGISTER field?(in my sample is OFFSET)
say, if you want to read the Memory Base Address 0 (0x1c) field of the header into val,do it like this:

Code: Select all

val = read_pci(bus,dev,func,0x1c);

static u64 read_pci(u64 bus, u64 dev, u64 func, u64 offset){
	register u64 idx;
	idx = 0x80000000 + ((bus&0xFF)<<16) + ((dev&0x1F)<<11) + ((func&0x7)<<8) + (offset&0xFC);
	outdword(0xCF8, idx);
	return indword(0xCFC);
}
and notice: you could only read data from 0xcfc,a 32bits register.
say, you can read only 32bits at a time.

cheers,
lemonyii

Re: PCI ?

Posted: Sun May 09, 2010 11:08 pm
by Sam111
Sorry if I am alittle confused.

But
No, you just read the 0xcfc io port 256 times (reading 32bit dwords), after selecting 256 different register numbers via the 0xcf8 port. You have to select each configuration space "register" individually.
So if for example from osdev wiki they have a configuration space table
like this

Code: Select all

register 	 bits 31-24 	 bits 23-16 	 bits 15-8 	 bits 7-0
00 	Device ID 	Vendor ID
04 	Status 	Command
08 	Class code 	Subclass 	Prog IF 	Revision ID
0C 	BIST 	Header type 	Latency Timer 	Cache Line Size
10 	Base address #0 (BAR0)
14 	Base address #1 (BAR1)
18 	Base address #2 (BAR2)
1C 	Base address #3 (BAR3)
20 	Base address #4 (BAR4)
24 	Base address #5 (BAR5)
28 	Cardbus CIS Pointer
2C 	Subsystem ID 	Subsystem Vendor ID
30 	Expansion ROM base address
34 	Reserved 	Capabilities Pointer
38 	Reserved
3C 	Max latency 	Min Grant 	Interrupt PIN 	Interrupt Line 
Does that mean calling indword(0xCFC); for the first time brings back
Device ID Vendor ID
and if so another indword(0xCFC) read after that will bring back the next 32bit register
04 Status Command

...and so on

so too get the complete table I would have to read from 0xCFC 64 times in a row?
If that is true then the functions on the wiki
i.e

Code: Select all

 unsigned short pciConfigReadWord (unsigned short bus, unsigned short slot,
                                   unsigned short func, unsigned short offset)
 {
    unsigned long address;
    unsigned long lbus = (unsigned long)bus;
    unsigned long lslot = (unsigned long)slot;
    unsigned long lfunc = (unsigned long)func;
    unsigned short tmp = 0;
 
    /* create configuration address as per Figure 1 */
    address = (unsigned long)((lbus << 16) | (lslot << 11) |
              (lfunc << 8) | (offset & 0xfc) | ((UINT32)0x80000000));
 
    /* write out the address */
    sysOutLong (0xCF8, address);
    /* read in the data */
    tmp = (unsigned short)((sysInLong (0xCFC) >> ((offset & 2) * 8)) & 0xffff);
    return (tmp);
 }
only ever returns halve of the first register 00 Device ID Vendor ID
If I am correct with my understanding
then to read the second 32bit register by modifying this function to read the second register line 04 Status Command

All I would have to do is skip the first read.

like this

Code: Select all

unsigned long pciConfigReadDWord (unsigned short bus, unsigned short slot,
                                   unsigned short func, unsigned short offset)
 {
    unsigned long address;
    unsigned long lbus = (unsigned long)bus;
    unsigned long lslot = (unsigned long)slot;
    unsigned long lfunc = (unsigned long)func;
    unsigned long tmp = 0;
 
    /* create configuration address as per Figure 1 */
    address = (unsigned long)((lbus << 16) | (lslot << 11) |
              (lfunc << 8) | (offset & 0xfc) | ((UINT32)0x80000000));
 
    /* write out the address */
    sysOutLong (0xCF8, address);
    /* read in the data */
    sysInLong (0xCFC) ; // skip the first 32 bit register
    tmp = sysInLong (0xCFC) ;   
    return (tmp);
 }
Thanks


Add *****
I thought about it and maybe you meant you do it this way
from this header static u64 read_pci(u64 bus, u64 dev, u64 func, u64 offset)
if you have a device on bus= 0, dev=1, func=2 the 256 byte configuration space table entries would be access by the offset
if offset = 0x00 your going to read the first register in the configuration space
which is 00 Device ID Vendor ID
if offset = 0x01
you would read the second 32 bit register in the configuration space
which is 04 Status Command
and so on
offset = 64 would read the last 32bit register in the table.

Let me know if this is the way it works.
If so to probe all the PCI bus completely for devices and function you would run thur a triple for loop
for( int i = 0 ; i < bus_size ; i++)
for( int j = 0 ; j < dev_size ; j++)
for( int k = 0 ; k < func_size ; k++)
{
if( isvaildPCIBUSDEVICE( i , j , k) != 0xFFFF )
{
//read the configuration table with the offset incremented 0 , 1 , 2,...,64
//to get the whole table into an array ....etc
}


}

return 0 ;

Re: PCI ?

Posted: Mon May 10, 2010 12:44 am
by lemonyii
ohh...
the offset shoul be aligned to 4bytes,say,multi of 4.
the first (vendor/device id) is 0x00
the 2nd (class,subclass,progid,verson) is 0x04
...
if you want to read the register IRQ, just:
outdword(0xcf8,0x3c)
val = indword(0xcfc)&0xff;
you don't have to read starting with vendor id,and you can never reach the class/subclass field no matter how many times you read 0xcfc,if you don't set 0xcf8 to 0xNNNNNN4.
do you understand now?

Re: PCI ?

Posted: Mon May 10, 2010 12:03 pm
by Sam111
So are you saying if I do
outdword(0xcf8,address)
return indword(0xcfc)&0xffff

where
address =

Code: Select all

Enable Bit     Reserved     Bus Number     Device Number     Function Number     Register Number     00 
And by incrementing the register number in the address by 4bytes you get the next 32bit register in the configuration table.

so reister number = 0x00 brings back

Code: Select all

00    Device ID    Vendor ID
register number = 0x04

Code: Select all

04    Status    Command
register number = 0x08

Code: Select all

08    Class code    Subclass    Prog IF    Revision ID
...and so on.

If that is it then I had the correct idea in the last paragraph I wrote in last post I just didn't use proper 4byte alignment (i.e incrementing it by 4 to get the next register in the configuration space)

As for your example

Code: Select all

outdword(0xcf8,0x3c)
val = indword(0xcfc)&0xff;
You are assumeing bus =0 , dev=0 ,func=0 , register = 0x3C and if that device exists
you will receive the 32bit register at 0x3C in the table and then you take the lower part of the 32bit address which is for the 16 bit IRQ line register and store it in val.

But don't you at least have to have the Enable Bit = 1 so 0x3C for the address should be
really 0x80000000 | 0x3C ????

Let me know if I got it correct now

So for probing the complete PCI you would just have nested 3 for loop running thru all the possible bus states 0 - 255
all the possible devices states 0 - 31
all the possible functions states 0-7
keeping the register number 0x00
Once found that 0xCFC doesn't return 0xFFFF that means this is a vailid PCI configuration space table entry and then you can obtain the whole table by incrementing the register value by 4bytes all the way up to
0x3C to obtain the complete table.... Or just specify a specific register value to get to the appropriate register you want in your example 0x3C for the IRQ.
Then just repeat the process for the next non 0xFFFF entry.

This will give you all the info that the PCI has in all it's existing configuration space tables.

Re: PCI ?

Posted: Mon May 10, 2010 11:39 pm
by Sam111
do I got it correct now?

Re: PCI ?

Posted: Tue May 11, 2010 5:32 am
by lemonyii
yes!
what you stated is nothing wrong now.at least i didn't find any.
and if you like, see my previous post at
http://forum.osdev.org/viewtopic.php?f=1&t=21955
i think you may find more things availuable.
cheers!

Re: PCI ?

Posted: Tue May 11, 2010 5:36 am
by lemonyii
oh, and it seems that i sent a message to you about IOAPIC about a month ago. do you remember it and how is your IOAPIC now? i got a lot troubles with it and i skipped.
i'm annoyed about the IOAPIC now.

Re: PCI ?

Posted: Tue May 11, 2010 1:01 pm
by Sam111
Ya , I had a few problems with it. At first it seemed to work but then it started giving me werid interrupts. So I stopped coding/using it for the time being I will go back to it later and try to figure it out (but I need a break from it I was not making any progress)
Wish I could help you out on this one.

right now I am working on a few things
1) Understanding/Coding for the PCI (probing ,setting ,..etc stuff fot it or on it)
This seems really easy knock on wood but I don't want to get my hopes up to much.
2) after complete understanding of the PCI then I am going to try to write some device drivers for some of the PCI card devices.... (here is where part 1 gets hard probably )

I do have a few question you may know the answer to about the PCI

one is on the wiki and in the spec's it specifically states the configuration spaces is 256 bytes long doesn't this below table violate this (which was on the wiki)

Code: Select all

This table is applicable if the Header Type is 02h (PCI-to-CardBus bridge)
register 	bits 31-24 	bits 23-16 	bits 15-8 	bits 7-0
00 	Device ID 	Vendor ID
04 	Status 	Command
08 	Class code 	Subclass 	Prog IF 	Revision ID
0C 	BIST 	Header type 	Latency Timer 	Cache Line Size
10 	CardBus Socket/ExCa base address
14 	Secondary status 	Reserved 	Offset of capabilities list
18 	CardBus latency timer 	Subordinate bus number 	CardBus bus number 	PCI bus number
1C 	Memory Base Address 0
20 	Memory Limit 0
24 	Memory Base Address 1
28 	Memory Limit 1
2C 	I/O Base Address 0
30 	I/O Limit 0
34 	I/O Base Address 1
38 	I/O Limit 1
3C 	Bridge Control 	Interrupt PIN 	Interrupt Line
40 	Subsystem Vendor ID 	Subsystem Device ID
44 	16-bit PC Card legacy mode base address 
If this is true then I cann't create a structure that is 256bytes long to hold the complete
I would have to create different size structures based on the header info???

Also I am just curious the wiki shows header = 0x00 ,0x01,0x02 is their any other types of headers or better said is their any different tables then the 3 listed on the wiki that I could run into probing the whole system???

Also for the Class Code, Subclass, and Prog IF registers is their any way I can get the current list of what every possible numbers mean.
Like on the wiki they have a table

Code: Select all

Class Code 	 Subclass 	 Prog IF 	 Description
0x00 	0x00 	0x00 	Any device except for VGA-Compatible devices
0x01 	0x00 	VGA-Compatible Device
0x01 	0x00 	0x00 	SCSI Bus Controller
0x01 	0x-- 	IDE Controller
0x02 	0x00 	Floppy Disk Controller
0x03 	0x00 	IPI Bus Controller
0x04 	0x00 	RAID Controller
0x05 	0x20 	ATA Controller (Single DMA)
0x30 	ATA Controller (Chained DMA)
0x06 	0x00 	Serial ATA (Direct Port Access)
0x80 	0x00 	Other Mass Storage Controller 
.....etc
Is their away to get a list of the most up to date table in a format that I can easyly make into an array of strings corrosponding or some type of lookup table so when I check the
PCI configuration space and have 0x01 0x00 0x00 for the class , sub ,prog I can just print out SCSI Bus Controller

Re: PCI ?

Posted: Tue May 11, 2010 8:37 pm
by lemonyii
If this is true then I cann't create a structure that is 256bytes long to hold the complete
I would have to create different size structures based on the header info???
maybe. but i don't suggest this.
my method is to store vendor id, (bus,device,func,),the first base field, (class,subclass,progid) in the DEVICE structure.
these are basic info, you may need them frequently,and these might be enough for general work.
as to the drivers, they may get any infomation they need through (bus,device,func,) stored in the DEVICE structure.
my consideration is that many info is just for the driver but not for the system.the kernel do not need to know, so the don't have to be stored.
and that many info may be changed by the driver. if you stored them,you need to build complex mechinism to modify them in many places.
Is their away to get a list of the most up to date table in a format that I can easyly make into an array of strings corrosponding or some type of lookup table so when I check the
PCI configuration space and have 0x01 0x00 0x00 for the class , sub ,prog I can just print out SCSI Bus Controller
yes,right. i use tables too.and it works pretty now.But notice, you have to take something whose ProgID is 0x-- like IDE controller carefully.
it started giving me werid interrupts
and you got interrupt through IOAPIC? my god, i never get any!
the problem might be that you didn't mask i8259? it not masked,it may cause many problems,like giving a interrupt of within 32.
may i see your code about IOAPIC? if you like, you may mail it to
[email protected]
thx and cheers!