| /* |
| * 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/i386/pc/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 |
| DATA32 call 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 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 |
| |
| |
| .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 |
| 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 |
| |
| sidt LOCAL(realidt) |
| lidt protidt |
| |
| /* return on the old (or initialized) stack! */ |
| ret |
| /* |
| * 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/i386/pc/memory.h> |
| |
| 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 */ |
| 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 |