[syslinux:master] efi/x86_64: leave long mode properly

syslinux-bot for Thomas Letan thomas.letan at ssi.gouv.fr
Sun Sep 6 05:27:05 PDT 2015


Commit-ID:  ae853e99a7aed22cb28b387e1e3cb32dbf1ab8fa
Gitweb:     http://www.syslinux.org/commit/ae853e99a7aed22cb28b387e1e3cb32dbf1ab8fa
Author:     Thomas Letan <thomas.letan at ssi.gouv.fr>
AuthorDate: Sun, 6 Sep 2015 07:50:39 -0400
Committer:  Gene Cumm <gene.cumm at gmail.com>
CommitDate: Sun, 6 Sep 2015 07:51:26 -0400

efi/x86_64: leave long mode properly

Syslinux 6.03 (efi64) fails to boot a 32-bit kernel. The way Syslinux
leaves long mode in kernel_jump assembly routine does not follow AMD64
specifications. More precisely:
1. After setting a new GADT, `cs` has to be refresh by doing a long
jump, but it is not
2. Other segments have to be updated, but they are not
3. Disabling paging has to be done before disabling long mode, but the
implementation does the opposite

In most cases, a computer that tries to execute the kernel_jump routine
reboot (it can also hangs). This patch fixes the kernel_jump routine.

Signed-off-by: Thomas Letan <thomas.letan at ssi.gouv.fr>
Tested-by: Patrick Masotta <masottaus at yahoo.com>
Tested-by: Celelibi <celelibi at gmail.com>

---
 efi/x86_64/linux.S | 61 ++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 41 insertions(+), 20 deletions(-)

diff --git a/efi/x86_64/linux.S b/efi/x86_64/linux.S
index 0a0e996..972c0b2 100644
--- a/efi/x86_64/linux.S
+++ b/efi/x86_64/linux.S
@@ -10,8 +10,9 @@
  *
  * ----------------------------------------------------------------------- */
 
-#define CR0_PG_FLAG	0x80000000
-#define MSR_EFER	0xc0000080
+#define CR0_PG_BIT    31
+#define CR4_PAE_BIT   5
+#define MSR_EFER      0xc0000080
 
 	.globl kernel_jump
 	.type kernel_jump, at function
@@ -19,30 +20,50 @@
 kernel_jump:
 	cli
 
-	/*
-	 * Setup our segment selector (0x10) and return address (%rdi)
-	 * on the stack in preparation for the far return below.
-	 */
-	mov	$0x1000000000, %rcx
-	addq	%rcx, %rdi
-	pushq	%rdi
+	/* save the content of rsi (boot_param argument of kernel_jump function) */
+	mov %rsi, %rbx
+
+	call base_address
+base_address:
+	pop %rsi
+
+	/* need to perform a long jump to update cs
+
+	/* load absolute address of pm_code in jmp_address location */
+	lea (pm_code - base_address)(%rsi, 1), %rax
+	mov %eax, (jmp_address - base_address)(%rsi, 1)
+
+	ljmp *(jmp_address - base_address)(%rsi, 1)
+
+jmp_address:
+	.long 0    /* address */
+	.word 0x10 /* segment */
 
 	.code32
 pm_code:
 
-	/* Disable IA-32e mode by clearing IA32_EFER.LME */
-	xorl	%eax, %eax
-	xorl	%edx, %edx
-	movl	$MSR_EFER, %ecx
-	wrmsr
+	/* cs segment has been updated, now update the rest */
+	mov $0x18, %eax
+	mov %eax, %ds
+	mov %eax, %es
+	mov %eax, %fs
+	mov %eax, %gs
+	mov %eax, %ss
 
-	/* Turn off paging to disable long mode */
-	movl	%cr0, %eax
-	andl	$~CR0_PG_FLAG, %eax
-	movl	%eax, %cr0
+	/* disable paging. */
+	mov %cr0, %eax
+	btr $CR0_PG_BIT, %eax /* PG in CR0 */
+	mov %eax, %cr0
+
+	/* disable long mode. */
+	mov $MSR_EFER, %ecx
+	rdmsr
+	btr $8, %eax
+	wrmsr
 
-	/* Far return */
-	lret
+	/* kernel jump */
+	mov %ebx, %esi
+	jmp *%edi
 
 	.code64
 	.align 4


More information about the Syslinux-commits mailing list