PCI gives strange values

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
Klakap
Member
Member
Posts: 299
Joined: Sat Mar 10, 2018 10:16 am

PCI gives strange values

Post by Klakap »

Good day! PCI gives me strange values. On 65025 devices is vendorID 3327, deviceID 0, classcode 0, subclass 0 and progIF 12. I use Virtualbox. Please what it is? My code is in https://github.com/Klaykap/LightningOS/issues/1
User avatar
SpyderTL
Member
Member
Posts: 1074
Joined: Sun Sep 19, 2010 10:05 pm

Re: PCI gives strange values

Post by SpyderTL »

From a quick look at your code, I think

Code: Select all

tmp = (uint16_t)((inl(0xCFC) >> ((offset & 2) * 8)) & 0xffff);
Should be

Code: Select all

tmp = (uint16_t)((inl(0xCFC) >> ((offset & 1) == 0 ? 8 : 0)) & 0xffff);
Or something like that.
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott
Klakap
Member
Member
Posts: 299
Joined: Sat Mar 10, 2018 10:16 am

Re: PCI gives strange values

Post by Klakap »

Thank you, I change code but output is now:
vendorID 12
deviceID 12
classcode 0
subclass 12
progIF 0
on all 65025 slots.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: PCI gives strange values

Post by BenLunt »

Hi,

When reading the PCI space, you need to keep a few things in mind.

The read register is a dword register, but can be read as a byte, word, or dword, as well as reading any one of the bytes within the dword.

For example, the two lines below produce the exact same results:

word = inpw(PCI_DATA + 2);
word = inpd(PCI_DATA) >> 16;

Therefore, you can read any byte, word, or dword at any offset in the PCI space, with the understanding that you cannot read across a dword boundary. For example, you cannot do:

dword = inpd(PCI_DATA + 2);

expecting to read the high word of the first dword and the low word of the next dword.

With this in mind, the following lines are all valid:

byte = inpb(PCI_DATA + 0);
byte = inpb(PCI_DATA + 1);
byte = inpb(PCI_DATA + 2);
byte = inpb(PCI_DATA + 3);
word = inpw(PCI_DATA + 0);
word = inpw(PCI_DATA + 1);
word = inpw(PCI_DATA + 2);
dword = inpd(PCI_DATA + 0);

However, the following lines are *not* valid:

byte = inpb(PCI_DATA + x); // where x > 3
word = inpw(PCI_DATA + x); // where x > 2
dword = inpd(PCI_DATA + x); // where x > 0

Now, take this in to mind and how about you do the following:

Code: Select all

  pci_read_dword(..., ..., offset) 
   begin
     if (offset > 0) then
       begin
         return (pci_read_word(offset) << 0) | (pci_read_word(offset) << 16)
       end
     else
       return dword
   end
Then pci_read_word() does the same thing, but for bytes. i.e.: if the word offset would cross a dword boundary, it calls pci_read_byte twice.

Do you get the idea?

If the current sized read will read across the dword boundary, call the smaller sized read twice, else just read in the sized value.

For example, a dword read from offset 2 in the config space will actually do:

Code: Select all

  read_pci_dword calls:
     read_pci_word(offset + 0)  // offset = 2
     read_pci_word(offset + 2)  // offset = 2
How above a dword read from offset 3?

Code: Select all

  read_pci_dword calls:
     read_pci_word calls
        read_pci_byte(offset + 0)  // offset = 3
        read_pci_byte(offset + 1)  // offset = 3
     read_pci_word(offset + 2)     // offset = 3
Two more things:
1) If the first function of a device returns 0xFFFF as the vendor ID, the remaining functions of this device are not valid. They, infact, might return other values, though since the first function returned 0xFFFF, these functions are to be ignored.
2) The first function has a bit in the header that states whether the remaining functions are valid. If this bit indicates there are no more functions, you must skip the remaining 7 functions while enumerating the PCI. As an example, I have an ATA device controller that will return the same data for all 8 functions read even though there is only one valid function. If your PCI enumerator didn't ignore the remaining 7 functions, you might think you have 8 ATA controllers at this device...

Does this help,
Ben
- http://www.fysnet.net/osdesign_book_series.htm
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: PCI gives strange values

Post by BenLunt »

