OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 1:58 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 15 posts ] 
Author Message
 Post subject: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Fri Apr 15, 2022 9:38 am 
Offline

Joined: Sat Oct 16, 2021 11:57 am
Posts: 15
Hiya all. Sorry for the frequent questions to this forum, but I have another problem. I've recently been trying to implement PCI.I've been going off the PCI page on the wiki for the configuration reading and interpretation. All seemed well, and I read a fairly rational looking list of devices, with notably one mass storage controller of type 0x6 (SATA, hopefully). However, once I tried reading the BAR* addresses for function 2 (or function 1, if you start counting at 0), I ran into a perplexing set of data. Here is the struct that my function produced (note that here, I only scanned for the base addresses of function 2 for now); Note that all numbers are in base 10:
Code:
PCIDevice {
    bus: 0,
    device: 1,
    vendor_id: 32902,
    device_id: 28672,
    class: MassStorageController,
    subclass_id: 6,
    header_type: 0,
    base_addresses_f1: Some(
        [
            0,
            0,
            0,
            0,
            49249,
            0,
        ],
    ),
}

This is strange, because the other devices read like this:
Code:
PCIDevice {
    bus: 0,
    device: 2,
    vendor_id: 4660,
    device_id: 4369,
    class: Unclassified,
    subclass_id: 3,
    header_type: 0,
    base_addresses_f1: Some(
        [
            4294967295,
            4294967295,
            4294967295,
            4294967295,
            4294967295,
            4294967295,
        ],
    ),
}

Granted, it may be nonsensical to scan for function 1 addresses for most devices, so I can understand why they would return 2³²-1. But why is the SATA device's address a (base 10) part of this number? This seems odd. Has anyone got an explanation for this?

As an aside, does anyone know any good resources for actual PCI communication? Do I just use the numbers returned as port numbers or memory addresses? Sorry if this is a stupid question or just something I overlooked on the wiki article?

Many thanks.


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Fri Apr 15, 2022 10:44 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4591
Location: Chichester, UK
I’m not quite clear what your problem is. Is it the port number (0xC060)? That seems reasonable to me.


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Fri Apr 15, 2022 10:52 am 
Offline

Joined: Sat Oct 16, 2021 11:57 am
Posts: 15
iansjack wrote:
I’m not quite clear what your problem is. Is it the port number (0xC060)? That seems reasonable to me.


First, thank you for confirming (?) that it is a port I am looking at. Second, perhaps it is just a coincidence that the port number resembles the other numbers in decimal. Maybe I'm just being a bit paranoid :?


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Fri Apr 15, 2022 11:05 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4591
Location: Chichester, UK
Looks like it’s just a coincidence. The fact that it’s a multiple of 16 makes it look like a valid port number to me.

The real answer is to try reading/writing to that port range to see if you get the expected results.


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Fri Apr 15, 2022 11:29 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
skyesp wrote:
Granted, it may be nonsensical to scan for function 1 addresses for most devices, so I can understand why they would return 2³²-1.

Indeed. If function 0 says the device isn't multifunction, then scanning any of the other 7 functions could return all kinds of garbage.

skyesp wrote:
But why is the SATA device's address a (base 10) part of this number? This seems odd. Has anyone got an explanation for this?

It is purely a coincidence. You should print your numbers in hexadecimal so it's easier to read the individual bits.

The address itself makes no sense for an AHCI controller. Are you sure you're looking at one?

skyesp wrote:
As an aside, does anyone know any good resources for actual PCI communication? Do I just use the numbers returned as port numbers or memory addresses? Sorry if this is a stupid question or just something I overlooked on the wiki article?

The low bits of each BAR tell you whether it's a memory or I/O address, as well as some other important information. For example, a BAR containing 0xC061 indicates an I/O address of 0xC060. All of this information comes from the PCI Local Bus Specification, which I'm sure you can "borrow" from someone.


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Sat Apr 16, 2022 2:54 am 
Offline

Joined: Sat Oct 16, 2021 11:57 am
Posts: 15
Octocontrabass, I am not 100 percent sure I am reading correctly. If you feel like it, you take a quick peek at my source code. The pci_config_read_word function is translated from the wiki page, but my offsets might be wrong.

