OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Oct 21, 2021 9:48 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 8 posts ] 
Author Message
 Post subject: reading pci id is always 0x1111
PostPosted: Mon Apr 12, 2021 12:06 pm 
Offline

Joined: Mon Nov 02, 2020 4:53 pm
Posts: 20
Hello there,

I am attempting to enumerate the pci configuration space (on qemu, if that matters, I haven't had a chance to try on real hardware), but the ID always comes out as 0x1111, and the vendor always comes out as 0.

my pci code looks like this
Code:
#define PCI_ADDR 0x0CF8
#define PCI_DATA 0x0CFC

// port is the byte offset from 0x00
unsigned char pci_read_byte(const int bus, const int dev, const int func, const int port) {
   const int shift = ((port & 3) * 8);
   const unsigned int val = 0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (port & 0xFC);
   outl(PCI_ADDR, val);
   return (inl(PCI_DATA) >> shift) & 0xFF;
}

// port is the byte offset from 0x00
unsigned short pci_read_word(const int bus, const int dev, const int func, const int port) {
   if ((port & 3) <= 2) {
      const int shift = ((port & 3) * 8);
      const unsigned int val = 0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (port & 0xFC);
      outl(PCI_ADDR, val);
      return (inl(PCI_DATA) >> shift) & 0xFFFF;
   } else
      return (pci_read_byte(bus, dev, func, port + 1) << 8) | pci_read_byte(bus, dev, func, port);
}

// port is the byte offset from 0x00
unsigned int pci_read_dword(const int bus, const int dev, const int func, const int port) {
   if ((port & 3) == 0) {
      const unsigned int val = 0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (port & 0xFC);
      outl(PCI_ADDR, val);
      return inl(PCI_DATA);
   } else
      return (pci_read_word(bus, dev, func, port + 2) << 16) | pci_read_word(bus, dev, func, port);
}


and I'm calling it like this (please excuse my debug code)
Code:
/// max pci buses to enumerate
pub const PCI_MAX_BUS: i32 = 256;
/// max number of devices to enumerate in a bus
pub const PCI_MAX_DEV: i32 = 32;
/// max number of functions to enumerate in a device
pub const PCI_MAX_FUNC: i32 = 8;

pub unsafe fn probe() {
    let mut found = false;
    let mut nonzero = false;

    // enumerate pci buses
    for bus in 0..PCI_MAX_BUS {
        for dev in 0..PCI_MAX_DEV {
            for func in 0..PCI_MAX_FUNC {
                // read the first 16 bytes of the function
                // if it is not 0xFFFF, then it's a valid function
                let id = pci::pci_read_dword(bus, dev, func, 0x00);

                // vendor is bits 31:16
                let vendor = (id & 0xFFFF0000) << 15;
                dbgln!(
                    "[{}][{}][{}] -> id: {i:b} ({i:x}), vendor: {v:b} ({v:x})",
                    bus,
                    dev,
                    func,
                    i = id,
                    v = vendor
                );

                if vendor != 0xFFFF {
                    // 0x09 is CC - Class Code
                    // see 2.1.5 of the intel serial ata spec
                    let class_code = pci::pci_read_dword(bus, dev, func, 0x09);

                    // base class code, bits 23:16
                    let bcc = (class_code & 0x0000FF00) << 15;
                    // sub class code, bits 15:08
                    let scc = (class_code & 0x00FF0000) << 7;

                    dbgln!("[{}][{}][{}] -> class code: {:b}, base class code: {:b}, sub class code: {:b}", bus, dev, func, class_code, bcc, scc);

                    if bcc == 0x01 && scc == 0x06 {
                        found = true;
                    }

                    if bcc != 0 || scc != 0 {
                        nonzero = true;
                    }
                }
            }
        }
    }

    dbgln!(
        "found a suitable drive: {}\nany values nonzero? : {}",
        found,
        nonzero
    );
}


a snippet of the output
Code:
[1][12][0] -> class code: 10001000000000000000000010001, base class code: 0, sub class code: 0
[1][12][1] -> id: 1000100010001 (1111), vendor: 0 (0)
[1][12][1] -> class code: 10001000000000000000000010001, base class code: 0, sub class code: 0
[1][12][2] -> id: 1000100010001 (1111), vendor: 0 (0)
[1][12][2] -> class code: 10001000000000000000000010001, base class code: 0, sub class code: 0
[1][12][3] -> id: 1000100010001 (1111), vendor: 0 (0)
[1][12][3] -> class code: 10001000000000000000000010001, base class code: 0, sub class code: 0
[1][12][4] -> id: 1000100010001 (1111), vendor: 0 (0)
[1][12][4] -> class code: 10001000000000000000000010001, base class code: 0, sub class code: 0


the issue could be from the fact that I'm using ffi to call from rust, but I sort of doubt it. I'm pretty bad at math so I could possibly be doing shifting wrong or something, or it could be a deeper issue.

thank you,
skylar alexandra bleed


Top
 Profile  
 
 Post subject: Re: reading pci id is always 0x1111
PostPosted: Mon Apr 12, 2021 1:42 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 3406
monarrk wrote:
Code:
// port is the byte offset from 0x00

They're called registers, not ports.

monarrk wrote:
Code:
   return (inl(PCI_DATA) >> shift) & 0xFF;

PCI_DATA allows byte and word accesses, you don't have to access the whole dword every time. Add the lowest two bits of the register address to PCI_DATA. (But you still can't cross a dword boundary.)

