Seminar Datenverarbeitung WS 97/98. Treiberprogrammierung unter Windows NT 4.0 am Beispiel des Treibers MAPMEM



Ähnliche Dokumente
Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Tapps mit XP-Mode unter Windows 7 64 bit (V2.0)

1 Vom Problem zum Programm

Um ein solches Dokument zu erzeugen, muss eine Serienbriefvorlage in Word erstellt werden, das auf die von BüroWARE erstellte Datei zugreift.

Artikel Schnittstelle über CSV

Virtueller Seminarordner Anleitung für die Dozentinnen und Dozenten

SafeRun-Modus: Die Sichere Umgebung für die Ausführung von Programmen

Auto-Provisionierung tiptel 30x0 mit Yeastar MyPBX

teischl.com Software Design & Services e.u. office@teischl.com

Handbuch B4000+ Preset Manager

Übung - Konfigurieren einer Windows Vista-Firewall

Computeria Solothurn

Leitfaden zur Nutzung des System CryptShare

Er musste so eingerichtet werden, dass das D-Laufwerk auf das E-Laufwerk gespiegelt

Zählen von Objekten einer bestimmten Klasse

Zentrale Installation

Nutzung von GiS BasePac 8 im Netzwerk

Matrix42. Use Case - Sicherung und Rücksicherung persönlicher Einstellungen über Personal Backup. Version September

Gruppenrichtlinien und Softwareverteilung

Datensicherung. Beschreibung der Datensicherung

Datenaustausch mit Datenbanken

LPT1 Anschluss mit PCMCIA Karte

Meldung Lokale Anwendung inkompatibel oder Microsoft Silverlight ist nicht aktuell bei Anmeldung an lokal gespeicherter RWE SmartHome Anwendung

Wie wird ein Jahreswechsel (vorläufig und endgültig) ausgeführt?

Backup der Progress Datenbank

GEORG.NET Anbindung an Ihr ACTIVE-DIRECTORY

ICS-Addin. Benutzerhandbuch. Version: 1.0

Es sollte die MS-DOS Eingabeaufforderung starten. Geben Sie nun den Befehl javac ein.

2. Einrichtung der ODBC-Schnittstelle aus orgamax (für 32-bit-Anwendungen)

Qt-Projekte mit Visual Studio 2005

Objektorientierte Programmierung

Handbuch USB Treiber-Installation

Konfiguration VLAN's. Konfiguration VLAN's IACBOX.COM. Version Deutsch

C++ Grundlagen. ++ bedeutet Erweiterung zum Ansi C Standard. Hier wird eine Funktion eingeleitet

EasyWk DAS Schwimmwettkampfprogramm

Firmware-Update für den SUPER COOLSCAN 4000 ED

Effiziente Administration Ihrer Netzwerkumgebung

Inhalt. Inhalt Voraussetzungen Liegenschaften und Adressen auswählen Abgleich mit Internet-Office Dokumente...

Ablaufbeschreibung für das neu Aufsetzen von Firebird und Interbase Datenbanken mit der IBOConsole

Einrichtung des Cisco VPN Clients (IPSEC) in Windows7

Version 0.3. Installation von MinGW und Eclipse CDT

SANDBOXIE konfigurieren

Microsoft PowerPoint 2013 Folien gemeinsam nutzen

Professionelle Seminare im Bereich MS-Office

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen:

Fachbericht zum Thema: Anforderungen an ein Datenbanksystem

Windows 10. Vortrag am Fleckenherbst Bürgertreff Neuhausen.

! " # $ " % & Nicki Wruck worldwidewruck

Bedienungsanleitung Anlassteilnehmer (Vereinslisten)

Aufruf der Weboberflache des HPM- Warmepumpenmanagers aus dem Internet TIPPS

Vorkurs C++ Programmierung

Installation OMNIKEY 3121 USB

Step by Step Webserver unter Windows Server von Christian Bartl

Archiv - Berechtigungen

OP-LOG

Stellen Sie bitte den Cursor in die Spalte B2 und rufen die Funktion Sverweis auf. Es öffnet sich folgendes Dialogfenster

- Zweimal Wöchentlich - Windows Update ausführen - Live Update im Norton Antivirusprogramm ausführen

Installieren von Microsoft Office Version 2.1

Mit der Maus im Menü links auf den Menüpunkt 'Seiten' gehen und auf 'Erstellen klicken.

Für Windows 7 Stand:

Bedienungsanleitung. 1. Eine direkte (1 1) Verbindung muss mit einem gekreuzten (Crossover) Netzwerkkabel hergestellt werden.

Kapitel 7 - Wägungen

METTLER TOLEDO USB-Option Installation der Treiber unter Windows XP

GeoPilot (Android) die App

Shellfire PPTP Setup Windows 7

Windows Server 2012 R2 Essentials & Hyper-V

Leitfaden zur ersten Nutzung der R FOM Portable-Version für Windows (Version 1.0)

VIDA ADMIN KURZANLEITUNG

Wie halte ich Ordnung auf meiner Festplatte?

Übung - Konfigurieren einer Windows 7-Firewall

Mandant in den einzelnen Anwendungen löschen

Im Folgenden wird Ihnen an einem Beispiel erklärt, wie Sie Excel-Anlagen und Excel-Vorlagen erstellen können.

Individuelle Formulare

Erstellen einer PostScript-Datei unter Windows XP

Netzwerk einrichten unter Windows

Übung - Konfigurieren einer Windows-XP-Firewall

Umstellung des Vergütungsverfahrens externer Gutachter beim Versorgungsamt Hamburg

Collax Archive Howto

Installation von Updates

KURZANLEITUNG MSDAS DMS SYSTEM - SILVERDAT II SCHNITTSTELLE

Updatehinweise für die Version forma 5.5.5

Eine Einführung in die Installation und Nutzung von cygwin

Dokumentenverwaltung. Copyright 2012 cobra computer s brainware GmbH

I. Travel Master CRM Installieren

MSDE 2000 mit Service Pack 3a

Windows7 32/64bit Installationsanleitung der ROBO-PRO-Software

Anlegen eines DLRG Accounts

Einrichten einer Festplatte mit FDISK unter Windows 95/98/98SE/Me

Whitepaper. Produkt: combit Relationship Manager / address manager. Dateiabgleich im Netzwerk über Offlinedateien

Rillsoft Project - Installation der Software

lññáåé=iáåé===pìééçêíáåñçêã~íáçå=

Hilfe zur Urlaubsplanung und Zeiterfassung

TrekStor - ebook-reader TrekStor (TS) Edition - Firmware-Update

System-Update Addendum

Der Kalender im ipad

Berechtigungen im Kalender Anleitung für die Rechtevergabe im Outlook Kalender FHNW, Services, ICT

1. Einführung. 2. Die Mitarbeiterübersicht

