ainDlg", 0, (FARPROC) MainDlgProc) ; BogusStop() ; } } else MessageBox(0, "Bogus device not found", "WinTest", MB_ICOMMAND|MB_OK|MB_APPLMODAL) ; } else MessageBox(0, "Another instance already running", "WinTest", MB_ICONEXCLAMATION|MB_OK|MB_APPLMODAL) ; return 0 ; } /* конец файла */ ______________________________________________________________________ Листинг 5. Программа wintest.c. Драйвер виртуального устройства Файл vxd2.asm (листинги 6 и 7) представляет собой исходный код драйвера фиктивного устройства. Следует отметить, что для того, чтобы построить этот драйвер, необходимо иметь комплект драйверов устройств (Device Driver Kit - DDK) системы Windows фирмы Microsoft, т.к. код написан для 32-битового ассемблера, предусмотренного в комплекте DDK (MASM5). Результирующий модуль может быть скомпонован только DDK-компоновщиком LINK386 и утилитой послекомпоновочной обработки ADDHDR. Кроме того, данный исходный код ссылается на определенное количество включаемых файлов (include files), которые входят в состав только комплекта DDK. Как было указано, типичный драйвер VxD содержит обязательные включаемые файлы, а кроме того он начинается с вызова макроса Declare _Virtual_Device, который создает блок данных, описывающий виртуальный драйвер для ядра системы Windows. Этот блок данных, фактически, - единственное обозначение, экспортируемое из драйвера VxD. Все остальные точки входа являются производными от данных, содержащихся внутри. Кроме всего прочего, данный макрос описывает имя устройства, порядок его инициализации и его точки входа. Виртуальный драйвер VxD может обслуживать запросы приложений как в реальном, так и в защищен- ном режимах. Точки входа для такого обслуживания также описываются данным макросом. _____________________________________________________________________ PAGE ,132 title VxD2B.ASM - Пример драйвера устройства #2b ;EM VxD2B - Пример драйвера устройства #2b ; ; Copyright 1992, Cherry Hill Software ; All rights reserved ; ; SUMMARY (Резюме) ; Данный драйвер имитирует прерываемое устройство. Порт ; управления (выход) имеет следующее назначение битов: ; ; Бит 0 - Начать (Start) ввод-вывод. Запись нуля в данный бит ; начинает пересылку ввода-вывода. Пересылка длится ; около 1/10 секунды. Запись единицы в этот бит не ; дает результата. ; ; Бит 1 - Устройству посылается EOI. Запись нуля в данный бит ; приводит к посылке признака "Конец прерывания" (End- ; of-interrupt - EOI) устройству и удаляет любой запрос ; на отложенное прерывание. Запись единицы в этот бит не ; дает результата. ; Все остальные биты: Всегда записываются единицы для дальнейшей ; совместимости. ; ; При чтении порта следующие значения возвращаются: ; ; Бит 0 - Первоначально присваивается значение 1, бит ; сбрасывается, когда бит 1 выходного порта сбрасывается, ; и устанавливается, когда добавляется запрос на ; прерывание. Данный бит равен нулю, когда устройство ; передает данные и устанавливается в 1, чтобы указать, ; когда передача завершена. ; ; Бит 1 - Первоначально присваивается значение 1, бит ; сбрасывается, когда добавляется запрос на прерывание и ; устанавливается, когда устройство удаляет запрос на ; прерывание. Значение данного бита, равное нулю, ; указывает на отложенное прерывание, бит устанавливается ; в 1, если нет отложенного прерывания. ; ; Все остальные биты: возвращаемое значение игнорируется для ; дальнейшей совместимости. ; ; WARNINGS (Предупреждения) ; ; .386p .xlist include vmm.inc include debug.inc include v86mmgr.inc include vpicd.inc include ..\include\bogus.inc .list VM_Not_Executable equ VM_Not_Executeable ; acckk; subttl VxD Declaration/Definition page VxD2B_Init_Order equ VNETBIOS_Init_Order+100 ; Данная операция выполняется после запуска виртуальной сети VxD2B_Device_ID equ Bogus_Device_ID Declare_Virtual_Device VXD2, 1, 0, Vxd2B_Control, VxD2B_Device_ID, \ VxD2B_Init_Order VxD_DATA_SEG ; ; Структура дескриптора виртуального прерывания ; ; Данная структура передается VPIDC_Virtualize_IRQ. В данной ; структуре описывается уровень прерывания, процедура прерывания ; аппаратных средств, и процедура, которую вызывает VPICD, когда ; прерывание диспетчируется в виртуальной машине VM и когда VM ; возвращается из прерывания. ; IRQD VPICD_IRQ_Descriptor hIRQ dd -1 ; обработчик IRQ hOwner dd -1 ; обработчик, владеющий VM hTimeout dd 0 ; обработчик к обратному вызову по тайм-ауту bFakeData dd 01111111b ; имитировать данные порта ввода-вывода VxD_DATA_ENDS subttl Dispatch VxD Control page VxD_LOCKED_CODE_SEG BeginProc CheckOwner, NO_LOG cmp ebx,hOwner jne short col ret ; выйти, если вызывается владелец col: cmp hOwner,-1 jne short co2 ; пропустить, если вызов не владельца mov hOwner,ebx ;установить владельца ret co2: mov al,-1 ret EndProc CheckOwner BeginProc TimeoutProc mov hTimeout,0 ;почистить обработчик cmp edx,hOwner ; все еще тот же владелец? jne short tol ; пропустить, если нет test bFakeData,FAKE_STAT_BUSY ;отложенный ввод-вывод? jnz short tol ; пропустить, если нет cmp hOwner,-1 ; имеется ли владелец? je short tol ; пропустить, если нет mov eax,hIRQ mov ebx,hOwner VxDcall VPICD_Set_Int_Request ;добавить прерывание mov al,bFakeData and al,NOT (FAKE_STAT_IRQ) ; указывает также в порте состояния or al,FAKE_STAT_BUSY ; указывает, что больше не занято mov bFakeData,al tol: ret End Proc TimeoutProc ;IP Port_IO_Callback - выполняет доступ к FAKE_PORT ; ; ENTRY (вход) ; EAX - выходное значение (для выходных операторов) ; EBX - обработчик к текущему VM ; ECX - тип операции ввода-вывода ; DS,ES - FLAT ; ; EXIT (выход) ; EAX - входное значение (для входных операторов) ; ; WARNINGS (предупреждения) ; ; NOTES (примечания) ; Следует отметить, что мы даже не смотрим регистровый фрейм ; клиента. ; ; Мы просто читаем и увеличиваем. ; ; CALLS (вызовы) BeginProc Port_IO_Callback, NO_LOG Dispatch_Byte_IO Fall_through,Port_Output_Callback Port_Input_Callback: call CheckOwner jc short ioexit mov al,bFakeData or bFakeData,FAKE_STAT_ERROR ; почистить отложенную ошибку ioexit: ret Port_Output_Callback: call CheckOwner jc short ioexit ;игнорировать ввод-вывод, если не владелец test al,FAKE_CTL_START jnz short,poc1 ;пропустить, если не начинается ввод-вывод test bFakeData,FAKE_START_BUSY jz short,poc1 ;пропустить, если уже занято test bFakeData,FAKE_START_IRQ jz short,poc1 ;пропустить, если отложенное IRQ push eax push edx and bFakeData,NOT (FAKE_STAT_ERROR) ; предположить ошибку mov eax,100 ; обратный вызов в 1/10 секунды mov edx,hOwner ; передать владельца обратному вызову mov esi,OFFSET32 TimeoutProc VMMcall Set_VM_Time_Out pop edx pop eax or esi,esi jz short,poc1 ;пропустить, если ошибка and bFakeData,NOT (FAKE_STAT_BUSY) ; указать на занятость or bFakeData,FAKE_STAT_ERROR ; в противном случае почистить индикацию ошибки mov hTimeout,esi ;сохранить обработчик тайм-аута poc1: test al,FAKE_CTL_EOI jnz short poc2 ; пропустить, если не посылается EOI test bFakeData,FAKE_STAT_IRQ ;прерывание отложено? jnz short poc2 ; пропустить, если нет or bFakeData,FAKE_STAT_IRQ ;показать, что прерывание уже не- отложеное push eax mov eax,hIRQ VxDcall VPICD_Clear_Int_Request pop eax poc2: ret EndProc Port_IO_Callback ; ECX == 0 if unmasking (enabling), ECX != 0 if masking (disabling). BeginProc VxD2_Mask_Change_Proc call CheckOwner jc short mcp9 ; игнорировать, если нет владельца jcxz mcp9 ; пропустить, если не маскировано (включено) ; ; Владелец освобождает управление. Разрешается другой VM войти в ; систему. ; mov hOwner,-1 ; почистить владельца mcp9: ret EndProc VxD2_Mask_Change_Proc ; Вызывается, когда выполняется программа ISR BeginProc VxD2_VInt_Proc mov eax,High_Pri_Device_Boost VMMCall Adjust_Exec_Priority ;повышенный приоритет для начальной обработки ret EndProc VxD2_VInt_Proc ; вызывается при возврате из программы ISR (IRETs) BeginProc VxD2_IRET_Proc mov eax,-(High_Pri_Device_Boost) VMMCall Adjust_Exec_Priority ;восстановить приоритет ret EndProc VxD2_IRET_Proc ifdef DEBUG BeginProc VxD2B_Debug_Query Trace_Out "VxD2 has no debug command support." clc ret End Proc VxD2B_Debug_Query endif ; ; VxD2B_Control ; CtlDisp macro x Control_Dispatch x, VxD2B_&x endm Begin_Control_Dispatch VxD2B CtlDisp Device_Init ifdef DEBUG CtlDisp Debug_Query endif End_Control_Dispatch VxD2B VxD_LOCKED_CODE_ENDS VxD_CODE_SEG VxD_CODE_ENDS subttl VxD Initialization page VxD_ICODE_SEG page ; EP VxD2B_Device_Init - Некритическая инициализация устройства ; ; ENTRY (вход) ; EBP - фрейм клиента ; EBX - системный обработчик VM ; DS,ES - FLAT ; ; EXIT (выход) ; SUCCESS (успешный) ; Carry clear ("нет переноса") ; FAILURE (аварийный) ; Carry set ("есть перенос") ; ; ; WARNINGS (предупреждения) ; ; NOTES (примечания) ; ; CALLS (вызовы) ; BeginProc VxD2B_Device_Init Debug_Out "VxD2B_Device_Init" mov edi,OFFSET32 IRQD VxDcall VPICD_Virtualize_IRQ ; виртуализировать прерывание jc short vdi1 ; выход, если ошибка mov hIRQ,eax ; сохранить обработчик mov edx,FAKE_PORT mov esi,OFFSET32 Port_IO_Callback VMMCall Install_IO_Handler VMMCall Enable_Global_Trapping ; clc ; нет ошибки vdi1: ret EndProc VxD2B_Device_Init VxD_ICODE_ENDS VxD_REAL_INIT_SEG VxD2B_Real_Init LABEL FAR ;вызывается перед тем, как система Windows входит в защищенный режим mov ax,Device_Load_OK ;позволяет VxD загрузиться xor bx,bx ; нет исключенных (Exclude) страниц EMM xor si,si ; нет элементов экземпляров данных ; передать edx немодифицированным ret VxD_REAL_INIT_ENDS END VxD2B_Real_Init ; Конец файла _____________________________________________________________________ Листинг 6. Программа vxd2.asm _____________________________________________________________________ LIBRARY VXD2 DESCRIPTION 'Enhanced Windows VXD2(B) Device (Version 1.0)' EXETYPE DEV386 SEGMENTS _LTEXT PRELOAD NONDISCARDABLE _LDATA PRELOAD NONDISCARDABLE _ITEXT CLASS 'ICODE' DISCARDABLE _IDATA CLASS 'ICODE' DISCARDABLE _TEXT CLASS 'PCODE' NONDISCARDABLE _DATA CLASS 'PCODE' NONDISCARDABLE EXPORTS VXD2_DDB @1 _____________________________________________________________________ Листинг 7. Программа vxd2.def События, управляющие устройством По мере того, как система Windows в своей работе проходит различные стадии, начиная со стадии инициализации самой системы, через инициализацию виртуальной машины VM и так далее, каждый установленный драйвер VxD вызывается неоднократно, а именно один раз на каждую стадию. В таблице, приведенной ниже, перечисляются фазы системы Windows и главные события, для которых вызывается каждый драйвер VxD. Таблица Управляющие сообщения драйвера VxD -------------------+-------------------------------------------------- Sys_Critical_Init | Первое управляющее событие; прерывания | отключаются. Драйвер VxD определяет готовность | устройства. -------------------+-------------------------------------------------- Device_Init | Прерывания разрешаются; драйвер VxD инициализиру- | ет устройство; могут быть вызваны программы и | драйверы системы DOS. -------------------+-------------------------------------------------- Init_Complete | Указывает, что все драйверы VxD прошли стадию | Device_Init. -------------------+-------------------------------------------------- System_Exit | Указывает, что система Windows готовится к | закрытию и возврату в систему DOS. Память для | системы DOS восстановлена в состояние, которое | было до работы системы Windows. -------------------+-------------------------------------------------- Sys_Critical_Exit | Последнее управляющее событие; прерывания | отключаются. -------------------+-------------------------------------------------- Create_VM | Вызывается перед моментом создания виртуальной | машины VM; драйвер VxD указывает, доступны ли | ресурсы для создания виртуальной машины VM. -------------------+-------------------------------------------------- VM_Critical_Init | Вторая фаза создания виртуальной машины VM. -------------------+-------------------------------------------------- VM_Init | Третья фаза создания виртуальной машины VM. Sys_VM_Init | Драйвер VxD может аварийно завершить работу | виртуальной машины VM. -------------------+-------------------------------------------------- Query_Destroy | Позволяет драйверу VxD предупредить пользователя | о затруднениях при разрушении виртуальной машины | VM. -------------------+-------------------------------------------------- VM_Terminate | Первая стадия успешного завершения виртуальной Sys_VM_Terminate | машины VM. Если это системная виртуальная | машина VM, то сообщение указывает, что | производится нормальное, вызванное пользователем, | завершение системы Windows. -------------------+-------------------------------------------------- VM_Not_Executeable | Виртуальная машина VM закрывается. Первая стадия | аварийного завершения виртуальной машины VM. -------------------+-------------------------------------------------- Драйвер VxD примера выполняет управление только фазой Device_Init. На этой стадии устанавливается связь с портом ввода- вывода и уровнем прерывания 11, а также производится их виртуализация. Обычно драйвер VxD виртуализирует порты ввода-вывода и прерывание в соответствии с физическим аппаратным оборудованием. Но в данном случае драйвер VxD может виртуализировать и делает это с портом и прерыванием, которые не имеют соответствующего подключенного аппаратного оборудования. Код Install_IO_Handler вызывается, чтобы виртуализировать единственный порт ввода-вывода. Затем всякий раз, когда осуществляется доступ к описанному порту ввода-вывода из виртуальной машины VM, программа управления виртуальной машиной системы Windows (Virtual Machine Manager - VMM) вызывает обратно драйвер VxD для того, чтобы разрешить ему имитировать операции ввода-вывода. Код VPICD_Virtualize_IRQ вызывается, чтобы виртуализировать уровень прерывания. Выполняя его, можно имитировать прерывание аппаратного оборудования (в частности IRQ 11) в виртуальной машине. "Фиктивное" устройство Когда к порту ввода-вывода (141) устройства осуществляется доступ виртуальной машиной VM (либо в реальном, либо в защищенном режиме), то машина вызывает программу драйвера VxD Port_IO_Callback (см. Листинг 6). В этой программе подпрограмма Dispatch_Byte_IO сводит большое количество возможных типов доступа ввода-вывода (а именно: byte, word, dword, string и т.д.) к двум: байтовому вводу и байтовому выводу. Для устройства из примера байтовый ввод представляет собой чтение из регистра состояния устройства. Он возвращает просто переменную, которая сохраняется в памяти. Байтовый вывод - немного более сложная операция, так как представляет фактическую работу устройства. При запуске устройства также запускается таймер, который выполняет обратный вызов (к коду TimeoutProc) в течении 1/10 секунды и устанавливает состояние BUSY. Если вывод подтверждает прием прерывания, то производится очистка виртуального запроса на прерывание путем вызова кода VPICD_Clear_Int_ Request и очистка состояния в регистре состояния. Обратный вызов кода TimeoutProc представляет завершение операции ввода-вывода на устройстве и именно в данный момент он моделирует прерывание аппаратного оборудования к виртуальной машине VM путем вызова кода VPICD_Clear_Int_Request и очистки состояния занятости устройства. Драйвер устройства в приложениях dostest и wintest будет обычно обрабатывать прерывание путем подтвержения приема его (посылая EOI) и повторного запуска процесса на всем протяжении снова. Следует отметить процедуры VxD2_VInt_Proc и VxD2_IRET _Proc. На данные две процедуры существует ссылка в структуре, которая передается коду VPICD_Virtualize_IRQ. Они вызываются в начале и конце процесса виртуализации прерывания в виртуальную машину VM. Все их функции сводятся к увеличению и сохранению приоритета виртуальной машины VM, которая временно обрабатывает данное прерывание. Таким способом драйвер VxD может управлять приоритетом виртуальной машины VM, которая считается соответствующей. (Всегда желательно, чтобы программа обслуживания прерывания в любой виртуальной машине VM имела приоритет выше, чем приоритет обычной обработки в других виртуальных машинах VM.) Установка драйвера VxD После построения драйвера VxD, до первого обращения к нему программы Windows необходимо добавить его как строку device= в секцию [386Enh] кода system.ini. Система Windows должна быть запущена заново, чтобы включить драйвер VxD и виртуальное устройство. После этого, можно выполнять и тестировать приложения dostest и wintest. Заключение Хотя драйверы устройств системы Windows кажутся в настоящее время очень сложными, обычные и виртуальные драйверы устройств предоставляют огромное количество возможностей. Однако следует учитывать, насколько более сложными они должны быть на машине MIPS, эксплуатирующей систему Windows NT и код эмулятора 80x86, чтобы обеспечить работу виртуальной машины системы MS-DOS.