PCI BIOS call
PCI BIOS call
Is it absolutly required to call the PCI BIOS (the 'BSD') in order to detect PCI devices using port 0xCF8 ?
The problem is that I don't detect any PCI device if I don't call the BSD (and the call is still buggy because the cpu tells me "wrong opcode" when doing it)
The problem is that I don't detect any PCI device if I don't call the BSD (and the call is still buggy because the cpu tells me "wrong opcode" when doing it)
Re:PCI BIOS call
Hi,
Cheers,
Brendan
No - you can manually detect which mechanism is supported and use it without ever using the PCI BIOS. IIRC manual detection won't tell you if the PCI bus supports "special cycles" though, but most OS's don't use special cycles anyway (and half the chipsets don't support it either).CyberP1708 wrote:Is it absolutly required to call the PCI BIOS (the 'BSD') in order to detect PCI devices using port 0xCF8 ?
Your code to call the PCI BIOS is buggy and your code for manual detection is buggy?CyberP1708 wrote:The problem is that I don't detect any PCI device if I don't call the BSD (and the call is still buggy because the cpu tells me "wrong opcode" when doing it)
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re:PCI BIOS call
Well I think so ;DYour code to call the PCI BIOS is buggy and your code for manual detection is buggy?
Here is my code because I think I am going to need some help:
Code: Select all
#define pci_vendor_id 0x0
#define pci_device_id 0x2
#define pci_class 0x9
#define pci_header_type 0xE
unsigned int pci_list_count;
void pci_detect() {
unsigned short bus, device;
unsigned int functions_count;
u8 header_type;
u16 vendor_id;
pci_list_count = 0;
for (bus = 0; bus < 0x10; ++bus) {
for (device = 0; device < 0x40; ++device) {
header_type = pci_read(bus, device, 0, pci_header_type);
vendor_id =pci_read(bus, device, 0, pci_vendor_id);
if (!vendor_id || vendor_id == 0xFFFF) continue;
functions_count = (header_type & 0x80) ? 8 : 1;
++pci_list_count;
// here is not finished but pci_list_count remains null at the end of the function
}
}
}
unsigned int pci_read(unsigned short bus, unsigned short device, unsigned short function, unsigned short reg) {
u32 toret;
u32 msg = 0x80000000 | ((bus & 0xFF) << 16) | ((device & 0x1F) << 11) | ((function & 0x07) << 8) | (reg & 0xFC);
u32 org = ind(0xCF8);
outd(0xCF8, msg);
toret = ind(0xCFC);
outd(0xCF8, org);
return toret;
}
edit: it is the code to detect the devices
I am going to get rid of the pci bios
Re:PCI BIOS call
Well I had the intelligence to test it on real hardware and it is working (18 devices detected)
So my last question is : why is it not working with bochs ?
In the configuration file I put this line :
So it should at least detect the ne2k
I'm not able to test it with VMWare because it freezes while GRUB is loading
So my last question is : why is it not working with bochs ?
In the configuration file I put this line :
Code: Select all
ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=linux, ethdev=eth0
I'm not able to test it with VMWare because it freezes while GRUB is loading
Re:PCI BIOS call
hi,
you have to activate PCI in bochs at compile time, that means you have to ./configure it with --enable-pci. Quoting from bochs documentation: "Enable limited i440FX PCI support. This is still incomplete, but usable."
you have to activate PCI in bochs at compile time, that means you have to ./configure it with --enable-pci. Quoting from bochs documentation: "Enable limited i440FX PCI support. This is still incomplete, but usable."
Re:PCI BIOS call
Hi,
The first problem is that "pci_read()" will always return a 32 bit value that is always aligned to the nearest 32 bits. When you try to read the "pci_device_id" you will actually get a 32 bit value where the Device ID is in bits 16 to 31.
The PCI BIOS solves this by providing functions to return a dword, word or byte (and doing the necessary bit shifting to make it work). You could do the same, but to be honest this is lame - the first thing you're doing is getting the Device ID and the Vendor ID which are in the same dword. Rather than reading the same dword from configuration space twice, it's better to read it once and do the bit shifting stuff afterwards.
For example:
Secondly, I'm not sure why your "pci_read()" function saves and restores the original value (I can't see why you'd need to). If you're worried about re-entrancy then use a re-entrancy lock (or for single CPU only, just disable interrupts).
None of this explains why "if (!vendor_id || vendor_id == 0xFFFF) continue;" doesn't work correctly, as vendor_id should still be correct (as it's truncated to 16 bit). Also, theoretically "vendor ID = 0x0000" is possible (but hasn't been assigned to a vendor yet). In any case you should be able to simplify this test.
This leaves something like:
This is a start. The next step is to see if works (fingers crossed), and then implement more.
[continued]
The first problem is that "pci_read()" will always return a 32 bit value that is always aligned to the nearest 32 bits. When you try to read the "pci_device_id" you will actually get a 32 bit value where the Device ID is in bits 16 to 31.
The PCI BIOS solves this by providing functions to return a dword, word or byte (and doing the necessary bit shifting to make it work). You could do the same, but to be honest this is lame - the first thing you're doing is getting the Device ID and the Vendor ID which are in the same dword. Rather than reading the same dword from configuration space twice, it's better to read it once and do the bit shifting stuff afterwards.
For example:
Code: Select all
temp = pci_read(bus, device, 0, 0);
vendor_id = temp & 0xFFFF;
device_id = (temp >> 16) & 0xFFFF;
None of this explains why "if (!vendor_id || vendor_id == 0xFFFF) continue;" doesn't work correctly, as vendor_id should still be correct (as it's truncated to 16 bit). Also, theoretically "vendor ID = 0x0000" is possible (but hasn't been assigned to a vendor yet). In any case you should be able to simplify this test.
This leaves something like:
Code: Select all
unsigned int pci_list_count;
void pci_detect() {
unsigned short bus, device;
unsigned int functions_count;
u32 temp;
u8 header_type;
u16 vendor_id;
pci_list_count = 0;
for (bus = 0; bus < 0x10; ++bus) {
for (device = 0; device < 0x40; ++device) {
temp = pci_read_dword(bus, device, 0, 0);
vendor_id = temp & 0xFFFF;
device_id = (temp >> 16) & 0xFFFF;
vendor_id =pci_read_dword(bus, device, 0, pci_vendor_id);
if (vendor_id != 0xFFFF) {
++pci_list_count;
// here is not finished...
}
}
}
}
unsigned int pci_read_dword(unsigned short bus, unsigned short device, unsigned short function, unsigned short reg) {
U32 result;
u32 msg = 0x80000000 | ((bus & 0xFF) << 16) | ((device & 0x1F) << 11) | ((function & 0x07) << 8) | (reg & 0xFC);
// Re-entrancy lock or "CLI" needed here
outd(0xCF8, msg);
result = ind(0xCFC);
// Unlock re-entrancy lock or "STI" needed here
return result;
}
This is a start. The next step is to see if works (fingers crossed), and then implement more.
[continued]
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re:PCI BIOS call
[continued]
Rather than scanning every possible bus number, you can detect if a "PCI to PCI Bridge" has been found and get the new bus number (for the bus on the other side of the bridge) from the PCI bridge's header.
Also, the first device (bus=0, device=0) should always be the "PCI host controller". This is normally a single function device (one PCI host controller), but systems with more than one PCI host controller are becoming more common. In this case the first device can be a multi-function device. For example, if the first device has 2 functions then you've got a system with dual PCI host controllers.
Taking this into account, I'd go something like:
The code above is an example only. It might actually work, but it hasn't been tested (I made it up form memory)...
Cheers,
Brendan
[Edit: Just a silly typo..]
Rather than scanning every possible bus number, you can detect if a "PCI to PCI Bridge" has been found and get the new bus number (for the bus on the other side of the bridge) from the PCI bridge's header.
Also, the first device (bus=0, device=0) should always be the "PCI host controller". This is normally a single function device (one PCI host controller), but systems with more than one PCI host controller are becoming more common. In this case the first device can be a multi-function device. For example, if the first device has 2 functions then you've got a system with dual PCI host controllers.
Taking this into account, I'd go something like:
Code: Select all
#define PCI_TYPE_BRIDGE 0x01
unsigned int pci_list_count;
void pci_detect() {
scan_PCI_bus(0);
}
void scan_PCI_bus(u8 bus);
u8 device;
u8 function;
u16 vendor_id;
u16 device_id;
u8 header_type;
u32 temp;
for (device = 0; device < 0x40; device++) {
temp = pci_read_dword(bus, device, 0, 0);
vendor_id = temp & 0xFFFF;
device_id = (temp >> 16) & 0xFFFF;
// Check if this device is valid
if(vendor_id != 0xFFFF) {
temp = pci_read_dword(bus, device, 0, 0x0C);
header_type = (temp >> 16) & 0x7F;
register_PCI(bus, device, 0, device_ID, vendor_id);
// Check if this device is a multi-function device
if( (temp & 0x00800000) != 0) {
for(function = 1; function < 0x08; function++) {
temp = pci_read_dword(bus, device, function, 0);
vendor_id = temp & 0xFFFF;
device_id = (temp >> 16) & 0xFFFF;
register_PCI(bus, device, function, device_ID, vendor_id);
}
}
}
}
}
void register_PCI(u8 bus, u8 device, u8 function, u16 device_ID, u16 vendor_id) {
u8 header_type;
u8 new_bus;
u32 temp;
printf("Found PCI device at %x:%x:%x, device ID: %x, vendor ID: %x\n",
bus, device, function,
device_id, vendor_id);
pci_list_count++;
// Remember this device exists(?)
// Check if it's a PCI bridge
if( (bus != 0) || (device != 0) || (function != 0) ) {
temp = pci_read_dword(bus, device, 0, 0x0C);
header_type = (temp >> 16) & 0x7F;
if(header_type == PCI_TYPE_BRIDGE) {
temp = pci_read_dword(bus, device, 0, 0x18);
new_bus = (temp >> 8) & 0xFF;
scan_PCI_bus(new_bus); // Recursion!!!
}
}
}
Cheers,
Brendan
[Edit: Just a silly typo..]
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:PCI BIOS call
No. The implementation of NE2000 in bochs is the ISA card, not the PCI clones. QEMU has PCI ne2000 though.CyberP1708 wrote: Well I had the intelligence to test it on real hardware and it is working (18 devices detected)
So my last question is : why is it not working with bochs ?
In the configuration file I put this line :So it should at least detect the ne2kCode: Select all
ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=linux, ethdev=eth0
Re:PCI BIOS call
Thank you very much for this code. It answers many questions I was asking myself.
I was not knowing why it was not working so I tried everything I saw
With Bochs on Windows it detects 4 pci devices
I'll try to figure out why it doesn't work on Linux
I did this because they do the same here: http://www.osdev.org/osfaq2/index.php/Where%20can%20I%20find%20programming%20info%20on%20PCI%3FSecondly, I'm not sure why your "pci_read()" function saves and restores the original value (I can't see why you'd need to). If you're worried about re-entrancy then use a re-entrancy lock (or for single CPU only, just disable interrupts).
I was not knowing why it was not working so I tried everything I saw
With Bochs on Windows it detects 4 pci devices
I'll try to figure out why it doesn't work on Linux