0x01 Aperçu général du bus PCI. 0x02 Communication avec le bus. 0x03 Former une requête. 0x04 Enumération des périphériques. 0x05 Récupération des BARs. 0x06 Le framebuffer.
0×01 Aperçu général du bus PCI:
Le Peripheral Component Interconnect (PCI) sert à ajouter des cartes
d’extension sur la carte mère. Il rend possible la communication des
cartes entre elles et l’écriture en mémoire sans passer par le processeur.
Chaque carte est un dispositif plug and play, il se charge automatiquement.
En effet celle ci peut demander des domaines de port I/O et aussi de la
mémoire RAM, le BIOS inspecte la configuration du bus et traite les requêtes
des divers périphériques connectés.
0×02 Communication avec le bus:
Pour communiquer avec le bus PCI il faut utiliser deux ports I/O:
- le premier est le CONFIG_ADDRESS (0xCF8)
- le second est le CONFIG_DATA (0xCFC)
Tout d’abord il faut formuler une requête sur 4 octets et envoyer celle-ci
sur le port 0xCF8 ensuite le bus va transférer la réponse à notre requête
sur le port 0xCFC.
0×03 Former une requête:
Tout d’abord intéressons nous au format de celle-ci:
+------+--------+-----+------+--------+--------+---+ | 31| 30-24|23-16| 15-11| 10-8| 7-2|1-0| +------+--------+-----+------+--------+--------+---+ |Enable|Reserved| Bus|Device|Function|Register| 00| +------+--------+-----+------+--------+--------+---+
Une fois ce format connu il suffit de faire une fonction qui prend touts
ces paramètres et qui construit notre requête.
Cette fonction a été trouvée sur osdev et a ete adaptée pour mon driver.
VOID PCISendConfig (unsigned short bus, unsigned short slot,
unsigned short func, unsigned short offset)
{
unsigned long address;
unsigned long lbus = (unsigned long)bus;
unsigned long lslot = (unsigned long)slot;
unsigned long lfunc = (unsigned long)func;
/* create configuration address */
address = ((lbus << 16) | (lslot << 11) |(lfunc << 8 ) | (offset & 0xfc) |
((UINT32)0x80000000));
/* write out the address */
WRITE_PORT_ULONG(((PULONG)0xCF8), address);
}
0×04 Enumération des périphériques:
Nous avons vu comment envoyer une requette au bus PCI maintenant regardons
les informations qu’il peut nous renvoyer.
+----------+------------+------------+-------------+---------------+ | register | bits 31-24 | bits 23-16 | bits 15-8 | bits 7-0 | +----------+------------+------------+-------------+---------------+ | 00 | Device ID | Vendor ID | +----------+-------------------------+-----------------------------+ | 04 | Status | Command | +----------+------------+------------+-----------------------------+ | 08 | Class code | Subclass | Revision ID | +----------+------------+------------+-------------+---------------+ | 0C | BIST | Header type|Latency Timer|Cache Line Size| +----------+------------+------------+-------------+---------------+ | 10 |Base address #0 (BAR0) | +----------+-------------------------------------------------------+ | 14 |Base address #1 (BAR1) | +----------+-------------------------------------------------------+ | 18 |Base address #2 (BAR2) | +----------+-------------------------------------------------------+ | 1C |Base address #3 (BAR3) | +----------+-------------------------------------------------------+ | 20 |Base address #4 (BAR4) | +----------+-------------------------------------------------------+ | 24 |Base address #5 (BAR5) | +----------+-------------------------------------------------------+ | 28 |Cardbus CIS Pointer | +----------+-------------------------+-----------------------------+ | 2C | Subsystem ID | Subsystem Vendor ID | +----------+-------------------------+-----------------------------+ | 30 |Expansion ROM base address | +----------+-------------------------------------------------------+ | 34 |Reserved | +----------+-------------------------------------------------------+ | 38 |Reserved | +----------+------------+------------+-------------+---------------+ | 3C |Max latency | Min Grant |Interrupt PIN| Interrupt Line| +----------+------------+------------+-------------+---------------+
Maintenant nous voulons récupérer le framebuffer qui est un des 6 Base Address
de notre carte graphique branchée sur le bus PCI. Il nous faut dans un premier
temps énumérer les différents périphériques sur le bus PCI…
Pour ce faire nous allons faire 2 boucles qui incrémenteront le bus et le slot,
on récupérera le Vendor ID et on vérifie sa validité puis on récupère le
Class code, si celui-ci est égal a 03 c’est qu’on a affaire a une carte vidéo.
Nous allons parcourir 32 slots par bus et ceci sur 255 bus.
Donc les paramètres seront le bus, le slot, function sera nul et offset sera
égal à 2 car il correspond au registre Vendor ID.
#define Display_controller 0x03
#define MAX_PCI_BUS 255
#define MAX_PCI_SLOT 32
for(bus = 0; bus < MAX_PCI_BUS; bus++)
{
for(slot=0;slot < MAX_PCI_SLOT; slot++)
{
PCISendConfig(bus,slot,0,2); //to get vendor ID
if(0xFFFF != PCIGetConfigWord()) //check valid vendor ID
{
PCISendConfig(bus,slot,0,8); //to get Class Code
if(Display_controller == (PCIGetConfigLong()>>0×18))
{
// Get BARs
}
}
}
}
0×05 Récupération des BARs:
Maintenant il ne nous reste plus qu’a récupérer les BARs ainsi que leur
taille puis nous prendrons le plus grand, celui a le plus de chances d’être
notre framebuffer.
Cependant il est impératif d’en connaitre un peu plus sur ces adresses.
Le BAR est une adresse physique, pour pouvoir y accéder il nous faudra la
convertir en une adresse virtuelle.
Pour récupérer la taille il faut écrire dans le BAR 0xffffffff ensuite on
lit ce même BAR et on récupère la taille qu’il faudra convertir en nombre non
signé.
//Get bars PCISendConfig(bus,slot,0,0x10); //to get BAR#0 pbar_regs->BAR0 = PCIGetConfigLong(); ... //Get bar size PCISendConfig(bus,slot,0,0x10); //to get sz BAR#0 WRITE_PORT_ULONG(((PULONG)0xCFC), 0xffffffff); pbar_regs->szBAR0 = PCIGetConfigLong(); pbar_regs->szBAR0 = ConvertBARsz(pbar_regs->szBAR0); ... //restore bars PCISendConfig(bus,slot,0,0x10); //to get BAR#0 WRITE_PORT_ULONG(((PULONG)0xCFC),pbar_regs->BAR0);
0×06 Le framebuffer:
Voila nous arrivons à la fin de notre parcours il nous faut « convertir »
l’adresse physique du BAR en une adresse virtuelle pour ce faire nous allons
utiliser l’api MmMapIoSpace.
PHYSICAL_ADDRESS bigestBAR; unsigned long szbigestBAR; unsigned long framebuffer; ... framebuffer = MmMapIoSpace(bigestBAR,szbigestBAR,FALSE);
Il nous suffit juste d’écrire à l’adresse retournée pour afficher ce qu’on
veut. (Bien sûr, avant il vous faudra connaitre la résolution de l’écran et
le mode)
Post a Comment