summaryrefslogtreecommitdiffstats
path: root/grub-core/kern/i386/realmode.S
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/kern/i386/realmode.S')
-rw-r--r--grub-core/kern/i386/realmode.S281
1 files changed, 281 insertions, 0 deletions
diff --git a/grub-core/kern/i386/realmode.S b/grub-core/kern/i386/realmode.S
new file mode 100644
index 0000000..265cdcb
--- /dev/null
+++ b/grub-core/kern/i386/realmode.S
@@ -0,0 +1,281 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/machine/memory.h>
+
+/*
+ * Note: These functions defined in this file may be called from C.
+ * Be careful of that you must not modify some registers. Quote
+ * from gcc-2.95.2/gcc/config/i386/i386.h:
+
+ 1 for registers not available across function calls.
+ These must include the FIXED_REGISTERS and also any
+ registers that can be used without being saved.
+ The latter must include the registers where values are returned
+ and the register where structure-value addresses are passed.
+ Aside from that, you can include as many other registers as you like.
+
+ ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
+{ 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
+ */
+
+/*
+ * Note: GRUB is compiled with the options -mrtd and -mregparm=3.
+ * So the first three arguments are passed in %eax, %edx, and %ecx,
+ * respectively, and if a function has a fixed number of arguments
+ * and the number if greater than three, the function must return
+ * with "ret $N" where N is ((the number of arguments) - 3) * 4.
+ */
+
+/*
+ * This is the area for all of the special variables.
+ */
+
+protstack:
+ .long GRUB_MEMORY_MACHINE_PROT_STACK
+
+ .macro PROT_TO_REAL
+ call prot_to_real
+ .endm
+
+ .macro REAL_TO_PROT
+ calll real_to_prot
+ .endm
+
+/*
+ * This is the Global Descriptor Table
+ *
+ * An entry, a "Segment Descriptor", looks like this:
+ *
+ * 31 24 19 16 7 0
+ * ------------------------------------------------------------
+ * | | |B| |A| | | |1|0|E|W|A| |
+ * | BASE 31..24 |G|/|L|V| LIMIT |P|DPL| TYPE | BASE 23:16 | 4
+ * | | |D| |L| 19..16| | |1|1|C|R|A| |
+ * ------------------------------------------------------------
+ * | | |
+ * | BASE 15..0 | LIMIT 15..0 | 0
+ * | | |
+ * ------------------------------------------------------------
+ *
+ * Note the ordering of the data items is reversed from the above
+ * description.
+ */
+
+ .p2align 5 /* force 32-byte alignment */
+gdt:
+ .word 0, 0
+ .byte 0, 0, 0, 0
+
+ /* -- code segment --
+ * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present
+ * type = 32bit code execute/read, DPL = 0
+ */
+ .word 0xFFFF, 0
+ .byte 0, 0x9A, 0xCF, 0
+
+ /* -- data segment --
+ * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present
+ * type = 32 bit data read/write, DPL = 0
+ */
+ .word 0xFFFF, 0
+ .byte 0, 0x92, 0xCF, 0
+
+ /* -- 16 bit real mode CS --
+ * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present
+ * type = 16 bit code execute/read only/conforming, DPL = 0
+ */
+ .word 0xFFFF, 0
+ .byte 0, 0x9E, 0, 0
+
+ /* -- 16 bit real mode DS --
+ * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present
+ * type = 16 bit data read/write, DPL = 0
+ */
+ .word 0xFFFF, 0
+ .byte 0, 0x92, 0, 0
+
+
+ .p2align 5
+/* this is the GDT descriptor */
+gdtdesc:
+ .word 0x27 /* limit */
+ .long gdt /* addr */
+LOCAL(realidt):
+ .word 0x400
+ .long 0
+protidt:
+ .word 0
+ .long 0
+
+/*
+ * These next two routines, "real_to_prot" and "prot_to_real" are structured
+ * in a very specific way. Be very careful when changing them.
+ *
+ * NOTE: Use of either one messes up %eax and %ebp.
+ */
+
+real_to_prot:
+ .code16
+ cli
+
+ /* load the GDT register */
+ xorw %ax, %ax
+ movw %ax, %ds
+#ifdef GRUB_MACHINE_QEMU
+ /*
+ qemu is special: gdtdesc is in ROM.
+ %cs = 0xf000
+ _start + GRUB_BOOT_MACHINE_SIZE = 0x100000
+ So
+ _start + GRUB_BOOT_MACHINE_SIZE - 0x10000 points to the same point
+ as %cs.
+ gdtdesc - (_start + GRUB_BOOT_MACHINE_SIZE - 0x10000)
+ = gdtdesc - _start - GRUB_BOOT_MACHINE_SIZE + 0x10000
+ but the later can be computed by assembly.
+ */
+ lgdtl %cs:(gdtdesc - _start - GRUB_BOOT_MACHINE_SIZE + 0x10000)
+#else
+ lgdtl gdtdesc
+#endif
+
+ /* turn on protected mode */
+ movl %cr0, %eax
+ orl $GRUB_MEMORY_CPU_CR0_PE_ON, %eax
+ movl %eax, %cr0
+
+ /* jump to relocation, flush prefetch queue, and reload %cs */
+ ljmpl $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $protcseg
+
+ .code32
+protcseg:
+ /* reload other segment registers */
+ movw $GRUB_MEMORY_MACHINE_PROT_MODE_DSEG, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+
+ /* put the return address in a known safe location */
+ movl (%esp), %eax
+ movl %eax, GRUB_MEMORY_MACHINE_REAL_STACK
+
+ /* get protected mode stack */
+ movl protstack, %eax
+ movl %eax, %esp
+ movl %eax, %ebp
+
+ /* get return address onto the right stack */
+ movl GRUB_MEMORY_MACHINE_REAL_STACK, %eax
+ movl %eax, (%esp)
+
+ /* zero %eax */
+ xorl %eax, %eax
+
+ sidt LOCAL(realidt)
+ lidt protidt
+
+ /* return on the old (or initialized) stack! */
+ ret
+ /* prot_to_real assumes that this code is under 64K which is not
+ true for qemu. */
+#ifndef GRUB_MACHINE_QEMU
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+prot_to_real:
+ /* just in case, set GDT */
+ lgdt gdtdesc
+
+ sidt protidt
+ lidt LOCAL(realidt)
+
+ /* save the protected mode stack */
+ movl %esp, %eax
+ movl %eax, protstack
+
+ /* get the return address */
+ movl (%esp), %eax
+ movl %eax, GRUB_MEMORY_MACHINE_REAL_STACK
+
+ /* set up new stack */
+ movl $GRUB_MEMORY_MACHINE_REAL_STACK, %eax
+ movl %eax, %esp
+ movl %eax, %ebp
+
+ /* set up segment limits */
+ movw $GRUB_MEMORY_MACHINE_PSEUDO_REAL_DSEG, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+
+ /* this might be an extra step */
+ /* jump to a 16 bit segment */
+ ljmp $GRUB_MEMORY_MACHINE_PSEUDO_REAL_CSEG, $tmpcseg
+
+tmpcseg:
+ .code16
+
+ /* clear the PE bit of CR0 */
+ movl %cr0, %eax
+ andl $(~GRUB_MEMORY_CPU_CR0_PE_ON), %eax
+ movl %eax, %cr0
+
+ /* flush prefetch queue, reload %cs */
+ ljmpl $0, $realcseg
+
+realcseg:
+ /* we are in real mode now
+ * set up the real mode segment registers : DS, SS, ES
+ */
+ /* zero %eax */
+ xorl %eax, %eax
+
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+
+#ifdef GRUB_MACHINE_PCBIOS
+ /* restore interrupts */
+ sti
+#endif
+
+ /* return on new stack! */
+ retl
+#endif
+ .code32