[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine

Gene Cumm
Sun, 23 Aug 2015 18:57:21 -0000

On Tue, Aug 4, 2015 at 2:55 AM, Thomas Letan via Syslinux
<syslinux at zytor.com> wrote:
> 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>
> ---
>  efi/x86_64/linux.S | 61
> ++++++++++++++++++++++++++++++++++++------------------
>  1 file changed, 41 insertions(+), 20 deletions(-)
>

Your mailer or something else mangled this patch pretty well as the
copy in my mailbox and on the archives are unusable.  This however is.



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



--
-Gene