Kernel arch-dependent functions use user_contexts
authorBarret Rhoden <brho@cs.berkeley.edu>
Sat, 13 Apr 2013 00:57:52 +0000 (17:57 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Sat, 13 Apr 2013 01:05:31 +0000 (18:05 -0700)
It's up to each arch to determine how they will use the HW and SW TFs in
the user_context, including callee-saved FP state.

Just to help with bugs, I put some asserts in each arch's pop_ctx.

kern/arch/i686/env.c
kern/arch/i686/process.c
kern/arch/riscv/process.c
kern/arch/riscv/trap.c
kern/arch/sparc/process.c
kern/include/env.h
kern/include/process.h
kern/src/elf.c
kern/src/process.c

index 7c48e45..d1da684 100644 (file)
@@ -9,84 +9,6 @@
 #include <pmap.h>
 #include <smp.h>
 
-//
-// This exits the kernel and starts executing some environment's code.
-// This function does not return.
-// Uses 'iret' or 'sysexit' depending on CS.
-//
-void env_pop_tf(trapframe_t *tf)
-{
-       /* Bug with this whole idea (TODO: (TLSV))*/
-       /* Load the LDT for this process.  Slightly ghetto doing it here. */
-       /* copy-in and check the LDT location.  the segmentation hardware writes the
-        * accessed bit, so we want the memory to be in the user-writeable area. */
-       segdesc_t *ldt = current->procdata->ldt;
-       ldt = (segdesc_t*)MIN((uintptr_t)ldt, UWLIM - LDT_SIZE);
-       /* Only set up the ldt if a pointer to the ldt actually exists */
-       if(ldt != NULL) {
-               segdesc_t *my_gdt = per_cpu_info[core_id()].gdt;
-               segdesc_t ldt_temp = SEG_SYS(STS_LDT, (uint32_t)ldt, LDT_SIZE, 3);
-               my_gdt[GD_LDT >> 3] = ldt_temp;
-               asm volatile("lldt %%ax" :: "a"(GD_LDT));
-       }
-
-       /* In case they are enabled elsewhere.  We can't take an interrupt in these
-        * routines, due to how they play with the kernel stack pointer. */
-       disable_irq();
-       /*
-        * If the process entered the kernel via sysenter, we need to leave via
-        * sysexit.  sysenter trapframes have 0 for a CS, which is pushed in
-        * sysenter_handler.
-        */
-       if(tf->tf_cs) {
-               /*
-                * Restores the register values in the Trapframe with the 'iret'
-                * instruction.  This exits the kernel and starts executing some
-                * environment's code.  This function does not return.
-                */
-               asm volatile ("movl %0,%%esp;           "
-                             "popal;                   "
-                             "popl %%gs;               "
-                             "popl %%fs;               "
-                             "popl %%es;               "
-                             "popl %%ds;               "
-                             "addl $0x8,%%esp;         "
-                             "iret                     "
-                             : : "g" (tf) : "memory");
-               panic("iret failed");  /* mostly to placate the compiler */
-       } else {
-               /* Return path of sysexit.  See sysenter_handler's asm for details.
-                * One difference is that this tf could be somewhere other than a stack
-                * (like in a struct proc).  We need to make sure esp is valid once
-                * interrupts are turned on (which would happen on popfl normally), so
-                * we need to save and restore a decent esp (the current one).  We need
-                * a place to save it that is accessible after we change the stack
-                * pointer to the tf *and* that is specific to this core/instance of
-                * sysexit.  The simplest and nicest is to use the tf_esp, which we
-                * can just pop.  Incidentally, the value in oesp would work too.
-                * To prevent popfl from turning interrupts on, we hack the tf's eflags
-                * so that we have a chance to change esp to a good value before
-                * interrupts are enabled.  The other option would be to throw away the
-                * eflags, but that's less desirable. */
-               tf->tf_eflags &= !FL_IF;
-               tf->tf_esp = read_esp();
-               asm volatile ("movl %0,%%esp;           "
-                             "popal;                   "
-                             "popl %%gs;               "
-                             "popl %%fs;               "
-                             "popl %%es;               "
-                             "popl %%ds;               "
-                             "addl $0x10,%%esp;        "
-                             "popfl;                   "
-                             "movl %%ebp,%%ecx;        "
-                             "popl %%esp;              "
-                             "sti;                     "
-                             "sysexit                  "
-                             : : "g" (tf) : "memory");
-               panic("sysexit failed");  /* mostly to placate your mom */
-       }
-}
-
 /* Walks len bytes from start, executing 'callback' on every PTE, passing it a
  * specific VA and whatever arg is passed in.  Note, this cannot handle jumbo
  * pages. */
index c5164c3..af2cd57 100644 (file)
@@ -8,9 +8,90 @@
 #include <assert.h>
 #include <stdio.h>
 
-void proc_init_trapframe(trapframe_t *tf, uint32_t vcoreid,
-                         uintptr_t entryp, uintptr_t stack_top)
+/* TODO: handle user and kernel contexts */
+void proc_pop_ctx(struct user_context *ctx)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       assert(ctx->type = ROS_HW_CTX);
+
+       /* Bug with this whole idea (TODO: (TLSV))*/
+       /* Load the LDT for this process.  Slightly ghetto doing it here. */
+       /* copy-in and check the LDT location.  the segmentation hardware writes the
+        * accessed bit, so we want the memory to be in the user-writeable area. */
+       segdesc_t *ldt = current->procdata->ldt;
+       ldt = (segdesc_t*)MIN((uintptr_t)ldt, UWLIM - LDT_SIZE);
+       /* Only set up the ldt if a pointer to the ldt actually exists */
+       if(ldt != NULL) {
+               segdesc_t *my_gdt = per_cpu_info[core_id()].gdt;
+               segdesc_t ldt_temp = SEG_SYS(STS_LDT, (uint32_t)ldt, LDT_SIZE, 3);
+               my_gdt[GD_LDT >> 3] = ldt_temp;
+               asm volatile("lldt %%ax" :: "a"(GD_LDT));
+       }
+
+       /* In case they are enabled elsewhere.  We can't take an interrupt in these
+        * routines, due to how they play with the kernel stack pointer. */
+       disable_irq();
+       /*
+        * If the process entered the kernel via sysenter, we need to leave via
+        * sysexit.  sysenter trapframes have 0 for a CS, which is pushed in
+        * sysenter_handler.
+        */
+       if(tf->tf_cs) {
+               /*
+                * Restores the register values in the Trapframe with the 'iret'
+                * instruction.  This exits the kernel and starts executing some
+                * environment's code.  This function does not return.
+                */
+               asm volatile ("movl %0,%%esp;           "
+                             "popal;                   "
+                             "popl %%gs;               "
+                             "popl %%fs;               "
+                             "popl %%es;               "
+                             "popl %%ds;               "
+                             "addl $0x8,%%esp;         "
+                             "iret                     "
+                             : : "g" (tf) : "memory");
+               panic("iret failed");  /* mostly to placate the compiler */
+       } else {
+               /* Return path of sysexit.  See sysenter_handler's asm for details.
+                * One difference is that this tf could be somewhere other than a stack
+                * (like in a struct proc).  We need to make sure esp is valid once
+                * interrupts are turned on (which would happen on popfl normally), so
+                * we need to save and restore a decent esp (the current one).  We need
+                * a place to save it that is accessible after we change the stack
+                * pointer to the tf *and* that is specific to this core/instance of
+                * sysexit.  The simplest and nicest is to use the tf_esp, which we
+                * can just pop.  Incidentally, the value in oesp would work too.
+                * To prevent popfl from turning interrupts on, we hack the tf's eflags
+                * so that we have a chance to change esp to a good value before
+                * interrupts are enabled.  The other option would be to throw away the
+                * eflags, but that's less desirable. */
+               tf->tf_eflags &= !FL_IF;
+               tf->tf_esp = read_esp();
+               asm volatile ("movl %0,%%esp;           "
+                             "popal;                   "
+                             "popl %%gs;               "
+                             "popl %%fs;               "
+                             "popl %%es;               "
+                             "popl %%ds;               "
+                             "addl $0x10,%%esp;        "
+                             "popfl;                   "
+                             "movl %%ebp,%%ecx;        "
+                             "popl %%esp;              "
+                             "sti;                     "
+                             "sysexit                  "
+                             : : "g" (tf) : "memory");
+               panic("sysexit failed");  /* mostly to placate your mom */
+       }
+}
+
+/* TODO: consider using a SW context */
+void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
+                   uintptr_t stack_top)
+{
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       ctx->type = ROS_HW_CTX;
+
        memset(tf,0,sizeof(*tf));
 
        /* Set up appropriate initial values for the segment registers.
@@ -33,8 +114,11 @@ void proc_init_trapframe(trapframe_t *tf, uint32_t vcoreid,
        tf->tf_regs.reg_eax = vcoreid;
 }
 
-void proc_secure_trapframe(struct trapframe *tf)
+/* TODO: handle both HW and SW contexts */
+void proc_secure_ctx(struct user_context *ctx)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       ctx->type = ROS_HW_CTX;
        /* we normally don't need to set the non-CS regs, but they could be
         * gibberish and cause a GPF.  gs can still be gibberish, but we don't
         * necessarily know what it ought to be (we could check, but that's a pain).
index a4a0a29..0f09b36 100644 (file)
@@ -8,9 +8,22 @@
 #include <assert.h>
 #include <stdio.h>
 
-void proc_init_trapframe(trapframe_t *tf, uint32_t vcoreid,
-                         uintptr_t entryp, uintptr_t stack_top)
+/* TODO: handle user and kernel contexts */
+void proc_pop_ctx(struct user_context *ctx)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       assert(ctx->type = ROS_HW_CTX);
+       extern void env_pop_tf(struct hw_trapframe *tf);        /* in asm */
+       env_pop_tf(tf);
+}
+
+/* TODO: consider using a SW context */
+void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
+                   uintptr_t stack_top)
+{
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       ctx->type = ROS_HW_CTX;
+
        memset(tf, 0, sizeof(*tf));
 
        tf->gpr[30] = stack_top-64;
@@ -23,8 +36,11 @@ void proc_init_trapframe(trapframe_t *tf, uint32_t vcoreid,
        tf->gpr[4] = vcoreid;
 }
 
-void proc_secure_trapframe(struct trapframe *tf)
+/* TODO: handle both HW and SW contexts */
+void proc_secure_ctx(struct user_context *ctx)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       ctx->type = ROS_HW_CTX;
        tf->sr = SR_U64;
 }
 
