summaryrefslogtreecommitdiff
path: root/arch/x86
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2017-06-13 23:26:38 +0200
committerRichard Braun <rbraun@sceen.net>2017-06-14 01:09:02 +0200
commit3350443b4a800a16f4663e0573903633ad82225f (patch)
treec1a7a23d0c7920fbbc3776d302e33197b3dd3af4 /arch/x86
parent72ed0dc2f153e7cf1d6e96f86a773bbe490e9e1c (diff)
x86: improve TCB load and context switch
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/machine/tcb.c62
-rw-r--r--arch/x86/machine/tcb.h21
-rw-r--r--arch/x86/machine/tcb_asm.S60
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