summaryrefslogtreecommitdiff
path: root/efi/x86_64/linux.S
diff options
context:
space:
mode:
Diffstat (limited to 'efi/x86_64/linux.S')
-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