Драйверы устройств в системе Windows

Интерфейс системы MS-DOS для защищенного режима


     Чтобы  установить  связь  с  вектором  реального  режима  из кода системы Windows защищенного режима, необходимо работать с  интерфейсом системы  MS-DOS   для  защищенного   режима  (MS-DOS   Protected  Mode Interface -  DPMI). (Текущая  версия DPMI  представляет собой  уровень 1.0, но система Windows  наиболее полно реализует только  уровень 0.9. Некоторые функции уровня 1.0 реализованы в системе Windows 3.1.)

     Функция   DPMI_SetRMVector   вызывает   интерфейс   DPMI,   чтобы установить вектор реального режима.  Можно видеть, что интерфейс  DPMI взаимодействует  через  регистры  (регистр  AX содержит функциональный код)  и  INT31h.  Автор  включил  высокоуровневый  интерфейс  в данную и  другие  функции  DPMI  (доступен  только  на  диске  кодов  или   в интерактивном  режиме),  чтобы  можно  было  иметь доступ к интерфейсу DPMI из  языка Си  и выделил  код, написанный  на языке  ассемблер, на случай, если возникнет  необходимость использовать что-то  отличное от компилятора Си фирмы Microsoft.

     Функция  DPMI_AllocateRMCallback  вызывает  интерфейс DPMI, чтобы распределить обратный  вызов (callback),  представляющий собой  адрес, вызываемый  из  реального  режима,  который  передает  управление коду защищенного  режима.  Например,  программа  TSR  системы  MS-DOS может вызвать код в библиотеке DLL системы Windows через обратный вызов.

     Функция  DPMI_AllocateRMCallback  принимает  два параметра: адрес кода  защищенного   режима,  который   будет  вызываться   обратно,  и регистровую структуру,  которая обновляется  при выполнении  реального обратного  вызова,   таким  образом   код  защищенного   режима  может исследовать содержимое регистров  реального режима во  время обратного вызова.

     Функция  DPMI_FreeRMCallback  освобождает  все структуры, которые были    распределены    в    результате    обращения      к    функции DPMI_AllocateRMCallback. Функция DPMI_FreeRMCallback должна вызываться только тогда, когда больше нет необходимости в обратном вызове.




     При написании драйвера,  который будет выполняться  в стандартном режиме  работы  системы  Windows,  необходимо  учитывать   возможность появления  прерывания,  когда  процессор  работает  в реальном режиме. Даже если работают только приложения системы Windows, а не  приложения системы  MS-DOS,   процессор  часто   переключается  из   реального  в защищенный  режим.   Так  как   система  Windows   3.1  не    является операционной  системой,   а  скорее   представляет  собой    окружение пользовательского интерфейса,  она возлагает  выполнение определенного количества основных функций,  включая функцию ввода-вывода  файлов, на операционную систему (а именно MS-DOS).

     Поэтому  когда  приложение  системы  Windows  выполняет   функцию MS-DOS ввода-вывода  файла и  процессор при  этом работает  в реальном режиме, устройство может прерывать ЦПУ. По умолчанию, если  библиотека DLL обеспечила связь с прерыванием, то система Windows переключит  ЦПУ в защищенный режим  для обработки прерывания  и, как только  программа ISR  завершит  работу,  переключит  ЦПУ  обратно  в реальный режим для продолжения выполнения функций системы MS-DOS.

     Хотя  это  в  меньшей  мере  относится  к ЦПУ 80386, переключение процессора  из  защищенного  режима  в  реальный  режим,  например  на процессоре  80286,  создает  огромные  накладные  расходы,   требующие контролируемого   сброса   ЦПУ,   который   выполняется   в    течении миллисекунд.  Если  необходимо  ускорить  среднее  время ответа, нужно предотвратить  переключение  процессора  в  защищенный  режим, если он получает прерывание, работая в реальном режиме.

     Обеспечение связи  с вектором  прерывания в  защищенном режиме из библиотеки  DLL  системы  Windows  -  тривиально,  что  и  показано  в программе  SetPMVector,   представленной  в   листинге  4   (программа bogus.c).  Установление  связи  с   вектором  производится  таким   же способом,  как  и  в  системе  MS-DOS,  -  с помощью функции setvector системы  MS-DOS.  Однако  в  отличие  от  вызова  в  системе MS-DOS, в системе  Windows  при  обращении  к  функции  передаются  селектор   и смещение, а  не сегмент  и смещение.  Ядро системы  Windows следит  за всем.  Функции  следует  передать   нормальный  селектор  и   смещение (натуральный  указатель  far  для  системы  Windows),  а  не сегмент и смещение (натуральный указатель far для системы MS-DOS).



     Однако,  как  уже  упоминалось,  установления  связи  с  вектором прерывания  в   защищенном  режиме   недостаточно.  Необходимо   также обеспечить связь  с вектором  прерывания в  реальном режиме,  а это не тривиальная задача.



