This patch changes vmlinux_entry64 to switch to protected (32-bit) mode
and jump to the start32 in order to setup transition to long mode
in a single consistent way.

In essence this patch removes unnecesary duplication between
vmlinux-boot.S and boot.S. But more importantly it corrects
the vmlinux boot code (used with firecracker) by making sure
that all data segment registers (ES, DS, etc) are setup correctly to point
to OSv GDT table (see start32) which is critical when kernel
is not mapped 1:1.

Signed-off-by: Waldemar Kozaczuk <[email protected]>
---
 arch/x64/boot.S           | 28 +++++++++++++++
 arch/x64/vmlinux-boot64.S | 71 ++++++++++++++-------------------------
 2 files changed, 54 insertions(+), 45 deletions(-)

diff --git a/arch/x64/boot.S b/arch/x64/boot.S
index c1a4e013..1402e5d0 100644
--- a/arch/x64/boot.S
+++ b/arch/x64/boot.S
@@ -44,12 +44,24 @@ gdt_desc:
     .short gdt_end - gdt - 1
     .long gdt
 
+# Set up the 64-bit compatible version of GDT description structure
+# that points to the same GDT (Global segments Descriptors Table) and
+# is used in vmlinux_entry64 to switch back to the protected (32-bit) mode
+# from long mode (64-bit).
+# Please note the address of the GDT is a 8-bytes-long field instead of
+# 4-bytes only in regular 32 version (gdt_desc)
+.align 8
+gdt64_desc:
+    .short gdt_end - gdt - 1
+    .quad gdt
+
 .align 8
 gdt = . - 8
     .quad 0x00af9b000000ffff # 64-bit code segment
     .quad 0x00cf93000000ffff # 64-bit data segment
     .quad 0x00cf9b000000ffff # 32-bit code segment
 gdt_end = .
+.globl gdt64_desc
 
 .align 8
 . = . + 4  # make sure tss_ist is aligned on a quad boundary
@@ -63,10 +75,16 @@ init_stack_top = .
 .text
 
 .globl start32
+.globl start32_from_64
 start32:
     # boot16.S set %eax to ELF start address, we'll use it later
     mov %eax, %ebp
+    mov $0x0, %edi
     lgdt gdt_desc
+
+# Add an address the vmlinux_entry64 will jump to when
+# switching from 64-bit to 32-bit mode
+start32_from_64:
     mov $0x10, %eax
     mov %eax, %ds
     mov %eax, %es
@@ -103,6 +121,16 @@ start64:
     .cfi_startproc simple
     .cfi_def_cfa %rsp, 0
     .cfi_undefined %rip
+    # Check for non-zero value in RDI register to detect if
+    # it contains the address of the boot_params structure
+    # that would be set if we came here from vmlinux_entry64
+    cmp $0x0, %rdi
+    jz start64_continue
+    call extract_linux_boot_params
+    mov $0x1000, %rbx
+    mov $0x200000, %rbp
+
+start64_continue:
     lea .bss, %rdi
     lea .edata, %rcx
     sub %rdi, %rcx
diff --git a/arch/x64/vmlinux-boot64.S b/arch/x64/vmlinux-boot64.S
index 2c61270b..230afd3c 100644
--- a/arch/x64/vmlinux-boot64.S
+++ b/arch/x64/vmlinux-boot64.S
@@ -3,54 +3,35 @@
 # This work is open source software, licensed under the terms of the
 # BSD license as described in the LICENSE file in the top-level directory.
 
-#include "processor-flags.h"
-
-#define BOOT_CR0 ( X86_CR0_PE \
-                 | X86_CR0_WP \
-                 | X86_CR0_PG )
-
-#define BOOT_CR4 ( X86_CR4_DE         \
-                 | X86_CR4_PSE        \
-                 | X86_CR4_PAE        \
-                 | X86_CR4_PGE        \
-                 | X86_CR4_PCE        \
-                 | X86_CR4_OSFXSR     \
-                 | X86_CR4_OSXMMEXCPT )
-
 .text
 .code64
 .global vmlinux_entry64
 vmlinux_entry64:
-    # The address of boot_params structed is passed in RSI
-    # register so pass it to extract_linux_boot_params fuction
-    # which will extract cmdline and memory information and verify
-    # that loader.elf was indeed called as Linux 64-bit vmlinux ELF
+    # The address of the boot_params structure is passed in the RSI
+    # register so store it in RDI register so that it can be received
+    # by the extract_linux_boot_params fuction later
     mov %rsi, %rdi
-    call extract_linux_boot_params
-
-    # Even though we are in 64-bit long mode we need to reset
-    # page tables and other CPU settings the way OSv expects it
-
-    # Enable PAE (Physical Address Extension) - ability to address 64GB
-    # TODO: Add more comments to processor-flags.h what each flag does
-    mov $BOOT_CR4, %rax
-    mov %rax, %cr4
-
-    # Set root of a page table in cr3
-    lea ident_pt_l4, %rax
-    mov %rax, %cr3
-
-    # Set long mode
-    mov $0xc0000080, %ecx // EFER MSR number
-    mov $0x00000900, %eax // Set LME = 1
-    xor %edx, %edx
-    wrmsr // Write contents of EDX:EAX (0:to Model Specific Register specified 
by ECX register
-
-    # Activate paging and ...?
-    # TODO: Add more comments to processor-flags.h what each flag does
-    mov $BOOT_CR0, %rax
-    mov %rax, %cr0
 
-    mov $OSV_KERNEL_BASE, %rbp
-    mov $0x1000, %rbx
-    jmp start64
+    # Load the 64-bit version of the GDT
+    lgdt gdt64_desc
+
+    # Setup the stack to switch back to 32-bit mode in order
+    # to converge with the code that sets up transiton to 64-bit mode later.
+    # Switching back to 32-bit when we are already in 64-bit seems
+    # counter intuitive but in fact it allows us to remove code duplication
+    # around setting up 64-bit mode of CPU the OSv-way. Besides setting up
+    # paging and other control registers this also makes sure that the segment
+    # registers are setup correctly as well.
+    #
+    # The transition from 64-bit back to 32-bit mode is unfortunately not very
+    # well documented. For details please read
+    # http://blog.dolezel.info/2017/02/running-32-bit-code-in-64-bit-linux.html
+    # In short we need to push the 32-bit code segment descriptor GDT offset 
(0x18)
+    # and the address of the instruction (start32_from_64) we want to jump to
+    # The lret instruction pops the address and the segment descriptor and 
jumpt
+    # to start32_from_64 which is where the boot process converges.
+    subq $8, %rsp
+    movl $0x18, 4(%rsp)
+    movl $start32_from_64, %eax
+    movl %eax, (%rsp)
+    lret
-- 
2.20.1

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/20190615034622.23154-1-jwkozaczuk%40gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to