From 14933205d1109ea759949bfceced54553824a331 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Tue, 12 Jan 2010 17:48:51 +0100 Subject: [PATCH] Relocator64 support --- conf/i386.rmk | 1 + include/grub/i386/memory.h | 3 +- include/grub/i386/relocator.h | 16 +++ lib/i386/relocator.c | 54 ++++++++- lib/i386/relocator64.S | 206 ++++++++++++++++++++++++++++++++++ 5 files changed, 277 insertions(+), 3 deletions(-) create mode 100644 lib/i386/relocator64.S diff --git a/conf/i386.rmk b/conf/i386.rmk index 674170d01..72ea6d465 100644 --- a/conf/i386.rmk +++ b/conf/i386.rmk @@ -17,6 +17,7 @@ vga_text_mod_LDFLAGS = $(COMMON_LDFLAGS) pkglib_MODULES += relocator.mod relocator_mod_SOURCES = lib/relocator.c lib/i386/relocator32.S \ + lib/i386/relocator64.S \ lib/i386/relocator_asm.S lib/i386/relocator.c relocator_mod_CFLAGS = $(COMMON_CFLAGS) relocator_mod_ASFLAGS = $(COMMON_ASFLAGS) diff --git a/include/grub/i386/memory.h b/include/grub/i386/memory.h index 466947cc6..fe2f6e4e1 100644 --- a/include/grub/i386/memory.h +++ b/include/grub/i386/memory.h @@ -22,7 +22,8 @@ /* The flag for protected mode. */ #define GRUB_MEMORY_CPU_CR0_PE_ON 0x1 -#define GRUB_MEMORY_CPU_CR4_PAE_ON 0x00000040 +#define GRUB_MEMORY_CPU_CR4_PAE_ON 0x00000020 +#define GRUB_MEMORY_CPU_CR4_PSE_ON 0x00000010 #define GRUB_MEMORY_CPU_CR0_PAGING_ON 0x80000000 #define GRUB_MEMORY_CPU_AMD64_MSR 0xc0000080 #define GRUB_MEMORY_CPU_AMD64_MSR_ON 0x00000100 diff --git a/include/grub/i386/relocator.h b/include/grub/i386/relocator.h index e4c9e7ca7..ac49dd29e 100644 --- a/include/grub/i386/relocator.h +++ b/include/grub/i386/relocator.h @@ -34,7 +34,23 @@ struct grub_relocator32_state grub_uint32_t esi; }; +struct grub_relocator64_state +{ + grub_uint64_t rsp; + grub_uint64_t rax; + grub_uint64_t rbx; + grub_uint64_t rcx; + grub_uint64_t rdx; + grub_uint64_t rip; + grub_uint64_t rsi; + grub_addr_t cr3; +}; + grub_err_t grub_relocator32_boot (struct grub_relocator *rel, struct grub_relocator32_state state); +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); + #endif /* ! GRUB_RELOCATOR_CPU_HEADER */ diff --git a/lib/i386/relocator.c b/lib/i386/relocator.c index 11e673cf7..e81dd8e1e 100644 --- a/lib/i386/relocator.c +++ b/lib/i386/relocator.c @@ -26,8 +26,6 @@ #include #include -extern grub_uint8_t grub_relocator32_start; -extern grub_uint8_t grub_relocator32_end; extern grub_uint8_t grub_relocator_forward_start; extern grub_uint8_t grub_relocator_forward_end; extern grub_uint8_t grub_relocator_backward_start; @@ -41,6 +39,8 @@ extern void *grub_relocator_forward_dest; extern void *grub_relocator_forward_src; extern grub_size_t grub_relocator_forward_chunk_size; +extern grub_uint8_t grub_relocator32_start; +extern grub_uint8_t grub_relocator32_end; extern grub_uint32_t grub_relocator32_eax; extern grub_uint32_t grub_relocator32_ebx; extern grub_uint32_t grub_relocator32_ecx; @@ -49,6 +49,18 @@ extern grub_uint32_t grub_relocator32_eip; extern grub_uint32_t grub_relocator32_esp; extern grub_uint32_t grub_relocator32_esi; +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; + #define RELOCATOR_SIZEOF(x) (&grub_relocator##x##_end - &grub_relocator##x##_start) grub_size_t grub_relocator_align = 1; @@ -141,3 +153,41 @@ grub_relocator32_boot (struct grub_relocator *rel, /* Not reached. */ return GRUB_ERR_NONE; } + +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_addr_t target; + void *src; + grub_err_t err; + grub_addr_t relst; + + err = grub_relocator_alloc_chunk_align (rel, &src, &target, min_addr, + max_addr - RELOCATOR_SIZEOF (64), + RELOCATOR_SIZEOF (64), 16); + 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; + + grub_memmove (src, &grub_relocator64_start, RELOCATOR_SIZEOF (64)); + + err = grub_relocator_prepare_relocs (rel, target, &relst); + if (err) + return err; + + asm volatile ("cli"); + ((void (*) (void)) relst) (); + + /* Not reached. */ + return GRUB_ERR_NONE; +} diff --git a/lib/i386/relocator64.S b/lib/i386/relocator64.S new file mode 100644 index 000000000..42f61e32e --- /dev/null +++ b/lib/i386/relocator64.S @@ -0,0 +1,206 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 +#include + +#ifdef __x86_64__ +#define RAX %rax +#define RSI %rdi +#else +#define RAX %eax +#define RSI %esi +#endif + +#define CODE32_SEGMENT 0x18 +#define CODE64_SEGMENT 0x08 + +/* The data segment of the protected mode. */ +#define DATA_SEGMENT 0x10 + + .p2align 4 /* force 16-byte alignment */ + +VARIABLE(grub_relocator64_start) +LOCAL(base): + /* %rax contains now our new 'base'. */ + mov RAX, RSI + + add $(LOCAL(cont0) - LOCAL(base)), RAX + jmp *RAX +LOCAL(cont0): + lea (LOCAL(cont1) - LOCAL(base)) (RSI, 1), RAX + mov RAX, (LOCAL(jump_vector) - LOCAL(base)) (RSI, 1) + + lea (LOCAL(gdt) - LOCAL(base)) (RSI, 1), RAX + mov RAX, (LOCAL(gdt_addr) - LOCAL(base)) (RSI, 1) + +#ifndef __x86_64__ + /* Disable paging. */ + movl %cr0, %eax + andl $(~GRUB_MEMORY_CPU_CR0_PAGING_ON), %eax + movl %eax, %cr0 + + /* Turn on PAE. */ + movl %cr4, %eax + orl $(GRUB_MEMORY_CPU_CR4_PAE_ON | GRUB_MEMORY_CPU_CR4_PSE_ON), %eax + movl %eax, %cr4 + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator64_cr3) + .long 0 + movl %eax, %cr3 + + /* Turn on amd64. */ + movl $GRUB_MEMORY_CPU_AMD64_MSR, %ecx + rdmsr + orl $GRUB_MEMORY_CPU_AMD64_MSR_ON, %eax + wrmsr + + /* Enable paging. */ + movl %cr0, %eax + orl $GRUB_MEMORY_CPU_CR0_PAGING_ON, %eax + movl %eax, %cr0 +#else + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator64_cr3) + .quad 0 + movl %rax, %cr3 +#endif + /* Load GDT. */ + lgdt (LOCAL(gdtdesc) - LOCAL(base)) (RSI, 1) + + /* Update %cs. */ + ljmp *(LOCAL(jump_vector) - LOCAL(base)) (RSI, 1) + +LOCAL(cont1): + .code64 + + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator64_rsp) + .quad 0 + + movq %rax, %rsp + + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator64_rsi) + .quad 0 + + movq %rax, %rsi + + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator64_rax) + .quad 0 + + /* mov imm64, %rbx */ + .byte 0x48 + .byte 0xbb +VARIABLE(grub_relocator64_rbx) + .quad 0 + + /* mov imm64, %rcx */ + .byte 0x48 + .byte 0xb9 +VARIABLE(grub_relocator64_rcx) + .quad 0 + + /* mov imm64, %rdx */ + .byte 0x48 + .byte 0xba +VARIABLE(grub_relocator64_rdx) + .quad 0 + + /* Cleared direction flag is of no problem with any current + payload and makes this implementation easier. */ + cld + + jmp *LOCAL(jump_addr) (%rip) + +LOCAL(jump_addr): +VARIABLE(grub_relocator64_rip) + .quad 0 + + .p2align 4 +LOCAL(gdt): + /* NULL. */ + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + + /* 64-bit segment. */ + .word 0xffff /* Limit xffff. */ + .word 0x0000 /* Base xxxx0000. */ + .byte 0x00 /* Base xx00xxxx. */ + .byte (0x8 /* Type 8. */ | (1 << 4) /* Code. */ \ + | (0 << 5) /* Ring 0. */ | (1 << 7) /* Present. */) + .byte (0xf /* Limit fxxxx. */ | (0 << 4) /* AVL flag. */ \ + | (1 << 5) /* 64-bit. */ | (0 << 6) \ + | (1 << 7) /* 4K granular. */) + .byte 0x00 /* Base 00xxxxxx. */ + + /* Data segment*/ + .word 0xffff /* Limit xffff. */ + .word 0x0000 /* Base xxxx0000. */ + .byte 0x00 /* Base xx00xxxx. */ + .byte (0x0 /* Type 0. */ | (0 << 4) /* Data. */ \ + | (0 << 5) /* Ring 0. */ | (1 << 7) /* Present. */) + .byte (0xf /* Limit fxxxx. */ | (0 << 4) /* AVL flag. */ \ + | (0 << 5) /* Data. */ | (0 << 6) \ + | (1 << 7) /* 4K granular. */) + .byte 0x00 /* Base 00xxxxxx. */ + + /* Compatibility segment. */ + .word 0xffff /* Limit xffff. */ + .word 0x0000 /* Base xxxx0000. */ + .byte 0x00 /* Base xx00xxxx. */ + .byte (0x8 /* Type 8. */ | (1 << 4) /* Code. */ \ + | (0 << 5) /* Ring 0. */ | (1 << 7) /* Present. */) + .byte (0xf /* Limit fxxxx. */ | (0 << 4) /* AVL flag. */ \ + | (0 << 5) /* 32-bit. */ | (1 << 6) /* 32-bit. */ \ + | (1 << 7) /* 4K granular. */) + .byte 0x00 /* Base 00xxxxxx. */ + + .p2align 4 +LOCAL(gdtdesc): + .word 0x20 +LOCAL(gdt_addr): +#ifdef __x86_64__ + /* Filled by the code. */ + .quad 0 +#else + /* Filled by the code. */ + .long 0 +#endif + + .p2align 4 +LOCAL(jump_vector): + /* Jump location. Is filled by the code */ +#ifdef __x86_64__ + .quad 0 +#else + .long 0 +#endif + .long CODE64_SEGMENT + +VARIABLE(grub_relocator64_end)