· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.ASSEMBLER —› Обработка прерываний в защищённом режиме со страничной адресацией

Посл.отвђт Сообщенiе


Дата: Янв 7, 2004 18:27:14 · Поправил: coder

Постигаю премудрости pm & paginatation. Написал код, который пытается обрабатывать прерывания. Но разрешая прерывания (после, вроде как правильно заполненной idt), получаю перезагрузку :( То есть где-то возникает старшное исключение. А я не понимаю почему. И где тоже не понимаю. Обращаюсь к вам с надеждой на помощь и всё такое :) Хотя вообще имеется пара стоящих мыслей: либо я таки неправильно заполняю IDT, либо криво инициализирую стэк (он используется толко в обработчтиках прерываний). Думается, опытный взгляд сразу заметит огрехи... На это и уповаю :)
; 2pm.asm
format binary

   use16

   org   0x100 ; DOS COM

   mov   [rs], cs

   ; чистим экран
   mov   ax, 0x3
   int   0x10

   ; open A20-line
   in    al, 0x92
   or    al, 0x2
   out   0x92, al

   ; вычисляем линейный адрес точки входа в защищённый режим
   xor   eax, eax
   mov   ax, cs
   shl   eax, 0x4
   mov   ebx, eax
   add   eax, pm_entry_point
   mov   [pmep], eax

   ; вычисляем линейный адрес GDT
   mov   eax, ebx
   add   eax, gdt
   mov   [gdtr_address], eax

   ; загружаем GDT
   lgdt  fword [gdtr]

   ; вычисляем линейный адрес IDT
   mov   eax, ebx
   add   eax, idt
   mov   [idtr_address], eax

   ; загружаем IDT
   lidt  fword [idtr]

   ; запрещаем маскируемые прерывания
   cli

   ; запрещаем немаскируемые прерывания
   in    al, 0x70
   or    al, 0x80
   out   0x70, al

   ; переключаемся в защищённый режим
   mov   eax, cr0
   or    al, 0x1
   mov   cr0, eax

   ; загружаем селектор сегмента кода в CS
   db    0x66  ; префикс изменения разрядности операнда
   db    0xEA  ; опкод инструкции JMP FAR
   pmep  rd 1  ; смещение метки PM_ENTRY
   dw    00001000b ; селектор сегмента кода

; +-------------------------+
; | Global Descriptor Table |
; +-------------------------+
align 0x4 ; выравание по границе параграфа
gdt:
       times 8 db 0x0 ; нулевой дескриптор
; база = 0x0, размер = 0x100000000 (4Gb)
dCode  db 0xFF, 0xFF, 0x0, 0x0 , 0x0 , 10011010b, 11001111b, 0x0
; база = 0x0, размер = 0x100000000 (4Gb)
dData  db 0xFF, 0xFF, 0x0, 0x0 , 0x0 , 10010010b, 11001111b, 0x0
; база = 0x102000 (1Mb+8Kb), размер = 0x100000 (1Mb)
dStack db 0xFF, 0xFF, 0x0, 0x20, 0x10, 10010010b, 01001111b, 0x0

SZGDT equ $ - gdt ; размер GDT

; Определяем символические имена для селекторов
SCODE  equ 00001000b 
SDATA  equ 00010000b 
SSTACK equ 00011000b 


; +-----------------------------+
; | Данные, загружаемые в GDTR  |
; +-----------------------------+
gdtr:
gdtr_size dw SZGDT - 1 ; размер GDT
gdtr_address rd 1      ; адрес GDT

; +----------------------------+
; | Interrupt Descriptor Table |
; +----------------------------+
align 0x4 ; выравание по границе параграфа
idt:
repeat 0x100  ; всего 256 дескрипторов
   ; каркас дескриптора 
   rw 1           ; младшее слово смещения обработчика прерывания
   dw SCODE       ; дескриптор семгмента, в котором расположен обработчик
   db 0x0         ; зарезервировано
   db 11101110b   ; биты 0-3: тип шлюза (1110 - шлюз прерывания)
                  ;    ьит 4: зарезервировано (должен быть 0)
                  ; биты 5-6: dpl
                  ;    бит 7: флаг присутсвия
   rw 1           ; старшее слово смещения обработчика прерывания
