Board.KolibriOS.org
http://board.kolibrios.org/

Как писать драйвера
http://board.kolibrios.org/viewtopic.php?f=3&t=707
Page 1 of 3

Author:  diamond [ Sat Apr 07, 2007 1:18 pm ]
Post subject:  Как писать драйвера

Вот, написал статью, демонстрирующую программирование драйвера.
http://diamondz.land.ru/writedrv.htm
Приведённый там пример может быть полезен и безотносительно к обучению - написан фильтр, показывающий все обращения программ к файловой системе.

Author:  Serge [ Sat Apr 07, 2007 5:22 pm ]
Post subject: 

Есть два дополнения.

Это не обязательно, но желательно иметь функцию для получения версии API драйвера. Для этого зарезервирован ioctl.io_code=0

вот пример из недоделанного драйвера uart.

Code:
SRV_GETVERSION equ 0

; retval
;  ebx= service version
;  eax= error code
;    0= no error
;   -1= common error

align 4
init_uart:
           mov eax, 68
           mov ebx, 16
           mov ecx, szUart
           int 0x40

           mov [Uart], eax
           test eax, eax
           jz .fail

           push 0              ;storage for version
           mov eax, esp        ;eax= pointer to output buffer
           xor ebx, ebx

           push 4              ;.out_size
           push eax            ;.output
           push ebx            ;.inp_size
           push ebx            ;.input
           push SRV_GETVERSION ;.code
           push [Uart]         ;.handle

           mov eax, 68
           mov ebx, 17
           mov ecx, esp        ;address of IOCTL in app stack
           int 0x40
           add esp, 24         ;sizeof IOCTL
           pop ebx             ;load version
           ret
.fail:
           or eax, -1
           ret


68.16 получает логический номер драйвера и сохраняет его в переменной Uart. После чего в стеке формируется стрктура IOCTL. Чтобы избежать проблем в будущем рекомендую заполнять все поля структуры. В данном примере это происходит в обратном порядке. Последним в стек помещается логический номер драйвера после чего esp становится указателем на структуру IOCTL. После вызова стек восстанавливается командой add esp,24 а pop ebx загружает возвращённый драйвером номер версии.

Второе замечание по кодам ошибок.
Вызовы 68.17 возвращают в eax коды ошибок. Это 0 в случае успеха и ненулевое значение в случае неудачи, обычно -1 как признак общей ошибки, другие коды неопределены. Поэтому драйвер не должен использовать регистр eax для
возврата значений приложению. Все данные от драйвера должны возвращаться в буфере IOCTL.output

Кстати, есть смысл определить стандартные коды ошибок при вызовах 68.17 Например от -1 коды ошибок ядра: драйвер не установлен, неправильный логический номер драйвера и т.д. от +1 коды внутренних ошибок драйвера

Author:  N†OSKRNL [ Sat Apr 07, 2007 5:36 pm ]
Post subject: 

diamond - очень захватывающая статья, в высоком и живом стиле. :) Думаю еще раз пять перечитаю, и на досуге отважусь с подобными экспериментами c колибри (0.6.5.0).

Author:  Serial [ Sat Apr 07, 2007 6:59 pm ]
Post subject: 

diamond, спасибо за интересную статью.

Author:  diamond [ Sat Apr 07, 2007 7:39 pm ]
Post subject: 

Serge
Спасибо, будет исправлено (к понедельнику).

Author:  Serge [ Sat Apr 07, 2007 10:08 pm ]
Post subject: 

diamond
Хорошая статья, но есть ещё замечание.
По собственному опыту я бы переписал вызов драйвера в mainloop
Code:
           push 0              ;storage for output logsize
           mov eax, esp        ;eax= pointer to output data

           push 16*1024        ;logsize
           push logbuf         ;pointer to log buffer
           mov  ebx, esp       ;pointer to input data
           push 4              ;.out_size
           push eax            ;.output
           push 8              ;.inp_size
           push ebx            ;.input
           push 2              ;.code
           push [handle]       ;.handle

           mov eax, 68
           mov ebx, 17
           mov ecx, esp        ;address of IOCTL in app stack
           int 0x40
           add esp, 24+8       ;sizeof IOCTL+ptr_logbuf+log_size
           pop ecx             ;load output logsize

Так конечно длиннее и выглядит сложнее, но есть смысл рассматривать .input как указатель на структуру содержащую входные данные а .output на структуру содержащую выходные данные. В данном случае входные данные адрес logbuf и его размер, а выходные количество записанных байт. Если передаётся и возвращается только одно двойное слово всегда хочется сделать это в полях .input и .output. Но думаю что лучше так не делать потому что надо добавить в ядро проверку адресов .input и .output и страниц памяти на присутствие/запись/право доступа. Сейчас передав неправильный указатель можно обрушить всю систему.

Author:  diamond [ Mon Apr 09, 2007 5:59 pm ]
Post subject: 

