|
|
| Посл.отвђт | Сообщен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 |