Un、Initialisation E1000
1.fonction_principale
void fonction_principale() {
...
#ifdef LAB_RESEAU
initialisation_pci();
initialisation_socket();
#endif
...
}
2.initialisation_pci
void initialisation_pci() {
// Les registres e1000 sont mappés à cette adresse.
// vm.c mappe cette plage.
uint64 registres_e1000 = 0x40000000L;
// qemu -machine virt place l'espace de configuration PCIe ici.
// vm.c mappe cette plage.
uint32 *ecam = (uint32 *) 0x30000000L;
// Examine chaque périphérique PCI possible sur le bus 0.
for(int periph = 0; periph < 32; periph++){
int bus = 0;
int fonction = 0;
int decalage = 0;
uint32 off = (bus << 16) | (periph << 11) | (fonction << 8) | (decalage);
volatile uint32 *base = ecam + off;
uint32 id = base[0];
// 100e:8086 est un e1000
if(id == 0x100e8086){
// Registre de commande et de statut.
// bit 0 : Activation de l'accès E/S
// bit 1 : Activation de l'accès mémoire
// bit 2 : Activation du maître
base[1] = 7;
__sync_synchronize();
for(int i = 0; i < 6; i++){
uint32 ancien = base[4+i];
// Écrire tous les 1 dans le BAR provoque son
// remplacement par sa taille.
base[4+i] = 0xffffffff;
__sync_synchronize();
base[4+i] = ancien;
}
// Indique à l'e1000 de révéler ses registres à
// l'adresse physique 0x40000000.
base[4+0] = registres_e1000;
initialisation_e1000((uint32*)registres_e1000);
}
}
}
Selon memlayout.h, la disposition mémoire de xv6 est déterminée par hw/riscv/virt.c, en examinant le code pertinent
https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c, on trouve les définitions suivantes
static const MemMapEntry memoire_virtuelle[] = {
...
[VIRT_PCIE_ECAM] = { 0x30000000, 0x10000000 },
[VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 },
[VIRT_DRAM] = { 0x80000000, 0x0 },
};
Les adresses 0x30000000 et 0x40000000 correspondent respectivement à PCIE_ECAM et PCIE_MMIO
PCIE_ECAM
ECAM (PCI Express Configuration Access Mechanism) est un mécanisme permettant d'accéder à l'espace de configuration PCIe, mappant les registres de configuration des périphériques PCIe en mémoire, permettant au logiciel d'accéder à ces registres comme s'il s'agissait de mémoire ordinaire.
PCIE_MMIO
MMIO (Memory-Mapped I/O), mappant les registres dans l'espace d'adresse E/S des périphériques PCIe en mémoire. L'accès à ces registres de périphérique peut être réalisé en lisant et écrivant des adresses mémoire, permettant le contrôle et l'échange de données avec les périphériques.
Espace de configuration PCIe
https://en.wikipedia.org/wiki/PCI_configuration_space
Il s'agit des registres de contrôle des périphériques, permettant de contrôler les périphériques PCIe en manipulant ces registres.
Chaque registre de contrôle a une taille de 32 bits
Note : Les registres de contrôle mentionnés ici sont ceux du périphérique, pas ceux de la carte mère.
La configuration pertinente de l'E1000 PCIe est décrite dans le chapitre 4 Interface Bus PCI Local
Explication du code
Le pointeur base indique vers les registres de contrôle
- base[0] stocke l'ID Fournisseur et l'ID Périphérique, selon le manuel E1000 (page 95), les deux valeurs pour le
82540EMsont respectivement 8086 et 100e