//----------------------------------------------------------------- // 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). // // to assemble: $ as whycrash.s -o whycrash.o // and to link: $ ld whycrash.o -T ldscript -o whycrash.b // and install: $ dd if=whycrash.b of=/dev/sda4 seek=1 // // NOTE: This code begins ecxecuting with CS:IP = 1000:0002. // // programmer: ALLAN CRUSE // written on: 22 SEP 2008 //----------------------------------------------------------------- .section .text #------------------------------------------------------------------ .word 0xABCD # programming 'signature' #------------------------------------------------------------------ main: .code16 # starts in x86 real-mode mov %sp, %cs:ipltos+0 # preserve loader's SP mov %ss, %cs:ipltos+2 # preserve loader's SS mov %cs, %ax # address program's data mov %ax, %ss # using SS register lea tos, %sp # establish new stacktop call build_interrupt_gate call enter_protected_mode call execute_fault13_demo call leave_protected_mode lss %cs:ipltos, %sp # recover loader's SS:SP lret # and exit to the loader #------------------------------------------------------------------ ipltos: .word 0, 0 # for saving stack-address #------------------------------------------------------------------ theGDT: .quad 0x0000000000000000 # required null-descriptor .equ sel_cs, .-theGDT # selector for code-segment .quad 0x00009A010000FFFF # code-segment's descriptor .equ sel_ds, .-theGDT # selector for data-segment .quad 0x000092010000FFFF # data-segment's descriptor .equ sel_es, .-theGDT # selector for vram-segment .quad 0x0000920B80007FFF # vram-segment's descriptor .equ limGDT, (.-theGDT)-1 # segment-limit of our GDT #------------------------------------------------------------------ #------------------------------------------------------------------ theIDT: .zero 256*8 # for 256 gate-descriptors .equ limIDT, (.-theIDT)-1 # segment-limit of our IDT #------------------------------------------------------------------ build_interrupt_gate: # setup gate-descriptor for General Protection Exceptions mov $0x0D, %ebx # gate-number into EBX lea theIDT(,%ebx,8), %di # point DS:DI to entry movw $isrGPF, %ss:0(%di) # loword of entry-point movw $sel_cs, %ss:2(%di) # code-segment selector movw $0x8E00, %ss:4(%di) # gate-type=0xE (32-bit) movw $0x0000, %ss:6(%di) # hiword of entry-point ret #------------------------------------------------------------------ enter_protected_mode: cli # no device interrupts mov %cr0, %eax # current machine status bts $0, %eax # set image of PE-bit mov %eax, %cr0 # turn on protection lgdt %cs:regGDT # establish the GDT lidt %cs:regIDT # establish the IDT mov $sel_ds, %ax # address program stack mov %ax, %ss # using SS register ljmp $sel_cs, $pm # also reload CS and IP pm: ret # back to main procedure #------------------------------------------------------------------ leave_protected_mode: mov $sel_ds, %ax # real-mode limit/rights mov %ax, %ds # into DS register mov %ax, %es # also ES register mov %ax, %fs # also FS register mov %ax, %gs # also GS register mov %cr0, %eax # get machine status btr $0, %eax # reset PE-bit image mov %eax, %cr0 # turn off protection ljmp $0x1000, $rm # must reload CS and IP rm: mov %cs, %ax # address program stack mov %ax, %ss # with real-mode SS lidt %cs:regIVT # restore real-mode IVT sti # device interrupts ok ret # back to main procedure #------------------------------------------------------------------ #------------------------------------------------------------------ regGDT: .word limGDT, theGDT, 0x0001 # image for register GDTR regIDT: .word limIDT, theIDT, 0x0001 # image for register IDTR regIVT: .word 0x03FF, 0x0000, 0x0000 # image for register IDTR #------------------------------------------------------------------ execute_fault13_demo: # preserve the stack-address upon entry to this procedure # (so our fault-handler will be able to return to 'main') mov %sp, %ss:tossav+0 # save SP register-value mov %ss, %ss:tossav+2 # save SS register-value # Next we will try executing an impermissible instruction # in order to trigger a processor 'fault', and thus enter # our fault-handler for any General Protection Exception int $0x10 #<---- no gate exists for this ID ret #<---- Note: this is 'dead' code! #------------------------------------------------------------------ tossav: .word 0, 0 # to hold ring0's stacktop #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ isrGPF: # Interrupt Service Routine for General Protection Faults pushal # push general registers pushl %ds # push DS (as longword) pushl %es # push ES (as longword) pushl %fs # push FS (as longword) pushl %gs # push GS (as longword) # setup DS and ES to address our data and video memory mov $sel_es, %ax # address video memory mov %ax, %es # with ES register mov $sel_ds, %ax # address program data mov %ax, %ds # with DS register # the following loop draws each stack-element onscreen mov %esp, %ebp # copy stacktop to EBP xor %edx, %edx # start element-count nxelt: call draw_stack_element # draw current element inc %edx # increment the count cmp $ELTS, %edx # all elements shown? jb nxelt # no, show another one lss %cs:tossav, %sp # load saved stack-address ret # for a return to 'main' #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ #------------------------------------------------------------------ field: .ascii " GS= FS= ES= DS=EDI=ESI=EBP=ESP=" .ascii "EBX=EDX=ECX=EAX=err=EIP= CS=EFL=" .equ ELTS, ( . - field )/4 # number of stack elements #------------------------------------------------------------------ hex: .ascii "0123456789ABCDEF" # list of the hex numerals buf: .ascii " nnn=xxxxxxxx " # buffer for output-string len: .word . - buf # length for output-string hue: .byte 0x70 # colors: black-upon-white #------------------------------------------------------------------ draw_stack_element: # the element-number is found in regster EDX mov field(,%edx,4), %eax # get the field's name mov %eax, buf+1 # put name into buffer mov (%ebp,%edx,4), %eax # get the field's value lea buf+5, %di # point DS:DI into buffer call eax2hex # convert value to string mov $22, %ax # bottom-item line-number sub %dx, %ax # minus count of elements imul $160, %ax, %di # times size of screenrow sub $30, %di # minus right-hand indent cld # do forward processing lea buf, %si # point DS:SI to source mov hue, %ah # setup color-code in AH mov len, %cx # setup character-count nxchr: lodsb # fetch next character stosw # store char and color loop nxchr # again if other chars ret # back to the caller #------------------------------------------------------------------ eax2hex: # converts value in EAX to hexadecimal string at DS:DI pushal # preserve registers mov $8, %cx # number of nybbles nxnyb: rol $4, %eax # next nybble into AL mov %al, %bl # copy nybble into BL and $0xF, %bx # isolate nybble bits mov hex(%bx), %dl # lookup nybble digit mov %dl, (%di) # put digit in buffer inc %di # advance buffer-index loop nxnyb # again if more nybbles popal # restore registers ret # back to the caller #------------------------------------------------------------------ .align 16 # assures stack alignment .space 512, 0xFF # initialize for this demo tos: # label for 'top-of-stack' #------------------------------------------------------------------ .end # nothing else to assemble