Dokumentation IBIS Monitor

2. Word-Dokumente verwalten

Durchführung der Datenübernahme nach Reisekosten 2011

Transkript:

Seminar Datenverarbeitung WS 97/98 Treiberprogrammierung unter Windows NT 4.0 am Beispiel des Treibers MAPMEM Vortragender: Holger Pfennig Betreuer : Matthias Ortmann

1 Inhaltsverzeichnis 1 Inhaltsverzeichnis 1 INHALTSVERZEICHNIS... 2 2 GRUNDLAGEN... 3 2.1 SPEICHERVERWALTUNG... 3 2.2 SPEICHERZUGRIFFSSTRATEGIEN... 3 2.3 DAS I/O REQUEST PACKET... 3 2.4 DAS DRIVER OBJECT... 3 2.5 DIE DISPATCH ROUTINE... 4 2.6 I/O-CONTROL CODE... 6 2.7 RICHTLINIEN FÜR DIE PROGRAMMIERUNG... 7 3 GRUNDANFORDERUNGEN AN EINEN TREIBER... 8 3.1 EINBINDEN DES TREIBERS IN DAS SYSTEM... 8 3.2 ERKENNUNG, EINBINDUNG UND INITIALISIERUNG DER HARDWARE... 8 3.3 REALISIERUNG DER EIGENTLICHEN FUNKTIONEN DES TREIBERS... 8 3.4 ENTFERNEN DES TREIBERS AUS DEM SYSTEM... 8 4 DIE DRIVERENTRY ROUTINE... 9 4.1 DAS DEVICE OBJECT... 9 4.2 ERSTELLEN DES SYMBOLIC LINKS... 10 4.3 ABLAUFDIAGRAMM EINER DRIVERENTRY ROUTINE... 10 5 DAS BEISPIEL MAPMEM... 11 5.1 HEADER FILE VON MAPMEM... 11 5.2 DRIVERENTRY ROUTINE AM BEISPIEL MAPMEM... 12 6 VERARBEITUNG VON SYSTEMANFRAGEN IN DISPATCH ROUTINEN... 14 6.1 DISPATCH ROUTINE AM BEISPIEL VON MAPMEM... 14 7 DRIVER UNLOAD ROUTINE... 17 7.1 UNLOAD ROUTINE AM BEISPIEL VON MAPMEM... 17 8 AUFRUF DER TREIBERFUNKTIONEN... 18 8.1 DAS TESTPROGRAMM MAPTEST... 18 9 SCHLUßBEMERKUNG... 20 10 LITERATURVERZEICHNIS... 21-2-

2 Grundlagen 2 Grundlagen 2.1 Speicherverwaltung Unter Windows NT ist der Speicher in einzelne Seiten unterteilt, welche bei Bedarf in den ausführbaren Adreßraum gemapped werden können. Hierbei kann man zwei verschiedene Arten von Speicherseiten unterscheiden. Zum einen besitzt jeder Prozeß Speicherseiten, auf die er exklusiv zugreifen kann. Diese Speicherseiten werden paged Pool genannt. Die Exklusivität dieser Speicherseiten führt jedoch zu Problemen beim Austausch von Daten zwischen verschiedenen Prozessen. Eine Möglichkeit dieses Problem zu umgehen bietet die zweite Speicherseitenart, der sogenannte nonepaged Pool. Anders als beim paged Pool können alle Prozesse auf diese Speicherseiten zugreifen. Daher eignen sie sich besonders gut für den Austausch von Daten. 2.2 Speicherzugriffsstrategien Wie schon erwähnt ist der Zugriff auf Daten eines Prozesses seitens eines anderen Prozesses unter Windows NT problematisch. Dies gilt auch für das Win32 Subsystem und einen Gerätetreiber. Wie kann nun ein Datenaustausch zwischen diesen beiden Prozessen realisiert werden. Windows NT bietet hierzu zwei Speicherzugriffsstrategien an. Eine Möglichkeit besteht darin, den Datentransfer über einen Puffer im nonepaged Pool zu realisieren. Diese Vorgehensweise nennt man Buffered I/O. Eine andere Vorgehensweise verwendet eine Liste von Zeigern auf die Speicherseiten des anderen Prozesses. Mit dieser Liste kann dann direkt auf die entsprechenden Speicherseiten zugegriffen werden. Diese Methode wird Direct I/O genannt. 2.3 Das I/O Request Packet Unter Windows NT wird ein großer Teil des I/O Transfers über Pakete abgewickelt. Diese Pakete werden I/O Request Packets (IRP) genannt. Bei einem Aufruf seitens des Win32 Subsystems wird ein solches IRP vom I/O-Manager erzeugt. Es enthält den Auftrag, den entsprechenden Aufruf anzustoßen. Soll nun eine Gerät gesteuert werden, so wird das IRP an den entsprechenden Gerätetreiber weitergeleitet, welcher dann die nötigen Operationen ausführt. Die nebenstehende Grafik zeigt den prinzipiellen Aufbau eines IRPs. Zunächst kann ein IRP in zwei Blöcke unterteilt werden, den Header und den Stack. Beim Header ist besonders das Feld IoStatus hervorzuheben, welches in seinen Feldern Status und Information den Status des IRPs anzeigt. Im Stack sind die Felder MajorFunction und DeviceIo- Control wichtig. Sie enthalten den eigentlichen Auftrag des IRPs. -3- Header Stack IRP IoStatus IO_STATUS_BLOCK Status; Information; IO_STACK_LOCATION MajorFunction; MinorFunction union struct Read; struct Write; struct DeviceIoControl; : Parameters; Bild 1: Prinzipieller Aufbau eines IRPs 2.4 Das Driver Object Einen weiteren wichtigen Bestandteil des Treiberkonzeptes unter Windows NT stellt das Driver Object dar. Beim Laden eines Treibers in das System wird zunächst das Driver Object erstellt. Hierbei ist es nicht relevant, ob der Treiber beim Booten des Systems oder im laufenden Betrieb manuell eingebunden wurde.

