Ressources matérielles, ports I/O et MMIO dans un driver WDM

Ce cours dédiée au développement de drivers en kernel mode sans Framework

Moderator: Rick

Post Reply
Hydraxx
Site Admin
Posts: 144
Joined: Mon Jan 12, 2026 4:04 pm
Location: France
Contact:

Ressources matérielles, ports I/O et MMIO dans un driver WDM

Post by Hydraxx »

Ressources matérielles, ports I/O et MMIO dans un driver WDM

Introduction

Un vrai driver matériel doit connaître les ressources assignées à son périphérique : plages de ports, régions mémoire, interruption, canal DMA, etc.

Dans WDM, ces informations arrivent généralement lors de IRP_MN_START_DEVICE.

1. Ressources brutes et ressources traduites

Lors du démarrage du périphérique, le PnP Manager fournit deux listes :
  • Raw resources : ressources telles qu'elles existent sur le bus.
  • Translated resources : ressources traduites dans un format directement exploitable par le processeur et le HAL.
On les trouve dans :

Code: Select all

stack->Parameters.StartDevice.AllocatedResources
stack->Parameters.StartDevice.AllocatedResourcesTranslated
En pratique, un driver utilise souvent la liste traduite pour accéder réellement au matériel.

2. Structures principales

Structures importantes
  • CM_RESOURCE_LIST
  • CM_FULL_RESOURCE_DESCRIPTOR
  • CM_PARTIAL_RESOURCE_LIST
  • CM_PARTIAL_RESOURCE_DESCRIPTOR
Un CM_PARTIAL_RESOURCE_LIST contient un tableau de descripteurs :

Code: Select all

PCM_PARTIAL_RESOURCE_LIST list;

for (ULONG i = 0; i < list->Count; ++i)
{
    PCM_PARTIAL_RESOURCE_DESCRIPTOR res =
        &list->PartialDescriptors[i];

    switch (res->Type)
    {
        case CmResourceTypePort:
            break;

        case CmResourceTypeMemory:
            break;

        case CmResourceTypeInterrupt:
            break;

        case CmResourceTypeDma:
            break;
    }
}
3. Ressource de type Port

Une ressource CmResourceTypePort décrit une plage de ports d'E/S.

Informations importantes :

Code: Select all

res->u.Port.Start
res->u.Port.Length
res->Flags
Selon l'architecture, la ressource peut représenter un véritable espace de ports I/O ou une plage mémoire utilisée comme registre matériel.

4. Port-mapped I/O

Sur x86/x64, certains périphériques exposent des registres via un espace d'adressage I/O distinct.

Les API classiques sont :

API importantes
  • READ_PORT_UCHAR
  • READ_PORT_USHORT
  • READ_PORT_ULONG
  • WRITE_PORT_UCHAR
  • WRITE_PORT_USHORT
  • WRITE_PORT_ULONG
Exemple :

Code: Select all

UCHAR value = READ_PORT_UCHAR(portBase);
WRITE_PORT_UCHAR(portBase, 0x01);
5. Memory-Mapped I/O (MMIO)

De nombreux périphériques modernes exposent leurs registres dans une plage d'adresses mémoire.

Le driver mappe la ressource physique avec :

Code: Select all

PVOID regs = MmMapIoSpace(
    physicalAddress,
    length,
    MmNonCached
);
Puis libère le mapping avec :

Code: Select all

MmUnmapIoSpace(regs, length);
API d'accès aux registres
  • READ_REGISTER_UCHAR
  • READ_REGISTER_USHORT
  • READ_REGISTER_ULONG
  • READ_REGISTER_BUFFER_*
  • WRITE_REGISTER_UCHAR
  • WRITE_REGISTER_USHORT
  • WRITE_REGISTER_ULONG
  • WRITE_REGISTER_BUFFER_*
Exemple :

Code: Select all

volatile ULONG* statusReg =
    (volatile ULONG*)((PUCHAR)regs + STATUS_OFFSET);

ULONG status = READ_REGISTER_ULONG(statusReg);
6. Pourquoi utiliser READ_REGISTER et WRITE_REGISTER ?

Un registre matériel n'est pas de la RAM ordinaire. L'ordre des accès, les barrières mémoire, le cache et les contraintes de plateforme peuvent être importants.

Il faut donc préférer les macros prévues par le WDK plutôt qu'un simple :

Code: Select all

*reg = value;
7. Ressource d'interruption

Une ressource CmResourceTypeInterrupt décrit l'interruption assignée au périphérique.

On y retrouve selon le type de ressource :

Code: Select all

res->u.Interrupt.Level
res->u.Interrupt.Vector
res->u.Interrupt.Affinity
res->Flags
Ces informations servent ensuite à connecter une ISR.

8. Ressource DMA

Pour les périphériques utilisant un canal DMA système, on peut recevoir :

Code: Select all

res->u.Dma.Channel
res->u.Dma.Port
Les périphériques bus-master modernes utilisent souvent un DMA adapter plutôt qu'un canal DMA ISA traditionnel.

9. Stocker les ressources dans DEVICE_EXTENSION

Une pratique classique consiste à conserver les ressources utiles dans l'extension du périphérique.

Code: Select all

typedef struct _DEVICE_EXTENSION
{
    PDEVICE_OBJECT LowerDevice;

    PUCHAR PortBase;
    ULONG PortLength;

    PVOID RegisterBase;
    ULONG RegisterLength;

    PKINTERRUPT InterruptObject;

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
10. Exemple de parsing

Code: Select all

for (ULONG i = 0; i < list->Count; ++i)
{
    PCM_PARTIAL_RESOURCE_DESCRIPTOR res =
        &list->PartialDescriptors[i];

    switch (res->Type)
    {
        case CmResourceTypePort:
            DbgPrint("PORT: %I64x length=%lu\n",
                res->u.Port.Start.QuadPart,
                res->u.Port.Length);
            break;

        case CmResourceTypeMemory:
            DbgPrint("MEM: %I64x length=%lu\n",
                res->u.Memory.Start.QuadPart,
                res->u.Memory.Length);
            break;

        case CmResourceTypeInterrupt:
            DbgPrint("IRQ vector=%lu level=%lu\n",
                res->u.Interrupt.Vector,
                res->u.Interrupt.Level);
            break;
    }
}
11. Nettoyage lors du Stop/Remove

Lorsqu'un périphérique est arrêté ou supprimé, le driver doit libérer ce qu'il a acquis :
  • MmUnmapIoSpace pour les régions MMIO.
  • IoDisconnectInterrupt / routine moderne équivalente pour l'interruption.
  • Libération des objets DMA si nécessaire.
  • Arrêt des timers et DPC associés.
12. Résumé

Le chemin typique est :

Code: Select all

IRP_MN_START_DEVICE
        |
        v
CM_RESOURCE_LIST
        |
        +--> Port
        +--> Memory/MMIO
        +--> Interrupt
        +--> DMA
        |
        v
DEVICE_EXTENSION
        |
        v
accès au matériel

Who is online

Users browsing this forum: No registered users and 1 guest