Re: Bug in Intel PCI support(repost)

Eric Valette (valette@crf.canon.fr)
Mon, 29 Dec 1997 16:23:40 +0100 (CET)


>>>>> "Benjamin" == Benjamin Redelings <bredelin@ucsd.edu> writes:

Uwe> Probably nobody will object a patch :-)
>>
>> If I had to design a patch for linux PCI support, I would probably
>> completely redesign the code because :
>>
Benjamin> [snip]
>>
>> I have already done this work to implement PCI support for the Chorus kernel
>> starting from Linux code so I think its feasible :-)
>>
>> I have a document describing the API and code mail me if you are really
>> interested.

Benjamin> It would be really nice if you could post this to the
Benjamin> linux-kernel@vger.rutgers.edu mailing list. That is where most kernel
Benjamin> development goes on. Although I don't know much about bus
Benjamin> specifications, I think this is a COOL idea !

>>
>> Nevertheless, I love Linux each day more...
>>
Benjamin> Me too :)

Benjamin> -BenRI

Extract of the document I wrote regarding ading PCI support for Chorus

-----------------------------------------------------------------

3 Interface for PCI support

3.1 API definition

>From the previous chapter we have identified 3 requirements for PCI support :

1. The need to retrieve the whole PCI configuration,
2. The need to retrieve a single PCI slot configuration,
3. The need to modify a single PCI slot configuration,

Therefore I have added three system calls :

1. int sysGetPciConf (const unsigned int max, pci_slot* table).

This system call enables to retrieve a complete PCI configuration. The inputs
parameters are a table where the various PCI slots information will be
updated by the system as well as the number of entries of the table itself.
The system call returns the number of PCI slots detected or K_ENOTIMP. If the
number of tables entries provided by the user is less than the number of PCI
devices actually detected, only max entries in the table will be updated.

2. int sysGetPciSlot (const pci_dev_id pcidev, pci_slot* slot).

This system call enable to retrieve a single PCI entry. This entry is identified
by a couple device_id, vendor_id stored into a single 32 bits word (pcidev)
because anyway a device driver must know these particular values for the device
it handles in order to identify it.The system returns either K_OK, K_EINVAL or
K_ENOTIMP. In case several device have the same (picdev) value, the previous
system call can be used to find the right one.

3. int sysSetPciSlot (const pci_dev_id pcidev, pci_slot* slot)
This system call enable to modify a single PCI entry. This entry is identified
the same way as in the previous system call. The reason why the parameter slot
is not a pointer to a constant type is because not all devices allow to change
the configuration register value. Thus the parameter slot is an in/out parameter.
The set of values really stored is returned. I may be different than the
requested values if some registers are read only. The system returns either K_OK,
K_EINVAL or K_ENOTIMP,

The definition of the structures mentionned in the system calls above are :

typedef struct {

unsigned int device_id : 16;

unsigned int vendor_id : 16;

}pci_dev_id;


typedef struct {

unsigned int device_id : 16;

unsigned int vendor_id : 16;

unsigned int status_reg : 16;

unsigned int command_reg : 16;

unsigned int class_code : 24;

unsigned int rev_id : 8;

unsigned int bist : 8;

unsigned int header_type : 8;

unsigned int latency_timer : 8;

unsigned int cache_line_size : 8;

unsigned int base_addr_0 : 32;

unsigned int base_addr_1 : 32;

unsigned int base_addr_2 : 32;

unsigned int base_addr_3 : 32;

unsigned int base_addr_4 : 32;

unsigned int base_addr_5 : 32;

unsigned int cardBus_cis_ptr : 32;

unsigned int subsys_id : 16;

unsigned int subsys_vendor_id : 16;

unsigned int exp_rom_base_addr : 32;

unsigned int : 32;

unsigned int : 32;

unsigned int max_lat : 8;

unsigned int min_gnt : 8;

unsigned int intr_pin : 8;

unsigned int intr_line : 8;

}pci_conf_header;


typedef struct {

unsigned int device_id : 16;

unsigned int vendor_id : 16;

unsigned int status_reg : 16;

unsigned int command_reg : 16;

unsigned int class_code : 24;

unsigned int rev_id : 8;

unsigned int bist : 8;

unsigned int header_type : 8;

unsigned int latency_timer : 8;

unsigned int cache_line_size : 8;

unsigned int base_addr_0 : 32;

unsigned int base_addr_1 : 32;

unsigned int secondary_latency_timer : 8;

unsigned int subordinate_bus_number : 8;

unsigned int secondary__bus_number : 8;

unsigned int primary_bus_number : 8;

unsigned int secondary_status : 16;

unsigned int io_limit : 8;

unsigned int io_base : 8;

unsigned int memory_limit : 16;

unsigned int memory_base : 16;

unsigned int prefetch_memory_limit : 16;

unsigned int prefetch_memory_base : 16;

unsigned int prefetch_base_upper_32 : 32;

unsigned int prefetch_limit_upper_32 : 32;

unsigned int io_limit_upper_16 : 16;

unsigned int io_base_upper_16 : 16;

unsigned int : 32;

unsigned int exp_rom_base_addr : 32;

unsigned int bridge_control : 16;

unsigned int intr_pin : 8;

unsigned int intr_line : 8;

}pci_bridge_header;


typedef struct {

unsigned int : 24;

unsigned int slot : 5;

unsigned int function : 3;

}device_func;


