OSDev.org

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

All times are UTC - 6 hours




Post new topic Reply to topic  [ 23 posts ]  Go to page Previous  1, 2
Author Message
 Post subject: Re: Determining valid PCI address ranges
PostPosted: Sat Oct 16, 2021 5:01 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
You're detecting 64-bit BARs incorrectly: you're only checking bits 1 and 2, but you also need to check bit 0 since only memory BARs can be 64-bit.

Your code for restoring the upper half of a 64-bit BAR will never actually do that: you've got the condition backwards.

I forgot to ask earlier: why are you using count_ones() to find the number of set bits when you could calculate the size of the BAR directly?


Top
 Profile  
 
 Post subject: Re: Determining valid PCI address ranges
PostPosted: Sat Oct 16, 2021 5:26 pm 
Offline
Member
Member

Joined: Sun Jun 23, 2019 5:36 pm
Posts: 618
Location: North Dakota, United States
Octocontrabass wrote:
You're detecting 64-bit BARs incorrectly: you're only checking bits 1 and 2, but you also need to check bit 0 since only memory BARs can be 64-bit.

Your code for restoring the upper half of a 64-bit BAR will never actually do that: you've got the condition backwards.

Oopes! I can't believe I forgot that. :P

Octocontrabass wrote:
I forgot to ask earlier: why are you using count_ones() to find the number of set bits when you could calculate the size of the BAR directly?

Static typing constraints, or at least that was my reasoning at the time. The count_ones method returns the same type regardless of what actual type you were originally using, and I'm trying to store the (reversed) BAR in the outermost bar variable. I've also noticed I'm not adding 1 to the result -- would I do that when doing the 1 << ones operation or would I do that after NOTting the BAR (so (!bar + 1).count_ones())?
I think I've figured out two of the problems -- I was (1) counting *all* the bits set, including the informational ones, and (2) I was doing count_ones() twice. Which, you know, really doesn't work well. :P Not my day of coding today.
Should I continue skipping BARs that are zero? Not really sure if its worth it but... Also, what do I do about card bus bridges? Or should I keep ignoring those?


Last edited by Ethin on Sat Oct 16, 2021 5:55 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Determining valid PCI address ranges
PostPosted: Sat Oct 16, 2021 5:50 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
Ethin wrote:
I've also noticed I'm not adding 1 to the result -- would I do that when doing the 1 << ones operation or would I do that after NOTting the BAR (so (!bar + 1).count_ones())?

Neither. If you add 1 after NOTing the BAR (after clearing the non-address bits), the resulting value is the size. You wouldn't need any shifting or bit counting.


Top
 Profile  
 
 Post subject: Re: Determining valid PCI address ranges
PostPosted: Sat Oct 16, 2021 6:18 pm 
Offline
Member
Member

Joined: Sun Jun 23, 2019 5:36 pm
Posts: 618
Location: North Dakota, United States
Octocontrabass wrote:
Ethin wrote:
I've also noticed I'm not adding 1 to the result -- would I do that when doing the 1 << ones operation or would I do that after NOTting the BAR (so (!bar + 1).count_ones())?

Neither. If you add 1 after NOTing the BAR (after clearing the non-address bits), the resulting value is the size. You wouldn't need any shifting or bit counting.

Trying this and I'm getting addition overflows for 32-bit BARs.
Code:
            if bar2 == 0x00 {
                let mut bar = if !oldbar.get_bit(0) {
                    *bar.set_bits(0..4, 0)
                } else {
                    *bar.set_bits(0..2, 0)
                };
                bar = (!bar) + 1;
                info!("BAR {} uses {} bytes", idx, bar);
            } else {
                let mut bar = (bar2 as u64) << 32 | (bar as u64);
                bar.set_bits(0..4, 0);
                bar = (!bar) + 1;
                info!("BAR {} uses {} bytes", idx, bar);
            }

It doesn't happen with 64-bit BARs but the 32-bit one is confusing. Its like its completely saturating the value when it NOTs it, but that doesn't make any sense.


Top
 Profile  
 
 Post subject: Re: Determining valid PCI address ranges
PostPosted: Sat Oct 16, 2021 7:01 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
Ethin wrote:
Its like its completely saturating the value when it NOTs it, but that doesn't make any sense.

It makes sense for unimplemented BARs. All 32 bits are wired to 0 if the BAR is unimplemented, so when you NOT it you get the highest possible 32-bit integer.

Ethin wrote:
Also, what do I do about card bus bridges?

