1/*
2 * Copyright 2016, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(D61_BSD)
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <assert.h>
17#include <autoconf.h>
18#include <sel4utils/elf.h>
19#include <refos/vmlayout.h>
20#include <refos/refos.h>
21#include <refos-rpc/proc_server.h>
22#include <sel4utils/process.h>
23
24#include "../../state.h"
25#include "pid.h"
26#include "process.h"
27#include "thread.h"
28#include "proc_client_watch.h"
29#include <refos/refos.h>
30
31/*! @file
32    @brief Process management module for process server. */
33
34/* ------------------------------ Proc Helper functions ------------------------------------------*/
35
36static int
37proc_staticparam_create_and_set(struct proc_pcb *p, char *param)
38{
39    assert(p && param);
40    size_t paramLen = strlen(param);
41    assert(paramLen <= PROCESS_STATICPARAM_SIZE && paramLen <= REFOS_PAGE_SIZE);
42    struct vs_vspace *vs = &p->vspace;
43    int error = EINVALID;
44
45    /* Allocate the frame to map. */
46    vka_object_t frame;
47    error = vka_alloc_frame(&procServ.vka, seL4_PageBits, &frame);
48    if (error != ESUCCESS || !frame.cptr) {
49        ROS_ERROR("Could not allocate frame kobj. Procserv out of memory.");
50        return ENOMEM;
51    }
52
53    /* Write param data to frame. */
54    error = procserv_frame_write(frame.cptr, param, paramLen, 0);
55    if (error) {
56        ROS_ERROR("Could not write to param frame.");
57        error = ENOMEM;
58        goto exit0;
59    }
60
61    /* Create the static parameter window. */
62    int windowID = W_INVALID_WINID;
63    error = vs_create_window(vs, PROCESS_STATICPARAM_ADDR, PROCESS_STATICPARAM_SIZE,
64            W_PERMISSION_WRITE | W_PERMISSION_READ, true, &windowID);
65    if (error) {
66        ROS_ERROR("Could not create static param window.");
67        goto exit0;
68    }
69
70    /* Map the static parameter window. */
71    error = vs_map(vs, PROCESS_STATICPARAM_ADDR, &frame.cptr, 1);
72    if (error) {
73        ROS_ERROR("Could not map static param window.");
74        goto exit1;
75    }
76
77    return ESUCCESS;
78
79    /* Exit stack. */
80exit1:
81    if (windowID != W_INVALID_WINID) {
82        vs_delete_window(vs, windowID);
83    }
84exit0:
85    vka_free_object(&procServ.vka, &frame);
86    return error;
87}
88
89static void
90proc_pass_badge(struct proc_pcb *p, seL4_CPtr destCSlot, seL4_CPtr ep, seL4_CapRights_t rights,
91                seL4_CapData_t badge)
92{
93    cspacepath_t pathSrc, pathDest;
94    vka_cspace_make_path(&procServ.vka, ep, &pathSrc);
95
96    pathDest.root = p->vspace.cspace.capPtr;
97    pathDest.capPtr = destCSlot;
98    pathDest.capDepth = seL4_WordBits;
99
100    /* Mint a badged endpoint into the client's vspace. */
101    int error = vka_cnode_mint(&pathDest, &pathSrc, seL4_AllRights, badge);
102    if (error) {
103        dprintf("WARNING: proc_pass_badge to dest cslot %d failed.\n", destCSlot);
104        assert(!"proc_pass_badge failed to pass badge.");
105    }
106}
107
108static void
109proc_copy_badge(struct proc_pcb *p, seL4_CPtr destCSlot, seL4_CPtr ep, seL4_CapRights_t rights)
110{
111    cspacepath_t pathSrc, pathDest;
112    vka_cspace_make_path(&procServ.vka, ep, &pathSrc);
113
114    pathDest.root = p->vspace.cspace.capPtr;
115    pathDest.capPtr = destCSlot;
116    pathDest.capDepth = seL4_WordBits;
117
118    /* Move an object into the client's vspace. */
119    int error = vka_cnode_copy(&pathDest, &pathSrc, rights);
120    if (error) {
121        dprintf("WARNING: proc_copy_badge to dest cslot %d failed.\n", destCSlot);
122        assert(!"proc_copy_badge failed to pass badge.");
123    }
124}
125
126static void
127proc_setup_environment(struct proc_pcb *p, char *param)
128{
129    assert(p);
130
131    /* Pass the process its static parameter contents. */
132    proc_staticparam_create_and_set(p, param);
133
134    /* Tell the process about ourself, the process server. */
135    proc_pass_badge (
136            p, REFOS_PROCSERV_EP, procServ.endpoint.cptr,
137            seL4_NoRead,
138            seL4_CapData_Badge_new(pid_get_badge(p->pid))
139    );
140
141    /* Tell the process about its own liveness cap. */
142    proc_pass_badge (
143            p, REFOS_LIVENESS, procServ.endpoint.cptr,
144            seL4_NoRead,
145            seL4_CapData_Badge_new(pid_get_liveness_badge(p->pid))
146    );
147
148    /* Tell the process about its initial thread. */
149    struct proc_tcb *t0 = proc_get_thread(p, 0);
150    assert(t0);
151    proc_pass_badge (
152            p, REFOS_THREAD_TCB, thread_tcb_obj(t0), seL4_AllRights,
153            seL4_CapData_Badge_new(0)
154    );
155
156    #if defined(PLAT_PC99)
157    /* Give any process with IO port permission to read / write to IO ports. */
158    if (p->systemCapabilitiesMask & PROCESS_PERMISSION_DEVICE_IOPORT) {
159        proc_copy_badge (
160            p, REFOS_DEVICE_IO_PORTS, seL4_CapIOPort, seL4_AllRights
161        );
162    }
163    #else
164    (void) proc_copy_badge;
165    #endif
166}
167
168static void
169proc_purge_pid_callback(struct proc_pcb *pcb, void *cookie)
170{
171    uint32_t deathPID = (int) cookie;
172    assert(pcb && pcb->magic == REFOS_PCB_MAGIC);
173    if (pcb->pid == deathPID) {
174        return;
175    }
176
177    /* Orphan the child PID. */
178    if (pcb->parentPID == deathPID) {
179        pcb->parentPID = PID_NULL;
180        pcb->parentWaiting = false;
181    }
182}
183
184/* ----------------------------- Proc interface functions ----------------------------------------*/
185
186int
187proc_config_new(struct proc_pcb *p, uint32_t pid, uint8_t priority, char *imageName,
188                uint32_t systemCapabilitiesMask)
189{
190    assert(p);
191    if (!imageName) {
192        return EINVALIDPARAM;
193    }
194    memset(p, 0, sizeof(struct proc_pcb));
195    int error = EINVALID;
196
197    /* Set initial info. */
198    p->magic = REFOS_PCB_MAGIC;
199    p->systemCapabilitiesMask = systemCapabilitiesMask;
200    p->pid = pid;
201    p->paramBuffer = NULL;
202    p->notificationBuffer = NULL;
203
204    /* Allocate a vspace. */
205    dvprintf("Initialising vspace for %s...\n", imageName);
206    error = vs_initialise(&p->vspace, pid);
207    if (error != ESUCCESS) {
208        dprintf("Failed vspace allocation.\n");
209        goto exit0;
210    }
211
212    /* Create thread. */
213    dvprintf("Allocating thread structure for %s...\n", imageName);
214    cvector_init(&p->threads);
215    struct proc_tcb *thread = kmalloc(sizeof(struct proc_tcb));
216    if (!thread) {
217        ROS_ERROR("Failed to malloc thread structure.\n");
218        error = ENOMEM;
219        goto exit1;
220    }
221
222    /* Load ELF image from CPIO archive. */
223    dvprintf("Loading ELF file %s...\n", imageName);
224    void *entryPoint = sel4utils_elf_load (
225            &p->vspace.vspace, &procServ.vspace, &procServ.vka,
226            &procServ.vka, imageName
227    );
228    if (entryPoint == NULL) {
229        ROS_ERROR("Failed to load ELF file %s.", imageName);
230        error = EINVALIDPARAM;
231        goto exit2;
232    }
233
234    uintptr_t sysInfo = sel4utils_elf_get_vsyscall(imageName);
235
236    /* Configure initial thread. Note that we do this after loading the ELF into vspace, to
237       avoid potentially clobbering the vspace ELF regions. */
238    error = thread_config(thread, priority, (vaddr_t) entryPoint, &p->vspace);
239    if (error) {
240        ROS_ERROR("Failed to configure thread for %s.", imageName);
241        goto exit2;
242    }
243
244    /* Future Work 1:
245       How the process server creates and starts new processes and threads should be modified.
246       Currently the process server creates new processes by creating a 'dummy'
247       sel4utils_process_t structure and then making a call to sel4utils_spawn_process_v()
248       with the sel4utils_process_t structure. The existing vspace, sel4utilsThread and entryPoint
249       are copied into the sel4utils_process_t structure. Instead of using sel4utils_process_t the
250       conventional way, RefOS implements its own structure for managing processes and threads. The
251       RefOS defined proc_pcb structure performs overall the same functionality as the (now existing)
252       sel4utils_process_t which most seL4 projects rely on. Further RefOS work is to modify RefOS
253       so that it does not use its proc_pcb structure and instead uses the sel4utils_process_t structure
254       entirely.
255    */
256    sel4utils_process_t n_process;
257    n_process.vspace = p->vspace.vspace;
258    n_process.thread = thread->sel4utilsThread;
259    n_process.sysinfo = sysInfo;
260    n_process.entry_point = entryPoint;
261
262    error = sel4utils_spawn_process_v(&n_process, &procServ.vka, &procServ.vspace, 0, NULL, 0);
263    if (error) {
264        ROS_ERROR("Failed to spawn process for %s.", imageName);
265        goto exit2;
266    }
267
268    thread->sel4utilsThread = n_process.thread;
269
270    /* Add thread to list. */
271    dvprintf("Adding to threads list...\n");
272    cvector_add(&p->threads, (cvector_item_t) thread);
273    assert(cvector_count(&p->threads) == 1);
274
275    /* Initialise miscellaneous process state. */
276    client_watch_init(&p->clientWatchList);
277    strcpy(p->debugProcessName, imageName);
278
279    return ESUCCESS;
280
281    /* Exit stack. */
282exit2:
283    kfree(thread);
284exit1:
285    vs_unref(&p->vspace);
286exit0:
287    memset(p, 0, sizeof(struct proc_pcb));
288    return error;
289}
290
291int
292proc_start_thread(struct proc_pcb *p, int tindex, void* arg0, void* arg1)
293{
294    dvprintf("Starting PID %d thread %d!!!\n", p->pid, tindex);
295    assert(cvector_count(&p->threads) >= 1);
296    struct proc_tcb *t = (struct proc_tcb *) cvector_get(&p->threads, tindex);
297    assert(t);
298    assert(t->entryPoint);
299    return thread_start(t, arg0, arg1);
300}
301
302int
303proc_load_direct(char *name, int priority, char *param, unsigned int parentPID,
304                 uint32_t systemCapabilitiesMask)
305{
306    /* Allocate a PID. */
307    dprintf("Allocating PID and PCB...\n");
308    uint32_t npid = pid_alloc(&procServ.PIDList);
309    if (npid == PID_NULL) {
310        dprintf("Failed PID allocation.\n");
311        return ENOMEM;
312    }
313    dprintf("Allocated PID %d!...\n", npid);
314    struct proc_pcb *pcb = pid_get_pcb(&procServ.PIDList, npid);
315    assert(pcb);
316
317    /* Configure the process. */
318    dprintf("Configuring process for PID %d!...\n", npid);
319    int error = proc_config_new(pcb, npid, priority, name, systemCapabilitiesMask);
320    if (error != ESUCCESS) {
321        pid_free(&procServ.PIDList, npid);
322        return error;
323    }
324    pcb->parentPID = parentPID;
325
326    /* If we are selfloading this process, then the actual image name is in the param string.
327       This is a bit of a hacky way, but the debug name is only used for debugging so its not too
328       bad. */
329    if (!strcmp(name, "selfloader")) {
330        strcpy(pcb->debugProcessName, param);
331    }
332
333    /* Configure the process' vspace and cspace for the RefOS userland environment. */
334    proc_setup_environment(pcb, param);
335
336    /* Start the initial thread (thread 0). */
337    error = proc_start_thread(pcb, 0, NULL, NULL);
338    if (error) {
339        ROS_ERROR("Could not start thread 0!");
340        pid_free(&procServ.PIDList, npid);
341        return error;
342    }
343
344    return ESUCCESS;
345}
346
347int
348proc_nice(struct proc_pcb *p, int tindex, int priority)
349{
350    assert(cvector_count(&p->threads) >= 1);
351    struct proc_tcb *t = (struct proc_tcb *) cvector_get(&p->threads, tindex);
352    if (!t) {
353        ROS_WARNING("proc_nice warning: no such thread %d!", tindex);
354        return EINVALIDPARAM;
355    }
356    assert(thread_tcb_obj(t));
357    int error = seL4_TCB_SetPriority(thread_tcb_obj(t), priority);
358    if (error) {
359        ROS_ERROR("proc_nice failed to set TCB priority.");
360        return EINVALID;
361    }
362    t->priority = priority;
363    return ESUCCESS;
364}
365
366static void proc_parent_reply(struct proc_pcb *p);
367
368void
369proc_release(struct proc_pcb *p)
370{
371    assert(p && p->magic == REFOS_PCB_MAGIC);
372    memset(&p->rpcClient, 0, sizeof(rpc_client_state_t));
373
374    /* For anybody that's watching us, they've got to be notified, and then unwatched. */
375    pid_iterate(&procServ.PIDList, client_watch_notify_death_callback, (void*) p->pid);
376
377    /* For any children PID under us, they are now orphaned. */
378    dvprintf("    orphaning children...\n");
379    pid_iterate(&procServ.PIDList, proc_purge_pid_callback, (void*) p->pid);
380
381    /* If there's potentially a process waiting for us to exit, then notify that process. */
382    dvprintf("    replying parent...\n");
383    if (p->parentPID != PID_NULL) {
384        proc_parent_reply(p);
385    }
386
387    /* Unreference the parameter buffer. */
388    dvprintf("    unreffing parameter buffer...\n");
389    if (p->paramBuffer) {
390        ram_dspace_unref(p->paramBuffer->parentList, p->paramBuffer->ID);
391        p->paramBuffer = NULL;
392    }
393
394    /* Release notification buffer. */
395    dvprintf("    releasing notification buffer...\n");
396    if (p->notificationBuffer) {
397        rb_delete(p->notificationBuffer);
398        p->notificationBuffer = NULL;
399    }
400
401    /* Release fault reply cap. */
402    dvprintf("    releasing caller EP...\n");
403    if (p->faultReply.capPtr) {
404        vka_cnode_delete(&p->faultReply);
405        vka_cspace_free(&procServ.vka, p->faultReply.capPtr);
406        p->faultReply.capPtr = 0;
407    }
408
409    /* Unreference vspace. */
410    dvprintf("    Unref vspace...\n");
411    vs_unref(&p->vspace);
412
413    /* Clean up threads. */
414    dvprintf("    Cleaning up threads...\n");
415    int nthreads = cvector_count(&p->threads);
416    for (int i = 0; i < nthreads; i++) {
417        dvprintf("       Cleaning up thread %d...\n", i);
418        struct proc_tcb *thread = (struct proc_tcb *) cvector_get(&p->threads, i);
419        assert(thread && thread->magic == REFOS_PROCESS_THREAD_MAGIC);
420        thread_release(thread);
421        kfree(thread);
422    }
423    cvector_free(&p->threads);
424
425    dvprintf("    process released OK.\n");
426    p->magic = 0;
427    p->pid = 0;
428    p->systemCapabilitiesMask = 0x0;
429}
430
431void
432proc_queue_release(struct proc_pcb *p)
433{
434    assert(p && p->magic == REFOS_PCB_MAGIC);
435    assert(procServ.exitProcessPID == PID_NULL);
436    procServ.exitProcessPID = p->pid;
437}
438
439/* ------------------------------- Proc interface helper functions ------------------------------ */
440
441void
442proc_set_parambuffer(struct proc_pcb *p, struct ram_dspace *paramBuffer)
443{
444    assert(p && p->magic == REFOS_PCB_MAGIC);
445    if (p->paramBuffer == paramBuffer) {
446        /* Same parameter buffer, nothing to do here. */
447        return;
448    } else if (p->paramBuffer != NULL) {
449        /* We need to undeference the previous parameter buffer. */
450        ram_dspace_unref(p->paramBuffer->parentList, p->paramBuffer->ID);
451        p->paramBuffer = NULL;
452    }
453    if (paramBuffer != NULL) {
454        /* Now reference the new parameter buffer. */
455        ram_dspace_ref(paramBuffer->parentList, paramBuffer->ID);
456    }
457    p->paramBuffer = paramBuffer;
458}
459
460int
461proc_set_notificationbuffer(struct proc_pcb *p, struct ram_dspace *notifBuffer)
462{
463    /* Release old notification buffer. */
464    if (p->notificationBuffer) {
465        rb_delete(p->notificationBuffer);
466        p->notificationBuffer = NULL;
467    }
468    if (!notifBuffer) {
469        return ESUCCESS;
470    }
471    p->notificationBuffer = rb_create(notifBuffer, RB_WRITEONLY);
472    if (!p->notificationBuffer) {
473        ROS_ERROR("Could not create notification buffer");
474        return ENOMEM;
475    }
476    return ESUCCESS;
477}
478
479void
480proc_dspace_delete_callback(struct proc_pcb *p, void *cookie)
481{
482    assert(p && p->magic == REFOS_PCB_MAGIC);
483    struct ram_dspace *dspace = (struct ram_dspace *) cookie;
484    assert(dspace && dspace->magic == RAM_DATASPACE_MAGIC);
485
486    if (p->paramBuffer) {
487        if (p->paramBuffer == dspace || (p->paramBuffer->parentList == dspace->parentList &&
488                p->paramBuffer->ID == dspace->ID)) {
489            /* The process's parameter buffer just got deleted. Unset the process's parambuffer. */
490            proc_set_parambuffer(p, NULL);
491            assert(p->paramBuffer == NULL);
492        }
493    }
494
495    if (p->notificationBuffer) {
496        if (p->notificationBuffer->dataspace == dspace ||
497                (p->notificationBuffer->dataspace->parentList == dspace->parentList &&
498                 p->notificationBuffer->dataspace->ID == dspace->ID) ) {
499            rb_delete(p->notificationBuffer);
500            p->notificationBuffer = NULL;
501        }
502    }
503
504}
505
506struct proc_tcb *
507proc_get_thread(struct proc_pcb *p, int tindex)
508{
509    assert(p && p->magic == REFOS_PCB_MAGIC);
510    if (tindex >= cvector_count(&p->threads)) {
511        return NULL;
512    }
513    struct proc_tcb *t = (struct proc_tcb *) cvector_get(&p->threads, tindex);
514    assert(t && t->magic == REFOS_PROCESS_THREAD_MAGIC);
515    assert(t->entryPoint);
516    return t;
517}
518
519int
520proc_save_caller(struct proc_pcb *p)
521{
522    assert(p && p->magic == REFOS_PCB_MAGIC);
523
524    /* Release any previous fault reply cap. */
525    if (p->faultReply.capPtr) {
526        vka_cnode_delete(&p->faultReply);
527        vka_cspace_free(&procServ.vka, p->faultReply.capPtr);
528        p->faultReply.capPtr = 0;
529    }
530
531    /* Allocate the cslot for reply cap to be saved to. */
532    int error = vka_cspace_alloc_path(&procServ.vka, &p->faultReply);
533    if (error != seL4_NoError) {
534        ROS_ERROR("Could not allocate path to save caller. PRocserv outof cslots.");
535        return ENOMEM;
536    }
537    assert(p->faultReply.capPtr);
538
539    /* Save the caller cap. */
540    error = vka_cnode_saveCaller(&p->faultReply);
541    if (error != seL4_NoError) {
542        ROS_ERROR("Could not copy out reply cap.");
543        return EINVALID;
544    }
545
546    return ESUCCESS;
547}
548
549int
550proc_clone(struct proc_pcb *p, int *threadID, vaddr_t stackAddr, vaddr_t entryPoint)
551{
552    assert(p && p->magic == REFOS_PCB_MAGIC);
553    if (threadID) {
554        (*threadID) = -1;
555    }
556
557    /* No support for fork() behaviour. */
558    if (!stackAddr || !entryPoint) {
559        return EINVALIDPARAM;
560    }
561
562    /* Get the main thread of process to read its priority. */
563    struct proc_tcb * t = proc_get_thread(p, 0);
564    if (!t) {
565        ROS_ERROR("Failed to retrieve main thread.\n");
566        return EINVALID;
567    }
568
569    /* Create the TCB struct for the clone thread. */
570    dvprintf("Allocating thread structure...\n");
571    struct proc_tcb *thread = kmalloc(sizeof(struct proc_tcb));
572    if (!thread) {
573        ROS_ERROR("Failed to malloc thread structure.\n");
574        return ENOMEM;
575    }
576
577    /* Configure new thread, sharing the process's address space */
578    int error = thread_config(thread, t->priority, (vaddr_t) entryPoint, &p->vspace);
579    if (error) {
580        ROS_ERROR("Failed to configure thread for new thread.");
581        goto exit1;
582    }
583
584    /* Future Work 1:
585       How the process server creates and starts new processes and threads should be modified.
586       Currently the process server creates new processes by creating a 'dummy'
587       sel4utils_process_t structure and then making a call to sel4utils_spawn_process_v()
588       with the sel4utils_process_t structure. The existing vspace, sel4utilsThread and entryPoint
589       are copied into the sel4utils_process_t structure. Instead of using sel4utils_process_t the
590       conventional way, RefOS implements its own structure for managing processes and threads. The
591       RefOS defined proc_pcb structure performs overall the same functionality as the (now existing)
592       sel4utils_process_t which most seL4 projects rely on. Further RefOS work is to modify RefOS
593       so that it does not use its proc_pcb structure and instead uses the sel4utils_process_t structure
594       entirely.
595    */
596    sel4utils_process_t n_process;
597    n_process.vspace = p->vspace.vspace;
598    n_process.thread = thread->sel4utilsThread;
599    n_process.sysinfo = 0;
600    n_process.entry_point = (void *) entryPoint;
601
602    error = sel4utils_spawn_process_v(&n_process, &procServ.vka, &procServ.vspace, 0, NULL, 0);
603    if (error) {
604        ROS_ERROR("Failed to spawn process.");
605        goto exit1;
606    }
607
608    thread->sel4utilsThread = n_process.thread;
609
610    /* Add thread to list. */
611    dvprintf("Adding to threads list...\n");
612    int tID = cvector_count(&p->threads);
613    if (threadID) {
614        (*threadID) = tID;
615    }
616    cvector_add(&p->threads, (cvector_item_t) thread);
617    assert(cvector_count(&p->threads) >= 1);
618
619    /* Start the new child thread. */
620    error = proc_start_thread(p, tID, NULL, NULL);
621    if (error) {
622        ROS_ERROR("Could not start child thread %d!", tID);
623        goto exit2;
624    }
625
626    return ESUCCESS;
627
628    /* Exit stack. */
629exit2:
630    cvector_delete(&p->threads, tID);
631    thread_release(thread);
632exit1:
633    kfree(thread);
634    assert(error != ESUCCESS);
635    return error;
636}
637
638extern seL4_MessageInfo_t _dispatcherEmptyReply;
639
640void
641proc_fault_reply(struct proc_pcb *p)
642{
643    if (!p->faultReply.capPtr) {
644        return;
645    }
646    seL4_Send(p->faultReply.capPtr, _dispatcherEmptyReply);
647    vka_cnode_delete(&p->faultReply);
648    vka_cspace_free(&procServ.vka, p->faultReply.capPtr);
649    p->faultReply.capPtr = 0;
650}
651
652static void
653proc_parent_reply(struct proc_pcb *p)
654{
655    assert(p->parentPID != PID_NULL);
656
657    /* Retrieve the parent PCB. */
658    struct proc_pcb* parentPCB = pid_get_pcb(&procServ.PIDList, p->parentPID);
659    if (!parentPCB) {
660        ROS_WARNING("proc_parent_reply Could not get parent PID.");
661        return;
662    }
663    if (!parentPCB->parentWaiting) {
664        /* Nothing to do here. */
665        return;
666    }
667    if (!parentPCB->faultReply.capPtr) {
668        ROS_WARNING("proc_parent_reply No reply endpoint! Book-keeping error.");
669        return;
670    }
671
672    dprintf("proc_parent_reply childPID %d parentPID %d.\n", p->pid, parentPCB->pid);
673    parentPCB->rpcClient.skip_reply = false;
674    parentPCB->rpcClient.reply = parentPCB->faultReply.capPtr;
675    reply_proc_new_proc((void*) parentPCB, &p->exitStatus, (refos_err_t) EXIT_SUCCESS);
676
677    vka_cnode_delete(&parentPCB->faultReply);
678    vka_cspace_free(&procServ.vka, parentPCB->faultReply.capPtr);
679    parentPCB->faultReply.capPtr = 0;
680}
681
682void
683proc_syscall_postaction(void)
684{
685     if (procServ.exitProcessPID != PID_NULL) {
686        struct proc_pcb *pcb = pid_get_pcb(&procServ.PIDList, procServ.exitProcessPID);
687        if (!pcb) {
688            ROS_WARNING("proc_syscall_postaction error: No such PID!");
689            procServ.exitProcessPID = PID_NULL;
690            return;
691        }
692
693        /* Delete the actual process. */
694        uint32_t pid = pcb->pid;
695        pcb->rpcClient.skip_reply = true;
696        dprintf("    Releasing process %d [%s]...\n", pcb->pid, pcb->debugProcessName);
697        proc_release(pcb);
698
699        /* Kill the PID. */
700        dvprintf("    Freeing PID...\n");
701        pid_free(&procServ.PIDList, pid);
702        dvprintf("    Process exit OK!\n");
703
704        procServ.exitProcessPID = PID_NULL;
705    }
706}
707