typedef union {

device_func dvf;

unsigned int word;

}dev_and_func;



typedef struct {

unsigned int : 24;

unsigned int bus_id : 8;

}bus_ident;


typedef struct {

union {

pci_conf_header device;

pci_bridge_header bridge;

}dev_or_bridge;

dev_and_func access;

bus_ident bus;

unsigned int master :1;

unsigned int bridge :1;

}pci_slot;

In addition to these system calls, a library function has been added to the
utils.a Chorus library in order to convert the configuration space registers
into a printable ASCII character string so that the information returned by
the previous system calls can be printed in an human readable form similar
to what is obtained on Linux by the cat /proc/pci command. The interface for
this library function is :

int pci_dev_config_to_ascii(pci_slot *dev, char *buf, int size).

The return value is either the size of the resulting string stored
at address buf or 0 indicating that this value is bigger than the input
parameter size.

The reasons why this function is in library rather than a system call like the
previous one are :

This functionality is not needed by device drivers. It is currently used
only in a PCI demo program I wrote to check the implementation of the system calls
(see next paragraph). The amount of data used by the implementation of this function
is quite huge,

The program mentionned above could be improved to be a tutorial showing how
a PCI device driver should be written and in particular how to access a PCI device
via memory mapped IO (using the svPhysMap(2) system call).

3.2 Example of API usage

The followoing program gives a result similar to the command
cat /proc/pci on a linux system :

#include "sched/chSched.h"

#include <mem/chMem.h>

#include <util/pci.h>

#include <pci/chPci.h>

#include <stdio.h>

#include <stdlib.h>

#include <assert.h>


main()

{

KnRgnDesc region;

int res;

int i;

pci_slot* slotPtr;

VmSize pageSize;

char* printBuf;

unsigned pciSlotNb;


pageSize = vmPageSize();

region.size = 2 * pageSize; /* first page for PCI slots, second for print buffer */

region.options = K_ANYWHERE | K_WRITABLE | K_SUPERVISOR;

res = rgnAllocate(K_MYACTOR, &region);

assert(res == 0);


pciSlotNb = pageSize / sizeof (pci_slot);

slotPtr = (pci_slot*) region.startAddr;

printBuf = (char*) region.startAddr + pageSize;

res = sysGetPciConf(pciSlotNb, slotPtr);

assert ((res >= 0) && (res <= pciSlotNb));

if (res < 0) {

printf("PCI service not available!. Check if PCI support feature is on\n");

exit(1);

}

for (i = 0; i < res; i++) {

pci_dev_config_to_ascii(&slotPtr[i], printBuf, pageSize);

printf("%s\n", printBuf);

}


exit(0);

}

The result of running this program on my machine while it runs ClassiX r3.1 is :

Bus 0, device 11, function 0:

VGA compatible controller: Matrox Millennium (rev 1).

Medium devsel. Fast back-to-back capable. IRQ 11.

Non-prefetchable 32 bit memory at 0xe5800000.

Prefetchable 32 bit memory at 0xe7000000.

Bus 0, device 10, function 0:

SCSI storage controller: Adaptec AIC-7871 (rev 0).

Medium devsel. Fast back-to-back capable. IRQ 10. Master Capable. Latency=32. Min Gnt=8.Max Lat=8.

I/O at 0xe000.

Non-prefetchable 32 bit memory at 0xe6000000.

Bus 0, device 7, function 1:

IDE interface: Intel 82371SB Natoma/Triton II PIIX3 (rev 0).

Medium devsel. Fast back-to-back capable. Master Capable. Latency=32.

I/O at 0xe800.

Bus 0, device 7, function 0:

ISA bridge: Intel 82371SB Natoma/Triton II PIIX3 (rev 1).

Medium devsel. Fast back-to-back capable. Master Capable. No bursts.

Bus 0, device 0, function 0:

Host bridge: Intel 82439HX Triton II (rev 3).

Medium devsel. Master Capable. Latency=32.

Here is another program that could be used to search a particular device :

#include "sched/chSched.h"

#include <mem/chMem.h>

#include <util/pci.h>

#include <pci/chPci.h>

#include <stdio.h>

#include <stdlib.h>

#include <assert.h>


main()

{

pci_dev_id dev;

pci_slot slot;


dev.vendor_id = PCI_VENDOR_ID_ADAPTEC;

dev.device_id = PCI_DEVICE_ID_ADAPTEC_7871;


res = sysGetPciSlot(dev, &slot);

if (res < 0) {

printf("ADAPTEC 7871 SCSI/PCI device not found\n");

exit(1);

}

pci_dev_config_to_ascii(&slot, printBuf, pageSize);

printf("%s\n", printBuf);


exit(0);

}

And the output is :

Bus 0, device 10, function 0:

SCSI storage controller: Adaptec AIC-7871 (rev 0).

Medium devsel. Fast back-to-back capable. IRQ 10. Master Capable. Latency=32. Min Gnt=8.Max Lat=8.

I/O at 0xe000.

Non-prefetchable 32 bit memory at 0xe6000000.

-- 
   __                 
  /  `                   	Eric Valette
 /--   __  o _.          	Canon CRF
(___, / (_(_(__         	Rue de la touche lambert
				35517 Cesson-Sevigne  Cedex

Tel: +33 (0)2 99 87 68 91 Fax: +33 (0)2 99 84 11 30 E-mail: valette@crf.canon.fr