; Exemple manuel de passage en mode protege et de retour en mode ; reel, on commence par tester le mode courant du processeur ; Description des selecteurs page 4-8 du bouquin 486DX2 ; Les 3 bits de poids faibles nuls indiquent RPL=0 (et GDT) et les ; autres bits forment un index dans la GDT (et non LDT) ; Auteur : Benoit Papillault ; Creation : Dimanche 10 Octoble 1996 ; Derniere modification : Jeudi 24 Avril 1997 ; On utilise 4 segments: un segment de donnee (data), un segment de code ; 16 bits (code16) et un segment de code 32 bits (code32) et un segment ; de pile (ma_pile) ; la gdt contient les descripteurs de notre programme, a savoir: ; selecteur descripteur segment ; code32_sel code32_descriptor code32 ; data32_sel data32_descriptor data ; core32_sel core32_descriptor 0000h ; code16_sel code16_descriptor code16 ; data16_sel data16_descriptor data ; stack32_sel stack32_descriptor data ; tss_sel tss_descriptor data:le_tss .386p include tss.inc include registre.inc include tache2.inc include task_v86.inc include except32.inc Segment_descriptor struc seg_length0_15 dw ? ; low word of the segment length base_addr0_15 dw ? ; low word of base address base_addr16_23 db ? ; low byte of high word of base addr. flags db ? ; segment type and misc. flags access db ? ; highest nibble of segment length ; and access flags base_addr24_31 db ? ; highest byte of base address Segment_descriptor ends Interrupt_descriptor struc offset0_15 dw ? ; low word of handler offset selector0_15 dw ? ; segment selector zero_byte db 0 ; unused in this descriptor format flags db ? ; flag-byte offset16_31 dw ? ; high-word of handler offset Interrupt_descriptor ends stack_size equ 1000h Data segment para public use16 extrn le_tss:byte db stack_size dup (0) ; 32-bit stack stack16 label byte gdt label byte dummy_descriptor segment_descriptor <0,0,0,0,0,0> code32_descriptor segment_descriptor <0ffffh,0,0,9ah,0cfh,0> ; 4GB 32-bit code data32_descriptor segment_descriptor <0ffffh,0,0,92h,0cfh,0> ; 4GB 32-bit data core32_descriptor segment_descriptor <0ffffh,0,0,92h,0cfh,0> ; 4GB 32-bit core code16_descriptor segment_descriptor <0ffffh,0,0,9ah,0,0> ; 64k 16-bit code data16_descriptor segment_descriptor <0ffffh,0,0,92h,0,0> ; 64k 16-bit data stack32_descriptor segment_descriptor <0ffffh,0,0,92h,0cfh,0>; expand down stack tss_descriptor segment_descriptor <68h+io_range,0,0,89h,0,0> tss2_descriptor segment_descriptor <68h+io_range,0,0,89h,0,0> tss_v86_descriptor segment_descriptor <68h+io_range,0,0,89h,0,0> gdt_size=$-(offset dummy_descriptor) public code32_sel,data32_sel,core32_sel,code16_sel,data16_sel public stack32_sel,tss_sel,tss2_sel,tss_v86_sel ; la definition des selecteurs code32_sel equ code32_descriptor - gdt data32_sel equ data32_descriptor - gdt core32_sel equ core32_descriptor - gdt code16_sel equ code16_descriptor - gdt data16_sel equ data16_descriptor - gdt stack32_sel equ stack32_descriptor - gdt tss_sel equ tss_descriptor - gdt tss2_sel equ tss2_descriptor - gdt tss_v86_sel equ tss_v86_descriptor - gdt public cr0_pe,cr0_ts ; les bits interresants de CR0 cr0_pe equ 1h cr0_ts equ 8h interrupt_0 Interrupt_descriptor <,code32_sel,0,8eh,0> ;00 Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> Interrupt_descriptor <,code32_sel,0,8eh,0> idt_size=$-(offset interrupt_0) notre_gdt dw gdt_size gdt_base dd ? notre_idt dw idt_size idt_base dd ? idt_real df 3ffh ; Real Mode IDT caca dd 0 ; Les messages MIDT db 'Chargement de IDT ok',13,10,'$' MGDT db 'Chargement de GDT ok',13,10,'$' MFin db 'Fin normale du programme',13,10,'$' MProtected db 40h,0,10,0,1fh,'Coucou, mode protege',0 MInt0 db 40h,0,11,0,1fh,'Nous sommes dans l',27h,'interruption 0',0 MDummy db 40h,0,12,0,1fh,'Dummy int',0 MData db 'Data = $' MCRLF db 13,10,'$' MCode32 db 'Code32 = $' MCode16 db 'Code16 = $' MV86 db 'Vous ‚tes en mode V86',13,10,'$' m_idtr db 40h,0,13,0,1fh,'valeur de idtr = ',0 m_gdtr db 40h,0,14,0,1fh,'valeur de gdtr = ',0 m_exception db 40h,0,15,0,1fh,'une exception s',27h,'est produite...',0 val_idtr dq 011111111h val_gdtr dq 011111111h m_exc_0 db 40h,0,15,0,1fh,'l',27h,'exception 0 s',27h,'est produite...',0 m_exc_1 db 40h,0,15,0,1fh,'l',27h,'exception 1 s',27h,'est produite...',0 m_exc_2 db 40h,0,15,0,1fh,'l',27h,'exception 2 s',27h,'est produite...',0 m_exc_3 db 40h,0,15,0,1fh,'l',27h,'exception 3 s',27h,'est produite...',0 m_exc_4 db 40h,0,15,0,1fh,'l',27h,'exception 4 s',27h,'est produite...',0 m_exc_5 db 40h,0,15,0,1fh,'l',27h,'exception 5 s',27h,'est produite...',0 m_exc_6 db 40h,0,15,0,1fh,'l',27h,'exception 6 s',27h,'est produite...',0 m_exc_7 db 40h,0,15,0,1fh,'l',27h,'exception 7 s',27h,'est produite...',0 m_exc_8 db 40h,0,15,0,1fh,'l',27h,'exception 8 s',27h,'est produite...',0 m_exc_9 db 40h,0,15,0,1fh,'l',27h,'exception 9 s',27h,'est produite...',0 m_exc_10 db 40h,0,15,0,1fh,'l',27h,'exception 10 s',27h,'est produite...',0 m_exc_11 db 40h,0,15,0,1fh,'l',27h,'exception 11 s',27h,'est produite...',0 m_exc_12 db 40h,0,15,0,1fh,'l',27h,'exception 12 s',27h,'est produite...',0 m_exc_13 db 40h,0,15,0,1fh,'l',27h,'exception 13 s',27h,'est produite...',0 m_exc_14 db 40h,0,15,0,1fh,'l',27h,'exception 14 s',27h,'est produite...',0 m_exc_15 db 40h,0,15,0,1fh,'l',27h,'exception 15 s',27h,'est produite...',0 m_exc_16 db 40h,0,15,0,1fh,'l',27h,'exception 16 s',27h,'est produite...',0 m_exc_17 db 40h,0,15,0,1fh,'l',27h,'exception 17 s',27h,'est produite...',0 m_test dw 2*79 dw 24 db 0fch,'#',0 Data ends ; Pour definir main32 Code32 segment para public use32 public main32 code32 ends Code16 segment para public use16 assume cs:Code16 ; extrn Debut_exception:near ; except.asm ; extrn Fin_exception:near ; except.asm extrn Enable_A20:near ; a20bis.asm extrn Disable_A20:near ; a20bis.asm extrn WriteHexa32:near ; hexa.asm extrn WriteHexa:near ; hexa.asm public Crlf extrn init_tss:near Affiche_descriptor proc near ; Affiche le descripteur contenu dans ds:di push eax mov eax,ds:[di] call WriteHexa32 mov eax,ds:[di+4] call WriteHexa32 call Crlf pop eax ret Affiche_descriptor endp main: ; Debut du programme ; On charge le segment de donnees mov ax,Data mov ds,ax ; On teste le mode courant du processeur smsw ax ; smsw peut etre execute en mode V86, alors ; que mov eax,cr0 aurait provoque une exception 13 test al,1 jz Suite mov dx,offset MV86 mov ah,9h int 21h mov ax,4c00h int 21h Suite: ; On initialise le mode texte (efface l'ecran) mov ax,3 int 10h ; Quelques affichage mov ah,09h mov dx,offset MData int 21h mov ax,Data call WriteHexa call Crlf ; Charger GDT ; le segment de code 32 bits mov ah,09h mov dx,offset MCode32 int 21h mov ax,Code32 call WriteHexa call Crlf movzx eax,ax shl eax,4 mov [ds:code32_descriptor.base_addr0_15],ax shr eax,16 mov [ds:code32_descriptor.base_addr16_23],al ; le segment de donnes 32 bits et 16 bits et la pile mov ax,Data movzx eax,ax shl eax,4 mov ds:[data32_descriptor.base_addr0_15],ax mov ds:[data16_descriptor.base_addr0_15],ax mov ds:[stack32_descriptor.base_addr0_15],ax shr eax,16 mov ds:[data32_descriptor.base_addr16_23],al mov ds:[data16_descriptor.base_addr16_23],al mov ds:[stack32_descriptor.base_addr16_23],al ; le tss call init_tss xor eax,eax mov ax,data mov ds,ax shl eax,4 xor ebx,ebx mov bx,offset le_tss add eax,ebx mov ds:[tss_descriptor].base_addr0_15,ax shr eax,16 mov ds:[tss_descriptor].base_addr16_23,al ; le second tss call init_tss2 xor eax,eax mov ax,data mov ds,ax shl eax,4 xor ebx,ebx mov ebx,offset le_tss2 add eax,ebx mov ds:[tss2_descriptor].base_addr0_15,ax shr eax,16 mov ds:[tss2_descriptor].base_addr16_23,al ; le tss pour la tache v86 call init_tss_v86 xor eax,eax mov ax,data mov ds,ax shl eax,4 add eax,offset data:le_tss_v86 mov ds:[tss_v86_descriptor].base_addr0_15,ax shr eax,16 mov ds:[tss_v86_descriptor].base_addr16_23,al ; le segment de code 16 bits mov ah,09h mov dx,offset MCode16 int 21h mov ax,Code16 ; get code segment into AX call WriteHexa call Crlf movzx eax,ax ; clear high word shl eax,4 ; make a physical address mov [ds:code16_descriptor.base_addr0_15],ax ; store it in the dscr shr eax,16 mov [ds:code16_descriptor.base_addr16_23],al xor eax,eax mov ax,Data ; get 32-bit code segment into AX shl eax,4 ; make a physical address xor ebx,ebx mov bx,offset dummy_descriptor add eax,ebx ; calculate physical address of GDT mov ds:[gdt_base],eax mov ah,09h mov dx,offset MGDT int 21h ; initialisation de la idt (interrupt descriptor table) mov ax,data ; assume di:ptr interrupt_descriptor mov di,offset interrupt_0 mov eax,offset demo_int mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_1 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_2 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_3 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_4 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_5 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_6 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_7 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_8 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_9 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_10 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_11 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_12 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_13 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_14 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_15 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_16 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor mov eax,offset exc_17 mov ds:[di].offset0_15,ax add di,size interrupt_descriptor ; Charger IDT (256+ octets) xor eax,eax mov ax,Data ; get 32-bit code segment into AX shl eax,4 ; make a physical address xor ebx,ebx mov bx,offset interrupt_0 add eax,ebx ; calculate physical address of IDT mov ds:[idt_base],eax mov ah,09h mov dx,offset MIDT int 21h ; Affichage de la GDT ; mov di,offset dummy_descriptor ; call Affiche_descriptor ; mov di,offset code32_descriptor ; call Affiche_descriptor ; mov di,offset data32_descriptor ; call Affiche_descriptor ; mov di,offset core32_descriptor ; call Affiche_descriptor ; mov di,offset code16_descriptor ; call Affiche_descriptor ; mov di,offset data16_descriptor ; call Affiche_descriptor ; mov di,offset stack32_descriptor ; call Affiche_descriptor ; Encore des affichages call dump_reg_16 ; Autoriser la ligne A20 call Enable_A20 cli ; on definit le jump mov eax,offset main32 mov cs:[uoffset],ax ; On redirige qlq exceptions ; call Debut_exception lgdt fword ptr [ds:notre_gdt] lidt fword ptr [ds:notre_idt] ; Pour tester l'appel a main32 xor ecx,ecx ; Mettre CR0 bit PE = 1 mov eax,cr0 or al,cr0_pe mov cr0,eax ; On defini le registre de tache mov ax,tss_sel ltr ax ; Intersegment JMP db 0eah ; opcode for far jump (to set CS correctly) uoffset dw ? dw code32_sel Retour16: ; On charge les registres de segment avec des valeurs acceptables pour ; le mode 16 bits mov ax,data16_sel mov es,ax mov ds,ax mov fs,ax mov gs,ax mov ss,ax ; Chargement idt au mode reel lidt ds:idt_real ; Mettre CR0 bit PE = 0 ; mov eax,cr0 ; get CR0 into EAX ; and al,not (cr0_pe or cr0_ts) ; on efface le bit corespondant au mode protege ; ainsi que le bit corespondant au changement de tache ; celui-ci pouvant provoquer un erreur lors de calcul ; en virgule flottante (cf Intel 486 DX Microprocessor ; page 25 (Task Switched Bit) ; mov cr0,eax ; after this we are back in Real Mode! ; Autre solution plus radicale mov eax,10h mov cr0,eax xor eax,eax mov cr3,eax db 0eah dw offset flush_ipq,code16 flush_ipq: ; Charger les registres de segments avec leurs valeurs en mode reel mov ax,data mov ds,ax mov es,ax mov fs,ax mov gs,ax ; Restaurer SS:SP avant de reautoriser les interruptions mov ax,data mov ss,ax mov esp,offset stack16 sti ; On affiche la valeur de CX mov eax,ecx call WriteHexa32 call Crlf ; On affiche la valeur de caca mov eax,[ds:caca] call WriteHexa32 call Crlf ; Interdire la ligne A20 call Disable_A20 ; Message de fin mov ah,09h mov dx,offset MFin int 21h ; On restaure les exceptions ; call Fin_exception ; Fin du programme mov ax,4c00h int 21h Crlf proc near push dx push ax mov ah,09h mov dx,offset MCRLF int 21h pop ax pop dx ret Crlf endp Code16 ENDS Code32 segment para public use32 assume cs:Code32 extrn WriteHexa4_32:near ;---------------------------------------------------------------------------- ; protected mode translation of write_msg ; In: DS:ESI - pointer to format string ; le format de la chaine est le suivant ; 2 octets : x*2 ; 2 octets : y ; 1 octet : attribut ; la chaine terminee par zero ; Attention (a verifier) je sauve edx car il doit etre modifie par mul edi ; de maniere indirecte public write_msg write_msg proc near push eax push ecx push edx push esi push edi push es mov ax,core32_sel ; segment of text screen mov es,ax ; get it to ES xor eax,eax mov ax,ds:[esi+2] ; get Y position mov edi,160 mul edi xor ecx,ecx mov cx,ds:[esi] ; get X position add eax,ecx add eax,0b8000h mov edi,eax mov ah,ds:[esi+4] ; get attribute byte add esi,5 write_loop_pm: mov al,[esi] or al,al ; end of string? jz loop_end_pm inc si mov es:[edi],ax inc edi inc edi jmp write_loop_pm loop_end_pm: pop es pop edi pop esi pop edx pop ecx pop eax ret write_msg endp ;---------------- La routine d'interruption 0 ------------------------------- Demo_int proc far xor esi,esi mov si,offset MInt0 ; on affiche le message de l'interruption 0 call write_msg mov eax,ss:[esp] mov dx,0f20h call WriteHexa4_32 mov eax,ss:[esp+4] mov dx,1020h call WriteHexa4_32 mov eax,ss:[esp+8] mov dx,1120h call WriteHexa4_32 mov eax,ss:[esp+12] mov dx,1220h call WriteHexa4_32 iretd Demo_int endp test_mem proc near push ds push eax push ebx push ecx push edx ; eax = taille memoire existante ; ebx = valeur de verification ; ecx = offset 32 bits courant ; edx = valeur sauvegardee mov ax,core32_sel mov ds,ax mov eax,0100000h ; 1Mo pour eviter de scanner le premier mega mov ecx,eax pas equ 1000h boucle: ; On affiche la valeur courante push eax mov eax,ecx mov dx,420h call WriteHexa4_32 pop eax ; On affiche la taille existante mov dx,220h call WriteHexa4_32 mov edx,ds:[ecx] mov dword ptr ds:[ecx],0 mov ebx,ds:[ecx] cmp ebx,0 jne Fin_boucle mov dword ptr ds:[ecx],0ffffffffh mov ebx,ds:[ecx] cmp ebx,0ffffffffh jne Fin_boucle add eax,pas Fin_boucle: mov ds:[ecx],edx add ecx,pas cmp ecx,64*1024*1024 jbe boucle pop edx pop ecx pop ebx pop eax pop ds ret test_mem endp main32 proc far ; Load all Data Segment Register mov ax,data32_sel mov ds,ax mov es,ax mov fs,ax mov gs,ax ; Creation de la pile mov ax,stack32_sel mov ss,ax mov esp,offset stack16 ; pour tester la pile push ax pop ax ; On est en mode protege mov esi,offset MProtected ; just put the message... call write_msg ; On teste notre interruption mov eax,11111111h push eax int 0 pop eax ; Pour tester le passage en mode protege mov ecx,cr0 mov ds:caca,stack_size mov dx,20h mov eax,cr0 call WriteHexa4_32 ; On affiche les valeurs de idtr et gdtr sidt ds:val_idtr sgdt ds:val_gdtr mov esi,offset m_idtr call write_msg mov eax,dword ptr ds:val_idtr mov dx,0d39h call WriteHexa4_32 mov eax,dword ptr ds:val_idtr[4] mov dx,0d31h call writehexa4_32 mov esi,offset m_gdtr call write_msg mov eax,dword ptr ds:val_gdtr mov dx,0e39h call WriteHexa4_32 mov eax,dword ptr ds:val_gdtr[4] mov dx,0e31h call WriteHexa4_32 ; Pour rire ; sti ; => exception 9 ; On affiche tous les registres (en cours de test) mov eax,11111111h mov ebx,22222222h mov ecx,33333333h mov edx,44444444h mov ebp,esp mov esi,55555555h mov edi,66666666h mov dh,15 call dump_reg_32 ; On affiche un # clignotant ; mov esi,offset m_test ; call write_msg ; On teste la memoire ; call test_mem ; On commute vers la tache2 (cf tache2.asm) call appel_tache2 ; On commute vers la tache v86 (cf task_v86.asm) call appel_tache_v86 ; On produit une exception ; mov ax,0 ; mov ds,ax ; mov byte ptr ds:0,0 ; => provoque une exception 13, geree public fin32 ; On retourne au mode reel fin32: db 0eah ; far jump opcode ; when main returns, get back dw offset Retour16,0,code16_sel ; to the Real Mode code main32 endp exc_0: mov esi,offset m_exc_0 jmp exception exc_1: mov esi,offset m_exc_1 jmp exception exc_2: mov esi,offset m_exc_2 jmp exception exc_3: mov esi,offset m_exc_3 jmp exception exc_4: mov esi,offset m_exc_4 jmp exception exc_5: mov esi,offset m_exc_5 jmp exception exc_6: mov esi,offset m_exc_6 jmp exception exc_7: mov esi,offset m_exc_7 jmp exception exc_8: mov esi,offset m_exc_8 jmp exception exc_9: mov esi,offset m_exc_9 jmp exception exc_10: mov esi,offset m_exc_10 jmp exception exc_11: mov esi,offset m_exc_11 jmp exception exc_12: mov esi,offset m_exc_12 jmp exception exc_13: mov esi,offset m_exc_13 jmp exception exc_14: mov esi,offset m_exc_14 jmp exception exc_15: mov esi,offset m_exc_15 jmp exception exc_16: mov esi,offset m_exc_16 jmp exception exc_17: mov esi,offset m_exc_17 jmp exception la_pile segment stack Stack16_size db 4096 dup (?) la_pile ends Code32 ends END main