| #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> |
| |
| void proc_pop_ctx(struct user_context *ctx) |
| { |
| disable_irq(); |
| /* for both HW and SW, note we pass an offset into the TF, beyond the fs and |
| * gs bases */ |
| if (ctx->type == ROS_HW_CTX) { |
| struct hw_trapframe *tf = &ctx->tf.hw_tf; |
| write_msr(MSR_GS_BASE, (uint64_t)tf->tf_gsbase); |
| write_msr(MSR_FS_BASE, (uint64_t)tf->tf_fsbase); |
| asm volatile ("movq %0, %%rsp; " |
| "popq %%rax; " |
| "popq %%rbx; " |
| "popq %%rcx; " |
| "popq %%rdx; " |
| "popq %%rbp; " |
| "popq %%rsi; " |
| "popq %%rdi; " |
| "popq %%r8; " |
| "popq %%r9; " |
| "popq %%r10; " |
| "popq %%r11; " |
| "popq %%r12; " |
| "popq %%r13; " |
| "popq %%r14; " |
| "popq %%r15; " |
| "addq $0x10, %%rsp; " |
| "iretq " |
| : : "g" (&tf->tf_rax) : "memory"); |
| panic("iretq failed"); |
| } else { |
| struct sw_trapframe *tf = &ctx->tf.sw_tf; |
| write_msr(MSR_GS_BASE, (uint64_t)tf->tf_gsbase); |
| write_msr(MSR_FS_BASE, (uint64_t)tf->tf_fsbase); |
| /* We need to 0 out any registers that aren't part of the sw_tf and that |
| * we won't use/clobber on the out-path. While these aren't part of the |
| * sw_tf, we also don't want to leak any kernel register content. */ |
| asm volatile ("movq %0, %%rsp; " |
| "movq $0, %%rax; " |
| "movq $0, %%rdx; " |
| "movq $0, %%rsi; " |
| "movq $0, %%rdi; " |
| "movq $0, %%r8; " |
| "movq $0, %%r9; " |
| "movq $0, %%r10; " |
| "popq %%rbx; " |
| "popq %%rbp; " |
| "popq %%r12; " |
| "popq %%r13; " |
| "popq %%r14; " |
| "popq %%r15; " |
| "movq %1, %%r11; " |
| "popq %%rcx; " |
| "popq %%rsp; " |
| "rex.w sysret " |
| : : "g"(&tf->tf_rbx), "i"(FL_IF) : "memory"); |
| panic("sysret failed"); |
| } |
| panic("Unknown context type!\n"); |
| } |
| |
| /* Helper: if *addr isn't a canonical user address, poison it. Use this when |
| * you need a canonical address (like MSR_FS_BASE) */ |
| static void enforce_user_canon(uintptr_t *addr) |
| { |
| if (*addr >> 47 != 0) |
| *addr = 0x5a5a5a5a; |
| } |
| |
| /* 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_ss = GD_UD | 3; |
| /* Stack pointers in a fresh stackframe need to be such that adding or |
| * subtracting 8 will result in 16 byte alignment (AMD64 ABI). The reason |
| * is so that input arguments (on the stack) are 16 byte aligned. The |
| * extra 8 bytes is the retaddr, pushed on the stack. Compilers know they |
| * can subtract 8 to get 16 byte alignment for instructions like movaps. */ |
| tf->tf_rsp = ROUNDDOWN(stack_top, 16) - 8; |
| tf->tf_cs = GD_UT | 3; |
| /* set the env's EFLAGSs to have interrupts enabled */ |
| tf->tf_rflags |= 0x00000200; // bit 9 is the interrupts-enabled |
| tf->tf_rip = 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_rax = vcoreid; |
| tf->tf_fsbase = tls_desc; |
| enforce_user_canon(&tf->tf_fsbase); |
| } |
| |
| void proc_secure_ctx(struct user_context *ctx) |
| { |
| if (ctx->type == ROS_SW_CTX) { |
| struct sw_trapframe *tf = &ctx->tf.sw_tf; |
| enforce_user_canon(&tf->tf_gsbase); |
| enforce_user_canon(&tf->tf_fsbase); |
| } else { |
| /* If we aren't SW, we're assuming (and forcing) a HW ctx. If this is |
| * somehow fucked up, userspace should die rather quickly. */ |
| struct hw_trapframe *tf = &ctx->tf.hw_tf; |
| ctx->type = ROS_HW_CTX; |
| enforce_user_canon(&tf->tf_gsbase); |
| enforce_user_canon(&tf->tf_fsbase); |
| tf->tf_ss = GD_UD | 3; |
| tf->tf_cs = GD_UT | 3; |
| tf->tf_rflags |= 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). */ |
| void __abandon_core(void) |
| { |
| struct per_cpu_info *pcpui = &per_cpu_info[core_id()]; |
| lcr3(boot_cr3); |
| proc_decref(pcpui->cur_proc); |
| pcpui->cur_proc = 0; |
| } |