end repeat

SZIDT equ $ - idt ; размер IDT

; +-----------------------------+
; | Данные, загружаемые в IDTR  |
; +-----------------------------+
idtr:
idtr_size dw SZIDT - 1 ; размер IDT
idtr_address rd 1		  ; адрес IDT

align 0x4 ; выравание по границе параграфа
pm_entry_point: ; точка входа в защищённый режим

   use32

   xor   eax, eax
   mov   ax, ds
   shl   eax, 0x4
   mov   ebx, eax

   ; загружаем селектор сегмента даннных в DS и ES
   mov   ax, SDATA
   mov   ds, ax
   mov   es, ax

   ; создаём каталог страниц
   mov   edi, 0x100000  ; он будет расположен по смещению 1 Mb
   mov   eax, 0x101007  ; запись о таблице страниц (она у нас единственная)
   stosd                ; [es:edi] <- eax
   xor   eax, eax       ; остальные записи нулевые
   mov   ecx, 1023
   rep   stosd

   ; создаём таблицу страниц
   mov   eax, 0x7   ; первая запись  - адрес нулевой страницы. Он равен 0
   mov   ecx, 1023 ; количество записей в таблице
@@:
   stosd
   add   eax, 0x1000 ; добавляем 4 Kb
   loop  @b 

   ; загружаем базу каталога страниц в cr3
   mov   eax, 0x100000
   mov   cr3, eax

   ; инициализируем стэк
   mov   ax, SSTACK
   mov   ss, ax
   mov   esp, 0x201FFF

   ; включаем страничную адресацию
   mov   eax, cr0
   or    eax, 0x80000000
   mov   cr0, eax

   ; вычисляем линейные адреса обработчиков прерываний
   ; (сохраняем их по адресу 0x202000)
   mov   eax, ebx
   add   eax, int_handler
   mov   [0x202000], eax   ; [ds:0x202000] <- pint_handler
   mov   eax, ebx
   add   eax, irq0_7_handler
   mov   [0x202004], eax   ; [ds:0x202004] <- pirq0_7_handler
   mov   eax, ebx
   add   eax, irq8_15_handler
   mov   [0x202008], eax   ; [ds:0x20200C] <- pirq8_15_handler

   ; заполняем дескрипторы прерываний
   mov   edi, idtr_address

   ; для int0-int7
   mov   ecx, 8
@@:
   mov   esi, 0x202000
   movsw
   add   edi, 4
   movsw
   add   edi, 8
   loop  @b

   ; для int8-int15 (irq0-irq7)
	mov	ecx, 8
@@:
   mov   esi, 0x202004
   movsw
   add   edi, 4
   movsw
   add	edi, 8
   loop	@b

   ; для int16-int111
   mov	ecx, 96
@@:
   mov	esi, 0x202000
   movsw
   add	edi, 4
   movsw
   add	edi, 8
   loop	@b

   ; для int112-int119 (irq8-irq15)
   mov	ecx, 8
@@:
   mov   esi, 0x202008
   movsw
   add   edi, 4
   movsw
   add	edi, 8
   loop	@b
	
	; для int120-int255
	mov	ecx, 135
@@:
	mov	esi, 0x202000
	movsw
	add	edi, 4
	movsw
	add	edi, 8
	loop	@b		

   ; разрешаем немаскируемые прерывания
   in    al, 0x70
   and   al, 0x7F
   out   0x70, al

   ; разрешаем остальные прерывания
   sti

   jmp   $


; код обработчиков прерываний

int_handler:
   iretd

irq0_7_handler:
   push  eax
   mov   al, 0x20
   out   0x20, al
   pop   eax
   iretd

irq8_15_handler:
   push  eax
   mov   al, 0x20
   out   0xA1, al
   pop   eax
   iretd