CardBus bridges implement only BAR0, much like how PCI-to-PCI bridges implement BAR0 and BAR1. The other address-related registers decide which address ranges pass through the bridge to address devices behind the bridge. (Except the register at offset 0x44, but your CardBus bridge driver shouldn't use that one.)


Top
 Profile  
 
 Post subject: Re: Determining valid PCI address ranges
PostPosted: Sat Oct 16, 2021 7:55 pm 
Offline
Member
Member

Joined: Sun Jun 23, 2019 5:36 pm
Posts: 618
Location: North Dakota, United States
Octocontrabass wrote:
Ethin wrote:
Its like its completely saturating the value when it NOTs it, but that doesn't make any sense.

It makes sense for unimplemented BARs. All 32 bits are wired to 0 if the BAR is unimplemented, so when you NOT it you get the highest possible 32-bit integer.

Ethin wrote:
Also, what do I do about card bus bridges?

CardBus bridges implement only BAR0, much like how PCI-to-PCI bridges implement BAR0 and BAR1. The other address-related registers decide which address ranges pass through the bridge to address devices behind the bridge. (Except the register at offset 0x44, but your CardBus bridge driver shouldn't use that one.)

Oh okay, thanks. This is my new code (I threw in some debug calls because I'm getting unimplemented BARs for devices that (should) have them -- I'll explain that below):
Code:
    let mut idx = 0;
    loop {
        if (dev.header_type == 0x00 && idx > 5)
            || (dev.header_type == 0x01 && idx > 1)
            || (dev.header_type == 0x02 && idx > 0)
        {
            break;
        }
        if dev.bars[&idx] == 0x00 {
            break;
        }
        let real_idx = match idx {
            0 => BAR0,
            1 => BAR1,
            2 => BAR2,
            3 => BAR3,
            4 => BAR4,
            5 => BAR5,
            _ => 0,
        };
        let oldbar = read_dword(addr, real_idx);
        let oldbar2 = if !oldbar.get_bit(0) && oldbar.get_bits(1..=2) == 0x02 {
            read_dword(addr, real_idx + 4)
        } else {
            0
        };
        debug!(
            "Read raw BARs from index {:X} and {:X}: {:X}, {:X}",
            real_idx,
            real_idx + 4,
            oldbar,
            oldbar2
        );
        write_dword(addr, real_idx, u32::MAX);
        debug!("Wrote {:X} to index {:X}", u32::MAX, real_idx);
        if !oldbar.get_bit(0) && oldbar.get_bits(1..=2) == 0x02 {
            write_dword(addr, real_idx + 4, u32::MAX);
            debug!(
                "BAR is 64 bits, so wrote {:X} to index {:X}",
                u32::MAX,
                real_idx + 4
            );
        }
        let mut bar = read_dword(addr, real_idx);
        debug!("Read {:X} from index {:X}", bar, real_idx);
        let bar2 = if !oldbar.get_bit(0) && oldbar.get_bits(1..=2) == 0x02 {
            read_dword(addr, real_idx + 4)
        } else {
            0
        };
        if bar2 != 0x00 {
            debug!(
                "BAR is 64 bits, so read {:X} from index {:X}",
                bar2,
                real_idx + 4
            );
        }
        write_dword(addr, real_idx, oldbar);
        debug!("Wrote old BAR value: {:X}, index {:X}", oldbar, real_idx);
        if bar2 != 0x00 {
            write_dword(addr, real_idx + 4, oldbar2);
            debug!(
                "Wrote old BAR value: {:X}, index {:X}",
                oldbar2,
                real_idx + 4
            );
        }
        if bar2 == 0x00 {
            let mut bar = if !oldbar.get_bit(0) {
                *bar.set_bits(0..4, 0)
            } else {
                *bar.set_bits(0..2, 0)
            };
            bar = (!bar) + 1;
            info!("BAR {} uses {} bytes", idx, bar);
        } else {
            let mut bar = (bar2 as u64) << 32 | (bar as u64);
            bar.set_bits(0..4, 0);
            bar = (!bar) + 1;
            info!("BAR {} uses {} bytes", idx, bar);
        }
        if oldbar.get_bits(1..=2) == 0x02 {
            idx += 2;
        } else {
            idx += 1;
        }
    }

The unimplemented BAR issue is a bit of a weird one. I'm launching qemu and requesting both an NVMe device and an AHCI device each with their own disk images attached to them. This is so I can work on my AHCI and NVMe drivers in sequence and just keep building on the VM as I get more and more of a functional system in future (since I think I've figured out AHCI finally). The device PCI tree looks something like this, and I'm quite confused (this is what my loop finds, not the actual values):
  • Device 0:0:0:0 is a host bridge with no BARs
  • Device 0:0:1:0 is a VGA-compatible display controller with 1 BAR
  • Device 0:0:2:0 is an ethernet NIC with 4 BARs
  • Device 0:0:3:0 is a non-volatile memory controller with 1 BAR
  • Device 0:0:4:0 is a SATA controller with no BARs
  • Device 0:0:5:0 is an audio device with 1 BAR
  • Device 0:0:6:0 is a USB controller with 1 BAR
  • Devices 0:0:1D:0, 0:0:1D:1, and 0:0:1D:2 are USB controllers with no BARs
  • Device 0:0:1D:7 is yet another USB controller with 1 BAR
  • Device 0:0:1F:0 is an ISA bridge with no BARs
  • Device 0:0:1F:2 is a SATA controller with no BARs
  • Device 0:0:1F:3 is an SMBus with no BARs
I've only removed the VirtIO stuff. But it does appear as though all the BARs are being found -- my loop just skips them for some reason. I could iterate through the BARs by key, but the problem is that I'd like to skip upper halves of 32-bit BARs (my initial BAR scan doesn't do this and just pulls in everything, but I'm hoping to refactor that too).


Top
 Profile  
 
 Post subject: Re: Determining valid PCI address ranges
PostPosted: Sat Oct 16, 2021 8:34 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
You're breaking out of the loop when you encounter an unimplemented BAR, when you actually want to continue to the next iteration.


Top
 Profile  
 
 Post subject: Re: Determining valid PCI address ranges
PostPosted: Sat Oct 16, 2021 9:17 pm 
Offline
Member
Member

Joined: Sun Jun 23, 2019 5:36 pm
Posts: 618
Location: North Dakota, United States
Octocontrabass wrote:
You're breaking out of the loop when you encounter an unimplemented BAR, when you actually want to continue to the next iteration.

Thanks. Got it working just fine now (it appears that way, anyway). Will definitely consider merging my BAR location algorithm into this one so it does both simultaneously and I don't read BARs that won't actually work (that is, I read all BARs, even if one of them was used in the calculation for the BAR before the one I'm reading).


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page Previous  1, 2

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], Majestic-12 [Bot] and 73 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