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.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2014, Paweł Dziepak, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _KERNEL_ARCH_X86_64_CPU_H
|
||||
#define _KERNEL_ARCH_X86_64_CPU_H
|
||||
|
||||
|
||||
#include <arch_thread_types.h>
|
||||
|
||||
|
||||
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
|
||||
@@ -13,12 +13,13 @@
|
||||
#ifndef _ASSEMBLER
|
||||
|
||||
#include <module.h>
|
||||
|
||||
#include <arch_thread_types.h>
|
||||
|
||||
#include <arch/x86/descriptors.h>
|
||||
|
||||
#ifdef __x86_64__
|
||||
# include <arch/x86/64/iframe.h>
|
||||
#else
|
||||
# include <arch/x86/32/iframe.h>
|
||||
# include <arch/x86/64/cpu.h>
|
||||
#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);
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
#define _KERNEL_ARCH_x86_THREAD_TYPES_H
|
||||
|
||||
|
||||
#include <arch_cpu.h>
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#ifdef __x86_64__
|
||||
# include <arch/x86/64/iframe.h>
|
||||
#else
|
||||
# include <arch/x86/32/iframe.h>
|
||||
#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];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<uintptr_t*>(_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<uintptr_t>(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;
|
||||
|
||||
Reference in New Issue
Block a user