2 Grundlagen Die einzige Funktion eines Treibers, welche dem I/O-Manager namentlich bekannt ist, ist die Initialisierungsroutine DriverEntry. Dies führt nun zu dem Problem, die anderen Funktionen des Treibers anzusprechen. Das Driver Object bietet hierzu eine Lösung. Es enthält eine Reihe von Zeigern auf die eigentlichen Funktionen des Treibers. Weiterhin ist es mit allen Geräten verknüpft, welche von dem Treiber gesteuert werden sollen. Grundsätzlich lassen sich die Funktionen, die vom Driver Object verwaltet werden, in drei Gruppen unterteilen : 1. Start I/O- Routine 2. Unload-Routine 3. Dispatch-Routinen Die Dispatch-Routinen werden über eine Liste von Zeigern im Feld MajorFunction verwaltet. Der benötigte Zeiger wird über den I/O- Operation Code ermittelt. Dieser Code entspricht dem Feld MajorFunction des IRPs. Aus der nebenstehenden Grafik ist weiterhin zu entnehmen, wie die einzelnen Geräte als dynamische Liste verwaltet werden. Device Object Device Object Driver Object DeviceObject DriverStartIo DriverUnload MajorFunction[ ] Bild 2: Aufbau des Driver Objects : Start I/O Routine Unload Routine Dispatch Routine Dispatch Routine 2.5 Die Dispatch Routine Abhängig vom jeweiligen I/O-Operation Code wird bei einer Anfrage des Systems die entsprechende Dispatch Routine des Treibers aktiviert. Diese Dispatch Routinen enthalten die eigentliche Funktionalität des Treibers. Zu den verschiedenen I/O-Operation Codes existieren Befehle des Win32 Subsystems, welche den I/O-Manager veranlassen, ein IRP mit dem entsprechenden I/O-Operation Code zu erzeugen. Für jeden I/O-Operation Code können separate Dispatch Routinen existieren. Es ist jedoch auch möglich, mehrere Operation Codes innerhalb einer Dispatch Routine mit Hilfe einer Fallunterscheidung abzuarbeiten. Die folgende Grafik verdeutlicht den Zugriff des I/O-Managers auf die zu einem I/O-Operation Code gehörige Dispatch Routine. IRP IRP_MJ_WRITE IRP_MJ_WRITE : Driver Object MajorFunction[ ] _IopInvalidDeviceRequest XxDispatchWrite _IopInvalidDeviceRequest Write Dispatch Routine Bild 3: Zugriff auf Dispatch Routinen Bei einem Systemaufruf greift der I/O-Manager nach Erstellung des IRPs auf das Driver Object des anzusprechenden Treibers zu. Wird der I/O-Operation Code des IRPs im Driver Object nicht unterstützt, so ist im entsprechenden Element der Liste MajorFunction der voreingestellte Zeiger auf die interne Funktion _IopInvalidDeviceRequest des I/O-Managers gespeichert. Diese Funktion sorgt für eine Beendigung des IRPs mit einer entsprechenden Fehlermeldung. Im folgenden sind einige I/O-Operation Codes aufgelistet und erklärt: -4-

2 Grundlagen IRP_MJ_Create Der Operation Code IRP_MJ_Create wird von dem Win32 Aufruf CreateFile angesprochen. Da jedes Gerät über einen Dateihandle angesprochen wird, ist diese Funktionen eine Voraussetzung, um auf den Treiber zugreifen zu können. IRP_MJ_Cleanup Der Operation Code wird von dem Win32 Aufruf CloseHandle erzeugt. Beim Schließen des Dateihandles wird ein abhängiges, geöffnetes IRP geschlossen. IRP_MJ_Close Wie bei IRP_MJ_Cleanup wird dieser Operation Code durch den Win32 Aufruf CloseHandle erzeugt. Der Dateihandle für das Gerät wird geschlossen. IRP_MJ_Read Der Win32 Befehl ReadFile erzeugt diesen Operation Code. Es erfolgt ein lesender Zugriff auf das Gerät. Der Zugriff auf die Daten erfolgt unter Berücksichtigung der im Device Object angegebenen Pufferstrategie. IRP_MJ_Write Dieser Operation Code wird vom Win32 Befehl WriteFile übergeben. Es erfolgt ein schreibender Zugriff auf das Gerät. Auch hier wird die im Device Object angegebene Pufferstrategie verwendet. IRP_MJ_Device_Control Dieser Operation Code wird durch die Win32 Funktion DeviceIoControl erzeugt. Hierbei ist zu beachten, daß beim Aufruf der Funktion zusätzlich die Variable IoControlCode übergeben wird. Somit lassen sich weitere Fallunterscheidungen realisieren. Dieser Operation Code kann sowohl von Anwendungen als auch von Kernel Routinen angesprochen werden IRP_MJ_Internal_Device_Control Die Funktion dieses Operation Codes ist ähnlich wie die von IRP_MJ_Device_Control. Der Unterschied besteht jedoch darin, daß nur Kernel Routinen auf diesen Operation Code Zugriff haben. Somit existieren keine Win32 Aufrufe, die diesen Operation Code unterstützen. IRP_MJ_Query_Information Dieser Operation Code wird über den Befehl GetFileSize erzeugt und wird zur Ermittlung der Länge einer Datei benötigt. IRP_MJ_Set_Information Mit diesem Operation Code wird der Win32 Befehl SetEndOfFile unterstützt, welcher die Länge einer Datei festlegt. IRP_MJ_Flush_Buffers Dieser Operation Code wird von verschiedenen Win32 Befehlen angesprochen, um eine Ausgabe des Ausgabebuffers bzw. eine Löschung des Eingabebuffers zu erzwingen. Die zugehörigen Win32 Befehle sind hierbei : FlushFileBuffers FlushConsoleInputBuffer PurgeComm IRP_MJ_Shutdown Wird das System heruntergefahren, so wird zuvor jeder Treiber mit dem Operation Code IRP_MJ_Shutdown aufgerufen. Somit kann das System in einen definierten Zustand gefahren werden. Eine Freigabe von belegten Systemressourcen kann hierbei vernachlässigt werden, da das System in Kürze seine Arbeit einstellen wird. Das Herunterfahren des Systems kann durch den Aufruf der Funktion InitiateSystemShutdown eingeleitet werden. Soll der Treiber eine Shutdown Dispatch Routine verwenden, muß dem I/O-Manager zusätzlich zu der Definition der entsprechenden MajorFunction mitgeteilt werden, daß diese Vorgehensweise er- -5-