Дата: Янв 7, 2004 19:47:47

Не вижу, где СКИ. Надо сменить базовые вектора обоих контроллеров и сделать обработчики всех исключений.
Ни в коем случае не используй вектора 0-32 для апаратных прерываний!
Причина не в этом, но сначала добейся, чтобы пейджинг работал без хардовых прерываний, обрабатывались все исключения и выводилась на экран отладочная инфа. Тогда будет гарантия, что пейджинг правильный. А потом уже сделай обработчики таймера и т п.


Дата: Янв 7, 2004 19:56:29

coder
В целях облегчения жизни при изучении защищенного режима рекомендую следующую архитектуру программы:
База стека,данных и кода там, куда ДОС загрузила программу, лимит 64K. Сегменты 32х битные. ESP=10000h. Плюс еще один сегмент данных размером 4Gb для доступа ко всей памяти. При такой архитектуре IDT строится на этапе компиляции. Обработчики исключений 0-1F должны вывести состояние регистров и остановить процессор. А потом потихоньку их заменять более гуманным кодом. Контроллер прерываний необходимо перепрограммировать чтобы аппаратные прерывания не накладывались на исключения процессора. После того как эта система заработает можно приниматься за страничную переадресацию. А эта программа не работает скорее всего потому что IDT не правильно построена:
   mov   ecx, 8
@@:
   mov   esi, 0x202000
   movsw
   add   edi, 4
   movsw
   add   edi, 8;по моему это не нужно(не только здесь)
   loop  @b

int_handler:
   iretd;дальше продолжать работу смысла нет
;кроме того некоторые исключения еще и код ошибки в стек помещают
;и перед возвратом его нужно удалить командой add esp,4 

irq8_15_handler:
   push  eax
   mov   al, 0x20
   out   0xA1, al;здесь не 0xA0 должно быть?
   pop   eax
   iretd


Дата: Янв 7, 2004 20:40:29

Black_mirror тебе уже показал причину.


Можно что-то вроде

trap struc
offs_l dw 0
sel dw 0
rsrv db 0
attr db 8fh
offs_h dw 0
trap ends
И т п
а потом
int00 trap <ля, ля, ля>
int01 trap <бля, бля>
и тп

Обязательно int to hex и вывод в видеобуфер.

А почему mov esp, 0x201FFF? Что тебя смущает 1 байт прибавить? Ведь первый push будет тогда ровно на границе.


Дата: Янв 8, 2004 00:50:16

2Valery: Не вижу, где СКИ
Что это такое хоть? %)

Всем огромное спасибо за оперативность! Понял, что надо изменить подход, а то действительно много сложностей возникает при текущем раскладе. И правда, зачем вычислять в рантайме адреса всяких меток, если можно описать сегменты так, что всё посчитается на этапе компиляции... Буду работать.

Очень здравая, кстати, мысль - разделить исключения и аппаратные прерывания %) Чего это я сам не догадался? Наверно, во всём виновато дурное влияние кривоватых примеров в Зубкове. Ибо этот код был хоть и осознанно, но почти переписан оттуда...

Black_mirror:
add   edi, 8 ; по моему это не нужно(не только здесь)

А ведь и правда не нужно :) Причём понял я это в процессе написания той части этого постинга, в которой доказывал, что данная строка необходима %) Вот косяк...

Valery: А почему mov esp, 0x201FFF? Что тебя смущает 1 байт прибавить? Ведь первый push будет тогда ровно на границе.
А потому что мой воспалённый мозг, "забыв", что стэк растёт вниз, решил, что если добавить байт, то первый же push вызовет #GP, ибо адрес вылезет за границы сегмента :) Бывает... Исправлюсь. Когда-нибудь 8)


Дата: Янв 10, 2004 04:56:51 · Поправил: coder

