Page 1 of 1

inpl instruction???

Posted: Thu Nov 11, 2010 1:56 am
by sancho1980
hi

i am currently reading up on pci device enumeration and came across the wiki article http://wiki.osdev.org/PCI.
It mentions the OUTL and INPL instructions, and I cant find anywhere an explanation of these instructions
they sound like they read and write double word values from specific ports, but that interpretation doesnt really make sense with the function given as an example (pciConfigReadWord), because after reading from the port, the value read is cast to a word (unsigned short), so I am asking myself, why on earth would i first use a special instruction to read a double word and then cast that value to a word again?

Re: inpl instruction???

Posted: Thu Nov 11, 2010 3:17 am
by Brendan
Hi,
sancho1980 wrote:i am currently reading up on pci device enumeration and came across the wiki article http://wiki.osdev.org/PCI.
It mentions the OUTL and INPL instructions, and I cant find anywhere an explanation of these instructions
they sound like they read and write double word values from specific ports, but that interpretation doesnt really make sense with the function given as an example (pciConfigReadWord), because after reading from the port, the value read is cast to a word (unsigned short), so I am asking myself, why on earth would i first use a special instruction to read a double word and then cast that value to a word again?
For PCI configuration space, you can only read dwords. Therefore, if you really want to read a word instead, then you have to read a dword and discard half of it.

As an example, the wiki shows one function for reading one word. It could (but doesn't) show several similar functions, like this:

Code: Select all

unsigned long long pciConfigReadQword (unsigned short bus, unsigned short slot, unsigned short func, unsigned short offset);
unsigned int pciConfigReadDword (unsigned short bus, unsigned short slot, unsigned short func, unsigned short offset);
unsigned short pciConfigReadWord (unsigned short bus, unsigned short slot, unsigned short func, unsigned short offset);
unsigned char pciConfigReadByte (unsigned short bus, unsigned short slot, unsigned short func, unsigned short offset);
Of course if you implement "pciConfigReadDword()" first, then you can use it as the basis of the other functions. For example:

Code: Select all

unsigned short pciConfigReadWord (unsigned short bus, unsigned short slot, unsigned short func, unsigned short offset)
 {
    if(offset & 0x02) return (unsigned short) ( (pciConfigReadDword(bus, slot, func, offset & 0xFC) >> 16) & 0xFFFF);
    else return (unsigned short) (pciConfigReadDword(bus, slot, func, offset & 0xFC) & 0xFFFF);
 }
The alternative (which IMHO is probably better anyway) is to only provide the "pciConfigReadDword()" function. That way if the caller wants (for e.g.) the Device ID and the Vendor ID, then they don't make the mistake of using "pciConfigReadWord()" twice (and reading the same dword from PCI configuration space twice, when only one read is needed).

To be honest, I'd probably implement the abstraction more like this:

Code: Select all

typedef uint64_t PCI_address;

PCI_address getPCIaddress(uint8_t bus, uint8_t device, uint8_t func) {
    if(PCI_access_mechanism == PCI_CONF_MECH_1) {
        return ((PCI_function_ID)bus << 16) | ((PCI_function_ID)device << 11) | ((PCI_function_ID)func << 8) | ((PCI_function_ID)0x80000000);
    } else if(PCI_access_mechanism == PCI_CONF_MECH_2) {
        return /* code for PCI access mechanism #2 here */
    } else if(PCI_access_mechanism == PCI_CONF_MECH_EXTENDED) {
        return /* code for PCI express extended access mechanism here */
    } else {
        kernelPanic("Attempt to use unknown PCI access mechanism");
    }
};

uint32_t pciConfigReadDword(PCI_address address, uint16_t offset) {
    if( (offset & 0x0003) != 0) {
        kernelPanic("Attempt to use bad PCI configuration space offset");
    }
    if(PCI_access_mechanism == PCI_CONF_MECH_1) {
        sysOutLong (0xCF8, address | offset);
        return sysInLong (0xCFC);
    } else if(PCI_access_mechanism == PCI_CONF_MECH_2) {
        return /* code for PCI access mechanism #2 here */
    } else if(PCI_access_mechanism == PCI_CONF_MECH_EXTENDED) {
        return /* code for PCI express extended access mechanism here */
    } else {
        kernelPanic("Attempt to use unknown PCI access mechanism");
    }
}
Of course I'd also consider using function pointers to avoid those "if/else" statements (but they're ugly in C, so I didn't bother for the example above).


Cheers,

Brendan