index feb63a9..a50c2af 100644 (file)
@@ -276,6 +276,7 @@ handle_fp_disabled(struct hw_trapframe *hw_tf)
                panic("kernel executed an FP instruction!");
 
        hw_tf->sr |= SR_EF;
+       extern void env_pop_tf(struct hw_trapframe *tf);        /* in asm */
        env_pop_tf(hw_tf); /* We didn't save our TF, so don't proc_restartcore */
 }
 
@@ -350,6 +351,7 @@ handle_trap(struct hw_trapframe *hw_tf)
                }
        }
        
+       extern void env_pop_tf(struct hw_trapframe *tf);        /* in asm */
        /* Return to the current process, which should be runnable.  If we're the
         * kernel, we should just return naturally.  Note that current and tf need
         * to still be okay (might not be after blocking) */
index 5a23224..a4d7d81 100644 (file)
 #pragma nosharc
 #endif
 
-void
-proc_init_trapframe(trapframe_t *tf, uint32_t vcoreid,
-                    uintptr_t entryp, uintptr_t stack_top)
+/* TODO: handle user and kernel contexts */
+void proc_pop_ctx(struct user_context *ctx)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       assert(ctx->type = ROS_HW_CTX);
+       extern void env_pop_tf(struct hw_trapframe *tf);        /* in asm */
+       env_pop_tf(tf);
+}
+
+/* TODO: consider using a SW context */
+void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
+                   uintptr_t stack_top)
+{
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       ctx->type = ROS_HW_CTX;
+
        memset(tf,0,sizeof(*tf));
 
        tf->psr = PSR_S; // but PS = 0
@@ -28,8 +40,11 @@ proc_init_trapframe(trapframe_t *tf, uint32_t vcoreid,
        tf->gpr[6] = vcoreid;
 }
 
-void proc_secure_trapframe(struct trapframe *tf)
+/* TODO: handle both HW and SW contexts */
+void proc_secure_ctx(struct user_context *ctx)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       ctx->type = ROS_HW_CTX;
        // only take the condition codes from the user.  we set S=1, PS=0
        tf->psr = (tf->psr & PSR_ICC) | PSR_S;
 }
