Основы разработки прикладных виртуальных драйверов
Страница 2.


 

Главная функция WinMain() выполняет три характерные для нее процедуры: регистрацию класса главного окна, создание и показ главного окна, цикл обработки сообщений. В цикле обработки сообщений имеется два принципиальных отличия от примера предыдущей части статьи: в каждом шаге цикла проверяется состояние флага завершения измерений request, и если флаг оказывается установленным, то вызывается функция Windows PostMessage(), которая ставит в очередь сообщений приведенного выше приложения наше сообщение с кодом WM_USER. Для того чтобы в цикл обработки сообщений включить проверку флага, пришлось заменить в нем функцию GetMessage() на функцию PeekMessage(), которая, в отличие от GetMessage(), при отсутствии сообщений в очереди возвращает управление в программу, что и дает возможность включить в цикл дополнительные действия. Однако PeekMessage() не анализирует сообщение WM_QUIT о завершении программы, поэтому <вылавливание> этого сообщения (и завершение программы оператором return 0 в случае его прихода) приходится выполнять вручную. Конструкция:

do{  
... 
   }while(1)            

позволяет организовать бесконечный цикл, поскольку условие продолжения цикла, анализируемое оператором while, безусловно удовлетворяется (константа 1 никогда не может стать равной 0).

В оконной функции WndProc() фиксируется приход трех сообщений: WM_COMMAND от пунктов меню, WM_DESTROY от команд завершения приложения и WM_USER, свидетельствующего об окончании измерений. Поскольку для сообщения WM_USER в файле windowsx.h отсутствует макрос HANDLE_WM_USER, его пришлось определить в начале программы с помощью оператора #define, построив макрорасширение по аналогии с каким-либо из макросов вида HANDLE_сообщение из файла windowsx.h, хотя бы с макросом HANDLE_WM_DESTROY.

Фрагмент программы, выполняемый при выборе пользователем пункта меню <Пуск>, содержит лишь вызов функции InitCard(). В ней вызовом вложенной функции GetAPIEntry определяется адрес API-процедуры драйвера, а затем, после заполнения ряда регистров параметрами, передаваемыми в драйвер, вызывается эта процедура. В драйвер передаются следующие параметры: селектор сегмента данных приложения, три константы для инициализации платы, а также селектор и смещение обработчика прерываний приложения isr(). Передача в драйвер содержимого сегментного регистра DS (селектора сегмента данных) необходима потому, что при вызове драйвером (точнее, VMM) нашей функции isr() не восстанавливается операционная среда приложения, в частности регистр DS не указывает на поля данных приложения, которые в результате оказываются недоступными. Передав в драйвер содержимое DS, мы сможем вернуть его назад вместе с другими данными, передаваемыми из драйвера в приложение, восстановив тем самым адресуемость данных.

При выборе пользователем пунктов меню <Чтение> или <Выход> выполняются те же действия, что и в предыдущем примере.

По сравнению с предыдущим примером упростилась функция OnDestroy(). Поскольку восстановление маски в контроллере прерываний возложено теперь на драйвер, а исходный вектор мы в этом варианте программы не восстанавливаем, то в функции OnDestroy() лишь вызывается функция Windows PostQuitMessage(), приводящая к завершению программы.

В обработчике прерываний приложения isr() после засылки в регистр DS нашего же селектора сегмента данных, переданного ранее в драйвер и полученного из него в качестве первого параметра функции isr(), выполняется инкремент флага request и пересылка в переменную data второго параметра функции isr() - результата измерений.

Перейдем к рассмотрению программы виртуального драйвера, входящего в состав нашего программного комплекса.

Текст виртуального драйвера, обрабатывающего аппаратные прерывания

;При вызове AX=DS приложения, BX=C0, CX=C1, DX=C2, DI=селектор isr ,SI=смещение isr  
.386p 
.XLIST 
include vmm.inc 
include vpicd.inc 
.LIST 
Declare_Virtual_Device VMyD,1,0,VMyD_Control,8000h, \ 
         Undefined_Init_Order,,API_Handler 
;======================= 
VxD_REAL_INIT_SEG 
BeginProc VMyD_Real_Init 
;Текст процедуры инициализации реального режима (см. часть 2 этого цикла) 
EndProc   VMyD_Real_Init 
VxD_REAL_INIT_ENDS 
;====================== 
VxD_DATA_SEG 
Data dw     0     ;Ячейка для результата измерений 
DSseg dw     0     ;Ячейка для хранения селектора приложения 
Segment_Callback dw 0     ;Селектор функции isr приложения 
Offset_Callback dd 0     ;Смещение функции isr приложения 
IRQ_Handle dd 0     ;Дескриптор виртуального прерывания 
VMyD_Int13_Desc label dword;32-битовый адрес следующей далее структуры 
VPICD_IRQ_Descriptor <5,,OFFSET32 VMyD_Int_13>;Структура с информацией 
               ;о виртуализованном прерывании 
