diff options
author | Richard Braun <rbraun@sceen.net> | 2017-06-13 23:26:38 +0200 |
---|---|---|
committer | Richard Braun <rbraun@sceen.net> | 2017-06-14 01:09:02 +0200 |
commit | 3350443b4a800a16f4663e0573903633ad82225f (patch) | |
tree | c1a7a23d0c7920fbbc3776d302e33197b3dd3af4 /arch/x86 | |
parent | 72ed0dc2f153e7cf1d6e96f86a773bbe490e9e1c (diff) |
x86: improve TCB load and context switch
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/machine/tcb.c | 62 | ||||
-rw-r--r-- | arch/x86/machine/tcb.h | 21 | ||||
-rw-r--r-- | arch/x86/machine/tcb_asm.S | 60 |
3 files changed, 80 insertions, 63 deletions
diff --git a/arch/x86/machine/tcb.c b/arch/x86/machine/tcb.c index df1b222..5d16ea2 100644 --- a/arch/x86/machine/tcb.c +++ b/arch/x86/machine/tcb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Richard Braun. + * Copyright (c) 2012-2017 Richard Braun. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,18 +24,58 @@ #include <machine/strace.h> #include <machine/tcb.h> -/* - * Low level functions. - */ void __noreturn tcb_context_load(struct tcb *tcb); void __noreturn tcb_start(void); +void tcb_context_restore(void); struct tcb *tcb_current_ptr __percpu; +static void +tcb_stack_push(struct tcb *tcb, uintptr_t word) +{ + uintptr_t *ptr; + + ptr = (uintptr_t *)tcb->sp; + ptr--; + *ptr = word; + tcb->sp = (uintptr_t)ptr; +} + +#ifdef __LP64__ + +static void +tcb_stack_forge(struct tcb *tcb, void (*fn)(void *), void *arg) +{ + tcb_stack_push(tcb, (uintptr_t)arg); + tcb_stack_push(tcb, (uintptr_t)fn); + tcb_stack_push(tcb, (uintptr_t)tcb_start); /* Return address */ + tcb_stack_push(tcb, CPU_EFL_ONE); /* RFLAGS */ + tcb_stack_push(tcb, 0); /* RBX */ + tcb_stack_push(tcb, 0); /* R12 */ + tcb_stack_push(tcb, 0); /* R13 */ + tcb_stack_push(tcb, 0); /* R14 */ + tcb_stack_push(tcb, 0); /* R15 */ +} + +#else /* __LP64__ */ + +static void +tcb_stack_forge(struct tcb *tcb, void (*fn)(void *), void *arg) +{ + tcb_stack_push(tcb, (uintptr_t)arg); + tcb_stack_push(tcb, (uintptr_t)fn); + tcb_stack_push(tcb, (uintptr_t)tcb_start); /* Return address */ + tcb_stack_push(tcb, CPU_EFL_ONE); /* EFLAGS */ + tcb_stack_push(tcb, 0); /* EBX */ + tcb_stack_push(tcb, 0); /* EDI */ + tcb_stack_push(tcb, 0); /* ESI */ +} + +#endif /* __LP64__ */ + int -tcb_init(struct tcb *tcb, void *stack, void (*fn)(void)) +tcb_init(struct tcb *tcb, void *stack, void (*fn)(void *), void *arg) { - void **ptr; int error; error = pmap_thread_init(thread_from_tcb(tcb)); @@ -45,12 +85,8 @@ tcb_init(struct tcb *tcb, void *stack, void (*fn)(void)) } tcb->bp = 0; - tcb->sp = (unsigned long)stack + STACK_SIZE - sizeof(unsigned long); - tcb->ip = (unsigned long)tcb_start; - - ptr = (void **)tcb->sp; - *ptr = fn; - + tcb->sp = (uintptr_t)stack + STACK_SIZE; + tcb_stack_forge(tcb, fn, arg); return 0; } @@ -66,5 +102,5 @@ tcb_load(struct tcb *tcb) void tcb_trace(const struct tcb *tcb) { - strace_show(tcb->ip, tcb->bp); + strace_show((uintptr_t)tcb_context_restore, tcb->bp); } diff --git a/arch/x86/machine/tcb.h b/arch/x86/machine/tcb.h index 914ec9d..0f67b76 100644 --- a/arch/x86/machine/tcb.h +++ b/arch/x86/machine/tcb.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Richard Braun. + * Copyright (c) 2012-2017 Richard Braun. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ #define _X86_TCB_H #include <assert.h> +#include <stdint.h> #include <kern/macros.h> #include <machine/cpu.h> @@ -30,25 +31,19 @@ * Thread control block. */ struct tcb { - unsigned long bp; - unsigned long sp; - unsigned long ip; + uintptr_t bp; + uintptr_t sp; }; /* * Initialize a TCB. * * Prepare the given stack for execution. The context is defined so that it - * will call fn() with interrupts disabled when loaded. + * will call thread_main(fn, arg) with interrupts disabled when loaded. * * In addition, initialize any thread-local machine-specific data. */ -int tcb_init(struct tcb *tcb, void *stack, void (*fn)(void)); - -/* - * Low level context switch function. - */ -void tcb_context_switch(struct tcb *prev, struct tcb *next); +int tcb_init(struct tcb *tcb, void *stack, void (*fn)(void *), void *arg); static inline struct tcb * tcb_current(void) @@ -79,6 +74,8 @@ void __noreturn tcb_load(struct tcb *tcb); static inline void tcb_switch(struct tcb *prev, struct tcb *next) { + void tcb_context_switch(struct tcb *prev, struct tcb *next); + assert(!cpu_intr_enabled()); tcb_set_current(next); @@ -88,7 +85,7 @@ tcb_switch(struct tcb *prev, struct tcb *next) /* * Dump the stack trace of a TCB. * - * The thread associated to the TCB should not be running. + * The thread associated to the TCB must not be running. */ void tcb_trace(const struct tcb *tcb); diff --git a/arch/x86/machine/tcb_asm.S b/arch/x86/machine/tcb_asm.S index e4e2c45..7c9140f 100644 --- a/arch/x86/machine/tcb_asm.S +++ b/arch/x86/machine/tcb_asm.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013 Richard Braun. + * Copyright (c) 2012-2017 Richard Braun. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,24 +24,18 @@ #ifdef __LP64__ ASM_ENTRY(tcb_context_load) - movq (%rdi), %rbp /* load frame pointer from TCB */ - movq 8(%rdi), %rsp /* load stack pointer from TCB */ - movq 16(%rdi), %rax /* load instruction pointer from TCB */ - pushq $CPU_EFL_ONE /* prepare new RFLAGS register value */ - popfq /* load value into RFLAGS register */ - jmp *%rax /* branch to loaded instruction pointer */ + movq (%rdi), %rbp /* load frame pointer from TCB */ + movq 8(%rdi), %rsp /* load stack pointer from TCB */ + jmp tcb_context_restore ASM_END(tcb_context_load) #else /* __LP64__ */ ASM_ENTRY(tcb_context_load) - movl 4(%esp), %eax /* load TCB address */ - movl (%eax), %ebp /* load frame pointer from TCB */ - movl 4(%eax), %esp /* load stack pointer from TCB */ - movl 8(%eax), %ecx /* load instruction pointer from TCB */ - pushl $CPU_EFL_ONE /* prepare new EFLAGS register value */ - popfl /* load value into EFLAGS register */ - jmp *%ecx /* branch to loaded instruction pointer */ + movl 4(%esp), %eax /* load TCB address */ + movl (%eax), %ebp /* load frame pointer from TCB */ + movl 4(%eax), %esp /* load stack pointer from TCB */ + jmp tcb_context_restore ASM_END(tcb_context_load) #endif /* __LP64__ */ @@ -51,13 +45,13 @@ ASM_END(tcb_context_load) #ifdef __LP64__ ASM_ENTRY(tcb_start) - popq %rax /* load function passed at TCB initialization (this - makes the stack pointer reach the stack top) */ - call *%rax /* branch to loaded function, pushing the return - address to start a clean stack trace */ + popq %rdi /* load function */ + popq %rsi /* load argument */ + call thread_main /* use the call instruction to start + a clean stack trace */ /* Never reached */ - nop /* Make the return address point to an instruction + nop /* make the return address point to an instruction inside the function to build a clean stack trace */ ASM_END(tcb_start) @@ -70,19 +64,12 @@ ASM_ENTRY(tcb_context_switch) pushq %r15 movq %rbp, (%rdi) /* store frame pointer into prev TCB */ movq %rsp, 8(%rdi) /* store stack pointer into prev TCB */ - movq $1f, 16(%rdi) /* store next instruction address into prev TCB */ movq (%rsi), %rbp /* load frame pointer from next TCB */ movq 8(%rsi), %rsp /* load stack pointer from next TCB */ - movq 16(%rsi), %rax /* load instruction pointer from next TCB */ - jmp *%rax /* branch to loaded instruction pointer */ -/* - * This code is run on context restoration. The frame and stack pointers - * have already been loaded to their correct values. Load registers which - * were stored on the stack when the context was saved and return. - */ -1: - popq %r15 +.global tcb_context_restore +tcb_context_restore: + popq %r15 /* load registers as required by ABI */ popq %r14 popq %r13 popq %r12 @@ -94,13 +81,12 @@ ASM_END(tcb_context_switch) #else /* __LP64__ */ ASM_ENTRY(tcb_start) - popl %eax /* load function passed at TCB initialization (this - makes the stack pointer reach the stack top) */ - call *%eax /* branch to loaded function, pushing the return - address to start a clean stack trace */ + call thread_main /* the stack already contains the expected arguments + in the expected order, use the call instruction to + start a clean stack trace */ /* Never reached */ - nop /* Make the return address point to an instruction + nop /* make the return address point to an instruction inside the function to build a clean stack trace */ ASM_END(tcb_start) @@ -113,18 +99,16 @@ ASM_ENTRY(tcb_context_switch) pushl %esi movl %ebp, (%eax) /* store frame pointer into prev TCB */ movl %esp, 4(%eax) /* store stack pointer into prev TCB */ - movl $1f, 8(%eax) /* store next instruction address into prev TCB */ movl (%ecx), %ebp /* load frame pointer from next TCB */ movl 4(%ecx), %esp /* load stack pointer from next TCB */ - movl 8(%ecx), %edx /* load instruction pointer from next TCB */ - jmp *%edx /* branch to loaded instruction pointer */ /* * This code is run on context restoration. The frame and stack pointers * have already been loaded to their correct values. Load registers which * were stored on the stack when the context was saved and return. */ -1: +.global tcb_context_restore +tcb_context_restore: popl %esi popl %edi popl %ebx |