______________________________________________________________________

/*EM  BOGUS.C - Драйвер фиктивного устройства библиотеки DLL

*

* SUMMARY (Резюме)

*     Базовые функции LibMain, WEP

*

* COMMENTS (Комментарии)

*

* WARNINGS (Предупреждения)

*

*/

#include

#include "bogusa.h"

#include "pic.h"

#include "dpmi.h"

#define EXPORT _export _loadds

#include "bogus.h"

#define FAKE_PORT   0x141 /* Уровень фиктивности (bogosity) - 9.4 */

#define FAKE_IRQ    11   /* Уровень фиктивности (bogosity) - 9.8 */

#define FAKE_CTL_START   0x01

  /* команда "начать" фиктивного порта (устанавливается в нуль) */

#define FAKE_CTL_EOI     0x02

  /* EOI фиктивного порта  */

#define FAKE_STAT_BUSY   0x01

  /* индикация занятости фиктивного порта (zero=>busy) */

#define FAKE_STAT_IRQ    0x02

  /* IRQ фиктивного порта (zero=>IRQ) */

#define FAKE_STAT_ERROR  0x04

  /* ошибка ввода-вывода (zero=>error) (сбрасывается при чтении) */

/* Установить переменные для нашего номера прерывания */

#if (FAKE_IRQ<8)

#define INT_DEV (INT_MASTER_0+(FAKE_IRQ & 7))

#define PIC00 INTA00

#define PIC01 INTA01

#else

#define INT_DEV (INT_SLAVE_0+(FAKE_IRQ & 7))

#define PIC00 INTB00

#define PIC01 INTB01

#endif

#define INT_MASK (1 << (FAKE_IRQ & 7))

BOOL FAR PASCAL LibMain(HANDLE hInstance

   /* обработчик библиотечного экземпляра*/

                ,WORD wDataSeg

   /* сегмент данных по умолчанию */

                ,WORD cbHeap

   /* размер динамической области по умолчанию */

                ,LPSTR lpszCmdLine) ;

   /* командная строка */

int FAR PASCAL WEP(int fSystemExit) ;

#pragma alloc_text(INIT_TEXT,LibMain)



   /* держать вместе с LIBENTRY.ASM  */

#pragma alloc_text(FIXED_TEXT,WEP)

HANDLE hLibInstance ;

FARPROC lpfnPrevISR ;  /* Сохраненная предыдущая программа ISR*/

DWORD lpfnPrevRMISR ;

    /* Сохраненная предыдущая программа ISR реального режима*/

HANDLE hReflector ;

DWORD DPMI_AllocateRMCallback(FARPROC lpfnCallback,

_RMCS FAR *lpRMCS)

{

     DWORD dwRet ;

     _asm {

     push    ds

     lds si,lpfnCallback

     les di,lpRMCS

     mov ax,DPMI_ALLOCRMC

     int IVEC_DPMI

     pop ds

     jc  lbl1

     mov word ptr dwRet,dx  ; возврат адреса обратного вызова

     mov word ptr dwRet+2,cx

     jmp short lbl2

lbl1:

     mov word ptr dwRet,ax  ; код ошибки в регистре ax

     mov word ptr dwRet+2,0 ; возвратить seg=0,если произошла ошибка

lbl2:

     }

     return dwRet ;

}

DWORD DPMI_FreeRMCallback(FARPROC lpfnCallback)

{

     DWORD wRet ;

     _asm {

     mov dx,word ptr lpfnCallback

     mov cx,word ptr lpfnCallback+2

     mov ax,DPMI_FREERMC

     int IVEC_DPMI

     jc  lbl1

     xor ax,ax

lbl1:

     mov wRet,ax

     }

     return wRet ;

}

DWORD DPMI_GetRMVector(int iVector)

{

     DWORD dwRet ;

     _asm {

     mov ax,DPMI_GETRMVEC

     mov bl,byte ptr iVector

     int 31h

     mov word ptr dwRet,dx

     mov word ptr dwRet+2,cx

     }

     return dwRet ;

}