2 Grundlagen wünscht ist. Daher muß in der DriverEntry Routine der Aufruf der Funktion IoRegisterShutdownNotification erfolgen. Die nachfolgende Zeile zeigt den Funktionsprototypen dieser Funktion: NTSTATUS IoRegisterShutdownNotification( IN PDEVICE_OBJECT DeviceObject); 2.6 I/O-Control Code Leider ist es nicht möglich, die I/O-Operation Codes durch eigene Definitionen zu erweitern. Eine Möglichkeit diesen Engpaß zu beseitigen, bieten die I/O-Operation Codes IRP_MJ_Device_Control und IRP_MJ_Internal_Device_Control. Diese beiden I/O-Operation Codes verfügen über einen zusätzlichen Parameter, welcher beim Aufruf durch das Win32 Subsystem übergeben wird, den I/O-Control Code. Anhand dieses Parameters kann innerhalb der zugehörigen Dispatch Routine eine Fallunterscheidung durchgeführt werden. Anders als beim I/O-Operation Code ist es möglich, eigene I/O-Control Codes zu definieren. Der I/O- Control Code wird durch einen 32 Bit Wert beschrieben. Er besitzt eine feste Struktur, welche über das Makro CTL_CODE des Driver Development Kit (DDK) von Micorsoft unterstützt wird. 31-16 15-14 13-2 1-0 Geräte Typ Zugriffsart Control Code Transfer Typ Der Wert wird nach dem folgenden Schema gebildet : Bild 4: Aufbau des I/O-Control Codes Parameter Geräte Typ Zugriffsart Control Code Beschreibung FILE_DEVICE_XXX 0x0000 bis 0x7FFF : reserviert für Microsoft 0x8000 bis 0xFFFF : frei für benutzerdefinierte Geräte Bit 14 und 15 des I/O-Control Codes definieren den Dateizugriff, der beim Win32 Aufruf CreateFile mindestens verwendet werden muß: FILE_ANY_ACCESS Es ist jede Zugriffsart auf die Datei erlaubt. FILE_READ_DATA Auf die Datei darf nur lesend zugegriffen werden. FILE_WRITE_DATA Auf die Datei darf nur schreibend zugegriffen werden. FILE_READ_DATA FILE_WRITE_DATA Sowohl lesender als auch schreibender Zugriff auf die Datei muß erlaubt sein. 0x000 bis 0x7FF : reserviert für Microsoft -6-

2 Grundlagen 0x800 bis 0xFFF : benutzerdefinierte Control Codes Transfer Typ Die unteren zwei Bits geben den Puffermechanismus, der bei einem Datentransfer verwendet werden soll, wieder. METHOD_BUFFERED Sowohl Eingabe als auch Ausgabe werden in einem nonepaged Speicherbereich gepuffert. METHOD_IN_DIRECT Die Eingabe erfolgt im direkten Zugriff, die Ausgabe wird gepuffert. METHOD_OUT_DIRECT Die Eingabe erfolgt im direkten Zugriff, die Ausgabe wird gepuffert. METHOD_NEITHER Es wird kein Speicherzugriff vom IO-Manager unterstützt. Der Treiber muß den Datentransfer selbst verwalten. 2.7 Richtlinien für die Programmierung Bei der Programmierung von Treibern gibt es eine Reihe von Methoden und Konventionen, welche unbedingt eingehalten werden sollten. Zuerst sollte darauf geachtet werden, daß eine einheitliche Namensgebung verwendet wird. Dies erleichtert die Lesbarkeit eines Treibers sehr stark. Ein weiterer Punkt besteht darin, jede Statusmeldung seitens eines Systemaufrufes zu überprüfen, da jede Systemfunktion grundsätzlich fehlschlagen kann. Wird eine solche Überprüfung nicht durchgeführt, so kann dies im schlimmsten Fall zum Absturz des Systems führen. Aus dem gleichen Grund muß beachtet werden, daß der Treiber keine Laufzeitfehler verursacht. Als letzter Punkt sollte beachtet werden, daß alle System Ressourcen, die nicht mehr benötigt werden, vom Treiber wieder freigegeben werden. Ansonsten wird der Treiber mit der Zeit immer mehr Speicher für sich in Anspruch nehmen, bis das System schließlich nicht mehr arbeitsfähig ist. Bei den Namenskonventionen sind die folgenden vier Beispiele zu nennen : IoXxx() Funktionen des I/O-Managers ZwXxx() RtlXxx() TreibernameXxx() Interne Systemaufrufe Funktionen der Runtime Library für Gerätetreiber Bei dieser Gruppe von Funktionen sollte erwähnt werden, daß viele Funktionen von Bibliotheken einiger C-Compiler nicht die Anforderungen an einen Treiber erfüllen. Daher sollte generell auf diese Funktionen verzichtet werden. Diese werden dann durch die entsprechenden RtlXxx() Funktionen ersetzt. Aufruf einer im Treiber realisierten Funktion Auch bei der Benennung von Parametern sollte eine einheitliche Namensgebung erfolgen. IRP_MJ_Xxx Diese Konstruktion stellt innerhalb der DDK von Microsoft grundsätzlich einen I/O-Operation Code dar. FILE_DEVICE_Xxx Dieser Ausdruck stellt den Namen eines Gerätes dar. Hier werden seitens der DDK keine Vorgaben getroffen. Vielmehr ist an dieser Stelle die Disziplin des Programmierers gefragt. IOCTL_FILE_DEVICE_Xxx Der I/O-Control Code sollte grundsätzlich mit der Zei- -7-

3 Grundanforderungen an einen Treiber chenkette IOCTL_ gefolgt von dem Gerätenamen des Treibers beginnen. 3 Grundanforderungen an einen Treiber Die erste Frage, die sich bei der Programmierung von Treibern unter Windows NT stellt, ist, welche Funktionen ein Treiber erfüllen muß. Die Anforderungen kann man in die folgenden Bereiche einteilen : Der Treiber muß in der Lage sein, sich in das System einbinden zu können. Dies muß sowohl bei einem Neustart aber auch im laufenden Betrieb möglich sein. Dient der Treiber als Schnittstelle zwischen System und Hardware, so muß der Treiber in der Lage sein, diese zu erkennen und einzubinden. In vielen Fällen muß anschließend eine Initialisierung der Hardware vorgenommen werden. Die eigentliche Aufgabe des Treibers besteht im Datentransfer zwischen System und Hardware bzw. zwischen verschiedenen Systemkomponenten. Diese Funktionen müssen in den Treiber eingebunden werden. Eine weitere Forderung an einen Treiber besteht darin, daß dieser in der Lage sein muß, durch einen entsprechenden Aufruf durch das System, sich vollständig aus diesem zu entfernen. 3.1 Einbinden des Treibers in das System Das Einbinden des Treibers in das System geschieht in dessen DriverEntry Routine. Diese Funktion muß in jedem Treiber realisiert werden. Sie ist die einzige Funktion, die dem I/O-Manager namentlich bekannt ist. Beim Start des Systems oder beim manuellen Laden des Treibers wird DriverEntry automatisch aufgerufen. 3.2 Erkennung, Einbindung und Initialisierung der Hardware Verfügt die Hardware über automatische Erkennungsmechanismem (Plug and Play) so wird dem Treiber ein großer Teil der Arbeit abgenommen. Beim Start des Systems wird die Hardware von einem dafür vorgesehenen Programm analysiert. Alle nötigen Informationen werden anschließend in die Registry von Windows NT geschrieben. Diese Informationen können nun in der DriverEntry Routine des Treibers eingelesen und in der Device Extension des Driver Objects abgespeichert werden. Sollte eine automatische Erkennung der Hardware nicht möglich sein, so müssen diese Informationen vorher durch geeignete Software in die Registry eingelesen werden. Diese Vorgehensweise stellt jedoch eine Fehlerquelle dar, da die gespeicherten Daten nur beschränkt überprüft werden können. 3.3 Realisierung der eigentlichen Funktionen des Treibers Wie schon erwähnt sind die Funktionen des Treibers dem System nicht namentlich bekannt. Daher benötigt man einen geeigneten Mechanismus, um auf diese Funktionen zugreifen zu können. Eine Lösung bietet hierbei das Driver Object. Es verfügt über entsprechende Variablen, in denen die Zeiger auf die einzelnen Funktionen gespeichert werden. 3.4 Entfernen des Treibers aus dem System Das Feld des Driver Objects DriverUnload verwaltet eine spezielle Funktion zum entfernen des Treibers aus dem System. In gewisser Weise stellt diese Funktion das Gegenstück zur DriverEntry Routine des Treibers dar. Diese Funktion muß so aufgebaut werden, daß alle Ressourcen, auf welche der Treiber während seiner Laufzeit Zugriff hatte, vollständig freige- -8-

