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 "proc_syscall.h"
14
15#include <vka/kobject_t.h>
16#include <refos-rpc/proc_common.h>
17#include <refos-rpc/proc_server.h>
18
19#include "../system/process/pid.h"
20#include "../system/process/process.h"
21#include "../system/process/proc_client_watch.h"
22#include "../system/addrspace/vspace.h"
23#include "../system/memserv/window.h"
24#include "../system/memserv/dataspace.h"
25#include "../system/memserv/ringbuffer.h"
26
27/*! @file
28    @brief Dispatcher for the procserv syscalls.
29
30    Handles calls to process server syscall interface. The process server interface provides process
31    abstraction, simple naming, server registration, memory windows...etc. (For more details, refer
32    to the protocol design document.). The methods here implement the functions in the generated
33    header file <refos-rpc/proc_server.h>.
34
35    The memory related process server syscalls resides in mem_syscall.c and mem_syscall.h.
36*/
37
38/* ---------------------------- Proc Server syscall helper functions ---------------------------- */
39
40/*! @brief Creates a kernel endpoint object for the given process.
41
42    Creates a kernel endpoint object for the given process. Also does the book-keeping underneath so
43    that the created objects can be destroyed when the process exits.
44
45    @param pcb Handle to the process to create the object for. (no ownership)
46    @param type The kernel endpoint object type to create for the process; sync or async.
47    @return Handle to the created object if successful (no ownership), NULL if there was an
48            error. (eg. ran out of heap or untyped memory).
49 */
50static seL4_CPtr
51proc_syscall_allocate_endpoint(struct proc_pcb *pcb, kobject_t type)
52{
53    assert(pcb && pcb->magic == REFOS_PCB_MAGIC);
54
55    /* Allocate the kernel object. */
56    vka_object_t endpoint;
57    int error = -1;
58    if (type == KOBJECT_ENDPOINT) {
59        error = vka_alloc_endpoint(&procServ.vka, &endpoint);
60    } else if (type == KOBJECT_NOTIFICATION) {
61        error = vka_alloc_notification(&procServ.vka, &endpoint);
62    } else {
63        assert(!"Invalid endpoint type.");
64    }
65    if (error || endpoint.cptr == 0) {
66        ROS_ERROR("failed to allocate endpoint for process. Procserv out of memory.\n");
67        return 0;
68    }
69
70    /* Track this allocation, so it may be freed along with the process vspace. */
71    vs_track_obj(&pcb->vspace, endpoint);
72    return endpoint.cptr;
73}
74
75/* -------------------------------- Proc Server syscall handlers -------------------------------- */
76
77/*! @brief Handles ping syscalls. */
78refos_err_t
79proc_ping_handler(void *rpc_userptr) {
80    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
81    struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr;
82    assert(pcb->magic == REFOS_PCB_MAGIC);
83
84    (void) pcb;
85    (void) m;
86
87    dprintf(COLOUR_G "PROCESS SERVER RECIEVED PING!!! HELLO THERE! (����������)������" COLOUR_RESET "\n");
88    return ESUCCESS;
89}
90
91/*! @brief Handles sync endpoint creation syscalls. */
92seL4_CPtr
93proc_new_endpoint_internal_handler(void *rpc_userptr , refos_err_t* rpc_errno)
94{
95    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
96    assert(pcb->magic == REFOS_PCB_MAGIC);
97    dprintf("Process server creating endpoint!\n");
98    seL4_CPtr ep = proc_syscall_allocate_endpoint(pcb, KOBJECT_ENDPOINT);
99    if (!ep) {
100        SET_ERRNO_PTR(rpc_errno, ENOMEM);
101        return 0;
102    }
103    SET_ERRNO_PTR(rpc_errno, ESUCCESS);
104    return ep;
105}
106
107/*! @brief Handles async endpoint creation syscalls. */
108seL4_CPtr
109proc_new_async_endpoint_internal_handler(void *rpc_userptr , refos_err_t* rpc_errno)
110{
111    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
112    assert(pcb->magic == REFOS_PCB_MAGIC);
113    dprintf("Process server creating async endpoint!\n");
114    seL4_CPtr ep = proc_syscall_allocate_endpoint(pcb, KOBJECT_NOTIFICATION);
115    if (!ep) {
116        SET_ERRNO_PTR(rpc_errno, ENOMEM);
117        return 0;
118    }
119    SET_ERRNO_PTR(rpc_errno, ESUCCESS);
120    return ep;
121}
122
123/*! @brief Handles client watching syscalls.
124
125    Most servers would need to call this in order to be notified of client death in order to be able
126    to delete any internal book-keeping for the dead client.
127 */
128refos_err_t
129proc_watch_client_handler(void *rpc_userptr , seL4_CPtr rpc_liveness , seL4_CPtr rpc_deathEP ,
130                          int32_t* rpc_deathID)
131{
132    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
133    struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr;
134    assert(pcb->magic == REFOS_PCB_MAGIC);
135
136    if (!check_dispatch_caps(m, 0x00000001, 2)) {
137        return EINVALIDPARAM;
138    }
139
140    /* Retrieve the corresponding client's ASID unwrapped from its liveness cap. */
141    if (!dispatcher_badge_liveness(rpc_liveness)) {
142        return EINVALIDPARAM;
143    }
144
145    /* Verify the corresponding client. */
146    struct proc_pcb *client = pid_get_pcb(&procServ.PIDList,
147                                          rpc_liveness - PID_LIVENESS_BADGE_BASE);
148    if (!client) {
149        return EINVALIDPARAM;
150    }
151    assert(client->magic == REFOS_PCB_MAGIC);
152
153    /* Copy out the death notification endpoint. */
154    seL4_CPtr deathNotifyEP = dispatcher_copyout_cptr(rpc_deathEP);
155    if (!deathNotifyEP) {
156        ROS_ERROR("could not copy out deathNotifyEP.");
157        return ENOMEM;
158    }
159
160    /* Add the new client to the watch list of the calling process. */
161    int error = client_watch(&pcb->clientWatchList, client->pid, deathNotifyEP);
162    if (error) {
163        ROS_ERROR("failed to add to watch list. Procserv possibly out of memory.");
164        dispatcher_release_copyout_cptr(deathNotifyEP);
165        return error;
166    }
167    if (rpc_deathID) {
168        (*rpc_deathID) = client->pid;
169    }
170
171    return ESUCCESS;
172}
173
174
175/*! @brief Handles client un-watching syscalls. */
176refos_err_t
177proc_unwatch_client_handler(void *rpc_userptr , seL4_CPtr rpc_liveness)
178{
179    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
180    struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr;
181    assert(pcb->magic == REFOS_PCB_MAGIC);
182
183    if (!check_dispatch_caps(m, 0x00000001, 1)) {
184        return EINVALIDPARAM;
185    }
186
187    /* Retrieve the corresponding client's ASID unwrapped from its liveness cap. */
188    if (!dispatcher_badge_liveness(rpc_liveness)) {
189        return EINVALIDPARAM;
190    }
191
192    /* Verify the corresponding client. */
193    struct proc_pcb *client = pid_get_pcb(&procServ.PIDList,
194                                          rpc_liveness - PID_LIVENESS_BADGE_BASE);
195    if (!client) {
196        return EINVALIDPARAM;
197    }
198    assert(client->magic == REFOS_PCB_MAGIC);
199
200    /* Remove the given client PID from the watch list. */
201    client_unwatch(&pcb->clientWatchList, client->pid);
202    return ESUCCESS;
203}
204
205/*! @brief Sets the process's parameter buffer.
206
207    Sets the process's parameter buffer to the given RAM dataspace. Only support a RAM dataspace
208    which orginated from the process server's own dataspace implementation, does not support
209    an external dataspace.
210*/
211refos_err_t
212proc_set_parambuffer_handler(void *rpc_userptr , seL4_CPtr rpc_dataspace , uint32_t rpc_size)
213{
214    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
215    struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr;
216    assert(pcb->magic == REFOS_PCB_MAGIC);
217    struct ram_dspace *dataspace;
218
219    /* Special case zero size and NULL parameter buffer - means unset the parameter buffer. */
220    if (rpc_size == 0 && rpc_dataspace == 0) {
221        proc_set_parambuffer(pcb, NULL);
222        return ESUCCESS;
223    }
224
225    if (!check_dispatch_caps(m, 0x00000001, 1)) {
226        return EINVALIDPARAM;
227    }
228
229    /* Check if the given badge is a RAM dataspace. */
230    if (!dispatcher_badge_dspace(rpc_dataspace)) {
231        return EINVALIDPARAM;
232    }
233
234    /* Retrieve RAM dataspace structure. */
235    dataspace = ram_dspace_get_badge(&procServ.dspaceList, rpc_dataspace);
236    if (!dataspace) {
237        dvprintf("No such dataspace!.\n");
238        return EINVALID;
239    }
240
241    /* Set new parameter buffer. */
242    proc_set_parambuffer(pcb, dataspace);
243    return ESUCCESS;
244}
245
246
247/*! @brief Starts a new process. */
248refos_err_t
249proc_new_proc_handler(void *rpc_userptr , char* rpc_name , char* rpc_params , bool rpc_block ,
250                      int32_t rpc_priority , int32_t* rpc_status)
251{
252    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
253    assert(pcb->magic == REFOS_PCB_MAGIC);
254
255    if (!rpc_name) {
256        return EINVALIDPARAM;
257    }
258
259    /* Kick off an instance of selfloader, which will do the actual process loading work. */
260    int error = proc_load_direct("selfloader", rpc_priority, rpc_name, pcb->pid, 0x0);
261    if (error != ESUCCESS) {
262        ROS_WARNING("failed to run selfloader for new process [%s].", rpc_name);
263        return error;
264    }
265
266    /* Optionally block parent process until child process has finished. */
267    if (rpc_block) {
268        /* Save the reply endpoint. */
269        proc_save_caller(pcb);
270        pcb->parentWaiting = true;
271        pcb->rpcClient.skip_reply = true;
272        return ESUCCESS;
273    }
274
275    /* Immediately resume the parent process. */
276    return ESUCCESS;
277}
278
279/*! @brief Exits and deletes the process which made this call. */
280refos_err_t
281proc_exit_handler(void *rpc_userptr , int32_t rpc_status)
282{
283    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
284    assert(pcb->magic == REFOS_PCB_MAGIC);
285    dprintf("Process PID %u exiting with status %d !!!\n", pcb->pid, rpc_status);
286    pcb->exitStatus = rpc_status;
287    pcb->rpcClient.skip_reply = true;
288    proc_queue_release(pcb);
289    return ESUCCESS;
290}
291
292int
293proc_clone_internal_handler(void *rpc_userptr , seL4_Word rpc_entryPoint , seL4_Word rpc_childStack
294        , int rpc_flags , seL4_Word rpc_arg , refos_err_t* rpc_errno)
295{
296    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
297    assert(pcb->magic == REFOS_PCB_MAGIC);
298
299    int threadID = -1;
300    int error = proc_clone(pcb, &threadID, (vaddr_t) rpc_childStack, (vaddr_t) rpc_entryPoint);
301    SET_ERRNO_PTR(rpc_errno, error);
302    return threadID;
303}
304
305refos_err_t
306proc_nice_handler(void *rpc_userptr , int rpc_threadID , int rpc_priority)
307{
308    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
309    assert(pcb->magic == REFOS_PCB_MAGIC);
310    return proc_nice(pcb, rpc_threadID, rpc_priority);
311}
312
313seL4_CPtr
314proc_get_irq_handler_handler(void *rpc_userptr , int rpc_irq)
315{
316    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
317    assert(pcb->magic == REFOS_PCB_MAGIC);
318    if ((pcb->systemCapabilitiesMask & PROCESS_PERMISSION_DEVICE_IRQ) == 0) {
319        return 0;
320    }
321    dprintf("Process %d (%s) getting IRQ number %d...\n", pcb->pid, pcb->debugProcessName, rpc_irq);
322    return procserv_get_irq_handler(rpc_irq);
323}
324
325
326/* ------------------------------------ Dispatcher functions ------------------------------------ */
327
328int
329check_dispatch_syscall(struct procserv_msg *m, void **userptr) {
330    return check_dispatch_interface(m, userptr, RPC_PROC_LABEL_MIN, RPC_PROC_LABEL_MAX);
331}
332