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
18#include <refos/vmlayout.h>
19#include <refos/refos.h>
20#include <refos/error.h>
21#include <refos/share.h>
22#include <refos-util/cspace.h>
23#include <refos-util/serv_common.h>
24#include <refos-util/serv_connect.h>
25
26#include <refos-rpc/proc_client.h>
27#include <refos-rpc/proc_client_helper.h>
28#include <refos-rpc/name_client.h>
29#include <refos-rpc/name_client_helper.h>
30
31/* -------------------- Server Default Client Table Handler Helpers ----------------------------- */
32
33struct srv_client*
34srv_ctable_connect_direct_handler(srv_common_t *srv, srv_msg_t *m,
35        seL4_CPtr liveness, int* _errno)
36{
37    assert(srv && srv->magic == SRV_MAGIC && m);
38
39    /* Check that the liveness cap passed in correctly. */
40    if(!srv_check_dispatch_caps(m, 0x00000000, 1)) {
41        SET_ERRNO_PTR(_errno, EINVALIDPARAM);
42        return NULL;
43    }
44    int error = ENOMEM;
45
46    /* Copyout the liveness cap, create session cap cslot. Do not printf before the copyout. */
47    seL4_CPtr livenessCP = rpc_copyout_cptr(liveness);
48    if (!liveness || !livenessCP) {
49        goto error0;
50    }
51
52    /* Allocate the client structure. */
53    struct srv_client *c = client_alloc(&srv->clientTable, livenessCP);
54    if (!c) {
55        goto error1;
56    }
57
58    dprintf("Adding new %s client cID = %d. Hi! (:D)\n", srv->config.serverName, c->cID);
59    assert(c->session);
60
61    /* Authenticate the client to the process server, using its liveness cap. */
62    error = proc_watch_client(c->liveness, srv->notifyClientFaultDeathAsyncEP, &c->deathID);
63    if (error != ESUCCESS) {
64        goto error2;
65    }
66
67    SET_ERRNO_PTR(_errno, ESUCCESS);
68    return c;
69
70    /* Exit stack. */
71error2:
72    client_queue_delete(&srv->clientTable, c->cID);
73    client_table_postaction(&srv->clientTable);
74error1:
75    seL4_CNode_Delete(REFOS_CSPACE, livenessCP, REFOS_CDEPTH);
76    csfree(livenessCP);
77error0:
78    SET_ERRNO_PTR(_errno, error);
79    return NULL;
80
81}
82
83refos_err_t
84srv_ctable_set_param_buffer_handler(srv_common_t *srv, struct srv_client *c,
85        srv_msg_t *m, seL4_CPtr parambufferDataspace, uint32_t parambufferSize)
86{
87    assert(srv && srv->magic == SRV_MAGIC);
88    assert(c && m);
89
90    /* Special case: unset the parameter buffer. */
91    if (!parambufferDataspace && parambufferSize == 0) {
92        seL4_CNode_Revoke(REFOS_CSPACE, c->paramBuffer, REFOS_CDEPTH);
93        seL4_CNode_Delete(REFOS_CSPACE, c->paramBuffer, REFOS_CDEPTH);
94        csfree(c->paramBuffer);
95        c->paramBuffer = 0;
96        c->paramBufferSize = 0;
97        return ESUCCESS;
98    }
99
100    /* Sanity check parameters. */
101    if (!srv_check_dispatch_caps(m, 0x00000000, 1)) {
102        return EINVALIDPARAM;
103    }
104    if (parambufferSize == 0) {
105        return EINVALIDPARAM;
106    }
107
108    /* Set the parameter buffer by copying out the given dspace cap.
109       Do not printf before copyout. */
110    c->paramBuffer = rpc_copyout_cptr(parambufferDataspace);
111    if (!c->paramBuffer) {
112        ROS_ERROR("Failed to copyout the cap.");
113        return ENOMEM;
114    }
115    c->paramBufferSize = parambufferSize;
116    dprintf("Set param buffer for client cID = %d...\n", c->cID);
117
118    return ESUCCESS;
119
120}
121
122void
123srv_ctable_disconnect_direct_handler(srv_common_t *srv, struct srv_client *c)
124{
125    assert(srv && srv->magic == SRV_MAGIC && c);
126    dprintf("Disconnecting %s client cID = %d. Bye! (D:)\n", srv->config.serverName, c->cID);
127    client_queue_delete(&srv->clientTable, c->cID);
128    proc_unwatch_client(c->liveness);
129}
130
131/* ---------------------------------------------------------------------------------------------- */
132
133int
134srv_common_init(srv_common_t *s, srv_common_config_t config)
135{
136    assert(s);
137
138    dprintf("Initialising %s common server state...\n", config.serverName);
139    memset(s, 0, sizeof(srv_common_t));
140    s->config = config;
141
142    /* Create sync anon EP. */
143    dprintf("    creating %s anon endpoint...\n", config.serverName);
144    s->anonEP = proc_new_endpoint();
145    if (!s->anonEP) {
146        ROS_ERROR("srv_common_init could not create endpoint for %s.", config.serverName);
147        return ENOMEM;
148    }
149
150    /* Create async EP. */
151    dprintf("    creating %s async endpoint...\n", config.serverName);
152    s->notifyAsyncEP = proc_new_async_endpoint();
153    if (!s->notifyAsyncEP) {
154        ROS_ERROR("srv_common_init could not create async endpoint.");
155        return ENOMEM;
156    }
157
158    /* Mint badged async EP. */
159    dprintf("    creating async death and/or fault notification badged EP...\n");
160    s->notifyClientFaultDeathAsyncEP = srv_mint(config.faultDeathNotifyBadge, s->notifyAsyncEP);
161    if (!s->notifyClientFaultDeathAsyncEP) {
162        ROS_ERROR("srv_common_init could not create minted async endpoint.");
163        return EINVALID;
164    }
165
166    /* Bind the notification AEP. */
167    dprintf("    binding notification AEP...\n");
168    int error = seL4_TCB_BindNotification(REFOS_THREAD_TCB, s->notifyAsyncEP);
169    if (error != seL4_NoError) {
170        ROS_ERROR("srv_common_init could not bind async endpoint.");
171        return EINVALID;
172    }
173
174    /* Register ourselves under our mountpoint name. */
175    if (config.mountPointPath != NULL) {
176        dprintf("    registering under the mountpoint [%s]...\n", config.mountPointPath);
177        error = nsv_register(config.nameServEP, config.mountPointPath, s->anonEP);
178        if (error != ESUCCESS) {
179            ROS_ERROR("srv_common_init could not register anon ep.");
180            return error;
181        }
182    }
183
184    /* Initialise client table. */
185    if (config.maxClients > 0) {
186        dprintf("    initialising client table for %s...\n", config.serverName);
187        client_table_init(&s->clientTable, config.maxClients, config.clientBadgeBase,
188                          config.clientMagic, s->anonEP);
189
190        dprintf("    initialising client table default handlers for %s...\n", config.serverName);
191        s->ctable_connect_direct_handler = srv_ctable_connect_direct_handler;
192        s->ctable_set_param_buffer_handler = srv_ctable_set_param_buffer_handler;
193        s->ctable_disconnect_direct_handler = srv_ctable_disconnect_direct_handler;
194    }
195
196    /* Set up our server --> process server notification buffer. */
197    if (config.notificationBufferSize > 0) {
198
199        /* Open and map the notification buffer on our side. */
200        dprintf("    initialising notification buffer for %s...\n", config.serverName);
201        s->notifyBuffer = data_open_map(REFOS_PROCSERV_EP, "anon", 0, 0,
202                                        config.notificationBufferSize, -1);
203        if (s->notifyBuffer.err != ESUCCESS) {
204            ROS_ERROR("srv_common_init failed to open & map anon dspace for notifications.");
205            return s->notifyBuffer.err;
206        }
207
208        /* Share and set the notification buffer on the procserv side. */
209        assert(s->notifyBuffer.dataspace);
210        error = proc_notification_buffer(s->notifyBuffer.dataspace);
211        if (error != ESUCCESS) {
212            ROS_ERROR("srv_common_init failed to set notification buffer.");
213            return error;
214        }
215
216        s->notifyBufferStart = 0;
217    }
218
219    /* Set up our server --> process server parameter buffer. */
220    if (config.paramBufferSize > 0) {
221
222        dprintf("    initialising procserv param buffer for %s...\n", config.serverName);
223        s->procServParamBuffer = data_open_map(REFOS_PROCSERV_EP, "anon", 0, 0,
224                                               config.paramBufferSize, -1);
225        if (s->procServParamBuffer.err != ESUCCESS) {
226            ROS_ERROR("srv_common_init failed to open & map anon dspace for param buffer.");
227            return s->procServParamBuffer.err;
228        }
229        error = proc_set_parambuffer(s->procServParamBuffer.dataspace, config.paramBufferSize);
230        if (error != ESUCCESS) {
231            ROS_ERROR("srv_common_init failed to set procserv param buffer.");
232            return error;
233        }
234    }
235
236    s->magic = SRV_MAGIC;
237    return ESUCCESS;
238}
239
240seL4_CPtr
241srv_mint(int badge, seL4_CPtr ep)
242{
243    assert(ep);
244    seL4_CPtr mintEP = csalloc();
245    if (!mintEP) {
246        ROS_ERROR("Could not allocate cslot to mint badge.");
247        return 0;
248    }
249    int error = seL4_CNode_Mint (
250        REFOS_CSPACE, mintEP, REFOS_CDEPTH,
251        REFOS_CSPACE, ep, REFOS_CDEPTH,
252        seL4_NoRead,
253        seL4_CapData_Badge_new(badge)
254    );
255    if (error != seL4_NoError) {
256        ROS_ERROR("Could not mint badge.");
257        csfree(mintEP);
258        return 0;
259    }
260    return mintEP;
261}
262
263bool
264srv_check_dispatch_caps(srv_msg_t *m, seL4_Word unwrappedMask, int numExtraCaps)
265{
266    assert(m);
267    if (seL4_MessageInfo_get_capsUnwrapped(m->message) != unwrappedMask ||
268            seL4_MessageInfo_get_extraCaps(m->message) != numExtraCaps) {
269        return false;
270    }
271    return true;
272}
273
274int
275srv_dispatch_notification(srv_common_t *srv, srv_common_notify_handler_callbacks_t callbacks)
276{
277    assert(srv && srv->magic == SRV_MAGIC);
278
279    /* Allocate a notification structure. */
280    struct proc_notification *notification = malloc(sizeof(struct proc_notification));
281    if (!notification) {
282        dprintf("Out of memory while creating notification struct.\n");
283        return DISPATCH_ERROR;
284    }
285
286    dvprintf("%s recieved fault notification!\n", srv->config.serverName);
287    data_mapping_t *nb = &srv->notifyBuffer;
288    uint32_t bytesRead = 0;
289    int error = DISPATCH_PASS;
290
291    while (1) {
292        bytesRead = 0;
293
294        error = refos_share_read (
295                (char*) notification, sizeof(struct proc_notification),
296                nb->vaddr, nb->size,
297                &srv->notifyBufferStart, &bytesRead
298        );
299        if (!error && bytesRead == 0) {
300            /* No more notifications to read. */
301            break;
302        }
303
304        if (error || bytesRead != sizeof(struct proc_notification)) {
305            /* Error during reading notification. */
306            dprintf("Could not read information from notification buffer!\n");
307            dprintf("    error = %d\n, read = %d", error, bytesRead);
308            free(notification);
309            return DISPATCH_ERROR;
310        }
311
312        /* Paranoid sanity check here. */
313        assert(notification->magic == PROCSERV_NOTIFICATION_MAGIC);
314
315        error = DISPATCH_ERROR;
316        switch (notification->label) {
317        case PROCSERV_NOTIFY_FAULT_DELEGATION:
318            if (callbacks.handle_server_fault) {
319                error = callbacks.handle_server_fault(notification);
320                break;
321            }
322        case PROCSERV_NOTIFY_CONTENT_INIT:
323            if (callbacks.handle_server_content_init) {
324                error = callbacks.handle_server_content_init(notification);
325                break;
326            }
327        case PROCSERV_NOTIFY_DEATH:
328            if (callbacks.handle_server_death_notification) {
329                error = callbacks.handle_server_death_notification(notification);
330                break;
331            }
332        default:
333            /* Should never get here. */
334            ROS_WARNING("Unknown notification.\n");
335            assert(!"Unknown notification.");
336            break;
337        }
338    }
339
340    if (error) {
341        ROS_WARNING("Notification handling error on %s: %d\n", srv->config.serverName, error);
342    }
343
344    free(notification);
345    return error;
346}
347