[SeaBIOS] [RFC 2/3] Transitions to and from 64 bits

Marc Marí markmb at redhat.com
Mon Sep 21 13:14:06 CET 2015


Signed-off-by: Marc Marí <markmb at redhat.com>
---
 src/config.h    |   2 ++
 src/misc.c      |   4 +++
 src/romlayout.S | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/stacks.c    |  77 ++++++++++++++++++++++++++++++++++++++++
 src/stacks.h    |   2 ++
 src/x86.h       |   1 +
 6 files changed, 192 insertions(+)

diff --git a/src/config.h b/src/config.h
index 6c47f16..dfbed89 100644
--- a/src/config.h
+++ b/src/config.h
@@ -69,6 +69,8 @@
 #define SEG32_MODE16_DS    (4 << 3)
 #define SEG32_MODE16BIG_CS (5 << 3)
 #define SEG32_MODE16BIG_DS (6 << 3)
+#define SEG32_MODE64_CS    (7 << 3)
+#define SEG32_MODE64_DS    (8 << 3)
 
 // Debugging levels.  If non-zero and CONFIG_DEBUG_LEVEL is greater
 // than the specified value, then the corresponding irq handler will
diff --git a/src/misc.c b/src/misc.c
index 8caaf31..6c9a490 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -168,6 +168,10 @@ u64 rombios32_gdt[] VARFSEG __aligned(8) = {
     GDT_GRANLIMIT(0xffffffff) | GDT_CODE | GDT_BASE(BUILD_BIOS_ADDR),
     // 16 bit data segment base=0 limit=0xffffffff (SEG32_MODE16BIG_DS)
     GDT_GRANLIMIT(0xffffffff) | GDT_DATA,
+    // 64 bit code segment (SEG32_MODE64_CS)
+    GDT_GRANLIMIT(0xffffffff) | GDT_CODE | GDT_L,
+    // 64 bit code segment (SEG32_MODE64_CS)
+    GDT_GRANLIMIT(0xffffffff) | GDT_DATA | GDT_L,
 };
 
 // GDT descriptor
diff --git a/src/romlayout.S b/src/romlayout.S
index fefc212..4cb9f4c 100644
--- a/src/romlayout.S
+++ b/src/romlayout.S
@@ -115,6 +115,112 @@ transition16big:
 
         jmpl *%edx
 
