|
@@ -307,11 +307,27 @@ ENTRY(startup_64)
|
|
|
|
|
|
/*
|
|
|
* At this point we are in long mode with 4-level paging enabled,
|
|
|
- * but we want to enable 5-level paging.
|
|
|
+ * but we might want to enable 5-level paging or vice versa.
|
|
|
*
|
|
|
- * The problem is that we cannot do it directly. Setting LA57 in
|
|
|
- * long mode would trigger #GP. So we need to switch off long mode
|
|
|
- * first.
|
|
|
+ * The problem is that we cannot do it directly. Setting or clearing
|
|
|
+ * CR4.LA57 in long mode would trigger #GP. So we need to switch off
|
|
|
+ * long mode and paging first.
|
|
|
+ *
|
|
|
+ * We also need a trampoline in lower memory to switch over from
|
|
|
+ * 4- to 5-level paging for cases when the bootloader puts the kernel
|
|
|
+ * above 4G, but didn't enable 5-level paging for us.
|
|
|
+ *
|
|
|
+ * The same trampoline can be used to switch from 5- to 4-level paging
|
|
|
+ * mode, like when starting 4-level paging kernel via kexec() when
|
|
|
+ * original kernel worked in 5-level paging mode.
|
|
|
+ *
|
|
|
+ * For the trampoline, we need the top page table to reside in lower
|
|
|
+ * memory as we don't have a way to load 64-bit values into CR3 in
|
|
|
+ * 32-bit mode.
|
|
|
+ *
|
|
|
+ * We go though the trampoline even if we don't have to: if we're
|
|
|
+ * already in a desired paging mode. This way the trampoline code gets
|
|
|
+ * tested on every boot.
|
|
|
*/
|
|
|
|
|
|
/* Make sure we have GDT with 32-bit code segment */
|
|
@@ -336,13 +352,18 @@ ENTRY(startup_64)
|
|
|
/* Save the trampoline address in RCX */
|
|
|
movq %rax, %rcx
|
|
|
|
|
|
+ /*
|
|
|
+ * Load the address of trampoline_return() into RDI.
|
|
|
+ * It will be used by the trampoline to return to the main code.
|
|
|
+ */
|
|
|
+ leaq trampoline_return(%rip), %rdi
|
|
|
|
|
|
/* Switch to compatibility mode (CS.L = 0 CS.D = 1) via far return */
|
|
|
pushq $__KERNEL32_CS
|
|
|
- leaq compatible_mode(%rip), %rax
|
|
|
+ leaq TRAMPOLINE_32BIT_CODE_OFFSET(%rax), %rax
|
|
|
pushq %rax
|
|
|
lretq
|
|
|
-lvl5:
|
|
|
+trampoline_return:
|
|
|
/* Restore the stack, the 32-bit trampoline uses its own stack */
|
|
|
leaq boot_stack_end(%rbx), %rsp
|
|
|
|
|
@@ -492,8 +513,14 @@ relocated:
|
|
|
jmp *%rax
|
|
|
|
|
|
.code32
|
|
|
+/*
|
|
|
+ * This is the 32-bit trampoline that will be copied over to low memory.
|
|
|
+ *
|
|
|
+ * RDI contains the return address (might be above 4G).
|
|
|
+ * ECX contains the base address of the trampoline memory.
|
|
|
+ * Non zero RDX on return means we need to enable 5-level paging.
|
|
|
+ */
|
|
|
ENTRY(trampoline_32bit_src)
|
|
|
-compatible_mode:
|
|
|
/* Set up data and stack segments */
|
|
|
movl $__KERNEL_DS, %eax
|
|
|
movl %eax, %ds
|
|
@@ -534,24 +561,34 @@ compatible_mode:
|
|
|
1:
|
|
|
movl %eax, %cr4
|
|
|
|
|
|
- /* Calculate address we are running at */
|
|
|
- call 1f
|
|
|
-1: popl %edi
|
|
|
- subl $1b, %edi
|
|
|
+ /* Calculate address of paging_enabled() once we are executing in the trampoline */
|
|
|
+ leal paging_enabled - trampoline_32bit_src + TRAMPOLINE_32BIT_CODE_OFFSET(%ecx), %eax
|
|
|
|
|
|
- /* Prepare stack for far return to Long Mode */
|
|
|
+ /* Prepare the stack for far return to Long Mode */
|
|
|
pushl $__KERNEL_CS
|
|
|
- leal lvl5(%edi), %eax
|
|
|
- push %eax
|
|
|
+ pushl %eax
|
|
|
|
|
|
- /* Enable paging back */
|
|
|
+ /* Enable paging again */
|
|
|
movl $(X86_CR0_PG | X86_CR0_PE), %eax
|
|
|
movl %eax, %cr0
|
|
|
|
|
|
lret
|
|
|
|
|
|
+ .code64
|
|
|
+paging_enabled:
|
|
|
+ /* Return from the trampoline */
|
|
|
+ jmp *%rdi
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The trampoline code has a size limit.
|
|
|
+ * Make sure we fail to compile if the trampoline code grows
|
|
|
+ * beyond TRAMPOLINE_32BIT_CODE_SIZE bytes.
|
|
|
+ */
|
|
|
+ .org trampoline_32bit_src + TRAMPOLINE_32BIT_CODE_SIZE
|
|
|
+
|
|
|
+ .code32
|
|
|
no_longmode:
|
|
|
- /* This isn't an x86-64 CPU so hang */
|
|
|
+ /* This isn't an x86-64 CPU, so hang intentionally, we cannot continue */
|
|
|
1:
|
|
|
hlt
|
|
|
jmp 1b
|