//----------------------------------------------------------------- // conform.s // // This program demonstrates the use of a 'conforming' code- // segment, which permits an inter-segment procedure-call to // be accomplished without requiring a call-gate descriptor. // Thus a change in base-address and segment-limit can occur // without the overhead of a transition in privilege-levels. // // to assemble: $ as conform.s -o conform.o // and to link: $ ld conform.o -T ldscript -o conform.b // // NOTE: This code begins executing with CS:IP = 1000:1002. // // programmer: ALLAN CRUSE // date begun: 23 JUN 2008 // completion: 24 JUN 2008 //----------------------------------------------------------------- .section .text #------------------------------------------------------------------ .word 0xABCD # application's signature #------------------------------------------------------------------ main: .code16 # executes in '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 this segment mov %ax, %ds # with DS register mov %ax, %ss # also SS register lea tos0, %sp # and set new stacktop call enter_protected_mode call exec_conforming_code call leave_protected_mode call show_success_message lss %cs:exit_pointer, %sp # recover saved stacktop lret # exit to program loader #------------------------------------------------------------------ exit_pointer: .word 0, 0 # to store stack-address #------------------------------------------------------------------ theGDT: .word 0x0000, 0x0000, 0x0000, 0x0000 # null descriptor .equ selTSS, (.-theGDT)+0 # task-segment's selector .word 0x0005, theTSS, 0x8101, 0x0000 # task descriptor .equ privCS, (.-theGDT)+0 # code-segment's selector .word 0xFFFF, 0x0000, 0x9A01, 0x0000 # code descriptor .equ privSS, (.-theGDT)+0 # data-segment's selector .word 0xFFFF, 0x0000, 0x9201, 0x0000 # data descriptor .equ userCS, (.-theGDT)+3 # code-segment's selector .word 0xFFFF, 0x0000, 0xFA01, 0x0000 # code descriptor .equ userSS, (.-theGDT)+3 # data-segment's selector .word 0xFFFF, 0x0000, 0xF201, 0x0000 # data descriptor .equ userES, (.-theGDT)+3 # vram-segment's selector .word 0x7FFF, 0x8000, 0xF20B, 0x0000 # vram descriptor .equ confCS, (.-theGDT)+0 # code-segment's selector .word 0xFFFF, 0x0000, 0xFC01, 0x0000 # CODE descriptor .equ privFS, (.-theGDT)+0 # flat-segment's selector .word 0xFFFF, 0x0000, 0x9200, 0x008F # vram descriptor .equ gate16, (.-theGDT)+0 # 286 call-gate selector .word resume, privCS, 0xE400, 0x0000 # gate descriptor .equ limGDT, (.-theGDT)-1 # the GDT's segment-limit #------------------------------------------------------------------ theTSS: .word 0x0000, 0x0000, 0x0000 # 286 Task-State Segment #------------------------------------------------------------------ regGDT: .word limGDT, theGDT, 0x0001 # image for register GDTR #------------------------------------------------------------------ enter_protected_mode: cli mov %cr0, %eax bts $0, %eax mov %eax, %cr0 lgdt %cs:regGDT lidt %cs:regIDT ljmp $privCS, $pm pm: mov $privSS, %ax mov %ax, %ss mov %ax, %ds xor %ax, %ax mov %ax, %es mov %ax, %fs mov %ax, %gs ret #------------------------------------------------------------------ leave_protected_mode: mov %ss, %ax mov %ax, %ds mov %ax, %es mov $privFS, %ax 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 mov %ax, %ds lidt regIVT sti ret #------------------------------------------------------------------ msg1: .ascii " Return to ring-0 succeeded " len1: .word . - msg1 hue1: .byte 0x03 #------------------------------------------------------------------ show_success_message: mov $0xB800, %ax mov %ax, %es mov $640, %di mov %cs, %ax mov %ax, %ds lea msg1, %si cld mov hue1, %ah mov len1, %cx nxpel1: lodsb stosw loop nxpel1 ret #------------------------------------------------------------------ msg: .ascii " Hello from 'conforming' code-segment " len: .word . - msg # length of message string hue: .byte 0x5F # bright white on magenta #------------------------------------------------------------------ exec_conforming_code: # first we save our current top-of-stack address mov %sp, %ss:theTSS+2 # preserve SP register mov %ss, %ss:theTSS+4 # preserve SS register # next we establish our Task-State Segment mov $selTSS, %ax # load selector for TSS ltr %ax # into task register TR # next we setup ring-0 stack for a 'return' to ring-3 pushw $userSS # image for register SS pushw $tos3 # image for register SP pushw $userCS # image for register CS pushw $demo # image for register IP lret # switch to ring-3 demo demo: # Here is our 'direct' far call to 'conforming' code lcall $confCS, $write_message_string # <============ # Now transfer (via call-gate) back to ring-0 for exit lcall $gate16, $0 resume: lss %cs:theTSS+2, %sp ret #------------------------------------------------------------------ write_message_string: mov $userES, %ax # address video memory mov %ax, %es # with ES register mov $userSS, %ax # address code segment mov %ax, %ds # with DS register mov $480, %di # beginning on row 3 cld # do forward processing lea msg, %si # point to message text mov hue, %ah # setup color attribute mov len, %cx # load message's length nxpel: lodsb # fetch next character stosw # store char and color loop nxpel # back if chars remain # hlt # <-- uncomment to see the values in registers lret #------------------------------------------------------------------ #================================================================== #------------------------------------------------------------------ hex: .ascii "0123456789ABCDEF" #------------------------------------------------------------------ ax2hex: pushal mov $4, %cx nxnyb: rol $4, %ax mov %al, %bl and $0x0F, %bx mov hex(%bx), %dl mov %dl, (%di) inc %di loop nxnyb popal ret #------------------------------------------------------------------ #------------------------------------------------------------------ names: .ascii " GS= FS= ES= DS= DI= SI= BP= SP= BX= DX= CX= AX=" .ascii "err= IP= CS= FL= SP= SS=" outln: .ascii " nnn=xxxx " #------------------------------------------------------------------ isrGPF: pushaw pushw %ds pushw %es pushw %fs pushw %gs mov $userES, %ax mov %ax, %es mov $privSS, %ax mov %ax, %ds movzx %sp, %esp xor %ebx, %ebx nxelt: # setup next element-name mov names(,%ebx,4), %eax mov %eax, outln+1 # setup next element-value mov (%esp, %ebx, 2), %ax lea outln+5, %edi call ax2hex # draw element-information to screen mov $22, %eax sub %ebx, %eax imul $160, %eax, %edi add $128, %edi cld lea outln, %esi mov $0x70, %ah mov $10, %ecx nxch: lodsb stosw loop nxch inc %ebx cmp $18, %ebx jb nxelt lss %cs:theTSS+2, %sp ret #------------------------------------------------------------------ theIDT: .rept 13 .word 0x0000, 0x0000, 0x0000, 0x0000 .endr .word isrGPF, privCS, 0x8600, 0x0000 .rept 242 .word 0x0000, 0x0000, 0x0000, 0x0000 .endr .equ limIDT, (.-theIDT)-1 #------------------------------------------------------------------ regIDT: .word limIDT, theIDT, 0x0001 regIVT: .word 0x03FF, 0x0000, 0x0000 #------------------------------------------------------------------ .align 0x1000 .align 16 # assure stack alignment .space 512, '3' # reserved for stack use tos3: # label for ring-3 stack .space 512, '0' # reserved for stack use tos0: # label for ring-0 stack #------------------------------------------------------------------ .end # nothing more to assemble