cddee5eab2c2f0becdea7588c67f4c8467c2f1e7
[akaros.git] / kern / arch / x86 / process64.c
1 #include <arch/arch.h>
2 #include <trap.h>
3 #include <process.h>
4 #include <pmap.h>
5 #include <smp.h>
6
7 #include <string.h>
8 #include <assert.h>
9 #include <stdio.h>
10
11 static void __attribute__((noreturn)) proc_pop_hwtf(struct hw_trapframe *tf)
12 {
13         /* for both HW and SW, note we pass an offset into the TF, beyond the fs and
14          * gs bases */
15         if (x86_hwtf_is_partial(tf)) {
16                 swap_gs();
17         } else {
18                 write_msr(MSR_GS_BASE, (uint64_t)tf->tf_gsbase);
19                 write_msr(MSR_FS_BASE, (uint64_t)tf->tf_fsbase);
20         }
21         asm volatile ("movq %0, %%rsp;          "
22                       "popq %%rax;              "
23                       "popq %%rbx;              "
24                       "popq %%rcx;              "
25                       "popq %%rdx;              "
26                       "popq %%rbp;              "
27                       "popq %%rsi;              "
28                       "popq %%rdi;              "
29                       "popq %%r8;               "
30                       "popq %%r9;               "
31                       "popq %%r10;              "
32                       "popq %%r11;              "
33                       "popq %%r12;              "
34                       "popq %%r13;              "
35                       "popq %%r14;              "
36                       "popq %%r15;              "
37                       "addq $0x10, %%rsp;       "
38                       "iretq                    "
39                       : : "g" (&tf->tf_rax) : "memory");
40         panic("iretq failed");
41 }
42
43 static void __attribute__((noreturn)) proc_pop_swtf(struct sw_trapframe *tf)
44 {
45         if (x86_swtf_is_partial(tf)) {
46                 swap_gs();
47         } else {
48                 write_msr(MSR_GS_BASE, (uint64_t)tf->tf_gsbase);
49                 write_msr(MSR_FS_BASE, (uint64_t)tf->tf_fsbase);
50         }
51         /* We need to 0 out any registers that aren't part of the sw_tf and that we
52          * won't use/clobber on the out-path.  While these aren't part of the sw_tf,
53          * we also don't want to leak any kernel register content. */
54         asm volatile ("movq %0, %%rsp;          "
55                       "movq $0, %%rax;          "
56                       "movq $0, %%rdx;          "
57                       "movq $0, %%rsi;          "
58                       "movq $0, %%rdi;          "
59                       "movq $0, %%r8;           "
60                       "movq $0, %%r9;           "
61                       "movq $0, %%r10;          "
62                       "popq %%rbx;              "
63                       "popq %%rbp;              "
64                       "popq %%r12;              "
65                       "popq %%r13;              "
66                       "popq %%r14;              "
67                       "popq %%r15;              "
68                       "movq %1, %%r11;          "
69                       "popq %%rcx;              "
70                       "popq %%rsp;              "
71                       "rex.w sysret             "
72                       : : "g"(&tf->tf_rbx), "i"(FL_IF) : "memory");
73         panic("sysret failed");
74 }
75
76 /* If popping a VM TF fails for some reason, we need to reflect it back to the
77  * user.  It is possible that the reflection fails.  We still need to run
78  * something, and it's a lousy time to try something else.  So We'll give them a
79  * TF that will probably fault right away and kill them. */
80 static void __attribute__((noreturn)) handle_bad_vm_tf(struct vm_trapframe *tf)
81 {
82         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
83
84         tf->tf_exit_reason |= VMX_EXIT_REASONS_FAILED_VMENTRY;
85         tf->tf_flags |= VMCTX_FL_HAS_FAULT;
86         if (reflect_current_context()) {
87                 printk("[kernel] Unable to reflect after a bad VM enter\n");
88                 proc_init_ctx(pcpui->cur_ctx, 0, 0xcafebabe, 0, 0);
89         }
90         proc_pop_ctx(pcpui->cur_ctx);
91 }
92
93 static void __attribute__((noreturn)) proc_pop_vmtf(struct vm_trapframe *tf)
94 {
95         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
96         struct proc *p = pcpui->cur_proc;
97         struct guest_pcore *gpc;
98
99         if (x86_vmtf_is_partial(tf)) {
100                 gpc = lookup_guest_pcore(p, tf->tf_guest_pcoreid);
101                 assert(gpc);
102                 assert(pcpui->guest_pcoreid == tf->tf_guest_pcoreid);
103         } else {
104                 gpc = load_guest_pcore(p, tf->tf_guest_pcoreid);
105                 if (!gpc) {
106                         tf->tf_exit_reason = EXIT_REASON_GUEST_IN_USE;
107                         handle_bad_vm_tf(tf);
108                 }
109         }
110         vmcs_write(GUEST_RSP, tf->tf_rsp);
111         vmcs_write(GUEST_CR3, tf->tf_cr3);
112         vmcs_write(GUEST_RIP, tf->tf_rip);
113         vmcs_write(GUEST_RFLAGS, tf->tf_rflags);
114         /* cr2 is not part of the VMCS state; we need to save/restore it manually */
115         lcr2(tf->tf_cr2);
116         vmcs_write(VM_ENTRY_INTR_INFO_FIELD, tf->tf_trap_inject);
117         /* Someone may have tried poking the guest and posting an IRQ, but the IPI
118          * missed (concurrent vmexit).  In these cases, the 'outstanding
119          * notification' bit should still be set, and we can resend the IPI.  This
120          * will arrive after we vmenter, since IRQs are currently disabled. */
121         if (test_bit(VMX_POSTED_OUTSTANDING_NOTIF, gpc->posted_irq_desc))
122                 send_self_ipi(I_POKE_CORE);
123         /* vmlaunch/resume can fail, so we need to be able to return from this.
124          * Thus we can't clobber rsp via the popq style of setting the registers.
125          * Likewise, we don't want to lose rbp via the clobber list.
126          *
127          * Partial contexts have already been launched, so we resume them. */
128         asm volatile ("testl $"STRINGIFY(VMCTX_FL_PARTIAL)", %c[flags](%0);"
129                       "pushq %%rbp;              "      /* save in case we fail */
130                       "movq %c[rbx](%0), %%rbx;  "
131                       "movq %c[rcx](%0), %%rcx;  "
132                       "movq %c[rdx](%0), %%rdx;  "
133                       "movq %c[rbp](%0), %%rbp;  "
134                       "movq %c[rsi](%0), %%rsi;  "
135                       "movq %c[rdi](%0), %%rdi;  "
136                       "movq %c[r8](%0),  %%r8;   "
137                       "movq %c[r9](%0),  %%r9;   "
138                       "movq %c[r10](%0), %%r10;  "
139                       "movq %c[r11](%0), %%r11;  "
140                       "movq %c[r12](%0), %%r12;  "
141                       "movq %c[r13](%0), %%r13;  "
142                       "movq %c[r14](%0), %%r14;  "
143                       "movq %c[r15](%0), %%r15;  "
144                       "movq %c[rax](%0), %%rax;  "      /* clobber our *tf last */
145                       "jnz 1f;                   "      /* jump if partial */
146                       ASM_VMX_VMLAUNCH";         "      /* non-partial gets launched */
147                       "jmp 2f;                   "
148                       "1: "ASM_VMX_VMRESUME";    "      /* partials get resumed */
149                       "2: popq %%rbp;            "      /* vmlaunch failed */
150                       :
151                       : "a" (tf),
152                         [rax]"i"(offsetof(struct vm_trapframe, tf_rax)),
153                         [rbx]"i"(offsetof(struct vm_trapframe, tf_rbx)),
154                         [rcx]"i"(offsetof(struct vm_trapframe, tf_rcx)),
155                         [rdx]"i"(offsetof(struct vm_trapframe, tf_rdx)),
156                         [rbp]"i"(offsetof(struct vm_trapframe, tf_rbp)),
157                         [rsi]"i"(offsetof(struct vm_trapframe, tf_rsi)),
158                         [rdi]"i"(offsetof(struct vm_trapframe, tf_rdi)),
159                          [r8]"i"(offsetof(struct vm_trapframe, tf_r8)),
160                          [r9]"i"(offsetof(struct vm_trapframe, tf_r9)),
161                         [r10]"i"(offsetof(struct vm_trapframe, tf_r10)),
162                         [r11]"i"(offsetof(struct vm_trapframe, tf_r11)),
163                         [r12]"i"(offsetof(struct vm_trapframe, tf_r12)),
164                         [r13]"i"(offsetof(struct vm_trapframe, tf_r13)),
165                         [r14]"i"(offsetof(struct vm_trapframe, tf_r14)),
166                         [r15]"i"(offsetof(struct vm_trapframe, tf_r15)),
167                         [flags]"i"(offsetof(struct vm_trapframe, tf_flags))
168                       : "cc", "memory", "rbx", "rcx", "rdx", "rsi", "rdi",
169                         "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15");
170         /* vmlaunch/resume failed.  It could be for a few reasons, including things
171          * like launching instead of resuming, not having a VMCS loaded, failing a
172          * host-state area check, etc.  Those are kernel problems.
173          *
174          * The user also might be able to trigger some of these failures.  For
175          * instance, rflags could be bad, or the trap_injection could be
176          * misformatted.  We might catch that in secure_tf, or we could reflect
177          * those to the user.  Detecting btw the kernel and user mistakes might be
178          * a pain.
179          *
180          * For now, the plan is to just reflect everything back to the user and
181          * whitelist errors that are known to be kernel bugs.
182          *
183          * Also we should always have a non-shadow VMCS, so ZF should be 1 and we
184          * can read the error register. */
185         assert(read_flags() & FL_ZF);
186         tf->tf_exit_reason = EXIT_REASON_VMENTER_FAILED;
187         tf->tf_exit_qual = vmcs_read(VM_INSTRUCTION_ERROR);
188         handle_bad_vm_tf(tf);
189 }
190
191 void proc_pop_ctx(struct user_context *ctx)
192 {
193         disable_irq();
194         switch (ctx->type) {
195         case ROS_HW_CTX:
196                 proc_pop_hwtf(&ctx->tf.hw_tf);
197                 break;
198         case ROS_SW_CTX:
199                 proc_pop_swtf(&ctx->tf.sw_tf);
200                 break;
201         case ROS_VM_CTX:
202                 proc_pop_vmtf(&ctx->tf.vm_tf);
203                 break;
204         default:
205                 /* We should have caught this when securing the ctx */
206                 panic("Unknown context type %d!", ctx->type);
207         }
208 }
209
210 /* Helper: if *addr isn't a canonical user address, poison it.  Use this when
211  * you need a canonical address (like MSR_FS_BASE) */
212 static void enforce_user_canon(uintptr_t *addr)
213 {
214         if (*addr >> 47 != 0)
215                 *addr = 0x5a5a5a5a;
216 }
217
218 void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
219                    uintptr_t stack_top, uintptr_t tls_desc)
220 {
221         struct sw_trapframe *sw_tf = &ctx->tf.sw_tf;
222         /* zero the entire structure for any type, prevent potential disclosure */
223         memset(ctx, 0, sizeof(struct user_context));
224         ctx->type = ROS_SW_CTX;
225         /* Stack pointers in a fresh stack frame need to be 16 byte aligned
226          * (AMD64 ABI). If we call this function from within load_elf(), it
227          * should already be aligned properly, but we round again here for good
228          * measure. We used to subtract an extra 8 bytes here to allow us to
229          * write our _start() function in C instead of assembly. This was
230          * necessary to account for a preamble inserted the compiler which
231          * assumed a return address was pushed on the stack. Now that we properly
232          * pass our arguments on the stack, we will have to rewrite our _start()
233          * function in assembly to handle things properly. */
234         sw_tf->tf_rsp = ROUNDDOWN(stack_top, 16);
235         sw_tf->tf_rip = entryp;
236         sw_tf->tf_rbp = 0;      /* for potential backtraces */
237         sw_tf->tf_mxcsr = 0x00001f80;   /* x86 default mxcsr */
238         sw_tf->tf_fpucw = 0x037f;               /* x86 default FP CW */
239         /* Coupled closely with user's entry.S.  id is the vcoreid, which entry.S
240          * uses to determine what to do.  vcoreid == 0 is the main core/context. */
241         sw_tf->tf_rbx = vcoreid;
242         sw_tf->tf_fsbase = tls_desc;
243         proc_secure_ctx(ctx);
244 }
245
246 static void proc_secure_hwtf(struct hw_trapframe *tf)
247 {
248         enforce_user_canon(&tf->tf_gsbase);
249         enforce_user_canon(&tf->tf_fsbase);
250         /* GD_UD is the user data segment selector in the GDT, and
251          * GD_UT is the user text segment selector (see inc/memlayout.h).
252          * The low 2 bits of each segment register contains the
253          * Requestor Privilege Level (RPL); 3 means user mode. */
254         tf->tf_ss = GD_UD | 3;
255         tf->tf_cs = GD_UT | 3;
256         tf->tf_rflags |= FL_IF;
257         x86_hwtf_clear_partial(tf);
258 }
259
260 static void proc_secure_swtf(struct sw_trapframe *tf)
261 {
262         enforce_user_canon(&tf->tf_gsbase);
263         enforce_user_canon(&tf->tf_fsbase);
264         enforce_user_canon(&tf->tf_rip);
265         x86_swtf_clear_partial(tf);
266 }
267
268 static void proc_secure_vmtf(struct vm_trapframe *tf)
269 {
270         /* The user can say whatever it wants for the bulk of the TF, but the only
271          * thing it can't fake is whether or not it is a partial context, which
272          * other parts of the kernel rely on. */
273         tf->tf_rflags |= FL_RSVD_1;
274         tf->tf_rflags &= FL_RSVD_0;
275         x86_vmtf_clear_partial(tf);
276 }
277
278 void proc_secure_ctx(struct user_context *ctx)
279 {
280         switch (ctx->type) {
281         case ROS_HW_CTX:
282                 proc_secure_hwtf(&ctx->tf.hw_tf);
283                 break;
284         case ROS_SW_CTX:
285                 proc_secure_swtf(&ctx->tf.sw_tf);
286                 break;
287         case ROS_VM_CTX:
288                 proc_secure_vmtf(&ctx->tf.vm_tf);
289                 break;
290         default:
291                 /* If we aren't another ctx type, we're assuming (and forcing) a HW ctx.
292                  * If this is somehow fucked up, userspace should die rather quickly. */
293                 ctx->type = ROS_HW_CTX;
294                 proc_secure_hwtf(&ctx->tf.hw_tf);
295         }
296 }
297
298 /* Called when we are currently running an address space on our core and want to
299  * abandon it.  We need a known good pgdir before releasing the old one.  We
300  * decref, since current no longer tracks the proc (and current no longer
301  * protects the cr3). */
302 void __abandon_core(void)
303 {
304         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
305         lcr3(boot_cr3);
306         proc_decref(pcpui->cur_proc);
307         pcpui->cur_proc = 0;
308 }