вобщем, суть в следующем: написал я вторичный загрузчик (в будущем), который грузится из фат-раздела по адресу 01000h и выполняет следующие операции: отключает прерывания, сохраняет текущие значения ss,ds,es и др. в память для возврата в реальный режим, создает дескрипторы кода (4Gb), данных (по такому же адресу что и code), стэка (фиксирован), видеопамяти (по адресу 0B8000h, размер 4000) для защищенного режима и 2 дескриптора кода и данных (16 бит, лимит 0FFFFh) для реального, настаивает gdt и загружает ее. потом в защищенном режиме выводит пару строк и переходит назад в реальный. но проблема вот в чем.. при включении прерываний уже в реальном режиме, система попросту перезагружается (если запускать на реальной машине), а если на vmware (4.05) или Bochs (2.26) то все нормально, т.е. sti включает прерывания и далее выводится строка посредством int10h см. строку 221 (на jmp $ не обращайте внимание )) до нее всеравно дело не доходит, это я так ловил где баг) я если чесно понятия не имею почему так происходит и поэтому прошу помочь. вот код загрузчика: Код (Text): 001 format binary 002 org 0 003 include 'kboot.inc' 004 include 'pmode.inc' 005 include 'routines.inc' 006 007 code_selector = 8 008 stack_selector = 16 009 data_selector = 24 010 screen_selector = 32 011 rcode_selector = 40 012 rdata_selector = 48 013 014 use16 015 016 start: 017 ; load segment regs, set stack 018 cli 019 mov ax,cs 020 mov ds,ax 021 mov es,ax 022 mov ss,ax 023 mov sp,stacksize 024 sti 025 026 ; show hello msg 027 mov si, szLoaderMsg 028 call kputzs_bios 029 030 ; msg about pmode entering 031 mov si, szMsgPMEnter 032 call kputzs_bios 033 034 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 035 ; save real mode descriptors 036 mov [rmode_ss],ss 037 mov [rmode_ds],ds 038 mov [rmode_es],es 039 mov [rmode_fs],fs 040 mov [rmode_gs],gs 041 042 ; set real mode return address 043 mov [rmode_seg],cs 044 lea ax,[rmode_entry] 045 mov [rmode_off],ax 046 047 mov bx,gdt+8 ; skip zero-descriptor 048 049 xor eax,eax 050 mov edx,eax 051 052 push cs 053 pop ax ; ax = cs = segment address of current code segment 054 055 shl eax,4 ; eax = physical address of beginning of code segment 056 057 058 ; set limit-hi and GDXU bits 059 ; limit-hi = 0Fh (maximum), G=1 (4 kbyte pages), D=1 (32 bit), 060 ; X=0 (reserved, must be 0), U=0 (for system purposes) 061 mov dx,11001111b 062 shl edx,16 ; move them to edx-hi part 063 mov dx,0FFFFh ; set limit-low (here - maximum) 064 ; finaly we have 4Gbyte limit for code descriptor 065 066 ; code segment access rights (P = 1, 067 ; DPL = 00b, S = 1, type = 100b, A = 0) 068 mov cl,10011000b 069 070 call gdt_set_desc ; build code descriptor 071 072 lea dx,[stack_seg_start] ; edx = dx = stack beginning 073 074 add eax,edx ; eax - beginning of code segment 075 ; set limit-hi and GDXU bits 076 ; limit-hi = 00h (zero), G=1 (4 kbyte pages), D=1 (32 bit), 077 ; X=0 (reserved, must be 0), U=0 (for system purposes) 078 mov dx,11001111b 079 shl edx,16 ; move them to edx-hi part 080 mov dx,1024 ; set limit-low (stack size) 081 082 ; stack segment access rights 083 ; (P = 1, DPL = 00b, S = 1, 084 ; type = 011b, A = 0). 085 mov cl,10010110b 086 087 call gdt_set_desc ; build stack descriptor 088 089 xor eax,eax ; eax = 0 090 mov ax,ds 091 shl eax,4 ; ecx = physical address of beginning of data segment 092 mov dx,11001111b ; set limit-hi and GDXU bits 093 shl edx,16 094 mov dx,0FFFFh ; limit - maximum 095 ; 4GB data segment is ready 096 097 ; data segment access rights (P = 1, 098 ; DPL = 00b, S = 1, type = 001, A = 0). 099 mov cl,10010010b 100 101 call gdt_set_desc ; build data descriptor 102 103 mov eax,0B8000h ; physical address of videomem segment beginning 104 105 mov edx,4000 ; videomem segment size (80*25*2 = 4000). 106 mov cl,10010010b ; access rights (like data segment) 107 call gdt_set_desc ; build video memory segment 108 109 ; now set additional real mode selectors 110 xor eax,eax 111 mov edx,eax 112 113 push cs 114 pop ax 115 116 shl eax,4 ; eax - physical address of code segment 117 118 mov edx,0FFFFh 119 mov cl,10011010b 120 call gdt_set_desc ; rmode code 121 122 mov cl,10010010b 123 call gdt_set_desc ; rmode data 124 125 126 ; set gdtr: 127 128 xor eax,eax ; eax = 0 129 mov edx,eax ; edx = 0 130 131 mov ax,ds 132 shl eax,4 ; eax = physical address of beginning of data segment 133 lea dx,[gdt] 134 add eax,edx ; eax = physical address of gdt 135 mov [gdt_adr],eax ; save it in gdt address field 136 137 mov dx,55 ; gdt limit = 8 * (1 + 6) - 1 138 mov [gdt_lim],dx ; save it in gdtr limit field 139 140 cli ; disable interrupts 141 142 lgdt [gdtr] ; load gdtr 143 144 mov [rmode_sp],sp ; save stack pointer at the end 145 146 ; go to pmode 147 148 mov eax,cr0 149 or al,1 150 mov cr0,eax 151 152 ; load code selector into cs register 153 ; using far jump to pmode code 154 155 jmp far code_selector:pmode_entry 156 157 ;--------------------- now we're in 32 bit PM------------------------------- 158 use32 159 pmode_entry: 160 ; cs - code segment selector 161 ; load segment registers 162 mov ax,screen_selector 163 mov es,ax 164 165 mov ax,data_selector 166 mov ds,ax 167 168 mov ax,stack_selector 169 mov ss,ax 170 mov sp,0 171 172 ; show what we are now in pmode 173 mov ebx,szMsgPM ; ds:ebx - msg pointer 174 mov edi,480 ; 3 string in video memory 175 call kputzs ; print msg from pmode 176 177 ; entering rmode 178 mov ebx,szMsgRMEnter ; ds:ebx - msg pointer 179 mov edi,640 ; 4 string in video memory 180 call kputzs ; print msg from pmode 181 182 jmp far rcode_selector:rmode_preentry 183 184 rmode_preentry: 185 mov ax,rdata_selector 186 mov ss,ax 187 mov ds,ax 188 mov es,ax 189 mov fs,ax 190 mov gs,ax 191 192 mov eax,cr0 193 and al,0FEh 194 mov cr0,eax 195 196 ; far jump to real mode 197 ; jmp far rmode_seg:rmode_off 198 db 0eah 199 rmode_off dw 0 200 rmode_seg dw 0 201 202 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 203 ; ---------------------------- routines ---------------------------------- 204 init_kputzs ; printing routine (uses video memory) 205 use16 206 init_gdt_set_desc ; gdt setting routine 207 init_kputzs_bios ; printing routine (uses int10) 208 init_set_curpos_bios ; setting cursor routine 209 210 ; real mode now 211 rmode_entry: 212 ; restore ss,ds,es,gs,fs 213 mov ss,[rmode_ss] 214 mov ds,[rmode_ds] 215 mov es,[rmode_es] 216 mov gs,[rmode_gs] 217 ; restore stack pointer 218 mov sp,[rmode_sp] 219 220 ; allow interrupts 221 sti 222 jmp $ 223 224 ; set cursor to the 5th line 225 mov dx,0500h 226 call set_curpos_bios 227 228 mov si,szMsgRM 229 call kputzs_bios 230 231 jmp $ 232 233 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 234 235 ; gdt register image: 236 237 gdtr: 238 gdt_lim dw 0 239 gdt_adr dd 0 240 ;----------------------------------------------------------------- --------- 241 gdt: 242 dd 0,0 ; 0 descriptor 243 dd 0,0 ; 1 descriptor (code) 244 dd 0,0 ; 2 descriptor (stack) 245 dd 0,0 ; 3 descriptor (data) 246 dd 0,0 ; 4 descriptor (videomem) 247 dd 0,0 ; 5 descriptor (realm code) 248 dd 0,0 ; 6 descriptor (realm data) 249 real: 250 rmode_sp dw 0 251 rmode_ss dw 0 252 rmode_ds dw 0 253 rmode_es dw 0 254 rmode_fs dw 0 255 rmode_gs dw 0 256 257 szLoaderMsg db 'HELLO FROM KLOADER!!',13,10,0 258 szMsgPMEnter db 'ENTERING PMODE...',13,10,0 259 szMsgRM db 'IN 16 BIT UNREAL MODE NOW!',13,10,0 260 261 262 ;----------------------------------------------------------------- --------- 263 264 szMsgPM db 'IN 32 BIT PMODE NOW!',0 265 szMsgRMEnter db 'ENTERING RMODE...',0 266 267 268 times 1024 db 0 ; reserved for stack 269 stack_seg_start: ; stack beginning вот код процедуры создания дескрипторов gdt_set_desc: Код (Text): 001 ; gdt descriptor setting subroutine 002 003 ; creates descriptor 004 ; DS:BX = descriptor in gdt 005 ; EAX = segment address 006 ; EDX = segment limit 007 ; CL = access rights byte 008 macro init_gdt_set_desc { 009 gdt_set_desc: 010 011 push eax ecx edx ; we use eax, ecx, edx 012 013 014 push cx ; temporary save access rights 015 016 mov cx,ax ; copy lower part of address to cx, 017 shl ecx,16 ; and shift it to higher part 018 019 ; copy lower part of limit to cx 020 ; now ecx contains lower part of 021 ; the descriptor 022 mov cx,dx 023 024 mov [bx],ecx ; write lower part of descriptor to gdt 025 026 shr eax,16 ; now shift higher part of address to lower (ax) 027 028 mov ch,ah ; address bits 24 - 31 029 shr edx,16 ; working with higher part of limit (limit hi+GDXU) 030 mov cl,dl ; limit bits 16-19 + GDXU 031 shl ecx,16 ; shift it to higher part of ecx 032 mov cl,al ; and address bits 16 - 23 - lower byte 033 034 pop ax ; restore access rights to ax 035 ; and copy them into the second byte (of 4 available) 036 ; of ecx 037 mov ch,al 038 039 ; write the second part of 040 ; descriptor into gdt 041 mov [bx+4],ecx 042 043 add bx,8 ; move gdt poiner to next descriptor 044 pop edx ecx eax 045 ret 046 } 047 выкладываю полный исходник+образ дискеты на всякий случай буду очень благодарен за помощь заранее спасибо _744065779__lowexp_b.zip
Я не вполне уверен, но, возможно, стоит попробовать поточнее восстановить DS после перехода обратно в RealMode. Вот как это сделано у Финогенова: ; turn back DOS media return: mov ax,data mov ds,ax mov ax,stk mov ss,ax mov sp,256 mov ss,real_ss ;interrupt on Это пример из стартующего DOS'овского экзешника, те "mov ax,data" - это загрузка константы. Можно попробовать эту команду править (т.е. модифицировать код - значение, которое нужно восстановить - в команде mov ax,<Value_DS>
Chingachguk хмм.. очень интересная весчь получается.. когда уже в реальном режиме записать в ds ноль, халта системы не происходит.. но сообщение не выводится (адрес не тот).. когда пишу в ds реальный адрес, по которому загружается вторичный загрузчик (т.е. значение cs), происходит сброс системы на sti.. интересно почему так?
NeTxXx Кажется, у Финогенова было сказано примерно следующее: ~"после перехода в RM из PM в теневых полях селекторов остаются прежние значения и это некоторое время позволяет работать с ними..." Ну или что-то в этом роде (дома поточнее посмотрю - понимаю, что написал пургу). Попробуй туда не CS писать, а константу типа 0 (или валидный адрес вбивай прямо в команду его загрузки еще до перехода в PM).
Есть проблема - между строками 145 - 146 должен быть код который разрешает использование A20 разряда адреса. Обычно через порты клавиатуры. Если машина на которой ты работаешь - реально совместима с IBM, то это необходимо делать до перехода в зашищенный режим, иначе физические адреса будут вырабатываться неправильно.
Воторой момент: выводится строка посредством int10h в защищеном режиме не все VideoBIOS поддерживают вывод через int 10, а реальном во многих биосах (материнки) есть ошибки, и потому они зачастую тоже неправильно обрабатывают int 10 даже в реальном режиме, до загрузки DOS например. А потому kputzs_bios: не универсальна.
Зачем же так жестоко GDT формировать? Неужели нельзя было определить структуру для дескриптора, с нормальными полями, прописать большую часть собержимого дескрипторов (все флаги, все длины) просто в данных, а в коде устанавливать только базовый адрес? Сделать структуру для GDTR, заполнять поле limit автоматически.. Это я к тому что, сейчас проверять твой код, заполняющий GDT очень сложно, я думаю даже тебе. Так что если баг там, то вряд ли его кто-то обнаружит. Да, вот мой вариант структур и таблицы, вдруг тебе понравится: Код (Text): ; Дескриптор segment_descriptor struct limit_low dw 0 ; Младшие два байта поля Segment limit base_low dw 0 ; Младшие два байта поля Base Address base_high0 db 0 ; Второй байт поля Base Address type_and_permit db 0 ; Флаги. flags db 0 ; Ещё одни флаги base_high1 db 0 ; Старший байт поля Base Address segment_descriptor ends ; GDTR table_register struct limit dw 0 ; Table Limit base dd 0 ; Linear Base Address table_register ends ; А так это выглядит в области данных: ; Глобальная таблица дескрипторов GDT label byte ; Нулевой дескриптор segment_descriptor <> ; Дескриптор сегмента кода, размер 4 Gb segment_descriptor <0ffffh, 0, 0, 10011010b, 10001111b, 0> ; 10011010b - 1001, C/D - 1, 0, R/W - 1, 0 ; 10001111b - G - 1, 000, Limit - 1111 ; Дескриптор сегмента кода, размер 64 Kb dsc64kb segment_descriptor <0ffffh, 0, 0, 10011010b, 0, 0> ; 10011010b - 1001, C/D - 1, 0, R/W - 1, 0 ; 0 - G - 0, 000, Limit - 0 ; Данные для загрузки в GDTR gdtr table_register <$ - GDT - 1, 0> dsc64kb нужно чтобы из кода нормально установить базовый адрес дескриптора.
Да, сообщение выше - это что-то типа брюзжания в обще педагогических целях Теперь по делу. Имхо, сама по себе команда sti не может рнять машину, так как она просто устанавливает флаг, машину роняет вызов обработчика прерывания или сам обработчик, или возврат из него. Например таймера, или ещё какой-нибудь. Вызов может ронять в двух случаях: - если в таблице прерываний кривой адрес. Например, если ты её где-то портишь. - если у нас проблемы со стеком Сам обработчик может ронять, если у него кривой код. Опять же, для этого нужно испортить либо адрес, либо код. Возврат из обработчика может ронять систему, если текущее значение CS не соответствует реальности. Например, мы сменили режим, а CS остался от старого режима. При этом всё более менее работает, но вот при возврате из прерывания и восстановлении адреса из стека, CS будет трактоваться как CS текущего режима. Со всеми вытекающими. С CS у тебя вроде всё впорядке, память ты вроде не портишь, остаётся стек. Проверить очень просто - не менять значение SS и SP при переходе между режимами, ни при переходе туда, ни при переходе обратно. Если заработает, значит именно тут собака и порылась. Если нет, нужно думать дальше.
sergh Надо же! вы правы Ж) действительно дело в стеке.. закомментил след. строчки: 168-170,186,213,218 запустил на реальной тачке.. все ок ) странно, а что тогда со стеком? может не стоит для него отдельный селектор делать? а если так, то как лучше тогда его оформить (ss)? а на счет статических GDT и селекторов: в том то и дело что мне нужно будет создавать их динамически.. позже сделаю побольше всю GDT и тогда можно уже будет штамповать новые селекторы "налету" в любом случае спасибо за советы. PROFi на счет A20 я знаю.. просто второпях код постил.. щас все ок.. а на счет kputzs_bios все норма.. по крайней мере в unreal mode она работает ок.. как и в real
я тут еще раз поэкспериментировал и... вот такая весчъ получается: когда я не стал создавать отдельный селектор под стек, а записал в ss селектор для ds (это все в pmode вначале - стр.165), а после как и раньше загрузил ss селектором realmode данных (185), то все стало ок.. но ведь стек растет вниз? а значит когда я загрузил ss селектором для ds ничего плохого не произойдет потом? фактически, ds расположен по адресу 01000h - сюда грузится вторичный загрузчик.
NeTxXx Как лучше оформить - смотря что от него нужно. Если тебе нужно просто unreal mode получить, то лучше вообще его не трогать, так же как и cs кстати. А вот если действительно планируешь в защищённом режиме работать, моежет быть нужен отдельный дескриптор и селектор. Что именно плохо со стеком - не знаю, просто методем исключения вычислил Попробуй упростить программу - например, для начала, сделать статические дескрипторы Убрать лишнее, ну и т.п. Или наоборот, взять простой, но работающий вариант, и постепенно довести его до нужного тебе функционала.
NeTxXx Во-первых, то, какой селектор/дескриптор у стека был в PM не может никак влиять на поведение стека после переключения режимов и перезаписи значения SS. Во всяком случае, я не вижу никаких механизмов влияния. Так что единственное, что приходит в голову - портится сохранянное значение SS и SP. Посмотри ещё раз свой алгоритм формирования GDT, я понимаю, что всё время в одну точку долблю, но просто это самая запутанная часть программы. Во-вторях, можно совместить с даными, если размера хватает, ничего плохого не случится. Защищённый режим конечно отличается от реального, но всё-таки не слишком сильно.
Гм, разобрался вроде с тем как ты дескрипторы генеришь, всё не так страшно. Сделай стеку type 001, а не 011, после этого глядишь и заработает. С дескрипторами с типом 011 нужно не так обращаться, почитай документацию внимательно. А лучше их вообще не использовать.
sergh все ок. спасибо. от отдельного дескриптора стека я вообще пока отказался. загружаю его дескриптором от сегмента данных.