Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/30118
Change subject: arch/x86/boot: Call payload in protected mode ......................................................................
arch/x86/boot: Call payload in protected mode
On ARCH_RAMSTAGE_X86_64 call the payload in protected mode. Add a helper function to call arbitraty code in protected mode, similar to the real mode call handler.
Tested using SeaBios as payload. Untested for anything else.
Change-Id: I6552ac30f1b6205e08e16d251328e01ce3fbfd14 Signed-off-by: Patrick Rudolph siro@das-labor.org --- M src/arch/x86/boot.c M src/arch/x86/c_start.S M src/include/program_loading.h 3 files changed, 186 insertions(+), 10 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/18/30118/1
diff --git a/src/arch/x86/boot.c b/src/arch/x86/boot.c index 2967cf6..cb9f34b 100644 --- a/src/arch/x86/boot.c +++ b/src/arch/x86/boot.c @@ -32,13 +32,20 @@
void arch_prog_run(struct prog *prog) { - __asm__ volatile ( -#ifdef __x86_64__ - "jmp *%%rdi\n" +#ifdef __ARCH_x86_64__ +#if ENV_RAMSTAGE + protected_mode_call(false, (uintptr_t)prog_entry(prog), + (uintptr_t)prog_entry_arg(prog)); #else - "jmp *%%edi\n" -#endif - + __asm__ volatile ( + "jmp *%%rdi\n" :: "D"(prog_entry(prog)) ); +#endif +#else + __asm__ volatile ( + "jmp *%%edi\n" + :: "D"(prog_entry(prog)) + ); +#endif } diff --git a/src/arch/x86/c_start.S b/src/arch/x86/c_start.S index 6426ef3..8148e58 100644 --- a/src/arch/x86/c_start.S +++ b/src/arch/x86/c_start.S @@ -50,7 +50,7 @@ movl %eax, %gs #ifdef __x86_64__ mov $0x48, %ecx - call SetCodeSelector + call SetCodeSelector64 #endif
post_code(POST_ENTRY_C_START) /* post 13 */ @@ -207,7 +207,7 @@
.section ".text._start", "ax", @progbits #ifdef __x86_64__ -SetCodeSelector: +SetCodeSelector64: # save rsp because iret will align it to a 16 byte boundary mov %rsp, %rdx
@@ -219,14 +219,14 @@ push %rsp pushfq push %rcx # cx is code segment selector from caller - mov $setCodeSelectorLongJump, %rax + mov $setCodeSelectorLongJump64, %rax push %rax
# the iret will continue at next instruction, with the new cs value # loaded iretq
-setCodeSelectorLongJump: +setCodeSelectorLongJump64: # restore rsp, it might not have been 16-byte aligned on entry mov %rdx, %rsp ret @@ -237,3 +237,161 @@ .previous .code32 #endif + +#ifdef __ARCH_x86_64__ + + /* + * Functions to handle mode switches from longmode to protected + * mode. Similar to realmode switches. + */ + .section .bss, "aw", @nobits + + .section ".text._mode_switch", "ax", @progbits +.code64 + + .globl protected_mode_call +protected_mode_call: + + push %rbp + mov %rsp, %rbp + + /* Preserve registers */ + push %rbx + push %r12 + push %r13 + push %r14 + push %r15 + + /* Arguments to stack */ + push %rdi + push %rsi + push %rdx + + /* Ensure cache is clean. */ + invd + + /* Running in compatibility mode? */ + mov -48(%rbp), %rax + test %rax, %rax + jne 1f + + /* Disable paging */ + mov %cr0, %rax + and $0x7FFFFFFF, %rax + mov %rax, %cr0 + + /* Disable long mode */ + mov $0xC0000080, %rcx + rdmsr + and $(~0x100), %rax + wrmsr + + /* Disable PAE */ + mov %cr4, %rax + and $(~0x20), %rax + mov %rax, %cr4 +1: + /* Set 32-bit code segment and ss */ + mov $0x10, %rcx + call SetCodeSelector32 + +.code32 + /* Use flat 32-bit data segment. */ + movl $0x18, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + movl -56(%ebp), %eax /* Function to call */ + movl -64(%ebp), %ebx /* Argument 0 */ + + /* Align the stack */ + andl $0xFFFFFFF0, %esp + subl $12, %esp + pushl %ebx /* Argument 0 */ + + call *%eax + + pushl %eax + pushl $0 + + /* Running in compatibility mode? */ + mov -48(%ebp), %eax + test %eax, %eax + jne 1f + + /* Enable PAE */ + mov %cr4, %eax + or $0x20, %eax + mov %eax, %cr4 + + /* Enable long mode */ + mov $0xC0000080, %ecx + rdmsr + or $0x100, %eax + wrmsr + + /* Enable paging */ + mov %cr0, %eax + or $0x80000000, %eax + mov %eax, %cr0 + + /* Just to be sure ... */ + lgdt %cs:gdtaddr +1: + /* Back to long mode */ + ljmp $0x48, $1f +.code64 +1: movl $0x18, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + /* Place return value in rax */ + pop %rax + + /* Restore registers */ + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %rbx + + /* Restore stack pointer */ + mov %rbp, %rsp + pop %rbp + + ret + +.code64 +SetCodeSelector32: + # save rsp because iret will align it to a 16 byte boundary + mov %rsp, %rdx + + # use iret to jump to a 32-bit offset in a new code segment + # iret will pop cs:rip, flags, then ss:rsp + mov %ss, %ax # need to push ss.. + push %rax # push ss instuction not valid in x64 mode, + # so use ax + push %rsp + pushfq + push %rcx # cx is code segment selector from caller + mov $setCodeSelectorLongJump32, %rax + push %rax + + # the iret will continue at next instruction, with the new cs value + # loaded + iretq + +.code32 +setCodeSelectorLongJump32: + # restore esp, it might not have been 16-byte aligned on entry + mov %edx, %esp + ret + + .previous +#endif diff --git a/src/include/program_loading.h b/src/include/program_loading.h index 468f0b3..84e5194 100644 --- a/src/include/program_loading.h +++ b/src/include/program_loading.h @@ -182,6 +182,17 @@ * if ramstage overwrites low memory. */ void backup_ramstage_section(uintptr_t base, size_t size);
+/* Run function in protected mode. + * @arg compatibility_mode Use 32-bit compatibility mode instead of + * protected mode + * @arg func_ptr Function to call in protected mode + * @arg Argument to pass to called function + * + * @return The called function returned value +*/ +uint32_t protected_mode_call(bool compatibility_mode, uintptr_t func_ptr, + uint32_t argument); + /*********************** * PAYLOAD LOADING * ***********************/