diff --git a/ChangeLog b/ChangeLog index 371a7ed3b..191ce8a34 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2009-05-17 Vladimir Serbinenko + + trampoline for linux on 64-bit platform + + * conf/x86_64-efi.rmk (linux_mod_SOURCES): added + loader/i386/efi/linux_trampoline.S + * include/grub/x86_64/efi/loader.h (grub_linux_real_boot): removed + declration + * kern/x86_64/efi/startup.S (grub_linux_real_boot): moved from here + * loader/i386/linux_trampoline.S: moved here + * loader/i386/efi/linux.c (allocate_pages): reserve space for trampoline + (jumpvector): removed + (grub_linux_trampoline_start): new declaration + (grub_linux_trampoline_end): likewise + (grub_linux_boot): use trampoline when on 64-bit platform + * loader/i386/linux.c: likewise + 2009-05-16 Pavel Roskin * script/lua/grub_lib.c (grub_lua_getenv): Make name and value diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk index 391cda312..5d342b37f 100644 --- a/conf/x86_64-efi.rmk +++ b/conf/x86_64-efi.rmk @@ -137,7 +137,7 @@ appleldr_mod_CFLAGS = $(COMMON_CFLAGS) appleldr_mod_LDFLAGS = $(COMMON_LDFLAGS) # For linux.mod. -linux_mod_SOURCES = loader/i386/efi/linux.c +linux_mod_SOURCES = loader/i386/efi/linux.c loader/i386/linux_trampoline.S linux_mod_CFLAGS = $(COMMON_CFLAGS) linux_mod_LDFLAGS = $(COMMON_LDFLAGS) diff --git a/include/grub/x86_64/efi/loader.h b/include/grub/x86_64/efi/loader.h index fac67463b..7c302e8a5 100644 --- a/include/grub/x86_64/efi/loader.h +++ b/include/grub/x86_64/efi/loader.h @@ -22,6 +22,5 @@ #include #include -void EXPORT_FUNC(grub_linux_real_boot) (void); #endif /* ! GRUB_LOADER_MACHINE_HEADER */ diff --git a/kern/x86_64/efi/startup.S b/kern/x86_64/efi/startup.S index 2cb1fd4a5..fb4fc7b64 100644 --- a/kern/x86_64/efi/startup.S +++ b/kern/x86_64/efi/startup.S @@ -61,26 +61,3 @@ codestart: call EXT_C(grub_main) ret - .code32 - -FUNCTION(grub_linux_real_boot) - /* Turn off PG bit in CR0 and set CR3 to zero. */ - movl %cr0, %eax - andl $0x7FFFFFFF, %eax - movl %eax, %cr0 - - /* Setup EFER (Extended Feature Enable Register). */ - movl $0xc0000080, %ecx - rdmsr - - /* Disable Long Mode. */ - andl $0xFFFFFEFF, %eax - - /* Make changes effective. */ - wrmsr - - /* Disable PAE. */ - xorl %eax, %eax - movl %eax, %cr4 - - jmp *%ebx diff --git a/loader/i386/efi/linux.c b/loader/i386/efi/linux.c index 9be88aa79..dae0f6dcd 100644 --- a/loader/i386/efi/linux.c +++ b/loader/i386/efi/linux.c @@ -241,7 +241,7 @@ allocate_pages (grub_size_t prot_size) /* Next, find free pages for the protected mode code. */ /* XXX what happens if anything is using this address? */ - prot_mode_mem = grub_efi_allocate_pages (0x100000, prot_mode_pages); + prot_mode_mem = grub_efi_allocate_pages (0x100000, prot_mode_pages + 1); if (! prot_mode_mem) { grub_error (GRUB_ERR_OUT_OF_MEMORY, @@ -286,11 +286,8 @@ grub_e820_add_region (struct grub_e820_mmap *e820_map, int *e820_num, } #ifdef __x86_64__ -struct -{ - grub_uint32_t kernel_entry; - grub_uint32_t kernel_cs; -} jumpvector; +extern grub_uint8_t grub_linux_trampoline_start[]; +extern grub_uint8_t grub_linux_trampoline_end[]; #endif static grub_err_t @@ -384,6 +381,18 @@ grub_linux_boot (void) params->v0204.efi_mmap_size = mmap_size; } +#ifdef __x86_64__ + + grub_memcpy ((char *) prot_mode_mem + (prot_mode_pages << 12), + grub_linux_trampoline_start, + grub_linux_trampoline_end - grub_linux_trampoline_start); + + ((void (*) (unsigned long, void *)) ((char *) prot_mode_mem + + (prot_mode_pages << 12))) + (params->code32_start, real_mode_mem); + +#else + /* Hardware interrupts are not safe any longer. */ asm volatile ("cli" : : ); @@ -391,18 +400,6 @@ grub_linux_boot (void) asm volatile ("lidt %0" : : "m" (idt_desc)); asm volatile ("lgdt %0" : : "m" (gdt_desc)); -#ifdef __x86_64__ - - jumpvector.kernel_entry = (grub_uint64_t) grub_linux_real_boot; - jumpvector.kernel_cs = 0x10; - - asm volatile ( "mov %0, %%rbx" : : "m" (params->code32_start)); - asm volatile ( "mov %0, %%rsi" : : "m" (real_mode_mem)); - - asm volatile ( "ljmp *%0" : : "m" (jumpvector)); - -#else - /* Pass parameters. */ asm volatile ("movl %0, %%ecx" : : "m" (params->code32_start)); asm volatile ("movl %0, %%esi" : : "m" (real_mode_mem)); diff --git a/loader/i386/linux.c b/loader/i386/linux.c index 8a7288ecd..ca44fb46f 100644 --- a/loader/i386/linux.c +++ b/loader/i386/linux.c @@ -433,11 +433,8 @@ grub_linux_setup_video (struct linux_kernel_params *params) } #ifdef __x86_64__ -struct -{ - grub_uint32_t kernel_entry; - grub_uint32_t kernel_cs; -} jumpvector; +extern grub_uint8_t grub_linux_trampoline_start[]; +extern grub_uint8_t grub_linux_trampoline_end[]; #endif static grub_err_t @@ -549,6 +546,16 @@ grub_linux_boot (void) grub_mmap_iterate (hook); params->mmap_size = e820_num; +#ifdef __x86_64__ + + grub_memcpy ((char *) prot_mode_mem + (prot_mode_pages << 12), + grub_linux_trampoline_start, + grub_linux_trampoline_end - grub_linux_trampoline_start); + + ((void (*) (unsigned long, void *)) ((char *) prot_mode_mem + + (prot_mode_pages << 12))) + (params->code32_start, real_mode_mem); +#else /* Hardware interrupts are not safe any longer. */ asm volatile ("cli" : : ); @@ -557,18 +564,6 @@ grub_linux_boot (void) asm volatile ("lidt %0" : : "m" (idt_desc)); asm volatile ("lgdt %0" : : "m" (gdt_desc)); -#ifdef __x86_64__ - - jumpvector.kernel_entry = (grub_uint64_t) grub_linux_real_boot; - jumpvector.kernel_cs = 0x10; - - asm volatile ( "mov %0, %%rbx" : : "m" (params->code32_start)); - asm volatile ( "mov %0, %%rsi" : : "m" (real_mode_mem)); - - asm volatile ( "ljmp *%0" : : "m" (jumpvector)); - -#else - /* Pass parameters. */ asm volatile ("movl %0, %%ecx" : : "m" (params->code32_start)); asm volatile ("movl %0, %%esi" : : "m" (real_mode_mem)); diff --git a/loader/i386/linux_trampoline.S b/loader/i386/linux_trampoline.S new file mode 100644 index 000000000..39821e1d3 --- /dev/null +++ b/loader/i386/linux_trampoline.S @@ -0,0 +1,112 @@ +/* + * 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 . + */ + +#include + + + .p2align 4 /* force 16-byte alignment */ +VARIABLE(grub_linux_trampoline_start) + cli + /* %rdi contains protected memory start and %rsi + contains real memory start. */ + + mov %rsi, %rbx + + call base +base: + pop %rsi + + lea (cont1-base)(%rsi, 1), %rax + mov %eax, (jump_vector-base)(%rsi,1) + + lea (gdt-base)(%rsi, 1), %rax + mov %rax, (gdtaddr-base)(%rsi,1) + + /* Switch to compatibility mode. */ + + lidt (idtdesc-base)(%rsi,1) + lgdt (gdtdesc-base)(%rsi,1) + + /* Update %cs. Thanks to David Miller for pointing this mistake out. */ + ljmp *(jump_vector-base)(%rsi,1) +cont1: + .code32 + + /* Update other registers. */ + mov $0x18, %eax + mov %eax, %ds + mov %eax, %es + mov %eax, %fs + mov %eax, %gs + mov %eax, %ss + + /* Disable paging. */ + mov %cr0, %eax + and $0x7fffffff, %eax + mov %eax, %cr0 + + /* Disable amd64. */ + mov $0xc0000080, %ecx + rdmsr + and $0xfffffeff, %eax + wrmsr + + /* Turn off PAE. */ + movl %cr4, %eax + and $0xffffffcf, %eax + mov %eax, %cr4 + + jmp cont2 +cont2: + .code32 + + mov %ebx, %esi + + jmp *%edi + + /* GDT. */ + .p2align 4 +gdt: + /* NULL. */ + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + + /* Reserved. */ + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + + /* Code segment. */ + .byte 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00 + + /* Data segment. */ + .byte 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00 + +gdtdesc: + .word 31 +gdtaddr: + .quad gdt + +idtdesc: + .word 0 +idtaddr: + .quad 0 + + .p2align 4 +jump_vector: + /* Jump location. Is filled by the code */ + .long 0 + .long 0x10 +VARIABLE(grub_linux_trampoline_end)