4 Die DriverEntry Routine geben werden. Dies gilt sowohl für eventuell verwaltete Hardwareerweiterungen als auch für den Speicher des Systems. Es ist zu beachten, daß Ressourcen, welche in der DriverUnload Routine nicht freigegeben wurden, bis zu einem Neustart des Systems nicht mehr zur Verfügung stehen. 4 Die DriverEntry Routine Alle Aufgaben die sich auf das Laden und Initialisieren des Treibers beziehen werden in der DriverEntry Routine behandelt. Im wesentlichen kann man hierbei die folgenden vier Bereiche unterscheiden : 1. Erstellung des Device Objects 2. Hardwareerkennung und Initialisierung 3. Festlegung der einzelnen Funktionen des Treibers im Driver Object 4. Erstellen des Symbolic Links 4.1 Das Device Object Jedem virtuellen, logischen oder physikalischen Gerät wird ein Device Object zugewiesen. Über das Device Object können seitens des I/O-Managers und des Systems Informationen über den Zustand der einzelnen Geräte ermittelt werden. Driver Object Wartender IRP Device Object NextDevice Flags DriverObject CurrentIrp Aktueller IRP Device Extension DeviceExtension Wartender IRP Device Queue Object Bild 5: Aufbau eines Device Objects Der I/O-Manager greift mit Hilfe von Dateihandles auf die entsprechenden Funktionen zu. Das Device Object verfügt über einen Zeiger auf das zugehörige Driver Object, welches die Funktionen des Treibers verwaltet. Somit wird eine Verknüpfung zwischen dem Gerät und der zugehörigen Treibersoftware erreicht. Ein weiterer Bestandteil des Device Objects ist die Device Extension. In ihr werden alle relevanten Daten, die für den Betrieb des Treibers nötig sind, verwaltet. Das Device Object verfügt weiterhin über ein Feld, welches eine Reihe von Flags verwaltet. Einige Beispiele sind : DO_DEVICE_INITIALIZING Dieses Flag veranlaßt den I/O-Manager automatisch die Initialisierung des Device Objects vorzunehmen. Der I/O-Manager setzt dieses Flag automatisch beim Verlassen der DriverEntry Routine zurück. Wird das Device Object in einer anderen Funktion als der DriverEntry erzeugt, so muß das Flag in der entsprechenden Funktion zurückgesetzt werden. DO_BUFFERED_IO Veranlaßt den I/O-Manager Daten zwischen Benutzer- und Systempuffer hin und her zu kopieren. DO_DIRECT_IO Während eines I/O-Zugriffs werden Benutzerpuffer im physikalischen Speicher abgelegt. Wenn keine Pufferstrategie vorgegeben wird, so wird seitens des I/O-Managers davon -9-

4 Die DriverEntry Routine ausgegangen, daß der Treiber den Zugriff auf Daten eigenständig übernimmt. 4.2 Erstellen des Symbolic Links Unter Windows NT hat jedes Gerät mehr als einen Namen. Zum einen wird der Name verwaltet, der der ausführenden Schicht des Systems bekannt ist. Auf der anderen Seite muß jedoch auch der Zugriff durch das Win32, Win16 und virtuelle DOS System gewährleistet werden. Daher muß jedem Gerät ein zusätzlicher DOS Name zugewiesen werden. Die Zuweisung dieses zweiten Namens erfolgt über einen Symbolic Link. Das Erstellen des Symbolic Links stellt den abschließenden Schritt der DriverEntry Routine dar. Somit ist das Device Object für die erwähnten Untersysteme sichtbar. Ohne Symbolic Link ist es nicht möglich, seitens eines Programms auf den Treiber zuzugreifen. Nachfolgendes Bild veranschaulicht die Verbindung von NT und Win32 Gerätenamen im Object Manager über ein Symbolic Link. \ Device DosDevice Xx0 Symbolic Link XX1 Bild 6: Symbolic Link 4.3 Ablaufdiagramm einer DriverEntry Routine NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING Regestry Path) Erstellen des Device Objects Erzeugung des Device Objects erfolgreich? Nein DriverEntry fehlgeschlagen Einbinden der Treiberfunktionen in das Driver Object Nein Erstellen des Symbolic Links Erstellung des Symbolic Links erfolgreich? Ja Löschen des Device Objects DriverEntry fehlgeschlagen Rückmelden des NT-Status Ja DriverEntry erfolgreich Bild 7: Ablaufdiagramm einer DriverEntry Routine Prinzipiell kann jede Funktion, die von DriverEntry aufgerufen wird, aufgrund unvorhersehbarer Zustände im System oder mangelnden Systemressourcen, fehlschlagen. Daher muß an jeder Stelle, wo seitens des Systems ein Status zurückgeliefert wird, dieser auch überprüft werden. Vernachlässigt man diese Überprüfungen, so kann dies zu dramatischen Folgen, bis hin zum Systemabsturz, führen. Jede DriverEntry Funktion greift auf jeden Fall auf die Funktionen IoCreate- Device und IoCreateSymbolicLink zu. Berücksichtigt man nur diese beiden Funktionen, so ergibt sich das nebenstehende Ablaufdiagramm. -10-

