/* * 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 . */ #include /* * 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. */ .p2align 2 /* force 4-byte alignment */ protstack: .long GRUB_MEMORY_MACHINE_PROT_STACK /* * 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 2 /* force 4-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 /* this is the GDT descriptor */ gdtdesc: .word 0x27 /* limit */ .long gdt /* addr */ /* * 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 DATA32 ADDR32 lgdt gdtdesc /* 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 */ DATA32 ljmp $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 /* return on the old (or initialized) stack! */ ret prot_to_real: /* just in case, set GDT */ lgdt gdtdesc /* 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 */ DATA32 ljmp $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! */ DATA32 ret .code32