Code:
fn pci_config_read_dword(bus: u8, device: u8, function: u8, offset: u8) -> u32{
    let long_bus = bus as u32;
    let long_slot = device as u32;
    let long_func = function as u32;
    let mut tmp = 0u32;
    //Create the address as per figure 1 at the link above.
    let address = ((long_bus << 16) | (long_slot << 11)
        | (long_func << 8) | (offset & 0xFC) as u32 | (0x80000000 as u32)) as u32;
    //Port to be used for the address.
    let mut port1:PortGeneric<u32, ReadWriteAccess> = Port::new(0xCF8);
    unsafe {
        //Write the address
        port1.write(address);
    }
    //Port to be used for the data.
    let mut port2: PortGeneric<u32, ReadWriteAccess> = Port::new(0xCFC);
    unsafe { port2.read() }
}

^^ this is the function to read a dword from the configuration register. This might perhaps be a naive approach, I am not sure.

Code:
///Read the header type of a pci device from its configuration space
fn get_pci_header_type(bus: u8, device: u8)->u8{
    pci_config_read_word(bus, device, 0, 0xC+0x2).to_le_bytes()[1]
}

///Get the vendor ID of a PCI device. Header type independent.
fn get_vendor_id(bus: u8, device: u8, function: u8) -> u16{
    pci_config_read_word(bus, device, function, 0)
}

///Get the device ID of a PCI device. Header type independent.
fn get_device_id(bus: u8, device: u8, function: u8) -> u16{
    pci_config_read_word(bus, device, function, 0x2)
}

///Get the PCI Device Class of a PCI device. Header type independent.
fn check_pci_device_class(bus: u8, device: u8, function: u8) -> u8{
    pci_config_read_word(bus, device, function, 0xA).to_le_bytes()[0]
}

///Get the PCI device subclass of a PCI device. Header type independent.
fn check_pci_device_subclass(bus: u8, device: u8, function: u8) -> u8{
    pci_config_read_word(bus, device, function, 0xA).to_le_bytes()[1]
}


Finally, this is the function to check the device.
Code:
///Check a PCI device and return None if the device does not exist or Some<PCIDevice> if it does.
fn check_device(bus: u8, device: u8) -> Option<PCIDevice>{
    //FIXME: multiple function devices
    let header_type = get_pci_header_type(bus, device);
    let vendor_id = get_vendor_id(bus, device, 0);
    if vendor_id == 0xFFFF{
        return None;
    }
    //Device vendor is valid
    let device_id = get_device_id(bus, device, 0);
    let class_id = check_pci_device_class(bus, device, 0);
    let subclass_id = check_pci_device_subclass(bus, device, 0);
    let mut base_addresses_f1 = None;
    if header_type == 0x0{
        base_addresses_f1 = Some([
            pci_config_read_dword(bus, device, 1, 0x10),
            pci_config_read_dword(bus, device, 1, 0x14),
            pci_config_read_dword(bus, device, 1, 0x18),
            pci_config_read_dword(bus, device, 1, 0x1C),
            pci_config_read_dword(bus, device, 1, 0x20),
            pci_config_read_dword(bus, device, 1, 0x24),
        ])
    }
    //println!("PCI: header type: {:#x}, vendor = {:#x}, device = {:#x}, class = {:#x}, subclass = {:#x}", header_type, vendor_id, device_id, class_id, subclass_id);
    Some(PCIDevice{
        bus,
        device,
        vendor_id,
        device_id,
        subclass_id,
        class: class_id.into(),
        base_addresses_f1,
        header_type
    })
}


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Sat Apr 16, 2022 6:13 am 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1593
skyesp wrote:
Octocontrabass, I am not 100 percent sure I am reading correctly.
Well, what is not clear about what he wrote? You enumerate a device by enumerating its function 0, and if that worked and function 0 said the device is multifunction, then enumerate functions 1-7. So your function check_device is missing a parameter and your enumerator must be changed to account for MF devices.

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Sat Apr 16, 2022 6:41 am 
Offline

Joined: Sat Oct 16, 2021 11:57 am
Posts: 15
nullplan wrote:
skyesp wrote:
Octocontrabass, I am not 100 percent sure I am reading correctly.
Well, what is not clear about what he wrote? You enumerate a device by enumerating its function 0, and if that worked and function 0 said the device is multifunction, then enumerate functions 1-7. So your function check_device is missing a parameter and your enumerator must be changed to account for MF devices.

