Fixup of some headers.
[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 <systime.h>
23 #include <barrelfish_kpi/syscalls.h>
24 #include <barrelfish_kpi/lmp.h>
25 #include <trace/trace.h>
26 #include <trace_definitions/trace_defs.h>
27 #include <barrelfish_kpi/dispatcher_shared_target.h>
28 #include <barrelfish_kpi/cpu_arch.h>
29 #include <barrelfish_kpi/registers_arch.h>
30
31 #include <bitmacros.h>
32
33 #if defined(__x86_64__) || defined(__i386__)
34 #  include <arch/x86/apic.h>
35 #endif
36
37 #if defined(__x86_64__) && !defined(__k1om__)
38 #  include <vmkit.h>
39 #endif
40
41 #ifdef FPU_LAZY_CONTEXT_SWITCH
42 #  include <fpu.h>
43 #endif
44
45 /**
46  * \brief The kernel timeslice given in system ticks
47  */
48 systime_t kernel_timeslice;
49
50 unsigned int config_timeslice = CONFIG_TIMESLICE;
51
52 /// Counter for number of context switches
53 uint64_t context_switch_counter = 0;
54
55 /// Current execution dispatcher (when in system call or exception)
56 struct dcb *dcb_current = NULL;
57
58 /// Remembered FPU-using DCB (NULL if none)
59 struct dcb *fpu_dcb = NULL;
60
61 #ifdef FPU_LAZY_CONTEXT_SWITCH
62 void
63 fpu_lazy_top(struct dcb *dcb) {
64     // XXX: It should be possible to merge this code fragment with the
65     // other FPU restore fragment below
66     if(fpu_dcb != NULL && !dcb->is_vm_guest) {
67         struct dispatcher_shared_generic *disp =
68             get_dispatcher_shared_generic(dcb->disp);
69
70         // Switch FPU trap on if we switch away from FPU DCB and target is enabled
71         // If target disabled, we eagerly restore the FPU
72         if(fpu_dcb != dcb && !dcb->disabled) {
73             disp->fpu_trap = 1;
74         }
75
76         // Restore FPU trap state
77         if(disp->fpu_trap) {
78             fpu_trap_on();
79         } else {
80             fpu_trap_off();
81         }
82     }
83 }
84
85 void
86 fpu_lazy_bottom(struct dcb *dcb) {
87     struct dispatcher_shared_generic *disp =
88         get_dispatcher_shared_generic(dcb->disp);
89
90     // Eagerly restore FPU if it was used disabled and set FPU trap accordingly
91     if(disp->fpu_used && dcb->disabled) {
92         // Context switch if FPU state is stale
93         if(fpu_dcb != dcb) {
94             // XXX: Need to reset fpu_dcb when that DCB is deleted
95             struct dispatcher_shared_generic *dst =
96                 get_dispatcher_shared_generic(fpu_dcb->disp);
97
98             fpu_trap_off();
99
100             // Store old FPU state if it was used
101             if(fpu_dcb->disabled) {
102                 fpu_save(dispatcher_get_disabled_fpu_save_area(fpu_dcb->disp));
103                 dst->fpu_used = 1;
104             } else {
105                 assert(!fpu_dcb->disabled);
106                 fpu_save(dispatcher_get_enabled_fpu_save_area(fpu_dcb->disp));
107                 dst->fpu_used = 2;
108             }
109
110             if(disp->fpu_used == 1) {
111               fpu_restore(dispatcher_get_disabled_fpu_save_area(dcb->disp));
112             } else {
113               assert(disp->fpu_used == 2);
114               fpu_restore(dispatcher_get_enabled_fpu_save_area(dcb->disp));
115             }
116
117             // Restore trap state once more, since we modified it
118             if(disp->fpu_trap) {
119                 fpu_trap_on();
120             } else {
121                 fpu_trap_off();
122             }
123         }
124         fpu_dcb = dcb;
125     }
126 }
127 #endif
128
129 #if CONFIG_TRACE && NETWORK_STACK_BENCHMARK
130 #define TRACE_N_BM 1
131 #endif // CONFIG_TRACE && NETWORK_STACK_BENCHMARK
132
133
134 void __attribute__ ((noreturn)) dispatch(struct dcb *dcb)
135 {
136 #ifdef FPU_LAZY_CONTEXT_SWITCH
137     // Save state of FPU trap for this domain (treat it like normal context switched state)
138     if(dcb_current != NULL && !dcb_current->is_vm_guest) {
139         struct dispatcher_shared_generic *disp =
140             get_dispatcher_shared_generic(dcb_current->disp);
141         disp->fpu_trap = fpu_trap_get();
142     }
143 #endif
144
145     // XXX FIXME: Why is this null pointer check on the fast path ?
146     // If we have nothing to do we should call something other than dispatch
147     if (dcb == NULL) {
148         dcb_current = NULL;
149         wait_for_interrupt();
150     }
151
152     // Don't context switch if we are current already
153     if (dcb_current != dcb) {
154
155 #ifdef TRACE_CSWITCH
156         trace_event(TRACE_SUBSYS_KERNEL,
157                     TRACE_EVENT_KERNEL_CSWITCH,
158                     (uint32_t)(lvaddr_t)dcb & 0xFFFFFFFF);
159 #endif
160
161         context_switch(dcb);
162         dcb_current = dcb;
163     }
164
165     assert(dcb != NULL);
166
167     dispatcher_handle_t handle = dcb->disp;
168     struct dispatcher_shared_generic *disp =
169         get_dispatcher_shared_generic(handle);
170     arch_registers_state_t *disabled_area =
171         dispatcher_get_disabled_save_area(handle);
172
173     if(disp != NULL) {
174         disp->systime = systime_now() + kcb_current->kernel_off;
175     }
176     TRACE(KERNEL, SC_YIELD, 1);
177
178     if (dcb->disabled) {
179         if (disp != NULL) {
180             debug(SUBSYS_DISPATCH, "resume %.*s at 0x%" PRIx64 ", %s\n",
181                   DISP_NAME_LEN, disp->name,
182                   (uint64_t)registers_get_ip(disabled_area),
183                   disp->disabled ? "disp->disabled" : "disp->enabled"
184                 );
185         }
186
187 #if defined(__x86_64__) && !defined(__k1om__)
188         if(!dcb->is_vm_guest) {
189             resume(disabled_area);
190         } else {
191             vmkit_vmenter(dcb);
192         }
193 #else
194         resume(disabled_area);
195 #endif
196     } else {
197         if (disp != NULL) {
198             debug(SUBSYS_DISPATCH, "dispatch %.*s\n", DISP_NAME_LEN, disp->name);
199             assert(disp->dispatcher_run != 0);
200             disp->disabled = true;
201         }
202 #if defined(__x86_64__) && !defined(__k1om__)
203         if(!dcb->is_vm_guest) {
204             execute(disp->dispatcher_run);
205         } else {
206             vmkit_vmexec(dcb, (disp) ? disp->dispatcher_run : 0);
207         }
208 #else
209         execute(disp->dispatcher_run);
210 #endif
211     }
212 } // end function: dispatch
213
214 /**
215  * \brief Transfer cap from 'send' to 'ep', according to 'msg'.
216  *
217  * Reads the cap transfer spec in the LMP message 'msg' and transfers
218  * the cap from CSpace in DCB 'send' accordingly.
219  *
220  * \param ep    Endpoint capability of destination
221  * \param send  Pointer to sending DCB.
222  * \param send_cptr Address of capability in sender's cspace
223  * \param send_level Depth/level of capability in sender's cspace
224  *
225  * \return      Error code
226  */
227 static errval_t lmp_transfer_cap(struct capability *ep, struct dcb *send,
228                                  capaddr_t send_cptr, uint8_t send_level,
229                                  bool give_away)
230 {
231     errval_t err;
232     /* Parameter checking */
233     assert(send_cptr != CPTR_NULL);
234     assert(send != NULL);
235     assert(ep != NULL);
236     assert(ep->type == ObjType_EndPoint);
237     struct dcb *recv = ep->u.endpoint.listener;
238     assert(recv != NULL);
239     assert(ep->u.endpoint.epoffset != 0);
240
241     // printk(LOG_NOTE, "%s: ep->u.endpoint.epoffset = %"PRIuLVADDR"\n", __FUNCTION__, ep->u.endpoint.epoffset);
242     /* Look up the slot receiver can receive caps in */
243     struct lmp_endpoint_kern *recv_ep
244         = (void *)((uint8_t *)recv->disp + ep->u.endpoint.epoffset);
245
246     // Lookup cspace root for receiving
247     struct capability *recv_cspace_cap;
248     // XXX: do we want a level into receiver's cspace here?
249     // printk(LOG_NOTE, "recv_cspace_ptr = %"PRIxCADDR"\n", recv_ep->recv_cspc);
250     err = caps_lookup_cap(&recv->cspace.cap, recv_ep->recv_cspc, 2,
251                           &recv_cspace_cap, CAPRIGHTS_READ_WRITE);
252     if (err_is_fail(err) || recv_cspace_cap->type != ObjType_L1CNode) {
253         return SYS_ERR_LMP_CAPTRANSFER_DST_CNODE_INVALID;
254     }
255     // Check index into L1 cnode
256     capaddr_t l1index = recv_ep->recv_cptr >> L2_CNODE_BITS;
257     if (l1index >= cnode_get_slots(recv_cspace_cap)) {
258         return SYS_ERR_LMP_CAPTRANSFER_DST_CNODE_INVALID;
259     }
260     // Get the cnode
261     struct cte *recv_cnode_cte = caps_locate_slot(get_address(recv_cspace_cap),
262                                                   l1index);
263     struct capability *recv_cnode_cap = &recv_cnode_cte->cap;
264     // Check for cnode type
265     if (recv_cnode_cap->type != ObjType_L2CNode) {
266         return SYS_ERR_LMP_CAPTRANSFER_DST_CNODE_INVALID;
267     }
268     // The slot within the cnode
269     struct cte *recv_cte;
270     recv_cte = caps_locate_slot(get_address(recv_cnode_cap),
271                                 recv_ep->recv_cptr & MASK(L2_CNODE_BITS));
272
273     /* Look up source slot in sender */
274     struct cte *send_cte;
275     err = caps_lookup_slot(&send->cspace.cap, send_cptr, send_level,
276                            &send_cte, CAPRIGHTS_READ);
277     if (err_is_fail(err)) {
278         return err_push(err, SYS_ERR_LMP_CAPTRANSFER_SRC_LOOKUP);
279     }
280
281     /* Is destination empty */
282     if (recv_cte->cap.type != ObjType_Null) {
283         debug(SUBSYS_DISPATCH, "%s: dest slot occupied\n", __FUNCTION__);
284         return SYS_ERR_LMP_CAPTRANSFER_DST_SLOT_OCCUPIED;
285     }
286
287     //caps_trace(__func__, __LINE__, send_cte, "transferring");
288     //TRACE_CAP_MSG("transferring", send_cte);
289
290     /* Insert send cap into recv cap */
291     err = caps_copy_to_cte(recv_cte, send_cte, false, 0, 0);
292     assert(err_is_ok(err)); // Cannot fail after checking that slot is empty
293
294     if (give_away) {
295         err = caps_delete(send_cte);
296         if (err_is_fail(err)) {
297             printk(LOG_NOTE, "deleting source of lmp captransfer failed: %"PRIuERRV"\n", err);
298         }
299         assert(err_is_ok(err)); // A copy now exists in the recv slot, so this
300                                 // should not fail
301     }
302
303     return SYS_ERR_OK;
304 }
305
306 /**
307  * \brief Check if it would be possible to deliver LMP payload, but do not deliver it
308  *
309  * \param ep     Endpoint capability to send to
310  * \param payload_len Length (in number of words) of payload
311  */
312 errval_t lmp_can_deliver_payload(struct capability *ep,
313                                  size_t payload_len)
314 {
315     assert(ep != NULL);
316     assert(ep->type == ObjType_EndPoint);
317     struct dcb *recv = ep->u.endpoint.listener;
318     assert(recv != NULL);
319
320     /* check that receiver exists and has specified an endpoint buffer */
321     if (recv->disp == 0 || ep->u.endpoint.epoffset == 0) {
322         return SYS_ERR_LMP_NO_TARGET;
323     }
324
325     /* locate receiver's endpoint buffer */
326     struct lmp_endpoint_kern *recv_ep
327         = (void *)((uint8_t *)recv->disp + ep->u.endpoint.epoffset);
328
329     /* check delivered/consumed state */
330     uint32_t epbuflen = ep->u.endpoint.epbuflen;
331     uint32_t pos = recv_ep->delivered;
332     uint32_t consumed = recv_ep->consumed;
333     if (pos >= epbuflen || consumed >= epbuflen) {
334         return SYS_ERR_LMP_EP_STATE_INVALID;
335     }
336
337     /* compute space available in endpoint */
338     uint32_t epspace;
339     if (pos >= consumed) {
340         epspace = epbuflen - (pos - consumed);
341     } else {
342         epspace = consumed - pos;
343     }
344
345     /* Check if there's enough space for another msg.
346      * We always keep one word free, to avoid having the special case where
347      * delivered == consumed may mean the buffer is both completely full and
348      * completely empty */
349     if (epspace <= payload_len + LMP_RECV_HEADER_LENGTH) {
350         return SYS_ERR_LMP_BUF_OVERFLOW;
351     }
352
353     return SYS_ERR_OK;
354 }
355
356 /**
357  * \brief Deliver the payload of an LMP message to a dispatcher.
358  *
359  * \param ep     Endpoint capability to send to
360  * \param send   DCB of the sender. Can be NULL for kernel-originated messages
361  * \param payload     Message payload
362  * \param payload_len Length (in number of words) of payload
363  * \param captransfer True iff a cap has also been delivered
364  *
365  * \return Error code
366  */
367 errval_t lmp_deliver_payload(struct capability *ep, struct dcb *send,
368                              uintptr_t *payload, size_t payload_len,
369                              bool captransfer, bool now)
370 {
371     assert(ep != NULL);
372     assert(ep->type == ObjType_EndPoint);
373     struct dcb *recv = ep->u.endpoint.listener;
374     assert(recv != NULL);
375     assert(payload != NULL || payload_len == 0);
376
377     errval_t err;
378
379     err = lmp_can_deliver_payload(ep, payload_len);
380     if (err_is_fail(err)) {
381         return err;
382     }
383
384     /* locate receiver's endpoint buffer */
385     struct lmp_endpoint_kern *recv_ep
386         = (void *)((uint8_t *)recv->disp + ep->u.endpoint.epoffset);
387
388     /* read current pos and buflen */
389     uint32_t epbuflen = ep->u.endpoint.epbuflen;
390     uint32_t pos = recv_ep->delivered;
391
392     struct dispatcher_shared_generic *send_disp =
393         send ? get_dispatcher_shared_generic(send->disp) : NULL;
394     struct dispatcher_shared_generic *recv_disp =
395         get_dispatcher_shared_generic(recv->disp);
396     debug(SUBSYS_DISPATCH, "LMP %.*s -> %.*s\n",
397           DISP_NAME_LEN, send ? send_disp->name : "kernel",
398           DISP_NAME_LEN, recv_disp->name);
399
400     // Setup receiver's message flags
401     union lmp_recv_header recvheader = { .raw = 0 };
402     recvheader.x.flags.captransfer = captransfer;
403     recvheader.x.length = payload_len;
404
405     /* Deliver header */
406     recv_ep->buf[pos] = recvheader.raw;
407     if (++pos == epbuflen) {
408         pos = 0;
409     }
410
411     /* Transfer the msg */
412     for(int i = 0; i < payload_len; i++) {
413         recv_ep->buf[pos] = payload[i];
414         if (++pos == epbuflen) {
415             pos = 0;
416         }
417     }
418
419     // update the delivered pos
420     recv_ep->delivered = pos;
421
422     // tell the dispatcher that it has an outstanding message in one of its EPs
423     recv_disp->lmp_delivered += payload_len + LMP_RECV_HEADER_LENGTH;
424
425     // ... and give it a hint which one to look at
426     recv_disp->lmp_hint = ep->u.endpoint.epoffset;
427
428     // Make target runnable
429     make_runnable(recv);
430     if (now)
431         schedule_now(recv);
432
433     return SYS_ERR_OK;
434 }
435
436 /**
437  * \brief Deliver an LMP message to a dispatcher.
438  *
439  * \param ep     Endpoint capability to send to
440  * \param send   DCB of the sender. Can be NULL for kernel-originated messages
441  * \param payload Buffer containing message payload
442  * \param len    Length of message payload, as number of words
443  * \param send_cptr Capability to be transferred with LMP
444  * \param send_level CSpace level of cptr
445  */
446 errval_t lmp_deliver(struct capability *ep, struct dcb *send,
447                      uintptr_t *payload, size_t len,
448                      capaddr_t send_cptr, uint8_t send_level, bool give_away)
449 {
450     bool captransfer;
451     assert(ep != NULL);
452     assert(ep->type == ObjType_EndPoint);
453     struct dcb *recv = ep->u.endpoint.listener;
454     assert(recv != NULL);
455     assert(payload != NULL);
456
457     errval_t err;
458
459     /* Is the sender trying to send a cap? */
460     if (send_cptr != CPTR_NULL) {
461         /* Don't attempt to transfer the cap if we can't send the payload */
462         err = lmp_can_deliver_payload(ep, len);
463         if (err_is_fail(err)) {
464             return err;
465         }
466
467         err = lmp_transfer_cap(ep, send, send_cptr, send_level, give_away);
468         if (err_is_fail(err)) {
469             return err;
470         }
471
472         captransfer = true;
473     } else {
474         captransfer = false;
475     }
476
477     /* Send msg */
478     err = lmp_deliver_payload(ep, send, payload, len, captransfer, false);
479     // shouldn't fail, if we delivered the cap successfully
480     assert(!(captransfer && err_is_fail(err)));
481     return err;
482 }