Changeable kernel stacks
authorBarret Rhoden <brho@cs.berkeley.edu>
Sun, 17 Oct 2010 07:02:38 +0000 (00:02 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:55 +0000 (17:35 -0700)
We can now set and query the default kernel stack (the one you'll trap
in to).  On x86, this info is saved in the TSS.  On sparc, it's in an
array.  If that array gets a lot of cache traffic, we'll need to put it
in per_cpu_info, which is a minor pain.

kern/arch/i686/trap.c
kern/arch/i686/trap.h
kern/arch/sparc/entry.S
kern/arch/sparc/spillfill.S
kern/arch/sparc/trap.c
kern/arch/sparc/trap.h
kern/arch/sparc/trap_entry.S
kern/include/trap.h

index 7efd206..1305ba8 100644 (file)
@@ -74,6 +74,24 @@ static const char *NTS trapname(int trapno)
        return "(unknown trap)";
 }
 
+/* Set stacktop for the current core to be the stack the kernel will start on
+ * when trapping/interrupting from userspace.  Don't use this til after
+ * smp_percpu_init().  We can probably get the TSS by reading the task register
+ * and then the GDT.  Still, it's a pain. */
+void set_stack_top(uintptr_t stacktop)
+{
+       struct per_cpu_info *pcpu = &per_cpu_info[core_id()];
+       /* No need to reload the task register, this takes effect immediately */
+       pcpu->tss->ts_esp0 = stacktop;
+}
+
+/* Note the check implies we only are on a one page stack (or the first page) */
+uintptr_t get_stack_top(void)
+{
+       uintptr_t stacktop = per_cpu_info[core_id()].tss->ts_esp0;
+       assert(stacktop == ROUNDUP(read_esp(), PGSIZE));
+       return stacktop;
+}
 
 void
 idt_init(void)
index 1baf057..e422040 100644 (file)
@@ -82,6 +82,11 @@ static inline void restore_fp_state(struct ancillary_state *silly)
        asm volatile("fxrstor %0" : : "m"(*silly));
 }
 
+static inline void set_stack_pointer(uintptr_t sp)
+{
+       asm volatile("mov %0,%%esp" : : "r"(sp));
+}
+
 #endif /* !__ASSEMBLER__ */
 
 #endif /* !ROS_INC_ARCH_TRAP_H */
index 371214f..11244a3 100644 (file)
@@ -135,6 +135,16 @@ wait_for_reloc:
        bl      wait_for_reloc
         nop
 
+       // Set our stacktop so we can find it on a trap or spill/fill
+       mov     CORE_ID_REG,%l1
+       sll     %l1,KSTKSHIFT,%l1
+       set     bootstacktop,%l2
+       sub     %l2,%l1,%l1             // %l1 now holds our current stacktop
+       mov     CORE_ID_REG,%l0
+       sll     %l0,2,%l0
+       set     core_stacktops,%l2
+       st      %l1,[%l2 + %l0]         // store the stacktop in its slot
+
        // now it's safe to enable traps
        mov     %psr,%g1
        wr      %g1,PSR_ET,%psr
@@ -161,6 +171,10 @@ wait_for_reloc:
        // use a stack in the data section (as opposed to bss) here,
        // since kernel_init will zero the stack
        set     core0_bootstacktop-64,%sp
+       // reset core0's stacktop to core0_bootstacktop
+       set     core0_bootstacktop,%l1
+       set     core_stacktops,%l2
+       st      %l1,[%l2]               // store the stacktop in its slot
 
        sub     %sp,64,%sp              ! 64 >= sizeof(multiboot_header_t)
        call    build_multiboot_info
index 0608bce..5e18294 100644 (file)
@@ -5,10 +5,12 @@
 #include <arch/trap_table.h>
 
 #define MAKE_STACK \
-       set     bootstacktop - SIZEOF_TRAPFRAME_T - 64,%fp; \
-       mov     CORE_ID_REG,%l0; \
-       sll     %l0,KSTKSHIFT,%l0; \
-       sub     %fp,%l0,%fp
+       mov     CORE_ID_REG,%l1; \
+       sll     %l1,2,%l1; \
+       set     core_stacktops,%l2; \
+       ld      [%l2 + %l1],%l1; \
+       set     SIZEOF_TRAPFRAME_T + 64,%l2; \
+       sub     %l1,%l2,%fp
 
 ! preconditions:
 ! WIM & (1<<CWP) != 0
