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