VMM: Use the I_POKE_CORE IRQ for posted IRQs
[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         /* vmlaunch/resume can fail, so we need to be able to return from this.
118          * Thus we can't clobber rsp via the popq style of setting the registers.
119          * Likewise, we don't want to lose rbp via the clobber list.
120          *
121          * Partial contexts have already been launched, so we resume them. */
122         asm volatile ("testl $"STRINGIFY(VMCTX_FL_PARTIAL)", %c[flags](%0);"
123                       "pushq %%rbp;              "      /* save in case we fail */
124                       "movq %c[rbx](%0), %%rbx;  "
125                       "movq %c[rcx](%0), %%rcx;  "
126                       "movq %c[rdx](%0), %%rdx;  "
127                       "movq %c[rbp](%0), %%rbp;  "
128                       "movq %c[rsi](%0), %%rsi;  "
129                       "movq %c[rdi](%0), %%rdi;  "
130                       "movq %c[r8](%0),  %%r8;   "
131                       "movq %c[r9](%0),  %%r9;   "
132                       "movq %c[r10](%0), %%r10;  "
133                       "movq %c[r11](%0), %%r11;  "
134                       "movq %c[r12](%0), %%r12;  "
135                       "movq %c[r13](%0), %%r13;  "
136                       "movq %c[r14](%0), %%r14;  "
137                       "movq %c[r15](%0), %%r15;  "
138                       "movq %c[rax](%0), %%rax;  "      /* clobber our *tf last */
139                       "jnz 1f;                   "      /* jump if partial */
140                       ASM_VMX_VMLAUNCH";         "      /* non-partial gets launched */
141                       "jmp 2f;                   "
142                       "1: "ASM_VMX_VMRESUME";    "      /* partials get resumed */
143                       "2: popq %%rbp;            "      /* vmlaunch failed */
144                       :
145                       : "a" (tf),
146                         [rax]"i"(offsetof(struct vm_trapframe, tf_rax)),
147                         [rbx]"i"(offsetof(struct vm_trapframe, tf_rbx)),
148                         [rcx]"i"(offsetof(struct vm_trapframe, tf_rcx)),
149                         [rdx]"i"(offsetof(struct vm_trapframe, tf_rdx)),
150                         [rbp]"i"(offsetof(struct vm_trapframe, tf_rbp)),
151                         [rsi]"i"(offsetof(struct vm_trapframe, tf_rsi)),
152                         [rdi]"i"(offsetof(struct vm_trapframe, tf_rdi)),
153                          [r8]"i"(offsetof(struct vm_trapframe, tf_r8)),
154                          [r9]"i"(offsetof(struct vm_trapframe, tf_r9)),
155                         [r10]"i"(offsetof(struct vm_trapframe, tf_r10)),
156                         [r11]"i"(offsetof(struct vm_trapframe, tf_r11)),
157                         [r12]"i"(offsetof(struct vm_trapframe, tf_r12)),
158                         [r13]"i"(offsetof(struct vm_trapframe, tf_r13)),
159                         [r14]"i"(offsetof(struct vm_trapframe, tf_r14)),
160                         [r15]"i"(offsetof(struct vm_trapframe, tf_r15)),
161                         [flags]"i"(offsetof(struct vm_trapframe, tf_flags))
162                       : "cc", "memory", "rbx", "rcx", "rdx", "rsi", "rdi",
163                         "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15");
164         /* vmlaunch/resume failed.  It could be for a few reasons, including things
165          * like launching instead of resuming, not having a VMCS loaded, failing a
166          * host-state area check, etc.  Those are kernel problems.
167          *
168          * The user also might be able to trigger some of these failures.  For
169          * instance, rflags could be bad, or the trap_injection could be
170          * misformatted.  We might catch that in secure_tf, or we could reflect
171          * those to the user.  Detecting btw the kernel and user mistakes might be
172          * a pain.
173          *
174          * For now, the plan is to just reflect everything back to the user and
175          * whitelist errors that are known to be kernel bugs.
176          *
177          * Also we should always have a non-shadow VMCS, so ZF should be 1 and we
178          * can read the error register. */
179         assert(read_flags() & FL_ZF);
180         tf->tf_exit_reason = EXIT_REASON_VMENTER_FAILED;
181         tf->tf_exit_qual = vmcs_read(VM_INSTRUCTION_ERROR);
182         handle_bad_vm_tf(tf);
183 }
184
185 void proc_pop_ctx(struct user_context *ctx)
186 {
187         disable_irq();
188         switch (ctx->type) {
189         case ROS_HW_CTX:
190                 proc_pop_hwtf(&ctx->tf.hw_tf);
191                 break;
192         case ROS_SW_CTX:
193                 proc_pop_swtf(&ctx->tf.sw_tf);
194                 break;
195         case ROS_VM_CTX:
196                 proc_pop_vmtf(&ctx->tf.vm_tf);
197                 break;
198         default:
199                 /* We should have caught this when securing the ctx */
200                 panic("Unknown context type %d!", ctx->type);
201         }
202 }
203
204 /* Helper: if *addr isn't a canonical user address, poison it.  Use this when
205  * you need a canonical address (like MSR_FS_BASE) */
206 static void enforce_user_canon(uintptr_t *addr)
207 {
208         if (*addr >> 47 != 0)
209                 *addr = 0x5a5a5a5a;
210 }
211
212 void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
213                    uintptr_t stack_top, uintptr_t tls_desc)
214 {
215         struct sw_trapframe *sw_tf = &ctx->tf.sw_tf;
216         /* zero the entire structure for any type, prevent potential disclosure */
217         memset(ctx, 0, sizeof(struct user_context));
218         ctx->type = ROS_SW_CTX;
219         /* Stack pointers in a fresh stack frame need to be 16 byte aligned
220          * (AMD64 ABI). If we call this function from within load_elf(), it
221          * should already be aligned properly, but we round again here for good
222          * measure. We used to subtract an extra 8 bytes here to allow us to
223          * write our _start() function in C instead of assembly. This was
224          * necessary to account for a preamble inserted the compiler which
225          * assumed a return address was pushed on the stack. Now that we properly
226          * pass our arguments on the stack, we will have to rewrite our _start()
227          * function in assembly to handle things properly. */
228         sw_tf->tf_rsp = ROUNDDOWN(stack_top, 16);
229         sw_tf->tf_rip = entryp;
230         sw_tf->tf_rbp = 0;      /* for potential backtraces */
231         sw_tf->tf_mxcsr = 0x00001f80;   /* x86 default mxcsr */
232         sw_tf->tf_fpucw = 0x037f;               /* x86 default FP CW */
233         /* Coupled closely with user's entry.S.  id is the vcoreid, which entry.S
234          * uses to determine what to do.  vcoreid == 0 is the main core/context. */
235         sw_tf->tf_rbx = vcoreid;
236         sw_tf->tf_fsbase = tls_desc;
237         proc_secure_ctx(ctx);
238 }
239
240 static void proc_secure_hwtf(struct hw_trapframe *tf)
241 {
242         enforce_user_canon(&tf->tf_gsbase);
243         enforce_user_canon(&tf->tf_fsbase);
244         /* GD_UD is the user data segment selector in the GDT, and
245          * GD_UT is the user text segment selector (see inc/memlayout.h).
246          * The low 2 bits of each segment register contains the
247          * Requestor Privilege Level (RPL); 3 means user mode. */
248         tf->tf_ss = GD_UD | 3;
249         tf->tf_cs = GD_UT | 3;
250         tf->tf_rflags |= FL_IF;
251         x86_hwtf_clear_partial(tf);
252 }
253
254 static void proc_secure_swtf(struct sw_trapframe *tf)
255 {
256         enforce_user_canon(&tf->tf_gsbase);
257         enforce_user_canon(&tf->tf_fsbase);
258         enforce_user_canon(&tf->tf_rip);
259         x86_swtf_clear_partial(tf);
260 }
261
262 static void proc_secure_vmtf(struct vm_trapframe *tf)
263 {
264         /* The user can say whatever it wants for the bulk of the TF, but the only
265          * thing it can't fake is whether or not it is a partial context, which
266          * other parts of the kernel rely on. */
267         tf->tf_rflags |= FL_RSVD_1;
268         tf->tf_rflags &= FL_RSVD_0;
269         x86_vmtf_clear_partial(tf);
270 }
271
272 void proc_secure_ctx(struct user_context *ctx)
273 {
274         switch (ctx->type) {
275         case ROS_HW_CTX:
276                 proc_secure_hwtf(&ctx->tf.hw_tf);
277                 break;
278         case ROS_SW_CTX:
279                 proc_secure_swtf(&ctx->tf.sw_tf);
280                 break;
281         case ROS_VM_CTX:
282                 proc_secure_vmtf(&ctx->tf.vm_tf);
283                 break;
284         default:
285                 /* If we aren't another ctx type, we're assuming (and forcing) a HW ctx.
286                  * If this is somehow fucked up, userspace should die rather quickly. */
287                 ctx->type = ROS_HW_CTX;
288                 proc_secure_hwtf(&ctx->tf.hw_tf);
289         }
290 }
291
292 /* Called when we are currently running an address space on our core and want to
293  * abandon it.  We need a known good pgdir before releasing the old one.  We
294  * decref, since current no longer tracks the proc (and current no longer
295  * protects the cr3). */
296 void __abandon_core(void)
297 {
298         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
299         lcr3(boot_cr3);
300         proc_decref(pcpui->cur_proc);
301         pcpui->cur_proc = 0;
302 }