From 88e8e24c84aded0ff085aa93402c71862c5e4fe0 Mon Sep 17 00:00:00 2001 From: Pawel Dziepak Date: Tue, 6 May 2014 21:15:55 +0200 Subject: [PATCH] kernel/x86_64: improve context switch implementation The goal of this patch is to amortize the cost of context switch by making the compiler aware that context switch clobbers all registers. Because all register need to be saved anyway there is no additional cost of using callee saved register in the function that does the context switch. --- headers/private/kernel/arch/x86/64/cpu.h | 32 ++++++++++++++++ headers/private/kernel/arch/x86/arch_cpu.h | 11 +++--- .../kernel/arch/x86/arch_thread_types.h | 13 +++++-- src/system/kernel/arch/x86/64/arch.S | 38 +++++-------------- src/system/kernel/arch/x86/64/thread.cpp | 22 +++-------- 5 files changed, 62 insertions(+), 54 deletions(-) create mode 100644 headers/private/kernel/arch/x86/64/cpu.h diff --git a/headers/private/kernel/arch/x86/64/cpu.h b/headers/private/kernel/arch/x86/64/cpu.h new file mode 100644 index 0000000000..273115914c --- /dev/null +++ b/headers/private/kernel/arch/x86/64/cpu.h @@ -0,0 +1,32 @@ +/* + * Copyright 2014, Paweł Dziepak, pdziepak@quarnos.org. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_ARCH_X86_64_CPU_H +#define _KERNEL_ARCH_X86_64_CPU_H + + +#include + + +static inline void +x86_context_switch(arch_thread* oldState, arch_thread* newState) +{ + asm volatile( + "pushq %%rbp;" + "movq $1f, %c[rip](%0);" + "movq %%rsp, %c[rsp](%0);" + "movq %c[rsp](%1), %%rsp;" + "jmp *%c[rip](%1);" + "1:" + "popq %%rbp;" + : + : "a" (oldState), "d" (newState), + [rsp] "i" (offsetof(arch_thread, current_stack)), + [rip] "i" (offsetof(arch_thread, instruction_pointer)) + : "rbx", "rcx", "rdi", "rsi", "r8", "r9", "r10", "r11", "r12", "r13", + "r14", "r15", "memory"); +} + + +#endif // _KERNEL_ARCH_X86_64_CPU_H diff --git a/headers/private/kernel/arch/x86/arch_cpu.h b/headers/private/kernel/arch/x86/arch_cpu.h index f745c50c6d..b59717f477 100644 --- a/headers/private/kernel/arch/x86/arch_cpu.h +++ b/headers/private/kernel/arch/x86/arch_cpu.h @@ -13,12 +13,13 @@ #ifndef _ASSEMBLER #include + +#include + #include #ifdef __x86_64__ -# include -#else -# include +# include #endif #endif // !_ASSEMBLER @@ -452,8 +453,6 @@ void __x86_setup_system_time(uint32 conversionFactor, uint32 conversionFactorNsecs, bool conversionFactorNsecsShift); #endif -void x86_context_switch(struct arch_thread* oldState, - struct arch_thread* newState); void x86_userspace_thread_exit(void); void x86_end_userspace_thread_exit(void); void x86_swap_pgdir(addr_t newPageDir); @@ -482,6 +481,8 @@ void x86_hardware_interrupt(iframe* frame); void x86_page_fault_exception(iframe* iframe); #ifndef __x86_64__ +void x86_context_switch(struct arch_thread* oldState, + struct arch_thread* newState); void x86_fnsave(void* fpuState); void x86_frstor(const void* fpuState); diff --git a/headers/private/kernel/arch/x86/arch_thread_types.h b/headers/private/kernel/arch/x86/arch_thread_types.h index 85dfc6bb08..f02ae41ad6 100644 --- a/headers/private/kernel/arch/x86/arch_thread_types.h +++ b/headers/private/kernel/arch/x86/arch_thread_types.h @@ -9,7 +9,13 @@ #define _KERNEL_ARCH_x86_THREAD_TYPES_H -#include +#include + +#ifdef __x86_64__ +# include +#else +# include +#endif namespace BKernel { @@ -40,7 +46,8 @@ struct arch_thread { uint64* syscall_rsp; uint64* user_rsp; - uint64* current_stack; + uintptr_t* current_stack; + uintptr_t instruction_pointer; #else struct farcall current_stack; struct farcall interrupt_stack; @@ -72,7 +79,7 @@ struct arch_fork_arg { inline addr_t arch_thread::GetFramePointer() const { - return current_stack[1]; + return current_stack[0]; } diff --git a/src/system/kernel/arch/x86/64/arch.S b/src/system/kernel/arch/x86/64/arch.S index 3f9b6d1e48..3553081c19 100644 --- a/src/system/kernel/arch/x86/64/arch.S +++ b/src/system/kernel/arch/x86/64/arch.S @@ -79,38 +79,18 @@ FUNCTION_END(x86_write_msr) /* void x86_64_thread_entry(); */ FUNCTION(x86_64_thread_entry): - movq %r15, %rdi - jmp *%r14 + xorq %rbp, %rbp + + movq %rsp, %rax + addq $16, %rsp + andq $0xfffffffffffffff0, %rsp + subq $8, %rsp + + movq 8(%rax), %rdi + jmp *(%rax) FUNCTION_END(x86_64_thread_entry) -/* void x86_context_switch(struct arch_thread* oldState, - struct arch_thread* newState); */ -FUNCTION(x86_context_switch): - // Just need to save callee-save registers: RBP, RBX, R12-15. - push %r15 - push %r14 - push %r13 - push %r12 - push %rbp - push %rbx - - // Swap the stack pointers. - movq %rsp, ARCH_THREAD_current_stack(%rdi) - movq ARCH_THREAD_current_stack(%rsi), %rsp - - // Restore callee-save registers. - pop %rbx - pop %rbp - pop %r12 - pop %r13 - pop %r14 - pop %r15 - - ret -FUNCTION_END(x86_context_switch) - - /* void x86_swap_pgdir(uint64 newPageDir); */ FUNCTION(x86_swap_pgdir): movq %rdi, %cr3 diff --git a/src/system/kernel/arch/x86/64/thread.cpp b/src/system/kernel/arch/x86/64/thread.cpp index 5ee839a711..71da974abf 100644 --- a/src/system/kernel/arch/x86/64/thread.cpp +++ b/src/system/kernel/arch/x86/64/thread.cpp @@ -165,7 +165,7 @@ void arch_thread_init_kthread_stack(Thread* thread, void* _stack, void* _stackTop, void (*function)(void*), const void* data) { - addr_t* stackTop = (addr_t*)_stackTop; + uintptr_t* stackTop = static_cast(_stackTop); TRACE("arch_thread_init_kthread_stack: stack top %p, function %p, data: " "%p\n", _stackTop, function, data); @@ -173,23 +173,11 @@ arch_thread_init_kthread_stack(Thread* thread, void* _stack, void* _stackTop, // Save the stack top for system call entry. thread->arch_info.syscall_rsp = (uint64*)thread->kernel_stack_top; - // x86_64 uses registers for argument passing, first argument in RDI, - // however we don't save RDI on every context switch (there is no need - // for us to: it is not callee-save, and only contains the first argument - // to x86_context_switch). However, this presents a problem since we - // cannot store the argument for the entry function here. Therefore, we - // save the function address in R14 and the argument in R15 (which are - // restored), and then set up the stack to initially call a wrapper - // function which passes the argument correctly. + thread->arch_info.instruction_pointer + = reinterpret_cast(x86_64_thread_entry); - *--stackTop = 0; // Dummy return address. - *--stackTop = (addr_t)x86_64_thread_entry; // Wrapper function. - *--stackTop = (addr_t)data; // R15: argument. - *--stackTop = (addr_t)function; // R14: entry function. - *--stackTop = 0; // R13. - *--stackTop = 0; // R12. - *--stackTop = 0; // RBP. - *--stackTop = 0; // RBX. + *--stackTop = uintptr_t(data); + *--stackTop = uintptr_t(function); // Save the stack position. thread->arch_info.current_stack = stackTop;