123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- /*
- * 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.
- */
- GRUB_EXPORT_START
- GRUB_EXPORT(grub_reboot)
- GRUB_EXPORT_END
- .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 */
- .byte 0x67, 0x66, 0xf, 0x1, 0x15
- .long 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 */
- .byte 0x66, 0xea
- .long protcseg
- .short GRUB_MEMORY_MACHINE_PROT_MODE_CSEG
- .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 */
- .byte 0x66, 0xea
- .long realcseg
- .short 0
- 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 */
- .byte 0x66, 0xc3
- .code32
- /*
- * grub_reboot()
- *
- * Reboot the system. At the moment, rely on BIOS.
- */
- FUNCTION(grub_reboot)
- call prot_to_real
- .code16
- cold_reboot:
- /* set 0x472 to 0x0000 for cold boot (0x1234 for warm boot) */
- movw $0x0472, %di
- movw %ax, (%di)
- ljmp $0xf000, $0xfff0
- .code32
|