monarrk wrote:
Code:
                let vendor = (id & 0xFFFF0000) << 15;

You're shifting the wrong way.

There may be other issues, I'm not very familiar with Rust.


Top
 Profile  
 
 Post subject: Re: reading pci id is always 0x1111
PostPosted: Tue Apr 13, 2021 12:08 pm 
Offline
Member
Member

Joined: Sun Jun 23, 2019 5:36 pm
Posts: 442
Location: North Dakota, United States
First, your code you provided at the top is not Rust. Its C. I don't know if your trying to link Rust and C together or what, but I would hesitate in doing that in a low-level project like this, if only because the compiler might not compile your code correctly. (Rust obviously can't read C code, so it has to go to Clang or another C compiler.)
Next, I don't think your reading code is correct. The PCI article explicitly states:
Code:
uint16_t pciConfigReadWord (uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
    uint32_t address;
    uint32_t lbus  = (uint32_t)bus;
    uint32_t lslot = (uint32_t)slot;
    uint32_t lfunc = (uint32_t)func;
    uint16_t tmp = 0;
 
    /* create configuration address as per Figure 1 */
    address = (uint32_t)((lbus << 16) | (lslot << 11) |
              (lfunc << 8) | (offset & 0xfc) | ((uint32_t)0x80000000));
 
    /* write out the address */
    outl(0xCF8, address);
    /* read in the data */
    /* (offset & 2) * 8) = 0 will choose the first word of the 32 bits register */
    tmp = (uint16_t)((inl(0xCFC) >> ((offset & 2) * 8)) & 0xffff);
    return (tmp);
}

