inpl instruction???

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
sancho1980
Member
Member
Posts: 199
Joined: Fri Jul 13, 2007 6:37 am
Location: Stuttgart/Germany
Contact:

inpl instruction???

Post 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?
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: inpl instruction???

Post 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
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.
Post Reply