Sorry, I meant I am not sure whether I am reading the configuration register correctly.


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Sat Apr 16, 2022 1:53 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
skyesp wrote:
The pci_config_read_word function is translated from the wiki page, but my offsets might be wrong.

[...]

^^ this is the function to read a dword from the configuration register. This might perhaps be a naive approach, I am not sure.

The wiki sets a bad example. You don't need separate functions to read different sizes - it's perfectly valid to read an entire dword every time. But if you are going to read only a word, why would you use the IN instruction to read an entire dword? Port 0xCFC allows partial reads and writes!

skyesp wrote:
Code:
///Read the header type of a pci device from its configuration space
fn get_pci_header_type(bus: u8, device: u8)->u8{
    pci_config_read_word(bus, device, 0, 0xC+0x2).to_le_bytes()[1]
}

You're reading the byte at offset 0xF, not 0xE, so you have no idea what the actual header type is or whether a device actually is multifunction. Also, the header type can be different for each function, but you've hardcoded function 0.

skyesp wrote:
Code:
///Get the PCI Device Class of a PCI device. Header type independent.
fn check_pci_device_class(bus: u8, device: u8, function: u8) -> u8{
    pci_config_read_word(bus, device, function, 0xA).to_le_bytes()[0]
}

///Get the PCI device subclass of a PCI device. Header type independent.
fn check_pci_device_subclass(bus: u8, device: u8, function: u8) -> u8{
    pci_config_read_word(bus, device, function, 0xA).to_le_bytes()[1]
}

The class code is three bytes. You need all three bytes to identify a device.

skyesp wrote:
Finally, this is the function to check the device.

Well, it's no wonder the results are nonsense. You're only reading the BARs from function 1 and nothing else. You can't interpret the BARs - or even know if they exist - without reading the rest of that function's configuration space.


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Mon Apr 18, 2022 5:15 am 
Offline

Joined: Sat Oct 16, 2021 11:57 am
Posts: 15
@Octocontrabass, thank you very much for all your help. I will implement the changes you suggested and see if the results make more sense.


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Mon Apr 18, 2022 5:39 am 
Offline

Joined: Sat Oct 16, 2021 11:57 am
Posts: 15
Octocontrabass wrote:
The class code is three bytes. You need all three bytes to identify a device.

I don't quite understand this. The wiki says the class code is from bits 31-24 at the offset. Is the wiki incorrect?


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Mon Apr 18, 2022 7:17 am 
Offline
Member
Member

Joined: Sun Jun 23, 2019 5:36 pm
Posts: 618
Location: North Dakota, United States
The class code consists of the class, subclass, and program interface codes. You need all three to know what kind of device your working with. In some cases, devices do not have a particular program interface, making the precise device unclear; for that, you should also include the vendor and device IDs.


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Mon Apr 18, 2022 10:24 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
skyesp wrote:
The wiki says the class code is from bits 31-24 at the offset. Is the wiki incorrect?

The wiki should say "base class". The class code is three bytes taken together: the base class, the subclass, and the programming interface.


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Mon Apr 18, 2022 11:28 am 
Offline
Member
Member

Joined: Mon Mar 14, 2016 5:34 am
Posts: 40
for the ahci I'm not sure but for the ide controller I observed that sometimes when the controller is in compatibility mode (see https://wiki.osdev.org/PCI_IDE_Controller#Detecting_a_PCI_IDE_Controller), BAR0 to BAR4 return 0 or strange values

as the bar0 to bar4 of the ahci are reserved for a compatibility operating mode, the operation may be identical


Top
 Profile  
 
 Post subject: Re: PCI Configuration reads strange address for BAR0-BAR5
PostPosted: Tue Apr 19, 2022 12:11 am 
Offline

Joined: Sat Oct 16, 2021 11:57 am
Posts: 15
Octocontrabass wrote:
skyesp wrote:
The wiki says the class code is from bits 31-24 at the offset. Is the wiki incorrect?

The wiki should say "base class". The class code is three bytes taken together: the base class, the subclass, and the programming interface.

Alright, that clears it up. Thank you!


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

All times are UTC - 6 hours


Who is online

Users browsing this forum: Amazonbot [bot], DotBot [Bot], Google [Bot] and 43 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