index 0a4b46a..445d3df 100644 (file)
 #pragma nodeputy
 #endif
 
+/* These are the stacks the kernel will load when it receives a trap from user
+ * space.  The deal is that they get set right away in entry.S, and can always
+ * be used for finding the top of the stack (from which you should subtract the
+ * sizeof the trapframe.  Note, we need to have a junk value in the array so
+ * that this is NOT part of the BSS.  If it is in the BSS, it will get 0'd in
+ * kernel_init(), which is after these values get set.
+ *
+ * TODO: if these end up becoming contended cache lines, move this to
+ * per_cpu_info. */
+uintptr_t core_stacktops[MAX_NUM_CPUS] = {0xcafebabe, 0};
+
 struct kmem_cache *kernel_msg_cache;
 void kernel_msg_init(void)
 {
@@ -70,6 +81,23 @@ advance_pc(trapframe_t* state)
        state->npc += 4;
 }
 
+/* Set stacktop for the current core to be the stack the kernel will start on
+ * when trapping/interrupting from userspace */
+void set_stack_top(uintptr_t stacktop)
+{
+       core_stacktops[core_id()] = stacktop;
+}
+
+/* Note the assertion assumes we are in the top page of the stack. */
+uintptr_t get_stack_top(void)
+{
+       uintptr_t sp, stacktop;
+       stacktop = core_stacktops[core_id()];
+       asm volatile("mov %%sp,%0" : "=r"(sp));
+       assert(ROUNDUP(sp, PGSIZE) == stacktop);
+       return stacktop;
+}
+
 void
 idt_init(void)
 {
index 03cf352..2b0163d 100644 (file)
@@ -8,8 +8,13 @@
 
 #include <ros/common.h>
 #include <ros/arch/trapframe.h>
+#include <arch/ros/arch.h>
 #include <arch/sparc.h>
 
+/* These are the stacks the kernel will load when it receives a trap from user
+ * space. */
+uintptr_t core_stacktops[MAX_NUM_CPUS];
+
 /* the struct trapframe and friends are in ros/arch/trapframe.h */
 
 void data_access_exception(trapframe_t* state);
@@ -26,6 +31,13 @@ static inline bool in_kernel(struct trapframe *tf)
        return tf->psr & PSR_PS;
 }
 
+/* Needs to leave room for a trapframe at the top of the stack. */
+static inline void set_stack_pointer(uintptr_t sp)
+{
+       sp = sp - SIZEOF_TRAPFRAME_T;
+       asm volatile("mov %0,%%sp" : : "r"(sp));
+}
+
 #endif /* !__ASSEMBLER__ */
 
 #endif /* !ROS_INC_ARCH_TRAP_H */
index edc1b7e..819e619 100644 (file)
@@ -94,10 +94,14 @@ handle_trap:
        RESTORE_MINIMAL_TF(%sp+96)
 
        // Trap came from user.  Spill a window if necessary.
+       // Set %l1 to be the stacktop for the current core
 1:     mov     CORE_ID_REG,%l1
-       sll     %l1,KSTKSHIFT,%l1
-       set     bootstacktop-SIZEOF_TRAPFRAME_T,%l2
-       sub     %l2,%l1,%l1
+       sll     %l1,2,%l1
+       set     core_stacktops,%l2
+       ld      [%l2 + %l1],%l1
+       set     SIZEOF_TRAPFRAME_T,%l2
+       sub     %l1,%l2,%l1
+
        SAVE_MINIMAL_TF(%l1)
        sub     %l1,96,%g1
        restore
index 041a0c6..05ca4d2 100644 (file)
@@ -42,6 +42,10 @@ extern void sysenter_handler();
 
 void save_fp_state(struct ancillary_state *silly);
 void restore_fp_state(struct ancillary_state *silly);
+/* Set stacktop for the current core to be the stack the kernel will start on
+ * when trapping/interrupting from userspace */
+void set_stack_top(uintptr_t stacktop);
+uintptr_t get_stack_top(void);
 
 /* Kernel messages.  Each arch implements them in their own way.  Both should be
  * guaranteeing in-order delivery.  Kept here in trap.h, since sparc is using