Изменённый в соответствии со здравым смыслом код есть у меня теперь :) Всё, вроде, правильно... Однако :) В обработчике прерывания клавитуры есть кусок, который должен рисовать символ '#' в левом врехнем углу экрана. Но не рисует... Какова?
; pmi.asm
format binary

   use16

   org   0x100 ; DOS COM application

   ; clear screen
   mov   ax, 0x3
   int   0x10

   ; correct 'base' field in descriptors
   ; of dCode, dData & dStack segments
   xor   eax, eax
   mov   ax, cs
   shl   eax, 0x4
   mov   ebx, eax

   mov   word [dCode+2], ax
   mov   word [dData+2], ax
   mov   word [dStack+2], ax

   shr   eax, 0x10

   mov   byte [dCode+4], al
   mov   byte [dData+4], al
   mov   byte [dStack+4], al

   mov   byte [dCode+7], ah
   mov   byte [dData+7], ah
   mov   byte [dStack+7], ah

   ; open A20-line
   in    al, 0x92
   or    al, 0x2
   out   0x92, al

   ; calculate address of global descriptor table
   mov   eax, ebx
   add   eax, gdt
   mov   [gdtr_address], eax

   ; load global descriptor table
   lgdt  fword [gdtr]

   ; calculate address of interrupt descriptor table
   mov   eax, ebx
   add   eax, idt
   mov   [idtr_address], eax

   ; load interrupt descriptor table
   lidt  fword [idtr]

   ; disable maskable interrupts
   cli

   ; disable non maskable interrupts
   in    al, 0x70
   or    al, 0x80
   out   0x70, al

   ; switch to protected mode
   mov   eax, cr0
   or    al, 0x1
   mov   cr0, eax

   ; load new selector to CS reister
   db    0x66  ; prefix for change operand capacity to 32
   db    0xEA  ; opcod of JMP FAR command
   dd    pm_entry_point  ; 32-bit offset (of pm_entry_point)
   dw    00001000b ; selector of the first descriptor

; +-------------------------+
; | Global Descriptor Table |
; +-------------------------+
align 0x4
gdt:
times 8 db 0x0 ; null descriptor

; base field of following 3 descriptor will be corrected
; in run-time to be equal address where DOS loaded this
; application (base = CS * 0x10)
dCode  db 0xF, 0x0, 0x0, 0x0, 0x0, 10011010b, 11000000b, 0x0 ; size = (0xF+1)*0x1000 = 64Kb
dData  db 0xF, 0x0, 0x0, 0x0, 0x0, 10010010b, 11000000b, 0x0 ; size = 64Kb
dStack db 0xF, 0x0, 0x0, 0x0, 0x0, 10010010b, 11000000b, 0x0 ; size = 64Kb

; Global data segment descriptor for access to whole memory
dGData db 0xFF, 0xFF, 0x0, 0x0 , 0x0, 10010010b, 11001111b, 0x0

SZGDT equ $ - gdt ; Global Descriptor Table Size

; define symbolic names for segment selectors
SCODE  equ 00001000b ; dCode segment selector
SDATA  equ 00010000b ; dData segment selector
SSTACK equ 00011000b ; dStack segment selector
SGDATA equ 00100000b ; dGData segment selector

; +-----------------------------+
; | Data to be loaded into gdtr |
; +-----------------------------+
gdtr:
gdtr_size dw SZGDT - 1 ; size of global descriptor table
gdtr_address rd 1      ; linear address of global desriptor table

; +----------------------------+
; | Interrupt Descriptor Table |
; +----------------------------+

; macro used to define interrupt descriptor
macro defidts sel, off, count
{
   repeat count
      dw off mod 0x10000 ; low word of interrupt handler offset in a segment
      dw sel             ; selector of the descriptor of the segment with a handler
      db 0x0             ; reserved, high 2 bits must be 0
      db 0xEE            ; bits 0-3: type of gate (1110 - interrupt gate)
                         ;    bit 4: reserved (must be 0)
                         ; bits 5-6: dpl
                         ;    bit 7: present flag
      dw off / 0x10000   ; high word of interrupt handler offset in a segment
   end repeat
}

align 0x4
idt:

defidts SCODE, int_handler, 0x20    ; descriptor for int0-int31

