16 bit real mode to protected mode and back, a neat trick!
Posted: Sat Feb 25, 2012 12:59 am
Firstly, let me say how much I love osdev.org, you just can't find this sort of information anywhere else.
I've been playing with a tiny (64k) 32 bit kernel implemented with FASM, and I thought I'd share a neat trick I've developed. Firstly, the code, data and stack are stored in an identity mapped segment in low memory. This is how I enter and exit protected mode:
Because the kernel resides in a single 64k segment, the high word of EIP and ESP is always zero, so this works. To transition to real mode and change the video mode I'd do something like:
(note: I extend the syntax of PUSH/POP to support multiple args using a macro)
I think it's rather neat to make the mode switch behave like an instruction
I've been playing with a tiny (64k) 32 bit kernel implemented with FASM, and I thought I'd share a neat trick I've developed. Firstly, the code, data and stack are stored in an identity mapped segment in low memory. This is how I enter and exit protected mode:
Code: Select all
;---------------------------------------------------------------------------------------------------
; macro: to32
; "switch to 32 bit (protected) mode"
; notes:
; processor must be in 16 bit (real) mode
;---------------------------------------------------------------------------------------------------
macro to32 {
call core16_to32
use32
}
;---------------------------------------------------------------------------------------------------
; macro: to16
; "switch to 16 bit (real) mode"
; notes:
; processor must be in 32 bit (protected) mode
;---------------------------------------------------------------------------------------------------
macro to16 {
call core32_to16
use16
}
;---------------------------------------------------------------------------------------------------
; core16_to32()
; "transition to 32 bit (protected) mode"
; returns:
; cs = core code segment (protected)
; ds, es, ss, fs, gs = core data segment (protected)
; assumes:
; cpu is in 16 bit (real) mode
; ds -> core data segment
; interrupts are disabled
;---------------------------------------------------------------------------------------------------
use16
core16_to32:
; extend return address to 32 bits
sub esp, 2
; save
push eax
; mov high word of return address to low word
xor eax, eax
mov ax, [esp + 6]
mov [esp + 4], eax
; load the page directory
mov eax, [core.cr3]
mov cr3, eax
; load the global descriptor table
lgdt [core.gdtr]
; load the interrupt descriptor table
lidt [core.idtr]
; enter protected mode (enable paging and protection)
mov eax, cr0
or eax, 0x80000001
mov cr0, eax
; transfer to 32 bit segment
jmp core.CS32:.step0
use32
.step0:
; load segment registers
mov ax, core.DS32
mov ss, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; restore and return
pop eax
ret
;---------------------------------------------------------------------------------------------------
; core32_to16()
; "transition to 16 bit (real) mode"
; returns:
; cs, ds, es, fs, gs, ss = core segment (real)
; assumes:
; cpu is in 32 bit (protected) mode
; ds -> core data segment
; interrupts are disabled
;---------------------------------------------------------------------------------------------------
use32
core32_to16:
; save eax
push eax
; switch to 16 bit execution
jmp core.CS16:.step0
use16
.step0:
; load 16 bit segment limits
mov ax, core.DS16
mov ss, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; clear protection and paging bits
mov eax, cr0
and eax, 0xffffffff xor 0x80000001
mov cr0, eax
; reload cs
push word [core.image_real_seg]
push word .step1
retf
.step1:
; load real mode segment values
mov ax, cs
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; restore the real mode IDT
lidt [core.real_idtr]
; restore eax
pop eax
; return, remove high word of return address
ret 2
Code: Select all
; in 32 bit mode here ...
push ds, es, fs, gs
pushf
; function = 0x00 "switch mode"
mov ah, 0x00
mov al, VIDEO_MODE
; call BIOS int 0x10
cli
to16
int 0x10
to32
; restore interrupt flag and segments
popf
pop ds, es, fs, gs
I think it's rather neat to make the mode switch behave like an instruction