5 Das Beispiel MAPMEM 5 Das Beispiel MAPMEM Die bisher besprochenen Elemente eines Treibers werden im folgenden anhand eines Beispiels näher veranschaulicht: Der Beispieltreiber MAPMEM ist ein Bestandteil des Driver Divelopment Kit von Microsoft. MAPMEM demonstriert die Vorgehensweise, wie ein Kernel-Mode Treiber in der Lage ist, eine physikalische Adresse in den User-Mode Adressraum zu mappen bzw. dies rückgängig zu machen. Dies kann erforderlich werden, um von einer Anwendung direkt auf den physikalischen Speicher zugreifen zu können, da Win32 Anwendungen normalerweise dieser Zugriff verweigert wird. 5.1 Header File von MAPMEM Zunächst soll das Header File von MAPMEM näher erläutert werden. An dieser Stelle wird der Gerätetyp zugewiesen. Die Werte 0 bis 32767 sind für Microsoft reserviert. Der Wertebereich von 32768 bis 65535 ist frei für benutzerdefinierte Geräte. Der MAPMEM Treiber verwendet einen Wert von 32768. #define FILE_DEVICE_MAPMEM 0x00008000 Als nächstes werden die I/O Control Codes definiert. Der für Microsoft reservierte Bereich liegt zwischen 0 und 2047. Der Bereich für benutzerdefinierte Werte reicht von 2048 bis 4095. Bei MAPMEM werden die Werte wie folgt zugewiesen : IOCTL_MAPMEM_MAP_USER_PHYSICAL_MEMORY = 2048 IOCTL_MAPMEM_UNMAP_USER_PHYSICAL_MEMORY = 2049 #define MAPMEM_IOCTL_INDEX 0x800 #define IOCTL_MAPMEM_MAP_USER_PHYSICAL_MEMORY CTL_CODE( FILE_DEVICE_MAPMEM, \ // 0x8000 MAPMEM_IOCTL_INDEX, \ // 0x0800 METHOD_BUFFERED, \ // Eingabe und Ausgabe FILE_ANY_ACCESS) // Uneingeschränkter Zu- // griff #define IOCTL_MAPMEM_UNMAP_USER_PHYSICAL_MEMORY CTL_CODE( FILE_DEVICE_MAPMEM, \ // 0x8000 MAPMEM_IOCTL_INDEX+1,\ // 0x0801 METHOD_BUFFERED, \ // Eingabe und Ausgabe FILE_ANY_ACCESS) // Uneingeschränkter Zu- // griff typedef struct -11-

5 Das Beispiel MAPMEM INTERFACE_TYPE InterfaceType; // Isa, Eisa, usw. ULONG BusNumber; // Bus Nummer PHYSICAL_ADDRESS BusAddress; // relative Bus Addresse ULONG AddressSpace; // 0 : Speicher, 1 : I/O ULONG Length; // Länge des zu mappenden Speicherbe- // reichs PHYSICAL_MEMORY_INFO, *PPHYSICAL_MEMORY_INFO; 5.2 DriverEntry Routine am Beispiel MAPMEM Im nun folgenden Abschnitt wird die DriverEntry Routine des Treibers erläutert: NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegristryPath) PDEVICE_OBJECT deviceobject = NULL; NTSTATUS ntstatus; WCHAR devicenamebuffer[ ] = L \\Device\\MapMem ; UNICODE_STRING devicenameunicodestring; WCHAR devicelinkbuffer[ ] = L \\DosDevices\\MAPMEM ; UNICODE_STRING DeviceLinkUnicodeString; MapMemKdPrint(( MAPMEM.SYS: entering DriverEntry\n )); Im folgenden Abschnitt der DriverEntry Routine wird das Device Object erzeugt. Diese Aufgabe wird von der Funktion IoCreateDevice erfüllt, wobei eine Rückmeldung des Status erfolgt. IoCreateDevice benötigt hierzu eine Reihe von Parametern, wie der nachfolgend gezeigte Funktionsprototyp näher veranschaulicht: NTSTATUS IoCreateDevice( IN PDRIVER_OBJECT IN ULONG IN PUNICODE_STRING IN DEVICE_TYPE IN ULONG IN BOOLEAN OUT PDEVICE_OBJECT ); // Zeiger auf Driver Object // Größe der Device Extension // Zeiger auf Gerätename (optional) // Gerätetyp // Charakteristika für Massenspeicher // Exklusive Nutzung // Zeiger auf das Device Object Da im Beispiel MAPMEM keine Device Extension benötigt wird, wird dessen Größe auf 0 gesetzt. Der Gerätename wird als Unicode String übergeben. Dieser wird mit dem Befehl RtlInitUnicodeString aus dem String DeviceNameBuffer[ ] = L \\Device\\MapMem erzeugt. Der Gerätetyp wird als FILE_DEVICE_MAPMEM definiert. Das Device Object wird exklusiv verwaltet, d.h. es kann nur eine Anfrage auf einmal abgearbeitet werden. Da kein Zugriff auf einen Massenspeicher vorliegt, kann der Wert für die Charakteristika für Massenspeicher ignoriert und auf 0 gesetzt werden. -12-

5 Das Beispiel MAPMEM Da die Erzeugung des Device Objects innerhalb der DriverEntry Routine erfolgt, kann das Flag DO_DEVICE_INITIALIZING unberücksichtigt bleiben. Auch die Flags DO_DIRECT_IO und DO_BUFFERED_IO werden nicht gesetzt, da weder IRP_MJ_Read noch IRP_MJ_Write vom Treiber unterstützt werden. RtlInitUnicodeString (&DeviceNameUnicodeString, devicenamebuffer); ntstatus=iocreatedevice( DriverObject, 0, &devicenameunicodestring, FILE_DEVICE_MAPMEM, 0, True, &deviceobject); An dieser Stelle werden die einzelnen Treiberfunktionen mit dem Driver Object verknüpft. Im Beispiel MAPMEM sind hier nur zwei Funktionen vorgesehen: MapMemDispatch Alle Major Functions werden über die gleiche Dispatch Routine abgearbeitet. Es wäre jedoch auch möglich, für jeden Operation Code eine separate Dispatch Routine zu verwenden. MapMemUnload Diese Funktion wird als DriverUnload Routine in das Driver Object eingebunden. Da in diesem Beispiel kein Datentransfer benötigt wird, bleibt das Feld DriverStartIo undefiniert. if (NT_SUCCESS(ntStatus)) DriverObject->MajorFunction[IRP_MJ_CREATE] = MapMemDispatch; DriverObject->MajorFunction[IRP_MJ_CLOSE] = MapMemDispatch; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MapMemDispatch; DriverObject->DriverUnload = MapMemUnload; Nachdem die Funktionen des Treibers in das Driver Object eingebunden worden sind, wird der Symbolic Link erstellt. Der Name des Symbolic Links wird wieder über einen Unicode String bereitgestellt. NTSTATUS IoCreateSymbolicLink( IN PUNICODE_STRING IN PUNICODE_STRING ); SymbolicLinkName, DeviceName RtlInitUnicodeString(&deviceLinkUnicodeString, devicelinkbuffer) ntstatus = IoCreateSymbolicLink (&devicelinkunicodestring, // Erstellen des &devicenameunicodestring); // Symbolic Links -13-

6 Verarbeitung von Systemanfragen in Dispatch Routinen Wenn die Erstellung des Symbolic Links nicht erfolgreich war, kann seitens der Win32 Ebene nicht auf den Treiber zugegriffen werden. Daher muß an dieser Stelle das Device Object wieder gelöscht werden. Hierzu wird die Funktion IoDeleteDevice benutzt. if (!NT_SUCCESS(ntStatus)) MapMemKdPrint (( MAPMEM.SYS: IoCreateSymbolicLink failed\n )); IoDeleteDevice (deviceobject) else MapMemKdPrint (( MAPMEM.SYS: IoCreateDevice failed\n )); Die DriverEntry Routine liefert den Wert ntstatus an den I/O-Manager zurück. return ntstatus; 6 Verarbeitung von Systemanfragen in Dispatch Routinen Wird über eine Anfrage seitens des Systems oder der Win32 Oberfläche der I/O-Manager angewiesen, auf ein Gerät zuzugreifen, so wird im ersten Schritt das Driver Object angesprochen. Mit Hilfe der dort gespeicherten Zeiger wird eine sogenannte Dispatch Routine gestartet, welche die eigentliche Abarbeitung der Anfrage vornimmt. Die Auswahl der benötigten Dispatch Routine erfolgt über den I/O-Operation Code. Für jeden Operation Code kann eine eigene Dispatch Routine existieren. Es ist jedoch auch möglich, mehrere Operation Codes in einer Routine abzuarbeiten. 6.1 Dispatch Routine am Beispiel von MAPMEM Nachdem das Einbinden eines Treibers im vorhergehenden Abschnitt erklärt wurde, wird in diesem Kapitel die Realisierung der eigentlichen Treiberfunktionen beschrieben: NTSTATUS MapMemDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) PIO_STACK_LOCATION irpstack; PVOID iobuffer; ULONG inputbufferlength; ULONG outputbufferlength; ULOAN iocontrolcode; NTSTATUS ntstatus; An dieser Stelle werden die Statusvariablen des I/O Request Packets initialisiert. Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; -14-