Since you didn't reply yet, either you fixed the problem and haven't been able to stop since, or you still are having problems and have pulled all your hair out thinking about it.

How are you doing?

Ben
Klakap
Member
Member
Posts: 299
Joined: Sat Mar 10, 2018 10:16 am

Re: PCI gives strange values

Post by Klakap »

Sorry, but I did not understand as I do that the methods pci_read_* (in PCI on the wikipage is only pci_read_word) I can't program in Pascal . Please how is done in C?
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: PCI gives strange values

Post by BenLunt »

Klakap wrote:Sorry, but I did not understand as I do that the methods pci_read_* (in PCI on the wikipage is only pci_read_word) I can't program in Pascal . Please how is done in C?
Sorry, I won't write the code for you.

The wiki gives an excellent example of reading a word from a given address. It should be fairly simple to modify it to read a byte or a dword.

Remember, when you write the bitmap to the ADDRESS register, the PCI bus will populate the DATA register with a 32-bit value read from the dword offset specified by the bit map written to the ADDRESS register. It is up to your code to extract a byte, word, or the whole dword from this register by either reading the register as an 8-bit byte, a 16-bit word, or a 32-bit dword. The architecture allows you to read a word from DATA + 0, DATA + 1, or DATA + 2, for example. Or you can read the whole 32-bit dword then AND/SHR the result to get the smaller sized value.

However, the point is, the PCI BUS will only populate the register with DWORDs on DWORD boundaries. You cannot read a dword from PCI Config Space byte offset 2. If you wish to read a dword from this offset, you must tell the PCI bus to read the dword at offset 0, extract the word at offset 2, *and* read the dword from offset 4 (the second dword), then extract the remaining word from this value to combine to create the dword you wish to read.

Here is a figure from Chapter 2 of my USB book:

Image

Assuming each is in little ending format:

Example 1 shows that if I wish to read a byte from PCI Config Space byte offset 20, which is the first byte of Dword 5, I must tell the PCI bus to read the dword at DWORD 5. Then I can extract the byte by either reading a byte from the register (DATA + 0, in this case), or reading the whole dword register and ANDing the 32-bit value to extract the 8-bit result.

Example 2 shows that if I wish to read a word from PCI Config Space byte offset 30, which is the higher word of Dword 7, I must tell the PCI bus to read the dword at DWORD 7. Then I can extract the word by either reading a word from the register (DATA + 2, in this case), or reading the whole dword register and SHRing the 32-bit value to extract the 16-bit result.

Example 3 shows that if I wish to read a dword from PCI Config Space byte offset 34, I *must* read from the PCI bus twice to get the result dword value. I *must* read from Dword 8, extract the high word, placing it in the low word of my result value, then I *must* read from Dword 9, extracting the low word and placing it in the high word of my resultant value.

This is quite confusing, but with a little reading and understanding, it will get quite easy, quite quickly.

My point to this and my previous post is that if you write three routines, a read_dword, a read_word, and a read_byte routine, you can call either of them with a byte offset to the value you wish to read. Then if the dword or word routine calculates that it would be reading across a dword boundary, it can call the lesser-sized routine twice to extract this value, of course knowing that a byte read will not cross a dword boundary, because you are reading a byte from a byte offset.

Therefore, if I wish to read a dword from byte offset 34, as in the example above, I call my read_dword routine with an offset value of 34. My dword routine does a:

Code: Select all

  if ((offset + 4) % 4) > 0) {
or
  if ((offset & 3) > 0) {
or
  if (any other way to know if we would cross a boundary) {
     low_word = read_word(offset + 0);
     high_word = read_word(offset + 2);
     return (high_word << 16) | low_word;
  } else
     return dword_read_from_DATA_register;
The read_word routine does the same thing, calling two byte-reads if necessary.

This technique lets me read a dword from any byte offset within the PCI Config space by calling only read_dword(), allowing the read_dword() routine to "farm" out the necessary 16-bit reads, then allowing read_word() "farm" out the necessary 8-bit reads.

Does this make any sense? Clear as mud?

Ben
- http://www.fysnet.net/osdesign_book_series.htm
Klakap
Member
Member
Posts: 299
Joined: Sat Mar 10, 2018 10:16 am

Re: PCI gives strange values

Post by Klakap »

Thank you for advice, I will try to do a driver for the PCI.
Post Reply