6a43c6b68650e7cff9a359e959406475ae781447
[barrelfish] / kernel / syscall.c
1 /**
2  * \file
3  * \brief Arch-generic system calls implementation.
4  */
5
6 /*
7  * Copyright (c) 2007, 2008, 2009, 2010, 2012, 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 <stdio.h>
17 #include <string.h>
18 #include <syscall.h>
19 #include <barrelfish_kpi/syscalls.h>
20 #include <capabilities.h>
21 #include <cap_predicates.h>
22 #include <coreboot.h>
23 #include <mdb/mdb.h>
24 #include <mdb/mdb_tree.h>
25 #include <cap_predicates.h>
26 #include <dispatch.h>
27 #include <distcaps.h>
28 #include <wakeup.h>
29 #include <paging_kernel_helper.h>
30 #include <paging_kernel_arch.h>
31 #include <exec.h>
32 #include <irq.h>
33 #include <trace/trace.h>
34 #include <trace_definitions/trace_defs.h>
35 #include <kcb.h>
36
37 errval_t sys_print(const char *str, size_t length)
38 {
39     /* FIXME: check that string is mapped and accessible to caller! */
40     printf("%.*s", (int)length, str);
41     return SYS_ERR_OK;
42 }
43
44 /* FIXME: lots of missing argument checks in this function */
45 struct sysret
46 sys_dispatcher_setup(struct capability *to, capaddr_t cptr, int depth,
47                      capaddr_t vptr, capaddr_t dptr, bool run, capaddr_t odptr)
48 {
49     errval_t err = SYS_ERR_OK;
50     assert(to->type == ObjType_Dispatcher);
51     struct dcb *dcb = to->u.dispatcher.dcb;
52
53     lpaddr_t lpaddr;
54
55     /* 1. set cspace root */
56     if (cptr != CPTR_NULL) {
57         struct cte *root;
58         err = caps_lookup_slot(&dcb_current->cspace.cap, cptr, depth,
59                                &root, CAPRIGHTS_READ);
60         if (err_is_fail(err)) {
61             return SYSRET(err_push(err, SYS_ERR_DISP_CSPACE_ROOT));
62         }
63         if (root->cap.type != ObjType_CNode) {
64             return SYSRET(err_push(err, SYS_ERR_DISP_CSPACE_INVALID));
65         }
66         err = caps_copy_to_cte(&dcb->cspace, root, false, 0, 0);
67         if (err_is_fail(err)) {
68             return SYSRET(err_push(err, SYS_ERR_DISP_CSPACE_ROOT));
69         }
70     }
71
72     /* 2. set vspace root */
73     if (vptr != CPTR_NULL) {
74         struct capability *vroot;
75         err = caps_lookup_cap(&dcb_current->cspace.cap, vptr, CPTR_BITS,
76                               &vroot, CAPRIGHTS_WRITE);
77         if (err_is_fail(err)) {
78             return SYSRET(err_push(err, SYS_ERR_DISP_VSPACE_ROOT));
79         }
80
81         // Insert as dispatcher's VSpace root
82         switch(vroot->type) {
83         case ObjType_VNode_x86_64_pml4:
84             dcb->vspace =
85                 (lvaddr_t)gen_phys_to_local_phys(vroot->u.vnode_x86_64_pml4.base);
86             break;
87 #ifdef CONFIG_PAE
88         case ObjType_VNode_x86_32_pdpt:
89             dcb->vspace =
90                 (lvaddr_t)gen_phys_to_local_phys(vroot->u.vnode_x86_32_pdpt.base);
91             break;
92 #else
93         case ObjType_VNode_x86_32_pdir:
94             dcb->vspace =
95                 (lvaddr_t)gen_phys_to_local_phys(vroot->u.vnode_x86_32_pdir.base);
96             break;
97 #endif
98         case ObjType_VNode_ARM_l1:
99             dcb->vspace =
100                 (lvaddr_t)gen_phys_to_local_phys(vroot->u.vnode_arm_l1.base);
101             break;
102
103         default:
104             return SYSRET(err_push(err, SYS_ERR_DISP_VSPACE_INVALID));
105         }
106     }
107
108     /* 3. set dispatcher frame pointer */
109     if (dptr != CPTR_NULL) {
110         struct cte *dispcte;
111         err = caps_lookup_slot(&dcb_current->cspace.cap, dptr, CPTR_BITS,
112                                &dispcte, CAPRIGHTS_WRITE);
113         if (err_is_fail(err)) {
114             return SYSRET(err_push(err, SYS_ERR_DISP_FRAME));
115         }
116         struct capability *dispcap = &dispcte->cap;
117         if (dispcap->type != ObjType_Frame) {
118             return SYSRET(err_push(err, SYS_ERR_DISP_FRAME_INVALID));
119         }
120
121         /* FIXME: check rights, check size */
122
123         lpaddr = gen_phys_to_local_phys(dispcap->u.frame.base);
124         dcb->disp = local_phys_to_mem(lpaddr);
125         // Copy the cap to dcb also
126         err = caps_copy_to_cte(&dcb->disp_cte, dispcte, false, 0, 0);
127         // If copy fails, something wrong in kernel
128         assert(err_is_ok(err));
129     }
130
131     /* 5. Make runnable if desired -- Set pointer to ipi_data */
132     if (run) {
133         if (dcb->vspace == 0 ||
134         (!dcb->is_vm_guest &&
135         (dcb->disp == 0 || dcb->cspace.cap.type != ObjType_CNode))) {
136             return SYSRET(err_push(err, SYS_ERR_DISP_NOT_RUNNABLE));
137         }
138
139         // XXX: dispatchers run disabled the first time they start
140         dcb->disabled = 1;
141         //printf("DCB: %p %.*s\n", dcb, DISP_NAME_LEN, dcb->disp->name);
142         make_runnable(dcb);
143     }
144
145     /* 6. Copy domain ID off given dispatcher */
146     if(odptr != CPTR_NULL) {
147         struct capability *odisp;
148         err = caps_lookup_cap(&dcb_current->cspace.cap, odptr, CPTR_BITS,
149                               &odisp, CAPRIGHTS_READ_WRITE);
150         if (err_is_fail(err)) {
151             return SYSRET(err_push(err, SYS_ERR_DISP_OCAP_LOOKUP));
152         }
153         dcb->domain_id = odisp->u.dispatcher.dcb->domain_id;
154     }
155
156     /* 7. (HACK) Set current core id */
157     {
158     struct dispatcher_shared_generic *disp =
159         get_dispatcher_shared_generic(dcb->disp);
160     disp->curr_core_id = my_core_id;
161     }
162
163     if(!dcb->is_vm_guest) {
164         struct dispatcher_shared_generic *disp =
165                     get_dispatcher_shared_generic(dcb->disp);
166         err = trace_new_application(disp->name, (uintptr_t) dcb);
167
168         if (err == TRACE_ERR_NO_BUFFER) {
169             // Try to use the boot buffer.
170             trace_new_boot_application(disp->name, (uintptr_t) dcb);
171         }
172     }
173
174     return SYSRET(SYS_ERR_OK);
175 }
176
177 struct sysret
178 sys_dispatcher_properties(struct capability *to,
179                           enum task_type type, unsigned long deadline,
180                           unsigned long wcet, unsigned long period,
181                           unsigned long release, unsigned short weight)
182 {
183     assert(to->type == ObjType_Dispatcher);
184
185 #ifdef CONFIG_SCHEDULER_RBED
186     struct dcb *dcb = to->u.dispatcher.dcb;
187
188     assert(type >= TASK_TYPE_BEST_EFFORT && type <= TASK_TYPE_HARD_REALTIME);
189     assert(wcet <= deadline);
190     assert(wcet <= period);
191     assert(type != TASK_TYPE_BEST_EFFORT || weight > 0);
192
193     trace_event(TRACE_SUBSYS_KERNEL, TRACE_EVENT_KERNEL_SCHED_REMOVE,
194                 152);
195     scheduler_remove(dcb);
196
197     /* Set task properties */
198     dcb->type = type;
199     dcb->deadline = deadline;
200     dcb->wcet = wcet;
201     dcb->period = period;
202     dcb->release_time = (release == 0) ? kernel_now : release;
203     dcb->weight = weight;
204
205     make_runnable(dcb);
206 #endif
207
208     return SYSRET(SYS_ERR_OK);
209 }
210
211 /**
212  * \param root                  Root CNode to invoke
213  * \param source_cptr           Source capability cptr
214  * \param type                  Type to retype to
215  * \param objbits               Object bits for variable-sized types
216  * \param dest_cnode_cptr       Destination cnode cptr
217  * \param dest_slot             Destination slot number
218  * \param dest_vbits            Valid bits in destination cnode cptr
219  */
220 struct sysret
221 sys_retype(struct capability *root, capaddr_t source_cptr, enum objtype type,
222            uint8_t objbits, capaddr_t dest_cnode_cptr, cslot_t dest_slot,
223            uint8_t dest_vbits, bool from_monitor)
224 {
225     errval_t err;
226
227     /* Parameter checking */
228     if (type == ObjType_Null || type >= ObjType_Num) {
229         return SYSRET(SYS_ERR_ILLEGAL_DEST_TYPE);
230     }
231
232     /* Source capability */
233     struct cte *source_cap;
234     err = caps_lookup_slot(root, source_cptr, CPTR_BITS, &source_cap,
235                            CAPRIGHTS_READ);
236     if (err_is_fail(err)) {
237         return SYSRET(err_push(err, SYS_ERR_SOURCE_CAP_LOOKUP));
238     }
239     assert(source_cap != NULL);
240
241     /* Destination cnode */
242     struct capability *dest_cnode_cap;
243     err = caps_lookup_cap(root, dest_cnode_cptr, dest_vbits,
244                           &dest_cnode_cap, CAPRIGHTS_READ_WRITE);
245     if (err_is_fail(err)) {
246         return SYSRET(err_push(err, SYS_ERR_DEST_CNODE_LOOKUP));
247     }
248     if (dest_cnode_cap->type != ObjType_CNode) {
249         return SYSRET(SYS_ERR_DEST_CNODE_INVALID);
250     }
251
252     return SYSRET(caps_retype(type, objbits, dest_cnode_cap, dest_slot,
253                               source_cap, from_monitor));
254 }
255
256 struct sysret sys_create(struct capability *root, enum objtype type,
257                          uint8_t objbits, capaddr_t dest_cnode_cptr,
258                          cslot_t dest_slot, int dest_vbits)
259 {
260     errval_t err;
261     uint8_t bits = 0;
262     genpaddr_t base = 0;
263
264     /* Paramter checking */
265     if (type == ObjType_Null || type >= ObjType_Num) {
266         return SYSRET(SYS_ERR_ILLEGAL_DEST_TYPE);
267     }
268
269     /* Destination CNode */
270     struct capability *dest_cnode_cap;
271     err = caps_lookup_cap(root, dest_cnode_cptr, dest_vbits,
272                           &dest_cnode_cap, CAPRIGHTS_READ_WRITE);
273     if (err_is_fail(err)) {
274         return SYSRET(err_push(err, SYS_ERR_DEST_CNODE_LOOKUP));
275     }
276
277     /* Destination slot */
278     struct cte *dest_cte;
279     dest_cte = caps_locate_slot(dest_cnode_cap->u.cnode.cnode, dest_slot);
280     if (dest_cte->cap.type != ObjType_Null) {
281         return SYSRET(SYS_ERR_SLOTS_IN_USE);
282     }
283
284     /* List capabilities allowed to be created at runtime. */
285     switch(type) {
286
287     case ObjType_ID:
288         break;
289
290     // only certain types of capabilities can be created at runtime
291     default:
292         return SYSRET(SYS_ERR_TYPE_NOT_CREATABLE);
293     }
294
295     return SYSRET(caps_create_new(type, base, bits, objbits, my_core_id, dest_cte));
296 }
297
298 /**
299  * Common code for copying and minting except the mint flag and param passing
300  */
301 struct sysret
302 sys_copy_or_mint(struct capability *root, capaddr_t destcn_cptr, cslot_t dest_slot,
303              capaddr_t source_cptr, int destcn_vbits, int source_vbits,
304              uintptr_t param1, uintptr_t param2, bool mint)
305 {
306     errval_t err;
307
308     if (!mint) {
309         param1 = param2 = 0;
310     }
311
312     /* Lookup source cap */
313     struct cte *src_cap;
314     err = caps_lookup_slot(root, source_cptr, source_vbits,
315                            &src_cap, CAPRIGHTS_READ);
316     if (err_is_fail(err)) {
317         return SYSRET(err_push(err, SYS_ERR_SOURCE_CAP_LOOKUP));
318     }
319
320     /* Lookup destination cnode cap */
321     struct cte *dest_cnode_cap;
322     err = caps_lookup_slot(root, destcn_cptr, destcn_vbits,
323                            &dest_cnode_cap, CAPRIGHTS_READ_WRITE);
324     if (err_is_fail(err)) {
325         return SYSRET(err_push(err, SYS_ERR_DEST_CNODE_LOOKUP));
326     }
327
328     /* Perform copy */
329     if (dest_cnode_cap->cap.type == ObjType_CNode) {
330         return SYSRET(caps_copy_to_cnode(dest_cnode_cap, dest_slot, src_cap,
331                                          mint, param1, param2));
332     } else {
333         return SYSRET(SYS_ERR_DEST_TYPE_INVALID);
334     }
335 }
336
337 struct sysret
338 sys_map(struct capability *ptable, cslot_t slot, capaddr_t source_cptr,
339         int source_vbits, uintptr_t flags, uintptr_t offset,
340         uintptr_t pte_count)
341 {
342     assert (type_is_vnode(ptable->type));
343
344     errval_t err;
345
346     /* Lookup source cap */
347     struct capability *root = &dcb_current->cspace.cap;
348     struct cte *src_cte;
349     err = caps_lookup_slot(root, source_cptr, source_vbits, &src_cte,
350                            CAPRIGHTS_READ);
351     if (err_is_fail(err)) {
352         return SYSRET(err_push(err, SYS_ERR_SOURCE_CAP_LOOKUP));
353     }
354
355     /* Perform map */
356     // XXX: this does not check if we do have CAPRIGHTS_READ_WRITE on
357     // the destination cap (the page table we're inserting into)
358     return SYSRET(caps_copy_to_vnode(cte_for_cap(ptable), slot, src_cte, flags,
359                                      offset, pte_count));
360 }
361
362 struct sysret sys_delete(struct capability *root, capaddr_t cptr, uint8_t bits)
363 {
364     errval_t err;
365     struct cte *slot;
366     err = caps_lookup_slot(root, cptr, bits, &slot, CAPRIGHTS_READ_WRITE);
367     if (err_is_fail(err)) {
368         return SYSRET(err);
369     }
370
371     err = caps_delete(slot);
372     return SYSRET(err);
373 }
374
375 struct sysret sys_revoke(struct capability *root, capaddr_t cptr, uint8_t bits)
376 {
377     errval_t err;
378     struct cte *slot;
379     err = caps_lookup_slot(root, cptr, bits, &slot, CAPRIGHTS_READ_WRITE);
380     if (err_is_fail(err)) {
381         return SYSRET(err);
382     }
383
384     err = caps_revoke(slot);
385     return SYSRET(err);
386 }
387
388 struct sysret sys_get_state(struct capability *root, capaddr_t cptr, uint8_t bits)
389 {
390     errval_t err;
391     struct cte *slot;
392     err = caps_lookup_slot(root, cptr, bits, &slot, CAPRIGHTS_READ);
393     if (err_is_fail(err)) {
394         return SYSRET(err);
395     }
396
397     distcap_state_t state = distcap_get_state(slot);
398     return (struct sysret) { .error = SYS_ERR_OK, .value = state };
399 }
400
401 struct sysret sys_yield(capaddr_t target)
402 {
403     dispatcher_handle_t handle = dcb_current->disp;
404     struct dispatcher_shared_generic *disp =
405         get_dispatcher_shared_generic(handle);
406
407
408     debug(SUBSYS_DISPATCH, "%.*s yields%s\n", DISP_NAME_LEN, disp->name,
409           !disp->haswork && disp->lmp_delivered == disp->lmp_seen
410            ? " and is removed from the runq" : "");
411
412     if (!disp->disabled) {
413         printk(LOG_ERR, "SYSCALL_YIELD while enabled\n");
414         return SYSRET(SYS_ERR_CALLER_ENABLED);
415     }
416
417     struct capability *yield_to = NULL;
418     if (target != CPTR_NULL) {
419         errval_t err;
420
421         /* directed yield */
422         err = caps_lookup_cap(&dcb_current->cspace.cap, target, CPTR_BITS,
423                               &yield_to, CAPRIGHTS_READ);
424         if (err_is_fail(err)) {
425             return SYSRET(err);
426         } else if (yield_to == NULL ||
427                    (yield_to->type != ObjType_EndPoint
428                     && yield_to->type != ObjType_Dispatcher)) {
429             return SYSRET(SYS_ERR_INVALID_YIELD_TARGET);
430         }
431         /* FIXME: check rights? */
432     }
433
434     disp->disabled = false;
435     dcb_current->disabled = false;
436
437     // Remove from queue when no work and no more messages and no missed wakeup
438     systime_t wakeup = disp->wakeup;
439     if (!disp->haswork && disp->lmp_delivered == disp->lmp_seen
440         && (wakeup == 0 || wakeup > (kernel_now + kcb_current->kernel_off))) {
441
442         trace_event(TRACE_SUBSYS_NNET, TRACE_EVENT_NNET_SCHED_REMOVE,
443             (uint32_t)(lvaddr_t)dcb_current & 0xFFFFFFFF);
444         trace_event(TRACE_SUBSYS_KERNEL, TRACE_EVENT_KERNEL_SCHED_REMOVE,
445                 151);
446
447         scheduler_remove(dcb_current);
448         if (wakeup != 0) {
449             wakeup_set(dcb_current, wakeup);
450         }
451     } else {
452         // Otherwise yield for the timeslice
453         scheduler_yield(dcb_current);
454     }
455
456     if (yield_to != NULL) {
457         struct dcb *target_dcb = NULL;
458         if (yield_to->type == ObjType_EndPoint) {
459             target_dcb = yield_to->u.endpoint.listener;
460         } else if (yield_to->type == ObjType_Dispatcher) {
461             target_dcb = yield_to->u.dispatcher.dcb;
462         } else {
463             panic("invalid type in yield cap");
464         }
465
466         trace_event(TRACE_SUBSYS_NNET, TRACE_EVENT_NNET_YIELD,
467             (uint32_t)(lvaddr_t)target_dcb & 0xFFFFFFFF);
468         make_runnable(target_dcb);
469         dispatch(target_dcb);
470     } else {
471 //        trace_event(TRACE_SUBSYS_BNET, TRACE_EVENT_BNET_YIELD,
472 //            0);
473
474         /* undirected yield */
475         dispatch(schedule());
476     }
477
478     panic("Yield returned!");
479 }
480
481 struct sysret sys_suspend(bool do_halt)
482 {
483     dispatcher_handle_t handle = dcb_current->disp;
484     struct dispatcher_shared_generic *disp =
485         get_dispatcher_shared_generic(handle);
486
487     debug(SUBSYS_DISPATCH, "%.*s suspends (halt: %d)\n", DISP_NAME_LEN, disp->name, do_halt);
488
489     if (!disp->disabled) {
490         printk(LOG_ERR, "SYSCALL_SUSPEND while enabled\n");
491         return SYSRET(SYS_ERR_CALLER_ENABLED);
492     }
493
494     disp->disabled = false;
495     dcb_current->disabled = false;
496
497     if (do_halt) {
498         //printf("%s:%s:%d: before halt of core (%"PRIuCOREID")\n",
499         //       __FILE__, __FUNCTION__, __LINE__, my_core_id);
500         halt();
501     } else {
502         // Note this only works if we're calling this inside
503         // the kcb we're currently running
504         printk(LOG_NOTE, "in sys_suspend(<no_halt>)!\n");
505         printk(LOG_NOTE, "calling switch_kcb!\n");
506         struct kcb *next = kcb_current->next;
507         kcb_current->next = NULL;
508         switch_kcb(next);
509         // enable kcb scheduler
510         printk(LOG_NOTE, "enabling kcb scheduler!\n");
511         kcb_sched_suspended = false;
512         // schedule something in the other kcb
513         dispatch(schedule());
514     }
515
516     panic("Yield returned!");
517 }
518
519
520 /**
521  * The format of the returned ID is:
522  *
523  * --------------------------------------------------------------------
524  * |             0 (unused) | coreid |         core_local_id          |
525  * --------------------------------------------------------------------
526  * 63                        39       31                              0 Bit
527  *
528  */
529 struct sysret sys_idcap_identify(struct capability *cap, idcap_id_t *id)
530 {
531     STATIC_ASSERT_SIZEOF(coreid_t, 1);
532
533     idcap_id_t coreid = (idcap_id_t) cap->u.id.coreid;
534     *id = coreid << 32 | cap->u.id.core_local_id;
535
536     return SYSRET(SYS_ERR_OK);
537 }
538
539 /**
540  * Calls correct handler function to spawn an app core.
541  *
542  * At the moment spawn_core_handlers is set-up per
543  * architecture inside text_init() usually found in init.c.
544  *
545  * \note Generally the x86 terms of BSP and APP core are used
546  * throughout Barrelfish to distinguish between bootstrap core (BSP)
547  * and application cores (APP).
548  *
549  * \param  core_id  Identifier of the core which we want to boot
550  * \param  cpu_type Architecture of the core.
551  * \param  entry    Entry point for code to start execution.
552  *
553  * \retval SYS_ERR_OK Core successfully booted.
554  * \retval SYS_ERR_ARCHITECTURE_NOT_SUPPORTED No handler registered for
555  *     the specified cpu_type.
556  * \retval SYS_ERR_CORE_NOT_FOUND Core failed to boot.
557  */
558 struct sysret sys_monitor_spawn_core(coreid_t core_id, enum cpu_type cpu_type,
559                                      genvaddr_t entry)
560 {
561     assert(cpu_type < CPU_TYPE_NUM);
562     // TODO(gz): assert core_id valid
563     // TODO(gz): assert entry range?
564
565     if (cpu_type < CPU_TYPE_NUM &&
566         coreboot_get_spawn_handler(cpu_type) == NULL) {
567         assert(!"Architecture not supported -- " \
568                "or you failed to register spawn handler?");
569         return SYSRET(SYS_ERR_ARCHITECTURE_NOT_SUPPORTED);
570     }
571
572     int r = (coreboot_get_spawn_handler(cpu_type))(core_id, entry);
573     if (r != 0) {
574         return SYSRET(SYS_ERR_CORE_NOT_FOUND);
575     }
576
577     return SYSRET(SYS_ERR_OK);
578 }
579
580 struct sysret sys_kernel_add_kcb(struct kcb *new_kcb)
581 {
582     kcb_add(new_kcb);
583
584     // update kernel_now offset
585     new_kcb->kernel_off -= kernel_now;
586     // reset scheduler statistics
587     scheduler_reset_time();
588     // update current core id of all domains
589     kcb_update_core_id(new_kcb);
590     // upcall domains with registered interrupts to tell them to re-register
591     irq_table_notify_domains(new_kcb);
592
593     return SYSRET(SYS_ERR_OK);
594 }
595
596 struct sysret sys_kernel_remove_kcb(struct kcb * to_remove)
597 {
598     return SYSRET(kcb_remove(to_remove));
599 }
600
601 struct sysret sys_kernel_suspend_kcb_sched(bool suspend)
602 {
603     printk(LOG_NOTE, "in kernel_suspend_kcb_sched invocation!\n");
604     kcb_sched_suspended = suspend;
605     return SYSRET(SYS_ERR_OK);
606 }
607
608 struct sysret sys_handle_kcb_identify(struct capability* to)
609 {
610     // Return with physical base address of frame
611     // XXX: pack size into bottom bits of base address
612     assert(to->type == ObjType_KernelControlBlock);
613     lvaddr_t vkcb = (lvaddr_t) to->u.kernelcontrolblock.kcb;
614     assert((vkcb & BASE_PAGE_MASK) == 0);
615
616     return (struct sysret) {
617         .error = SYS_ERR_OK,
618         .value = mem_to_local_phys(vkcb) | OBJBITS_KCB,
619     };
620 }
621
622 struct sysret sys_get_absolute_time(void)
623 {
624     // Return kernel_now.
625     // XXX: this may not provide all the properties of absolute time we want,
626     // but should be sufficient to implement stuff that needs timing with 1/10
627     // of a second accuracy range.
628     return (struct sysret) {
629         .error = SYS_ERR_OK,
630         .value = kernel_now + kcb_current->kernel_off,
631     };
632 }
633
634 struct sysret
635 sys_debug_print_capabilities(void) {
636
637     caps_debug_print(&dcb_current->cspace);
638
639     return SYSRET(SYS_ERR_OK);
640 }