Serge
Старые замечания учтены, статья обновлена, ссылка та же. Только у меня появился вопрос: номер версии для ioctl=0 следует возвращать в каком-то фиксированном формате или совершенно произвольном? (текущая реализации ядра это вообще игнорирует, но мало ли какие могут быть планы...) В первом случае, возможно, вместо банального dword имеет смысл стандартный формат с возможностью контроля совместимости версий?
По поводу последнего замечания: в статье .input и .output содержат действительно указатели, только на глобальные переменные, а не стековые, как в твоём примере. Конечно, если нужно посылать запросы из разных потоков многопоточной программы, нужен стек, но подобное встречается редко. Соответственно если вставить в ядро проверку на принадлежность приложению буферов, на которые указывают .input/.output, размера .inp_size/.outp_size, то разобранный в статье пример по-прежнему будет работать. А размер передаётся в поле .outp_size (причём как in/out - на входе в драйвер он содержит размер пользовательского буфера, на выходе - размер записанных данных), а иначе зачем вообще нужно это поле?

Author:  Mario79 [ Tue Apr 10, 2007 7:27 am ]
Post subject: 

diamond
Статья конечно интересная и полезная, однако у меня возник вопрос при прочтении - "Та организация, которую некоторые считают ругательным словом, не будет иметь претензии на формат заголовка?" Может быть, использовать другой более свободный вариант?
И еще подменять можно всю функцию целиком или подфункцию тоже можно?

Author:  Serge [ Tue Apr 10, 2007 7:33 am ]
Post subject: 

diamond

С этими версиями одна головная боль. Для ядра важно только старшее слово - версия драйверной модели так что младшее может хранить что угодно, например ревизию svn. Для звука там хранится текущая версия API а приложения получают диапазон версий SOUND_VERSION.

SOUND_VERSION equ 0x01000100 ;старшее слово - минимально совместимая, младшее - текущая версия.
version dd (4 shl 16) or (SOUND_VERSION and 0xFFFF)

Если есть идеи по формату и контролю версий, предлагай. У меня идеи уже закончились.

По поводу .input и .output. Глобальные или локальные конечно не важно. Если программа делает много разных вызовов размещение в стеке удобней и уменьшает размер кода. Дело в передаче параметров. Все поля ioctl для драйвера входные данные - указатель на структуру входных данных, размер структуры, указатель на структуру выходных данных, размер структуры.
То есть поля не предназначены для возврата значения. Сейчас все вызовы обрабатываются в контексте вызвавшего потока. Указатель на ioctl передаётся непосредственно. если способ передачи изменится и структура будет копироваться код перестанет работать.

Author:  diamond [ Wed Apr 11, 2007 6:01 pm ]
Post subject: 

Mario79
Думаю, не будет. Всё-таки базовым для MS COFF (и PE, между прочим) является COFF, а он изначально был под *nix.
Подфункцию подменять тоже можно. Самый простой способ - перехватить всю функцию и в начале обработчика проверить, вызвана ли нужная нам подфункция и если нет, то передать управление ядерному обработчику (в коде из статьи - командой jmp [oldfn70]).
Serge
Ну раз поля не предназначены для возврата значения, тогда надо немного изменить код. Будет исправлено.

Author:  Ghost [ Sat Apr 21, 2007 11:32 am ]
Post subject: 

[offtop]Добавь в статью её адресс, т.к. статья обновляется - всегда можно будет легко проверить соответствие сохранённой и web версий[/offtop]

Author:  diamond [ Mon Apr 23, 2007 5:19 pm ]
Post subject: 

Да я бы не сказал, что она обновляется - вот сейчас исправил последнее замечание Serge и никаких изменений в будущем не предвидится... А дата последнего обновления всегда указана на http://diamondz.land.ru

Author:  Mihail [ Fri May 11, 2007 2:05 pm ]
Post subject: 

Кто как и когда эти драйверы запускает?

Я скомпилировал текст
Code:
format MS COFF

include 'proc32.inc'
include 'main.inc'
include 'imports.inc'

DEBUG            equ 1

OS_BASE          equ 0
new_app_base     equ 0x60400000
PROC_BASE        equ OS_BASE+0x0080000


struc IOCTL
{  .handle           dd ?
   .io_code          dd ?
   .input            dd ?
   .inp_size         dd ?
   .output           dd ?
   .out_size         dd ?
}

virtual at 0
  IOCTL IOCTL
end virtual

section '.flat' code readable align 16

proc START stdcall, state:dword

        mov    esi,msgStart
        call   boot_log
        mov esi, msgStart
        call SysMsgBoardStr
   jmp $
endp


version       dd 0x00030003

msgStart      db 'start...',13,10,0



Записал полученный объектник в каталог drivers , но никакого эффекта не увидел.

Author:  k@sTIg@r [ Fri May 11, 2007 2:34 pm ]
Post subject: 

Есть 2 варианта.
1) В нужном месте в ядре вызываешь ф-цию load_driver , которая принимает один параметр - имя драйвера без .obj (загружает /rd/1/drivers/name.obj)
2) загружаешь драйвер программно с помощью 68.16(смотри подробное описание ф-ции).
Для начала советовал бы 2 вариант.

Author:  Mihail [ Fri May 11, 2007 5:23 pm ]
Post subject: 

k@sTIg@r wrote:
Есть 2 варианта.


В обоих случаях возвращается 0 в EAX.
С другими драйверами тоже.
Я вставлял диагностику при
stdcall load_driver, szHwMouse
в kernel\video\cursors.inc
возвращается 0.

Page 1 of 3 All times are UTC+03:00
Powered by phpBB® Forum Software © phpBB Limited
https://www.phpbb.com/