//----------------------------------------------------------------- // pmhello.s // // This program provides an example of segment-register usage // when entering protected-mode and for exiting to real-mode. // // to assemble: $ as pmhello.s -o pmhello.o // and to link: $ ld pmhello.o -T ldscript -o pmhello.b // and install: $ dd if=pmhello.b of=/dev/sda4 seek=1 // // NOTE: This program begins executing with CS:IP = 1000:0002 // // programmer: ALLAN CRUSE // written on: 10 SEP 2008 //----------------------------------------------------------------- .section .text #------------------------------------------------------------------ .word 0xABCD # application 'signature' #------------------------------------------------------------------ main: .code16 # CPU starts in real-mode # preserve the caller's stack-address (for a return later) mov %sp, %cs:retptr+0 # save SP register-value mov %ss, %cs:retptr+2 # save SS register-value # setup SS and SP registers (to establish our own stack) mov %cs, %ax # address program area mov %ax, %ss # with SS register lea tos, %sp # set SS:SP to stacktop call enter_protected_mode call draw_pm_confirmation call leave_protected_mode call draw_rm_confirmation # restore original stack-address and return to our loader lss %cs:retptr, %sp # recover saved (SS:SP) lret # give control to caller #------------------------------------------------------------------ retptr: .word 0, 0 # space for saving SS:SP #------------------------------------------------------------------ theGDT: .word 0x0000, 0x0000, 0x0000, 0x0000 # null descriptor .equ sel_cs0, . - theGDT # selector for ring0 code .word 0xFFFF, 0x0000, 0x9A01, 0x0000 # code descriptor .equ sel_ds0, . - theGDT # selector for ring0 data .word 0xFFFF, 0x0000, 0x9201, 0x0000 # data descriptor .equ sel_es0, . - theGDT # selector for ring0 vram .word 0x7FFF, 0x8000, 0x920B, 0x0000 # vram descriptor .equ limGDT, (. - theGDT)-1 # our GDT's segment-limit #------------------------------------------------------------------ regGDT: .word limGDT, theGDT, 0x0001 # register-image for GDTR #------------------------------------------------------------------ #------------------------------------------------------------------ enter_protected_mode: # # Here we enter 'protected-mode' -- but with interrupts disabled # (since we have not setup infrastructure needed to handle them) # cli # interrupts not allowed mov %cr0, %eax # get machine status bts $0, %eax # set PE-bit's image mov %eax, %cr0 # turn on the PE-bit lgdt %cs:regGDT # load the GDTR register ljmp $sel_cs0, $pm # then jump to reload CS pm: mov $sel_ds0, %ax # address our stack area mov %ax, %ss # with SS register xor %ax, %ax # load 'null' selector mov %ax, %ds # into DS register mov %ax, %es # also ES register mov %ax, %fs # also FS register mov %ax, %gs # also GS register ret #------------------------------------------------------------------ leave_protected_mode: # # Here we return to 'real-mode' after insuring segment-registers # have segment-limits and access-rights suitable for 'real-mode' # mov $sel_ds0, %ax # 'writable' 64K data mov %ax, %ds # into DS cache mov %ax, %es # also ES cache mov %ax, %fs # also FS cache mov %ax, %gs # also GS cache mov %cr0, %eax # get machine's status btr $0, %eax # clear PE-bit's image mov %eax, %cr0 # turn off protection ljmp $0x1000, $rm # code-segment into CS rm: mov %cs, %ax # then copy CS segment mov %ax, %ss # also to register SS ret #------------------------------------------------------------------ msg1: .ascii " Hello from protected mode " # message's text len1: .short . - msg1 # size of message string att1: .byte 0x2E # colors: yellow on green row1: .word 2 # row-number for display #------------------------------------------------------------------ msg2: .ascii " OK, returned to real mode " # message's text len2: .short . - msg2 # size of message string att2: .byte 0x1F # colors: white on blue row2: .word 4 # row-number for display #------------------------------------------------------------------ #------------------------------------------------------------------ # Here is a subroutine that we can call from either CPU mode to # write a string of text-characters directly into video memory. # # EXPECTS: DS:SI = address of text string # ES:DI = address in video memory # CX = count of text characters # AH = color-attribute for text # dodraw: cld # do forward processing nxchr: lodsb # fetch next character stosw # store char and color loop nxchr # again if chars remain ret # go back to the caller #------------------------------------------------------------------ draw_pm_confirmation: mov $sel_ds0, %ax # address program data mov %ax, %ds # with DS register lea msg1, %si # point DS:SI to string mov $sel_es0, %ax # address video memory mov %ax, %es # with ES register imul $160, row1, %di # point ES:DI to screen mov att1, %ah # setup attribute in AH mov len1, %cx # string's length in CX call dodraw # do drawing subroutine ret #------------------------------------------------------------------ draw_rm_confirmation: mov $0x1000, %ax # address program data mov %ax, %ds # with DS register lea msg2, %si # point DS:SI to screen mov $0xB800, %ax # address video memory mov %ax, %es # with ES register imul $160, row2, %di # point ES:DI to screen mov att2, %ah # setup attribute in AH mov len2, %cx # string's length in CX call dodraw # do drawing subroutine ret #------------------------------------------------------------------ .align 16 # insure stack-alignmemt .space 512 # reserved for stack use tos: # label for top-of-stack #------------------------------------------------------------------ .end # nothing more to assemble