Hi,
Ryu wrote:In the PCI 2.2 specifications, I am a bit confused how would one determine the range of either I/O or mapped addresses of the device? I understand that there is base address registers at register windows 10h, 14h, 18h, 1Ch, 20h however nothing there that gives me the range. My goal is to be able to map all PCI I/O or memory address spaces without relying on BIOS interrupt services. If someone could shed some light, thanks
If you write 0xFFFFFFFF (or 0xFFFFFFFFFFFFFFFF for 64 bit) to the BARs then the hardware will zero out some of the lower bits. The number of bits that it sets to zero will tell you the range of the area.
For example:
Code: Select all
unsigned int BAR_offset;
void some_PCI_init_loop(bus, device, function) {
u64_int size;
BAR_offset = 0x10;
do {
size = get_BAR_size(bus, device, function);
} while (BAR_offset < 0x28);
}
u64_int get_BAR_size(bus, device, function) {
u64_int BAR;
u64_int temp_BAR;
u64_int size;
BAR = PCI_read_32(bus, device, function, BAR_offset);
if(BAR = 0) {
// BAR not implemented
size = 0;
BAR_offset += 4 // Set offset of next BAR (if any)
} else {
if ( (BAR & 1) == 0) {
// Memory
type = (BAR >> 1) & 0x03;
if( (type & 2) == 0) {
// 32 bit Memory
temp_BAR = BAR;
disable_ints();
PCI_write_32(bus, device, function, BAR_offset, 0xFFFFFFFF);
BAR = PCI_read_32(bus, device, function, BAR_offset);
BAR = BAR & 0xFFFFFFF0; // Clear flags
size = (BAR ^ 0xFFFFFFFF) + 1;
PCI_write_32(bus, device, function, BAR_offset, temp_BAR);
enable_ints();
printf("PCI BAR %d:%d:%d offset %d = %d bytes (32 bit)\n", bus, device, function, BAR_offset, size);
BAR_offset += 4 // Set offset of next BAR (if any)
} else {
// 64 bit Memory
temp_BAR = PCI_read_64(bus, device, function, BAR_offset);
disable_ints();
PCI_write_64(bus, device, function, BAR_offset, 0xFFFFFFFFFFFFFFFF);
BAR = PCI_read_64(bus, device, function, BAR_offset);
BAR = BAR & 0xFFFFFFFFFFFFFFF0; // Clear flags
size = (BAR ^ 0xFFFFFFFFFFFFFFFF) + 1;
PCI_write_64(bus, device, function, BAR_offset, temp_BAR);
enable_ints();
printf("PCI BAR %d:%d:%d offset %d = %d bytes (64 bit)\n", bus, device, function, BAR_offset, size);
BAR_offset += 8 // Set offset of next BAR (if any)
}
} else {
// IO space
temp_BAR = BAR;
disable_ints();
PCI_write_32(bus, device, function, BAR_offset, 0xFFFFFFFF);
BAR = PCI_read_32(bus, device, function, BAR_offset);
BAR = BAR & 0xFFFFFFFC; // Clear flags
size = (BAR ^ 0xFFFFFFFF) + 1;
PCI_write_32(bus, device, function, BAR_offset, temp_BAR);
enable_ints();
printf("PCI BAR %d:%d:%d offset %d = %d I/O ports\n", bus, device, function, BAR_offset, size);
BAR_offset += 4 // Set offset of next BAR (if any)
}
}
return size;
}
Ryu wrote:Actually I have another question, I've just made a simple test function that enumarates all devices. This is done by checking if vendor and manufactor id is not 0FFFFh, however my question is I get some devices that are the same but differnt function numbers but the base addesses are the same, in fact the entire configuration registers are the same. Is it safe to mark these to be same devices?
No - if your code doesn't have bugs then these would be seperate devices. For example, bus 0, device 3 might be a multi-function device, where function 0 is the primary ATA/ATAPI controller and function 1 is the secondary ATA/ATAPI controller.
I'm wondering if you code has bugs though - does it match the information you get from other OSs, or does it match your hardware?
Ryu wrote:Ex. 1) bus 0, device 0, function 0
Ex. 2) bus 0, device 0, function 1
Ex. 3) bus 0, device 0, function 2
Also note that bus 0, device 0 is the PCI host bridge. If this is a multi-function device then you've got multiple PCI host bridges with seperate PCI buses behind each of them...
Cheers,
Brendan