PCI Access Clarification

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
rkennedy9064
Member
Member
Posts: 36
Joined: Wed Sep 01, 2010 3:54 pm

PCI Access Clarification

Post by rkennedy9064 »

Hey everyone, I've been testing some stuff out recently and I'm back with another clarification question if anyone has time to help out. I'm working on a pci device and feel like I'm missing something when it comes to accessing it. I was able to find my device by locating its vendor_id by looping through all the devices and using ports 0xCF8 and 0xCFC. According to the devices documentation, it stores its base addresses in these locations.
Memory aperture base address (PCI configuration space offset 0x10).

The memory aperture base address value at offset 0x10 within the PCI configuration space
is in bits [31:26] of its DWORD. Therefore, to isolate the proper bits, the value should be
logically ANDed with 0xFC000000.

Register aperture base address (PCI configuration space offset 0x18).

The register aperture base value resides in bits [31:14] of its DWORD (at offset 0x18).
Therefore, to isolate the proper bits, the value should be logically ANDed with
0xFFFFC000.

I/O base address (PCI configuration space offset 0x14).

For the I/O base aperture, the actual value is within bits [31:8] of its DWORD (at offset
0x14). Therefore, to isolate the proper bits, the value should be logically ANDed with
0xFFFFFF00.
Using that info, I was able to determine locations that should be at these locations.
memory_base = 0xFC00_0000
io_base = 0xC000
register_base = 0xFEBF000
I wanted to test these mappings by trying to read the vendor id from one of the read only pci configuration copy registers. According to the documentation the register should be accessible with these criteria.
VENDOR_ID CFG:0x0000 MMR:0x0F00 MMR_1:0x0F00 IND:0x0F00
If I understand that, does that mean I should be able to access it from 0xCF00 using ports, or 0xFC00_0F00 using memory mapped i/o? The value I'm looking for is 0x1002. When I try to read it using a port, I just get 0xFFFF returned, which I assume is what qemu returns if something about that read wasn't valid.

When I try to read the value using the memory base at 0xFC00_0F00 I get a page fault exception because I don't have a page mapped. I tried mapping a page for that address range and was able to successfully read the memory location, but it didn't give the correct result.

I'm not really sure where to look next for debugging this since this is my first time trying to read from a pci device. I think I'm probably doing something wrong in my calculations, or maybe my understanding of how the base addresses need to be used is incorrect. Any help, or suggestions, or if you notice something obvious I'm doing wrong would be greatly appreciated.
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

Re: PCI Access Clarification

Post by Klakap »

Please show your code, otherwise is hard to say what's wrong.

But one idea, if you writing PCI driver, try to connect intel hd audio card to QEMU(-soundhw hda) and search for device with class 0x4, subclass 0x3 and progif 0x0. If you will found it, try to parse value from BAR0. It should be 0xFEBF0000.
nullplan
Member
Member
Posts: 1794
Joined: Wed Aug 30, 2017 8:24 am

Re: PCI Access Clarification

Post by nullplan »

rkennedy9064 wrote:When I try to read the value using the memory base at 0xFC00_0F00 I get a page fault exception because I don't have a page mapped.
Well there's your problem. You think those addresses are virtual when they are physical. You have to map the entire range without caching to be able to use it. All addresses you send to hardware outside the CPU must necessarily be physical addresses.
rkennedy9064 wrote: I tried mapping a page for that address range and was able to successfully read the memory location, but it didn't give the correct result.
What did you map and where? And what was the result?
Carpe diem!
rkennedy9064
Member
Member
Posts: 36
Joined: Wed Sep 01, 2010 3:54 pm

Re: PCI Access Clarification

Post by rkennedy9064 »

This is what I have so far, along with the output produced.

Code: Select all