void DPMI_SetRMVector(int iVector, DWORD lpfnRMISR)

{

     _asm {

     mov ax,DPMI_SETRMVEC

     mov bl,byte ptr iVector

     mov dx,word ptr lpfnRMISR

     mov cx,word ptr lpfnRMISR+2

     int 31h

     }

}

FARPROC GetPMVector(int iVector)

{

      FARPROC dwRet  ;

     _asm {

     mov bl,byte ptr iVector

     mov ah,35h

     int 21h

     mov word ptr dwRet,bx

     mov word ptr dwRet+2,es  ; Сохранить

     }

     return dwRet ;

}

void SetPMVector(int iVector, FARPROC lpfnISR)

{

     _asm {

     push    ds

     lds dx,lpfnISR

     mov al,byte ptr iVector

     mov ah,25h



     int 21h       ; Установить нашу программу ISR

     pop ds

     }

}

HANDLE AllocIntReflector(int iVector, FARPROC lpfnCallback)

{

     DWORD dwDosMem ;

     LPSTR lpLowRMISR ;

     DWORD lpfnRMCallback ;

     _RMCS FAR *lpSaveRegs ;

/* Распределить память DOS для программы обслуживания прерывания ISR,

*работающей в реальном режиме */

     dwDosMem = GlobalDosAlloc(16 + sizeof (int) + sizeof (_RMCS) ;

     if (dwDosMem == 0)

     return 0;

     lpLowRMISR = (LPSTR) MAKELONG(0,LOWORD(dwDosMem)) ;

     lpSaveRegs = (_RMCS FAR *) (&lpLowRMISR[16]) ;

/* Распределить   обратный  вызов (callback), работающий  в  реальном

* режиме */

     lpfnRMCallback =

DPMI_AllocateRMCallback((FARPROC)lpfnCallback,

lpSaveRegs)

;

     if (HIWORD((DWORD)lpfnRMCallback == 0)

     {

     GlobalDosFree(LOWORD(dwDosMem)) ;

     return 0;

     }

     /* Сгенерировать код в нижних адресах памяти (только 6 байтов)*/

     lpLowRMISR[0] = 0x9A ; /* Вызов указателя на FAR */

     *((DWORD FAR *)&(lpLowRMISR[1])) = lpfnRMCallback ;

     lpLowRMISR[5] = 0xCF ;   /*IRET */

     *((int FAR *)&(lpLowRMISR[6])) = iVector ;

     /* Установить связь с вектором прерываний реального режима */

     DPMI_SetRMVector(iVector,MAKELONG(0,HIWORD(dwDosMem))) ;

     return (HANDLE) LOWORD(dwDosMem) ;

     /* возврат обработчика-отражателя */

}

void FreeIntReflector(HANDLE hReflector)

{

     LPSTR lpLowRMISR ;

     DWORD lpfnRMCallback ;

     /* Получить адрес нижнего ISR в защищенном режиме */

     lpLowRMISR = (LPSTR)MAKELONG(0,(WORD)hReflector) ;

     /* Следует убедиться, что это отражатель */

     if ((lpLowRMISR[0] != 0x9A) || (lpLowRMISR[5] != 0xCF))

     return ;   /* выход, если не отражатель */

    /* Выбрать адрес обратного вызова и освободить обратный вызов */

     lpfnRMCallback = *((DWORD FAR *)&((lpLowRMISR[1])) ;

     DPMI_FreeRMCallback(lpfnRMCallback) ;



  /* Освободить программу обслуживания прерываний реального режима*/

     GlobalDosFree((WORD)hReflector) ;

}

/*XP<   LibMain - основная библиотечная точка входа */

*

*  ENTRY (вход)

*

*  EXIT (выход)

*

*  RETURNS (возврат)

* Если инициализация завершается  успешно принимает  значение, равное

* TRUE, в противном случае - FALSE

*

*  WARNINGS (предупреждения)

*

*  CALLS (вызовы)

*

*  NOTES (примечание)

* Настоящая библиотечная точка входа  находится в ассемблерном модуле

* LIBENTRY.ASM, а в данную точку просто передается управление

*

*/

BOOL FAR PASCAL LibMain(HANDLE hInstance

   /* обработчик библиотечного экземпляра*/

                ,WORD wDataSeg

   /* сегмент данных по умолчанию */

                ,WORD cbHeap

   /* размер динамической области по умолчанию */

                ,LPSTR lpszCmdLine) ;

   /* командная строка */

/*>*/

{

     lpszCmdLine = lpszCmdLine ;

     /* Избегать предупреждения -W4   */

     wDataSeg = wDataSeg ;

     cbHeap = cbHeap ;

     hInstance = hInstance ;

     /* Это может понадобиться позже для доступа к ресурсам из нашего

     *исполнительного модуля */

     return TRUE ;

}

/*XP<   WEP - процедура выхода в системе Windows */

*

*  ENTRY (вход)

* fSystemExit указывает  на завершение  сессии  в системе  Windows. В

* противном случае происходит только разгрузка данной библиотеки DLL.

*  RETURNS (возврат)

* Всегда возвращается значение 1

*

*  WARNINGS (предупреждения)

* Из-за  ошибок  в системе  Windows 3.0  и  более  ранних  версиях (а

* возможно и  в более  поздних  версиях) данная  функция  должна быть

*помещена в фиксированный сегмент. Эти же ошибки приводят к тому, что

* значение DS  сомнительно,  а поэтому нельзя его использовать (также

* как и любые статические данные).

*

* В любом случае, несомненно не надо ничего делать в этой точке.

*

*  CALLS (вызовы)

* Нет

*  NOTES (примечания)

* Это стандартная процедура выхода DLL.

*



*/

int FAR PASCAL WEP(int fSystemExit)

/*>*/

{

     fSystemExit = fSystemExit

     /* Избегать предупреждения -W4   */

     return 1 ;   /* всегда указывает на успешное завершение */

}

int EXPORT FAR PASCAL BogusCheck(void)

{

     BYTE bPortVal ;

     _asm {

     mov dx,FAKE_PORT

     in  al,dx        ; Присутсвует фиктивное устройство ?

     mov bPortVal,al

     }

     return ((bPortVal & 0x80) == 0) ;

     /* Возвращает значение TRUE, если устройство присутствует */

}

void EXPORT FAR PASCAL BogusStart(HWND hWnd, WPARAM wParam)

{

     wParamEvent = wParam ;

     hWndEvent = hWnd ;

     if (!lpfnPrevISR)

     {

     /* Сохранить предыдущую программу ISR и загрузить новую */

     _asm cli

     lpfnPrevISR = GetPMVector(INT_DEV) ;

     SetPMVector(INT_DEV,(FARPROC)IntSvcRtn) ;

     _asm sti

     /* Сохранить предыдущую программу ISR реального режима и

     *отразить на новую    */

     lpfnPrevRMISR = DPMI_GetRMVector(INT_DEV) ;

     hReflector = AllocIntReflector(INT_DEV,(FARPROC)BogusCallback) ;

     /* Разрешить прерывание и начать операцию ввода-вывода на

     *устройстве  */

     _asm {

          cli

          in    al,PIC01      ; разрешить прерывание

          and   al,NOT INT_MASK

          out   PIC01,al

          sti

          mov   al,NOT FAKE_CTL_START

          mov   dx,FAKE_PORT

          out   dx,al    ;начать операцию ввода-вывода на устройстве

          }

     }

}

int EXPORT FAR PASCAL BogusGetEvent(void)

{

     WORD wCountRet ;

     _asm {

     mov ax,SEG wCount

     mov es,ax

     xor ax,ax

     xchg   ax,es:wCount     ;получить счетчик, установить в нуль

     mov wCountRet,ax

     }

     return wCountRet ;

}

void EXPORT FAR PASCAL BogusStop(void)

{

     hWndEvent - 0x0000  ;  /*команда для ISR "завершить работу"*/

     if (!lpfnPrevISR)

     return ;      /*  возвратиться, если программа не стартовала */

     _asm {

          mov dx,FAKE_PORT

     l1:

          in  al,dx

          rcr al,1

          jnc l1     ; цикл, если занято

          cli

          in  al,PIC01

          or  al,INT_MASK

          out PIC01,al   ; маскировать уровень прерывания

          sti

          }

     DPMI_SetRMVector(INT_DEV, lpfnPrevRMISR) ;

     /* Восстановить вектор реального режима */

     FreeIntReflector(hReflector) ;

     /* Освободить отражатель */

     SetPMVector(INT_DEV, lpfnPrevISR) ;

     /* Восстановить вектор защищенного режима */

     lpfnPrevISR = NULL ;

}

/* конец файла*/

_____________________________________________________________________

     Листинг 4. Программа bogus.c


Содержание раздела