Signed-off-by: Marc MarĂ markmb@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