aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Letan <thomas.letan@ssi.gouv.fr>2015-09-06 07:50:39 -0400
committerGene Cumm <gene.cumm@gmail.com>2015-09-06 07:51:26 -0400
commitae853e99a7aed22cb28b387e1e3cb32dbf1ab8fa (patch)
treeb6381beb5bee75cbf41c921b2c624c0d4875651c
parent62f63cb7706974eba2c34dbea425bf2e9ede0f9e (diff)
downloadsyslinux-ae853e99a7aed22cb28b387e1e3cb32dbf1ab8fa.tar.gz
syslinux-ae853e99a7aed22cb28b387e1e3cb32dbf1ab8fa.tar.xz
syslinux-ae853e99a7aed22cb28b387e1e3cb32dbf1ab8fa.zip
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@ssi.gouv.fr> Tested-by: Patrick Masotta <masottaus@yahoo.com> Tested-by: Celelibi <celelibi@gmail.com>
-rw-r--r--efi/x86_64/linux.S61
1 files changed, 41 insertions, 20 deletions
diff --git a/efi/x86_64/linux.S b/efi/x86_64/linux.S
index 0a0e9965..972c0b2c 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,@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