diff options
Diffstat (limited to 'lib_i386/realmode_switch.S')
-rw-r--r-- | lib_i386/realmode_switch.S | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/lib_i386/realmode_switch.S b/lib_i386/realmode_switch.S new file mode 100644 index 0000000..9f212c2 --- /dev/null +++ b/lib_i386/realmode_switch.S @@ -0,0 +1,223 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, daniel@omicron.se + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + + +/* 32bit -> 16bit -> 32bit mode switch code */ + +/* + * Stack frame at 0xe00 + * e00 ebx; + * e04 ecx; + * e08 edx; + * e0c esi; + * e10 edi; + * e14 ebp; + * e18 eax; + * e1c ds; + * e20 es; + * e24 fs; + * e28 gs; + * e2c orig_eax; + * e30 eip; + * e34 cs; + * e38 eflags; + * e3c esp; + * e40 ss; + */ + +#define a32 .byte 0x67; /* address size prefix 32 */ +#define o32 .byte 0x66; /* operand size prefix 32 */ + +.section .realmode, "ax" +.code16 + + /* 16bit protected mode code here */ +.globl realmode_enter +realmode_enter: +o32 pusha +o32 pushf + cli + sidt saved_idt + sgdt saved_gdt + movl %esp, %eax + movl %eax, saved_protected_mode_esp + + movl $0x10, %eax + movl %eax, %esp + movw $0x28, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + lidt realmode_idt_ptr + movl %cr0, %eax /* Go back into real mode by */ + andl $0x7ffffffe, %eax /* clearing PE to 0 */ + movl %eax, %cr0 + ljmp $0x0,$do_realmode /* switch to real mode */ + +do_realmode: /* realmode code from here */ + movw %cs,%ax + movw %ax,%ds + movw %ax,%es + movw %ax,%fs + movw %ax,%gs + + /* create a temporary stack */ + + movw $0xc0, %ax + movw %ax, %ss + movw $0x200, %ax + movw %ax, %sp + + popl %ebx + popl %ecx + popl %edx + popl %esi + popl %edi + popl %ebp + popl %eax + movl %eax, temp_eax + popl %eax + movw %ax, %ds + popl %eax + movw %ax, %es + popl %eax + movw %ax, %fs + popl %eax + movw %ax, %gs + popl %eax /* orig_eax */ + popl %eax +cs movw %ax, temp_ip + popl %eax +cs movw %ax, temp_cs +o32 popf + popl %eax + popw %ss + movl %eax, %esp +cs movl temp_eax, %eax + wbinvd /* self-modifying code, + * better flush the cache */ + + .byte 0x9a /* lcall */ +temp_ip: + .word 0 /* new ip */ +temp_cs: + .word 0 /* new cs */ +realmode_ret: + /* save eax, esp and ss */ +cs movl %eax, saved_eax + movl %esp, %eax +cs movl %eax, saved_esp + movw %ss, %ax +cs movw %ax, saved_ss + + /* restore the stack, note that we set sp to 0x244; + * pt_regs is 0x44 bytes long and we push the structure + * backwards on to the stack, bottom first */ + + movw $0xc0, %ax + movw %ax, %ss + movw $0x244, %ax + movw %ax, %sp + + xorl %eax,%eax +cs movw saved_ss, %ax + pushl %eax +cs movl saved_esp, %eax + pushl %eax +o32 pushf + xorl %eax,%eax +cs movw temp_cs, %ax + pushl %eax +cs movw temp_ip, %ax + pushl %eax + pushl $0 + movw %gs, %ax + pushl %eax + movw %fs, %ax + pushl %eax + movw %es, %ax + pushl %eax + movw %ds, %ax + pushl %eax + movl saved_eax, %eax + pushl %eax + pushl %ebp + pushl %edi + pushl %esi + pushl %edx + pushl %ecx + pushl %ebx + +o32 cs lidt saved_idt +o32 cs lgdt saved_gdt /* Set GDTR */ + + movl %cr0, %eax /* Go back into protected mode */ + orl $1,%eax /* reset PE to 1 */ + movl %eax, %cr0 + jmp next_line /* flush prefetch queue */ +next_line: + movw $return_ptr, %ax + movw %ax,%bp +o32 cs ljmp *(%bp) + +.code32 +protected_mode: + movl $0x18,%eax /* reload GDT[3] */ + movw %ax,%fs /* reset FS */ + movw %ax,%ds /* reset DS */ + movw %ax,%gs /* reset GS */ + movw %ax,%es /* reset ES */ + movw %ax,%ss /* reset SS */ + movl saved_protected_mode_esp, %eax + movl %eax, %esp + popf + popa + ret + +temp_eax: + .long 0 + +saved_ss: + .word 0 +saved_esp: + .long 0 +saved_eax: + .long 0 + +realmode_idt_ptr: + .word 0x400 + .word 0x0, 0x0 + +saved_gdt: + .word 0, 0, 0, 0 +saved_idt: + .word 0, 0, 0, 0 + +saved_protected_mode_esp: + .long 0 + +return_ptr: + .long protected_mode + .word 0x10 |