Skip to content

Commit

Permalink
efi/x86_64: leave long mode properly
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
lthms authored and geneC committed Sep 6, 2015
1 parent 62f63cb commit ae853e9
Showing 1 changed file with 41 additions and 20 deletions.
61 changes: 41 additions & 20 deletions efi/x86_64/linux.S
Expand Up @@ -10,39 +10,60 @@
*
* ----------------------------------------------------------------------- */

#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
.code64
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
Expand Down

0 comments on commit ae853e9

Please sign in to comment.