Therefore, the equivalent Rust translation would be something like (note, untested):
Code:
pub fn pciConfigReadWord (bus: u8, slot: u8, func: u8, offset: u8) -> u16 {
    /* create configuration address as per Figure 1 */
    let address = (((bus as u32) << 16) | ((slot as u32) << 11) | ((func as u32) << 8) | ((offset as u32) & 0xfc) | 0x80000000;
    /* write out the address */
    unsafe {
        outl(0xCF8, address);
    }
    /* read in the data */
    /* (offset & 2) * 8) = 0 will choose the first word of the 32 bits register */
    let res = unsafe {
        inw(0xcfc)
        };
((res >> (((offset as u16) & 2) * 8)) & 0xffff)
}

One implementation that I found that worked was this:
Code:
    let lbus = bus as u32;
    let lslot = slot as u32;
    let lfunc = func as u32;
    unsafe {
        outl(
            ((((lbus as u32) << 16) as u32)
                | (((lslot as u32) << 11) as u32)
                | (((lfunc as u32) << 8) as u32)
                | ((offset as u32) & 0xfc)
                | (0x80000000)) as u32,
            0xCF8,
        );
        inw(0xCFC)
    }

This can be trivially adapted for byte and dword accesses. If you tweak it a bit you can make a universal PCI read/write function in one.


Top
 Profile  
 
 Post subject: Re: reading pci id is always 0x1111
PostPosted: Tue Apr 13, 2021 12:22 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 3406
Ethin wrote:
The PCI article explicitly states:

That code is an awful example. It does work, but there are easier ways.

Ethin wrote:
One implementation that I found that worked was this:

It only works when the low two bits of the offset are zero. If you want to read the word at offset 1 or 2, for example, you would need to add those two bits to 0xCFC so that the final inw call reads from 0xCFD or 0xCFE. (What about offset 3? That crosses a dword boundary, so you have to split it into two byte accesses.)


Top
 Profile  
 
 Post subject: Re: reading pci id is always 0x1111
PostPosted: Tue Apr 13, 2021 6:14 pm 
Offline
Member
Member

Joined: Sun Jun 23, 2019 5:36 pm
Posts: 442
Location: North Dakota, United States
Octocontrabass wrote:
Ethin wrote:
The PCI article explicitly states:

That code is an awful example. It does work, but there are easier ways.

Ethin wrote:
One implementation that I found that worked was this:

It only works when the low two bits of the offset are zero. If you want to read the word at offset 1 or 2, for example, you would need to add those two bits to 0xCFC so that the final inw call reads from 0xCFD or 0xCFE. (What about offset 3? That crosses a dword boundary, so you have to split it into two byte accesses.)

And the PCI article conveniently leaves something like that, which is very important, out? Wow. Talk about deliberately making people's lives hard. Thanks for that -- no one ever said anything like that when I wrote that code. My kernel doesn't use that code anymore -- I've switched completely over to PCIe now -- but still, that's useful to know.


Top
 Profile  
 
 Post subject: Re: reading pci id is always 0x1111
PostPosted: Wed Apr 14, 2021 6:22 am 
Offline

Joined: Mon Nov 02, 2020 4:53 pm
Posts: 20
Quote:
First, your code you provided at the top is not Rust. Its C. I don't know if your trying to link Rust and C together or what, but I would hesitate in doing that in a low-level project like this, if only because the compiler might not compile your code correctly. (Rust obviously can't read C code, so it has to go to Clang or another C compiler.)


I am aware. I am using ffi, like I said in the original post


Top
 Profile  
 
 Post subject: Re: reading pci id is always 0x1111
PostPosted: Wed Apr 14, 2021 8:12 am 
Online
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1109
Ethin wrote:
And the PCI article conveniently leaves something like that, which is very important, out?
Why is this in any way important? My PCI stack uses only aligned DWORD reads and writes, and somehow I make do with that. I cannot think of a single field anywhere in PCI that crosses a DWORD boundary and is misaligned. Even for the MMIO method, I only use aligned DWORD accesses. It can be done.

_________________
Thou hast outraged, not insulted me, sir; but for that I ask thee not to beware of Starbuck; thou wouldst but laugh; but let Ahab beware of Ahab; beware of thyself, old man.


Top
 Profile  
 
 Post subject: Re: reading pci id is always 0x1111
PostPosted: Thu Apr 15, 2021 8:00 am 
Offline
Member
Member

Joined: Sat Mar 10, 2018 10:16 am
Posts: 230
My favorite PCI read method in C:
Code:
//this method can read everything from PCI
uint32_t read_pci(uint32_t bus, uint32_t dev, uint32_t func, uint32_t offset) {
    outl(0xCF8, (0x80000000 | (bus<<16) | (dev<<11) | (func<<8) | (offset & 0xFC));
    return inl(0xCFC);
}

//and how read what you want
#define read_pci_vendorid(bus, dev, func) (read_pci(bus, dev, func, 0x00) & 0xFFFF)
#define read_pci_deviceid(bus, dev, func) (read_pci(bus, dev, func, 0x00) >> 16)
#define read_pci_class(bus, dev, func) (read_pci(bus, dev, func, 0x08) >> 24)
#define read_pci_subclass(bus, dev, func) ((read_pci(bus, dev, func, 0x08) >> 16) & 0xFF)
#define read_pci_progif(bus, dev, func) ((read_pci(bus, dev, func, 0x08) >> 8) & 0xFF)
//...


Hope that this will help.

_________________
https://github.com/Klaykap/BleskOS


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Kamal123, nullplan and 20 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group