Написание драйвера.

Assembler programming questions
  • Странно. Может, подключается imports из какой-то другой папки? Хотя вряд ли.
    Вопрос остается открытым.
  • То есть с кодом всё в принципе в порядке?
  • Статью не читаем, исходники не смотрим.

    Code: Select all

    ;public version
    В папке kernel/trunk/drivers специально лежит sceletone.asm
  • Статью я читаю раза 2 в день :) Видимо не внимательно, но если не:

    Code: Select all

    ;public version
    
    он даже не пишет:

    Code: Select all

    in module .....
    
    То-есть не загружает?

    Это в первом варианте, с макросами, а как быть во втором?
  • version ведь не случайно public объявляется. Загрузчик драйвера проверяет версию драйверной модели.
  • Функция START тоже должна быть объявлена как public. Во втором случае этого не сделано.
    Сделаем мир лучше!
  • В первом коде, действительно, нужно было public version, так заработало, тут моя невнимательность и торопливость, но во втором примере так нельзя сделать, пишет что символ переопределён...что тут можно сделать?
  • popovpa, подробнее, пожалуйста. У меня этот код с добавлением "public version" и "public START" не компилируется, но только потому, что imports.inc хочет объявления "DEBUG equ 1" - или "... equ 0" - до включения, после такого объявления компиляция успешна.
    Сделаем мир лучше!
  • Простите, но было что-то странное, не компилировалось из тинипада, на доске отладки писало что данное имя уже используется, сейчас перезагрузился и всё откомпилировалось и запустилось без проблем...извините за беспокойство...видимо солнечная активность...:) разбираюсь дальше...
  • Здравствуйте!

    Почему не выводится сообщение в service_proc:

    Code: Select all

    mov	esi, msgProc
    call	SysMsgBoardStr
    

    Code: Select all

    format MS COFF
    
    include 'proc32.inc'
    include 'imports.inc'
    
    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
    
    public START
    public service_proc
    public version
    
    section '.flat' code readable align 16
    
    START:
    	push	ebp
    	mov	ebp,esp
    	mov	eax,[ebp+8]
    
    	cmp	eax,1
    	jnz	.exit
    
    	mov	esi, msgStart
    	call	SysMsgBoardStr
    
    	push	service_proc
    	push	my_service
    	call	RegService
    
    	pop	ebp
    	ret	4
    
    .exit:
    	mov	esi, msgBad
    	call	SysMsgBoardStr
    
    	pop	ebp
    	xor	eax,eax
    	ret	4
    
    handle	   equ	IOCTL.handle
    io_code    equ	IOCTL.io_code
    input	   equ	IOCTL.input
    inp_size   equ	IOCTL.inp_size
    output	   equ	IOCTL.output
    out_size   equ	IOCTL.out_size
    
    service_proc:
    
    	push	ebp
    	mov	ebp,esp
    
    	   mov ebx, [ebp+4]
    	   mov eax, [ebx+io_code]
    	   cmp eax, 0
    	   jne @F
    
    	   mov eax, [ebx+output]
    	   cmp [ebx+out_size], 4
    	   jne .fail
    	   mov [eax], dword 0
    
    	mov	esi, msgProc
    	call	SysMsgBoardStr
    
    	   xor eax, eax
    	   ret
    @@:
    .fail:
    
    	mov	esi, msgProc
    	call	SysMsgBoardStr
    
    	   or eax, -1
    	   ret
    
    
    version 	dd	0x50005
    my_service	db	'bcm4312',0
    
    section '.data' data readable writable align 16
    
    
    msgStart db 'Start...',0
    msgProc db 'Proc...',0
    msgBad db 'Bad...',0
    
    И ещё 1 вопрос, структура ioctl служит для общения драйвера с операционной системой? И если да, то можно объяснить хоть в общих чертах что-же это всё таки такое.

    Спасибо!
  • popovpa, во-первых, убедись, что управляющая программа вообще вызывает функцию 68.17, потому что само по себе ядро этого делать не будет. Во-вторых, ошибка в строке

    Code: Select all

    mov ebx,[ebp+4]
    
    Я поясню, откуда вообще берутся эти смещения. В часто используемой модели вызова stdcall - которая, в частности, применяется при вызове ядром service_proc - параметры кладутся в стек, начиная с последнего; поскольку стек растёт вниз, то перед вызовом stdcall-функции с параметрами arg1, arg2, arg3 стек выглядит так:

    Code: Select all

    arg3 <- esp+8
    arg2 <- esp+4
    arg1 <- esp - верхушка стека
    
    Вызов функции в x86 осуществляется путём сохранения в тот же стек адреса возврата - откуда его потом может достать команда ret - и передачи управления функции. Таким образом, esp уменьшается на 4 и перед первой инструкцией функции стек выглядит так:

    Code: Select all

    arg3 <- esp+12
    arg2 <- esp+8
    arg1 <- esp+4
    адрес возврата <- esp
    
    Поскольку esp в пределах функции часто "плавает", то везде адресовать относительно esp неудобно, хотя и можно. Кроме того, адресация через esp длиннее на байт. Поэтому часто начальное значение esp копируется в ebp, который уже не "плавает", и смещения относительно ebp постоянны. В модели вызова stdcall функция не должна разрушать регистр ebp - между прочим, к регистрам ebx, esi, edi это тоже относится - поэтому его нужно сохранить в том же стеке, а при выходе не забыть восстановить. Первая инструкция push ebp сохраняет текущее значение ebp, после чего esp ещё немного "уплывает" и стек выглядит так:

    Code: Select all

    arg3 <- esp+16
    arg2 <- esp+12
    arg1 <- esp+8
    адрес возврата <- esp+4
    сохранённое значение ebp <- esp
    
    В этот момент регистру ebp присваивается значение esp, и внутри функции дальше можно обращаться к аргументам так:

    Code: Select all

    arg3 <- ebp+16
    arg2 <- ebp+12
    arg1 <- ebp+8
    адрес возврата <- ebp+4
    сохранённое значение ebp <- ebp
    
    Структура IOCTL используется для передачи драйверу запросов из внешнего мира - обычно из приложения посредством функции 68.17. Сама структура содержит параметры этих запросов - код запроса и, опционально, указатели на входные и/или выходные данные и размеры этих данных. Драйвер обращается к внешнему миру посредством API ядра, объявленных в imports.inc.
    Сделаем мир лучше!
  • И ещё 1 вопрос, структура ioctl служит для общения драйвера с операционной системой? И если да, то можно объяснить хоть в общих чертах что-же это всё таки такое.
    Попробую ответить

    После команды загрузки драйвера (68.16) ядро выделяет память под драйвер загружает, и после успешной проверки передает ему управление на точку START. Далее идет твой код до точки REG_SERVICE, после передается управление этой подпрограмме в ядре которая регистрирует точку входа твоей подпрограммы с параметром service_proc ну и еще кое какой хлам.Если все пройдет нормально то подпрограмма вернет тебе HENDL твоего драйвера, и драйвер должен вернуть управление системе. После вызова функции 68.17 по ХЕНДЛУ ядро по таблице находит точку входа service_proc и передает управление ей. А структура ioctl это кусок памяти в твоем приложении, где ты сохраняешь ХКНДЛ, код функции ,длину и т.д. драйвер если сказать проще открывает ТОННЕЛЬ к ней.
    Все сложное - просто!
  • Здравствуйте!

    Я не оставил надежд написать драйвер, пишу драйвер для своей wi-fi (встроенной) карточки. Так как тема явно растянется, прошу модератором переименовать тему в что-то подобное: "Драйвер для bcm43xx"...

    А теперь собственно вопросы:

    Я нашёл своё устройство, спасибо sceletone.asm. Затем как я понял необходимо считать Base Address Registers, а уже зная его, считывать данные из устройства (это будут смещения относительно этого адреса?). Дальше у меня есть драйвер для моей карточки под Linux, но я так и не смог с ним разобраться, точнее если первое утверждение было верное, я не смог найти смещения которые надо считывать или записывать, может кто-то более опытный взглянет краем глаза и ткнёт носом что примерно и где искать? Буду очень благодарен. Или я всё не правильно понял? А если так может посоветуете что почитать, желательно на русском, чтобы я стал на пол миллиметра ближе к профи....

    Спасибо!
  • Способ обращения зависит от того, располагается BAR в I/O пространстве или в памяти. Разные BARы одного устройства могут жить в разных пространствах. Младший бит каждого BARа - read-only, 1 для BARов в I/O, 0 для BARов в памяти. Для конкретного устройства один и тот же регистр всегда находится в одном и том же пространстве, хотя конкретный адрес можно изменять. Каждый BAR задаёт базовый адрес некоторого диапазона адресов; смещения регистров внутри диапазона фиксированы. Детали
    Для BARов из I/O пространства базовый адрес есть значение BARа с обнулёнными двумя младшими битами, обращение осуществляется командами in/out:

    Code: Select all

    ; если eax = значение BAR
    and al,not 3
    lea edx,[eax+DeviceRegister]
    in {al|ax|eax},dx ; al/ax/eax = register value
    out dx,{al|ax|eax} ; set register value to al|ax|eax
    
    Для BARов в памяти базовый физический адрес есть значение BARа с обнулёнными четырьмя младшими битами. Прежде чем использовать такой диапазон, о нём нужно известить ОС, после чего с ним можно работать как с любой памятью:

    Code: Select all

    ; если eax = значение BAR
    and al,not 0Fh
    stdcall MapIoMem,eax,<size>,1Bh ; PG_SW+PG_NOCACHE, kernel-writable non-cacheable memory range
    test eax,eax
    jz die
    mov ecx,[eax+DeviceRegister] ; ecx = register value
    or dword [eax+DeviceRegister],10000h ; set bit in the register
    
    Сделаем мир лучше!
  • Who is online

    Users browsing this forum: No registered users and 3 guests