blob: de969c439afaf8227492464856ae002d87e0e637 [file] [log] [blame] [edit]
#include <arch/arch.h>
#include <trap.h>
#include <process.h>
#include <pmap.h>
#include <smp.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
/* 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_sp();
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, uintptr_t tls_desc)
{
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.
* GD_UD is the user data segment selector in the GDT, and
* GD_UT is the user text segment selector (see inc/memlayout.h).
* The low 2 bits of each segment register contains the
* Requestor Privilege Level (RPL); 3 means user mode. */
tf->tf_ds = GD_UD | 3;
tf->tf_es = GD_UD | 3;
tf->tf_ss = GD_UD | 3;
tf->tf_esp = stack_top-64;
tf->tf_cs = GD_UT | 3;
/* set the env's EFLAGSs to have interrupts enabled */
tf->tf_eflags |= 0x00000200; // bit 9 is the interrupts-enabled
tf->tf_eip = entryp;
/* Coupled closely with user's entry.S. id is the vcoreid, which entry.S
* uses to determine what to do. vcoreid == 0 is the main core/context. */
tf->tf_regs.reg_eax = vcoreid;
/* Note we don't pass the tlsdesc. 32 bit TLS is pretty jacked up, so we
* let userspace deal with it. TODO: (TLSV) */
}
/* 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).
* the code protecting the kernel from TLS related things ought to be able
* to handle GPFs on popping gs. TODO: (TLSV) */
tf->tf_ds = GD_UD | 3;
tf->tf_es = GD_UD | 3;
tf->tf_fs = 0;
//tf->tf_gs = whatevs. ignoring this.
tf->tf_ss = GD_UD | 3;
tf->tf_cs ? GD_UT | 3 : 0; // can be 0 for sysenter TFs.
tf->tf_eflags |= 0x00000200; // bit 9 is the interrupts-enabled
}
/* Called when we are currently running an address space on our core and want to
* abandon it. We need a known good pgdir before releasing the old one. We
* decref, since current no longer tracks the proc (and current no longer
* protects the cr3). We also need to clear out the TLS registers (before
* unmapping the address space!) */
void __abandon_core(void)
{
struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
asm volatile ("movw %%ax,%%gs; lldt %%ax" :: "a"(0));
lcr3(boot_cr3);
proc_decref(pcpui->cur_proc);
pcpui->cur_proc = 0;
}