6 Verarbeitung von Systemanfragen in Dispatch Routinen Die Längen der Ein- und Ausgabepuffer werden ermittelt. IrpStack inputbufferlength outputbufferlength = IoGetCurrentIrpStackLocation.SystemBuffer; = irpstack->parameters.deviceiocontrol.inputbufferlength; = irpstack->parameters.deviceiocontrol.outputbufferlength; Nachdem alle vorbereitenden Operationen und Initialisierungen durchgeführt worden sind, kann die Abarbeitung der einzelnen Treiberfunktionen erfolgen. Hierbei wird anhand des Operation Codes (IrpStack->MajorFunction) eine Fallunterscheidung durchgeführt. switch (irpstack->majorfunction) Sowohl IRP_MJ_CREATE als auch IRP_MJ_CLOSE erfüllen abgesehen von einer Textausgabe keine Funktion. Da jedoch Create und Close auf jeden Fall vom Treiber unterstützt werden müssen, um einen Dateihandle zu verwalten, sind diese Fallunterscheidungen nötig. case IRP_MJ_CREATE: MapMemKdPrint (( MAPMEM.SYS: IRP_MJ_CREATE\n )); break; case IRP_MJ_CLOSE: MapMemKdPrint ( MAPMEM.SYS: IRP_MJ_CLOSE\n )); break; Das eigentliche Mappen bzw. Unmappen des Speichers geschieht bei der Übergabe des Operation Codes IRP_MJ_DEVICE_CONTROL. case IRP_MJ_DEVICE_CONTROL: Da das Mappen und das Unmappen über den gleichen Operation Code aufgerufen werden, ist an dieser Stelle eine weitere Fallunterscheidung nötig. Diese wird anhand der Variablen Io- ControlCode vorgenommen, die durch die Win32 Funktion DeviceIoControl übergeben wird. iocontrolcode = irpstack->parameters.devicecontrol.iocontrolcode; switch (iocontrolcode) case IOCTL_MAPMEM_MAP_USER_PHYSICAL_MEMORY: Das Mappen des Speichers erfolgt durch eine eigene Funktion, die separat im Treiber definiert wird. Sie liefert einen Status zurück der in Irp->IoStatus.Status gespeichert wird. Schlägt -15-

