grub/grub-core/lib/i386/relocator.c

283 lines
7.9 KiB
C
Raw Normal View History

2009-11-25 22:39:59 +00:00
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 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/mm.h>
#include <grub/misc.h>
#include <grub/types.h>
#include <grub/err.h>
2010-01-10 14:06:17 +00:00
#include <grub/term.h>
2009-11-25 22:39:59 +00:00
#include <grub/i386/relocator.h>
2010-01-09 23:30:33 +00:00
#include <grub/relocator_private.h>
#include <grub/i386/relocator_private.h>
2010-01-09 23:30:33 +00:00
extern grub_uint8_t grub_relocator_forward_start;
extern grub_uint8_t grub_relocator_forward_end;
extern grub_uint8_t grub_relocator_backward_start;
extern grub_uint8_t grub_relocator_backward_end;
extern void *grub_relocator_backward_dest;
extern void *grub_relocator_backward_src;
2010-01-11 12:40:59 +00:00
extern grub_size_t grub_relocator_backward_chunk_size;
2010-01-09 23:30:33 +00:00
extern void *grub_relocator_forward_dest;
extern void *grub_relocator_forward_src;
2010-01-11 12:40:59 +00:00
extern grub_size_t grub_relocator_forward_chunk_size;
2010-01-09 23:30:33 +00:00
2010-01-12 21:15:50 +00:00
extern grub_uint8_t grub_relocator16_start;
extern grub_uint8_t grub_relocator16_end;
extern grub_uint16_t grub_relocator16_cs;
extern grub_uint16_t grub_relocator16_ip;
extern grub_uint16_t grub_relocator16_ds;
extern grub_uint16_t grub_relocator16_es;
extern grub_uint16_t grub_relocator16_fs;
extern grub_uint16_t grub_relocator16_gs;
extern grub_uint16_t grub_relocator16_ss;
extern grub_uint16_t grub_relocator16_sp;
extern grub_uint32_t grub_relocator16_edx;
2010-12-25 23:38:20 +00:00
extern grub_uint32_t grub_relocator16_ebx;
extern grub_uint32_t grub_relocator16_esi;
extern grub_uint16_t grub_relocator16_keep_a20_enabled;
2010-01-12 21:15:50 +00:00
2010-01-12 16:48:51 +00:00
extern grub_uint8_t grub_relocator32_start;
extern grub_uint8_t grub_relocator32_end;
2010-01-09 23:30:33 +00:00
extern grub_uint32_t grub_relocator32_eax;
extern grub_uint32_t grub_relocator32_ebx;
extern grub_uint32_t grub_relocator32_ecx;
extern grub_uint32_t grub_relocator32_edx;
extern grub_uint32_t grub_relocator32_eip;
extern grub_uint32_t grub_relocator32_esp;
extern grub_uint32_t grub_relocator32_ebp;
2010-01-11 13:59:01 +00:00
extern grub_uint32_t grub_relocator32_esi;
extern grub_uint32_t grub_relocator32_edi;
2010-01-09 23:30:33 +00:00
2010-01-12 16:48:51 +00:00
extern grub_uint8_t grub_relocator64_start;
extern grub_uint8_t grub_relocator64_end;
extern grub_uint64_t grub_relocator64_rax;
extern grub_uint64_t grub_relocator64_rbx;
extern grub_uint64_t grub_relocator64_rcx;
extern grub_uint64_t grub_relocator64_rdx;
extern grub_uint64_t grub_relocator64_rip;
extern grub_uint64_t grub_relocator64_rip_addr;
extern grub_uint64_t grub_relocator64_rsp;
extern grub_uint64_t grub_relocator64_rsi;
extern grub_addr_t grub_relocator64_cr3;
2010-01-09 23:30:33 +00:00
#define RELOCATOR_SIZEOF(x) (&grub_relocator##x##_end - &grub_relocator##x##_start)
grub_size_t grub_relocator_align = 1;
grub_size_t grub_relocator_forward_size;
grub_size_t grub_relocator_backward_size;
2010-01-12 22:30:52 +00:00
#ifdef __x86_64__
grub_size_t grub_relocator_jumper_size = 12;
#else
grub_size_t grub_relocator_jumper_size = 7;
#endif
2010-01-09 23:30:33 +00:00
void
grub_cpu_relocator_init (void)
{
grub_relocator_forward_size = RELOCATOR_SIZEOF(_forward);
grub_relocator_backward_size = RELOCATOR_SIZEOF(_backward);
}
2009-11-25 22:39:59 +00:00
2010-01-09 23:30:33 +00:00
void
grub_cpu_relocator_jumper (void *rels, grub_addr_t addr)
2009-11-25 22:39:59 +00:00
{
2010-01-09 23:30:33 +00:00
grub_uint8_t *ptr;
ptr = rels;
2010-01-12 22:30:52 +00:00
#ifdef __x86_64__
/* movq imm64, %rax (for relocator) */
*(grub_uint8_t *) ptr = 0x48;
ptr++;
*(grub_uint8_t *) ptr = 0xb8;
ptr++;
*(grub_uint64_t *) ptr = addr;
ptr += sizeof (grub_uint64_t);
#else
/* movl imm32, %eax (for relocator) */
2010-01-10 14:06:17 +00:00
*(grub_uint8_t *) ptr = 0xb8;
ptr++;
*(grub_uint32_t *) ptr = addr;
2010-01-12 22:30:52 +00:00
ptr += sizeof (grub_uint32_t);
#endif
/* jmp $eax/$rax */
*(grub_uint8_t *) ptr = 0xff;
ptr++;
*(grub_uint8_t *) ptr = 0xe0;
2010-01-09 23:30:33 +00:00
ptr++;
}
2009-11-25 22:39:59 +00:00
2010-01-09 23:30:33 +00:00
void
grub_cpu_relocator_backward (void *ptr, void *src, void *dest,
grub_size_t size)
{
grub_relocator_backward_dest = dest;
grub_relocator_backward_src = src;
2010-01-11 12:40:59 +00:00
grub_relocator_backward_chunk_size = size;
2009-11-25 22:39:59 +00:00
grub_memmove (ptr,
2010-01-09 23:30:33 +00:00
&grub_relocator_backward_start,
RELOCATOR_SIZEOF (_backward));
2009-11-25 22:39:59 +00:00
}
2010-01-09 23:30:33 +00:00
void
grub_cpu_relocator_forward (void *ptr, void *src, void *dest,
grub_size_t size)
2009-11-25 22:39:59 +00:00
{
2010-01-09 23:30:33 +00:00
grub_relocator_forward_dest = dest;
grub_relocator_forward_src = src;
2010-01-11 12:40:59 +00:00
grub_relocator_forward_chunk_size = size;
2009-12-23 Felix Zielcke <fzielcke@z-51.de> * commands/i386/pc/drivemap.c: Remove all trailing whitespace. * commands/lspci.c: Likewise. * commands/probe.c: Likewise. * commands/xnu_uuid.c: Likewise. * conf/i386-coreboot.rmk: Likewise. * conf/i386-efi.rmk: Likewise. * conf/i386-ieee1275.rmk: Likewise. * conf/i386-pc.rmk: Likewise. * conf/powerpc-ieee1275.rmk: Likewise. * conf/sparc64-ieee1275.rmk: Likewise. * conf/x86_64-efi.rmk: Likewise. * fs/i386/pc/pxe.c: Likewise. * gettext/gettext.c: Likewise. * include/grub/efi/graphics_output.h: Likewise. * include/grub/i386/pc/memory.h: Likewise. * kern/env.c: Likewise. * kern/i386/qemu/startup.S: Likewise. * lib/i386/pc/biosnum.c: Likewise. * lib/i386/relocator.c: Likewise. * lib/i386/relocator_asm.S: Likewise. * lib/relocator.c: Likewise. * loader/i386/bsd.c: Likewise. * loader/i386/multiboot.c: Likewise. * loader/i386/pc/chainloader.c: Likewise. * loader/i386/xnu.c: Likewise. * loader/xnu.c: Likewise. * normal/main.c: Likewise. * normal/menu_text.c: Likewise. * util/getroot.c: Likewise. * util/grub-mkconfig_lib.in: Likewise. * util/grub.d/00_header.in: Likewise. * util/i386/pc/grub-mkimage.c: Likewise. * util/mkisofs/eltorito.c: Likewise. * util/mkisofs/exclude.h: Likewise. * util/mkisofs/hash.c: Likewise. * util/mkisofs/iso9660.h: Likewise. * util/mkisofs/joliet.c: Likewise. * util/mkisofs/mkisofs.c: Likewise. * util/mkisofs/mkisofs.h: Likewise. * util/mkisofs/multi.c: Likewise. * util/mkisofs/name.c: Likewise. * util/mkisofs/rock.c: Likewise. * util/mkisofs/tree.c: Likewise. * util/mkisofs/write.c: Likewise. * video/efi_gop.c: Likewise.
2009-12-23 16:41:32 +00:00
2009-11-25 22:39:59 +00:00
grub_memmove (ptr,
2010-01-09 23:30:33 +00:00
&grub_relocator_forward_start,
RELOCATOR_SIZEOF (_forward));
2009-11-25 22:39:59 +00:00
}
2010-01-09 23:30:33 +00:00
grub_err_t
grub_relocator32_boot (struct grub_relocator *rel,
struct grub_relocator32_state state)
{
grub_err_t err;
void *relst;
2010-04-21 17:13:45 +00:00
grub_relocator_chunk_t ch;
2010-01-10 14:06:17 +00:00
2010-04-21 17:13:45 +00:00
err = grub_relocator_alloc_chunk_align (rel, &ch, 0,
2010-01-09 23:30:33 +00:00
(0xffffffff - RELOCATOR_SIZEOF (32))
+ 1, RELOCATOR_SIZEOF (32), 16,
GRUB_RELOCATOR_PREFERENCE_NONE);
2010-01-09 23:30:33 +00:00
if (err)
return err;
grub_relocator32_eax = state.eax;
grub_relocator32_ebx = state.ebx;
grub_relocator32_ecx = state.ecx;
grub_relocator32_edx = state.edx;
grub_relocator32_eip = state.eip;
grub_relocator32_esp = state.esp;
grub_relocator32_ebp = state.ebp;
2010-01-11 13:59:01 +00:00
grub_relocator32_esi = state.esi;
grub_relocator32_edi = state.edi;
2010-01-09 23:30:33 +00:00
2010-04-21 17:13:45 +00:00
grub_memmove (get_virtual_current_address (ch), &grub_relocator32_start,
RELOCATOR_SIZEOF (32));
2010-01-09 23:30:33 +00:00
2010-04-21 17:13:45 +00:00
err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch),
&relst, NULL);
2010-01-09 23:30:33 +00:00
if (err)
return err;
2010-01-10 14:06:17 +00:00
2010-01-09 23:30:33 +00:00
asm volatile ("cli");
((void (*) (void)) relst) ();
2010-01-12 21:15:50 +00:00
/* Not reached. */
return GRUB_ERR_NONE;
}
grub_err_t
grub_relocator16_boot (struct grub_relocator *rel,
struct grub_relocator16_state state)
{
grub_err_t err;
void *relst;
2010-04-21 17:13:45 +00:00
grub_relocator_chunk_t ch;
2010-01-12 21:15:50 +00:00
/* Put it higher than the byte it checks for A20 check. */
err = grub_relocator_alloc_chunk_align (rel, &ch, 0x8010,
0xa0000 - RELOCATOR_SIZEOF (16)
- GRUB_RELOCATOR16_STACK_SIZE,
RELOCATOR_SIZEOF (16)
+ GRUB_RELOCATOR16_STACK_SIZE, 16,
2010-01-12 21:15:50 +00:00
GRUB_RELOCATOR_PREFERENCE_NONE);
if (err)
return err;
grub_relocator16_cs = state.cs;
grub_relocator16_ip = state.ip;
grub_relocator16_ds = state.ds;
grub_relocator16_es = state.es;
grub_relocator16_fs = state.fs;
grub_relocator16_gs = state.gs;
grub_relocator16_ss = state.ss;
grub_relocator16_sp = state.sp;
2010-12-25 23:38:20 +00:00
grub_relocator16_ebx = state.ebx;
grub_relocator16_edx = state.edx;
grub_relocator16_esi = state.esi;
grub_relocator16_keep_a20_enabled = state.a20;
2010-04-21 17:13:45 +00:00
grub_memmove (get_virtual_current_address (ch), &grub_relocator16_start,
RELOCATOR_SIZEOF (16));
2010-01-12 21:15:50 +00:00
2010-04-21 17:13:45 +00:00
err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch),
&relst, NULL);
2010-01-12 21:15:50 +00:00
if (err)
return err;
asm volatile ("cli");
((void (*) (void)) relst) ();
2010-01-09 23:30:33 +00:00
/* Not reached. */
return GRUB_ERR_NONE;
}
2010-01-12 16:48:51 +00:00
grub_err_t
grub_relocator64_boot (struct grub_relocator *rel,
struct grub_relocator64_state state,
grub_addr_t min_addr, grub_addr_t max_addr)
{
grub_err_t err;
void *relst;
2010-04-21 17:13:45 +00:00
grub_relocator_chunk_t ch;
2010-01-12 16:48:51 +00:00
2010-04-21 17:13:45 +00:00
err = grub_relocator_alloc_chunk_align (rel, &ch, min_addr,
2010-01-12 16:48:51 +00:00
max_addr - RELOCATOR_SIZEOF (64),
RELOCATOR_SIZEOF (64), 16,
GRUB_RELOCATOR_PREFERENCE_NONE);
2010-01-12 16:48:51 +00:00
if (err)
return err;
grub_relocator64_rax = state.rax;
grub_relocator64_rbx = state.rbx;
grub_relocator64_rcx = state.rcx;
grub_relocator64_rdx = state.rdx;
grub_relocator64_rip = state.rip;
grub_relocator64_rsp = state.rsp;
grub_relocator64_rsi = state.rsi;
grub_relocator64_cr3 = state.cr3;
2010-04-21 17:13:45 +00:00
grub_memmove (get_virtual_current_address (ch), &grub_relocator64_start,
RELOCATOR_SIZEOF (64));
2010-01-12 16:48:51 +00:00
2010-04-21 17:13:45 +00:00
err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch),
&relst, NULL);
2010-01-12 16:48:51 +00:00
if (err)
return err;
asm volatile ("cli");
((void (*) (void)) relst) ();
/* Not reached. */
return GRUB_ERR_NONE;
}