;//--------------------------------------------------------------- ;// whycrash.s ;// ;// This program creates an exception-handler for any General ;// Protection Exceptions (Interrupt-0x0D) which will display ;// some diagnostic information (to aid us in determining the ;// cause of a system 'crash' that occurs in protected-mode). ;// ;// assemble with: $ as86 whycrash.s -b whycrash.b ;// install using: $ dd if=whycrash.b of=/dev/fd0 seek=1 ;// ;// NOTE: This code begins executing with CS:IP = 1000:0002. ;// ;// programmer: ALLAN CRUSE ;// written on: 02 MAR 2004 ;//--------------------------------------------------------------- .WORD 0xABCD ; programming signature ;----------------------------------------------------------------- main: mov ax, cs ; address this segment mov ds, ax ; with DS register pop dword return_address ; store return-address mov ss, ax ; adjust SS register lea esp, tos0 ; and set new stacktop call build_interrupt_gate call enter_protected_mode call execute_fault13_demo call leave_protected_mode push dword return_address ; recover our exit-address retf ; exit to program launcher ;----------------------------------------------------------------- return_address: .LONG 0 ; to store exit-address ;----------------------------------------------------------------- ; EQUATES realCS EQU 0x1000 ; segment-address of code mswGDT EQU 0x0001 ; base-address upper-word limGDT EQU 0x003F ; allocates 8 descriptors sel_es EQU 0x0008 ; vram-segment selector sel_cs EQU 0x0010 ; code-segment selector sel_ss EQU 0x0018 ; data-segment selector ;----------------------------------------------------------------- .ALIGN 8 theGDT: .WORD 0x0000, 0x0000, 0x0000, 0x0000 ; null descriptor .WORD 0x7FFF, 0x8000, 0x920B, 0x0000 ; vram descriptor .WORD 0xFFFF, 0x0000, 0x9A01, 0x0000 ; code descriptor .WORD 0xFFFF, 0x0000, 0x9201, 0x0000 ; data descriptor .WORD 0x0000, 0x0000, 0x0000, 0x0000 ; null descriptor .WORD 0x0000, 0x0000, 0x0000, 0x0000 ; null descriptor .WORD 0x0000, 0x0000, 0x0000, 0x0000 ; null descriptor .WORD 0x0000, 0x0000, 0x0000, 0x0000 ; null descriptor ;----------------------------------------------------------------- theIDT: .SPACE 2048 ; for 256 gate-descriptors ;----------------------------------------------------------------- ;----------------------------------------------------------------- build_interrupt_gate: ; setup gate-descriptor for General Protection Exceptions mov edi, #13 ; gate-descriptor's index lea di, theIDT[edi*8] ; gate-descriptor address mov 0[di], #isrGPF ; loword of entry-point mov 2[di], #sel_cs ; code-segment selector mov 4[di], #0x8E00 ; gate-type (386 intr) mov 6[di], #0x0000 ; hiword of entry-point ret ;----------------------------------------------------------------- enter_protected_mode: cli ; no device interrupts mov eax, cr0 ; get machine status bts eax, #0 ; set PE-bit to 1 mov cr0, eax ; enable protection push #mswGDT ; push base-address hiword push #theGDT ; push base-address loword push #limGDT ; push GDT's segment-limit lgdt [esp] ; load GDTR register-image add sp, #6 ; discard three stackwords push #0x0001 ; push base-address hiword push #theIDT ; push base-address loword push #0x07FF ; push IDT's segment-limit lidt [esp] ; load IDTR register-image add esp, #6 ; discard three stackwords jmpf #pm, #sel_cs ; reload register CS pm: mov ax, #sel_ss mov ss, ax ; reload register SS mov ds, ax ; reload register DS xor ax, ax ; use 'null' selector mov es, ax ; to purge invalid ES mov fs, ax ; to purge invalid FS mov gs, ax ; to purge invalid GS ret ; back to main routine ;----------------------------------------------------------------- execute_fault13_demo: ; here we try executing an impermissible instruction ; in order to trigger the processor's entry into our ; fault-handler for General Protection exceptions int 0x10 ; no gate is present for INT-0x10 ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- leave_protected_mode: push #0x0000 ; push base-address hiword push #0x0000 ; push base-address loword push #0x03FF ; push IVT's segment-limit lidt [esp] ; load IDTR register-image add esp, #6 ; discard three stackwords mov ax, ss ; address 64KB r/w segment mov ds, ax ; using DS register mov es, ax ; and ES register mov eax, cr0 ; get machine status btr eax, #0 ; reset PE-bit to 0 mov cr0, eax ; disable protection jmpf #rm, #realCS ; reload register CS rm: mov ax, cs mov ss, ax ; reload register SS mov ds, ax ; reload register DS sti ; interrupts allowed ret ; back to main routine ;----------------------------------------------------------------- ;---- Our 'exception-handler' for General Protection faults ---- ;----------------------------------------------------------------- isrGPF: pushad ; push general registers push dword #0 ; push longword zero mov [esp], ds ; and store DS value push dword #0 ; push longword zero mov [esp], es ; and store ES value push dword #0 ; push longword zero mov [esp], fs ; and store FS value push dword #0 ; push longword zero mov [esp], gs ; and store GS value ; the following loop draws each stack field onscreen mov ax, #sel_ss ; address this segment mov ds, ax ; with DS register mov ax, #sel_es ; address video memory mov es, ax ; with ES register mov ebp, esp ; setup base-pointer xor edx, edx ; begin count at zero .L1: call draw_stack_field ; show item on screen inc edx ; increment counter cmp edx, count ; all items shown? jb .L1 ; no, show another call leave_protected_mode ; go back to Real-Mode push dword return_address ; setup for termination retf ; exit to program loader ;----------------------------------------------------------------- ;----------------------------------------------------------------- field: .ASCII " GS= FS= ES= DS=EDI=ESI=EBP=ESP=" .ASCII "EBX=EDX=ECX=EAX=err=EIP= CS=EFL=" count: .LONG ( * - field )/4 ; number of stack fields hexlst: .ASCII "0123456789ABCDEF" ; list of hex numerals outbuf: .ASCII " nnn=xxxxxxxx " ; output buffer buflen: .WORD * - outbuf ; buffer length ;----------------------------------------------------------------- draw_stack_field: ; the element-number is found in register EDX imul esi, edx, #4 ; offset to field name mov eax, field[esi] ; load the name-string mov outbuf+1, eax ; place name in buffer mov eax, [ebp+edx*4] ; load the field-value lea di, outbuf+5 ; point to destination call eax2hex ; convert to hex string mov ecx, count ; total number of items sub ecx, edx ; minus this item-number imul edi, ecx, #160 ; offset to screen row add edi, #130 ; plus column's offset cld ; do forward processing lea si, outbuf ; point DS:SI to buffer mov ah, #0x50 ; setup color attribute mov cx, buflen ; setup character count .L2: lodsb ; fetch next character stosw ; write char and color loop .L2 ; show the full string ret ; back to the caller ;----------------------------------------------------------------- eax2hex: ; converts value in EAX to hexadecimal string at DS:DI pushad ; preserve registers mov edx, eax ; copy value to EAX lea ebx, hexlst ; setup xlat table mov ecx, #8 ; number of nybbles .L0: rol edx, #4 ; next nybble to DL mov al, dl ; copy nybble's byte and al, #0x0F ; isolate the nybble xlat ; convert to numeral mov [di], al ; numeral into buffer inc di ; advance the pointer loop .L0 ; do the next nybble popad ; restore registers ret ; back to the caller ;----------------------------------------------------------------- .ALIGN 16 ; alignment at paragraph .SPACE 512 ; reserved for stack use tos0: ; label fop top-of-stack ;----------------------------------------------------------------- END