index 0df0724..642c1f5 100644 (file)
@@ -107,7 +107,4 @@ void        env_pagetable_free(env_t* e);
 typedef int (*mem_walk_callback_t)(env_t* e, pte_t* pte, void* va, void* arg);
 int            env_user_mem_walk(env_t* e, void* start, size_t len, mem_walk_callback_t callback, void* arg);
 
-// The following three functions do not return
-void   env_pop_tf(trapframe_t *tf) __attribute__((noreturn));
-
 #endif // !ROS_KERN_ENV_H
index 7c4c09d..042ed69 100644 (file)
@@ -136,9 +136,10 @@ void __death(uint32_t srcid, long a0, long a1, long a2);
 void __tlbshootdown(uint32_t srcid, long a0, long a1, long a2);
 
 /* Arch Specific */
-void proc_init_trapframe(trapframe_t *SAFE tf, uint32_t vcoreid,
-                         uintptr_t entryp, uintptr_t stack_top);
-void proc_secure_trapframe(struct trapframe *tf);
+void proc_pop_ctx(struct user_context *ctx) __attribute__((noreturn));
+void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
+                   uintptr_t stack_top);
+void proc_secure_ctx(struct user_context *ctx);
 void __abandon_core(void);
 
 /* Degubbing */
index d4f79a6..d7e03db 100644 (file)
@@ -256,7 +256,7 @@ int load_elf(struct proc* p, struct file* f)
        memcpy(p->procinfo->argp+auxp_pos,auxp,sizeof(auxp));
 
        uintptr_t core0_entry = ei.dynamic ? interp_ei.entry : ei.entry;