6 Verarbeitung von Systemanfragen in Dispatch Routinen das Mappen fehl, so wird eine entsprechende Fehlermeldung erzeugt und das Feld Information von Irp->IoStatus gesetzt. Irp->IoStatus.Status = MapMemMapTheMemory ( DeviceObject, iobuffer, inputbufferlength, outputbufferlength); if (NT_SUCCESS(Irp->IoStatus.Status) Irp->IoStatus.Information = sizeof(pvoid); // Mappen erfolgreich MapMemKdPrint (( MAPMEM.SYS: memory successfully mapped\n )); else Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; MapMemKdPrint (( MAPMEM.SYS: memory ma faild : (\n )); break; case IOCTL_MAPMEM_UNMAP_USER_PHYSICAL_MEMORY: Anders als das Mappen des Speichers ist das Unmappen direkt an dieser Stelle realisiert. Vor dem eigentlichen Unmappen durch ZwUnmapViewOfSection wird die Länge des Eingabepuffers überprüft, um bei nicht ausreichenden Systemressourcen eine Fehlermeldung zu erzeugen. if (inputbufferlength >= sizeof(pvoid)) Irp->IoStatus.Status = ZwUnmapViewOfSection (( HANDLE) -1, *((PVOID*) iobuffer) ); MapMemKdPrint (( MAPMEM.SYS: memory successfully unmapped\n )); else Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; MapMemKdPrint (( MAPMEM.SYS: ZwUnmapViewOfSectiob failed\n )); break; default: Dieser Teil der Fallunterscheidung kann nur bei fehlerhaftem I/O-Control Code beim Aufruf der Funktion DeviceIoControl ausgeführt werden. Somit wird an dieser Stelle auch nur eine entsprechende Fehlermeldung ausgegeben und der Status gesetzt. MapMemKdPrint (( MAPMEM.SYS: unknown IRP_MJ_DEVICE_CONTROL\n )); Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; break; break; -16-

7 Driver Unload Routine An dieser Stelle ist zu beachten, daß eine Dispatch Routine einen Status an den I/O-Manager zurückliefern muß. Zuvor wird jedoch das I/O Request Packet durch die Funktion IoCompleteRequest abgeschlossen. Da nun die Statusvariable nicht mehr zur Verfügung steht, muß diese unbedingt vorher zwischengespeichert werden. ntstatus = Irp->IoStatus.Status; IoCompleteRequest (Irp, IO_NO_INCREMENT); return ntstatus; 7 Driver Unload Routine Wird ein Treiber aus dem System entfernt, so müssen alle Ressourcen, die der Treiber belegt, freigegeben werden. Dies geschieht in der umgekehrten Reihenfolge wie das Belegen der Ressourcen in der DriverEntry Routine. Die für die Freigabe verantwortliche Funktion ist die Driver Unload Routine. Die Freigabe erfolgt über die Funktionen IoDeleteSymbolicLink, mit der der Symbolic Link gelöscht wird, und IoDeleteDevice, um das Device Object zu löschen. Anders als bei der DriverEntry Routine liefern diese Funktionen keinen Status zurück. Dies führt zu einer starken Vereinfachung der Driver Unload Routine. 7.1 Unload Routine am Beispiel von MAPMEM Nun soll die Unload Routine des Beispieltreibers MAPMEM genauer betrachtet werden. Void MapMemUnload( In PDRIVER_OBJECT DriverObject) WCHAR devicelinkbuffer[] = L \\DosDevices\\MAPMEM ; Unicode_String devicelinkunicodestring; An dieser Stelle wird der Symbolic Link gelöscht. Der Name wird als Unicode String übergeben. RtlInitUnicodeString (&devicelinkunicodestring, devicelinkbuffer); IoDeleteSymbolicLink (&devicelinkunicodestring); Nachdem der Symbolic Link gelöscht worden ist, kann das Device Object gelöscht werden. MapMemKdPrint (( MAPMEM.SYS: unloading\n )); IoDeleteDevice (DriverObject->DeviceObject); // löschen des Device Objects -17-

8 Aufruf der Treiberfunktionen 8 Aufruf der Treiberfunktionen Nachdem nun das Grundgerüst des Treibers vorgestellt wurde stellt sich nun die Frage, wie man auf die einzelnen Funktionen zugreifen kann. Hierbei kann man prinzipiell den folgenden Ablauf zugrunde legen : Als erstes erfolgt das Öffnen eines Dateihandles mit CreateFile um den Zugriff auf den Treiber zu ermöglichen. Anschließend kann mit diesem Dateihandle über die Win32 Funktionen ReadFile, WriteFile, DeviceIoControl, GetFileSize, SetEndOfFile, FlushFileBuffers, FlushConsoleInputBuffer, PurgeComm und InitiateSystemShutdown auf die Treiberfunktionen zugegriffen werden. Nach Durchführung aller benötigten Zugriffe auf den Treiber erfolgt das Schließen des Dateihandles mit CloseHandle. 8.1 Das Testprogramm MAPTEST Das Testprogramm MAPTEST führt ein Mappen des Speichers gefolgt vom Unmappen des Speichers durch. Hierzu wird der Befehl DeviceIoControl verwendet. Es ist in der Datei MAPTEST.C des Driver Development Kits (DDK) von Microsoft zu finden. #include windows.h #include winioctl.h #include studio.h #include stdlib.h #include ioaccess.h typedef enum _INTERFACE_TYPE Internal, Isa, Eisa, MicroChannel, TurboChannel, MaximumInterfaceType INTERFACE_TYPE, *PINTERFACE_TYPE; typedef LARGE_INTEGER PHYSICAL_ADDRESS; #include mapmem.h int cdecl main( IN int argc, IN char *argv[ ]) HANDLE hdriver; PHYSICAL_MEMORY_INFO pmi; PVOID ppartymem; DWORD cbreturned; ULONG CHAR length; *ainterfacetype[ ] = Internal, Isa, Eisa MicroChannel, TurboChannel ; -18-

8 Aufruf der Treiberfunktionen pmi.interfacetype =(INTERFACE_TYPE) atoi (argv[1]); pmi.busnumber =(ULONG) atoi (argv[2]); pmi.busaddress.highpart=(long) 0x00000000; pmi.addressspace =(LONG) atoi (argv[4]); sscanf (argv[3], %x, &pmi.busadress.lowpart); sscanf (argv[5], %x, &pmi.length); An dieser Stelle wird der Dateihandel geöffnet und in der Variable hdriver gespeichert. Weiterhin wird überprüft, ob die Funktion erfolgreich war. Anschließend wird eine entsprechende Meldung ausgegeben. if ((hdriver = CreateFile( \\\\.\\MAPMEM, GENERIC_READ GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ))!= ((HANDEL)-1)) printf ( \nretrieved valid handle for MAPMEM driver\n ); else printf ( Can t get a handle to MAPMEM driver\n ); return 0; Hier erfolgt der Aufruf zum Mappen des Speichers. Es ist zu erkennen, daß der I/O-Control Code IOCTL_MAPMEM_MAP_USER_PHYSICAL_MEMORY, welcher in der entsprechenden Fallunterscheidung der Dispatch Routine des Treibers verwendet wird, an dieser Stelle als Parameter von DeviceIoControl übergeben wird. Nach der Durchführung von DeviceIoControl wird ein Status zurückgeliefert, welcher den Erfolg der Funktion anzeigt. if (DeviceIoControl( hdriver, (DWORD) IOCTL_MAPMEM_MAP_USER_PHYSICAL_MEMORY, &pmi, sizeof(physical_memory_info), &ppartmem, sizeof(pvoid), &cbreturned, 0 ) ) -19-

9 Schlußbemerkung War das Mappen des Speichers erfolgreich, so kann dieser nun ausgelesen werden. ULONG j; if (ppartymem) UCHAR uc; for (j = 0;j < length; j++) uc = READ_REGISTER_UCHAR( (PUCHAR) ppartymem + j ); WRITE_REGISTER_UCHAR( (PUCHAR)pPartMem + j, 0x47 ); Nun wird das Unmappen des Speichers durchgeführt. Hierzu wird beim Aufruf der Funktion DeviceIoControl der I/O-Control Code IOCTL_MAPMEM_UNMAP_USER_PHYSICAL_MEMORY an den Treiber übergeben. DeviceIoControl( hdriver, (DWORD) IOCTL_MAPMEM_UNMAP_USER_PHYSICAL_MEMORY, &ppartymem, sizeof(pvoid), NULL, 0, &cbreturned, 0); else printf ( ppartymem = NULL\n ); else printf ( DeviceIoControl failed\n ); Abschließend wird der Dateihandel geschlossen und der Zugriff auf den Treiber beendet. CloseHandle(hDriver); Return 1; 9 Schlußbemerkung Die besprochenen Bestandteile der Treiberprogrammierung stellen lediglich die minimalen Anforderungen an einen Treiber dar. Insbesondere wurden die Aspekte des Datentransfers vernachlässigt. Ich möchte diejenigen, die sich intensiver mit diesem Thema beschäftigen möchten, auf die DDK von Microsoft und auf das Nachschlagewerk The Windows NT Device Driver Book von Art Baker verweisen. Hierbei möchte ich insbesondere hervorheben, daß die Programmierung eines Treibers ohne die DDK nicht möglich ist. -20-

10 Literaturverzeichnis 10 Literaturverzeichnis Online Hilfe der Microsoft DDK Art Baker: The Windows NT Device Driver Book 1997 by Prentice Hall PTR ISBN 0-13-184474-1 -21-