PCI: Difference between revisions

145 bytes added ,  29 days ago
m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
(→‎Base Address Registers: mention disabling IO and memory decode for BAR info reading)
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(4 intermediate revisions by 3 users not shown)
Line 41:
The following code segment illustrates the use of configuration mechanism #1 to read 16-bit fields from configuration space. Note that this segment, the outl(port, value) and inl(port) functions refer to the OUTL and INL Pentium assembly language instructions.
 
<sourcesyntaxhighlight lang="c">
uint16_t pciConfigReadWord(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
uint32_t address;
Line 60:
return tmp;
}
</syntaxhighlight>
</source>
 
When a configuration access attempts to select a device that does not exist, the host bridge will complete the access without error, dropping all data on writes and returning all ones on reads. The following code segment illustrates the read of a non-existent device.
 
<sourcesyntaxhighlight lang="c">
uint16_t pciCheckVendor(uint8_t bus, uint8_t slot) {
uint16_t vendor, device;
Line 74:
} return (vendor);
}
</syntaxhighlight>
</source>
 
=== Configuration Space Access Mechanism #2 ===
Line 1,018:
For all 3 methods, you need to be able to check if a specific device on a specific bus is present and if it is multi-function or not. Pseudo-code might look like this:
 
<sourcesyntaxhighlight lang="c">
void checkDevice(uint8_t bus, uint8_t device) {
uint8_t function = 0;
Line 1,038:
void checkFunction(uint8_t bus, uint8_t device, uint8_t function) {
}
</syntaxhighlight>
</source>
 
Please note that if you don't check bit 7 of the header type and scan all functions, then some single-function devices will report details for "function 0" for every function.
Line 1,046:
For the brute force method, the remaining code is relatively simple. Pseudo-code might look like this:
 
<sourcesyntaxhighlight lang="c">
void checkAllBuses(void) {
uint16_t bus;
Line 1,057:
}
}
</syntaxhighlight>
</source>
 
For this method, there are 32 devices per bus and 256 buses, so you call "checkDevice()" 8192 times.
Line 1,065:
The first step for the recursive scan is to implement a function that scans one bus. Pseudo-code might look like this:
 
<sourcesyntaxhighlight lang="c">
void checkBus(uint8_t bus) {
uint8_t device;
Line 1,073:
}
}
</syntaxhighlight>
</source>
 
The next step is to add code in "checkFunction()" that detects if the function is a PCI to PCI bridge. If the device is a PCI to PCI bridge then you want to extract the "secondary bus number" from the bridge's configuration space and call "checkBus()" with the number of the bus on the other side of the bridge.
Line 1,079:
Pseudo-code might look like this:
 
<sourcesyntaxhighlight lang="c">
void checkFunction(uint8_t bus, uint8_t device, uint8_t function) {
uint8_t baseClass;
Line 1,092:
}
}
</syntaxhighlight>
</source>
 
The final step is to handle systems with multiple PCI host controllers correctly. Start by checking if the device at bus 0, device 0 is a multi-function device. If it's not a multi-function device, then there is only one PCI host controller and bus 0, device 0, function 0 will be the PCI host controller responsible for bus 0. If it's a multi-function device, then bus 0, device 0, function 0 will be the PCI host controller responsible for bus 0; bus 0, device 0, function 1 will be the PCI host controller responsible for bus 1, etc (up to the number of functions supported).
Line 1,098:
Pseudo-code might look like this:
 
<sourcesyntaxhighlight lang="c">
void checkAllBuses(void) {
uint8_t function;
Line 1,116:
}
}
</syntaxhighlight>
</source>
 
=== Recursive Scan With Bus Configuration ===
Line 1,142:
If you're using the old [[PIC]], your life is really easy. You have the ''Interrupt Line'' field of the header, which is read/write (you can change it's value!) and it says which interrupt will the PCI device fire when it needs attention.
 
If you plan to use the [[I/O APIC]], things aren't so easy. Basically the PCI bus specifies that theirthere are 4 interrupt pins. They are labeled INTA#, INTB#, INTC#, and INTD#. You find out what pin a device is using by reading the ''Interrupt Pin'' field of the header. So far, so good.
 
The only problem is that the PCI pins correspond to an arbitrary I/O APIC pin. It's up to the programmer to find the mapping. How is that done? You must parse the [[MP]] Tables or the [[ACPI]] tables. The MP tables are easy, only they aren't supported on newer hardware. The ACPI tables, however, involve parsing AML, which is not an easy task. If one wants to take a shortcut, you can use [[ACPICA]].
Line 1,157:
=== Enabling MSI ===
First, check that the device has a pointer to the capabilities list (status register bit 4 set to 1).
Then, traverse the capabilities list. The low 8 bits of a capability register are the ID - <code>0x50x05</code> for MSI. The next 8 bits are the offset (in [[#Configuration Space|PCI Configuration Space]]) of the next capability.
 
The MSI capability is as follows:
Line 1,206:
 
The message address/data is architecture specific. On x86(-64), it is as follows:
<sourcesyntaxhighlight lang="c">
uint64_t arch_msi_address(uint64_t *data, size_t vector, uint32_t processor, uint8_t edgetrigger, uint8_t deassert) {
*data = (vector & 0xFF) | (edgetrigger == 1 ? 0 : (1 << 15)) | (deassert == 1 ? 0 : (1 << 14));
return (0xFEE00000 | (processor << 12));
}
</syntaxhighlight>
</source>
MSI interrupts seem to be invariably edge triggered high.