defidts SCODE, irq0_7_handler, 0x1  ; for irq0
defidts SCODE, irq1_handler, 0x1    ; for irq1 (keyboard)
defidts SCODE, irq0_7_handler, 0x6  ; for irq2-irq7
defidts SCODE, irq8_15_handler, 0x8 ; for irq8-irq15
defidts SCODE, int_handler, 0xD0    ; for int48-int255

SZIDT equ $ - idt ; interrupt descriptor table size

; +-----------------------------+
; | Data to be loaded into idtr |
; +-----------------------------+
idtr:
idtr_size dw SZIDT - 1  ; size
idtr_address rd 1       ; address

align 0x4
pm_entry_point:
   use32

   ; selector of dGData segment to DS and to ES
   mov   ax, SGDATA
   mov   ds, ax
   mov   es, ax

   ; create page directory
   mov   edi, 0x100000  ; page directory will be situated in memory at 1 Mb
   mov   eax, 0x101007  ; page directory entry for our page table
   stosd                ; [es:edi] <- eax
   xor   eax, eax       ; other 1023 elements are null
   mov   ecx, 1023
   rep   stosd

   ; create page table
   mov   eax, 0x7   ; first entry  - address of 0th page. It's equal 0
   mov   ecx, 1023 ; count of pages in a table
@@:
   stosd
   add   eax, 0x1000 ; add 4 Kb
   loop  @b

   ; base of page directory to cr3
   mov   eax, 0x100000
   mov   cr3, eax

   ; init stack
   mov   ax, SSTACK
   mov   ss, ax
   mov   esp, 0x10000

   ; turn on pagintaition mode
   mov   eax, cr0
   or    eax, 0x80000000
   mov   cr0, eax

   ; init pic
   mov   al, 000100001b ; icw1
   out   0x20, al ; for 1st controller
   out   0xA0, al ; for 2nd controller
   mov   al, 0x20 ; icw2 for 1st controller
   out   0x21, al
   mov   al, 0x28 ; icw2 for 2nd controller
   out   0xA1, al
   mov   al, 00000100b ; icw3 for 1st controller
   out   0x21, al
   mov   al, 00000010b ; icw3 for 2nd controller
   out   0xA1, al
   mov   al, 00001101b ; icw4 for 1st controller
   out   0x21, al
   mov   al, 00001001b ; icw4 for 2nd controller
   out   0xA1, al

   ; enable NMI interrupts
   in    al, 0x70
   and   al, 0x7F
   out   0x70, al

   ; enable other interrupts
   sti

   jmp   $

int_handler:
   jmp   $

irq1_handler:
   push  eax
   push  edx
   push  ds

   in    al, 0x60

   ; draw '#' in an upper left conner of the screen
   mov   ax, SGDATA
   mov   ds, ax
   mov   byte [0xB8000], '#'
   mov   byte [0xB8001], 0x7

   in    al, 0x61
   or    al, 0x80
   out   0x61, al

   mov   al, 0x20
   out   0x20, al

   pop   ds
   pop   edx
   pop   eax
   iretd

irq0_7_handler:
   push  eax

   mov   al, 0x20
   out   0x20, al

   pop   eax
   iretd

irq8_15_handler:
   push  eax
   mov   al, 0x20
   out   0xA0, al
   pop   eax
   iretd


Дата: Янв 10, 2004 17:16:12

Рекомендую для отладки поместить в FS SGDATA и при прохождении программой некоторых точек выволить в определенную позицию какой-нибудь символ. Такой код стоит поместить во все обработчики прерываний, чтобы знать вызывалось ли вообще какой-нибудь из них. А вместо Jmp $ поместить следующий код:
l:
  inc byte [fs:0B8000+n*2];n - номер позиции
  jmp l

Запрещать немаскируемые прерывания при переходе в защищенный режим вроде не обязательно. Для инициализации PIC попробуй использовать в качестве ICW4 1fh для первого и 1bh для второго. Еще перед разрешением прерываний возможно стоит сбросить контроллер клавиатуры.


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.083