//----------------------------------------------------------------- // usevm86.s (A modification of our 'vm86demo.s' demo) // // Here we have modified our 'vm86demo.s' program so that now // some i/o-sensitive instructions are encountered within our // virtual-8086 mode subroutine (specifically 'int $0x1C' and // 'iret'). These will trigger General Progection faults and // their actions must be 'emulated' by our software if we are // to complete this new virtual-8086 subroutine successfully. // // to assemble: $ as usevm86.s -o usevm86.o // and to link: $ ld usevm86.o -T ldscript -o usevm86.b // and install: $ dd if=usevm86.b of=/dev/sda4 seek=1 // // NOTE: This code begins executing with CS:IP = 1000:0002. // // programmer: ALLAN CRUSE // written on: 04 DEC 2008 //----------------------------------------------------------------- .section .text #------------------------------------------------------------------ .word 0xABCD #------------------------------------------------------------------ main: .code16 mov %sp, %cs:ipltos+0 mov %ss, %cs:ipltos+2 mov %cs, %ax mov %ax, %ss lea tos0, %sp call enter_protected_mode call execute_program_demo call leave_protected_mode lss %cs:ipltos, %sp lret #------------------------------------------------------------------ ipltos: .word 0, 0 #------------------------------------------------------------------ theTSS: .space 0x68 .equ limTSS, (.-theTSS)-1 #------------------------------------------------------------------ theIDT: .space 13 * 8 .word isrGPF, privCS, 0x8E00, 0x0000 .equ limIDT, (.-theIDT)-1 #------------------------------------------------------------------ theGDT: .quad 0x0000000000000000 .equ privCS, (.-theGDT)+0 .quad 0x00409A010000FFFF .equ privSS, (.-theGDT)+0 .quad 0x004092010000FFFF .equ sel_cs, (.-theGDT)+0 .quad 0x00009A010000FFFF .equ sel_ds, (.-theGDT)+0 .quad 0x000092010000FFFF .equ sel_es, (.-theGDT)+0 .quad 0x0000920B8000FFFF .equ sel_fs, (.-theGDT)+0 .quad 0x008F92000000FFFF .equ selTSS, (.-theGDT) .word limTSS, theTSS, 0x8901, 0x0000 .equ limGDT, (.-theGDT)-1 #------------------------------------------------------------------ regGDT: .word limGDT, theGDT, 0x0001 regIDT: .word limIDT, theIDT, 0x0001 regIVT: .word 0x03FF, 0x0000, 0x0000 #------------------------------------------------------------------ enter_protected_mode: cli mov %cr0, %eax bts $0, %eax mov %eax, %cr0 lgdt %cs:regGDT lidt %cs:regIDT ljmp $sel_cs, $pm pm: mov $sel_ds, %ax mov %ax, %ss xor %ax, %ax mov %ax, %ds mov %ax, %es mov %ax, %fs mov %ax, %gs ret #------------------------------------------------------------------ execute_program_demo: # preserve our current stacktop address mov %esp, %ss:tossav+0 mov %ss, %ss:tossav+4 # switch to a 32-bit stack mov $privSS, %ax mov %ax, %ss and $~0x3, %esp # establish the Task-State Segment mov $selTSS, %ax ltr %ax # initialize the SS0:ESP0 fields in our TSS mov %esp, %ss:theTSS+4 mov %ss, %ss:theTSS+8 # prepare the stack for a transfer to Virtual-8086 mode pushl $0 # image for GS pushl $0 # image for FS pushl $0 # image for DS pushl $0 # image for ES pushl $0x1000 # image for SS pushl $tos3 # image for SP pushl $0x00020000 # image for EFLAGS pushl $0x1000 # image for CS pushl $vm86 # image for IP # transfer to Virtual-8086 mode (in ring3) iretl #------------------------------------------------------------------ tossav: .long 0, 0 # area for holding SS:ESP #------------------------------------------------------------------ back_to_main_routine: lss %cs:tossav, %esp # recover former stacktop ret # and go back to 'main' #------------------------------------------------------------------ vm86: .code16 # let's try executing a software interrupt int $0x1C # usually just returns # set all text-mode video attributes to a blue-background mov $0xB800, %ax # address video memory mov %ax, %ds # with DS register mov %ax, %es # also ES register xor %si, %si # point DS:SI to start xor %di, %di # point ES:DI to start cld # do forward processing mov $0x4000, %cx # number of video words nxpel: lodsw # fetch picture-element mov $0x17, %ah # set attribute-byte stosw # store picture-element loop nxpel # again for entire vram hlt # generate a GP-exception #------------------------------------------------------------------ isrGPF: .code32 # this is our exception-handler for GP-exceptions # verify that this exception occurred in Virtual-8086 mode btl $17, 12(%esp) # was VM-bit set? jc emulate # yes, do emulation ljmp $sel_cs, $back_to_main_routine emulate: # # Here we carefully preserve registers while we are determining # which opcode has triggered this General Protection Fault, and # then we will 'emulate' the usual actions for that instuction. # (Note that segment-registers already got saved on the stack.) # enter $0, $0 # setup stack access pushal # preserve registers mov $sel_fs, %ax # address flat memory mov %ax, %ds # with DS register # compute address of the faulting instruction in EBX movzxw 8(%ebp), %ebx # fetch the IP-image movzxw 12(%ebp), %eax # fetch the CS-image shl $4, %eax # sixteen times CS add %eax, %ebx # is added to IP # now we 'switch' on the faulting instruction's opcode cmpb $0xCD, (%ebx) # was it 'int-nn'? je emulate_int # yes, then enulate cmpb $0xCF, (%ebx) # was it 'iret'? je emulate_iret # yes, then emulate # --- check for other i/o-sensitive opcodes here --- # --- i.e., cli, sti, pushf, popf, pushfl, popfl --- # --- and add code that 'emulates' their actions --- # else we terminate this demo by returning to 'main' ljmp $sel_cs, $back_to_main_routine em_done: popal # restore saved registers leave # and former base-pointer add $4, %esp # discard the error-code iret # resume the VM86 task emulate_int: # decrement the SP-image to make room for three words subw $6, 20(%ebp) # make room for 3 words # compute address of the ring3 stacktop in register EDI movzxw 20(%ebp), %edi # fetch the SP-image movzxw 24(%ebp), %eax # fetch the SS-image shl $4, %eax # sixteen times SS add %eax, %edi # is added to SP # advance the IP-image past the 'int-nn' instruction addw $2, 8(%ebp) # advance the IP-image # transfer the IP, CS, FLAGS images to the ring3 stack mov 8(%ebp), %ax # fetch IP-image mov %ax, 0(%edi) # store IP-image mov 12(%ebp), %ax # fetch CS-image mov %ax, 2(%edi) # store CS-image mov 16(%ebp), %ax # fetch FLAGS-image mov %ax, 4(%edi) # store FLAGS-image # clear the IF-bit and TF-bit in the EFLAGS image btrw $9, 16(%ebp) # clear the IF-image btrw $8, 16(%ebp) # clear the TF-image # use real-mode interrupt-vector as the 'return' address movzxb 1(%ebx), %edx # vector-number in EDX mov 0(, %edx, 4), %ax # fetch vector loword mov %ax, 8(%ebp) # store as IP-image mov 2(, %edx, 4), %ax # fetch vector hiword mov %ax, 12(%ebp) # store as CS-image # ok, we're ready to resume the ring3 task jmp em_done emulate_iret: # compute address of ring3 stacktop in register ESI movzxw 20(%ebp), %esi # fetch the SP-image movzxw 24(%ebp), %eax # fetch the SS-image shl $4, %eax # sixteen times SS add %eax, %esi # is added to SP # copy IP, CS, FLAGS from ring3 stack to ring0 stack mov 0(%esi), %ax # fetch IP-image mov %ax, 8(%ebp) # store IP-image mov 2(%esi), %ax # fetch CS-image mov %ax, 12(%ebp) # store CS-image mov 4(%esi), %ax # fetch FLAGS-image mov %ax, 16(%ebp) # store FLAGS-image # increment the SP-image to discard those three words addw $6, 20(%ebp) # discard three words # ok, we're ready to resume the ring3 task jmp em_done #------------------------------------------------------------------ leave_protected_mode: .code16 mov $sel_ds, %ax mov %ax, %ds mov %ax, %es mov %ax, %fs mov %ax, %gs mov %cr0, %eax btr $0, %eax mov %eax, %cr0 ljmp $0x1000, $rm rm: mov %cs, %ax mov %ax, %ss lidt %cs:regIVT sti ret #------------------------------------------------------------------ .align 16 .space 256 tos3: .space 256 tos0: #------------------------------------------------------------------ .end