7.5. Драйверы устройств

В Windows, так же как и в DOS, существует еще один вид исполняемых фай­лов - драйверы устройств. Windows З.хх и Windows 95 используют одну модель драйверов, Windows NT - другую, a Windows 98 - уже третью, хотя и во многом близкую к модели Windows NT. В Windows З.хх/95 применяются два типа драйве­ров устройств - виртуальные драйверы (VxD), выполняющиеся с уровнем приви­легий 0 (обычно имеют расширение .386 для Windows З.хх и .УХОдля Windows 95), и непривилегированные драйверы, исполняющиеся, как и обычные программы, с уровнем привилегий 3 (как правило, они имеют расширение .DRV). Windows NT использует несовместимую модель драйверов, так называемую kernel-mode (режим ядра). На основе модели kernel-mode с добавлением поддержки техноло­гии PNP И понятия потоков данных в 1996 году была создана модель WDM (Win32 driver model), которая теперь используется в Windows 98/NT и, по-види­мому, будет играть главную роль в дальнейшем.

Как и следовало ожидать, основным средством создания драйверов является ассемблер, и хотя использование С здесь возможно (в отличие от драйверов DOS), но сделать это сложнее: функции, к которым обращается драйвер, могут передавать параметры в регистрах; сегменты, из которых состоит драйвер, долж­ны называться определенным образом и т. д. При создании драйверов часто пользуются одновременно С и ассемблером или С и специальным пакетом, кото­рый включает в себя все необходимые для инициализации средства.

Чтобы самостоятельно создавать драйверы для любой версии Windows, необ­ходим комплект программ, документации, включаемых файлов и библиотек, рас­пространяемый Microsoft, который называется DDK - Drivers Development Kit. (DDK для Windows NT/98 распространяются бесплатно.)

Мы не будем рассматривать программирование драйверов для Windows в де­талях, так как этой теме посвящено много литературы, не говоря уже о докумен­тации, прилагающейся к DDK. Чтобы создать драйвер, в любом случае лучше

всего начать с одного из прилагающихся примеров и изменять/добавлять проце­дуры инициализации, обработчики сообщений, прерываний и исключений, обра­ботчики для API, предоставляемого драйвером, и т. д. Рассмотрим, как выглядит исходный текст драйвера, потому что он несколько отличается от привычных нам

ассемблерных программ.

Любой драйвер начинается с директивы include vmm.inc, которая включает

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

BeginProc BIOSXlat_Sys_Critical_Init

mov

Макроопределения вида VXD_LOCKED_CODE_SEG/VXD^CKED_CODE_ ENDS соответствуют директивам начала и конца сегментов (в данном случае сег­мента _LTEXT). Другие важные макроопределения - Declare_Virtual_Device и VMMCall/WDMCall. Первое - просто определение, получающее в качестве па­раметров идентификатор драйвера, название, версию, порядок загрузки и адреса основных процедур драйвера, из которых строится его заголовок. Второе - заме­на команды call, получающая в качестве параметра имя функции VMM или WDM, к которой надо обратиться. Например, элемент кода драйвера BIOSXLAT, перехватывающий прерывание 10h, выглядит следующим образом:

VxD_ICODE_SEG Начало сегмента _1ТЕХТ  (сегмент кода

; инициализации, исполняющийся

в защищенном режиме, который удаляется из памяти после сообщения Init_Complete). Процедура, вызываемая для обработчика сообщения Sys_C'ritical_Init - первого

сообщения, которое получает драйвер.

Обычно обработчики сообщений должны сохранять регистры ЕВХ,   EDI,   ESI и ЕВР, хотя в данном случае этого можно не делать.

;   Адрес обработчика INT 10h в регистр ESI.   Важно использовать макроопределение OFFSET32 всюду вместо offset. Любое число,   которое будет помещаться в EDX при вызове

регистрируемого обработчика.

Зарегистрировать точку входа, ; обращаясв к которой, программы из защищенного режима в      будут передавать управление процедуре в драйвере  (но не ; \ип32-программа) - они должны использовать DevicelOControl для работы с драйверами.

jc BXLSCI_NoPM ;  Если CF = 1 - произошла ошибка:

xchg ;  точку входа - в EDX, число

(теперь это номер перехватываемого прерывания mov ecx,edx

shr       есхДОп ; Селектор точки входа.

movzx     edx.dx ; Смещение точки входа.

VMMCall   Set_PM_Int_Vector ■ Установила обработчик прерывания

;  Если эта функция вызвана до Sys_VM_Init, установленный обработчик становится звеном цепочки обработчиков для всех виртуальных машин. ; После того как прерывание проходит по цепочке обработчиков ; в защищенном режиме,  оно отображается в V86 точно так же,  как в DPMI [код перехвата других прерываний].

EndProc BIOSXlat_Sys_Critical_Init ;   Конец процедуры.

VxD_ICODE_ENDS ;  Конец сегмента инициализации.

esi,OFFSET32 BI0SXlat_Int10

edx,10h

VMMCall Allocate_PM_Call_Back

в ЕАХ

INTтот

Соответственно, чтобы эта процедура была вызвана для сообщения Sys_Criti-cal_Init, в сегменте фиксированного кода _LTEXT должна быть следующая за­пись:

VxD_LOCKED_CODE_SEG    . ;   Начало сегмента _1_ТЕХТ.

BeginProc BIOSXlat_Control       .     ;  Начало процедуры.

ContrQlJWspatch Sys_Critical_Init, BIOSXlat_Sys_Critical_Init

;  При помощи еще одного макроопределения изугшЛпс зарегистрировать процедуру ;   BIOSXlat_Sys_Critical_Init как обработчик сообщения Sys_Critical_Init.

clc ;   Процедура-обработчик управляющих

;   сообщений должна возвращать CF = 0.

ret

EndProc BIOSXlat_Control ;   Конец процедуры.

VxD_L0CKED_C0DE_EN0S ;   Конец сегмента _НТЕХТ.

И наконец, процедура BIOSXlat_Control регистрируется в заголовке драйве­ра как процедура, получающая управляющие сообщения:

;  Первая строка после и

Declare_Virtual_Device     BlOSXlat,  1,  0,   BIOSXlat_Control, BIOSXlat_Device_ID,

BIOSXlat_Init_Order

Это не слишком сложно и, пользуясь примерами и документацией из DDK, а также отладчиком можно справиться практически с любой задачей, для

которой есть смысл создавать драйвер.