-       proc_init_trapframe(&p->scp_ctx.tf.hw_tf, 0, core0_entry, USTACKTOP);
+       proc_init_ctx(&p->scp_ctx, 0, core0_entry, USTACKTOP);
        p->env_entry = ei.entry;
 
        int flags = MAP_FIXED | MAP_ANONYMOUS;
index b719b2b..00f46a1 100644 (file)
@@ -493,8 +493,8 @@ void proc_run_s(struct proc *p)
                                vcpd->notif_tf = p->scp_ctx.tf.hw_tf;   /* TODO CTX */
                                pcpui->cur_ctx = &pcpui->actual_ctx;
                                memset(pcpui->cur_ctx, 0, sizeof(struct user_context));
-                               proc_init_trapframe(&pcpui->cur_ctx->tf.hw_tf, 0, p->env_entry,
-                                                   vcpd->transition_stack);
+                               proc_init_ctx(pcpui->cur_ctx, 0, p->env_entry,
+                                             vcpd->transition_stack);
                        } else {
                                /* If they have no transition stack, then they can't receive
                                 * events.  The most they are getting is a wakeup from the
@@ -629,7 +629,7 @@ static void __proc_startcore(struct proc *p, struct user_context *ctx)
                env_pop_ancillary_state(p);
        /* Clear the current_ctx, since it is no longer used */
        current_ctx = 0;        /* TODO: might not need this... */
-       env_pop_tf(&ctx->tf.hw_tf); /* TODO CTX */
+       proc_pop_ctx(ctx);
 }
 
 /* Restarts/runs the current_ctx, which must be for the current process, on the
@@ -1709,12 +1709,12 @@ static void __set_curctx_to_vcoreid(struct proc *p, uint32_t vcoreid,
                restore_fp_state(&vcpd->preempt_anc);
                /* copy-in the tf we'll pop, then set all security-related fields */
                pcpui->actual_ctx.tf.hw_tf = vcpd->preempt_tf;  /* TODO CTX */
-               proc_secure_trapframe(&pcpui->actual_ctx.tf.hw_tf);
+               proc_secure_ctx(&pcpui->actual_ctx);
        } else { /* not restarting from a preemption, use a fresh vcore */
                assert(vcpd->transition_stack);
                /* TODO: consider 0'ing the FP state.  We're probably leaking. */
-               proc_init_trapframe(&pcpui->actual_ctx.tf.hw_tf, vcoreid, p->env_entry,
-                                   vcpd->transition_stack);
+               proc_init_ctx(&pcpui->actual_ctx, vcoreid, p->env_entry,
+                             vcpd->transition_stack);
                /* Disable/mask active notifications for fresh vcores */
                vcpd->notif_disabled = TRUE;
        }
@@ -1927,8 +1927,8 @@ void __notify(uint32_t srcid, long a0, long a1, long a2)
         * silly state isn't our business for a notification. */
        vcpd->notif_tf = pcpui->cur_ctx->tf.hw_tf; /* TODO CTX */
        memset(pcpui->cur_ctx, 0, sizeof(struct user_context));
-       proc_init_trapframe(&pcpui->cur_ctx->tf.hw_tf, vcoreid, p->env_entry,
-                           vcpd->transition_stack);
+       proc_init_ctx(pcpui->cur_ctx, vcoreid, p->env_entry,
+                     vcpd->transition_stack);
        /* this cur_ctx will get run when the kernel returns / idles */
 }