kernel: do not drop RAM caps
[barrelfish] / kernel / dispatch.c
1 /**
2  * \file
3  * \brief Kernel management of dispatchers (implementation).
4  */
5
6 /*
7  * Copyright (c) 2007, 2008, 2009, 2010, 2011, 2013, ETH Zurich.
8  * All rights reserved.
9  *
10  * This file is distributed under the terms in the attached LICENSE file.
11  * If you do not find this file, copies can be found by writing to:
12  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13  */
14
15 #include <kernel.h>
16 #include <barrelfish_kpi/cpu.h>
17 #include <exec.h> /* XXX wait_for_interrupt, resume, execute */
18 #include <paging_kernel_arch.h>
19 #include <dispatch.h>
20 #include <kcb.h>
21 #include <wakeup.h>
22 #include <barrelfish_kpi/syscalls.h>
23 #include <barrelfish_kpi/lmp.h>
24 #include <trace/trace.h>
25 #include <trace_definitions/trace_defs.h>
26 #include <barrelfish_kpi/dispatcher_shared_target.h>
27 #include <barrelfish_kpi/cpu_arch.h>
28 #include <barrelfish_kpi/registers_arch.h>
29
30 #if defined(__x86_64__) || defined(__i386__)
31 #  include <arch/x86/apic.h>
32 #endif
33
34 #ifdef __x86_64__
35 #  include <vmkit.h>
36 #endif
37
38 #ifdef FPU_LAZY_CONTEXT_SWITCH
39 #  include <fpu.h>
40 #endif
41
42 #define MIN(a,b)        ((a) < (b) ? (a) : (b))
43
44 /**
45  * \brief The kernel timeslice given in milliseconds.
46  */
47 int kernel_timeslice = CONFIG_TIMESLICE;
48
49 /// Counter for number of context switches
50 uint64_t context_switch_counter = 0;
51
52 /// Current execution dispatcher (when in system call or exception)
53 struct dcb *dcb_current = NULL;
54
55 /// Remembered FPU-using DCB (NULL if none)
56 struct dcb *fpu_dcb = NULL;
57
58 /**
59  * \brief Switch context to 'dcb'.
60  *
61  * This is a wrapper function to call the real, hardware-dependent
62  * context-switch function to switch to the dispatcher, pointed to by
63  * 'dcb'. It also sets 'dcb_current'.
64  *
65  * \param dcb        Pointer to dispatcher to which to switch context.
66  */
67 static inline void context_switch(struct dcb *dcb)
68 {
69 //    printf("Executing the context switch\n");
70     assert(dcb != NULL);
71     assert(dcb->vspace != 0);
72
73     // VM guests do not have a user space dispatcher
74     if (!dcb->is_vm_guest) {
75         assert(dcb->disp != 0);
76     }
77
78 #ifdef FPU_LAZY_CONTEXT_SWITCH
79     // XXX: It should be possible to merge this code fragment with the
80     // other FPU restore fragment below
81     if(fpu_dcb != NULL && !dcb->is_vm_guest) {
82         struct dispatcher_shared_generic *disp =
83             get_dispatcher_shared_generic(dcb->disp);
84
85         // Switch FPU trap on if we switch away from FPU DCB and target is enabled
86         // If target disabled, we eagerly restore the FPU
87         if(fpu_dcb != dcb && !dcb->disabled) {
88             disp->fpu_trap = 1;
89         }
90
91         // Restore FPU trap state
92         if(disp->fpu_trap) {
93             fpu_trap_on();
94         } else {
95             fpu_trap_off();
96         }
97     }
98 #endif
99
100     paging_context_switch(dcb->vspace);
101     context_switch_counter++;
102
103     if (!dcb->is_vm_guest) {
104         assert(dcb->disp_cte.cap.type == ObjType_Frame);
105
106         /* FIXME: incomplete clean-up of "thread_register" in progress here.
107          * Complain vigorously to AB if he checks this mess in
108          */
109 #if defined(__x86_64__) || defined(__k1om__)  /* Setup new LDT */
110         maybe_reload_ldt(dcb, false);
111 #else
112         struct dispatcher_shared_generic *disp =
113             get_dispatcher_shared_generic(dcb->disp);
114
115 #ifdef FPU_LAZY_CONTEXT_SWITCH
116         // Eagerly restore FPU if it was used disabled and set FPU trap accordingly
117         if(disp->fpu_used && dcb->disabled) {
118             // Context switch if FPU state is stale
119             if(fpu_dcb != dcb) {
120                 // XXX: Need to reset fpu_dcb when that DCB is deleted
121                 struct dispatcher_shared_generic *dst =
122                     get_dispatcher_shared_generic(fpu_dcb->disp);
123
124                 fpu_trap_off();
125
126                 // Store old FPU state if it was used
127                 if(fpu_dcb->disabled) {
128                     fpu_save(dispatcher_get_disabled_fpu_save_area(fpu_dcb->disp));
129                     dst->fpu_used = 1;
130                 } else {
131                     assert(!fpu_dcb->disabled);
132                     fpu_save(dispatcher_get_enabled_fpu_save_area(fpu_dcb->disp));
133                     dst->fpu_used = 2;
134                 }
135
136                 if(disp->fpu_used == 1) {
137                   fpu_restore(dispatcher_get_disabled_fpu_save_area(dcb->disp));
138                 } else {
139                   assert(disp->fpu_used == 2);
140                   fpu_restore(dispatcher_get_enabled_fpu_save_area(dcb->disp));
141                 }
142
143                 // Restore trap state once more, since we modified it
144                 if(disp->fpu_trap) {
145                     fpu_trap_on();
146                 } else {
147                     fpu_trap_off();
148                 }
149             }
150             fpu_dcb = dcb;
151         }
152 #endif /* FPU_LAZY_CONTEXT_SWITCH */
153
154         /*
155          * The name of the function is somewhat misleading. we need an unused
156          * user register that always stores the pointer to the current
157          * dispatcher. most ABIs define a register for thread-local storage,
158          * and we have been abusing that on x64 for the dispatcher pointer
159          * --arch_set_thread_ register sets this pointer.  Obviously this
160          * needs to change to support thread-local storage using a standard
161          * ABI, so we will have to figure out how to get to the dispatcher
162          * from something like a thread-local variable.  The reason that this
163          * is in the switch path and not in resume/execute is that on x86_64
164          * loading the thread register (fs) is stupidly expensive, so we avoid
165          * doing it unless we switch contexts -- presumably that could be a
166          * local optimisation in the x86_64 dispatch paths rather than the
167          * generic context_switch path/
168          */
169         arch_set_thread_register(disp->udisp);
170 #endif
171     }
172 }
173
174 #ifdef __scc__
175 struct dcb *run_next = NULL;
176 #endif
177
178 #if CONFIG_TRACE && NETWORK_STACK_BENCHMARK
179 #define TRACE_N_BM 1
180 #endif // CONFIG_TRACE && NETWORK_STACK_BENCHMARK
181
182
183 void __attribute__ ((noreturn)) dispatch(struct dcb *dcb)
184 {
185 #ifdef FPU_LAZY_CONTEXT_SWITCH
186     // Save state of FPU trap for this domain (treat it like normal context switched state)
187     if(dcb_current != NULL && !dcb_current->is_vm_guest) {
188         struct dispatcher_shared_generic *disp =
189             get_dispatcher_shared_generic(dcb_current->disp);
190         disp->fpu_trap = fpu_trap_get();
191     }
192 #endif
193
194     // XXX FIXME: Why is this null pointer check on the fast path ?
195     // If we have nothing to do we should call something other than dispatch
196     if (dcb == NULL) {
197         dcb_current = NULL;
198 #if defined(__x86_64__) || defined(__i386__) || defined(__k1om__)
199         // Can this be moved into wait_for_interrupt?
200         // Or wait_for_nonscheduling_interrupt()?
201         if (!wakeup_is_pending()) {
202             apic_mask_timer();
203         }
204 #endif
205         wait_for_interrupt();
206     }
207
208     // XXX: run_next scheduling hack
209 #ifdef __scc__
210     if(run_next != NULL) {
211         dcb = run_next;
212         run_next = NULL;
213     }
214 #endif
215
216     // Don't context switch if we are current already
217     if (dcb_current != dcb) {
218
219 #ifdef TRACE_CSWITCH
220         trace_event(TRACE_SUBSYS_KERNEL,
221                     TRACE_EVENT_KERNEL_CSWITCH,
222                     (uint32_t)(lvaddr_t)dcb & 0xFFFFFFFF);
223 #endif
224
225         context_switch(dcb);
226         dcb_current = dcb;
227     }
228
229     assert(dcb != NULL);
230
231     dispatcher_handle_t handle = dcb->disp;
232     struct dispatcher_shared_generic *disp =
233         get_dispatcher_shared_generic(handle);
234     arch_registers_state_t *disabled_area =
235         dispatcher_get_disabled_save_area(handle);
236
237     assert(disp != NULL);
238     disp->systime = kernel_now + kcb_current->kernel_off;
239         TRACE(KERNEL, SC_YIELD, 1);
240         
241     if (dcb->disabled) {
242         debug(SUBSYS_DISPATCH, "resume %.*s at 0x%" PRIx64 "\n", DISP_NAME_LEN,
243               disp->name, (uint64_t)registers_get_ip(disabled_area));
244         assert(dispatcher_is_disabled_ip(handle,
245                                          registers_get_ip(disabled_area)));
246
247 #if defined(__x86_64__) && !defined(__k1om__)
248         if(!dcb->is_vm_guest) {
249             resume(disabled_area);
250         } else {
251             vmkit_vmenter(dcb);
252         }
253 #else
254         resume(disabled_area);
255 #endif
256     } else {
257         debug(SUBSYS_DISPATCH, "dispatch %.*s\n", DISP_NAME_LEN, disp->name);
258         assert(disp->dispatcher_run != 0);
259         disp->disabled = 1;
260 #if defined(__x86_64__) && !defined(__k1om__)
261         if(!dcb->is_vm_guest) {
262             execute(disp->dispatcher_run);
263         } else {
264             vmkit_vmexec(dcb, disp->dispatcher_run);
265         }
266 #else
267         execute(disp->dispatcher_run);
268 #endif
269     }
270 } // end function: dispatch
271
272 /**
273  * \brief Transfer cap from 'send' to 'ep', according to 'msg'.
274  *
275  * Reads the cap transfer spec in the LMP message 'msg' and transfers
276  * the cap from CSpace in DCB 'send' accordingly.
277  *
278  * \param ep    Endpoint capability of destination
279  * \param send  Pointer to sending DCB.
280  * \param send_cptr Address of capability in sender's cspace
281  * \param send_bits Valid bits in #send_cptr
282  *
283  * \return      Error code
284  */
285 static errval_t lmp_transfer_cap(struct capability *ep, struct dcb *send,
286                                  capaddr_t send_cptr, uint8_t send_bits,
287                                  bool give_away)
288 {
289     errval_t err;
290     /* Parameter checking */
291     assert(send_cptr != CPTR_NULL);
292     assert(send != NULL);
293     assert(ep != NULL);
294     assert(ep->type == ObjType_EndPoint);
295     struct dcb *recv = ep->u.endpoint.listener;
296     assert(recv != NULL);
297     assert(ep->u.endpoint.epoffset != 0);
298
299     /* Look up the slot receiver can receive caps in */
300     struct lmp_endpoint_kern *recv_ep
301         = (void *)((uint8_t *)recv->disp + ep->u.endpoint.epoffset);
302
303     // The cnode
304     struct capability *recv_cnode_cap;
305     err = caps_lookup_cap(&recv->cspace.cap, recv_ep->recv_cptr,
306                           recv_ep->recv_bits, &recv_cnode_cap,
307                           CAPRIGHTS_READ_WRITE);
308     if (err_is_fail(err)) {
309         return err_push(err, SYS_ERR_LMP_CAPTRANSFER_DST_CNODE_LOOKUP);
310     }
311     // Check for cnode type
312     if (recv_cnode_cap->type != ObjType_CNode) {
313         return SYS_ERR_LMP_CAPTRANSFER_DST_CNODE_INVALID;
314     }
315     // The slot within the cnode
316     struct cte *recv_cte;
317     recv_cte = caps_locate_slot(recv_cnode_cap->u.cnode.cnode,
318                                 recv_ep->recv_slot);
319
320     /* Look up source slot in sender */
321     struct cte *send_cte;
322     err = caps_lookup_slot(&send->cspace.cap, send_cptr, send_bits, &send_cte,
323                            CAPRIGHTS_READ);
324     if (err_is_fail(err)) {
325         return err_push(err, SYS_ERR_LMP_CAPTRANSFER_SRC_LOOKUP);
326     }
327
328     /* Is destination empty */
329     if (recv_cte->cap.type != ObjType_Null) {
330         return SYS_ERR_LMP_CAPTRANSFER_DST_SLOT_OCCUPIED;
331     }
332
333     //caps_trace(__func__, __LINE__, send_cte, "transferring");
334     //TRACE_CAP_MSG("transferring", send_cte);
335
336     /* Insert send cap into recv cap */
337     err = caps_copy_to_cte(recv_cte, send_cte, false, 0, 0);
338     assert(err_is_ok(err)); // Cannot fail after checking that slot is empty
339
340     if (give_away) {
341         err = caps_delete(send_cte);
342         if (err_is_fail(err)) {
343             printk(LOG_NOTE, "deleting source of lmp captransfer failed: %"PRIuERRV"\n", err);
344         }
345         assert(err_is_ok(err)); // A copy now exists in the recv slot, so this
346                                 // should not fail
347     }
348
349     return SYS_ERR_OK;
350 }
351
352 /**
353  * \brief Check if it would be possible to deliver LMP payload, but do not deliver it
354  *
355  * \param ep     Endpoint capability to send to
356  * \param payload_len Length (in number of words) of payload
357  */
358 errval_t lmp_can_deliver_payload(struct capability *ep,
359                                  size_t payload_len)
360 {
361     assert(ep != NULL);
362     assert(ep->type == ObjType_EndPoint);
363     struct dcb *recv = ep->u.endpoint.listener;
364     assert(recv != NULL);
365
366     /* check that receiver exists and has specified an endpoint buffer */
367     if (recv->disp == 0 || ep->u.endpoint.epoffset == 0) {
368         return SYS_ERR_LMP_NO_TARGET;
369     }
370
371     /* locate receiver's endpoint buffer */
372     struct lmp_endpoint_kern *recv_ep
373         = (void *)((uint8_t *)recv->disp + ep->u.endpoint.epoffset);
374
375     /* check delivered/consumed state */
376     uint32_t epbuflen = ep->u.endpoint.epbuflen;
377     uint32_t pos = recv_ep->delivered;
378     uint32_t consumed = recv_ep->consumed;
379     if (pos >= epbuflen || consumed >= epbuflen) {
380         return SYS_ERR_LMP_EP_STATE_INVALID;
381     }
382
383     /* compute space available in endpoint */
384     uint32_t epspace;
385     if (pos >= consumed) {
386         epspace = epbuflen - (pos - consumed);
387     } else {
388         epspace = consumed - pos;
389     }
390
391     /* Check if there's enough space for another msg.
392      * We always keep one word free, to avoid having the special case where
393      * delivered == consumed may mean the buffer is both completely full and
394      * completely empty */
395     if (epspace <= payload_len + LMP_RECV_HEADER_LENGTH) {
396         return SYS_ERR_LMP_BUF_OVERFLOW;
397     }
398
399     return SYS_ERR_OK;
400 }
401
402 /**
403  * \brief Deliver the payload of an LMP message to a dispatcher.
404  *
405  * \param ep     Endpoint capability to send to
406  * \param send   DCB of the sender. Can be NULL for kernel-originated messages
407  * \param payload     Message payload
408  * \param payload_len Length (in number of words) of payload
409  * \param captransfer True iff a cap has also been delivered
410  *
411  * \return Error code
412  */
413 errval_t lmp_deliver_payload(struct capability *ep, struct dcb *send,
414                              uintptr_t *payload, size_t payload_len,
415                              bool captransfer)
416 {
417     assert(ep != NULL);
418     assert(ep->type == ObjType_EndPoint);
419     struct dcb *recv = ep->u.endpoint.listener;
420     assert(recv != NULL);
421     assert(payload != NULL || payload_len == 0);
422
423     errval_t err;
424
425     err = lmp_can_deliver_payload(ep, payload_len);
426     if (err_is_fail(err)) {
427         return err;
428     }
429
430     /* locate receiver's endpoint buffer */
431     struct lmp_endpoint_kern *recv_ep
432         = (void *)((uint8_t *)recv->disp + ep->u.endpoint.epoffset);
433
434     /* read current pos and buflen */
435     uint32_t epbuflen = ep->u.endpoint.epbuflen;
436     uint32_t pos = recv_ep->delivered;
437
438     struct dispatcher_shared_generic *send_disp =
439         send ? get_dispatcher_shared_generic(send->disp) : NULL;
440     struct dispatcher_shared_generic *recv_disp =
441         get_dispatcher_shared_generic(recv->disp);
442     debug(SUBSYS_DISPATCH, "LMP %.*s -> %.*s\n",
443           DISP_NAME_LEN, send ? send_disp->name : "kernel",
444           DISP_NAME_LEN, recv_disp->name);
445
446     // Setup receiver's message flags
447     union lmp_recv_header recvheader = { .raw = 0 };
448     recvheader.x.flags.captransfer = captransfer;
449     recvheader.x.length = payload_len;
450
451     /* Deliver header */
452     recv_ep->buf[pos] = recvheader.raw;
453     if (++pos == epbuflen) {
454         pos = 0;
455     }
456
457     /* Transfer the msg */
458     for(int i = 0; i < payload_len; i++) {
459         recv_ep->buf[pos] = payload[i];
460         if (++pos == epbuflen) {
461             pos = 0;
462         }
463     }
464
465     // update the delivered pos
466     recv_ep->delivered = pos;
467
468     // tell the dispatcher that it has an outstanding message in one of its EPs
469     recv_disp->lmp_delivered += payload_len + LMP_RECV_HEADER_LENGTH;
470
471     // ... and give it a hint which one to look at
472     recv_disp->lmp_hint = ep->u.endpoint.epoffset;
473
474     // Make target runnable
475     make_runnable(recv);
476
477     return SYS_ERR_OK;
478 }
479
480 /**
481  * \brief Deliver an LMP message to a dispatcher.
482  *
483  * \param ep     Endpoint capability to send to
484  * \param send   DCB of the sender. Can be NULL for kernel-originated messages
485  * \param payload Buffer containing message payload
486  * \param len    Length of message payload, as number of words
487  * \param send_cptr Capability to be transferred with LMP
488  * \param send_bits Valid bits in #send_cptr
489  */
490 errval_t lmp_deliver(struct capability *ep, struct dcb *send,
491                      uintptr_t *payload, size_t len,
492                      capaddr_t send_cptr, uint8_t send_bits, bool give_away)
493 {
494     bool captransfer;
495     assert(ep != NULL);
496     assert(ep->type == ObjType_EndPoint);
497     struct dcb *recv = ep->u.endpoint.listener;
498     assert(recv != NULL);
499     assert(payload != NULL);
500
501     errval_t err;
502
503     /* Is the sender trying to send a cap? */
504     if (send_cptr != CPTR_NULL) {
505         /* Don't attempt to transfer the cap if we can't send the payload */
506         err = lmp_can_deliver_payload(ep, len);
507         if (err_is_fail(err)) {
508             return err;
509         }
510
511         err = lmp_transfer_cap(ep, send, send_cptr, send_bits, give_away);
512         if (err_is_fail(err)) {
513             return err;
514         }
515
516         captransfer = true;
517     } else {
518         captransfer = false;
519     }
520
521     /* Send msg */
522     err = lmp_deliver_payload(ep, send, payload, len, captransfer);
523     // shouldn't fail, if we delivered the cap successfully
524     assert(!(captransfer && err_is_fail(err)));
525     return err;
526 }