Un driver de périphérique ne choisit normalement pas arbitrairement les adresses, IRQ ou canaux DMA qu'il va utiliser. Sous Windows PnP, le bus driver décrit les ressources possibles, le PnP Manager arbitre les conflits, puis le driver reçoit les ressources qui lui ont été attribuées lors de IRP_MN_START_DEVICE.
Ce cours explique comment lire ces ressources et comment les transformer en accès matériels utilisables par un driver WDM.
1. Qu'est-ce qu'une ressource matérielle ?
Une ressource est une portion du système attribuée à un périphérique.
Les types les plus fréquents sont :
- CmResourceTypePort : plage de ports d'E/S.
- CmResourceTypeMemory : plage de mémoire physique, souvent utilisée pour du MMIO.
- CmResourceTypeInterrupt : ligne/vecteur d'interruption.
- CmResourceTypeDma : ressource DMA sur les architectures/périphériques qui l'utilisent.
- CmResourceTypeBusNumber : plage de numéros de bus.
- CmResourceTypeDevicePrivate : données privées propres à certains bus.
Lors de IRP_MN_START_DEVICE :
Code: Select all
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
PCM_RESOURCE_LIST raw =
stack->Parameters.StartDevice.AllocatedResources;
PCM_RESOURCE_LIST translated =
stack->Parameters.StartDevice.AllocatedResourcesTranslated;
- Raw resources : représentation relative au bus.
- Translated resources : représentation traduite par le HAL/PnP Manager dans l'espace système exploitable par le driver.
3. Structure CM_RESOURCE_LIST
La hiérarchie est :
Code: Select all
CM_RESOURCE_LIST
|
+-- CM_FULL_RESOURCE_DESCRIPTOR
|
+-- CM_PARTIAL_RESOURCE_LIST
|
+-- CM_PARTIAL_RESOURCE_DESCRIPTOR[0]
+-- CM_PARTIAL_RESOURCE_DESCRIPTOR[1]
+-- ...
Forme simplifiée :
Code: Select all
typedef struct _CM_RESOURCE_LIST
{
ULONG Count;
CM_FULL_RESOURCE_DESCRIPTOR List[1];
} CM_RESOURCE_LIST;
4. CM_PARTIAL_RESOURCE_DESCRIPTOR
Chaque ressource réelle est décrite par un CM_PARTIAL_RESOURCE_DESCRIPTOR.
Le champ Type détermine l'union à lire :
Code: Select all
switch (desc->Type)
{
case CmResourceTypeMemory:
// desc->u.Memory.Start
// desc->u.Memory.Length
break;
case CmResourceTypePort:
// desc->u.Port.Start
// desc->u.Port.Length
break;
case CmResourceTypeInterrupt:
// desc->u.Interrupt.Level
// desc->u.Interrupt.Vector
// desc->u.Interrupt.Affinity
break;
case CmResourceTypeDma:
// desc->u.Dma.Channel
// desc->u.Dma.Port
break;
}
5. Parcourir la liste de ressources
Exemple :
Code: Select all
VOID ParseResources(PCM_RESOURCE_LIST Resources)
{
if (Resources == NULL || Resources->Count == 0)
return;
PCM_FULL_RESOURCE_DESCRIPTOR full = &Resources->List[0];
PCM_PARTIAL_RESOURCE_LIST partial = &full->PartialResourceList;
for (ULONG i = 0; i < partial->Count; ++i)
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR d = &partial->PartialDescriptors[i];
switch (d->Type)
{
case CmResourceTypeMemory:
DbgPrint("MEM start=%llx length=%lu\n",
d->u.Memory.Start.QuadPart,
d->u.Memory.Length);
break;
case CmResourceTypePort:
DbgPrint("PORT start=%llx length=%lu\n",
d->u.Port.Start.QuadPart,
d->u.Port.Length);
break;
case CmResourceTypeInterrupt:
DbgPrint("IRQ vector=%lu level=%lu\n",
d->u.Interrupt.Vector,
d->u.Interrupt.Level);
break;
}
}
}
Beaucoup de périphériques modernes exposent leurs registres via une plage de mémoire physique.
Le CPU accède alors à des registres matériels comme à de la mémoire, mais cette mémoire n'est pas de la RAM normale.
Chaîne typique :
Code: Select all
CM_RESOURCE_DESCRIPTOR
|
Physical Address + Length
|
MmMapIoSpaceEx
|
Virtual Address kernel
|
READ_REGISTER_xxx / WRITE_REGISTER_xxx
Code: Select all
PHYSICAL_ADDRESS pa = desc->u.Memory.Start;
ULONG length = desc->u.Memory.Length;
PVOID regs = MmMapIoSpaceEx(
pa,
length,
PAGE_READWRITE | PAGE_NOCACHE
);
if (regs == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
Lors de STOP/REMOVE :
Code: Select all
MmUnmapIoSpace(regs, length);
7. Lire et écrire des registres MMIO
Pour les registres mémoire-mappés, utiliser les primitives prévues :
- READ_REGISTER_UCHAR
- READ_REGISTER_USHORT
- READ_REGISTER_ULONG
- READ_REGISTER_BUFFER_UCHAR/USHORT/ULONG
- WRITE_REGISTER_UCHAR
- WRITE_REGISTER_USHORT
- WRITE_REGISTER_ULONG
- WRITE_REGISTER_BUFFER_UCHAR/USHORT/ULONG
Code: Select all
volatile ULONG* control = (volatile ULONG*)((PUCHAR)regs + 0x20);
ULONG value = READ_REGISTER_ULONG(control);
WRITE_REGISTER_ULONG(control, value | 0x1);
8. Port I/O
Sur certaines architectures/périphériques, une ressource CmResourceTypePort correspond à un véritable espace de ports d'E/S.
Les primitives classiques sont :
- READ_PORT_UCHAR / USHORT / ULONG
- WRITE_PORT_UCHAR / USHORT / ULONG
- READ_PORT_BUFFER_*
- WRITE_PORT_BUFFER_*
Code: Select all
PUCHAR port = (PUCHAR)(ULONG_PTR)desc->u.Port.Start.QuadPart;
UCHAR status = READ_PORT_UCHAR(port);
WRITE_PORT_UCHAR(port, 0x01);
9. Interruptions
Une ressource CmResourceTypeInterrupt contient les informations nécessaires à la connexion de l'interruption.
Les champs peuvent inclure :
- Vector
- Level
- Affinity
- Flags indiquant notamment le mode/partage selon la ressource
IoConnectInterruptEx
Elle permet d'enregistrer une ISR en utilisant les informations d'interruption fournies par PnP.
La déconnexion se fait avec :
IoDisconnectInterruptEx
La logique détaillée ISR/DPC appartient au cours Interruptions/DPC.
10. Ressources raw et translated
Pourquoi deux listes ?
Un bus peut exprimer une adresse dans son propre espace. Le système traduit cette adresse vers une adresse exploitable au niveau CPU/système.
Exemple conceptuel :
Code: Select all
Adresse bus : 0x00002000
|
Bus driver / HAL / PnP
v
Adresse système : 0xF7800000
11. Requirements vs Assigned Resources
Il faut distinguer :
- Resource requirements : ce que le périphérique PEUT ou DOIT recevoir.
- Assigned resources : ce que le PnP Manager a réellement choisi.
Code: Select all
IO_RESOURCE_REQUIREMENTS_LIST
|
+-- IO_RESOURCE_LIST
|
+-- IO_RESOURCE_DESCRIPTOR[]
Code: Select all
CM_RESOURCE_LIST
|
+-- CM_PARTIAL_RESOURCE_DESCRIPTOR[]
12. IRP_MN_FILTER_RESOURCE_REQUIREMENTS
Cette requête est envoyée pour permettre au function driver d'ajuster les exigences de ressources avant l'allocation finale.
Le pointeur principal est retourné dans :
Code: Select all
Irp->IoStatus.Information
Le driver peut par exemple :
- Vérifier qu'une ressource requise existe.
- Restreindre une plage acceptable.
- Ajouter/modifier une alternative lorsque le bus le permet.
Important : l'ordre des ressources et les contraintes du bus doivent être respectés. Modifier arbitrairement une requirements list peut rendre l'allocation impossible ou incohérente.
13. Cycle de vie d'un mapping hardware
Un mapping n'est pas permanent.
Séquence correcte :
Code: Select all
IRP_MN_START_DEVICE
|
+--> lire ressources
+--> MmMapIoSpaceEx
+--> configurer hardware
|
v
I/O normales
|
STOP / REMOVE / SURPRISE REMOVAL
|
+--> arrêter hardware
+--> déconnecter interruptions
+--> MmUnmapIoSpace
14. API et structures essentielles
- CM_RESOURCE_LIST : liste des ressources attribuées.
- CM_FULL_RESOURCE_DESCRIPTOR : descripteur complet d'une liste.
- CM_PARTIAL_RESOURCE_LIST : ensemble de ressources partielles.
- CM_PARTIAL_RESOURCE_DESCRIPTOR : une ressource attribuée.
- IO_RESOURCE_REQUIREMENTS_LIST : liste de configurations de ressources possibles.
- IO_RESOURCE_LIST : une configuration alternative.
- IO_RESOURCE_DESCRIPTOR : une exigence de ressource.
- MmMapIoSpaceEx : mappe une ressource physique MMIO.
- MmUnmapIoSpace : détruit le mapping.
- READ_REGISTER_* / WRITE_REGISTER_* : accès aux registres MMIO.
- READ_PORT_* / WRITE_PORT_* : accès à l'espace de ports.
- IoConnectInterruptEx : connecte une ISR.
- IoDisconnectInterruptEx : déconnecte l'interruption.
- IoGetCurrentIrpStackLocation : accès aux Parameters.StartDevice.
Certains vieux ouvrages WDM montrent des mécanismes historiques comme IoReportResourceForDetection ou des modèles d'allocation datant des périphériques non-PnP.
Pour un driver matériel PnP moderne, le modèle normal est :
Code: Select all
Bus driver décrit les besoins
|
PnP Manager arbitre
|
IRP_MN_START_DEVICE
|
CM_RESOURCE_LIST attribuée au driver
16. Erreurs fréquentes
- Confondre IO_RESOURCE_REQUIREMENTS_LIST et CM_RESOURCE_LIST.
- Utiliser les raw resources à la place des translated resources sans raison.
- Caster une adresse physique en pointeur virtuel.
- Lire les registres matériels avec un simple pointeur sans respecter les primitives REGISTER/PORT adaptées.
- Oublier MmUnmapIoSpace lors de l'arrêt ou de la suppression.
- Continuer à accéder au hardware après SURPRISE_REMOVAL.
- Hardcoder une adresse MMIO ou une IRQ qui doit être fournie par PnP.
- Ignorer les Flags de CM_PARTIAL_RESOURCE_DESCRIPTOR.
La chaîne complète est :
Code: Select all
Resource requirements
|
PnP arbitration
|
IRP_MN_START_DEVICE
|
CM_RESOURCE_LIST raw + translated
|
Parse descriptors
|
MMIO / PORT / INTERRUPT / DMA
|
Driver opérationnel