VxD_DATA_ENDS 
;====================== 
VxD_CODE_SEG 
BeginProc VMyD_Control 
;Включим в состав драйвера процедуру обработки системного сообщения 
;Device_Init об инициализации драйвера 
Control_Dispatch Device_Init, VMyD_Device_Init 
         clc 
         ret 
EndProc VMyD_Control 
;---------------------- 
;Процедура, вызываемая при инициализации драйвера системой 
BeginProc VMyD_Device_Init 
     mov     EDI,OFFSET32 VMyD_Int13_Desc;Адрес структуры VPICD_IRQ_Descriptor 
     VxDCall VPICD_Virtualize_IRQ;Виртуализация устройства 
     mov     IRQ_Handle,EAX;Сохраним дескриптор виртуального IRQ 
         clc 
         ret 
EndProc VMyD_Device_Init 
;------------------------- 
;API-процедура, вызываемая из приложения  
BeginProc API_Handler 
;Получим параметры из приложения 
     push     [EBP.Client_DI] 
     pop     Segment_Callback 
     push     [EBP.Client_AX];DS 
     pop     DSseg 
     movzx     ESI,[EBP.Client_SI] 
     mov     Offset_Callback,ESI 
;Общий сброс 
     mov     DX,30Ch 
     in     AL,DX 
;Размаскируем уровень 5 в физическом контроллере прерываний 
     mov     EAX,IRQ_Handle 
     VxDCall VPICD_Physically_Unmask  
;Засылаем управляющие слова по каналам 
     mov     DX,303h 
     mov     AL,36h     ;Канал 0 
     out     DX,AL 
     mov     AL,70h     ;Канал 1 
     out     DX,AL 
     mov     AL,0B6h     ;Канал 2 
     out     DX,AL 
;Засылаем константы в каналы 
     mov     DX,300h     ;Канал 0 
     mov     AX,[EBP.Client_BX];Константа С0 
     out     DX,AL     ;Младший байт частоты 
     xchg     AL,AH 
     out     DX,AL     ;Старший байт частоты 
     mov     DX,301h     ;Канал 1 
     mov     AX,[EBP.Client_CX];Константа С1 
     out     DX,AL     ;Младший байт интервала 
     xchg     AL,AH 
     out     DX,AL     ;Старший байт интервала 
     mov     DX,302h     ;Канал 2 
     mov     AX,[EBP.Client_DX];Константа С2 
     out     DX,AL     ;Младший байт частоты 
     xchg     AH,AL 
     out     DX,AL     ;Старший байт частоты 
;Установим флаг S2 разрешения счета 
     mov     DX,30Bh 
     in     AL,DX 
         ret 
EndProc     API_Handler 
;------------------------- 
;Процедура обработки аппаратного прерывания IRQ5 (вектор 13) 
BeginProc VMyD_Int_13, High_Freq 
;Получим результат измерений из выходного регистра счетчика 
     mov     DX,309h     ;Порт старшего байта 
     in     AL,DX     ;Получим старший байт 
     mov     AH,AL     ;Отправим его в AH 
     dec     DX     ;DX=308h 
     in     AL,DX     ;Получим младший байт 
     mov     Data,AX     ;Весь результат в Data 
;Выполним завершающие действия в PIC и вызовем функцию приложения 
     mov     EAX,IRQ_Handle 
     VxDCall VPICD_Phys_EOI;EOI в физический контроллер прерываний 
     VxDCall VPICD_Physically_Mask;Маскируем наш уровень 
;Перейдем на синхронный уровень 
     mov     EDX,0     ;Данные отсутствуют 
     mov     ESI,OFFSET32 Reflect_Int;Адрес синхронной процедуры 
     VMMCall Call_VM_Event;Установим запрос на ее вызов из VMM 
         clc 
         ret 
EndProc VMyD_Int_13 
;------------------------- 
;Процедура уровня отложенных прерываний  
BeginProc Reflect_Int 
     Push_Client_State     ;Выделим место на стеке для регистров клиента 
     VMMCall Begin_Nest_Exec;Начнем вложенный блок выполнения 
     mov     AX,Data     ;Отправим данное 
     VMMCall Simulate_Push;в стек клиента 
     mov     AX,DSseg     ;Отправим полученный ранее DS 
     VMMCall Simulate_Push;в стек клиента 
     mov     CX,Segment_Callback;Зашлем полученный ранее адрес функции isr 
     mov     EDX,Offset_Callback;в CS:IP клиента, чтобы после возврата из VMM 
     VMMCall Simulate_Far_Call;в виртуальную машину вызвалась эта функция 
     VMMCall Resume_Exec;Возврат из VMM в текущую виртуальную машину 
     VMMCall End_Nest_Exec;Завершим вложенный блок выполнения 
     Pop_Client_State     ;Освободим место на стеке для регистров клиента 
         clc 
         ret 
EndProc     Reflect_Int 
VxD_CODE_ENDS 
end VMyD_Real_Init            
 
« Предыдущая статья   Следующая статья »