Hello. On 64-bit systems the code may be loaded above 4GiB. When grub tries
to switch to 32-bit mode before launching linux it causes the reboot. Here
is a patch to make this part of code into a trampoline

-- 
Regards
Vladimir 'phcoder' Serbinenko
diff --git a/ChangeLog b/ChangeLog
index c307abe..6bc043a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2009-05-09  Vladimir Serbinenko  <phco...@gmail.com>
+
+	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/efi/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-08  Robert Millan  <rmh.g...@aybabtu.com>
 
 	* util/i386/pc/grub-setup.c (setup): Factorize find_usable_region(),
diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk
index 92ad9f7..45e8527 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/efi/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 fac6746..7c302e8 100644
--- a/include/grub/x86_64/efi/loader.h
+++ b/include/grub/x86_64/efi/loader.h
@@ -22,6 +22,5 @@
 #include <grub/types.h>
 #include <grub/symbol.h>
 
-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 2cb1fd4..fb4fc7b 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 9be88aa..09b8d82 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,8 @@ grub_linux_boot (void)
       params->v0204.efi_mmap_size = mmap_size;
     }
 
+#else
+
   /* Hardware interrupts are not safe any longer.  */
   asm volatile ("cli" : : );
   
@@ -391,18 +390,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/efi/linux_trampoline.S b/loader/i386/efi/linux_trampoline.S
new file mode 100644
index 0000000..39821e1
--- /dev/null
+++ b/loader/i386/efi/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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/symbol.h>
+	
+
+	.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)
diff --git a/loader/i386/linux.c b/loader/i386/linux.c
index d67a5f8..07fdb99 100644
--- a/loader/i386/linux.c
+++ b/loader/i386/linux.c
@@ -432,11 +432,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
@@ -548,6 +545,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 (*) (void *, unsigned long)) ((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" : : );
@@ -556,18 +563,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));
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to