+// Place CPU into 64bit mode from 32bit mode.
+// %edi = Valid page table.
+// %edx = return location (in 32bit mode)
+// Clobbers: ebx, ecx, flags, control registers
+
+        DECLFUNC transition64
+        .code32
+transition64:
+        // Paging is already disabled
+
+        // Enable PAE
+        movl %cr4, %ebx
+        orl $0x20, %ebx
+        movl %ebx, %cr4
+
+        // Point CR3 at the PML4
+        movl %edi, %cr3
+
+        // Save valuable info in %eax and %edx before rdmsr erases it
+        movl %edx, %ebx
+        movl %eax, %edi
+
+        // Enable IA-32E mode
+        movl $0xC0000080, %ecx
+        rdmsr
+        orl $0x100, %eax
+        wrmsr
+
+        // Restore saved info
+        movl %ebx, %edx
+        movl %edi, %eax
+
+        // Enable paging
+        movl %cr0, %ebx
+        orl $0x80000000, %ebx
+        movl %ebx, %cr0
+
+        ljmpl $SEG32_MODE64_CS, $(BUILD_BIOS_ADDR + 1f)
+
+        // Set segment descriptor
+1:      movl $SEG32_MODE64_DS, %ebx
+        movw %bx, %ds
+        movw %bx, %es
+        movw %bx, %ss
+        movw %bx, %fs
+        movw %bx, %gs
+
+        // Jump
+        jmpl *%edx
+
+        .code16
+
+// Place CPU into 32bit mode from 64bit mode.
+// %edx = return location (in 32bit mode)
+// Clobbers: ebx, ecx, edi, flags, control registers
+
+        DECLFUNC transition32_from_64
+transition32_from_64:
+        // Jump into compatibility mode
+        .code64
+        movl $SEG32_MODE32_CS, %ecx
+        pushq %rcx // CS
+        movl $(BUILD_BIOS_ADDR + 1f), %ecx
+        pushq %rcx // IP
+        retfq
+
+        .code32
+        // Set 32 bit mode segments
+1:      movl $SEG32_MODE32_DS, %ebx
+        movw %bx, %ds
+        movw %bx, %es
+        movw %bx, %ss
+        movw %bx, %fs
+        movw %bx, %gs
+
+        // Disable paging
+        movl %cr0, %ebx
+        andl $(~0x80000000), %ebx
+        movl %ebx, %cr0
+
+        // Point cr3 to 0 again
+        movl $0, %ebx
+        movl %ebx, %cr3
+
+        // Disable PAE
+        movl %cr4, %ebx
+        andl $(~0x20), %ebx
+        movl %ebx, %cr4
+
+        // Save valuable info in %eax and %edx before rdmsr erases it
+        movl %edx, %ebx
+        movl %eax, %edi
+
+        // Disable IA-32E mode
+        movl $0xC0000080, %ecx
+        rdmsr
+        andl $(~0x100), %eax
+        wrmsr
+
+        // Restore saved info
+        movl %ebx, %edx
+        movl %edi, %eax
+
+        // Jump
+        jmpl *%edx
+        .code16
 
 /****************************************************************
  * External calling trampolines
diff --git a/src/stacks.c b/src/stacks.c
index 850a335..f71a184 100644
--- a/src/stacks.c
+++ b/src/stacks.c
@@ -15,6 +15,8 @@
 #include "stacks.h" // struct mutex_s
 #include "string.h" // memset
 #include "util.h" // useRTC
+#include "memmap.h" // PAGE_SIZE
+#include "string.h" // memset
 
 #define MAIN_STACK_MAX (1024*1024)
 
@@ -106,6 +108,7 @@ call16_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx))
 #define ASM32_BACK32   "  .popsection\n  .code32\n"
 #define ASM16_SWITCH32 "  .code32\n"
 #define ASM16_BACK16   "  .code16gcc\n"
+#define ASM32_SWITCH64 "  .code64\n"
 
 // Call a SeaBIOS C function in 32bit mode using smm trampoline
 static u32
@@ -308,6 +311,24 @@ call16big(u32 eax, u32 edx, void *func)
     return call16_back(eax, edx, func);
 }
 
+u32 call64(void *page_table, void *func, u32 eax)
+{
+    ASSERT32FLAT();
+
+    asm volatile(
+        // Transition to 64bit mode
+        "  movl $1f, %%edx\n"
+        "  jmp transition64\n"
+        "1:movl %1, %%edx\n"
+        "  calll *%%edx\n"
+        "  movl $2f, %%edx\n"
+        "  jmp transition32_from_64\n"
+        "2:\n"
+        : "+a" (eax)
+        : "g" (func), "D" (page_table)
+        : "edx", "ebx", "ecx", "cc", "memory");
+    return eax;
+}
 
 /****************************************************************
  * Extra 16bit stack
@@ -762,3 +783,59 @@ call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret)
     return call32(_cfunc32flat_call32_params_helper
                   , (u32)MAKE_FLATPTR(GET_SEG(SS), &params), errret);
 }
+
+/****************************************************************
+ * Page table
+ ****************************************************************/
+void *gen_identity_page_table(u64 max_addr)
+{
+    /* Map directly all the addresses */
+    u32 pt_entries = (max_addr + 0xFFF) >> 12;
+    u32 pdt_entries = (pt_entries + 0x1FF) >> 9;
+    u32 pdpt_entries = (pdt_entries + 0x1FF) >> 9;
+    u32 pml4_entries = (pdpt_entries + 0x1FF) >> 9;
+
+    if (pml4_entries > 1) {
+        dprintf(1, "Page table too big\n");
+        return NULL;
+    }
+
+    u32 table_size = (pdt_entries << 12) // PT size
+                    + (pdpt_entries << 12) // PDT size
+                    + (pml4_entries << 12) // PDPT size
+                    + ((pml4_entries + 0xFFF) >> 12); // PML4 size
+
+    void *table = memalign_tmp(PAGE_SIZE, table_size);
+    u64 *cur_pos = table;
+    u32 i;
+
+    memset(table, 0, table_size);
+
+    void *pt_start = cur_pos;
+    for (i = 0; i < pt_entries; ++i, ++cur_pos) {
+        *cur_pos = ((u64)i << 12) | 3;
+    }
+
+    cur_pos = (u64 *)ALIGN((u32)cur_pos, PAGE_SIZE);
+    void *pdt_start = cur_pos;
+
+    for (i = 0; i < pdt_entries; ++i, ++cur_pos) {
+        *cur_pos = ((u32)pt_start + (i << 12)) | 3;
+    }
+
+    cur_pos = (u64 *)ALIGN((u32)cur_pos, PAGE_SIZE);
+    void *pdpt_start = cur_pos;
+
+    for (i = 0; i < pdpt_entries; ++i, ++cur_pos) {
+        *cur_pos = ((u32)pdt_start + (i << 12)) | 3;
+    }
+
+    cur_pos = (u64 *)ALIGN((u32)cur_pos, PAGE_SIZE);
+    void *pml4_start = cur_pos;
+
+    for (i = 0; i < pml4_entries; ++i, ++cur_pos) {
+        *cur_pos = ((u32)pdpt_start + (i << 12)) | 3;
+    }
+
+    return pml4_start;
+}
diff --git a/src/stacks.h b/src/stacks.h
index a3b031c..d29aa88 100644
--- a/src/stacks.h
+++ b/src/stacks.h
@@ -40,6 +40,8 @@ void finish_preempt(void);
 int wait_preempt(void);
 void check_preempt(void);
 u32 call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret);
+u32 call64(void *page_table, void *func, u32 eax);
+void *gen_identity_page_table(u64 max_addr);
 
 // Inline functions
 
diff --git a/src/x86.h b/src/x86.h
index 234a6e2..8da4d74 100644
--- a/src/x86.h
+++ b/src/x86.h
@@ -218,6 +218,7 @@ static inline u8 readb(const void *addr) {
 // GDT bits
 #define GDT_CODE     (0x9bULL << 40) // Code segment - P,R,A bits also set
 #define GDT_DATA     (0x93ULL << 40) // Data segment - W,A bits also set
+#define GDT_L        (0X1ULL << 53)  // Long flag
 #define GDT_B        (0x1ULL << 54)  // Big flag
 #define GDT_G        (0x1ULL << 55)  // Granularity flag
 // GDT bits for segment base
-- 
2.4.3




More information about the SeaBIOS mailing list