//------------------------------------------------------------------- // exambugs.s // // This program is supposed to display the current value held // in system register CR0, but unfortunately it contains some // programming 'bugs'. Your job is to apply you knowledge of // x86-based PC-programming to find and eliminate these bugs. // // to assemble: $ as exambugs.s -o exambugs.o // and to link: $ ld exambugs.o -T ldscript -o exambugs.b // and install: $ dd if=exambugs.b of=/dev/sda4 seek=1 // // NOTE: This program begins executing with CS:IP = 1000:0002. // // programmer: ALLAN CRUSE // written on: 03 NOV 2008 // // revised by: // changed on: 04 NOV 2008 //------------------------------------------------------------------- # manifest constant .equ realCS, 0x1000 .section .text #------------------------------------------------------------------- .word 0xABCD # our application signature #------------------------------------------------------------------- main: .code16 # for Pentium 'real-mode' mov %sp, %cs:exit_pointer+0 # preserve the loader's SP mov %ss, %cs:exit_pointer+2 # preserve the loader's SS mov %cs, %ax # address program's data mov %ax, %ss # also SS register lea tos0, %sp # and setup new stacktop call initialize_os_tables call enter_protected_mode call execute_program_demo call leave_protected_mode lss %cs:exit_pointer, %sp # recover saved SS and SP lret # exit back to the loader #------------------------------------------------------------------- exit_pointer: .word 0, 0 # for loader's SS and SP #------------------------------------------------------------------- theTSS: .long 0 # TSS back-link (unused) .long 0 # reserved for ESP0 .long 0 # reserved for SS0 .equ limTSS, (.-theTSS)-1 # our TSS-segment's limit #------------------------------------------------------------------- theIDT: .space 256 * 8 # enough for 256 gate-descriptors .equ limIDT, (.-theIDT)-1 # our IDT-segment's limit #------------------------------------------------------------------- #------------------------------------------------------------------- theGDT: .word 0x0000, 0x0000, 0x0000, 0x0000 # null descriptor .equ sel_cs, (.-theGDT)+0 # code-segment's selector .word 0xFFFF, 0x0000, 0x9A01, 0x0000 # code descriptor .equ sel_ds, (.-theGDT)+0 # data-segment's selector .word 0xFFFF, 0x0000, 0x9201, 0x0000 # data descriptor .equ sel_es, (.-theGDT)+3 # vram-segment's selector .word 0x0007, 0x8000, 0xF20B, 0x0080 # vram descriptor .equ sel_ss, (.-theGDT)+0 # stak-segment's selector .word 0xFFFF, 0x0000, 0x9201, 0x0000 # stak descriptor .equ userCS, (.-theGDT)+3 # code-segment's selector .word 0xFFFF, 0x0000, 0xFA01, 0x0000 # code descriptor .equ userDS, (.-theGDT)+3 # data-segment's selector .word 0xFFFF, 0x0000, 0xF201, 0x0000 # data descriptor .equ selTSS, (.-theGDT)+0 # task-segment's selector .word limTSS, theTSS, 0x8B01, 0x0000 # task descriptor .equ toexit, (.-theGDT)+0 # selector for call gate .word finish, sel_cs, 0xEC00, 0x0000 # gate descriptor .equ limGDT, (.-theGDT)-1 # our GDT-segment's limit #------------------------------------------------------------------- regGDT: .word limGDT, theGDT, 0x0001 # register-image for GDTR regIDT: .word limIDT, theIDT, 0x0001 # register-image for IDTR regIVT: .word 0x03FF, 0x0000, 0x0000 # register-image for IDTR #------------------------------------------------------------------- initialize_os_tables: # initialize IDT descriptor for gate 0x0D mov $0x0D, %ebx # ID-number for the gate lea theIDT(, %ebx, 8), %di # gate's offset-address movw $isrGPF, %ss:0(%di) # entry-point's loword movw $sel_cs, %ss:2(%di) # code-segment selector movw $0x8E00, %ss:4(%di) # 386 interrupt-gate movw $0x0000, %ss:6(%di) # entry-point's hiword ret #------------------------------------------------------------------- enter_protected_mode: cli # no device interrupts mov %cr0, %eax # get machine status bts $0, %eax # set PE-bit to 1 mov %eax, %cr0 # enable protection lgdt %cs:regGDT # setup GDTR register lidt %cs:regIDT # setup IDTR register ljmp $sel_cs, $pm # reload register CS pm: mov $sel_ds, %ax mov %ax, %ss # reload register SS xor %ax, %ax # use "null" selector mov %ax, %ds # to purge invalid DS mov %ax, %es # to purge invalid ES mov %ax, %fs # to purge invalid FS mov %ax, %gs # to purge invalid GS ret #------------------------------------------------------------------- #------------------------------------------------------------------- tossave: .word 0, 0, 0 # stores a 48-bit pointer #------------------------------------------------------------------- execute_program_demo: mov %esp, %ss:tossave+0 # preserve 32-bit offset mov %ss, %ss:tossave+4 # plus 16-bit selector movl $0x10000, %ss:theTSS+4 # initialize ESP0 field movl $sel_ss, %ss:theTSS+8 # initialize SS0 field mov $selTSS, %ax # selector for our TSS ltr %ax # loaded into TR pushl $userDS # image for register SS pushl $tos3 # image for register ESP pushl $userCS # image for register CS pushl $show_info # image for register EIP lretl # transfer into ring3 finish: lss %cs:tossave, %esp # reload our saved SS:ESP ret # return to main function #------------------------------------------------------------------- #------------------------------------------------------------------- msg: .ascii " CR0=" # label for the information buf: .ascii "xxxxxxxx " # buffer for register value len: .short . - msg # number of message's bytes att: .byte 0x66 # colors for message's text #------------------------------------------------------------------- show_info: mov $sel_es, %ax # address video memory mov %ax, %es # with ES register xor %di, %di # point ES:DI to start mov $2000, %cx # count of screen cells mov $0x0720, %ax # blanks w/normal color cld # do foward memory-fill rep stosw # clear the full screen mov %ss, %ax # address message area mov %ax, %ds # with DS register lea buf, %di # point DS:DI to buffer smsw %eax # store CR0 into EAX call eax2hex # convert to hex string lea msg, %si # point DS:SI to string mov $1990, %di # point ES:DI to screen mov len, %cx # string length into CX mov att, %ah # and text colors to AH nxchr: lodsb # load next character stosw # store char and color loop nxchr # do the entire string lcall $toexit, $0 # exit through call-gate #------------------------------------------------------------------- #------------------------------------------------------------------- leave_protected_mode: mov $sel_ds, %ax # address 64K r/w segment mov %ax, %ds # using DS register mov %ax, %es # and ES register mov %ax, %fs # using FS register mov %ax, %gs # and GS register mov %cr0, %eax # get machine status btr $0, %eax # reset PE-bit to 0 mov %eax, %cr0 # disable protection ljmp $realCS, $rm # reload register CS rm: mov %cs, %ax mov %ax, %ss # reload register SS lidt %cs:regIVT # restore vector table sti # and allow interrupts ret #------------------------------------------------------------------- #------------------------------------------------------------------- isrGPF: # our fault-handler for General Protection Exceptions pushal # preserve registers pushl $0 mov %ds, (%esp) # store DS pushl $0 mov %es, (%esp) # store ES pushl $0 mov %fs, (%esp) # store FS pushl $0 mov %gs, (%esp) # store GS pushl $0 mov %ss, (%esp) # store SS pushl $0 strw (%esp) # store TR mov %esp, %ebp # setup frame base call draw_stack # display registers ljmp $sel_cs, $finish # transfer to demo finish #------------------------------------------------------------------- hex: .ascii "0123456789ABCDEF" # array of hex digits names: .ascii " TR SS" .ascii " GS FS ES DS" .ascii " EDI ESI EBP ESP" .ascii " EBX EDX ECX EAX" .ascii " err EIP CS EFL" .equ NELTS, (. - names)/4 # number of elements buf0: .ascii " nnn=xxxxxxxx " # buffer for output len0: .int . - buf0 # length of output att0: .byte 0x70 # color attributes #------------------------------------------------------------------- #------------------------------------------------------------------- eax2hex: # converts value in EAX to hexadecimal string at DS:DI pushal mov $8, %cx # setup digit counter nxnyb: rol $4, %eax # next nybble into AL mov %al, %bl # copy nybble into BL and $0xF, %bx # isolate nybble's bits mov hex(%bx), %dl # lookup ascii-numeral mov %dl, (%di) # put numeral into buf inc %di # advance buffer index loop nxnyb # back for next nybble popal ret #------------------------------------------------------------------- draw_stack: pushal # preserve registers 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 cld # do forward processing xor %ebx, %ebx # initial element index nxelt: # put element's label into buffer mov names(, %ebx, 4), %eax # fetch element's label mov %eax, buf0 # store label into buf # put element's value into buffer mov (%ebp, %ebx, 4), %eax # fetch element's value lea buf0+5, %edi # point to value field call eax2hex # convert value to hex # compute element's screen-offset imul $160, %ebx, %eax # offset to screen line mov $3810, %edi # from starting location sub %eax, %edi # destination goes in EDI # write buffer to screen memory lea buf0, %esi # point DS:ESI to buffer mov len0, %ecx # setup buffer's length mov att0, %ah # setup color attribute nxpel: lodsb # fetch next character stosw # store char and color loop nxpel # again if more chars inc %ebx # increment element number cmp $NELTS, %ebx # more elements to show? jb nxelt # yes, back for next one popal # restore registers ret # and return to caller #------------------------------------------------------------------- #------------------------------------------------------------------- .align 16 # assure stack alignment .space 512 # reserved for stack use tos3: # label for top-of-stack .space 512 # reserved for stack use tos0: # label for top-of-stack #------------------------------------------------------------------- .end # nothing more to assemble