[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
Gene Cumm
gene.cumm at gmail.com
Sun Aug 23 11:57:21 PDT 2015
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
More information about the Syslinux
mailing list