[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), ¶ms), 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