if let Some(pci_device) = PciDevice::find_device(0x1002) {
    let memory_base_address = pci_device.base_address_0 & 0xFC00_0000;
    let io_base_address = pci_device.base_address_1 & 0xFFFF_FF00;
    let register_base_address = pci_device.base_address_2 & 0xFFFF_C000;
    println!("memory base = {:X}", memory_base_address);
    println!("io base = {:X}", io_base_address);
    println!("register base = {:X}", register_base_address);

    let virtual_address = VirtAddr::new(0xD000_0F00);
    let page = Page::containing_address(VirtAddr::new(0xD000_0F00));
    memory::create_example_mapping(
        (memory_base_address + 0x0F00) as u64,
        page,
        &mut mapper,
        &mut frame_allocator,
    );

    let physical_address = mapper.translate_addr(virtual_address);
    println!("{:?} -> {:?}", virtual_address, physical_address.unwrap());

    unsafe {
        let port_address = 0xCF00;
        let mut port: Port<u16> = Port::new(port_address);
        let port_value = port.read();
        let memory_address = 0xD000_0F00 as *mut u16;
        let memory_value = memory_address.read_volatile();
        println!("memory address {:p} = {:X}", memory_address, memory_value);
        println!("port address {:X} = {:X}", port_address, port_value);
    }
}

// Output
memory base = 0xFC00_0000
io base = 0xC00
register base = 0xFEBF_0000

VirtAddr(0xD000_0F00) -> PhysAddr(0xFC00_0F00)
memory address 0xD000_0F00 = 0
port address 0xCf00 = FFFF
Octocontrabass
Member
Member
Posts: 5578
Joined: Mon Mar 25, 2013 7:01 pm

Re: PCI Access Clarification

Post by Octocontrabass »

rkennedy9064 wrote:
VENDOR_ID CFG:0x0000 MMR:0x0F00 MMR_1:0x0F00 IND:0x0F00
If I understand that, does that mean I should be able to access it from 0xCF00 using ports, or 0xFC00_0F00 using memory mapped i/o?
It says you may access it at offset 0xF00 from the register aperture base as memory-mapped I/O (physical address 0xFEBFF00), or at index 0xF00 using the MM_INDEX and MM_DATA I/O port pair (write dword 0xF00 to port 0xC000, read a dword from port 0xC004).

It also says you can access it at offset 0 within the PCI configuration space, but you've already got that part working.
rkennedy9064
Member
Member
Posts: 36
Joined: Wed Sep 01, 2010 3:54 pm

Re: PCI Access Clarification

Post by rkennedy9064 »

Octocontrabass wrote:
rkennedy9064 wrote:
VENDOR_ID CFG:0x0000 MMR:0x0F00 MMR_1:0x0F00 IND:0x0F00
If I understand that, does that mean I should be able to access it from 0xCF00 using ports, or 0xFC00_0F00 using memory mapped i/o?
It says you may access it at offset 0xF00 from the register aperture base as memory-mapped I/O (physical address 0xFEBFF00), or at index 0xF00 using the MM_INDEX and MM_DATA I/O port pair (write dword 0xF00 to port 0xC000, read a dword from port 0xC004).

It also says you can access it at offset 0 within the PCI configuration space, but you've already got that part working.
Well I feel dumb. I just tried writing 0x0F00 to 0xC000 and then read 0xC004 and got the expected result. I also changed my page to use 0xFEBFF00, read the value and got the expected result.

I thought IND:0x0F00 meant the port was 0xC000 + 0x0F00. If you don't mind, where did you come up with 0xC004 for the data port number? Is that just a convention?

As for the memory i/o, I assumed the memory location they labeled as the memory aperture base address would be used for memory i/o, but register aperture base address makes sense for register access. Would that make the memory aperture base address be the location that the actual memory of the device has mapped?
Octocontrabass
Member
Member
Posts: 5578
Joined: Mon Mar 25, 2013 7:01 pm

Re: PCI Access Clarification

Post by Octocontrabass »

rkennedy9064 wrote:I thought IND:0x0F00 meant the port was 0xC000 + 0x0F00. If you don't mind, where did you come up with 0xC004 for the data port number? Is that just a convention?
It's in the manual, although they do a terrible job of explaining how to use it. Registers listed with "IND" may be accessed indirectly using the MM_INDEX/MM_DATA pair.
MM_INDEX MMR: 00, MMR_1: 00, IOR: 00
MM_DATA MMR: 04, MMR_1: 04, IOR: 04
Registers listed with "IOR" may be accessed directly from the specified offset from the I/O base.
rkennedy9064 wrote:As for the memory i/o, I assumed the memory location they labeled as the memory aperture base address would be used for memory i/o, but register aperture base address makes sense for register access. Would that make the memory aperture base address be the location that the actual memory of the device has mapped?
Correct. (And it can also be accessed indirectly through MM_INDEX/MM_DATA.)
Post Reply