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 <sel4/sel4.h>
14#include "data_syscall.h"
15#include <refos-rpc/data_server.h>
16#include <refos/refos.h>
17
18#include "../system/memserv/window.h"
19#include "../system/memserv/dataspace.h"
20#include "../system/memserv/ringbuffer.h"
21#include "../system/process/process.h"
22#include "../system/process/pid.h"
23
24/*! @file
25   @brief Process server anon dataspace syscall handler.
26
27   This module is reponsible for implementing the dataspace interface in the generated
28   <refos-rpc/data_server.h>, for anon dataspaces.
29*/
30
31seL4_CPtr
32data_open_handler(void *rpc_userptr , char* rpc_name , int rpc_flags , int rpc_mode , int rpc_size ,
33                  int* rpc_errno)
34{
35    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
36    assert(pcb && pcb->magic == REFOS_PCB_MAGIC);
37
38    (void) pcb;
39    (void) rpc_mode;
40
41    if (rpc_size <= 0) {
42        SET_ERRNO_PTR(rpc_errno, EINVALIDPARAM);
43        return 0;
44    }
45
46    /* Name must either be NULL, empty string or anon. */
47    if (rpc_name != NULL) {
48        if (strlen(rpc_name) > 0 && strcmp(rpc_name, "anon") != 0) {
49            SET_ERRNO_PTR(rpc_errno, EFILENOTFOUND);
50            return 0;
51        }
52    }
53
54    /* Create a ram dataspace of the given size. */
55    struct ram_dspace *newDataspace = ram_dspace_create(&procServ.dspaceList, rpc_size);
56    if (!newDataspace) {
57        ROS_ERROR("Failed to create new_dataspace.\n");
58        SET_ERRNO_PTR(rpc_errno, ENOMEM);
59        return 0;
60    }
61
62    /* Set physical address mode, if required. */
63    if ((rpc_flags & PROCSERV_DSPACE_FLAG_DEVICE_PADDR) != 0) {
64        int error = EACCESSDENIED;
65        if (pcb->systemCapabilitiesMask & PROCESS_PERMISSION_DEVICE_MAP) {
66            error = ram_dspace_set_to_paddr(newDataspace, (uint32_t) rpc_mode);
67        }
68        if (error) {
69            ram_dspace_unref(&procServ.dspaceList, newDataspace->ID);
70            SET_ERRNO_PTR(rpc_errno, error);
71            return 0;
72        }
73    }
74
75    SET_ERRNO_PTR(rpc_errno, ESUCCESS);
76    assert(newDataspace->magic == RAM_DATASPACE_MAGIC);
77    return newDataspace->capability.capPtr;
78}
79
80refos_err_t
81data_close_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd)
82{
83    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
84    struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr;
85    assert(pcb && pcb->magic == REFOS_PCB_MAGIC);
86
87    if (!check_dispatch_caps(m, 0x00000001, 1)) {
88        ROS_ERROR("data_close EINVALIDPARAM: bad dataspace capability.\n");
89        return EINVALIDWINDOW;
90    }
91
92    /* Verify and find the RAM dataspace. */
93    if (!dispatcher_badge_dspace(rpc_dspace_fd)) {
94        ROS_ERROR("EINVALIDPARAM: invalid RAM dataspace badge..\n");
95        return EINVALIDPARAM;
96    }
97    struct ram_dspace *dspace = ram_dspace_get_badge(&procServ.dspaceList, rpc_dspace_fd);
98    if (!dspace) {
99        ROS_ERROR("EINVALIDPARAM: dataspace not found.\n");
100        return EINVALIDPARAM;
101    }
102
103    /* Purge the dataspace from all windows, unmapping every instance of it. */
104    w_purge_dspace(&procServ.windowList, dspace);
105
106    /* Purge the dataspace from all notification buffers and ring buffers. */
107    pid_iterate(&procServ.PIDList, proc_dspace_delete_callback, (void*) dspace);
108
109    /* Check that this is the last reference to the dataspace. */
110    if (dspace->ref != 1) {
111        ROS_WARNING("Dataspace reference is %d and not 1.", dspace->ref);
112        ROS_WARNING("This is either a book-keeping bug or corruption.");
113    }
114
115    /* And finally destroy the RAM dataspace. */
116    ram_dspace_unref(&procServ.dspaceList, dspace->ID);
117    return ESUCCESS;
118}
119
120int
121data_getc_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd , int rpc_block)
122{
123    (void) rpc_userptr;
124    (void) rpc_dspace_fd;
125    return EUNIMPLEMENTED;
126}
127
128off_t
129data_lseek_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd , off_t rpc_offset ,
130                   int rpc_whence)
131{
132    (void) rpc_userptr;
133    (void) rpc_dspace_fd;
134    (void) rpc_offset;
135    (void) rpc_whence;
136    return EUNIMPLEMENTED;
137}
138
139uint32_t
140data_get_size_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd)
141{
142    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
143    struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr;
144    assert(pcb && pcb->magic == REFOS_PCB_MAGIC);
145
146    if (!check_dispatch_caps(m, 0x00000001, 1)) {
147        ROS_ERROR("bad dspace capability.\n");
148        return 0;
149    }
150
151    /* Verify and find the RAM dataspace. */
152    if (!dispatcher_badge_dspace(rpc_dspace_fd)) {
153        ROS_ERROR("EINVALIDPARAM: invalid RAM dataspace badge..\n");
154        return EINVALIDPARAM;
155    }
156    struct ram_dspace *dspace = ram_dspace_get_badge(&procServ.dspaceList, rpc_dspace_fd);
157    if (!dspace) {
158        ROS_ERROR("EINVALIDPARAM: dataspace not found.\n");
159        return EINVALIDPARAM;
160    }
161
162    return dspace->npages * REFOS_PAGE_SIZE;
163}
164
165refos_err_t
166data_expand_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd , uint32_t rpc_size)
167{
168    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
169    struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr;
170    assert(pcb && pcb->magic == REFOS_PCB_MAGIC);
171
172    if (!check_dispatch_caps(m, 0x00000001, 1)) {
173        return EINVALIDPARAM;
174    }
175
176    /* Verify and find the RAM dataspace. */
177    if (!dispatcher_badge_dspace(rpc_dspace_fd)) {
178        ROS_ERROR("EINVALIDPARAM: invalid RAM dataspace badge..\n");
179        return EINVALIDPARAM;
180    }
181    struct ram_dspace *dspace = ram_dspace_get_badge(&procServ.dspaceList, rpc_dspace_fd);
182    if (!dspace) {
183        ROS_ERROR("EINVALIDPARAM: dataspace not found.\n");
184        return EINVALIDPARAM;
185    }
186
187    return ram_dspace_expand(dspace, rpc_size);
188}
189
190/*! \brief Maps the given dataspace to the given memory window. */
191refos_err_t
192data_datamap_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd , seL4_CPtr rpc_memoryWindow,
193                     uint32_t rpc_offset)
194{
195    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
196    struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr;
197    assert(pcb && pcb->magic == REFOS_PCB_MAGIC);
198
199    if (!check_dispatch_caps(m, 0x00000003, 2)) {
200        return EINVALIDPARAM;
201    }
202
203    /* Retrieve and validate window badge. */
204    if (!dispatcher_badge_window(rpc_memoryWindow)) {
205        return EINVALIDWINDOW;
206    }
207    struct w_window *window = w_get_window(&procServ.windowList, rpc_memoryWindow - W_BADGE_BASE);
208    if (!window) {
209        return EINVALIDWINDOW;
210    }
211
212    /* Verify and find the RAM dataspace. */
213    if (!dispatcher_badge_dspace(rpc_dspace_fd)) {
214        ROS_ERROR("EINVALIDPARAM: invalid RAM dataspace badge..\n");
215        return EINVALIDPARAM;
216    }
217    struct ram_dspace *dspace = ram_dspace_get_badge(&procServ.dspaceList, rpc_dspace_fd);
218    if (!dspace) {
219        ROS_ERROR("EINVALIDPARAM: dataspace not found.\n");
220        return EINVALIDPARAM;
221    }
222
223    /* Check that the offset is sane. */
224    if (rpc_offset > (dspace->npages * REFOS_PAGE_SIZE)) {
225        return EINVALIDPARAM;
226    }
227
228    /* Associate the dataspace with the window. This will release whatever the window was associated
229       with beforehand. */
230    w_set_anon_dspace(window, dspace, rpc_offset);
231    return ESUCCESS;
232}
233
234refos_err_t
235data_dataunmap_handler(void *rpc_userptr , seL4_CPtr rpc_memoryWindow)
236{
237    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
238    struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr;
239    assert(pcb && pcb->magic == REFOS_PCB_MAGIC);
240
241    if (!check_dispatch_caps(m, 0x00000001, 1)) {
242        return EINVALIDPARAM;
243    }
244
245    /* Retrieve and validate window badge. */
246    if (!dispatcher_badge_window(rpc_memoryWindow)) {
247        return EINVALIDWINDOW;
248    }
249    struct w_window *window = w_get_window(&procServ.windowList, rpc_memoryWindow - W_BADGE_BASE);
250    if (!window) {
251        return EINVALIDWINDOW;
252    }
253
254    /* If window is already empty, then there's nothing to do here. */
255    if (window->mode == W_MODE_EMPTY) {
256        return ESUCCESS;
257    }
258
259    /* If window is mapped to something else, the un-do operation should not be data_unmap. */
260    if (window->mode != W_MODE_ANONYMOUS) {
261        return EINVALIDPARAM;
262    }
263
264    /* Reset the window back to empty. */
265    w_set_anon_dspace(window, NULL, 0);
266    return ESUCCESS;
267}
268
269
270refos_err_t
271data_init_data_handler(void *rpc_userptr , seL4_CPtr rpc_destDataspace ,
272                             seL4_CPtr rpc_srcDataspace , uint32_t rpc_srcDataspaceOffset)
273{
274    (void) rpc_userptr;
275    (void) rpc_destDataspace;
276    (void) rpc_srcDataspace;
277    (void) rpc_srcDataspaceOffset;
278
279    /* Process server doesn't support init_data syscall.
280       Who on earth would want to initialise another dataspace by anonymous memory? */
281
282    return EUNIMPLEMENTED;
283}
284
285/*! \brief Call from external dataserver asking to be the content initialiser for this dataspace. */
286refos_err_t
287data_have_data_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd , seL4_CPtr rpc_faultNotifyEP ,
288                       uint32_t* rpc_dataID)
289{
290    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
291    struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr;
292    assert(pcb && pcb->magic == REFOS_PCB_MAGIC);
293
294    if (!(check_dispatch_caps(m, 0x00000001, 2) || check_dispatch_caps(m, 0x00000001, 1))) {
295        return EINVALIDPARAM;
296    }
297
298    if (rpc_dataID) {
299        (*rpc_dataID) = 0;
300    }
301
302    /* Verify and find the RAM dataspace. */
303    if (!dispatcher_badge_dspace(rpc_dspace_fd)) {
304        ROS_ERROR("EINVALIDPARAM: invalid RAM dataspace badge..\n");
305        return EINVALIDPARAM;
306    }
307    struct ram_dspace *dspace = ram_dspace_get_badge(&procServ.dspaceList, rpc_dspace_fd);
308    if (!dspace) {
309        ROS_ERROR("EINVALIDPARAM: dataspace not found.\n");
310        return EINVALIDPARAM;
311    }
312
313    /* Special case - no fault notify EP, means unset content-init mode. */
314    if (!rpc_faultNotifyEP) {
315        cspacepath_t path;
316        vka_cspace_make_path(&procServ.vka, 0, &path);
317        return ram_dspace_content_init(dspace, path, PID_NULL);
318    }
319
320    /* Copyout the content-init fault notify EP. */
321    seL4_CPtr faultNotifyEP = dispatcher_copyout_cptr(rpc_faultNotifyEP);
322    if (!faultNotifyEP) {
323        dvprintf("could not copy out faultNotifyEP.");
324        return EINVALIDPARAM;
325    }
326    cspacepath_t path;
327    vka_cspace_make_path(&procServ.vka, faultNotifyEP, &path);
328
329    /* Initialise the dataspace content with given dataserver EP. */
330    int error = ram_dspace_content_init(dspace, path, pcb->pid);
331    if (error != ESUCCESS) {
332        dispatcher_release_copyout_cptr(faultNotifyEP);
333        return error;
334    }
335
336    if (rpc_dataID) {
337        (*rpc_dataID) = dspace->ID;
338    }
339    return ESUCCESS;
340}
341
342refos_err_t
343data_unhave_data_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd)
344{
345    return data_have_data_handler(rpc_userptr, rpc_dspace_fd, 0, NULL);
346}
347
348/*! \brief Reply from another dataserver to provide the process server with content, in reply to a
349           notification the process server has sent it which asked for content.
350*/
351refos_err_t
352data_provide_data_from_parambuffer_handler(void *rpc_userptr , seL4_CPtr rpc_dspace_fd ,
353                                           uint32_t rpc_offset , uint32_t rpc_contentSize)
354{
355    struct proc_pcb *pcb = (struct proc_pcb*) rpc_userptr;
356    struct procserv_msg *m = (struct procserv_msg*) pcb->rpcClient.userptr;
357    assert(pcb && pcb->magic == REFOS_PCB_MAGIC);
358
359    if (!check_dispatch_caps(m, 0x00000001, 1)) {
360        return EINVALIDPARAM;
361    }
362
363    /* Verify and find the RAM dataspace. */
364    if (!dispatcher_badge_dspace(rpc_dspace_fd)) {
365        ROS_ERROR("EINVALIDPARAM: invalid RAM dataspace badge..\n");
366        return EINVALIDPARAM;
367    }
368    struct ram_dspace *dspace = ram_dspace_get_badge(&procServ.dspaceList, rpc_dspace_fd);
369    if (!dspace) {
370        ROS_ERROR("EINVALIDPARAM: dataspace not found.\n");
371        return EINVALIDPARAM;
372    }
373
374    char *initContentBuffer = dispatcher_read_param(pcb, rpc_contentSize);
375    if (!initContentBuffer) {
376        ROS_WARNING("data_provide_data_from_parambuffer_handler: failed to read from paramBuffer.");
377        return ENOPARAMBUFFER;
378    }
379
380    /* Initialise the page of the ram dataspace with these contents. */
381    int error = ram_dspace_write(initContentBuffer, rpc_contentSize, dspace, rpc_offset);
382    if (error != ESUCCESS) {
383        return error;
384    }
385
386    /* Set the bit that says this page has been provided, and notify all waiters blocking on it. */
387    rpc_offset = REFOS_PAGE_ALIGN(rpc_offset);
388    int npages = (rpc_contentSize / REFOS_PAGE_SIZE) + (rpc_contentSize % REFOS_PAGE_SIZE ? 1 : 0);
389    for (vaddr_t i = 0; i < npages; i++) {
390        vaddr_t offset = rpc_offset + (i * REFOS_PAGE_SIZE);
391        ram_dspace_set_content_init_provided(dspace, offset);
392        ram_dspace_content_init_reply_waiters(dspace, offset);
393    }
394
395    return ESUCCESS;
396}
397
398int
399check_dispatch_dataspace(struct procserv_msg *m, void **userptr)
400{
401    return check_dispatch_interface(m, userptr, RPC_DATA_LABEL_MIN, RPC_DATA_LABEL_MAX);
402}
403
404